@zq-silk/yui 0.15.7 → 0.15.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/ARCHITECTURE.md +194 -399
  2. package/ARCHITECTURE.zh-CN.md +151 -0
  3. package/README.md +306 -1131
  4. package/dist/agent/adapterCatalog.js +15 -2
  5. package/dist/agent/agent.js +23 -3
  6. package/dist/agent/argumentPolicy.js +7 -1
  7. package/dist/agent/connectionPlan.js +62 -0
  8. package/dist/agent/executionComponents.js +158 -0
  9. package/dist/agent/launchEnvironment.js +31 -3
  10. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  11. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  12. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  13. package/dist/artifacts/artifactCapability.js +74 -0
  14. package/dist/artifacts/artifactCommitLock.js +249 -0
  15. package/dist/artifacts/artifactPaths.js +151 -0
  16. package/dist/artifacts/gitArtifactRef.js +146 -0
  17. package/dist/artifacts/managedGit.js +332 -0
  18. package/dist/artifacts/taskArtifactRepository.js +277 -0
  19. package/dist/brief/taskBrief.js +12 -0
  20. package/dist/cli/agentConfigurationPicker.js +13 -0
  21. package/dist/cli/commandCatalog.js +165 -74
  22. package/dist/cli/interactionCandidates.js +5 -5
  23. package/dist/cli/interactionPolicy.js +38 -8
  24. package/dist/cli/invocationRouter.js +1 -1
  25. package/dist/cli/managedDiagnostics.js +28 -0
  26. package/dist/cli/operatorWizard.js +1 -7
  27. package/dist/cli/roleOptionOrder.js +27 -0
  28. package/dist/cli/roleWizard.js +50 -14
  29. package/dist/cli/updateOrchestrator.js +1 -1
  30. package/dist/cli/updatePorts.js +3 -4
  31. package/dist/cli.js +245 -95
  32. package/dist/commands/agentCommands.js +72 -14
  33. package/dist/commands/capabilityCommands.js +9 -6
  34. package/dist/commands/configCommands.js +20 -20
  35. package/dist/commands/deliveryGuardPreflight.js +2 -2
  36. package/dist/commands/executionAuditCommands.js +24 -24
  37. package/dist/commands/globalRoleCommands.js +1 -1
  38. package/dist/commands/grantCommands.js +4 -4
  39. package/dist/commands/operatorCommands.js +34 -9
  40. package/dist/commands/projectCommands.js +4 -4
  41. package/dist/commands/resourcesCommands.js +2 -2
  42. package/dist/commands/roleConfiguration.js +25 -5
  43. package/dist/commands/roleRuntimeGuard.js +4 -5
  44. package/dist/commands/sessionCommands.js +3 -7
  45. package/dist/commands/taskActivationCommands.js +281 -0
  46. package/dist/commands/taskActor.js +28 -49
  47. package/dist/commands/taskCommands.js +1461 -720
  48. package/dist/commands/taskContextCommand.js +39 -583
  49. package/dist/commands/taskExecutionCommands.js +32 -32
  50. package/dist/commands/taskInputCommands.js +40 -104
  51. package/dist/commands/taskIntegrationCommands.js +3 -2
  52. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  53. package/dist/commands/taskNextActionCommand.js +8 -8
  54. package/dist/commands/taskOverviewCommand.js +33 -45
  55. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  56. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  57. package/dist/commands/telemetryCommands.js +36 -38
  58. package/dist/config/configCatalog.js +4 -4
  59. package/dist/config/yuiConfig.js +8 -8
  60. package/dist/context/contextSnapshot.js +10 -10
  61. package/dist/context/dispatchContext.js +11 -11
  62. package/dist/context/roleSessionContext.js +6 -3
  63. package/dist/context/{turnContextPack.js → runContextPack.js} +158 -81
  64. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  65. package/dist/context/sessionBootstrapManifest.js +21 -2
  66. package/dist/context/sourceRunContext.js +30 -0
  67. package/dist/context/taskContext.js +481 -0
  68. package/dist/context/wakeNotification.js +27 -27
  69. package/dist/controller/agentRuntimeObserver.js +21 -24
  70. package/dist/controller/capabilityBridge.js +17 -6
  71. package/dist/controller/clientRuntime.js +65 -92
  72. package/dist/controller/controller.js +120 -188
  73. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  74. package/dist/controller/jobControl.js +54 -85
  75. package/dist/controller/resourceInventory.js +8 -27
  76. package/dist/controller/resourceInventoryLinux.js +12 -13
  77. package/dist/controller/runtime.js +529 -479
  78. package/dist/controller/runtimeEventInbox.js +55 -25
  79. package/dist/controller/runtimeEventProcessor.js +22 -31
  80. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  81. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  82. package/dist/controller/runtimeObservationHook.js +14 -18
  83. package/dist/controller/sessionNotify.js +16 -24
  84. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  85. package/dist/controller/structuredProviderObservation.js +138 -99
  86. package/dist/coordination/workMailbox.js +3 -3
  87. package/dist/coordination/workMailboxQueue.js +36 -33
  88. package/dist/core/boundedRpc.js +8 -1
  89. package/dist/core/controllerClient.js +20 -1
  90. package/dist/core/controllerServer.js +4 -4
  91. package/dist/doctor/doctor.js +13 -2
  92. package/dist/domain/agentResultTransport.js +9 -9
  93. package/dist/execution/codexThreadNaming.js +2 -8
  94. package/dist/execution/executionHealth.js +51 -63
  95. package/dist/execution/reviewMainRun.js +137 -0
  96. package/dist/execution/workItemExecution.js +28 -29
  97. package/dist/execution/workItemExecutionProjection.js +99 -107
  98. package/dist/execution/workItemMainRun.js +141 -0
  99. package/dist/executor/agentAdapter.js +227 -20
  100. package/dist/executor/agentConfigurationCatalog.js +126 -4
  101. package/dist/executor/agentConfigurationProbe.js +162 -4
  102. package/dist/executor/agentExecutor.js +79 -78
  103. package/dist/executor/effectiveLaunch.js +105 -18
  104. package/dist/executor/executorRegistry.js +29 -44
  105. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  106. package/dist/executor/workspacePreflightClassification.js +16 -16
  107. package/dist/grant/capabilityGrant.js +6 -3
  108. package/dist/input/inputRequest.js +12 -10
  109. package/dist/integration/gitIntegrationService.js +4 -11
  110. package/dist/integration/integrationQueueService.js +4 -4
  111. package/dist/interaction/operatorPresentation.js +1 -1
  112. package/dist/kernel/builtinCapabilities.js +255 -12
  113. package/dist/kernel/capabilityRegistry.js +64 -18
  114. package/dist/kernel/instanceHost.js +12 -1
  115. package/dist/kernel/kernelPorts.js +2 -2
  116. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  117. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  118. package/dist/message/message.js +118 -6
  119. package/dist/message/messageContinuation.js +204 -0
  120. package/dist/observability/executionAudit.js +70 -72
  121. package/dist/observability/faultClassification.js +2 -2
  122. package/dist/observability/orchestrationMetrics.js +8 -8
  123. package/dist/operator/operatorSessionHistory.js +1 -7
  124. package/dist/output/agentConfigurationPresentation.js +8 -3
  125. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  126. package/dist/output/rolePresentation.js +54 -3
  127. package/dist/plugins/pluginChild.js +104 -0
  128. package/dist/plugins/pluginIntent.js +26 -0
  129. package/dist/plugins/pluginInterpreter.js +43 -0
  130. package/dist/plugins/pluginPackage.js +101 -0
  131. package/dist/plugins/pluginProcess.js +112 -0
  132. package/dist/plugins/pluginService.js +388 -0
  133. package/dist/profile/agentProfile.js +1 -1
  134. package/dist/repository/gitWorkspace.js +26 -4
  135. package/dist/repository/project.js +19 -4
  136. package/dist/repository/taskBaseFreshness.js +13 -13
  137. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  138. package/dist/repository/taskWorkspacePreparer.js +344 -83
  139. package/dist/resources/autoResourceGc.js +3 -3
  140. package/dist/resources/liveReferences.js +3 -3
  141. package/dist/resources/projectResource.js +75 -0
  142. package/dist/resources/projectResourceService.js +343 -0
  143. package/dist/resources/resourceDiscovery.js +6 -6
  144. package/dist/resources/resourceGc.js +1 -1
  145. package/dist/resources/resourceRegistrar.js +1 -1
  146. package/dist/resources/resourceTypes.js +1 -1
  147. package/dist/review/deltaRecheck.js +3 -3
  148. package/dist/review/reviewAcceptance.js +16 -16
  149. package/dist/review/reviewDecision.js +7 -7
  150. package/dist/review/reviewRound.js +21 -20
  151. package/dist/review/reviewerAvailability.js +2 -2
  152. package/dist/role/role.js +51 -7
  153. package/dist/role/taskRoleUpdate.js +30 -0
  154. package/dist/runtime/acpProtocol.js +425 -0
  155. package/dist/runtime/acpSession.js +731 -0
  156. package/dist/runtime/acpSessionConfiguration.js +260 -0
  157. package/dist/runtime/agentDriver.js +30 -11
  158. package/dist/runtime/agentEndpoint.js +278 -0
  159. package/dist/runtime/agentEndpointIdentity.js +86 -0
  160. package/dist/runtime/agentEndpointOwnership.js +239 -0
  161. package/dist/runtime/agentError.js +2 -10
  162. package/dist/runtime/agentHost.js +565 -314
  163. package/dist/runtime/agentRunConfiguration.js +258 -0
  164. package/dist/runtime/builtinAgentDrivers.js +134 -18
  165. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  166. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  167. package/dist/runtime/claude-process-owner +0 -0
  168. package/dist/runtime/codexAppServerRuntime.js +38 -30
  169. package/dist/runtime/codexInteractiveHost.js +41 -6
  170. package/dist/runtime/continuationManager.js +2 -6
  171. package/dist/runtime/executionEnvironment.js +30 -0
  172. package/dist/runtime/firstProgressAdvisory.js +11 -11
  173. package/dist/runtime/index.js +4 -3
  174. package/dist/runtime/jsonLineChannel.js +109 -0
  175. package/dist/runtime/launchBroker.js +91 -16
  176. package/dist/runtime/launchDiagnostics.js +2 -2
  177. package/dist/runtime/lifecycleReservation.js +10 -18
  178. package/dist/runtime/managedCaller.js +61 -17
  179. package/dist/runtime/nativeSessionControl.js +102 -0
  180. package/dist/runtime/ports.js +6 -21
  181. package/dist/runtime/processExitObservation.js +8 -7
  182. package/dist/runtime/promptEnvelope.js +17 -6
  183. package/dist/runtime/providerContinuation.js +3 -9
  184. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  185. package/dist/runtime/providerControl.js +2 -7
  186. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  187. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  188. package/dist/runtime/runtimeBinding.js +0 -1
  189. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  190. package/dist/runtime/runtimeDeadlines.js +9 -0
  191. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  192. package/dist/runtime/runtimeObservation.js +29 -65
  193. package/dist/runtime/runtimeProjection.js +43 -51
  194. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  195. package/dist/runtime/sessionLaunchRequest.js +3 -7
  196. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  197. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  198. package/dist/runtime/sessionReconciliation.js +4 -8
  199. package/dist/runtime/sessionTerminationGuard.js +70 -259
  200. package/dist/runtime/sessionTokenMetrics.js +5 -16
  201. package/dist/runtime/structuredProviderHost.js +237 -117
  202. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  203. package/dist/runtime/tmuxAdapters.js +39 -86
  204. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  205. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  206. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  207. package/dist/scheduler/ports.js +80 -9
  208. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  209. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  210. package/dist/scheduler/taskExecutionProjection.js +120 -124
  211. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  212. package/dist/scheduler/taskWake.js +11 -4
  213. package/dist/scheduler/wakeReason.js +9 -1
  214. package/dist/setup/setupCommand.js +3 -7
  215. package/dist/storage/migrations/agentRunContract.js +159 -0
  216. package/dist/storage/migrations/artifactsToGit.js +338 -0
  217. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  218. package/dist/storage/migrations/submitIntent.js +126 -0
  219. package/dist/storage/sqliteSchema.js +467 -7
  220. package/dist/storage/sqliteStore.js +355 -220
  221. package/dist/storage/storageVersions.js +1 -1
  222. package/dist/storage/storeRpc.js +10 -5
  223. package/dist/storage/taskStore.js +13 -11
  224. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  225. package/dist/surface/surfaceContributions.js +102 -0
  226. package/dist/task/completionReadiness.js +32 -6
  227. package/dist/task/deliveryGuard.js +16 -16
  228. package/dist/task/draftPlan.js +72 -12
  229. package/dist/task/nextAction.js +144 -128
  230. package/dist/task/remoteDelivery.js +6 -6
  231. package/dist/task/task.js +184 -18
  232. package/dist/task/taskActivation.js +327 -0
  233. package/dist/task/taskActivationService.js +408 -0
  234. package/dist/task/taskRecordReference.js +5 -4
  235. package/dist/task/taskRecordRetirement.js +1 -1
  236. package/dist/task/taskSubmission.js +236 -0
  237. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  238. package/dist/telemetry/telemetryConfig.js +14 -14
  239. package/dist/telemetry/telemetryWiring.js +2 -2
  240. package/dist/web/assets/assetManifest.js +2 -0
  241. package/dist/web/assets/client/app.js +121 -20
  242. package/dist/web/assets/client/components.js +87 -54
  243. package/dist/web/assets/client/i18n.js +83 -41
  244. package/dist/web/assets/client/markdown.js +1 -1
  245. package/dist/web/assets/client/taskSurface.js +442 -0
  246. package/dist/web/assets/client/view.js +49 -44
  247. package/dist/web/assets/shell.js +1 -1
  248. package/dist/web/assets/styles/cards.js +22 -4
  249. package/dist/web/controllerWeb.js +60 -0
  250. package/dist/web/webMutation.js +28 -0
  251. package/dist/web/webServer.js +133 -8
  252. package/dist/web/webSnapshot.js +81 -74
  253. package/dist/web/webTaskSurface.js +64 -0
  254. package/dist/workItem/dependencyGate.js +1 -1
  255. package/dist/workItem/workItem.js +84 -48
  256. package/dist/workspace/workItemChangeSetManager.js +16 -9
  257. package/docs/agent-result-consumption.md +96 -0
  258. package/docs/agent-result-consumption.zh-CN.md +81 -0
  259. package/docs/agent-runtime-drivers.md +93 -0
  260. package/docs/agent-runtime-drivers.zh-CN.md +77 -0
  261. package/docs/architecture/README.md +50 -0
  262. package/docs/architecture/README.zh-CN.md +43 -0
  263. package/docs/architecture/capabilities-and-resources.md +118 -0
  264. package/docs/architecture/capabilities-and-resources.zh-CN.md +83 -0
  265. package/docs/managed-turn-and-session-runtime.md +224 -0
  266. package/docs/managed-turn-and-session-runtime.zh-CN.md +180 -0
  267. package/docs/observability/README.md +83 -0
  268. package/docs/observability/README.zh-CN.md +71 -0
  269. package/docs/plugin-sdk.md +393 -0
  270. package/docs/plugin-sdk.zh-CN.md +293 -0
  271. package/docs/provider-runtime.md +165 -0
  272. package/docs/provider-runtime.zh-CN.md +132 -0
  273. package/docs/release-workflow.md +305 -0
  274. package/docs/release-workflow.zh-CN.md +237 -0
  275. package/docs/roles-and-configuration.md +115 -0
  276. package/docs/roles-and-configuration.zh-CN.md +96 -0
  277. package/docs/sqlite-control-plane-design.md +78 -0
  278. package/docs/sqlite-control-plane-design.zh-CN.md +62 -0
  279. package/docs/task-dag-semantics.md +80 -0
  280. package/docs/task-dag-semantics.zh-CN.md +59 -0
  281. package/docs/task-delivery.md +105 -0
  282. package/docs/task-delivery.zh-CN.md +82 -0
  283. package/docs/task-local-identity.md +8 -6
  284. package/docs/task-local-identity.zh-CN.md +58 -0
  285. package/docs/testing/verification-levels.md +88 -0
  286. package/docs/testing/verification-levels.zh-CN.md +69 -0
  287. package/i18n/README.zh-CN.md +270 -722
  288. package/package.json +3 -2
  289. package/skills/yui-leader/SKILL.md +88 -304
  290. package/skills/yui-leader/references/execution.md +303 -0
  291. package/skills/yui-leader/references/integration.md +39 -0
  292. package/skills/yui-leader/references/planning.md +109 -0
  293. package/skills/yui-leader/references/replicated-execution.md +42 -0
  294. package/skills/yui-leader/references/task-plugins.md +37 -0
  295. package/skills/yui-operator/SKILL.md +46 -62
  296. package/skills/yui-reviewer/SKILL.md +35 -36
  297. package/skills/yui-runtime/SKILL.md +88 -24
  298. package/skills/yui-runtime/references/publication.md +22 -0
  299. package/skills/yui-runtime/references/recovery.md +64 -0
  300. package/skills/yui-worker/SKILL.md +37 -39
  301. package/dist/cli/roleOptionCatalog.js +0 -68
  302. package/dist/context/sourceTurnContext.js +0 -30
  303. package/dist/execution/reviewMainTurn.js +0 -161
  304. package/dist/execution/workItemMainTurn.js +0 -164
  305. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  306. package/dist/runtime/preallocatedNativeSession.js +0 -13
  307. package/dist/runtime/runtimeStopReceipt.js +0 -42
  308. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -47,9 +47,10 @@ const state = {
47
47
  detail: null,
48
48
  detailKey: null
49
49
  };
