@zq-silk/yui 0.15.12 → 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 (236) hide show
  1. package/ARCHITECTURE.md +3 -2
  2. package/ARCHITECTURE.zh-CN.md +3 -2
  3. package/dist/artifacts/artifactCommitLock.js +16 -55
  4. package/dist/artifacts/taskArtifactRepository.js +3 -3
  5. package/dist/cli/agentConfigurationPicker.js +1 -1
  6. package/dist/cli/commandCatalog.js +57 -41
  7. package/dist/cli/completion.js +18 -18
  8. package/dist/cli/completionWizard.js +1 -1
  9. package/dist/cli/dynamicCompletion.js +1 -1
  10. package/dist/cli/interactionPolicy.js +7 -3
  11. package/dist/cli/invocationAuthority.js +64 -0
  12. package/dist/cli/managedDiagnostics.js +1 -1
  13. package/dist/cli/parseRepeatable.js +35 -0
  14. package/dist/cli/updatePorts.js +26 -5
  15. package/dist/cli.js +1280 -1243
  16. package/dist/commands/agentCommands.js +4 -4
  17. package/dist/commands/capabilityCommands.js +1 -1
  18. package/dist/commands/configCommands.js +6 -57
  19. package/dist/commands/configOverview.js +2 -2
  20. package/dist/commands/durableJobCommands.js +12 -6
  21. package/dist/commands/globalRoleCommands.js +53 -29
  22. package/dist/commands/grantCommands.js +0 -3
  23. package/dist/commands/jobCommands.js +1 -4
  24. package/dist/commands/operatorCommands.js +3 -3
  25. package/dist/commands/projectCommands.js +1 -10
  26. package/dist/commands/resourcesCommands.js +10 -40
  27. package/dist/commands/roleConfiguration.js +2 -6
  28. package/dist/commands/taskActivationCommands.js +4 -8
  29. package/dist/commands/taskAgentCapabilities.js +9 -0
  30. package/dist/commands/taskCommandSupport.js +155 -0
  31. package/dist/commands/taskCommandTypes.js +1 -0
  32. package/dist/commands/taskCommands.js +271 -710
  33. package/dist/commands/taskCompletionGate.js +1 -1
  34. package/dist/commands/taskExecutionCommands.js +1 -1
  35. package/dist/commands/taskFactCommands.js +325 -0
  36. package/dist/commands/taskInputCommands.js +12 -42
  37. package/dist/commands/taskIntegrationCommands.js +17 -57
  38. package/dist/commands/taskNextActionCommand.js +7 -6
  39. package/dist/commands/taskPublicationAdoptCommand.js +3 -3
  40. package/dist/commands/taskPublicationCommands.js +1 -1
  41. package/dist/commands/taskPublicationVerifyCommand.js +1 -1
  42. package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
  43. package/dist/commands/taskUpstreamCommands.js +19 -8
  44. package/dist/commands/workflowCommands.js +6 -1
  45. package/dist/completion/completionInstaller.js +26 -26
  46. package/dist/completion/completionState.js +26 -26
  47. package/dist/completion/fileCompletionManager.js +6 -11
  48. package/dist/config/configCatalog.js +0 -2
  49. package/dist/config/timeZone.js +14 -0
  50. package/dist/config/yuiConfig.js +0 -29
  51. package/dist/context/runContextPack.js +2 -3
  52. package/dist/context/taskCatalog.js +3 -5
  53. package/dist/context/taskContext.js +139 -151
  54. package/dist/context/wakeRunReferences.js +11 -0
  55. package/dist/controller/agentCapabilities.js +58 -0
  56. package/dist/controller/agentRuntimeObserver.js +6 -11
  57. package/dist/controller/clientRuntime.js +22 -14
  58. package/dist/controller/controller.js +9 -22
  59. package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
  60. package/dist/controller/jobClient.js +4 -4
  61. package/dist/controller/jobControl.js +60 -25
  62. package/dist/controller/jobSupervisor.js +4 -4
  63. package/dist/controller/providerRetryDelivery.js +7 -7
  64. package/dist/controller/runtime.js +51 -44
  65. package/dist/controller/runtimeEventProcessor.js +0 -5
  66. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  67. package/dist/controller/taskAgentError.js +35 -0
  68. package/dist/coordination/workMailboxQueue.js +2 -24
  69. package/dist/core/controllerClient.js +2 -2
  70. package/dist/core/fileLockOwner.js +74 -0
  71. package/dist/decision/decision.js +17 -0
  72. package/dist/doctor/doctor.js +18 -9
  73. package/dist/event/taskEvent.js +12 -0
  74. package/dist/execution/workItemExecutionProjection.js +2 -2
  75. package/dist/executor/agentCapabilityConfig.js +43 -0
  76. package/dist/executor/agentConfigurationCatalog.js +49 -11
  77. package/dist/executor/agentExecutor.js +5 -8
  78. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  79. package/dist/executor/taskAgentCapabilities.js +64 -0
  80. package/dist/integration/deliveryObligation.js +1 -69
  81. package/dist/integration/gitIntegrationService.js +60 -84
  82. package/dist/integration/integrationAttempt.js +3 -14
  83. package/dist/integration/integrationSourceApplication.js +4 -12
  84. package/dist/integration/manifestTags.js +2 -2
  85. package/dist/job/durableJob.js +4 -25
  86. package/dist/job/jobAssignmentScope.js +22 -0
  87. package/dist/job/jobOperation.js +16 -0
  88. package/dist/job/jobRunner.js +2 -1
  89. package/dist/job/stepDirectory.js +15 -0
  90. package/dist/kernel/builtinCapabilities.js +15 -11
  91. package/dist/kernel/kernelPorts.js +1 -17
  92. package/dist/lifecycle/exactRunTerminalization.js +1 -9
  93. package/dist/message/inputControlResolution.js +3 -4
  94. package/dist/message/message.js +17 -20
  95. package/dist/message/messageContinuation.js +1 -1
  96. package/dist/milestone/milestone.js +11 -0
  97. package/dist/observability/orchestrationMetrics.js +24 -1
  98. package/dist/observability/runtimeIdentity.js +1 -17
  99. package/dist/output/agentConfigurationPresentation.js +2 -0
  100. package/dist/output/timePresentation.js +1 -14
  101. package/dist/plugins/pluginService.js +12 -4
  102. package/dist/release/releaseWorkflowPorts.js +45 -96
  103. package/dist/release/runtimeRelease.js +9 -15
  104. package/dist/repository/projectMaintenanceLock.js +23 -64
  105. package/dist/repository/taskBaseFreshness.js +1 -1
  106. package/dist/repository/taskWorkspaceCoordinator.js +13 -22
  107. package/dist/repository/taskWorkspacePreparer.js +29 -25
  108. package/dist/repository/workspaceCleanupInspection.js +5 -5
  109. package/dist/resources/autoResourceGc.js +4 -77
  110. package/dist/resources/liveReferences.js +7 -54
  111. package/dist/resources/resourceDiscovery.js +5 -118
  112. package/dist/resources/resourceGc.js +233 -181
  113. package/dist/resources/resourceRegistrar.js +5 -4
  114. package/dist/resources/resourceRegistry.js +3 -37
  115. package/dist/resources/resourceRegistryStore.js +13 -36
  116. package/dist/resources/sqliteResourceRegistry.js +43 -26
  117. package/dist/review/deltaRecheck.js +1 -1
  118. package/dist/review/reviewAcceptance.js +1 -1
  119. package/dist/review/reviewDecision.js +1 -1
  120. package/dist/review/reviewRound.js +8 -7
  121. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  122. package/dist/runtime/acpProtocol.js +4 -51
  123. package/dist/runtime/acpSession.js +10 -75
  124. package/dist/runtime/acpSessionConfiguration.js +1 -8
  125. package/dist/runtime/agentEndpoint.js +3 -2
  126. package/dist/runtime/agentFailureContext.js +43 -0
  127. package/dist/runtime/agentHost.js +15 -15
  128. package/dist/runtime/codexInteractiveHost.js +2 -2
  129. package/dist/runtime/index.js +0 -1
  130. package/dist/runtime/managedCaller.js +21 -1
  131. package/dist/runtime/providerErrors.js +38 -0
  132. package/dist/runtime/runtimeObservation.js +0 -5
  133. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  134. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  135. package/dist/runtime/structuredProviderHost.js +4 -41
  136. package/dist/runtime/tmuxAdapters.js +6 -8
  137. package/dist/scheduler/activeRoleRunDelivery.js +7 -7
  138. package/dist/scheduler/leaderWakeupProcessor.js +19 -6
  139. package/dist/scheduler/operatorEvent.js +12 -20
  140. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  141. package/dist/scheduler/ports.js +5 -8
  142. package/dist/scheduler/roleRunLiveness.js +4 -4
  143. package/dist/scheduler/roleRunStall.js +16 -23
  144. package/dist/scheduler/taskExecutionProjection.js +25 -33
  145. package/dist/scheduler/taskObservabilityProjection.js +0 -1
  146. package/dist/scheduler/taskWake.js +5 -10
  147. package/dist/scheduler/wakeReason.js +2 -0
  148. package/dist/scheduler/wakeupQueue.js +2 -4
  149. package/dist/setup/setupCommand.js +1 -1
  150. package/dist/storage/contextRecords.js +106 -0
  151. package/dist/storage/currentTaskStore.js +1 -1
  152. package/dist/storage/homeLayout.js +13 -17
  153. package/dist/storage/migrations/agentFailureContext.js +22 -0
  154. package/dist/storage/migrations/currentInputContract.js +86 -0
  155. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  156. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  157. package/dist/storage/migrations/integrationContinuation.js +5 -4
  158. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  159. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  160. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  161. package/dist/storage/migrations/verificationPolicy.js +74 -0
  162. package/dist/storage/migrations/workItemHistory.js +46 -0
  163. package/dist/storage/persistenceWorker.js +12 -4
  164. package/dist/storage/recordValidation.js +87 -0
  165. package/dist/storage/sqliteSchema.js +142 -1
  166. package/dist/storage/sqliteStore.js +156 -162
  167. package/dist/storage/storageSchema.js +2 -14
  168. package/dist/storage/storageVersions.js +1 -1
  169. package/dist/storage/storeRpc.js +15 -7
  170. package/dist/storage/taskStore.js +3 -7
  171. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  172. package/dist/task/completionReadiness.js +5 -24
  173. package/dist/task/draftPlan.js +0 -53
  174. package/dist/task/nextAction.js +7 -13
  175. package/dist/task/remoteDeliveryService.js +89 -0
  176. package/dist/task/taskActivation.js +2 -25
  177. package/dist/task/taskActivationService.js +0 -2
  178. package/dist/task/taskRecordReference.js +0 -1
  179. package/dist/task/taskSubmission.js +3 -14
  180. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  181. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  182. package/dist/telemetry/telemetryWiring.js +4 -3
  183. package/dist/tmux/tmuxManager.js +29 -20
  184. package/dist/verification/gateArtifact.js +15 -24
  185. package/dist/verification/gateArtifactStore.js +14 -7
  186. package/dist/verification/verificationGateService.js +29 -88
  187. package/dist/verification/verificationPlan.js +27 -72
  188. package/dist/web/assets/client/app.js +1 -1
  189. package/dist/web/assets/client/taskSurface.js +4 -4
  190. package/dist/web/webServer.js +4 -6
  191. package/dist/web/webSnapshot.js +3 -83
  192. package/dist/web/webTaskSurface.js +17 -46
  193. package/dist/workItem/workItem.js +1 -12
  194. package/docs/architecture/capabilities-and-resources.md +14 -2
  195. package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
  196. package/docs/managed-turn-and-session-runtime.md +83 -10
  197. package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
  198. package/docs/plugin-sdk.md +4 -2
  199. package/docs/provider-runtime.md +3 -1
  200. package/docs/provider-runtime.zh-CN.md +4 -2
  201. package/docs/release-workflow.md +190 -7
  202. package/docs/release-workflow.zh-CN.md +139 -5
  203. package/docs/roles-and-configuration.md +29 -0
  204. package/docs/roles-and-configuration.zh-CN.md +21 -0
  205. package/docs/sqlite-control-plane-design.md +16 -3
  206. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  207. package/docs/task-dag-semantics.md +3 -3
  208. package/docs/task-dag-semantics.zh-CN.md +2 -2
  209. package/docs/task-delivery.md +91 -5
  210. package/docs/task-delivery.zh-CN.md +72 -4
  211. package/docs/task-discovery.md +10 -11
  212. package/docs/task-discovery.zh-CN.md +8 -9
  213. package/docs/testing/verification-levels.md +145 -9
  214. package/docs/testing/verification-levels.zh-CN.md +103 -8
  215. package/package.json +1 -1
  216. package/skills/yui-leader/SKILL.md +5 -0
  217. package/skills/yui-leader/references/integration.md +9 -5
  218. package/skills/yui-leader/references/planning.md +9 -3
  219. package/skills/yui-operator/SKILL.md +15 -10
  220. package/skills/yui-runtime/SKILL.md +6 -6
  221. package/skills/yui-runtime/references/recovery.md +47 -0
  222. package/dist/agent/agentRegistry.js +0 -10
  223. package/dist/agentRun/runIdentity.js +0 -22
  224. package/dist/commands/deliveryGuardPreflight.js +0 -30
  225. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  226. package/dist/commands/taskOverviewCommand.js +0 -377
  227. package/dist/completion/completionWizard.js +0 -125
  228. package/dist/context/dispatchContext.js +0 -110
  229. package/dist/context/wakeNotification.js +0 -144
  230. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  231. package/dist/integration/integrationQueueEntry.js +0 -191
  232. package/dist/integration/integrationQueueService.js +0 -810
  233. package/dist/release/fakeReleasePorts.js +0 -55
  234. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  235. package/dist/task/deliveryGuard.js +0 -226
  236. /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)
