@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
@@ -111,10 +111,6 @@ function renderSidebar(deps, progress) {
111
111
  reset(deps, progress);
112
112
  });
113
113
  el.catalogCount.textContent = fill(t("catalog.count"), { shown: state.tasks.length, total: state.catalogTotal });
114
- renderSource(el.source, state, t, function (value) {
115
- state.source = value; state.attentionFilter = null; state.query = ""; el.search.value = "";
116
- syncUrl(state, false); reset(deps, progress);
117
- });
118
114
  el.catalogReset.hidden = !state.catalogCursor;
119
115
  el.catalogNext.hidden = !state.nextCursor && !state.catalogCursor;
120
116
  el.catalogAttentionReset.hidden = !state.attentionFilter;
@@ -122,27 +118,6 @@ function renderSidebar(deps, progress) {
122
118
  el.lastSync.textContent = state.generatedAt ? formatClock(state.generatedAt, deps.locale()) : "—";
123
119
  }
124
120
 
125
- // Task category: search and attention are cross-category, so they show "all";
126
- // picking a category clears them, as the server filter then applies again.
127
- function renderSource(container, state, t, onPick) {
128
- const current = state.query.trim() || state.attentionFilter ? "all" : state.source || "ordinary";
129
- // Bound once; the handler reads the live state, not this render's snapshot.
130
- if (!container.childElementCount) {
131
- ["ordinary", "job", "all"].forEach(function (value) {
132
- container.append(h("button.seg-btn", { type: "button", role: "radio", dataset: { source: value },
133
- onclick: function () {
134
- if (value !== (state.source || "ordinary") || state.query.trim() || state.attentionFilter) onPick(value);
135
- } }));
136
- });
137
- }
138
- container.querySelectorAll("[data-source]").forEach(function (button) {
139
- const value = button.dataset.source;
140
- button.textContent = t("jobs.categoryShort." + value);
141
- button.title = t("jobs.category." + value);
142
- button.setAttribute("aria-checked", String(value === current));
143
- });
144
- }
145
-
146
121
  // Redraw the list in place, keeping its scroll position.
