@zq-silk/yui 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/README.md +19 -3
  2. package/dist/artifacts/artifactCapability.js +39 -10
  3. package/dist/artifacts/imageMetadata.js +120 -0
  4. package/dist/artifacts/managedGit.js +7 -4
  5. package/dist/artifacts/taskArtifactRepository.js +6 -0
  6. package/dist/cli/commandCatalog.js +25 -11
  7. package/dist/cli/interactionPolicy.js +4 -0
  8. package/dist/cli/invocationAuthority.js +1 -1
  9. package/dist/commands/executionAuditCommands.js +5 -0
  10. package/dist/commands/globalRoleCommands.js +41 -3
  11. package/dist/commands/projectCommands.js +89 -94
  12. package/dist/commands/roleSkillValidation.js +4 -8
  13. package/dist/commands/taskCommands.js +30 -9
  14. package/dist/commands/taskIntegrationCommands.js +5 -16
  15. package/dist/commands/taskUsageCommand.js +38 -0
  16. package/dist/context/roleSessionContext.js +18 -2
  17. package/dist/context/runContextPack.js +5 -1
  18. package/dist/context/sessionBootstrapManifest.js +10 -1
  19. package/dist/context/skillCatalog.js +108 -0
  20. package/dist/context/skillPackage.js +161 -0
  21. package/dist/context/taskContext.js +3 -2
  22. package/dist/context/taskSearch.js +127 -0
  23. package/dist/controlPlaneCli.js +51 -39
  24. package/dist/controller/clientRuntime.js +3 -2
  25. package/dist/controller/fileSchedulerStoreAdapter.js +32 -6
  26. package/dist/controller/jobControl.js +2 -0
  27. package/dist/controller/nativeHostInput.js +37 -0
  28. package/dist/controller/runtime.js +93 -2
  29. package/dist/controller/runtimeEventInbox.js +2 -2
  30. package/dist/controller/runtimeEventProcessor.js +3 -3
  31. package/dist/doctor/doctor.js +10 -0
  32. package/dist/execution/reviewMainRun.js +1 -0
  33. package/dist/execution/workItemMainRun.js +1 -0
  34. package/dist/executor/codexConfigConflict.js +6 -4
  35. package/dist/executor/effectiveLaunch.js +24 -1
  36. package/dist/executor/fileRoleLaunchPlanner.js +13 -5
  37. package/dist/integration/gitIntegrationService.js +15 -79
  38. package/dist/integration/integrationSourceApplication.js +4 -2
  39. package/dist/job/httpPreview.js +61 -0
  40. package/dist/job/jobCleanupInspection.js +67 -0
  41. package/dist/job/jobRunner.js +12 -2
  42. package/dist/kernel/builtinCapabilities.js +3 -1
  43. package/dist/message/message.js +15 -1
  44. package/dist/output/rolePresentation.js +4 -1
  45. package/dist/repository/executionLaneGitSnapshot.js +2 -1
  46. package/dist/repository/gitCompatibility.js +62 -0
  47. package/dist/repository/gitWorkspace.js +86 -80
  48. package/dist/repository/gitWorktreeInventory.js +121 -0
  49. package/dist/repository/project.js +74 -25
  50. package/dist/repository/taskWorkspaceCoordinator.js +72 -145
  51. package/dist/repository/taskWorkspacePreparer.js +39 -43
  52. package/dist/repository/workspaceCleanupInspection.js +88 -7
  53. package/dist/resources/resourceDiscovery.js +2 -2
  54. package/dist/resources/resourceGc.js +7 -4
  55. package/dist/resources/resourceRegistrar.js +18 -11
  56. package/dist/runtime/agentEndpoint.js +8 -0
  57. package/dist/runtime/agentHost.js +170 -3
  58. package/dist/runtime/codexAppServerRuntime.js +1 -1
  59. package/dist/runtime/codexNativeAccess.js +175 -0
  60. package/dist/runtime/controlledCodexTui.js +116 -0
  61. package/dist/runtime/integrationRuntimeIsolation.js +21 -0
  62. package/dist/runtime/publicReply.js +23 -0
  63. package/dist/runtime/runtimeObservation.js +48 -0
  64. package/dist/runtime/structuredProviderHost.js +16 -7
  65. package/dist/runtime/taskCostMetrics.js +133 -0
  66. package/dist/runtime/taskRuntimeIsolation.js +91 -21
  67. package/dist/runtime/taskUsageMetrics.js +11 -2
  68. package/dist/runtime/taskUsageQuery.js +38 -0
  69. package/dist/scheduler/taskExecutionProjection.js +2 -0
  70. package/dist/scheduler/taskObservabilityProjection.js +9 -1
  71. package/dist/storage/homeLayout.js +13 -0
  72. package/dist/storage/sqliteSchema.js +44 -0
  73. package/dist/storage/sqliteStore.js +53 -0
  74. package/dist/storage/storageVersions.js +1 -1
  75. package/dist/storage/storeRpc.js +2 -0
  76. package/dist/storage/taskSearch.js +35 -0
  77. package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
  78. package/dist/task/archiveDiagnostics.js +26 -8
  79. package/dist/task/archivePreflight.js +26 -9
  80. package/dist/task/leaderArchive.js +9 -15
  81. package/dist/task/ordinaryArchive.js +42 -0
  82. package/dist/tmux/tmuxManager.js +8 -8
  83. package/dist/web/assets/assetManifest.js +11 -51
  84. package/dist/web/assets/client/app/catalog.js +215 -0
  85. package/dist/web/assets/client/app/main.js +148 -0
  86. package/dist/web/assets/client/app/selection.js +139 -0
  87. package/dist/web/assets/client/app/settings.js +326 -0
  88. package/dist/web/assets/client/app/taskView.js +280 -0
  89. package/dist/web/assets/client/domain/context.js +96 -0
  90. package/dist/web/assets/client/domain/records.js +126 -0
  91. package/dist/web/assets/client/domain/runs.js +119 -0
  92. package/dist/web/assets/client/domain/taskForms.js +197 -0
  93. package/dist/web/assets/client/domain/vocab.js +69 -0
  94. package/dist/web/assets/client/domain/work.js +116 -0
  95. package/dist/web/assets/client/i18n/en.js +900 -0
  96. package/dist/web/assets/client/i18n/zh-CN.js +898 -0
  97. package/dist/web/assets/client/index.js +114 -0
  98. package/dist/web/assets/client/layout/resizer.js +37 -0
  99. package/dist/web/assets/client/layout/router.js +35 -0
  100. package/dist/web/assets/client/layout/shortcuts.js +45 -0
  101. package/dist/web/assets/client/layout/sizing.js +95 -0
  102. package/dist/web/assets/client/layout/workspace.js +247 -0
  103. package/dist/web/assets/client/{api.js → lib/api.js} +28 -4
  104. package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
  105. package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
  106. package/dist/web/assets/client/lib/i18n.js +54 -0
  107. package/dist/web/assets/client/lib/markdown.js +167 -0
  108. package/dist/web/assets/client/lib/prefs.js +28 -0
  109. package/dist/web/assets/client/lib/theme.js +68 -0
  110. package/dist/web/assets/client/ui/containers.js +101 -0
  111. package/dist/web/assets/client/ui/controls.js +51 -0
  112. package/dist/web/assets/client/ui/forms.js +48 -0
  113. package/dist/web/assets/client/ui/metrics.js +137 -0
  114. package/dist/web/assets/client/ui/primitives.js +92 -0
  115. package/dist/web/assets/client/ui/text.js +33 -0
  116. package/dist/web/assets/client/ui/toast.js +12 -0
  117. package/dist/web/assets/client/views/bodySearch.js +172 -0
  118. package/dist/web/assets/client/views/createTask.js +85 -0
  119. package/dist/web/assets/client/views/dock/conversation.js +612 -0
  120. package/dist/web/assets/client/views/dock/discussion.js +173 -0
  121. package/dist/web/assets/client/views/dock/preview.js +129 -0
  122. package/dist/web/assets/client/views/dock/terminal.js +199 -0
  123. package/dist/web/assets/client/views/globalInput.js +67 -0
  124. package/dist/web/assets/client/{overview.js → views/overview.js} +62 -56
  125. package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
  126. package/dist/web/assets/client/views/skills.js +152 -0
  127. package/dist/web/assets/client/views/task/actions.js +36 -0
  128. package/dist/web/assets/client/views/task/delivery.js +185 -0
  129. package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
  130. package/dist/web/assets/client/views/task/files.js +272 -0
  131. package/dist/web/assets/client/views/task/observation.js +153 -0
  132. package/dist/web/assets/client/views/task/overview.js +161 -0
  133. package/dist/web/assets/client/views/task/page.js +131 -0
  134. package/dist/web/assets/client/views/task/records.js +163 -0
  135. package/dist/web/assets/client/views/task/runtime.js +130 -0
  136. package/dist/web/assets/client/views/task/timeline.js +190 -0
  137. package/dist/web/assets/shared/geometry.js +19 -0
  138. package/dist/web/assets/{icons.js → shared/icons.js} +4 -0
  139. package/dist/web/assets/shared/taskTabs.js +8 -0
  140. package/dist/web/assets/shared/themes.js +93 -0
  141. package/dist/web/assets/shell/dialogs.js +63 -0
  142. package/dist/web/assets/shell/dock.js +40 -0
  143. package/dist/web/assets/shell/index.js +40 -0
  144. package/dist/web/assets/shell/settings.js +34 -0
  145. package/dist/web/assets/shell/sidebar.js +45 -0
  146. package/dist/web/assets/styles/index.js +41 -0
  147. package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +15 -13
  148. package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
  149. package/dist/web/assets/styles/markdown.js +22 -3
  150. package/dist/web/assets/styles/tokens.js +10 -63
  151. package/dist/web/assets/styles/ui/controls.js +76 -0
  152. package/dist/web/assets/styles/ui/forms.js +46 -0
  153. package/dist/web/assets/styles/ui/surfaces.js +122 -0
  154. package/dist/web/assets/styles/views/delivery.js +29 -0
  155. package/dist/web/assets/styles/views/dock.js +71 -0
  156. package/dist/web/assets/styles/views/page.js +32 -0
  157. package/dist/web/assets/styles/views/records.js +70 -0
  158. package/dist/web/assets/styles/views/settings.js +26 -0
  159. package/dist/web/assets/styles/views/sidebar.js +74 -0
  160. package/dist/web/assets/styles/views/task.js +80 -0
  161. package/dist/web/assets/styles/views/terminal.js +38 -0
  162. package/dist/web/taskPreviews.js +205 -0
  163. package/dist/web/tmuxWebTerminal.js +16 -9
  164. package/dist/web/webConversation.js +373 -0
  165. package/dist/web/webMaterials.js +155 -0
  166. package/dist/web/webServer.js +306 -18
  167. package/dist/web/webSettings.js +331 -0
  168. package/dist/web/webSkills.js +86 -0
  169. package/dist/web/webSnapshot.js +40 -8
  170. package/dist/web/webTaskSurface.js +114 -9
  171. package/dist/workspace/gitChangeSetCapture.js +3 -1
  172. package/docs/managed-turn-and-session-runtime.md +9 -0
  173. package/docs/managed-turn-and-session-runtime.zh-CN.md +7 -0
  174. package/docs/observability/README.md +13 -3
  175. package/docs/observability/README.zh-CN.md +10 -3
  176. package/docs/release-workflow.md +53 -0
  177. package/docs/release-workflow.zh-CN.md +40 -0
  178. package/docs/storage-baseline.md +38 -1
  179. package/docs/storage-baseline.zh-CN.md +22 -0
  180. package/docs/task-delivery.md +36 -8
  181. package/docs/task-delivery.zh-CN.md +24 -7
  182. package/docs/task-discovery.md +28 -0
  183. package/docs/testing/verification-levels.md +2 -2
  184. package/i18n/README.zh-CN.md +4 -3
  185. package/package.json +1 -1
  186. package/skills/yui-leader/references/execution.md +2 -1
  187. package/skills/yui-leader/references/integration.md +20 -0
  188. package/skills/yui-operator/SKILL.md +4 -0
  189. package/skills/yui-reviewer/SKILL.md +3 -0
  190. package/skills/yui-runtime/SKILL.md +4 -0
  191. package/skills/yui-runtime/references/publication.md +20 -4
  192. package/skills/yui-worker/SKILL.md +3 -0
  193. package/dist/web/assets/client/app.js +0 -804
  194. package/dist/web/assets/client/components.js +0 -418
  195. package/dist/web/assets/client/detail.js +0 -333
  196. package/dist/web/assets/client/dock.js +0 -225
  197. package/dist/web/assets/client/evidence.js +0 -172
  198. package/dist/web/assets/client/forms.js +0 -230
  199. package/dist/web/assets/client/i18n.js +0 -1161
  200. package/dist/web/assets/client/markdown.js +0 -114
  201. package/dist/web/assets/client/records.js +0 -63
  202. package/dist/web/assets/client/sections.js +0 -313
  203. package/dist/web/assets/client/theme.js +0 -71
  204. package/dist/web/assets/shell.js +0 -161
  205. package/dist/web/assets/styles/components.js +0 -169
  206. package/dist/web/assets/styles/views.js +0 -292
