@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.
- package/dist/cli/commandCatalog.js +19 -12
- package/dist/cli/interactionCandidates.js +20 -5
- package/dist/cli/interactionPolicy.js +5 -4
- package/dist/cli/roleWizard.js +7 -0
- package/dist/commands/controllerCommands.js +13 -4
- package/dist/commands/durableJobCommands.js +50 -3
- package/dist/commands/globalRoleCommands.js +31 -14
- package/dist/commands/inboxCommands.js +1 -0
- package/dist/commands/operatorCommands.js +12 -2
- package/dist/commands/profileCommands.js +19 -2
- package/dist/commands/roleConfiguration.js +3 -0
- package/dist/commands/taskCommands.js +16 -0
- package/dist/commands/taskRoleRuntimeStatus.js +3 -0
- package/dist/controlPlaneCli.js +21 -4
- package/dist/controller/agentJobControl.js +9 -1
- package/dist/controller/clientRuntime.js +34 -13
- package/dist/controller/controller.js +35 -22
- package/dist/controller/diagnosticJournal.js +214 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +174 -105
- package/dist/controller/globalInputDelivery.js +19 -9
- package/dist/controller/globalRuntimeAttention.js +11 -3
- package/dist/controller/jobControl.js +1 -1
- package/dist/controller/jobDefinitionControl.js +24 -13
- package/dist/controller/jobSupervisor.js +4 -0
- package/dist/controller/providerRetryAdmission.js +9 -0
- package/dist/controller/providerRetryDelivery.js +16 -0
- package/dist/controller/runtime.js +80 -4
- package/dist/controller/runtimeLaunchCoordinator.js +3 -1
- package/dist/controller/workerSchedulerStore.js +24 -0
- package/dist/core/boundedRpc.js +4 -0
- package/dist/core/controllerServer.js +1 -1
- package/dist/core/controllerTelemetry.js +92 -19
- package/dist/executor/fileRoleLaunchPlanner.js +3 -0
- package/dist/job/globalShell.js +51 -0
- package/dist/job/jobBusinessResult.js +4 -3
- package/dist/job/jobDefinition.js +6 -1
- package/dist/job/jobRunner.js +9 -0
- package/dist/job/scheduleService.js +2 -0
- package/dist/message/globalInboxRecovery.js +30 -19
- package/dist/message/globalProviderRetry.js +7 -1
- package/dist/message/messageContinuation.js +4 -0
- package/dist/output/rolePresentation.js +3 -0
- package/dist/profile/agentProfile.js +8 -0
- package/dist/profile/agentProfileRuntime.js +16 -1
- package/dist/review/reviewDecision.js +6 -0
- package/dist/role/availability.js +28 -0
- package/dist/role/role.js +6 -0
- package/dist/runtime/builtinAgentErrorMappers.js +4 -0
- package/dist/runtime/codexNativeAccess.js +21 -4
- package/dist/runtime/globalSessionHealth.js +39 -0
- package/dist/runtime/tmuxAdapters.js +5 -0
- package/dist/scheduler/activeRoleRunDelivery.js +9 -0
- package/dist/scheduler/leaderWakeupProcessor.js +8 -1
- package/dist/scheduler/roleRunLiveness.js +3 -0
- package/dist/scheduler/roleRunStall.js +10 -10
- package/dist/storage/persistenceWorker.js +41 -19
- package/dist/storage/sqliteSchema.js +43 -9
- package/dist/storage/sqliteStore.js +86 -8
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +25 -11
- package/dist/storage/taskReadRevisionSchema.js +39 -0
- package/dist/web/assets/client/app/catalog.js +0 -29
- package/dist/web/assets/client/app/jobs.js +115 -0
- package/dist/web/assets/client/app/main.js +32 -28
- package/dist/web/assets/client/app/manage.js +1 -0
- package/dist/web/assets/client/app/settings.js +1 -5
- package/dist/web/assets/client/app/taskView.js +7 -11
- package/dist/web/assets/client/i18n/en.js +265 -36
- package/dist/web/assets/client/i18n/zh-CN.js +264 -35
- package/dist/web/assets/client/index.js +4 -0
- package/dist/web/assets/client/layout/manageShell.js +61 -15
- package/dist/web/assets/client/layout/router.js +2 -1
- package/dist/web/assets/client/layout/shortcuts.js +2 -2
- package/dist/web/assets/client/layout/workspace.js +99 -112
- package/dist/web/assets/client/lib/prefs.js +1 -1
- package/dist/web/assets/client/ui/primitives.js +15 -2
- package/dist/web/assets/client/views/dock/conversation.js +421 -123
- package/dist/web/assets/client/views/dock/session.js +229 -0
- package/dist/web/assets/client/views/dock/terminal.js +48 -39
- package/dist/web/assets/client/views/jobs.js +1042 -160
- package/dist/web/assets/client/views/overview.js +2 -2
- package/dist/web/assets/client/views/sidebar.js +3 -0
- package/dist/web/assets/client/views/task/page.js +2 -12
- package/dist/web/assets/shared/manageNav.js +3 -1
- package/dist/web/assets/shell/appRail.js +2 -1
- package/dist/web/assets/shell/dock.js +9 -27
- package/dist/web/assets/shell/manage.js +7 -2
- package/dist/web/assets/shell/models.js +4 -3
- package/dist/web/assets/shell/settingsContent.js +0 -3
- package/dist/web/assets/shell/sidebar.js +0 -1
- package/dist/web/assets/styles/views/conversation.js +96 -17
- package/dist/web/assets/styles/views/dock.js +17 -5
- package/dist/web/assets/styles/views/jobs.js +301 -12
- package/dist/web/assets/styles/views/sidebar.js +1 -2
- package/dist/web/assets/styles/views/task.js +1 -2
- package/dist/web/assets/styles/views/terminal.js +12 -8
- package/dist/web/webConversation.js +1 -0
- package/dist/web/webServer.js +28 -1
- package/dist/web/webSettings.js +10 -2
- package/docs/global-shell-jobs.md +78 -0
- package/docs/job-runs.md +6 -3
- package/docs/managed-turn-and-session-runtime.md +84 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +57 -0
- package/docs/release-workflow.md +67 -0
- package/docs/release-workflow.zh-CN.md +48 -0
- package/docs/roles-and-configuration.md +45 -1
- package/docs/roles-and-configuration.zh-CN.md +35 -0
- package/docs/storage-baseline.md +19 -1
- package/docs/storage-baseline.zh-CN.md +10 -1
- package/docs/testing/review-evaluation.md +35 -1
- package/docs/testing/review-evaluation.zh-CN.md +25 -1
- package/docs/testing/verification-levels.md +2 -2
- package/docs/testing/verification-levels.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/VERSION +1 -1
- package/skills/yui-leader/references/execution.md +19 -8
- package/skills/yui-reviewer/SKILL.md +13 -2
- 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
|
|
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
|
|
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 {
|
|
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"),
|
|
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"),
|
|
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"),
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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,
|
|
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:
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
168
|
-
|
|
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)
|
|
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,
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
243
|
-
//
|
|
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
|
-
|
|
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
|
});
|