@songsid/agend 2.3.0-beta.1 → 2.3.0-beta.11
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/agent-cli-instructions.md +5 -2
- package/dist/agent-cli.js +43 -7
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +4 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-file-read.d.ts +14 -6
- package/dist/agent-file-read.js +207 -19
- package/dist/agent-file-read.js.map +1 -1
- package/dist/agent-whats-new.d.ts +11 -0
- package/dist/agent-whats-new.js +29 -15
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +30 -1
- package/dist/auth-api.js +45 -13
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/authority-state.d.ts +38 -0
- package/dist/authority/authority-state.js +114 -0
- package/dist/authority/authority-state.js.map +1 -0
- package/dist/authority/session-authorizations.d.ts +43 -0
- package/dist/authority/session-authorizations.js +102 -0
- package/dist/authority/session-authorizations.js.map +1 -0
- package/dist/authority/web-authority.d.ts +120 -0
- package/dist/authority/web-authority.js +157 -0
- package/dist/authority/web-authority.js.map +1 -0
- package/dist/backend/claude-code.d.ts +2 -2
- package/dist/backend/claude-code.js +10 -5
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/types.d.ts +14 -0
- package/dist/backend/types.js +25 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-availability.d.ts +120 -0
- package/dist/backend-availability.js +396 -0
- package/dist/backend-availability.js.map +1 -0
- package/dist/backend-command.d.ts +21 -3
- package/dist/backend-command.js +23 -4
- package/dist/backend-command.js.map +1 -1
- package/dist/bundled-skills.d.ts +14 -0
- package/dist/bundled-skills.js +29 -0
- package/dist/bundled-skills.js.map +1 -0
- package/dist/cache-analysis.d.ts +47 -1
- package/dist/cache-analysis.js +68 -8
- package/dist/cache-analysis.js.map +1 -1
- package/dist/cache-ledger.d.ts +16 -3
- package/dist/cache-ledger.js +33 -13
- package/dist/cache-ledger.js.map +1 -1
- package/dist/cache-report-tool.d.ts +17 -0
- package/dist/cache-report-tool.js +40 -0
- package/dist/cache-report-tool.js.map +1 -0
- package/dist/cache-service.d.ts +70 -3
- package/dist/cache-service.js +178 -17
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +7 -0
- package/dist/channel/adapters/discord.js +35 -0
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-tools.js +22 -10
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +6 -0
- package/dist/classic-channel-manager.d.ts +10 -5
- package/dist/classic-channel-manager.js +12 -9
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +130 -31
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +103 -4
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +7 -0
- package/dist/daemon.js +63 -16
- package/dist/daemon.js.map +1 -1
- package/dist/decision-list.d.ts +25 -0
- package/dist/decision-list.js +30 -0
- package/dist/decision-list.js.map +1 -0
- package/dist/delivery-outbox.d.ts +10 -0
- package/dist/delivery-outbox.js +12 -1
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/fleet-context.d.ts +10 -0
- package/dist/fleet-manager.d.ts +417 -11
- package/dist/fleet-manager.js +1387 -109
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-traffic.d.ts +74 -0
- package/dist/fleet-traffic.js +196 -0
- package/dist/fleet-traffic.js.map +1 -0
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +20 -5
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +6 -1
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +3 -2
- package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +257 -23
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +478 -0
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
- package/dist/human-delivery-receipts.d.ts +24 -1
- package/dist/human-delivery-receipts.js +64 -11
- package/dist/human-delivery-receipts.js.map +1 -1
- package/dist/instance-config-impact.js +3 -1
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-names.d.ts +4 -0
- package/dist/instance-names.js +12 -0
- package/dist/instance-names.js.map +1 -0
- package/dist/instructions.d.ts +12 -1
- package/dist/instructions.js +13 -2
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +100 -8
- package/dist/locale.js.map +1 -1
- package/dist/ls-attach-resolve.d.ts +95 -0
- package/dist/ls-attach-resolve.js +174 -0
- package/dist/ls-attach-resolve.js.map +1 -0
- package/dist/memory-hogs.d.ts +27 -0
- package/dist/memory-hogs.js +124 -0
- package/dist/memory-hogs.js.map +1 -0
- package/dist/outbound-handlers.d.ts +10 -0
- package/dist/outbound-handlers.js +122 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +164 -4
- package/dist/outbound-schemas.js +99 -3
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +8 -2
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +9 -0
- package/dist/public-web-link.js +8 -0
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart-api.d.ts +10 -1
- package/dist/quickstart-api.js +12 -4
- package/dist/quickstart-api.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +4 -0
- package/dist/reply-buttons-controller.js +2 -2
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/restart-progress.d.ts +65 -1
- package/dist/restart-progress.js +236 -10
- package/dist/restart-progress.js.map +1 -1
- package/dist/rollout-index.d.ts +5 -0
- package/dist/rollout-index.js +26 -5
- package/dist/rollout-index.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts +12 -0
- package/dist/scheduler/scheduler.js +83 -10
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/service-installer.d.ts +2 -1
- package/dist/service-installer.js +18 -3
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.d.ts +19 -0
- package/dist/settings-api.js +137 -2
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +104 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +17 -1
- package/dist/settings-effect.js.map +1 -1
- package/dist/tmux-manager.d.ts +5 -0
- package/dist/tmux-manager.js +16 -0
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions.d.ts +4 -6
- package/dist/tool-permissions.js +53 -5
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.d.ts +7 -1
- package/dist/topic-commands.js +18 -4
- package/dist/topic-commands.js.map +1 -1
- package/dist/types.d.ts +48 -1
- package/dist/ui/app-effects.js +77 -31
- package/dist/ui/chat-ambient.js +307 -0
- package/dist/ui/chat-effect-posters.js +8 -8
- package/dist/ui/chat-effects.js +101 -13
- package/dist/ui/chat-render.js +21 -3
- package/dist/ui/chat-strings.js +59 -6
- package/dist/ui/chat-thread.js +159 -21
- package/dist/ui/effect-trial-controller.js +13 -8
- package/dist/ui/effect-trial-panel.js +19 -16
- package/dist/ui/fleet-cache.js +23 -1
- package/dist/ui/fleet-canvas-layout.js +111 -0
- package/dist/ui/fleet-canvas.js +525 -0
- package/dist/ui/fleet-org.js +27 -12
- package/dist/ui/fleet-traffic.js +343 -0
- package/dist/ui/fleet-usage.js +401 -0
- package/dist/ui/panel-chat.js +154 -49
- package/dist/ui/panel-details.js +2 -1
- package/dist/ui/panel-fleet.js +43 -17
- package/dist/ui/panel-settings.js +26 -8
- package/dist/ui/preview.js +3 -2
- package/dist/ui/settings-appearance.js +42 -18
- package/dist/ui/settings-catalog.js +92 -0
- package/dist/ui/settings-dialogs.js +40 -12
- package/dist/ui/settings-effects.js +4 -3
- package/dist/ui/settings-guests.js +117 -0
- package/dist/ui/settings-learn.js +25 -0
- package/dist/ui/settings-model.js +15 -6
- package/dist/ui/settings-mods.js +23 -9
- package/dist/ui/settings-owner-effects.js +9 -9
- package/dist/ui/settings-strings.js +120 -42
- package/dist/ui/settings-web-authority.js +91 -0
- package/dist/ui/shared/agend-auth.js +1 -1
- package/dist/ui/shared/app-i18n.js +4 -2
- package/dist/ui/shared/app-prefs.js +5 -4
- package/dist/ui/shared/app-route.js +19 -1
- package/dist/ui/shared/app-session.js +2 -0
- package/dist/ui/shared/app-shell.js +92 -27
- package/dist/ui/shared/app-store.js +26 -1
- package/dist/ui/shared/app-stream.js +3 -1
- package/dist/ui/shared/app-themes.js +24 -6
- package/dist/ui/shared/app.css +365 -17
- package/dist/ui/shared/app.js +21 -14
- package/dist/ui/shared/card-strings.js +4 -0
- package/dist/ui/shared/effect-app-runtime.js +68 -19
- package/dist/ui/shared/effect-chat-runtime.js +16 -3
- package/dist/ui/shared/effect-frame.js +26 -7
- package/dist/ui/shared/effect-geometry.js +24 -9
- package/dist/ui/shared/effect-policy.js +21 -7
- package/dist/ui/shared/effect-profiler.js +31 -9
- package/dist/ui/shared/effect-registry.js +87 -48
- package/dist/ui/shared/effect-stock-snapshot.js +9 -0
- package/dist/ui/shared/effect-stock-trust.js +62 -10
- package/dist/ui/shared/effect-trial-shadow.js +62 -0
- package/dist/ui/shared/effect-trial-source.js +1 -1
- package/dist/ui/shared/effect-trial-stage.js +54 -31
- package/dist/ui/shared/effect-trial.css +40 -0
- package/dist/ui/shared/fx-motion.js +67 -0
- package/dist/ui/shared/identity-card.js +75 -4
- package/dist/ui/shared/identity-color.js +3 -3
- package/dist/ui/shared/instance-nav.js +194 -28
- package/dist/ui/shared/panel-view.js +24 -4
- package/dist/ui/shared/tokens.css +9 -3
- package/dist/ui/shared/ui-icons.js +1 -0
- package/dist/ui/shared/usage-dialog.js +22 -2
- package/dist/ui/shared/view-strings.js +68 -2
- package/dist/ui/shared/web-authorize.js +151 -0
- package/dist/ui/signin.html +1 -1
- package/dist/ui/web-terminal/terminal.css +4 -4
- package/dist/usage/providers.d.ts +28 -0
- package/dist/usage/providers.js +126 -36
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage-report.d.ts +158 -0
- package/dist/usage-report.js +226 -0
- package/dist/usage-report.js.map +1 -0
- package/dist/usage-rollup.d.ts +51 -0
- package/dist/usage-rollup.js +168 -0
- package/dist/usage-rollup.js.map +1 -0
- package/dist/view-api.d.ts +39 -0
- package/dist/view-api.js +65 -4
- package/dist/view-api.js.map +1 -1
- package/dist/web-ambient.d.ts +72 -0
- package/dist/web-ambient.js +161 -0
- package/dist/web-ambient.js.map +1 -0
- package/dist/web-api.d.ts +69 -5
- package/dist/web-api.js +362 -45
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-policy.d.ts +4 -6
- package/dist/web-appearance-policy.js +5 -12
- package/dist/web-appearance-policy.js.map +1 -1
- package/dist/web-appearance-tools.d.ts +3 -0
- package/dist/web-appearance-tools.js +22 -3
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance-view.d.ts +27 -2
- package/dist/web-appearance-view.js +129 -14
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-appearance.d.ts +17 -3
- package/dist/web-appearance.js +82 -7
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-asset-parts.d.ts +5 -10
- package/dist/web-asset-parts.js +3 -3
- package/dist/web-asset-parts.js.map +1 -1
- package/dist/web-auth.js +3 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-card.d.ts +14 -0
- package/dist/web-card.js +40 -6
- package/dist/web-card.js.map +1 -1
- package/dist/web-channel-echo.d.ts +7 -1
- package/dist/web-channel-echo.js +6 -2
- package/dist/web-channel-echo.js.map +1 -1
- package/dist/web-chat-history.d.ts +50 -0
- package/dist/web-chat-history.js +87 -5
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +1 -0
- package/dist/web-effect-bootstrap.d.ts +12 -0
- package/dist/web-effect-bootstrap.js +49 -0
- package/dist/web-effect-bootstrap.js.map +1 -0
- package/dist/web-effect-bundle.js +46 -1
- package/dist/web-effect-bundle.js.map +1 -1
- package/dist/web-effect-playback.d.ts +30 -0
- package/dist/web-effect-playback.js +71 -0
- package/dist/web-effect-playback.js.map +1 -0
- package/dist/web-effect-profiles.d.ts +4 -4
- package/dist/web-effect-service.d.ts +11 -4
- package/dist/web-effect-service.js +28 -5
- package/dist/web-effect-service.js.map +1 -1
- package/dist/web-effect-tools.d.ts +1 -0
- package/dist/web-effect-tools.js +8 -5
- package/dist/web-effect-tools.js.map +1 -1
- package/dist/web-effect-trials.d.ts +4 -4
- package/dist/web-effect-trials.js +6 -6
- package/dist/web-effect-trials.js.map +1 -1
- package/dist/web-effect-upload.d.ts +2 -4
- package/dist/web-effect-upload.js +3 -8
- package/dist/web-effect-upload.js.map +1 -1
- package/dist/web-effect-worker.d.ts +1 -1
- package/dist/web-effect-worker.js +2 -2
- package/dist/web-effect-worker.js.map +1 -1
- package/dist/web-effect.d.ts +14 -0
- package/dist/web-effect.js +21 -0
- package/dist/web-effect.js.map +1 -1
- package/dist/web-guest-access.d.ts +125 -0
- package/dist/web-guest-access.js +234 -0
- package/dist/web-guest-access.js.map +1 -0
- package/dist/web-guest-rooms.d.ts +82 -0
- package/dist/web-guest-rooms.js +135 -0
- package/dist/web-guest-rooms.js.map +1 -0
- package/dist/web-identity-color.d.ts +1 -1
- package/dist/web-identity-color.js +2 -2
- package/dist/web-identity-color.js.map +1 -1
- package/dist/web-identity-palette.d.ts +1 -1
- package/dist/web-identity-palette.js +2 -2
- package/dist/web-identity-palette.js.map +1 -1
- package/dist/web-image-meta.d.ts +13 -0
- package/dist/web-image-meta.js +62 -0
- package/dist/web-image-meta.js.map +1 -1
- package/dist/web-login.d.ts +47 -0
- package/dist/web-login.js +115 -0
- package/dist/web-login.js.map +1 -1
- package/dist/web-mod-agent.d.ts +1 -1
- package/dist/web-mod-agent.js +12 -8
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-mod-bundle.d.ts +14 -1
- package/dist/web-mod-bundle.js +68 -3
- package/dist/web-mod-bundle.js.map +1 -1
- package/dist/web-mod-export.d.ts +9 -0
- package/dist/web-mod-export.js +21 -0
- package/dist/web-mod-export.js.map +1 -1
- package/dist/web-mod-store.d.ts +7 -1
- package/dist/web-mod-store.js +21 -7
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-prefs.d.ts +14 -0
- package/dist/web-prefs.js +24 -4
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +6 -2
- package/dist/web-preview.js +4 -2
- package/dist/web-preview.js.map +1 -1
- package/dist/web-returning-device.d.ts +41 -0
- package/dist/web-returning-device.js +57 -5
- package/dist/web-returning-device.js.map +1 -1
- package/dist/web-session-authorization.d.ts +132 -0
- package/dist/web-session-authorization.js +193 -0
- package/dist/web-session-authorization.js.map +1 -0
- package/dist/web-session.d.ts +70 -10
- package/dist/web-session.js +137 -22
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +49 -2
- package/dist/web-settings-patch.js +145 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.d.ts +89 -0
- package/dist/web-settings-tools.js +270 -0
- package/dist/web-settings-tools.js.map +1 -0
- package/dist/web-shell-routes.d.ts +1 -1
- package/dist/web-shell-routes.js +1 -1
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-theme-store.d.ts +39 -0
- package/dist/web-theme-store.js +122 -11
- package/dist/web-theme-store.js.map +1 -1
- package/dist/web-theme.d.ts +2 -0
- package/dist/web-theme.js +10 -1
- package/dist/web-theme.js.map +1 -1
- package/dist/web-upload.d.ts +13 -0
- package/dist/web-upload.js +39 -3
- package/dist/web-upload.js.map +1 -1
- package/dist/workflow-templates/default.md +1 -1
- package/package.json +1 -1
- package/templates/systemd.service.ejs +8 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// #1408 §3: the app's routes on the client. The same table as the server's classifier (src/web-shell-routes.ts;
|
|
2
2
|
// a test runs both over one list of cases). Pure: no DOM, no history.
|
|
3
3
|
|
|
4
|
-
export const FLEET_TABS = ["tasks", "schedules", "teams", "org", "cache", "config"];
|
|
4
|
+
export const FLEET_TABS = ["tasks", "schedules", "teams", "org", "cache", "usage", "config"];
|
|
5
5
|
export const SETTINGS_SECTIONS = ["device", "appearance", "agents", "bots", "classic", "general", "web", "advanced"]; // #1604: device first
|
|
6
6
|
const MAX_INSTANCE_NAME = 128;
|
|
7
7
|
const PATH_SEPARATOR_OR_CONTROL = /[/\\\u0000-\u001f\u007f]/;
|
|
@@ -50,6 +50,24 @@ export function viewPath(instance) { return instance ? `/view/${encodeURICompone
|
|
|
50
50
|
export function detailsPath(instance) { return `/ui/fleet/agent/${encodeURIComponent(instance)}`; }
|
|
51
51
|
export function logPath(instance) { return `/ui/log/${encodeURIComponent(instance)}`; }
|
|
52
52
|
export function fleetPath(tab) { return tab && tab !== "tasks" ? `/ui/fleet/${tab}` : "/ui/fleet"; }
|
|
53
|
+
/** #2183: this device's last Fleet tab (Fleet remembers it; the Org chart the first time). */
|
|
54
|
+
export const LAST_FLEET_TAB_KEY = "agend_last_fleet_tab";
|
|
55
|
+
export function lastFleetTab() {
|
|
56
|
+
let tab = null;
|
|
57
|
+
try { tab = localStorage.getItem(LAST_FLEET_TAB_KEY); } catch { /* storage blocked: the default */ }
|
|
58
|
+
return FLEET_TABS.includes(tab) ? tab : "org";
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* #2183: where Fleet (the sidebar's entry, the phone's tab) goes — on a Fleet tab, that tab (the route, not storage: the
|
|
62
|
+
* panel stores it in an effect after this render, and a storage write re-renders nothing); anywhere else, the last one.
|
|
63
|
+
*/
|
|
64
|
+
export function fleetEntryPath(route) {
|
|
65
|
+
return fleetPath(route && route.panel === "fleet" ? route.tab : lastFleetTab()); // parseRoute names only FLEET_TABS
|
|
66
|
+
}
|
|
67
|
+
export function rememberFleetTab(tab) {
|
|
68
|
+
if (!FLEET_TABS.includes(tab)) return;
|
|
69
|
+
try { localStorage.setItem(LAST_FLEET_TAB_KEY, tab); } catch { /* this page only */ }
|
|
70
|
+
}
|
|
53
71
|
export const NEEDS_PATH = "/ui/needs";
|
|
54
72
|
export function settingsPath(section) { return section && section !== "agents" ? `/settings/${section}` : "/settings"; }
|
|
55
73
|
export function routePath(route) {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { html, useEffect, useRef, useState } from "./app-html.js";
|
|
5
5
|
import { Icon } from "./ui-icons.js";
|
|
6
6
|
import { t, lang } from "./app-i18n.js";
|
|
7
|
+
import { AuthorizeChip } from "./web-authorize.js";
|
|
7
8
|
|
|
8
9
|
async function getJson(url) {
|
|
9
10
|
try {
|
|
@@ -53,6 +54,7 @@ export function SessionMenu() {
|
|
|
53
54
|
${!me ? html`<h3>${t("app.signedOut")}</h3><a class="btn" href=${`/signin?next=${encodeURIComponent(location.pathname)}`}>${t("app.signIn")}</a>` : html`
|
|
54
55
|
<h3>${t("app.signedIn")} · ${me.label}</h3>
|
|
55
56
|
<p class="sub">${t("app.expires", clock(me.expiresAt), span(me.idleMs))}</p>
|
|
57
|
+
<${AuthorizeChip} />
|
|
56
58
|
${rows.length > 1 ? html`<h3>${t("app.devices")}</h3><ul class="devices">${rows.map(s => html`<li key=${s.handle}>
|
|
57
59
|
<span class="grow"><span class="name">${s.label}${s.current ? ` · ${t("app.thisDevice")}` : ""}</span><span class="when">${t("app.lastActive", ago(s.lastSeen))}</span></span>
|
|
58
60
|
${s.current ? null : html`<button type="button" class="btn btn-sm danger" onClick=${() => act("DELETE", `/auth/sessions/${encodeURIComponent(s.handle)}`, refresh)}>${t("app.signOut")}</button>`}</li>`)}</ul>` : null}
|
|
@@ -7,9 +7,9 @@ import { Toasts } from "./ui-toast.js";
|
|
|
7
7
|
import { ConfirmHost } from "./ui-confirm.js";
|
|
8
8
|
import { ColorDialogHost } from "./identity-color.js";
|
|
9
9
|
import { t, lang, setLang, onLang } from "./app-i18n.js";
|
|
10
|
-
import { appStore, createStore, useStore } from "./app-store.js";
|
|
10
|
+
import { appStore, createStore, guestBadge, guestPage, useStore } from "./app-store.js";
|
|
11
11
|
import { navStore } from "./app-nav.js";
|
|
12
|
-
import { chatPath, viewPath, settingsPath, routeKey, NEEDS_PATH,
|
|
12
|
+
import { chatPath, viewPath, settingsPath, routeKey, NEEDS_PATH, fleetEntryPath } from "./app-route.js";
|
|
13
13
|
import { SessionMenu } from "./app-session.js";
|
|
14
14
|
import { savePref } from "./app-prefs.js";
|
|
15
15
|
import { ErrorState, Skeleton } from "./ui-states.js";
|
|
@@ -39,6 +39,25 @@ let newInstanceOpener = null;
|
|
|
39
39
|
export function setNewInstanceOpener(fn) { newInstanceOpener = fn; }
|
|
40
40
|
export function requestNewInstance() { if (newInstanceOpener) newInstanceOpener(); }
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* #2074: whether this page's session may make an agent — an admin's (/auth/session's tier); a guest's create is refused.
|
|
44
|
+
* Asked once per page; a session that ends takes the buttons away. A display hint only: the create route decides.
|
|
45
|
+
*/
|
|
46
|
+
export const canCreateStore = createStore({ value: false });
|
|
47
|
+
let canCreateOf = null; // the AgendAuth asked (one per page)
|
|
48
|
+
export function useCanCreate() {
|
|
49
|
+
const s = useStore(canCreateStore);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const auth = typeof window !== "undefined" ? window.AgendAuth : undefined;
|
|
52
|
+
if (!auth || typeof auth.canWrite !== "function" || canCreateOf === auth) return;
|
|
53
|
+
canCreateOf = auth;
|
|
54
|
+
canCreateStore.set({ value: false });
|
|
55
|
+
Promise.resolve(auth.canWrite()).then((v) => { if (canCreateOf === auth && !auth.sessionEnded?.()) canCreateStore.set({ value: v === true }); }, () => {});
|
|
56
|
+
auth.onSessionEnd?.(() => { if (canCreateOf === auth) canCreateStore.set({ value: false }); });
|
|
57
|
+
}, []);
|
|
58
|
+
return s.value;
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
/** An app-level dialog (New instance): rendered by the shell until closed, whatever panel is showing. */
|
|
43
62
|
export function showDialog(Component, props = {}) { shellStore.set({ dialog: { Component, props } }); }
|
|
44
63
|
export function closeDialog() { shellStore.set({ dialog: null }); }
|
|
@@ -247,9 +266,8 @@ export function signInHref() {
|
|
|
247
266
|
function Sidebar({ route, onNewInstance, viewOnly }) {
|
|
248
267
|
const app = useStore(appStore);
|
|
249
268
|
const shell = useStore(shellStore);
|
|
250
|
-
|
|
251
|
-
const on = (panel) => !!route &&
|
|
252
|
-
const openInstance = openInstanceOf(route);
|
|
269
|
+
const canCreate = useCanCreate();
|
|
270
|
+
const on = (panel) => !!route && route.panel === panel;
|
|
253
271
|
// A count badge is hidden at 0; its number is also in the link's accessible name.
|
|
254
272
|
const navLink = (panel, href, icon, label, count = 0) => html`<a class=${`side-row${on(panel) ? " active" : ""}`} href=${href}
|
|
255
273
|
aria-current=${on(panel) ? "page" : undefined} aria-label=${count ? `${label} (${t("app.needsCount", count)})` : undefined} onClick=${closeDrawer}>
|
|
@@ -271,22 +289,37 @@ function Sidebar({ route, onNewInstance, viewOnly }) {
|
|
|
271
289
|
<${SidebarResize} />
|
|
272
290
|
</aside>`;
|
|
273
291
|
}
|
|
274
|
-
// #
|
|
275
|
-
//
|
|
292
|
+
// #1920 phase 3: a guest's sidebar — its own agents to pick from, who it is signed in as, and its session; no fleet,
|
|
293
|
+
// Needs you, View, Settings, new agent or widget (each is an admin's, and the server refuses a guest them anyway).
|
|
294
|
+
if (guestPage()) {
|
|
295
|
+
return html`<aside id="sidebar" class="sidebar" aria-label=${t("app.menu")}>
|
|
296
|
+
<div class="side-head" data-trust="navigation">
|
|
297
|
+
<a class="brand" href="/ui" onClick=${closeDrawer}>${t("app.brand")}</a>
|
|
298
|
+
<button type="button" class="icon-btn side-collapse" onClick=${toggleSidebar} aria-label=${narrow() ? t("app.closeMenu") : t("app.collapse")} title=${narrow() ? t("app.closeMenu") : t("app.collapse")} aria-controls="sidebar"><${Icon} name="sidebar" /></button>
|
|
299
|
+
</div>
|
|
300
|
+
<${InstanceNav} route=${route} items=${app.instances} loaded=${app.ready !== false} exec=${app.exec} awaiting=${app.awaiting} onPick=${closeDrawer} />
|
|
301
|
+
<div class="side-foot" data-trust="navigation">
|
|
302
|
+
<${GuestChip} />
|
|
303
|
+
<${SessionMenu} />
|
|
304
|
+
</div>
|
|
305
|
+
<${SidebarResize} />
|
|
306
|
+
</aside>`;
|
|
307
|
+
}
|
|
308
|
+
// #1675: the whole fleet, from anywhere — top left, so at the top of a phone's drawer too. #2183 (the user's call): the
|
|
309
|
+
// one Fleet entry ("Fleet" / "Fleet 概覽"), on the tab this device used last (the Org chart the first time); an agent's
|
|
310
|
+
// View, Log and Details are its header's tabs, so the sidebar has no "Fleet" (Details) or "View" row of its own.
|
|
276
311
|
const onFleet = !!route && route.panel === "fleet";
|
|
277
|
-
const overview = html`<a class=${`side-row side-overview${onFleet ? " active" : ""}`} href=${
|
|
312
|
+
const overview = html`<a class=${`side-row side-overview${onFleet ? " active" : ""}`} href=${fleetEntryPath(route)}
|
|
278
313
|
aria-current=${onFleet ? "page" : undefined} onClick=${closeDrawer}><${Icon} name="org" /><span class="grow">${t("app.fleetOverview")}</span></a>`;
|
|
279
314
|
return html`<aside id="sidebar" class="sidebar" aria-label=${t("app.menu")}>
|
|
280
315
|
<div class="side-head" data-trust="navigation">
|
|
281
316
|
<a class="brand" href="/ui" onClick=${closeDrawer}>${t("app.brand")}</a>
|
|
282
317
|
<button type="button" class="icon-btn side-collapse" onClick=${toggleSidebar} aria-label=${narrow() ? t("app.closeMenu") : t("app.collapse")} title=${narrow() ? t("app.closeMenu") : t("app.collapse")} aria-controls="sidebar"><${Icon} name="sidebar" /></button>
|
|
283
|
-
<button type="button" class="icon-btn" onClick=${onNewInstance} aria-label=${t("app.newInstance")} title=${t("app.newInstance")}><${Icon} name="plus" /></button>
|
|
284
318
|
</div>
|
|
285
319
|
<nav data-trust="navigation" class="side-nav" aria-label=${t("app.menu")}>
|
|
286
320
|
${overview}
|
|
287
321
|
${navLink("needs", NEEDS_PATH, "inbox", t("app.needsNav"), needsCount)}
|
|
288
|
-
${
|
|
289
|
-
${navLink("view", "/view", "view", t("app.view"))}
|
|
322
|
+
${canCreate && onNewInstance ? html`<button type="button" class="side-row side-new" onClick=${onNewInstance}><${Icon} name="plus" /><span class="grow">${t("app.newAgent")}</span></button>` : null}
|
|
290
323
|
</nav>
|
|
291
324
|
${shell.side ? html`<${shell.side.Component} />` : html`<${InstanceNav} route=${route} items=${app.instances} loaded=${app.ready !== false}
|
|
292
325
|
exec=${app.exec} awaiting=${app.awaiting} onPick=${closeDrawer} />`}
|
|
@@ -299,6 +332,17 @@ function Sidebar({ route, onNewInstance, viewOnly }) {
|
|
|
299
332
|
</aside>`;
|
|
300
333
|
}
|
|
301
334
|
|
|
335
|
+
/** #1920 phase 3: "Guest · <name> (<platform>)" — who this guest's page is signed in as; what a guest sees, in its title. */
|
|
336
|
+
const PLATFORM_NAMES = { discord: "Discord", telegram: "Telegram" };
|
|
337
|
+
function GuestChip() {
|
|
338
|
+
const { name, platform } = guestBadge();
|
|
339
|
+
const where = PLATFORM_NAMES[platform] || platform;
|
|
340
|
+
const label = name ? (where ? t("app.guestAsOn", name, where) : t("app.guestAs", name)) : t("app.guestChip");
|
|
341
|
+
// Inside .side-foot (a marked, stock-trusted element): no trust marker of its own — effect-stock-trust's closed inventory
|
|
342
|
+
// refuses any element it does not know, which would hide a guest's static effect posters (#2101 review).
|
|
343
|
+
return html`<p class="guest-chip" title=${t("app.guestHint")}><${Icon} name="user" size=${14} /><span class="grow">${label}</span></p>`;
|
|
344
|
+
}
|
|
345
|
+
|
|
302
346
|
/** #1629 r4-4: the person's own sidebar widget (their synced preference), with the fleet's counts. */
|
|
303
347
|
function SidebarWidget({ app }) {
|
|
304
348
|
useStore(sidebarWidgetStore);
|
|
@@ -347,20 +391,23 @@ function PrefsPopover() {
|
|
|
347
391
|
* The header every panel shows: ☰ (phone, or a collapsed sidebar), its title and status, then its actions. `nav`
|
|
348
392
|
* (#1523 N2): an instance page's view switch — beside the title on a wide screen, a row of tabs under it on a phone.
|
|
349
393
|
*/
|
|
350
|
-
export function PanelHeader({ title, sub, children, headingRef, nav }) {
|
|
394
|
+
export function PanelHeader({ title, sub, children, headingRef, nav, notice }) {
|
|
351
395
|
const shell = useStore(shellStore);
|
|
396
|
+
const canCreate = useCanCreate() && !!newInstanceOpener;
|
|
352
397
|
return html`<header data-trust="header" class=${`panel-head${shell.collapsed ? " collapsed" : ""}`}>
|
|
353
398
|
<button type="button" id="sbOpen" class="icon-btn sb-open" onClick=${() => (narrow() ? openDrawer() : toggleSidebar())}
|
|
354
399
|
aria-label=${narrow() ? t("app.openMenu") : t("app.expand")} title=${narrow() ? t("app.openMenu") : t("app.expand")} aria-controls="sidebar" aria-expanded=${shell.drawer ? "true" : "false"}><${Icon} name="menu" /></button>
|
|
400
|
+
${canCreate && shell.collapsed ? html`<button type="button" class="icon-btn sb-new" onClick=${requestNewInstance} aria-label=${t("app.newAgent")} title=${t("app.newAgent")}><${Icon} name="plus" /></button>` : null}
|
|
355
401
|
<div class="panel-title"><h1 ref=${headingRef} tabindex="-1">${title}</h1>${sub ? html`<div class="panel-sub">${sub}</div>` : null}</div>
|
|
356
402
|
${nav || null}
|
|
357
403
|
<div class="panel-actions">${children}</div>
|
|
404
|
+
${notice || null}
|
|
358
405
|
</header>`;
|
|
359
406
|
}
|
|
360
407
|
|
|
361
408
|
/**
|
|
362
|
-
* #1523 N2: the instance a page is about — Chat, View, Log (#1218) or Details of it — or null.
|
|
363
|
-
*
|
|
409
|
+
* #1523 N2: the instance a page is about — Chat, View, Log (#1218) or Details of it — or null. #2183: with one open, the
|
|
410
|
+
* phone's Chat tab is current and goes to its chat.
|
|
364
411
|
*/
|
|
365
412
|
export function openInstanceOf(route) {
|
|
366
413
|
return route && (route.panel === "chat" || route.panel === "view" || route.panel === "log" || route.panel === "details") && route.instance ? route.instance : null;
|
|
@@ -371,24 +418,28 @@ function BottomTabs({ route, viewOnly }) {
|
|
|
371
418
|
let last = null;
|
|
372
419
|
try { last = localStorage.getItem("agend_last_instance"); } catch { /* none */ }
|
|
373
420
|
const open = openInstanceOf(route);
|
|
374
|
-
|
|
421
|
+
// #2183: Chat stands for the open agent's pages (its View, Log and Details are the tabs under its header), so it goes to that agent's chat.
|
|
422
|
+
const chatHref = open ? chatPath(open) : last ? chatPath(last) : "/ui";
|
|
375
423
|
const anyAwaiting = Object.keys(app.awaiting).length;
|
|
376
424
|
const tab = (href, icon, label, active, badge, badgeLabel) => html`<a class=${`tab${active ? " active" : ""}`} href=${href} aria-current=${active ? "page" : undefined}>
|
|
377
425
|
<${Icon} name=${icon} size=${20} /><span>${label}</span>${badge ? html`<span class="tab-badge" aria-label=${badgeLabel || t("app.needsYou")}>${badge}</span>` : null}</a>`;
|
|
378
|
-
let lastView = null;
|
|
379
|
-
try { lastView = localStorage.getItem("agend_last_view"); } catch { /* none */ }
|
|
380
|
-
const viewHref = route && (route.panel === "view" || route.panel === "log" || route.panel === "details") && route.instance ? viewPath(route.instance) : viewPath(lastView);
|
|
381
426
|
if (viewOnly) {
|
|
427
|
+
let lastView = null;
|
|
428
|
+
try { lastView = localStorage.getItem("agend_last_view"); } catch { /* none */ }
|
|
429
|
+
const viewHref = route && route.panel === "view" && route.instance ? viewPath(route.instance) : viewPath(lastView);
|
|
382
430
|
return html`<nav data-trust="navigation" class="tabs" aria-label=${t("app.menu")}>
|
|
383
431
|
${tab(viewHref, "view", t("app.view"), route && route.panel === "view")}
|
|
384
432
|
${tab(signInHref(), "user", t("app.signIn"), false)}
|
|
385
433
|
</nav>`;
|
|
386
434
|
}
|
|
435
|
+
// #1920: a guest has only its chats (the drawer lists them): no tabs to switch between.
|
|
436
|
+
if (guestPage()) return null;
|
|
437
|
+
// #2183 (the user's call): four tabs — Chat (current on /ui and on any of an agent's pages), Needs you, Fleet (on its
|
|
438
|
+
// last tab), Settings.
|
|
387
439
|
return html`<nav data-trust="navigation" class="tabs" aria-label=${t("app.menu")}>
|
|
388
|
-
${tab(chatHref, "chat", t("app.chat"), route && route.panel === "chat", anyAwaiting)}
|
|
440
|
+
${tab(chatHref, "chat", t("app.chat"), !!open || (!!route && route.panel === "chat"), anyAwaiting)}
|
|
389
441
|
${tab(NEEDS_PATH, "inbox", t("app.needsTab"), route && route.panel === "needs", (app.needs || []).length, t("app.needsCount", (app.needs || []).length))}
|
|
390
|
-
${tab(
|
|
391
|
-
${tab(viewHref, "view", t("app.view"), route && route.panel === "view")}
|
|
442
|
+
${tab(fleetEntryPath(route), "fleet", t("app.fleet"), route && route.panel === "fleet")}
|
|
392
443
|
${tab(settingsPath(), "settings", t("app.settings"), route && route.panel === "settings")}
|
|
393
444
|
</nav>`;
|
|
394
445
|
}
|
|
@@ -435,8 +486,21 @@ export function PendingChanges({ inline = false } = {}) {
|
|
|
435
486
|
const { pendingChanges } = useStore(appStore);
|
|
436
487
|
const list = pendingChanges || [];
|
|
437
488
|
useTick(list.some(p => p.state === "pending"));
|
|
489
|
+
// #2168: inline (Settings' own column), a request that has just started waiting is brought into view — on a phone the
|
|
490
|
+
// person applied far down the list, and the card that says where to confirm it sat above the screen. Once per request.
|
|
491
|
+
const box = useRef(null), shown = useRef(new Set());
|
|
492
|
+
useEffect(() => {
|
|
493
|
+
if (!inline || !box.current) return;
|
|
494
|
+
for (const p of list) {
|
|
495
|
+
if (p.state !== "pending" || shown.current.has(p.id)) continue;
|
|
496
|
+
shown.current.add(p.id);
|
|
497
|
+
const card = box.current.querySelector(`[data-pending="${p.id}"]`);
|
|
498
|
+
const still = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
499
|
+
if (card && typeof card.scrollIntoView === "function") card.scrollIntoView({ block: "nearest", behavior: still ? "auto" : "smooth" });
|
|
500
|
+
}
|
|
501
|
+
});
|
|
438
502
|
if (!list.length) return null;
|
|
439
|
-
return html`<div data-trust="confirmation" class=${`pending-stack${inline ? " inline" : ""}`} role="region" aria-label=${t("app.pendingTitle")}>${list.map(p => html`<section key=${p.id} class=${`pending-card ${p.state}`} role="status">
|
|
503
|
+
return html`<div data-trust="confirmation" ref=${box} class=${`pending-stack${inline ? " inline" : ""}`} role="region" aria-label=${t("app.pendingTitle")}>${list.map(p => html`<section key=${p.id} data-pending=${p.id} class=${`pending-card ${p.state}`} role="status">
|
|
440
504
|
<div class="pending-head"><${Icon} name=${p.state === "applied" ? "check" : p.state === "pending" || p.state === "applying" ? "clock" : "alert"} size=${16} />
|
|
441
505
|
<strong>${t(`app.pending_${p.state}`)}</strong>
|
|
442
506
|
${p.state === "pending" ? html`<span class="pending-left">${t("app.pendingLeft", mmss(p.deadline - NOW()))}</span>` : null}
|
|
@@ -508,23 +572,24 @@ export function Shell({ panels, onNewInstance, viewOnly = false }) {
|
|
|
508
572
|
vv.addEventListener("resize", onResize);
|
|
509
573
|
return () => vv.removeEventListener("resize", onResize);
|
|
510
574
|
}, []);
|
|
511
|
-
const
|
|
575
|
+
const guest = guestPage(); // #1920: a guest's page — no operations, usage, pending changes or owner CSS
|
|
576
|
+
const cls = ["shell", shell.collapsed ? "sb-collapsed" : "", shell.drawer ? "sb-open" : "", shell.keyboard ? "kb-open" : "", guest ? "guest" : ""].filter(Boolean).join(" ");
|
|
512
577
|
return html`<div class=${cls}>
|
|
513
578
|
<a data-trust="navigation" class="skip" href="#main">${t("app.skip")}</a>
|
|
514
579
|
<${Sidebar} route=${nav.route} onNewInstance=${onNewInstance} viewOnly=${viewOnly} />
|
|
515
580
|
<div class="scrim" onClick=${closeDrawer} aria-hidden="true"></div>
|
|
516
581
|
<main id="main" class="main" tabindex="-1" inert=${shell.drawer && narrow() ? true : undefined}>
|
|
517
582
|
<${ConnectionLine} />
|
|
518
|
-
${viewOnly ? null : html`<${OperationLine} route=${nav.route} />`}
|
|
583
|
+
${viewOnly || guest ? null : html`<${OperationLine} route=${nav.route} />`}
|
|
519
584
|
<${Outlet} route=${nav.route} seq=${nav.seq} panels=${panels} />
|
|
520
585
|
</main>
|
|
521
|
-
|
|
586
|
+
${guest ? null : html`<${UsageRail} route=${nav.route} />`}
|
|
522
587
|
<${BottomTabs} route=${nav.route} viewOnly=${viewOnly} />
|
|
523
588
|
${shell.dialog ? html`<${shell.dialog.Component} ...${shell.dialog.props} onClose=${closeDialog} />` : null}
|
|
524
|
-
${viewOnly || (nav.route && nav.route.panel === "settings") ? null : html`<${PendingChanges} />`}
|
|
589
|
+
${viewOnly || guest || (nav.route && nav.route.panel === "settings") ? null : html`<${PendingChanges} />`}
|
|
525
590
|
<${ConfirmHost} />
|
|
526
591
|
<${ColorDialogHost} />
|
|
527
|
-
${viewOnly ? null : html`<${OwnerCssKeepBar} />`}
|
|
592
|
+
${viewOnly || guest ? null : html`<${OwnerCssKeepBar} />`}
|
|
528
593
|
<${Toasts} />
|
|
529
594
|
</div>`;
|
|
530
595
|
}
|
|
@@ -26,6 +26,20 @@ export function createStore(initial) {
|
|
|
26
26
|
export const appStore = createStore({ ready: false, instances: [], uptime: 0, exec: {}, awaiting: {}, connection: "connecting", needs: [], connections: null,
|
|
27
27
|
hydration: "none", retryHydration: null });
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* #1920 phase 3: this page is a guest's — the server says so on <body> (data-tier="guest"). A guest sees only its own
|
|
31
|
+
* agents' chats: no fleet, Needs you, View, Settings, filters, commands or new agent, and nothing it may not read is asked
|
|
32
|
+
* for (each would be refused). The server checks every request regardless; this only keeps the page to what works.
|
|
33
|
+
*/
|
|
34
|
+
export function guestPage() {
|
|
35
|
+
return typeof document !== "undefined" && !!document.body && document.body.dataset.tier === "guest";
|
|
36
|
+
}
|
|
37
|
+
/** Who a guest's page is signed in as: its web name and its platform (the server's, on <body>). */
|
|
38
|
+
export function guestBadge() {
|
|
39
|
+
const d = typeof document !== "undefined" && document.body ? document.body.dataset : {};
|
|
40
|
+
return { name: d.guestName || "", platform: d.guestPlatform || "" };
|
|
41
|
+
}
|
|
42
|
+
|
|
29
43
|
/** Fold one status frame (stream or poll) into the store. */
|
|
30
44
|
export function applyStatus(d) {
|
|
31
45
|
if (!d || !Array.isArray(d.instances)) return;
|
|
@@ -42,7 +56,18 @@ export function applyStatus(d) {
|
|
|
42
56
|
// #1629: the agents' themes' revision (app-themes.js re-reads them when it moves).
|
|
43
57
|
themesRev: typeof d.themes_rev === "number" ? d.themes_rev : null,
|
|
44
58
|
// #1685: host memory pressure slowing or holding new agents ("elevated" | "critical"), else null.
|
|
45
|
-
hostMemory: d.host_memory === "elevated" || d.host_memory === "critical" ? d.host_memory : null
|
|
59
|
+
hostMemory: d.host_memory === "elevated" || d.host_memory === "critical" ? d.host_memory : null,
|
|
60
|
+
// #2015: while it lasts, the largest processes in the fleet and whose they are (at most 3; anything else dropped).
|
|
61
|
+
hostMemoryTop: hostMemoryTop(d.host_memory_top) });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** #2015: the status frame's host_memory_top, as the dashboard keeps it: well-formed entries only, at most 3. */
|
|
65
|
+
export function hostMemoryTop(raw) {
|
|
66
|
+
if (!Array.isArray(raw)) return [];
|
|
67
|
+
return raw.slice(0, 3).flatMap(p => p && typeof p.name === "string" && Number.isSafeInteger(p.pid) && Number.isSafeInteger(p.rssBytes) && p.rssBytes >= 0
|
|
68
|
+
&& ["fleet", "tmux", "instance", "other"].includes(p.owner)
|
|
69
|
+
? [{ name: p.name.slice(0, 64), pid: p.pid, rssBytes: p.rssBytes, owner: p.owner, ...(p.owner === "instance" && typeof p.instance === "string" ? { instance: p.instance.slice(0, 128) } : {}) }]
|
|
70
|
+
: []);
|
|
46
71
|
}
|
|
47
72
|
|
|
48
73
|
/** One "activity" event: an instance started or stopped working. */
|
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
// - suspend()/resume(): the page is leaving (pagehide) / came back from the back-forward cache.
|
|
31
31
|
|
|
32
32
|
// #1218: steps_tick — an instance's transcript moved while a Log watches it (SSE only: a poll page asks the Log itself).
|
|
33
|
-
|
|
33
|
+
// #1918: traffic — a message between instances moved; delegation — a task opened, or its report closed it (SSE only: the
|
|
34
|
+
// org canvas reads /ui/traffic when it has no stream).
|
|
35
|
+
const EVENTS = ["status", "message", "agent_reply_live", "delivery", "deliveries", "prompt", "prompts", "prompt_resolved", "activity", "needs", "reply_buttons", "steps_tick", "clock", "traffic", "delegation"];
|
|
34
36
|
const MAX_HELD = 1000; // live events held during one catch-up read; more → read again afterwards
|
|
35
37
|
const RETRY_DELAYS = [1000, 2000, 4000]; // after the first failed catch-up read; then it waits for Retry
|
|
36
38
|
const POLL_LIMIT_MS = 10000; // a poll that has not answered by then counts as failed
|
|
@@ -10,8 +10,11 @@ import { t } from "./app-i18n.js";
|
|
|
10
10
|
import { toast } from "./ui-toast.js";
|
|
11
11
|
import { confirmDialog } from "./ui-confirm.js";
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* { rev, themes: { <name>: theme }, locked: [<name>], widgets: { <name>: { <slot>: widget } }, ambient: { <name>: { loop?, click? } } }
|
|
15
|
+
* as GET /ui/themes said (rev -1: not read yet).
|
|
16
|
+
*/
|
|
17
|
+
export const themesStore = createStore({ rev: -1, themes: {}, locked: [], widgets: {}, ambient: {}, shown: true });
|
|
15
18
|
const own = (o, k) => !!o && Object.prototype.hasOwnProperty.call(o, k);
|
|
16
19
|
|
|
17
20
|
const COLOR = /^#[0-9a-f]{6}$/;
|
|
@@ -30,14 +33,15 @@ function labelOk(s) {
|
|
|
30
33
|
return n >= 1 && n <= 24;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
// Public sessions cannot write host prefs. Keep their explicit switch apart from boot-cached
|
|
36
|
+
// Public/guest sessions cannot write host prefs. Keep their explicit browser switch apart from boot-cached defaults.
|
|
37
|
+
// Keep the existing public key for compatibility; both read-only viewer surfaces share this device choice.
|
|
34
38
|
const PUBLIC_FX_SHOW = "agend_public_fx_show";
|
|
35
39
|
let publicFxChoice = (() => { try { const v = localStorage.getItem(PUBLIC_FX_SHOW); return v === "on" ? true : v === "off" ? false : null; } catch { return null; } })();
|
|
36
40
|
|
|
37
41
|
/** Whether this page shows agents' themes now: the viewer's switch (default on), and never in safe mode. */
|
|
38
42
|
export function themesShown() {
|
|
39
43
|
if (safeMode()) return false;
|
|
40
|
-
if (
|
|
44
|
+
if (localAppearanceViewer() && publicFxChoice !== null) return publicFxChoice;
|
|
41
45
|
try {
|
|
42
46
|
const f = prefInForce("fx.show"); // this page's choice, else the host's, else the default
|
|
43
47
|
if (f) return f.value !== false;
|
|
@@ -46,7 +50,7 @@ export function themesShown() {
|
|
|
46
50
|
}
|
|
47
51
|
/** The viewer's switch (Settings → Appearance): kept with the person's preferences, and in force on this page at once. */
|
|
48
52
|
export function setThemesShown(on) {
|
|
49
|
-
if (
|
|
53
|
+
if (localAppearanceViewer()) {
|
|
50
54
|
publicFxChoice = !!on;
|
|
51
55
|
try { localStorage.setItem(PUBLIC_FX_SHOW, on ? "on" : "off"); } catch { /* the in-memory choice still applies */ }
|
|
52
56
|
} else savePref("fx.show", on ? null : false);
|
|
@@ -122,7 +126,8 @@ async function read(rev) {
|
|
|
122
126
|
if (!body || typeof body !== "object") break;
|
|
123
127
|
themesStore.set({ rev: typeof body.rev === "number" ? body.rev : want, themes: body.themes && typeof body.themes === "object" ? body.themes : {},
|
|
124
128
|
locked: Array.isArray(body.locked) ? body.locked.filter((n) => typeof n === "string") : [],
|
|
125
|
-
widgets: body.widgets && typeof body.widgets === "object" && !Array.isArray(body.widgets) ? body.widgets : {}
|
|
129
|
+
widgets: body.widgets && typeof body.widgets === "object" && !Array.isArray(body.widgets) ? body.widgets : {},
|
|
130
|
+
ambient: body.ambient && typeof body.ambient === "object" && !Array.isArray(body.ambient) ? body.ambient : {} });
|
|
126
131
|
if (want === wanted) break;
|
|
127
132
|
}
|
|
128
133
|
} finally { reading = false; }
|
|
@@ -144,6 +149,7 @@ export function startThemes() {
|
|
|
144
149
|
}
|
|
145
150
|
|
|
146
151
|
const publicLink = () => typeof document !== "undefined" && !!document.body && document.body.dataset.publicLink === "1";
|
|
152
|
+
const localAppearanceViewer = () => publicLink() || typeof document !== "undefined" && document.body?.dataset.tier === "guest";
|
|
147
153
|
|
|
148
154
|
/** A person's Reset or Lock of an agent's theme (an admin session; never over the public link). */
|
|
149
155
|
async function themeAction(name, what, body) {
|
|
@@ -169,6 +175,18 @@ async function removeWidget(name, slot) {
|
|
|
169
175
|
else toast(t("chat.widgetRemoved", name));
|
|
170
176
|
}
|
|
171
177
|
|
|
178
|
+
/** #2117: a person's Clear of an agent's ambient, one kind ("loop" or "click"). */
|
|
179
|
+
export async function clearAmbient(name, which) {
|
|
180
|
+
let r;
|
|
181
|
+
try {
|
|
182
|
+
const res = await fetch(`/ui/themes/${encodeURIComponent(name)}/ambient`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ which }) });
|
|
183
|
+
r = await res.json().catch(() => ({}));
|
|
184
|
+
if (!res.ok) r = { error: r && r.error ? r.error : `HTTP ${res.status}` };
|
|
185
|
+
} catch (err) { r = { error: err && err.message ? err.message : "network" }; }
|
|
186
|
+
if (r && r.error) toast(t("chat.themeFailed", r.error), false);
|
|
187
|
+
else toast(t("chat.ambientCleared", name));
|
|
188
|
+
}
|
|
189
|
+
|
|
172
190
|
/** The chat's and Details' ⋯ entries for an agent's theme: Lock/Unlock, Reset… and its widgets' Remove — none over the public link. */
|
|
173
191
|
export function themeMenuItems(name) {
|
|
174
192
|
if (publicLink()) return [];
|