@zq-silk/yui 2.0.0 → 2.2.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 (138) hide show
  1. package/README.md +4 -0
  2. package/dist/cli/commandCatalog.js +318 -289
  3. package/dist/cli/commandDiscovery.js +72 -0
  4. package/dist/cli/completion.js +22 -227
  5. package/dist/cli/dynamicCompletion.js +12 -6
  6. package/dist/cli/invocationAuthority.js +2 -2
  7. package/dist/cli/invocationRouter.js +18 -7
  8. package/dist/cli.js +23 -3030
  9. package/dist/commands/capabilityCommands.js +5 -5
  10. package/dist/commands/globalRoleCommands.js +2 -1
  11. package/dist/commands/taskCommands.js +90 -21
  12. package/dist/commands/taskIntegrationCommands.js +3 -1
  13. package/dist/commands/taskUpstreamCommands.js +3 -1
  14. package/dist/context/runContextPack.js +132 -5
  15. package/dist/context/sourceRunContext.js +4 -2
  16. package/dist/context/taskContext.js +12 -3
  17. package/dist/controlPlaneCli.js +3092 -0
  18. package/dist/controller/fileSchedulerStoreAdapter.js +19 -6
  19. package/dist/controller/jobControl.js +21 -0
  20. package/dist/executor/agentExecutor.js +1 -1
  21. package/dist/executor/effectiveLaunch.js +18 -5
  22. package/dist/executor/fileRoleLaunchPlanner.js +15 -19
  23. package/dist/integration/gitIntegrationService.js +50 -15
  24. package/dist/kernel/accessAssessment.js +1 -0
  25. package/dist/kernel/builtinCapabilities.js +28 -1
  26. package/dist/kernel/capabilityRegistry.js +64 -19
  27. package/dist/message/messageContinuation.js +7 -2
  28. package/dist/nativeAgent/agent.js +275 -0
  29. package/dist/nativeAgent/cliDemo.js +37 -0
  30. package/dist/nativeAgent/codingTools.js +11 -0
  31. package/dist/nativeAgent/commandTool.js +215 -0
  32. package/dist/nativeAgent/compactionDemo.js +158 -0
  33. package/dist/nativeAgent/composition.js +71 -0
  34. package/dist/nativeAgent/context/budget.js +44 -0
  35. package/dist/nativeAgent/context/index.js +339 -0
  36. package/dist/nativeAgent/context/providerCompressor.js +103 -0
  37. package/dist/nativeAgent/contracts.js +1 -0
  38. package/dist/nativeAgent/demo.js +40 -0
  39. package/dist/nativeAgent/evaluation/cases.js +38 -0
  40. package/dist/nativeAgent/evaluation/checks.js +91 -0
  41. package/dist/nativeAgent/evaluation/demo.js +19 -0
  42. package/dist/nativeAgent/evaluation/files.js +54 -0
  43. package/dist/nativeAgent/evaluation/fixture.js +36 -0
  44. package/dist/nativeAgent/evaluation/index.js +239 -0
  45. package/dist/nativeAgent/executionOwner.js +209 -0
  46. package/dist/nativeAgent/filePatterns.js +170 -0
  47. package/dist/nativeAgent/fileToolsSupport.js +202 -0
  48. package/dist/nativeAgent/index.js +16 -0
  49. package/dist/nativeAgent/interaction/cli.js +358 -0
  50. package/dist/nativeAgent/interaction/contracts.js +1 -0
  51. package/dist/nativeAgent/interaction/index.js +3 -0
  52. package/dist/nativeAgent/interaction/memoryDemo.js +115 -0
  53. package/dist/nativeAgent/interaction/renderer.js +34 -0
  54. package/dist/nativeAgent/localSafety.js +258 -0
  55. package/dist/nativeAgent/mockProvider.js +43 -0
  56. package/dist/nativeAgent/model/anthropicMessages.js +204 -0
  57. package/dist/nativeAgent/model/chatCompletions.js +210 -0
  58. package/dist/nativeAgent/model/errors.js +47 -0
  59. package/dist/nativeAgent/model/gateway.js +423 -0
  60. package/dist/nativeAgent/model/index.js +7 -0
  61. package/dist/nativeAgent/model/observationAdapter.js +21 -0
  62. package/dist/nativeAgent/model/protocols.js +19 -0
  63. package/dist/nativeAgent/model/responses.js +263 -0
  64. package/dist/nativeAgent/model/types.js +1 -0
  65. package/dist/nativeAgent/model/wire.js +73 -0
  66. package/dist/nativeAgent/observability/index.js +220 -0
  67. package/dist/nativeAgent/product/catalog.js +82 -0
  68. package/dist/nativeAgent/product/config.js +295 -0
  69. package/dist/nativeAgent/product/facts.js +30 -0
  70. package/dist/nativeAgent/product/index.js +62 -0
  71. package/dist/nativeAgent/product/location.js +44 -0
  72. package/dist/nativeAgent/product/runtime.js +276 -0
  73. package/dist/nativeAgent/product/storage.js +49 -0
  74. package/dist/nativeAgent/product/tools.js +47 -0
  75. package/dist/nativeAgent/product/transport.js +54 -0
  76. package/dist/nativeAgent/projectGuidance/index.js +425 -0
  77. package/dist/nativeAgent/searchTools.js +305 -0
  78. package/dist/nativeAgent/session/backends.js +293 -0
  79. package/dist/nativeAgent/session/catalog.js +97 -0
  80. package/dist/nativeAgent/session/catalogDemo.js +87 -0
  81. package/dist/nativeAgent/session/contracts.js +1 -0
  82. package/dist/nativeAgent/session/format.js +269 -0
  83. package/dist/nativeAgent/session/index.js +5 -0
  84. package/dist/nativeAgent/session/location.js +36 -0
  85. package/dist/nativeAgent/session/sqliteFormat.js +134 -0
  86. package/dist/nativeAgent/session/store.js +248 -0
  87. package/dist/nativeAgent/textTools.js +293 -0
  88. package/dist/nativeAgent/toolManager/executor.js +290 -0
  89. package/dist/nativeAgent/toolManager/index.js +4 -0
  90. package/dist/nativeAgent/validation.js +95 -0
  91. package/dist/plugins/pluginService.js +98 -8
  92. package/dist/resources/projectResourceService.js +23 -1
  93. package/dist/runtime/managedIdentity.js +6 -0
  94. package/dist/surface/surfaceContributions.js +3 -0
  95. package/dist/task/taskAuthority.js +61 -10
  96. package/dist/web/assets/assetManifest.js +38 -22
  97. package/dist/web/assets/client/api.js +115 -0
  98. package/dist/web/assets/client/app.js +615 -862
  99. package/dist/web/assets/client/components.js +323 -897
  100. package/dist/web/assets/client/detail.js +333 -0
  101. package/dist/web/assets/client/dock.js +225 -0
  102. package/dist/web/assets/client/dom.js +55 -3
  103. package/dist/web/assets/client/evidence.js +172 -0
  104. package/dist/web/assets/client/format.js +25 -14
  105. package/dist/web/assets/client/forms.js +230 -0
  106. package/dist/web/assets/client/i18n.js +1024 -791
  107. package/dist/web/assets/client/overview.js +118 -0
  108. package/dist/web/assets/client/records.js +63 -0
  109. package/dist/web/assets/client/sections.js +313 -0
  110. package/dist/web/assets/client/sidebar.js +146 -0
  111. package/dist/web/assets/client/theme.js +61 -22
  112. package/dist/web/assets/icons.js +39 -0
  113. package/dist/web/assets/shell.js +125 -118
  114. package/dist/web/assets/styles/base.js +41 -0
  115. package/dist/web/assets/styles/components.js +169 -0
  116. package/dist/web/assets/styles/layout.js +56 -77
  117. package/dist/web/assets/styles/markdown.js +16 -24
  118. package/dist/web/assets/styles/responsive.js +41 -44
  119. package/dist/web/assets/styles/tokens.js +65 -89
  120. package/dist/web/assets/styles/views.js +292 -0
  121. package/dist/web/webServer.js +2 -1
  122. package/docs/agent-result-consumption.md +16 -0
  123. package/docs/agent-result-consumption.zh-CN.md +13 -0
  124. package/docs/examples/agent-offline.mjs +194 -0
  125. package/docs/native-agent.md +283 -0
  126. package/docs/release-workflow.md +61 -0
  127. package/docs/release-workflow.zh-CN.md +46 -0
  128. package/docs/roles-and-configuration.md +32 -0
  129. package/docs/roles-and-configuration.zh-CN.md +26 -0
  130. package/package.json +1 -1
  131. package/skills/yui-leader/SKILL.md +9 -0
  132. package/skills/yui-reviewer/SKILL.md +5 -0
  133. package/skills/yui-runtime/SKILL.md +33 -0
  134. package/dist/web/assets/client/taskSummary.js +0 -350
  135. package/dist/web/assets/client/taskSurface.js +0 -616
  136. package/dist/web/assets/client/view.js +0 -608
  137. package/dist/web/assets/styles/cards.js +0 -247
  138. package/dist/web/assets/styles/widgets.js +0 -168
