@zq-silk/yui 0.15.11 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/ARCHITECTURE.md +11 -6
  2. package/ARCHITECTURE.zh-CN.md +8 -4
  3. package/README.md +13 -5
  4. package/dist/agentRun/agentRun.js +3 -0
  5. package/dist/artifacts/artifactCommitLock.js +16 -55
  6. package/dist/artifacts/taskArtifactRepository.js +3 -3
  7. package/dist/cli/agentConfigurationPicker.js +1 -1
  8. package/dist/cli/commandCatalog.js +92 -48
  9. package/dist/cli/completion.js +18 -18
  10. package/dist/cli/completionWizard.js +1 -1
  11. package/dist/cli/dynamicCompletion.js +1 -1
  12. package/dist/cli/interactionPolicy.js +11 -3
  13. package/dist/cli/invocationAuthority.js +64 -0
  14. package/dist/cli/managedDiagnostics.js +2 -2
  15. package/dist/cli/parseRepeatable.js +35 -0
  16. package/dist/cli/updatePorts.js +26 -5
  17. package/dist/cli.js +1303 -1235
  18. package/dist/commands/agentCommands.js +4 -4
  19. package/dist/commands/capabilityCommands.js +1 -1
  20. package/dist/commands/configCommands.js +6 -57
  21. package/dist/commands/configOverview.js +2 -2
  22. package/dist/commands/durableJobCommands.js +12 -6
  23. package/dist/commands/executionAuditCommands.js +10 -0
  24. package/dist/commands/globalRoleCommands.js +78 -29
  25. package/dist/commands/grantCommands.js +0 -3
  26. package/dist/commands/jobCommands.js +1 -4
  27. package/dist/commands/operatorCommands.js +3 -3
  28. package/dist/commands/projectCommands.js +45 -25
  29. package/dist/commands/resourcesCommands.js +10 -40
  30. package/dist/commands/roleConfiguration.js +2 -6
  31. package/dist/commands/taskActivationCommands.js +4 -8
  32. package/dist/commands/taskAgentCapabilities.js +9 -0
  33. package/dist/commands/taskCommandSupport.js +155 -0
  34. package/dist/commands/taskCommandTypes.js +1 -0
  35. package/dist/commands/taskCommands.js +355 -732
  36. package/dist/commands/taskCompletionGate.js +1 -1
  37. package/dist/commands/taskExecutionCommands.js +1 -1
  38. package/dist/commands/taskFactCommands.js +325 -0
  39. package/dist/commands/taskInputCommands.js +12 -42
  40. package/dist/commands/taskIntegrationCommands.js +20 -58
  41. package/dist/commands/taskNextActionCommand.js +7 -6
  42. package/dist/commands/taskPublicationAdoptCommand.js +127 -0
  43. package/dist/commands/taskPublicationCommands.js +12 -3
  44. package/dist/commands/taskPublicationVerifyCommand.js +24 -40
  45. package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
  46. package/dist/commands/taskUpstreamCommands.js +19 -8
  47. package/dist/commands/workflowCommands.js +6 -1
  48. package/dist/completion/completionInstaller.js +26 -26
  49. package/dist/completion/completionState.js +26 -26
  50. package/dist/completion/fileCompletionManager.js +6 -11
  51. package/dist/config/configCatalog.js +0 -2
  52. package/dist/config/timeZone.js +14 -0
  53. package/dist/config/yuiConfig.js +0 -29
  54. package/dist/context/runContextPack.js +5 -3
  55. package/dist/context/taskCatalog.js +185 -0
  56. package/dist/context/taskContext.js +152 -151
  57. package/dist/context/wakeRunReferences.js +11 -0
  58. package/dist/controller/agentCapabilities.js +58 -0
  59. package/dist/controller/agentRuntimeObserver.js +6 -11
  60. package/dist/controller/clientRuntime.js +22 -14
  61. package/dist/controller/controller.js +20 -24
  62. package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
  63. package/dist/controller/globalInputDelivery.js +13 -0
  64. package/dist/controller/jobClient.js +4 -4
  65. package/dist/controller/jobControl.js +60 -25
  66. package/dist/controller/jobSupervisor.js +4 -4
  67. package/dist/controller/providerRetryAdmission.js +100 -0
  68. package/dist/controller/providerRetryDelivery.js +218 -0
  69. package/dist/controller/runtime.js +85 -43
  70. package/dist/controller/runtimeEventProcessor.js +0 -5
  71. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  72. package/dist/controller/taskAgentError.js +35 -0
  73. package/dist/coordination/workMailboxQueue.js +2 -24
  74. package/dist/core/controllerClient.js +2 -2
  75. package/dist/core/fileLockOwner.js +74 -0
  76. package/dist/decision/decision.js +17 -0
  77. package/dist/doctor/doctor.js +18 -9
  78. package/dist/event/taskEvent.js +12 -0
  79. package/dist/execution/workItemExecutionProjection.js +2 -2
  80. package/dist/executor/agentCapabilityConfig.js +43 -0
  81. package/dist/executor/agentConfigurationCatalog.js +49 -11
  82. package/dist/executor/agentExecutor.js +5 -8
  83. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  84. package/dist/executor/taskAgentCapabilities.js +64 -0
  85. package/dist/integration/deliveryObligation.js +1 -69
  86. package/dist/integration/gitIntegrationService.js +79 -90
  87. package/dist/integration/integrationAttempt.js +3 -14
  88. package/dist/integration/integrationSourceApplication.js +4 -12
  89. package/dist/integration/manifestTags.js +2 -2
  90. package/dist/job/durableJob.js +4 -25
  91. package/dist/job/jobAssignmentScope.js +22 -0
  92. package/dist/job/jobOperation.js +16 -0
  93. package/dist/job/jobRunner.js +2 -1
  94. package/dist/job/stepDirectory.js +15 -0
  95. package/dist/kernel/builtinCapabilities.js +15 -11
  96. package/dist/kernel/kernelPorts.js +1 -17
  97. package/dist/lifecycle/exactRunTerminalization.js +5 -10
  98. package/dist/message/globalProviderRetry.js +15 -0
  99. package/dist/message/inputControlResolution.js +3 -4
  100. package/dist/message/message.js +17 -20
  101. package/dist/message/messageContinuation.js +1 -1
  102. package/dist/milestone/milestone.js +11 -0
  103. package/dist/observability/executionAudit.js +19 -0
  104. package/dist/observability/orchestrationMetrics.js +24 -1
  105. package/dist/observability/runtimeIdentity.js +1 -17
  106. package/dist/output/agentConfigurationPresentation.js +2 -0
  107. package/dist/output/timePresentation.js +1 -14
  108. package/dist/plugins/pluginService.js +12 -4
  109. package/dist/release/releaseWorkflowPorts.js +45 -96
  110. package/dist/release/runtimeRelease.js +9 -15
  111. package/dist/repository/gitWorkspace.js +371 -105
  112. package/dist/repository/projectMaintenanceLock.js +98 -82
  113. package/dist/repository/taskBaseFreshness.js +1 -1
  114. package/dist/repository/taskWorkspaceCoordinator.js +82 -144
  115. package/dist/repository/taskWorkspacePreparer.js +113 -47
  116. package/dist/repository/workspaceCleanupInspection.js +187 -0
  117. package/dist/resources/autoResourceGc.js +4 -77
  118. package/dist/resources/liveReferences.js +7 -54
  119. package/dist/resources/resourceDiscovery.js +5 -118
  120. package/dist/resources/resourceGc.js +233 -181
  121. package/dist/resources/resourceRegistrar.js +5 -4
  122. package/dist/resources/resourceRegistry.js +3 -37
  123. package/dist/resources/resourceRegistryStore.js +13 -36
  124. package/dist/resources/sqliteResourceRegistry.js +43 -26
  125. package/dist/review/deltaRecheck.js +1 -1
  126. package/dist/review/reviewAcceptance.js +1 -1
  127. package/dist/review/reviewDecision.js +1 -1
  128. package/dist/review/reviewRound.js +8 -7
  129. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  130. package/dist/runtime/acpProtocol.js +4 -51
  131. package/dist/runtime/acpSession.js +10 -75
  132. package/dist/runtime/acpSessionConfiguration.js +1 -8
  133. package/dist/runtime/agentEndpoint.js +3 -2
  134. package/dist/runtime/agentError.js +5 -3
  135. package/dist/runtime/agentFailureContext.js +43 -0
  136. package/dist/runtime/agentHost.js +42 -25
  137. package/dist/runtime/builtinAgentErrorMappers.js +91 -0
  138. package/dist/runtime/codexAppServerRuntime.js +34 -3
  139. package/dist/runtime/codexInteractiveHost.js +2 -2
  140. package/dist/runtime/index.js +0 -1
  141. package/dist/runtime/managedCaller.js +21 -1
  142. package/dist/runtime/providerControl.js +5 -1
  143. package/dist/runtime/providerErrors.js +38 -0
  144. package/dist/runtime/providerRetry.js +198 -0
  145. package/dist/runtime/providerRuntimeIdentity.js +28 -2
  146. package/dist/runtime/runtimeObservation.js +0 -5
  147. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  148. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  149. package/dist/runtime/sessionTokenMetrics.js +15 -5
  150. package/dist/runtime/structuredProviderHost.js +10 -43
  151. package/dist/runtime/taskUsageMetrics.js +275 -0
  152. package/dist/runtime/tmuxAdapters.js +6 -8
  153. package/dist/scheduler/activeRoleRunDelivery.js +19 -7
  154. package/dist/scheduler/leaderWakeupProcessor.js +24 -6
  155. package/dist/scheduler/operatorEvent.js +12 -20
  156. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  157. package/dist/scheduler/ports.js +5 -8
  158. package/dist/scheduler/roleRunLiveness.js +4 -4
  159. package/dist/scheduler/roleRunStall.js +16 -23
  160. package/dist/scheduler/taskExecutionProjection.js +49 -36
  161. package/dist/scheduler/taskObservabilityProjection.js +6 -45
  162. package/dist/scheduler/taskWake.js +5 -10
  163. package/dist/scheduler/wakeReason.js +2 -0
  164. package/dist/scheduler/wakeupQueue.js +2 -4
  165. package/dist/setup/setupCommand.js +1 -1
  166. package/dist/storage/contextRecords.js +106 -0
  167. package/dist/storage/currentTaskStore.js +1 -1
  168. package/dist/storage/homeLayout.js +13 -17
  169. package/dist/storage/migrations/agentFailureContext.js +22 -0
  170. package/dist/storage/migrations/currentInputContract.js +86 -0
  171. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  172. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  173. package/dist/storage/migrations/integrationContinuation.js +5 -4
  174. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  175. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  176. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  177. package/dist/storage/migrations/verificationPolicy.js +74 -0
  178. package/dist/storage/migrations/workItemHistory.js +46 -0
  179. package/dist/storage/persistenceWorker.js +12 -4
  180. package/dist/storage/recordValidation.js +87 -0
  181. package/dist/storage/sqliteSchema.js +173 -1
  182. package/dist/storage/sqliteStore.js +172 -161
  183. package/dist/storage/storageSchema.js +2 -14
  184. package/dist/storage/storageVersions.js +1 -1
  185. package/dist/storage/storeRpc.js +16 -7
  186. package/dist/storage/taskCatalog.js +123 -0
  187. package/dist/storage/taskStore.js +5 -7
  188. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  189. package/dist/task/archiveDiagnostics.js +1 -0
  190. package/dist/task/archivePreflight.js +124 -0
  191. package/dist/task/completionReadiness.js +5 -24
  192. package/dist/task/draftPlan.js +0 -53
  193. package/dist/task/nextAction.js +7 -13
  194. package/dist/task/publicationAdoption.js +56 -0
  195. package/dist/task/publicationReference.js +10 -0
  196. package/dist/task/remoteDelivery.js +31 -16
  197. package/dist/task/remoteDeliveryService.js +89 -0
  198. package/dist/task/taskActivation.js +2 -25
  199. package/dist/task/taskActivationService.js +0 -2
  200. package/dist/task/taskRecordReference.js +0 -1
  201. package/dist/task/taskSubmission.js +3 -14
  202. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  203. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  204. package/dist/telemetry/telemetryWiring.js +4 -3
  205. package/dist/tmux/tmuxManager.js +29 -20
  206. package/dist/verification/gateArtifact.js +15 -24
  207. package/dist/verification/gateArtifactStore.js +14 -7
  208. package/dist/verification/verificationGateService.js +29 -88
  209. package/dist/verification/verificationPlan.js +27 -72
  210. package/dist/web/assets/client/app.js +92 -17
  211. package/dist/web/assets/client/components.js +55 -13
  212. package/dist/web/assets/client/i18n.js +72 -4
  213. package/dist/web/assets/client/taskSurface.js +6 -5
  214. package/dist/web/assets/client/view.js +35 -7
  215. package/dist/web/assets/shell.js +6 -0
  216. package/dist/web/assets/styles/layout.js +7 -0
  217. package/dist/web/webServer.js +12 -3
  218. package/dist/web/webSnapshot.js +14 -85
  219. package/dist/web/webTaskSurface.js +17 -46
  220. package/dist/workItem/workItem.js +1 -12
  221. package/dist/workspace/cleanupInspection.js +63 -0
  222. package/dist/workspace/workItemChangeSetManager.js +110 -50
  223. package/docs/agent-result-consumption.md +4 -0
  224. package/docs/agent-result-consumption.zh-CN.md +3 -0
  225. package/docs/agent-runtime-drivers.md +7 -0
  226. package/docs/agent-runtime-drivers.zh-CN.md +5 -0
  227. package/docs/architecture/README.md +2 -0
  228. package/docs/architecture/README.zh-CN.md +3 -1
  229. package/docs/architecture/capabilities-and-resources.md +42 -5
  230. package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
  231. package/docs/managed-turn-and-session-runtime.md +121 -1
  232. package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
  233. package/docs/observability/README.md +62 -0
  234. package/docs/observability/README.zh-CN.md +47 -0
  235. package/docs/plugin-sdk.md +4 -2
  236. package/docs/project-refresh.md +77 -0
  237. package/docs/project-refresh.zh-CN.md +59 -0
  238. package/docs/provider-retry.md +70 -0
  239. package/docs/provider-runtime.md +3 -1
  240. package/docs/provider-runtime.zh-CN.md +4 -2
  241. package/docs/release-workflow.md +190 -7
  242. package/docs/release-workflow.zh-CN.md +139 -5
  243. package/docs/roles-and-configuration.md +29 -0
  244. package/docs/roles-and-configuration.zh-CN.md +21 -0
  245. package/docs/sqlite-control-plane-design.md +16 -3
  246. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  247. package/docs/task-dag-semantics.md +3 -3
  248. package/docs/task-dag-semantics.zh-CN.md +2 -2
  249. package/docs/task-delivery.md +223 -17
  250. package/docs/task-delivery.zh-CN.md +171 -14
  251. package/docs/task-discovery.md +101 -0
  252. package/docs/task-discovery.zh-CN.md +85 -0
  253. package/docs/testing/verification-levels.md +161 -9
  254. package/docs/testing/verification-levels.zh-CN.md +116 -9
  255. package/i18n/README.zh-CN.md +13 -7
  256. package/package.json +1 -1
  257. package/skills/yui-leader/SKILL.md +5 -0
  258. package/skills/yui-leader/references/execution.md +3 -2
  259. package/skills/yui-leader/references/integration.md +9 -5
  260. package/skills/yui-leader/references/planning.md +9 -3
  261. package/skills/yui-operator/SKILL.md +24 -8
  262. package/skills/yui-reviewer/SKILL.md +4 -0
  263. package/skills/yui-runtime/SKILL.md +17 -2
  264. package/skills/yui-runtime/references/publication.md +26 -4
  265. package/skills/yui-runtime/references/recovery.md +71 -0
  266. package/dist/agent/agentRegistry.js +0 -10
  267. package/dist/agentRun/runIdentity.js +0 -22
  268. package/dist/commands/deliveryGuardPreflight.js +0 -30
  269. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  270. package/dist/commands/taskOverviewCommand.js +0 -376
  271. package/dist/completion/completionWizard.js +0 -125
  272. package/dist/context/dispatchContext.js +0 -110
  273. package/dist/context/wakeNotification.js +0 -144
  274. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  275. package/dist/integration/integrationQueueEntry.js +0 -191
  276. package/dist/integration/integrationQueueService.js +0 -810
  277. package/dist/release/fakeReleasePorts.js +0 -55
  278. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  279. package/dist/task/deliveryGuard.js +0 -226
  280. /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
