@zq-silk/yui 1.1.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +2 -1
- package/ARCHITECTURE.zh-CN.md +2 -1
- package/README.md +2 -0
- package/dist/cli/commandCatalog.js +344 -324
- package/dist/cli/commandDiscovery.js +69 -0
- package/dist/cli/completion.js +22 -227
- package/dist/cli/dynamicCompletion.js +12 -6
- package/dist/cli/invocationAuthority.js +2 -2
- package/dist/cli/invocationRouter.js +18 -7
- package/dist/cli/managedDiagnostics.js +2 -1
- package/dist/cli/updatePorts.js +0 -5
- package/dist/cli.js +184 -130
- package/dist/commands/capabilityCommands.js +5 -5
- package/dist/commands/controllerCommands.js +2 -1
- package/dist/commands/globalRoleCommands.js +86 -44
- package/dist/commands/grantCommands.js +48 -8
- package/dist/commands/taskCommands.js +157 -196
- package/dist/commands/taskContextCommand.js +29 -6
- package/dist/commands/taskFactCommands.js +10 -69
- package/dist/commands/taskInputCommands.js +26 -35
- package/dist/commands/taskPublicationCommands.js +2 -35
- package/dist/commands/taskPublicationVerifyCommand.js +1 -2
- package/dist/commands/taskUpstreamCommands.js +2 -1
- package/dist/context/runContextPack.js +8 -29
- package/dist/context/sessionBootstrapManifest.js +5 -101
- package/dist/context/taskContext.js +104 -41
- package/dist/controller/clientRuntime.js +16 -8
- package/dist/controller/fileSchedulerStoreAdapter.js +24 -0
- package/dist/controller/runtime.js +6 -0
- package/dist/core/controllerClient.js +46 -21
- package/dist/core/protocol.js +12 -7
- package/dist/doctor/doctor.js +4 -3
- package/dist/errors/cliError.js +4 -4
- package/dist/errors/cliFailure.js +213 -0
- package/dist/executor/fileRoleLaunchPlanner.js +1 -4
- package/dist/grant/capabilityGrant.js +13 -0
- package/dist/grant/taskAuthorization.js +127 -0
- package/dist/kernel/accessAssessment.js +1 -0
- package/dist/kernel/builtinCapabilities.js +88 -9
- package/dist/kernel/capabilityRegistry.js +64 -19
- package/dist/kernel/kernelPorts.js +2 -2
- package/dist/nativeAgent/agent.js +173 -0
- package/dist/nativeAgent/contracts.js +1 -0
- package/dist/nativeAgent/demo.js +29 -0
- package/dist/nativeAgent/index.js +3 -0
- package/dist/nativeAgent/mockProvider.js +43 -0
- package/dist/nativeAgent/textTools.js +156 -0
- package/dist/nativeAgent/validation.js +95 -0
- package/dist/output/boundedRead.js +118 -0
- package/dist/plugins/pluginService.js +98 -8
- package/dist/release/releaseWorkflowEngine.js +8 -1
- package/dist/resources/projectResourceService.js +23 -1
- package/dist/runtime/managedCaller.js +2 -2
- package/dist/runtime/managedIdentity.js +6 -0
- package/dist/runtime/runtimeCoherence.js +10 -8
- package/dist/storage/sqliteSchema.js +10 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/taskStore.js +3 -0
- package/dist/surface/surfaceContributions.js +3 -0
- package/dist/task/leaderArchive.js +111 -0
- package/dist/task/leaderArchiveAuthority.js +24 -0
- package/dist/task/taskAuthority.js +5 -10
- package/dist/tmux/commandExecutor.js +7 -5
- package/dist/web/assets/assetManifest.js +38 -22
- package/dist/web/assets/client/api.js +115 -0
- package/dist/web/assets/client/app.js +555 -863
- package/dist/web/assets/client/components.js +323 -897
- package/dist/web/assets/client/detail.js +333 -0
- package/dist/web/assets/client/dock.js +225 -0
- package/dist/web/assets/client/dom.js +55 -3
- package/dist/web/assets/client/evidence.js +172 -0
- package/dist/web/assets/client/format.js +25 -14
- package/dist/web/assets/client/forms.js +230 -0
- package/dist/web/assets/client/i18n.js +1020 -791
- package/dist/web/assets/client/overview.js +115 -0
- package/dist/web/assets/client/records.js +63 -0
- package/dist/web/assets/client/sections.js +313 -0
- package/dist/web/assets/client/sidebar.js +117 -0
- package/dist/web/assets/client/theme.js +61 -22
- package/dist/web/assets/icons.js +39 -0
- package/dist/web/assets/shell.js +124 -118
- package/dist/web/assets/styles/base.js +41 -0
- package/dist/web/assets/styles/components.js +167 -0
- package/dist/web/assets/styles/layout.js +49 -77
- package/dist/web/assets/styles/markdown.js +16 -24
- package/dist/web/assets/styles/responsive.js +33 -44
- package/dist/web/assets/styles/tokens.js +65 -89
- package/dist/web/assets/styles/views.js +289 -0
- package/dist/web/webServer.js +2 -1
- package/docs/architecture/README.md +1 -0
- package/docs/architecture/README.zh-CN.md +1 -0
- package/docs/cli-information-contract.md +107 -0
- package/docs/cli-information-contract.zh-CN.md +81 -0
- package/docs/managed-turn-and-session-runtime.md +9 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
- package/docs/plugin-sdk.md +7 -3
- package/docs/plugin-sdk.zh-CN.md +5 -2
- package/docs/release-workflow.md +42 -9
- package/docs/release-workflow.zh-CN.md +30 -7
- package/docs/storage-baseline.md +5 -0
- package/docs/testing/verification-levels.md +2 -2
- package/i18n/README.zh-CN.md +2 -0
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +8 -1
- package/skills/yui-leader/references/authorization.md +64 -0
- package/skills/yui-leader/references/execution.md +18 -4
- package/skills/yui-leader/references/task-plugins.md +4 -2
- package/skills/yui-operator/SKILL.md +9 -2
- package/skills/yui-runtime/SKILL.md +77 -9
- package/skills/yui-runtime/references/publication.md +3 -1
- package/dist/web/assets/client/taskSummary.js +0 -350
- package/dist/web/assets/client/taskSurface.js +0 -616
- package/dist/web/assets/client/view.js +0 -608
- package/dist/web/assets/styles/cards.js +0 -247
- package/dist/web/assets/styles/widgets.js +0 -168
|
@@ -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
|
|
21
|
-
|
|
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
|
|
34
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
123
|
-
|
|
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.
|
|
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
|
-
`;
|