@@ -52,6 +52,32 @@ Provider 权限策略、Profile 访问意图和 Project 写范围是不同的合
52
52
  不授予对另一个 Project 的写入。受管工作区 owner、精确 Assignment 和资源 grant
53
53
  落实 Yui 操作;宽泛的原生权限不是 OS 沙箱。
54
54
 
55
+ 以 write 意图在自己 Task-owned main 工作区启动的 delivery Leader,从该工作区
56
+ 捕获已绑定 Project 的写域,不要求存在 open WorkItem;已接受结果也不会移除这个
57
+ 直接交付写域。显式 Assignment(包括空写域)仍决定受派发执行的范围。
58
+ Planning、read 意图执行和全局 Role 不获得这个 Task-main 写域;Reviewer 仍只可写
59
+ 精确的 Review 工作区。Project 实际操作重新校验当前 Session 的冻结写域、Task
60
+ 生命周期和工作区 owner;它不授权稳定 Project checkout、其他 Task、私有
61
+ Worker/Review 工作区或 Home 修改。
62
+
63
+ 受管 Leader 的 Integration 目标必须是 Task-main 分支,且该分支实际 checkout
64
+ 在捕获的 Task-main 目录。入场、检查及目标 CAS 都重新校验此边界;
65
+ `--target` 不授予对同仓库另一工作区的写入。
66
+
67
+ `integration abort` 可以把已发生的 CAS 确认为 committed,而不授予新项目写域。
68
+ 结算仍校验当前 delivery 身份、捕获的 Task-main 归属、实际 checkout、
69
+ 候选及检查/Job 证据。未执行的 CAS 仍是项目写操作,必须具备完整写域。
70
+
71
+ 运行时更新后,旧的空写域 Session 仍保持空域,包括失败 Run 的普通 retry。
72
+ Resume 不改变捕获的 Profile 访问意图或项目写域。先用
73
+ `yui task role session inspect <task> leader --json` 读取 `session.effective`,
74
+ 再通过 `yui task role session new <task> leader --reason "<采用新写域>"`
75
+ 请求正式替换。替换自己时应结束当前 turn。Yui 确认清理并启动 successor 后,
76
+ 读回其 `executionAuthority`、`profileAccess`、`writeProjectIds` 和 workspace,
77
+ 确认后再编辑;替换请求本身不证明采用成功。精确 Run Context 与启动环境从同一
78
+ 冻结 effective snapshot 取得写域;模型、effort、accepted Candidate 和原验收
79
+ 历史保持不变。
80
+
55
81
  Yui 提供其通用 Role Skill 和 Context 指针。Project Skill 仍是由 Agent 原生发现的
