@zq-silk/yui 0.15.6 → 0.15.8

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 (282) hide show
  1. package/ARCHITECTURE.md +192 -399
  2. package/README.md +127 -1149
  3. package/dist/agent/adapterCatalog.js +15 -2
  4. package/dist/agent/agent.js +23 -3
  5. package/dist/agent/argumentPolicy.js +7 -1
  6. package/dist/agent/connectionPlan.js +62 -0
  7. package/dist/agent/executionComponents.js +158 -0
  8. package/dist/agent/launchEnvironment.js +31 -3
  9. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  10. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  11. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  12. package/dist/brief/taskBrief.js +12 -0
  13. package/dist/cli/agentConfigurationPicker.js +13 -0
  14. package/dist/cli/commandCatalog.js +167 -70
  15. package/dist/cli/interactionCandidates.js +5 -5
  16. package/dist/cli/interactionPolicy.js +38 -8
  17. package/dist/cli/invocationRouter.js +1 -1
  18. package/dist/cli/managedDiagnostics.js +28 -0
  19. package/dist/cli/operatorWizard.js +1 -7
  20. package/dist/cli/roleOptionOrder.js +27 -0
  21. package/dist/cli/roleWizard.js +50 -14
  22. package/dist/cli/updateOrchestrator.js +1 -1
  23. package/dist/cli/updatePorts.js +3 -4
  24. package/dist/cli.js +188 -95
  25. package/dist/commands/agentCommands.js +72 -14
  26. package/dist/commands/capabilityCommands.js +57 -0
  27. package/dist/commands/configCommands.js +20 -20
  28. package/dist/commands/deliveryGuardPreflight.js +2 -2
  29. package/dist/commands/executionAuditCommands.js +24 -24
  30. package/dist/commands/globalRoleCommands.js +1 -1
  31. package/dist/commands/grantCommands.js +4 -4
  32. package/dist/commands/operatorCommands.js +1 -7
  33. package/dist/commands/projectCommands.js +4 -4
  34. package/dist/commands/resourcesCommands.js +2 -2
  35. package/dist/commands/roleConfiguration.js +25 -5
  36. package/dist/commands/roleRuntimeGuard.js +4 -5
  37. package/dist/commands/sessionCommands.js +3 -7
  38. package/dist/commands/taskActivationCommands.js +259 -0
  39. package/dist/commands/taskActor.js +28 -49
  40. package/dist/commands/taskCommands.js +1216 -730
  41. package/dist/commands/taskContextCommand.js +39 -583
  42. package/dist/commands/taskExecutionCommands.js +32 -32
  43. package/dist/commands/taskInputCommands.js +40 -104
  44. package/dist/commands/taskIntegrationCommands.js +3 -2
  45. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  46. package/dist/commands/taskNextActionCommand.js +8 -8
  47. package/dist/commands/taskOverviewCommand.js +33 -45
  48. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  49. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  50. package/dist/commands/telemetryCommands.js +36 -38
  51. package/dist/config/configCatalog.js +4 -4
  52. package/dist/config/yuiConfig.js +8 -8
  53. package/dist/context/contextSnapshot.js +10 -10
  54. package/dist/context/dispatchContext.js +11 -11
  55. package/dist/context/roleSessionContext.js +6 -3
  56. package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
  57. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  58. package/dist/context/sessionBootstrapManifest.js +21 -2
  59. package/dist/context/sourceRunContext.js +30 -0
  60. package/dist/context/taskContext.js +458 -0
  61. package/dist/context/wakeNotification.js +27 -27
  62. package/dist/controller/agentRuntimeObserver.js +21 -24
  63. package/dist/controller/capabilityBridge.js +77 -0
  64. package/dist/controller/clientRuntime.js +65 -92
  65. package/dist/controller/controller.js +127 -188
  66. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  67. package/dist/controller/jobControl.js +54 -85
  68. package/dist/controller/resourceInventory.js +8 -27
  69. package/dist/controller/resourceInventoryLinux.js +12 -13
  70. package/dist/controller/runtime.js +530 -476
  71. package/dist/controller/runtimeEventInbox.js +55 -25
  72. package/dist/controller/runtimeEventProcessor.js +22 -31
  73. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  74. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  75. package/dist/controller/runtimeObservationHook.js +14 -18
  76. package/dist/controller/sessionNotify.js +16 -24
  77. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  78. package/dist/controller/structuredProviderObservation.js +138 -99
  79. package/dist/coordination/workMailbox.js +3 -3
  80. package/dist/coordination/workMailboxQueue.js +36 -33
  81. package/dist/core/boundedRpc.js +8 -1
  82. package/dist/core/controllerClient.js +20 -1
  83. package/dist/core/controllerServer.js +4 -4
  84. package/dist/doctor/doctor.js +13 -2
  85. package/dist/domain/agentResultTransport.js +9 -9
  86. package/dist/execution/codexThreadNaming.js +2 -8
  87. package/dist/execution/executionHealth.js +51 -63
  88. package/dist/execution/reviewMainRun.js +137 -0
  89. package/dist/execution/workItemExecution.js +28 -29
  90. package/dist/execution/workItemExecutionProjection.js +99 -107
  91. package/dist/execution/workItemMainRun.js +141 -0
  92. package/dist/executor/agentAdapter.js +227 -20
  93. package/dist/executor/agentConfigurationCatalog.js +126 -4
  94. package/dist/executor/agentConfigurationProbe.js +162 -4
  95. package/dist/executor/agentExecutor.js +79 -78
  96. package/dist/executor/effectiveLaunch.js +105 -18
  97. package/dist/executor/executorRegistry.js +29 -44
  98. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  99. package/dist/executor/workspacePreflightClassification.js +16 -16
  100. package/dist/grant/capabilityGrant.js +6 -3
  101. package/dist/input/inputRequest.js +12 -10
  102. package/dist/integration/gitIntegrationService.js +4 -11
  103. package/dist/integration/integrationQueueService.js +4 -4
  104. package/dist/interaction/operatorPresentation.js +1 -1
  105. package/dist/kernel/builtinCapabilities.js +423 -0
  106. package/dist/kernel/capabilityRegistry.js +314 -0
  107. package/dist/kernel/capabilitySchema.js +91 -0
  108. package/dist/kernel/instanceHost.js +18 -1
  109. package/dist/kernel/kernelPorts.js +4 -1
  110. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  111. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  112. package/dist/message/message.js +62 -6
  113. package/dist/message/messageContinuation.js +204 -0
  114. package/dist/observability/executionAudit.js +70 -72
  115. package/dist/observability/faultClassification.js +2 -2
  116. package/dist/observability/orchestrationMetrics.js +8 -8
  117. package/dist/operator/operatorSessionHistory.js +1 -7
  118. package/dist/output/agentConfigurationPresentation.js +8 -3
  119. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  120. package/dist/output/rolePresentation.js +54 -3
  121. package/dist/plugins/pluginChild.js +104 -0
  122. package/dist/plugins/pluginIntent.js +26 -0
  123. package/dist/plugins/pluginInterpreter.js +43 -0
  124. package/dist/plugins/pluginPackage.js +101 -0
  125. package/dist/plugins/pluginProcess.js +112 -0
  126. package/dist/plugins/pluginService.js +380 -0
  127. package/dist/profile/agentProfile.js +1 -1
  128. package/dist/repository/gitWorkspace.js +26 -4
  129. package/dist/repository/project.js +19 -4
  130. package/dist/repository/taskBaseFreshness.js +13 -13
  131. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  132. package/dist/repository/taskWorkspacePreparer.js +344 -83
  133. package/dist/resources/autoResourceGc.js +3 -3
  134. package/dist/resources/liveReferences.js +3 -3
  135. package/dist/resources/projectResource.js +123 -0
  136. package/dist/resources/projectResourceService.js +421 -0
  137. package/dist/resources/resourceDiscovery.js +6 -6
  138. package/dist/resources/resourceGc.js +1 -1
  139. package/dist/resources/resourceRegistrar.js +1 -1
  140. package/dist/resources/resourceTypes.js +1 -1
  141. package/dist/review/deltaRecheck.js +3 -3
  142. package/dist/review/reviewAcceptance.js +16 -16
  143. package/dist/review/reviewDecision.js +7 -7
  144. package/dist/review/reviewRound.js +21 -20
  145. package/dist/review/reviewerAvailability.js +2 -2
  146. package/dist/role/role.js +51 -7
  147. package/dist/role/taskRoleUpdate.js +30 -0
  148. package/dist/runtime/acpProtocol.js +425 -0
  149. package/dist/runtime/acpSession.js +731 -0
  150. package/dist/runtime/acpSessionConfiguration.js +260 -0
  151. package/dist/runtime/agentDriver.js +30 -11
  152. package/dist/runtime/agentEndpoint.js +278 -0
  153. package/dist/runtime/agentEndpointIdentity.js +86 -0
  154. package/dist/runtime/agentEndpointOwnership.js +239 -0
  155. package/dist/runtime/agentError.js +2 -10
  156. package/dist/runtime/agentHost.js +565 -314
  157. package/dist/runtime/agentRunConfiguration.js +258 -0
  158. package/dist/runtime/builtinAgentDrivers.js +134 -18
  159. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  160. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  161. package/dist/runtime/claude-process-owner +0 -0
  162. package/dist/runtime/codexAppServerRuntime.js +38 -30
  163. package/dist/runtime/codexInteractiveHost.js +41 -6
  164. package/dist/runtime/continuationManager.js +2 -6
  165. package/dist/runtime/executionEnvironment.js +30 -0
  166. package/dist/runtime/firstProgressAdvisory.js +11 -11
  167. package/dist/runtime/index.js +4 -3
  168. package/dist/runtime/jsonLineChannel.js +109 -0
  169. package/dist/runtime/launchBroker.js +91 -16
  170. package/dist/runtime/launchDiagnostics.js +2 -2
  171. package/dist/runtime/lifecycleReservation.js +10 -18
  172. package/dist/runtime/managedCaller.js +61 -17
  173. package/dist/runtime/nativeSessionControl.js +102 -0
  174. package/dist/runtime/ports.js +6 -21
  175. package/dist/runtime/processExitObservation.js +8 -7
  176. package/dist/runtime/promptEnvelope.js +17 -6
  177. package/dist/runtime/providerContinuation.js +3 -9
  178. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  179. package/dist/runtime/providerControl.js +2 -7
  180. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  181. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  182. package/dist/runtime/runtimeBinding.js +0 -1
  183. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  184. package/dist/runtime/runtimeDeadlines.js +9 -0
  185. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  186. package/dist/runtime/runtimeObservation.js +29 -65
  187. package/dist/runtime/runtimeProjection.js +43 -51
  188. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  189. package/dist/runtime/sessionLaunchRequest.js +3 -7
  190. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  191. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  192. package/dist/runtime/sessionReconciliation.js +4 -8
  193. package/dist/runtime/sessionTerminationGuard.js +70 -259
  194. package/dist/runtime/sessionTokenMetrics.js +5 -16
  195. package/dist/runtime/structuredProviderHost.js +237 -117
  196. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  197. package/dist/runtime/tmuxAdapters.js +39 -86
  198. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  199. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  200. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  201. package/dist/scheduler/ports.js +80 -9
  202. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  203. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  204. package/dist/scheduler/taskExecutionProjection.js +120 -124
  205. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  206. package/dist/scheduler/taskWake.js +11 -4
  207. package/dist/scheduler/wakeReason.js +9 -1
  208. package/dist/setup/setupCommand.js +1 -0
  209. package/dist/storage/migrations/agentRunContract.js +159 -0
  210. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  211. package/dist/storage/sqliteSchema.js +431 -5
  212. package/dist/storage/sqliteStore.js +375 -220
  213. package/dist/storage/storageVersions.js +1 -1
  214. package/dist/storage/storeRpc.js +10 -5
  215. package/dist/storage/taskStore.js +13 -11
  216. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  217. package/dist/surface/surfaceContributions.js +102 -0
  218. package/dist/task/completionReadiness.js +32 -6
  219. package/dist/task/deliveryGuard.js +16 -16
  220. package/dist/task/draftPlan.js +72 -12
  221. package/dist/task/nextAction.js +144 -128
  222. package/dist/task/remoteDelivery.js +6 -6
  223. package/dist/task/task.js +184 -18
  224. package/dist/task/taskActivation.js +301 -0
  225. package/dist/task/taskActivationService.js +392 -0
  226. package/dist/task/taskRecordReference.js +5 -4
  227. package/dist/task/taskRecordRetirement.js +1 -1
  228. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  229. package/dist/telemetry/telemetryConfig.js +14 -14
  230. package/dist/telemetry/telemetryWiring.js +2 -2
  231. package/dist/web/assets/assetManifest.js +2 -0
  232. package/dist/web/assets/client/app.js +120 -20
  233. package/dist/web/assets/client/components.js +87 -54
  234. package/dist/web/assets/client/i18n.js +83 -41
  235. package/dist/web/assets/client/markdown.js +1 -1
  236. package/dist/web/assets/client/taskSurface.js +353 -0
  237. package/dist/web/assets/client/view.js +49 -44
  238. package/dist/web/assets/shell.js +1 -1
  239. package/dist/web/assets/styles/cards.js +22 -4
  240. package/dist/web/controllerWeb.js +60 -0
  241. package/dist/web/webMutation.js +28 -0
  242. package/dist/web/webServer.js +118 -8
  243. package/dist/web/webSnapshot.js +81 -74
  244. package/dist/web/webTaskSurface.js +64 -0
  245. package/dist/workItem/dependencyGate.js +1 -1
  246. package/dist/workItem/workItem.js +80 -48
  247. package/dist/workspace/workItemChangeSetManager.js +16 -9
  248. package/docs/agent-result-consumption.md +94 -0
  249. package/docs/agent-runtime-drivers.md +91 -0
  250. package/docs/architecture/README.md +38 -0
  251. package/docs/architecture/capabilities-and-resources.md +79 -0
  252. package/docs/managed-turn-and-session-runtime.md +222 -0
  253. package/docs/observability/README.md +81 -0
  254. package/docs/plugin-sdk.md +290 -0
  255. package/docs/provider-runtime.md +163 -0
  256. package/docs/release-workflow.md +303 -0
  257. package/docs/roles-and-configuration.md +113 -0
  258. package/docs/sqlite-control-plane-design.md +76 -0
  259. package/docs/task-dag-semantics.md +57 -0
  260. package/docs/task-delivery.md +103 -0
  261. package/docs/task-local-identity.md +6 -6
  262. package/docs/testing/verification-levels.md +86 -0
  263. package/i18n/README.zh-CN.md +98 -739
  264. package/package.json +5 -5
  265. package/skills/yui-leader/SKILL.md +130 -103
  266. package/skills/yui-leader/references/integration.md +39 -0
  267. package/skills/yui-leader/references/replicated-execution.md +42 -0
  268. package/skills/yui-leader/references/task-plugins.md +33 -0
  269. package/skills/yui-operator/SKILL.md +30 -59
  270. package/skills/yui-reviewer/SKILL.md +35 -36
  271. package/skills/yui-runtime/SKILL.md +88 -24
  272. package/skills/yui-runtime/references/publication.md +22 -0
  273. package/skills/yui-runtime/references/recovery.md +64 -0
  274. package/skills/yui-worker/SKILL.md +37 -39
  275. package/dist/cli/roleOptionCatalog.js +0 -68
  276. package/dist/context/sourceTurnContext.js +0 -30
  277. package/dist/execution/reviewMainTurn.js +0 -161
  278. package/dist/execution/workItemMainTurn.js +0 -164
  279. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  280. package/dist/runtime/preallocatedNativeSession.js +0 -13
  281. package/dist/runtime/runtimeStopReceipt.js +0 -42
  282. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -0,0 +1,353 @@