147
122
  function renderTaskList(deps) {
148
123
  const scroll = deps.el.tasks.scrollTop;
@@ -175,9 +150,6 @@ function catalogQuery(state) {
175
150
  if (state.filter !== "all") query.set("status", state.filter);
176
151
  if (state.query.trim()) query.set("search", state.query.trim());
177
152
  if (state.attentionFilter) query.set("attention", state.attentionFilter);
178
- // Search and attention remain cross-category discovery; the server owns filtering.
179
- if (!state.query.trim() && !state.attentionFilter && state.source !== "all")
180
- query.set("source", state.source || "ordinary");
181
153
  if (state.catalogCursor) query.set("cursor", state.catalogCursor);
182
154
  return query.toString();
183
155
  }
@@ -233,7 +205,6 @@ function applyFilters(deps, params) {
233
205
  state.query = params.get("q") || "";
234
206
  state.attentionFilter = ATTENTION_KINDS.includes(params.get("attention")) ? params.get("attention") : null;
235
207
  state.catalogAll = params.get("all") === "true";
236
- state.source = ["ordinary", "job", "all"].includes(params.get("source")) ? params.get("source") : "ordinary";
237
208
  deps.el.search.value = state.query;
238
209
  }
239
210
 
@@ -0,0 +1,115 @@
1
+ export const JOBS_APP_SCRIPT = String.raw `
2
+ import { h, icon } from "/assets/js/lib/dom.js";
3
+ import { formatClock } from "/assets/js/lib/format.js";
4
+ import { requestJson } from "/assets/js/lib/api.js";
5
+ import { i18n, registerManagePage, navigateManage, replaceManageRoute, pushManageRoute } from "/assets/js/layout/manageShell.js";
6
+ import { mountJobs } from "/assets/js/views/jobs.js";
7
+
8
+ // The Jobs entry: one shared Job module (the same one the Task page uses)
9
+ // over exactly one scope at a time, global or one owning Task. The scope,
10
+ // the selected Job and Run live in the URL, so deep links, refresh and
11
+ // Back/Forward all reopen the same view. One view is live at a time: a
12
+ // replaced view is retired, so its late reads cannot write this page.
13
+ const t = i18n.t;
14
+ const root = document.querySelector("#jobs-root");
15
+ const index = document.querySelector("#jobs-index");
16
+ const scopeSelect = h("select", { id: "jobs-scope", "aria-describedby": "jobs-scope-help" });
17
+ const scopeLabel = h("label", { for: "jobs-scope" });
18
+ const scopeHelp = h("p.jobs-scope-help", { id: "jobs-scope-help" });
19
+ const listMount = h("div.jobs-index-body");
20
+ // The list head works as on the workbench: one primary "new" action at the
21
+ // top of the list (creating in the selected scope), the last read time and
22
+ // a refresh control in the head. The page itself carries no second title.
23
+ const createLabel = h("span");
24
+ const create = h("button.btn.btn-primary.side-create", { id: "jobs-create", type: "button",
25
+ onclick: () => { if (view) view.create(); } }, icon("plus"), createLabel);
26
+ const sync = document.querySelector("#jobs-sync");
27
+ const lastSync = document.querySelector("#jobs-last-sync");
28
+ const refreshButton = document.querySelector("#jobs-refresh");
29
+ if (refreshButton) refreshButton.addEventListener("click", () => { if (view && !view.isBusy()) void view.refresh(); });
30
+ index.replaceChildren(create, h("div.jobs-scope", null, scopeLabel, scopeSelect, scopeHelp), listMount);
31
+ let route = null, view = null, mountedScope, scopes = null, scopesError = null;
32
+
33
+ scopeSelect.addEventListener("change", () => {
34
+ const task = scopeSelect.value || null;
35
+ if ((route && route.task) === task) return;
36
+ if (!navigateManage({ page: "jobs", section: "jobs", task })) drawScopes();
37
+ });
38
+
39
+ function drawScopes() {
40
+ createLabel.textContent = t("jobs.newJob");
41
+ create.title = t("jobs.newJobIn") + " · " + (route && route.task ? route.task : t("jobs.ownerGlobal"));
42
+ scopeLabel.textContent = t("jobs.scopeLabel");
43
+ scopeHelp.textContent = scopesError ? t("jobs.scopesError") + " " + scopesError : t("jobs.scopeHelp");
44
+ scopeHelp.classList.toggle("is-error", !!scopesError);
45
+ const current = route ? route.task : null;
46
+ const tasks = scopes ? scopes.tasks.slice() : [];
47
+ // A deep-linked Task stays selectable even when it has no Job yet.
48
+ if (current && !tasks.some(item => item.taskId === current)) tasks.unshift({ taskId: current, title: "" });
49
+ scopeSelect.replaceChildren(h("option", { value: "" }, t("jobs.ownerGlobal")),
50
+ ...tasks.map(item => h("option", { value: item.taskId }, item.taskId + (item.title ? " · " + item.title : ""))));
51
+ scopeSelect.value = current || "";
52
+ }
53
+ async function loadScopes() {
54
+ try { scopes = await requestJson("/api/jobs/scopes"); scopesError = null; }
55
+ catch (error) { scopesError = error.message; }
56
+ drawScopes();
57
+ }
58
+ function mount(next) {
59
+ if (view) view.dispose();
60
+ mountedScope = next.task || null;
61
+ let self = null;
62
+ self = view = mountJobs(root, t, mountedScope || undefined, {
63
+ index: listMount,
64
+ navigate: selection => navigateManage({ ...route, job: selection.job, jobRun: null, artifact: null, moved: undefined }),
65
+ pin: (selection, step) => pin(self, selection, step),
66
+ sync: observation => observe(self, observation),
67
+ notice: next.moved ? t("jobs.moved") : "",
68
+ locale: i18n.getLocale
69
+ });
70
+ }
71
+ // The last read of the live view, in the list head (as the workbench's sync).
72
+ function observe(owner, observation) {
73
+ if (owner !== view || !sync) return;
74
+ sync.dataset.state = observation.state;
75
+ if (observation.at) lastSync.textContent = formatClock(observation.at, i18n.getLocale());
76
+ sync.title = observation.text;
77
+ }
78
+ // Only the live view, while Jobs is the shown page, records its selection.
79
+ // A Run the reader opened or closed in place is a history step; anything else
80
+ // the view settles on refines the current entry.
81
+ function pin(owner, selection, step) {
82
+ if (owner !== view || root.hidden || !route || route.page !== "jobs") return;
83
+ route = { ...route, ...selection, moved: undefined };
84
+ if (step) pushManageRoute(route); else replaceManageRoute(route);
85
+ }
86
+ function show(next) {
87
+ route = next;
88
+ drawScopes();
89
+ if (!view || mountedScope !== (next.task || null)) mount(next);
90
+ else { view.resume(); view.select(next.job, next.jobRun, next.artifact); }
91
+ }
92
+ // Leaving Jobs sets a modal editor aside (its draft is kept for the return).
93
+ function hide() { if (view) view.suspend(); }
94
+ // Asked before any route change that leaves or enters Jobs, whatever its
95
+ // source. Leaving Jobs keeps its view (hidden) as it is; a change of scope or
96
+ // Job replaces the editor, so a draft needs confirmation; while a write is in
97
+ // flight the selection stays put so its receipt remains observable.
98
+ function canNavigate(next) {
99
+ if (!view || next.page !== "jobs" || !route) return true;
100
+ const same = key => (next[key] || null) === (route[key] || null);
101
+ if ((next.task || null) === mountedScope && same("job") && same("jobRun") && same("artifact")) return true;
102
+ if (view.isBusy() && root.hidden) { window.alert(t("jobs.leaveBusy")); return false; }
103
+ return view.leave((next.task || null) !== mountedScope || !same("job"));
104
+ }
105
+ registerManagePage("jobs", { show, hide, canNavigate, crumb: () => route && route.task ? route.task : t("jobs.ownerGlobal") });
106
+ void loadScopes();
107
+ i18n.subscribe(() => {
108
+ drawScopes();
109
+ if (route && route.page === "jobs" && view && !view.isDirty() && !view.isBusy()) mount({ ...route, moved: false });
110
+ });
111
+ window.addEventListener("beforeunload", event => {
112
+ if (!view || !view.isDirty()) return;
113
+ event.preventDefault(); event.returnValue = "";
114
+ });
115
+ `;
@@ -7,7 +7,8 @@ export const APP_SCRIPT = String.raw `
7
7
  // Everything kept in the browser is view state; Task facts always come from
8
8
  // the loopback API.
9
9
  //
10
- // layout/workspace sidebar | center | dock columns, dock modes, sizing
10
+ // layout/workspace sidebar | center | dock columns, dock views, sizing
11
+ // views/dock/session the dock's session page: one Role and Session, chat or terminal
11
12
  // app/taskView draws the selected Task, the dock discussion, the overview
12
13
  // app/selection selected Task, its detail reads, URL-driven navigation
13
14
  // app/catalog refresh loop, sidebar, catalog paging and filters
@@ -17,21 +18,20 @@ export const APP_SCRIPT = String.raw `
17
18
  // refresh catalog.refresh → sidebar, then the overview (nothing selected)
18
19
  // or selection.reload → taskView.render
19
20
  // select sidebar row / overview row / URL → selection.selectTask
20
- // leave rail Workbench or Escape → selection.clearSelection
21
+ // leave rail Tasks or Escape → selection.clearSelection
21
22
  // write Task page or discussion afterWrite → quiet catalog refresh
22
23
  // URL load and back/forward → catalog.applyFilters + selection.followUrl
23
24
  import { createI18n } from "/assets/js/lib/i18n.js";
24
25
  import { createThemeController } from "/assets/js/lib/theme.js";
25
26
  import { api } from "/assets/js/lib/api.js";
26
27
  import { createToast } from "/assets/js/ui/toast.js";
27
- import { createTerminalController } from "/assets/js/views/dock/terminal.js";
28
- import { createConversationController } from "/assets/js/views/dock/conversation.js";
28
+ import { createSessionPage } from "/assets/js/views/dock/session.js";
29
29
  import { createPreviewController } from "/assets/js/views/dock/preview.js";
30
30
  import { bindGlobalInput } from "/assets/js/views/globalInput.js";
31
31
  import { bindCreateTask } from "/assets/js/views/createTask.js";
32
32
  import { bindBodySearch } from "/assets/js/views/bodySearch.js";
33
- import { readQuery } from "/assets/js/layout/router.js";
34
- import { rememberPlace, linkPlaces } from "/assets/js/lib/prefs.js";
33
+ import { readQuery, applyUrl } from "/assets/js/layout/router.js";
34
+ import { rememberPlace, linkPlaces, readSessionAccessMode } from "/assets/js/lib/prefs.js";
35
35
  import { createWorkspace } from "/assets/js/layout/workspace.js";
36
36
  import { createSidebarNav } from "/assets/js/layout/sidebarNav.js";
37
37
  import { bindShortcuts } from "/assets/js/layout/shortcuts.js";
@@ -43,7 +43,7 @@ const $ = function (selector) { return document.querySelector(selector); };
43
43
  const el = {
44
44
  sidebar: $(".sidebar"), rail: $(".app-rail"), railWorkbench: $("#rail-workbench"),
45
45
  sidebarScrim: $("#sidebar-scrim"), sidebarCollapse: $("#sidebar-collapse"), sidebarDivider: $("#sidebar-divider"), center: $("#center"), detail: $("#detail"),
46
- search: $("#search"), source: $("#catalog-source"), filters: $("#status-filters"), attention: $("#attention-bar"), tasks: $("#task-list"),
46
+ search: $("#search"), filters: $("#status-filters"), attention: $("#attention-bar"), tasks: $("#task-list"),
47
47
  catalogNext: $("#catalog-next"), catalogReset: $("#catalog-reset"), catalogCount: $("#catalog-count"),
48
48
  catalogAttentionReset: $("#catalog-attention-reset"),
49
49
  refresh: $("#refresh"), sync: $("#sync-state"), lastSync: $("#last-sync"), toast: $("#toast"),
@@ -51,12 +51,10 @@ const el = {
51
51
  dockTargetOptions: $("#dock-target-options"), globalInputOpen: $("#global-input-open"), settings: $("#settings-dialog"),
52
52
  locale: $("#locale-select"), themeOptions: $("#theme-options"),
53
53
  dock: $("#dock"), divider: $("#dock-divider"), dockSwap: $("#dock-swap"), dockClose: $("#dock-close"),
54
- dockTabDiscussion: $("#dock-tab-discussion"), dockTabSession: $("#dock-tab-session"),
54
+ dockTabDiscussion: $("#dock-tab-discussion"),
55
55
  dockTabConversation: $("#dock-tab-conversation"), conversation: $("#dock-conversation"),
56
56
  dockTabPreview: $("#dock-tab-preview"), preview: $("#dock-preview"),
57
- discussion: $("#dock-discussion"), session: $("#dock-session"),
58
- terminalHost: $("#terminal-host"), terminalEmpty: $("#terminal-empty"), terminalState: $("#terminal-state"),
59
- sessionTargets: $("#session-targets"), settingsOpen: $("#settings-open"), terminalCli: $("#terminal-cli")
57
+ discussion: $("#dock-discussion"), settingsOpen: $("#settings-open")
60
58
  };
61
59
 
62
60
  // View state. Each group has exactly one writer.
@@ -77,28 +75,23 @@ const t = i18n.t;
77
75
  const locale = i18n.getLocale;
78
76
  const theme = createThemeController(el.themeOptions, t);
79
77
  const toast = createToast(el.toast);
80
- const conversation = createConversationController(el.conversation, t, locale);
81
78
  const preview = createPreviewController(el.preview, t);
82
- const terminal = createTerminalController({
83
- host: el.terminalHost, empty: el.terminalEmpty, state: el.terminalState, targets: el.sessionTargets, cli: el.terminalCli
84
- }, t, locale, toast);
85
- theme.subscribe(function () { terminal.retheme(); });
79
+ // The mode changed inside the session page: the dock header follows it.
80
+ const session = createSessionPage(el.conversation, t, locale, toast, function () { workspace.syncDockButtons(); });
81
+ theme.subscribe(function () { session.retheme(); });
86
82
 
87
83
  // Controllers are created in dependency order; the arrow callbacks below are
88
84
  // the only edges pointing at controllers created later.
89
85
  let taskView = null;
90
86
  let selection = null;
91
87
  let catalog = null;
92
- const workspace = createWorkspace({
93
- el: el, state: state, t: t, terminal: terminal, conversation: conversation, preview: preview,
94
- sessionOpened: function () { taskView.updateSessionTargets(); }
95
- });
88
+ const workspace = createWorkspace({ el: el, state: state, t: t, session: session, preview: preview });
96
89
  const sidebarNav = createSidebarNav({
97
90
  el: { sidebar: el.sidebar, rail: el.rail, scrim: el.sidebarScrim, collapse: el.sidebarCollapse },
98
91
  relayout: function () { workspace.updateLayout(); }
99
92
  });
100
93
  taskView = createTaskView({
101
- el: el, state: state, t: t, locale: locale, api: api, toast: toast, terminal: terminal, workspace: workspace,
94
+ el: el, state: state, t: t, locale: locale, api: api, toast: toast, workspace: workspace,
102
95
  selectTask: function (taskId) { return selection.selectTask(taskId); },
103
96
  clearSelection: function () { selection.clearSelection(); },
104
97
  revealSidebar: function () { sidebarNav.reveal(); },
@@ -121,16 +114,16 @@ bindCreateTask({ t: t, api: api, selectTask: selection.selectTask,
121
114
  afterWrite: function () { catalog.refresh({ quiet: true }); } });
122
115
  bindBodySearch({ t: t, api: api, selectTask: selection.selectTask,
123
116
  currentTask: function () { return state.selected; },
124
- captureDraftTarget: conversation.captureDraftTarget, insertReference: conversation.insertReference });
117
+ captureDraftTarget: session.captureDraftTarget, insertReference: session.insertReference });
125
118
  bindShortcuts({
126
119
  el: el, state: state, globalInput: globalInput, workspace: workspace, sidebarNav: sidebarNav,
127
120
  selection: selection, catalog: catalog, taskView: taskView
128
121
  });
129
- // Workbench <-> Models/Settings: each side remembers where the user was, so a
122
+ // Tasks <-> Jobs/Models/Settings: each side remembers where the user was, so a
130
123
  // round trip returns to the same filters, Task and management section.
131
124
  function rememberWorkbench() { rememberPlace("workbench", window.location.pathname + window.location.search); }
132
125
  el.rail.querySelectorAll("a[data-nav-area]").forEach(function (link) { link.addEventListener("click", rememberWorkbench); });
133
- // The rail's Workbench entry on the workbench itself returns to the overview.
126
+ // The rail's Tasks entry on the Task workbench itself returns to the overview.
134
127
  el.railWorkbench.addEventListener("click", function (event) {
135
128
  if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
136
129
  event.preventDefault();
@@ -142,9 +135,20 @@ function linkAll() { linkPlaces(el.rail); linkPlaces(el.sidebar); }
142
135
  window.addEventListener("pageshow", linkAll);
143
136
  linkAll();
144
137
 
138
+ // The retired Task category filter (?source=ordinary|job|all): the Task
139
+ // list now always shows every real Task, so drop the parameter in place
140
+ // (no extra history step) and say why once; Jobs have their own entry.
141
+ let sourceHinted = false;
142
+ function stripRetiredSource(params) {
143
+ if (!params.has("source")) return params;
144
+ params.delete("source");
145
+ applyUrl(params, true);
146
+ if (!sourceHinted) { sourceHinted = true; toast(t("catalog.sourceRemoved")); }
147
+ return params;
148
+ }
145
149
  // The URL names the catalog filters and the selected Task (with its section).
146
150
  function applyStateFromUrl() {
147
- const params = readQuery();
151
+ const params = stripRetiredSource(readQuery());
148
152
  catalog.applyFilters(params);
149
153
  selection.followUrl(params);
150
154
  }
@@ -155,7 +159,7 @@ window.addEventListener("popstate", function () {
155
159
 
156
160
  i18n.subscribe(function () {
157
161
  theme.render();
158
- terminal.relabel();
162
+ session.relabel();
159
163
  catalog.renderSidebar();
160
164
  taskView.relabel();
161
165
  workspace.updateLayout();
@@ -164,8 +168,8 @@ i18n.subscribe(function () {
164
168
  workspace.updateLayout();
165
169
  taskView.showOverview();
166
170
  // The panel starts closed: name its owner without polling until it opens.
167
- conversation.open({ scope: "global", roleName: "operator" });
168
- conversation.close();
171
+ session.open({ scope: "global", roleName: "operator" }, { mode: readSessionAccessMode() === "native" ? "terminal" : "chat" });
172
+ session.close();
169
173
  applyStateFromUrl();
170
174
  catalog.refresh();
171
175
  window.setInterval(function () { catalog.refresh({ quiet: true }); }, 5000);
@@ -1,6 +1,7 @@
1
1
  export const MANAGE_SCRIPT = String.raw `
2
2
  import "/assets/js/app/settings.js";
3
3
  import "/assets/js/app/models.js";
4
+ import "/assets/js/app/jobs.js";
4
5
  import { startManagement } from "/assets/js/layout/manageShell.js";
5
6
  import { createSidebarNav } from "/assets/js/layout/sidebarNav.js";
6
7
  import { isEditing } from "/assets/js/lib/dom.js";
@@ -8,7 +8,6 @@ import { createThemeController } from "/assets/js/lib/theme.js";
8
8
  import { clearPreference, readSessionAccessMode, writeSessionAccessMode } from "/assets/js/lib/prefs.js";
9
9
  import { i18n, createManageShell, pageSections, registerManagePage, navigateManage } from "/assets/js/layout/manageShell.js";
10
10
  import { mountSkills } from "/assets/js/views/skills.js";
11
- import { mountJobs } from "/assets/js/views/jobs.js";
12
11
  import { mountPermissions } from "/assets/js/views/permissions.js";
13
12
 
14
13
  const $ = selector => document.querySelector(selector);
@@ -20,14 +19,12 @@ function readSection() {
20
19
  return SECTIONS.includes(section) ? section : SECTIONS[0];
21
20
  }
22
21
  let section = readSection();
23
- let jobs;
24
22
  let permissions;
25
23
  const shell = createManageShell({ page: "settings", t, onNavigate: navigate });
26
24
  function showSection(next) {
27
25
  section = next;
28
26
  document.querySelectorAll("#settings-root > .manage-section[data-section]").forEach(panel => { panel.hidden = panel.dataset.section !== next; });
29
27
  shell.set(next);
30
- if (next === "jobs" && !jobs) jobs = mountJobs($("#global-jobs"), t);
31
28
  if (next === "permissions" && !permissions) permissions = mountPermissions($("#permission-browser"), t);
32
29
  }
33
30
  function navigate(next) {
@@ -41,7 +38,6 @@ function markDirty(name, value) {
41
38
  registerManagePage("settings", { show: next => showSection(next.section) });
42
39
  i18n.subscribe(() => shell.render());
43
40
  i18n.subscribe(() => {
44
- if (jobs && !jobs.isDirty() && !jobs.isBusy()) jobs = mountJobs($("#global-jobs"), t);
45
41
  permissions?.translate();
46
42
  });
47
43
  showSection(section);
@@ -69,7 +65,7 @@ let searchTimer;
69
65
  const more = h("button.btn", { type: "button", hidden: true }, t("settings.more"));
70
66
  container.after(more);
71
67
  window.addEventListener("beforeunload", event => {
72
- if (!groups.some(g => g.dirty) && !jobs?.isDirty()) return;
68
+ if (!groups.some(g => g.dirty)) return;
73
69
  event.preventDefault(); event.returnValue = "";
74
70
  });
75
71
 
@@ -12,7 +12,6 @@ import { renderOverview } from "/assets/js/views/overview.js";
12
12
  import { renderTaskDetail, selectTab, TABS, TAB_ALIASES } from "/assets/js/views/task/page.js";
13
13
  import { updateObservation } from "/assets/js/views/task/observation.js";
14
14
  import { renderDiscussion, discussionHasUnsent } from "/assets/js/views/dock/discussion.js";
15
- import { sameTarget } from "/assets/js/views/dock/terminal.js";
16
15
  import { setSectionParam } from "/assets/js/layout/router.js";
17
16
  import { enter } from "/assets/js/lib/motion.js";
18
17
 
@@ -29,14 +28,13 @@ function runtimeSignature(detail) {
29
28
  }).join(",");
30
29
  }
31
30
 
32
- // deps: { el, state, t, locale, api, toast, terminal, workspace,
31
+ // deps: { el, state, t, locale, api, toast, workspace,
33
32
  // selectTask, clearSelection, revealSidebar, pickAttention, readPageSessions, refreshCatalog }
34
33
  // el #detail, #center and the dock discussion
35
34
  // state reads selected, detail, detailKey and the catalog fields the
36
35
  // overview shows; writes activeTab
37
36
  // api Task reads for lazy blocks and the Task page's writes
38
- // terminal the dock's native Session (views/dock/terminal)
39
- // workspace layout/workspace: dock toggles and opening Sessions
37
+ // workspace layout/workspace: dock toggles, the session page and its Role list
40
38
  // selectTask(taskId) an overview row was chosen (app/selection)
41
39
  // clearSelection() the Task page's "Tasks" crumb (app/selection)
42
40
  // revealSidebar() the page header's task-list switch (layout/sidebarNav)
@@ -155,7 +153,7 @@ function detailContext(view) {
155
153
  },
156
154
  onBack: deps.clearSelection,
157
155
  revealSidebar: deps.revealSidebar,
158
- showDock: deps.workspace.toggleDock,
156
+ togglePanel: function () { deps.workspace.toggleDock(); },
159
157
  toggleOperator: deps.workspace.toggleOperator,
160
158
  openConversation: function (materials) {
161
159
  deps.workspace.openConversation({ scope: "task", taskId: deps.state.selected, roleName: "leader" }, materials);
@@ -239,8 +237,8 @@ function drawDetail(deps, ctx) {
239
237
  el.center.scrollTop = scroll;
240
238
  }
241
239
 
242
- // The dock's Session chips: every role of the selected Task plus the global
243
- // operator, keeping the Session currently open even when it left the list.
240
+ // The Roles the dock's target menu offers: every Role of the selected Task,
241
+ // with whether it has a live native Session (Operator is always offered).
244
242
  function updateSessionTargets(deps) {
245
243
  const detail = deps.state.detail;
246
244
  const targets = [];
@@ -252,10 +250,7 @@ function updateSessionTargets(deps) {
252
250
  live: !!(runtimeRole && runtimeRole.runtimeSession && runtimeRole.runtimeSession.nativeSessionId) });
253
251
  });
254
252
  }
255
- targets.push({ scope: "global", roleName: "operator" });
256
- const current = deps.terminal.current();
257
- if (current && !targets.some(function (target) { return sameTarget(target, current); })) targets.unshift(current);
258
- deps.terminal.setTargets(targets);
253
+ deps.workspace.setTargets(targets);
259
254
  }
260
255
 
261
256
  function showOverview(deps) {
@@ -264,6 +259,7 @@ function showOverview(deps) {
264
259
  select: deps.selectTask,
265
260
  filterAttention: deps.pickAttention,
266
261
  readSessions: deps.readPageSessions,
262
+ togglePanel: function () { deps.workspace.toggleDock(); },
267
263
  toggleOperator: deps.workspace.toggleOperator,
268
264
  revealSidebar: deps.revealSidebar
269
265
  });