56
82
  普通 Project 文件。Project Knowledge 维护在 `YUI_HOME` 下;把仓库材料复制进 prompt
57
83
  并不使其成为权威 Knowledge。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zq-silk/yui",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Local control plane for long-running native agent CLI sessions backed by tmux.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -19,6 +19,15 @@ not copied reports. Follow Runtime's `nextCursor` and `contentPage` rules:
19
19
  read the full relevant window and originals before disposition, without
20
20
  unconditionally walking unrelated history.
21
21
 
22
+ Before WorkItem dispatch, put the complete bounded requirements in the WorkItem
23
+ or explicitly select up to 16 relevant readable materials with repeated
24
+ `--context-ref <store/refId@digest>` on `task work dispatch`. Obtain exact IDs and
25
+ digests from current Task context list/inspect. Core freezes these selected
26
+ values after the existing caller-scope checks; mentioning a record in `--input`
27
+ does not select it or grant access. Missing, forbidden or changed references
28
+ refuse dispatch before Provider startup: correct the selection or make the
29
+ WorkItem self-contained, never ask the recipient to bypass expansion rejection.
30
+
22
31
  ## Select the applicable stage
23
32
 
24
33
  Use current lifecycle, latest intent and the Session's actual planning/delivery
@@ -28,6 +28,11 @@ WorkItem Develop workspace, and never become a ChangeSet source.
28
28
 
29
29
  For a dispatched Review, the AgentRun Context Pack identifies the ReviewRound,
30
30
  frozen Project commits, and assigned workspace. Inspect those exact commits.
31
+ For a Run-backed Candidate, read its frozen `source-run` reference whose
32
+ `evidenceOf` names that Candidate, including every content page of the original
33
+ report. A summary or a newer Run is not a replacement. Missing historical
34
+ evidence is a bounded infrastructure diagnosis, not permission to read outside
35
+ the inventory or require the Leader to manually copy a report.
31
36
  The current mutable Task-main checkout is context only and must never replace,
32
37
  widen, or silently update the assigned Review scope.
33
38
 
@@ -169,6 +169,39 @@ For unrelated discovery, stop once the necessary evidence is found. Task
169
169
  event deltas retain their fixed upper bound and are history, not replacement
170
170
  current state. No read acknowledges implementation or consumes a queue.
171
171
 