@@ -0,0 +1,153 @@
1
+ export const TASK_OBSERVATION_SCRIPT = String.raw `
2
+ // Runtime observation slots. The observation arrives separately from the
3
+ // Context snapshot, so updateObservation() redraws only the [data-slot]
4
+ // elements the panels left for it: the execution badge, the Now banner and
5
+ // next owner, Role statuses and Sessions, and the Diagnostics facts.
6
+ import { h, icon, clear } from "/assets/js/lib/dom.js";
7
+ import { fill, formatClock, formatDateTime, relativeTime } from "/assets/js/lib/format.js";
8
+ import { badge, dot, kv, mono, note } from "/assets/js/ui/primitives.js";
9
+ import { label, sessionBadges, statusBadge, tone } from "/assets/js/domain/vocab.js";
10
+
11
+ export function updateObservation(container, data, t, locale) {
12
+ const available = data.runtimeStatus === "available" && data.runtime;
13
+ const execution = available ? data.runtime.execution : null;
14
+ const execBadge = container.querySelector('[data-slot="exec-badge"]');
15
+ if (execBadge) {
16
+ clear(execBadge);
17
+ if (execution && execution.status !== data.task.status) execBadge.append(statusBadge(t, "exec", "exec.status", execution.status));
18
+ }
19
+ const now = container.querySelector('[data-slot="now"]');
20
+ if (now && !now.contains(document.activeElement)) drawNow(now, data, execution, t, locale);
21
+ const next = container.querySelector('[data-slot="next"]');
22
+ if (next) drawNext(next, execution, t);
23
+ drawSessions(container, available ? data.runtime.sessions : null, t, locale);
24
+ const raw = container.querySelector('[data-slot="runtime-raw"]');
25
+ if (raw) raw.textContent = available ? JSON.stringify({ roles: data.runtime.roles, runtimeHealth: data.runtime.runtimeHealth }, null, 2) : "";
26
+ const rawStatus = container.querySelector('[data-slot="runtime-status"]');
27
+ if (rawStatus) rawStatus.textContent = t("observation." + data.runtimeStatus) + " · " + formatDateTime(data.runtimeObservedAt, locale);
28
+ container.querySelectorAll("[data-role-status]").forEach(function (slot) {
29
+ const role = available ? (data.runtime.roles || []).find(function (item) { return item.name === slot.dataset.roleStatus; }) : null;
30
+ clear(slot);
31
+ if (role) slot.append(statusBadge(t, "role", "role", role.status));
32
+ });
33
+ }
34
+
35
+ function drawNext(slot, execution, t) {
36
+ clear(slot);
37
+ if (!execution || (execution.next.owner === "none" && execution.next.action === "none")) return;
38
+ slot.append(icon("flag", "icon-sm"), h("span", null, t("now.nextOwner") + " "),
39
+ h("strong", null, label(t, "exec.owner", execution.next.owner)), h("span", null, " · " + label(t, "exec.action", execution.next.action)));
40
+ }
41
+
42
+ // --- Now banner --------------------------------------------------------------
43
+ const BANNER_TONES = ["tone-info", "tone-ok", "tone-warn", "tone-bad", "tone-idle", "tone-accent"];
44
+
45
+ function drawNow(slot, data, execution, t, locale) {
46
+ clear(slot);
47
+ const inputCount = data.core.attention.openInputs.count;
48
+ const waiting = inputCount ? fill(t("now.inputs"), { count: inputCount }) : null;
49
+ const banner = slot.closest(".now-card");
50
+ if (banner) {
51
+ banner.classList.remove.apply(banner.classList, BANNER_TONES);
52
+ banner.classList.add("tone-" + (execution ? tone("exec", execution.status) : data.runtimeStatus === "unavailable" ? "bad" : "idle"));
53
+ }
54
+ if (!execution) {
55
+ slot.append(h("div.now-head", null,
56
+ badge(t("observation." + data.runtimeStatus), data.runtimeStatus === "unavailable" ? "bad" : "idle", { dot: true }),
57
+ h("span.now-owner", null, waiting || t("now.noInputs"))));
58
+ slot.append(h("p.now-note", null, data.runtimeStatus === "waiting" ? t("now.reading") : t("now.unavailable")));
59
+ return;
60
+ }
61
+ slot.append(nowHead(data, execution, t, locale));
62
+ if (execution.summary) slot.append(h("p.now-summary", null, execution.summary));
63
+ if (execution.reason) slot.append(h("p.now-reason", null, execution.reason));
64
+ const signals = nowSignals(data, execution, t);
65
+ if (signals.length) slot.append(h("ul.signal-list", null, signals));
66
+ else slot.append(h("p.now-note", null, waiting || t("now.clear")));
67
+ }
68
+
69
+ function nowHead(data, execution, t, locale) {
70
+ const head = h("div.now-head", null,
71
+ statusBadge(t, "exec", "exec.status", execution.status),
72
+ execution.owner === "none" && execution.action === "none" ? null
73
+ : h("span.now-owner", null, label(t, "exec.owner", execution.owner) + " · " + label(t, "exec.action", execution.action)));
74
+ const activeCount = execution.activeRunCount === undefined ? (execution.activeRuns || []).length : execution.activeRunCount;
75
+ if (activeCount) head.append(badge(activeCount + " " + t("now.activeRuns"), "info"));
76
+ if (execution.monitoring === "stopped") head.append(badge(t("now.monitoringStopped"), "warn"));
77
+ if (execution.failClosed) head.append(badge(t("now.failClosed"), "bad"));
78
+ head.append(h("span.spacer"), h("span.faint.small", { title: formatDateTime(data.runtimeObservedAt, locale) },
79
+ t("now.observed") + " " + formatClock(data.runtimeObservedAt, locale)));
80
+ return head;
81
+ }
82
+
83
+ // Attention, blockers and native Sessions waiting on the user, in that order.
84
+ function nowSignals(data, execution, t) {
85
+ const signals = [];
86
+ (execution.attention || []).forEach(function (item) {
87
+ signals.push(signalRow("warn", "alert", label(t, "exec.attention", item.kind), item.summary,
88
+ item.owner ? label(t, "exec.owner", item.owner) : null));
89
+ });
90
+ (execution.blockers || []).forEach(function (item) {
91
+ signals.push(signalRow("bad", "alert", label(t, "exec.blocker", item.kind), item.summary, label(t, "exec.owner", item.owner)));
92
+ });
93
+ const sessions = (data.runtime.sessions && data.runtime.sessions.sessions) || [];
94
+ sessions.filter(function (session) { return session.group === "waiting" && ["user", "permission"].includes(session.waitingReason); })
95
+ .forEach(function (session) { signals.push(signalRow("warn", "user", session.roleName, t("now.nativeWait"), null)); });
96
+ return signals;
97
+ }
98
+
99
+ function signalRow(toneName, iconName, kind, text, owner) {
100
+ return h("li.signal-row.tone-" + toneName, null, icon(iconName, "icon-sm"),
101
+ h("span.signal-kind", null, kind), h("span", null, text), owner ? h("span.faint.small", null, owner) : null);
102
+ }
103
+
104
+ // --- Sessions ----------------------------------------------------------------
105
+ // Native Session observation, drawn into the Runtime tab: one line per Role,
106
+ // the group counts beside the Roles heading, and the timestamps under
107
+ // Diagnostics. Activity is never delivery progress.
108
+ function drawSessions(container, observation, t, locale) {
109
+ const sessions = observation ? observation.sessions : [];
110
+ container.querySelectorAll("[data-role-session]").forEach(function (slot) {
111
+ const session = sessions.find(function (item) { return item.roleName === slot.dataset.roleSession; });
112
+ drawRoleSession(slot, session, observation, t, locale);
113
+ });
114
+ const counts = container.querySelector('[data-slot="session-counts"]');
115
+ if (counts) {
116
+ clear(counts);
117
+ if (observation) counts.append(...sessionBadges(observation.counts, t));
118
+ }
119
+ const facts = container.querySelector('[data-slot="session-facts"]');
120
+ if (facts) drawSessionFacts(facts, observation, sessions, t, locale);
121
+ }
122
+
123
+ function drawRoleSession(slot, session, observation, t, locale) {
124
+ clear(slot);
125
+ if (!session) {
126
+ slot.append(h("span.faint.small", null, observation ? t("session.noneRecorded") : t("sessions.unavailable")));
127
+ return;
128
+ }
129
+ slot.append(h("div.role-session-head", null, dot(tone("session", session.group)),
130
+ h("strong", null, t("session." + session.group)),
131
+ h("span.faint.small", null, session.lastActivityAt ? relativeTime(session.lastActivityAt, locale, t) : t("sessions.unobserved"))));
132
+ if (session.reason) slot.append(h("p.small", null, session.reason));
133
+ if (session.waitingReason) slot.append(h("p.small", null, t("sessions.waitingFor") + " " + session.waitingReason));
134
+ }
135
+
136
+ function drawSessionFacts(facts, observation, sessions, t, locale) {
137
+ clear(facts);
138
+ if (!observation) return;
139
+ facts.append(h("h4.sub-head", null, t("sessions.facts")));
140
+ if (!sessions.length) facts.append(note(t("sessions.none")));
141
+ sessions.forEach(function (session) {
142
+ facts.append(kv([
143
+ [t("sessions.role"), session.roleName],
144
+ [t("sessions.lastActivity"), session.lastActivityAt ? formatDateTime(session.lastActivityAt, locale) : t("sessions.unobserved")],
145
+ [t("sessions.inputUpdated"), formatDateTime(session.sourceUpdatedAt, locale)],
146
+ [t("sessions.operations"), session.operations.length ? session.operations.join(", ") : null],
147
+ [t("sessions.background"), session.background.length ? session.background.map(function (item) { return item.id + " · " + item.execution; }).join(", ") : null],
148
+ [t("sessions.identity"), mono([session.nativeSessionId, session.nativeTurnId, session.attemptId].filter(Boolean).join(" / "))]
149
+ ]));
150
+ });
151
+ facts.append(h("p.faint.small", null, t("sessions.scope") + " " + formatDateTime(observation.readAt, locale)));
152
+ }
153
+ `;
@@ -0,0 +1,161 @@
1
+ export const TASK_OVERVIEW_SCRIPT = String.raw `
2
+ // Task › Overview: one reading column, top to bottom — where the Task stands
3
+ // now, what needs the user, what it has reported, its goal and its current
4
+ // decisions. A Draft also reports its planning Turn.
5
+ import { h } from "/assets/js/lib/dom.js";
6
+ import { formatDateTime } from "/assets/js/lib/format.js";
7
+ import { bulletList, button, codeBlock, dot, emptyState, note, timeTag } from "/assets/js/ui/primitives.js";
8
+ import { card, disclosure } from "/assets/js/ui/containers.js";
9
+ import { richText } from "/assets/js/ui/text.js";
10
+ import { entriesOf, exactCache, totalOf, valuesOf } from "/assets/js/domain/context.js";
11
+ import { lazyRow, recordCard } from "/assets/js/domain/records.js";
12
+ import { inputCard } from "/assets/js/domain/work.js";
13
+ import { taskActions } from "/assets/js/views/task/actions.js";
14
+
15
+ export function renderOverviewPanel(panel, data, t, locale, ctx) {
16
+ // Now: the derived execution status (observation), the open questions and
17
+ // who acts next. drawNow() tints the banner with the status tone.
18
+ const now = card({ className: "now-card" });
19
+ now.body.append(h("p.faint.small", null, t("workbench.observation")),
20
+ h("div", { dataset: { slot: "now" } }), h("p.next-line", { dataset: { slot: "next" } }));
21
+ panel.append(now);
22
+ panel.append(taskActions(data.task, t, ctx));
23
+ if (data.core.attention.openInputs.count) panel.append(needsCard(data, t, locale, ctx));
24
+ const progress = progressCard(data, t, locale, ctx);
25
+ const goal = goalCard(data, t, ctx);
26
+ panel.append(progress);
27
+ panel.append(goal, workAndEvidence(data, t, ctx), decisionsCard(data, t, locale, ctx));
28
+ }
29
+
30
+ // Needs you: open InputRequests with their answer controls. Open questions
31
+ // the bounded read did not carry are listed by reference.
32
+ function needsCard(data, t, locale, ctx) {
33
+ const task = data.task;
34
+ const openInputs = data.core.attention.openInputs;
35
+ const inputs = entriesOf(data.core, "input-request").filter(function (entry) { return !entry.omitted && entry.value.status === "open"; });
36
+ const missing = openInputs.refs.filter(function (ref) {
37
+ return !inputs.some(function (entry) { return entry.ref.refId === ref.refId; });
38
+ });
39
+ const needs = card({ title: t("overview.needsYou"), icon: "inbox", count: openInputs.count, className: "needs-card" });
40
+ needs.id = "detail-attention";
41
+ inputs.forEach(function (entry) { needs.body.append(inputCard(entry.value, t, locale, ctx.answerInput)); });
42
+ const seen = new Set(inputs.map(function (entry) { return entry.ref.refId; }));
43
+ function deferredInput(item) {
44
+ seen.add(item.ref.refId);
45
+ return lazyRow(item, task.id, t, ctx, {
46
+ head: h("span", null, item.summary || item.ref.refId),
47
+ cache: exactCache(data.viewState),
48
+ render: function (value) {
49
+ return value.status === "open" ? inputCard(value, t, locale, ctx.answerInput)
50
+ : note(value.id + " · " + value.status);
51
+ }
52
+ });
53
+ }
54
+ missing.forEach(function (ref) {
55
+ const entry = entriesOf(data.core, "input-request").find(function (entry) { return entry.ref.refId === ref.refId; });
56
+ needs.body.append(deferredInput({ ref: ref, summary: entry && entry.summary }));
57
+ });
58
+ if (openInputs.count > inputs.length + missing.length) {
59
+ let cursor;
60
+ const more = button(t("overview.moreInputs"), { onClick: async function () {
61
+ more.disabled = true;
62
+ try {
63
+ const page = await ctx.list(task.id, "input-request", { status: "open", limit: 20, cursor: cursor });
64
+ page.items.forEach(function (item) { if (!seen.has(item.ref.refId)) needs.body.insertBefore(deferredInput(item), more); });
65
+ cursor = page.nextCursor;
66
+ more.disabled = !cursor;
67
+ } catch (error) { needs.body.append(note(error.message, "bad")); more.disabled = false; }
68
+ } });
69
+ needs.body.append(more);
70
+ }
71
+ return needs;
72
+ }
73
+
74
+ function briefOf(data) {
75
+ return data.briefValue || valuesOf(data.core, "task-brief")[0];
76
+ }
77
+
78
+ function progressCard(data, t, locale, ctx) {
79
+ const task = data.task;
80
+ const element = card({ title: t("progress.title"), icon: "flag" });
81
+ const working = task.status === "draft" || task.status === "active";
82
+ if (task.completionSummary) element.body.append(richText(t("progress.completion"), task.completionSummary, t, { className: "callout.tone-ok" }));
83
+ if (task.retirementSummary) element.body.append(richText(t("progress.retirement"), task.retirementSummary, t, { className: "callout" }));
84
+ if (task.archiveSummary || task.archiveReason) element.body.append(richText(t("progress.archive"), [task.archiveSummary, task.archiveReason].filter(Boolean).join("\n\n"), t, { className: "callout" }));
85
+ const briefEntry = entriesOf(data.core, "task-brief")[0];
86
+ const brief = briefOf(data);
87
+ if (brief) {
88
+ element.body.append(richText(t(working ? "progress.focus" : "progress.lastFocus"), brief.currentFocus, t));
89
+ element.body.append(richText(t(working ? "progress.report" : "progress.lastReport"), brief.leaderSummary, t, { muted: true }));
90
+ element.body.append(h("p.faint.small", null, t("progress.briefUpdated") + " " + formatDateTime(brief.updatedAt, locale)));
91
+ } else if (briefEntry) element.body.append(recordCard(briefEntry, task.id, t, ctx, { compact: true }));
92
+ else element.body.append(emptyState(t("progress.none")));
93
+ return element;
94
+ }
95
+
96
+ function goalCard(data, t, ctx) {
97
+ const task = data.task;
98
+ const brief = briefOf(data);
99
+ const element = card({ title: t("goal.title"), icon: "target" });
100
+ if (brief && brief.objective) element.body.append(richText(null, brief.objective, t));
101
+ if (brief && brief.boundaries && brief.boundaries.length) {
102
+ element.body.append(h("div.prose-block", null, h("h4.prose-label", null, t("goal.boundaries")), bulletList(brief.boundaries)));
103
+ }
104
+ if (brief && brief.technicalApproach) element.body.append(proseDisclosure(t("goal.approach"), "approach", brief.technicalApproach, t));
105
+ if (task.description) element.body.append(proseDisclosure(t("goal.requirements"), "requirements", task.description, t));
106
+ element.body.append(note(t("workbench.summary")));
107
+ ["task", "task-brief"].forEach(function (store) {
108
+ const entry = entriesOf(data.core, store)[0];
109
+ if (entry) element.body.append(recordCard({ ref: entry.ref, omitted: true }, task.id, t, ctx));
110
+ });
111
+ if (!element.body.childNodes.length) element.body.append(emptyState(t("goal.none")));
112
+ return element;
113
+ }
114
+
115
+ function workAndEvidence(data, t, ctx) {
116
+ const element = card({ title: t("workbench.evidence"), icon: "layers" });
117
+ entriesOf(data.core, "work-item").slice(0, 3).forEach(function (entry) {
118
+ element.body.append(recordCard(entry, data.task.id, t, ctx, { compact: true }));
119
+ });
120
+ const evidence = data.evidenceSummary || { reviews: [], integrations: [] };
121
+ evidence.reviews.forEach(function (review) {
122
+ element.body.append(h("p.small", null, review.id + " · " + t("workbench.reviewExecution") + " " + review.status),
123
+ review.excerpt ? richText(review.runId, review.excerpt, t) : null);
124
+ });
125
+ evidence.integrations.forEach(function (integration) {
126
+ element.body.append(h("p.small", null, integration.id + " · " + integration.status),
127
+ integration.summary ? richText(null, integration.summary, t) : null);
128
+ (integration.checks || []).forEach(function (check) {
129
+ element.body.append(h("p.faint.small", null, check.name + " · " + check.outcome));
130
+ });
131
+ });
132
+ if (!evidence.reviews.length && !evidence.integrations.length) element.body.append(note(t("workbench.noEvidence")));
133
+ element.body.append(button(t("workbench.openEvidence"), { variant: "link", onClick: function () { ctx.onTab("delivery"); } }));
134
+ return element;
135
+ }
136
+
137
+ function proseDisclosure(title, viewKey, text, t) {
138
+ const element = disclosure(title, viewKey);
139
+ element.body.append(richText(null, text, t));
140
+ return element;
141
+ }
142
+
143
+ function decisionsCard(data, t, locale, ctx) {
144
+ const active = entriesOf(data.core, "task-decision").filter(function (entry) { return !entry.omitted && entry.value.status === "active"; });
145
+ const total = totalOf(data.core, "task-decision");
146
+ const element = card({ id: "detail-decisions", title: t("decisions.title"), icon: "check", count: total || null, hint: t("decisions.hint") });
147
+ active.slice(0, 3).forEach(function (entry) {
148
+ const value = entry.value;
149
+ element.body.append(h("article.decision", null,
150
+ h("header.decision-head", null, dot("ok"), h("strong", null, value.title), timeTag(value.createdAt, locale, t)),
151
+ value.rationale ? richText(null, value.rationale, t, { muted: true, threshold: 260 }) : null));
152
+ });
153
+ if (!active.length) element.body.append(emptyState(t("decisions.none")));
154
+ element.body.append(button(t("decisions.more"), { variant: "link", icon: "chevron", onClick: function () {
155
+ data.viewState.recordsFilter = "decision";
156
+ ctx.onTab("records");
157
+ } }));
158
+ return element;
159
+ }
160
+
161
+ `;
@@ -0,0 +1,131 @@
1
+ import { TASK_TABS, TASK_TAB_ALIASES, TASK_TAB_ICONS } from "../../../shared/taskTabs.js";
2
+ export const TASK_PAGE_SCRIPT = String.raw `
3
+ // Task page: header, meta line, section tabs and the four panels. The runtime
4
+ // observation arrives separately from the Context snapshot, so every fact it
5
+ // feeds lives in a [data-slot] that updateObservation() redraws in place.
6
+ import { h, icon, clear } from "/assets/js/lib/dom.js";
7
+ import { badge, chip, timeTag } from "/assets/js/ui/primitives.js";
8
+ import { label, statusBadge } from "/assets/js/domain/vocab.js";
9
+ import { totalOf } from "/assets/js/domain/context.js";
10
+ import { renderOverviewPanel } from "/assets/js/views/task/overview.js";
11
+ import { updateObservation } from "/assets/js/views/task/observation.js";
12
+ import { renderDelivery } from "/assets/js/views/task/delivery.js";
13
+ import { renderRuntime } from "/assets/js/views/task/runtime.js";
14
+ import { renderRecords } from "/assets/js/views/task/records.js";
15
+
16
+ // Overview: where it stands. Delivery: what it produced. Runtime: what is
17
+ // live now. Records: what happened. Retired section names stay addressable.
18
+ export const TABS = ${JSON.stringify(TASK_TABS)};
19
+ export const TAB_ALIASES = ${JSON.stringify(TASK_TAB_ALIASES)};
20
+ const TAB_ICONS = ${JSON.stringify(TASK_TAB_ICONS)};
21
+
22
+ export function renderTaskDetail(container, data, t, locale, ctx) {
23
+ clear(container);
24
+ const task = data.task;
25
+ container.dataset.taskId = task.id;
26
+ data.viewState.coreKey = data.core.coreCursor;
27
+ const page = h("div.page.task-page");
28
+ page.append(h("header.task-header", null, taskBar(task, t, ctx)),
29
+ h("div.task-hero", null, h("h1.task-title", null, task.title), taskMeta(task, t, locale)),
30
+ h("div.tabs-wrap", null, taskTabs(data.core, t, ctx)));
31
+ const panels = {};
32
+ TABS.forEach(function (tab) {
33
+ panels[tab] = h("section.tab-panel", { id: "panel-" + tab, role: "tabpanel", "aria-labelledby": "tab-" + tab, hidden: ctx.activeTab !== tab });
34
+ page.append(panels[tab]);
35
+ });
36
+ renderOverviewPanel(panels.overview, data, t, locale, ctx);
37
+ [["delivery", renderDelivery], ["runtime", renderRuntime], ["records", renderRecords]].forEach(function (entry) {
38
+ const panel = panels[entry[0]];
39
+ panel.onShow = function () {
40
+ panel.onShow = null;
41
+ entry[1](panel, data, t, locale, ctx);
42
+ updateObservation(container, data, t, locale, ctx);
43
+ if (panel.onShow) panel.onShow();
44
+ };
45
+ });
46
+ container.append(page);
47
+ updateObservation(container, data, t, locale, ctx);
48
+ const active = panels[ctx.activeTab];
49
+ if (active && active.onShow) active.onShow();
50
+ }
51
+
52
+ // A panel may read on first show (evidence, record pages); it keeps those
53
+ // reads in view state, so showing it again does not repeat them.
54
+ export function selectTab(container, tab) {
55
+ container.querySelectorAll(".tab").forEach(function (button) {
56
+ const active = button.dataset.tab === tab;
57
+ button.setAttribute("aria-selected", String(active));
58
+ button.tabIndex = active ? 0 : -1;
59
+ });
60
+ container.querySelectorAll(".tab-panel").forEach(function (panel) {
61
+ panel.hidden = panel.id !== "panel-" + tab;
62
+ if (!panel.hidden && panel.onShow) panel.onShow();
63
+ });
64
+ }
65
+
66
+ function taskBar(task, t, ctx) {
67
+ return h("div.task-bar", null,
68
+ h("button.icon-btn.back-btn", { type: "button", "aria-label": t("actions.back"), title: t("actions.back"), onclick: ctx.onBack }, icon("back")),
69
+ h("nav.crumbs", { "aria-label": t("crumbs.label") },
70
+ h("button.crumb", { type: "button", onclick: ctx.onBack }, t("crumbs.tasks")),
71
+ h("span.crumb-sep", { "aria-hidden": "true" }, "/"),
72
+ h("code.crumb-current", null, task.id)),
73
+ h("span.spacer"),
74
+ dockToggle("discussion", "chat", t("dock.discussion") + " · D", t, ctx),
75
+ dockToggle("session", "terminal", t("dock.session"), t, ctx));
76
+ }
77
+
78
+ function dockToggle(mode, iconName, title, t, ctx) {
79
+ return h("button.btn.btn-ghost.dock-toggle", {
80
+ type: "button", dataset: { dockToggle: mode }, title: title,
81
+ onclick: function () { ctx.showDock(mode); }
82
+ }, icon(iconName), h("span", null, t("dock." + mode)));
83
+ }
84
+
85
+ function taskMeta(task, t, locale) {
86
+ return h("div.task-meta", null,
87
+ statusBadge(t, "task", "status", task.status),
88
+ h("span", { dataset: { slot: "exec-badge" } }),
89
+ task.priority ? badge(label(t, "priority", task.priority), task.priority === "urgent" || task.priority === "high" ? "accent" : "idle") : null,
90
+ (task.tags || []).map(function (tag) { return chip("#" + tag); }),
91
+ h("span.meta-item", null, icon("clock", "icon-sm"), t("task.updated") + " ", timeTag(task.updatedAt, locale, t)),
92
+ (task.projectBindings || []).length ? h("span.meta-item", null, icon("layers", "icon-sm"),
93
+ task.projectBindings.map(function (binding) { return binding.projectId; }).join(", ")) : null);
94
+ }
95
+
96
+ function taskTabs(core, t, ctx) {
97
+ // Tab counts are current totals from the same bounded read, not samples:
98
+ // open questions, open work items and open execution records.
99
+ const counts = {
100
+ overview: core.attention.openInputs.count,
101
+ delivery: totalOf(core, "work-item"),
102
+ runtime: totalOf(core, "run")
103
+ };
104
+ const tabs = h("nav.tabs", { role: "tablist", "aria-label": t("tabs.label") }, TABS.map(function (tab, index) {
105
+ return h("button.tab", {
106
+ type: "button", role: "tab", id: "tab-" + tab,
107
+ "aria-controls": "panel-" + tab,
108
+ "aria-selected": String(ctx.activeTab === tab),
109
+ tabIndex: ctx.activeTab === tab ? 0 : -1,
110
+ title: t("tabs." + tab) + " · " + (index + 1),
111
+ dataset: { tab: tab },
112
+ onclick: function () { ctx.onTab(tab); }
113
+ }, icon(TAB_ICONS[tab], "icon-sm"), h("span", null, t("tabs." + tab)),
114
+ counts[tab] ? h("span.count" + (tab === "overview" ? ".tone-warn" : ""), null, String(counts[tab])) : null);
115
+ }));
116
+ tabs.addEventListener("keydown", function (event) { moveTab(tabs, event, ctx); });
117
+ return tabs;
118
+ }
119
+
120
+ // Left/Right arrows move the selection and the focus to the adjacent tab.
121
+ function moveTab(tabs, event, ctx) {
122
+ if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;
123
+ event.preventDefault();
124
+ const selected = tabs.querySelector('[aria-selected="true"]');
125
+ const index = TABS.indexOf(selected.dataset.tab);
126
+ const next = TABS[(index + (event.key === "ArrowRight" ? 1 : TABS.length - 1)) % TABS.length];
127
+ ctx.onTab(next);
128
+ const button = tabs.querySelector('[data-tab="' + next + '"]');
129
+ if (button) button.focus();
130
+ }
131
+ `;
@@ -0,0 +1,163 @@
1
+ export const TASK_RECORDS_SCRIPT = String.raw `
2
+ // Task › Records: what happened — the History timeline, the Task facts with
3
+ // the title form, and the Advanced disclosure (Context cursors and provider
4
+ // panels, read when it is first opened). Every #detail-* anchor stays
5
+ // addressable via ?section=.
6
+ import { h, clear } from "/assets/js/lib/dom.js";
7
+ import { formatDateTime } from "/assets/js/lib/format.js";
8
+ import { button, chip, emptyState, externalLink, jsonBlock, kv, mono, note, safeUrl } from "/assets/js/ui/primitives.js";
9
+ import { card, cardDisclosure, disclosure, loadedNote, moreButton } from "/assets/js/ui/containers.js";
10
+ import { richText } from "/assets/js/ui/text.js";
11
+ import { exactCache, familyState, loadFamily, summaryFields, totalOf } from "/assets/js/domain/context.js";
12
+ import { lazyRow } from "/assets/js/domain/records.js";
13
+ import { label, statusBadge } from "/assets/js/domain/vocab.js";
14
+ import { titleForm } from "/assets/js/domain/taskForms.js";
15
+ import { historyCard } from "/assets/js/views/task/timeline.js";
16
+
17
+ export function renderRecords(panel, data, t, locale, ctx) {
18
+ panel.append(historyCard(panel, data, t, locale, ctx));
19
+ panel.append(factsCard(data.task, t, locale, ctx));
20
+ panel.append(knowledgeCard(data, t, ctx));
21
+ panel.append(advancedCard(data.core, data.task, t, locale, ctx));
22
+ }
23
+
24
+ // Current, bound-project Knowledge only. Read summaries in pages and fetch an
25
+ // exact full version (including source and prior versions) on first row open.
26
+ function knowledgeCard(data, t, ctx) {
27
+ const view = data.viewState;
28
+ exactCache(view);
29
+ view.openRows = view.openRows || {};
30
+ const element = cardDisclosure("detail-knowledge", t("knowledge.title"), "info",
31
+ String(totalOf(data.core, "project-knowledge")));
32
+ function draw() {
33
+ clear(element.body);
34
+ element.body.append(note(t("knowledge.help")));
35
+ const state = familyState(view, "project-knowledge");
36
+ if (state.error) {
37
+ element.body.append(note(state.error, "bad"));
38
+ const retry = button(t("panels.read"), { variant: "ghost" });
39
+ retry.addEventListener("click", function () { load(false); });
40
+ element.body.append(retry);
41
+ }
42
+ if (!state.items.length) element.body.append(note(t(state.pending ? "record.reading" : "records.none")));
43
+ state.items.forEach(function (item) {
44
+ const fields = summaryFields(item.summary);
45
+ element.body.append(lazyRow(item, data.task.id, t, ctx, {
46
+ cache: view.exact, openRows: view.openRows,
47
+ head: [h("span.lazy-title", null, fields.title || item.ref.refId), mono(item.ref.refId)],
48
+ render: function (value) {
49
+ return h("div.stack", null, kv([
50
+ [t("knowledge.version"), String(value.version)],
51
+ [t("details.status"), value.status],
52
+ [t("knowledge.scope"), value.scope || t("knowledge.unspecified")],
53
+ [t("knowledge.expires"), value.expiresWhen || t("knowledge.unspecified")],
54
+ [t("knowledge.source"), value.provenance ? value.provenance.taskId + (value.provenance.proposalId ? " / " + value.provenance.proposalId : "") : t("knowledge.direct")]
55
+ ]), richText(null, value.body, t), note(t("knowledge.history")));
56
+ }
57
+ }));
58
+ });
59
+ if (state.pages) element.body.append(loadedNote(t, state.items.length, state.total));
60
+ if (state.nextCursor) element.body.append(moreButton(t, function () { load(true); }));
61
+ }
62
+ function load(more) {
63
+ const promise = loadFamily(view, data.task.id, "project-knowledge", data.core.coreCursor, ctx, { more: more, limit: 40 });
64
+ draw();
65
+ promise.then(draw);
66
+ }
67
+ element.addEventListener("toggle", function () {
68
+ if (element.open) load(false);
69
+ });
70
+ draw();
71
+ return element;
72
+ }
73
+
74
+ // --- Task information --------------------------------------------------------
75
+ function factsCard(task, t, locale, ctx) {
76
+ const facts = card({ id: "detail-facts", title: t("details.facts"), icon: "info" });
77
+ facts.body.append(kv([
78
+ [t("details.id"), mono(task.id)],
79
+ [t("details.status"), statusBadge(t, "task", "status", task.status)],
80
+ [t("details.priority"), task.priority ? label(t, "priority", task.priority) : null],
81
+ [t("details.tags"), (task.tags || []).length ? h("span.chip-row", null, task.tags.map(function (tag) { return chip(tag); })) : null],
82
+ [t("details.projects"), (task.projectBindings || []).length ? h("span.chip-row", null, task.projectBindings.map(function (b) { return chip(b.projectId); })) : null],
83
+ [t("details.created"), formatDateTime(task.createdAt, locale)],
84
+ [t("details.updated"), formatDateTime(task.updatedAt, locale)],
85
+ [t("details.workspace"), task.cwd ? mono(task.cwd) : null]
86
+ ]));
87
+ facts.body.append(h("div.prose-block", null, h("h4.prose-label", null, t("details.title")), titleForm(task, t, ctx)));
88
+ return facts;
89
+ }
90
+
91
+ // --- Advanced ------------------------------------------------------------------
92
+ function advancedCard(core, task, t, locale, ctx) {
93
+ const advanced = cardDisclosure("detail-advanced", t("advanced.title"), "advanced", t("advanced.meta"));
94
+ advanced.body.append(h("h4.sub-head", null, t("details.context")), jsonBlock({
95
+ coreCursor: core.coreCursor, throughCursor: core.throughCursor, count: core.count, collections: core.collections, omitted: core.omitted
96
+ }), note(t("details.contextHelp")));
97
+ const panels = h("div.stack");
98
+ advanced.body.append(h("h4.sub-head", null, t("panels.title")), panels);
99
+ let loading = false;
100
+ let loaded = false;
101
+ advanced.addEventListener("toggle", async function () {
102
+ if (!advanced.open || loading || loaded) return;
103
+ loading = true;
104
+ try { loaded = await readPanels(panels, task, t, locale, ctx); } finally { loading = false; }
105
+ });
106
+ return advanced;
107
+ }
108
+
109
+ // Provider panels are read once, on the first open; a failure may retry.
110
+ async function readPanels(holder, task, t, locale, ctx) {
111
+ clear(holder);
112
+ holder.append(h("p.faint", null, t("panels.reading")));
113
+ try {
114
+ const current = await ctx.panels(task.id);
115
+ clear(holder);
116
+ holder.append(h("p.faint.small", null, t("panels.observed") + " " + formatDateTime(current.observedAt, locale)));
117
+ if (!current.panels.length) holder.append(emptyState(t("panels.none")));
118
+ current.panels.forEach(function (item) { holder.append(panelCard(item, task, t, ctx)); });
119
+ return true;
120
+ } catch (error) {
121
+ clear(holder);
122
+ holder.append(note(t("panels.unavailable") + " " + error.message, "bad"));
123
+ return false;
124
+ }
125
+ }
126
+
127
+ function panelCard(item, task, t, ctx) {
128
+ const panel = item.panel;
129
+ const element = h("article.record", null,
130
+ h("header.record-head", null, h("strong", null, panel.title), h("span.spacer"), h("span.faint.small", null, item.capability + " · " + item.provider.id)));
131
+ if (item.unavailable) { element.append(note(item.unavailable, "warn")); return element; }
132
+ if (panel.kind === "text") element.append(h("p.small", null, panel.text));
133
+ else if (panel.kind === "link") {
134
+ const href = safeUrl(panel.href);
135
+ if (href) element.append(externalLink(href, panel.title));
136
+ } else if (panel.kind === "data" && panel.renderer === "json") element.append(...dataPanel(item, task, t, ctx));
137
+ return element;
138
+ }
139
+
140
+ // A data panel is read on request with the user's JSON input.
141
+ function dataPanel(item, task, t, ctx) {
142
+ const input = h("textarea.mono", { rows: 3 });
143
+ input.value = JSON.stringify({ taskId: task.id });
144
+ const schema = disclosure(t("panels.schema"), null);
145
+ schema.body.append(jsonBlock(item.inputSchema));
146
+ const output = h("pre.code-block");
147
+ output.hidden = true;
148
+ const read = button(t("panels.read"), { icon: "eye", variant: "ghost" });
149
+ read.addEventListener("click", async function () {
150
+ if (read.disabled) return;
151
+ read.disabled = true;
152
+ output.hidden = false;
153
+ try {
154
+ const result = await ctx.readPanel(task.id, {
155
+ capability: item.capability, contractVersion: item.contractVersion, provider: item.provider
156
+ }, JSON.parse(input.value));
157
+ output.textContent = JSON.stringify(result, null, 2);
158
+ } catch (error) { output.textContent = t("panels.unavailable") + " " + error.message; }
159
+ finally { read.disabled = false; }
160
+ });
161
+ return [h("label.field", null, h("span", null, t("panels.input")), input), schema, h("div.form-actions", null, read), output];
162
+ }
163
+ `;