@zq-silk/yui 0.15.7 → 0.15.9

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 (308) hide show
  1. package/ARCHITECTURE.md +194 -399
  2. package/ARCHITECTURE.zh-CN.md +151 -0
  3. package/README.md +306 -1131
  4. package/dist/agent/adapterCatalog.js +15 -2
  5. package/dist/agent/agent.js +23 -3
  6. package/dist/agent/argumentPolicy.js +7 -1
  7. package/dist/agent/connectionPlan.js +62 -0
  8. package/dist/agent/executionComponents.js +158 -0
  9. package/dist/agent/launchEnvironment.js +31 -3
  10. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  11. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  12. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  13. package/dist/artifacts/artifactCapability.js +74 -0
  14. package/dist/artifacts/artifactCommitLock.js +249 -0
  15. package/dist/artifacts/artifactPaths.js +151 -0
  16. package/dist/artifacts/gitArtifactRef.js +146 -0
  17. package/dist/artifacts/managedGit.js +332 -0
  18. package/dist/artifacts/taskArtifactRepository.js +277 -0
  19. package/dist/brief/taskBrief.js +12 -0
  20. package/dist/cli/agentConfigurationPicker.js +13 -0
  21. package/dist/cli/commandCatalog.js +165 -74
  22. package/dist/cli/interactionCandidates.js +5 -5
  23. package/dist/cli/interactionPolicy.js +38 -8
  24. package/dist/cli/invocationRouter.js +1 -1
  25. package/dist/cli/managedDiagnostics.js +28 -0
  26. package/dist/cli/operatorWizard.js +1 -7
  27. package/dist/cli/roleOptionOrder.js +27 -0
  28. package/dist/cli/roleWizard.js +50 -14
  29. package/dist/cli/updateOrchestrator.js +1 -1
  30. package/dist/cli/updatePorts.js +3 -4
  31. package/dist/cli.js +245 -95
  32. package/dist/commands/agentCommands.js +72 -14
  33. package/dist/commands/capabilityCommands.js +9 -6
  34. package/dist/commands/configCommands.js +20 -20
  35. package/dist/commands/deliveryGuardPreflight.js +2 -2
  36. package/dist/commands/executionAuditCommands.js +24 -24
  37. package/dist/commands/globalRoleCommands.js +1 -1
  38. package/dist/commands/grantCommands.js +4 -4
  39. package/dist/commands/operatorCommands.js +34 -9
  40. package/dist/commands/projectCommands.js +4 -4
  41. package/dist/commands/resourcesCommands.js +2 -2
  42. package/dist/commands/roleConfiguration.js +25 -5
  43. package/dist/commands/roleRuntimeGuard.js +4 -5
  44. package/dist/commands/sessionCommands.js +3 -7
  45. package/dist/commands/taskActivationCommands.js +281 -0
  46. package/dist/commands/taskActor.js +28 -49
  47. package/dist/commands/taskCommands.js +1461 -720
  48. package/dist/commands/taskContextCommand.js +39 -583
  49. package/dist/commands/taskExecutionCommands.js +32 -32
  50. package/dist/commands/taskInputCommands.js +40 -104
  51. package/dist/commands/taskIntegrationCommands.js +3 -2
  52. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  53. package/dist/commands/taskNextActionCommand.js +8 -8
  54. package/dist/commands/taskOverviewCommand.js +33 -45
  55. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  56. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  57. package/dist/commands/telemetryCommands.js +36 -38
  58. package/dist/config/configCatalog.js +4 -4
  59. package/dist/config/yuiConfig.js +8 -8
  60. package/dist/context/contextSnapshot.js +10 -10
  61. package/dist/context/dispatchContext.js +11 -11
  62. package/dist/context/roleSessionContext.js +6 -3
  63. package/dist/context/{turnContextPack.js → runContextPack.js} +158 -81
  64. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  65. package/dist/context/sessionBootstrapManifest.js +21 -2
  66. package/dist/context/sourceRunContext.js +30 -0
  67. package/dist/context/taskContext.js +481 -0
  68. package/dist/context/wakeNotification.js +27 -27
  69. package/dist/controller/agentRuntimeObserver.js +21 -24
  70. package/dist/controller/capabilityBridge.js +17 -6
  71. package/dist/controller/clientRuntime.js +65 -92
  72. package/dist/controller/controller.js +120 -188
  73. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  74. package/dist/controller/jobControl.js +54 -85
  75. package/dist/controller/resourceInventory.js +8 -27
  76. package/dist/controller/resourceInventoryLinux.js +12 -13
  77. package/dist/controller/runtime.js +529 -479
  78. package/dist/controller/runtimeEventInbox.js +55 -25
  79. package/dist/controller/runtimeEventProcessor.js +22 -31
  80. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  81. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  82. package/dist/controller/runtimeObservationHook.js +14 -18
  83. package/dist/controller/sessionNotify.js +16 -24
  84. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  85. package/dist/controller/structuredProviderObservation.js +138 -99
  86. package/dist/coordination/workMailbox.js +3 -3
  87. package/dist/coordination/workMailboxQueue.js +36 -33
  88. package/dist/core/boundedRpc.js +8 -1
  89. package/dist/core/controllerClient.js +20 -1
  90. package/dist/core/controllerServer.js +4 -4
  91. package/dist/doctor/doctor.js +13 -2
  92. package/dist/domain/agentResultTransport.js +9 -9
  93. package/dist/execution/codexThreadNaming.js +2 -8
  94. package/dist/execution/executionHealth.js +51 -63
  95. package/dist/execution/reviewMainRun.js +137 -0
  96. package/dist/execution/workItemExecution.js +28 -29
  97. package/dist/execution/workItemExecutionProjection.js +99 -107
  98. package/dist/execution/workItemMainRun.js +141 -0
  99. package/dist/executor/agentAdapter.js +227 -20
  100. package/dist/executor/agentConfigurationCatalog.js +126 -4
  101. package/dist/executor/agentConfigurationProbe.js +162 -4
  102. package/dist/executor/agentExecutor.js +79 -78
  103. package/dist/executor/effectiveLaunch.js +105 -18
  104. package/dist/executor/executorRegistry.js +29 -44
  105. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  106. package/dist/executor/workspacePreflightClassification.js +16 -16
  107. package/dist/grant/capabilityGrant.js +6 -3
  108. package/dist/input/inputRequest.js +12 -10
  109. package/dist/integration/gitIntegrationService.js +4 -11
  110. package/dist/integration/integrationQueueService.js +4 -4
  111. package/dist/interaction/operatorPresentation.js +1 -1
  112. package/dist/kernel/builtinCapabilities.js +255 -12
  113. package/dist/kernel/capabilityRegistry.js +64 -18
  114. package/dist/kernel/instanceHost.js +12 -1
  115. package/dist/kernel/kernelPorts.js +2 -2
  116. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  117. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  118. package/dist/message/message.js +118 -6
  119. package/dist/message/messageContinuation.js +204 -0
  120. package/dist/observability/executionAudit.js +70 -72
  121. package/dist/observability/faultClassification.js +2 -2
  122. package/dist/observability/orchestrationMetrics.js +8 -8
  123. package/dist/operator/operatorSessionHistory.js +1 -7
  124. package/dist/output/agentConfigurationPresentation.js +8 -3
  125. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  126. package/dist/output/rolePresentation.js +54 -3
  127. package/dist/plugins/pluginChild.js +104 -0
  128. package/dist/plugins/pluginIntent.js +26 -0
  129. package/dist/plugins/pluginInterpreter.js +43 -0
  130. package/dist/plugins/pluginPackage.js +101 -0
  131. package/dist/plugins/pluginProcess.js +112 -0
  132. package/dist/plugins/pluginService.js +388 -0
  133. package/dist/profile/agentProfile.js +1 -1
  134. package/dist/repository/gitWorkspace.js +26 -4
  135. package/dist/repository/project.js +19 -4
  136. package/dist/repository/taskBaseFreshness.js +13 -13
  137. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  138. package/dist/repository/taskWorkspacePreparer.js +344 -83
  139. package/dist/resources/autoResourceGc.js +3 -3
  140. package/dist/resources/liveReferences.js +3 -3
  141. package/dist/resources/projectResource.js +75 -0
  142. package/dist/resources/projectResourceService.js +343 -0
  143. package/dist/resources/resourceDiscovery.js +6 -6
  144. package/dist/resources/resourceGc.js +1 -1
  145. package/dist/resources/resourceRegistrar.js +1 -1
  146. package/dist/resources/resourceTypes.js +1 -1
  147. package/dist/review/deltaRecheck.js +3 -3
  148. package/dist/review/reviewAcceptance.js +16 -16
  149. package/dist/review/reviewDecision.js +7 -7
  150. package/dist/review/reviewRound.js +21 -20
  151. package/dist/review/reviewerAvailability.js +2 -2
  152. package/dist/role/role.js +51 -7
  153. package/dist/role/taskRoleUpdate.js +30 -0
  154. package/dist/runtime/acpProtocol.js +425 -0
  155. package/dist/runtime/acpSession.js +731 -0
  156. package/dist/runtime/acpSessionConfiguration.js +260 -0
  157. package/dist/runtime/agentDriver.js +30 -11
  158. package/dist/runtime/agentEndpoint.js +278 -0
  159. package/dist/runtime/agentEndpointIdentity.js +86 -0
  160. package/dist/runtime/agentEndpointOwnership.js +239 -0
  161. package/dist/runtime/agentError.js +2 -10
  162. package/dist/runtime/agentHost.js +565 -314
  163. package/dist/runtime/agentRunConfiguration.js +258 -0
  164. package/dist/runtime/builtinAgentDrivers.js +134 -18
  165. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  166. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  167. package/dist/runtime/claude-process-owner +0 -0
  168. package/dist/runtime/codexAppServerRuntime.js +38 -30
  169. package/dist/runtime/codexInteractiveHost.js +41 -6
  170. package/dist/runtime/continuationManager.js +2 -6
  171. package/dist/runtime/executionEnvironment.js +30 -0
  172. package/dist/runtime/firstProgressAdvisory.js +11 -11
  173. package/dist/runtime/index.js +4 -3
  174. package/dist/runtime/jsonLineChannel.js +109 -0
  175. package/dist/runtime/launchBroker.js +91 -16
  176. package/dist/runtime/launchDiagnostics.js +2 -2
  177. package/dist/runtime/lifecycleReservation.js +10 -18
  178. package/dist/runtime/managedCaller.js +61 -17
  179. package/dist/runtime/nativeSessionControl.js +102 -0
  180. package/dist/runtime/ports.js +6 -21
  181. package/dist/runtime/processExitObservation.js +8 -7
  182. package/dist/runtime/promptEnvelope.js +17 -6
  183. package/dist/runtime/providerContinuation.js +3 -9
  184. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  185. package/dist/runtime/providerControl.js +2 -7
  186. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  187. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  188. package/dist/runtime/runtimeBinding.js +0 -1
  189. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  190. package/dist/runtime/runtimeDeadlines.js +9 -0
  191. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  192. package/dist/runtime/runtimeObservation.js +29 -65
  193. package/dist/runtime/runtimeProjection.js +43 -51
  194. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  195. package/dist/runtime/sessionLaunchRequest.js +3 -7
  196. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  197. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  198. package/dist/runtime/sessionReconciliation.js +4 -8
  199. package/dist/runtime/sessionTerminationGuard.js +70 -259
  200. package/dist/runtime/sessionTokenMetrics.js +5 -16
  201. package/dist/runtime/structuredProviderHost.js +237 -117
  202. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  203. package/dist/runtime/tmuxAdapters.js +39 -86
  204. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  205. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  206. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  207. package/dist/scheduler/ports.js +80 -9
  208. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  209. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  210. package/dist/scheduler/taskExecutionProjection.js +120 -124
  211. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  212. package/dist/scheduler/taskWake.js +11 -4
  213. package/dist/scheduler/wakeReason.js +9 -1
  214. package/dist/setup/setupCommand.js +3 -7
  215. package/dist/storage/migrations/agentRunContract.js +159 -0
  216. package/dist/storage/migrations/artifactsToGit.js +338 -0
  217. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  218. package/dist/storage/migrations/submitIntent.js +126 -0
  219. package/dist/storage/sqliteSchema.js +467 -7
  220. package/dist/storage/sqliteStore.js +355 -220
  221. package/dist/storage/storageVersions.js +1 -1
  222. package/dist/storage/storeRpc.js +10 -5
  223. package/dist/storage/taskStore.js +13 -11
  224. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  225. package/dist/surface/surfaceContributions.js +102 -0
  226. package/dist/task/completionReadiness.js +32 -6
  227. package/dist/task/deliveryGuard.js +16 -16
  228. package/dist/task/draftPlan.js +72 -12
  229. package/dist/task/nextAction.js +144 -128
  230. package/dist/task/remoteDelivery.js +6 -6
  231. package/dist/task/task.js +184 -18
  232. package/dist/task/taskActivation.js +327 -0
  233. package/dist/task/taskActivationService.js +408 -0
  234. package/dist/task/taskRecordReference.js +5 -4
  235. package/dist/task/taskRecordRetirement.js +1 -1
  236. package/dist/task/taskSubmission.js +236 -0
  237. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  238. package/dist/telemetry/telemetryConfig.js +14 -14
  239. package/dist/telemetry/telemetryWiring.js +2 -2
  240. package/dist/web/assets/assetManifest.js +2 -0
  241. package/dist/web/assets/client/app.js +121 -20
  242. package/dist/web/assets/client/components.js +87 -54
  243. package/dist/web/assets/client/i18n.js +83 -41
  244. package/dist/web/assets/client/markdown.js +1 -1
  245. package/dist/web/assets/client/taskSurface.js +442 -0
  246. package/dist/web/assets/client/view.js +49 -44
  247. package/dist/web/assets/shell.js +1 -1
  248. package/dist/web/assets/styles/cards.js +22 -4
  249. package/dist/web/controllerWeb.js +60 -0
  250. package/dist/web/webMutation.js +28 -0
  251. package/dist/web/webServer.js +133 -8
  252. package/dist/web/webSnapshot.js +81 -74
  253. package/dist/web/webTaskSurface.js +64 -0
  254. package/dist/workItem/dependencyGate.js +1 -1
  255. package/dist/workItem/workItem.js +84 -48
  256. package/dist/workspace/workItemChangeSetManager.js +16 -9
  257. package/docs/agent-result-consumption.md +96 -0
  258. package/docs/agent-result-consumption.zh-CN.md +81 -0
  259. package/docs/agent-runtime-drivers.md +93 -0
  260. package/docs/agent-runtime-drivers.zh-CN.md +77 -0
  261. package/docs/architecture/README.md +50 -0
  262. package/docs/architecture/README.zh-CN.md +43 -0
  263. package/docs/architecture/capabilities-and-resources.md +118 -0
  264. package/docs/architecture/capabilities-and-resources.zh-CN.md +83 -0
  265. package/docs/managed-turn-and-session-runtime.md +224 -0
  266. package/docs/managed-turn-and-session-runtime.zh-CN.md +180 -0
  267. package/docs/observability/README.md +83 -0
  268. package/docs/observability/README.zh-CN.md +71 -0
  269. package/docs/plugin-sdk.md +393 -0
  270. package/docs/plugin-sdk.zh-CN.md +293 -0
  271. package/docs/provider-runtime.md +165 -0
  272. package/docs/provider-runtime.zh-CN.md +132 -0
  273. package/docs/release-workflow.md +305 -0
  274. package/docs/release-workflow.zh-CN.md +237 -0
  275. package/docs/roles-and-configuration.md +115 -0
  276. package/docs/roles-and-configuration.zh-CN.md +96 -0
  277. package/docs/sqlite-control-plane-design.md +78 -0
  278. package/docs/sqlite-control-plane-design.zh-CN.md +62 -0
  279. package/docs/task-dag-semantics.md +80 -0
  280. package/docs/task-dag-semantics.zh-CN.md +59 -0
  281. package/docs/task-delivery.md +105 -0
  282. package/docs/task-delivery.zh-CN.md +82 -0
  283. package/docs/task-local-identity.md +8 -6
  284. package/docs/task-local-identity.zh-CN.md +58 -0
  285. package/docs/testing/verification-levels.md +88 -0
  286. package/docs/testing/verification-levels.zh-CN.md +69 -0
  287. package/i18n/README.zh-CN.md +270 -722
  288. package/package.json +3 -2
  289. package/skills/yui-leader/SKILL.md +88 -304
  290. package/skills/yui-leader/references/execution.md +303 -0
  291. package/skills/yui-leader/references/integration.md +39 -0
  292. package/skills/yui-leader/references/planning.md +109 -0
  293. package/skills/yui-leader/references/replicated-execution.md +42 -0
  294. package/skills/yui-leader/references/task-plugins.md +37 -0
  295. package/skills/yui-operator/SKILL.md +46 -62
  296. package/skills/yui-reviewer/SKILL.md +35 -36
  297. package/skills/yui-runtime/SKILL.md +88 -24
  298. package/skills/yui-runtime/references/publication.md +22 -0
  299. package/skills/yui-runtime/references/recovery.md +64 -0
  300. package/skills/yui-worker/SKILL.md +37 -39
  301. package/dist/cli/roleOptionCatalog.js +0 -68
  302. package/dist/context/sourceTurnContext.js +0 -30
  303. package/dist/execution/reviewMainTurn.js +0 -161
  304. package/dist/execution/workItemMainTurn.js +0 -164
  305. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  306. package/dist/runtime/preallocatedNativeSession.js +0 -13
  307. package/dist/runtime/runtimeStopReceipt.js +0 -42
  308. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -0,0 +1,126 @@