172
+ ## Discover operations for the current caller
173
+
174
+ Use `yui help` or `yui --json help [command ...]` through the authorized CLI
175
+ entry for the current caller's command view. Help, examples and shell completion
176
+ share the catalog projection; public terminals and unmanaged Agents share one
177
+ public view. This offline view is only guidance, not an authenticated permission
178
+ or a way to switch Roles. An incomplete or stale managed identity must not be
179
+ cleared to obtain public execution authority.
180
+
181
+ For Codex, the native `CODEX_THREAD_ID` takes precedence over the transport's
182
+ `YUI_NATIVE_SESSION_ID`; a newly created thread can legitimately lack the latter.
183
+ Other adapters do not borrow Codex identity for offline discovery. A missing
184
+ transport variable or hidden help entry alone does not prove Session failure.
185
+ Discovery does not establish that a Session is current: actual execution still
186
+ revalidates Session, Task/Assignment, workspace, resource and Grant boundaries.
187
+
188
+ When capability discovery appears in that view, use `capability search` for
189
+ templates and `capability describe <name> --task
190
+ <task-id> --input '<json>'` for supported exact authorization assessments.
191
+ No `access` field means no exact authorization conclusion was made: supply the
192
+ required parameters and follow the domain's existing execution checks. Hidden
193
+ operations are omitted; `requestable` includes the exact missing Grant bounds
194
+ and existing request route; `authorized` remains subject to execution-time
195
+ revalidation. Provider outages do not change this authorization observation.
196
+
197
+ Discovery never sends requests, consumes Grants, starts plugins or approves
198
+ effects. If the Task actually needs a requestable operation, use the existing
199
+ Assignment Message to Leader, Leader InputRequest, or Operator path. Preserve
200
+ the exact action, resource/plugin, digest, environment and phase. A response is
201
+ not a Grant. Existing source-authorized Leader Grant operations and Operator
202
+ Grant authority remain unchanged; never expand an authorization source or
203
+ solicit real-resource validation merely because an operation is requestable.
204
+
172
205
  ## Preserve intent and authority
173
206
 
174
207
  An analysis, diagnosis, or review request is read-only unless the user also
