@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.
Files changed (115) hide show
  1. package/ARCHITECTURE.md +2 -1
  2. package/ARCHITECTURE.zh-CN.md +2 -1
  3. package/README.md +2 -0
  4. package/dist/cli/commandCatalog.js +344 -324
  5. package/dist/cli/commandDiscovery.js +69 -0
  6. package/dist/cli/completion.js +22 -227
  7. package/dist/cli/dynamicCompletion.js +12 -6
  8. package/dist/cli/invocationAuthority.js +2 -2
  9. package/dist/cli/invocationRouter.js +18 -7
  10. package/dist/cli/managedDiagnostics.js +2 -1
  11. package/dist/cli/updatePorts.js +0 -5
  12. package/dist/cli.js +184 -130
  13. package/dist/commands/capabilityCommands.js +5 -5
  14. package/dist/commands/controllerCommands.js +2 -1
  15. package/dist/commands/globalRoleCommands.js +86 -44
  16. package/dist/commands/grantCommands.js +48 -8
  17. package/dist/commands/taskCommands.js +157 -196
  18. package/dist/commands/taskContextCommand.js +29 -6
  19. package/dist/commands/taskFactCommands.js +10 -69
  20. package/dist/commands/taskInputCommands.js +26 -35
  21. package/dist/commands/taskPublicationCommands.js +2 -35
  22. package/dist/commands/taskPublicationVerifyCommand.js +1 -2
  23. package/dist/commands/taskUpstreamCommands.js +2 -1
  24. package/dist/context/runContextPack.js +8 -29
  25. package/dist/context/sessionBootstrapManifest.js +5 -101
  26. package/dist/context/taskContext.js +104 -41
  27. package/dist/controller/clientRuntime.js +16 -8
  28. package/dist/controller/fileSchedulerStoreAdapter.js +24 -0
  29. package/dist/controller/runtime.js +6 -0
  30. package/dist/core/controllerClient.js +46 -21
  31. package/dist/core/protocol.js +12 -7
  32. package/dist/doctor/doctor.js +4 -3
  33. package/dist/errors/cliError.js +4 -4
  34. package/dist/errors/cliFailure.js +213 -0
  35. package/dist/executor/fileRoleLaunchPlanner.js +1 -4
  36. package/dist/grant/capabilityGrant.js +13 -0
  37. package/dist/grant/taskAuthorization.js +127 -0
  38. package/dist/kernel/accessAssessment.js +1 -0
  39. package/dist/kernel/builtinCapabilities.js +88 -9
  40. package/dist/kernel/capabilityRegistry.js +64 -19
  41. package/dist/kernel/kernelPorts.js +2 -2
  42. package/dist/nativeAgent/agent.js +173 -0
  43. package/dist/nativeAgent/contracts.js +1 -0
  44. package/dist/nativeAgent/demo.js +29 -0
  45. package/dist/nativeAgent/index.js +3 -0
  46. package/dist/nativeAgent/mockProvider.js +43 -0
  47. package/dist/nativeAgent/textTools.js +156 -0
  48. package/dist/nativeAgent/validation.js +95 -0
  49. package/dist/output/boundedRead.js +118 -0
  50. package/dist/plugins/pluginService.js +98 -8
  51. package/dist/release/releaseWorkflowEngine.js +8 -1
  52. package/dist/resources/projectResourceService.js +23 -1
  53. package/dist/runtime/managedCaller.js +2 -2
  54. package/dist/runtime/managedIdentity.js +6 -0
  55. package/dist/runtime/runtimeCoherence.js +10 -8
  56. package/dist/storage/sqliteSchema.js +10 -0
  57. package/dist/storage/storageVersions.js +1 -1
  58. package/dist/storage/taskStore.js +3 -0
  59. package/dist/surface/surfaceContributions.js +3 -0
  60. package/dist/task/leaderArchive.js +111 -0
  61. package/dist/task/leaderArchiveAuthority.js +24 -0
  62. package/dist/task/taskAuthority.js +5 -10
  63. package/dist/tmux/commandExecutor.js +7 -5
  64. package/dist/web/assets/assetManifest.js +38 -22
  65. package/dist/web/assets/client/api.js +115 -0
  66. package/dist/web/assets/client/app.js +555 -863
  67. package/dist/web/assets/client/components.js +323 -897
  68. package/dist/web/assets/client/detail.js +333 -0
  69. package/dist/web/assets/client/dock.js +225 -0
  70. package/dist/web/assets/client/dom.js +55 -3
  71. package/dist/web/assets/client/evidence.js +172 -0
  72. package/dist/web/assets/client/format.js +25 -14
  73. package/dist/web/assets/client/forms.js +230 -0
  74. package/dist/web/assets/client/i18n.js +1020 -791
  75. package/dist/web/assets/client/overview.js +115 -0
  76. package/dist/web/assets/client/records.js +63 -0
  77. package/dist/web/assets/client/sections.js +313 -0
  78. package/dist/web/assets/client/sidebar.js +117 -0
  79. package/dist/web/assets/client/theme.js +61 -22
  80. package/dist/web/assets/icons.js +39 -0
  81. package/dist/web/assets/shell.js +124 -118
  82. package/dist/web/assets/styles/base.js +41 -0
  83. package/dist/web/assets/styles/components.js +167 -0
  84. package/dist/web/assets/styles/layout.js +49 -77
  85. package/dist/web/assets/styles/markdown.js +16 -24
  86. package/dist/web/assets/styles/responsive.js +33 -44
  87. package/dist/web/assets/styles/tokens.js +65 -89
  88. package/dist/web/assets/styles/views.js +289 -0
  89. package/dist/web/webServer.js +2 -1
  90. package/docs/architecture/README.md +1 -0
  91. package/docs/architecture/README.zh-CN.md +1 -0
  92. package/docs/cli-information-contract.md +107 -0
  93. package/docs/cli-information-contract.zh-CN.md +81 -0
  94. package/docs/managed-turn-and-session-runtime.md +9 -0
  95. package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
  96. package/docs/plugin-sdk.md +7 -3
  97. package/docs/plugin-sdk.zh-CN.md +5 -2
  98. package/docs/release-workflow.md +42 -9
  99. package/docs/release-workflow.zh-CN.md +30 -7
  100. package/docs/storage-baseline.md +5 -0
  101. package/docs/testing/verification-levels.md +2 -2
  102. package/i18n/README.zh-CN.md +2 -0
  103. package/package.json +1 -1
  104. package/skills/yui-leader/SKILL.md +8 -1
  105. package/skills/yui-leader/references/authorization.md +64 -0
  106. package/skills/yui-leader/references/execution.md +18 -4
  107. package/skills/yui-leader/references/task-plugins.md +4 -2
  108. package/skills/yui-operator/SKILL.md +9 -2
  109. package/skills/yui-runtime/SKILL.md +77 -9
  110. package/skills/yui-runtime/references/publication.md +3 -1
  111. package/dist/web/assets/client/taskSummary.js +0 -350
  112. package/dist/web/assets/client/taskSurface.js +0 -616
  113. package/dist/web/assets/client/view.js +0 -608
  114. package/dist/web/assets/styles/cards.js +0 -247
  115. package/dist/web/assets/styles/widgets.js +0 -168
