@zq-silk/yui 2.1.0 → 2.3.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/README.md +8 -2
- package/dist/cli/commandCatalog.js +32 -6
- package/dist/cli/commandDiscovery.js +4 -1
- package/dist/cli/interactionPolicy.js +4 -0
- package/dist/cli.js +23 -3078
- package/dist/commands/taskCommands.js +84 -28
- package/dist/commands/taskIntegrationCommands.js +8 -17
- package/dist/commands/taskUpstreamCommands.js +3 -1
- package/dist/context/runContextPack.js +132 -5
- package/dist/context/sourceRunContext.js +4 -2
- package/dist/context/taskContext.js +6 -1
- package/dist/controlPlaneCli.js +3099 -0
- package/dist/controller/clientRuntime.js +3 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +19 -6
- package/dist/controller/jobControl.js +21 -0
- package/dist/executor/agentExecutor.js +1 -1
- package/dist/executor/effectiveLaunch.js +18 -5
- package/dist/executor/fileRoleLaunchPlanner.js +15 -19
- package/dist/integration/gitIntegrationService.js +58 -82
- package/dist/job/jobCleanupInspection.js +67 -0
- package/dist/message/messageContinuation.js +7 -2
- package/dist/nativeAgent/agent.js +176 -74
- package/dist/nativeAgent/cliDemo.js +37 -0
- package/dist/nativeAgent/codingTools.js +11 -0
- package/dist/nativeAgent/commandTool.js +215 -0
- package/dist/nativeAgent/compactionDemo.js +158 -0
- package/dist/nativeAgent/composition.js +71 -0
- package/dist/nativeAgent/context/budget.js +44 -0
- package/dist/nativeAgent/context/index.js +339 -0
- package/dist/nativeAgent/context/providerCompressor.js +103 -0
- package/dist/nativeAgent/demo.js +12 -1
- package/dist/nativeAgent/evaluation/cases.js +38 -0
- package/dist/nativeAgent/evaluation/checks.js +91 -0
- package/dist/nativeAgent/evaluation/demo.js +19 -0
- package/dist/nativeAgent/evaluation/files.js +54 -0
- package/dist/nativeAgent/evaluation/fixture.js +36 -0
- package/dist/nativeAgent/evaluation/index.js +239 -0
- package/dist/nativeAgent/executionOwner.js +209 -0
- package/dist/nativeAgent/filePatterns.js +170 -0
- package/dist/nativeAgent/fileToolsSupport.js +202 -0
- package/dist/nativeAgent/index.js +13 -0
- package/dist/nativeAgent/interaction/cli.js +358 -0
- package/dist/nativeAgent/interaction/contracts.js +1 -0
- package/dist/nativeAgent/interaction/index.js +3 -0
- package/dist/nativeAgent/interaction/memoryDemo.js +115 -0
- package/dist/nativeAgent/interaction/renderer.js +34 -0
- package/dist/nativeAgent/localSafety.js +258 -0
- package/dist/nativeAgent/model/anthropicMessages.js +204 -0
- package/dist/nativeAgent/model/chatCompletions.js +210 -0
- package/dist/nativeAgent/model/errors.js +47 -0
- package/dist/nativeAgent/model/gateway.js +423 -0
- package/dist/nativeAgent/model/index.js +7 -0
- package/dist/nativeAgent/model/observationAdapter.js +21 -0
- package/dist/nativeAgent/model/protocols.js +19 -0
- package/dist/nativeAgent/model/responses.js +263 -0
- package/dist/nativeAgent/model/types.js +1 -0
- package/dist/nativeAgent/model/wire.js +73 -0
- package/dist/nativeAgent/observability/index.js +220 -0
- package/dist/nativeAgent/product/catalog.js +82 -0
- package/dist/nativeAgent/product/config.js +295 -0
- package/dist/nativeAgent/product/facts.js +30 -0
- package/dist/nativeAgent/product/index.js +62 -0
- package/dist/nativeAgent/product/location.js +44 -0
- package/dist/nativeAgent/product/runtime.js +276 -0
- package/dist/nativeAgent/product/storage.js +49 -0
- package/dist/nativeAgent/product/tools.js +47 -0
- package/dist/nativeAgent/product/transport.js +54 -0
- package/dist/nativeAgent/projectGuidance/index.js +425 -0
- package/dist/nativeAgent/searchTools.js +305 -0
- package/dist/nativeAgent/session/backends.js +293 -0
- package/dist/nativeAgent/session/catalog.js +97 -0
- package/dist/nativeAgent/session/catalogDemo.js +87 -0
- package/dist/nativeAgent/session/contracts.js +1 -0
- package/dist/nativeAgent/session/format.js +269 -0
- package/dist/nativeAgent/session/index.js +5 -0
- package/dist/nativeAgent/session/location.js +36 -0
- package/dist/nativeAgent/session/sqliteFormat.js +134 -0
- package/dist/nativeAgent/session/store.js +248 -0
- package/dist/nativeAgent/textTools.js +270 -133
- package/dist/nativeAgent/toolManager/executor.js +290 -0
- package/dist/nativeAgent/toolManager/index.js +4 -0
- package/dist/nativeAgent/validation.js +2 -2
- package/dist/repository/gitWorkspace.js +41 -12
- package/dist/repository/taskWorkspaceCoordinator.js +72 -145
- package/dist/repository/taskWorkspacePreparer.js +39 -43
- package/dist/repository/workspaceCleanupInspection.js +88 -7
- package/dist/resources/resourceRegistrar.js +16 -10
- package/dist/runtime/integrationRuntimeIsolation.js +21 -0
- package/dist/runtime/taskRuntimeIsolation.js +91 -21
- package/dist/task/archiveDiagnostics.js +26 -8
- package/dist/task/archivePreflight.js +26 -9
- package/dist/task/leaderArchive.js +9 -15
- package/dist/task/taskAuthority.js +56 -0
- package/dist/web/assets/client/api.js +8 -0
- package/dist/web/assets/client/app.js +74 -9
- package/dist/web/assets/client/components.js +95 -36
- package/dist/web/assets/client/detail.js +110 -64
- package/dist/web/assets/client/dock.js +95 -11
- package/dist/web/assets/client/evidence.js +286 -94
- package/dist/web/assets/client/i18n.js +182 -62
- package/dist/web/assets/client/overview.js +9 -10
- package/dist/web/assets/client/records.js +142 -7
- package/dist/web/assets/client/sections.js +234 -198
- package/dist/web/assets/client/sidebar.js +31 -2
- package/dist/web/assets/icons.js +4 -0
- package/dist/web/assets/shell.js +2 -1
- package/dist/web/assets/styles/components.js +64 -15
- package/dist/web/assets/styles/layout.js +11 -4
- package/dist/web/assets/styles/responsive.js +16 -6
- package/dist/web/assets/styles/tokens.js +1 -1
- package/dist/web/assets/styles/views.js +62 -42
- package/dist/web/webServer.js +12 -1
- package/dist/web/webTaskSurface.js +8 -1
- package/docs/agent-result-consumption.md +16 -0
- package/docs/agent-result-consumption.zh-CN.md +13 -0
- package/docs/examples/agent-offline.mjs +194 -0
- package/docs/native-agent.md +283 -0
- package/docs/observability/README.md +13 -3
- package/docs/observability/README.zh-CN.md +10 -3
- package/docs/release-workflow.md +72 -9
- package/docs/release-workflow.zh-CN.md +55 -6
- package/docs/roles-and-configuration.md +32 -0
- package/docs/roles-and-configuration.zh-CN.md +26 -0
- package/docs/task-delivery.md +36 -8
- package/docs/task-delivery.zh-CN.md +24 -7
- package/i18n/README.zh-CN.md +3 -2
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +9 -0
- package/skills/yui-leader/references/execution.md +2 -1
- package/skills/yui-leader/references/integration.md +20 -0
- package/skills/yui-operator/SKILL.md +4 -0
- package/skills/yui-reviewer/SKILL.md +8 -0
- package/skills/yui-runtime/SKILL.md +11 -0
- package/skills/yui-runtime/references/publication.md +20 -4
- package/skills/yui-worker/SKILL.md +3 -0
|
@@ -8,11 +8,15 @@ import {
|
|
|
8
8
|
badge, statusBadge, label, tone, card, disclosure, richText, bulletList, emptyState,
|
|
9
9
|
note, button, chip, mono, inputCard, kv, dot, timeTag
|
|
10
10
|
} from "/assets/js/components.js";
|
|
11
|
-
import { entriesOf, valuesOf, recordCard } from "/assets/js/records.js";
|
|
12
|
-
import {
|
|
11
|
+
import { entriesOf, valuesOf, totalOf, recordCard, readExact } from "/assets/js/records.js";
|
|
12
|
+
import { renderRuntime, renderRecords } from "/assets/js/sections.js";
|
|
13
|
+
import { renderDelivery } from "/assets/js/evidence.js";
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
// Overview: where it stands. Delivery: what it produced. Runtime: what is
|
|
16
|
+
// live now. Records: what happened. Retired section names stay addressable.
|
|
17
|
+
export const TABS = ["overview", "delivery", "runtime", "records"];
|
|
18
|
+
export const TAB_ALIASES = { work: "delivery", execution: "runtime", history: "records", details: "records" };
|
|
19
|
+
const TAB_ICONS = { overview: "target", delivery: "package", runtime: "pulse", records: "history" };
|
|
16
20
|
const SESSION_GROUPS = ["active", "waiting", "background", "quiet", "diagnostic", "stopped", "unknown", "idle"];
|
|
17
21
|
|
|
18
22
|
export function renderTaskDetail(container, data, t, locale, ctx) {
|
|
@@ -20,6 +24,7 @@ export function renderTaskDetail(container, data, t, locale, ctx) {
|
|
|
20
24
|
const task = data.task;
|
|
21
25
|
const core = data.core;
|
|
22
26
|
container.dataset.taskId = task.id;
|
|
27
|
+
data.viewState.coreKey = core.coreCursor;
|
|
23
28
|
const page = h("div.page.task-page");
|
|
24
29
|
|
|
25
30
|
// --- Header ----------------------------------------------------------------
|
|
@@ -48,10 +53,12 @@ export function renderTaskDetail(container, data, t, locale, ctx) {
|
|
|
48
53
|
(task.projectBindings || []).length ? h("span.meta-item", null, icon("layers", "icon-sm"),
|
|
49
54
|
task.projectBindings.map(function (binding) { return binding.projectId; }).join(", ")) : null);
|
|
50
55
|
|
|
56
|
+
// Tab counts are current totals from the same bounded read, not samples:
|
|
57
|
+
// open questions, open work items and open execution records.
|
|
51
58
|
const counts = {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
overview: core.attention.openInputs.count,
|
|
60
|
+
delivery: totalOf(core, "work-item"),
|
|
61
|
+
runtime: totalOf(core, "run")
|
|
55
62
|
};
|
|
56
63
|
const tabs = h("nav.tabs", { role: "tablist", "aria-label": t("tabs.label") }, TABS.map(function (tab, index) {
|
|
57
64
|
return h("button.tab", {
|
|
@@ -63,7 +70,7 @@ export function renderTaskDetail(container, data, t, locale, ctx) {
|
|
|
63
70
|
dataset: { tab: tab },
|
|
64
71
|
onclick: function () { ctx.onTab(tab); }
|
|
65
72
|
}, icon(TAB_ICONS[tab], "icon-sm"), h("span", null, t("tabs." + tab)),
|
|
66
|
-
counts[tab] ? h("span.count", null, String(counts[tab])) : null);
|
|
73
|
+
counts[tab] ? h("span.count" + (tab === "overview" ? ".tone-warn" : ""), null, String(counts[tab])) : null);
|
|
67
74
|
}));
|
|
68
75
|
tabs.addEventListener("keydown", function (event) {
|
|
69
76
|
if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;
|
|
@@ -87,29 +94,38 @@ export function renderTaskDetail(container, data, t, locale, ctx) {
|
|
|
87
94
|
page.append(panels[tab]);
|
|
88
95
|
});
|
|
89
96
|
renderOverviewPanel(panels.overview, data, t, locale, ctx);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
renderDetails(panels.details, data, t, locale, ctx);
|
|
97
|
+
renderDelivery(panels.delivery, data, t, locale, ctx);
|
|
98
|
+
renderRuntime(panels.runtime, data, t, locale, ctx);
|
|
99
|
+
renderRecords(panels.records, data, t, locale, ctx);
|
|
94
100
|
container.append(page);
|
|
95
101
|
updateObservation(container, data, t, locale, ctx);
|
|
102
|
+
const active = panels[ctx.activeTab];
|
|
103
|
+
if (active && active.onShow) active.onShow();
|
|
96
104
|
}
|
|
97
105
|
|
|
106
|
+
// A panel may read on first show (evidence, record pages); it keeps those
|
|
107
|
+
// reads in view state, so showing it again does not repeat them.
|
|
98
108
|
export function selectTab(container, tab) {
|
|
99
109
|
container.querySelectorAll(".tab").forEach(function (button) {
|
|
100
110
|
const active = button.dataset.tab === tab;
|
|
101
111
|
button.setAttribute("aria-selected", String(active));
|
|
102
112
|
button.tabIndex = active ? 0 : -1;
|
|
103
113
|
});
|
|
104
|
-
container.querySelectorAll(".tab-panel").forEach(function (panel) {
|
|
114
|
+
container.querySelectorAll(".tab-panel").forEach(function (panel) {
|
|
115
|
+
panel.hidden = panel.id !== "panel-" + tab;
|
|
116
|
+
if (!panel.hidden && panel.onShow) panel.onShow();
|
|
117
|
+
});
|
|
105
118
|
}
|
|
106
119
|
|
|
120
|
+
// One reading column, top to bottom: where the Task stands now, what needs the
|
|
121
|
+
// user, what it has reported, its goal and its current decisions.
|
|
107
122
|
function renderOverviewPanel(panel, data, t, locale, ctx) {
|
|
108
123
|
const task = data.task;
|
|
109
124
|
const core = data.core;
|
|
110
|
-
// Now: the derived execution status (observation)
|
|
125
|
+
// Now: the derived execution status (observation), the open questions and
|
|
126
|
+
// who acts next. drawNow() tints the banner with the status tone.
|
|
111
127
|
const now = card({ className: "now-card" });
|
|
112
|
-
now.body.append(h("div", { dataset: { slot: "now" } }));
|
|
128
|
+
now.body.append(h("div", { dataset: { slot: "now" } }), h("p.next-line", { dataset: { slot: "next" } }));
|
|
113
129
|
panel.append(now);
|
|
114
130
|
|
|
115
131
|
// Needs you: open InputRequests with their answer controls.
|
|
@@ -129,15 +145,28 @@ function renderOverviewPanel(panel, data, t, locale, ctx) {
|
|
|
129
145
|
panel.append(needs);
|
|
130
146
|
}
|
|
131
147
|
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
sessions.body.append(h("div", { dataset: { slot: "sessions" } }));
|
|
136
|
-
grid.append(sessions);
|
|
137
|
-
grid.append(goalCard(data, t, ctx));
|
|
138
|
-
grid.append(decisionsCard(data, t, locale, ctx));
|
|
139
|
-
panel.append(grid);
|
|
148
|
+
const progress = progressCard(data, t, locale, ctx);
|
|
149
|
+
const goal = goalCard(data, t, ctx);
|
|
150
|
+
panel.append(progress);
|
|
140
151
|
if (task.status === "draft") panel.append(planningCard(data, t, locale));
|
|
152
|
+
panel.append(goal, decisionsCard(data, t, locale, ctx));
|
|
153
|
+
// The Brief is the Overview's main source; when the bounded read withheld
|
|
154
|
+
// its value, read it exactly once per digest and redraw both cards.
|
|
155
|
+
const briefEntry = entriesOf(core, "task-brief")[0];
|
|
156
|
+
if (briefEntry && briefEntry.omitted) {
|
|
157
|
+
const view = data.viewState;
|
|
158
|
+
view.exact = view.exact || {};
|
|
159
|
+
readExact(briefEntry, task.id, ctx, view.exact).then(function (result) {
|
|
160
|
+
if (!progress.isConnected) return;
|
|
161
|
+
const exact = { ...data, briefValue: result.value };
|
|
162
|
+
progress.replaceWith(progressCard(exact, t, locale, ctx));
|
|
163
|
+
goal.replaceWith(goalCard(exact, t, ctx));
|
|
164
|
+
}, function () {});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function briefOf(data) {
|
|
169
|
+
return data.briefValue || valuesOf(data.core, "task-brief")[0];
|
|
141
170
|
}
|
|
142
171
|
|
|
143
172
|
function progressCard(data, t, locale, ctx) {
|
|
@@ -148,20 +177,19 @@ function progressCard(data, t, locale, ctx) {
|
|
|
148
177
|
if (task.retirementSummary) element.body.append(richText(t("progress.retirement"), task.retirementSummary, t, { className: "callout" }));
|
|
149
178
|
if (task.archiveSummary || task.archiveReason) element.body.append(richText(t("progress.archive"), [task.archiveSummary, task.archiveReason].filter(Boolean).join("\n\n"), t, { className: "callout" }));
|
|
150
179
|
const briefEntry = entriesOf(data.core, "task-brief")[0];
|
|
151
|
-
const brief =
|
|
180
|
+
const brief = briefOf(data);
|
|
152
181
|
if (brief) {
|
|
153
182
|
element.body.append(richText(t(working ? "progress.focus" : "progress.lastFocus"), brief.currentFocus, t));
|
|
154
183
|
element.body.append(richText(t(working ? "progress.report" : "progress.lastReport"), brief.leaderSummary, t, { muted: true }));
|
|
155
184
|
element.body.append(h("p.faint.small", null, t("progress.briefUpdated") + " " + formatDateTime(brief.updatedAt, locale)));
|
|
156
185
|
} else if (briefEntry) element.body.append(recordCard(briefEntry, task.id, t, ctx, { compact: true }));
|
|
157
186
|
else element.body.append(emptyState(t("progress.none")));
|
|
158
|
-
element.body.append(h("p.next-line", { dataset: { slot: "next" } }));
|
|
159
187
|
return element;
|
|
160
188
|
}
|
|
161
189
|
|
|
162
190
|
function goalCard(data, t, ctx) {
|
|
163
191
|
const task = data.task;
|
|
164
|
-
const brief =
|
|
192
|
+
const brief = briefOf(data);
|
|
165
193
|
const element = card({ title: t("goal.title"), icon: "target" });
|
|
166
194
|
if (brief && brief.objective) element.body.append(richText(null, brief.objective, t));
|
|
167
195
|
if (brief && brief.boundaries && brief.boundaries.length) {
|
|
@@ -184,7 +212,8 @@ function goalCard(data, t, ctx) {
|
|
|
184
212
|
function decisionsCard(data, t, locale, ctx) {
|
|
185
213
|
const entries = entriesOf(data.core, "task-decision");
|
|
186
214
|
const active = entries.filter(function (entry) { return !entry.omitted && entry.value.status === "active"; });
|
|
187
|
-
const
|
|
215
|
+
const total = totalOf(data.core, "task-decision");
|
|
216
|
+
const element = card({ id: "detail-decisions", title: t("decisions.title"), icon: "check", count: total || null, hint: t("decisions.hint") });
|
|
188
217
|
active.slice(0, 3).forEach(function (entry) {
|
|
189
218
|
const value = entry.value;
|
|
190
219
|
element.body.append(h("article.decision", null,
|
|
@@ -192,9 +221,10 @@ function decisionsCard(data, t, locale, ctx) {
|
|
|
192
221
|
value.rationale ? richText(null, value.rationale, t, { muted: true, threshold: 260 }) : null));
|
|
193
222
|
});
|
|
194
223
|
if (!active.length) element.body.append(emptyState(t("decisions.none")));
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
224
|
+
element.body.append(button(t("decisions.more"), { variant: "link", icon: "chevron", onClick: function () {
|
|
225
|
+
data.viewState.recordsFilter = "decision";
|
|
226
|
+
ctx.onTab("records");
|
|
227
|
+
} }));
|
|
198
228
|
return element;
|
|
199
229
|
}
|
|
200
230
|
|
|
@@ -240,12 +270,7 @@ export function updateObservation(container, data, t, locale, ctx) {
|
|
|
240
270
|
if (execution && !(execution.next.owner === "none" && execution.next.action === "none")) next.append(icon("flag", "icon-sm"), h("span", null, t("now.nextOwner") + " "),
|
|
241
271
|
h("strong", null, label(t, "exec.owner", execution.next.owner)), h("span", null, " · " + label(t, "exec.action", execution.next.action)));
|
|
242
272
|
}
|
|
243
|
-
|
|
244
|
-
if (sessions && !sessions.contains(document.activeElement)) {
|
|
245
|
-
const open = sessions.querySelector("details") && sessions.querySelector("details").open;
|
|
246
|
-
drawSessions(sessions, available ? data.runtime.sessions : null, t, locale);
|
|
247
|
-
if (open && sessions.querySelector("details")) sessions.querySelector("details").open = true;
|
|
248
|
-
}
|
|
273
|
+
drawSessions(container, available ? data.runtime.sessions : null, t, locale);
|
|
249
274
|
const raw = container.querySelector('[data-slot="runtime-raw"]');
|
|
250
275
|
if (raw) raw.textContent = available ? JSON.stringify({ roles: data.runtime.roles, runtimeHealth: data.runtime.runtimeHealth }, null, 2) : "";
|
|
251
276
|
const rawStatus = container.querySelector('[data-slot="runtime-status"]');
|
|
@@ -257,9 +282,16 @@ export function updateObservation(container, data, t, locale, ctx) {
|
|
|
257
282
|
});
|
|
258
283
|
}
|
|
259
284
|
|
|
285
|
+
const BANNER_TONES = ["tone-info", "tone-ok", "tone-warn", "tone-bad", "tone-idle", "tone-accent"];
|
|
286
|
+
|
|
260
287
|
function drawNow(slot, data, execution, t, locale) {
|
|
261
288
|
clear(slot);
|
|
262
289
|
const inputCount = data.core.attention.openInputs.count;
|
|
290
|
+
const banner = slot.closest(".now-card");
|
|
291
|
+
if (banner) {
|
|
292
|
+
banner.classList.remove.apply(banner.classList, BANNER_TONES);
|
|
293
|
+
banner.classList.add("tone-" + (execution ? tone("exec", execution.status) : data.runtimeStatus === "unavailable" ? "bad" : "idle"));
|
|
294
|
+
}
|
|
263
295
|
if (!execution) {
|
|
264
296
|
slot.append(h("div.now-head", null,
|
|
265
297
|
badge(t("observation." + data.runtimeStatus), data.runtimeStatus === "unavailable" ? "bad" : "idle", { dot: true }),
|
|
@@ -300,34 +332,48 @@ function drawNow(slot, data, execution, t, locale) {
|
|
|
300
332
|
else slot.append(h("p.now-note", null, inputCount ? t("now.inputs").replace("{count}", String(inputCount)) : t("now.clear")));
|
|
301
333
|
}
|
|
302
334
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
h("
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
observation.sessions.forEach(function (session) {
|
|
321
|
-
facts.body.append(kv([
|
|
322
|
-
[t("sessions.role"), session.roleName],
|
|
323
|
-
[t("sessions.lastActivity"), session.lastActivityAt ? formatDateTime(session.lastActivityAt, locale) : t("sessions.unobserved")],
|
|
324
|
-
[t("sessions.inputUpdated"), formatDateTime(session.sourceUpdatedAt, locale)],
|
|
325
|
-
[t("sessions.operations"), session.operations.length ? session.operations.join(", ") : null],
|
|
326
|
-
[t("sessions.background"), session.background.length ? session.background.map(function (item) { return item.id + " · " + item.execution; }).join(", ") : null],
|
|
327
|
-
[t("sessions.identity"), mono([session.nativeSessionId, session.nativeTurnId, session.attemptId].filter(Boolean).join(" / "))]
|
|
328
|
-
]));
|
|
335
|
+
// Native Session observation, drawn into the Runtime tab: one line per Role,
|
|
336
|
+
// the group counts beside the Roles heading, and the timestamps under
|
|
337
|
+
// Diagnostics. Activity is never delivery progress.
|
|
338
|
+
function drawSessions(container, observation, t, locale) {
|
|
339
|
+
const sessions = observation ? observation.sessions : [];
|
|
340
|
+
container.querySelectorAll("[data-role-session]").forEach(function (slot) {
|
|
341
|
+
clear(slot);
|
|
342
|
+
const session = sessions.find(function (item) { return item.roleName === slot.dataset.roleSession; });
|
|
343
|
+
if (!session) {
|
|
344
|
+
slot.append(h("span.faint.small", null, observation ? t("session.noneRecorded") : t("sessions.unavailable")));
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
slot.append(h("div.role-session-head", null, dot(tone("session", session.group)),
|
|
348
|
+
h("strong", null, t("session." + session.group)),
|
|
349
|
+
h("span.faint.small", null, session.lastActivityAt ? relativeTime(session.lastActivityAt, locale, t) : t("sessions.unobserved"))));
|
|
350
|
+
if (session.reason) slot.append(h("p.small", null, session.reason));
|
|
351
|
+
if (session.waitingReason) slot.append(h("p.small", null, t("sessions.waitingFor") + " " + session.waitingReason));
|
|
329
352
|
});
|
|
330
|
-
|
|
331
|
-
|
|
353
|
+
const counts = container.querySelector('[data-slot="session-counts"]');
|
|
354
|
+
if (counts) {
|
|
355
|
+
clear(counts);
|
|
356
|
+
if (observation) SESSION_GROUPS.forEach(function (group) {
|
|
357
|
+
if (observation.counts[group]) counts.append(badge(observation.counts[group] + " " + t("session." + group), tone("session", group), { dot: true }));
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
const facts = container.querySelector('[data-slot="session-facts"]');
|
|
361
|
+
if (facts) {
|
|
362
|
+
clear(facts);
|
|
363
|
+
if (!observation) return;
|
|
364
|
+
facts.append(h("h4.sub-head", null, t("sessions.facts")));
|
|
365
|
+
if (!sessions.length) facts.append(note(t("sessions.none")));
|
|
366
|
+
sessions.forEach(function (session) {
|
|
367
|
+
facts.append(kv([
|
|
368
|
+
[t("sessions.role"), session.roleName],
|
|
369
|
+
[t("sessions.lastActivity"), session.lastActivityAt ? formatDateTime(session.lastActivityAt, locale) : t("sessions.unobserved")],
|
|
370
|
+
[t("sessions.inputUpdated"), formatDateTime(session.sourceUpdatedAt, locale)],
|
|
371
|
+
[t("sessions.operations"), session.operations.length ? session.operations.join(", ") : null],
|
|
372
|
+
[t("sessions.background"), session.background.length ? session.background.map(function (item) { return item.id + " · " + item.execution; }).join(", ") : null],
|
|
373
|
+
[t("sessions.identity"), mono([session.nativeSessionId, session.nativeTurnId, session.attemptId].filter(Boolean).join(" / "))]
|
|
374
|
+
]));
|
|
375
|
+
});
|
|
376
|
+
facts.append(h("p.faint.small", null, t("sessions.scope") + " " + formatDateTime(observation.readAt, locale)));
|
|
377
|
+
}
|
|
332
378
|
}
|
|
333
379
|
`;
|
|
@@ -8,7 +8,7 @@ import { FitAddon } from "/assets/vendor/addon-fit.mjs";
|
|
|
8
8
|
import { h, icon, clear } from "/assets/js/dom.js";
|
|
9
9
|
import { formatShort } from "/assets/js/format.js";
|
|
10
10
|
import { label, chip, emptyState, richText, dot } from "/assets/js/components.js";
|
|
11
|
-
import { entriesOf,
|
|
11
|
+
import { entriesOf, totalOf, readExact, recordCard } from "/assets/js/records.js";
|
|
12
12
|
import { messageComposer } from "/assets/js/forms.js";
|
|
13
13
|
import { pageToken } from "/assets/js/api.js";
|
|
14
14
|
|
|
@@ -31,6 +31,7 @@ export function renderDiscussion(host, data, t, locale, actions) {
|
|
|
31
31
|
feed = host.querySelector(".feed");
|
|
32
32
|
}
|
|
33
33
|
host.dataset.taskId = task.id;
|
|
34
|
+
latest.set(task.id, { data: data, t: t, locale: locale, actions: actions });
|
|
34
35
|
if (!feed.querySelector('[data-reading="true"]')) drawFeed(feed, data, t, locale, actions);
|
|
35
36
|
feed.scrollTop = atEnd ? feed.scrollHeight : previousScroll;
|
|
36
37
|
if (!keepComposer) {
|
|
@@ -44,21 +45,92 @@ export function discussionHasUnsent(host) {
|
|
|
44
45
|
return !!host.querySelector('[data-unsent="true"]');
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
// The Context read carries the newest messages as summaries; each is read
|
|
49
|
+
// exactly once per digest (kept in the Task's view state) and drawn in full,
|
|
50
|
+
// so a message needs no separate raw-record reader.
|
|
51
|
+
// Older messages are listed on request, newest first, and read the same way.
|
|
52
|
+
const older = new Map();
|
|
53
|
+
const failedReads = new Set();
|
|
54
|
+
const latest = new Map();
|
|
55
|
+
|
|
56
|
+
function readKey(entry) { return entry.ref.refId + "@" + entry.ref.digest; }
|
|
57
|
+
|
|
58
|
+
function exactValue(entry, cache) {
|
|
59
|
+
if (!entry.omitted) return entry.value;
|
|
60
|
+
const hit = cache[entry.ref.store + "/" + entry.ref.refId];
|
|
61
|
+
return hit && hit.digest === entry.ref.digest && hit.result ? hit.result.value : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function loadOlder(data, actions, state, shownIds) {
|
|
65
|
+
state.pending = true;
|
|
66
|
+
state.error = null;
|
|
67
|
+
let rounds = 0;
|
|
68
|
+
function next() {
|
|
69
|
+
return actions.list(data.task.id, "task-message", { limit: 20, cursor: state.cursor }).then(function (page) {
|
|
70
|
+
const known = new Set(state.items.map(function (item) { return item.ref.refId; }));
|
|
71
|
+
const fresh = page.items.filter(function (item) { return !known.has(item.ref.refId) && !shownIds.has(item.ref.refId); });
|
|
72
|
+
state.items = state.items.concat(fresh);
|
|
73
|
+
state.cursor = page.nextCursor;
|
|
74
|
+
state.done = !page.nextCursor;
|
|
75
|
+
rounds += 1;
|
|
76
|
+
if (!fresh.length && page.nextCursor && rounds < 10) return next();
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// A listing that moved invalidates its continuation; the next request
|
|
80
|
+
// starts again from the newest page and skips what is already shown.
|
|
81
|
+
return next().catch(function (error) { state.error = error.message; state.cursor = null; })
|
|
82
|
+
.then(function () { state.pending = false; });
|
|
83
|
+
}
|
|
84
|
+
|
|
47
85
|
function drawFeed(feed, data, t, locale, actions) {
|
|
48
86
|
clear(feed);
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
87
|
+
const task = data.task;
|
|
88
|
+
const cache = data.viewState.exact = data.viewState.exact || {};
|
|
89
|
+
const contextEntries = entriesOf(data.core, "task-message");
|
|
90
|
+
const shownIds = new Set(contextEntries.map(function (entry) { return entry.ref.refId; }));
|
|
91
|
+
if (!older.has(task.id)) older.set(task.id, { items: [], cursor: null, done: false, pending: false, error: null });
|
|
92
|
+
const state = older.get(task.id);
|
|
93
|
+
const olderItems = state.items.filter(function (item) { return !shownIds.has(item.ref.refId); })
|
|
94
|
+
.sort(function (a, b) { return Date.parse(a.createdAt || 0) - Date.parse(b.createdAt || 0); });
|
|
95
|
+
// Context lists messages newest first; the feed reads oldest first.
|
|
96
|
+
const rows = olderItems.map(function (item) { return { entry: { ref: item.ref, summary: item.summary, omitted: true }, at: item.createdAt }; })
|
|
97
|
+
.concat(contextEntries.slice().reverse().map(function (entry) { return { entry: entry, at: null }; }));
|
|
98
|
+
const reads = [];
|
|
99
|
+
rows.forEach(function (row) {
|
|
100
|
+
row.value = exactValue(row.entry, cache);
|
|
101
|
+
if (!row.value && !failedReads.has(readKey(row.entry))) {
|
|
102
|
+
row.reading = true;
|
|
103
|
+
reads.push(readExact(row.entry, task.id, actions, cache).catch(function () { failedReads.add(readKey(row.entry)); }));
|
|
104
|
+
}
|
|
105
|
+
row.at = (row.value && row.value.createdAt) || row.at;
|
|
52
106
|
});
|
|
53
|
-
if (
|
|
54
|
-
|
|
107
|
+
if (rows.every(function (row) { return row.at; })) rows.sort(function (a, b) { return Date.parse(a.at) - Date.parse(b.at); });
|
|
108
|
+
|
|
109
|
+
const remaining = totalOf(data.core, "task-message") - shownIds.size - olderItems.length;
|
|
110
|
+
if (actions.list && remaining > 0 && !state.done) {
|
|
111
|
+
const more = h("button.show-more.feed-older", { type: "button", disabled: state.pending },
|
|
112
|
+
state.pending ? t("discussion.olderLoading") : t("discussion.older").replace("{count}", String(remaining)));
|
|
113
|
+
more.addEventListener("click", function () {
|
|
114
|
+
more.disabled = true;
|
|
115
|
+
more.textContent = t("discussion.olderLoading");
|
|
116
|
+
loadOlder(data, actions, state, shownIds).then(function () { redraw(feed, task.id, true); });
|
|
117
|
+
});
|
|
118
|
+
feed.append(more);
|
|
119
|
+
}
|
|
120
|
+
if (state.error) feed.append(h("p.feed-note", null, t("records.unavailable") + " " + state.error));
|
|
121
|
+
if (!rows.length) {
|
|
55
122
|
feed.append(emptyState(t("discussion.empty"), "chat"));
|
|
56
123
|
return;
|
|
57
124
|
}
|
|
58
125
|
let lastDay = "";
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
|
|
126
|
+
rows.forEach(function (row) {
|
|
127
|
+
if (!row.value) {
|
|
128
|
+
const placeholder = recordCard(row.entry, task.id, t, actions, { compact: true });
|
|
129
|
+
if (row.reading) placeholder.dataset.reading = "true";
|
|
130
|
+
feed.append(placeholder);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const value = row.value;
|
|
62
134
|
const day = value.createdAt ? new Intl.DateTimeFormat(locale, { dateStyle: "medium" }).format(new Date(value.createdAt)) : "";
|
|
63
135
|
if (day && day !== lastDay) { feed.append(h("div.feed-day", null, h("span", null, day))); lastDay = day; }
|
|
64
136
|
const type = (value.author && value.author.type) || "system";
|
|
@@ -69,12 +141,24 @@ function drawFeed(feed, data, t, locale, actions) {
|
|
|
69
141
|
h("header.msg-head", null, h("strong", null, author),
|
|
70
142
|
value.intent ? chip(label(t, "intent", value.intent)) : null,
|
|
71
143
|
value.kind === "role-result" ? chip(label(t, "messageKind", value.kind)) : null,
|
|
72
|
-
h("time", { dateTime: value.createdAt || "" }, formatShort(value.createdAt, locale))),
|
|
144
|
+
h("time", { dateTime: value.createdAt || "", title: row.entry.ref.refId }, formatShort(value.createdAt, locale))),
|
|
73
145
|
h("div.msg-bubble", null, richText(null, value.body || "", t, { threshold: 900 })));
|
|
74
|
-
main.append(recordReader(entry, data.task.id, t, actions));
|
|
75
146
|
message.append(main);
|
|
76
147
|
feed.append(message);
|
|
77
148
|
});
|
|
149
|
+
if (reads.length) Promise.all(reads).then(function () { redraw(feed, task.id, false); });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Redraw after a read without losing the reader's place: the bottom stays
|
|
153
|
+
// pinned, and prepended older messages keep the current message in view.
|
|
154
|
+
function redraw(feed, taskId, prepend) {
|
|
155
|
+
const host = feed.closest("[data-task-id]");
|
|
156
|
+
const current = latest.get(taskId);
|
|
157
|
+
if (!feed.isConnected || !host || host.dataset.taskId !== taskId || !current) return;
|
|
158
|
+
const atEnd = feed.scrollHeight - feed.clientHeight - feed.scrollTop < 40;
|
|
159
|
+
const fromBottom = feed.scrollHeight - feed.scrollTop;
|
|
160
|
+
drawFeed(feed, current.data, current.t, current.locale, current.actions);
|
|
161
|
+
feed.scrollTop = atEnd && !prepend ? feed.scrollHeight : feed.scrollHeight - fromBottom;
|
|
78
162
|
}
|
|
79
163
|
|
|
80
164
|
// --- Live native Session ----------------------------------------------------------
|