1
+ export const TASK_SURFACE_SCRIPT = `
2
+ import { node, clear } from "/assets/js/dom.js";
3
+ import { anchorSection, sectionHead, richText, inputCard, roleCard, runCard, pill } from "/assets/js/components.js";
4
+
5
+ // All business facts below retain their Context reference. Expanded values
6
+ // are current reads, not mutations of a historical Context snapshot.
7
+ export function renderTaskSurface(container, data, t, locale, actions) {
8
+ clear(container);
9
+ const core = data.core;
10
+ const task = data.task;
11
+ container.dataset.taskId = task.id;
12
+ const zh = locale.startsWith("zh");
13
+ const say = (en, cn) => zh ? cn : en;
14
+ const records = (store) => core.records.filter((entry) => entry.ref.store === store);
15
+ const values = (store) => records(store).filter((entry) => !entry.omitted).map((entry) => entry.value);
16
+ const scaffold = node("div", "detail-scaffold task-surface");
17
+ const summary = node("div", "section-body");
18
+ summary.append(node("span", "detail-kicker", task.id), node("h2", "detail-title", task.title), pill(t, "status", task.status));
19
+ const conversation = node("button", "record-open", say("View Leader Session (read-only)", "查看 Leader Session(只读)"));
20
+ conversation.type = "button";
21
+ // A Draft's planning Session is a real Leader Session, so it is viewable once it
22
+ // actually exists. The button is disabled only when there is no Session to show
23
+ // — not because the Task has yet to be activated. The Session lives in the
24
+ // runtime observation, which arrives separately from the Context snapshot.
25
+ const leaderRole = (data.runtime?.roles ?? []).find((role) => role.name === "leader") ?? null;
26
+ const hasLeaderSession = Boolean(leaderRole?.runtimeSession?.nativeSessionId);
27
+ conversation.disabled = task.status === "archived" || !hasLeaderSession;
28
+ conversation.addEventListener("click", () => actions.openTerminal({ scope: "task", taskId: task.id, roleName: "leader" }));
29
+ summary.append(conversation);
30
+ const chat = node("form", "record-card");
31
+ const chatLabel = node("label", "", say("Message to Task Leader", "发送给 Task Leader"));
32
+ const message = node("textarea", "");
33
+ message.required = true;
34
+ message.maxLength = 8000;
35
+ chatLabel.append(message);
36
+ chat.addEventListener("input", () => { chat.dataset.unsent = message.value ? "true" : "false"; });
37
+ const send = node("button", "record-open", say("Send", "发送"));
38
+ send.type = "submit";
39
+ send.disabled = !["active", "draft"].includes(task.status);
40
+ const sent = node("p", "muted", say("Not submitted", "未提交"));
41
+ sent.setAttribute("role", "status");
42
+ chat.append(chatLabel, send, sent);
43
+ chat.addEventListener("submit", async (event) => {
44
+ event.preventDefault();
45
+ if (send.disabled) return;
46
+ send.disabled = true;
47
+ chat.dataset.unsent = "true";
48
+ const requestId = crypto.randomUUID();
49
+ sent.textContent = say("Waiting for receipt · ", "等待回执 · ") + requestId;
50
+ try {
51
+ const receipt = await actions.sendMessage(task.id, message.value, requestId);
52
+ sent.textContent = (receipt.disposition === "queued"
53
+ ? say("Queued for Leader; not proof of execution · ", "已为 Leader 排队,不代表已执行 · ")
54
+ : say("Saved to Task context; this Task's Leader is not waking · ",
55
+ "已保存到 Task 上下文;该 Task 的 Leader 不会被唤醒 · "))
56
+ + receipt.record.id;
57
+ message.value = "";
58
+ chat.dataset.unsent = "false";
59
+ send.disabled = false;
60
+ } catch (error) {
61
+ if (error.disposition === "not-submitted") {
62
+ sent.textContent = say("Not submitted: ", "未提交:") + error.message;
63
+ send.disabled = false;
64
+ return;
65
+ }
66
+ sent.textContent = say("Unknown submission outcome. Reload and inspect saved messages before sending again · ",
67
+ "提交结果未知。请重新加载并检查已保存消息,不要盲目重发 · ") + requestId;
68
+ }
69
+ });
70
+ summary.append(chat);
71
+ // A Draft's Leader conversation IS its planning Turn, so this entry point does
72
+ // reach a Leader. Report the real planning facts the snapshot carries — Turn,
73
+ // Session, environment and the time they were observed — instead of asserting
74
+ // that no runtime exists. When nothing has been dispatched yet, say exactly
75
+ // that rather than implying the entry point is inert.
76
+ if (task.status === "draft") {
77
+ // Planning facts come from the Context snapshot, which is the authoritative
78
+ // durable read and the one that drives re-rendering. The runtime observation
79
+ // is explicitly optional (it may time out and only ever enriches a live
80
+ // Session), so it must not decide whether a planning Turn is reported.
81
+ const turnEntries = records("turn");
82
+ const planningTurns = values("turn").filter((turn) =>
83
+ turn.roleName === "leader" && turn.purpose === "planning");
84
+ // The latest planning Turn, whatever state it reached. A completed planning
85
+ // Turn is still the real observed fact; reporting only active ones would call
86
+ // a Draft that has genuinely talked to its Leader "not dispatched".
87
+ const planningTurn = planningTurns[planningTurns.length - 1] ?? null;
88
+ // A bounded snapshot may withhold Turn values. That is not evidence that no
89
+ // Turn exists, so it is reported as withheld rather than as "not dispatched".
90
+ const withheldTurns = planningTurn === null && turnEntries.some((entry) => entry.omitted);
91
+ const environment = planningTurn?.effective?.executionEnvironment ?? null;
92
+ const liveLeader = (data.runtime?.roles ?? []).find((role) => role.name === "leader") ?? null;
93
+ const planning = node("div", "record-card");
94
+ planning.dataset.planning = planningTurn !== null
95
+ ? planningTurn.status
96
+ : withheldTurns ? "withheld" : "not-dispatched";
97
+ planning.append(node("p", "muted", withheldTurns
98
+ ? say("This bounded snapshot withholds Turn detail; read the Turn directly to see planning state.",
99
+ "当前受限快照未展开 Turn 详情;请直接读取 Turn 查看规划状态。")
100
+ : planningTurn === null
101
+ ? say("No planning Turn has been dispatched yet. Sending a message queues the Leader.",
102
+ "尚未派发 planning Turn;发送消息会为 Leader 排队。")
103
+ : planningTurn.status === "active"
104
+ ? say("Planning Turn is active; messages reach the Task Leader.",
105
+ "planning Turn 正在进行;消息会送达 Task Leader。")
106
+ : say("Planning Turn reached " + planningTurn.status
107
+ + "; messages reach the same Task Leader and queue the next Turn.",
108
+ "planning Turn 已" + planningTurn.status + ";消息仍送达同一 Task Leader 并为下一个 Turn 排队。")));
109
+ const facts = [
110
+ [say("Planning Turn", "planning Turn"), planningTurn
111
+ ? planningTurn.id + " (" + planningTurn.status + ")"
112
+ : withheldTurns ? say("withheld by this snapshot", "本快照未展开") : say("none", "无")],
113
+ [say("Provider terminal", "Provider 终态"),
114
+ planningTurn?.result?.provider?.status ?? say("not reported", "未上报")],
115
+ // The Provider conversation is the durable transport identity of the
116
+ // Session this Turn actually ran on; the live Session, when observed,
117
+ // is reported separately so a stale read is never dressed up as current.
118
+ [say("Provider conversation", "Provider 会话"),
119
+ planningTurn?.result?.provider?.conversationId ?? say("not reported", "未上报")],
120
+ [say("Live Leader Session", "在线 Leader Session"),
121
+ liveLeader?.runtimeSession?.nativeSessionId
122
+ ?? say("not currently running", "当前未在运行")],
123
+ [say("Environment", "环境"), environment?.environmentRef
124
+ ?? say("empty (legal for planning)", "空环境(规划阶段合法)")],
125
+ [say("Turn observed at", "Turn 观测时间"),
126
+ planningTurn?.result?.completedAt ?? planningTurn?.updatedAt ?? say("unknown", "未知")]
127
+ ];
128
+ for (const [label, value] of facts) {
129
+ planning.append(node("p", "muted", label + ": " + value));
130
+ }
131
+ summary.append(planning);
132
+ }
133
+ if (task.description) summary.append(richText(say("Current requirements", "当前要求"), task.description, t));
134
+ if (task.completionSummary) summary.append(richText(t("detail.conclusion"), task.completionSummary, t));
135
+ const edit = node("details", "record-card");
136
+ edit.append(node("summary", "", say("Edit Task title", "修改任务标题")));
137
+ const form = node("form", "record-block");
138
+ const label = node("label", "", say("Title", "标题"));
139
+ const title = node("input", "");
140
+ title.value = task.title;
141
+ title.required = true;
142
+ title.disabled = task.status === "archived";
143
+ title.maxLength = 500;
144
+ label.append(title);
145
+ form.addEventListener("input", () => { form.dataset.unsent = "true"; });
146
+ const save = node("button", "record-open", say("Save", "保存"));
147
+ save.type = "submit";
148
+ save.disabled = task.status === "archived";
149
+ const feedback = node("p", "muted", say("Not submitted", "未提交"));
150
+ feedback.setAttribute("role", "status");
151
+ form.append(label, save, feedback);
152
+ form.addEventListener("submit", async (event) => {
153
+ event.preventDefault();
154
+ if (save.disabled) return;
155
+ save.disabled = true;
156
+ form.dataset.unsent = "true";
157
+ const requestId = crypto.randomUUID();
158
+ feedback.textContent = say("Waiting for save receipt · ", "正在等待保存回执 · ") + requestId;
159
+ try {
160
+ const receipt = await actions.updateTask(task.id, { title: title.value }, requestId);
161
+ feedback.textContent = say("Saved · revision ", "已保存 · revision ") + receipt.revision;
162
+ title.value = receipt.record.title;
163
+ form.dataset.unsent = "false";
164
+ save.disabled = false;
165
+ } catch (error) {
166
+ if (error.disposition === "not-submitted") {
167
+ feedback.textContent = say("Not saved: ", "未保存:") + error.message;
168
+ save.disabled = false;
169
+ return;
170
+ }
171
+ // Local mutation IDs correlate requests; they are not a deduplication
172
+ // ledger. A lost response must never automatically replay a write.
173
+ feedback.textContent = say("Unknown save outcome. Reload this page to read current facts before submitting again. Request: ",
174
+ "保存结果未知。请重新加载页面读取当前事实,再决定是否提交。请求:") + requestId;
175
+ }
176
+ });
177
+ edit.append(form);
178
+ summary.append(edit);
179
+ scaffold.append(anchorSection("detail-top", sectionHead(t("tabs.summary")), summary));
180
+
181
+ function recordCard(entry) {
182
+ const card = node("article", "record-card");
183
+ card.append(node("strong", "", entry.ref.store + " · " + entry.ref.refId));
184
+ const ref = node("small", "muted", say("Revision: ", "版本:") + entry.ref.revision);
185
+ card.append(ref);
186
+ if (!entry.omitted) {
187
+ const value = entry.value;
188
+ const text = value.content || value.summary || value.body || value.objective || value.title || value.leaderSummary;
189
+ if (text) card.append(richText(null, text, t));
190
+ if (value.status) card.append(node("p", "muted", value.status));
191
+ if (value.roleName) card.append(node("p", "muted", value.roleName));
192
+ if (entry.ref.store === "work-item") {
193
+ card.append(node("p", "muted", say("Owner: ", "负责人:") + (value.assignee || "Leader")));
194
+ if (value.acceptance) card.append(richText(say("Acceptance criteria", "验收标准"), value.acceptance.join("\\n"), t));
195
+ }
196
+ if (entry.ref.store === "job") {
197
+ const status = value.status;
198
+ const label = status === "queued" ? say("Queued", "已排队")
199
+ : status === "running" ? say("Waiting for a terminal receipt", "等待明确终态")
200
+ : status === "unknown-needs-attention" ? say("Unknown effect — inspect the original operation; do not resend", "效果未知——检查原操作,不要重发")
201
+ : say("Terminal record", "已记录终态");
202
+ card.append(node("p", "", label + " · " + task.id + "/" + value.id));
203
+ }
204
+ } else card.append(node("p", "muted", entry.summary || say("Value omitted from compact read", "紧凑读取已省略正文")));
205
+ const expand = node("button", "record-open", say("Read full record / source", "读取完整记录/来源"));
206
+ expand.type = "button";
207
+ expand.addEventListener("click", async () => {
208
+ expand.disabled = true;
209
+ try {
210
+ const result = await actions.inspect(task.id, entry.ref);
211
+ const content = node("pre", "surface-json", JSON.stringify(result.value, null, 2));
212
+ card.append(content);
213
+ if (result.result?.output) card.append(richText(say("Original execution report", "原始执行报告"), result.result.output, t));
214
+ if (result.result?.diagnostic) card.append(richText(say("Execution diagnostic", "执行诊断"), result.result.diagnostic, t));
215
+ if (result.execution) card.append(node("pre", "surface-json", JSON.stringify(result.execution, null, 2)));
216
+ expand.remove();
217
+ } catch {
218
+ expand.disabled = false;
219
+ expand.textContent = say("Record changed or unavailable; refresh context", "记录已变化或不可用;请刷新 Context");
220
+ }
221
+ });
222
+ card.append(expand);
223
+ return card;
224
+ }
225
+ function section(id, name, entries) {
226
+ const body = node("div", "section-body");
227
+ entries.forEach((entry) => body.append(recordCard(entry)));
228
+ if (!entries.length) body.append(node("p", "muted", core.omitted.records
229
+ ? say("Not included in this compact read", "此紧凑读取未包含该记录")
230
+ : say("No records", "暂无记录")));
231
+ scaffold.append(anchorSection(id, sectionHead(name, { count: entries.length }), body));
232
+ return body;
233
+ }
234
+ const focus = section("detail-focus", say("Goal and current focus", "目标与当前关注"), records("task-brief"));
235
+ const brief = values("task-brief")[0];
236
+ if (brief) {
237
+ focus.prepend(richText(t("detail.focus"), brief.currentFocus, t));
238
+ if (brief.boundaries.length) focus.append(richText(say("Boundaries", "边界"), brief.boundaries.join("\\n"), t));
239
+ }
240
+ const questions = node("div", "section-body");
241
+ records("input-request").filter((entry) => entry.omitted || entry.value.status === "open").forEach((entry) => {
242
+ questions.append(entry.omitted ? recordCard(entry) : inputCard(entry.value, { single: true }, t, locale, actions));
243
+ });
244
+ scaffold.append(anchorSection("detail-attention", sectionHead(t("detail.attention")), questions));
245
+ section("detail-work", say("Responsibilities and acceptance", "工作责任与验收"), records("work-item"));
246
+ section("detail-results", say("Saved results and provenance", "已保存结果与来源"), records("artifact").concat(records("candidate")));
247
+ section("detail-messages", t("detail.messages"), records("task-message"));
248
+ section("detail-history", say("Decisions and milestones", "决策与里程碑"), records("task-decision").concat(records("task-milestone")));
249
+ section("detail-reviews", t("detail.reviews"), records("review-round"));
250
+ const roles = node("div", "section-body");
251
+ records("role").forEach((entry) => {
252
+ if (entry.omitted) { roles.append(recordCard(entry)); return; }
253
+ const role = entry.value;
254
+ const active = values("run").find((run) => run.roleName === role.name && run.status === "active");
255
+ const incomplete = core.omitted.records > 0 || records("run").some((entry) => entry.omitted);
256
+ roles.append(roleCard({
257
+ ...role, status: active || incomplete ? "unknown" : "idle",
258
+ effectiveLaunch: active ? active.effective : null,
259
+ launchDrift: active && active.effective.sourceDesiredRevision !== role.launchRevision
260
+ }, task, t, locale, actions));
261
+ if (!active) roles.append(node("p", "muted", say(
262
+ "No active AgentRun in this read; Session activity is a separate observation.",
263
+ "此读取中没有活跃 AgentRun;Session 活动属于独立观察。")));
264
+ });
265
+ scaffold.append(anchorSection("detail-roles", sectionHead(t("detail.roles")), roles));
266
+ const execution = node("details", "record-card");
267
+ execution.append(node("summary", "", say("Execution and observations", "展开执行与观察")));
268
+ execution.append(node("p", "muted", say(
269
+ "An open execution record is not proof of Agent activity. Native admission and Task progress are separate facts.",
270
+ "执行记录未结清不等于 Agent 正在工作;原生准入、运行观察与 Task 进展分别呈现。")));
271
+ records("run").forEach((entry) => execution.append(entry.omitted ? recordCard(entry)
272
+ : runCard({ ...entry.value, execution: entry.execution }, t, locale)));
273
+ (core.observations || []).forEach((observation) => execution.append(node("p", "muted",
274
+ observation.source + " · " + observation.status + " · " + observation.coverage + " · " + observation.observedAt)));
275
+ const runtimeStatus = node("p", "muted", data.runtimeStatus + " · " + data.runtimeObservedAt);
276
+ runtimeStatus.dataset.runtimeStatus = "";
277
+ execution.append(runtimeStatus);
278
+ const raw = node("pre", "surface-json", data.runtime ? JSON.stringify({
279
+ roles: data.runtime.roles, runtimeHealth: data.runtime.runtimeHealth
280
+ }, null, 2) : "");
281
+ raw.dataset.runtimeValue = "";
282
+ execution.append(raw);
283
+ scaffold.append(anchorSection("detail-exec", sectionHead(t("detail.execution")), execution));
284
+ section("detail-operations", say("Original operation facts (no automatic retry)", "原始操作事实(不自动重试)"), records("job"));
285
+ const refs = node("details", "record-card");
286
+ refs.append(node("summary", "", say("Context cursor and omitted references", "Context 游标与省略引用")));
287
+ refs.append(node("pre", "surface-json", JSON.stringify({
288
+ coreCursor: core.coreCursor, throughCursor: core.throughCursor, count: core.count, omitted: core.omitted
289
+ }, null, 2)));
290
+ refs.append(node("p", "muted", say("Reads do not acknowledge messages. Omitted records can be located with the CLI domain query and inspected by reference.",
291
+ "读取不确认消息。被省略的记录可经 CLI 领域查询定位后按引用读取。")));
292
+ scaffold.append(refs);
293
+ const panels = node("details", "record-card");
294
+ panels.append(node("summary", "", say("Capability panels", "能力面板")));
295
+ const panelBody = node("div", "section-body");
296
+ panels.append(panelBody);
297
+ let loadingPanels = false;
298
+ panels.addEventListener("toggle", async () => {
299
+ if (!panels.open || loadingPanels) return;
300
+ loadingPanels = true;
301
+ panelBody.textContent = say("Reading current contributions…", "读取当前贡献…");
302
+ try {
303
+ const current = await actions.panels(task.id);
304
+ clear(panelBody);
305
+ panelBody.append(node("small", "muted", current.observedAt));
306
+ if (!current.panels.length) panelBody.append(node("p", "muted", say("No visible panels", "暂无可见面板")));
307
+ current.panels.forEach((item) => {
308
+ const card = node("article", "record-card");
309
+ const panel = item.panel;
310
+ card.append(node("strong", "", panel.title), node("small", "muted", item.capability + " · " + item.provider.id));
311
+ if (item.unavailable) card.append(node("p", "muted", item.unavailable));
312
+ else if (panel.kind === "text") card.append(node("p", "", panel.text));
313
+ else if (panel.kind === "link") {
314
+ const url = new URL(panel.href);
315
+ if (["http:", "https:"].includes(url.protocol) && !url.username && !url.password) {
316
+ const link = node("a", "", panel.title);
317
+ link.href = url.href;
318
+ link.rel = "noopener noreferrer";
319
+ card.append(link);
320
+ }
321
+ } else if (panel.kind === "data" && panel.renderer === "json") {
322
+ const label = node("label", "", say("Query input (JSON)", "查询输入(JSON)"));
323
+ const input = node("textarea", "");
324
+ input.value = JSON.stringify({ taskId: task.id });
325
+ label.append(input);
326
+ const contract = node("details", "");
327
+ contract.append(node("summary", "", say("Input schema", "输入结构")));
328
+ contract.append(node("pre", "surface-json", JSON.stringify(item.inputSchema, null, 2)));
329
+ const read = node("button", "record-open", say("Read", "读取"));
330
+ read.type = "button";
331
+ const output = node("pre", "surface-json");
332
+ read.addEventListener("click", async () => {
333
+ if (read.disabled) return;
334
+ read.disabled = true;
335
+ try {
336
+ const result = await actions.readPanel(task.id, {
337
+ capability: item.capability, contractVersion: item.contractVersion, provider: item.provider
338
+ }, JSON.parse(input.value));
339
+ output.textContent = JSON.stringify(result, null, 2);
340
+ } catch (error) { output.textContent = say("Panel unavailable: ", "面板不可用:") + error.message; }
341
+ finally { read.disabled = false; }
342
+ });
343
+ card.append(label, contract, read, output);
344
+ }
345
+ panelBody.append(card);
346
+ });
347
+ } catch (error) { panelBody.textContent = say("Panels unavailable: ", "面板不可用:") + error.message; }
348
+ finally { loadingPanels = false; }
349
+ });
350
+ scaffold.append(panels);
351
+ container.append(scaffold);
352
+ }
353
+ `;
@@ -2,6 +2,7 @@ export const VIEW_SCRIPT = `
2
2
  // Page composition: sidebar lists, overview, and the task detail scaffold.
3
3
  // All reusable widgets and cards come from components.js.
4
4
  import { clear, node } from "/assets/js/dom.js";
5
+ import { renderTaskSurface } from "/assets/js/task-surface.js";
5
6
  import { byNewest, formatDateTime } from "/assets/js/format.js";
6
7
  import {
7
8
  anchorSection,
@@ -23,14 +24,14 @@ import {
23
24
  reviewCard,
24
25
  richText,
25
26
  roleCard,
26
- turnCard,
27
+ runCard,
27
28
  sectionHead,
28
29
  taskCard,
29
30
  translatedStatus,
30
31
  workItemCard
31
32
  } from "/assets/js/components.js";
32
33
 
33
- const statuses = ["all", "active", "draft", "completed", "retired", "archived"];
34
+ const statuses = ["all", "active", "draft", "completed", "cancelled", "archived"];
34
35
 
35
36
  export function renderFilters(container, state, t, onFilter) {
36
37
  const counts = state.counts || {};
@@ -83,7 +84,7 @@ function taskGroupOf(task, attentionIds) {
83
84
  if (attentionIds.has(task.id)) return "attention";
84
85
  if (task.status === "active") return "active";
85
86
  if (task.status === "draft") return "draft";
86
- if (task.status === "retired") return "retired";
87
+ if (task.status === "cancelled") return "cancelled";
87
88
  if (task.status === "archived") return "archived";
88
89
  return "finished";
89
90
  }
@@ -102,7 +103,7 @@ export function renderTasks(container, state, t, locale, onSelect) {
102
103
  clear(container);
103
104
  const query = state.query.trim().toLocaleLowerCase(locale);
104
105
  const attentionIds = new Set((state.attention || []).map(function (item) { return item.taskId; }));
105
- const groups = { attention: [], active: [], draft: [], finished: [], retired: [], archived: [] };
106
+ const groups = { attention: [], active: [], draft: [], finished: [], cancelled: [], archived: [] };
106
107
  (state.tasks || []).forEach(function (task) {
107
108
  if (state.filter !== "all" && task.status !== state.filter) return;
108
109
  if (!taskMatchesQuery(task, query, locale)) return;
@@ -114,7 +115,7 @@ export function renderTasks(container, state, t, locale, onSelect) {
114
115
  ["active", t("group.active")],
115
116
  ["draft", t("group.draft")],
116
117
  ["finished", t("group.finished")],
117
- ["retired", t("group.retired")],
118
+ ["cancelled", t("group.cancelled")],
118
119
  ["archived", t("group.archived")]
119
120
  ];
120
121
  const firstGroup = order.find(function (entry) { return groups[entry[0]].length > 0; });
@@ -182,7 +183,7 @@ export function renderOverview(detail, state, t, locale, onSelect) {
182
183
  wrap.append(inbox);
183
184
 
184
185
  // Tasks whose Task-first projection says they need attention: blocked,
185
- // recovering, or in an attention state. This replaces the raw stalled-Turn
186
+ // recovering, or in an attention state. This replaces the raw stalled-AgentRun
186
187
  // count with the derived execution status.
187
188
  const attentionTasks = (state.tasks || []).filter(function (task) {
188
189
  const status = task.executionStatus;
@@ -232,23 +233,23 @@ export function renderError(container, message) {
232
233
  container.append(node("div", "error", message));
233
234
  }
234
235
 
235
- const TURN_PAGE_SIZE = 12;
236
+ const RUN_PAGE_SIZE = 12;
236
237
  const LIST_PAGE_SIZE = 10;
237
- const TURN_FILTERS = ["all", "active", "completed", "failed"];
238
+ const RUN_FILTERS = ["all", "active", "completed", "failed"];
238
239
 
239
- function turnFilterRow(sortedTurns, t, onChange) {
240
- const row = node("div", "filter-row turn-filter");
240
+ function runFilterRow(sortedRuns, t, onChange) {
241
+ const row = node("div", "filter-row run-filter");
241
242
  const buttons = [];
242
- TURN_FILTERS.forEach(function (status) {
243
+ RUN_FILTERS.forEach(function (status) {
243
244
  const count = status === "all"
244
- ? sortedTurns.length
245
- : sortedTurns.filter(function (turn) { return turn.status === status; }).length;
245
+ ? sortedRuns.length
246
+ : sortedRuns.filter(function (run) { return run.status === status; }).length;
246
247
  if (status !== "all" && count === 0) return;
247
248
  const btn = node("button", "filter-chip");
248
249
  btn.type = "button";
249
250
  btn.dataset.status = status;
250
251
  if (status !== "all") btn.append(node("span", "filter-dot " + status));
251
- btn.append(document.createTextNode(status === "all" ? t("status.all") : t("turn." + status)));
252
+ btn.append(document.createTextNode(status === "all" ? t("status.all") : t("run." + status)));
252
253
  btn.append(node("span", "filter-count", String(count)));
253
254
  btn.addEventListener("click", function () { onChange(status); });
254
255
  buttons.push({ status: status, element: btn });
@@ -265,6 +266,7 @@ function turnFilterRow(sortedTurns, t, onChange) {
265
266
  }
266
267
 
267
268
  export function renderTaskDetail(detail, data, t, locale, actions) {
269
+ if (data.core) return renderTaskSurface(detail, data, t, locale, actions);
268
270
  actions = actions || {};
269
271
  clear(detail);
270
272
  const task = data.task;
@@ -346,14 +348,14 @@ export function renderTaskDetail(detail, data, t, locale, actions) {
346
348
  conclusion.append(richText(null, task.completionSummary, t));
347
349
  conclusion.append(conclusionMeta(task, t, locale, "completed"));
348
350
  summaryBody.append(conclusion);
349
- } else if (task.status === "retired" || task.retirementSummary) {
351
+ } else if (task.status === "cancelled" || task.retirementSummary) {
350
352
  const conclusion = node("div", "conclusion archived");
351
- conclusion.append(node("h3", "", t("detail.retired")));
353
+ conclusion.append(node("h3", "", t("detail.cancelled")));
352
354
  if (task.retirementSummary) conclusion.append(richText(null, task.retirementSummary, t));
353
355
  if (task.replacementTaskId) {
354
356
  conclusion.append(node("p", "muted", t("detail.replacement") + " · " + task.replacementTaskId));
355
357
  }
356
- conclusion.append(conclusionMeta(task, t, locale, "retired"));
358
+ conclusion.append(conclusionMeta(task, t, locale, "cancelled"));
357
359
  summaryBody.append(conclusion);
358
360
  } else if (task.status === "archived" || task.archiveSummary || task.archiveReason) {
359
361
  const conclusion = node("div", "conclusion archived");
@@ -377,23 +379,23 @@ export function renderTaskDetail(detail, data, t, locale, actions) {
377
379
  attentionBody));
378
380
  }
379
381
 
380
- const stalledTurns = data.runtimeHealth && data.runtimeHealth.needsAttentionTurns
381
- ? data.runtimeHealth.needsAttentionTurns
382
+ const stalledRuns = data.runtimeHealth && data.runtimeHealth.needsAttentionRuns
383
+ ? data.runtimeHealth.needsAttentionRuns
382
384
  : [];
383
- if (stalledTurns.length) {
385
+ if (stalledRuns.length) {
384
386
  const runtimeHealthBody = node("div", "section-body");
385
- stalledTurns.forEach(function (turn) {
387
+ stalledRuns.forEach(function (run) {
386
388
  const card = node("article", "record-card");
387
389
  card.append(
388
- node("strong", "", turn.roleName + " · " + turn.turnId),
389
- node("p", "record-copy", (turn.kind || "workflow-not-progressing") + " · " + (turn.classification || "truly-stalled")),
390
- node("small", "", t("detail.lastProgress") + " · " + formatDateTime(turn.progressAt, locale))
390
+ node("strong", "", run.roleName + " · " + run.runId),
391
+ node("p", "record-copy", (run.kind || "workflow-not-progressing") + " · " + (run.classification || "truly-stalled")),
392
+ node("small", "", t("detail.lastProgress") + " · " + formatDateTime(run.progressAt, locale))
391
393
  );
392
394
  runtimeHealthBody.append(card);
393
395
  });
394
396
  scaffold.append(anchorSection(
395
397
  "detail-health",
396
- sectionHead(t("detail.runtimeHealth"), { count: stalledTurns.length }),
398
+ sectionHead(t("detail.runtimeHealth"), { count: stalledRuns.length }),
397
399
  runtimeHealthBody
398
400
  ));
399
401
  }
@@ -438,35 +440,35 @@ export function renderTaskDetail(detail, data, t, locale, actions) {
438
440
  sectionHead(t("detail.workItems"), { count: data.workItems.length }),
439
441
  workBody));
440
442
 
441
- // 5. Turns (anchor #detail-exec) — execution grid with status filter + paging
443
+ // 5. AgentRuns (anchor #detail-exec) — execution grid with status filter + paging
442
444
  const execWrap = node("div", "section-body");
443
- const execBody = node("div", "turn-grid");
444
- const sortedTurns = data.turns.slice().sort(byNewest);
445
- if (!sortedTurns.length) {
446
- execBody.append(emptyRow(t, "empty.turns"));
445
+ const execBody = node("div", "run-grid");
446
+ const sortedRuns = data.runs.slice().sort(byNewest);
447
+ if (!sortedRuns.length) {
448
+ execBody.append(emptyRow(t, "empty.runs"));
447
449
  execWrap.append(execBody);
448
450
  } else {
449
- let activeTurnFilter = "all";
450
- const filter = turnFilterRow(sortedTurns, t, function (status) {
451
- activeTurnFilter = status;
451
+ let activeRunFilter = "all";
452
+ const filter = runFilterRow(sortedRuns, t, function (status) {
453
+ activeRunFilter = status;
452
454
  filter.sync(status);
453
- renderTurnGrid();
455
+ renderRunGrid();
454
456
  });
455
- function renderTurnGrid() {
457
+ function renderRunGrid() {
456
458
  clear(execBody);
457
- const visible = activeTurnFilter === "all"
458
- ? sortedTurns
459
- : sortedTurns.filter(function (turn) { return turn.status === activeTurnFilter; });
460
- pagedList(execBody, visible, TURN_PAGE_SIZE, function (turn) {
461
- return turnCard(turn, t, locale);
459
+ const visible = activeRunFilter === "all"
460
+ ? sortedRuns
461
+ : sortedRuns.filter(function (run) { return run.status === activeRunFilter; });
462
+ pagedList(execBody, visible, RUN_PAGE_SIZE, function (run) {
463
+ return runCard(run, t, locale);
462
464
  }, t);
463
465
  }
464
- filter.sync(activeTurnFilter);
466
+ filter.sync(activeRunFilter);
465
467
  execWrap.append(filter.row, execBody);
466
- renderTurnGrid();
468
+ renderRunGrid();
467
469
  }
468
470
  scaffold.append(anchorSection("detail-exec",
469
- sectionHead(t("detail.execution"), { count: data.turns.length }),
471
+ sectionHead(t("detail.execution"), { count: data.runs.length }),
470
472
  execWrap));
471
473
 
472
474
  // 5b. Reviews (anchor #detail-reviews) — review rounds for completed candidates
@@ -522,7 +524,10 @@ export function renderTaskDetail(detail, data, t, locale, actions) {
522
524
  messagesBody.append(emptyRow(t));
523
525
  } else {
524
526
  pagedList(messagesBody, data.messages.slice().sort(byNewest), LIST_PAGE_SIZE, function (message) {
525
- return messageCard(message, t, locale);
527
+ const source = message.resultRef && data.runs.find(function (run) {
528
+ return run.id === message.resultRef.runId && run.roleName === message.author.roleName;
529
+ });
530
+ return messageCard(message, t, locale, source && source.result);
526
531
  }, t);
527
532
  }
528
533
  scaffold.append(anchorSection("detail-messages",
@@ -83,7 +83,7 @@ export const DASHBOARD_HTML = `<!doctype html>
83
83
  <button class="tab" type="button" data-target="detail-top" data-i18n="tabs.summary">Summary</button>