@@ -17,8 +17,8 @@ workspace layout, native transcript, or an earlier AgentRun.
17
17
  ## Enter through the current context
18
18
 
19
19
  There are two normal Task entry points. A user may continue directly with the
20
- current, unrevoked Leader Session: read current context using the Session CLI
21
- with `task context <task-id> --json`. Do not request a self-wake, reopen a Task,
20
+ current, unrevoked Leader Session: read current context using
21
+ `yui task context <task-id> --json`. Do not request a self-wake, reopen a Task,
22
22
  or reuse an old completed AgentRun snapshot merely to obtain authority. Pending
23
23
  delivery, unknown execution evidence and missing reports do not themselves
24
24
  revoke Session authority. Task lifecycle, scope, Assignment, workspace and
@@ -27,26 +27,35 @@ authority merely because the Task becomes active.
27
27
  Use the runtime-provided `TMPDIR` for temporary context or diagnostic files,
28
28
  not fixed shared `/tmp` names or the logical multi-Project workspace container.
29
29
 
30
+ Ordinary commands use `yui` from the intended launch environment's PATH and
31
+ `YUI_HOME`. Session/native identity, Task/Assignment/workspace authority and
32
+ Controller protocol/storage checks still apply; they do not distinguish every
33
+ installation with the same protocol and storage versions. If the entry or Home
34
+ is wrong or unavailable, report that fact and use the explicitly authorized
35
+ entry/environment; do not guess another installation or clear Session identity.
36
+
30
37
  For every explicitly dispatched managed Task AgentRun:
31
38
 
32
39
  1. Read the exact AgentRun identity from the newest Bootstrap Envelope.
33
- 2. Before acting, load its authorized pack with the Session CLI named by the
34
- current Session Manifest:
40
+ 2. Before acting, load its authorized pack using the current Session Manifest's
41
+ Context command and the exact Task/Run identity:
35
42
 
36
43
  ```sh
37
- "$YUI_SESSION_CLI" task run context "$YUI_TASK_ID/<run-id>" --json
44
+ yui task run context "<task-id>/<run-id>" --json
38
45
  ```
39
46
 
40
47
  3. Verify that the returned Task, AgentRun, Role, purpose, Snapshot digest, workspace,
41
48
  and Adapter match the Envelope and Session Manifest. Stop and report a
42
49
  context-load failure if the pack is missing, stale, unauthorized, malformed,
43
50
  or mismatched. Never request an inline/full-prompt fallback.
44
- 4. Use pack summaries and pointers first. Expand only an authorized ref when
51
+ 4. Use the pack's `pointers` (including each pointer's summary) first. They are
52
+ the single readable-ref inventory; `deltaRefs` contains only changed identities.
53
+ Expand only an authorized ref when
45
54
  its full value is needed, selecting it by the pointer's exact `store` and
46
55
  `refId`:
47
56
 
48
57
  ```sh
49
- "$YUI_SESSION_CLI" task run context expand "$YUI_TASK_ID/<run-id>" <ref-id> --store <store> --mode full --json
58
+ yui task run context expand "<task-id>/<run-id>" <ref-id> --store <store> --mode full --json
50
59
  ```