50
- const VALID_FILTERS = ["all", "active", "draft", "completed", "retired", "archived"];
50
+ const VALID_FILTERS = ["all", "active", "draft", "completed", "cancelled", "archived"];
51
51
  let terminalSession = null;
52
52
  let terminalStateKey = "terminal.closed";
53
+ const submittedRequests = new Set();
53
54
 
54
55
  const i18n = createI18n(elements.locale);
55
56
  createThemeController(elements.theme);
@@ -125,7 +126,19 @@ function syncUrlFromState(options) {
125
126
  function detailActions() {
126
127
  return {
127
128
  answerInput: answerInput,
128
- openTerminal: openTerminal
129
+ openTerminal: openTerminal,
130
+ inspect: inspectRecord,
131
+ sendMessage: (taskId, body, requestId, intent) => submitMutation(taskId + "/messages",
132
+ "/api/tasks/" + encodeURIComponent(taskId) + "/messages",
133
+ { body, requestId, ...(intent === undefined ? {} : { intent }) }),
134
+ updateTask: (taskId, patch, requestId) => submitMutation(taskId + "/metadata",
135
+ "/api/tasks/" + encodeURIComponent(taskId) + "/metadata", { patch, requestId }),
136
+ panels: (taskId) => requestJson("/api/tasks/" + encodeURIComponent(taskId) + "/panels",
137
+ { signal: AbortSignal.timeout(3000) }),
138
+ readPanel: (taskId, ref, input) => requestJson("/api/tasks/" + encodeURIComponent(taskId) + "/panels", {
139
+ method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ ref, input }),
140
+ signal: AbortSignal.timeout(3000)
141
+ })
129
142
  };
