@zq-silk/yui 1.1.2 → 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/ARCHITECTURE.md +2 -1
- package/ARCHITECTURE.zh-CN.md +2 -1
- package/README.md +2 -0
- package/dist/cli/commandCatalog.js +344 -324
- 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/managedDiagnostics.js +2 -1
- package/dist/cli/updatePorts.js +0 -5
- package/dist/cli.js +184 -130
- package/dist/commands/capabilityCommands.js +5 -5
- package/dist/commands/controllerCommands.js +2 -1
- package/dist/commands/globalRoleCommands.js +86 -44
- package/dist/commands/grantCommands.js +48 -8
- package/dist/commands/taskCommands.js +157 -196
- package/dist/commands/taskContextCommand.js +29 -6
- package/dist/commands/taskFactCommands.js +10 -69
- package/dist/commands/taskInputCommands.js +26 -35
- package/dist/commands/taskPublicationCommands.js +2 -35
- package/dist/commands/taskPublicationVerifyCommand.js +1 -2
- package/dist/commands/taskUpstreamCommands.js +2 -1
- package/dist/context/runContextPack.js +8 -29
- package/dist/context/sessionBootstrapManifest.js +5 -101
- package/dist/context/taskContext.js +104 -41
- package/dist/controller/clientRuntime.js +16 -8
- package/dist/controller/fileSchedulerStoreAdapter.js +24 -0
- package/dist/controller/runtime.js +6 -0
- package/dist/core/controllerClient.js +46 -21
- package/dist/core/protocol.js +12 -7
- package/dist/doctor/doctor.js +4 -3
- package/dist/errors/cliError.js +4 -4
- package/dist/errors/cliFailure.js +213 -0
- package/dist/executor/fileRoleLaunchPlanner.js +1 -4
- package/dist/grant/capabilityGrant.js +13 -0
- package/dist/grant/taskAuthorization.js +127 -0
- package/dist/kernel/accessAssessment.js +1 -0
- package/dist/kernel/builtinCapabilities.js +88 -9
- package/dist/kernel/capabilityRegistry.js +64 -19
- package/dist/kernel/kernelPorts.js +2 -2
- 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/output/boundedRead.js +118 -0
- package/dist/plugins/pluginService.js +98 -8
- package/dist/release/releaseWorkflowEngine.js +8 -1
- package/dist/resources/projectResourceService.js +23 -1
- package/dist/runtime/managedCaller.js +2 -2
- package/dist/runtime/managedIdentity.js +6 -0
- package/dist/runtime/runtimeCoherence.js +10 -8
- package/dist/storage/sqliteSchema.js +10 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/taskStore.js +3 -0
- package/dist/surface/surfaceContributions.js +3 -0
- package/dist/task/leaderArchive.js +111 -0
- package/dist/task/leaderArchiveAuthority.js +24 -0
- package/dist/task/taskAuthority.js +5 -10
- package/dist/tmux/commandExecutor.js +7 -5
- 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/architecture/README.md +1 -0
- package/docs/architecture/README.zh-CN.md +1 -0
- package/docs/cli-information-contract.md +107 -0
- package/docs/cli-information-contract.zh-CN.md +81 -0
- package/docs/managed-turn-and-session-runtime.md +9 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
- package/docs/plugin-sdk.md +7 -3
- package/docs/plugin-sdk.zh-CN.md +5 -2
- package/docs/release-workflow.md +42 -9
- package/docs/release-workflow.zh-CN.md +30 -7
- package/docs/storage-baseline.md +5 -0
- package/docs/testing/verification-levels.md +2 -2
- package/i18n/README.zh-CN.md +2 -0
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +8 -1
- package/skills/yui-leader/references/authorization.md +64 -0
- package/skills/yui-leader/references/execution.md +18 -4
- package/skills/yui-leader/references/task-plugins.md +4 -2
- package/skills/yui-operator/SKILL.md +9 -2
- package/skills/yui-runtime/SKILL.md +77 -9
- package/skills/yui-runtime/references/publication.md +3 -1
- 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,608 +0,0 @@
|
|
|
1
|
-
export const VIEW_SCRIPT = `
|
|
2
|
-
// Page composition: sidebar lists, overview, and the task detail scaffold.
|
|
3
|
-
// All reusable widgets and cards come from components.js.
|
|
4
|
-
import { clear, node } from "/assets/js/dom.js";
|
|
5
|
-
import { renderTaskSurface } from "/assets/js/task-surface.js";
|
|
6
|
-
import { renderSessionSummary } from "/assets/js/task-summary.js";
|
|
7
|
-
import { byNewest, formatDateTime } from "/assets/js/format.js";
|
|
8
|
-
import {
|
|
9
|
-
anchorSection,
|
|
10
|
-
attentionRow,
|
|
11
|
-
conclusionMeta,
|
|
12
|
-
emptyRow,
|
|
13
|
-
executionBand,
|
|
14
|
-
historyEventRow,
|
|
15
|
-
inputCard,
|
|
16
|
-
messageCard,
|
|
17
|
-
metaItem,
|
|
18
|
-
metricTile,
|
|
19
|
-
observabilityMetricCard,
|
|
20
|
-
dagGraph,
|
|
21
|
-
overviewRow,
|
|
22
|
-
pagedList,
|
|
23
|
-
pathMetaItem,
|
|
24
|
-
pill,
|
|
25
|
-
reviewCard,
|
|
26
|
-
richText,
|
|
27
|
-
roleCard,
|
|
28
|
-
runCard,
|
|
29
|
-
sectionHead,
|
|
30
|
-
taskCard,
|
|
31
|
-
translatedStatus,
|
|
32
|
-
workItemCard
|
|
33
|
-
} from "/assets/js/components.js";
|
|
34
|
-
|
|
35
|
-
const statuses = ["all", "active", "draft", "completed", "cancelled", "archived"];
|
|
36
|
-
|
|
37
|
-
export function renderFilters(container, state, t, onFilter) {
|
|
38
|
-
const counts = state.counts || {};
|
|
39
|
-
// Single-row status chip row: every status is visible at once with its count,
|
|
40
|
-
// so the user can switch filters without opening a dropdown. The row never
|
|
41
|
-
// wraps; on narrow widths it scrolls horizontally.
|
|
42
|
-
//
|
|
43
|
-
// The row and its chips are created once and reused across renders so that
|
|
44
|
-
// switching filters does not reset the row's horizontal scroll position.
|
|
45
|
-
let row = container.querySelector(":scope > .filter-row");
|
|
46
|
-
if (!row) {
|
|
47
|
-
row = node("div", "filter-row");
|
|
48
|
-
statuses.forEach(function (status) {
|
|
49
|
-
const btn = node("button", "filter-chip");
|
|
50
|
-
btn.type = "button";
|
|
51
|
-
btn.dataset.status = status;
|
|
52
|
-
if (status !== "all") {
|
|
53
|
-
const dot = node("span", "filter-dot");
|
|
54
|
-
dot.classList.add(status);
|
|
55
|
-
btn.append(dot);
|
|
56
|
-
}
|
|
57
|
-
btn.append(node("span", "filter-label", status === "all" ? t("filter.unarchived") : translatedStatus(t, "status", status)));
|
|
58
|
-
const badge = node("span", "filter-count");
|
|
59
|
-
btn.append(badge);
|
|
60
|
-
btn.addEventListener("click", function () { onFilter(status); });
|
|
61
|
-
row.append(btn);
|
|
62
|
-
});
|
|
63
|
-
container.append(row);
|
|
64
|
-
}
|
|
65
|
-
statuses.forEach(function (status) {
|
|
66
|
-
const btn = row.querySelector('.filter-chip[data-status="' + status + '"]');
|
|
67
|
-
if (!btn) return;
|
|
68
|
-
btn.classList.toggle("is-active", state.filter === status);
|
|
69
|
-
const label = btn.querySelector(".filter-label");
|
|
70
|
-
if (label) label.textContent = status === "all"
|
|
71
|
-
? t(state.catalogAll ? "catalog.all" : "filter.unarchived") : translatedStatus(t, "status", status);
|
|
72
|
-
const count = status === "all"
|
|
73
|
-
? (counts.total === undefined ? undefined : counts.total - (state.catalogAll ? 0 : counts.archived || 0))
|
|
74
|
-
: counts[status];
|
|
75
|
-
const badge = btn.querySelector(".filter-count");
|
|
76
|
-
if (badge) {
|
|
77
|
-
if (status === "archived" && state.catalogScope && state.catalogScope.archived === "excluded") {
|
|
78
|
-
badge.hidden = true;
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if (count !== undefined && count !== null) {
|
|
82
|
-
badge.textContent = String(count);
|
|
83
|
-
badge.hidden = false;
|
|
84
|
-
} else {
|
|
85
|
-
badge.hidden = true;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function taskGroupOf(task, attentionIds) {
|
|
92
|
-
if (attentionIds.has(task.id) || (task.attention && (task.attention.openInputs > 0 || task.attention.unknownOperations > 0))) return "attention";
|
|
93
|
-
if (task.status === "active") return "active";
|
|
94
|
-
if (task.status === "draft") return "draft";
|
|
95
|
-
if (task.status === "cancelled") return "cancelled";
|
|
96
|
-
if (task.status === "archived") return "archived";
|
|
97
|
-
return "finished";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function taskMatchesQuery(task, query, locale) {
|
|
101
|
-
if (!query) return true;
|
|
102
|
-
const hay = [task.title, task.id]
|
|
103
|
-
.concat(task.tags || [])
|
|
104
|
-
.concat(task.projectNames || [])
|
|
105
|
-
.join(" ")
|
|
106
|
-
.toLocaleLowerCase(locale);
|
|
107
|
-
return hay.includes(query);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function renderTasks(container, state, t, locale, onSelect) {
|
|
111
|
-
clear(container);
|
|
112
|
-
const query = state.query.trim().toLocaleLowerCase(locale);
|
|
113
|
-
const attentionIds = new Set((state.attention || []).map(function (item) { return item.taskId; }));
|
|
114
|
-
const groups = { attention: [], active: [], draft: [], finished: [], cancelled: [], archived: [] };
|
|
115
|
-
(state.tasks || []).forEach(function (task) {
|
|
116
|
-
if (state.filter === "all" && !state.catalogAll && task.status === "archived") return;
|
|
117
|
-
if (state.filter !== "all" && task.status !== state.filter) return;
|
|
118
|
-
if (!state.catalogScope && !taskMatchesQuery(task, query, locale)) return;
|
|
119
|
-
groups[taskGroupOf(task, attentionIds)].push(task);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const order = [
|
|
123
|
-
["attention", t("group.attention")],
|
|
124
|
-
["active", t("group.active")],
|
|
125
|
-
["draft", t("group.draft")],
|
|
126
|
-
["finished", t("group.finished")],
|
|
127
|
-
["cancelled", t("group.cancelled")],
|
|
128
|
-
["archived", t("group.archived")]
|
|
129
|
-
];
|
|
130
|
-
const firstGroup = order.find(function (entry) { return groups[entry[0]].length > 0; });
|
|
131
|
-
if (!firstGroup) {
|
|
132
|
-
container.append(node("div", "empty", t("empty.tasks")));
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
order.forEach(function (entry) {
|
|
136
|
-
const list = groups[entry[0]];
|
|
137
|
-
if (!list.length) return;
|
|
138
|
-
const block = node("section", "task-group");
|
|
139
|
-
const head = node("h4", "task-group-head");
|
|
140
|
-
head.append(node("span", "", entry[1]), node("span", "count", String(list.length)));
|
|
141
|
-
block.append(head);
|
|
142
|
-
list.forEach(function (task) {
|
|
143
|
-
block.append(taskCard(task, attentionIds.has(task.id), state, t, locale, onSelect));
|
|
144
|
-
});
|
|
145
|
-
container.append(block);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function overviewBlock(head, tasks, t, locale, onSelect) {
|
|
150
|
-
const block = node("section", "overview-block");
|
|
151
|
-
block.append(sectionHead(head, { count: tasks.length }));
|
|
152
|
-
const list = node("div", "overview-list");
|
|
153
|
-
tasks.forEach(function (task) {
|
|
154
|
-
list.append(overviewRow(task, null, null, t, locale, onSelect));
|
|
155
|
-
});
|
|
156
|
-
block.append(list);
|
|
157
|
-
return block;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function renderOverview(detail, state, t, locale, onSelect) {
|
|
161
|
-
clear(detail);
|
|
162
|
-
const wrap = node("div", "overview");
|
|
163
|
-
const counts = state.counts;
|
|
164
|
-
|
|
165
|
-
const total = counts ? counts.total : 0;
|
|
166
|
-
const say = (en, cn) => locale.startsWith("zh") ? cn : en;
|
|
167
|
-
wrap.append(node("p", "muted", say(
|
|
168
|
-
"Attention counts cover the authorized catalog before search/status filters; task lists below cover this page.",
|
|
169
|
-
"关注计数覆盖搜索/状态过滤前的可见目录;下方任务列表仅覆盖当前页。")));
|
|
170
|
-
const rail = node("div", "command-rail");
|
|
171
|
-
rail.append(
|
|
172
|
-
metricTile(t("metrics.active"), counts ? counts.active : "—", { hot: true }),
|
|
173
|
-
metricTile(t("metrics.inputs"), counts ? counts.openInputs : "—", { warning: true }),
|
|
174
|
-
metricTile(t("metrics.completed"), counts ? counts.completed : "—", {}),
|
|
175
|
-
metricTile(t("metrics.total"), total, {})
|
|
176
|
-
);
|
|
177
|
-
wrap.append(rail);
|
|
178
|
-
|
|
179
|
-
if (state.catalogAttention) {
|
|
180
|
-
const catalog = node("section", "overview-block");
|
|
181
|
-
catalog.append(sectionHead(t("catalog.attention")));
|
|
182
|
-
catalog.append(node("p", "", t("catalog.signalsHelp")));
|
|
183
|
-
const groups = node("div", "overview-list");
|
|
184
|
-
Object.entries(state.catalogAttention).forEach(function (entry) {
|
|
185
|
-
const kind = entry[0], value = entry[1];
|
|
186
|
-
const button = node("button", "overview-row",
|
|
187
|
-
t("catalog." + kind) + " · " + value.count + " / " + value.taskCount + " " + t("catalog.tasks"));
|
|
188
|
-
button.type = "button";
|
|
189
|
-
button.dataset.catalogAttention = kind;
|
|
190
|
-
button.disabled = value.taskCount === 0;
|
|
191
|
-
groups.append(button);
|
|
192
|
-
});
|
|
193
|
-
catalog.append(groups);
|
|
194
|
-
wrap.append(catalog);
|
|
195
|
-
}
|
|
196
|
-
const inbox = node("section", "overview-block");
|
|
197
|
-
const inboxHead = sectionHead(t("overview.inbox"), {
|
|
198
|
-
count: counts ? counts.openInputs : (state.attention || []).length
|
|
199
|
-
});
|
|
200
|
-
inbox.append(inboxHead);
|
|
201
|
-
const items = state.attention || [];
|
|
202
|
-
if (!items.length) {
|
|
203
|
-
const empty = node("div", "inbox-empty");
|
|
204
|
-
empty.append(node("span", "dot"), node("span", "", counts && counts.openInputs > 0
|
|
205
|
-
? t("catalog.inspectInputs") : t("overview.inboxEmpty")));
|
|
206
|
-
inbox.append(empty);
|
|
207
|
-
} else {
|
|
208
|
-
const list = node("div", "inbox-list");
|
|
209
|
-
items.slice(0, 8).forEach(function (item) {
|
|
210
|
-
list.append(attentionRow(item, t, locale, onSelect));
|
|
211
|
-
});
|
|
212
|
-
inbox.append(list);
|
|
213
|
-
}
|
|
214
|
-
wrap.append(inbox);
|
|
215
|
-
if (state.catalogScope) wrap.append(node("p", "", t("catalog.pageScope")));
|
|
216
|
-
const sessions = node("section", "overview-block");
|
|
217
|
-
sessions.append(sectionHead(say("Session activity · current page only", "会话活动 · 仅当前页")));
|
|
218
|
-
sessions.append(node("p", "muted", say(
|
|
219
|
-
"Not loaded by the compact list. Active Task / AgentRun counts cannot establish how many native Sessions are running.",
|
|
220
|
-
"紧凑列表不加载会话观察。Active Task/AgentRun 数不能说明有几个原生会话在运行。")));
|
|
221
|
-
const observe = node("button", "record-open", say("Read this page's Sessions", "读取本页会话"));
|
|
222
|
-
observe.type = "button";
|
|
223
|
-
observe.dataset.observeSessions = "";
|
|
224
|
-
observe.disabled = state.sessionLoading || !state.tasks.length;
|
|
225
|
-
sessions.append(observe);
|
|
226
|
-
if (state.sessionError) sessions.append(node("p", "muted", state.sessionError));
|
|
227
|
-
if (state.sessionOverview) {
|
|
228
|
-
sessions.append(node("p", "muted", say("Read at: ", "读取于:") + formatDateTime(state.sessionOverview.readAt, locale)));
|
|
229
|
-
for (const task of state.sessionOverview.tasks) {
|
|
230
|
-
const card = node("article", "record-card");
|
|
231
|
-
const open = node("button", "record-open", task.taskId + " · " + task.title);
|
|
232
|
-
open.type = "button";
|
|
233
|
-
open.addEventListener("click", () => onSelect(task.taskId));
|
|
234
|
-
card.append(open);
|
|
235
|
-
const body = node("div", "");
|
|
236
|
-
renderSessionSummary(body, task, locale);
|
|
237
|
-
card.append(body);
|
|
238
|
-
sessions.append(card);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
wrap.append(sessions);
|
|
242
|
-
|
|
243
|
-
// Tasks whose Task-first projection says they need attention: blocked,
|
|
244
|
-
// recovering, or in an attention state. This replaces the raw stalled-AgentRun
|
|
245
|
-
// count with the derived execution status.
|
|
246
|
-
const attentionTasks = (state.tasks || []).filter(function (task) {
|
|
247
|
-
const status = task.executionStatus;
|
|
248
|
-
return status === "blocked"
|
|
249
|
-
|| status === "attention"
|
|
250
|
-
|| status === "recovering"
|
|
251
|
-
|| status === "progressing-with-attention";
|
|
252
|
-
});
|
|
253
|
-
if (attentionTasks.length) {
|
|
254
|
-
const attentionBlock = node("section", "overview-block");
|
|
255
|
-
attentionBlock.append(node("h3", "", t("overview.attention") + " · " + attentionTasks.length));
|
|
256
|
-
const list = node("div", "overview-list");
|
|
257
|
-
attentionTasks.forEach(function (task) {
|
|
258
|
-
list.append(overviewRow(
|
|
259
|
-
task,
|
|
260
|
-
t("exec.status." + task.executionStatus),
|
|
261
|
-
"has-inputs",
|
|
262
|
-
t,
|
|
263
|
-
locale,
|
|
264
|
-
onSelect
|
|
265
|
-
));
|
|
266
|
-
});
|
|
267
|
-
attentionBlock.append(list);
|
|
268
|
-
wrap.append(attentionBlock);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Active work and the freshest updates sit side by side on wide screens, so
|
|
272
|
-
// the overview fills the reading surface instead of ending at the inbox.
|
|
273
|
-
const duo = node("div", "overview-duo");
|
|
274
|
-
const activeTasks = (state.tasks || []).filter(function (task) { return task.status === "active"; });
|
|
275
|
-
if (activeTasks.length) {
|
|
276
|
-
duo.append(overviewBlock(t("overview.activeNow"), activeTasks, t, locale, onSelect));
|
|
277
|
-
}
|
|
278
|
-
const recentTasks = (state.tasks || []).filter(function (task) { return task.status !== "archived"; }).sort(byNewest).slice(0, 8);
|
|
279
|
-
if (recentTasks.length) {
|
|
280
|
-
duo.append(overviewBlock(t("overview.recent"), recentTasks, t, locale, onSelect));
|
|
281
|
-
}
|
|
282
|
-
if (duo.childNodes.length) wrap.append(duo);
|
|
283
|
-
detail.append(wrap);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export function renderLoading(container, t, key) {
|
|
287
|
-
clear(container);
|
|
288
|
-
container.append(node("div", "loading", t(key)));
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export function renderError(container, message) {
|
|
292
|
-
clear(container);
|
|
293
|
-
container.append(node("div", "error", message));
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const RUN_PAGE_SIZE = 12;
|
|
297
|
-
const LIST_PAGE_SIZE = 10;
|
|
298
|
-
const RUN_FILTERS = ["all", "active", "completed", "failed"];
|
|
299
|
-
|
|
300
|
-
function runFilterRow(sortedRuns, t, onChange) {
|
|
301
|
-
const row = node("div", "filter-row run-filter");
|
|
302
|
-
const buttons = [];
|
|
303
|
-
RUN_FILTERS.forEach(function (status) {
|
|
304
|
-
const count = status === "all"
|
|
305
|
-
? sortedRuns.length
|
|
306
|
-
: sortedRuns.filter(function (run) { return run.status === status; }).length;
|
|
307
|
-
if (status !== "all" && count === 0) return;
|
|
308
|
-
const btn = node("button", "filter-chip");
|
|
309
|
-
btn.type = "button";
|
|
310
|
-
btn.dataset.status = status;
|
|
311
|
-
if (status !== "all") btn.append(node("span", "filter-dot " + status));
|
|
312
|
-
btn.append(document.createTextNode(status === "all" ? t("status.all") : t("run." + status)));
|
|
313
|
-
btn.append(node("span", "filter-count", String(count)));
|
|
314
|
-
btn.addEventListener("click", function () { onChange(status); });
|
|
315
|
-
buttons.push({ status: status, element: btn });
|
|
316
|
-
row.append(btn);
|
|
317
|
-
});
|
|
318
|
-
return {
|
|
319
|
-
row: row,
|
|
320
|
-
sync: function (active) {
|
|
321
|
-
buttons.forEach(function (entry) {
|
|
322
|
-
entry.element.classList.toggle("is-active", entry.status === active);
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
export function renderTaskDetail(detail, data, t, locale, actions) {
|
|
329
|
-
if (data.core) return renderTaskSurface(detail, data, t, locale, actions);
|
|
330
|
-
actions = actions || {};
|
|
331
|
-
clear(detail);
|
|
332
|
-
const task = data.task;
|
|
333
|
-
const scaffold = node("div", "detail-scaffold");
|
|
334
|
-
|
|
335
|
-
// 1. Summary (anchor #detail-top)
|
|
336
|
-
const summaryBody = node("div", "section-body");
|
|
337
|
-
const headBlock = node("div", "detail-head");
|
|
338
|
-
headBlock.append(node("span", "detail-kicker", task.id));
|
|
339
|
-
headBlock.append(node("h2", "detail-title", task.title));
|
|
340
|
-
if (task.description) headBlock.append(node("p", "detail-description", task.description));
|
|
341
|
-
const meta = node("div", "detail-meta");
|
|
342
|
-
meta.append(metaItem(t("detail.updated"), formatDateTime(task.updatedAt, locale)));
|
|
343
|
-
meta.append(pill(t, "status", task.status));
|
|
344
|
-
if (task.priority) meta.append(pill(t, "priority", task.priority));
|
|
345
|
-
if (task.dueAt) meta.append(metaItem(t("detail.due"), formatDateTime(task.dueAt, locale)));
|
|
346
|
-
if (task.projectNames && task.projectNames.length) {
|
|
347
|
-
meta.append(metaItem(t("detail.project"), task.projectNames.join(", ")));
|
|
348
|
-
}
|
|
349
|
-
if (task.cwd) meta.append(pathMetaItem(t("detail.workspace"), task.cwd));
|
|
350
|
-
headBlock.append(meta);
|
|
351
|
-
summaryBody.append(headBlock);
|
|
352
|
-
|
|
353
|
-
// Task-first execution status: the derived status, owner, next action, and
|
|
354
|
-
// the attention/blocker facts behind it.
|
|
355
|
-
const band = executionBand(data.execution, t, locale);
|
|
356
|
-
if (band) summaryBody.append(band);
|
|
357
|
-
if (data.remoteDelivery) {
|
|
358
|
-
const remote = data.remoteDelivery;
|
|
359
|
-
const card = node("article", "record-card");
|
|
360
|
-
const head = node("div", "record-head");
|
|
361
|
-
head.append(
|
|
362
|
-
node("strong", "record-title", t("detail.remoteDelivery")),
|
|
363
|
-
pill(t, "remoteDelivery", remote.status)
|
|
364
|
-
);
|
|
365
|
-
card.append(head);
|
|
366
|
-
card.append(node(
|
|
367
|
-
"p",
|
|
368
|
-
"record-copy",
|
|
369
|
-
t("detail.expectedHeads") + " · " + remote.source
|
|
370
|
-
+ (remote.provisional ? " · " + t("detail.provisional") : "")
|
|
371
|
-
));
|
|
372
|
-
const facts = node("div", "record-meta");
|
|
373
|
-
const unavailable = remote.status === "unavailable";
|
|
374
|
-
facts.append(
|
|
375
|
-
node("span", "", t("detail.allMerged") + " · "
|
|
376
|
-
+ (unavailable
|
|
377
|
-
? t("detail.unavailable")
|
|
378
|
-
: remote.allMerged ? t("common.yes") : t("common.no"))),
|
|
379
|
-
node("span", "", t("detail.allVerified") + " · "
|
|
380
|
-
+ (unavailable
|
|
381
|
-
? t("detail.unavailable")
|
|
382
|
-
: remote.allVerified ? t("common.yes") : t("common.no"))),
|
|
383
|
-
node("span", "", t("detail.integratedCoverage") + " · "
|
|
384
|
-
+ (unavailable
|
|
385
|
-
? t("detail.unavailable")
|
|
386
|
-
: remote.integratedCoverageSatisfied ? t("detail.satisfied") : t("detail.blocked")))
|
|
387
|
-
);
|
|
388
|
-
card.append(facts);
|
|
389
|
-
remote.projects.forEach(function (project) {
|
|
390
|
-
card.append(node(
|
|
391
|
-
"p",
|
|
392
|
-
"muted mono",
|
|
393
|
-
project.directory + " · " + project.coverage + " · "
|
|
394
|
-
+ (project.expectedLocalCommit ? project.expectedLocalCommit.slice(0, 12) : "unknown")
|
|
395
|
-
+ " → " + (project.deliveryLocalCommit ? project.deliveryLocalCommit.slice(0, 12) : "unknown")
|
|
396
|
-
+ " → "
|
|
397
|
-
+ (project.remoteCommit ? project.remoteCommit.slice(0, 12) : "unknown")
|
|
398
|
-
+ " · PR/MR: " + (project.state || "none")
|
|
399
|
-
+ (project.adoption ? " · " + project.adoption.id : "")
|
|
400
|
-
));
|
|
401
|
-
card.append(node("p", "record-copy", project.reason));
|
|
402
|
-
});
|
|
403
|
-
summaryBody.append(card);
|
|
404
|
-
}
|
|
405
|
-
const observability = data.observability || (data.execution && data.execution.observability);
|
|
406
|
-
const metrics = observabilityMetricCard(observability, t);
|
|
407
|
-
if (metrics) summaryBody.append(metrics);
|
|
408
|
-
|
|
409
|
-
if (task.completionSummary) {
|
|
410
|
-
const conclusion = node("div", "conclusion");
|
|
411
|
-
conclusion.append(node("h3", "", t("detail.conclusion")));
|
|
412
|
-
conclusion.append(richText(null, task.completionSummary, t));
|
|
413
|
-
conclusion.append(conclusionMeta(task, t, locale, "completed"));
|
|
414
|
-
summaryBody.append(conclusion);
|
|
415
|
-
} else if (task.status === "cancelled" || task.retirementSummary) {
|
|
416
|
-
const conclusion = node("div", "conclusion archived");
|
|
417
|
-
conclusion.append(node("h3", "", t("detail.cancelled")));
|
|
418
|
-
if (task.retirementSummary) conclusion.append(richText(null, task.retirementSummary, t));
|
|
419
|
-
if (task.replacementTaskId) {
|
|
420
|
-
conclusion.append(node("p", "muted", t("detail.replacement") + " · " + task.replacementTaskId));
|
|
421
|
-
}
|
|
422
|
-
conclusion.append(conclusionMeta(task, t, locale, "cancelled"));
|
|
423
|
-
summaryBody.append(conclusion);
|
|
424
|
-
} else if (task.status === "archived" || task.archiveSummary || task.archiveReason) {
|
|
425
|
-
const conclusion = node("div", "conclusion archived");
|
|
426
|
-
conclusion.append(node("h3", "", t("detail.archived")));
|
|
427
|
-
if (task.archiveSummary) conclusion.append(richText(null, task.archiveSummary, t));
|
|
428
|
-
if (task.archiveReason) conclusion.append(node("p", "muted", task.archiveReason));
|
|
429
|
-
conclusion.append(conclusionMeta(task, t, locale, "archived"));
|
|
430
|
-
summaryBody.append(conclusion);
|
|
431
|
-
}
|
|
432
|
-
scaffold.append(anchorSection("detail-top", sectionHead(t("tabs.summary")),
|
|
433
|
-
summaryBody));
|
|
434
|
-
|
|
435
|
-
// 2. Attention (anchor #detail-attention) — open input requests, promoted from old mid-list position
|
|
436
|
-
if (data.openInputs.length) {
|
|
437
|
-
const attentionBody = node("div", "section-body");
|
|
438
|
-
data.openInputs.forEach(function (input) {
|
|
439
|
-
attentionBody.append(inputCard(input, { single: true }, t, locale, actions));
|
|
440
|
-
});
|
|
441
|
-
scaffold.append(anchorSection("detail-attention",
|
|
442
|
-
sectionHead(t("detail.attention"), { count: data.openInputs.length }),
|
|
443
|
-
attentionBody));
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const stalledRuns = data.runtimeHealth && data.runtimeHealth.needsAttentionRuns
|
|
447
|
-
? data.runtimeHealth.needsAttentionRuns
|
|
448
|
-
: [];
|
|
449
|
-
if (stalledRuns.length) {
|
|
450
|
-
const runtimeHealthBody = node("div", "section-body");
|
|
451
|
-
stalledRuns.forEach(function (run) {
|
|
452
|
-
const card = node("article", "record-card");
|
|
453
|
-
card.append(
|
|
454
|
-
node("strong", "", run.roleName + " · " + run.runId),
|
|
455
|
-
node("p", "record-copy", (run.kind || "workflow-not-progressing") + " · " + (run.classification || "truly-stalled")),
|
|
456
|
-
node("small", "", t("detail.lastProgress") + " · " + formatDateTime(run.progressAt, locale))
|
|
457
|
-
);
|
|
458
|
-
runtimeHealthBody.append(card);
|
|
459
|
-
});
|
|
460
|
-
scaffold.append(anchorSection(
|
|
461
|
-
"detail-health",
|
|
462
|
-
sectionHead(t("detail.runtimeHealth"), { count: stalledRuns.length }),
|
|
463
|
-
runtimeHealthBody
|
|
464
|
-
));
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (observability && observability.dag) {
|
|
468
|
-
const dagBody = node("div", "section-body");
|
|
469
|
-
dagBody.append(dagGraph(observability.dag, t));
|
|
470
|
-
scaffold.append(anchorSection(
|
|
471
|
-
"detail-dag",
|
|
472
|
-
sectionHead(t("detail.dag"), { count: observability.dag.nodes.length }),
|
|
473
|
-
dagBody
|
|
474
|
-
));
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// 3. Focus (anchor #detail-focus) — brief + technical approach
|
|
478
|
-
const focusBody = node("div", "section-body");
|
|
479
|
-
if (data.brief) {
|
|
480
|
-
const focusCard = node("article", "record-card");
|
|
481
|
-
const working = task.status === "active" || task.status === "draft";
|
|
482
|
-
focusCard.append(richText(working
|
|
483
|
-
? t("detail.focus") : (locale.startsWith("zh") ? "结束前的工作重点" : "Last working focus"),
|
|
484
|
-
data.brief.currentFocus || data.brief.objective, t));
|
|
485
|
-
if (data.brief.technicalApproach) {
|
|
486
|
-
focusCard.append(richText(t("detail.technicalApproach"), data.brief.technicalApproach, t, { muted: true }));
|
|
487
|
-
}
|
|
488
|
-
if (data.brief.leaderSummary) focusCard.append(richText(working
|
|
489
|
-
? t("detail.leaderSummary") : (locale.startsWith("zh") ? "结束前的进展摘要" : "Last working report"),
|
|
490
|
-
data.brief.leaderSummary, t, { muted: true }));
|
|
491
|
-
focusBody.append(focusCard);
|
|
492
|
-
} else {
|
|
493
|
-
focusBody.append(emptyRow(t, "empty.brief"));
|
|
494
|
-
}
|
|
495
|
-
scaffold.append(anchorSection("detail-focus", sectionHead(t("tabs.focus")), focusBody));
|
|
496
|
-
|
|
497
|
-
// 4. Work items (anchor #detail-work) — the operational deck
|
|
498
|
-
const workBody = node("div", "section-body");
|
|
499
|
-
if (!data.workItems.length) {
|
|
500
|
-
workBody.append(emptyRow(t));
|
|
501
|
-
} else {
|
|
502
|
-
const workItemTitles = {};
|
|
503
|
-
data.workItems.forEach(function (item) { workItemTitles[item.id] = item.title; });
|
|
504
|
-
data.workItems.slice().sort(byNewest).forEach(function (item) {
|
|
505
|
-
workBody.append(workItemCard(item, workItemTitles, t, locale, actions, task.id));
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
scaffold.append(anchorSection("detail-work",
|
|
509
|
-
sectionHead(t("detail.workItems"), { count: data.workItems.length }),
|
|
510
|
-
workBody));
|
|
511
|
-
|
|
512
|
-
// 5. AgentRuns (anchor #detail-exec) — execution grid with status filter + paging
|
|
513
|
-
const execWrap = node("div", "section-body");
|
|
514
|
-
const execBody = node("div", "run-grid");
|
|
515
|
-
const sortedRuns = data.runs.slice().sort(byNewest);
|
|
516
|
-
if (!sortedRuns.length) {
|
|
517
|
-
execBody.append(emptyRow(t, "empty.runs"));
|
|
518
|
-
execWrap.append(execBody);
|
|
519
|
-
} else {
|
|
520
|
-
let activeRunFilter = "all";
|
|
521
|
-
const filter = runFilterRow(sortedRuns, t, function (status) {
|
|
522
|
-
activeRunFilter = status;
|
|
523
|
-
filter.sync(status);
|
|
524
|
-
renderRunGrid();
|
|
525
|
-
});
|
|
526
|
-
function renderRunGrid() {
|
|
527
|
-
clear(execBody);
|
|
528
|
-
const visible = activeRunFilter === "all"
|
|
529
|
-
? sortedRuns
|
|
530
|
-
: sortedRuns.filter(function (run) { return run.status === activeRunFilter; });
|
|
531
|
-
pagedList(execBody, visible, RUN_PAGE_SIZE, function (run) {
|
|
532
|
-
return runCard(run, t, locale);
|
|
533
|
-
}, t);
|
|
534
|
-
}
|
|
535
|
-
filter.sync(activeRunFilter);
|
|
536
|
-
execWrap.append(filter.row, execBody);
|
|
537
|
-
renderRunGrid();
|
|
538
|
-
}
|
|
539
|
-
scaffold.append(anchorSection("detail-exec",
|
|
540
|
-
sectionHead(t("detail.execution"), { count: data.runs.length }),
|
|
541
|
-
execWrap));
|
|
542
|
-
|
|
543
|
-
// 5b. Reviews (anchor #detail-reviews) — review rounds for completed candidates
|
|
544
|
-
const reviewBody = node("div", "row-list");
|
|
545
|
-
const sortedRounds = (data.reviewRounds || []).slice().sort(byNewest);
|
|
546
|
-
if (!sortedRounds.length) {
|
|
547
|
-
reviewBody.append(emptyRow(t));
|
|
548
|
-
} else {
|
|
549
|
-
pagedList(reviewBody, sortedRounds, LIST_PAGE_SIZE, function (round) {
|
|
550
|
-
return reviewCard(round, t, locale);
|
|
551
|
-
}, t);
|
|
552
|
-
}
|
|
553
|
-
scaffold.append(anchorSection("detail-reviews",
|
|
554
|
-
sectionHead(t("detail.reviews"), { count: sortedRounds.length }),
|
|
555
|
-
reviewBody));
|
|
556
|
-
|
|
557
|
-
// 6. Roles (anchor #detail-roles)
|
|
558
|
-
const rolesBody = node("div", "row-list");
|
|
559
|
-
if (!data.roles.length) rolesBody.append(emptyRow(t));
|
|
560
|
-
data.roles.forEach(function (role) {
|
|
561
|
-
rolesBody.append(roleCard(role, task, t, locale, actions));
|
|
562
|
-
});
|
|
563
|
-
scaffold.append(anchorSection("detail-roles",
|
|
564
|
-
sectionHead(t("detail.roles"), { count: data.roles.length }),
|
|
565
|
-
rolesBody));
|
|
566
|
-
|
|
567
|
-
// 7. History (anchor #detail-history) — merged milestones + decisions, chronological
|
|
568
|
-
const historyEvents = [];
|
|
569
|
-
(data.milestones || []).forEach(function (m) {
|
|
570
|
-
historyEvents.push({ kind: "milestone", item: m });
|
|
571
|
-
});
|
|
572
|
-
(data.decisions || []).forEach(function (d) {
|
|
573
|
-
historyEvents.push({ kind: "decision", item: d });
|
|
574
|
-
});
|
|
575
|
-
historyEvents.sort(function (a, b) {
|
|
576
|
-
return Date.parse(b.item.createdAt || b.item.updatedAt) - Date.parse(a.item.createdAt || a.item.updatedAt);
|
|
577
|
-
});
|
|
578
|
-
const historyBody = node("div", "section-body");
|
|
579
|
-
if (!historyEvents.length) {
|
|
580
|
-
historyBody.append(emptyRow(t, "detail.historyEmpty"));
|
|
581
|
-
} else {
|
|
582
|
-
pagedList(historyBody, historyEvents, LIST_PAGE_SIZE, function (event) {
|
|
583
|
-
return historyEventRow(event, t, locale);
|
|
584
|
-
}, t);
|
|
585
|
-
}
|
|
586
|
-
scaffold.append(anchorSection("detail-history",
|
|
587
|
-
sectionHead(t("tabs.history"), { count: historyEvents.length }),
|
|
588
|
-
historyBody));
|
|
589
|
-
|
|
590
|
-
// 8. Messages (anchor #detail-messages)
|
|
591
|
-
const messagesBody = node("div", "row-list");
|
|
592
|
-
if (!data.messages.length) {
|
|
593
|
-
messagesBody.append(emptyRow(t));
|
|
594
|
-
} else {
|
|
595
|
-
pagedList(messagesBody, data.messages.slice().sort(byNewest), LIST_PAGE_SIZE, function (message) {
|
|
596
|
-
const source = message.resultRef && data.runs.find(function (run) {
|
|
597
|
-
return run.id === message.resultRef.runId && run.roleName === message.author.roleName;
|
|
598
|
-
});
|
|
599
|
-
return messageCard(message, t, locale, source && source.result);
|
|
600
|
-
}, t);
|
|
601
|
-
}
|
|
602
|
-
scaffold.append(anchorSection("detail-messages",
|
|
603
|
-
sectionHead(t("detail.messages"), { count: data.messages.length }),
|
|
604
|
-
messagesBody));
|
|
605
|
-
|
|
606
|
-
detail.append(scaffold);
|
|
607
|
-
}
|
|
608
|
-
`;
|