1
+ import { createTaskEvent } from "../../event/taskEvent.js";
2
+ /**
3
+ * Storage 18 -> 19 data step for task-32 Requirement A (submit intent & Draft
4
+ * auto-routing).
5
+ *
6
+ * This helper is INDEPENDENT, deterministic and idempotent. The unified 18 -> 19
7
+ * migration owned by the artifacts WorkItem REGISTERS it (as its `migrateData`)
8
+ * and bumps `CURRENT_STORAGE_VERSION`; this module neither registers a second
9
+ * migration nor changes any schema.
10
+ *
11
+ * Requirement A adds three optional fields inside `messages.payload`, all part of
12
+ * this same 18 -> 19 contract even though each is optional — "optional" governs a
13
+ * single record's shape, not the Home compatibility line, so the version
14
+ * transition is still declared here and carried by the unified migration:
15
+ *
16
+ * - `intent` (record | discuss | develop): an absent value already reads as
17
+ * `discuss` (never `develop`), so historical Messages need no rewrite.
18
+ * - `submissionKey`: the client-chosen idempotency key. Only ever set going
19
+ * forward; NO historical key is fabricated (task-32 §2.3, message review), so
20
+ * old Messages stay keyless and non-idempotent exactly as before.
21
+ * - `submissionReceipt`: the frozen disposition a keyed submission replays from.
22
+ * Written only alongside a new `submissionKey`, so it too never appears on
23
+ * historical data.
24
+ *
25
+ * Because none of the three needs a value backfilled onto old rows, the only data
26
+ * step this migration performs is the planning-entered reconstruction below.
27
+ *
28
+ * The one fact old data cannot express is "this Draft already entered planning".
29
+ * Before Requirement A a Leader-waking user/operator submission on a Draft WAS
30
+ * the planning discussion, but it left no `task.planning-entered` fact because
31
+ * that event type did not exist. After the upgrade `develop` auto-activates an
32
+ * *unplanned* Draft; without this step a Draft that was mid-discussion would be
33
+ * mis-read as unplanned and snatched into auto-activation (task-32 §2.2, §4).
34
+ *
35
+ * So for every Draft that received at least one legitimate historical planning
36
+ * submission this appends exactly one `task.planning-entered` event, which makes
37
+ * {@link draftHasEnteredPlanning} derive `true` and routes a later develop to
38
+ * manual activation. A qualifying submission is an *unaddressed* user/operator
39
+ * Message that was not save-only (`wakePolicy !== "none"`) — byte-for-byte the
40
+ * same shape the new discuss path routes through the shared submission service.
41
+ *
42
+ * This is deliberately the conservative direction: it can only ever make develop
43
+ * MORE cautious, never activate a Draft it should not. Save-only Messages (old
44
+ * `wakePolicy === "none"`, the shape `record` now names) are excluded, so a Draft
45
+ * that only ever recorded context stays legitimately unplanned and its first
46
+ * develop may still auto-activate. Non-Draft Tasks are skipped because
47
+ * {@link draftHasEnteredPlanning} answers only about Drafts, and Activation
48
+ * requests are left untouched — a legacy request's absent `origin` is the
49
+ * truthful marker that it stays behind explicit activation.
50
+ */
51
+ export function migrateSubmitIntent(db) {
52
+ const drafts = db.prepare("SELECT task_id FROM tasks_catalog WHERE status = 'draft'").all();
53
+ if (drafts.length === 0)
54
+ return;
55
+ const listMessages = db.prepare("SELECT payload FROM messages WHERE task_id = ? ORDER BY seq ASC");
56
+ const listEventRows = db.prepare("SELECT event_id, type FROM events WHERE task_id = ?");
57
+ const readHighWater = db.prepare("SELECT high_water FROM id_sequences WHERE task_id = ? AND kind = 'event'");
58
+ // Advance the per-Task event high-water to exactly the id we just consumed, so
59
+ // the runtime keeps allocating strictly above every migrated event (mirrors
60
+ // sqliteStore's id_sequences bookkeeping).
61
+ const upsertHighWater = db.prepare(`INSERT INTO id_sequences (task_id, kind, high_water) VALUES (?, 'event', ?)
62
+ ON CONFLICT(task_id, kind) DO UPDATE SET high_water = ?`);
63
+ const insertEvent = db.prepare("INSERT INTO events (task_id, event_id, type, occurred_at, payload) VALUES (?, ?, ?, ?, ?)");
64
+ for (const { task_id: taskId } of drafts) {
65
+ const eventRows = listEventRows.all(taskId);
66
+ // Idempotent: a Draft that already carries the fact (from a prior run of this
67
+ // migration, or from a real runtime planning entry) is left untouched.
68
+ if (eventRows.some((row) => row.type === PLANNING_ENTERED_EVENT_TYPE))
69
+ continue;
70
+ const entry = firstPlanningSubmission(listMessages.all(taskId));
71
+ if (entry === undefined)
72
+ continue;
73
+ // Allocate the next event id from the greater of the recorded high-water and
74
+ // the largest event id actually present, so a fixture or historical database
75
+ // whose id_sequences lags the events table can never produce a colliding id.
76
+ const highWater = readHighWater.get(taskId)?.high_water ?? 0;
77
+ const maxEventSeq = eventRows.reduce((max, row) => Math.max(max, eventSequence(row.event_id)), 0);
78
+ const seq = Math.max(highWater, maxEventSeq) + 1;
79
+ // Build through the shared constructor so a migrated event is byte-identical
80
+ // in shape to one the shared submission service writes at runtime, and dated
81
+ // to the historical submission rather than the wall-clock upgrade moment.
82
+ const event = createTaskEvent(`event-${seq}`, taskId, PLANNING_ENTERED_EVENT_TYPE, { messageId: entry.id, intent: "discuss", derivedBy: MIGRATION_PROVENANCE }, new Date(entry.createdAt));
83
+ insertEvent.run(taskId, event.id, event.type, event.createdAt, JSON.stringify(event));
84
+ upsertHighWater.run(taskId, seq, seq);
85
+ }
86
+ }
87
+ /**
88
+ * The never-compacted event type recording a Draft entered planning. Inlined as a
89
+ * literal rather than imported so this released migration stays frozen against a
90
+ * future runtime rename of the constant; `TASK_PLANNING_ENTERED_EVENT` is the
91
+ * runtime owner, and a tripwire test asserts the two never diverge.
92
+ */
93
+ const PLANNING_ENTERED_EVENT_TYPE = "task.planning-entered";
94
+ /** Marks the event as reconstructed by this migration, for later audit. */
95
+ const MIGRATION_PROVENANCE = "submit-intent-migration";
96
+ /**
97
+ * The earliest Message on a Draft that represents a historical planning
98
+ * submission, or `undefined` if the Draft only ever recorded save-only context.
99
+ *
100
+ * A qualifying Message is an unaddressed (`recipient === undefined`) user or
101
+ * operator Message whose wake policy was not `none`. An absent wake policy
102
+ * qualifies: before Requirement A that was the default Leader-waking submission,
103
+ * exactly the shape an omitted intent normalizes to `discuss` today.
104
+ */
105
+ function firstPlanningSubmission(rows) {
106
+ for (const row of rows) {
107
+ const message = JSON.parse(row.payload);
108
+ if (message.kind !== "user" && message.kind !== "operator")
109
+ continue;
110
+ if (message.recipient !== undefined)
111
+ continue; // an addressed continuation, not a submission
112
+ if (message.wakePolicy === "none")
113
+ continue; // old save-only == record, never planning
114
+ if (typeof message.id !== "string")
115
+ continue;
116
+ if (typeof message.createdAt !== "string")
117
+ continue;
118
+ return { id: message.id, createdAt: message.createdAt };
119
+ }
120
+ return undefined;
121
+ }
122
+ /** The numeric suffix of an `event-<n>` id, or 0 for any unexpected shape. */
123
+ function eventSequence(eventId) {
124
+ const match = /^event-(\d+)$/.exec(eventId);
125
+ return match === null ? 0 : Number.parseInt(match[1], 10);
126
+ }
@@ -21,10 +21,13 @@
21
21
  * is a no-op; a crash mid-upgrade rolls the whole migration transaction back.
