@zq-silk/yui 2.16.0 → 2.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/cli/commandCatalog.js +19 -12
  2. package/dist/cli/interactionCandidates.js +20 -5
  3. package/dist/cli/interactionPolicy.js +5 -4
  4. package/dist/cli/roleWizard.js +7 -0
  5. package/dist/commands/controllerCommands.js +13 -4
  6. package/dist/commands/durableJobCommands.js +50 -3
  7. package/dist/commands/globalRoleCommands.js +31 -14
  8. package/dist/commands/inboxCommands.js +1 -0
  9. package/dist/commands/operatorCommands.js +12 -2
  10. package/dist/commands/profileCommands.js +19 -2
  11. package/dist/commands/roleConfiguration.js +3 -0
  12. package/dist/commands/taskCommands.js +16 -0
  13. package/dist/commands/taskRoleRuntimeStatus.js +3 -0
  14. package/dist/controlPlaneCli.js +21 -4
  15. package/dist/controller/agentJobControl.js +9 -1
  16. package/dist/controller/clientRuntime.js +34 -13
  17. package/dist/controller/controller.js +35 -22
  18. package/dist/controller/diagnosticJournal.js +214 -0
  19. package/dist/controller/fileSchedulerStoreAdapter.js +174 -105
  20. package/dist/controller/globalInputDelivery.js +19 -9
  21. package/dist/controller/globalRuntimeAttention.js +11 -3
  22. package/dist/controller/jobControl.js +1 -1
  23. package/dist/controller/jobDefinitionControl.js +24 -13
  24. package/dist/controller/jobSupervisor.js +4 -0
  25. package/dist/controller/providerRetryAdmission.js +9 -0
  26. package/dist/controller/providerRetryDelivery.js +16 -0
  27. package/dist/controller/runtime.js +80 -4
  28. package/dist/controller/runtimeLaunchCoordinator.js +3 -1
  29. package/dist/controller/workerSchedulerStore.js +24 -0
  30. package/dist/core/boundedRpc.js +4 -0
  31. package/dist/core/controllerServer.js +1 -1
  32. package/dist/core/controllerTelemetry.js +92 -19
  33. package/dist/executor/fileRoleLaunchPlanner.js +3 -0
  34. package/dist/job/globalShell.js +51 -0
  35. package/dist/job/jobBusinessResult.js +4 -3
  36. package/dist/job/jobDefinition.js +6 -1
  37. package/dist/job/jobRunner.js +9 -0
  38. package/dist/job/scheduleService.js +2 -0
  39. package/dist/message/globalInboxRecovery.js +30 -19
  40. package/dist/message/globalProviderRetry.js +7 -1
  41. package/dist/message/messageContinuation.js +4 -0
  42. package/dist/output/rolePresentation.js +3 -0
  43. package/dist/profile/agentProfile.js +8 -0
  44. package/dist/profile/agentProfileRuntime.js +16 -1
  45. package/dist/review/reviewDecision.js +6 -0
  46. package/dist/role/availability.js +28 -0
  47. package/dist/role/role.js +6 -0
  48. package/dist/runtime/builtinAgentErrorMappers.js +4 -0
  49. package/dist/runtime/codexNativeAccess.js +21 -4
  50. package/dist/runtime/globalSessionHealth.js +39 -0
  51. package/dist/runtime/tmuxAdapters.js +5 -0
  52. package/dist/scheduler/activeRoleRunDelivery.js +9 -0
  53. package/dist/scheduler/leaderWakeupProcessor.js +8 -1
  54. package/dist/scheduler/roleRunLiveness.js +3 -0
  55. package/dist/scheduler/roleRunStall.js +10 -10
  56. package/dist/storage/persistenceWorker.js +41 -19
  57. package/dist/storage/sqliteSchema.js +43 -9
  58. package/dist/storage/sqliteStore.js +86 -8
  59. package/dist/storage/storageVersions.js +1 -1
  60. package/dist/storage/storeRpc.js +25 -11
  61. package/dist/storage/taskReadRevisionSchema.js +39 -0
  62. package/dist/web/assets/client/app/catalog.js +0 -29
  63. package/dist/web/assets/client/app/jobs.js +115 -0
  64. package/dist/web/assets/client/app/main.js +32 -28
  65. package/dist/web/assets/client/app/manage.js +1 -0
  66. package/dist/web/assets/client/app/settings.js +1 -5
  67. package/dist/web/assets/client/app/taskView.js +7 -11
  68. package/dist/web/assets/client/i18n/en.js +265 -36
  69. package/dist/web/assets/client/i18n/zh-CN.js +264 -35
  70. package/dist/web/assets/client/index.js +4 -0
  71. package/dist/web/assets/client/layout/manageShell.js +61 -15
  72. package/dist/web/assets/client/layout/router.js +2 -1
  73. package/dist/web/assets/client/layout/shortcuts.js +2 -2
  74. package/dist/web/assets/client/layout/workspace.js +99 -112
  75. package/dist/web/assets/client/lib/prefs.js +1 -1
  76. package/dist/web/assets/client/ui/primitives.js +15 -2
  77. package/dist/web/assets/client/views/dock/conversation.js +421 -123
  78. package/dist/web/assets/client/views/dock/session.js +229 -0
  79. package/dist/web/assets/client/views/dock/terminal.js +48 -39
  80. package/dist/web/assets/client/views/jobs.js +1042 -160
  81. package/dist/web/assets/client/views/overview.js +2 -2
  82. package/dist/web/assets/client/views/sidebar.js +3 -0
  83. package/dist/web/assets/client/views/task/page.js +2 -12
  84. package/dist/web/assets/shared/manageNav.js +3 -1
  85. package/dist/web/assets/shell/appRail.js +2 -1
  86. package/dist/web/assets/shell/dock.js +9 -27
  87. package/dist/web/assets/shell/manage.js +7 -2
  88. package/dist/web/assets/shell/models.js +4 -3
  89. package/dist/web/assets/shell/settingsContent.js +0 -3
  90. package/dist/web/assets/shell/sidebar.js +0 -1
  91. package/dist/web/assets/styles/views/conversation.js +96 -17
  92. package/dist/web/assets/styles/views/dock.js +17 -5
  93. package/dist/web/assets/styles/views/jobs.js +301 -12
  94. package/dist/web/assets/styles/views/sidebar.js +1 -2
  95. package/dist/web/assets/styles/views/task.js +1 -2
  96. package/dist/web/assets/styles/views/terminal.js +12 -8
  97. package/dist/web/webConversation.js +1 -0
  98. package/dist/web/webServer.js +28 -1
  99. package/dist/web/webSettings.js +10 -2
  100. package/docs/global-shell-jobs.md +78 -0
  101. package/docs/job-runs.md +6 -3
  102. package/docs/managed-turn-and-session-runtime.md +84 -0
  103. package/docs/managed-turn-and-session-runtime.zh-CN.md +57 -0
  104. package/docs/release-workflow.md +67 -0
  105. package/docs/release-workflow.zh-CN.md +48 -0
  106. package/docs/roles-and-configuration.md +45 -1
  107. package/docs/roles-and-configuration.zh-CN.md +35 -0
  108. package/docs/storage-baseline.md +19 -1
  109. package/docs/storage-baseline.zh-CN.md +10 -1
  110. package/docs/testing/review-evaluation.md +35 -1
  111. package/docs/testing/review-evaluation.zh-CN.md +25 -1
  112. package/docs/testing/verification-levels.md +2 -2
  113. package/docs/testing/verification-levels.zh-CN.md +2 -2
  114. package/package.json +1 -1
  115. package/skills/yui-leader/VERSION +1 -1
  116. package/skills/yui-leader/references/execution.md +19 -8
  117. package/skills/yui-reviewer/SKILL.md +13 -2
  118. package/skills/yui-reviewer/VERSION +1 -1