84
84
  <button class="tab" type="button" data-target="detail-focus" data-i18n="tabs.focus">Focus</button>
85
85
  <button class="tab" type="button" data-target="detail-work" data-i18n="tabs.work">Work items</button>
86
- <button class="tab" type="button" data-target="detail-exec" data-i18n="tabs.exec">Turns</button>
86
+ <button class="tab" type="button" data-target="detail-exec" data-i18n="tabs.exec">AgentRuns</button>
87
87
  <button class="tab" type="button" data-target="detail-reviews" data-i18n="tabs.reviews">Reviews</button>
88
88
  <button class="tab" type="button" data-target="detail-roles" data-i18n="tabs.roles">Roles</button>
89
89
  <button class="tab" type="button" data-target="detail-history" data-i18n="tabs.history">History</button>
@@ -1,10 +1,28 @@
1
1
  /*
2
2
  * CARDS — content-bearing surfaces.
3
3
  * Overview blocks, detail head / conclusion band, sections, record cards,
4
- * input cards, Turn cards, criteria lists. Widgets (pills, chips, metrics)
4
+ * input cards, AgentRun cards, criteria lists. Widgets (pills, chips, metrics)
5
5
  * live in widgets.ts; markdown typography lives in markdown.ts.
6
6
  */
7
7
  export const CARD_STYLES = `
8
+ .surface-json { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 36rem; overflow: auto; }
9
+ .task-surface .section-body { display:grid; gap:12px; min-width:0; }
10
+ .task-surface { grid-template-columns:minmax(0,1fr); }
11
+ .task-surface .detail-section,.task-surface .record-card,.task-surface .execute-card { min-width:0; max-width:100%; }
12
+ .task-surface .record-meta { min-width:0; overflow-wrap:anywhere; }
13
+ @media(max-width:620px){
14
+ .task-surface .record-head{flex-wrap:wrap}
15
+ .task-surface .record-title-row{flex-basis:100%}
16
+ }
17
+ .task-surface .section-body > .pill { width:max-content; }
18
+ .task-surface form,.task-surface label { display:grid; gap:10px; min-width:0; }
19
+ .task-surface label { font-size:13px; font-weight:500; }
20
+ .task-surface input,.task-surface textarea { box-sizing:border-box; width:100%; min-width:0; padding:12px; border:1px solid var(--border-strong); border-radius:var(--radius); background:var(--bg-1); color:var(--text); font:inherit; }
21
+ .task-surface textarea { min-height:96px; resize:vertical; }
22
+ .task-surface input:focus-visible,.task-surface textarea:focus-visible,.task-surface summary:focus-visible { outline:2px solid var(--accent); outline-offset:3px; }
23
+ .task-surface button { min-height:40px; }
24
+ .task-surface summary { cursor:pointer; padding:8px 0; font-weight:600; }
25
+ .task-surface [role=status] { overflow-wrap:anywhere; }
8
26
  /* Overview scaffolding */
9
27
  .overview{display:grid;gap:14px;padding-top:2px}
10
28
  .overview-duo{display:grid;grid-template-columns:1fr 1fr;gap:14px;align-items:start}
@@ -109,9 +127,9 @@ details.work-item-card>summary.record-head~*{margin-top:0}
109
127
  .input-answer:hover{border-color:var(--accent-line);color:var(--accent);background:var(--accent-soft)}
110
128
  .input-form{display:flex;gap:8px;flex:1}
111
129
  .input-form input{min-width:0;flex:1;padding:8px 12px;color:var(--text);background:var(--bg-2);border:1px solid var(--border);border-radius:var(--radius);font-family:var(--font-body);font-size:12.5px}
112
- /* Execution Turns */
113
- .turn-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(320px,100%),1fr));gap:9px}
114
- .turn-filter{margin-bottom:3px}
130
+ /* Execution AgentRuns */
131
+ .run-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(320px,100%),1fr));gap:9px}
132
+ .run-filter{margin-bottom:3px}
115
133
  .execute-card{background:var(--bg-2);border:1px solid var(--border);border-left:2px solid var(--border);border-radius:var(--radius);padding:9px 12px;display:grid;gap:6px;box-shadow:var(--shadow-card);transition:border-color var(--motion-fast)}
116
134
  .execute-card:hover{border-left-color:var(--border-strong)}
117
135
  .execute-card[data-status=active]{border-left-color:var(--accent)}