@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.
Files changed (61) hide show
  1. package/dist/cli/commandCatalog.js +294 -286
  2. package/dist/cli/commandDiscovery.js +69 -0
  3. package/dist/cli/completion.js +22 -227
  4. package/dist/cli/dynamicCompletion.js +12 -6
  5. package/dist/cli/invocationAuthority.js +2 -2
  6. package/dist/cli/invocationRouter.js +18 -7
  7. package/dist/cli.js +71 -23
  8. package/dist/commands/capabilityCommands.js +5 -5
  9. package/dist/commands/globalRoleCommands.js +2 -1
  10. package/dist/commands/taskCommands.js +11 -0
  11. package/dist/context/taskContext.js +6 -2
  12. package/dist/kernel/accessAssessment.js +1 -0
  13. package/dist/kernel/builtinCapabilities.js +28 -1
  14. package/dist/kernel/capabilityRegistry.js +64 -19
  15. package/dist/nativeAgent/agent.js +173 -0
  16. package/dist/nativeAgent/contracts.js +1 -0
  17. package/dist/nativeAgent/demo.js +29 -0
  18. package/dist/nativeAgent/index.js +3 -0
  19. package/dist/nativeAgent/mockProvider.js +43 -0
  20. package/dist/nativeAgent/textTools.js +156 -0
  21. package/dist/nativeAgent/validation.js +95 -0
  22. package/dist/plugins/pluginService.js +98 -8
  23. package/dist/resources/projectResourceService.js +23 -1
  24. package/dist/runtime/managedIdentity.js +6 -0
  25. package/dist/surface/surfaceContributions.js +3 -0
  26. package/dist/task/taskAuthority.js +5 -10
  27. package/dist/web/assets/assetManifest.js +38 -22
  28. package/dist/web/assets/client/api.js +115 -0
  29. package/dist/web/assets/client/app.js +555 -863
  30. package/dist/web/assets/client/components.js +323 -897
  31. package/dist/web/assets/client/detail.js +333 -0
  32. package/dist/web/assets/client/dock.js +225 -0
  33. package/dist/web/assets/client/dom.js +55 -3
  34. package/dist/web/assets/client/evidence.js +172 -0
  35. package/dist/web/assets/client/format.js +25 -14
  36. package/dist/web/assets/client/forms.js +230 -0
  37. package/dist/web/assets/client/i18n.js +1020 -791
  38. package/dist/web/assets/client/overview.js +115 -0
  39. package/dist/web/assets/client/records.js +63 -0
  40. package/dist/web/assets/client/sections.js +313 -0
  41. package/dist/web/assets/client/sidebar.js +117 -0
  42. package/dist/web/assets/client/theme.js +61 -22
  43. package/dist/web/assets/icons.js +39 -0
  44. package/dist/web/assets/shell.js +124 -118
  45. package/dist/web/assets/styles/base.js +41 -0
  46. package/dist/web/assets/styles/components.js +167 -0
  47. package/dist/web/assets/styles/layout.js +49 -77
  48. package/dist/web/assets/styles/markdown.js +16 -24
  49. package/dist/web/assets/styles/responsive.js +33 -44
  50. package/dist/web/assets/styles/tokens.js +65 -89
  51. package/dist/web/assets/styles/views.js +289 -0
  52. package/dist/web/webServer.js +2 -1
  53. package/docs/release-workflow.md +23 -0
  54. package/docs/release-workflow.zh-CN.md +16 -0
  55. package/package.json +1 -1
  56. package/skills/yui-runtime/SKILL.md +26 -0
  57. package/dist/web/assets/client/taskSummary.js +0 -350
  58. package/dist/web/assets/client/taskSurface.js +0 -616
  59. package/dist/web/assets/client/view.js +0 -608
  60. package/dist/web/assets/styles/cards.js +0 -247
  61. package/dist/web/assets/styles/widgets.js +0 -168
