@zq-silk/yui 1.0.0 → 1.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/README.md +27 -9
- package/dist/cli/commandCatalog.js +7 -5
- package/dist/cli/invocationAuthority.js +4 -0
- package/dist/cli.js +130 -5
- package/dist/commands/releaseCommands.js +33 -24
- package/dist/commands/taskCommands.js +18 -28
- package/dist/commands/taskFactCommands.js +6 -2
- package/dist/controller/clientRuntime.js +7 -2
- package/dist/controller/domainIdentity.js +7 -7
- package/dist/controller/handoverCandidate.js +2 -5
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/resourceCleanupLinux.js +35 -19
- package/dist/controller/resourceInventoryLinux.js +104 -7
- package/dist/controller/runtime.js +4 -13
- package/dist/controller/runtimeLaunchCoordinator.js +2 -4
- package/dist/controller/sessionOwnerReconciliation.js +40 -16
- package/dist/core/controllerClient.js +44 -16
- package/dist/core/controllerEndpoint.js +4 -3
- package/dist/core/controllerProcessIdentity.js +54 -0
- package/dist/core/controllerServer.js +16 -11
- package/dist/core/fileLockOwner.js +53 -12
- package/dist/doctor/doctor.js +230 -12
- package/dist/execution/workItemMainRun.js +1 -1
- package/dist/executor/agentAdapter.js +0 -8
- package/dist/executor/agentExecutor.js +4 -1
- package/dist/executor/fileRoleLaunchPlanner.js +15 -25
- package/dist/message/messageContinuation.js +1 -1
- package/dist/release/releaseHandover.js +77 -25
- package/dist/release/runtimeRelease.js +7 -7
- package/dist/repository/gitWorkspace.js +9 -8
- package/dist/repository/taskWorkspacePreparer.js +15 -57
- package/dist/resources/liveReferences.js +64 -7
- package/dist/runtime/agentEndpointIdentity.js +4 -1
- package/dist/runtime/agentHost.js +13 -9
- package/dist/runtime/codexInteractiveHost.js +1 -1
- package/dist/runtime/codexSharedDaemon.js +211 -0
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/jsonLineChannel.js +6 -1
- package/dist/runtime/localStartup.js +65 -0
- package/dist/runtime/native/darwin-arm64/claude-process-owner +0 -0
- package/dist/runtime/native/darwin-arm64/process-identity +0 -0
- package/dist/runtime/native/darwin-x64/claude-process-owner +0 -0
- package/dist/runtime/native/darwin-x64/process-identity +0 -0
- package/dist/runtime/{claude-process-owner → native/linux-x64/claude-process-owner} +0 -0
- package/dist/runtime/nativeExecutable.js +10 -0
- package/dist/runtime/runtimeCoherence.js +10 -3
- package/dist/runtime/sessionOwnerIdentity.js +19 -0
- package/dist/runtime/sessionTerminationGuard.js +10 -3
- package/dist/runtime/sessionTitle.js +15 -0
- package/dist/runtime/structuredProviderHost.js +94 -29
- package/dist/scheduler/activeRoleRunDelivery.js +2 -11
- package/dist/scheduler/leaderWakeupProcessor.js +1 -2
- package/dist/scheduler/ports.js +1 -8
- package/dist/scheduler/taskExecutionProjection.js +1 -6
- package/dist/storage/sqliteSchema.js +15 -3
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/taskCatalog.js +10 -2
- package/dist/storage/upgrade/taskMainWorkspaceMigration.js +83 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -6
- package/dist/task/task.js +0 -12
- package/dist/tmux/tmuxManager.js +8 -2
- package/dist/tmux/tmuxSocketEndpoint.js +23 -1
- package/dist/web/assets/client/app.js +131 -11
- package/dist/web/assets/client/i18n.js +18 -0
- package/dist/web/assets/client/taskSummary.js +7 -2
- package/dist/web/assets/client/taskSurface.js +54 -5
- package/dist/web/assets/client/view.js +7 -2
- package/dist/web/assets/shell.js +21 -11
- package/dist/web/assets/styles/layout.js +33 -6
- package/dist/web/assets/styles/responsive.js +18 -5
- package/dist/web/assets/styles/tokens.js +1 -1
- package/dist/web/assets/styles/widgets.js +5 -0
- package/docs/managed-turn-and-session-runtime.md +7 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +5 -0
- package/docs/provider-runtime.md +7 -0
- package/docs/provider-runtime.zh-CN.md +5 -0
- package/docs/release-workflow.md +7 -1
- package/docs/task-delivery.md +5 -4
- package/docs/testing/verification-levels.md +9 -7
- package/i18n/README.zh-CN.md +19 -6
- package/package.json +5 -6
- package/skills/yui-leader/SKILL.md +8 -0
- package/skills/yui-leader/references/execution.md +4 -1
- package/skills/yui-operator/SKILL.md +35 -15
|
@@ -30,6 +30,12 @@ const elements = {
|
|
|
30
30
|
topbar: document.querySelector(".topbar"),
|
|
31
31
|
detailBack: document.querySelector("#detail-back"),
|
|
32
32
|
detailTabs: document.querySelector("#detail-tabs"),
|
|
33
|
+
conversationToggle: document.querySelector("#conversation-toggle"),
|
|
34
|
+
conversationPanel: document.querySelector("#conversation-panel"),
|
|
35
|
+
conversationContent: document.querySelector("#conversation-content"),
|
|
36
|
+
conversationClose: document.querySelector("#conversation-close"),
|
|
37
|
+
conversationSwap: document.querySelector("#conversation-swap"),
|
|
38
|
+
paneDivider: document.querySelector("#pane-divider"),
|
|
33
39
|
pageTitle: document.querySelector("#page-title"),
|
|
34
40
|
toast: document.querySelector("#toast"),
|
|
35
41
|
lastSync: document.querySelector("#last-sync"),
|
|
@@ -68,6 +74,10 @@ const VALID_ATTENTION = ["openInputs", "pendingOperations", "unknownOperations",
|
|
|
68
74
|
let terminalSession = null;
|
|
69
75
|
let terminalStateKey = "terminal.closed";
|
|
70
76
|
const submittedRequests = new Set();
|
|
77
|
+
let conversationVisible = true;
|
|
78
|
+
let paneWidth = Number(localStorage.getItem("yui.conversation.width")) || 400;
|
|
79
|
+
const paneSide = localStorage.getItem("yui.conversation.side") === "left" ? "left" : "right";
|
|
80
|
+
document.body.classList.toggle("pane-left", paneSide === "left");
|
|
71
81
|
|
|
72
82
|
const i18n = createI18n(elements.locale);
|
|
73
83
|
createThemeController(elements.theme);
|
|
@@ -146,6 +156,7 @@ function syncUrlFromState(options) {
|
|
|
146
156
|
|
|
147
157
|
function detailActions() {
|
|
148
158
|
return {
|
|
159
|
+
conversationHost: elements.conversationContent,
|
|
149
160
|
answerInput: answerInput,
|
|
150
161
|
openTerminal: openTerminal,
|
|
151
162
|
inspect: inspectRecord,
|
|
@@ -190,6 +201,33 @@ function setDetailActive(active) {
|
|
|
190
201
|
elements.pageTitle.dataset.i18n = "page.title";
|
|
191
202
|
}
|
|
192
203
|
}
|
|
204
|
+
updateConversationLayout();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function maxPaneWidth() {
|
|
208
|
+
return Math.max(280, Math.min(640,
|
|
209
|
+
window.innerWidth - elements.tasks.closest(".sidebar").offsetWidth - 460 - 8));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function setPaneWidth(width, persist, keepPreference) {
|
|
213
|
+
const effectiveWidth = Math.max(280, Math.min(maxPaneWidth(), width));
|
|
214
|
+
if (!keepPreference) paneWidth = effectiveWidth;
|
|
215
|
+
document.documentElement.style.setProperty("--pane-w", effectiveWidth + "px");
|
|
216
|
+
elements.paneDivider.setAttribute("aria-valuemax", String(maxPaneWidth()));
|
|
217
|
+
elements.paneDivider.setAttribute("aria-valuenow", String(effectiveWidth));
|
|
218
|
+
if (persist) localStorage.setItem("yui.conversation.width", String(effectiveWidth));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function updateConversationLayout() {
|
|
222
|
+
const visible = !!state.selected && conversationVisible && !terminalPanelOpen();
|
|
223
|
+
document.body.classList.toggle("conversation-active", visible);
|
|
224
|
+
elements.conversationPanel.hidden = !visible;
|
|
225
|
+
elements.paneDivider.hidden = !visible;
|
|
226
|
+
elements.conversationToggle.hidden = !state.selected || terminalPanelOpen();
|
|
227
|
+
elements.conversationToggle.setAttribute("aria-expanded", String(visible));
|
|
228
|
+
elements.conversationToggle.textContent = i18n.t(visible ? "conversation.hide" : "conversation.show");
|
|
229
|
+
elements.conversationToggle.setAttribute("aria-label", elements.conversationToggle.textContent);
|
|
230
|
+
setPaneWidth(paneWidth, false, true);
|
|
193
231
|
}
|
|
194
232
|
|
|
195
233
|
function showOverview() {
|
|
@@ -237,12 +275,17 @@ function renderCurrentDetail(force) {
|
|
|
237
275
|
if (elements.detail.dataset.taskId === state.detail.task.id
|
|
238
276
|
&& (elements.detail.querySelector('[data-unsent="true"]')
|
|
239
277
|
|| elements.detail.querySelector('[data-reading="true"]')
|
|
240
|
-
|| elements.detail.contains(document.activeElement)
|
|
278
|
+
|| elements.detail.contains(document.activeElement)
|
|
279
|
+
|| elements.conversationContent.querySelector('[data-unsent="true"]')
|
|
280
|
+
|| elements.conversationContent.contains(document.activeElement))) return;
|
|
241
281
|
const key = i18n.getLocale() + "|" + state.detailKey
|
|
242
282
|
+ "|" + runtimeSignatureOf(state.detail);
|
|
243
283
|
if (!force && key === renderedDetailKey) return;
|
|
244
284
|
const openSections = Array.from(elements.detail.querySelectorAll("details[data-view-key][open]"))
|
|
245
285
|
.map(element => element.dataset.viewKey);
|
|
286
|
+
const oldFeed = elements.conversationContent.querySelector(".conversation-feed");
|
|
287
|
+
const feedScroll = oldFeed ? oldFeed.scrollTop : 0;
|
|
288
|
+
const atFeedEnd = oldFeed && oldFeed.scrollHeight - oldFeed.clientHeight - feedScroll < 32;
|
|
246
289
|
renderTaskDetail(
|
|
247
290
|
elements.detail,
|
|
248
291
|
state.detail,
|
|
@@ -253,6 +296,8 @@ function renderCurrentDetail(force) {
|
|
|
253
296
|
elements.detail.querySelectorAll("details[data-view-key]").forEach(element => {
|
|
254
297
|
element.open = openSections.includes(element.dataset.viewKey);
|
|
255
298
|
});
|
|
299
|
+
const newFeed = elements.conversationContent.querySelector(".conversation-feed");
|
|
300
|
+
if (newFeed) newFeed.scrollTop = atFeedEnd ? newFeed.scrollHeight : feedScroll;
|
|
256
301
|
renderedDetailKey = key;
|
|
257
302
|
} else if (!state.selected) {
|
|
258
303
|
renderedDetailKey = null;
|
|
@@ -342,6 +387,7 @@ function clearSelection() {
|
|
|
342
387
|
state.selected = null;
|
|
343
388
|
state.detail = null;
|
|
344
389
|
setDetailActive(false);
|
|
390
|
+
elements.conversationContent.replaceChildren();
|
|
345
391
|
syncUrlFromState({ replace: !urlTaskId() });
|
|
346
392
|
showOverview();
|
|
347
393
|
const savedTaskScroll = elements.tasks.scrollTop;
|
|
@@ -386,12 +432,16 @@ async function submitMutation(key, path, body) {
|
|
|
386
432
|
}
|
|
387
433
|
}
|
|
388
434
|
|
|
389
|
-
async function loadTaskDetail(taskId,
|
|
390
|
-
|
|
435
|
+
async function loadTaskDetail(taskId, navigate) {
|
|
436
|
+
// navigate marks an explicit user navigation (task selection, deep link,
|
|
437
|
+
// back/forward). Only a navigation may move the viewport; the 5s background
|
|
438
|
+
// refresh (navigate=false) preserves the current scroll, so a quiet tick
|
|
439
|
+
// never yanks the page back to the URL section.
|
|
440
|
+
if (navigate) {
|
|
391
441
|
renderLoading(elements.detail, i18n.t, "loading.detail");
|
|
392
442
|
elements.mainCol.scrollTop = 0;
|
|
393
443
|
}
|
|
394
|
-
const savedScrollTop =
|
|
444
|
+
const savedScrollTop = elements.mainCol.scrollTop;
|
|
395
445
|
const base = "/api/tasks/" + encodeURIComponent(taskId);
|
|
396
446
|
// Rendering consumes the current snapshot, not event pages. Reconnect uses
|
|
397
447
|
// this same read; the independent delta API retains its fixed-bound contract.
|
|
@@ -428,11 +478,17 @@ async function loadTaskDetail(taskId, showLoading) {
|
|
|
428
478
|
// Reveal the tab bar before measuring/scroll so anchors land correctly.
|
|
429
479
|
setDetailActive(true);
|
|
430
480
|
updateStickyOffsets();
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (
|
|
435
|
-
|
|
481
|
+
// Only an explicit navigation may reposition the viewport. On navigation a
|
|
482
|
+
// section in the URL wins over the top; a background refresh keeps the reader
|
|
483
|
+
// where they are so the page does not jump on every poll.
|
|
484
|
+
if (navigate) {
|
|
485
|
+
const section = readQuery().get("section");
|
|
486
|
+
const anchor = section ? elements.detail.querySelector("#detail-" + section) : null;
|
|
487
|
+
if (anchor) {
|
|
488
|
+
anchor.scrollIntoView({ block: "start" });
|
|
489
|
+
} else {
|
|
490
|
+
elements.mainCol.scrollTop = savedScrollTop;
|
|
491
|
+
}
|
|
436
492
|
} else {
|
|
437
493
|
elements.mainCol.scrollTop = savedScrollTop;
|
|
438
494
|
}
|
|
@@ -477,6 +533,8 @@ async function selectTask(taskId) {
|
|
|
477
533
|
}
|
|
478
534
|
state.selected = taskId;
|
|
479
535
|
state.detail = null;
|
|
536
|
+
conversationVisible = true;
|
|
537
|
+
elements.conversationContent.replaceChildren();
|
|
480
538
|
renderedDetailKey = null;
|
|
481
539
|
// When the selection is driven by the URL (initial load or popstate), the
|
|
482
540
|
// URL already reflects the task id, so replace instead of pushing a
|
|
@@ -496,12 +554,14 @@ async function selectTask(taskId) {
|
|
|
496
554
|
}
|
|
497
555
|
|
|
498
556
|
function canLeaveDetail() {
|
|
499
|
-
return !elements.detail.querySelector('[data-unsent="true"]')
|
|
557
|
+
return !(elements.detail.querySelector('[data-unsent="true"]')
|
|
558
|
+
|| elements.conversationContent.querySelector('[data-unsent="true"]')) || window.confirm(
|
|
500
559
|
i18n.getLocale().startsWith("zh") ? "此任务有未提交或结果尚不确定的输入。仍要离开?"
|
|
501
560
|
: "This Task has unsent input or an unresolved submission. Leave anyway?");
|
|
502
561
|
}
|
|
503
562
|
window.addEventListener("beforeunload", event => {
|
|
504
|
-
if (!elements.detail.querySelector('[data-unsent="true"]')
|
|
563
|
+
if (!elements.detail.querySelector('[data-unsent="true"]')
|
|
564
|
+
&& !elements.conversationContent.querySelector('[data-unsent="true"]')) return;
|
|
505
565
|
event.preventDefault();
|
|
506
566
|
event.returnValue = "";
|
|
507
567
|
});
|
|
@@ -621,6 +681,7 @@ function openTerminalPanel() {
|
|
|
621
681
|
elements.terminalPanel.hidden = false;
|
|
622
682
|
elements.terminalPanel.setAttribute("aria-hidden", "false");
|
|
623
683
|
document.body.classList.add("terminal-active");
|
|
684
|
+
updateConversationLayout();
|
|
624
685
|
}
|
|
625
686
|
|
|
626
687
|
function closeTerminalPanel() {
|
|
@@ -628,6 +689,7 @@ function closeTerminalPanel() {
|
|
|
628
689
|
document.body.classList.remove("terminal-active");
|
|
629
690
|
elements.terminalPanel.setAttribute("aria-hidden", "true");
|
|
630
691
|
elements.terminalPanel.hidden = true;
|
|
692
|
+
updateConversationLayout();
|
|
631
693
|
}
|
|
632
694
|
|
|
633
695
|
function disposeTerminal() {
|
|
@@ -856,6 +918,52 @@ globalForm.addEventListener("submit", async event => {
|
|
|
856
918
|
});
|
|
857
919
|
elements.detailBack.addEventListener("click", clearSelection);
|
|
858
920
|
elements.terminalClose.addEventListener("click", closeTerminalPanel);
|
|
921
|
+
elements.conversationToggle.addEventListener("click", function () {
|
|
922
|
+
conversationVisible = !conversationVisible;
|
|
923
|
+
updateConversationLayout();
|
|
924
|
+
});
|
|
925
|
+
elements.conversationClose.addEventListener("click", function () {
|
|
926
|
+
conversationVisible = false;
|
|
927
|
+
updateConversationLayout();
|
|
928
|
+
elements.conversationToggle.focus();
|
|
929
|
+
});
|
|
930
|
+
elements.conversationSwap.addEventListener("click", function () {
|
|
931
|
+
const left = !document.body.classList.contains("pane-left");
|
|
932
|
+
document.body.classList.toggle("pane-left", left);
|
|
933
|
+
localStorage.setItem("yui.conversation.side", left ? "left" : "right");
|
|
934
|
+
});
|
|
935
|
+
let resizingPane = false;
|
|
936
|
+
elements.paneDivider.addEventListener("pointerdown", function (event) {
|
|
937
|
+
if (event.button !== 0 || elements.paneDivider.hidden || window.innerWidth <= 900) return;
|
|
938
|
+
resizingPane = true;
|
|
939
|
+
elements.paneDivider.setPointerCapture(event.pointerId);
|
|
940
|
+
document.body.classList.add("pane-resizing");
|
|
941
|
+
event.preventDefault();
|
|
942
|
+
});
|
|
943
|
+
elements.paneDivider.addEventListener("pointermove", function (event) {
|
|
944
|
+
if (!resizingPane) return;
|
|
945
|
+
const left = document.body.classList.contains("pane-left");
|
|
946
|
+
const width = left ? event.clientX - elements.tasks.closest(".sidebar").offsetWidth
|
|
947
|
+
: window.innerWidth - event.clientX;
|
|
948
|
+
setPaneWidth(width - 4, false);
|
|
949
|
+
});
|
|
950
|
+
function finishPaneResize() {
|
|
951
|
+
if (!resizingPane) return;
|
|
952
|
+
resizingPane = false;
|
|
953
|
+
document.body.classList.remove("pane-resizing");
|
|
954
|
+
localStorage.setItem("yui.conversation.width", String(paneWidth));
|
|
955
|
+
}
|
|
956
|
+
elements.paneDivider.addEventListener("pointerup", finishPaneResize);
|
|
957
|
+
elements.paneDivider.addEventListener("pointercancel", finishPaneResize);
|
|
958
|
+
elements.paneDivider.addEventListener("keydown", function (event) {
|
|
959
|
+
const left = document.body.classList.contains("pane-left");
|
|
960
|
+
const delta = left ? (event.key === "ArrowRight" ? 20 : event.key === "ArrowLeft" ? -20 : 0)
|
|
961
|
+
: (event.key === "ArrowLeft" ? 20 : event.key === "ArrowRight" ? -20 : 0);
|
|
962
|
+
if (!delta && !["Home", "End"].includes(event.key)) return;
|
|
963
|
+
event.preventDefault();
|
|
964
|
+
setPaneWidth(event.key === "Home" ? 280 : event.key === "End" ? maxPaneWidth() : paneWidth + delta, true);
|
|
965
|
+
});
|
|
966
|
+
window.addEventListener("resize", function () { setPaneWidth(paneWidth, false, true); });
|
|
859
967
|
document.addEventListener("keydown", function (event) {
|
|
860
968
|
if (globalDialog.open) return;
|
|
861
969
|
const active = document.activeElement;
|
|
@@ -864,6 +972,12 @@ document.addEventListener("keydown", function (event) {
|
|
|
864
972
|
closeTerminalPanel();
|
|
865
973
|
return;
|
|
866
974
|
}
|
|
975
|
+
if (event.key === "Escape" && document.body.classList.contains("conversation-active")) {
|
|
976
|
+
conversationVisible = false;
|
|
977
|
+
updateConversationLayout();
|
|
978
|
+
elements.conversationToggle.focus();
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
867
981
|
if (event.key === "Escape" && state.selected) {
|
|
868
982
|
clearSelection();
|
|
869
983
|
return;
|
|
@@ -901,9 +1015,14 @@ function applyStateFromUrl() {
|
|
|
901
1015
|
if (taskId) {
|
|
902
1016
|
selectTask(taskId);
|
|
903
1017
|
} else {
|
|
1018
|
+
if (state.selected && !canLeaveDetail()) {
|
|
1019
|
+
syncUrlFromState({ replace: true });
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
904
1022
|
state.selected = null;
|
|
905
1023
|
state.detail = null;
|
|
906
1024
|
setDetailActive(false);
|
|
1025
|
+
elements.conversationContent.replaceChildren();
|
|
907
1026
|
showOverview();
|
|
908
1027
|
}
|
|
909
1028
|
}
|
|
@@ -916,6 +1035,7 @@ window.addEventListener("popstate", function () {
|
|
|
916
1035
|
i18n.subscribe(function () {
|
|
917
1036
|
renderDynamicContent();
|
|
918
1037
|
if (terminalSession) setTerminalState(terminalStateKey);
|
|
1038
|
+
updateConversationLayout();
|
|
919
1039
|
if (!state.selected && elements.pageTitle) elements.pageTitle.textContent = i18n.t("page.title");
|
|
920
1040
|
});
|
|
921
1041
|
showOverview();
|
|
@@ -34,6 +34,15 @@ const messages = {
|
|
|
34
34
|
"sync.label": "LAST SYNC",
|
|
35
35
|
"actions.openSession": "Open Session",
|
|
36
36
|
"actions.operator": "Operator session",
|
|
37
|
+
"conversation.title": "Task discussion",
|
|
38
|
+
"conversation.source": "Recorded Task messages · not a Session transcript",
|
|
39
|
+
"conversation.show": "Discussion",
|
|
40
|
+
"conversation.hide": "Hide discussion",
|
|
41
|
+
"conversation.swap": "Move discussion to the other side",
|
|
42
|
+
"conversation.resize": "Resize task discussion",
|
|
43
|
+
"tabs.workResults": "Work & results",
|
|
44
|
+
"tabs.executionReviews": "Execution & reviews",
|
|
45
|
+
"tabs.historyMessages": "History & messages",
|
|
37
46
|
"actions.refresh": "Refresh",
|
|
38
47
|
"actions.showLess": "Show less",
|
|
39
48
|
"actions.showMore": "Show more",
|
|
@@ -458,6 +467,15 @@ const messages = {
|
|
|
458
467
|
"sync.label": "最近同步",
|
|
459
468
|
"actions.openSession": "打开会话",
|
|
460
469
|
"actions.operator": "Operator 会话",
|
|
470
|
+
"conversation.title": "任务讨论",
|
|
471
|
+
"conversation.source": "已记录的 Task 消息 · 非 Session 完整记录",
|
|
472
|
+
"conversation.show": "讨论",
|
|
473
|
+
"conversation.hide": "收起讨论",
|
|
474
|
+
"conversation.swap": "将讨论移至另一侧",
|
|
475
|
+
"conversation.resize": "调整讨论栏宽度",
|
|
476
|
+
"tabs.workResults": "工作与结果",
|
|
477
|
+
"tabs.executionReviews": "执行与审阅",
|
|
478
|
+
"tabs.historyMessages": "历史与消息",
|
|
461
479
|
"actions.refresh": "刷新",
|
|
462
480
|
"actions.showLess": "收起",
|
|
463
481
|
"actions.showMore": "展开更多",
|
|
@@ -99,10 +99,15 @@ export function renderTaskSummary(container, data, t, locale, actions, recordCar
|
|
|
99
99
|
const progress = block(say("Task progress", "任务进展"), "progress");
|
|
100
100
|
const briefEntry = entries("task-brief")[0];
|
|
101
101
|
const brief = available("task-brief")[0];
|
|
102
|
+
const working = data.task.status === "draft" || data.task.status === "active";
|
|
102
103
|
if (data.task.completionSummary) progress.append(richText(say("Recorded completion", "已记录完成"), data.task.completionSummary, t));
|
|
103
104
|
if (brief) {
|
|
104
|
-
progress.append(richText(
|
|
105
|
-
|
|
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));
|
|
106
111
|
progress.append(node("small", "muted", say("Brief updated: ", "Brief 更新:") + formatDateTime(brief.updatedAt, locale)));
|
|
107
112
|
} else if (briefEntry) progress.append(recordCard(briefEntry));
|
|
108
113
|
else progress.append(node("p", "muted", say("No progress summary in this read; no percentage or ETA is inferred.",
|
|
@@ -18,9 +18,10 @@ export function renderTaskSurface(container, data, t, locale, actions) {
|
|
|
18
18
|
const summary = node("div", "section-body");
|
|
19
19
|
summary.append(node("span", "detail-kicker", task.id), node("h2", "detail-title", task.title), pill(t, "status", task.status));
|
|
20
20
|
renderTaskSummary(summary, data, t, locale, actions, recordCard);
|
|
21
|
-
const discuss = node("
|
|
21
|
+
const discuss = node(actions.conversationHost ? "section" : "details",
|
|
22
|
+
actions.conversationHost ? "conversation-compose record-card" : "record-card");
|
|
22
23
|
discuss.dataset.viewKey = "discussion";
|
|
23
|
-
discuss.append(node("summary", "", say("Discuss this Task", "讨论此任务")));
|
|
24
|
+
discuss.append(node(actions.conversationHost ? "h3" : "summary", "", say("Discuss this Task", "讨论此任务")));
|
|
24
25
|
const conversation = node("button", "record-open", say("View Leader Session (read-only)", "查看 Leader Session(只读)"));
|
|
25
26
|
conversation.type = "button";
|
|
26
27
|
// A Draft's planning Session is a real Leader Session, so it is viewable once it
|
|
@@ -96,7 +97,43 @@ export function renderTaskSurface(container, data, t, locale, actions) {
|
|
|
96
97
|
}
|
|
97
98
|
});
|
|
98
99
|
discuss.append(chat);
|
|
99
|
-
|
|
100
|
+
if (actions.conversationHost) {
|
|
101
|
+
const feed = node("div", "conversation-feed");
|
|
102
|
+
feed.setAttribute("aria-label", say("Recorded Task messages", "已记录的 Task 消息"));
|
|
103
|
+
const messages = records("task-message");
|
|
104
|
+
messages.forEach((entry) => {
|
|
105
|
+
if (entry.omitted) {
|
|
106
|
+
feed.append(recordCard(entry));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const value = entry.value;
|
|
110
|
+
const item = node("article", "conversation-item");
|
|
111
|
+
if (value.author?.type === "user") item.classList.add("is-user");
|
|
112
|
+
const head = node("div", "conversation-item-head");
|
|
113
|
+
const author = value.author?.roleName || t("author." + (value.author?.type || "system"));
|
|
114
|
+
head.append(node("strong", "", author));
|
|
115
|
+
if (value.createdAt) {
|
|
116
|
+
const when = node("time", "", new Intl.DateTimeFormat(locale, {
|
|
117
|
+
month: "short", day: "numeric", hour: "2-digit", minute: "2-digit"
|
|
118
|
+
}).format(new Date(value.createdAt)));
|
|
119
|
+
when.dateTime = value.createdAt;
|
|
120
|
+
head.append(when);
|
|
121
|
+
}
|
|
122
|
+
item.append(head, richText(null, value.body || "", t));
|
|
123
|
+
feed.append(item);
|
|
124
|
+
});
|
|
125
|
+
if (!messages.length) feed.append(node("p", "muted", core.omitted.records
|
|
126
|
+
? say("Compact read omits some messages. Open History & messages for source references.",
|
|
127
|
+
"紧凑读取省略了部分消息;可在「历史与消息」查看来源引用。")
|
|
128
|
+
: say("No recorded Task messages yet.", "尚无已记录的 Task 消息。")));
|
|
129
|
+
if (core.omitted.records && messages.length) feed.append(node("p", "muted",
|
|
130
|
+
say("Compact read may omit older messages; see History & messages for source references.",
|
|
131
|
+
"紧凑读取可能省略较早消息;可在「历史与消息」查看来源引用。")));
|
|
132
|
+
actions.conversationHost.replaceChildren(feed, discuss);
|
|
133
|
+
actions.conversationHost.dataset.taskId = task.id;
|
|
134
|
+
} else {
|
|
135
|
+
summary.append(discuss);
|
|
136
|
+
}
|
|
100
137
|
// decision-3 three-action input control (queue / steer / interrupt). This is
|
|
101
138
|
// the same application-layer path the CLI drives; the Web surface adds no
|
|
102
139
|
// fourth action and no auto-fallback. Steer/interrupt name an exact current
|
|
@@ -378,10 +415,14 @@ export function renderTaskSurface(container, data, t, locale, actions) {
|
|
|
378
415
|
scaffold.append(anchorSection(id, null, folded));
|
|
379
416
|
return body;
|
|
380
417
|
}
|
|
381
|
-
const
|
|
418
|
+
const working = task.status === "active" || task.status === "draft";
|
|
419
|
+
const focus = section("detail-focus", working
|
|
420
|
+
? say("Goal and current focus", "目标与当前关注")
|
|
421
|
+
: say("Goal and last working focus", "目标与结束前的工作重点"), records("task-brief"));
|
|
382
422
|
const brief = values("task-brief")[0];
|
|
383
423
|
if (brief) {
|
|
384
|
-
focus.prepend(richText(
|
|
424
|
+
focus.prepend(richText(working
|
|
425
|
+
? t("detail.focus") : say("Last working focus", "结束前的工作重点"), brief.currentFocus, t));
|
|
385
426
|
if (brief.boundaries.length) focus.append(richText(say("Boundaries", "边界"), brief.boundaries.join("\\n"), t));
|
|
386
427
|
}
|
|
387
428
|
section("detail-work", say("Responsibilities and acceptance", "工作责任与验收"), records("work-item"));
|
|
@@ -430,6 +471,14 @@ export function renderTaskSurface(container, data, t, locale, actions) {
|
|
|
430
471
|
execution.append(raw);
|
|
431
472
|
scaffold.append(anchorSection("detail-exec", sectionHead(t("detail.execution")), execution));
|
|
432
473
|
section("detail-operations", say("Original operation facts (no automatic retry)", "原始操作事实(不自动重试)"), records("job"));
|
|
474
|
+
// Four reading groups; the source sections keep their stable deep-link IDs.
|
|
475
|
+
const readingOrder = ["detail-top", "detail-focus", "detail-work", "detail-results",
|
|
476
|
+
"detail-exec", "detail-reviews", "detail-roles", "detail-history",
|
|
477
|
+
"detail-messages", "detail-operations"];
|
|
478
|
+
readingOrder.forEach((id) => {
|
|
479
|
+
const target = scaffold.querySelector("#" + id);
|
|
480
|
+
if (target) scaffold.append(target);
|
|
481
|
+
});
|
|
433
482
|
const refs = node("details", "record-card");
|
|
434
483
|
refs.append(node("summary", "", say("Context cursor and omitted references", "Context 游标与省略引用")));
|
|
435
484
|
refs.append(node("pre", "surface-json", JSON.stringify({
|
|
@@ -478,11 +478,16 @@ export function renderTaskDetail(detail, data, t, locale, actions) {
|
|
|
478
478
|
const focusBody = node("div", "section-body");
|
|
479
479
|
if (data.brief) {
|
|
480
480
|
const focusCard = node("article", "record-card");
|
|
481
|
-
|
|
481
|
+
const working = task.status === "active" || task.status === "draft";
|
|
482
|
+
focusCard.append(richText(working
|
|
483
|
+
? t("detail.focus") : (locale.startsWith("zh") ? "结束前的工作重点" : "Last working focus"),
|
|
484
|
+
data.brief.currentFocus || data.brief.objective, t));
|
|
482
485
|
if (data.brief.technicalApproach) {
|
|
483
486
|
focusCard.append(richText(t("detail.technicalApproach"), data.brief.technicalApproach, t, { muted: true }));
|
|
484
487
|
}
|
|
485
|
-
if (data.brief.leaderSummary) focusCard.append(richText(
|
|
488
|
+
if (data.brief.leaderSummary) focusCard.append(richText(working
|
|
489
|
+
? t("detail.leaderSummary") : (locale.startsWith("zh") ? "结束前的进展摘要" : "Last working report"),
|
|
490
|
+
data.brief.leaderSummary, t, { muted: true }));
|
|
486
491
|
focusBody.append(focusCard);
|
|
487
492
|
} else {
|
|
488
493
|
focusBody.append(emptyRow(t, "empty.brief"));
|
package/dist/web/assets/shell.js
CHANGED
|
@@ -75,30 +75,40 @@ export const DASHBOARD_HTML = `<!doctype html>
|
|
|
75
75
|
</div>
|
|
76
76
|
</div>
|
|
77
77
|
<div class="topbar-actions">
|
|
78
|
+
<button id="conversation-toggle" class="operator-open" type="button" aria-controls="conversation-panel" aria-expanded="false" data-i18n="conversation.show" hidden>Task discussion</button>
|
|
78
79
|
<div class="clock">
|
|
79
80
|
<span data-i18n="sync.label">Last sync</span>
|
|
80
81
|
<time id="last-sync">—</time>
|
|
81
82
|
</div>
|
|
82
|
-
<button id="operator-terminal" class="operator-open" type="button">
|
|
83
|
+
<button id="operator-terminal" class="operator-open" type="button" aria-label="Operator session" data-i18n-aria-label="actions.operator">
|
|
83
84
|
<span class="operator-title" data-i18n="actions.operator">Operator session</span>
|
|
84
85
|
<span class="operator-shortcuts" aria-hidden="true"><kbd>O</kbd></span>
|
|
85
86
|
</button>
|
|
86
|
-
<button id="refresh" class="refresh" type="button" title="Refresh tasks"><span data-i18n="actions.refresh">Refresh</span> <kbd>R</kbd></button>
|
|
87
|
+
<button id="refresh" class="refresh" type="button" title="Refresh tasks" aria-label="Refresh" data-i18n-aria-label="actions.refresh"><span data-i18n="actions.refresh">Refresh</span> <kbd>R</kbd></button>
|
|
87
88
|
</div>
|
|
88
89
|
</header>
|
|
89
90
|
<nav id="detail-tabs" class="detail-tabs" aria-label="Task sections" data-i18n-aria-label="tabs.label" hidden>
|
|
90
|
-
<button class="tab" type="button" data-target="detail-top" data-i18n="tabs.summary">
|
|
91
|
-
<button class="tab" type="button" data-target="detail-
|
|
92
|
-
<button class="tab" type="button" data-target="detail-
|
|
93
|
-
<button class="tab" type="button" data-target="detail-
|
|
94
|
-
<button class="tab" type="button" data-target="detail-exec" data-i18n="tabs.exec">AgentRuns</button>
|
|
95
|
-
<button class="tab" type="button" data-target="detail-reviews" data-i18n="tabs.reviews">Reviews</button>
|
|
96
|
-
<button class="tab" type="button" data-target="detail-roles" data-i18n="tabs.roles">Roles</button>
|
|
97
|
-
<button class="tab" type="button" data-target="detail-history" data-i18n="tabs.history">History</button>
|
|
98
|
-
<button class="tab" type="button" data-target="detail-messages" data-i18n="tabs.messages">Messages</button>
|
|
91
|
+
<button class="tab" type="button" data-target="detail-top" data-i18n="tabs.summary">Overview</button>
|
|
92
|
+
<button class="tab" type="button" data-target="detail-work" data-i18n="tabs.workResults">Work & results</button>
|
|
93
|
+
<button class="tab" type="button" data-target="detail-exec" data-i18n="tabs.executionReviews">Execution & reviews</button>
|
|
94
|
+
<button class="tab" type="button" data-target="detail-history" data-i18n="tabs.historyMessages">History & messages</button>
|
|
99
95
|
</nav>
|
|
100
96
|
<main id="detail" class="detail" aria-labelledby="page-title" tabindex="-1"></main>
|
|
101
97
|
</div>
|
|
98
|
+
<div id="pane-divider" class="pane-divider" role="separator" tabindex="0" aria-label="Resize task discussion" aria-orientation="vertical" aria-valuemin="280" aria-valuemax="640" aria-valuenow="400" data-i18n-aria-label="conversation.resize" hidden><span aria-hidden="true"></span></div>
|
|
99
|
+
<aside id="conversation-panel" class="conversation-panel" aria-labelledby="conversation-title" hidden>
|
|
100
|
+
<header class="conversation-head">
|
|
101
|
+
<div>
|
|
102
|
+
<span class="conversation-eyebrow" data-i18n="conversation.source">Recorded Task messages · not a Session transcript</span>
|
|
103
|
+
<h2 id="conversation-title" data-i18n="conversation.title">Task discussion</h2>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="conversation-head-actions">
|
|
106
|
+
<button id="conversation-swap" class="pane-action" type="button" title="Move discussion to the other side" data-i18n-aria-label="conversation.swap" aria-label="Move discussion to the other side">⇄</button>
|
|
107
|
+
<button id="conversation-close" class="pane-action" type="button" title="Hide discussion" data-i18n-aria-label="conversation.hide" aria-label="Hide discussion">×</button>
|
|
108
|
+
</div>
|
|
109
|
+
</header>
|
|
110
|
+
<div id="conversation-content" class="conversation-content task-surface"></div>
|
|
111
|
+
</aside>
|
|
102
112
|
<dialog id="global-input-dialog" class="global-input-dialog" aria-labelledby="global-input-title">
|
|
103
113
|
<h2 id="global-input-title">Global Role input · 全局 Role 消息</h2>
|
|
104
114
|
<p class="muted">Queue is the default. A cancel request does not prove the Turn stopped.<br>默认排队;取消请求不代表执行已停止。</p>
|
|
@@ -6,10 +6,13 @@ export const LAYOUT_STYLES = `
|
|
|
6
6
|
*{box-sizing:border-box}
|
|
7
7
|
html{min-width:320px;font-family:var(--font-body)}
|
|
8
8
|
body{margin:0;min-width:320px;height:100vh;overflow:hidden;font-size:14px}
|
|
9
|
-
.app-shell{display:grid;grid-template-columns:var(--sidebar-w) minmax(0,1fr) 0;grid-template-rows:100vh;height:100vh;transition:grid-template-columns var(--motion-slow) var(--ease)}
|
|
10
|
-
body.
|
|
9
|
+
.app-shell{display:grid;grid-template-areas:"sidebar main divider pane";grid-template-columns:var(--sidebar-w) minmax(0,1fr) 0 0;grid-template-rows:100vh;height:100vh;transition:grid-template-columns var(--motion-slow) var(--ease)}
|
|
10
|
+
body.conversation-active .app-shell{grid-template-columns:var(--sidebar-w) minmax(0,1fr) 8px var(--pane-w)}
|
|
11
|
+
body.conversation-active.pane-left .app-shell{grid-template-areas:"sidebar pane divider main";grid-template-columns:var(--sidebar-w) var(--pane-w) 8px minmax(0,1fr)}
|
|
12
|
+
body.terminal-active .app-shell,body.terminal-active.pane-left .app-shell{grid-template-areas:"sidebar main divider pane";grid-template-columns:var(--sidebar-w) minmax(0,1fr) 0 var(--terminal-w)}
|
|
13
|
+
body.pane-resizing .app-shell{transition:none}
|
|
11
14
|
/* Sidebar = persistent work index */
|
|
12
|
-
.sidebar{display:flex;flex-direction:column;min-width:0;min-height:0;height:100vh;padding:16px 14px 14px}
|
|
15
|
+
.sidebar{grid-area:sidebar;display:flex;flex-direction:column;min-width:0;min-height:0;height:100vh;padding:16px 14px 14px}
|
|
13
16
|
.sidebar-brand{display:flex;gap:10px;align-items:center;flex:none;margin-bottom:14px}
|
|
14
17
|
.brand-text{display:grid;gap:3px;min-width:0;flex:1}
|
|
15
18
|
.sidebar .search{flex:none;margin-bottom:10px}
|
|
@@ -27,7 +30,7 @@ body.terminal-active .app-shell{grid-template-columns:var(--sidebar-w) minmax(0,
|
|
|
27
30
|
.catalog-pager button:disabled{opacity:.45;cursor:default}
|
|
28
31
|
#catalog-attention-reset{grid-column:1/-1}
|
|
29
32
|
/* Main column = top bar + optional section nav + reading surface */
|
|
30
|
-
.main-col{display:flex;flex-direction:column;min-width:0;min-height:0;height:100vh;overflow-y:auto;scrollbar-gutter:stable}
|
|
33
|
+
.main-col{grid-area:main;display:flex;flex-direction:column;min-width:0;min-height:0;height:100vh;overflow-y:auto;scrollbar-gutter:stable}
|
|
31
34
|
.topbar{position:sticky;top:0;z-index:30;flex:none;display:flex;align-items:center;justify-content:space-between;gap:18px;padding:11px max(var(--page-space),calc((100% - 1100px) / 2));background:color-mix(in srgb,var(--bg) 92%,transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-bottom:1px solid var(--border)}
|
|
32
35
|
.topbar-leading{display:flex;align-items:center;gap:14px;min-width:0}
|
|
33
36
|
.breadcrumb{display:flex;align-items:baseline;gap:8px;min-width:0}
|
|
@@ -43,8 +46,32 @@ body.detail-active .detail-tabs{display:flex}
|
|
|
43
46
|
.detail{flex:1;min-height:0;padding:16px max(var(--page-space),calc((100% - 1100px) / 2)) 72px;min-width:0}
|
|
44
47
|
.detail:focus-visible{outline:none}
|
|
45
48
|
.anchor{scroll-margin-top:calc(var(--topbar-h,44px) + var(--tabs-h,38px))}
|
|
46
|
-
/*
|
|
47
|
-
.
|
|
49
|
+
/* Adjustable discussion panel; the separator belongs to the task view, not Task state. */
|
|
50
|
+
.pane-divider{grid-area:divider;align-self:stretch;width:8px;min-width:0;cursor:col-resize;position:relative;touch-action:none;z-index:31}
|
|
51
|
+
.pane-divider[hidden]{display:none}
|
|
52
|
+
.pane-divider span{position:absolute;inset:0 3px;background:var(--border-strong);transition:background var(--motion-fast)}
|
|
53
|
+
.pane-divider:hover span,.pane-divider:focus-visible span,body.pane-resizing .pane-divider span{background:var(--accent)}
|
|
54
|
+
.pane-divider:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
|
|
55
|
+
.conversation-panel{grid-area:pane;min-width:0;height:100vh;min-height:0;display:flex;flex-direction:column;overflow:hidden}
|
|
56
|
+
.conversation-panel[hidden]{display:none}
|
|
57
|
+
.conversation-head{flex:none;min-height:72px;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:13px 16px}
|
|
58
|
+
.conversation-head>div:first-child{min-width:0}
|
|
59
|
+
.conversation-eyebrow{display:block;font-size:10px;line-height:1.4}
|
|
60
|
+
.conversation-head h2{margin:4px 0 0;font-size:16px;font-family:var(--font-display);line-height:1.25}
|
|
61
|
+
.conversation-head-actions{display:flex;gap:4px;align-items:center}
|
|
62
|
+
.conversation-content{display:flex;flex:1;flex-direction:column;min-height:0;padding:14px;gap:14px}
|
|
63
|
+
.conversation-feed{display:flex;flex:1;flex-direction:column;gap:10px;min-height:0;overflow-y:auto;scrollbar-gutter:stable;padding:2px 2px 12px}
|
|
64
|
+
.conversation-item{flex:none;padding:11px 12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-2);min-width:0;overflow-wrap:anywhere}
|
|
65
|
+
.conversation-item.is-user{border-color:var(--accent-line);background:var(--accent-soft)}
|
|
66
|
+
.conversation-item-head{display:flex;justify-content:space-between;gap:10px;color:var(--muted);font-size:11px;margin-bottom:8px}
|
|
67
|
+
.conversation-item-head time{white-space:nowrap}
|
|
68
|
+
.conversation-item .rich-text{font-size:13px}
|
|
69
|
+
.conversation-compose{flex:none;max-height:55%;overflow-y:auto}
|
|
70
|
+
.conversation-compose h3{margin:2px 0;font-size:13px}
|
|
71
|
+
.conversation-compose form{display:grid;gap:9px}
|
|
72
|
+
.conversation-compose textarea{min-height:76px}
|
|
73
|
+
/* Native Session occupies the pane temporarily; it is not the Task message feed. */
|
|
74
|
+
.terminal-panel{grid-area:pane;min-width:0;height:100vh;display:flex;flex-direction:column;overflow:hidden}
|
|
48
75
|
.terminal-panel[hidden]{display:none}
|
|
49
76
|
.terminal-head{flex:none;height:62px;display:flex;align-items:center;justify-content:space-between;gap:14px;padding:10px 14px}
|
|
50
77
|
.terminal-head>div{display:flex;align-items:center;gap:12px;min-width:0}
|
|
@@ -11,8 +11,7 @@ export const RESPONSIVE_STYLES = `
|
|
|
11
11
|
@media(max-width:900px){
|
|
12
12
|
/* Master-detail: sidebar list by default; selecting a task swaps to detail */
|
|
13
13
|
body{overflow:auto}
|
|
14
|
-
.app-shell{grid-template-columns:1fr;height:auto;min-height:100vh}
|
|
15
|
-
body.terminal-active .app-shell{grid-template-columns:1fr}
|
|
14
|
+
.app-shell,body.conversation-active .app-shell,body.conversation-active.pane-left .app-shell,body.terminal-active .app-shell,body.terminal-active.pane-left .app-shell{grid-template-areas:"sidebar" "main";grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto;height:auto;min-height:100vh}
|
|
16
15
|
.sidebar{height:auto;min-height:100vh;border-right:0;border-bottom:1px solid var(--border)}
|
|
17
16
|
.task-list{overflow:visible}
|
|
18
17
|
.main-col{display:none;height:auto;min-height:100vh}
|
|
@@ -20,10 +19,16 @@ export const RESPONSIVE_STYLES = `
|
|
|
20
19
|
body.detail-active .main-col{display:flex}
|
|
21
20
|
body.detail-active .detail-back{display:grid}
|
|
22
21
|
.detail{overflow:visible}
|
|
22
|
+
.pane-divider{display:none}
|
|
23
|
+
.conversation-panel{position:fixed;inset:0;z-index:70;height:100dvh;border-left:0}
|
|
24
|
+
.conversation-feed{min-height:0}
|
|
25
|
+
.conversation-head-actions #conversation-swap{display:none}
|
|
26
|
+
body.conversation-active .main-col{display:none}
|
|
27
|
+
body.conversation-active .conversation-panel{display:flex}
|
|
23
28
|
.overview-duo{grid-template-columns:1fr}
|
|
24
29
|
.observability-metrics{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
25
30
|
/* Terminal panel overlays full-screen instead of occupying a grid column */
|
|
26
|
-
.terminal-panel{position:fixed;inset:0;z-index:
|
|
31
|
+
.terminal-panel{position:fixed;inset:0;z-index:80;height:100dvh;border-left:0}
|
|
27
32
|
}
|
|
28
33
|
@media(max-width:620px){
|
|
29
34
|
.command-rail{grid-template-columns:repeat(2,1fr)}
|
|
@@ -31,8 +36,16 @@ export const RESPONSIVE_STYLES = `
|
|
|
31
36
|
.metric-value{font-size:26px}
|
|
32
37
|
/* Keep the topbar on one line: clock and key hints are expendable */
|
|
33
38
|
.clock{display:none}
|
|
34
|
-
.
|
|
35
|
-
.topbar{flex
|
|
39
|
+
.topbar{flex-wrap:nowrap;gap:8px;padding:10px 14px}
|
|
40
|
+
.topbar-leading{flex:1;gap:8px}
|
|
41
|
+
.breadcrumb .crumb,.breadcrumb .crumb-sep{display:none}
|
|
42
|
+
.topbar-actions{gap:6px}
|
|
43
|
+
#conversation-toggle,#operator-terminal,#refresh{width:38px;height:38px;min-width:38px;justify-content:center;padding:0}
|
|
44
|
+
#conversation-toggle{font-size:0}
|
|
45
|
+
#conversation-toggle::before{content:"▤";font-size:19px}
|
|
46
|
+
#operator-terminal .operator-title,#operator-terminal .operator-shortcuts,#refresh span,#refresh kbd{display:none}
|
|
47
|
+
#operator-terminal::before{content:">_";font-family:var(--font-mono);font-size:15px}
|
|
48
|
+
#refresh::before{content:"↻";font-size:20px}
|
|
36
49
|
.detail-tabs{mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent)}
|
|
37
50
|
.record-cols{grid-template-columns:1fr}
|
|
38
51
|
.observability-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}
|
|
@@ -35,7 +35,7 @@ export const TOKEN_STYLES = `
|
|
|
35
35
|
--font-mono:"JetBrains Mono","SFMono-Regular",Consolas,monospace;
|
|
36
36
|
--font-display:"Inter",system-ui,-apple-system,"Segoe UI",sans-serif;
|
|
37
37
|
--font-body:"Inter",system-ui,-apple-system,"Segoe UI",sans-serif;
|
|
38
|
-
--page-space:clamp(18px,2.6vw,40px);--sidebar-w:clamp(232px,18vw,288px);--terminal-w:clamp(420px,34vw,640px);
|
|
38
|
+
--page-space:clamp(18px,2.6vw,40px);--sidebar-w:clamp(232px,18vw,288px);--terminal-w:clamp(420px,34vw,640px);--pane-w:clamp(300px,29vw,500px);
|
|
39
39
|
--motion-fast:150ms;--motion-slow:320ms;--ease:cubic-bezier(.16,.84,.44,1);
|
|
40
40
|
}
|
|
41
41
|
:root,[data-theme="control-room"]{
|
|
@@ -142,6 +142,11 @@ kbd{background:var(--bg-3);border:1px solid var(--border);border-bottom-width:2p
|
|
|
142
142
|
.metric.is-warning .metric-value{color:var(--warning)}
|
|
143
143
|
/* Terminal panel */
|
|
144
144
|
.terminal-panel{color:#e8eef6;background:#080b11;border-left:1px solid #263244;box-shadow:var(--shadow-pop)}
|
|
145
|
+
.conversation-panel{color:var(--text);background:var(--bg-1);border-left:1px solid var(--border);box-shadow:var(--shadow-card)}
|
|
146
|
+
.conversation-head{border-bottom:1px solid var(--border);background:var(--bg-2)}
|
|
147
|
+
.conversation-eyebrow{color:var(--muted)}
|
|
148
|
+
.pane-action{display:grid;place-items:center;width:34px;height:34px;padding:0;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-1);color:var(--text);font:inherit;font-size:20px;cursor:pointer}
|
|
149
|
+
.pane-action:hover,.pane-action:focus-visible{border-color:var(--accent);color:var(--accent)}
|
|
145
150
|
.terminal-head{background:#0c111a;border-bottom:1px solid #263244}
|
|
146
151
|
.terminal-head h2{font-family:var(--font-display);font-size:15px;color:#e8eef6}
|
|
147
152
|
.terminal-close{display:grid!important;color:#8b99ad;background:#111826;border-color:#263244}
|
|
@@ -11,6 +11,13 @@ notifications cannot restart an explicitly stopped Session; normal Host detach
|
|
|
11
11
|
preserves an active Session and remains reconnectable. A switch after a partial
|
|
12
12
|
failure still settles the retained input before selecting a new conversation.
|
|
13
13
|
|
|
14
|
+
A newly created global Operator conversation requests the native title
|
|
15
|
+
`Yui · Operator · MMdd`, using Yui's configured time zone. The request is made
|
|
16
|
+
only after the provider returns the exact new conversation identity; restore
|
|
17
|
+
and reconnect paths never replay it. Providers without a post-create rename
|
|
18
|
+
capability keep the usable Session and expose a diagnostic instead of treating
|
|
19
|
+
metadata failure as Session-creation failure.
|
|
20
|
+
|
|
14
21
|
## Authority
|
|
15
22
|
|
|
16
23
|
Task, WorkItem, Message, Decision, Artifact and Project Knowledge preserve
|