@@ -14,22 +14,24 @@ import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runt
14
14
  * aggregate. This function only reads; it never starts a Controller, queues a
15
15
  * wake, writes a Message, or mutates any record.
16
16
  */
17
- export function buildTaskExecutionProjection(store, taskId, taskOverride, now = new Date()) {
18
- const task = store.getTask?.(taskId) ?? taskOverride ?? null;
17
+ export function buildTaskExecutionProjection(store, taskId, now = new Date()) {
18
+ const task = store.getTask(taskId);
19
19
  if (task === null)
20
20
  return null;
21
- const roles = store.listRoles?.(taskId) ?? [];
22
- const events = store.listEvents?.(taskId) ?? [];
23
- const usageRuns = store.listRuns?.(taskId) ?? [];
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);
24
26
  const runs = operationalTaskRecords(usageRuns, events, "run");
25
- const leaderMailbox = store.getWorkMailbox?.({
27
+ const leaderMailbox = store.getWorkMailbox({
26
28
  kind: "role",
27
29
  taskId,
28
30
  roleName: "leader"
29
- }) ?? null;
31
+ });
30
32
  const roleSessions = roles.flatMap((role) => {
31
- const session = store.getRoleSession?.(taskId, role.name);
32
- return session === null || session === undefined
33
+ const session = store.getRoleSession(taskId, role.name);
34
+ return session === null
33
35
  ? []
34
36
  : [{ roleName: role.name, ...session }];
35
37
  });
@@ -39,38 +41,28 @@ export function buildTaskExecutionProjection(store, taskId, taskOverride, now =
39
41
  runs,
40
42
  usageRuns,
41
43
  providerRetries: roles.flatMap(role => {
42
- const retry = providerRetryProjection(store.getTaskRoleSessionSet?.(taskId, role.name)?.providerBinding);
44
+ const retry = providerRetryProjection(store.getTaskRoleSessionSet(taskId, role.name)?.providerBinding);
43
45
  return retry === null ? [] : [retry];
44
46
  }),
45
47
  runDelivery: Object.fromEntries(runs.map((run) => {
46
- const observed = store.getTaskRoleSessionSet?.(taskId, run.roleName)?.providerBinding?.run;
48
+ const observed = store.getTaskRoleSessionSet(taskId, run.roleName)?.providerBinding?.run;
47
49
  return [run.id, observed?.runId === run.id ? observed.status : "unobserved"];
48
50
  })),
49
- executionGroups: store.listWorkItems === undefined && store.listReviewRounds === undefined
50
- ? []
51
- : collectExecutionGroups(store.listWorkItems?.(taskId) ?? [], store.listReviewRounds?.(taskId) ?? [], runs),
52
- workItems: store.listWorkItems?.(taskId) ?? [],
53
- ...(store.listContextSnapshots === undefined
54
- ? {}
55
- : { contextSnapshots: store.listContextSnapshots(taskId) }),
56
- inputRequests: store.listInputRequests?.(taskId) ?? [],
57
- ...(store.listReviewRounds === undefined
58
- ? {}
59
- : { reviewRounds: store.listReviewRounds(taskId) }),
60
- ...(store.listChangeSets === undefined
61
- ? {}
62
- : { changeSets: store.listChangeSets(taskId) }),
63
- ...(store.listIntegrationAttempts === undefined
64
- ? {}
65
- : { integrations: store.listIntegrationAttempts(taskId) }),
66
- ...(store.listEvents === undefined ? {} : { events }),
67
- ...(store.getTaskBrief === undefined ? {} : { brief: store.getTaskBrief(taskId) }),
68
- 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),
69
61
  leaderMailbox,
70
- leaderFailure: store.getLeaderFailure?.(taskId) ?? null,
62
+ leaderFailure: store.getLeaderFailure(taskId),
71
63
  roleSessions,
72
64
  now,
73
- runtimeHealthPolicy: resolveRuntimeHealth(store.getConfig?.().runtimeHealth)
65
+ runtimeHealthPolicy: resolveRuntimeHealth(store.getConfig().runtimeHealth)
74
66
  });