22
22
  */
23
23
  import { createHash } from "node:crypto";
24
+ import { REMOVE_RUNTIME_GENERATION_SQL, removeRuntimeGenerationRecords } from "./migrations/removeRuntimeGeneration.js";
25
+ import { migrateAgentRunContract } from "./migrations/agentRunContract.js";
26
+ import { migrateArtifactsToGit } from "./migrations/artifactsToGit.js";
24
27
  import { CURRENT_STORAGE_VERSION, MIN_SUPPORTED_STORAGE_VERSION } from "./storageVersions.js";
25
28
  /** Telemetry retention bounds (§4.4). Open question 3 in §11; defaults from the design. */
26
- export const TELEMETRY_KEEP_PER_GENERATION = 200;
27
- export const TELEMETRY_TURN_CAP = 50_000;
29
+ export const TELEMETRY_KEEP_PER_RUN = 200;
30
+ export const TELEMETRY_RUN_CAP = 50_000;
28
31
  /**
29
32
  * Version 1 migration: creates every table and index.
30
33
  *
@@ -697,6 +700,449 @@ ON durable_jobs(task_id, json_extract(payload, '$.operation.actorId'),
697
700
  // Accepted inputs/results can use an exact attempt without a native Turn id.
698
701
  // Preserve all valid historical records; never repair failed Turns or logs.
699
702
  sql: "SELECT 1; -- exact attempt identity without a fabricated native Turn id"
703
+ },
704
+ {
705
+ version: 4,
706
+ name: "session-and-process-identity",
707
+ introducedIn: "0.15.8",
708
+ sql: REMOVE_RUNTIME_GENERATION_SQL,
709
+ migrateData: removeRuntimeGenerationRecords
710
+ },
711
+ {
712
+ version: 5,
713
+ name: "project-resource-artifacts",
714
+ introducedIn: "0.15.8",
715
+ sql: `
716
+ CREATE TABLE artifacts (
717
+ task_id TEXT NOT NULL REFERENCES tasks_catalog(task_id),
718
+ id TEXT NOT NULL,
719
+ payload TEXT NOT NULL,
720
+ PRIMARY KEY (task_id, id)
721
+ );
722
+ CREATE TABLE local_resources (
723
+ id TEXT PRIMARY KEY,
724
+ canonical_identity TEXT NOT NULL UNIQUE,
725
+ payload TEXT NOT NULL
726
+ );
727
+ CREATE TABLE environment_preparations (
728
+ task_id TEXT NOT NULL REFERENCES tasks_catalog(task_id),
729
+ id TEXT NOT NULL,
730
+ payload TEXT NOT NULL,
731
+ PRIMARY KEY (task_id, id)
732
+ );
733
+ UPDATE projects SET payload = json_set(payload,
734
+ '$.schemaVersion', 6, '$.resourceRefs', json('[]'),
735
+ '$.defaultCapabilityProviders', json('{}'));
736
+ `
737
+ },
738
+ {
739
+ version: 6,
740
+ name: "session-endpoint-implementation",
741
+ introducedIn: "0.15.8",
742
+ // Valid earlier Sessions used these two built-in protocols. Generation 1
743
+ // retains those codecs and identities behind the new execution boundary.
744
+ // Preserve native IDs, effective snapshots and all historical results.
745
+ sql: `
746
+ UPDATE role_session_sets SET payload = json_set(payload, '$.sessions', json((
747
+ SELECT json_group_object(key, json_set(value, '$.schemaVersion', 6,
748
+ '$.endpointImplementation', json_object(
749
+ 'id', 'yui.agent-endpoint.' || json_extract(value, '$.adapterId'), 'generation', '1')))
750
+ FROM json_each(payload, '$.sessions')
751
+ )));
752
+ UPDATE role_session_sets SET payload = json_set(payload, '$.history', json((
753
+ SELECT json_group_array(json_set(value, '$.schemaVersion', 6,
754
+ '$.endpointImplementation', json_object(
755
+ 'id', 'yui.agent-endpoint.' || json_extract(value, '$.adapterId'), 'generation', '1')))
756
+ FROM json_each(payload, '$.history')
757
+ ))) WHERE json_type(payload, '$.history') = 'array';
758
+ UPDATE global_role_session_sets SET payload = json_set(payload, '$.sessions', json((
759
+ SELECT json_group_object(key, json_set(value, '$.schemaVersion', 6,
760
+ '$.endpointImplementation', json_object(
761
+ 'id', 'yui.agent-endpoint.' || json_extract(value, '$.adapterId'), 'generation', '1')))
762
+ FROM json_each(payload, '$.sessions')
763
+ )));
764
+ UPDATE global_role_session_sets SET payload = json_set(payload, '$.history', json((
765
+ SELECT json_group_object(key, json_set(value, '$.schemaVersion', 6,
766
+ '$.endpointImplementation', json_object(
767
+ 'id', 'yui.agent-endpoint.' || json_extract(value, '$.adapterId'), 'generation', '1')))
768
+ FROM json_each(payload, '$.history')
769
+ ))) WHERE json_type(payload, '$.history') = 'object';
770
+ `
771
+ },
772
+ {
773
+ version: 7,
774
+ name: "task-facts-and-explicit-acceptance",
775
+ introducedIn: "0.15.8",
776
+ // Retain the complete retirement metadata/events and diagnostic facts.
777
+ // Only the public lifecycle changes; ordinary stores read one shape.
778
+ sql: `
779
+ UPDATE task_records SET payload = json_set(payload, '$.status', 'cancelled')
780
+ WHERE json_extract(payload, '$.status') = 'retired';
781
+ UPDATE task_records SET payload = json_set(payload, '$.retirementIsolation', json('true'))
782
+ WHERE json_extract(payload, '$.retiredAt') IS NOT NULL;
783
+ UPDATE tasks_catalog SET status = 'cancelled' WHERE status = 'retired';
784
+ UPDATE tasks_catalog SET lifecycle = 'cancelled' WHERE lifecycle = 'retired';
785
+ UPDATE task_records SET brief = json_set(brief, '$.revision', 1) WHERE brief IS NOT NULL;
786
+ UPDATE work_items SET payload = json_set(payload,
787
+ '$.historicalState', json_patch(json('{}'), json_object(
788
+ 'status', json_extract(payload, '$.status'),
789
+ 'outcome', json_extract(payload, '$.outcome'),
790
+ 'endedAt', json_extract(payload, '$.endedAt'))),
791
+ '$.status', CASE status WHEN 'completed' THEN 'accepted' WHEN 'retired' THEN 'retired' ELSE 'open' END);
792
+ UPDATE work_items SET payload = json_remove(payload, '$.outcome', '$.endedAt')
793
+ WHERE json_extract(payload, '$.status') = 'open';
794
+ UPDATE work_items SET status = json_extract(payload, '$.status');
795
+ UPDATE work_items SET payload = json_set(payload, '$.currentCandidateId',
796
+ json_extract(payload, '$.candidates[#-1].id'))
797
+ WHERE json_extract(payload, '$.historicalState.status') = 'awaiting_acceptance';
798
+ UPDATE work_items SET payload = json_set(payload, '$.acceptedCandidateId',
799
+ json_extract(payload, '$.candidates[#-1].id'))
800
+ WHERE status = 'accepted' AND json_array_length(payload, '$.candidates') > 0;
801
+ `
802
+ },
803
+ {
804
+ version: 8,
805
+ name: "event-owned-edit-history",
806
+ introducedIn: "0.15.8",
807
+ // Move embedded histories to one immutable import event per Task.
808
+ // Existing events stay byte-identical; imports supply otherwise missing
809
+ // historical fields and are evidence only, never executable lifecycle input.
810
+ // Candidates may now carry optional fixed Artifact refs; absent refs remain
811
+ // valid for historical Candidates, so this payload addition needs no backfill.
812
+ sql: `
813
+ CREATE TEMP TABLE migrated_edit_history AS
814
+ SELECT records.task_id,
815
+ COALESCE(sequences.high_water, 0) + 1 AS sequence,
816
+ strftime('%Y-%m-%dT%H:%M:%fZ', 'now') AS imported_at,
817
+ json_object(
818
+ 'sourceStorageVersion', '7',
819
+ 'taskOutcomes', '' || COALESCE(json_extract(records.payload, '$.outcomeHistory'), '[]'),
820
+ 'workAcceptances', '' || COALESCE((
821
+ SELECT json_group_array(json_object(
822
+ 'workItemId', work_item_id,
823
+ 'history', json_extract(payload, '$.acceptanceHistory')))
824
+ FROM (SELECT * FROM work_items
825
+ WHERE task_id = records.task_id
826
+ AND json_array_length(payload, '$.acceptanceHistory') > 0
827
+ ORDER BY work_item_id)
828
+ ), '[]')
829
+ ) AS history
830
+ FROM task_records AS records
831
+ LEFT JOIN id_sequences AS sequences
832
+ ON sequences.task_id = records.task_id AND sequences.kind = 'event'
833
+ WHERE json_array_length(records.payload, '$.outcomeHistory') > 0
834
+ OR EXISTS (SELECT 1 FROM work_items
835
+ WHERE task_id = records.task_id AND json_array_length(payload, '$.acceptanceHistory') > 0);
836
+
837
+ INSERT INTO events (task_id, event_id, type, occurred_at, payload)
838
+ SELECT task_id, 'event-' || sequence, 'history.imported', imported_at,
839
+ json_object('schemaVersion', 2, 'id', 'event-' || sequence,
840
+ 'taskId', task_id, 'type', 'history.imported',
841
+ 'createdAt', imported_at, 'payload', json(history))
842
+ FROM migrated_edit_history;
843
+ INSERT INTO id_sequences (task_id, kind, high_water)
844
+ SELECT task_id, 'event', sequence FROM migrated_edit_history WHERE true
845
+ ON CONFLICT(task_id, kind) DO UPDATE SET high_water = excluded.high_water;
846
+ DROP TABLE migrated_edit_history;
847
+
848
+ UPDATE task_records SET payload = json_remove(payload, '$.outcomeHistory'),
849
+ brief = CASE WHEN brief IS NULL THEN NULL ELSE json_remove(brief, '$.revision') END;
850
+ UPDATE work_items SET payload = json_remove(payload, '$.acceptanceHistory');
851
+ `
852
+ },
853
+ {
854
+ version: 9,
855
+ name: "adopted-agent-execution-environment",
856
+ introducedIn: "0.15.8",
857
+ // Optional Role selection and immutable Session/Turn effective snapshot.
858
+ // Existing records deliberately keep their managed-workspace execution;
859
+ // adopted preparations must never be inferred as an automatic binding.
860
+ sql: "SELECT 1; -- explicit adopted execution environment; absent means managed workspace"
861
+ },
862
+ {
863
+ version: 10,
864
+ name: "plugin-validation-evidence",
865
+ introducedIn: "0.15.8",
866
+ sql: `
867
+ CREATE TABLE plugin_validations (
868
+ task_id TEXT NOT NULL REFERENCES tasks_catalog(task_id),
869
+ id TEXT NOT NULL,
870
+ payload TEXT NOT NULL,
871
+ PRIMARY KEY (task_id, id)
872
+ );
873
+ `
874
+ },
875
+ {
876
+ version: 11,
877
+ name: "plugin-enable-intent",
878
+ introducedIn: "0.15.8",
879
+ // A v10 validation proves no enable/disable choice. Preserve it unchanged;
880
+ // never infer desired configuration from historical reports or processes.
881
+ sql: `
882
+ CREATE TABLE plugin_intents (
883
+ task_id TEXT NOT NULL REFERENCES tasks_catalog(task_id),
884
+ plugin_id TEXT NOT NULL,
885
+ payload TEXT NOT NULL,
886
+ PRIMARY KEY (task_id, plugin_id)
887
+ );
888
+ `
889
+ },
890
+ {
891
+ // Renumbered from 10 to 12 when this branch adopted upstream master: PR320
892
+ // took 10 and 11 while this work was in review. Both are already merged, so
893
+ // they own those numbers; this migration appends after them rather than
894
+ // competing for a number, and no released entry is touched.
895
+ version: 12,
896
+ name: "draft-planning-and-deferred-activation",
897
+ // Same unreleased release as migrations 4-11: this does not bump the version.
898
+ introducedIn: "0.15.8",
899
+ // One current-version migration for everything this change adds. No existing
900
+ // record is rewritten and no payload is backfilled:
901
+ //
902
+ // - the optional Task activation request: absent means activation was
903
+ // never explicitly requested, so a Draft is never inferred to have
904
+ // requested it;
905
+ // - its bounded settled-request history: absent means no request ever
906
+ // reached a terminal disposition on this Task. This is a display
907
+ // projection, not the authority: whether an explicitly cancelled or
908
+ // adopted requestId may be replayed is decided from the durable
909
+ // activation event ledger (never compacted), so eviction from this
910
+ // bounded list never resurrects a decided outcome — per-request state
911
+ // inside the Task that owns it, not a second scheduling or operation
912
+ // ledger;
913
+ // - the planning Turn purpose: historical Turns keep their execution or
914
+ // review purpose untouched.
915
+ //
916
+ // The partial indexes bound Draft planning selection and pending-activation
917
+ // recovery to the few rows that qualify, so no execution phase scans Task
918
+ // history to find them. Creating an index adds no row and rewrites no
919
+ // payload.
920
+ sql: `
921
+ -- explicit activation request, its bounded settled history, and planning Turn
922
+ -- purpose; absent means activation was never requested
923
+ CREATE INDEX IF NOT EXISTS idx_turns_planning_active ON turns(task_id, turn_id)
924
+ WHERE status = 'active' AND json_extract(payload, '$.purpose') = 'planning';
925
+ CREATE INDEX IF NOT EXISTS idx_tasks_activation_pending ON task_records(task_id)
926
+ WHERE json_extract(payload, '$.activationRequest.disposition') = 'pending';
927
+ `
928
+ },
929
+ {
930
+ version: 13,
931
+ name: "session-authority-and-execution-admission",
932
+ introducedIn: "0.15.9",
933
+ sql: "SELECT 1; -- AgentRun contract, notification admission and Session authority",
934
+ migrateData: migrateAgentRunContract
935
+ },
936
+ {
937
+ version: 14,
938
+ name: "acp-session-workspace-configuration",
939
+ introducedIn: "0.15.10",
940
+ // ACP is a new legal adapter/configuration value, including its optional
941
+ // additional workspace roots. The v13 baseline has no ACP bindings; existing
942
+ // Codex/Claude configuration and Session history remain valid unchanged.
943
+ // This widens the persistent contract without rewriting payloads or earlier
944
+ // migration checksums. Workspace delivery is negotiated at initialize, not
945
+ // stored as another configuration authority.
946
+ sql: "SELECT 1; -- ACP bindings may carry additionalDirectories; history stays valid"
947
+ },
948
+ {
949
+ version: 15,
950
+ name: "agent-execution-component",
951
+ introducedIn: "0.15.10",
952
+ // Which product executes is now its own recorded fact, separate from the
953
+ // connection plan that reaches it. The plan keeps its `adapterId` name and
954
+ // its meaning; only the product identity is new.
955
+ //
956
+ // The backfill is a total function of the stored plan, never of a command
957
+ // string. `codex` and `claude` each have exactly one component, so those
958
+ // rows gain their true value. Every `acp` row becomes `unknown-acp-agent`:
959
+ // a v14 Home cannot say which product answered, and an executable named
960
+ // `claude-agent-acp` is not evidence that it was the Claude Agent SDK. The
961
+ // unidentified value is the honest one and stays correctable by hand.
962
+ //
963
+ // Sessions keep their own recorded component, so a Session started before
964
+ // this migration continues against exactly the implementation it began on.
965
+ //
966
+ // Every persisted effective snapshot must be reached, not only the ones on
967
+ // Session sets: `validateEffectiveLaunchSnapshot` demands schemaVersion 4,
968
+ // and the upgrade verifier replays it over Runs, WorkItem ExecutionLanes
969
+ // and ReviewRound ExecutionLanes too. A Home holding any of those would
970
+ // otherwise fail the upgrade and roll back. The list below is the
971
+ // verifier's own reachable set, not a scan for fields that look similar.
972
+ sql: `
973
+ UPDATE configured_agents SET payload = json_set(payload,
974
+ '$.schemaVersion', 3,
975
+ '$.component', CASE json_extract(payload, '$.adapterId')
976
+ WHEN 'codex' THEN 'codex-cli'
977
+ WHEN 'claude' THEN 'claude-code-cli'
978
+ ELSE 'unknown-acp-agent' END)
979
+ WHERE json_extract(payload, '$.component') IS NULL;
980
+
981
+ UPDATE global_roles SET payload = json_set(payload, '$.agentBindings', json((
982
+ SELECT json_group_object(key, json_set(value, '$.component',
983
+ CASE json_extract(value, '$.adapterId')
984
+ WHEN 'codex' THEN 'codex-cli'
985
+ WHEN 'claude' THEN 'claude-code-cli'
986
+ ELSE 'unknown-acp-agent' END))
987
+ FROM json_each(payload, '$.agentBindings')
988
+ ))) WHERE json_type(payload, '$.agentBindings') = 'object';
989
+
990
+ UPDATE task_roles SET payload = json_set(payload, '$.agentBindings', json((
991
+ SELECT json_group_object(key, json_set(value, '$.component',
992
+ CASE json_extract(value, '$.adapterId')
993
+ WHEN 'codex' THEN 'codex-cli'
994
+ WHEN 'claude' THEN 'claude-code-cli'
995
+ ELSE 'unknown-acp-agent' END))
996
+ FROM json_each(payload, '$.agentBindings')
997
+ ))) WHERE json_type(payload, '$.agentBindings') = 'object';
998
+
999
+ UPDATE global_role_session_sets SET payload = json_set(payload, '$.sessions', json((
1000
+ SELECT json_group_object(key, json_set(value, '$.effective.schemaVersion', 4,
1001
+ '$.effective.component', CASE json_extract(value, '$.effective.adapterId')
1002
+ WHEN 'codex' THEN 'codex-cli'
1003
+ WHEN 'claude' THEN 'claude-code-cli'
1004
+ ELSE 'unknown-acp-agent' END))
1005
+ FROM json_each(payload, '$.sessions')
1006
+ ))) WHERE json_type(payload, '$.sessions') = 'object';
1007
+ UPDATE global_role_session_sets SET payload = json_set(payload, '$.history', json((
1008
+ SELECT json_group_object(key, json_set(value, '$.effective.schemaVersion', 4,
1009
+ '$.effective.component', CASE json_extract(value, '$.effective.adapterId')
1010
+ WHEN 'codex' THEN 'codex-cli'
1011
+ WHEN 'claude' THEN 'claude-code-cli'
1012
+ ELSE 'unknown-acp-agent' END))
1013
+ FROM json_each(payload, '$.history')
1014
+ ))) WHERE json_type(payload, '$.history') = 'object';
1015
+
1016
+ UPDATE role_session_sets SET payload = json_set(payload, '$.sessions', json((
1017
+ SELECT json_group_object(key, json_set(value, '$.effective.schemaVersion', 4,
1018
+ '$.effective.component', CASE json_extract(value, '$.effective.adapterId')
1019
+ WHEN 'codex' THEN 'codex-cli'
1020
+ WHEN 'claude' THEN 'claude-code-cli'
1021
+ ELSE 'unknown-acp-agent' END))
1022
+ FROM json_each(payload, '$.sessions')
1023
+ ))) WHERE json_type(payload, '$.sessions') = 'object';
1024
+ UPDATE role_session_sets SET payload = json_set(payload, '$.history', json((
1025
+ SELECT json_group_array(json_set(value, '$.effective.schemaVersion', 4,
1026
+ '$.effective.component', CASE json_extract(value, '$.effective.adapterId')
1027
+ WHEN 'codex' THEN 'codex-cli'
1028
+ WHEN 'claude' THEN 'claude-code-cli'
1029
+ ELSE 'unknown-acp-agent' END))
1030
+ FROM json_each(payload, '$.history')
1031
+ ))) WHERE json_type(payload, '$.history') = 'array';
1032
+
1033
+ -- A Turn records the launch it actually ran on, one snapshot per row.
1034
+ UPDATE turns SET payload = json_set(payload,
1035
+ '$.effective.schemaVersion', 4,
1036
+ '$.effective.component', CASE json_extract(payload, '$.effective.adapterId')
1037
+ WHEN 'codex' THEN 'codex-cli'
1038
+ WHEN 'claude' THEN 'claude-code-cli'
1039
+ ELSE 'unknown-acp-agent' END)
1040
+ WHERE json_type(payload, '$.effective') = 'object';
1041
+
1042
+ -- ExecutionLane launch facts are frozen per Lane, inside an array of Groups
1043
+ -- that each hold an array of Lanes. A Lane that was never dispatched has no
1044
+ -- effective at all, and must keep that absence: json_set would otherwise
1045
+ -- create a partial snapshot the Lane validator rejects as incomplete.
1046
+ UPDATE work_items SET payload = json_set(payload, '$.executionGroups', json((
1047
+ SELECT json_group_array(json_set(grp.value, '$.lanes', json((
1048
+ SELECT json_group_array(CASE
1049
+ WHEN json_type(lane.value, '$.effective') = 'object'
1050
+ THEN json_set(lane.value, '$.effective.schemaVersion', 4,
1051
+ '$.effective.component', CASE json_extract(lane.value, '$.effective.adapterId')
1052
+ WHEN 'codex' THEN 'codex-cli'
1053
+ WHEN 'claude' THEN 'claude-code-cli'
1054
+ ELSE 'unknown-acp-agent' END)
1055
+ ELSE lane.value END)
1056
+ FROM json_each(grp.value, '$.lanes') AS lane
1057
+ ))))
1058
+ FROM json_each(payload, '$.executionGroups') AS grp
1059
+ ))) WHERE json_type(payload, '$.executionGroups') = 'array';
1060
+
1061
+ -- A ReviewRound holds at most one Group, so only the Lane array nests here.
1062
+ UPDATE review_rounds SET payload = json_set(payload, '$.executionGroup.lanes', json((
1063
+ SELECT json_group_array(CASE
1064
+ WHEN json_type(lane.value, '$.effective') = 'object'
1065
+ THEN json_set(lane.value, '$.effective.schemaVersion', 4,
1066
+ '$.effective.component', CASE json_extract(lane.value, '$.effective.adapterId')
1067
+ WHEN 'codex' THEN 'codex-cli'
1068
+ WHEN 'claude' THEN 'claude-code-cli'
1069
+ ELSE 'unknown-acp-agent' END)
1070
+ ELSE lane.value END)
1071
+ FROM json_each(payload, '$.executionGroup.lanes') AS lane
1072
+ ))) WHERE json_type(payload, '$.executionGroup.lanes') = 'array';
1073
+ `
1074
+ },
1075
+ {
1076
+ version: 16,
1077
+ name: "acp-session-run-configuration",
1078
+ introducedIn: "0.15.10",
1079
+ // An ACP Role binding may now carry a model, a reasoning effort and a
1080
+ // permission strategy beyond `default`, because Yui's ACP client implements
1081
+ // `session/set_config_option` and pushes those values to the Session before
1082
+ // it prompts. Previously the adapter rejected all three, so no stored
1083
+ // payload can contain them.
1084
+ //
1085
+ // This widens the contract without rewriting anything, and the absence of a
1086
+ // payload update is the substantive decision rather than an omission. A v10
1087
+ // ACP binding holds `permission.strategy = "default"`, which keeps exactly
1088
+ // the meaning it always had: Yui sends no mode, so the Agent's own default
1089
+ // stands. Rewriting those rows to `bypass` — or to any newly expressible
1090
+ // value — would grant authority the user never chose, on Homes whose owner
1091
+ // did nothing but upgrade. `bypass` is reachable only by asking for it.
1092
+ //
1093
+ // Effective launch snapshots need no change either. They already carry
1094
+ // optional `model` and `effort` on the shared base, and their ACP permission
1095
+ // is the same object the adapter canonicalizes, so a historical snapshot
1096
+ // still validates unchanged at schemaVersion 4 across Sessions, Session
1097
+ // history, Turns, WorkItem ExecutionLanes and ReviewRound ExecutionLanes.
1098
+ // A frozen snapshot therefore keeps describing the launch it actually ran,
1099
+ // which is what makes replaying old history honest.
1100
+ sql: "SELECT 1; -- ACP bindings may carry model/effort and a chosen "
1101
+ + "permission mode; existing `default` bindings keep their meaning"
1102
+ },
1103
+ {
1104
+ version: 17,
1105
+ name: "session-origin-input-requests",
1106
+ introducedIn: "0.15.8",
1107
+ // Widen requester provenance: notifications originate in a current Leader
1108
+ // Session without an AgentRun. Existing schemaVersion-3 requests keep their
1109
+ // exact Run/Session origin, including frozen historical Context values.
1110
+ // No guessed native identity or history rewrite is needed.
1111
+ sql: "SELECT 1; -- InputRequest requester.runId is optional when an exact nativeSessionId is present"
1112
+ },
1113
+ {
1114
+ version: 18,
1115
+ name: "replaceable-session-process-custody",
1116
+ introducedIn: "0.15.8",
1117
+ // Existing Session/Run history remains unchanged. Runtime mailboxes may
1118
+ // now retain explicit replacement intent; OS owner records may identify
1119
+ // the dedicated execution child, independently of a disposable Host.
1120
+ sql: "SELECT 1; -- Replacement intent, independent retained native control evidence/process custody, and optional fixed TaskWake refs"
1121
+ },
1122
+ {
1123
+ version: 19,
1124
+ name: "task-artifacts-local-git",
1125
+ introducedIn: "0.16.0",
1126
+ // Retire the DB-owned immutable Artifact store: file/directory artifacts now
1127
+ // live in a per-Task local Git repository, referenced by a self-certifying
1128
+ // `commit + relativePath`. The whole rewrite is payload work that must READ
1129
+ // the `artifacts` table before it is dropped, so it runs entirely in
1130
+ // `migrateData` (which executes after this `sql`) — the table is dropped
1131
+ // there, last, once its rows have been moved. Requirement A's independent,
1132
+ // deterministic submit-intent backfill (events + id_sequences only) is the
1133
+ // final step inside that same transaction. This `sql` is intentionally a
1134
+ // no-op: dropping the table here would destroy the rows before they move.
1135
+ //
1136
+ // The 18->19 contract also DECLARES three optional `messages.payload` fields
1137
+ // added by Requirement A — `intent` (record|discuss|develop), an optional
1138
+ // client idempotency `submissionKey`, and a frozen `submissionReceipt`
1139
+ // disposition. They add no column and no table and are only ever written
1140
+ // going forward (absent `intent` reads as discuss; absent key is keyless;
1141
+ // absent receipt is a pre-19 message), so NO historical row is rewritten and
1142
+ // no key or receipt is ever fabricated for old data — declaring them here
1143
+ // satisfies "optional still requires a migration declaration".
1144
+ sql: "SELECT 1; -- artifacts move to per-Task local Git; see migrateArtifactsToGit",
1145
+ migrateData: migrateArtifactsToGit
700
1146
  }
701
1147
  ]);
702
1148
  for (let index = 0; index < MIGRATIONS.length; index += 1) {
@@ -713,8 +1159,9 @@ if (MIGRATIONS.at(-1)?.version !== CURRENT_STORAGE_VERSION) {
713
1159
  /** Current hot-path indexes whose absence would invalidate a current Home. */