51
60
 
52
61
  `--store` is required, even when the ref id is unique. Select both fields
@@ -119,14 +128,73 @@ TUI, with the configured Agent, permissions and workspace unchanged. A live
119
128
  unmanaged Session is not silently replaced or adopted; use an explicit Session
120
129
  lifecycle action before enabling controlled delivery.
121
130
 
122
- Context reads never consume queue entries. Read the referenced Message in full
123
- from Session Context. Native/transport acceptance is not implementation, and
131
+ Context reads never consume queue entries. Global Context returns bounded
132
+ `pending` and `recent` discovery pages, not Message bodies. Read a referenced
133
+ original with `role message show <role> <message-id> --json`. Follow a pending
134
+ page's `nextCursor` using `role message list <role> --pending --cursor <cursor>`
135
+ when it is incomplete; accepted delivery does not erase the Message from the
136
+ separate all-message list. Native/transport acceptance is not implementation, and
124
137
  `interrupt-requested` is not a stopped Turn or stopped background resources.
138
+ Saving a Task Message or Brief is not notification delivery to Operator. A
139
+ Task Role uses the existing Task InputRequest path for a genuinely missing
140
+ user decision, not global Role input controls or fabricated progress questions.
125
141
  Only an exact terminal and the original Session/writer boundary can release a
126
142
  then handoff. An accepted or unconfirmed steer must not be submitted again by
127
143
  changing its request id or composing then. A conclusive rejection permits an
128
144
  explicit new control attempt; uncertainty does not.
129
145
 
146
+ ## Read bounded information completely when required
147
+
148
+ Structured CLI results live in the top-level `data`, not a JSON-encoded
149
+ `output` string. Task current Context includes bounded current facts and
150
+ message references; it does not include event or terminal Run history.
151
+ `collections` and `attention` identify incomplete discovery. Use
152
+ `task context list <task> --store <store> [--cursor <cursor>]` to discover one
153
+ family, then `task context inspect <task> --store <store> --ref <refId>
154
+ --digest <digest>` to read its exact current value. Common Task lists use the
155
+ same `items`, `total`, `complete`, and `nextCursor` contract. Do not treat the
156
+ first page or a summary as complete requirements.
157
+
158
+ Long detail reads return `contentPage` instead of the ordinary value (under
159
+ `data.context` for Run context/expand, `data.contextDelta` for Run Context delta). Repeat the same read with
160
+ `--cursor <contentPage.nextCursor>` until `complete: true`. Concatenate `text`
161
+ in offset order and parse the combined JSON once, retaining the same source
162
+ and digest. A source-change error requires a fresh read, not mixing old and
163
+ new chunks. Never use a read cursor to repeat a mutation.
164
+
165
+ Read all pages of a relevant original requirement, result, or authorization
166
+ before acting on it. For a wake, read its complete fixed window and the
167
+ referenced original Messages; the wake itself contains pointers, not reports.
168
+ For unrelated discovery, stop once the necessary evidence is found. Task
169
+ event deltas retain their fixed upper bound and are history, not replacement
170
+ current state. No read acknowledges implementation or consumes a queue.
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
+ When capability discovery appears in that view, use `capability search` for
182
+ templates and `capability describe <name> --task
183
+ <task-id> --input '<json>'` for supported exact authorization assessments.
184
+ No `access` field means no exact authorization conclusion was made: supply the
185
+ required parameters and follow the domain's existing execution checks. Hidden
186
+ operations are omitted; `requestable` includes the exact missing Grant bounds
187
+ and existing request route; `authorized` remains subject to execution-time
188
+ revalidation. Provider outages do not change this authorization observation.
189
+
190
+ Discovery never sends requests, consumes Grants, starts plugins or approves
191
+ effects. If the Task actually needs a requestable operation, use the existing
192
+ Assignment Message to Leader, Leader InputRequest, or Operator path. Preserve
193
+ the exact action, resource/plugin, digest, environment and phase. A response is
194
+ not a Grant. Existing source-authorized Leader Grant operations and Operator
195
+ Grant authority remain unchanged; never expand an authorization source or
196
+ solicit real-resource validation merely because an operation is requestable.
197
+
130
198
  ## Preserve intent and authority
131
199
 
132
200
  An analysis, diagnosis, or review request is read-only unless the user also
@@ -87,7 +87,9 @@ is not Candidate acceptance, Review, Integration or Task completion.
87
87
 
88
88
  ## Archive separately
89
89
 
90
- Completion does not authorize archive. The Operator obtains authorization for
90
+ Completion does not authorize archive. A delivery Leader with original user
91
+ authorization may use [ordinary archive](../../yui-leader/references/authorization.md);
92
+ this grants no force or abandonment authority. Otherwise the Operator obtains authorization for
91
93
  the exact Task, checks archive eligibility, then uses `--integrated` for verified
92
94
  merged delivery or `--abandon` for deliberate non-delivery. General archive
93
95
  approval never implies `--force` authority. Preserve the Task record.
@@ -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
- `;