@zq-silk/yui 0.14.1 → 0.15.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 (185) hide show
  1. package/ARCHITECTURE.md +91 -47
  2. package/README.md +275 -145
  3. package/dist/agent/managedRuntimeEnvironment.js +5 -8
  4. package/dist/cli/commandCatalog.js +74 -109
  5. package/dist/cli/helpRenderer.js +1 -2
  6. package/dist/cli/interactionCandidates.js +10 -0
  7. package/dist/cli/interactionPolicy.js +76 -37
  8. package/dist/cli/roleWizard.js +5 -6
  9. package/dist/cli/updateCommand.js +17 -9
  10. package/dist/cli/updateOrchestrator.js +87 -19
  11. package/dist/cli/updatePorts.js +79 -13
  12. package/dist/cli/upgradeCommand.js +105 -23
  13. package/dist/cli.js +268 -231
  14. package/dist/commands/agentCommands.js +13 -0
  15. package/dist/commands/configCommands.js +7 -19
  16. package/dist/commands/configOverview.js +3 -2
  17. package/dist/commands/controllerCommands.js +1 -1
  18. package/dist/commands/durableJobCommands.js +1 -1
  19. package/dist/commands/executionAuditCommands.js +4 -9
  20. package/dist/commands/globalRoleCommands.js +17 -0
  21. package/dist/commands/profileCommands.js +200 -30
  22. package/dist/commands/sessionCommands.js +1 -1
  23. package/dist/commands/taskActor.js +29 -127
  24. package/dist/commands/taskChangeSetCommands.js +1 -3
  25. package/dist/commands/taskCommands.js +1771 -2107
  26. package/dist/commands/taskCompletionGate.js +27 -103
  27. package/dist/commands/taskContextCommand.js +47 -80
  28. package/dist/commands/taskExecutionCommands.js +9 -27
  29. package/dist/commands/taskInputCommands.js +10 -10
  30. package/dist/commands/taskIntegrationCommands.js +69 -61
  31. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  32. package/dist/commands/taskNextActionCommand.js +8 -29
  33. package/dist/commands/taskOverviewCommand.js +25 -3
  34. package/dist/commands/taskPublicationCommands.js +151 -85
  35. package/dist/commands/taskPublicationVerifyCommand.js +181 -0
  36. package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
  37. package/dist/commands/taskRoleRuntimeStatus.js +5 -6
  38. package/dist/commands/taskUpstreamCommands.js +79 -95
  39. package/dist/config/configCatalog.js +1 -1
  40. package/dist/context/contextSnapshot.js +17 -2
  41. package/dist/context/sourceTurnContext.js +30 -0
  42. package/dist/context/turnContextPack.js +107 -48
  43. package/dist/context/turnInputContract.js +11 -4
  44. package/dist/context/wakeNotification.js +84 -22
  45. package/dist/controller/agentRuntimeObserver.js +9 -12
  46. package/dist/controller/clientRuntime.js +13 -13
  47. package/dist/controller/controller.js +11 -11
  48. package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
  49. package/dist/controller/jobControl.js +11 -10
  50. package/dist/controller/resourceInventory.js +6 -6
  51. package/dist/controller/resourceInventoryLinux.js +2 -2
  52. package/dist/controller/runtime.js +18 -61
  53. package/dist/controller/runtimeEventInbox.js +57 -34
  54. package/dist/controller/runtimeEventProcessor.js +36 -96
  55. package/dist/controller/runtimeHookTurnFence.js +33 -57
  56. package/dist/controller/runtimeLaunchCoordinator.js +44 -40
  57. package/dist/controller/runtimeObservationHook.js +18 -12
  58. package/dist/controller/sessionNotify.js +43 -28
  59. package/dist/controller/sessionOwnerReconciliation.js +21 -21
  60. package/dist/controller/structuredProviderObservation.js +13 -16
  61. package/dist/coordination/workMailboxQueue.js +132 -0
  62. package/dist/core/controllerServer.js +6 -5
  63. package/dist/doctor/doctor.js +37 -14
  64. package/dist/domain/agentResultTransport.js +60 -0
  65. package/dist/execution/executionHealth.js +111 -118
  66. package/dist/execution/reviewMainTurn.js +161 -0
  67. package/dist/execution/workItemExecution.js +454 -0
  68. package/dist/execution/workItemExecutionProjection.js +309 -0
  69. package/dist/execution/workItemMainTurn.js +164 -0
  70. package/dist/executor/agentExecutor.js +19 -19
  71. package/dist/executor/executorRegistry.js +7 -28
  72. package/dist/executor/fileRoleLaunchPlanner.js +16 -82
  73. package/dist/external/githubPublicationVerifier.js +114 -0
  74. package/dist/external/gitlabPublicationVerifier.js +158 -0
  75. package/dist/external/pinnedCommandRunner.js +73 -0
  76. package/dist/integration/changeSetManifest.js +0 -3
  77. package/dist/integration/deliveryObligation.js +43 -6
  78. package/dist/integration/gitIntegrationService.js +284 -101
  79. package/dist/integration/integrationAttempt.js +64 -14
  80. package/dist/integration/integrationQueueEntry.js +5 -35
  81. package/dist/integration/integrationQueueService.js +65 -194
  82. package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
  83. package/dist/lifecycle/exactTurnTerminalization.js +100 -97
  84. package/dist/message/message.js +16 -0
  85. package/dist/observability/executionAudit.js +4 -25
  86. package/dist/observability/faultClassification.js +58 -70
  87. package/dist/observability/orchestrationMetrics.js +8 -50
  88. package/dist/observability/runtimeIdentity.js +48 -50
  89. package/dist/profile/agentProfile.js +60 -15
  90. package/dist/profile/agentProfileRuntime.js +100 -0
  91. package/dist/release/releaseWorkflowPorts.js +5 -68
  92. package/dist/release/runtimeRelease.js +9 -1
  93. package/dist/repository/executionLaneGitSnapshot.js +80 -0
  94. package/dist/repository/gitWorkspace.js +9 -67
  95. package/dist/repository/taskBaseFreshness.js +3 -1
  96. package/dist/repository/taskWorkspaceCoordinator.js +32 -4
  97. package/dist/repository/taskWorkspacePreparer.js +252 -411
  98. package/dist/resources/liveReferences.js +6 -55
  99. package/dist/resources/resourceDiscovery.js +1 -30
  100. package/dist/resources/resourceRegistrar.js +0 -23
  101. package/dist/review/deltaRecheck.js +8 -65
  102. package/dist/review/reviewAcceptance.js +22 -19
  103. package/dist/review/reviewConfig.js +0 -10
  104. package/dist/review/reviewDecision.js +2 -2
  105. package/dist/review/reviewRound.js +147 -273
  106. package/dist/review/reviewerAvailability.js +13 -23
  107. package/dist/role/role.js +3 -0
  108. package/dist/runtime/agentDriverObservation.js +1 -1
  109. package/dist/runtime/agentHost.js +51 -51
  110. package/dist/runtime/builtinAgentDrivers.js +9 -2
  111. package/dist/runtime/codexAppServerRuntime.js +23 -5
  112. package/dist/runtime/exactControlPlane.js +22 -296
  113. package/dist/runtime/firstProgressAdvisory.js +1 -1
  114. package/dist/runtime/index.js +1 -1
  115. package/dist/runtime/launchBroker.js +11 -11
  116. package/dist/runtime/lifecycleReservation.js +2 -2
  117. package/dist/runtime/managedCaller.js +111 -0
  118. package/dist/runtime/ports.js +16 -3
  119. package/dist/runtime/preallocatedNativeSession.js +2 -2
  120. package/dist/runtime/processExitObservation.js +3 -2
  121. package/dist/runtime/providerContinuationReconciliationService.js +1 -1
  122. package/dist/runtime/runtimeBinding.js +1 -1
  123. package/dist/runtime/runtimeObservation.js +33 -16
  124. package/dist/runtime/runtimeSessionCandidate.js +2 -2
  125. package/dist/runtime/runtimeStopReceipt.js +11 -11
  126. package/dist/runtime/sessionLaunchRequest.js +2 -2
  127. package/dist/runtime/sessionOwnerIdentity.js +9 -9
  128. package/dist/runtime/sessionOwnerRegistry.js +13 -13
  129. package/dist/runtime/sessionReconciliation.js +4 -4
  130. package/dist/runtime/sessionTerminationGuard.js +9 -9
  131. package/dist/runtime/sessionTokenMetrics.js +5 -7
  132. package/dist/runtime/structuredProviderHost.js +5 -5
  133. package/dist/runtime/taskRuntimeIsolation.js +24 -24
  134. package/dist/runtime/tmuxAdapters.js +32 -30
  135. package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
  136. package/dist/scheduler/leaderWakeupProcessor.js +78 -55
  137. package/dist/scheduler/roleTurnLiveness.js +6 -6
  138. package/dist/scheduler/roleTurnStall.js +8 -9
  139. package/dist/scheduler/taskExecutionProjection.js +19 -41
  140. package/dist/scheduler/taskObservabilityProjection.js +75 -122
  141. package/dist/storage/currentTaskStore.js +6 -4
  142. package/dist/storage/sqliteSchema.js +135 -80
  143. package/dist/storage/sqliteStore.js +69 -52
  144. package/dist/storage/storageSchema.js +92 -230
  145. package/dist/storage/storageVersions.js +12 -13
  146. package/dist/storage/taskStore.js +8 -8
  147. package/dist/storage/upgrade/upgradeOrchestrator.js +276 -85
  148. package/dist/task/completionReadiness.js +17 -98
  149. package/dist/task/deliveryGuard.js +9 -11
  150. package/dist/task/draftPlan.js +142 -0
  151. package/dist/task/nextAction.js +179 -477
  152. package/dist/task/publicationVerification.js +1 -0
  153. package/dist/task/remoteDelivery.js +233 -0
  154. package/dist/task/task.js +108 -4
  155. package/dist/task/taskRecordReference.js +0 -1
  156. package/dist/turn/turn.js +109 -16
  157. package/dist/verification/verificationGateService.js +0 -26
  158. package/dist/version.js +3 -3
  159. package/dist/web/assets/client/components.js +99 -121
  160. package/dist/web/assets/client/i18n.js +136 -58
  161. package/dist/web/assets/client/view.js +44 -0
  162. package/dist/web/assets/styles/cards.js +3 -15
  163. package/dist/web/assets/styles/widgets.js +4 -0
  164. package/dist/web/webSnapshot.js +29 -14
  165. package/dist/workItem/dependencyGate.js +34 -0
  166. package/dist/workItem/workItem.js +99 -266
  167. package/dist/workspace/workItemChangeSetManager.js +15 -59
  168. package/docs/task-local-identity.md +9 -9
  169. package/i18n/README.zh-CN.md +189 -74
  170. package/package.json +1 -1
  171. package/skills/yui-leader/SKILL.md +272 -782
  172. package/skills/yui-operator/SKILL.md +190 -365
  173. package/skills/yui-reviewer/SKILL.md +40 -33
  174. package/skills/yui-runtime/SKILL.md +22 -0
  175. package/skills/yui-worker/SKILL.md +19 -6
  176. package/dist/execution/candidateConvergence.js +0 -623
  177. package/dist/execution/executionGroup.js +0 -854
  178. package/dist/execution/resourceBroker.js +0 -426
  179. package/dist/executor/launchPlan.js +0 -16
  180. package/dist/review/reviewFinding.js +0 -240
  181. package/dist/review/reviewFindingLedger.js +0 -550
  182. package/dist/review/reviewOutcomeClassifier.js +0 -255
  183. package/dist/scheduler/resourceQueueProjection.js +0 -71
  184. package/dist/storage/upgrade/recordVersions.js +0 -82
  185. package/dist/task/repairWave.js +0 -150
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { callController } from "../core/controllerClient.js";
3
3
  import { builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
4
4
  import { standardAgentError } from "../runtime/agentError.js";
5
+ import { transportAgentResult } from "../domain/agentResultTransport.js";
5
6
  import { createRuntimeObservation, runtimeObservationSemanticKey } from "../runtime/runtimeObservation.js";
6
7
  import { runtimeLifecycleSignalKey } from "../runtime/lifecycleReservation.js";
7
8
  import { isForeignHandoverLockHeld } from "../release/runtimeRelease.js";
@@ -28,8 +29,7 @@ export async function publishStructuredProviderStarted(input) {
28
29
  roleName: fence.roleName,
29
30
  agentId: fence.agentId,
30
31
  driverId: driver.id,
31
- launchId: fence.launchId,
32
- sessionGenerationId: fence.launchId,
32
+ runtimeGenerationId: fence.runtimeGenerationId,
33
33
  conversationId: input.started.conversationId,
34
34
  activationId: requireIdentity(input.activationId, "Provider Activation id"),
35
35
  nativeSessionId: input.started.nativeSessionId,
@@ -56,8 +56,7 @@ export async function publishStructuredProviderAccepted(input) {
56
56
  ...(fence.turnId === undefined ? {} : { turnId: fence.turnId }),
57
57
  agentId: fence.agentId,
58
58
  driverId: driver.id,
59
- launchId: fence.launchId,
60
- sessionGenerationId: fence.launchId,
59
+ runtimeGenerationId: fence.runtimeGenerationId,
61
60
  conversationId: input.receipt.conversationId,
62
61
  activationId: requireIdentity(input.activationId, "Provider Activation id"),
63
62
  nativeSessionId: input.receipt.nativeSessionId,
@@ -107,8 +106,7 @@ export async function publishStructuredProviderOpened(input) {
107
106
  ...(fence.turnId === undefined ? {} : { turnId: fence.turnId }),
108
107
  agentId: fence.agentId,
109
108
  driverId: driver.id,
110
- launchId: fence.launchId,
111
- sessionGenerationId: fence.launchId,
109
+ runtimeGenerationId: fence.runtimeGenerationId,
112
110
  conversationId: input.conversationId,
113
111
  activationId: requireIdentity(input.activationId, "Provider Activation id"),
114
112
  nativeSessionId: input.nativeSessionId,
@@ -153,8 +151,7 @@ export async function publishStructuredProviderGoal(input) {
153
151
  roleName: fence.roleName,
154
152
  agentId: fence.agentId,
155
153
  driverId: driver.id,
156
- launchId: fence.launchId,
157
- sessionGenerationId: fence.launchId,
154
+ runtimeGenerationId: fence.runtimeGenerationId,
158
155
  conversationId: input.conversationId,
159
156
  activationId: requireIdentity(input.activationId, "Provider Activation id"),
160
157
  nativeSessionId: input.conversationId
@@ -180,8 +177,7 @@ export async function publishStructuredConversationRecoverability(input) {
180
177
  ...(fence.turnId === undefined ? {} : { turnId: fence.turnId }),
181
178
  agentId: fence.agentId,
182
179
  driverId: driver.id,
183
- launchId: fence.launchId,
184
- sessionGenerationId: fence.launchId,
180
+ runtimeGenerationId: fence.runtimeGenerationId,
185
181
  conversationId: input.conversationId,
186
182
  activationId: requireIdentity(input.activationId, "Provider Activation id"),
187
183
  nativeSessionId: input.conversationId,
@@ -224,10 +220,13 @@ export async function publishStructuredProviderTerminal(input) {
224
220
  const kind = input.terminal.status === "completed"
225
221
  ? "turn.completed"
226
222
  : input.terminal.status === "cancelled" ? "turn.cancelled" : "turn.failed";
223
+ const transported = transportAgentResult(input.terminal.output);
227
224
  const payload = kind === "turn.completed"
228
225
  ? {
229
226
  ...(input.terminal.input === undefined ? {} : { input: input.terminal.input }),
230
- ...(input.terminal.summary === undefined ? {} : { summary: input.terminal.summary })
227
+ ...(transported.status === "completed"
228
+ ? { output: transported.output }
229
+ : { resultTransportDiagnostic: transported.diagnostic })
231
230
  }
232
231
  : kind === "turn.failed"
233
232
  ? {
@@ -263,8 +262,7 @@ export async function publishStructuredProviderTerminal(input) {
263
262
  ...(fence.turnId === undefined ? {} : { turnId: fence.turnId }),
264
263
  agentId: fence.agentId,
265
264
  driverId: driver.id,
266
- launchId: fence.launchId,
267
- sessionGenerationId: fence.launchId,
265
+ runtimeGenerationId: fence.runtimeGenerationId,
268
266
  conversationId: input.terminal.conversationId,
269
267
  activationId: requireIdentity(input.activationId, "Provider Activation id"),
270
268
  nativeSessionId: input.terminal.nativeSessionId,
@@ -290,8 +288,7 @@ export async function publishStructuredProviderActivationTerminal(input) {
290
288
  ...(fence.turnId === undefined ? {} : { turnId: fence.turnId }),
291
289
  agentId: fence.agentId,
292
290
  driverId: driver.id,
293
- launchId: fence.launchId,
294
- sessionGenerationId: fence.launchId,
291
+ runtimeGenerationId: fence.runtimeGenerationId,
295
292
  conversationId: requireIdentity(input.conversationId, "Provider Conversation id"),
296
293
  activationId: requireIdentity(input.activationId, "Provider Activation id"),
297
294
  nativeSessionId: requireIdentity(input.nativeSessionId, "native Session id"),
@@ -311,7 +308,7 @@ function observation(input) {
311
308
  payload: input.payload ?? {}
312
309
  };
313
310
  return createRuntimeObservation({
314
- schemaVersion: 2,
311
+ schemaVersion: 4,
315
312
  eventId,
316
313
  semanticKey: runtimeObservationSemanticKey(partial),
317
314
  kind: input.kind,
@@ -1,4 +1,11 @@
1
1
  import { completeProcessing, consumePendingBatch, createWorkMailbox, enqueueSignal, mailboxBatches, mailboxEntityRefKey } from "./workMailbox.js";
2
+ const LEGACY_ROLE_TURN_DISPATCH_REASONS = new Set([
3
+ "turn-dispatched",
4
+ "turn-retried",
5
+ "review-requested",
6
+ "workitem-synthesis-ready",
7
+ "review-synthesis-ready"
8
+ ]);
2
9
  /** Atomically useful when called inside the caller's TaskStore transaction. */
3
10
  export function enqueueWork(store, target, reason, occurredAt, refs = [], metadata = {}) {
4
11
  const mailbox = store.getWorkMailbox(target) ?? createWorkMailbox(target);
@@ -11,6 +18,106 @@ export function enqueueWork(store, target, reason, occurredAt, refs = [], metada
11
18
  store.saveWorkMailbox(queued);
12
19
  return queued;
13
20
  }
21
+ /**
22
+ * The sole ordinary Role Turn wake shape. The Turn is durable execution
23
+ * authority; this signal only asks the Controller to deliver that exact Turn.
24
+ * Leader is deliberately excluded because its Role mailbox is reserved for
25
+ * semantic event wakes and force-steer batches. A Leader active Turn is
26
+ * selected directly from durable state by its targeted Controller pass.
27
+ */
28
+ export function enqueueRoleTurnDispatch(store, input) {
29
+ if (input.roleName === "leader")
30
+ return null;
31
+ return enqueueWork(store, roleTurnTarget(input), input.reason, input.occurredAt, [roleTurnRef(input)], {
32
+ source: "turn-dispatch",
33
+ dedupeKey: roleTurnDispatchDedupeKey(input)
34
+ });
35
+ }
36
+ /**
37
+ * Captures only the mailbox prefix that contains one exact Role Turn dispatch.
38
+ * A later signal may append while Provider delivery is in flight; its suffix
39
+ * remains pending when this token is settled.
40
+ */
41
+ export function captureRoleTurnDispatch(mailbox, input) {
42
+ if (input.roleName === "leader"
43
+ || mailbox === null
44
+ || mailbox.target.kind !== "role"
45
+ || mailbox.target.taskId !== input.taskId
46
+ || mailbox.target.roleName !== input.roleName) {
47
+ return null;
48
+ }
49
+ const exactRef = roleTurnRef(input);
50
+ if (mailbox.processing?.executionRef !== undefined
51
+ && mailboxEntityRefKey(mailbox.processing.executionRef) === mailboxEntityRefKey(exactRef)) {
52
+ return {
53
+ kind: "processing",
54
+ batchId: mailbox.processing.batchId
55
+ };
56
+ }
57
+ const pending = mailbox.pending;
58
+ if (pending === null)
59
+ return null;
60
+ const dedupeKey = roleTurnDispatchDedupeKey(input);
61
+ if (mailbox.recentDedupeKeys.includes(dedupeKey))
62
+ return null;
63
+ const keyIndex = pending.dedupeKeys.indexOf(dedupeKey);
64
+ if (keyIndex >= 0 && pending.dedupeKeys.length === pending.requestCount) {
65
+ const turnSequence = pending.fromSequence + keyIndex;
66
+ if (turnSequence <= pending.toSequence) {
67
+ return {
68
+ kind: "pending",
69
+ fromSequence: pending.fromSequence,
70
+ toSequence: turnSequence
71
+ };
72
+ }
73
+ }
74
+ // Valid earlier dispatches used sequence-generated dedupe keys and could
75
+ // include a companion WorkItem ref. Consume that complete legacy batch once
76
+ // the exact Turn reaches an accepted or terminal boundary.
77
+ if (pending.requestCount === 1
78
+ && pending.sources.length === 1
79
+ && pending.sources[0] === "yui"
80
+ && pending.reasons.some((reason) => LEGACY_ROLE_TURN_DISPATCH_REASONS.has(reason))
81
+ && pending.refs.some((ref) => (mailboxEntityRefKey(ref) === mailboxEntityRefKey(exactRef)))) {
82
+ return {
83
+ kind: "pending",
84
+ fromSequence: pending.fromSequence,
85
+ toSequence: pending.toSequence
86
+ };
87
+ }
88
+ return null;
89
+ }
90
+ /**
91
+ * Settles one exact ordinary Role Turn dispatch. Provider acceptance is the
92
+ * normal boundary; terminalization calls the same operation for conclusively
93
+ * unaccepted and valid earlier dispatches.
94
+ */
95
+ export function settleRoleTurnDispatch(store, input, expected) {
96
+ if (expected === null)
97
+ return "absent";
98
+ const target = roleTurnTarget(input);
99
+ const mailbox = store.getWorkMailbox(target);
100
+ const current = captureRoleTurnDispatch(mailbox, input);
101
+ if (current === null || mailbox === null)
102
+ return "absent";
103
+ if (expected !== undefined && !sameRoleTurnDispatchToken(current, expected)) {
104
+ return "state-changed";
105
+ }
106
+ const token = expected ?? current;
107
+ if (token.kind === "processing") {
108
+ if (mailbox.processing?.batchId !== token.batchId)
109
+ return "state-changed";
110
+ store.saveWorkMailbox(completeProcessing(mailbox, token.batchId));
111
+ return "settled";
112
+ }
113
+ if (mailbox.pending === null
114
+ || mailbox.pending.fromSequence !== token.fromSequence
115
+ || mailbox.pending.toSequence < token.toSequence) {
116
+ return "state-changed";
117
+ }
118
+ store.saveWorkMailbox(consumePendingBatch(mailbox, token));
119
+ return "settled";
120
+ }
14
121
  /** Completes only the batch owned by the matching durable execution. */
15
122
  export function completeWorkExecution(store, target, executionRef) {
16
123
  const mailbox = store.getWorkMailbox(target);
@@ -76,6 +183,31 @@ export function settleExactWorkExecution(store, target, executionRef) {
76
183
  function timestamp(value) {
77
184
  return value instanceof Date ? value.toISOString() : value;
78
185
  }
186
+ function roleTurnTarget(input) {
187
+ return {
188
+ kind: "role",
189
+ taskId: input.taskId,
190
+ roleName: input.roleName
191
+ };
192
+ }
193
+ function roleTurnRef(input) {
194
+ return {
195
+ type: "turn",
196
+ taskId: input.taskId,
197
+ id: input.turnId
198
+ };
199
+ }
200
+ function roleTurnDispatchDedupeKey(input) {
201
+ return `role-turn:${input.taskId}:${input.roleName}:${input.turnId}`;
202
+ }
203
+ function sameRoleTurnDispatchToken(left, right) {
204
+ return left.kind === right.kind
205
+ && (left.kind === "pending"
206
+ ? right.kind === "pending"
207
+ && left.fromSequence === right.fromSequence
208
+ && left.toSequence === right.toSequence
209
+ : right.kind === "processing" && left.batchId === right.batchId);
210
+ }
79
211
  function targetLabel(target) {
80
212
  switch (target.kind) {
81
213
  case "operator": return "operator";
@@ -400,8 +400,8 @@ async function routeRequest(socket, line, token, dispatcher, stop, status, telem
400
400
  homeFilesystemId,
401
401
  controllerInstanceId,
402
402
  version: YUI_VERSION,
403
- storageLayoutVersion: yuiVersionIdentity().storageLayoutVersion,
404
- aggregateSchemaVersion: yuiVersionIdentity().aggregateSchemaVersion,
403
+ storageVersion: yuiVersionIdentity().storageVersion,
404
+ minimumStorageVersion: yuiVersionIdentity().minimumStorageVersion,
405
405
  ...(status === undefined ? {} : { runtime: status(telemetry) })
406
406
  }
407
407
  });
@@ -593,6 +593,7 @@ function parseBeginHandoverParams(value) {
593
593
  }
594
594
  const fromReleaseId = record.fromReleaseId;
595
595
  if (fromReleaseId !== undefined
596
+ && fromReleaseId !== null
596
597
  && (typeof fromReleaseId !== "string" || fromReleaseId.length > 256)) {
597
598
  return null;
598
599
  }
@@ -674,7 +675,7 @@ export function buildRuntimeIdentityReceipt(input) {
674
675
  }
675
676
  })();
676
677
  return Object.freeze({
677
- schemaVersion: 1,
678
+ schemaVersion: 2,
678
679
  version: YUI_VERSION,
679
680
  executablePath: process.execPath,
680
681
  args: process.argv.slice(1),
@@ -684,8 +685,8 @@ export function buildRuntimeIdentityReceipt(input) {
684
685
  cliRealpath: controllerCliRealpath(),
685
686
  controllerRealpath: realpathSync(fileURLToPath(import.meta.url)),
686
687
  controllerProtocolVersion: FILE_TASK_CONTROLLER_PROTOCOL_VERSION,
687
- storageLayoutVersion: yuiVersionIdentity().storageLayoutVersion,
688
- aggregateSchemaVersion: yuiVersionIdentity().aggregateSchemaVersion,
688
+ storageVersion: yuiVersionIdentity().storageVersion,
689
+ minimumStorageVersion: yuiVersionIdentity().minimumStorageVersion,
689
690
  storageBackend: input.storageBackend,
690
691
  workerEnabled: input.workerEnabled,
691
692
  pid: process.pid,
@@ -34,7 +34,7 @@ export const STORAGE_DOCTOR_CHECK_NAMES = Object.freeze([
34
34
  * healthy only when schema, compatibility, and state are all `ok`. Any
35
35
  * `unsupported` (version mismatch / needs-new-version), `invalid` (corrupted /
36
36
  * unreadable), or `missing` (uninitialized) storage check is blocking — even
37
- * though `yui doctor` itself exits 0.
37
+ * and the machine-readable command exits non-zero.
38
38
  */
39
39
  export function summarizeStorageHealth(checks) {
40
40
  const names = new Set(STORAGE_DOCTOR_CHECK_NAMES);
@@ -56,14 +56,16 @@ export function buildDoctorReport(env, executor) {
56
56
  }
57
57
  /**
58
58
  * Read the physical-backend facts for a Home (Issue 01 observability). Every
59
- * field is best-effort read-only evidence: an unreadable database or manifest
60
- * yields `null` for that field, and the `storage state` check reports the
61
- * structural problem separately.
59
+ * field is best-effort read-only evidence: an unreadable database yields
60
+ * `null` for that field, and the `storage state` check reports the structural
61
+ * problem separately.
62
62
  */
63
63
  function inspectStorageDetails(home, env) {
64
64
  const schema = inspectStorageSchema(home);
65
- const logicalLayout = schema.status === "current" || schema.status === "unsupported"
66
- ? schema.currentLayoutVersion
65
+ const storageVersion = schema.status === "current"
66
+ || schema.status === "upgradeable"
67
+ || schema.status === "unsupported"
68
+ ? schema.currentVersion
67
69
  : null;
68
70
  const authoritativeBackend = "sqlite";
69
71
  const dbPath = join(home, CURRENT_DATABASE_FILENAME);
@@ -88,7 +90,8 @@ function inspectStorageDetails(home, env) {
88
90
  }
89
91
  }
90
92
  return {
91
- logicalLayout,
93
+ storageVersion,
94
+ minimumSupportedVersion: schema.minimumSupportedVersion,
92
95
  authoritativeBackend,
93
96
  databasePath,
94
97
  journalMode,
@@ -216,13 +219,22 @@ function checkSchema(state) {
216
219
  return {
217
220
  name: "storage schema",
218
221
  status: "ok",
219
- detail: `current=${state.currentVersion} latest=${state.latestVersion}`
222
+ detail: `current=${state.currentVersion} latest=${state.latestVersion} `
223
+ + `minimum=${state.minimumSupportedVersion}`
224
+ };
225
+ case "upgradeable":
226
+ return {
227
+ name: "storage schema",
228
+ status: "unsupported",
229
+ detail: `current=${state.currentVersion} latest=${state.latestVersion} `
230
+ + `minimum=${state.minimumSupportedVersion} migration=available`
220
231
  };
221
232
  case "unsupported":
222
233
  return {
223
234
  name: "storage schema",
224
235
  status: "unsupported",
225
- detail: `current=${state.currentVersion} latest=${state.latestVersion} direction=${state.direction}`
236
+ detail: `current=${state.currentVersion} latest=${state.latestVersion} `
237
+ + `minimum=${state.minimumSupportedVersion} direction=${state.direction}`
226
238
  };
227
239
  case "invalid":
228
240
  return { name: "storage schema", status: "invalid", detail: state.detail };
@@ -251,9 +263,20 @@ function inspectCompatibility(home, homeCheck, schema) {
251
263
  check: {
252
264
  name,
253
265
  status: "unsupported",
254
- detail: `unsupported contract: ${schema.incompatibleComponent} is ${schema.direction} `
255
- + `(current=${schema.currentVersion}, required=${schema.latestVersion}). `
256
- + "Preserve this Home and initialize a new Home."
266
+ detail: `storage version is ${schema.direction} `
267
+ + `(current=${schema.currentVersion}, supported=`
268
+ + `${schema.minimumSupportedVersion}..${schema.latestVersion}).`
269
+ }
270
+ };
271
+ }
272
+ if (schema.status === "upgradeable") {
273
+ return {
274
+ storageStatus: "upgradeable",
275
+ check: {
276
+ name,
277
+ status: "unsupported",
278
+ detail: `migration available from ${schema.currentVersion} to ${schema.latestVersion}; `
279
+ + "run `yui upgrade` or `yui update`."
257
280
  }
258
281
  };
259
282
  }
@@ -262,8 +285,8 @@ function inspectCompatibility(home, homeCheck, schema) {
262
285
  check: {
263
286
  name,
264
287
  status: "ok",
265
- detail: `current layout=${schema.currentLayoutVersion}/${schema.latestLayoutVersion} `
266
- + `aggregate=${schema.currentAggregateSchemaVersion}/${schema.latestAggregateSchemaVersion}`
288
+ detail: `current=${schema.currentVersion}/${schema.latestVersion} `
289
+ + `minimum=${schema.minimumSupportedVersion}`
267
290
  }
268
291
  };
269
292
  }
@@ -0,0 +1,60 @@
1
+ export const MAX_TURN_RESULT_OUTPUT_BYTES = 512 * 1024;
2
+ export const MAX_TURN_FAILURE_DIAGNOSTIC_BYTES = 16 * 1024;
3
+ /**
4
+ * Validate only the transport envelope of an Agent result. The accepted text
5
+ * remains byte-for-byte unchanged; missing or untransportable text becomes a
6
+ * Core-owned failed outcome so the exact Turn can still terminalize.
7
+ */
8
+ export function transportAgentResult(value) {
9
+ if (typeof value !== "string") {
10
+ return {
11
+ status: "failed",
12
+ diagnostic: "Provider terminal event did not include an Agent result.",
13
+ failureReason: "missing-result"
14
+ };
15
+ }
16
+ if (value.includes("\0")) {
17
+ return {
18
+ status: "failed",
19
+ diagnostic: "Provider terminal event included an Agent result with an invalid NUL byte.",
20
+ failureReason: "missing-result"
21
+ };
22
+ }
23
+ if (value.trim().length === 0) {
24
+ return {
25
+ status: "failed",
26
+ diagnostic: "Provider terminal event included an empty Agent result.",
27
+ failureReason: "missing-result"
28
+ };
29
+ }
30
+ const bytes = Buffer.byteLength(value, "utf8");
31
+ if (bytes > MAX_TURN_RESULT_OUTPUT_BYTES) {
32
+ return {
33
+ status: "failed",
34
+ diagnostic: `Provider Agent result is ${bytes} bytes and exceeds the ${MAX_TURN_RESULT_OUTPUT_BYTES}-byte durable result limit; the result was not stored.`,
35
+ failureReason: "missing-result"
36
+ };
37
+ }
38
+ return { status: "completed", output: value };
39
+ }
40
+ /**
41
+ * Bound Core-owned failure diagnostics without rejecting a terminal event.
42
+ * This is intentionally separate from Agent result transport: successful
43
+ * Agent text is either retained exactly or rejected as a whole, never
44
+ * truncated. Provider/Core diagnostics may be clipped because they are only
45
+ * operational context for a failed Turn.
46
+ */
47
+ export function boundedTurnFailureDiagnostic(value, fallback = "Provider reported a failed Agent Turn.") {
48
+ const candidate = typeof value === "string" && value.trim().length > 0
49
+ ? value
50
+ : fallback;
51
+ const safe = candidate.replaceAll("\0", "\uFFFD");
52
+ if (Buffer.byteLength(safe, "utf8") <= MAX_TURN_FAILURE_DIAGNOSTIC_BYTES) {
53
+ return safe;
54
+ }
55
+ const prefix = Buffer
56
+ .from(safe, "utf8")
57
+ .subarray(0, MAX_TURN_FAILURE_DIAGNOSTIC_BYTES)
58
+ .toString("utf8");
59
+ return prefix.endsWith("\uFFFD") ? prefix.slice(0, -1) : prefix;
60
+ }