@zq-silk/yui 2.0.0 → 2.1.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.
- package/dist/cli/commandCatalog.js +294 -286
- package/dist/cli/commandDiscovery.js +69 -0
- package/dist/cli/completion.js +22 -227
- package/dist/cli/dynamicCompletion.js +12 -6
- package/dist/cli/invocationAuthority.js +2 -2
- package/dist/cli/invocationRouter.js +18 -7
- package/dist/cli.js +71 -23
- package/dist/commands/capabilityCommands.js +5 -5
- package/dist/commands/globalRoleCommands.js +2 -1
- package/dist/commands/taskCommands.js +11 -0
- package/dist/context/taskContext.js +6 -2
- package/dist/kernel/accessAssessment.js +1 -0
- package/dist/kernel/builtinCapabilities.js +28 -1
- package/dist/kernel/capabilityRegistry.js +64 -19
- package/dist/nativeAgent/agent.js +173 -0
- package/dist/nativeAgent/contracts.js +1 -0
- package/dist/nativeAgent/demo.js +29 -0
- package/dist/nativeAgent/index.js +3 -0
- package/dist/nativeAgent/mockProvider.js +43 -0
- package/dist/nativeAgent/textTools.js +156 -0
- package/dist/nativeAgent/validation.js +95 -0
- package/dist/plugins/pluginService.js +98 -8
- package/dist/resources/projectResourceService.js +23 -1
- package/dist/runtime/managedIdentity.js +6 -0
- package/dist/surface/surfaceContributions.js +3 -0
- package/dist/task/taskAuthority.js +5 -10
- package/dist/web/assets/assetManifest.js +38 -22
- package/dist/web/assets/client/api.js +115 -0
- package/dist/web/assets/client/app.js +555 -863
- package/dist/web/assets/client/components.js +323 -897
- package/dist/web/assets/client/detail.js +333 -0
- package/dist/web/assets/client/dock.js +225 -0
- package/dist/web/assets/client/dom.js +55 -3
- package/dist/web/assets/client/evidence.js +172 -0
- package/dist/web/assets/client/format.js +25 -14
- package/dist/web/assets/client/forms.js +230 -0
- package/dist/web/assets/client/i18n.js +1020 -791
- package/dist/web/assets/client/overview.js +115 -0
- package/dist/web/assets/client/records.js +63 -0
- package/dist/web/assets/client/sections.js +313 -0
- package/dist/web/assets/client/sidebar.js +117 -0
- package/dist/web/assets/client/theme.js +61 -22
- package/dist/web/assets/icons.js +39 -0
- package/dist/web/assets/shell.js +124 -118
- package/dist/web/assets/styles/base.js +41 -0
- package/dist/web/assets/styles/components.js +167 -0
- package/dist/web/assets/styles/layout.js +49 -77
- package/dist/web/assets/styles/markdown.js +16 -24
- package/dist/web/assets/styles/responsive.js +33 -44
- package/dist/web/assets/styles/tokens.js +65 -89
- package/dist/web/assets/styles/views.js +289 -0
- package/dist/web/webServer.js +2 -1
- package/docs/release-workflow.md +23 -0
- package/docs/release-workflow.zh-CN.md +16 -0
- package/package.json +1 -1
- package/skills/yui-runtime/SKILL.md +26 -0
- package/dist/web/assets/client/taskSummary.js +0 -350
- package/dist/web/assets/client/taskSurface.js +0 -616
- package/dist/web/assets/client/view.js +0 -608
- package/dist/web/assets/styles/cards.js +0 -247
- package/dist/web/assets/styles/widgets.js +0 -168
|
@@ -1,992 +1,418 @@
|
|
|
1
1
|
import { AGENT_ADAPTER_CATALOG } from "../../../agent/adapterCatalog.js";
|
|
2
2
|
import { AGENT_EXECUTION_COMPONENT_CATALOG } from "../../../agent/executionComponents.js";
|
|
3
|
-
export const COMPONENTS_SCRIPT = `
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
import { node } from "/assets/js/dom.js";
|
|
3
|
+
export const COMPONENTS_SCRIPT = String.raw `
|
|
4
|
+
// Pure builders: data + translator in, DOM out. Views compose these; nothing
|
|
5
|
+
// here reads application state or performs a request.
|
|
6
|
+
import { h, icon, node } from "/assets/js/dom.js";
|
|
7
7
|
import { formatDateTime, relativeTime } from "/assets/js/format.js";
|
|
8
|
-
import {
|
|
8
|
+
import { renderMarkdown } from "/assets/js/markdown.js";
|
|
9
9
|
|
|
10
10
|
// Generated from the server catalogs, not a second product registry.
|
|
11
11
|
const adapterLabels = ${JSON.stringify(Object.fromEntries(AGENT_ADAPTER_CATALOG.map(({ id, label }) => [id, label])))};
|
|
12
12
|
const componentLabels = ${JSON.stringify(Object.fromEntries(AGENT_EXECUTION_COMPONENT_CATALOG.map(({ id, label }) => [id, label])))};
|
|
13
|
-
function
|
|
14
|
-
return adapterLabels[adapterId] || adapterId;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
export function agentLabel(component, adapterId) {
|
|
14
|
+
return component ? componentLabels[component] || component : adapterLabels[adapterId] || adapterId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// --- Vocabulary ----------------------------------------------------------------
|
|
18
|
+
// A translated label for a domain value; unknown values stay visible verbatim
|
|
19
|
+
// instead of being hidden or guessed.
|
|
20
|
+
export function label(t, prefix, value) {
|
|
21
|
+
if (value === undefined || value === null || value === "") return "—";
|
|
22
|
+
return t(prefix + "." + value, String(value).replace(/[-_]/g, " "));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const TONES = {
|
|
26
|
+
task: { draft: "warn", active: "info", completed: "ok", cancelled: "idle", archived: "idle" },
|
|
27
|
+
exec: {
|
|
28
|
+
"needs-leader-action": "accent", "waiting-on-agents": "info", "waiting-user": "warn",
|
|
29
|
+
recovering: "warn", attention: "bad", "progressing-with-attention": "warn", blocked: "bad",
|
|
30
|
+
conflicted: "warn", working: "info", completed: "ok", retired: "idle", cancelled: "idle",
|
|
31
|
+
accepted: "ok", open: "info", archived: "idle"
|
|
32
|
+
},
|
|
33
|
+
run: { active: "info", completed: "ok", failed: "bad" },
|
|
34
|
+
work: { open: "info", accepted: "ok", retired: "idle" },
|
|
35
|
+
review: { pending: "idle", running: "info", completed: "ok", failed: "bad" },
|
|
36
|
+
job: { queued: "idle", running: "info", succeeded: "ok", failed: "bad", "timed-out": "bad",
|
|
37
|
+
cancelled: "idle", "unknown-needs-attention": "warn" },
|
|
38
|
+
session: { active: "ok", waiting: "warn", quiet: "idle", diagnostic: "bad", unknown: "idle",
|
|
39
|
+
stopped: "bad", idle: "idle", background: "warn" },
|
|
40
|
+
role: { running: "ok", waiting: "warn", idle: "idle", unknown: "idle", failed: "bad", exited: "idle", detached: "idle" },
|
|
41
|
+
decision: { active: "ok", superseded: "idle" },
|
|
42
|
+
input: { open: "warn", answered: "ok", cancelled: "idle", "auto-resolved": "ok" }
|
|
43
|
+
};
|
|
44
|
+
export function tone(kind, value) {
|
|
45
|
+
return (TONES[kind] && TONES[kind][value]) || "idle";
|
|
18
46
|
}
|
|
19
47
|
|
|
20
|
-
export function
|
|
21
|
-
|
|
48
|
+
export function badge(text, toneName, options) {
|
|
49
|
+
const element = h("span.badge.tone-" + (toneName || "idle"), null,
|
|
50
|
+
options && options.dot ? h("i.badge-dot", { "aria-hidden": "true" }) : null, text);
|
|
51
|
+
if (options && options.title) element.title = options.title;
|
|
52
|
+
return element;
|
|
22
53
|
}
|
|
23
54
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return item;
|
|
55
|
+
export function statusBadge(t, kind, prefix, value) {
|
|
56
|
+
const element = badge(label(t, prefix, value), tone(kind, value), { dot: true });
|
|
57
|
+
element.dataset.status = value;
|
|
58
|
+
return element;
|
|
29
59
|
}
|
|
30
60
|
|
|
31
|
-
export function
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
item.append(node("small", "", label), path);
|
|
36
|
-
return item;
|
|
61
|
+
export function dot(toneName, title) {
|
|
62
|
+
const element = h("span.dot.tone-" + (toneName || "idle"), { "aria-hidden": title ? null : "true" });
|
|
63
|
+
if (title) { element.title = title; element.setAttribute("role", "img"); element.setAttribute("aria-label", title); }
|
|
64
|
+
return element;
|
|
37
65
|
}
|
|
38
66
|
|
|
39
67
|
export function chip(text, extraClass) {
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function agentBadge(agent) {
|
|
44
|
-
if (!agent) return null;
|
|
45
|
-
const badge = node("span", "agent-badge");
|
|
46
|
-
if (agent.adapterId || agent.component) {
|
|
47
|
-
badge.append(chip(componentLabel(agent.component, agent.adapterId), "is-adapter"));
|
|
48
|
-
}
|
|
49
|
-
if (agent.model) badge.append(chip(agent.model));
|
|
50
|
-
if (agent.effort) badge.append(chip(agent.effort));
|
|
51
|
-
return badge.childNodes.length ? badge : null;
|
|
68
|
+
return h("span.chip" + (extraClass ? "." + extraClass : ""), null, text);
|
|
52
69
|
}
|
|
53
70
|
|
|
54
|
-
export function
|
|
55
|
-
|
|
56
|
-
if (!list.length) return null;
|
|
57
|
-
const block = node("div", "record-block");
|
|
58
|
-
block.append(node("small", "", label));
|
|
59
|
-
const row = node("div", "chip-row");
|
|
60
|
-
list.forEach(function (value) {
|
|
61
|
-
row.append(chip(String(value), activeValue !== undefined && value === activeValue ? "is-active" : ""));
|
|
62
|
-
});
|
|
63
|
-
block.append(row);
|
|
64
|
-
return block;
|
|
71
|
+
export function mono(text, extraClass) {
|
|
72
|
+
return h("code.id" + (extraClass ? "." + extraClass : ""), null, text);
|
|
65
73
|
}
|
|
66
74
|
|
|
67
|
-
export function
|
|
68
|
-
|
|
69
|
-
element
|
|
75
|
+
export function timeTag(iso, locale, t, options) {
|
|
76
|
+
if (!iso) return null;
|
|
77
|
+
const element = h("time.when", { dateTime: iso }, options && options.absolute
|
|
78
|
+
? formatDateTime(iso, locale) : relativeTime(iso, locale, t));
|
|
79
|
+
element.title = formatDateTime(iso, locale);
|
|
70
80
|
return element;
|
|
71
81
|
}
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function anchorSection(id, head, body) {
|
|
93
|
-
const section = node("section", "detail-section anchor");
|
|
94
|
-
section.id = id;
|
|
95
|
-
if (head) section.append(head);
|
|
96
|
-
section.append(body);
|
|
97
|
-
return section;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// --- Task-first execution status -------------------------------------------
|
|
101
|
-
// The projection is the current read-model vocabulary: one derived status,
|
|
102
|
-
// owner, next action, and the attention/blocker facts behind it.
|
|
103
|
-
const EXEC_STATUS_TONE = {
|
|
104
|
-
"needs-leader-action": "is-accent",
|
|
105
|
-
"waiting-on-agents": "is-active",
|
|
106
|
-
"waiting-user": "is-warning",
|
|
107
|
-
"recovering": "is-warning",
|
|
108
|
-
"attention": "is-danger",
|
|
109
|
-
"progressing-with-attention": "is-warning",
|
|
110
|
-
"blocked": "is-danger",
|
|
111
|
-
"conflicted": "is-warning",
|
|
112
|
-
"working": "is-active",
|
|
113
|
-
"completed": "is-muted",
|
|
114
|
-
"retired": "is-muted",
|
|
115
|
-
"cancelled": "is-muted",
|
|
116
|
-
"accepted": "is-muted",
|
|
117
|
-
"open": "is-active",
|
|
118
|
-
"archived": "is-muted"
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
export function executionBand(projection, t, locale) {
|
|
122
|
-
if (!projection) return null;
|
|
123
|
-
const tone = EXEC_STATUS_TONE[projection.status] || "";
|
|
124
|
-
const band = node("div", "exec-band " + tone);
|
|
125
|
-
|
|
126
|
-
const head = node("div", "exec-band-head");
|
|
127
|
-
head.append(pill(t, "exec.status", projection.status));
|
|
128
|
-
head.append(node("span", "exec-band-owner",
|
|
129
|
-
t("exec.owner." + projection.owner) + " · " + t("exec.action." + projection.action)));
|
|
130
|
-
if (projection.activeRuns && projection.activeRuns.length > 0) {
|
|
131
|
-
head.append(node("span", "exec-band-executors",
|
|
132
|
-
projection.activeRuns.length + " " + t("exec.executors")));
|
|
133
|
-
}
|
|
134
|
-
if (projection.monitoring === "stopped") {
|
|
135
|
-
head.append(node("span", "exec-band-stopped", t("exec.monitoring.stopped")));
|
|
136
|
-
}
|
|
137
|
-
if (projection.failClosed) {
|
|
138
|
-
head.append(node("span", "exec-band-failclosed", t("exec.failClosed")));
|
|
139
|
-
}
|
|
140
|
-
band.append(head);
|
|
141
|
-
|
|
142
|
-
if (projection.summary) {
|
|
143
|
-
band.append(node("p", "exec-band-summary", projection.summary));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (projection.attention && projection.attention.length) {
|
|
147
|
-
const list = node("div", "exec-signal-list");
|
|
148
|
-
projection.attention.forEach(function (item) {
|
|
149
|
-
const row = node("div", "exec-signal is-attention");
|
|
150
|
-
row.append(node("span", "exec-signal-kind", t("exec.attention." + item.kind)));
|
|
151
|
-
row.append(node("span", "exec-signal-text", item.summary));
|
|
152
|
-
list.append(row);
|
|
153
|
-
});
|
|
154
|
-
band.append(list);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (projection.blockers && projection.blockers.length) {
|
|
158
|
-
const list = node("div", "exec-signal-list");
|
|
159
|
-
projection.blockers.forEach(function (blocker) {
|
|
160
|
-
const row = node("div", "exec-signal is-blocker");
|
|
161
|
-
row.append(node("span", "exec-signal-kind",
|
|
162
|
-
t("exec.blocker." + blocker.kind) + " · " + t("exec.owner." + blocker.owner)));
|
|
163
|
-
row.append(node("span", "exec-signal-text", blocker.summary));
|
|
164
|
-
list.append(row);
|
|
165
|
-
});
|
|
166
|
-
band.append(list);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return band;
|
|
83
|
+
// --- Containers -------------------------------------------------------------------
|
|
84
|
+
export function card(options) {
|
|
85
|
+
const opts = options || {};
|
|
86
|
+
const element = h("section.card" + (opts.className ? "." + opts.className.split(" ").join(".") : ""));
|
|
87
|
+
if (opts.id) element.id = opts.id;
|
|
88
|
+
if (opts.title || opts.actions) {
|
|
89
|
+
const head = h("header.card-head", null,
|
|
90
|
+
h("h3.card-title", null, opts.icon ? icon(opts.icon) : null, h("span", null, opts.title),
|
|
91
|
+
opts.count !== undefined && opts.count !== null ? h("span.count", null, String(opts.count)) : null),
|
|
92
|
+
opts.actions ? h("div.card-actions", null, opts.actions) : null);
|
|
93
|
+
if (opts.hint) head.append(h("p.card-hint", null, opts.hint));
|
|
94
|
+
element.append(head);
|
|
95
|
+
}
|
|
96
|
+
const body = h("div.card-body");
|
|
97
|
+
element.append(body);
|
|
98
|
+
element.body = body;
|
|
99
|
+
return element;
|
|
170
100
|
}
|
|
171
101
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
card.append(head);
|
|
184
|
-
|
|
185
|
-
if (projection.lanes && projection.lanes.length) {
|
|
186
|
-
const lanes = node("div", "lane-list");
|
|
187
|
-
projection.lanes.forEach(function (lane) {
|
|
188
|
-
const row = node("div", "lane-row");
|
|
189
|
-
row.append(statusDot(lane.status));
|
|
190
|
-
row.append(node("span", "lane-role", lane.roleName));
|
|
191
|
-
row.append(node("span", "lane-status", t("workExec.lane." + lane.status)));
|
|
192
|
-
if (lane.delivery) row.append(chip(t("run.delivery." + lane.delivery)));
|
|
193
|
-
row.append(node("span", "mono", lane.currentRunId || t("detail.unobserved")));
|
|
194
|
-
if (lane.retryRunId) row.append(chip(t("workExec.retry") + " · " + lane.retryRunId, "is-danger"));
|
|
195
|
-
if (lane.settleRunId) row.append(chip(t("workExec.settle") + " · " + lane.settleRunId));
|
|
196
|
-
if (lane.session === "unobserved") row.append(chip(t("detail.unobserved")));
|
|
197
|
-
lanes.append(row);
|
|
198
|
-
});
|
|
199
|
-
card.append(lanes);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const facts = node("div", "record-meta execution-resource-meta");
|
|
203
|
-
facts.append(node("span", "", t("workExec.main") + " · "
|
|
204
|
-
+ (projection.mainRun.runId || t("detail.unobserved"))
|
|
205
|
-
+ " [" + t("workExec.mainStatus." + projection.mainRun.status) + "]"));
|
|
206
|
-
facts.append(node("span", "", t("workExec.candidate") + " · "
|
|
207
|
-
+ (projection.candidate.candidateId || t("workExec.none"))
|
|
208
|
-
+ " [" + t("workExec.candidateStatus." + projection.candidate.status) + "]"));
|
|
209
|
-
card.append(facts);
|
|
210
|
-
|
|
211
|
-
if (projection.candidate.sourceExecutionGroupId) {
|
|
212
|
-
const provenance = projection.candidate.successfulLaneRuns.map(function (lane) {
|
|
213
|
-
return lane.laneId + " → " + lane.successfulRunId;
|
|
214
|
-
}).join(", ") || t("detail.unobserved");
|
|
215
|
-
card.append(node("p", "muted mono", (projection.candidate.mainRunId || t("detail.unobserved"))
|
|
216
|
-
+ " → " + projection.candidate.sourceExecutionGroupId + " → " + provenance));
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const next = node("div", "exec-resolution");
|
|
220
|
-
next.append(node("span", "exec-resolution-decision", t("workExec.next")));
|
|
221
|
-
next.append(node("span", "exec-resolution-summary",
|
|
222
|
-
t("workExec.action." + projection.nextAction.kind)
|
|
223
|
-
+ " · " + t("workExec.owner") + " "
|
|
224
|
-
+ (projection.nextAction.owners.join(", ") || t("workExec.none"))));
|
|
225
|
-
if (projection.nextAction.targetIds.length) {
|
|
226
|
-
next.append(node("span", "mono", projection.nextAction.targetIds.join(", ")));
|
|
227
|
-
}
|
|
228
|
-
card.append(next);
|
|
229
|
-
return card;
|
|
102
|
+
export function disclosure(title, viewKey, options) {
|
|
103
|
+
const opts = options || {};
|
|
104
|
+
const element = h("details.disclosure" + (opts.className ? "." + opts.className : ""));
|
|
105
|
+
if (viewKey) element.dataset.viewKey = viewKey;
|
|
106
|
+
const summary = h("summary", null, icon("chevron", "disclosure-chevron"), h("span.disclosure-title", null, title));
|
|
107
|
+
if (opts.meta) summary.append(h("span.disclosure-meta", null, opts.meta));
|
|
108
|
+
element.append(summary);
|
|
109
|
+
const body = h("div.disclosure-body");
|
|
110
|
+
element.append(body);
|
|
111
|
+
element.body = body;
|
|
112
|
+
return element;
|
|
230
113
|
}
|
|
231
114
|
|
|
232
|
-
export function
|
|
233
|
-
|
|
234
|
-
|
|
115
|
+
export function sectionTitle(text, count, actions) {
|
|
116
|
+
return h("div.section-title", null, h("h2", null, text,
|
|
117
|
+
count !== undefined && count !== null ? h("span.count", null, String(count)) : null),
|
|
118
|
+
actions ? h("div.section-actions", null, actions) : null);
|
|
235
119
|
}
|
|
236
120
|
|
|
237
|
-
function
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return tile;
|
|
121
|
+
export function kv(rows) {
|
|
122
|
+
const list = h("dl.kv");
|
|
123
|
+
rows.forEach(function (row) {
|
|
124
|
+
if (!row || row[1] === undefined || row[1] === null || row[1] === "") return;
|
|
125
|
+
list.append(h("dt", null, row[0]), h("dd", null, row[1]));
|
|
126
|
+
});
|
|
127
|
+
return list;
|
|
245
128
|
}
|
|
246
129
|
|
|
247
|
-
export function
|
|
248
|
-
|
|
249
|
-
const card = node("div", "observability-metrics");
|
|
250
|
-
const cost = observability.cost || {};
|
|
251
|
-
const context = observability.context || {};
|
|
252
|
-
card.append(usageTile(t("detail.tokens"), cost.tokens, t));
|
|
253
|
-
card.append(usageTile(t("detail.toolCalls"), cost.toolCalls, t));
|
|
254
|
-
card.append(usageTile(t("detail.elapsed"), cost.elapsedSeconds, t, "s"));
|
|
255
|
-
card.append(usageTile(t("detail.executionSum"), cost.executionSeconds, t, "s"));
|
|
256
|
-
card.append(metricTile(t("detail.ready"), (observability.dag?.readyIds || []).length, { hot: true }));
|
|
257
|
-
card.append(metricTile(t("detail.contextSnapshots"), context.snapshotCount));
|
|
258
|
-
const contextMeta = node("div", "record-meta observability-context-meta");
|
|
259
|
-
contextMeta.append(node("span", "", t("detail.usageScope")));
|
|
260
|
-
contextMeta.append(node("span", "", t("detail.observedThrough") + " · "
|
|
261
|
-
+ (cost.observedThrough ? formatDateTime(cost.observedThrough) : t("detail.unobserved"))));
|
|
262
|
-
contextMeta.append(node("span", "", t("detail.contextBytes") + " · "
|
|
263
|
-
+ (context.totalBytes === null ? t("detail.partial") : context.totalBytes + " B")));
|
|
264
|
-
contextMeta.append(node("span", "", t("detail.compression") + " · " + t("detail.unavailable")));
|
|
265
|
-
contextMeta.append(node("span", "", t("detail.marginalValue") + " · " + t("detail.unavailable")));
|
|
266
|
-
card.append(contextMeta);
|
|
267
|
-
return card;
|
|
130
|
+
export function emptyState(text, iconName, extra) {
|
|
131
|
+
return h("div.empty", null, iconName ? icon(iconName) : null, h("p", null, text), extra || null);
|
|
268
132
|
}
|
|
269
133
|
|
|
270
|
-
export function
|
|
271
|
-
|
|
272
|
-
const graph = node("div", "dag-graph");
|
|
273
|
-
const edgeByTarget = {};
|
|
274
|
-
(dag.edges || []).forEach(function (edge) {
|
|
275
|
-
if (!edgeByTarget[edge.to]) edgeByTarget[edge.to] = [];
|
|
276
|
-
edgeByTarget[edge.to].push(edge);
|
|
277
|
-
});
|
|
278
|
-
dag.nodes.forEach(function (item) {
|
|
279
|
-
const row = node("div", "dag-node is-" + item.projectedStatus);
|
|
280
|
-
const top = node("div", "dag-node-head");
|
|
281
|
-
top.append(statusDot(item.projectedStatus), node("strong", "dag-node-title", item.title));
|
|
282
|
-
top.append(pill(t, "dag", item.projectedStatus));
|
|
283
|
-
if ((dag.readyIds || []).indexOf(item.id) >= 0) top.append(chip(t("dag.ready"), "is-active"));
|
|
284
|
-
row.append(top);
|
|
285
|
-
const edges = edgeByTarget[item.id] || [];
|
|
286
|
-
if (edges.length) {
|
|
287
|
-
const deps = node("div", "dag-node-deps");
|
|
288
|
-
deps.append(node("small", "", t("dag.dependsOn")));
|
|
289
|
-
edges.forEach(function (edge) {
|
|
290
|
-
deps.append(chip(edge.from + " · " + t("dag.edge." + edge.status), "is-" + edge.status));
|
|
291
|
-
});
|
|
292
|
-
row.append(deps);
|
|
293
|
-
}
|
|
294
|
-
if (item.rootCauseIds && item.rootCauseIds.length) {
|
|
295
|
-
row.append(node("small", "dag-root-cause", t("dag.rootCause") + " · " + item.rootCauseIds.join(" ← ")));
|
|
296
|
-
}
|
|
297
|
-
graph.append(row);
|
|
298
|
-
});
|
|
299
|
-
return graph;
|
|
134
|
+
export function note(text, toneName) {
|
|
135
|
+
return h("p.note" + (toneName ? ".tone-" + toneName : ""), null, text);
|
|
300
136
|
}
|
|
301
137
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
.forEach(function (candidate) {
|
|
310
|
-
const row = node("div", "candidate-row");
|
|
311
|
-
row.append(node("span", "candidate-seq", "#" + candidate.sequence));
|
|
312
|
-
row.append(node("span", "candidate-summary", candidate.summary));
|
|
313
|
-
const source = candidate.source.type === "direct"
|
|
314
|
-
? t("candidate.source.direct")
|
|
315
|
-
: t("candidate.source.run") + " " + candidate.source.runId;
|
|
316
|
-
row.append(node("span", "candidate-source", source));
|
|
317
|
-
row.append(node("time", "", formatDateTime(candidate.createdAt, locale)));
|
|
318
|
-
list.append(row);
|
|
319
|
-
});
|
|
320
|
-
block.append(list);
|
|
321
|
-
return block;
|
|
138
|
+
export function button(text, options) {
|
|
139
|
+
const opts = options || {};
|
|
140
|
+
const element = h("button.btn" + (opts.variant ? ".btn-" + opts.variant : ""), {
|
|
141
|
+
type: opts.type || "button", onclick: opts.onClick, disabled: !!opts.disabled, title: opts.title
|
|
142
|
+
}, opts.icon ? icon(opts.icon) : null, text ? h("span", null, text) : null);
|
|
143
|
+
if (opts.dataset) Object.assign(element.dataset, opts.dataset);
|
|
144
|
+
return element;
|
|
322
145
|
}
|
|
323
146
|
|
|
324
|
-
// ---
|
|
325
|
-
// Re-render is cheap and happens whenever the underlying data changes, so all
|
|
326
|
-
// disclosure state (collapsed blocks, visible pages) is deliberately
|
|
327
|
-
// deterministic per render: long content starts collapsed and expanding is a
|
|
328
|
-
// pure local toggle.
|
|
147
|
+
// --- Rich text ---------------------------------------------------------------------
|
|
329
148
|
function shouldCollapse(text, threshold) {
|
|
330
|
-
const limit = threshold || 700;
|
|
331
149
|
const value = String(text);
|
|
332
|
-
if (value.length >
|
|
150
|
+
if (value.length > (threshold || 700)) return true;
|
|
333
151
|
let lines = 0;
|
|
334
|
-
for (let
|
|
335
|
-
if (value[i] === "\\n") lines += 1;
|
|
336
|
-
}
|
|
152
|
+
for (let index = 0; index < value.length; index += 1) if (value[index] === "\n") lines += 1;
|
|
337
153
|
return lines > 12;
|
|
338
154
|
}
|
|
339
155
|
|
|
340
|
-
function
|
|
341
|
-
block.classList.add("is-collapsible", "is-collapsed");
|
|
342
|
-
const toggle = node("button", "md-toggle", t("actions.showMore"));
|
|
343
|
-
toggle.type = "button";
|
|
344
|
-
toggle.addEventListener("click", function () {
|
|
345
|
-
const collapsed = block.classList.toggle("is-collapsed");
|
|
346
|
-
toggle.textContent = collapsed ? t("actions.showMore") : t("actions.showLess");
|
|
347
|
-
});
|
|
348
|
-
block.append(toggle);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Rich text block: small label + Markdown body, auto-collapsed when long.
|
|
352
|
-
export function richText(label, text, t, options) {
|
|
156
|
+
export function richText(title, text, t, options) {
|
|
353
157
|
if (!text) return null;
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const body = node("div", "md" + (options && options.muted ? " muted" : ""));
|
|
158
|
+
const opts = options || {};
|
|
159
|
+
const block = h("div.prose-block" + (opts.className ? "." + opts.className : ""));
|
|
160
|
+
if (title) block.append(h("h4.prose-label", null, title));
|
|
161
|
+
const body = h("div.md" + (opts.muted ? ".muted" : ""));
|
|
359
162
|
body.innerHTML = renderMarkdown(text);
|
|
360
163
|
block.append(body);
|
|
361
|
-
if (shouldCollapse(text,
|
|
164
|
+
if (shouldCollapse(text, opts.threshold)) {
|
|
165
|
+
block.classList.add("is-collapsed");
|
|
166
|
+
const toggle = h("button.link-btn", { type: "button" }, t("actions.showMore"));
|
|
167
|
+
toggle.addEventListener("click", function () {
|
|
168
|
+
const collapsed = block.classList.toggle("is-collapsed");
|
|
169
|
+
toggle.textContent = t(collapsed ? "actions.showMore" : "actions.showLess");
|
|
170
|
+
});
|
|
171
|
+
block.append(toggle);
|
|
172
|
+
}
|
|
362
173
|
return block;
|
|
363
174
|
}
|
|
364
175
|
|
|
365
|
-
|
|
366
|
-
|
|
176
|
+
export function bulletList(items, className) {
|
|
177
|
+
const list = (items || []).filter(Boolean);
|
|
178
|
+
if (!list.length) return null;
|
|
179
|
+
return h("ul.bullets" + (className ? "." + className : ""), null, list.map(function (item) { return h("li", null, item); }));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// First page of a long list plus an in-place "show more".
|
|
367
183
|
export function pagedList(container, items, pageSize, renderItem, t) {
|
|
368
184
|
let shown = 0;
|
|
369
185
|
let more = null;
|
|
370
186
|
function renderChunk() {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const remaining = items.length - shown;
|
|
378
|
-
if (remaining <= 0) {
|
|
379
|
-
more.remove();
|
|
380
|
-
more = null;
|
|
381
|
-
} else {
|
|
382
|
-
more.textContent = t("actions.showRemaining").replace("{count}", String(remaining));
|
|
383
|
-
}
|
|
384
|
-
}
|
|
187
|
+
items.slice(shown, shown + pageSize).forEach(function (item) { container.insertBefore(renderItem(item), more); });
|
|
188
|
+
shown = Math.min(items.length, shown + pageSize);
|
|
189
|
+
if (!more) return;
|
|
190
|
+
const remaining = items.length - shown;
|
|
191
|
+
if (remaining <= 0) { more.remove(); more = null; }
|
|
192
|
+
else more.textContent = t("actions.showRemaining").replace("{count}", String(remaining));
|
|
385
193
|
}
|
|
386
194
|
if (items.length > pageSize) {
|
|
387
|
-
more =
|
|
388
|
-
more.type = "button";
|
|
389
|
-
more.addEventListener("click", renderChunk);
|
|
195
|
+
more = h("button.show-more", { type: "button", onclick: renderChunk });
|
|
390
196
|
container.append(more);
|
|
391
197
|
}
|
|
392
198
|
renderChunk();
|
|
393
199
|
}
|
|
394
200
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
list.forEach(function (item) {
|
|
402
|
-
const li = node("li", "");
|
|
403
|
-
li.innerHTML = inlineMarkdown(escapeHtml(item));
|
|
404
|
-
listElement.append(li);
|
|
405
|
-
});
|
|
406
|
-
block.append(listElement);
|
|
407
|
-
if (list.length > 6) collapseToggle(block, t);
|
|
408
|
-
return block;
|
|
201
|
+
// --- Metrics (DOM built with node() only; executed by the usage test) -------------
|
|
202
|
+
export function metricTile(labelText, value, options) {
|
|
203
|
+
const variant = options && options.tone ? " tone-" + options.tone : "";
|
|
204
|
+
const tile = node("div", "metric" + variant);
|
|
205
|
+
tile.append(node("span", "metric-label", labelText), node("strong", "metric-value", String(value)));
|
|
206
|
+
return tile;
|
|
409
207
|
}
|
|
410
208
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
return
|
|
414
|
-
? message.author.roleName
|
|
415
|
-
: t("author." + message.author.type);
|
|
209
|
+
export function usageMetricText(metric, t, suffix) {
|
|
210
|
+
if (!metric || metric.value == null) return t("detail.unobserved");
|
|
211
|
+
return metric.value + (suffix || "") + (metric.status === "partial" ? " · " + t("detail.partial") : "");
|
|
416
212
|
}
|
|
417
213
|
|
|
418
|
-
function
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
214
|
+
function usageTile(labelText, metric, t, suffix) {
|
|
215
|
+
const tile = metricTile(labelText, usageMetricText(metric, t, suffix));
|
|
216
|
+
if (metric && metric.reasons && metric.reasons.length) {
|
|
217
|
+
tile.append(node("small", "metric-note", metric.reasons.map(function (reason) {
|
|
218
|
+
return t("usage.reason." + reason);
|
|
219
|
+
}).join("; ")));
|
|
220
|
+
}
|
|
221
|
+
return tile;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function observabilityMetricCard(observability, t) {
|
|
225
|
+
if (!observability) return null;
|
|
226
|
+
const wrap = node("div", "usage");
|
|
227
|
+
const grid = node("div", "metric-grid");
|
|
228
|
+
const cost = observability.cost || {};
|
|
229
|
+
const context = observability.context || {};
|
|
230
|
+
grid.append(usageTile(t("usage.tokens"), cost.tokens, t));
|
|
231
|
+
grid.append(usageTile(t("usage.toolCalls"), cost.toolCalls, t));
|
|
232
|
+
grid.append(usageTile(t("usage.elapsed"), cost.elapsedSeconds, t, "s"));
|
|
233
|
+
grid.append(usageTile(t("usage.executionSum"), cost.executionSeconds, t, "s"));
|
|
234
|
+
grid.append(metricTile(t("usage.ready"), ((observability.dag && observability.dag.readyIds) || []).length));
|
|
235
|
+
grid.append(metricTile(t("usage.contextSnapshots"), context.snapshotCount == null ? t("detail.unobserved") : context.snapshotCount));
|
|
236
|
+
wrap.append(grid);
|
|
237
|
+
const meta = node("p", "usage-meta");
|
|
238
|
+
meta.append(node("span", "", t("usage.scope")));
|
|
239
|
+
meta.append(node("span", "", t("usage.observedThrough") + " "
|
|
240
|
+
+ (cost.observedThrough ? formatDateTime(cost.observedThrough) : t("detail.unobserved"))));
|
|
241
|
+
meta.append(node("span", "", t("usage.contextBytes") + " "
|
|
242
|
+
+ (context.totalBytes == null ? t("detail.partial") : context.totalBytes + " B")));
|
|
243
|
+
wrap.append(meta);
|
|
244
|
+
return wrap;
|
|
422
245
|
}
|
|
423
246
|
|
|
424
|
-
// ---
|
|
425
|
-
function
|
|
426
|
-
|
|
247
|
+
// --- Agent identity ------------------------------------------------------------------
|
|
248
|
+
export function agentChips(agent) {
|
|
249
|
+
if (!agent) return null;
|
|
250
|
+
const chips = [];
|
|
251
|
+
if (agent.adapterId || agent.component) chips.push(chip(agentLabel(agent.component, agent.adapterId), "chip-strong"));
|
|
252
|
+
if (agent.model) chips.push(chip(agent.model));
|
|
253
|
+
if (agent.effort) chips.push(chip(agent.effort));
|
|
254
|
+
return chips.length ? h("span.chip-row", null, chips) : null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// --- Input requests ----------------------------------------------------------------
|
|
258
|
+
export function inputCard(input, t, locale, onAnswer) {
|
|
259
|
+
const policy = input.policy && input.policy.kind;
|
|
260
|
+
const element = h("article.input-card");
|
|
261
|
+
element.append(h("div.input-head", null,
|
|
262
|
+
h("span.input-kicker", null, icon("inbox", "icon-sm"), t("input.kicker")),
|
|
263
|
+
policy ? badge(label(t, "input", policy), policy === "required" ? "bad" : "warn") : null,
|
|
264
|
+
input.policy && input.policy.timeoutAt
|
|
265
|
+
? h("span.input-timeout", null, icon("clock", "icon-sm"),
|
|
266
|
+
t("input.timeoutAt") + " " + relativeTime(input.policy.timeoutAt, locale, t)) : null,
|
|
267
|
+
timeTag(input.createdAt, locale, t)));
|
|
268
|
+
element.append(h("p.input-question", null, input.question));
|
|
269
|
+
const facts = [];
|
|
270
|
+
if (input.requester) facts.push(t("input.from") + " " + input.requester.roleName
|
|
271
|
+
+ " · " + (input.requester.runId || input.requester.nativeSessionId || ""));
|
|
272
|
+
if (input.blockedRefs && input.blockedRefs.length) facts.push(t("input.blocks") + " "
|
|
273
|
+
+ input.blockedRefs.map(function (ref) { return ref.type + " " + ref.id; }).join(", "));
|
|
274
|
+
if (facts.length) element.append(h("p.input-facts", null, facts.join(" · ")));
|
|
275
|
+
const recommended = input.policy && input.policy.recommendedChoiceKey;
|
|
427
276
|
if (input.choices && input.choices.length) {
|
|
428
|
-
input.choices.
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
answers.append(button);
|
|
435
|
-
});
|
|
277
|
+
element.append(h("div.input-choices", null, input.choices.map(function (choice) {
|
|
278
|
+
return h("button.choice" + (choice.key === recommended ? ".is-recommended" : ""), {
|
|
279
|
+
type: "button",
|
|
280
|
+
onclick: function (event) { onAnswer(input, { choiceKey: choice.key }, event.currentTarget); }
|
|
281
|
+
}, h("span", null, choice.label), choice.key === recommended ? badge(t("input.recommended"), "accent") : null);
|
|
282
|
+
})));
|
|
436
283
|
} else {
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
field.placeholder = t("input.freeText");
|
|
442
|
-
const submit = node("button", "input-answer", t("actions.answer"));
|
|
443
|
-
submit.type = "submit";
|
|
444
|
-
form.append(field, submit);
|
|
284
|
+
const field = h("input", { type: "text", required: true, placeholder: t("input.freeText"), maxLength: 8000 });
|
|
285
|
+
const form = h("form.input-form", null, field,
|
|
286
|
+
h("button.btn.btn-primary", { type: "submit" }, icon("send"), h("span", null, t("actions.answer"))));
|
|
287
|
+
form.addEventListener("input", function () { form.dataset.unsent = field.value ? "true" : "false"; });
|
|
445
288
|
form.addEventListener("submit", function (event) {
|
|
446
289
|
event.preventDefault();
|
|
447
290
|
const text = field.value.trim();
|
|
448
|
-
if (text
|
|
291
|
+
if (text) onAnswer(input, { text: text }, form.querySelector("button"));
|
|
449
292
|
});
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
return answers;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
export function inputCard(input, _options, t, locale, actions) {
|
|
456
|
-
const card = node("article", "input-card");
|
|
457
|
-
const top = node("div", "input-card-top");
|
|
458
|
-
top.append(node("small", "", t("detail.openInput")));
|
|
459
|
-
const question = node("p", "input-question", input.question);
|
|
460
|
-
top.append(question);
|
|
461
|
-
const context = node("div", "input-context");
|
|
462
|
-
if (input.policy) {
|
|
463
|
-
if (input.policy.kind === "recommended") {
|
|
464
|
-
context.append(pill(t, "input", "recommended"));
|
|
465
|
-
if (input.policy.timeoutAt) {
|
|
466
|
-
context.append(node("span", "", t("input.timeoutAt") + " · " + relativeTime(input.policy.timeoutAt, locale, t)));
|
|
467
|
-
}
|
|
468
|
-
} else {
|
|
469
|
-
context.append(pill(t, "input", "required"));
|
|
470
|
-
}
|
|
293
|
+
element.append(form);
|
|
471
294
|
}
|
|
472
|
-
|
|
473
|
-
top.append(context);
|
|
474
|
-
if (input.blockedRefs && input.blockedRefs.length) {
|
|
475
|
-
const blocked = input.blockedRefs.map(function (ref) { return ref.type + "·" + ref.id; }).join(" ");
|
|
476
|
-
top.append(node("span", "input-blocked", blocked));
|
|
477
|
-
}
|
|
478
|
-
if (input.requester) {
|
|
479
|
-
top.append(node("span", "input-requester",
|
|
480
|
-
t("detail.requester") + " · " + input.requester.roleName + " / " + (input.requester.runId ?? input.requester.nativeSessionId)));
|
|
481
|
-
}
|
|
482
|
-
card.append(top);
|
|
483
|
-
card.append(answerActions(input, actions, t));
|
|
484
|
-
return card;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// --- Conclusion band ------------------------------------------------------------
|
|
488
|
-
export function conclusionMeta(task, t, locale, kind) {
|
|
489
|
-
const meta = node("div", "conclusion-meta");
|
|
490
|
-
const actor = kind === "archived" ? task.archivedBy
|
|
491
|
-
: kind === "cancelled" ? task.retiredBy
|
|
492
|
-
: task.completedBy;
|
|
493
|
-
const at = kind === "archived" ? task.archivedAt
|
|
494
|
-
: kind === "cancelled" ? task.retiredAt
|
|
495
|
-
: task.completedAt;
|
|
496
|
-
const label = kind === "archived" ? t("detail.archivedBy")
|
|
497
|
-
: kind === "cancelled" ? t("detail.cancelledBy")
|
|
498
|
-
: t("detail.completedBy");
|
|
499
|
-
if (actor) meta.append(node("span", "", label + " · " + authorName(t, actor)));
|
|
500
|
-
if (at) meta.append(node("time", "", formatDateTime(at, locale)));
|
|
501
|
-
return meta;
|
|
295
|
+
return element;
|
|
502
296
|
}
|
|
503
297
|
|
|
504
|
-
// ---
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const body = node("div", "work-item-body");
|
|
522
|
-
const meta = node("div", "record-meta");
|
|
523
|
-
if (item.assignee) meta.append(node("span", "meta-name", item.assignee));
|
|
524
|
-
meta.append(node("span", "mono", item.id));
|
|
525
|
-
meta.append(node("time", "", formatDateTime(item.updatedAt, locale)));
|
|
526
|
-
if (item.endedAt) {
|
|
527
|
-
meta.append(node("span", "", t("detail.endedAt") + " · " + formatDateTime(item.endedAt, locale)));
|
|
528
|
-
}
|
|
529
|
-
body.append(meta);
|
|
530
|
-
|
|
531
|
-
if (item.objective && item.objective !== item.title) {
|
|
532
|
-
body.append(richText(t("detail.objective"), item.objective, t));
|
|
533
|
-
}
|
|
298
|
+
// --- Records -----------------------------------------------------------------------
|
|
299
|
+
export function workItemCard(item, t, locale, titles) {
|
|
300
|
+
const settled = item.status !== "open";
|
|
301
|
+
const element = h(settled ? "details.work-card.is-settled" : "article.work-card");
|
|
302
|
+
element.dataset.viewKey = "work:" + item.id;
|
|
303
|
+
const head = h(settled ? "summary.work-head" : "header.work-head", null,
|
|
304
|
+
settled ? icon("chevron", "disclosure-chevron") : null,
|
|
305
|
+
dot(tone("work", item.status)),
|
|
306
|
+
h("span.work-title", null, item.title),
|
|
307
|
+
statusBadge(t, "work", "work", item.status));
|
|
308
|
+
element.append(head);
|
|
309
|
+
const body = h("div.work-body");
|
|
310
|
+
body.append(h("div.meta-line", null,
|
|
311
|
+
item.assignee ? h("span.meta-strong", null, icon("user", "icon-sm"), item.assignee) : null,
|
|
312
|
+
mono(item.id), timeTag(item.updatedAt, locale, t)));
|
|
313
|
+
if (item.objective && item.objective !== item.title) body.append(richText(t("work.objective"), item.objective, t));
|
|
534
314
|
if (item.acceptance && item.acceptance.length) {
|
|
535
|
-
body.append(
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
// text blocks above and below stay full-width so the card reads top-to-bottom
|
|
540
|
-
// like an issue rather than a mismatched column grid.
|
|
541
|
-
const chipCols = node("div", "work-item-chips");
|
|
542
|
-
if (item.dependsOn && item.dependsOn.length) {
|
|
543
|
-
chipCols.append(chipRow(t("detail.dependsOn"), item.dependsOn.map(function (id) { return titles[id] || id; })));
|
|
315
|
+
body.append(h("div.prose-block", null, h("h4.prose-label", null, t("work.acceptance")),
|
|
316
|
+
h("ul.checklist", null, item.acceptance.map(function (entry) {
|
|
317
|
+
return h("li" + (item.status === "accepted" ? ".is-done" : ""), null, icon(item.status === "accepted" ? "check" : "target", "icon-sm"), h("span", null, entry));
|
|
318
|
+
}))));
|
|
544
319
|
}
|
|
320
|
+
const deps = (item.dependsOn || []).map(function (id) { return (titles && titles[id]) || id; });
|
|
321
|
+
if (deps.length) body.append(h("div.meta-line", null, h("span.meta-label", null, t("work.dependsOn")), deps.map(function (d) { return chip(d); })));
|
|
545
322
|
if (item.writeProjectIds && item.writeProjectIds.length) {
|
|
546
|
-
|
|
323
|
+
body.append(h("div.meta-line", null, h("span.meta-label", null, t("work.writeProjects")), item.writeProjectIds.map(function (d) { return chip(d); })));
|
|
547
324
|
}
|
|
548
|
-
if (
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
const observability = item.observability;
|
|
556
|
-
const metrics = node("div", "record-meta work-item-observability");
|
|
557
|
-
metrics.append(usageTile(t("detail.tokens"), observability.cost.tokens, t));
|
|
558
|
-
metrics.append(usageTile(t("detail.toolCalls"), observability.cost.toolCalls, t));
|
|
559
|
-
metrics.append(usageTile(t("detail.executionSum"), observability.cost.executionSeconds, t, "s"));
|
|
560
|
-
metrics.append(node("span", "", t("detail.contextSnapshots") + " · "
|
|
561
|
-
+ observability.context.snapshotCount));
|
|
562
|
-
metrics.append(node("span", "", t("detail.results") + " · "
|
|
563
|
-
+ (observability.resultCount == null
|
|
564
|
-
? t("detail.unobserved")
|
|
565
|
-
: observability.resultCount)));
|
|
566
|
-
body.append(metrics);
|
|
325
|
+
if (item.candidates && item.candidates.length) {
|
|
326
|
+
body.append(h("div.prose-block", null, h("h4.prose-label", null, t("work.candidates")),
|
|
327
|
+
h("ol.candidates", null, item.candidates.slice().sort(function (a, b) { return b.sequence - a.sequence; }).map(function (candidate) {
|
|
328
|
+
return h("li", null, h("span.candidate-seq", null, "#" + candidate.sequence), h("span", null, candidate.summary),
|
|
329
|
+
h("span.faint", null, candidate.source && candidate.source.type === "run" ? candidate.source.runId : t("work.direct")),
|
|
330
|
+
timeTag(candidate.createdAt, locale, t));
|
|
331
|
+
}))));
|
|
567
332
|
}
|
|
568
|
-
|
|
569
|
-
// Review candidates submitted for this WorkItem, newest first.
|
|
570
|
-
const candidates = candidateList(item.candidates, t, locale);
|
|
571
|
-
if (candidates) body.append(candidates);
|
|
572
|
-
|
|
573
|
-
// Explicit Leader retirement disposition.
|
|
574
333
|
if (item.disposition) {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const text = item.disposition.summary
|
|
578
|
-
+ (item.disposition.replacementWorkItemId
|
|
579
|
-
? " · " + t("detail.replacementWorkItem") + " " + item.disposition.replacementWorkItemId
|
|
580
|
-
: "");
|
|
581
|
-
disposition.append(node("p", "muted", text));
|
|
582
|
-
body.append(disposition);
|
|
334
|
+
body.append(note(t("work.disposition") + ": " + item.disposition.summary
|
|
335
|
+
+ (item.disposition.replacementWorkItemId ? " → " + item.disposition.replacementWorkItemId : "")));
|
|
583
336
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}
|
|
588
|
-
card.append(body);
|
|
589
|
-
return card;
|
|
337
|
+
if (item.outcome) body.append(richText(t("work.outcome"), item.outcome, t, { className: "callout" }));
|
|
338
|
+
element.append(body);
|
|
339
|
+
return element;
|
|
590
340
|
}
|
|
591
341
|
|
|
592
342
|
export function runCard(run, t, locale) {
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
if (run.result
|
|
613
|
-
card.append(richText(t("detail.outcome"), run.result.output, t, { className: "execute-io outcome", threshold: 320 }));
|
|
614
|
-
}
|
|
615
|
-
if (run.result?.diagnostic) {
|
|
616
|
-
card.append(richText(t("detail.failure"), run.result.diagnostic, t, { className: "execute-io outcome", threshold: 320 }));
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
const foot = node("div", "execute-foot");
|
|
620
|
-
const tags = node("div", "execute-tags");
|
|
621
|
-
tags.append(chip(t("mode." + run.mode)));
|
|
343
|
+
const element = h("article.run-card");
|
|
344
|
+
element.dataset.status = run.status;
|
|
345
|
+
const input = run.inputs && run.inputs.length ? run.inputs[0].input : null;
|
|
346
|
+
element.append(h("header.run-head", null,
|
|
347
|
+
dot(tone("run", run.status)),
|
|
348
|
+
h("span.run-role", null, run.roleName),
|
|
349
|
+
mono(run.id),
|
|
350
|
+
run.purpose ? chip(label(t, "run.purpose", run.purpose)) : null,
|
|
351
|
+
run.workItemId ? chip(run.workItemId) : null,
|
|
352
|
+
h("span.spacer"),
|
|
353
|
+
statusBadge(t, "run", "run", run.status)));
|
|
354
|
+
element.append(h("div.meta-line", null,
|
|
355
|
+
h("span", null, t("run.delivery") + ": " + label(t, "run.delivery", (run.execution && run.execution.delivery) || "unobserved")),
|
|
356
|
+
run.mode ? h("span", null, label(t, "mode", run.mode)) : null,
|
|
357
|
+
timeTag((run.result && run.result.completedAt) || run.updatedAt, locale, t),
|
|
358
|
+
agentChips(run.effective || (run.agentId ? run : null))));
|
|
359
|
+
const directive = input && (input.directive || input.action);
|
|
360
|
+
if (directive) element.append(richText(t("run.instruction"), directive, t, { threshold: 320 }));
|
|
361
|
+
if (run.result && run.result.output) element.append(richText(t("run.output"), run.result.output, t, { threshold: 320, className: "callout" }));
|
|
362
|
+
if (run.result && run.result.diagnostic) element.append(richText(t("run.failure"), run.result.diagnostic, t, { threshold: 320, className: "callout.tone-bad" }));
|
|
622
363
|
if (run.executionGroupId) {
|
|
623
|
-
|
|
624
|
-
+ (run.executionLaneId ? "/" + run.executionLaneId : "")));
|
|
625
|
-
}
|
|
626
|
-
const badge = run.effective ? agentBadge(run.effective) : (run.agentId ? agentBadge(run) : null);
|
|
627
|
-
if (badge) tags.append(badge);
|
|
628
|
-
foot.append(tags);
|
|
629
|
-
foot.append(pill(t, "run", run.status));
|
|
630
|
-
card.append(foot);
|
|
631
|
-
|
|
632
|
-
if (run.effective) {
|
|
633
|
-
const eff = node("div", "record-meta");
|
|
634
|
-
eff.append(node("span", "", t("detail.effective") + " · r" + run.effective.sourceDesiredRevision));
|
|
635
|
-
eff.append(node("span", "", t("detail.profileIntent") + " · " + run.effective.profileAccess));
|
|
636
|
-
eff.append(node("span", "", t("detail.permission") + " · " + run.effective.permission.strategy));
|
|
637
|
-
card.append(eff);
|
|
364
|
+
element.append(h("p.faint.mono-line", null, t("run.lineage") + " " + run.executionGroupId + (run.executionLaneId ? "/" + run.executionLaneId : "")));
|
|
638
365
|
}
|
|
639
|
-
|
|
640
|
-
return card;
|
|
366
|
+
return element;
|
|
641
367
|
}
|
|
642
368
|
|
|
643
369
|
export function reviewCard(round, t, locale) {
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
if (round.workItemId && round.candidateId) {
|
|
660
|
-
meta.append(node("span", "mono", round.workItemId + " · " + round.candidateId));
|
|
661
|
-
}
|
|
662
|
-
if (round.createdAt) meta.append(node("time", "", formatDateTime(round.createdAt, locale)));
|
|
663
|
-
meta.append(node("span", "", t("detail.reviewBase") + " · " + round.reviewBaseCommit));
|
|
664
|
-
if (round.workspace && round.workspace.root) {
|
|
665
|
-
meta.append(pathMetaItem(t("detail.workspace"), round.workspace.root));
|
|
666
|
-
}
|
|
667
|
-
if (round.reviewerRunId) {
|
|
668
|
-
meta.append(node("span", "mono", t("detail.reviewerRun") + " · " + round.reviewerRunId));
|
|
669
|
-
}
|
|
670
|
-
if (round.workspaceDisposition) {
|
|
671
|
-
meta.append(node("span", "", t("detail.workspaceDisposition") + " · "
|
|
672
|
-
+ t("disposition." + round.workspaceDisposition.kind)));
|
|
673
|
-
}
|
|
674
|
-
card.append(meta);
|
|
675
|
-
if (round.executionGroup) {
|
|
676
|
-
const group = round.executionGroup;
|
|
677
|
-
const execution = node("div", "record-block exec-group");
|
|
678
|
-
const groupMeta = node("div", "record-meta execution-resource-meta");
|
|
679
|
-
groupMeta.append(node("span", "mono", group.id));
|
|
680
|
-
groupMeta.append(node("span", "", t("reviewExec.assignment") + " · "
|
|
681
|
-
+ group.assignment.contextSnapshotRef.id));
|
|
682
|
-
groupMeta.append(node("span", "", t("reviewExec.main") + " · "
|
|
683
|
-
+ (round.reviewerRunId || t("detail.unobserved"))));
|
|
684
|
-
execution.append(groupMeta);
|
|
685
|
-
const lanes = node("div", "lane-list");
|
|
686
|
-
group.lanes.forEach(function (lane) {
|
|
687
|
-
const row = node("div", "lane-row");
|
|
688
|
-
row.append(statusDot(lane.disposition === "open" ? "running" : lane.disposition));
|
|
689
|
-
row.append(node("span", "lane-role", t("reviewExec.producer") + " " + lane.ordinal
|
|
690
|
-
+ " · " + lane.roleName));
|
|
691
|
-
row.append(node("span", "lane-status", t("reviewExec.status." + lane.disposition)));
|
|
692
|
-
row.append(node("span", "mono", lane.currentRunId || t("detail.unobserved")));
|
|
693
|
-
lanes.append(row);
|
|
694
|
-
});
|
|
695
|
-
execution.append(lanes);
|
|
696
|
-
card.append(execution);
|
|
697
|
-
}
|
|
698
|
-
if (round.failure) {
|
|
699
|
-
card.append(richText(
|
|
700
|
-
t("detail.failure"),
|
|
701
|
-
round.failure.kind + ": " + round.failure.message,
|
|
702
|
-
t
|
|
703
|
-
));
|
|
704
|
-
}
|
|
705
|
-
return card;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
export function roleCard(role, task, t, locale, actions) {
|
|
709
|
-
const card = node("article", "record-card");
|
|
710
|
-
const head = node("div", "record-head");
|
|
711
|
-
head.append(node("strong", "record-title", role.name));
|
|
712
|
-
const headRight = node("div", "record-pills");
|
|
713
|
-
headRight.append(pill(t, "role", role.status));
|
|
714
|
-
const open = node("button", "record-open", "");
|
|
715
|
-
open.type = "button";
|
|
716
|
-
open.append(node("span", "", t("actions.openSession")), node("span", "arrow", "→"));
|
|
717
|
-
open.addEventListener("click", function () {
|
|
718
|
-
if (actions.openTerminal) actions.openTerminal({ scope: "task", taskId: task.id, roleName: role.name });
|
|
719
|
-
});
|
|
720
|
-
headRight.append(open);
|
|
721
|
-
head.append(headRight);
|
|
722
|
-
card.append(head);
|
|
723
|
-
|
|
724
|
-
const meta = node("div", "record-meta");
|
|
725
|
-
meta.append(node("span", "meta-name", role.activeAgentId));
|
|
726
|
-
const activeBinding = role.agentBindings && role.agentBindings[role.activeAgentId];
|
|
727
|
-
if (activeBinding) {
|
|
728
|
-
const badge = agentBadge({
|
|
729
|
-
adapterId: activeBinding.adapterId,
|
|
730
|
-
component: activeBinding.component,
|
|
731
|
-
model: activeBinding.config && activeBinding.config.model,
|
|
732
|
-
effort: activeBinding.config && activeBinding.config.effort
|
|
733
|
-
});
|
|
734
|
-
if (badge) meta.append(badge);
|
|
735
|
-
}
|
|
736
|
-
if (role.launchRevision !== undefined) {
|
|
737
|
-
meta.append(node("span", "", t("detail.desired") + " · r" + role.launchRevision));
|
|
738
|
-
}
|
|
739
|
-
if (role.defaultAccess !== undefined) {
|
|
740
|
-
meta.append(node("span", "", t("detail.profileIntent") + " · " + role.defaultAccess));
|
|
741
|
-
}
|
|
742
|
-
if (role.updatedAt) meta.append(node("time", "", formatDateTime(role.updatedAt, locale)));
|
|
743
|
-
card.append(meta);
|
|
744
|
-
|
|
745
|
-
if (role.effectiveLaunch) {
|
|
746
|
-
const eff = node("div", "record-meta");
|
|
747
|
-
eff.append(node("span", "", t("detail.effectiveAgent") + " · " + role.effectiveLaunch.agentId));
|
|
748
|
-
const effBadge = agentBadge(role.effectiveLaunch);
|
|
749
|
-
if (effBadge) eff.append(effBadge);
|
|
750
|
-
if (role.effectiveLaunch.sourceDesiredRevision !== undefined) {
|
|
751
|
-
eff.append(node("span", "", t("detail.effective") + " · r" + role.effectiveLaunch.sourceDesiredRevision));
|
|
752
|
-
}
|
|
753
|
-
if (role.effectiveLaunch.profileAccess !== undefined) {
|
|
754
|
-
eff.append(node("span", "", t("detail.profileIntent") + " · " + role.effectiveLaunch.profileAccess));
|
|
755
|
-
}
|
|
756
|
-
if (role.effectiveLaunch.permission && role.effectiveLaunch.permission.strategy !== undefined) {
|
|
757
|
-
eff.append(node("span", "", t("detail.permission") + " · " + role.effectiveLaunch.permission.strategy));
|
|
758
|
-
}
|
|
759
|
-
eff.append(node("span", "", role.launchDrift ? t("launch.drift") : t("launch.current")));
|
|
760
|
-
card.append(eff);
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
if (role.providerRetry) {
|
|
764
|
-
const retry = role.providerRetry;
|
|
765
|
-
const retryMeta = node("p", "record-meta");
|
|
766
|
-
const zh = locale.startsWith("zh");
|
|
767
|
-
const labels = zh ? {
|
|
768
|
-
waiting: "等待 Provider 重试", "in-flight": "Provider 重试已提交",
|
|
769
|
-
recovered: "Provider 已恢复", cancelled: "自动重试已取消",
|
|
770
|
-
exhausted: "自动重试预算耗尽", "needs-attention": "需要检查 Provider",
|
|
771
|
-
"delivery-unknown": "投递结果未知,禁止重放"
|
|
772
|
-
} : {
|
|
773
|
-
waiting: "Waiting for Provider retry", "in-flight": "Provider retry submitted",
|
|
774
|
-
recovered: "Provider recovered", cancelled: "Automatic retry cancelled",
|
|
775
|
-
exhausted: "Automatic retry budget exhausted", "needs-attention": "Provider needs attention",
|
|
776
|
-
"delivery-unknown": "Delivery unknown; no replay"
|
|
777
|
-
};
|
|
778
|
-
retryMeta.textContent = (labels[retry.status] || retry.status) + " · "
|
|
779
|
-
+ (zh && retry.category === "rate-limit" ? "临时限流" : retry.category) + " · "
|
|
780
|
-
+ retry.attempts + "/" + retry.limit
|
|
781
|
-
+ (retry.status === "waiting" ? " · " + new Date(retry.nextEligibleAt).toLocaleString(locale) : "")
|
|
782
|
-
+ " · " + (zh ? "原成果保留" : "Existing work preserved")
|
|
783
|
-
+ (retry.reason ? " · " + retry.reason : "");
|
|
784
|
-
card.append(retryMeta);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
if (role.sessionTokens) {
|
|
788
|
-
const tokenMeta = node("div", "record-meta");
|
|
789
|
-
const cumulative = role.sessionTokens.cumulativeTotal || {};
|
|
790
|
-
const maximum = role.sessionTokens.maximumRequestInput || {};
|
|
791
|
-
tokenMeta.append(node("span", "", t("detail.sessionTotalTokens") + " · "
|
|
792
|
-
+ (cumulative.status === "observed" ? cumulative.totalTokens : t("detail.unobserved"))));
|
|
793
|
-
tokenMeta.append(node("span", "", t("detail.maximumRequestInputTokens") + " · "
|
|
794
|
-
+ (maximum.status === "observed" ? maximum.inputTokens : t("detail.unobserved"))));
|
|
795
|
-
card.append(tokenMeta);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
if (role.description) card.append(richText(null, role.description, t, { muted: true }));
|
|
799
|
-
|
|
800
|
-
const cols = node("div", "record-cols");
|
|
801
|
-
const bindingIds = role.agentBindings ? Object.keys(role.agentBindings) : [];
|
|
802
|
-
if (bindingIds.length > 1) {
|
|
803
|
-
const bindings = bindingIds.map(function (id) {
|
|
804
|
-
const binding = role.agentBindings[id];
|
|
805
|
-
const model = binding.config && binding.config.model;
|
|
806
|
-
return componentLabel(binding.component, binding.adapterId) + (model ? " · " + model : "");
|
|
807
|
-
});
|
|
808
|
-
const activeBindingLabel = activeBinding
|
|
809
|
-
? componentLabel(activeBinding.component, activeBinding.adapterId) + (activeBinding.config && activeBinding.config.model ? " · " + activeBinding.config.model : "")
|
|
810
|
-
: undefined;
|
|
811
|
-
cols.append(chipRow(t("detail.agents"), bindings, activeBindingLabel));
|
|
812
|
-
}
|
|
813
|
-
if (role.skills && role.skills.length) cols.append(chipRow(t("detail.skills"), role.skills));
|
|
814
|
-
if (role.responsibilities && role.responsibilities.length) cols.append(criteriaList(t("detail.responsibilities"), role.responsibilities, t));
|
|
815
|
-
if (role.constraints && role.constraints.length) cols.append(criteriaList(t("detail.constraints"), role.constraints, t));
|
|
816
|
-
if (role.expectedOutput) cols.append(richText(t("detail.expectedOutput"), role.expectedOutput, t, { muted: true }));
|
|
817
|
-
if (cols.childNodes.length) card.append(cols);
|
|
818
|
-
|
|
819
|
-
if (role.workspace) {
|
|
820
|
-
const workspaceMeta = node("div", "record-meta");
|
|
821
|
-
workspaceMeta.append(pathMetaItem(t("detail.workspace"), role.workspace));
|
|
822
|
-
card.append(workspaceMeta);
|
|
823
|
-
}
|
|
824
|
-
return card;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
export function historyEventRow(event, t, locale) {
|
|
828
|
-
if (event.kind === "milestone") {
|
|
829
|
-
const milestone = event.item;
|
|
830
|
-
const card = node("article", "record-card");
|
|
831
|
-
const head = node("div", "record-head");
|
|
832
|
-
const titleRow = node("div", "record-title-row");
|
|
833
|
-
titleRow.append(node("strong", "record-title", milestone.title));
|
|
834
|
-
head.append(titleRow);
|
|
835
|
-
head.append(pill(t, "history", "milestone"));
|
|
836
|
-
card.append(head);
|
|
837
|
-
const meta = node("div", "record-meta");
|
|
838
|
-
meta.append(node("span", "mono", milestone.id));
|
|
839
|
-
meta.append(node("time", "", formatDateTime(milestone.createdAt, locale)));
|
|
840
|
-
card.append(meta);
|
|
841
|
-
if (milestone.summary) card.append(richText(null, milestone.summary, t, { muted: true }));
|
|
842
|
-
return card;
|
|
843
|
-
}
|
|
844
|
-
const decision = event.item;
|
|
845
|
-
const card = node("article", "record-card");
|
|
846
|
-
const head = node("div", "record-head");
|
|
847
|
-
const titleRow = node("div", "record-title-row");
|
|
848
|
-
titleRow.append(node("strong", "record-title", decision.title));
|
|
849
|
-
head.append(titleRow);
|
|
850
|
-
head.append(pill(t, "history", "decision"));
|
|
851
|
-
card.append(head);
|
|
852
|
-
const meta = node("div", "record-meta");
|
|
853
|
-
meta.append(node("span", "mono", decision.id));
|
|
854
|
-
if (decision.createdAt) meta.append(node("time", "", formatDateTime(decision.createdAt, locale)));
|
|
855
|
-
if (decision.status) meta.append(pill(t, "decision", decision.status));
|
|
856
|
-
card.append(meta);
|
|
857
|
-
if (decision.rationale) card.append(richText(null, decision.rationale, t, { muted: true }));
|
|
858
|
-
if (decision.supersededReason) card.append(richText(null, decision.supersededReason, t, { muted: true }));
|
|
859
|
-
return card;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
export function messageCard(message, t, locale, result) {
|
|
863
|
-
const card = node("article", "record-card");
|
|
864
|
-
const head = node("div", "record-head");
|
|
865
|
-
const titleRow = node("div", "record-title-row");
|
|
866
|
-
titleRow.append(node("strong", "record-title", messageAuthor(message, t)));
|
|
867
|
-
head.append(titleRow);
|
|
868
|
-
if (message.kind) head.append(pill(t, "messageKind", message.kind));
|
|
869
|
-
if (message.status) head.append(pill(t, "status", message.status));
|
|
870
|
-
card.append(head);
|
|
871
|
-
|
|
872
|
-
const meta = node("div", "record-meta");
|
|
873
|
-
meta.append(node("span", "mono", message.id));
|
|
874
|
-
meta.append(node("time", "", formatDateTime(message.createdAt, locale)));
|
|
875
|
-
if (message.runId) meta.append(node("span", "mono", message.runId + (message.workItemId ? " · " + message.workItemId : "")));
|
|
876
|
-
if (message.recipient) {
|
|
877
|
-
meta.append(node("span", "mono", "→ " + message.recipient.roleName + " · "
|
|
878
|
-
+ (message.recipient.reviewRoundId || message.recipient.workItemId)));
|
|
879
|
-
meta.append(node("span", "mono", !message.recipient.ownerRunId ? t("messageDelivery.notification") : message.continuation?.runId
|
|
880
|
-
? t("messageDelivery.run") + " " + message.continuation.runId
|
|
881
|
-
: message.continuation?.notDeliveredReason
|
|
882
|
-
? t("messageDelivery.blocked") + " " + message.continuation.notDeliveredReason
|
|
883
|
-
: t("messageDelivery.pending")));
|
|
884
|
-
}
|
|
885
|
-
card.append(meta);
|
|
886
|
-
|
|
887
|
-
card.append(richText(null, message.body, t));
|
|
888
|
-
if (message.resultRef) {
|
|
889
|
-
card.append(node("small", "mono", message.resultRef.runId));
|
|
890
|
-
if (result?.output) card.append(richText(t("detail.outcome"), result.output, t));
|
|
891
|
-
if (result?.diagnostic) card.append(richText(t("detail.failure"), result.diagnostic, t));
|
|
892
|
-
if (!result) card.append(node("p", "muted", t("detail.unobserved")));
|
|
893
|
-
}
|
|
894
|
-
return card;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// --- Overview + sidebar rows --------------------------------------------------
|
|
898
|
-
export function metricTile(label, value, options) {
|
|
899
|
-
const variant = options && options.hot ? " is-hot" : options && options.warning ? " is-warning" : "";
|
|
900
|
-
const tile = node("article", "metric" + variant);
|
|
901
|
-
tile.append(node("span", "metric-label", label), node("strong", "metric-value", String(value)));
|
|
902
|
-
return tile;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
export function attentionRow(item, t, locale, onSelect) {
|
|
906
|
-
const row = node("button", "inbox-row");
|
|
907
|
-
row.type = "button";
|
|
908
|
-
const request = item.request || {};
|
|
909
|
-
const kind = request.policy && request.policy.kind === "required" ? "required"
|
|
910
|
-
: request.policy && request.policy.kind === "recommended" ? "recommended"
|
|
911
|
-
: null;
|
|
912
|
-
|
|
913
|
-
const lead = node("span", "inbox-lead");
|
|
914
|
-
lead.append(node("span", "inbox-dot"));
|
|
915
|
-
const head = node("span", "inbox-head");
|
|
916
|
-
head.append(node("span", "inbox-task", item.taskTitle));
|
|
917
|
-
if (kind) head.append(pill(t, "input", kind));
|
|
918
|
-
lead.append(head);
|
|
919
|
-
row.append(lead);
|
|
920
|
-
|
|
921
|
-
if (request.question) {
|
|
922
|
-
const question = node("span", "inbox-question", request.question);
|
|
923
|
-
question.title = request.question;
|
|
924
|
-
row.append(question);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
const foot = node("span", "inbox-foot");
|
|
928
|
-
if (request.createdAt) foot.append(node("time", "", relativeTime(request.createdAt, locale, t)));
|
|
929
|
-
foot.append(node("span", "inbox-go", t("actions.answer") + " →"));
|
|
930
|
-
row.append(foot);
|
|
931
|
-
|
|
932
|
-
row.addEventListener("click", function () { onSelect(item.taskId); });
|
|
933
|
-
return row;
|
|
370
|
+
const element = h("article.review-card");
|
|
371
|
+
element.append(h("header.run-head", null,
|
|
372
|
+
dot(tone("review", round.status)),
|
|
373
|
+
h("span.run-role", null, round.reviewerRoleName || t("review.reviewer")),
|
|
374
|
+
mono(round.id),
|
|
375
|
+
round.scope ? chip(label(t, "review.scope", round.scope)) : null,
|
|
376
|
+
h("span.spacer"),
|
|
377
|
+
statusBadge(t, "review", "review", round.status)));
|
|
378
|
+
element.append(h("div.meta-line", null,
|
|
379
|
+
round.workItemId ? mono(round.workItemId + (round.candidateId ? " · " + round.candidateId : "")) : null,
|
|
380
|
+
round.reviewBaseCommit ? h("span", null, t("review.base") + " ", mono(String(round.reviewBaseCommit).slice(0, 12))) : null,
|
|
381
|
+
round.reviewerRunId ? h("span", null, t("review.run") + " ", mono(round.reviewerRunId)) : null,
|
|
382
|
+
timeTag(round.createdAt, locale, t)));
|
|
383
|
+
if (round.failure) element.append(note(round.failure.kind + ": " + round.failure.message, "bad"));
|
|
384
|
+
return element;
|
|
934
385
|
}
|
|
935
386
|
|
|
936
|
-
export function
|
|
937
|
-
const
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
387
|
+
export function roleCard(role, t, locale, options) {
|
|
388
|
+
const opts = options || {};
|
|
389
|
+
const session = opts.runtimeRole && opts.runtimeRole.runtimeSession;
|
|
390
|
+
const element = h("article.role-card");
|
|
391
|
+
const binding = role.agentBindings && role.agentBindings[role.activeAgentId];
|
|
392
|
+
element.append(h("header.role-head", null,
|
|
393
|
+
h("span.avatar", { "aria-hidden": "true" }, String(role.name || "?").slice(0, 1).toUpperCase()),
|
|
394
|
+
h("div.role-title", null, h("strong", null, role.name),
|
|
395
|
+
h("span.faint", null, role.activeAgentId || "")),
|
|
396
|
+
opts.status ? statusBadge(t, "role", "role", opts.status) : null));
|
|
397
|
+
if (binding) element.append(agentChips({
|
|
398
|
+
adapterId: binding.adapterId, component: binding.component,
|
|
399
|
+
model: binding.config && binding.config.model, effort: binding.config && binding.config.effort
|
|
400
|
+
}));
|
|
401
|
+
if (role.description) element.append(richText(null, role.description, t, { muted: true, threshold: 280 }));
|
|
402
|
+
const rows = [];
|
|
403
|
+
rows.push([t("role.session"), session && session.nativeSessionId ? mono(session.nativeSessionId) : t("role.noSession")]);
|
|
404
|
+
if (role.launchRevision !== undefined) rows.push([t("role.desired"), "r" + role.launchRevision + (opts.launchDrift ? " · " + t("role.drift") : "")]);
|
|
405
|
+
if (role.defaultAccess !== undefined) rows.push([t("role.access"), role.defaultAccess]);
|
|
406
|
+
if (opts.retry) rows.push([t("role.providerRetry"), label(t, "retry", opts.retry.status) + " · "
|
|
407
|
+
+ opts.retry.attempts + "/" + opts.retry.limit
|
|
408
|
+
+ (opts.retry.status === "waiting" && opts.retry.nextEligibleAt ? " · " + formatDateTime(opts.retry.nextEligibleAt, locale) : "")]);
|
|
409
|
+
if (role.skills && role.skills.length) rows.push([t("role.skills"), h("span.chip-row", null, role.skills.map(function (s) { return chip(s); }))]);
|
|
410
|
+
element.append(kv(rows));
|
|
411
|
+
if (opts.onOpen) {
|
|
412
|
+
element.append(h("div.role-actions", null, button(t("role.openSession"), {
|
|
413
|
+
icon: "terminal", variant: "ghost", onClick: function () { opts.onOpen(role.name); }
|
|
414
|
+
})));
|
|
947
415
|
}
|
|
948
|
-
|
|
949
|
-
row.addEventListener("click", function () { onSelect(task.id); });
|
|
950
|
-
return row;
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
export function taskCard(task, hasAttention, state, t, locale, onSelect) {
|
|
954
|
-
const button = node("button", "task");
|
|
955
|
-
button.type = "button";
|
|
956
|
-
button.dataset.id = task.id;
|
|
957
|
-
button.setAttribute("aria-current", String(state.selected === task.id));
|
|
958
|
-
|
|
959
|
-
button.append(statusDot(task.status));
|
|
960
|
-
|
|
961
|
-
const main = node("span", "task-main");
|
|
962
|
-
main.append(node("strong", "task-title", task.title));
|
|
963
|
-
main.append(node("span", "task-meta", relativeTime(task.updatedAt, locale, t)));
|
|
964
|
-
button.append(main);
|
|
965
|
-
|
|
966
|
-
// Derived Task-first execution status for active tasks (blocked, waiting on
|
|
967
|
-
// agents, recovering, etc.) — a richer signal than the raw lifecycle status.
|
|
968
|
-
if (task.executionStatus && task.status === "active") {
|
|
969
|
-
const exec = node("span", "task-exec is-" + task.executionStatus,
|
|
970
|
-
t("exec.status." + task.executionStatus));
|
|
971
|
-
button.append(exec);
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
// One signal only: an open input (needs the user) outranks a running count.
|
|
975
|
-
if (hasAttention || (task.attention && task.attention.openInputs > 0)) {
|
|
976
|
-
const badge = node("span", "task-signal is-input", String(task.attention ? task.attention.openInputs : task.openInputCount || 1));
|
|
977
|
-
badge.title = t("stats.inputs");
|
|
978
|
-
button.append(badge);
|
|
979
|
-
} else if (task.attention && task.attention.executionSignals > 0) {
|
|
980
|
-
const badge = node("span", "task-signal", String(task.attention.executionSignals));
|
|
981
|
-
badge.title = t("catalog.executionSignals");
|
|
982
|
-
button.append(badge);
|
|
983
|
-
} else if (task.workItems && task.workItems.running > 0) {
|
|
984
|
-
const badge = node("span", "task-signal is-running", String(task.workItems.running));
|
|
985
|
-
badge.title = t("stats.running");
|
|
986
|
-
button.append(badge);
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
button.addEventListener("click", function () { onSelect(task.id); });
|
|
990
|
-
return button;
|
|
416
|
+
return element;
|
|
991
417
|
}
|
|
992
418
|
`;
|