75
67
  }
76
68
  /**
@@ -60,7 +60,6 @@ function projectDag(workItems) {
60
60
  const edges = [];
61
61
  for (const item of workItems) {
62
62
  for (const dependency of item.dependsOn) {
63
- const target = byId.get(dependency);
64
63
  const status = dependencyEdgeStatus(dependency, byId);
65
64
  edges.push({ from: dependency, to: item.id, status });
66
65
  dependents.get(dependency)?.push(item.id);
@@ -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
+ }
@@ -0,0 +1,86 @@
1
+ /** Frozen 33→34 boundary. An old immediate request without origin was not
2
+ * auto-adoptable. Its owner must activate or cancel it with the old release;
3
+ * an upgrade cannot make that choice, even while execution is stopped. */
4
+ export function preflightCurrentInputContract(db) {
5
+ const blocked = db.prepare(`SELECT task_id FROM task_records
6
+ WHERE json_extract(payload,'$.status')='draft'
7
+ AND json_type(payload,'$.retiredAt') IS NULL
8
+ AND json_extract(payload,'$.activationRequest.startMode')='immediate'
9
+ AND json_extract(payload,'$.activationRequest.disposition')='pending'
10
+ AND json_type(payload,'$.activationRequest.origin') IS NULL LIMIT 1`)
11
+ .get();
12
+ if (blocked !== undefined) {
13
+ throw new Error(`Input contract cutover requires explicit activation or cancellation of the origin-less pending request: ${blocked.task_id}. Preserve the request and resolve it with the old release before upgrading.`);
14
+ }
15
+ }
16
+ /** Preserve original representations as audit Events, not runtime alternatives.
17
+ * All interpretation of wakePolicy/origin ends at this migration. */
18
+ export function migrateCurrentInputContract(db) {
19
+ preflightCurrentInputContract(db);
20
+ const highWater = db.prepare(`SELECT max(
21
+ coalesce((SELECT high_water FROM id_sequences WHERE task_id=? AND kind='event'),0),
22
+ coalesce((SELECT max(CAST(substr(event_id,7) AS INTEGER)) FROM events WHERE task_id=?),0)
23
+ ) AS value`);
24
+ const saveEvent = db.prepare("INSERT INTO events(task_id,event_id,type,occurred_at,payload) VALUES(?,?,?,?,?)");
25
+ const saveCounter = db.prepare(`INSERT INTO id_sequences(task_id,kind,high_water) VALUES(?,'event',?)
26
+ ON CONFLICT(task_id,kind) DO UPDATE SET high_water=max(high_water,excluded.high_water)`);
27
+ const at = new Date().toISOString();
28
+ const archive = (taskId, type, record) => {
29
+ const seq = highWater.get(taskId, taskId).value + 1;
30
+ const event = { schemaVersion: 2, id: `event-${seq}`, taskId, type, createdAt: at,
31
+ payload: { record, disposition: "audit-only" } };
32
+ saveEvent.run(taskId, event.id, type, at, JSON.stringify(event));
33
+ saveCounter.run(taskId, seq);
34
+ };
35
+ const messages = db.prepare("SELECT task_id,message_id,payload FROM messages ORDER BY task_id,seq")
36
+ .all();
37
+ const saveMessage = db.prepare("UPDATE messages SET payload=? WHERE task_id=? AND message_id=?");
38
+ for (const row of messages) {
39
+ const message = JSON.parse(row.payload);
40
+ const user = message.kind === "user" || message.kind === "operator";
41
+ const hasPolicy = Object.hasOwn(message, "wakePolicy");
42
+ if (message.schemaVersion !== 3 || message.id !== row.message_id || message.taskId !== row.task_id
43
+ || (hasPolicy && (!user || !["leader", "none"].includes(message.wakePolicy)))
44
+ || (message.intent !== undefined && (!user || !["record", "discuss", "develop"].includes(message.intent)))
45
+ || (hasPolicy && message.intent !== undefined
46
+ && (message.wakePolicy === "none") !== (message.intent === "record"))) {
47
+ throw new Error(`Invalid historical Message input: ${row.task_id}/${row.message_id}. Preserve it for diagnosis.`);
48
+ }
49
+ if (!hasPolicy && (!user || message.intent !== undefined))
50
+ continue;
51
+ archive(row.task_id, "message.input-contract-retired", row.payload);
52
+ if (user && message.intent === undefined) {
53
+ message.intent = message.wakePolicy === "none" ? "record" : "discuss";
54
+ }
55
+ delete message.wakePolicy;
56
+ saveMessage.run(JSON.stringify(message), row.task_id, row.message_id);
57
+ }
58
+ const tasks = db.prepare("SELECT task_id,payload FROM task_records ORDER BY task_id")
59
+ .all();
60
+ const saveTask = db.prepare("UPDATE task_records SET payload=? WHERE task_id=?");
61
+ for (const row of tasks) {
62
+ const task = JSON.parse(row.payload);
63
+ const requests = [
64
+ ...(task.activationRequest === undefined ? [] : [task.activationRequest]),
65
+ ...(task.settledActivationRequests ?? [])
66
+ ];
67
+ let changed = false;
68
+ for (const request of requests) {
69
+ if (request === null || typeof request !== "object" || request.schemaVersion !== 1
70
+ || request.operation?.targetId !== row.task_id
71
+ || !["immediate", "after-planning-turn"].includes(request.startMode)
72
+ || !["pending", "failed", "cancelled", "adopted"].includes(request.disposition)
73
+ || (Object.hasOwn(request, "origin") && !["explicit", "submit-develop"].includes(request.origin))) {
74
+ throw new Error(`Invalid historical activation request: ${row.task_id}. Preserve it for diagnosis.`);
75
+ }
76
+ if (Object.hasOwn(request, "origin")) {
77
+ changed = true;
78
+ delete request.origin;
79
+ }
80
+ }
81
+ if (changed) {
82
+ archive(row.task_id, "task.activation-origin-retired", row.payload);
83
+ saveTask.run(JSON.stringify(task), row.task_id);
84
+ }
85
+ }
86
+ }