@@ -1,616 +0,0 @@
1
- export const TASK_SURFACE_SCRIPT = `
2
- import { node, clear } from "/assets/js/dom.js";
3
- import { anchorSection, sectionHead, richText, inputCard, roleCard, runCard, pill } from "/assets/js/components.js";
4
- import { renderTaskSummary, renderEvidence } from "/assets/js/task-summary.js";
5
-
6
- // All business facts below retain their Context reference. Expanded values
7
- // are current reads, not mutations of a historical Context snapshot.
8
- export function renderTaskSurface(container, data, t, locale, actions) {
9
- clear(container);
10
- const core = data.core;
11
- const task = data.task;
12
- container.dataset.taskId = task.id;
13
- const zh = locale.startsWith("zh");
14
- const say = (en, cn) => zh ? cn : en;
15
- const records = (store) => core.records.filter((entry) => entry.ref.store === store);
16
- const values = (store) => records(store).filter((entry) => !entry.omitted).map((entry) => entry.value);
17
- const scaffold = node("div", "detail-scaffold task-surface");
18
- const summary = node("div", "section-body");
19
- summary.append(node("span", "detail-kicker", task.id), node("h2", "detail-title", task.title), pill(t, "status", task.status));
20
- renderTaskSummary(summary, data, t, locale, actions, recordCard);
21
- const discuss = node(actions.conversationHost ? "section" : "details",
22
- actions.conversationHost ? "conversation-compose record-card" : "record-card");
23
- discuss.dataset.viewKey = "discussion";
24
- discuss.append(node(actions.conversationHost ? "h3" : "summary", "", say("Discuss this Task", "讨论此任务")));
25
- const conversation = node("button", "record-open", say("View Leader Session (read-only)", "查看 Leader Session(只读)"));
26
- conversation.type = "button";
27
- // A Draft's planning Session is a real Leader Session, so it is viewable once it
28
- // actually exists. The button is disabled only when there is no Session to show
29
- // — not because the Task has yet to be activated. The Session lives in the
30
- // runtime observation, which arrives separately from the Context snapshot.
31
- const leaderRole = (data.runtime?.roles ?? []).find((role) => role.name === "leader") ?? null;
32
- const hasLeaderSession = Boolean(leaderRole?.runtimeSession?.nativeSessionId);
33
- conversation.disabled = task.status === "archived" || !hasLeaderSession;
34
- conversation.addEventListener("click", () => actions.openTerminal({ scope: "task", taskId: task.id, roleName: "leader" }));
35
- discuss.append(conversation);
36
- const chat = node("form", "record-card");
37
- const chatLabel = node("label", "", say("Message to Task Leader", "发送给 Task Leader"));
38
- const message = node("textarea", "");
39
- message.required = true;
40
- message.maxLength = 8000;
41
- chatLabel.append(message);
42
- // Submission intent is an explicit user choice, never inferred from the body
43
- // (task-32 §2.5); discuss is the default so the control matches the service.
44
- const intentLabel = node("label", "", say("Intent", "提交意图"));
45
- const intentSelect = node("select", "");
46
- const intentOptions = [
47
- ["discuss", say("Discuss — route to planning", "讨论 — 进入规划")],
48
- ["record", say("Record — save only", "记录 — 仅保存")],
49
- ["develop", say("Develop — request activation", "开发 — 请求激活")]
50
- ];
51
- for (const [value, text] of intentOptions) {
52
- const option = node("option", "", text);
53
- option.value = value;
54
- intentSelect.append(option);
55
- }
56
- intentSelect.value = "discuss";
57
- intentLabel.append(intentSelect);
58
- chat.addEventListener("input", () => { chat.dataset.unsent = message.value ? "true" : "false"; });
59
- const send = node("button", "record-open", say("Send", "发送"));
60
- send.type = "submit";
61
- send.disabled = !["active", "draft"].includes(task.status);
62
- const sent = node("p", "muted", say("Not submitted", "未提交"));
63
- sent.setAttribute("role", "status");
64
- // §2.5 facets render here, one per line; cleared and repopulated on every submit.
65
- const facets = node("div", "section-body");
66
- facets.dataset.submissionFacets = "";
67
- chat.append(chatLabel, intentLabel, send, sent, facets);
68
- chat.addEventListener("submit", async (event) => {
69
- event.preventDefault();
70
- if (send.disabled) return;
71
- send.disabled = true;
72
- chat.dataset.unsent = "true";
73
- const requestId = crypto.randomUUID();
74
- const intent = intentSelect.value;
75
- clear(facets);
76
- sent.textContent = say("Waiting for receipt · ", "等待回执 · ") + requestId;
77
- try {
78
- // requestId is the submission key: the same key retried returns the original
79
- // Message and routing rather than a second submission (task-32 §2.3).
80
- const receipt = await actions.sendMessage(task.id, message.value, requestId, intent);
81
- sent.textContent = say("Saved · ", "已保存 · ") + receipt.record.id;
82
- renderSubmissionFacets(facets, receipt.submission, say);
83
- message.value = "";
84
- chat.dataset.unsent = "false";
85
- send.disabled = false;
86
- } catch (error) {
87
- if (error.disposition === "not-submitted") {
88
- sent.textContent = say("Not submitted: ", "未提交:") + error.message;
89
- send.disabled = false;
90
- return;
91
- }
92
- // A lost response must never auto-replay a write; the same requestId is safe
93
- // to resend by hand because the server dedups on it (§2.3), but the user
94
- // reloads and inspects first rather than blindly resending.
95
- sent.textContent = say("Unknown submission outcome. Reload and inspect saved messages before sending again · ",
96
- "提交结果未知。请重新加载并检查已保存消息,不要盲目重发 · ") + requestId;
97
- }
98
- });
99
- discuss.append(chat);
100
- if (actions.conversationHost) {
101
- const feed = node("div", "conversation-feed");
102
- feed.setAttribute("aria-label", say("Recorded Task messages", "已记录的 Task 消息"));
103
- const messages = records("task-message");
104
- messages.forEach((entry) => {
105
- if (entry.omitted) {
106
- feed.append(recordCard(entry));
107
- return;
108
- }
109
- const value = entry.value;
110
- const item = node("article", "conversation-item");
111
- if (value.author?.type === "user") item.classList.add("is-user");
112
- const head = node("div", "conversation-item-head");
113
- const author = value.author?.roleName || t("author." + (value.author?.type || "system"));
114
- head.append(node("strong", "", author));
115
- if (value.createdAt) {
116
- const when = node("time", "", new Intl.DateTimeFormat(locale, {
117
- month: "short", day: "numeric", hour: "2-digit", minute: "2-digit"
118
- }).format(new Date(value.createdAt)));
119
- when.dateTime = value.createdAt;
120
- head.append(when);
121
- }
122
- item.append(head, richText(null, value.body || "", t));
123
- feed.append(item);
124
- });
125
- if (!messages.length) feed.append(node("p", "muted", core.omitted.records
126
- ? say("Compact read omits some messages. Open History & messages for source references.",
127
- "紧凑读取省略了部分消息;可在「历史与消息」查看来源引用。")
128
- : say("No recorded Task messages yet.", "尚无已记录的 Task 消息。")));
129
- if (core.omitted.records && messages.length) feed.append(node("p", "muted",
130
- say("Compact read may omit older messages; see History & messages for source references.",
131
- "紧凑读取可能省略较早消息;可在「历史与消息」查看来源引用。")));
132
- actions.conversationHost.replaceChildren(feed, discuss);
133
- actions.conversationHost.dataset.taskId = task.id;
134
- } else {
135
- summary.append(discuss);
136
- }
137
- // decision-3 three-action input control (queue / steer / interrupt). This is
138
- // the same application-layer path the CLI drives; the Web surface adds no
139
- // fourth action and no auto-fallback. Steer/interrupt name an exact current
140
- // Turn (expected-target); a mismatch or missing Turn is a visible failure
141
- // receipt, never a silent downgrade to a queue.
142
- const control = node("details", "record-card");
143
- control.dataset.viewKey = "control";
144
- control.append(node("summary", "", say("Redirect a running Role (queue / steer / interrupt)",
145
- "改向运行中的 Role(排队/即时插话/打断)")));
146
- const controlForm = node("form", "record-block");
147
- const actionLabel = node("label", "", say("Action", "动作"));
148
- const action = node("select", "");
149
- for (const [val, en, cn] of [["queue", "Queue (deliver at next opportunity)", "排队(下次机会送达)"],
150
- ["steer", "Steer (inject into the exact current Turn)", "即时插话(注入当前 Turn)"],
151
- ["interrupt", "Interrupt (native cancel of the current Turn)", "打断(原生取消当前 Turn)"]]) {
152
- const opt = node("option", "", say(en, cn));
153
- opt.value = val;
154
- action.append(opt);
155
- }
156
- actionLabel.append(action);
157
- const roleLabel = node("label", "", say("Target Role", "目标 Role"));
158
- const role = node("input", "");
159
- role.placeholder = say("e.g. worker (blank = Leader for queue)", "如 worker(排队留空 = Leader)");
160
- roleLabel.append(role);
161
- const bodyLabel = node("label", "", say("Message", "消息内容"));
162
- const body = node("textarea", "");
163
- body.maxLength = 8000;
164
- bodyLabel.append(body);
165
- const targetLabel = node("label", "", say("Expected current Turn", "预期当前 Turn"));
166
- const expectedTarget = node("input", "");
167
- expectedTarget.placeholder = say("exact current Turn id (attempt/native)", "当前 Turn 的精确标识");
168
- targetLabel.append(expectedTarget);
169
- const workItemLabel = node("label", "", say("Work Item (optional)", "Work Item(可选)"));
170
- const workItem = node("input", "");
171
- workItemLabel.append(workItem);
172
- const thenLabel = node("label", "", say("Then-message ref (optional)", "打断后续消息引用(可选)"));
173
- const thenMessage = node("input", "");
174
- thenMessage.placeholder = say("task/message id already saved", "已保存的 task/message 引用");
175
- thenLabel.append(thenMessage);
176
- const controlSend = node("button", "record-open", say("Submit control", "提交控制"));
177
- controlSend.type = "submit";
178
- controlSend.disabled = !["active", "draft"].includes(task.status);
179
- const controlSent = node("p", "muted", say("Not submitted", "未提交"));
180
- controlSent.setAttribute("role", "status");
181
- // Field visibility follows the selected action so the form can only submit a
182
- // legal shape: interrupt carries no body; queue needs no expected-target;
183
- // then-message is an interrupt-only continuation.
184
- const applyAction = () => {
185
- const kind = action.value;
186
- bodyLabel.hidden = kind === "interrupt";
187
- targetLabel.hidden = kind === "queue";
188
- thenLabel.hidden = kind !== "interrupt";
189
- roleLabel.hidden = false;
190
- role.required = kind !== "queue";
191
- body.required = kind !== "interrupt";
192
- expectedTarget.required = kind !== "queue";
193
- };
194
- action.addEventListener("change", applyAction);
195
- applyAction();
196
- controlForm.addEventListener("input", () => { controlForm.dataset.unsent = "true"; });
197
- controlForm.append(actionLabel, roleLabel, bodyLabel, targetLabel, workItemLabel, thenLabel, controlSend, controlSent);
198
- controlForm.addEventListener("submit", async (event) => {
199
- event.preventDefault();
200
- if (controlSend.disabled) return;
201
- controlSend.disabled = true;
202
- controlForm.dataset.unsent = "true";
203
- const requestId = crypto.randomUUID();
204
- const kind = action.value;
205
- const payload = { action: kind, requestId };
206
- if (kind === "interrupt") {
207
- payload.role = role.value.trim();
208
- payload.expectedTarget = expectedTarget.value.trim();
209
- if (thenMessage.value.trim()) payload.thenMessage = thenMessage.value.trim();
210
- } else {
211
- payload.body = body.value;
212
- if (kind === "steer") { payload.to = role.value.trim(); payload.expectedTarget = expectedTarget.value.trim(); }
213
- else if (role.value.trim()) payload.to = role.value.trim();
214
- if (workItem.value.trim()) payload.workItem = workItem.value.trim();
215
- }
216
- controlSent.textContent = say("Waiting for receipt · ", "等待回执 · ") + requestId;
217
- try {
218
- const receipt = await actions.controlInput(task.id, payload, requestId);
219
- // The receipt states exactly what settled: a live steer/interrupt names its
220
- // Host outcome; a queue or a not-delivered/idempotent result names its own
221
- // state. None of these implies the Provider executed the input.
222
- const settled = receipt.steer || receipt.interrupt || receipt.delivery
223
- || { state: receipt.disposition };
224
- const outcome = settled.outcome ? " · outcome " + settled.outcome : "";
225
- const detail = settled.detail ? " · " + settled.detail : "";
226
- controlSent.textContent = say("Receipt: ", "回执:") + receipt.action + " · " + settled.state
227
- + outcome + detail + (receipt.record ? " · " + receipt.record.id : "");
228
- controlForm.dataset.unsent = "false";
229
- controlSend.disabled = false;
230
- } catch (error) {
231
- if (error.disposition === "not-submitted") {
232
- controlSent.textContent = say("Not submitted: ", "未提交:") + error.message;
233
- controlSend.disabled = false;
234
- return;
235
- }
236
- // A committed input whose live edge is delivery-unknown must not be
237
- // blindly re-sent under a new requestId or a different action.
238
- controlSent.textContent = say("Unknown outcome. Re-read the Session before acting; do not resend · ",
239
- "结果未知。请先重新读取 Session,不要盲目重发 · ") + requestId;
240
- }
241
- });
242
- control.append(controlForm);
243
- summary.append(control);
244
- // A Draft's Leader conversation IS its planning Turn, so this entry point does
245
- // reach a Leader. Report the real planning facts the snapshot carries — Turn,
246
- // Session, environment and the time they were observed — instead of asserting
247
- // that no runtime exists. When nothing has been dispatched yet, say exactly
248
- // that rather than implying the entry point is inert.
249
- if (task.status === "draft") {
250
- // Planning facts come from the Context snapshot, which is the authoritative
251
- // durable read and the one that drives re-rendering. The runtime observation
252
- // is explicitly optional (it may time out and only ever enriches a live
253
- // Session), so it must not decide whether a planning Turn is reported.
254
- const turnEntries = records("run");
255
- const planningTurns = values("run").filter((turn) =>
256
- turn.roleName === "leader" && turn.purpose === "planning");
257
- // The latest planning Turn, whatever state it reached. A completed planning
258
- // Turn is still the real observed fact; reporting only active ones would call
259
- // a Draft that has genuinely talked to its Leader "not dispatched".
260
- const planningTurn = planningTurns[planningTurns.length - 1] ?? null;
261
- // A bounded snapshot may withhold Turn values. That is not evidence that no
262
- // Turn exists, so it is reported as withheld rather than as "not dispatched".
263
- const withheldTurns = planningTurn === null && turnEntries.some((entry) => entry.omitted);
264
- const environment = planningTurn?.effective?.executionEnvironment ?? null;
265
- const liveLeader = (data.runtime?.roles ?? []).find((role) => role.name === "leader") ?? null;
266
- const planning = node("div", "record-card");
267
- planning.dataset.planning = planningTurn !== null
268
- ? planningTurn.status
269
- : withheldTurns ? "withheld" : "not-dispatched";
270
- planning.append(node("p", "muted", withheldTurns
271
- ? say("This bounded snapshot withholds AgentRun detail; read the original planning Run.",
272
- "当前受限快照未展开 AgentRun 详情;请读取原始 planning Run。")
273
- : planningTurn === null
274
- ? say("No planning AgentRun is included. Direct Session activity is shown separately.",
275
- "本次读取未包含 planning AgentRun;直接 Session 活动单独展示。")
276
- : planningTurn.status === "active"
277
- ? say("The planning AgentRun is open; this is not proof of native activity.",
278
- "planning AgentRun 尚未结清;这不证明原生会话仍有活动。")
279
- : say("Planning AgentRun reached " + planningTurn.status
280
- + "; messages reach the same Task Leader and queue the next Turn.",
281
- "planning AgentRun 已" + planningTurn.status + ";消息仍送达同一 Task Leader 并为下一个 Turn 排队。")));
282
- const facts = [
283
- [say("Planning AgentRun", "planning AgentRun"), planningTurn
284
- ? planningTurn.id + " (" + planningTurn.status + ")"
285
- : withheldTurns ? say("withheld by this snapshot", "本快照未展开") : say("none", "无")],
286
- [say("Provider terminal", "Provider 终态"),
287
- planningTurn?.result?.provider?.status ?? say("not reported", "未上报")],
288
- // The Provider conversation is the durable transport identity of the
289
- // Session this Turn actually ran on; the live Session, when observed,
290
- // is reported separately so a stale read is never dressed up as current.
291
- [say("Provider conversation", "Provider 会话"),
292
- planningTurn?.result?.provider?.conversationId ?? say("not reported", "未上报")],
293
- [say("Live Leader Session", "在线 Leader Session"),
294
- liveLeader?.runtimeSession?.nativeSessionId
295
- ?? say("not currently running", "当前未在运行")],
296
- [say("Environment", "环境"), environment?.environmentRef
297
- ?? say("empty (legal for planning)", "空环境(规划阶段合法)")],
298
- [say("Run record updated", "Run 记录更新时间"),
299
- planningTurn?.result?.completedAt ?? planningTurn?.updatedAt ?? say("unknown", "未知")]
300
- ];
301
- for (const [label, value] of facts) {
302
- planning.append(node("p", "muted", label + ": " + value));
303
- }
304
- summary.append(planning);
305
- }
306
- if (task.description) {
307
- const requirements = node("details", "record-card");
308
- requirements.dataset.viewKey = "requirements";
309
- requirements.append(node("summary", "", say("Original requirements and boundaries", "原始要求与边界")),
310
- richText(null, task.description, t));
311
- summary.append(requirements);
312
- }
313
- const edit = node("details", "record-card");
314
- edit.append(node("summary", "", say("Edit Task title", "修改任务标题")));
315
- const form = node("form", "record-block");
316
- const label = node("label", "", say("Title", "标题"));
317
- const title = node("input", "");
318
- title.value = task.title;
319
- title.required = true;
320
- title.disabled = task.status === "archived";
321
- title.maxLength = 500;
322
- label.append(title);
323
- form.addEventListener("input", () => { form.dataset.unsent = "true"; });
324
- const save = node("button", "record-open", say("Save", "保存"));
325
- save.type = "submit";
326
- save.disabled = task.status === "archived";
327
- const feedback = node("p", "muted", say("Not submitted", "未提交"));
328
- feedback.setAttribute("role", "status");
329
- form.append(label, save, feedback);
330
- form.addEventListener("submit", async (event) => {
331
- event.preventDefault();
332
- if (save.disabled) return;
333
- save.disabled = true;
334
- form.dataset.unsent = "true";
335
- const requestId = crypto.randomUUID();
336
- feedback.textContent = say("Waiting for save receipt · ", "正在等待保存回执 · ") + requestId;
337
- try {
338
- const receipt = await actions.updateTask(task.id, { title: title.value }, requestId);
339
- feedback.textContent = say("Saved · revision ", "已保存 · revision ") + receipt.revision;
340
- title.value = receipt.record.title;
341
- form.dataset.unsent = "false";
342
- save.disabled = false;
343
- } catch (error) {
344
- if (error.disposition === "not-submitted") {
345
- feedback.textContent = say("Not saved: ", "未保存:") + error.message;
346
- save.disabled = false;
347
- return;
348
- }
349
- // Local mutation IDs correlate requests; they are not a deduplication
350
- // ledger. A lost response must never automatically replay a write.
351
- feedback.textContent = say("Unknown save outcome. Reload this page to read current facts before submitting again. Request: ",
352
- "保存结果未知。请重新加载页面读取当前事实,再决定是否提交。请求:") + requestId;
353
- }
354
- });
355
- edit.append(form);
356
- summary.append(edit);
357
- scaffold.append(anchorSection("detail-top", sectionHead(t("tabs.summary")), summary));
358
-
359
- function recordCard(entry) {
360
- const card = node("article", "record-card");
361
- card.append(node("strong", "", entry.ref.store + " · " + entry.ref.refId));
362
- const ref = node("small", "muted", say("Revision: ", "版本:") + entry.ref.revision);
363
- card.append(ref);
364
- if (!entry.omitted) {
365
- const value = entry.value;
366
- const text = value.content || value.summary || value.body || value.objective || value.title || value.leaderSummary;
367
- if (text) card.append(richText(null, text, t));
368
- if (entry.ref.store === "task-decision" && value.rationale) {
369
- card.append(richText(say("Reason and boundaries", "理由与边界"), value.rationale, t));
370
- }
371
- if (value.status) card.append(node("p", "muted", value.status));
372
- if (value.roleName) card.append(node("p", "muted", value.roleName));
373
- if (entry.ref.store === "work-item") {
374
- card.append(node("p", "muted", say("Owner: ", "负责人:") + (value.assignee || "Leader")));
375
- if (value.acceptance) card.append(richText(say("Acceptance criteria", "验收标准"), value.acceptance.join("\\n"), t));
376
- }
377
- if (entry.ref.store === "job") {
378
- const status = value.status;
379
- const label = status === "queued" ? say("Queued", "已排队")
380
- : status === "running" ? say("Waiting for a terminal receipt", "等待明确终态")
381
- : status === "unknown-needs-attention" ? say("Unknown effect — inspect the original operation; do not resend", "效果未知——检查原操作,不要重发")
382
- : say("Terminal record", "已记录终态");
383
- card.append(node("p", "", label + " · " + task.id + "/" + value.id));
384
- }
385
- } else card.append(node("p", "muted", entry.summary || say("Value omitted from compact read", "紧凑读取已省略正文")));
386
- const expand = node("button", "record-open", say("Read full record / source", "读取完整记录/来源"));
387
- expand.type = "button";
388
- expand.addEventListener("click", async () => {
389
- expand.disabled = true;
390
- try {
391
- const result = await actions.inspect(task.id, entry.ref);
392
- const content = node("pre", "surface-json", JSON.stringify(result.value, null, 2));
393
- card.append(content);
394
- if (result.result?.output) card.append(richText(say("Original execution report", "原始执行报告"), result.result.output, t));
395
- if (result.result?.diagnostic) card.append(richText(say("Execution diagnostic", "执行诊断"), result.result.diagnostic, t));
396
- if (result.execution) card.append(node("pre", "surface-json", JSON.stringify(result.execution, null, 2)));
397
- expand.remove();
398
- } catch {
399
- expand.disabled = false;
400
- expand.textContent = say("Record changed or unavailable; refresh context", "记录已变化或不可用;请刷新 Context");
401
- }
402
- });
403
- card.append(expand);
404
- return card;
405
- }
406
- function section(id, name, entries) {
407
- const body = node("div", "section-body");
408
- entries.forEach((entry) => body.append(recordCard(entry)));
409
- if (!entries.length) body.append(node("p", "muted", core.omitted.records
410
- ? say("Not included in this compact read", "此紧凑读取未包含该记录")
411
- : say("No records", "暂无记录")));
412
- const folded = node("details", "record-card");
413
- folded.dataset.viewKey = id;
414
- folded.append(node("summary", "", name + " · " + entries.length), body);
415
- scaffold.append(anchorSection(id, null, folded));
416
- return body;
417
- }
418
- const working = task.status === "active" || task.status === "draft";
419
- const focus = section("detail-focus", working
420
- ? say("Goal and current focus", "目标与当前关注")
421
- : say("Goal and last working focus", "目标与结束前的工作重点"), records("task-brief"));
422
- const brief = values("task-brief")[0];
423
- if (brief) {
424
- focus.prepend(richText(working
425
- ? t("detail.focus") : say("Last working focus", "结束前的工作重点"), brief.currentFocus, t));
426
- if (brief.boundaries.length) focus.append(richText(say("Boundaries", "边界"), brief.boundaries.join("\\n"), t));
427
- }
428
- section("detail-work", say("Responsibilities and acceptance", "工作责任与验收"), records("work-item"));
429
- const evidence = node("div", "section-body");
430
- renderEvidence(evidence, data, t, locale, actions);
431
- scaffold.append(anchorSection("detail-results", sectionHead(say("Results and evidence", "成果与证据")), evidence));
432
- section("detail-messages", t("detail.messages"), records("task-message"));
433
- section("detail-history", say("Decisions and milestones", "决策与里程碑"), records("task-decision").concat(records("task-milestone")));
434
- section("detail-reviews", t("detail.reviews"), records("review-round"));
435
- const roles = node("div", "section-body");
436
- records("role").forEach((entry) => {
437
- if (entry.omitted) { roles.append(recordCard(entry)); return; }
438
- const role = entry.value;
439
- const active = values("run").find((run) => run.roleName === role.name && run.status === "active");
440
- roles.append(roleCard({
441
- ...role, providerRetry: entry.providerRetry,
442
- status: entry.providerRetry?.status === "waiting" ? "waiting" : "unknown",
443
- effectiveLaunch: active ? active.effective : null,
444
- launchDrift: active && active.effective.sourceDesiredRevision !== role.launchRevision
445
- }, task, t, locale, actions));
446
- if (!active) roles.append(node("p", "muted", say(
447
- "No active AgentRun in this read; Session activity is a separate observation.",
448
- "此读取中没有活跃 AgentRun;Session 活动属于独立观察。")));
449
- });
450
- const roleDetails = node("details", "record-card");
451
- roleDetails.dataset.viewKey = "roles";
452
- roleDetails.append(node("summary", "", t("detail.roles")), roles);
453
- scaffold.append(anchorSection("detail-roles", null, roleDetails));
454
- const execution = node("details", "record-card");
455
- execution.dataset.viewKey = "execution";
456
- execution.append(node("summary", "", say("Execution and observations", "展开执行与观察")));
457
- execution.append(node("p", "muted", say(
458
- "An open execution record is not proof of Agent activity. Native admission and Task progress are separate facts.",
459
- "执行记录未结清不等于 Agent 正在工作;原生准入、运行观察与 Task 进展分别呈现。")));
460
- records("run").forEach((entry) => execution.append(entry.omitted ? recordCard(entry)
461
- : runCard({ ...entry.value, execution: entry.execution }, t, locale)));
462
- (core.observations || []).forEach((observation) => execution.append(node("p", "muted",
463
- observation.source + " · " + observation.status + " · " + observation.coverage + " · " + observation.observedAt)));
464
- const runtimeStatus = node("p", "muted", data.runtimeStatus + " · " + data.runtimeObservedAt);
465
- runtimeStatus.dataset.runtimeStatus = "";
466
- execution.append(runtimeStatus);
467
- const raw = node("pre", "surface-json", data.runtime ? JSON.stringify({
468
- roles: data.runtime.roles, runtimeHealth: data.runtime.runtimeHealth
469
- }, null, 2) : "");
470
- raw.dataset.runtimeValue = "";
471
- execution.append(raw);
472
- scaffold.append(anchorSection("detail-exec", sectionHead(t("detail.execution")), execution));
473
- section("detail-operations", say("Original operation facts (no automatic retry)", "原始操作事实(不自动重试)"), records("job"));
474
- // Four reading groups; the source sections keep their stable deep-link IDs.
475
- const readingOrder = ["detail-top", "detail-focus", "detail-work", "detail-results",
476
- "detail-exec", "detail-reviews", "detail-roles", "detail-history",
477
- "detail-messages", "detail-operations"];
478
- readingOrder.forEach((id) => {
479
- const target = scaffold.querySelector("#" + id);
480
- if (target) scaffold.append(target);
481
- });
482
- const refs = node("details", "record-card");
483
- refs.append(node("summary", "", say("Context cursor and omitted references", "Context 游标与省略引用")));
484
- refs.append(node("pre", "surface-json", JSON.stringify({
485
- coreCursor: core.coreCursor, throughCursor: core.throughCursor, count: core.count, omitted: core.omitted
486
- }, null, 2)));
487
- refs.append(node("p", "muted", say("Reads do not acknowledge messages. Omitted records can be located with the CLI domain query and inspected by reference.",
488
- "读取不确认消息。被省略的记录可经 CLI 领域查询定位后按引用读取。")));
489
- scaffold.append(refs);
490
- const panels = node("details", "record-card");
491
- panels.append(node("summary", "", say("Capability panels", "能力面板")));
492
- const panelBody = node("div", "section-body");
493
- panels.append(panelBody);
494
- let loadingPanels = false;
495
- panels.addEventListener("toggle", async () => {
496
- if (!panels.open || loadingPanels) return;
497
- loadingPanels = true;
498
- panelBody.textContent = say("Reading current contributions…", "读取当前贡献…");
499
- try {
500
- const current = await actions.panels(task.id);
501
- clear(panelBody);
502
- panelBody.append(node("small", "muted", current.observedAt));
503
- if (!current.panels.length) panelBody.append(node("p", "muted", say("No visible panels", "暂无可见面板")));
504
- current.panels.forEach((item) => {
505
- const card = node("article", "record-card");
506
- const panel = item.panel;
507
- card.append(node("strong", "", panel.title), node("small", "muted", item.capability + " · " + item.provider.id));
508
- if (item.unavailable) card.append(node("p", "muted", item.unavailable));
509
- else if (panel.kind === "text") card.append(node("p", "", panel.text));
510
- else if (panel.kind === "link") {
511
- const url = new URL(panel.href);
512
- if (["http:", "https:"].includes(url.protocol) && !url.username && !url.password) {
513
- const link = node("a", "", panel.title);
514
- link.href = url.href;
515
- link.rel = "noopener noreferrer";
516
- card.append(link);
517
- }
518
- } else if (panel.kind === "data" && panel.renderer === "json") {
519
- const label = node("label", "", say("Query input (JSON)", "查询输入(JSON)"));
520
- const input = node("textarea", "");
521
- input.value = JSON.stringify({ taskId: task.id });
522
- label.append(input);
523
- const contract = node("details", "");
524
- contract.append(node("summary", "", say("Input schema", "输入结构")));
525
- contract.append(node("pre", "surface-json", JSON.stringify(item.inputSchema, null, 2)));
526
- const read = node("button", "record-open", say("Read", "读取"));
527
- read.type = "button";
528
- const output = node("pre", "surface-json");
529
- read.addEventListener("click", async () => {
530
- if (read.disabled) return;
531
- read.disabled = true;
532
- try {
533
- const result = await actions.readPanel(task.id, {
534
- capability: item.capability, contractVersion: item.contractVersion, provider: item.provider
535
- }, JSON.parse(input.value));
536
- output.textContent = JSON.stringify(result, null, 2);
537
- } catch (error) { output.textContent = say("Panel unavailable: ", "面板不可用:") + error.message; }
538
- finally { read.disabled = false; }
539
- });
540
- card.append(label, contract, read, output);
541
- }
542
- panelBody.append(card);
543
- });
544
- } catch (error) { panelBody.textContent = say("Panels unavailable: ", "面板不可用:") + error.message; }
545
- finally { loadingPanels = false; }
546
- });
547
- scaffold.append(panels);
548
- container.append(scaffold);
549
- }
550
-
551
- // Render each task-32 §2.5 submission facet on its own line, never collapsed into
552
- // a single "started". This repo has one current server contract: every submission
553
- // returns its faceted receipt, so there is no old-server flat-disposition path to
554
- // fall back to. A missing facet block is a contract violation, reported as a
555
- // limited diagnostic rather than reinterpreted as a plausible flat result.
556
- function renderSubmissionFacets(container, submission, say) {
557
- clear(container);
558
- if (!submission) {
559
- container.append(node("p", "muted",
560
- say("Submission receipt is missing its §2.5 facets.", "提交回执缺少 §2.5 分面字段。")));
561
- return;
562
- }
563
- const phaseText = {
564
- "active": say("Active — delivery context", "进行中 — 交付上下文"),
565
- "draft-planning": say("Draft, in planning", "草稿,规划中"),
566
- "draft-unplanned": say("Draft, not yet planned", "草稿,尚未规划")
567
- };
568
- const planningText = {
569
- "entered": say("Entered planning", "已进入规划"),
570
- "continued": say("Continued planning", "继续规划"),
571
- "none": say("No planning change", "规划无变化")
572
- };
573
- const activationText = {
574
- "requested": say("Activation requested", "已请求激活"),
575
- "pending": say("Activation pending", "激活待处理"),
576
- "failed": say("Activation failed", "激活失败"),
577
- "manual-required": say("Manual activation required", "需手动激活"),
578
- "execution-stopped": say("Execution stopped; not activated", "执行已停止;未激活"),
579
- "none": say("No activation", "无激活")
580
- };
581
- const deliveryText = {
582
- "queued": say("Leader queued to act now", "已为 Leader 排队处理"),
583
- "none": say("Leader not woken", "未唤醒 Leader")
584
- };
585
- const facts = [
586
- [say("Phase", "阶段"), phaseText[submission.phase] ?? submission.phase],
587
- [say("Planning", "规划"), planningText[submission.planning] ?? submission.planning],
588
- [say("Activation", "激活"), activationText[submission.activation] ?? submission.activation],
589
- [say("Delivery", "投递"), deliveryText[submission.delivery] ?? submission.delivery]
590
- ];
591
- for (const [label, value] of facts) {
592
- container.append(node("p", "muted", label + ": " + value));
593
- }
594
- const step = submission.nextStep;
595
- if (step) {
596
- const nextText = step.kind === "activate-manually"
597
- ? say("Next: request activation with an explicit environment (yui task activation request " + step.taskId + " --request-id <id> --environment <plan>).",
598
- "下一步:明确环境并请求激活(yui task activation request " + step.taskId + " --request-id <id> --environment <plan>)。")
599
- : step.kind === "start-execution"
600
- ? say("Next: start execution first, then request activation (" + step.taskId + ").",
601
- "下一步:先启动执行,再请求激活(" + step.taskId + ")。")
602
- : step.kind === "await-pending-activation"
603
- ? say("Next: waiting on the pending activation " + step.activationRef + ".",
604
- "下一步:等待待处理的激活 " + step.activationRef + "。")
605
- : step.kind === "resolve-failed-activation"
606
- ? say("Next: the prior activation failed (" + step.failure + "); retry with a new request or cancel " + step.activationRef + ".",
607
- "下一步:之前的激活失败(" + step.failure + ");请用新请求重试或取消 " + step.activationRef + "。")
608
- : "";
609
- if (nextText) {
610
- const next = node("p", "", nextText);
611
- next.dataset.nextStep = step.kind;
612
- container.append(next);
613
- }
614
- }
615
- }
616
- `;