714
1160
  const REQUIRED_SCHEMA_INDEXES = [
715
1161
  "idx_mailboxes_ready",
716
- "idx_runtime_session_cleanup_required",
717
- "idx_input_requests_open_hot"
1162
+ "idx_input_requests_open_hot",
1163
+ "idx_turns_planning_active",
1164
+ "idx_tasks_activation_pending"
718
1165
  ];
719
1166
  function checksum(sql) {
720
1167
  return createHash("sha256").update(sql).digest("hex");
@@ -929,7 +1376,12 @@ export function migrateSqliteSchema(db, options) {
929
1376
  throw new SqliteSchemaMigrationError(`storage version ${applied.currentVersion} is older than the minimum supported `
930
1377
  + `${MIN_SUPPORTED_STORAGE_VERSION}`, "admission");
931
1378
  }
932
- const pending = MIGRATIONS.filter((migration) => !applied.versions.has(migration.version));
1379
+ // Production callers omit `throughVersion`, so the effective target is head
1380
+ // and behavior is unchanged; a partial target is honored only in apply mode.
1381
+ const effectiveTarget = options.mode === "apply" && options.throughVersion !== undefined
1382
+ ? options.throughVersion
1383
+ : CURRENT_STORAGE_VERSION;
1384
+ const pending = MIGRATIONS.filter((migration) => !applied.versions.has(migration.version) && migration.version <= effectiveTarget);
933
1385
  if (!ledgerWasCreated && pending.length > 0 && options.mode === "validate") {
934
1386
  throw new SqliteSchemaMigrationError(`Storage version ${applied.currentVersion} requires an explicit upgrade to `
935
1387
  + `${CURRENT_STORAGE_VERSION}`, "admission");
@@ -937,12 +1389,17 @@ export function migrateSqliteSchema(db, options) {
937
1389
  const newlyApplied = [];
938
1390
  for (const migration of pending) {
939
1391
  db.exec(migration.sql);
1392
+ migration.migrateData?.(db);
940
1393
  const appliedAt = new Date().toISOString();
941
1394
  db.prepare(`INSERT INTO schema_migrations (version, name, applied_at, checksum)
942
1395
  VALUES (?, ?, ?, ?)`).run(migration.version, migration.name, appliedAt, checksum(migration.sql));
943
1396
  newlyApplied.push(migration.version);
944
1397
  }
945
- validateSchemaObjects(db);
1398
+ // The table/index inventory describes the HEAD shape; only assert it once the
1399
+ // migration has actually advanced to head (a deliberate partial target stops
1400
+ // earlier and is validated when the real upgrade later completes it).
1401
+ if (effectiveTarget >= CURRENT_STORAGE_VERSION)
1402
+ validateSchemaObjects(db);
946
1403
  return newlyApplied;
947
1404
  };
948
1405
  const newlyApplied = options.mode === "apply" && !db.inTransaction
@@ -952,6 +1409,10 @@ export function migrateSqliteSchema(db, options) {
952
1409
  }
953
1410
  /** The names of every table the schema creates (for tests/introspection). */
954
1411
  export const SQLITE_SCHEMA_TABLES = [
1412
+ "plugin_intents",
1413
+ "plugin_validations",
1414
+ "local_resources",
1415
+ "environment_preparations",
955
1416
  "schema_migrations",
956
1417
  "home_meta",
957
1418
  "config",
@@ -967,7 +1428,6 @@ export const SQLITE_SCHEMA_TABLES = [
967
1428
  "coordination_locks",
968
1429
  "integration_queue",
969
1430
  "durable_jobs",
970
- "job_caller_key_hashes",
971
1431
  "outbox",
972
1432
  "mailboxes",
973
1433
  "task_records",