@zq-silk/yui 2.1.0 → 2.3.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 (135) hide show
  1. package/README.md +8 -2
  2. package/dist/cli/commandCatalog.js +32 -6
  3. package/dist/cli/commandDiscovery.js +4 -1
  4. package/dist/cli/interactionPolicy.js +4 -0
  5. package/dist/cli.js +23 -3078
  6. package/dist/commands/taskCommands.js +84 -28
  7. package/dist/commands/taskIntegrationCommands.js +8 -17
  8. package/dist/commands/taskUpstreamCommands.js +3 -1
  9. package/dist/context/runContextPack.js +132 -5
  10. package/dist/context/sourceRunContext.js +4 -2
  11. package/dist/context/taskContext.js +6 -1
  12. package/dist/controlPlaneCli.js +3099 -0
  13. package/dist/controller/clientRuntime.js +3 -2
  14. package/dist/controller/fileSchedulerStoreAdapter.js +19 -6
  15. package/dist/controller/jobControl.js +21 -0
  16. package/dist/executor/agentExecutor.js +1 -1
  17. package/dist/executor/effectiveLaunch.js +18 -5
  18. package/dist/executor/fileRoleLaunchPlanner.js +15 -19
  19. package/dist/integration/gitIntegrationService.js +58 -82
  20. package/dist/job/jobCleanupInspection.js +67 -0
  21. package/dist/message/messageContinuation.js +7 -2
  22. package/dist/nativeAgent/agent.js +176 -74
  23. package/dist/nativeAgent/cliDemo.js +37 -0
  24. package/dist/nativeAgent/codingTools.js +11 -0
  25. package/dist/nativeAgent/commandTool.js +215 -0
  26. package/dist/nativeAgent/compactionDemo.js +158 -0
  27. package/dist/nativeAgent/composition.js +71 -0
  28. package/dist/nativeAgent/context/budget.js +44 -0
  29. package/dist/nativeAgent/context/index.js +339 -0
  30. package/dist/nativeAgent/context/providerCompressor.js +103 -0
  31. package/dist/nativeAgent/demo.js +12 -1
  32. package/dist/nativeAgent/evaluation/cases.js +38 -0
  33. package/dist/nativeAgent/evaluation/checks.js +91 -0
  34. package/dist/nativeAgent/evaluation/demo.js +19 -0
  35. package/dist/nativeAgent/evaluation/files.js +54 -0
  36. package/dist/nativeAgent/evaluation/fixture.js +36 -0
  37. package/dist/nativeAgent/evaluation/index.js +239 -0
  38. package/dist/nativeAgent/executionOwner.js +209 -0
  39. package/dist/nativeAgent/filePatterns.js +170 -0
  40. package/dist/nativeAgent/fileToolsSupport.js +202 -0
  41. package/dist/nativeAgent/index.js +13 -0
  42. package/dist/nativeAgent/interaction/cli.js +358 -0
  43. package/dist/nativeAgent/interaction/contracts.js +1 -0
  44. package/dist/nativeAgent/interaction/index.js +3 -0
  45. package/dist/nativeAgent/interaction/memoryDemo.js +115 -0
  46. package/dist/nativeAgent/interaction/renderer.js +34 -0
  47. package/dist/nativeAgent/localSafety.js +258 -0
  48. package/dist/nativeAgent/model/anthropicMessages.js +204 -0
  49. package/dist/nativeAgent/model/chatCompletions.js +210 -0
  50. package/dist/nativeAgent/model/errors.js +47 -0
  51. package/dist/nativeAgent/model/gateway.js +423 -0
  52. package/dist/nativeAgent/model/index.js +7 -0
  53. package/dist/nativeAgent/model/observationAdapter.js +21 -0
  54. package/dist/nativeAgent/model/protocols.js +19 -0
  55. package/dist/nativeAgent/model/responses.js +263 -0
  56. package/dist/nativeAgent/model/types.js +1 -0
  57. package/dist/nativeAgent/model/wire.js +73 -0
  58. package/dist/nativeAgent/observability/index.js +220 -0
  59. package/dist/nativeAgent/product/catalog.js +82 -0
  60. package/dist/nativeAgent/product/config.js +295 -0
  61. package/dist/nativeAgent/product/facts.js +30 -0
  62. package/dist/nativeAgent/product/index.js +62 -0
  63. package/dist/nativeAgent/product/location.js +44 -0
  64. package/dist/nativeAgent/product/runtime.js +276 -0
  65. package/dist/nativeAgent/product/storage.js +49 -0
  66. package/dist/nativeAgent/product/tools.js +47 -0
  67. package/dist/nativeAgent/product/transport.js +54 -0
  68. package/dist/nativeAgent/projectGuidance/index.js +425 -0
  69. package/dist/nativeAgent/searchTools.js +305 -0
  70. package/dist/nativeAgent/session/backends.js +293 -0
  71. package/dist/nativeAgent/session/catalog.js +97 -0
  72. package/dist/nativeAgent/session/catalogDemo.js +87 -0
  73. package/dist/nativeAgent/session/contracts.js +1 -0
  74. package/dist/nativeAgent/session/format.js +269 -0
  75. package/dist/nativeAgent/session/index.js +5 -0
  76. package/dist/nativeAgent/session/location.js +36 -0
  77. package/dist/nativeAgent/session/sqliteFormat.js +134 -0
  78. package/dist/nativeAgent/session/store.js +248 -0
  79. package/dist/nativeAgent/textTools.js +270 -133
  80. package/dist/nativeAgent/toolManager/executor.js +290 -0
  81. package/dist/nativeAgent/toolManager/index.js +4 -0
  82. package/dist/nativeAgent/validation.js +2 -2
  83. package/dist/repository/gitWorkspace.js +41 -12
  84. package/dist/repository/taskWorkspaceCoordinator.js +72 -145
  85. package/dist/repository/taskWorkspacePreparer.js +39 -43
  86. package/dist/repository/workspaceCleanupInspection.js +88 -7
  87. package/dist/resources/resourceRegistrar.js +16 -10
  88. package/dist/runtime/integrationRuntimeIsolation.js +21 -0
  89. package/dist/runtime/taskRuntimeIsolation.js +91 -21
  90. package/dist/task/archiveDiagnostics.js +26 -8
  91. package/dist/task/archivePreflight.js +26 -9
  92. package/dist/task/leaderArchive.js +9 -15
  93. package/dist/task/taskAuthority.js +56 -0
  94. package/dist/web/assets/client/api.js +8 -0
  95. package/dist/web/assets/client/app.js +74 -9
  96. package/dist/web/assets/client/components.js +95 -36
  97. package/dist/web/assets/client/detail.js +110 -64
  98. package/dist/web/assets/client/dock.js +95 -11
  99. package/dist/web/assets/client/evidence.js +286 -94
  100. package/dist/web/assets/client/i18n.js +182 -62
  101. package/dist/web/assets/client/overview.js +9 -10
  102. package/dist/web/assets/client/records.js +142 -7
  103. package/dist/web/assets/client/sections.js +234 -198
  104. package/dist/web/assets/client/sidebar.js +31 -2
  105. package/dist/web/assets/icons.js +4 -0
  106. package/dist/web/assets/shell.js +2 -1
  107. package/dist/web/assets/styles/components.js +64 -15
  108. package/dist/web/assets/styles/layout.js +11 -4
  109. package/dist/web/assets/styles/responsive.js +16 -6
  110. package/dist/web/assets/styles/tokens.js +1 -1
  111. package/dist/web/assets/styles/views.js +62 -42
  112. package/dist/web/webServer.js +12 -1
  113. package/dist/web/webTaskSurface.js +8 -1
  114. package/docs/agent-result-consumption.md +16 -0
  115. package/docs/agent-result-consumption.zh-CN.md +13 -0
  116. package/docs/examples/agent-offline.mjs +194 -0
  117. package/docs/native-agent.md +283 -0
  118. package/docs/observability/README.md +13 -3
  119. package/docs/observability/README.zh-CN.md +10 -3
  120. package/docs/release-workflow.md +72 -9
  121. package/docs/release-workflow.zh-CN.md +55 -6
  122. package/docs/roles-and-configuration.md +32 -0
  123. package/docs/roles-and-configuration.zh-CN.md +26 -0
  124. package/docs/task-delivery.md +36 -8
  125. package/docs/task-delivery.zh-CN.md +24 -7
  126. package/i18n/README.zh-CN.md +3 -2
  127. package/package.json +1 -1
  128. package/skills/yui-leader/SKILL.md +9 -0
  129. package/skills/yui-leader/references/execution.md +2 -1
  130. package/skills/yui-leader/references/integration.md +20 -0
  131. package/skills/yui-operator/SKILL.md +4 -0
  132. package/skills/yui-reviewer/SKILL.md +8 -0
  133. package/skills/yui-runtime/SKILL.md +11 -0
  134. package/skills/yui-runtime/references/publication.md +20 -4
  135. package/skills/yui-worker/SKILL.md +3 -0