@@ -1,350 +0,0 @@
1
- export const TASK_SUMMARY_SCRIPT = `
2
- import { node, clear } from "/assets/js/dom.js";
3
- import { richText, inputCard, observabilityMetricCard } from "/assets/js/components.js";
4
- import { formatDateTime } from "/assets/js/format.js";
5
-
6
- const sessionLabels = {
7
- active: ["Recent activity", "近期有活动"], waiting: ["Waiting", "正在等待"],
8
- quiet: ["Quiet · not a proven deadlock", "安静 · 尚不能判定卡死"],
9
- diagnostic: ["Needs diagnosis", "需要诊断"], unknown: ["Observation unknown", "观测未知"],
10
- stopped: ["Stopped / failed", "已停止/失败"], idle: ["Turn ended", "该轮已结束"],
11
- background: ["Background unsettled", "后台未结清"]
12
- };
13
- const sayFor = locale => (en, cn) => locale.startsWith("zh") ? cn : en;
14
- function block(title, key) {
15
- const section = node("section", "record-card summary-block");
16
- section.dataset.summary = key;
17
- section.append(node("h3", "", title));
18
- return section;
19
- }
20
- function disclosure(title, key) {
21
- const details = node("details", "record-card");
22
- details.dataset.viewKey = key;
23
- details.append(node("summary", "", title));
24
- return details;
25
- }
26
-
27
- export function renderSessionSummary(container, observation, locale) {
28
- clear(container);
29
- const say = sayFor(locale);
30
- container.append(node("h3", "", say("Session activity", "会话活动")));
31
- if (!observation) {
32
- container.append(node("p", "muted", say(
33
- "Observation unavailable or not yet read; this does not mean zero running Sessions.",
34
- "观测尚未读取或不可用;不代表运行会话为零。")));
35
- return;
36
- }
37
- const metrics = node("div", "session-counts");
38
- for (const [key, count] of Object.entries(observation.counts)) {
39
- if (!count) continue;
40
- const label = sessionLabels[key];
41
- metrics.append(node("span", "session-count", count + " · " + say(...label)));
42
- }
43
- if (!observation.sessions.length) metrics.append(node("p", "muted", say(
44
- "No selected native Session is recorded. Historical resources were not inspected.",
45
- "未记录当前选用的原生会话;未盘点历史资源。")));
46
- container.append(metrics, node("p", "muted", say(
47
- "Scope: this Task's selected native Sessions. Activity is not delivery progress; no live process probe.",
48
- "范围:本 Task 当前选用的原生会话。活动不等于交付进展;未探测实时进程。")));
49
- container.append(node("small", "muted", say("Independent observation read: ", "独立观察读取于:")
50
- + formatDateTime(observation.readAt, locale)));
51
- const details = disclosure(say("Session facts and timestamps", "会话依据与时间"), "sessions");
52
- for (const session of observation.sessions) {
53
- const row = node("article", "session-row");
54
- row.append(node("strong", "", session.roleName + " · " + say(...sessionLabels[session.group])));
55
- row.append(node("p", "record-copy", session.reason));
56
- if (session.waitingReason) row.append(node("p", "", say("Waiting for: ", "等待:") + session.waitingReason));
57
- row.append(node("p", "muted", say("Last activity: ", "最近活动:")
58
- + (session.lastActivityAt ? formatDateTime(session.lastActivityAt, locale) : say("unobserved", "未观测"))));
59
- row.append(node("p", "muted", say("Native input updated: ", "原生输入记录更新:") + formatDateTime(session.sourceUpdatedAt, locale)));
60
- if (session.operations.length) row.append(node("p", "muted", say(
61
- "Operations without a terminal (not proof of recent activity): ",
62
- "尚无终态的操作(不证明近期仍有活动):") + session.operations.join(", ")));
63
- if (session.background.length) row.append(node("p", "", say("Background: ", "后台:")
64
- + session.background.map(item => item.id + " · " + item.execution).join(", ")));
65
- row.append(node("small", "mono", [session.nativeSessionId, session.nativeTurnId, session.attemptId].filter(Boolean).join(" / ")));
66
- details.append(row);
67
- }
68
- container.append(details);
69
- }
70
-
71
- export function renderTaskSummary(container, data, t, locale, actions, recordCard) {
72
- const say = sayFor(locale);
73
- const entries = store => data.core.records.filter(entry => entry.ref.store === store);
74
- const available = store => entries(store).filter(entry => !entry.omitted).map(entry => entry.value);
75
- const scope = node("p", "muted", say("Scope: ", "范围:") + data.task.id + " · "
76
- + say("Reading does not accept results or acknowledge Messages.", "读取不验收成果,也不确认消息。"));
77
- container.append(scope);
78
- const grid = node("div", "task-summary-grid");
79
- container.append(grid);
80
- const attention = block(say("Needs your input", "需要你处理"), "attention");
81
- attention.id = "detail-attention";
82
- const inputCount = data.core.attention.openInputs.count;
83
- attention.append(node("p", "", inputCount ? inputCount + say(" open questions", " 项待答问题")
84
- : say("No open InputRequest requires your answer.", "当前没有待你回答的 InputRequest。")));
85
- const inputs = entries("input-request").filter(entry => !entry.omitted && entry.value.status === "open");
86
- inputs.forEach(entry => attention.append(inputCard(entry.value, { single: true }, t, locale, actions)));
87
- const missing = data.core.attention.openInputs.refs.filter(ref => !inputs.some(entry => entry.ref.refId === ref.refId));
88
- missing.forEach(ref => attention.append(recordCard({ ref, omitted: true })));
89
- if (inputCount > inputs.length + missing.length) attention.append(node("p", "muted", say(
90
- "Some open questions are outside this bounded read. Read all original questions with: ",
91
- "部分待答问题未包含在本次有界读取中,请读取全部原问题:") + "yui task input list " + data.task.id));
92
- const technical = node("div", "");
93
- technical.dataset.technicalSummary = "";
94
- attention.append(technical);
95
- grid.append(attention);
96
- const sessions = block("", "sessions");
97
- sessions.dataset.sessionSummary = "";
98
- grid.append(sessions);
99
- const progress = block(say("Task progress", "任务进展"), "progress");
100
- const briefEntry = entries("task-brief")[0];
101
- const brief = available("task-brief")[0];
102
- const working = data.task.status === "draft" || data.task.status === "active";
103
- if (data.task.completionSummary) progress.append(richText(say("Recorded completion", "已记录完成"), data.task.completionSummary, t));
104
- if (brief) {
105
- progress.append(richText(working
106
- ? say("Current focus", "当前工作")
107
- : say("Last working focus", "结束前的工作重点"), brief.currentFocus, t));
108
- progress.append(richText(working
109
- ? say("Leader's progress report", "Leader 进展摘要")
110
- : say("Last working report", "结束前的进展摘要"), brief.leaderSummary, t));
111
- progress.append(node("small", "muted", say("Brief updated: ", "Brief 更新:") + formatDateTime(brief.updatedAt, locale)));
112
- } else if (briefEntry) progress.append(recordCard(briefEntry));
113
- else progress.append(node("p", "muted", say("No progress summary in this read; no percentage or ETA is inferred.",
114
- "本次读取未包含进展摘要;不推算完成百分比或预计时间。")));
115
- const next = node("p", "muted");
116
- next.dataset.nextSummary = "";
117
- progress.append(next);
118
- grid.append(progress);
119
- const conclusions = block(say("Key conclusions", "关键结论"), "conclusions");
120
- const decisions = entries("task-decision").filter(entry => !entry.omitted && entry.value.status === "active");
121
- decisions.slice(0, 3).forEach(entry => {
122
- const card = recordCard(entry);
123
- card.prepend(node("small", "muted", say("Recorded decision", "已记录决定")));
124
- conclusions.append(card);
125
- });
126
- if (!decisions.length) conclusions.append(node("p", "muted", say(
127
- "No current Decision is included. Reports and recommendations are not user approval.",
128
- "本次读取未包含当前 Decision;报告和建议不代表用户批准。")));
129
- if (decisions.length > 3 || data.core.omitted.records || entries("task-decision").some(entry => entry.omitted)) {
130
- conclusions.append(node("p", "muted", say("More conclusions or omitted sources are available in history / Context.",
131
- "更多结论或省略来源请查看历史/Context。")));
132
- }
133
- grid.append(conclusions);
134
- updateTaskObservations(container, data, t, locale);
135
- }
136
-
137
- export function updateTaskObservations(container, data, t, locale) {
138
- const say = sayFor(locale);
139
- const sessions = container.querySelector("[data-session-summary]");
140
- if (sessions && !sessions.contains(document.activeElement)) {
141
- const wasOpen = sessions.querySelector("details")?.open;
142
- renderSessionSummary(sessions, data.runtimeStatus === "available" ? data.runtime?.sessions : null, locale);
143
- if (wasOpen && sessions.querySelector("details")) sessions.querySelector("details").open = true;
144
- }
145
- const technical = container.querySelector("[data-technical-summary]");
146
- const execution = data.runtimeStatus === "available" ? data.runtime?.execution : null;
147
- if (technical) {
148
- clear(technical);
149
- if (!execution) technical.append(node("p", "muted", say(
150
- "Technical attention and native waits have not been observed; no open question does not prove health.",
151
- "技术关注和原生等待尚未观测;没有待答问题不代表系统完全健康。")));
152
- else {
153
- const facts = execution.attention.concat(execution.blockers.filter(item => item.kind !== "input"));
154
- if (facts.length) technical.append(node("h4", "", say("Technical attention · owner assigned, not necessarily started",
155
- "技术关注 · 有负责人不代表已开始处理")));
156
- facts.forEach(item => technical.append(node("p", "record-copy", item.owner + " · " + item.summary + " · " + item.id)));
157
- if (!facts.length) technical.append(node("p", "muted", say("No technical attention in the current Task projection.",
158
- "当前任务投影未列出技术关注。")));
159
- }
160
- const waits = data.runtimeStatus === "available" ? data.runtime?.sessions?.sessions.filter(session =>
161
- session.group === "waiting" && ["user", "permission"].includes(session.waitingReason)) ?? [] : [];
162
- waits.forEach(session => technical.append(node("p", "", session.roleName + say(
163
- " is waiting for user/permission input. Inspect its original Session; this is not a new approval request.",
164
- " 明确等待用户/权限输入。请查看原 Session;这里没有新建审批请求。"))));
165
- }
166
- const next = container.querySelector("[data-next-summary]");
167
- if (next) next.textContent = execution ? say("Next owner: ", "下一步负责人:") + execution.next.owner
168
- + " · " + t("exec.action." + execution.next.action) + " · " + execution.reason
169
- : say("Next owner/action observation unavailable.", "下一步负责人/动作观测不可用。");
170
- }
171
-
172
- export function renderEvidence(container, data, t, locale, actions) {
173
- const say = sayFor(locale);
174
- const state = data.viewState;
175
- const artifacts = disclosure(say("Saved files · read a fixed version", "已保存文件 · 读取固定版本"), "artifacts");
176
- const body = node("div", "section-body");
177
- artifacts.append(body);
178
- const selection = node("div", "section-body");
179
- let readSequence = 0;
180
- const read = async (path, commit) => {
181
- const sequence = ++readSequence;
182
- selection.dataset.reading = "true";
183
- selection.textContent = say("Reading selected revision…", "正在读取所选版本…");
184
- try {
185
- const result = await actions.readArtifact(data.task.id, path, commit);
186
- if (sequence !== readSequence) return;
187
- state.artifact = result;
188
- drawSelection();
189
- } catch (error) {
190
- if (sequence === readSequence) selection.textContent = say("Selected revision unavailable: ", "所选版本不可用:") + error.message;
191
- } finally { if (sequence === readSequence) selection.dataset.reading = "false"; }
192
- };
193
- function drawSelection() {
194
- clear(selection);
195
- const value = state.artifact;
196
- if (!value) return;
197
- const ref = "git:" + value.commit + ":" + value.relativePath;
198
- selection.append(node("strong", "", say("Fixed result · ", "固定成果 · ") + value.relativePath));
199
- selection.append(node("p", "mono", ref));
200
- selection.append(node("p", "muted", say(
201
- "This revision stays selected across refreshes. Reading or discussing it does not accept, publish or execute it.",
202
- "刷新后仍保留此版本。读取或讨论不会验收、发布或执行该成果。")));
203
- const copy = node("button", "record-open", say("Copy source for discussion", "复制来源用于讨论"));
204
- copy.type = "button";
205
- copy.addEventListener("click", async () => {
206
- try { await navigator.clipboard.writeText(data.task.id + " " + ref); copy.textContent = say("Copied", "已复制"); }
207
- catch { copy.textContent = say("Copy the source shown above", "请复制上方来源"); }
208
- });
209
- selection.append(copy);
210
- const content = node("pre", "surface-json artifact-text", value.content);
211
- content.tabIndex = 0;
212
- content.setAttribute("aria-label", say("Fixed artifact text", "固定成果正文"));
213
- selection.append(content);
214
- }
215
- const load = node("button", "record-open", say("Read current file list", "读取当前文件列表"));
216
- load.type = "button";
217
- const list = node("div", "section-body");
218
- load.addEventListener("click", async () => {
219
- load.disabled = true;
220
- list.dataset.reading = "true";
221
- try {
222
- const result = await actions.artifacts(data.task.id);
223
- state.artifactList = result;
224
- drawList();
225
- } catch (error) { list.textContent = say("File list unavailable: ", "文件列表不可用:") + error.message; }
226
- finally { load.disabled = false; list.dataset.reading = "false"; }
227
- });
228
- function drawList() {
229
- clear(list);
230
- const current = state.artifactList;
231
- if (!current) return;
232
- if (state.artifact && current.commit !== state.artifact.commit) list.append(node("p", "", say(
233
- "The file list is at a different revision. Your selected result below has not changed.",
234
- "文件列表来自不同版本,下方所选成果未被替换。")));
235
- if (!current.entries.length) list.append(node("p", "muted", say("No saved files at this read.", "本次读取没有已保存文件。")));
236
- current.entries.forEach(entry => {
237
- const button = node("button", "record-open artifact-choice", entry.relativePath + " · " + entry.size + " B");
238
- button.type = "button";
239
- button.addEventListener("click", () => read(entry.relativePath, current.commit));
240
- list.append(button);
241
- });
242
- }
243
- body.append(node("p", "muted", say("Files are loaded only on request; missing Context rows do not mean no files.",
244
- "文件仅在请求时读取;Context 没有文件行不代表没有成果。")), load, list);
245
- // Explicit frozen references stay attached to their original revision.
246
- const sources = [data.task.completionArtifactRefs ?? [],
247
- data.core.records.filter(entry => !entry.omitted && ["task-brief", "task-decision"].includes(entry.ref.store))
248
- .map(entry => JSON.stringify(entry.value))].flat().join(" ");
249
- const refs = [...sources.matchAll(/git:([0-9a-f]{40}):([^\\s"\\\\]+?)(?=[\\s"\\\\]|$)/g)];
250
- const seen = new Set();
251
- for (const match of refs) {
252
- if (seen.has(match[0])) continue;
253
- seen.add(match[0]);
254
- const button = node("button", "record-open artifact-choice", say("Referenced revision · ", "已引用版本 · ") + match[2]);
255
- button.type = "button";
256
- button.addEventListener("click", () => read(match[2], match[1]));
257
- body.append(button);
258
- }
259
- body.append(selection);
260
- drawList();
261
- drawSelection();
262
- container.append(artifacts);
263
- const delivery = disclosure(say("Delivery, review and retained resources", "交付、审阅与保留资源"), "delivery");
264
- const output = node("div", "section-body");
265
- delivery.append(output);
266
- delivery.addEventListener("toggle", async () => {
267
- if (!delivery.open) return;
268
- clear(output);
269
- const runtime = data.runtimeStatus === "available" ? data.runtime : null;
270
- output.append(node("p", "", say("Local completion, review, remote delivery and resource cleanup are separate facts.",
271
- "本地完成、审阅、远程交付和资源清理是不同事实。")));
272
- if (!runtime) {
273
- output.append(node("p", "muted", say("Evidence observation unavailable. Core requirements remain readable.",
274
- "证据观测不可用;核心要求仍可读取。")));
275
- return;
276
- }
277
- const remote = runtime.remoteDelivery;
278
- if (remote) {
279
- output.append(node("h4", "", say("Remote delivery", "远程交付") + " · " + remote.status));
280
- remote.projects.forEach(project => {
281
- const card = node("article", "record-card");
282
- card.append(node("strong", "", project.directory + " · " + project.coverage));
283
- card.append(node("p", "", project.reason));
284
- card.append(node("p", "mono", say("Expected local commit: ", "期望本地提交:") + (project.expectedLocalCommit || say("unknown", "未知"))));
285
- card.append(node("p", "", "PR/MR · " + (project.state || "none") + " · "
286
- + say("Evidence: ", "证据:") + (project.verification || "unverified")));
287
- if (project.adoption) card.append(node("p", "mono", say("Adoption: ", "采用记录:") + project.adoption.id));
288
- output.append(card);
289
- });
290
- }
291
- output.append(node("p", "muted", say(
292
- "Review reports are evidence, not acceptance. Read the original report and its fixed candidate in Reviews below. Missing checks are not passes.",
293
- "审阅报告是证据,不是验收。请在下方审阅中读取原报告与固定候选;缺失检查不算通过。")));
294
- const original = node("div", "section-body");
295
- output.append(original);
296
- original.textContent = say("Reading original checks and review bindings…", "读取原始检查与审阅绑定…");
297
- void actions.evidence(data.task.id).then(evidence => {
298
- clear(original);
299
- original.append(node("h4", "", say("Integration checks · exact recorded candidate", "集成检查 · 精确记录候选")));
300
- if (!evidence.integrations.length) original.append(node("p", "muted", say(
301
- "No Integration check records. Direct Leader verification may be recorded in the completion report.",
302
- "没有 Integration 检查记录;Leader 直接执行的验证可能记录于完成报告。")));
303
- for (const attempt of evidence.integrations) {
304
- const card = node("article", "record-card");
305
- card.append(node("strong", "", attempt.id + " · " + attempt.status),
306
- node("p", "mono", (attempt.candidateCommit || say("No check candidate", "无检查候选"))),
307
- node("p", "muted", say("Coverage of the physical current head has not been checked here.",
308
- "此处未核对对物理当前 head 的覆盖。")));
309
- if (!attempt.checks?.length) card.append(node("p", "muted", say("No check outcomes recorded.", "未记录检查结果。")));
310
- (attempt.checks || []).forEach(check => card.append(node("p", "", check.name + " · " + check.outcome),
311
- ...(check.details ? [node("p", "muted", check.details)] : [])));
312
- original.append(card);
313
- }
314
- original.append(node("h4", "", say("Review bindings · not semantic approval", "审阅绑定 · 不等于语义通过")));
315
- if (!evidence.reviews.length) original.append(node("p", "muted", say("No Review records.", "没有 Review 记录。")));
316
- for (const round of evidence.reviews) {
317
- const card = node("article", "record-card");
318
- card.append(node("strong", "", round.id + " · " + round.status));
319
- card.append(node("p", "mono", round.taskCandidate
320
- ? round.taskCandidate.projects.map(p => p.projectId + " · " + p.commit).join("\\n")
321
- : [round.workItemId, round.candidateId, round.reviewBaseCommit].filter(Boolean).join(" · ")));
322
- if (round.reviewerRunId) {
323
- const report = node("button", "record-open", say("Read original review report", "读取原始审阅报告"));
324
- report.type = "button";
325
- report.addEventListener("click", async () => {
326
- report.disabled = true;
327
- try {
328
- const source = await actions.inspect(data.task.id, { store: "run", refId: round.reviewerRunId });
329
- card.append(richText(round.reviewerRunId, source.value.result?.output
330
- || say("No report recorded.", "未记录报告。"), t));
331
- } catch (error) { card.append(node("p", "muted", error.message)); report.disabled = false; }
332
- });
333
- card.append(report);
334
- }
335
- original.append(card);
336
- }
337
- original.append(node("h4", "", say("Recorded workspace owners", "已记录工作区所有者")));
338
- evidence.workspaces.forEach(workspace => original.append(node("p", "mono", workspace.owner.type + " · " + workspace.root)));
339
- }).catch(error => { original.textContent = say("Original evidence unavailable: ", "原始证据不可用:") + error.message; });
340
- const metrics = observabilityMetricCard(runtime.observability, t);
341
- if (metrics) output.append(metrics);
342
- output.append(node("h4", "", say("Retained workspaces · recorded ownership only", "保留工作区 · 仅已记录所有权")));
343
- output.append(node("p", "muted", say(
344
- "No physical cleanup inspection was run. Use the existing read-only preflight when needed; it does not authorize archive or cleanup:",
345
- "未执行物理清理检查。需要时使用既有只读预检;预检不授权归档或清理:")));
346
- output.append(node("code", "", "yui task archive-preflight " + data.task.id + " --integrated"));
347
- });
348
- container.append(delivery);
349
- }
350
- `;