130
143
  }
131
144
 
@@ -171,9 +184,30 @@ function detailKeyOf(detail) {
171
184
  return text.length + ":" + hash;
172
185
  }
173
186
 
187
+ // The optional observation arrives after the core snapshot has already been
188
+ // rendered, so the render key has to cover the observed facts the detail
189
+ // actually draws or they would stay stale until the core snapshot changed.
190
+ // Only the rendered identity is included: observation timestamps advance on
191
+ // every poll and would re-render the detail continuously.
192
+ function runtimeSignatureOf(detail) {
193
+ const roles = (detail.runtime && detail.runtime.roles) || [];
194
+ return detail.runtimeStatus + "|" + roles.map(function (role) {
195
+ const session = role.runtimeSession;
196
+ return role.name + ":" + (session
197
+ ? session.nativeSessionId + "/" + session.status
198
+ : "-");
199
+ }).join(",");
200
+ }
201
+
174
202
  function renderCurrentDetail(force) {
175
203
  if (state.detail) {
176
- const key = i18n.getLocale() + "|" + state.detailKey;
204
+ // Polling must not replace an unsent draft or an in-flight form, including
205
+ // after the user has moved focus. This is view state, never Task state.
206
+ if (elements.detail.dataset.taskId === state.detail.task.id
207
+ && (elements.detail.querySelector('[data-unsent="true"]')
208
+ || elements.detail.contains(document.activeElement))) return;
209
+ const key = i18n.getLocale() + "|" + state.detailKey
210
+ + "|" + runtimeSignatureOf(state.detail);
177
211
  if (!force && key === renderedDetailKey) return;
178
212
  renderTaskDetail(
179
213
  elements.detail,
@@ -275,31 +309,76 @@ async function requestJson(path, options) {
275
309
  ...options,
276
310
  headers: {
277
311
  accept: "application/json",
312
+ "x-yui-web-token": token,
278
313
  ...(options && options.headers ? options.headers : {})
279
314
  }
280
315
  });
281
316
  if (!response.ok) {
282
317
  let message = "HTTP " + response.status;
318
+ let disposition = "unknown";
283
319
  try {
284
320
  const body = await response.json();
285
321
  if (body && body.error) message = body.error;
322
+ if (body && body.disposition === "not-submitted") disposition = "not-submitted";
286
323
  } catch {}
287
- throw new Error(message);
324
+ throw Object.assign(new Error(message), { disposition });
288
325
  }
289
326
  return response.json();
290
327
  }
291
328
 
329
+ async function submitMutation(key, path, body) {
330
+ if (submittedRequests.has(key)) throw new Error("An earlier submission is unresolved; read current facts.");
331
+ submittedRequests.add(key);
332
+ try {
333
+ const receipt = await requestJson(path, {
334
+ method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body)
335
+ });
336
+ submittedRequests.delete(key);
337
+ return receipt;
338
+ } catch (error) {
339
+ if (error.disposition === "not-submitted") submittedRequests.delete(key);
340
+ throw error;
341
+ }
342
+ }
343
+
292
344
  async function loadTaskDetail(taskId, showLoading) {
293
345
  if (showLoading) {
294
346
  renderLoading(elements.detail, i18n.t, "loading.detail");
295
347
  elements.mainCol.scrollTop = 0;
296
348
  }
297
349
  const savedScrollTop = showLoading ? 0 : elements.mainCol.scrollTop;
298
- const detail = await requestJson("/api/tasks/" + encodeURIComponent(taskId));
350
+ const base = "/api/tasks/" + encodeURIComponent(taskId);
351
+ // Rendering consumes the current snapshot, not event pages. Reconnect uses
352
+ // this same read; the independent delta API retains its fixed-bound contract.
353
+ const core = await requestJson(base + "/context");
354
+ const taskEntry = core.records.find(function (entry) { return entry.ref.store === "task"; });
355
+ if (!taskEntry) throw new Error("Task reference unavailable.");
356
+ const task = taskEntry.omitted ? (await inspectRecord(taskId, taskEntry.ref)).value : taskEntry.value;
357
+ const previous = state.detail && state.detail.task.id === taskId ? state.detail : null;
358
+ const detail = {
359
+ task, core,
360
+ runtime: previous && previous.runtime,
361
+ runtimeStatus: previous ? previous.runtimeStatus : "waiting",
362
+ runtimeObservedAt: previous ? previous.runtimeObservedAt : new Date().toISOString()
363
+ };
299
364
  if (state.selected !== taskId) return;
300
365
  state.detail = detail;
301
- state.detailKey = detailKeyOf(detail);
302
- renderCurrentDetail(true);
366
+ state.detailKey = detailKeyOf(core);
367
+ renderCurrentDetail();
368
+ // Optional observation does not participate in core readiness or cursor.
369
+ void requestJson(base, { signal: AbortSignal.timeout(1000) }).then(function (runtime) {
370
+ if (state.detail !== detail) return;
371
+ detail.runtime = runtime;
372
+ detail.runtimeStatus = "available";
373
+ detail.runtimeObservedAt = new Date().toISOString();
374
+ updateRuntimePanel(detail);
375
+ }).catch(function () {
376
+ if (state.detail !== detail) return;
377
+ detail.runtime = null;
378
+ detail.runtimeStatus = "unavailable";
379
+ detail.runtimeObservedAt = new Date().toISOString();
380
+ updateRuntimePanel(detail);
381
+ });
303
382
  // Reveal the tab bar before measuring/scroll so anchors land correctly.
304
383
  setDetailActive(true);
305
384
  updateStickyOffsets();
@@ -314,6 +393,25 @@ async function loadTaskDetail(taskId, showLoading) {
314
393
  syncTabHighlight();
315
394
  }
316
395
 
396
+ function updateRuntimePanel(detail) {
397
+ const status = elements.detail.querySelector("[data-runtime-status]");
398
+ const value = elements.detail.querySelector("[data-runtime-value]");
399
+ if (status) status.textContent = detail.runtimeStatus + " · " + detail.runtimeObservedAt;
400
+ if (value) value.textContent = detail.runtime
401
+ ? JSON.stringify({ roles: detail.runtime.roles, runtimeHealth: detail.runtime.runtimeHealth }, null, 2)
402
+ : "";
403
+ // Patching the panel in place is not enough: the observation also feeds facts
404
+ // the detail rendered before it arrived. Re-render when the observed identity
405
+ // has actually changed — renderCurrentDetail still compares the render key,
406
+ // so an unchanged observation costs nothing and an unsent form is preserved.
407
+ if (state.detail === detail) renderCurrentDetail();
408
+ }
409
+
410
+ async function inspectRecord(taskId, ref) {
411
+ const query = new URLSearchParams({ store: ref.store, ref: ref.refId, digest: ref.digest });
412
+ return requestJson("/api/tasks/" + encodeURIComponent(taskId) + "/inspect?" + query);
413
+ }
414
+
317
415
  async function selectTask(taskId) {
318
416
  // Switching tasks drops the previous section; staying on the same task
319
417
  // (URL-driven loads, popstate) keeps it.
@@ -345,27 +443,27 @@ async function selectTask(taskId) {
345
443
  async function answerInput(input, answer) {
346
444
  if (!state.detail) return;
347
445
  const taskId = state.detail.task.id;
446
+ const key = taskId + "/input/" + input.id;
348
447
  try {
349
- await requestJson(
448
+ await submitMutation(key,
350
449
  "/api/tasks/" + encodeURIComponent(taskId)
351
450
  + "/inputs/" + encodeURIComponent(input.id) + "/answer",
352
- {
353
- method: "POST",
354
- headers: {
355
- "content-type": "application/json",
356
- "x-yui-web-token": token
357
- },
358
- body: JSON.stringify(answer)
359
- }
451
+ answer
360
452
  );
361
453
  showToast(i18n.t("input.answered"));
454
+ submittedRequests.delete(key);
362
455
  await refreshDashboard({ quiet: true });
363
- } catch {
364
- showToast(i18n.t("errors.answer"));
456
+ } catch (error) {
457
+ showToast(error.disposition === "not-submitted" ? error.message : i18n.getLocale().startsWith("zh")
458
+ ? "回答结果未知;请刷新检查原问题,不要盲目重发。"
459
+ : "Answer outcome unknown; refresh the original question before resubmitting.");
365
460
  }
366
461
  }
367
462
 
463
+ let refreshing = false;
368
464
  async function refreshDashboard(options) {
465
+ if (refreshing) return;
466
+ refreshing = true;
369
467
  const quiet = options && options.quiet;
370
468
  if (!quiet) {
371
469
  elements.refresh.disabled = true;
@@ -386,8 +484,10 @@ async function refreshDashboard(options) {
386
484
  if (previousInputs !== null && dashboard.counts.openInputs > previousInputs) {
387
485
  showToast(i18n.t("input.new"));
388
486
  }
389
- if (state.selected && !quiet) {
390
- try { await loadTaskDetail(state.selected, false); } catch {}
487
+ if (state.selected) {
488
+ try { await loadTaskDetail(state.selected, false); } catch {
489
+ showToast(i18n.getLocale().startsWith("zh") ? "连接不可用;保留上次读取。" : "Disconnected; showing the last read.");
490
+ }
391
491
  }
392
492
  } catch {
393
493
  if (!quiet) {
@@ -395,6 +495,7 @@ async function refreshDashboard(options) {
395
495
  showToast(i18n.t("errors.dashboard"));
396
496
  }
397
497
  } finally {
498
+ refreshing = false;
398
499
  if (!quiet) elements.refresh.disabled = false;
399
500
  }
400
501
  }
@@ -1,3 +1,5 @@
1
+ import { AGENT_ADAPTER_CATALOG } from "../../../agent/adapterCatalog.js";
2
+ import { AGENT_EXECUTION_COMPONENT_CATALOG } from "../../../agent/executionComponents.js";
1
3
  export const COMPONENTS_SCRIPT = `
2
4
  // Reusable widgets and record cards. Everything here is a pure builder:
3
5
  // data + i18n in, DOM out. Page composition lives in view.js.
@@ -5,15 +7,19 @@ import { node } from "/assets/js/dom.js";
5
7
  import { formatDateTime, relativeTime } from "/assets/js/format.js";
6
8
  import { escapeHtml, inlineMarkdown, renderMarkdown } from "/assets/js/markdown.js";
7
9
 
8
- export function translatedStatus(t, prefix, status) {
9
- return t(prefix + "." + status);
10
- }
11
-
12
- // Mirrors src/agent/adapterCatalog.ts to avoid a runtime import loop.
13
- const adapterLabels = { codex: "Codex", claude: "Claude" };
10
+ // Generated from the server catalogs, not a second product registry.
11
+ const adapterLabels = ${JSON.stringify(Object.fromEntries(AGENT_ADAPTER_CATALOG.map(({ id, label }) => [id, label])))};
12
+ const componentLabels = ${JSON.stringify(Object.fromEntries(AGENT_EXECUTION_COMPONENT_CATALOG.map(({ id, label }) => [id, label])))};
14
13
  function adapterLabel(adapterId) {
15
14
  return adapterLabels[adapterId] || adapterId;
16
15
  }
16
+ function componentLabel(component, adapterId) {
17
+ return component ? componentLabels[component] || component : adapterLabel(adapterId);
18
+ }
19
+
20
+ export function translatedStatus(t, prefix, status) {
21
+ return t(prefix + "." + status);
22
+ }
17
23
 
18
24
  // --- Small widgets -----------------------------------------------------------
19
25
  export function metaItem(label, value) {
@@ -37,7 +43,9 @@ export function chip(text, extraClass) {
37
43
  export function agentBadge(agent) {
38
44
  if (!agent) return null;
39
45
  const badge = node("span", "agent-badge");
40
- if (agent.adapterId) badge.append(chip(adapterLabel(agent.adapterId), "is-adapter"));
46
+ if (agent.adapterId || agent.component) {
47
+ badge.append(chip(componentLabel(agent.component, agent.adapterId), "is-adapter"));
48
+ }
41
49
  if (agent.model) badge.append(chip(agent.model));
42
50
  if (agent.effort) badge.append(chip(agent.effort));
43
51
  return badge.childNodes.length ? badge : null;
@@ -102,6 +110,9 @@ const EXEC_STATUS_TONE = {
102
110
  "working": "is-active",
103
111
  "completed": "is-muted",
104
112
  "retired": "is-muted",
113
+ "cancelled": "is-muted",
114
+ "accepted": "is-muted",
115
+ "open": "is-active",
105
116
  "archived": "is-muted"
106
117
  };
107
118
 
@@ -114,9 +125,9 @@ export function executionBand(projection, t, locale) {
114
125
  head.append(pill(t, "exec.status", projection.status));
115
126
  head.append(node("span", "exec-band-owner",
116
127
  t("exec.owner." + projection.owner) + " · " + t("exec.action." + projection.action)));
117
- if (projection.activeTurns && projection.activeTurns.length > 0) {
128
+ if (projection.activeRuns && projection.activeRuns.length > 0) {
118
129
  head.append(node("span", "exec-band-executors",
119
- projection.activeTurns.length + " " + t("exec.executors")));
130
+ projection.activeRuns.length + " " + t("exec.executors")));
120
131
  }
121
132
  if (projection.monitoring === "stopped") {
122
133
  head.append(node("span", "exec-band-stopped", t("exec.monitoring.stopped")));
@@ -176,9 +187,10 @@ export function workItemExecutionCard(projection, t) {
176
187
  row.append(statusDot(lane.status));
177
188
  row.append(node("span", "lane-role", lane.roleName));
178
189
  row.append(node("span", "lane-status", t("workExec.lane." + lane.status)));
179
- row.append(node("span", "mono", lane.currentTurnId || t("detail.unobserved")));
180
- if (lane.retryTurnId) row.append(chip(t("workExec.retry") + " · " + lane.retryTurnId, "is-danger"));
181
- if (lane.settleTurnId) row.append(chip(t("workExec.settle") + " · " + lane.settleTurnId));
190
+ if (lane.delivery) row.append(chip(t("run.delivery." + lane.delivery)));
191
+ row.append(node("span", "mono", lane.currentRunId || t("detail.unobserved")));
192
+ if (lane.retryRunId) row.append(chip(t("workExec.retry") + " · " + lane.retryRunId, "is-danger"));
193
+ if (lane.settleRunId) row.append(chip(t("workExec.settle") + " · " + lane.settleRunId));
182
194
  if (lane.session === "unobserved") row.append(chip(t("detail.unobserved")));
183
195
  lanes.append(row);
184
196
  });
@@ -187,18 +199,18 @@ export function workItemExecutionCard(projection, t) {
187
199
 
188
200
  const facts = node("div", "record-meta execution-resource-meta");
189
201
  facts.append(node("span", "", t("workExec.main") + " · "
190
- + (projection.mainTurn.turnId || t("detail.unobserved"))
191
- + " [" + t("workExec.mainStatus." + projection.mainTurn.status) + "]"));
202
+ + (projection.mainRun.runId || t("detail.unobserved"))
203
+ + " [" + t("workExec.mainStatus." + projection.mainRun.status) + "]"));
192
204
  facts.append(node("span", "", t("workExec.candidate") + " · "
193
205
  + (projection.candidate.candidateId || t("workExec.none"))
194
206
  + " [" + t("workExec.candidateStatus." + projection.candidate.status) + "]"));
195
207
  card.append(facts);
196
208
 
197
209
  if (projection.candidate.sourceExecutionGroupId) {
198
- const provenance = projection.candidate.successfulLaneTurns.map(function (lane) {
199
- return lane.laneId + " → " + lane.successfulTurnId;
210
+ const provenance = projection.candidate.successfulLaneRuns.map(function (lane) {
211
+ return lane.laneId + " → " + lane.successfulRunId;
200
212
  }).join(", ") || t("detail.unobserved");
201
- card.append(node("p", "muted mono", (projection.candidate.mainTurnId || t("detail.unobserved"))
213
+ card.append(node("p", "muted mono", (projection.candidate.mainRunId || t("detail.unobserved"))
202
214
  + " → " + projection.candidate.sourceExecutionGroupId + " → " + provenance));
203
215
  }
204
216
 
@@ -279,7 +291,7 @@ export function candidateList(candidates, t, locale) {
279
291
  row.append(node("span", "candidate-summary", candidate.summary));
280
292
  const source = candidate.source.type === "direct"
281
293
  ? t("candidate.source.direct")
282
- : t("candidate.source.turn") + " " + candidate.source.turnId;
294
+ : t("candidate.source.run") + " " + candidate.source.runId;
283
295
  row.append(node("span", "candidate-source", source));
284
296
  row.append(node("time", "", formatDateTime(candidate.createdAt, locale)));
285
297
  list.append(row);
@@ -444,7 +456,7 @@ export function inputCard(input, _options, t, locale, actions) {
444
456
  }
445
457
  if (input.requester) {
446
458
  top.append(node("span", "input-requester",
447
- t("detail.requester") + " · " + input.requester.roleName + " / " + input.requester.turnId));
459
+ t("detail.requester") + " · " + input.requester.roleName + " / " + (input.requester.runId ?? input.requester.nativeSessionId)));
448
460
  }
449
461
  card.append(top);
450
462
  card.append(answerActions(input, actions, t));
@@ -455,13 +467,13 @@ export function inputCard(input, _options, t, locale, actions) {
455
467
  export function conclusionMeta(task, t, locale, kind) {
456
468
  const meta = node("div", "conclusion-meta");
457
469
  const actor = kind === "archived" ? task.archivedBy
458
- : kind === "retired" ? task.retiredBy
470
+ : kind === "cancelled" ? task.retiredBy
459
471
  : task.completedBy;
460
472
  const at = kind === "archived" ? task.archivedAt
461
- : kind === "retired" ? task.retiredAt
473
+ : kind === "cancelled" ? task.retiredAt
462
474
  : task.completedAt;
463
475
  const label = kind === "archived" ? t("detail.archivedBy")
464
- : kind === "retired" ? t("detail.retiredBy")
476
+ : kind === "cancelled" ? t("detail.cancelledBy")
465
477
  : t("detail.completedBy");
466
478
  if (actor) meta.append(node("span", "", label + " · " + authorName(t, actor)));
467
479
  if (at) meta.append(node("time", "", formatDateTime(at, locale)));
@@ -471,7 +483,7 @@ export function conclusionMeta(task, t, locale, kind) {
471
483
  // --- Record cards -----------------------------------------------------------------
472
484
  // WorkItems that no longer need action render collapsed; everything actionable
473
485
  // (pending, running, awaiting acceptance, failed) stays expanded.
474
- const WORK_ITEM_OPEN_STATUSES = ["pending", "running", "awaiting_acceptance", "failed"];
486
+ const WORK_ITEM_OPEN_STATUSES = ["open"];
475
487
 
476
488
  export function workItemCard(item, titles, t, locale, actions, taskId) {
477
489
  const collapsible = WORK_ITEM_OPEN_STATUSES.indexOf(item.status) === -1;
@@ -561,46 +573,51 @@ export function workItemCard(item, titles, t, locale, actions, taskId) {
561
573
  return card;
562
574
  }
563
575
 
564
- export function turnCard(turn, t, locale) {
576
+ export function runCard(run, t, locale) {
565
577
  const card = node("article", "execute-card");
566
- card.dataset.status = turn.status;
578
+ card.dataset.status = run.status;
567
579
 
568
580
  const idRow = node("div", "execute-id");
569
- idRow.append(statusDot(turn.status));
570
- idRow.append(node("span", "role", turn.roleName));
571
- idRow.append(node("span", "", turn.id));
572
- if (turn.workItemId) idRow.append(node("span", "", t("detail.workItem") + " · " + turn.workItemId));
573
- if (turn.purpose) idRow.append(chip(t("turn.purpose." + turn.purpose)));
574
- idRow.append(node("time", "", formatDateTime(turn.result?.completedAt || turn.updatedAt, locale)));
581
+ idRow.append(statusDot(run.status));
582
+ idRow.append(node("span", "role", run.roleName));
583
+ idRow.append(node("span", "", run.id));
584
+ if (run.workItemId) idRow.append(node("span", "", t("detail.workItem") + " · " + run.workItemId));
585
+ if (run.purpose) idRow.append(chip(t("run.purpose." + run.purpose)));
586
+ idRow.append(node("time", "", formatDateTime(run.result?.completedAt || run.updatedAt, locale)));
575
587
  card.append(idRow);
588
+ const observation = node("div", "record-meta");
589
+ observation.append(node("span", "", t("run.recordState") + " · " + t("status." + run.status)));
590
+ observation.append(node("span", "", t("run.delivery") + " · "
591
+ + t("run.delivery." + (run.execution?.delivery || "unobserved"))));
592
+ card.append(observation);
576
593
 
577
- const visibleInput = turn.inputs && turn.inputs.length ? turn.inputs[0].input : null;
594
+ const visibleInput = run.inputs && run.inputs.length ? run.inputs[0].input : null;
578
595
  card.append(richText(t("detail.instruction"), visibleInput?.directive || visibleInput?.action || "-", t, { className: "execute-io", threshold: 320 }));
579
- if (turn.result?.output) {
580
- card.append(richText(t("detail.outcome"), turn.result.output, t, { className: "execute-io outcome", threshold: 320 }));
596
+ if (run.result?.output) {
597
+ card.append(richText(t("detail.outcome"), run.result.output, t, { className: "execute-io outcome", threshold: 320 }));
581
598
  }
582
- if (turn.result?.diagnostic) {
583
- card.append(richText(t("detail.failure"), turn.result.diagnostic, t, { className: "execute-io outcome", threshold: 320 }));
599
+ if (run.result?.diagnostic) {
600
+ card.append(richText(t("detail.failure"), run.result.diagnostic, t, { className: "execute-io outcome", threshold: 320 }));
584
601
  }
585
602
 
586
603
  const foot = node("div", "execute-foot");
587
604
  const tags = node("div", "execute-tags");
588
- tags.append(chip(t("mode." + turn.mode)));
589
- if (turn.executionGroupId) {
590
- tags.append(chip(t("detail.lineage") + " · " + turn.executionGroupId
591
- + (turn.executionLaneId ? "/" + turn.executionLaneId : "")));
605
+ tags.append(chip(t("mode." + run.mode)));
606
+ if (run.executionGroupId) {
607
+ tags.append(chip(t("detail.lineage") + " · " + run.executionGroupId
608
+ + (run.executionLaneId ? "/" + run.executionLaneId : "")));
592
609
  }
593
- const badge = turn.effective ? agentBadge(turn.effective) : (turn.agentId ? agentBadge(turn) : null);
610
+ const badge = run.effective ? agentBadge(run.effective) : (run.agentId ? agentBadge(run) : null);
594
611
  if (badge) tags.append(badge);
595
612
  foot.append(tags);
596
- foot.append(pill(t, "turn", turn.status));
613
+ foot.append(pill(t, "run", run.status));
597
614
  card.append(foot);
598
615
 
599
- if (turn.effective) {
616
+ if (run.effective) {
600
617
  const eff = node("div", "record-meta");
601
- eff.append(node("span", "", t("detail.effective") + " · r" + turn.effective.sourceDesiredRevision));
602
- eff.append(node("span", "", t("detail.profileIntent") + " · " + turn.effective.profileAccess));
603
- eff.append(node("span", "", t("detail.permission") + " · " + turn.effective.permission.strategy));
618
+ eff.append(node("span", "", t("detail.effective") + " · r" + run.effective.sourceDesiredRevision));
619
+ eff.append(node("span", "", t("detail.profileIntent") + " · " + run.effective.profileAccess));
620
+ eff.append(node("span", "", t("detail.permission") + " · " + run.effective.permission.strategy));
604
621
  card.append(eff);
605
622
  }
606
623
 
@@ -631,8 +648,8 @@ export function reviewCard(round, t, locale) {
631
648
  if (round.workspace && round.workspace.root) {
632
649
  meta.append(pathMetaItem(t("detail.workspace"), round.workspace.root));
633
650
  }
634
- if (round.reviewerTurnId) {
635
- meta.append(node("span", "mono", t("detail.reviewerTurn") + " · " + round.reviewerTurnId));
651
+ if (round.reviewerRunId) {
652
+ meta.append(node("span", "mono", t("detail.reviewerRun") + " · " + round.reviewerRunId));
636
653
  }
637
654
  if (round.workspaceDisposition) {
638
655
  meta.append(node("span", "", t("detail.workspaceDisposition") + " · "
@@ -647,7 +664,7 @@ export function reviewCard(round, t, locale) {
647
664
  groupMeta.append(node("span", "", t("reviewExec.assignment") + " · "
648
665
  + group.assignment.contextSnapshotRef.id));
649
666
  groupMeta.append(node("span", "", t("reviewExec.main") + " · "
650
- + (round.reviewerTurnId || t("detail.unobserved"))));
667
+ + (round.reviewerRunId || t("detail.unobserved"))));
651
668
  execution.append(groupMeta);
652
669
  const lanes = node("div", "lane-list");
653
670
  group.lanes.forEach(function (lane) {
@@ -656,7 +673,7 @@ export function reviewCard(round, t, locale) {
656
673
  row.append(node("span", "lane-role", t("reviewExec.producer") + " " + lane.ordinal
657
674
  + " · " + lane.roleName));
658
675
  row.append(node("span", "lane-status", t("reviewExec.status." + lane.disposition)));
659
- row.append(node("span", "mono", lane.currentTurnId || t("detail.unobserved")));
676
+ row.append(node("span", "mono", lane.currentRunId || t("detail.unobserved")));
660
677
  lanes.append(row);
661
678
  });
662
679
  execution.append(lanes);
@@ -694,6 +711,7 @@ export function roleCard(role, task, t, locale, actions) {
694
711
  if (activeBinding) {
695
712
  const badge = agentBadge({
696
713
  adapterId: activeBinding.adapterId,
714
+ component: activeBinding.component,
697
715
  model: activeBinding.config && activeBinding.config.model,
698
716
  effort: activeBinding.config && activeBinding.config.effort
699
717
  });
@@ -745,10 +763,10 @@ export function roleCard(role, task, t, locale, actions) {
745
763
  const bindings = bindingIds.map(function (id) {
746
764
  const binding = role.agentBindings[id];
747
765
  const model = binding.config && binding.config.model;
748
- return adapterLabel(binding.adapterId) + (model ? " · " + model : "");
766
+ return componentLabel(binding.component, binding.adapterId) + (model ? " · " + model : "");
749
767
  });
750
768
  const activeBindingLabel = activeBinding
751
- ? adapterLabel(activeBinding.adapterId) + (activeBinding.config && activeBinding.config.model ? " · " + activeBinding.config.model : "")
769
+ ? componentLabel(activeBinding.component, activeBinding.adapterId) + (activeBinding.config && activeBinding.config.model ? " · " + activeBinding.config.model : "")
752
770
  : undefined;
753
771
  cols.append(chipRow(t("detail.agents"), bindings, activeBindingLabel));
754
772
  }
@@ -801,7 +819,7 @@ export function historyEventRow(event, t, locale) {
801
819
  return card;
802
820
  }
803
821
 
804
- export function messageCard(message, t, locale) {
822
+ export function messageCard(message, t, locale, result) {
805
823
  const card = node("article", "record-card");
806
824
  const head = node("div", "record-head");
807
825
  const titleRow = node("div", "record-title-row");
@@ -814,10 +832,25 @@ export function messageCard(message, t, locale) {
814
832
  const meta = node("div", "record-meta");
815
833
  meta.append(node("span", "mono", message.id));
816
834
  meta.append(node("time", "", formatDateTime(message.createdAt, locale)));
817
- if (message.turnId) meta.append(node("span", "mono", message.turnId + (message.workItemId ? " · " + message.workItemId : "")));
835
+ if (message.runId) meta.append(node("span", "mono", message.runId + (message.workItemId ? " · " + message.workItemId : "")));
836
+ if (message.recipient) {
837
+ meta.append(node("span", "mono", "→ " + message.recipient.roleName + " · "
838
+ + (message.recipient.reviewRoundId || message.recipient.workItemId)));
839
+ meta.append(node("span", "mono", !message.recipient.ownerRunId ? t("messageDelivery.notification") : message.continuation?.runId
840
+ ? t("messageDelivery.run") + " " + message.continuation.runId
841
+ : message.continuation?.notDeliveredReason
842
+ ? t("messageDelivery.blocked") + " " + message.continuation.notDeliveredReason
843
+ : t("messageDelivery.pending")));
844
+ }
818
845
  card.append(meta);
819
846
 
820
847
  card.append(richText(null, message.body, t));
848
+ if (message.resultRef) {
849
+ card.append(node("small", "mono", message.resultRef.runId));
850
+ if (result?.output) card.append(richText(t("detail.outcome"), result.output, t));
851
+ if (result?.diagnostic) card.append(richText(t("detail.failure"), result.diagnostic, t));
852
+ if (!result) card.append(node("p", "muted", t("detail.unobserved")));
853
+ }
821
854
  return card;
822
855
  }
823
856