@@ -178,9 +178,8 @@ export function currentRoleRunProgressAt(store, taskId, roleName, run) {
178
178
  return { progressAt: run.createdAt };
179
179
  }
180
180
  /**
181
- * Computes the current semantic progress fence for an exact AgentRun. The optional
182
- * related-record readers add Work/Review/Integration evidence to the AgentRun and
183
- * event facts used by on-demand projections.
181
+ * Computes the current semantic progress fence for an exact AgentRun from
182
+ * required Work/Review/Integration readers and the AgentRun's event facts.
184
183
  */
185
184
  export function latestRunDurableProgressAt(store, taskId, roleName, runId, precomputed) {
186
185
  const run = store.getRun(taskId, runId);
@@ -204,20 +203,16 @@ export function latestRunDurableProgressAt(store, taskId, roleName, runId, preco
204
203
  if (run.workItemId === undefined) {
205
204
  return { progressAt: baseline };
206
205
  }
207
- const workItem = store.getWorkItem?.(taskId, run.workItemId) ?? null;
208
- const reviewRounds = store.listReviewRounds?.(taskId)
209
- .filter(({ workItemId }) => workItemId === run.workItemId)
210
- ?? [];
211
- const changeSets = store.listChangeSets?.(taskId)
212
- .filter(({ workItemId }) => workItemId === run.workItemId)
213
- ?? [];
214
- const integrations = store.listIntegrationAttempts?.(taskId)
215
- .filter(({ source }) => (source.kind === "work-item" && source.workItemId === run.workItemId))
216
- ?? [];
217
- const inputProgress = store.listInputRequests?.(taskId)
206
+ const workItem = store.getWorkItem(taskId, run.workItemId);
207
+ const reviewRounds = store.listReviewRounds(taskId)
208
+ .filter(({ workItemId }) => workItemId === run.workItemId);
209
+ const changeSets = store.listChangeSets(taskId)
210
+ .filter(({ workItemId }) => workItemId === run.workItemId);
211
+ const integrations = store.listIntegrationAttempts(taskId)
212
+ .filter(({ source }) => (source.kind === "work-item" && source.workItemId === run.workItemId));
213
+ const inputProgress = store.listInputRequests(taskId)
218
214
  .filter((request) => (request.requester.runId === run.id
219
- || request.blockedRefs.some((ref) => ref.type === "run" && ref.id === run.id)))
220
- ?? [];
215
+ || request.blockedRefs.some((ref) => ref.type === "run" && ref.id === run.id)));
221
216
  const related = [
222
217
  workItem?.updatedAt,
223
218
  ...reviewRounds.map(({ endedAt, createdAt }) => endedAt ?? createdAt),
@@ -433,8 +428,6 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
433
428
  // existing mailbox work without manufacturing another episode.
434
429
  if (selection !== undefined && !selection.full)
435
430
  return [];
436
- if (store.recordRoleRunStall === undefined)
437
- return [];
438
431
  // A Draft planning Turn can stall exactly like any other admitted Turn, so it
439
432
  // is selected here too; the episode stays diagnostic-only either way.
440
433
  const candidates = selectedActiveSchedulerTasks(store, selection, {
@@ -455,7 +448,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
455
448
  const existing = eventsByTask.get(taskId);
456
449
  if (existing !== undefined)
457
450
  return existing;
458
- const events = store.listEvents?.(taskId) ?? [];
451
+ const events = store.listEvents(taskId);
459
452
  eventsByTask.set(taskId, events);
460
453
  return events;
461
454
  };
@@ -469,7 +462,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
469
462
  const diagnosticStartedAt = now.toISOString();
470
463
  const finishDiagnostics = (outcome) => {
471
464
  for (const { task, role, run } of stallCandidates) {
472
- store.recordRoleRunDiagnostic?.({
465
+ store.recordRoleRunDiagnostic({
473
466
  taskId: task.id,
474
467
  roleName: role.name,
475
468
  runId: run.id,
@@ -676,7 +669,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
676
669
  // A new semantic progress point closes the previous episode first. It
677
670
  // may itself already be older than the window, in which case the same
678
671
  // pass records the next AgentRun+progressAt episode below.
679
- store.recordRoleRunProgress?.({
672
+ store.recordRoleRunProgress({
680
673
  taskId: candidate.task.id,
681
674
  roleName: candidate.role.name,
682
675
  runId: candidate.run.id,
@@ -745,7 +738,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
745
738
  const previous = store.getRunProgressFacts(candidate.task.id, candidate.run.id)?.latestStall;
746
739
  if (previous !== undefined
747
740
  && Date.parse(progressAt) > Date.parse(previous.progressAt)) {
748
- store.recordRoleRunProgress?.({
741
+ store.recordRoleRunProgress({
749
742
  taskId: candidate.task.id,
750
743
  roleName: candidate.role.name,
751
744
  runId: candidate.run.id,
@@ -827,7 +820,7 @@ function latestLeaderActionProgressAt(store, taskId, runId, startedAt, batch, no
827
820
  return "";
828
821
  let latest = startedAt;
829
822
  let latestMs = startedMs;
830
- for (const event of store.listEvents?.(taskId) ?? []) {
823
+ for (const event of store.listEvents(taskId)) {
831
824
  if (!LEADER_ACTION_PROGRESS_TYPES.has(event.type))
832
825
  continue;
833
826
  if (event.payload.taskId !== undefined && event.payload.taskId !== taskId)
@@ -1,5 +1,6 @@
1
1
  import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
2
2
  import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
3
+ import { providerRetryProjection } from "../runtime/providerRetry.js";
3
4
  import { taskOwnsManagedWorkspace } from "../task/task.js";
4
5
  import { currentWorkItemExecutionGroup } from "../workItem/workItem.js";
5
6
  import { mailboxBatches } from "../coordination/workMailbox.js";
@@ -13,21 +14,24 @@ import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runt
13
14
  * aggregate. This function only reads; it never starts a Controller, queues a
14
15
  * wake, writes a Message, or mutates any record.
15
16
  */
16
- export function buildTaskExecutionProjection(store, taskId, taskOverride, now = new Date()) {
17
- const task = store.getTask?.(taskId) ?? taskOverride ?? null;
17
+ export function buildTaskExecutionProjection(store, taskId, now = new Date()) {
18
+ const task = store.getTask(taskId);
18
19
  if (task === null)
19
20
  return null;
20
- const roles = store.listRoles?.(taskId) ?? [];
21
- const events = store.listEvents?.(taskId) ?? [];
22
- const runs = operationalTaskRecords(store.listRuns?.(taskId) ?? [], events, "run");
23
- const leaderMailbox = store.getWorkMailbox?.({
21
+ const roles = store.listRoles(taskId);
22
+ const events = store.listEvents(taskId);
23
+ const usageRuns = store.listRuns(taskId);
24
+ const workItems = store.listWorkItems(taskId);
25
+ const reviewRounds = store.listReviewRounds(taskId);
26
+ const runs = operationalTaskRecords(usageRuns, events, "run");
27
+ const leaderMailbox = store.getWorkMailbox({
24
28
  kind: "role",
25
29
  taskId,
26
30
  roleName: "leader"
27
- }) ?? null;
31
+ });
28
32
  const roleSessions = roles.flatMap((role) => {
29
- const session = store.getRoleSession?.(taskId, role.name);
30
- return session === null || session === undefined
33
+ const session = store.getRoleSession(taskId, role.name);
34
+ return session === null
31
35
  ? []
32
36
  : [{ roleName: role.name, ...session }];
33
37
  });
@@ -35,35 +39,30 @@ export function buildTaskExecutionProjection(store, taskId, taskOverride, now =
35
39
  task,
36
40
  roles,
37
41
  runs,
42
+ usageRuns,
43
+ providerRetries: roles.flatMap(role => {
44
+ const retry = providerRetryProjection(store.getTaskRoleSessionSet(taskId, role.name)?.providerBinding);
45
+ return retry === null ? [] : [retry];
46
+ }),
38
47
  runDelivery: Object.fromEntries(runs.map((run) => {
39
- const observed = store.getTaskRoleSessionSet?.(taskId, run.roleName)?.providerBinding?.run;
48
+ const observed = store.getTaskRoleSessionSet(taskId, run.roleName)?.providerBinding?.run;
40
49
  return [run.id, observed?.runId === run.id ? observed.status : "unobserved"];
41
50
  })),
42
- executionGroups: store.listWorkItems === undefined && store.listReviewRounds === undefined
43
- ? []
44
- : collectExecutionGroups(store.listWorkItems?.(taskId) ?? [], store.listReviewRounds?.(taskId) ?? [], runs),
45
- workItems: store.listWorkItems?.(taskId) ?? [],
46
- ...(store.listContextSnapshots === undefined
47
- ? {}
48
- : { contextSnapshots: store.listContextSnapshots(taskId) }),
49
- inputRequests: store.listInputRequests?.(taskId) ?? [],
50
- ...(store.listReviewRounds === undefined
51
- ? {}
52
- : { reviewRounds: store.listReviewRounds(taskId) }),
53
- ...(store.listChangeSets === undefined
54
- ? {}
55
- : { changeSets: store.listChangeSets(taskId) }),
56
- ...(store.listIntegrationAttempts === undefined
57
- ? {}
58
- : { integrations: store.listIntegrationAttempts(taskId) }),
59
- ...(store.listEvents === undefined ? {} : { events }),
60
- ...(store.getTaskBrief === undefined ? {} : { brief: store.getTaskBrief(taskId) }),
61
- pendingWakeup: store.getPendingWakeup?.(taskId) ?? null,
51
+ executionGroups: collectExecutionGroups(workItems, reviewRounds, runs),
52
+ workItems,
53
+ contextSnapshots: store.listContextSnapshots(taskId),
54
+ inputRequests: store.listInputRequests(taskId),
55
+ reviewRounds,
56
+ changeSets: store.listChangeSets(taskId),
57
+ integrations: store.listIntegrationAttempts(taskId),
58
+ events,
59
+ brief: store.getTaskBrief(taskId),
60
+ pendingWakeup: store.getPendingWakeup(taskId),
62
61
  leaderMailbox,
63
- leaderFailure: store.getLeaderFailure?.(taskId) ?? null,
62
+ leaderFailure: store.getLeaderFailure(taskId),
64
63
  roleSessions,
65
64
  now,
66
- runtimeHealthPolicy: resolveRuntimeHealth(store.getConfig?.().runtimeHealth)
65
+ runtimeHealthPolicy: resolveRuntimeHealth(store.getConfig().runtimeHealth)
67
66
  });
68
67
  }
69
68
  /**
@@ -79,6 +78,7 @@ export function projectTaskExecutionFromFacts(facts) {
79
78
  return projectTaskExecution({
80
79
  ...facts,
81
80
  runs,
81
+ usageRuns: facts.usageRuns ?? facts.runs,
82
82
  executionGroups
83
83
  });
84
84
  }
@@ -107,9 +107,10 @@ export function projectTaskExecution(facts) {
107
107
  });
108
108
  });
109
109
  const observability = buildTaskObservabilityProjection({
110
+ task,
110
111
  workItems,
111
112
  executionGroups: observabilityGroups,
112
- runs,
113
+ runs: facts.usageRuns ?? runs,
113
114
  events,
114
115
  contextSnapshots: facts.contextSnapshots,
115
116
  sessionTokens,
@@ -206,8 +207,12 @@ export function projectTaskExecution(facts) {
206
207
  const hasPendingLeaderWork = pendingWakeup !== null
207
208
  || leaderMailbox?.pending !== null;
208
209
  const recoveryPending = isRecoveryPending(pendingWakeup, leaderMailbox, leaderFailure);
209
- const failedWork = workItems.some((item) => item.status === "open"
210
- && runs.filter((run) => run.workItemId === item.id).at(-1)?.status === "failed");
210
+ const providerRecoveries = (facts.providerRetries ?? []).filter(retry => retry.status === "waiting" || retry.status === "in-flight");
211
+ const recoveringRunIds = new Set(providerRecoveries.flatMap(retry => [retry.previousRunId, retry.successorRunId].filter((id) => id !== undefined)));
212
+ const failedWork = workItems.some((item) => {
213
+ const last = runs.filter((run) => run.workItemId === item.id).at(-1);
214
+ return item.status === "open" && last?.status === "failed" && !recoveringRunIds.has(last.id);
215
+ });
211
216
  const candidateReady = workItems.some((item) => (item.status === "open" && item.currentCandidateId !== undefined));
212
217
  const blockedIntegration = integrations.some((attempt) => attempt.status === "blocked");
213
218
  const conflictedIntegration = integrations.find((attempt) => attempt.status === "conflicted");
@@ -258,7 +263,7 @@ export function projectTaskExecution(facts) {
258
263
  pendingWakeup
259
264
  });
260
265
  }
261
- if (laneRecovery !== undefined) {
266
+ if (laneRecovery !== undefined && (laneRecovery.runId === undefined || !recoveringRunIds.has(laneRecovery.runId))) {
262
267
  return render({
263
268
  task,
264
269
  status: laneRecovery.runtimeHealth === "confirmed-dead"
@@ -306,6 +311,14 @@ export function projectTaskExecution(facts) {
306
311
  pendingWakeup
307
312
  });
308
313
  }
314
+ if (providerRecoveries.length > 0 && healthyActiveRuns.length === 0) {
315
+ return render({
316
+ task, status: "recovering", owner: "none", action: "none",
317
+ summary: `${providerRecoveries.length} Provider recovery chain(s) are cooling down or awaiting their exact result; existing work is preserved. Do not dispatch a duplicate.`,
318
+ reason: "provider-recovery", monitoring, failClosed: false,
319
+ activeRuns: activeRunViews, attention, blockers, pendingWakeup
320
+ });
321
+ }
309
322
  if (recoveryPending) {
310
323
  return render({
311
324
  task,
@@ -1,3 +1,4 @@
1
+ import { projectTaskUsageMetrics } from "../runtime/taskUsageMetrics.js";
1
2
  /**
2
3
  * Build the read-only DAG, execution, cost, and context view consumed by CLI
3
4
  * and Web. It deliberately derives every value from existing Task records and
@@ -8,7 +9,7 @@ export function buildTaskObservabilityProjection(input) {
8
9
  const dag = projectDag(input.workItems);
9
10
  const workItems = input.workItems.map((item) => {
10
11
  const groups = item.executionGroups;
11
- const itemCost = projectWorkItemCost(groups, input.runs, now);
12
+ const itemCost = projectCost({ ...input, now }, groups, item.id);
12
13
  const itemContext = projectContext(groups, input.runs, input.contextSnapshots);
13
14
  const producerObservability = projectWorkItemProducerObservability(item, input.runs);
14
15
  return Object.freeze({
@@ -21,7 +22,7 @@ export function buildTaskObservabilityProjection(input) {
21
22
  resultCount: producerObservability?.resultCount ?? null
22
23
  });
23
24
  });
24
- const cost = projectCost(input.executionGroups, input.runs, input.events, now);
25
+ const cost = projectCost({ ...input, now }, input.executionGroups);
25
26
  const context = projectContext(input.executionGroups, input.runs, input.contextSnapshots);
26
27
  return Object.freeze({
27
28
  dag,
@@ -59,7 +60,6 @@ function projectDag(workItems) {
59
60
  const edges = [];
60
61
  for (const item of workItems) {
61
62
  for (const dependency of item.dependsOn) {
62
- const target = byId.get(dependency);
63
63
  const status = dependencyEdgeStatus(dependency, byId);
64
64
  edges.push({ from: dependency, to: item.id, status });
65
65
  dependents.get(dependency)?.push(item.id);
@@ -140,17 +140,13 @@ function dependencyEdgeStatus(id, byId) {
140
140
  return "active";
141
141
  return "active";
142
142
  }
143
- function projectCost(groups, runs, _events, now) {
143
+ function projectCost(input, groups, workItemId) {
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 = runs.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
146
+ const attempts = input.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
- tokens: 0,
150
- toolCalls: 0,
151
- wallClockSeconds: uniqueGroups.reduce((total, group) => total + groupDurationSeconds(group, now), 0),
152
- tokensObservable: false,
153
- toolCallsObservable: false,
149
+ ...projectTaskUsageMetrics({ ...input, workItemId }),
154
150
  laneCount,
155
151
  groupCount: uniqueGroups.length,
156
152
  retryCount: Math.max(0, attempts.length - laneCount),
@@ -158,32 +154,6 @@ function projectCost(groups, runs, _events, now) {
158
154
  marginalValueStatus: "unavailable"
159
155
  });
160
156
  }
161
- function projectWorkItemCost(groups, runs, now) {
162
- const groupIds = new Set(groups.map(({ id }) => id));
163
- const attempts = runs.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
164
- const wallClockSeconds = groups.reduce((total, group) => {
165
- const started = Date.parse(group.createdAt);
166
- const ended = group.lanes.some(({ disposition }) => disposition === "open")
167
- ? now.getTime()
168
- : Date.parse(group.updatedAt);
169
- return total + (Number.isFinite(started) && Number.isFinite(ended)
170
- ? Math.max(0, Math.floor((ended - started) / 1_000))
171
- : 0);
172
- }, 0);
173
- const laneCount = groups.reduce((total, group) => total + group.lanes.length, 0);
174
- return Object.freeze({
175
- tokens: 0,
176
- toolCalls: 0,
177
- wallClockSeconds,
178
- tokensObservable: false,
179
- toolCallsObservable: false,
180
- laneCount,
181
- groupCount: groups.length,
182
- retryCount: Math.max(0, attempts.length - laneCount),
183
- marginalValuePercent: null,
184
- marginalValueStatus: "unavailable"
185
- });
186
- }
187
157
  function projectContext(groups, runs, snapshots) {
188
158
  const refs = new Map();
189
159
  for (const group of groups) {
@@ -223,12 +193,3 @@ function projectContext(groups, runs, snapshots) {
223
193
  compressionStatus: "unavailable"
224
194
  });
225
195
  }
226
- function groupDurationSeconds(group, now) {
227
- const started = Date.parse(group.createdAt);
228
- const ended = group.lanes.some(({ disposition }) => disposition === "open")
229
- ? now.getTime()
230
- : Date.parse(group.updatedAt);
231
- return Number.isFinite(started) && Number.isFinite(ended)
232
- ? Math.max(0, Math.floor((ended - started) / 1_000))
233
- : 0;
234
- }
@@ -1,8 +1,10 @@
1
1
  import { requireIdentity, requirePositiveInteger, requireText, requireTimestamp } from "../domain/validation.js";
2
2
  import { TASK_RECORD_ID_PREFIXES } from "../task/taskRecordReference.js";
3
3
  import { copyRef } from "../coordination/workMailbox.js";
4
- export const CURRENT_TASK_WAKE_SCHEMA_VERSION = 1;
4
+ export const CURRENT_TASK_WAKE_SCHEMA_VERSION = 2;
5
5
  export function createTaskWake(input) {
6
+ if (Object.hasOwn(input, "runId"))
7
+ throw new Error("A notification wake cannot dispatch an AgentRun.");
6
8
  const wake = {
7
9
  schemaVersion: CURRENT_TASK_WAKE_SCHEMA_VERSION,
8
10
  id: requireIdentity(input.id, "Task wake id"),
@@ -13,13 +15,14 @@ export function createTaskWake(input) {
13
15
  fromCursor: requireTimestamp(input.fromCursor, "Wake fromCursor"),
14
16
  toCursor: requireTimestamp(input.toCursor, "Wake toCursor"),
15
17
  status: "dispatched",
16
- ...(input.runId === undefined ? {} : { runId: requireIdentity(input.runId, "Wake AgentRun id") }),
17
18
  createdAt: input.now.toISOString()
18
19
  };
19
20
  validateTaskWake(wake);
20
21
  return wake;
21
22
  }
22
23
  export function validateTaskWake(wake) {
24
+ if (Object.hasOwn(wake, "runId"))
25
+ throw new Error("Run-backed wakes are not current notification records.");
23
26
  if (wake.refs !== undefined) {
24
27
  if (!Array.isArray(wake.refs))
25
28
  throw new Error("Wake references must be an array.");
@@ -61,14 +64,6 @@ export function latestTaskWake(wakes) {
61
64
  }
62
65
  return latest;
63
66
  }
64
- /**
65
- * The delta lower bound for a Task's first wake: the last Leader AgentRun's
66
- * creation time, preserving the pre-ledger watermark semantics. When no
67
- * Leader AgentRun exists, the Task's own creation time bounds the window.
68
- */
69
- export function fallbackWakeCursor(input) {
70
- return input.leaderRunCreatedAt ?? input.taskCreatedAt;
71
- }
72
67
  function wakeSequence(id) {
73
68
  const prefix = `${TASK_RECORD_ID_PREFIXES.taskWake}-`;
74
69
  if (!id.startsWith(prefix)) {
@@ -25,6 +25,7 @@ export const WAKE_REASON_KINDS = Object.freeze([
25
25
  "role-turn-failed",
26
26
  "job-finished",
27
27
  "published-tree-authorized",
28
+ "notification-retry",
28
29
  "force-wake"
29
30
  ]);
30
31
  const IMMEDIATE_KINDS = new Set([
@@ -34,6 +35,7 @@ const IMMEDIATE_KINDS = new Set([
34
35
  "execution-started",
35
36
  "session-replaced",
36
37
  "force-wake",
38
+ "notification-retry",
37
39
  "activation-failed",
38
40
  "task-reopened",
39
41
  "leader-turn-failed",
@@ -1,10 +1,8 @@
1
1
  import { mergePendingWakeup } from "./pendingWakeup.js";
2
+ /** The caller's TaskStore transaction owns this read/merge/write operation. */
2
3
  export function queueLeaderWakeup(store, taskId, reason, now) {
3
- if (store.getTask?.(taskId)?.status === "archived")
4
+ if (store.getTask(taskId)?.status === "archived")
4
5
  return null;
5
- if (store.enqueueLeaderWakeup !== undefined) {
6
- return store.enqueueLeaderWakeup(taskId, reason, now);
7
- }
8
6
  const pending = mergePendingWakeup(taskId, reason, now, store.getPendingWakeup(taskId));
9
7
  store.savePendingWakeup(pending);
10
8
  return pending;
@@ -5,10 +5,10 @@ import { configuredAgentToDefinition, createConfiguredAgent } from "../agent/age
5
5
  import { assertRoleRuntimeMutationAllowed } from "../commands/roleRuntimeGuard.js";
6
6
  import { resolveTmuxBin } from "../config/yuiConfig.js";
7
7
  import { usageError } from "../errors/cliError.js";
8
- import { managedGlobalRoleWorkspace } from "../storage/homeLayout.js";
9
8
  import { createGlobalRole, createRoleAgentBinding, updateGlobalRole } from "../role/role.js";
10
9
  import { SYSTEM_LEADER_ROLE, SYSTEM_OPERATOR_ROLE } from "../role/systemRoles.js";
11
10
  import { initializeCurrentTaskStore } from "../storage/currentTaskStore.js";
11
+ import { managedGlobalRoleWorkspace } from "../storage/homeLayout.js";
12
12
  import { ensureYuiHome, resolveYuiHome } from "../storage/taskStore.js";
13
13
  const BUILTIN_AGENTS = Object.freeze([
14
14
  Object.freeze({
@@ -0,0 +1,106 @@
1
+ import { validateStoredRecord } from "./recordValidation.js";
2
+ import { validateWorkItemCandidate } from "../workItem/workItem.js";
3
+ /** Read-only projections of existing tables, not a new Context index or Store. */
4
+ const FAMILIES = {
5
+ "work-item": ["work_items", "work_item_id"],
6
+ "candidate": ["work_items", "work_item_id"],
7
+ "environment-preparation": ["environment_preparations", "id"],
8
+ "managed-workspace": ["managed_workspaces", "owner_id"],
9
+ "change-set": ["change_sets", "change_set_id"],
10
+ "task-message": ["messages", "message_id"],
11
+ "run": ["turns", "turn_id"],
12
+ "review-round": ["review_rounds", "review_round_id"],
13
+ "input-request": ["input_requests", "input_id"],
14
+ "task-decision": ["decisions", "decision_id"],
15
+ "task-milestone": ["milestones", "milestone_id"],
16
+ "job": ["durable_jobs", "job_id"],
17
+ "publication": ["publication_references", "publication_id"],
18
+ "task-event": ["events", "event_id"]
19
+ };
20
+ /** Only identities cross this query: shared user intent and the exact live
21
+ * Assignment's steers augment frozen read refs without loading other messages.
22
+ */
23
+ export function contextInputReferences(db, taskId, scope) {
24
+ const shared = `json_extract(m.payload, '$.kind') IN ('user','operator')
25
+ AND json_extract(m.payload, '$.recipient') IS NULL
26
+ AND json_extract(m.payload, '$.workItemId') IS NULL
27
+ AND json_extract(m.payload, '$.runId') IS NULL`;
28
+ const steer = `(COALESCE(json_extract(m.payload, '$.inputControl.action'), '') = 'steer'
29
+ OR COALESCE(json_extract(m.payload, '$.interruptThen.reusedInput.action'), '') = 'steer')`;
30
+ const matches = `m.task_id = @taskId AND (
31
+ ((${shared}) AND NOT ${steer}) OR (${steer}
32
+ AND json_extract(m.payload, '$.recipient.roleName') = @roleName
33
+ AND json_extract(m.payload, '$.recipient.workItemId') IS @workItemId
34
+ AND json_extract(m.payload, '$.recipient.reviewRoundId') IS @reviewRoundId))`;
35
+ const params = { taskId, roleName: scope.roleName,
36
+ workItemId: scope.workItemId ?? null, reviewRoundId: scope.reviewRoundId ?? null };
37
+ const ids = `SELECT m.message_id FROM messages m WHERE ${matches}`;
38
+ const messages = db.prepare(ids).all(params).map(row => row.message_id);
39
+ const events = db.prepare(`SELECT event_id FROM events
40
+ WHERE task_id = @taskId AND type GLOB 'message.*'
41
+ AND json_extract(payload, '$.payload.messageId') IN (${ids})`).all(params).map(row => row.event_id);
42
+ return { messages, events };
43
+ }
44
+ export function queryContextRecords(db, taskId, query) {
45
+ const family = FAMILIES[query.family];
46
+ if (family === undefined || !Number.isSafeInteger(query.limit) || query.limit < 0 || query.limit > 256
47
+ || !Number.isSafeInteger(query.offset ?? 0) || (query.offset ?? 0) < 0) {
48
+ throw new Error("Context record query exceeds its bounded contract.");
49
+ }
50
+ const [table, key] = family;
51
+ const candidate = query.family === "candidate";
52
+ const payload = candidate ? "candidate.value" : "r.payload";
53
+ const id = candidate ? `r.${key} || '/' || json_extract(candidate.value, '$.id')` : `r.${key}`;
54
+ const from = `${table} r${candidate ? ", json_each(r.payload, '$.candidates') candidate" : ""}`;
55
+ const where = ["r.task_id = @taskId"];
56
+ const params = { taskId };
57
+ if (query.ids !== undefined) {
58
+ where.push(`${id} IN (SELECT value FROM json_each(@ids))`);
59
+ params.ids = JSON.stringify(query.ids);
60
+ }
61
+ for (const [label, statuses, not] of [
62
+ ["statuses", query.statuses, ""],
63
+ ["excludeStatuses", query.excludeStatuses, "NOT"]
64
+ ]) {
65
+ if (statuses !== undefined) {
66
+ where.push(`json_extract(${payload}, '$.status') ${not} IN (SELECT value FROM json_each(@${label}))`);
67
+ params[label] = JSON.stringify(statuses);
68
+ }
69
+ }
70
+ const sequence = `CAST(substr(r.${key}, length(r.${key}) - length(ltrim(r.${key}, 'abcdefghijklmnopqrstuvwxyz-')) + 1) AS INTEGER)`;
71
+ // Task-local numeric IDs are the stable historical order, not lexical 1,10,2.
72
+ if (query.family === "task-event") {
73
+ if (query.eventTypes !== undefined) {
74
+ where.push("r.type IN (SELECT value FROM json_each(@types))");
75
+ params.types = JSON.stringify(query.eventTypes);
76
+ }
77
+ for (const [key, value, comparison] of [
78
+ ["after", query.afterSequence, ">"], ["through", query.throughSequence, "<="]
79
+ ]) {
80
+ if (value !== undefined) {
81
+ if (!Number.isSafeInteger(value) || value < 0)
82
+ throw new Error("Invalid event cursor.");
83
+ where.push(`${sequence} ${comparison} @${key}`);
84
+ params[key] = value;
85
+ }
86
+ }
87
+ }
88
+ const condition = where.join(" AND ");
89
+ const count = db.prepare(`SELECT count(*) AS n FROM ${from} WHERE ${condition}`).get(params).n;
90
+ if (query.limit === 0 || count === 0)
91
+ return { count, records: [] };
92
+ const direction = query.descending ? "DESC" : "ASC";
93
+ const order = query.family === "work-item" || candidate
94
+ ? `r.updated_at DESC, ${sequence} ASC${candidate ? ", candidate.key DESC" : ""}`
95
+ : query.family === "managed-workspace" ? `r.${key} ${direction}` : `${sequence} ${direction}, r.${key} ${direction}`;
96
+ const rows = db.prepare(`SELECT ${id} AS id, ${payload} AS payload
97
+ FROM ${from} WHERE ${condition} ORDER BY ${order} LIMIT @limit OFFSET @offset`).all({ ...params, limit: query.limit, offset: query.offset ?? 0 });
98
+ return { count, records: rows.map(row => {
99
+ const value = JSON.parse(row.payload);
100
+ if (candidate)
101
+ validateWorkItemCandidate(value);
102
+ else
103
+ validateStoredRecord(table, value);
104
+ return { id: row.id, value };
105
+ }) };
106
+ }
@@ -40,7 +40,7 @@ export function readCurrentHomeIdentity(home) {
40
40
  export function validateCurrentTaskStore(home) {
41
41
  const store = openCurrentTaskStore(home);
42
42
  try {
43
- store.getConfig();
43
+ store.validateCurrentRecords();
44
44
  }
45
45
  finally {
46
46
  store.close();
@@ -1,4 +1,5 @@
1
1
  import { isAbsolute, join, relative, resolve } from "node:path";
2
+ import { createHash } from "node:crypto";
2
3
  /**
3
4
  * The single authority for every Yui self-managed path derived from a Home.
4
5
  *
@@ -10,7 +11,7 @@ import { isAbsolute, join, relative, resolve } from "node:path";
10
11
  * single canonical YUI_HOME contains all self-managed data, with only the
11
12
  * approved short-path IPC sockets (Controller, tmux, Agent Host, integration
12
13
  * runtime) remaining outside it for the `sockaddr_un` length budget. Within the
13
- * managed workspace root, storage v20 further collapses the former
14
+ * managed workspace root, storage v25 further collapses the former
14
15
  * physical-worktree/symlink-view split into a single layer of real worktrees
15
16
  * under `tasks/` (see {@link managedTaskRoot}).
16
17
  *
@@ -27,33 +28,21 @@ function homeRoot(home) {
27
28
  return resolved;
28
29
  }
29
30
  /**
30
- * Root that contains every managed Git workspace. Since storage v20 this holds a
31
+ * Root that contains every managed Git workspace. Since storage v25 this holds a
31
32
  * single layer of real worktrees under `tasks/` (plus the legacy `worktree/`
32
- * root, preserved by the 19->20 migration as its rollback anchor). A single
33
+ * root, preserved by the 24->25 migration as its rollback anchor). A single
33
34
  * parent keeps the managed families adjacent and lets the storage migrations
34
35
  * relocate them as one subtree.
35
36
  */
36
37
  export function managedWorkspacesRoot(home) {
37
38
  return join(homeRoot(home), "workspaces");
38
39
  }
39
- /**
40
- * LEGACY physical worktree root: `<home>/workspaces/worktree`. Before storage
41
- * v20 every managed clone/linked worktree lived here under a per-Project subtree,
42
- * surfaced to each Task through a symlink view under `tasks/`. The 19->20
43
- * migration collapses those into real worktrees under `tasks/` and PRESERVES this
44
- * tree as the rollback anchor, so this path survives only for that migration's
45
- * inline layout and for post-upgrade cleanup — no current runtime code derives a
46
- * live worktree path from it.
47
- */
48
- export function managedWorktreeRoot(home) {
49
- return join(managedWorkspacesRoot(home), "worktree");
50
- }
51
40
  /**
52
41
  * Managed Git worktrees, one real worktree per Task owner and Project at
53
42
  * `<home>/workspaces/tasks/<taskId>/<owner>/<projectDirectory>` (owner being
54
43
  * `main`, `work-items/<id>`, `reviews/<name>`, `integrations/<id>`, or
55
- * `execution-lanes/<g>/<l>`). Before storage v20 these were symlink views over
56
- * the physical `worktree/` root; the 19->20 migration makes them the worktrees
44
+ * `execution-lanes/<g>/<l>`). Before storage v25 these were symlink views over
45
+ * the physical `worktree/` root; the 24->25 migration makes them the worktrees
57
46
  * themselves, so the logical entry path and the physical Git worktree coincide.
58
47
  */
59
48
  export function managedTaskRoot(home) {
@@ -79,6 +68,13 @@ export function managedRuntimeRoot(home) {
79
68
  export function managedIntegrationRuntimeRoot(home) {
80
69
  return join(homeRoot(home), "runtime", "integration-runtimes");
81
70
  }
71
+ /** The shared Home-scoped short IPC directory; ordinary integration data stays
72
+ * inside Home. A fixture that owns the whole Home may remove this when empty. */
73
+ export function integrationTmuxSocketRoot(home) {
74
+ const uid = typeof process.getuid === "function" ? process.getuid() : 0;
75
+ const homeDigest = createHash("sha256").update(homeRoot(home)).digest("hex").slice(0, 16);
76
+ return join("/tmp", `yi-${uid.toString(36)}-${homeDigest}`);
77
+ }
82
78
  /**
83
79
  * Disposable staging prefix for `yui update`'s side-by-side package install
84
80
  * (an upgrade artifact): `<home>/runtime/update-staging`. Kept under Home so a
@@ -0,0 +1,22 @@
1
+ /** Historical errors have no trustworthy launch snapshot. Preserve their raw
2
+ * payloads and explicitly record that fact; never infer it from today's Role. */
3
+ export function migrateAgentFailureContext(db) {
4
+ const archive = db.prepare(`INSERT INTO storage_migration_archive
5
+ (migration_version,family,record_key,payload,content) VALUES(36,'agent-error',?,?,NULL)`);
6
+ const update = db.prepare("UPDATE events SET payload=? WHERE task_id=? AND event_id=?");
7
+ const rows = db.prepare("SELECT task_id,event_id,payload FROM events WHERE type='runtime.agent-error'").all();
8
+ for (const row of rows) {
9
+ const event = JSON.parse(row.payload);
10
+ if (!event || event.schemaVersion !== 2 || event.type !== "runtime.agent-error" || event.taskId !== row.task_id
11
+ || event.id !== row.event_id || !event.payload || typeof event.payload !== "object"
12
+ || Array.isArray(event.payload) || Object.values(event.payload).some(value => typeof value !== "string")
13
+ || Object.hasOwn(event.payload, "capabilityContext")) {
14
+ throw new Error(`Invalid pre-context Agent error: ${row.task_id}/${row.event_id}.`);
15
+ }
16
+ archive.run(JSON.stringify([row.task_id, row.event_id]), row.payload);
17
+ event.payload.capabilityContext = JSON.stringify({
18
+ status: "unavailable", reason: "Launch configuration was not recorded by this historical error producer."
19
+ });
20
+ update.run(JSON.stringify(event), row.task_id, row.event_id);
21
+ }
22
+ }