@@ -4,7 +4,7 @@ export const OVERVIEW_SCRIPT = String.raw `
4
4
  // then catalog-wide signals and the explicit Session read.
5
5
  import { h, icon, clear } from "/assets/js/lib/dom.js";
6
6
  import { byNewest, fill, formatDateTime, relativeTime } from "/assets/js/lib/format.js";
7
- import { badge, button, dot, emptyState, note, operatorToggle, topBar } from "/assets/js/ui/primitives.js";
7
+ import { badge, button, dot, emptyState, note, panelToggle, topBar } from "/assets/js/ui/primitives.js";
8
8
  import { card } from "/assets/js/ui/containers.js";
9
9
  import { metricTile } from "/assets/js/ui/metrics.js";
10
10
  import { label, sessionBadges, tone } from "/assets/js/domain/vocab.js";
@@ -21,7 +21,7 @@ export function renderOverview(container, state, t, locale, actions) {
21
21
  h("span.crumb", null, t("manage.workbench")),
22
22
  h("span.crumb-sep", { "aria-hidden": "true" }, "/"),
23
23
  h("span.crumb-current", null, t("overview.title"))),
24
- [operatorToggle(t, actions.toggleOperator)]));
24
+ [panelToggle(t, actions.togglePanel)]));
25
25
  page.append(overviewHead(state.counts, t, actions), metricStrip(state.counts, t));
26
26
  page.append(h("div.card-stack", null,
27
27
  inboxCard(state, t, actions),
@@ -120,6 +120,9 @@ function taskRow(task, state, t, locale, onSelect) {
120
120
  h("span.task-body", null,
121
121
  h("span.task-line", null, h("span.task-name", null, task.title), h("time.task-time", { dateTime: task.updatedAt }, relativeTime(task.updatedAt, locale, t))),
122
122
  h("span.task-line.task-sub", null, h("code.task-id", null, task.id),
123
+ // A Task a Job Run created is still an ordinary real Task; mark where it came from.
124
+ task.source && task.source.kind === "job-run"
125
+ ? h("span.task-source", { title: t("jobs.sourceJob") + " · " + task.source.jobId }, t("catalog.sourceJob")) : null,
123
126
  counts.workItems > 0 ? h("span.task-count", { title: t("sidebar.workItems") }, icon("layers", "icon-sm"), String(counts.workItems)) : null,
124
127
  taskSignals(task, t))));
125
128
  }
@@ -4,7 +4,7 @@ export const TASK_PAGE_SCRIPT = String.raw `
4
4
  // observation arrives separately from the Context snapshot, so every fact it
5
5
  // feeds lives in a [data-slot] that updateObservation() redraws in place.
6
6
  import { h, icon, clear } from "/assets/js/lib/dom.js";
7
- import { badge, chip, operatorToggle, timeTag, topBar } from "/assets/js/ui/primitives.js";
7
+ import { badge, chip, panelToggle, timeTag, topBar } from "/assets/js/ui/primitives.js";
8
8
  import { label, statusBadge } from "/assets/js/domain/vocab.js";
9
9
  import { totalOf } from "/assets/js/domain/context.js";
10
10
  import { renderOverviewPanel } from "/assets/js/views/task/overview.js";
@@ -79,17 +79,7 @@ function taskBar(task, t, ctx) {
79
79
  h("button.crumb", { type: "button", title: t("overview.title"), onclick: ctx.onBack }, t("manage.workbench")),
80
80
  h("span.crumb-sep", { "aria-hidden": "true" }, "/"),
81
81
  h("code.crumb-current", { title: task.title }, task.id)),