@@ -1,18 +1,290 @@
1
1
  export const EVIDENCE_SCRIPT = String.raw `
2
- // Results and evidence. Files are read on request at a fixed revision; the
3
- // selected revision survives refreshes. Reading never accepts, publishes or
4
- // executes a result.
2
+ // The Delivery tab: work items, reviews, integration and remote delivery,
3
+ // saved files and retained workspaces. Opening the tab reads the original
4
+ // evidence once per Context cursor; reading never accepts, publishes,
5
+ // executes or cleans anything.
5
6
  import { h, icon, clear } from "/assets/js/dom.js";
6
- import { formatBytes } from "/assets/js/format.js";
7
- import { card, button, note, mono, emptyState, richText, badge, observabilityMetricCard } from "/assets/js/components.js";
7
+ import { formatBytes, formatClock } from "/assets/js/format.js";
8
+ import {
9
+ card, listCard, setCardCount, button, note, mono, chip, emptyState, richText, badge, statusBadge, label, tone, dot,
10
+ timeTag, disclosure, workItemCard, workItemBody, reviewCard
11
+ } from "/assets/js/components.js";
12
+ import { entriesOf, valuesOf, totalOf, summaryFields, readExact, exactRow, lazyRow, familyState, loadFamily } from "/assets/js/records.js";
8
13
 
9
- export function renderEvidence(container, data, t, locale, actions) {
14
+ const DELIVERY_TONES = { merged: "ok", partial: "warn", unverified: "warn", uncovered: "warn", pending: "info", none: "idle", unavailable: "idle" };
15
+
16
+ function newest(left, right) {
17
+ return Date.parse(right.createdAt || 0) - Date.parse(left.createdAt || 0);
18
+ }
19
+
20
+ // Only http(s) links without credentials leave the page.
21
+ function safeUrl(value) {
22
+ try {
23
+ const url = new URL(value);
24
+ return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password ? url.href : null;
25
+ } catch { return null; }
26
+ }
27
+
28
+ export function renderDelivery(panel, data, t, locale, ctx) {
29
+ const view = data.viewState;
30
+ const core = data.core;
31
+ const task = data.task;
32
+ view.exact = view.exact || {};
33
+ view.openRows = view.openRows || {};
34
+ view.reports = view.reports || {};
35
+ view.panels = view.panels || {};
36
+ view.panels.delivery = panel;
37
+
38
+ // --- Work items ------------------------------------------------------------
39
+ const work = listCard({ id: "detail-work", title: t("work.title"), icon: "layers", count: totalOf(core, "work-item") || null, hint: t("work.openHint") });
40
+ function drawWork() {
41
+ clear(work.list);
42
+ const family = familyState(view, "work-item");
43
+ const entries = entriesOf(core, "work-item");
44
+ const titles = {};
45
+ family.items.forEach(function (item) { const fields = summaryFields(item.summary); if (fields.title) titles[item.ref.refId] = fields.title; });
46
+ valuesOf(core, "work-item").forEach(function (item) { titles[item.id] = item.title; });
47
+ const shown = new Set(entries.map(function (entry) { return entry.ref.refId; }));
48
+ const extraOpen = family.items.filter(function (item) { return item.status === "open" && !shown.has(item.ref.refId); });
49
+ const finished = family.items.filter(function (item) { return item.status !== "open"; });
50
+ entries.forEach(function (entry) {
51
+ work.list.append(exactRow(entry, task.id, t, ctx, view.exact, function (value) { return workItemCard(value, t, locale, titles); }));
52
+ });
53
+ extraOpen.forEach(function (item) { work.list.append(workRow(item, titles)); });
54
+ const loaded = family.key && family.pages;
55
+ if (loaded) setCardCount(work, family.total || null);
56
+ if (!entries.length && !extraOpen.length) {
57
+ work.list.append(emptyState(loaded && family.total === 0 ? t("work.none") : t("work.noneOpen"), "layers"));
58
+ }
59
+ if (family.error) work.list.append(note(t("records.unavailable") + " " + family.error, "bad"));
60
+ if (!loaded) {
61
+ if (family.pending) work.list.append(h("p.row-label", null, t("work.reading")));
62
+ return;
63
+ }
64
+ const finishedTotal = Math.max(finished.length, (family.total || 0) - totalOf(core, "work-item"));
65
+ if (!finishedTotal) return;
66
+ const group = h("div.row-stack.is-finished", null, h("p.row-label", null, t("work.finished") + " · " + finishedTotal));
67
+ finished.forEach(function (item) { group.append(workRow(item, titles)); });
68
+ work.list.append(group);
69
+ if (family.nextCursor) work.list.append(listFoot(family, function () {
70
+ return loadFamily(view, task.id, "work-item", family.key, ctx, { more: true, limit: 40 });
71
+ }));
72
+ }
73
+ function workRow(item, titles) {
74
+ const fields = summaryFields(item.summary);
75
+ return lazyRow(item, task.id, t, ctx, {
76
+ cache: view.exact, openRows: view.openRows,
77
+ head: [dot(tone("work", item.status)), h("span.lazy-title", null, fields.title || item.ref.refId),
78
+ item.assignee ? h("span.faint.small.lazy-meta", null, item.assignee) : null,
79
+ statusBadge(t, "work", "work", item.status)],
80
+ render: function (value) { return workItemBody(value, t, locale, titles); }
81
+ });
82
+ }
83
+ function listFoot(family, more) {
84
+ const next = button(t("history.more"), { icon: "chevron", variant: "ghost" });
85
+ next.addEventListener("click", function () {
86
+ next.disabled = true;
87
+ more().then(refresh);
88
+ });
89
+ return h("div.list-foot", null,
90
+ h("span.faint.small", null, t("history.loaded").replace("{shown}", String(family.items.length)).replace("{total}", String(family.total))),
91
+ next);
92
+ }
93
+
94
+ // --- Reviews ---------------------------------------------------------------
95
+ const reviews = listCard({ id: "detail-reviews", title: t("reviews.title"), icon: "eye", hint: t("reviews.hint") });
96
+ function drawReviews() {
97
+ clear(reviews.list);
98
+ const evidence = view.evidence && view.evidence.key && view.evidence.value;
99
+ if (evidence) {
100
+ const rounds = evidence.reviews.slice().sort(newest);
101
+ setCardCount(reviews, rounds.length || null);
102
+ if (!rounds.length) reviews.list.append(emptyState(t("reviews.none"), "eye"));
103
+ rounds.forEach(function (round) { reviews.list.append(reviewRow(round)); });
104
+ return;
105
+ }
106
+ const entries = entriesOf(core, "review-round");
107
+ setCardCount(reviews, entries.length || null);
108
+ entries.forEach(function (entry) {
109
+ reviews.list.append(exactRow(entry, task.id, t, ctx, view.exact, function (value) { return reviewRow(value); }));
110
+ });
111
+ if (view.evidence && view.evidence.error) reviews.list.append(note(t("records.unavailable") + " " + view.evidence.error, "bad"));
112
+ else reviews.list.append(h("p.row-label", null, t("evidence.loading")));
113
+ }
114
+ function reviewRow(round) {
115
+ const element = reviewCard(round, t, locale);
116
+ if (!round.reviewerRunId) return element;
117
+ const holder = h("div.stack");
118
+ const cached = view.reports[round.id];
119
+ if (cached) { holder.append(richText(t("evidence.report"), cached, t, { threshold: 480 })); element.append(holder); return element; }
120
+ const report = button(t("evidence.readReport"), { icon: "eye", variant: "link" });
121
+ report.addEventListener("click", async function () {
122
+ report.disabled = true;
123
+ try {
124
+ const source = await ctx.inspect(task.id, { store: "run", refId: round.reviewerRunId });
125
+ const output = (source.result && source.result.output) || (source.value && source.value.result && source.value.result.output) || t("evidence.noReport");
126
+ view.reports[round.id] = output;
127
+ holder.append(richText(t("evidence.report"), output, t, { threshold: 480 }));
128
+ report.remove();
129
+ } catch (error) { holder.append(note(error.message, "bad")); report.disabled = false; }
130
+ });
131
+ element.append(h("div.record-actions", null, report), holder);
132
+ return element;
133
+ }
134
+
135
+ // --- Integration and remote delivery ------------------------------------------
136
+ const delivery = card({ id: "detail-results", title: t("delivery.title"), icon: "merge", hint: t("delivery.hint") });
137
+ const deliveryBody = h("div.stack.delivery-stack");
138
+ delivery.body.append(deliveryBody);
139
+ function remoteDelivery() {
140
+ const entry = entriesOf(core, "remote-delivery")[0];
141
+ if (entry && !entry.omitted) return entry.value;
142
+ const cached = entry && view.exact["remote-delivery/" + entry.ref.refId];
143
+ if (cached && cached.digest === entry.ref.digest && cached.result) return cached.result.value;
144
+ return (data.runtime && data.runtime.remoteDelivery) || null;
145
+ }
146
+ function drawDelivery() {
147
+ clear(deliveryBody);
148
+ const remote = remoteDelivery();
149
+ if (remote) {
150
+ deliveryBody.append(h("h4.sub-head", null, icon("package", "icon-sm"), h("span", null, t("evidence.remote")),
151
+ badge(t("remoteDelivery." + remote.status, remote.status), DELIVERY_TONES[remote.status] || "idle", { dot: true })));
152
+ if (remote.codeProjectCount) {
153
+ deliveryBody.append(h("p.faint.small", null, t("delivery.counts")
154
+ .replace("{merged}", String(remote.mergedProjectCount)).replace("{verified}", String(remote.verifiedProjectCount))
155
+ .split("{total}").join(String(remote.codeProjectCount))));
156
+ }
157
+ (remote.projects || []).forEach(function (project) { deliveryBody.append(projectRow(project)); });
158
+ if (!(remote.projects || []).length) deliveryBody.append(note(t("delivery.noProjects")));
159
+ } else deliveryBody.append(note(t("evidence.observationUnavailable")));
160
+
161
+ const state = view.evidence || {};
162
+ const evidence = state.key && state.value;
163
+ deliveryBody.append(h("h4.sub-head", null, icon("merge", "icon-sm"), h("span", null, t("evidence.integrations")),
164
+ evidence ? h("span.count", null, String(evidence.integrations.length)) : null));
165
+ if (!evidence) {
166
+ deliveryBody.append(state.error ? note(t("evidence.originalUnavailable") + " " + state.error, "bad") : h("p.faint.small", null, t("evidence.loading")));
167
+ return;
168
+ }
169
+ if (!evidence.integrations.length) deliveryBody.append(note(t("evidence.noIntegrations")));
170
+ const rows = h("div.row-stack.boxed");
171
+ evidence.integrations.slice().sort(newest).forEach(function (attempt) { rows.append(integrationRow(attempt)); });
172
+ if (rows.childNodes.length) deliveryBody.append(rows);
173
+ deliveryBody.append(h("p.faint.small", null, t("delivery.readAt") + " " + formatClock(state.readAt, locale)
174
+ + (state.pending ? " · " + t("evidence.loading") : "")));
175
+ }
176
+ function projectRow(project) {
177
+ const publication = project.publication;
178
+ const href = publication && publication.externalUrl && safeUrl(publication.externalUrl);
179
+ const element = h("article.record.delivery-project");
180
+ element.append(h("header.record-head", null, icon("folder", "icon-sm"), h("strong", null, project.directory || project.projectId),
181
+ project.coverage ? chip(label(t, "coverage", project.coverage)) : null,
182
+ h("span.spacer"),
183
+ publication ? (href
184
+ ? h("a.link", { href: href, rel: "noopener noreferrer", target: "_blank" }, label(t, "publication", publication.externalKind) + " #" + publication.externalId)
185
+ : mono(publication.externalKind + " " + publication.externalId)) : null));
186
+ if (publication && publication.title) element.append(h("p.small", null, publication.title));
187
+ if (project.reason) element.append(h("p.faint.small", null, project.reason));
188
+ element.append(h("div.meta-line", null,
189
+ h("span", null, t("evidence.expectedCommit") + " "), mono(project.expectedLocalCommit ? project.expectedLocalCommit.slice(0, 12) : t("common.unknown")),
190
+ project.state ? chip(label(t, "publicationState", project.state)) : null,
191
+ project.verification ? chip(t("evidence.verification") + " · " + label(t, "verification", project.verification)) : null,
192
+ project.adoption ? h("span", null, t("evidence.adoption") + " ", mono(project.adoption.id)) : null));
193
+ return element;
194
+ }
195
+ function integrationRow(attempt) {
196
+ const source = attempt.source || {};
197
+ const element = h("article.record.integration-row");
198
+ element.dataset.status = attempt.status;
199
+ element.append(h("header.record-head", null, dot(tone("integration", attempt.status)), mono(attempt.id),
200
+ chip(label(t, "integration.source", source.kind) + (source.workItemId ? " · " + source.workItemId : "")),
201
+ source.strategy ? chip(source.strategy) : null,
202
+ h("span.spacer"),
203
+ statusBadge(t, "integration", "integration", attempt.status)));
204
+ if (attempt.summary) element.append(h("p.small", null, attempt.summary));
205
+ element.append(h("div.meta-line", null,
206
+ attempt.projectId ? h("span", null, attempt.projectId) : null,
207
+ attempt.targetRef ? mono(attempt.targetRef) : null,
208
+ h("span", null, t("evidence.candidate") + " "), mono(attempt.candidateCommit ? attempt.candidateCommit.slice(0, 12) : t("evidence.noCandidate")),
209
+ timeTag(attempt.createdAt, locale, t)));
210
+ if (attempt.checks && attempt.checks.length) {
211
+ element.append(h("ul.check-list", null, attempt.checks.map(function (check) {
212
+ return h("li", null, dot(check.outcome === "passed" ? "ok" : check.outcome === "failed" ? "bad" : "idle"),
213
+ h("span.mono-line", null, check.name), h("span.faint", null, label(t, "check", check.outcome)),
214
+ check.details ? h("span.faint.small.check-details", null, check.details) : null);
215
+ })));
216
+ } else element.append(h("p.faint.small", null, t("evidence.noChecks")));
217
+ return element;
218
+ }
219
+
220
+ // --- Workspaces and cleanup --------------------------------------------------
221
+ const workspaces = disclosure(t("workspaces.title"), "workspaces", { className: "card-disclosure", meta: String(totalOf(core, "managed-workspace")) });
222
+ workspaces.id = "detail-workspaces";
223
+ function drawWorkspaces() {
224
+ clear(workspaces.body);
225
+ const evidence = view.evidence && view.evidence.key && view.evidence.value;
226
+ const list = evidence ? evidence.workspaces : valuesOf(core, "managed-workspace");
227
+ if (!list.length) workspaces.body.append(note(t("evidence.noWorkspaces")));
228
+ const rows = h("div.row-stack.boxed");
229
+ list.forEach(function (workspace) {
230
+ const owner = workspace.owner || {};
231
+ const ownerId = owner.workItemId || owner.reviewRoundId || owner.integrationAttemptId
232
+ || (owner.executionGroupId ? owner.executionGroupId + (owner.executionLaneId ? "/" + owner.executionLaneId : "") : "") || owner.taskId || "";
233
+ rows.append(h("article.record.is-compact", null,
234
+ h("header.record-head", null, chip(label(t, "workspace.owner", owner.type)), mono(ownerId), h("span.spacer"), timeTag(workspace.updatedAt, locale, t)),
235
+ h("p.small.mono-line", null, workspace.root),
236
+ (workspace.entries || []).length ? h("div.meta-line", null, workspace.entries.map(function (entry) {
237
+ return h("span", null, entry.directory + " · ", mono(entry.branch || entry.baseRef || ""));
238
+ })) : null));
239
+ });
240
+ if (rows.childNodes.length) workspaces.body.append(rows);
241
+ workspaces.body.append(note(t("evidence.cleanupNote")), h("pre.code-block", null, "yui task archive-preflight " + task.id + " --integrated"));
242
+ }
243
+
244
+ panel.append(work, reviews, delivery, filesCard(data, t, locale, ctx), workspaces);
245
+ panel.redraw = function () { drawWork(); drawReviews(); drawDelivery(); drawWorkspaces(); };
246
+ panel.redraw();
247
+
248
+ // Reads outlive one render: they redraw whichever Delivery panel is current
249
+ // and re-read when the Context cursor moved while they were in flight.
250
+ function refresh() {
251
+ const current = view.panels.delivery;
252
+ if (!current || !current.isConnected) return;
253
+ current.redraw();
254
+ if (!current.hidden) current.onShow();
255
+ }
256
+ panel.onShow = function () {
257
+ const key = core.coreCursor;
258
+ const previous = view.evidence || {};
259
+ if (previous.key !== key && previous.errorKey !== key && !previous.pending) {
260
+ view.evidence = { key: previous.key, value: previous.value, readAt: previous.readAt, error: null, pending: true };
261
+ ctx.evidence(task.id).then(function (value) {
262
+ view.evidence = { key: key, value: value, readAt: new Date().toISOString(), error: null, pending: false };
263
+ }, function (error) {
264
+ view.evidence = { key: previous.key, value: previous.value, readAt: previous.readAt, error: error.message, errorKey: key, pending: false };
265
+ }).then(refresh);
266
+ }
267
+ const entry = entriesOf(core, "remote-delivery")[0];
268
+ if (entry && entry.omitted && !(data.runtime && data.runtime.remoteDelivery)) {
269
+ const hit = view.exact["remote-delivery/" + entry.ref.refId];
270
+ if (!(hit && hit.digest === entry.ref.digest && hit.result)) {
271
+ readExact(entry, task.id, ctx, view.exact).then(refresh, function () {});
272
+ }
273
+ }
274
+ const family = familyState(view, "work-item");
275
+ if (family.key !== key && family.errorKey !== key && !family.pending) loadFamily(view, task.id, "work-item", key, ctx, { limit: 40 }).then(refresh);
276
+ };
277
+ }
278
+
279
+ // --- Saved files ------------------------------------------------------------------
280
+ // Files are read on request at a fixed revision; the selected revision
281
+ // survives refreshes.
282
+ function filesCard(data, t, locale, actions) {
10
283
  const state = data.viewState;
11
- const files = card({ title: t("evidence.files"), icon: "file", hint: t("evidence.filesHint") });
284
+ const load = button(state.artifactList ? t("evidence.reload") : t("evidence.load"), { icon: "refresh", variant: "ghost" });
285
+ const files = card({ id: "detail-files", title: t("evidence.files"), icon: "file", hint: t("evidence.filesHint"), actions: load });
12
286
  const list = h("div.file-list");
13
287
  const viewer = h("div.file-viewer");
14
- const load = button(state.artifactList ? t("evidence.reload") : t("evidence.load"), { icon: "refresh", variant: "ghost" });
15
- files.querySelector(".card-head").append(h("div.card-actions", null, load));
16
288
  let readSequence = 0;
17
289
  async function read(path, commit) {
18
290
  const sequence = ++readSequence;
@@ -41,8 +313,7 @@ export function renderEvidence(container, data, t, locale, actions) {
41
313
  viewer.append(h("header.viewer-head", null, icon("file"), h("strong", null, value.relativePath), h("span.spacer"), copy));
42
314
  viewer.append(h("p.viewer-ref", null, mono(ref)));
43
315
  viewer.append(h("p.faint.small", null, t("evidence.fixedNote")));
44
- const content = h("pre.code-block.artifact-text", { tabIndex: 0, "aria-label": t("evidence.fixedText") }, value.content);
45
- viewer.append(content);
316
+ viewer.append(h("pre.code-block.artifact-text", { tabIndex: 0, "aria-label": t("evidence.fixedText") }, value.content));
46
317
  }
47
318
  function drawList() {
48
319
  clear(list);
@@ -63,6 +334,7 @@ export function renderEvidence(container, data, t, locale, actions) {
63
334
  list.dataset.reading = "true";
64
335
  try {
65
336
  state.artifactList = await actions.artifacts(data.task.id);
337
+ if (placeholder) { placeholder.remove(); placeholder = null; }
66
338
  drawList();
67
339
  } catch (error) {
68
340
  clear(list);
@@ -82,91 +354,11 @@ export function renderEvidence(container, data, t, locale, actions) {
82
354
  referenced.append(h("button.file-row", { type: "button", onclick: function () { read(match[2], match[1]); } },
83
355
  icon("flag", "icon-sm"), h("span.file-name", null, match[2]), badge(t("evidence.referenced"), "accent")));
84
356
  });
85
- if (!state.artifactList && !seen.size) files.body.append(emptyState(t("evidence.notLoaded"), "file"));
357
+ let placeholder = !state.artifactList && !seen.size ? emptyState(t("evidence.notLoaded"), "file") : null;
358
+ if (placeholder) files.body.append(placeholder);
86
359
  files.body.append(referenced, list, viewer);
87
360
  drawList();
88
361
  drawViewer();
89
- container.append(files);
90
-
91
- const delivery = card({ title: t("evidence.delivery"), icon: "layers", hint: t("evidence.deliveryHint") });
92
- const output = h("div.stack");
93
- const open = button(t("evidence.readDelivery"), { icon: "eye", variant: "ghost" });
94
- delivery.querySelector(".card-head").append(h("div.card-actions", null, open));
95
- delivery.body.append(output);
96
- open.addEventListener("click", function () {
97
- open.disabled = true;
98
- drawDelivery(output, data, t, actions).finally(function () { open.disabled = false; });
99
- });
100
- container.append(delivery);
101
- }
102
-
103
- async function drawDelivery(output, data, t, actions) {
104
- clear(output);
105
- const runtime = data.runtimeStatus === "available" ? data.runtime : null;
106
- if (!runtime) output.append(note(t("evidence.observationUnavailable")));
107
- const remote = runtime && runtime.remoteDelivery;
108
- if (remote) {
109
- output.append(h("h4.sub-head", null, t("evidence.remote"), badge(t("remoteDelivery." + remote.status, remote.status), remote.status === "merged" ? "ok" : "idle")));
110
- remote.projects.forEach(function (project) {
111
- output.append(h("article.record", null,
112
- h("header.record-head", null, h("strong", null, project.directory), h("span.faint.small", null, project.coverage)),
113
- h("p.small", null, project.reason),
114
- h("p.faint.small", null, t("evidence.expectedCommit") + " ", mono(project.expectedLocalCommit || t("common.unknown"))),
115
- h("p.faint.small", null, "PR/MR · " + (project.state || "none") + " · " + t("evidence.verification") + " " + (project.verification || "unverified")),
116
- project.adoption ? h("p.faint.small", null, t("evidence.adoption") + " ", mono(project.adoption.id)) : null));
117
- });
118
- }
119
- const original = h("div.stack", null, h("p.faint", null, t("evidence.readingOriginal")));
120
- output.append(original);
121
- try {
122
- const evidence = await actions.evidence(data.task.id);
123
- clear(original);
124
- original.append(h("h4.sub-head", null, t("evidence.integrations")));
125
- if (!evidence.integrations.length) original.append(note(t("evidence.noIntegrations")));
126
- evidence.integrations.forEach(function (attempt) {
127
- const element = h("article.record", null,
128
- h("header.record-head", null, mono(attempt.id), h("span.spacer"), badge(attempt.status, attempt.status === "succeeded" ? "ok" : "idle")),
129
- h("p.faint.small", null, t("evidence.candidate") + " ", mono(attempt.candidateCommit || t("evidence.noCandidate"))));
130
- if (!attempt.checks || !attempt.checks.length) element.append(h("p.faint.small", null, t("evidence.noChecks")));
131
- (attempt.checks || []).forEach(function (check) {
132
- element.append(h("p.small", null, check.name + " · " + check.outcome));
133
- if (check.details) element.append(h("p.faint.small", null, check.details));
134
- });
135
- original.append(element);
136
- });
137
- original.append(h("h4.sub-head", null, t("evidence.reviews")));
138
- if (!evidence.reviews.length) original.append(note(t("evidence.noReviews")));
139
- evidence.reviews.forEach(function (round) {
140
- const element = h("article.record", null,
141
- h("header.record-head", null, mono(round.id), h("span.spacer"), badge(round.status, round.status === "completed" ? "ok" : "idle")),
142
- h("p.faint.small.mono-line", null, round.taskCandidate
143
- ? round.taskCandidate.projects.map(function (p) { return p.projectId + " · " + p.commit; }).join(" ")
144
- : [round.workItemId, round.candidateId, round.reviewBaseCommit].filter(Boolean).join(" · ")));
145
- if (round.reviewerRunId) {
146
- const report = button(t("evidence.readReport"), { icon: "eye", variant: "link" });
147
- report.addEventListener("click", async function () {
148
- report.disabled = true;
149
- try {
150
- const source = await actions.inspect(data.task.id, { store: "run", refId: round.reviewerRunId });
151
- element.append(richText(round.reviewerRunId, (source.value.result && source.value.result.output) || t("evidence.noReport"), t));
152
- report.remove();
153
- } catch (error) { element.append(note(error.message, "bad")); report.disabled = false; }
154
- });
155
- element.append(report);
156
- }
157
- original.append(element);
158
- });
159
- original.append(h("h4.sub-head", null, t("evidence.workspaces")));
160
- if (!evidence.workspaces.length) original.append(note(t("evidence.noWorkspaces")));
161
- evidence.workspaces.forEach(function (workspace) {
162
- original.append(h("p.small.mono-line", null, workspace.owner.type + " · " + workspace.root));
163
- });
164
- original.append(note(t("evidence.cleanupNote")), h("pre.code-block", null, "yui task archive-preflight " + data.task.id + " --integrated"));
165
- } catch (error) {
166
- clear(original);
167
- original.append(note(t("evidence.originalUnavailable") + " " + error.message, "bad"));
168
- }
169
- const metrics = runtime && observabilityMetricCard(runtime.observability, t);
170
- if (metrics) output.append(h("h4.sub-head", null, t("evidence.usage")), metrics);
362
+ return files;
171
363
  }
172
364
  `;