@zq-silk/yui 2.6.0 → 2.8.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 +23 -0
- package/dist/cli/commandCatalog.js +34 -7
- package/dist/cli/updateCommand.js +3 -0
- package/dist/commands/configCommands.js +1 -0
- package/dist/commands/globalRoleCommands.js +62 -35
- package/dist/commands/inboxCommands.js +372 -0
- package/dist/commands/roleSkillValidation.js +2 -0
- package/dist/commands/taskCommands.js +39 -7
- package/dist/commands/taskRoleRuntimeStatus.js +2 -0
- package/dist/context/currentSkills.js +48 -0
- package/dist/context/roleSessionContext.js +13 -16
- package/dist/context/runContextPack.js +50 -3
- package/dist/context/runInputContract.js +1 -1
- package/dist/context/sessionBootstrapManifest.js +27 -4
- package/dist/context/skillCatalog.js +2 -3
- package/dist/context/taskContext.js +38 -12
- package/dist/controlPlaneCli.js +58 -7
- package/dist/controller/controller.js +8 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +26 -6
- package/dist/controller/globalInputDelivery.js +86 -16
- package/dist/controller/globalRuntimeAttention.js +136 -6
- package/dist/controller/operatorNotification.js +6 -1
- package/dist/controller/providerRetryDelivery.js +3 -5
- package/dist/controller/runtime.js +6 -2
- package/dist/controller/structuredProviderObservation.js +51 -15
- package/dist/controller/taskHostTerminalRecovery.js +33 -0
- package/dist/doctor/doctor.js +25 -2
- package/dist/executor/effectiveLaunch.js +2 -2
- package/dist/executor/fileRoleLaunchPlanner.js +8 -15
- package/dist/interaction/notificationPresentation.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/message/globalInputBacklog.js +69 -0
- package/dist/message/globalMessageQuery.js +1 -0
- package/dist/message/inboxReceipt.js +62 -0
- package/dist/message/message.js +6 -1
- package/dist/message/messageContinuation.js +34 -13
- package/dist/message/taskInboxOwnership.js +43 -0
- package/dist/models/modelManagement.js +469 -0
- package/dist/models/modelManagementConfig.js +46 -0
- package/dist/models/modelManagementFiles.js +90 -0
- package/dist/models/modelManagementHttp.js +90 -0
- package/dist/models/modelManagementTypes.js +1 -0
- package/dist/nativeAgent/session/backends.js +1 -1
- package/dist/observability/runtimeIdentity.js +1 -1
- package/dist/output/boundedRead.js +43 -14
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/runtime/agentEndpointIdentity.js +8 -4
- package/dist/runtime/agentEndpointOwnership.js +5 -6
- package/dist/runtime/agentHost.js +165 -24
- package/dist/runtime/agentHostImplementation.js +18 -0
- package/dist/runtime/managedCaller.js +25 -1
- package/dist/runtime/managedIdentity.js +1 -1
- package/dist/runtime/providerRetry.js +19 -1
- package/dist/runtime/sessionCaller.js +53 -0
- package/dist/scheduler/operatorEvent.js +49 -0
- package/dist/scheduler/operatorInputNotificationProcessor.js +5 -4
- package/dist/scheduler/taskWake.js +5 -0
- package/dist/sqlite/driver.js +108 -0
- package/dist/storage/contextRecords.js +28 -0
- package/dist/storage/sqliteSchema.js +21 -1
- package/dist/storage/sqliteStore.js +64 -1
- package/dist/storage/storageSchema.js +1 -1
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +1 -0
- package/dist/storage/taskStore.js +4 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +1 -1
- package/dist/task/completionReadiness.js +13 -6
- package/dist/telemetry/sqliteTelemetryStore.js +1 -1
- package/dist/web/assets/assetManifest.js +28 -2
- package/dist/web/assets/client/app/manage.js +6 -0
- package/dist/web/assets/client/app/models.js +20 -0
- package/dist/web/assets/client/app/settings.js +41 -9
- package/dist/web/assets/client/i18n/en.js +180 -2
- package/dist/web/assets/client/i18n/zh-CN.js +178 -2
- package/dist/web/assets/client/index.js +8 -0
- package/dist/web/assets/client/layout/manageShell.js +94 -0
- package/dist/web/assets/client/lib/api.js +13 -1
- package/dist/web/assets/client/lib/i18n.js +5 -3
- package/dist/web/assets/client/views/dock/conversation.js +231 -80
- package/dist/web/assets/client/views/models.js +777 -0
- package/dist/web/assets/client/views/skills.js +22 -15
- package/dist/web/assets/shared/icons.js +2 -0
- package/dist/web/assets/shared/manageNav.js +13 -0
- package/dist/web/assets/shell/manage.js +54 -0
- package/dist/web/assets/shell/models.js +7 -0
- package/dist/web/assets/shell/settings.js +1 -35
- package/dist/web/assets/shell/settingsContent.js +30 -0
- package/dist/web/assets/shell/sidebar.js +1 -0
- package/dist/web/assets/styles/index.js +6 -0
- package/dist/web/assets/styles/views/conversation.js +133 -0
- package/dist/web/assets/styles/views/manage.js +71 -0
- package/dist/web/assets/styles/views/models.js +129 -0
- package/dist/web/assets/styles/views/settings.js +28 -14
- package/dist/web/assets/styles/views/sidebar.js +1 -0
- package/dist/web/webServer.js +59 -9
- package/docs/cli-information-contract.md +42 -11
- package/docs/cli-information-contract.zh-CN.md +28 -7
- package/docs/managed-turn-and-session-runtime.md +127 -5
- package/docs/managed-turn-and-session-runtime.zh-CN.md +83 -3
- package/docs/release-workflow.md +105 -9
- package/docs/release-workflow.zh-CN.md +73 -5
- package/docs/storage-baseline.md +22 -7
- package/docs/storage-baseline.zh-CN.md +14 -5
- package/docs/task-delivery.md +17 -8
- package/docs/task-delivery.zh-CN.md +11 -5
- package/docs/testing/review-evaluation.md +148 -0
- package/docs/testing/review-evaluation.zh-CN.md +117 -0
- package/docs/testing/verification-levels.md +26 -4
- package/docs/testing/verification-levels.zh-CN.md +11 -1
- package/i18n/README.zh-CN.md +19 -0
- package/package.json +5 -2
- package/skills/yui-leader/SKILL.md +28 -11
- package/skills/yui-leader/VERSION +1 -0
- package/skills/yui-leader/references/execution.md +29 -0
- package/skills/yui-leader/references/replicated-execution.md +7 -0
- package/skills/yui-operator/SKILL.md +15 -3
- package/skills/yui-operator/VERSION +1 -0
- package/skills/yui-operator/references/task-delivery.md +2 -0
- package/skills/yui-reviewer/SKILL.md +43 -1
- package/skills/yui-reviewer/VERSION +1 -0
- package/skills/yui-runtime/SKILL.md +144 -15
- package/skills/yui-runtime/VERSION +1 -0
- package/skills/yui-runtime/references/publication.md +16 -5
- package/skills/yui-runtime/references/recovery.md +24 -5
- package/skills/yui-worker/SKILL.md +8 -1
- package/skills/yui-worker/VERSION +1 -0
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
export const CONVERSATION_SCRIPT = String.raw `
|
|
2
|
-
import { h, clear } from "/assets/js/lib/dom.js";
|
|
2
|
+
import { h, clear, icon } from "/assets/js/lib/dom.js";
|
|
3
3
|
import { api, releaseMutation, requestJson, submitMutation } from "/assets/js/lib/api.js";
|
|
4
4
|
import { richText } from "/assets/js/ui/text.js";
|
|
5
5
|
import { readSessionAccessMode, writeSessionAccessMode } from "/assets/js/lib/prefs.js";
|
|
6
6
|
import { inputCard } from "/assets/js/domain/work.js";
|
|
7
7
|
|
|
8
8
|
export function createConversationController(host, t, locale = function () { return "en"; }) {
|
|
9
|
-
let owner = null, selected = null, facts = null,
|
|
9
|
+
let owner = null, selected = null, facts = null, next = null, latestItems = [], olderItems = [];
|
|
10
10
|
let generation = 0, busy = false, timer = null, pending = null, historyOk = false;
|
|
11
11
|
let recoveryUnavailable = false;
|
|
12
12
|
let rendered = new Map();
|
|
13
13
|
let materials = [], incomingMaterials = null;
|
|
14
14
|
let requestSignature = "";
|
|
15
15
|
let modelPending = null, modelCatalog = null, lastModelObservation = null;
|
|
16
|
-
const requests = h("div");
|
|
17
|
-
const questions = h("div");
|
|
16
|
+
const requests = h("div.cv-requests");
|
|
17
|
+
const questions = h("div.cv-requests");
|
|
18
18
|
let questionSignature = "";
|
|
19
|
+
let confirmedModel = null, modelRowMap = new Map();
|
|
19
20
|
const defaultMode = h("select", { "aria-label": t("conversation.defaultMode") },
|
|
20
21
|
h("option", { value: "structured" }, t("conversation.title")),
|
|
21
22
|
h("option", { value: "native" }, t("dock.session")));
|
|
@@ -25,44 +26,79 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
25
26
|
receipt.textContent = t(saved ? "conversation.defaultSaved" : "settings.storageFailed");
|
|
26
27
|
defaultMode.value = readSessionAccessMode();
|
|
27
28
|
});
|
|
28
|
-
const select = h("select", { "aria-label": t("conversation.sessions") });
|
|
29
|
-
const status = h("p.
|
|
30
|
-
const identity = h("p.
|
|
29
|
+
const select = h("select.cv-session", { "aria-label": t("conversation.sessions") });
|
|
30
|
+
const status = h("p.cv-status", { role: "status" });
|
|
31
|
+
const identity = h("p.cv-identity");
|
|
31
32
|
const feed = h("div.feed", { tabIndex: 0, "aria-label": t("conversation.history") });
|
|
32
|
-
const text = h("textarea.
|
|
33
|
-
const receipt = h("p.
|
|
34
|
-
const draftStatus = h("p.
|
|
35
|
-
const receiptDetail = h("pre");
|
|
36
|
-
const modelStatus = h("p.
|
|
37
|
-
const modelResult = h("p.
|
|
38
|
-
const modelSelect = h("select", { "aria-label": t("conversation.model") });
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
const text = h("textarea.cv-input", { rows: 2, placeholder: t("conversation.placeholder"), "aria-label": t("conversation.input"), maxLength: 12000 });
|
|
34
|
+
const receipt = h("p.cv-note", { role: "status" });
|
|
35
|
+
const draftStatus = h("p.cv-note", { role: "status" });
|
|
36
|
+
const receiptDetail = h("pre.cv-code");
|
|
37
|
+
const modelStatus = h("p.cv-note", { role: "status" });
|
|
38
|
+
const modelResult = h("p.cv-note", { role: "status" });
|
|
39
|
+
const modelSelect = h("select", { "aria-label": t("conversation.model"), hidden: true });
|
|
40
|
+
const modelRows = h("div.cv-options", { role: "listbox", "aria-label": t("conversation.selectModel") });
|
|
41
|
+
const modelLabel = h("span.cv-model-label", null, t("conversation.model"));
|
|
42
|
+
const modelLoad = h("button.cv-link", { type: "button" }, t("conversation.modelsLoad"));
|
|
43
|
+
const modelApply = h("button.btn.btn-primary", { type: "button" }, t("conversation.modelApply"));
|
|
44
|
+
const modelMenu = h("details.cv-pop.cv-model", null,
|
|
45
|
+
h("summary.cv-pill", null, modelLabel, icon("down")),
|
|
46
|
+
h("div.cv-popbody", null, h("p.cv-pop-title", null, t("conversation.selectModel")),
|
|
47
|
+
modelRows, modelSelect, modelStatus, modelResult,
|
|
48
|
+
h("div.cv-pop-actions", null, modelLoad, modelApply),
|
|
49
|
+
h("p.cv-note", null, t("conversation.modelBoundary"))));
|
|
44
50
|
const slash = h("div.conversation-slash", { "aria-label": t("conversation.commands") });
|
|
45
|
-
const literal = h("button.
|
|
46
|
-
const send = h("button.
|
|
47
|
-
const stop = h("button.
|
|
48
|
-
const check = h("button.
|
|
49
|
-
const older = h("button.
|
|
50
|
-
const latest = h("button.
|
|
51
|
-
const current = h("button.
|
|
52
|
-
const moreSessions = h("button.
|
|
51
|
+
const literal = h("button.cv-link", { type: "button", hidden: true }, t("conversation.literal"));
|
|
52
|
+
const send = h("button.cv-send", { type: "button", "aria-label": t("conversation.send"), title: t("conversation.send") }, icon("up"));
|
|
53
|
+
const stop = h("button.cv-stop", { type: "button", hidden: true, "aria-label": t("conversation.stop"), title: t("conversation.stop") }, icon("stop"));
|
|
54
|
+
const check = h("button.cv-link", { type: "button", hidden: true }, t("conversation.receipt"));
|
|
55
|
+
const older = h("button.cv-link", { type: "button", hidden: true }, t("conversation.older"));
|
|
56
|
+
const latest = h("button.cv-jump", { type: "button", hidden: true, "aria-label": t("conversation.jump"), title: t("conversation.latest") }, icon("down"));
|
|
57
|
+
const current = h("button.cv-link", { type: "button", hidden: true }, t("conversation.current"));
|
|
58
|
+
const moreSessions = h("button.cv-link", { type: "button", hidden: true }, t("conversation.moreSessions"));
|
|
59
|
+
const writerChip = h("span.cv-chip.tone-warn", { hidden: true }, icon("alert"), h("span", null, t("conversation.terminalWriter")));
|
|
53
60
|
const file = h("input", { type: "file", "aria-label": t("materials.attach") });
|
|
54
61
|
const materialList = h("div.row-stack");
|
|
55
|
-
const materialAlert = h("p.
|
|
56
|
-
const
|
|
57
|
-
h("
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
h("
|
|
62
|
-
h("div.
|
|
63
|
-
h("
|
|
64
|
-
|
|
65
|
-
h("
|
|
62
|
+
const materialAlert = h("p.cv-note", { role: "alert" });
|
|
63
|
+
const attachMenu = h("details.cv-pop.cv-attach", null,
|
|
64
|
+
h("summary.cv-iconbtn", { "aria-label": t("materials.attach"), title: t("materials.attach") }, icon("plus")),
|
|
65
|
+
h("div.cv-popbody", null, h("p.cv-pop-title", null, t("materials.attach")), file,
|
|
66
|
+
h("p.cv-note", null, t("materials.boundary"))));
|
|
67
|
+
const menu = h("details.cv-pop.cv-menu", null,
|
|
68
|
+
h("summary.cv-iconbtn", { "aria-label": t("conversation.more"), title: t("conversation.more") }, icon("dots")),
|
|
69
|
+
h("div.cv-popbody", null,
|
|
70
|
+
h("label.cv-field", null, h("span", null, t("conversation.defaultMode")), defaultMode),
|
|
71
|
+
moreSessions, identity,
|
|
72
|
+
h("details.cv-sub", null, h("summary", null, t("conversation.receiptDetails")), receiptDetail),
|
|
73
|
+
h("p.cv-note", null, t("conversation.boundary"))));
|
|
74
|
+
const pops = [menu, attachMenu, modelMenu];
|
|
75
|
+
pops.forEach(function (pop) {
|
|
76
|
+
pop.addEventListener("toggle", function () {
|
|
77
|
+
if (!pop.open) return;
|
|
78
|
+
pops.forEach(function (other) { if (other !== pop) other.open = false; });
|
|
79
|
+
if (pop === modelMenu && !modelCatalog && !modelLoad.disabled) loadModels();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
host.addEventListener("click", function (event) {
|
|
83
|
+
pops.forEach(function (pop) { if (pop.open && !pop.contains(event.target)) pop.open = false; });
|
|
84
|
+
});
|
|
85
|
+
host.addEventListener("keydown", function (event) {
|
|
86
|
+
if (event.key === "Escape") pops.forEach(function (pop) { pop.open = false; });
|
|
87
|
+
});
|
|
88
|
+
feed.addEventListener("scroll", function () {
|
|
89
|
+
syncJump();
|
|
90
|
+
// Reaching the top loads the next older page in place; the view keeps its position.
|
|
91
|
+
if (feed.scrollTop < 120 && next && historyOk && !busy) refresh(true);
|
|
92
|
+
});
|
|
93
|
+
host.append(
|
|
94
|
+
h("div.cv-head", null, h("div.cv-picker", null, select, current), menu), status,
|
|
95
|
+
h("div.cv-older", null, older),
|
|
96
|
+
h("div.cv-stage", null, feed, latest),
|
|
97
|
+
questions, requests,
|
|
98
|
+
h("div.cv-composer", null, slash,
|
|
99
|
+
h("div.cv-box", null, materialList, materialAlert, text,
|
|
100
|
+
h("div.cv-tools", null, attachMenu, writerChip, h("span.spacer"), modelMenu, stop, send)),
|
|
101
|
+
h("div.cv-hint", null, draftStatus, literal, check, receipt)));
|
|
66
102
|
|
|
67
103
|
function key() { return "yui.conversation." + JSON.stringify(owner) + "." + selected; }
|
|
68
104
|
function saved(suffix, value, expectedRequestId) {
|
|
@@ -98,6 +134,9 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
98
134
|
Object.entries(extra || {}).forEach(function (entry) { if (entry[1] != null) q.set(entry[0], String(entry[1])); });
|
|
99
135
|
return "/api/conversation?" + q;
|
|
100
136
|
}
|
|
137
|
+
function syncJump() {
|
|
138
|
+
latest.hidden = !((selected && !historyOk) || feed.scrollHeight - feed.clientHeight - feed.scrollTop > 80);
|
|
139
|
+
}
|
|
101
140
|
function controls() {
|
|
102
141
|
if (owner && selected) {
|
|
103
142
|
try { pending = JSON.parse(saved(".pending") || "null"); }
|
|
@@ -112,12 +151,18 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
112
151
|
stop.disabled = busy || recoveryUnavailable || !!pending || !writable || !turn || turn.status !== "accepted" || !turn.nativeTurnId;
|
|
113
152
|
check.disabled = busy || !pending;
|
|
114
153
|
older.disabled = busy || !next;
|
|
154
|
+
older.hidden = !next;
|
|
155
|
+
check.hidden = !pending;
|
|
156
|
+
stop.hidden = !turn || turn.status !== "accepted";
|
|
157
|
+
current.hidden = !facts || !selected || selected === facts.currentSessionId;
|
|
158
|
+
writerChip.hidden = !(facts && facts.terminalWriter);
|
|
115
159
|
literal.disabled = send.disabled;
|
|
160
|
+
syncJump();
|
|
116
161
|
modelLoad.disabled = busy || !active || active.adapterId !== "codex";
|
|
117
162
|
modelApply.disabled = busy || !!pending || !!modelPending || !writable || !modelSelect.value
|
|
118
163
|
|| !modelCatalog || modelCatalog.source === "fallback" || !!modelCatalog.failure
|
|
119
164
|
|| turn && ["submitting", "accepted", "delivery-unknown"].includes(turn.status);
|
|
120
|
-
|
|
165
|
+
attachMenu.hidden = !owner || owner.scope !== "task";
|
|
121
166
|
file.disabled = busy || !!pending || !writable || materials.length >= 8;
|
|
122
167
|
materialList.querySelectorAll("button").forEach(function (control) { control.disabled = busy || !!pending; });
|
|
123
168
|
}
|
|
@@ -135,6 +180,8 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
135
180
|
&& observation.axes.find(function (a) { return a.key === "model"; });
|
|
136
181
|
const value = axis && axis.current.status === "observed" ? axis.current.value : null;
|
|
137
182
|
const session = facts && facts.sessions.find(function (s) { return s.nativeSessionId === selected; });
|
|
183
|
+
confirmedModel = value;
|
|
184
|
+
modelLabel.textContent = value || (session && session.launchModel) || t("conversation.model");
|
|
138
185
|
modelStatus.textContent = t("conversation.launchModel") + ": " + (session && session.launchModel || "—")
|
|
139
186
|
+ " · " + t("conversation.confirmedModel") + ": " + (value || t("conversation.unknown"))
|
|
140
187
|
+ (value ? " · " + observation.observedAt : "");
|
|
@@ -146,6 +193,14 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
146
193
|
}
|
|
147
194
|
lastModelObservation = value ? observation.observedAt : null;
|
|
148
195
|
}
|
|
196
|
+
function pickModel(value) {
|
|
197
|
+
modelSelect.value = value;
|
|
198
|
+
modelRowMap.forEach(function (row, key) {
|
|
199
|
+
row.className = "cv-option" + (key === value ? " is-selected" : "");
|
|
200
|
+
row.ariaSelected = key === value ? "true" : "false";
|
|
201
|
+
});
|
|
202
|
+
controls();
|
|
203
|
+
}
|
|
149
204
|
async function loadModels() {
|
|
150
205
|
if (modelLoad.disabled) return;
|
|
151
206
|
const own = generation;
|
|
@@ -153,10 +208,15 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
153
208
|
try {
|
|
154
209
|
const result = await requestJson(url({ session: selected }).replace("/api/conversation?", "/api/conversation/model?"));
|
|
155
210
|
if (own !== generation) return;
|
|
156
|
-
modelCatalog = result; clear(modelSelect);
|
|
211
|
+
modelCatalog = result; clear(modelSelect); clear(modelRows); modelRowMap = new Map();
|
|
157
212
|
(result.catalog.models || []).forEach(function (m) {
|
|
158
213
|
modelSelect.append(h("option", { value: m.value }, m.label + " · " + m.value));
|
|
214
|
+
const row = h("button.cv-option", { type: "button", role: "option" },
|
|
215
|
+
h("span.cv-option-name", null, m.label), h("span.cv-option-id", null, m.value), icon("check"));
|
|
216
|
+
row.addEventListener("click", function () { pickModel(m.value); });
|
|
217
|
+
modelRowMap.set(m.value, row); modelRows.append(row);
|
|
159
218
|
});
|
|
219
|
+
pickModel(modelRowMap.has(confirmedModel) ? confirmedModel : "");
|
|
160
220
|
modelResult.textContent = result.source + " · " + (result.fetchedAt || result.attemptedAt)
|
|
161
221
|
+ (result.failure ? " · " + result.failure.message : "");
|
|
162
222
|
} catch (error) { if (own === generation) modelResult.textContent = error.message; }
|
|
@@ -189,7 +249,7 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
189
249
|
literal.hidden = !value.startsWith("/");
|
|
190
250
|
if (!value.startsWith("/") || /\s/.test(value)) return;
|
|
191
251
|
commands.filter(function (name) { return ("/" + name).startsWith(value); }).forEach(function (name) {
|
|
192
|
-
const button = h("button.
|
|
252
|
+
const button = h("button.cv-cmd", { type: "button" }, "/" + name + " · " + t("conversation.command." + name));
|
|
193
253
|
button.addEventListener("click", function () {
|
|
194
254
|
text.value = "/" + name; saved(".draft", text.value); drawCommands(); text.focus?.();
|
|
195
255
|
});
|
|
@@ -204,9 +264,9 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
204
264
|
else submit(true);
|
|
205
265
|
return;
|
|
206
266
|
}
|
|
207
|
-
if (name === "model") {
|
|
267
|
+
if (name === "model") { modelMenu.open = true; loadModels(); }
|
|
208
268
|
else if (name === "status") refresh();
|
|
209
|
-
else if (name === "latest")
|
|
269
|
+
else if (name === "latest") refresh();
|
|
210
270
|
receipt.textContent = name === "help" ? t("conversation.commandHelp") : t("conversation.command." + name);
|
|
211
271
|
text.value = ""; saved(".draft", null); drawCommands();
|
|
212
272
|
}
|
|
@@ -214,10 +274,10 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
214
274
|
function drawMaterials() {
|
|
215
275
|
clear(materialList);
|
|
216
276
|
materials.forEach(function (ref, index) {
|
|
217
|
-
const remove = h("button.
|
|
277
|
+
const remove = h("button.cv-x", { type: "button", "aria-label": t("materials.remove"), title: t("materials.remove") }, icon("close"));
|
|
218
278
|
remove.disabled = busy || !!pending;
|
|
219
279
|
remove.addEventListener("click", function () { materials.splice(index, 1); saveMaterials(); drawMaterials(); controls(); });
|
|
220
|
-
materialList.append(h("div", null, h("span.small", null,
|
|
280
|
+
materialList.append(h("div.cv-file", null, h("span.small", null,
|
|
221
281
|
ref.relativePath + " · " + ref.commit + " · sha256:" + ref.digest), remove));
|
|
222
282
|
});
|
|
223
283
|
}
|
|
@@ -226,8 +286,8 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
226
286
|
if (signature === requestSignature) return;
|
|
227
287
|
requestSignature = signature; clear(requests);
|
|
228
288
|
rows.forEach(function (request) {
|
|
229
|
-
const card = h("section.
|
|
230
|
-
h("p.
|
|
289
|
+
const card = h("section.cv-card", null, h("h3.cv-card-title", null, t("conversation.nativeRequest")),
|
|
290
|
+
h("p.cv-note", null, request.method + " · " + request.turnId));
|
|
231
291
|
const params = request.params, answers = {};
|
|
232
292
|
const pendingKey = ".nativePending." + request.turnId + "." + request.id;
|
|
233
293
|
if (request.method === "item/tool/requestUserInput") {
|
|
@@ -239,13 +299,13 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
239
299
|
field.addEventListener("input", function () { saved(draftKey, field.value); });
|
|
240
300
|
}
|
|
241
301
|
card.append(h("label", null, question.question, field));
|
|
242
|
-
if (question.options) card.append(h("p.
|
|
302
|
+
if (question.options) card.append(h("p.cv-note", null, question.options.map(function (o) { return o.label + ": " + o.description; }).join(" · ")));
|
|
243
303
|
answers[question.id] = field;
|
|
244
304
|
});
|
|
245
305
|
} else {
|
|
246
306
|
card.append(h("p", null, params.reason || params.command || t("conversation.nativeApproval")));
|
|
247
307
|
}
|
|
248
|
-
const outcome = h("p.
|
|
308
|
+
const outcome = h("p.cv-note", { role: "status" });
|
|
249
309
|
const buttons = [];
|
|
250
310
|
function button(label, result) {
|
|
251
311
|
const node = h("button.btn", { type: "button", disabled: !!facts.terminalWriter || !!saved(pendingKey) }, label);
|
|
@@ -278,8 +338,8 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
278
338
|
});
|
|
279
339
|
else if (request.method === "item/permissions/requestApproval") button(t("conversation.decline"), function () { return { permissions: {}, scope: "turn" }; });
|
|
280
340
|
else if (request.method === "mcpServer/elicitation/request") {
|
|
281
|
-
card.append(h("p.
|
|
282
|
-
card.append(h("p.
|
|
341
|
+
card.append(h("p.cv-note", null, request.params.message || ""));
|
|
342
|
+
card.append(h("p.cv-note", null, t("conversation.nativeForm")));
|
|
283
343
|
button(t("conversation.decline"), function () { return { action: "decline", content: null }; });
|
|
284
344
|
}
|
|
285
345
|
else {
|
|
@@ -295,7 +355,7 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
295
355
|
if (signature === questionSignature) return;
|
|
296
356
|
questionSignature = signature; clear(questions);
|
|
297
357
|
const taskId = owner.taskId;
|
|
298
|
-
if (rows.length) questions.append(h("h3", null, t("conversation.leaderQuestions")));
|
|
358
|
+
if (rows.length) questions.append(h("h3.cv-card-title", null, t("conversation.leaderQuestions")));
|
|
299
359
|
rows.forEach(function (input) {
|
|
300
360
|
questions.append(inputCard(input, t, locale(), async function (original, answer, control) {
|
|
301
361
|
const own = generation;
|
|
@@ -313,16 +373,17 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
313
373
|
}
|
|
314
374
|
}));
|
|
315
375
|
});
|
|
316
|
-
if (facts.questionsOmitted) questions.append(h("p.
|
|
376
|
+
if (facts.questionsOmitted) questions.append(h("p.cv-note", null, t("overview.moreInputs")));
|
|
317
377
|
}
|
|
318
378
|
function choose(id) {
|
|
319
379
|
if (selected) saved(".draft", text.value);
|
|
320
|
-
generation++; selected = id;
|
|
380
|
+
generation++; selected = id; next = null; latestItems = []; olderItems = []; historyOk = false;
|
|
321
381
|
text.value = saved(".draft") || "";
|
|
322
382
|
try { pending = JSON.parse(saved(".pending") || "null"); } catch { pending = null; }
|
|
323
383
|
try { materials = JSON.parse(saved(".materials") || "[]"); } catch { materials = []; }
|
|
324
384
|
try { modelPending = JSON.parse(saved(".modelPending") || "null"); } catch { modelPending = null; }
|
|
325
|
-
modelCatalog = null; lastModelObservation = null; clear(modelSelect);
|
|
385
|
+
modelCatalog = null; lastModelObservation = null; clear(modelSelect); clear(modelRows); modelRowMap = new Map(); confirmedModel = null;
|
|
386
|
+
modelLabel.textContent = t("conversation.model"); modelStatus.textContent = ""; modelResult.textContent = ""; receiptDetail.textContent = "";
|
|
326
387
|
drawCommands();
|
|
327
388
|
materialAlert.textContent = "";
|
|
328
389
|
if (incomingMaterials) {
|
|
@@ -346,22 +407,71 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
346
407
|
// Only refresh view state; never repeat a mutation or receipt submission.
|
|
347
408
|
if (own !== generation && timer !== null) refresh();
|
|
348
409
|
}
|
|
410
|
+
const GLYPHS = { commandExecution: "terminal", fileChange: "file", webSearch: "search", collabAgentToolCall: "user" };
|
|
411
|
+
function itemKey(item) { return JSON.stringify([item.turnId, item.id]); }
|
|
412
|
+
function itemMeta(item) {
|
|
413
|
+
return (item.source === "yui-input" ? t("conversation.linkedInput") + " " + item.messageId : item.kind)
|
|
414
|
+
+ " · " + item.turnId + (item.status ? " · " + item.status : "");
|
|
415
|
+
}
|
|
416
|
+
function activityRow(item) {
|
|
417
|
+
const parts = item.text.split(" · ");
|
|
418
|
+
return h("details.cv-activity", { title: itemMeta(item) },
|
|
419
|
+
h("summary", null, icon(GLYPHS[parts[0]] || "sparkle"), h("span.cv-act-label", null, t("conversation.activity." + parts[0], parts[0])),
|
|
420
|
+
h("span.cv-act-detail", null, parts.slice(1).join(" · "))),
|
|
421
|
+
h("pre.cv-code", null, item.text));
|
|
422
|
+
}
|
|
423
|
+
// Everything a turn did before its final reply (commentary and tool calls)
|
|
424
|
+
// folds into one collapsed line, like the Codex app's "Worked" row.
|
|
425
|
+
function processGroup(group) {
|
|
426
|
+
const counts = new Map();
|
|
427
|
+
group.forEach(function (item) {
|
|
428
|
+
const kind = item.kind === "activity" ? item.text.split(" · ")[0] : "message";
|
|
429
|
+
counts.set(kind, (counts.get(kind) || 0) + 1);
|
|
430
|
+
});
|
|
431
|
+
const parts = [...counts].filter(function (entry) { return entry[0] !== "message"; }).map(function (entry) {
|
|
432
|
+
return t("conversation.activity." + entry[0], entry[0]) + (entry[1] > 1 ? " ×" + entry[1] : "");
|
|
433
|
+
});
|
|
434
|
+
const label = t("conversation.worked") + (parts.length ? " · " + parts.join(" · ") : "");
|
|
435
|
+
return h("details.cv-activity.cv-group", { title: group.length + " · " + itemMeta(group[0]) },
|
|
436
|
+
h("summary", null, h("span.cv-act-label", null, label), icon("down", "cv-chevron")),
|
|
437
|
+
h("div.cv-group-body", null, ...group.map(function (item) {
|
|
438
|
+
return item.kind === "activity" ? activityRow(item) : h("div.cv-step", null, richText(null, item.text, t, { threshold: 1800 }));
|
|
439
|
+
})));
|
|
440
|
+
}
|
|
441
|
+
function messageNode(item) {
|
|
442
|
+
const meta = itemMeta(item);
|
|
443
|
+
if (item.kind === "user") {
|
|
444
|
+
return h("article.cv-msg.cv-user", { title: meta },
|
|
445
|
+
item.source === "yui-input" ? h("span.cv-tag", null, t("conversation.linkedInput") + " " + item.messageId) : null,
|
|
446
|
+
h("div.cv-bubble", null, richText(null, item.text, t, { threshold: 1800 })),
|
|
447
|
+
item.truncated ? h("p.cv-note", null, t("conversation.truncated")) : null);
|
|
448
|
+
}
|
|
449
|
+
const copy = h("button.cv-iconbtn", { type: "button", "aria-label": t("conversation.copy"), title: t("conversation.copy") }, icon("copy"));
|
|
450
|
+
copy.addEventListener("click", function () {
|
|
451
|
+
navigator.clipboard.writeText(item.text).then(function () { copy.title = t("conversation.copied"); });
|
|
452
|
+
});
|
|
453
|
+
return h("article.cv-msg.cv-assistant", { title: meta },
|
|
454
|
+
richText(null, item.text, t, { threshold: 1800 }),
|
|
455
|
+
item.truncated ? h("p.cv-note", null, t("conversation.truncated")) : null,
|
|
456
|
+
h("div.cv-actions", null, copy));
|
|
457
|
+
}
|
|
458
|
+
function allItems() {
|
|
459
|
+
const seen = new Set();
|
|
460
|
+
return latestItems.concat(olderItems).filter(function (item) {
|
|
461
|
+
const k = itemKey(item);
|
|
462
|
+
if (seen.has(k)) return false;
|
|
463
|
+
seen.add(k); return true;
|
|
464
|
+
});
|
|
465
|
+
}
|
|
349
466
|
function renderItems(items) {
|
|
350
467
|
const nodes = [], updated = new Map();
|
|
351
|
-
|
|
352
|
-
const key = JSON.stringify([item.turnId, item.id]), signature = JSON.stringify(item);
|
|
468
|
+
function place(key, signature, make, keepOpen) {
|
|
353
469
|
const previous = rendered.get(key);
|
|
354
470
|
let node = previous && previous.signature === signature ? previous.node : null;
|
|
355
471
|
if (!node) {
|
|
356
|
-
|
|
357
|
-
const meta = (item.source === "yui-input" ? t("conversation.linkedInput") + " " + item.messageId : item.kind)
|
|
358
|
-
+ " · " + item.turnId + (item.status ? " · " + item.status : "");
|
|
359
|
-
node = item.kind === "activity" ? h("details", null, h("summary", null, meta), content)
|
|
360
|
-
: h("article.msg.from-" + (item.kind === "user" ? "user" : "role"), null,
|
|
361
|
-
h("div.msg-main", null, h("header.msg-head", null, meta), h("div.msg-bubble", null, content),
|
|
362
|
-
item.truncated ? h("p.feed-note", null, t("conversation.truncated")) : null));
|
|
472
|
+
node = make();
|
|
363
473
|
if (previous) {
|
|
364
|
-
if (
|
|
474
|
+
if (keepOpen) node.open = previous.node.open;
|
|
365
475
|
const oldProse = previous.node.querySelectorAll(".prose-block");
|
|
366
476
|
node.querySelectorAll(".prose-block").forEach(function (prose, index) {
|
|
367
477
|
if (oldProse[index] && !oldProse[index].classList.contains("is-collapsed")
|
|
@@ -370,9 +480,33 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
370
480
|
}
|
|
371
481
|
}
|
|
372
482
|
updated.set(key, { signature: signature, node: node }); nodes.push(node);
|
|
373
|
-
}
|
|
483
|
+
}
|
|
484
|
+
const chronological = items.slice().reverse();
|
|
485
|
+
let index = 0;
|
|
486
|
+
while (index < chronological.length) {
|
|
487
|
+
let end = index;
|
|
488
|
+
while (end < chronological.length && chronological[end].turnId === chronological[index].turnId) end++;
|
|
489
|
+
const segment = chronological.slice(index, end);
|
|
490
|
+
index = end;
|
|
491
|
+
const last = segment[segment.length - 1];
|
|
492
|
+
const final = last.kind === "assistant" ? last : null;
|
|
493
|
+
const process = segment.filter(function (item) { return item.kind !== "user" && item !== final; });
|
|
494
|
+
let emitted = false;
|
|
495
|
+
segment.forEach(function (item) {
|
|
496
|
+
if (item.kind === "user" || item === final) {
|
|
497
|
+
place(itemKey(item), JSON.stringify(item), function () { return messageNode(item); }, false);
|
|
498
|
+
} else if (!emitted) {
|
|
499
|
+
emitted = true;
|
|
500
|
+
if (process.length === 1 && process[0].kind === "activity") {
|
|
501
|
+
place(itemKey(process[0]), JSON.stringify(process[0]), function () { return activityRow(process[0]); }, true);
|
|
502
|
+
} else {
|
|
503
|
+
place("turn:" + segment[0].turnId, JSON.stringify(process), function () { return processGroup(process); }, true);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
}
|
|
374
508
|
rendered = updated;
|
|
375
|
-
if (!nodes.length) nodes.push(h("p.
|
|
509
|
+
if (!nodes.length) nodes.push(h("p.cv-note", null, t("conversation.empty")));
|
|
376
510
|
if (feed.children.length !== nodes.length || nodes.some(function (n, i) { return feed.children[i] !== n; })) {
|
|
377
511
|
feed.replaceChildren(...nodes);
|
|
378
512
|
}
|
|
@@ -387,11 +521,22 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
387
521
|
select.value = selected || "";
|
|
388
522
|
moreSessions.hidden = !facts.nextOffset;
|
|
389
523
|
}
|
|
390
|
-
async function refresh() {
|
|
524
|
+
async function refresh(older) {
|
|
391
525
|
if (!owner || busy || host.hidden || host.closest("[hidden]") || document.hidden) return;
|
|
526
|
+
if (older && (!next || !selected || !facts || !historyOk)) return;
|
|
392
527
|
busy = true; controls();
|
|
393
528
|
let own = generation;
|
|
394
529
|
try {
|
|
530
|
+
if (older) {
|
|
531
|
+
const earlier = await requestJson(url({ session: selected, cursor: next }));
|
|
532
|
+
if (own !== generation) return;
|
|
533
|
+
olderItems = olderItems.concat(earlier.items); next = earlier.nextCursor;
|
|
534
|
+
const before = feed.scrollHeight, top = feed.scrollTop;
|
|
535
|
+
renderItems(allItems());
|
|
536
|
+
feed.scrollTop = top + (feed.scrollHeight - before);
|
|
537
|
+
syncJump();
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
395
540
|
const result = await requestJson(url());
|
|
396
541
|
if (own !== generation) return;
|
|
397
542
|
const previous = facts;
|
|
@@ -412,27 +557,30 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
412
557
|
// Preserve a historical choice even if it is outside the newest page.
|
|
413
558
|
drawSessions(facts.sessions, false);
|
|
414
559
|
renderQuestions();
|
|
415
|
-
const page = await requestJson(url({ session: selected
|
|
560
|
+
const page = await requestJson(url({ session: selected }));
|
|
416
561
|
if (own !== generation) return;
|
|
417
|
-
historyOk = true;
|
|
562
|
+
historyOk = true; latestItems = page.items;
|
|
563
|
+
if (!olderItems.length) next = page.nextCursor;
|
|
418
564
|
const turn = selected === facts.currentSessionId ? facts.turn : null;
|
|
419
565
|
let state = page.status === "waiting-user" ? "waiting" : page.status === "active" ? "active"
|
|
420
566
|
: page.status === "systemError" ? "failed" : page.status === "idle" ? "waiting" : "unknown";
|
|
421
567
|
const session = facts.sessions.find(function (s) { return s.nativeSessionId === selected; });
|
|
422
568
|
if (session && session.status === "ended") state = session.endReason === "failed" ? "failed" : "ended";
|
|
423
569
|
if (turn && turn.status === "delivery-unknown") state = "unknown";
|
|
424
|
-
status.textContent = t("conversation." + state)
|
|
425
|
-
|
|
570
|
+
status.textContent = t("conversation." + state);
|
|
571
|
+
status.title = page.observedAt + " · " + t(facts.terminalWriter ? "conversation.terminalWriter" : "conversation.structuredWriter");
|
|
572
|
+
status.className = "cv-status is-" + state;
|
|
573
|
+
if (page.live === "unavailable") status.textContent += " · " + t("conversation.liveUnavailable");
|
|
426
574
|
if (page.linkedInputsOmitted) status.textContent += " · " + t("conversation.linkedOmitted");
|
|
427
575
|
if (page.nativeRequestsOmitted) status.textContent += " · " + t("conversation.nativeOmitted");
|
|
428
|
-
status.textContent += " · " + t(facts.terminalWriter ? "conversation.terminalWriter" : "conversation.structuredWriter");
|
|
429
576
|
renderRequests(page.nativeRequests || []);
|
|
430
577
|
showModel(page.runConfiguration);
|
|
431
578
|
identity.textContent = (owner.taskId || "Global") + " / " + owner.roleName + " / " + selected
|
|
432
579
|
+ (turn ? " / " + (turn.nativeTurnId || turn.attemptId) + (turn.runId ? " / " + turn.runId : "") : "");
|
|
433
580
|
const scroll = feed.scrollTop, atEnd = feed.scrollHeight - feed.clientHeight - scroll < 40;
|
|
434
|
-
renderItems(
|
|
581
|
+
renderItems(allItems());
|
|
435
582
|
feed.scrollTop = atEnd ? feed.scrollHeight : scroll;
|
|
583
|
+
syncJump();
|
|
436
584
|
} catch (error) {
|
|
437
585
|
if (own === generation) { historyOk = false; status.textContent = t("conversation.disconnected") + " · " + error.message; }
|
|
438
586
|
} finally { finish(own); }
|
|
@@ -542,8 +690,10 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
542
690
|
});
|
|
543
691
|
select.addEventListener("change", function () { choose(select.value); refresh(); });
|
|
544
692
|
current.addEventListener("click", function () { if (facts) { choose(facts.currentSessionId); refresh(); } });
|
|
545
|
-
older.addEventListener("click", function () {
|
|
546
|
-
latest.addEventListener("click", function () {
|
|
693
|
+
older.addEventListener("click", function () { refresh(true); });
|
|
694
|
+
latest.addEventListener("click", function () {
|
|
695
|
+
refresh().then(function () { feed.scrollTop = feed.scrollHeight; syncJump(); });
|
|
696
|
+
});
|
|
547
697
|
send.addEventListener("click", function () { submit(false); });
|
|
548
698
|
stop.addEventListener("click", function () { submit(true); });
|
|
549
699
|
check.addEventListener("click", inspectReceipt);
|
|
@@ -584,7 +734,7 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
584
734
|
return { ...owner, ...(selected ? { nativeSessionId: selected } : {}) };
|
|
585
735
|
},
|
|
586
736
|
reconnect: function () {
|
|
587
|
-
if (!timer) timer = window.setInterval(function () {
|
|
737
|
+
if (!timer) timer = window.setInterval(function () { refresh(); }, 2000);
|
|
588
738
|
refresh();
|
|
589
739
|
},
|
|
590
740
|
open: function (target, options) {
|
|
@@ -593,9 +743,10 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
593
743
|
if (JSON.stringify(target) !== JSON.stringify(owner) || options && options.current) {
|
|
594
744
|
if (selected) saved(".draft", text.value);
|
|
595
745
|
generation++; owner = target; selected = null; facts = null; pending = null; clear(select); clear(feed);
|
|
596
|
-
|
|
746
|
+
next = null; latestItems = []; olderItems = []; historyOk = false; rendered.clear();
|
|
597
747
|
text.value = ""; receipt.textContent = ""; identity.textContent = ""; controls();
|
|
598
|
-
modelPending = null; modelCatalog = null; clear(modelSelect);
|
|
748
|
+
modelPending = null; modelCatalog = null; clear(modelSelect); clear(modelRows); modelRowMap = new Map(); confirmedModel = null;
|
|
749
|
+
modelLabel.textContent = t("conversation.model"); modelStatus.textContent = "";
|
|
599
750
|
modelResult.textContent = "";
|
|
600
751
|
receiptDetail.textContent = ""; draftStatus.textContent = ""; drawCommands();
|
|
601
752
|
materials = []; incomingMaterials = null; clear(materialList); materialAlert.textContent = "";
|
|
@@ -603,7 +754,7 @@ export function createConversationController(host, t, locale = function () { ret
|
|
|
603
754
|
clear(questions); questionSignature = "";
|
|
604
755
|
}
|
|
605
756
|
if (options && options.materials) incomingMaterials = options.materials;
|
|
606
|
-
if (!timer) timer = window.setInterval(function () {
|
|
757
|
+
if (!timer) timer = window.setInterval(function () { refresh(); }, 2000);
|
|
607
758
|
refresh();
|
|
608
759
|
},
|
|
609
760
|
close: function () { if (timer) window.clearInterval(timer); timer = null; generation++; }
|