82
- [dockToggle("discussion", "chat", t("dock.discussion") + " · D", t, ctx),
83
- dockToggle("session", "terminal", t("dock.session"), t, ctx),
84
- h("span.task-bar-sep", { "aria-hidden": "true" }),
85
- operatorToggle(t, ctx.toggleOperator)]);
86
- }
87
-
88
- function dockToggle(mode, iconName, title, t, ctx) {
89
- return h("button.btn.btn-ghost.dock-toggle", {
90
- type: "button", dataset: { dockToggle: mode }, title: title,
91
- onclick: function () { ctx.showDock(mode); }
92
- }, icon(iconName), h("span", null, t("dock." + mode)));
82
+ [panelToggle(t, ctx.togglePanel)]);
93
83
  }
94
84
 
95
85
  function taskMeta(task, t, locale) {
@@ -7,9 +7,11 @@ export const MANAGE_SECTIONS = Object.freeze({
7
7
  settings: [
8
8
  { page: "settings", section: "preferences", key: "settings.section.preferences", label: "Preferences" },
9
9
  { page: "settings", section: "skills", key: "settings.section.skills", label: "Skills" },
10
- { page: "settings", section: "jobs", key: "settings.section.jobs", label: "Jobs & schedules" },
11
10
  { page: "settings", section: "permissions", key: "settings.section.permissions", label: "Command permissions" },
12
11
  { page: "settings", section: "server", key: "settings.section.server", label: "Agents & server" },
13
12
  { page: "models", section: "connection", key: "models.section.connection", label: "Model connection" }
13
+ ],
14
+ jobs: [
15
+ { page: "jobs", section: "jobs", key: "nav.jobs", label: "Jobs" }
14
16
  ]
15
17
  });
@@ -6,7 +6,8 @@ export function appRail(current) {
6
6
  return `<nav class="app-rail" aria-label="Main navigation" data-i18n-aria-label="manage.nav">
7
7
  <a class="rail-brand" href="/"${brandNav} title="Yui"><span class="brand-mark" aria-hidden="true">${brandGlyphSvg()}</span><span class="sr-only">Yui</span></a>
8
8
  <div class="rail-items">
9
- ${item("workbench", "/", "workbench", "manage.workbench", "Workbench", current, current === "workbench" ? "rail-workbench" : undefined)}
9
+ ${item("workbench", "/", "workbench", "manage.workbench", "Tasks", current, current === "workbench" ? "rail-workbench" : undefined)}
10
+ ${item("jobs", "/jobs", "clock", "nav.jobs", "Jobs", current)}
10
11
  ${item("models", "/models", "layers", "models.title", "Models", current)}
11
12
  ${item("settings", "/settings", "settings", "settings.title", "Settings", current, current === "workbench" ? "settings-open" : undefined)}
12
13
  </div>
@@ -1,8 +1,9 @@
1
1
  import { DOCK } from "../shared/geometry.js";
2
2
  import { iconSvg } from "../shared/icons.js";
3
- // The dock divider and the dock region: the Discussion / Live session tabs,
4
- // their actions, the discussion mount and the terminal pane, whose host,
5
- // target chips and connection state must survive re-renders.
3
+ // The dock divider and the dock region: one header row with the target menu
4
+ // (the only Role selector), the view tabs (icon-only until the dock is wide)
5
+ // and the dock actions; then the session page mount (chat or terminal, built
6
+ // by views/dock/session), the discussion mount and the preview.
6
7
  // The string keeps its indentation inside .app and has no trailing newline.
7
8
  export const DOCK_HTML = ` <div id="dock-divider" class="dock-divider" role="separator" tabindex="0" aria-orientation="vertical" aria-valuemin="${DOCK.min}" aria-valuemax="${DOCK.max}" aria-valuenow="${DOCK.initial}" aria-label="Resize session panel" data-i18n-aria-label="dock.resize" hidden><span aria-hidden="true"></span></div>
8
9
  <aside id="dock" class="dock" aria-label="Session" data-i18n-aria-label="dock.label" hidden>
@@ -18,36 +19,17 @@ export const DOCK_HTML = ` <div id="dock-divider" class="dock-divider" role="
18
19
  <button id="global-input-open" class="dock-target-item" type="button">${iconSvg("send")}<span data-i18n="global.openRole">Message another Role…</span></button>
19
20
  </div>
20
21
  </details>
22
+ <div class="seg seg-sm dock-tabs" role="tablist" aria-label="Session view" data-i18n-aria-label="dock.label">
23
+ <button id="dock-tab-conversation" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-conversation" aria-label="Conversation" data-i18n-aria-label="conversation.title" title="Conversation" data-i18n-title="conversation.title">${iconSvg("sparkle")}<span data-i18n="conversation.title">Conversation</span></button>
24
+ <button id="dock-tab-discussion" class="seg-btn" type="button" role="tab" aria-selected="true" aria-controls="dock-discussion" aria-label="Discussion" data-i18n-aria-label="dock.discussion" title="Discussion" data-i18n-title="dock.discussion">${iconSvg("chat")}<span data-i18n="dock.discussion">Discussion</span></button>
25
+ <button id="dock-tab-preview" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-preview" aria-label="Preview" data-i18n-aria-label="preview.title" title="Preview" data-i18n-title="preview.title" disabled>${iconSvg("eye")}<span data-i18n="preview.title">Preview</span></button>
26
+ </div>
21
27
  <div class="dock-actions">
22
28
  <button id="dock-swap" class="icon-btn" type="button" aria-label="Swap with task details" data-i18n-aria-label="dock.swap" title="Swap with task details">${iconSvg("swap")}</button>
23
29
  <button id="dock-close" class="icon-btn" type="button" aria-label="Hide session panel" data-i18n-aria-label="dock.hide" title="Hide · D">${iconSvg("close")}</button>
24
30
  </div>
25
31
  </header>
26
- <div class="dock-tabs">
27
- <div class="seg seg-sm" role="tablist" aria-label="Session view" data-i18n-aria-label="dock.label">
28
- <button id="dock-tab-conversation" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-conversation"><span data-i18n="conversation.title">Conversation</span></button>
29
- <button id="dock-tab-discussion" class="seg-btn" type="button" role="tab" aria-selected="true" aria-controls="dock-discussion">${iconSvg("chat")}<span data-i18n="dock.discussion">Discussion</span></button>
30
- <button id="dock-tab-session" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-session">${iconSvg("terminal")}<span data-i18n="dock.session">Live session</span></button>
31
- <button id="dock-tab-preview" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-preview" disabled><span data-i18n="preview.title">Preview</span></button>
32
- </div>
33
- </div>
34
32
  <section id="dock-conversation" class="dock-pane dock-discussion" role="tabpanel" aria-labelledby="dock-tab-conversation" hidden></section>
35
33
  <section id="dock-discussion" class="dock-pane dock-discussion" role="tabpanel" aria-labelledby="dock-tab-discussion"></section>
36
34
  <section id="dock-preview" class="dock-pane dock-preview" role="tabpanel" aria-labelledby="dock-tab-preview" hidden></section>
37
- <section id="dock-session" class="dock-pane dock-session" role="tabpanel" aria-labelledby="dock-tab-session" hidden>
38
- <div class="session-bar">
39
- <div id="session-targets" class="session-targets" role="group" aria-label="Session target" data-i18n-aria-label="dock.target"></div>
40
- <span id="terminal-state" class="conn" data-state="idle"><i aria-hidden="true"></i><span data-i18n="terminal.idle">Not connected</span></span>
41
- </div>
42
- <div id="terminal-host" class="terminal-host"></div>
43
- <div id="terminal-empty" class="terminal-empty">
44
- ${iconSvg("terminal", "icon icon-xl")}
45
- <p class="terminal-empty-title" data-i18n="terminal.emptyTitle">Attach to a native Session</p>
46
- <p class="terminal-empty-text" data-i18n="terminal.emptyText">Pick a Role above. The panel shows the Session's current screen; attaching never starts or restarts an Agent.</p>
47
- </div>
48
- <footer class="session-foot">
49
- <span data-i18n="terminal.cliHint">Full terminal</span>
50
- <code id="terminal-cli">yui operator enter</code>
51
- </footer>
52
- </section>
53
35
  </aside>`;
@@ -4,7 +4,7 @@ import { BRAND_ICONS } from "../shared/brand.js";
4
4
  import { iconSvg } from "../shared/icons.js";
5
5
  import { appRail } from "./appRail.js";
6
6
  import { MANAGE_SECTIONS } from "../shared/manageNav.js";
7
- // One frame and entry point for /models/* and /settings/*. The frame is the
7
+ // One frame and entry point for /jobs, /models/* and /settings/*. The frame is the
8
8
  // workbench's own: the global rail, a collapsible list (here the current
9
9
  // area's sections), and the center column under the shared top bar. Narrow
10
10
  // screens open the rail and the list together as a drawer, exactly as on the
@@ -22,9 +22,14 @@ const area = (name, key, label) => `
22
22
  const RAIL = `<aside class="manage-rail sidebar" aria-label="Sections" data-i18n-aria-label="manage.sections">
23
23
  <header class="side-head">
24
24
  <h2 id="manage-area-title" class="side-title"></h2>
25
+ <span id="jobs-sync" class="sync" data-area-only="jobs" hidden title="Last sync" data-i18n-title="sync.label"><i aria-hidden="true"></i><time id="jobs-last-sync">—</time></span>
26
+ <button id="jobs-refresh" class="icon-btn" type="button" data-area-only="jobs" hidden aria-label="Refresh" data-i18n-aria-label="actions.refresh" title="Refresh" data-i18n-title="actions.refresh">${iconSvg("refresh")}</button>
25
27
  <button id="sidebar-collapse" class="icon-btn side-collapse" type="button" aria-label="Collapse sections" data-i18n-aria-label="manage.collapseSections" title="Collapse sections · [" data-i18n-title="manage.collapseSections">${iconSvg("sidebar")}</button>
26
28
  </header>
27
29
  <nav class="manage-nav" aria-label="Sections" data-i18n-aria-label="manage.sections">${area("models", "models.title", "Models")}${area("settings", "settings.title", "Settings")}
30
+ <div class="manage-area jobs-area" data-area-group="jobs">
31
+ <div id="jobs-index" class="jobs-index" role="region" aria-label="Jobs" data-i18n-aria-label="nav.jobs"></div>
32
+ </div>
28
33
  </nav>
29
34
  </aside>`;
30
35
  const TOP_BAR = `<header class="top-bar">
@@ -47,7 +52,7 @@ export function manageDocument(options) {
47
52
  </head>
48
53
  <body class="manage-body ${options.page}-body">
49
54
  <div class="app manage-app" data-page="${options.page}" hidden>
50
- ${appRail(options.page === "models" ? "models" : "settings")}
55
+ ${appRail(options.page)}
51
56
  ${RAIL}
52
57
  <div id="sidebar-scrim" class="sidebar-scrim"></div>
53
58
  <main id="manage-scroll" class="center manage-center" tabindex="-1">
@@ -1,7 +1,8 @@
1
1
  import { manageDocument } from "./manage.js";
2
2
  import { SETTINGS_MAIN } from "./settingsContent.js";
3
- // One document for /models/* and /settings/*. Both controllers stay mounted
4
- // across history navigation, preserving drafts, observations and locale.
3
+ // One document for /jobs, /models/* and /settings/*. The controllers stay
4
+ // mounted across history navigation, preserving drafts, observations and locale.
5
5
  export const MODELS_HTML = manageDocument({ page: "models", title: "Models", main: `
6
6
  <div id="settings-root" hidden>${SETTINGS_MAIN}</div>
7
- <div id="models-root" class="models-root" hidden></div>` });
7
+ <div id="models-root" class="models-root" hidden></div>
8
+ <div id="jobs-root" class="jobs-root" hidden></div>` });
@@ -22,9 +22,6 @@ export const SETTINGS_MAIN = `
22
22
  ${manageHead("settings-skills-title", "settings.section.skills", "Skills", "skills.browser.intro", "Read existing packages and choose additional bindings for one explicit Role. Scripts are displayed as data, never run.")}
23
23
  <section id="skill-browser" class="card settings-card settings-stack"></section>
24
24
  </section>
25
- <section class="manage-section" data-section="jobs" hidden>
26
- <section id="global-jobs"></section>
27
- </section>
28
25
  <section class="manage-section" data-section="server" aria-labelledby="settings-server-title" hidden>
29
26
  ${manageHead("settings-server-title", "settings.section.server", "Agents & server", "settings.hint.server", "Desired configuration for Yui, Roles, Agents and Profiles. Saving targets later launches; it is not proof that existing Sessions adopted it.")}
30
27
  <label class="field settings-search"><span data-i18n="settings.search">Find a setting</span><input id="settings-search" type="search" autocomplete="off"></label>
@@ -24,7 +24,6 @@ export const SIDEBAR_HTML = ` <aside class="sidebar" aria-label="Tasks" data-
24
24
  </label>
25
25
  <button id="body-search-open" class="icon-btn side-search-body" type="button" aria-label="Search task contents" data-i18n-aria-label="bodySearch.title" title="Search task contents" data-i18n-title="bodySearch.title">${iconSvg("textSearch")}</button>
26
26
  </div>
27
- <div id="catalog-source" class="seg seg-sm source-seg" role="radiogroup" aria-label="Task category" data-i18n-aria-label="jobs.taskCategory"></div>
28
27
  <div id="attention-bar" class="attention-bar" aria-live="polite"></div>
29
28
  </div>
30
29
  <div id="status-filters" class="status-tabs" role="group" aria-label="Filter by status" data-i18n-aria-label="filters.label"></div>
@@ -6,21 +6,55 @@
6
6
  */
7
7
  export const CONVERSATION_STYLES = `
8
8
  #dock-conversation{min-width:0}
9
+ /* Session page: one view at a time (chat or terminal), rebuilt on a mode switch */
10
+ .session-view{flex:1;min-height:0;min-width:0;display:flex;flex-direction:column}
11
+ .sp-modes{flex:none}
12
+ .sp-modes .seg-btn{gap:5px}
13
+ .sp-modes .seg-btn .icon{width:14px;height:14px;flex:none}
14
+ .sp-modes .seg-btn[aria-disabled="true"]{opacity:.45}
15
+ @container dock (max-width:440px){.sp-modes .seg-btn>span{display:none}.sp-modes .seg-btn{padding:0 7px}}
9
16
  .cv-head{flex:none;display:flex;align-items:center;gap:6px;padding:8px 10px 4px 8px}
10
- .cv-picker{flex:1;min-width:0;display:flex;align-items:center;gap:6px}
11
- /* background-color only: the shorthand would drop the global select arrow */
12
- .cv-session{flex:1;min-width:0;max-width:100%;height:30px;padding:0 28px 0 8px;border:0;background-color:transparent;color:var(--ink);font-weight:600;font-size:var(--fs-md);text-overflow:ellipsis;cursor:pointer;border-radius:var(--r-sm)}
13
- .cv-session:hover,.cv-session:focus{background-color:var(--surface-3);border:0;box-shadow:none}
14
- /* The global select:focus rule resets the whole background, so the arrow is restated for every state */
15
- .cv-session,.cv-session:hover,.cv-session:focus{background-image:linear-gradient(45deg,transparent 50%,var(--ink-3) 50%),linear-gradient(135deg,var(--ink-3) 50%,transparent 50%);background-position:calc(100% - 15px) 50%,calc(100% - 11px) 50%;background-size:4px 4px;background-repeat:no-repeat}
16
- .cv-status{flex:none;display:flex;align-items:center;gap:6px;margin:0;padding:0 16px 6px;color:var(--ink-3);font-size:var(--fs-xs)}
17
+ .cv-head .spacer{flex:1 1 0;min-width:0}
18
+ /* Session picker: one readable name; current and previous Sessions on demand */
19
+ .cv-pop.cv-picker{flex:0 1 auto;min-width:0;max-width:100%}
20
+ .cv-picker-btn{display:flex;align-items:center;gap:4px;min-width:0;height:30px;padding:0 6px 0 8px;border-radius:var(--r-sm);color:var(--ink);font-weight:600;font-size:var(--fs-md);cursor:pointer}
21
+ .cv-picker-btn:hover,.cv-picker[open]>.cv-picker-btn{background:var(--surface-3)}
22
+ .cv-picker-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
23
+ .cv-picker-chevron{width:13px;height:13px;flex:none;color:var(--ink-3)}
24
+ .cv-picker>.cv-popbody{left:0;top:calc(100% + 6px);width:min(320px,calc(100vw - 32px))}
25
+ .cv-session-list{display:grid;gap:1px}
26
+ .cv-session-opt{display:flex;align-items:center;gap:8px;width:100%;min-width:0;padding:6px 8px;border:0;border-radius:var(--r-sm);background:transparent;color:var(--ink);text-align:left;cursor:pointer}
27
+ .cv-session-opt:hover,.cv-session-opt[aria-selected=true]{background:var(--surface-3)}
28
+ .cv-so-dot{width:7px;height:7px;border-radius:50%;flex:none;background:var(--idle)}
29
+ .cv-so-dot.is-active{background:var(--ok)}
30
+ .cv-so-dot.is-failed{background:var(--bad)}
31
+ .cv-so-text{flex:1;min-width:0;display:grid}
32
+ .cv-so-main,.cv-so-meta{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
33
+ .cv-so-main{font-size:var(--fs-sm)}
34
+ .cv-so-meta{color:var(--ink-3);font-size:var(--fs-2xs)}
35
+ .cv-so-check{width:14px;height:14px;flex:none}
36
+ .cv-more-sessions{justify-self:start}
37
+ .cv-back{display:inline-flex;align-items:center;gap:3px;flex:none;white-space:nowrap}
38
+ .cv-back .icon{width:13px;height:13px}
39
+ .cv-back[hidden]{display:none}
40
+ /* Status: a dot plus one word; the reason and observation time live in the tooltip */
41
+ .cv-status{flex:0 1 auto;min-width:0;max-width:40%;display:inline-flex;align-items:center;gap:6px;color:var(--ink-3);font-size:var(--fs-xs);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
17
42
  .cv-status::before{content:"";width:7px;height:7px;border-radius:50%;background:var(--idle);flex:none}
18
43
  .cv-status.is-active::before{background:var(--ok)}
19
44
  .cv-status.is-waiting::before{background:var(--warn)}
20
45
  .cv-status.is-failed::before,.cv-status.is-unknown::before{background:var(--bad)}
21
46
  .cv-status:empty{display:none}
22
- .cv-older{flex:none;display:flex;justify-content:center}
23
- .cv-older:empty{display:none}
47
+ /* Details menu: labelled facts, then the exact identity for copying */
48
+ .cv-facts{display:grid;grid-template-columns:auto minmax(0,1fr);gap:4px 10px;margin:0;padding:0 4px;font-size:var(--fs-xs)}
49
+ .cv-facts dt{color:var(--ink-3)}
50
+ .cv-facts dd{margin:0;min-width:0;display:grid;color:var(--ink);overflow-wrap:anywhere}
51
+ .cv-facts small{color:var(--ink-4);font-size:var(--fs-2xs)}
52
+ .cv-identity-row{display:flex;align-items:flex-start;gap:6px;padding:0 4px;min-width:0}
53
+ .cv-identity-row .cv-identity{flex:1;min-width:0;font-family:var(--font-mono)}
54
+ .cv-identity-row .cv-link{display:inline-flex;align-items:center;gap:3px;flex:none}
55
+ .cv-identity-row .icon{width:12px;height:12px}
56
+ .cv-older{position:absolute;z-index:1;top:6px;left:50%;transform:translateX(-50%);padding:3px 10px;border:1px solid var(--line-2);border-radius:var(--r-pill);background:var(--surface-2);box-shadow:var(--shadow-1)}
57
+ .cv-older[hidden]{display:none}
24
58
  .cv-stage{position:relative;flex:1;min-height:0;display:flex;flex-direction:column}
25
59
  .cv-stage .feed{gap:18px;padding:12px 16px 20px}
26
60
  .cv-jump{position:absolute;left:50%;bottom:10px;transform:translateX(-50%);width:32px;height:32px;display:grid;place-items:center;padding:0;border:1px solid var(--line-2);border-radius:50%;background:var(--surface-2);color:var(--ink);box-shadow:var(--shadow-1);cursor:pointer}
@@ -29,14 +63,16 @@ export const CONVERSATION_STYLES = `
29
63
  .cv-note{margin:0;color:var(--ink-3);font-size:var(--fs-xs);overflow-wrap:anywhere}
30
64
  .cv-link{padding:2px 4px;border:0;background:none;color:var(--ink-3);font-size:var(--fs-xs);cursor:pointer;border-radius:var(--r-xs)}
31
65
  .cv-link:hover{color:var(--ink);background:var(--surface-3)}
32
- .cv-link[hidden],.cv-stop[hidden],.cv-chip[hidden]{display:none}
66
+ .cv-link[hidden],.cv-stop[hidden],.cv-sub[hidden],.cv-pop[hidden]{display:none}
33
67
  .cv-code{margin:6px 0 0;padding:8px 10px;max-height:200px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;border-radius:var(--r-sm);background:var(--sunken);font:var(--fs-xs)/1.5 var(--font-mono)}
68
+ .cv-note:empty{display:none}
34
69
  .cv-identity{margin:0;color:var(--ink-4);font-size:var(--fs-2xs);overflow-wrap:anywhere}
35
70
  /* Messages */
36
71
  .cv-msg{min-width:0;overflow-wrap:anywhere}
37
72
  .cv-user{align-self:flex-end;max-width:88%;display:flex;flex-direction:column;align-items:flex-end;gap:4px}
38
73
  .cv-bubble{padding:9px 14px;border-radius:18px;background:var(--info-soft);color:var(--ink);font-size:var(--fs-md);line-height:1.5}
39
74
  .cv-tag{color:var(--ink-4);font-size:var(--fs-2xs)}
75
+ /* A leading tagged context block folds to one line; the raw text stays one click away */
40
76
  .cv-assistant{position:relative;font-size:var(--fs-md);line-height:1.6}
41
77
  /* Copy: always visible under the newest message, a hover badge on older ones */
42
78
  .cv-actions{display:flex;gap:2px;position:absolute;top:-6px;right:0;opacity:0;transition:opacity var(--t-fast)}
@@ -75,14 +111,27 @@ export const CONVERSATION_STYLES = `
75
111
  /* An empty draft reads as inactive, like the reference composers */
76
112
  .cv-box:has(.cv-input:placeholder-shown) .cv-send{background:var(--surface-3);color:var(--ink-4)}
77
113
  .cv-send:disabled{background:var(--surface-3);color:var(--ink-4);cursor:default;opacity:1}
78
- .cv-chip{display:inline-flex;align-items:center;gap:5px;min-width:0;padding:3px 8px;border-radius:var(--r-pill);background:var(--tone-soft,var(--warn-soft));color:var(--tone,var(--warn));font-size:var(--fs-xs)}
79
- .cv-chip .icon{width:13px;height:13px;flex:none}
80
- .cv-chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
81
- .cv-pill{display:inline-flex;align-items:center;gap:4px;max-width:180px;height:28px;padding:0 8px;border-radius:var(--r-pill);color:var(--ink-3);font-size:var(--fs-sm);list-style:none;cursor:pointer}
114
+ .cv-notice{display:flex;align-items:center;gap:6px;min-width:0;margin:0;padding:0 6px;color:var(--ink-3);font-size:var(--fs-xs)}
115
+ .cv-notice.is-warn{color:var(--warn)}
116
+ .cv-notice[hidden]{display:none}
117
+ .cv-notice .icon{width:13px;height:13px;flex:none}
118
+ .cv-notice>span{min-width:0;overflow-wrap:anywhere}
119
+ .cv-notice .cv-link{flex:none;margin-left:auto}
120
+ .cv-pill{display:inline-flex;align-items:center;gap:4px;max-width:220px;height:28px;padding:0 8px;border-radius:var(--r-pill);color:var(--ink-3);font-size:var(--fs-sm);list-style:none;cursor:pointer}
82
121
  .cv-pill::-webkit-details-marker{display:none}
83
122
  .cv-pill:hover,.cv-pop[open]>.cv-pill{background:var(--surface-3);color:var(--ink)}
84
123
  .cv-pill .icon{width:13px;height:13px;flex:none}
124
+ .cv-model-state{flex:none;font-size:var(--fs-xs);font-weight:600;color:var(--ink-2)}
125
+ .cv-model-state[hidden]{display:none}
126
+ .cv-model[data-state=pending] .cv-model-state{color:var(--accent,var(--info))}
127
+ /* The model menu shrinks before the send button is pushed out; the state word stays whole and the model name ellipsizes */
128
+ .cv-pop.cv-model{flex:0 1 auto;min-width:0}
129
+ .cv-model>.cv-pill{max-width:min(220px,100%);box-sizing:border-box}
85
130
  .cv-model-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
131
+ .cv-model-dot{width:6px;height:6px;border-radius:50%;flex:none;background:var(--ok)}
132
+ .cv-model[data-state=unknown] .cv-model-dot{background:var(--warn)}
133
+ .cv-model[data-state=pending] .cv-model-dot{background:var(--accent,var(--info))}
134
+ .cv-model[data-state=unknown] .cv-model-label{font-style:italic}
86
135
  .cv-hint{display:flex;flex-wrap:wrap;align-items:center;gap:2px 8px;padding:0 6px;min-width:0}
87
136
  .cv-hint:empty{display:none}
88
137
  .cv-hint .cv-note:empty{display:none}
@@ -100,7 +149,37 @@ export const CONVERSATION_STYLES = `
100
149
  .cv-pop>summary::-webkit-details-marker{display:none}
101
150
  .cv-popbody{position:absolute;z-index:var(--z-popover);display:grid;gap:8px;width:min(300px,calc(100vw - 32px));max-height:min(420px,60vh);overflow:auto;padding:10px;border:1px solid var(--line-2);border-radius:var(--r-lg);background:var(--surface-2);box-shadow:var(--shadow-2);box-sizing:border-box}
102
151
  .cv-attach>.cv-popbody{left:0;bottom:calc(100% + 8px)}
103
- .cv-model>.cv-popbody{right:0;bottom:calc(100% + 8px)}
152
+ .cv-model>.cv-popbody{right:0;bottom:calc(100% + 8px);width:min(300px,calc(100vw - 24px));gap:0;padding:6px}
153
+ /* Model picker: two quiet facts, a short list, one action row. Background
154
+ polls update text in place and never toggle these controls. */
155
+ .cv-model .cv-model-facts{grid-template-columns:auto minmax(0,1fr);gap:5px 14px;align-items:baseline;margin:0;padding:8px 10px 10px;font-size:var(--fs-xs)}
156
+ .cv-model .cv-model-facts dt{color:var(--ink-3);white-space:nowrap}
157
+ .cv-model .cv-model-facts dd{display:flex;align-items:baseline;gap:8px;min-width:0}
158
+ .cv-mf-value{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink);font-size:var(--fs-sm);font-weight:600}
159
+ .cv-mf-value.is-empty{color:var(--ink-3);font-weight:400}
160
+ .cv-model .cv-model-facts .cv-mf-note{flex:none;margin-left:auto;color:var(--ink-4);font-size:var(--fs-2xs);white-space:nowrap}
161
+ .cv-mf-note[hidden]{display:none}
162
+ .cv-model[data-state=pending] .cv-mf-note{color:var(--accent)}
163
+ .cv-model .cv-options{gap:2px;max-height:216px;overflow:auto;min-height:0;padding:6px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
164
+ .cv-model .cv-options:empty{display:none}
165
+ .cv-model .cv-option{gap:10px;min-height:36px;padding:6px 10px;font-size:var(--fs-sm);transition:background var(--t-fast),color var(--t-fast)}
166
+ .cv-model .cv-option:hover:not(:disabled){background:var(--surface-3)}
167
+ .cv-model .cv-option.is-selected{background:var(--accent-soft)}
168
+ .cv-model .cv-option.is-selected .cv-option-name{font-weight:600}
169
+ .cv-model .cv-option.is-selected .icon{color:var(--accent)}
170
+ .cv-model .cv-option:disabled{cursor:default;color:var(--ink-3)}
171
+ .cv-model .cv-option.is-selected:hover:not(:disabled){background:var(--accent-soft)}
172
+ .cv-model .cv-option:disabled:not(.is-selected){background:transparent}
173
+ .cv-option-mark{width:6px;height:6px;flex:none;border-radius:50%;background:transparent}
174
+ .cv-option.is-current .cv-option-mark{background:var(--ok)}
175
+ .cv-model .cv-option-id{display:block;flex:0 1 auto;min-width:0;max-width:45%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink-4);font:var(--fs-2xs)/1.4 var(--font-mono)}
176
+ .cv-model .cv-pop-actions{flex-wrap:wrap;justify-content:flex-end;gap:6px 8px;padding:8px 4px 2px 10px}
177
+ .cv-model-notes{flex:1 1 140px;min-width:0;display:grid;gap:2px}
178
+ .cv-model-notes:not(:has(.cv-note:not(:empty))){display:none}
179
+ .cv-model .cv-pop-actions .cv-link{flex:none}
180
+ .cv-model .cv-note:empty{display:none}
181
+ .cv-apply{height:28px;padding:0 12px;flex:none}
182
+ .cv-apply:disabled{background:var(--surface-3);color:var(--ink-4);opacity:1}
104
183
  .cv-menu>.cv-popbody{right:0;top:calc(100% + 6px)}
105
184
  .cv-pop-title{margin:0;padding:0 4px;color:var(--ink-3);font-size:var(--fs-xs)}
106
185
  .cv-pop-actions{display:flex;align-items:center;justify-content:space-between;gap:8px}
@@ -123,11 +202,11 @@ export const CONVERSATION_STYLES = `
123
202
  .cv-card label{display:grid;gap:4px}
124
203
  .cv-card input,.cv-card select,.cv-card textarea{min-width:0;max-width:100%;box-sizing:border-box}
125
204
  /* Keyboard focus */
126
- .cv-iconbtn:focus-visible,.cv-send:focus-visible,.cv-stop:focus-visible,.cv-jump:focus-visible,.cv-pill:focus-visible,.cv-option:focus-visible,.cv-link:focus-visible,.cv-cmd:focus-visible,.cv-x:focus-visible,.cv-activity>summary:focus-visible,.cv-sub>summary:focus-visible{outline:none;box-shadow:var(--focus)}
127
- .cv-session:focus-visible{box-shadow:var(--focus)}
205
+ .cv-iconbtn:focus-visible,.cv-send:focus-visible,.cv-stop:focus-visible,.cv-jump:focus-visible,.cv-pill:focus-visible,.cv-option:focus-visible,.cv-link:focus-visible,.cv-cmd:focus-visible,.cv-x:focus-visible,.cv-activity>summary:focus-visible,.cv-sub>summary:focus-visible,.cv-picker-btn:focus-visible,.cv-session-opt:focus-visible{outline:none;box-shadow:var(--focus)}
128
206
  .cv-input:focus-visible{outline:none;box-shadow:none}
129
207
  @media(max-width:760px){
130
208
  .cv-user{max-width:94%}
209
+ .cv-status{max-width:30%}
131
210
  .cv-composer{padding-bottom:max(12px,env(safe-area-inset-bottom,0px))}
132
211
  }
133
212
  `;
@@ -1,10 +1,10 @@
1
1
  /*
2
2
  * DOCK VIEW — the Discussion pane: its sub-header, the message feed with day
3
3
  * separators and bubbles, the composer, submission facets and next steps.
4
- * The Live session pane is views/terminal; the dock frame is layout/workspace.
4
+ * The session page is views/conversation and views/terminal; the dock frame is layout/workspace.
5
5
  */
6
6
  export const DOCK_STYLES = `
7
- .dock-head{height:auto;min-height:var(--header-h);padding-top:6px;padding-bottom:6px;border-bottom:0}
7
+ .dock-head{height:auto;min-height:var(--header-h);padding-top:6px;padding-bottom:6px}
8
8
  .dock-actions{flex-shrink:0}
9
9
  /* Who the panel talks to: a title-like menu button with its target list */
10
10
  .dock-target{position:relative;min-width:0}
@@ -24,9 +24,21 @@ export const DOCK_STYLES = `
24
24
  .dock-target-item:hover{background:var(--surface-3);color:var(--ink)}
25
25
  .dock-target-item[aria-pressed="true"]{color:var(--ink);font-weight:550}
26
26
  .dock-target-item .dock-target-check{color:var(--accent)}
27
- .dock-tabs{flex:none;padding:0 12px 10px;border-bottom:1px solid var(--line)}
28
- .dock-tabs .seg{display:flex;flex-wrap:wrap;width:100%;min-width:0}
29
- .dock-tabs .seg-btn{flex:1 1 auto;justify-content:center}
27
+ .dock-target-dot{width:7px;height:7px;margin:0 4px;flex:none;border-radius:50%;background:var(--idle)}
28
+ .dock-target-dot.is-live{background:var(--ok)}
29
+ /* One header row: target, view tabs, actions. The target's name wins space
30
+ first: icon-only tabs up to 480px, the selected label up to 560px, then
31
+ every label. Tabs keep aria-label and title at every width. */
32
+ .dock{container-type:inline-size;container-name:dock}
33
+ .dock-target{flex:0 1 auto}
34
+ .dock-tabs{flex:0 0 auto;display:flex;min-width:0;margin-left:auto}
35
+ .dock-tabs .seg-btn{justify-content:center;gap:5px;min-width:0}
36
+ .dock-tabs .seg-btn .icon{width:14px;height:14px;flex:none}
37
+ .dock-tabs .seg-btn[aria-selected="false"]>span{display:none}
38
+ .dock-tabs .seg-btn>span{white-space:nowrap}
39
+ @container dock (min-width:560px){.dock-tabs .seg-btn[aria-selected="false"]>span{display:inline}}
40
+ @container dock (max-width:480px){.dock-tabs .seg-btn>span{display:none}}
41
+ @container dock (max-width:340px){.dock-target-icon{display:none}}
30
42
  .dock-preview{display:flex;flex-direction:column;min-height:0;overflow:auto}
31
43
  .dock-preview>.feed-note{text-align:left;padding:0 12px;color:var(--ink-3)}
32
44
  .preview-services{flex:none;max-height:40%;overflow:auto}