@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
package/dist/ui/panel-chat.js
CHANGED
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
// right, the agent's at full width, and one rounded composer pinned below with attach, Stop reply and Send inside it.
|
|
7
7
|
// The instance's own actions (details, start, stop, restart, delete) are in the header's ⋯ menu, and only the ones
|
|
8
8
|
// that apply to its state are offered (#1408 rough edges 4 and 7).
|
|
9
|
-
import { html, useEffect, useLayoutEffect, useRef, useState } from "/assets/app-html.js";
|
|
10
|
-
import { t } from "/assets/app-i18n.js";
|
|
11
|
-
import { appStore, useStore } from "/assets/app-store.js";
|
|
9
|
+
import { html, useCallback, useEffect, useLayoutEffect, useRef, useState } from "/assets/app-html.js";
|
|
10
|
+
import { lang, t } from "/assets/app-i18n.js";
|
|
11
|
+
import { appStore, guestPage, useStore } from "/assets/app-store.js";
|
|
12
|
+
import { AuthorizeChip } from "/assets/web-authorize.js";
|
|
12
13
|
import { useLease } from "/assets/app-ctx.js";
|
|
13
|
-
import { namesOf, namesStore } from "/assets/instance-nav.js";
|
|
14
|
+
import { modelText, namesOf, namesStore, navStore as instanceNavStore } from "/assets/instance-nav.js";
|
|
14
15
|
import { PanelHeader, onPanelKey, setTitle, statusClass, statusLabel, openDrawer, requestNewInstance } from "/assets/app-shell.js";
|
|
15
16
|
import { detailsPath, textLinkSegments } from "/assets/app-route.js";
|
|
16
17
|
import { InstanceSwitch } from "/assets/instance-switch.js";
|
|
@@ -23,7 +24,7 @@ import { Icon } from "/assets/ui-icons.js";
|
|
|
23
24
|
import { toast } from "/assets/ui-toast.js";
|
|
24
25
|
import { confirmDialog } from "/assets/ui-confirm.js";
|
|
25
26
|
import { savePref } from "/assets/app-prefs.js";
|
|
26
|
-
import { applyTheme, resolvedMode, themeFor, themeImageUrl, themeMenuItems, themesStore } from "/assets/app-themes.js";
|
|
27
|
+
import { applyTheme, clearAmbient, resolvedMode, themeFor, themeImageUrl, themeMenuItems, themesStore } from "/assets/app-themes.js";
|
|
27
28
|
import { colorMenuItem } from "/assets/identity-color.js";
|
|
28
29
|
import { IdentityCard, useCard } from "/assets/identity-card.js";
|
|
29
30
|
import { AgentWidget, instanceBinds, widgetFor } from "/assets/agent-widget.js";
|
|
@@ -56,6 +57,8 @@ import { installTour, refreshTourSpot } from "./chat-tour.js";
|
|
|
56
57
|
import { LightboxHost, openLightbox } from "./image-lightbox.js";
|
|
57
58
|
import { ChatEffectPosters } from "/ui/js/chat-effect-posters.js";
|
|
58
59
|
import { ChatEffects, chatEffectsMenuAvailable } from "./chat-effects.js";
|
|
60
|
+
import { setFollowsReducedMotion } from "/assets/fx-motion.js";
|
|
61
|
+
import { AmbientMotionNote, ChatAmbient, ambientMenuItems } from "./chat-ambient.js";
|
|
59
62
|
import { startAppEffects, getAppEffects } from "./app-effects.js";
|
|
60
63
|
|
|
61
64
|
const R = () => globalThis.AgendChatRender;
|
|
@@ -116,6 +119,7 @@ export function boot({ stream, boot: bootData, deps = {} }) {
|
|
|
116
119
|
}
|
|
117
120
|
// #1604: the preview opt-in and Tour live in Settings → This device now (and on each preview card's menu).
|
|
118
121
|
installTour();
|
|
122
|
+
// #2118: viewing current in-scope Chat effects needs no management or personal-slot authority.
|
|
119
123
|
startAppEffects({ store });
|
|
120
124
|
return store;
|
|
121
125
|
}
|
|
@@ -222,6 +226,9 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
222
226
|
const themeKey = JSON.stringify(theme);
|
|
223
227
|
const card = useCard(name, inst.card_v);
|
|
224
228
|
const [dialog, setDialog] = useState(null); // "delete" | null
|
|
229
|
+
const [canvasPlayback, setCanvasPlayback] = useState(null);
|
|
230
|
+
const publishCanvasPlayback = useCallback(value => setCanvasPlayback(previous =>
|
|
231
|
+
!previous && !value.playing || previous?.name === value.name && previous.playing === value.playing ? previous : value), []);
|
|
225
232
|
const [wrap, setWrap] = useState(codeWrap);
|
|
226
233
|
const view = useRef(null), split = useRef(null), thread = useRef(null), bg = useRef(null), effectSurface = useRef(null);
|
|
227
234
|
useLayoutEffect(() => { applyTheme(view.current, theme, mode); }, [themeKey, mode]);
|
|
@@ -274,24 +281,32 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
274
281
|
colorMenuItem(name, inst),
|
|
275
282
|
// #1629: a person's control over this agent's theme — not over the public link (it only views themes).
|
|
276
283
|
...themeMenuItems(name),
|
|
284
|
+
// #2117: its ambient — why it is not showing here (with the way to change that), and a person's Clear.
|
|
285
|
+
...ambientMenuItems(name, { publicLink: document.body.dataset.publicLink === "1", clear: clearAmbient }),
|
|
277
286
|
chatEffectsMenuAvailable() ? { key: "effects", label: t("chatFx.title"), onSelect: () => setDialog("effects") } : null,
|
|
278
287
|
{ key: "delete", label: t("chat.delete"), icon: "trash", danger: true, onSelect: () => setDialog("delete") },
|
|
279
288
|
];
|
|
280
289
|
const cls = statusClass(inst, exec, awaiting);
|
|
290
|
+
// #1920 phase 3: a guest has this chat only — no Details, View or Log to switch to, no lifecycle, colour, theme,
|
|
291
|
+
// effect management or delete (a guest's status frame carries no model or effort either, so those chips do not show).
|
|
292
|
+
const guest = guestPage();
|
|
281
293
|
// #1269 quick actions: the model and effort it runs, each a way to /model or /effort (the same pick list).
|
|
282
294
|
const pick = (command) => store.runCommand(name, command, "", { alive: () => lease.current() });
|
|
283
295
|
const sub = html`<span class="status"><span class=${`dot ${cls}`} aria-hidden="true"></span>${statusLabel(inst, exec, awaiting)}</span>
|
|
284
|
-
${inst.model ? html`<button type="button" class="hd-chip" title=${t("chat.chipModel", inst
|
|
296
|
+
${inst.model ? html`<button type="button" class="hd-chip" title=${t("chat.chipModel", modelText(inst))} aria-label=${t("chat.chipModel", modelText(inst))} onClick=${() => pick("model")}>${modelText(inst)}</button>` : null}
|
|
285
297
|
${inst.effort ? html`<button type="button" class="hd-chip" title=${t("chat.chipEffort", inst.effort)} aria-label=${t("chat.chipEffort", inst.effort)} onClick=${() => pick("effort")}>${inst.effort}</button>` : null}`;
|
|
286
298
|
return html`<div class=${`panel p-chat${wrap ? " wrap-code" : ""}`} ref=${view}>
|
|
287
|
-
<${PanelHeader} title=${headerName(namesOf(inst || { name }, names.primary))} sub=${sub} nav=${html`<${InstanceSwitch} name=${name} current="chat" />`}
|
|
299
|
+
<${PanelHeader} title=${headerName(namesOf(inst || { name }, names.primary))} sub=${sub} nav=${guest ? null : html`<${InstanceSwitch} name=${name} current="chat" />`} notice=${html`<${ChatEffects} name=${name} lease=${lease} element=${effectSurface} store=${store} open=${dialog === "effects"} onPlayback=${publishCanvasPlayback} onClose=${() => setDialog(null)} />`}><${ChatEffectPosters} name=${name} lease=${lease} element=${effectSurface} /><${HeaderTools} navKey=${navKey} />${guest ? null : html`<${Menu} items=${items} label=${t("app.more")} />`}</${PanelHeader}>
|
|
288
300
|
<${ChatCard} key=${name} name=${name} card=${card} />
|
|
289
301
|
<${AgentWidget} slot="chat_header" widget=${widgetFor(name, "chat_header")} binds=${instanceBinds(inst, exec, awaiting)} />
|
|
290
302
|
<div class="chat-split" ref=${split}>
|
|
291
303
|
<div ref=${effectSurface} class=${`chat-main${theme && theme.background ? " has-th-bg" : ""}`}>
|
|
292
304
|
${theme && theme.background ? html`<img ref=${bg} class=${`th-bg th-bg-${theme.background.fit}`} src=${themeImageUrl(name, theme.background.image)} alt="" aria-hidden="true" decoding="async" />` : null}
|
|
305
|
+
<${ChatAmbient} name=${name} surface=${effectSurface} part="layer" />
|
|
306
|
+
<${AmbientMotionNote} name=${name} canvasPlaying=${canvasPlayback?.name === name && canvasPlayback.playing} />
|
|
293
307
|
<${Thread} name=${name} th=${thread} />
|
|
294
308
|
<${Dock} name=${name} inst=${inst} lease=${lease} exec=${exec} awaiting=${awaiting} workingLabel=${theme ? theme.workingLabel : undefined} />
|
|
309
|
+
<${ChatAmbient} name=${name} surface=${effectSurface} part="front" />
|
|
295
310
|
</div>
|
|
296
311
|
<${PreviewPanel} name=${name} split=${split} msgs=${() => store.state.msgs[name] || []} subscribe=${(fn) => store.subscribe(fn)}
|
|
297
312
|
keyOf=${msgKey} reveal=${(k) => thread.current && thread.current.reveal(k)} download=${downloadHtml} allow=${(commit) => setPreviewOptIn(true, commit)} />
|
|
@@ -302,61 +317,145 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
302
317
|
</div>`;
|
|
303
318
|
}
|
|
304
319
|
|
|
305
|
-
/**
|
|
320
|
+
/**
|
|
321
|
+
* #2053: the last THREAD_KEEP chats' threads are kept when their view leaves (the chat view is keyed by instance, so
|
|
322
|
+
* each switch built the thread again: a revisit was no faster than a first visit). A kept thread is its list element
|
|
323
|
+
* and its renderer, and nothing more: it leaves as a chat always did (previews stop), with no subscription or timer
|
|
324
|
+
* left; shown again, one render draws only what changed meanwhile, where the reader was. A new language, another
|
|
325
|
+
* document, or more than the budget lets one go.
|
|
326
|
+
*
|
|
327
|
+
* #2184: the budget (raised for people who switch among ~10 favourites): up to THREAD_KEEP threads on a wide screen and
|
|
328
|
+
* THREAD_KEEP_NARROW on a narrow one (a phone), and THREAD_KEEP_NODES drawn messages in all, so memory stays bounded
|
|
329
|
+
* however long the chats are. Over it, the least recently shown goes first — one that is not a favourite before a
|
|
330
|
+
* favourite; the chat on screen never goes.
|
|
331
|
+
*/
|
|
332
|
+
export const THREAD_KEEP = 12;
|
|
333
|
+
export const THREAD_KEEP_NARROW = 6;
|
|
334
|
+
export const THREAD_KEEP_NODES = 2400;
|
|
335
|
+
/**
|
|
336
|
+
* The kept threads to let go, in order. `kept`: [name, drawn messages], least recently shown first; `current`: the
|
|
337
|
+
* one on screen (never let go).
|
|
338
|
+
*/
|
|
339
|
+
export function threadsToLetGo(kept, current, { keep, maxNodes, favorites }) {
|
|
340
|
+
let count = kept.length, nodes = kept.reduce((n, [, d]) => n + d, 0);
|
|
341
|
+
const out = [];
|
|
342
|
+
const order = [...kept.filter(([n]) => n !== current && !favorites.has(n)), ...kept.filter(([n]) => n !== current && favorites.has(n))];
|
|
343
|
+
for (const [n, d] of order) {
|
|
344
|
+
if (count <= keep && nodes <= maxNodes) break;
|
|
345
|
+
out.push(n); count--; nodes -= d;
|
|
346
|
+
}
|
|
347
|
+
return out;
|
|
348
|
+
}
|
|
349
|
+
const narrowScreen = () => typeof matchMedia === "function" && matchMedia("(max-width: 899px)").matches;
|
|
350
|
+
/** Let go what the budget does not hold (after `current` was shown, or drew more). */
|
|
351
|
+
function trimKept(current) {
|
|
352
|
+
const kept = [...keptThreads].map(([n, e]) => [n, e.thread.nodes.size]);
|
|
353
|
+
const favorites = new Set(instanceNavStore.get().favorites);
|
|
354
|
+
for (const n of threadsToLetGo(kept, current, { keep: narrowScreen() ? THREAD_KEEP_NARROW : THREAD_KEEP, maxNodes: THREAD_KEEP_NODES, favorites })) {
|
|
355
|
+
letGo(keptThreads.get(n), true); keptThreads.delete(n);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const keptThreads = new Map(); // name → { thread, list, sink, lang }
|
|
359
|
+
/** Tests only: let every kept thread go. */
|
|
360
|
+
export function forgetKeptThreads() { for (const e of keptThreads.values()) letGo(e, true); keptThreads.clear(); }
|
|
361
|
+
function letGo(e, own) { e.thread.dispose(own); e.list.remove(); }
|
|
362
|
+
|
|
363
|
+
/** A chat's thread: its own list element and renderer, and a sink for the view that shows it. */
|
|
364
|
+
function threadEntry(name, doc) {
|
|
365
|
+
const list = doc.createElement("div");
|
|
366
|
+
list.className = "thread";
|
|
367
|
+
list.setAttribute("role", "log");
|
|
368
|
+
list.setAttribute("aria-live", "off");
|
|
369
|
+
list.setAttribute("aria-label", t("chat.conversationWith", name));
|
|
370
|
+
const e = { list, sink: null, lang: lang(), thread: null };
|
|
371
|
+
const thread = createThread(list, doc.createElement("div"), {
|
|
372
|
+
t, tf: t, isUser: (x) => store.isUser(x, name), onJump: (j) => e.sink?.setJump(j), onEmpty: (v) => e.sink?.setEmpty(v),
|
|
373
|
+
setPreviewOptIn, copyText, download: downloadHtml, toggleWrap,
|
|
374
|
+
effectTrialStatus: x => getAppEffects()?.trialStatus(x) ?? "localAdmin",
|
|
375
|
+
effectTrialFailure: x => getAppEffects()?.trialFailure(x),
|
|
376
|
+
fixEffectTrial: (x, reason) => {
|
|
377
|
+
if (reason === "reducedMotion") setFollowsReducedMotion(false);
|
|
378
|
+
else if (reason === "busy") getAppEffects()?.stopTrial();
|
|
379
|
+
else if (reason === "transport") location.reload();
|
|
380
|
+
else if (reason === "localAdmin") location.href = "/signin?next=" + encodeURIComponent(location.pathname + location.search);
|
|
381
|
+
else navigate("/settings/appearance");
|
|
382
|
+
},
|
|
383
|
+
canTryEffect: x => getAppEffects()?.canTry(x) === true,
|
|
384
|
+
startEffectTrial: x => { const seq = navStore.get().seq; void getAppEffects()?.startTrial(x).then(ok => { if (!ok && e.sink && navStore.get().seq === seq && document.hasFocus?.() === true && document.visibilityState === "visible") toast(t("chat.effectTrialUnavailable")); }); },
|
|
385
|
+
clickReplyButton: (id, index) => store.clickReplyButton(name, id, index), // #1266
|
|
386
|
+
replyButtonBusy: (id) => store.state.rbBusy.has(id),
|
|
387
|
+
pollMine: (id) => store.state.pollMine[id], // #1823: this device's own vote
|
|
388
|
+
serverNow: () => Date.now() + store.state.clockSkew, // #1823: the deadline is the server's
|
|
389
|
+
panel: { shown: shownKey, open: (spec, o) => openPanel(spec, o), version: panelVersion, claim: claimPanel, claims: panelClaims }, // #1481, #1704
|
|
390
|
+
openImage: openLightbox, // an image, full size, in the page
|
|
391
|
+
emojis: (id) => !!emojiIndex && emojiIndex.known(id), // #1630: the fleet's server emoji
|
|
392
|
+
mentions: (kind, id) => (mentionIndex ? mentionIndex.name(kind, id) : null), // #1682: mention names
|
|
393
|
+
});
|
|
394
|
+
e.thread = thread;
|
|
395
|
+
return e;
|
|
396
|
+
}
|
|
397
|
+
/** While a thread is shown: what it follows. Returns what lets it all go. */
|
|
398
|
+
function follow(name, thread) {
|
|
399
|
+
// #1630: the server-emoji list is read once a message shows a tag; when it arrives, those messages are drawn again.
|
|
400
|
+
// #1682: the same for mention names — asked for the ids the messages carry; when names arrive, drawn again.
|
|
401
|
+
const wantEmojis = (msgs) => { if (emojiIndex && emojiIndex.wants(msgs)) emojiIndex.ensure(); if (mentionIndex) mentionIndex.ensure(msgs); };
|
|
402
|
+
const offs = [];
|
|
403
|
+
if (emojiIndex) offs.push(emojiIndex.subscribe(() => thread.render(store.state.msgs[name] || [])));
|
|
404
|
+
if (mentionIndex) offs.push(mentionIndex.subscribe(() => thread.render(store.state.msgs[name] || [])));
|
|
405
|
+
wantEmojis(store.state.msgs[name]);
|
|
406
|
+
offs.push(store.subscribe((instance, kind) => {
|
|
407
|
+
if (instance !== name) return;
|
|
408
|
+
if (kind === "msgs") { thread.render(store.state.msgs[name] || []); wantEmojis(store.state.msgs[name]); }
|
|
409
|
+
else if (kind === "sent") thread.jumpLatest();
|
|
410
|
+
}));
|
|
411
|
+
offs.push(P() ? P().onChange(() => thread.refreshCards()) : () => {});
|
|
412
|
+
offs.push(getAppEffects()?.subscribeTrial(() => thread.render(store.state.msgs[name] || [])) ?? (() => {}));
|
|
413
|
+
offs.push(panelStore.subscribe(() => thread.refreshCards())); // a card shows whether the panel has its block
|
|
414
|
+
return () => { for (const off of offs) off(); };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** The thread's view: Preact owns the scroller and the jump pill; the kept list (chat-thread.js) is put inside it. */
|
|
306
418
|
function Thread({ name, th }) {
|
|
307
|
-
const scroller = useRef(null),
|
|
419
|
+
const scroller = useRef(null), host = useRef(null);
|
|
308
420
|
const [jump, setJump] = useState({ show: false, unseen: 0 });
|
|
309
421
|
const [empty, setEmpty] = useState(false);
|
|
310
422
|
useLayoutEffect(() => {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
mentions: (kind, id) => (mentionIndex ? mentionIndex.name(kind, id) : null), // #1682: mention names
|
|
325
|
-
});
|
|
423
|
+
const doc = host.current.ownerDocument;
|
|
424
|
+
let e = keptThreads.get(name);
|
|
425
|
+
// Kept only while it can be shown as it is: the same language, and this page's own document.
|
|
426
|
+
if (e && (e.lang !== lang() || e.list.ownerDocument !== doc)) { letGo(e, true); keptThreads.delete(name); e = undefined; }
|
|
427
|
+
const kept = !!e;
|
|
428
|
+
if (e) keptThreads.delete(name);
|
|
429
|
+
else e = threadEntry(name, doc);
|
|
430
|
+
keptThreads.set(name, e); // the most recent last
|
|
431
|
+
trimKept(name);
|
|
432
|
+
const thread = e.thread, sc = scroller.current, ls = e.list;
|
|
433
|
+
e.sink = { setJump, setEmpty };
|
|
434
|
+
host.current.append(ls);
|
|
435
|
+
thread.attach(sc);
|
|
326
436
|
th.current = thread;
|
|
327
|
-
//
|
|
328
|
-
// #1682: the same for mention names — asked for the ids the messages carry; when names arrive, drawn again.
|
|
329
|
-
const wantEmojis = (msgs) => { if (emojiIndex && emojiIndex.wants(msgs)) emojiIndex.ensure(); if (mentionIndex) mentionIndex.ensure(msgs); };
|
|
330
|
-
const offEmojis = emojiIndex ? emojiIndex.subscribe(() => thread.render(store.state.msgs[name] || [])) : () => {};
|
|
331
|
-
const offMentions = mentionIndex ? mentionIndex.subscribe(() => thread.render(store.state.msgs[name] || [])) : () => {};
|
|
437
|
+
// Up to date, at the reader's place (the bottom unless they had scrolled up); a kept one's cards say what they can do.
|
|
332
438
|
thread.render(store.state.msgs[name] || [], { restore: store.state.scrollMemo[name] ?? null });
|
|
333
|
-
|
|
334
|
-
const
|
|
335
|
-
if (instance !== name) return;
|
|
336
|
-
if (kind === "msgs") { thread.render(store.state.msgs[name] || []); wantEmojis(store.state.msgs[name]); }
|
|
337
|
-
else if (kind === "sent") thread.jumpLatest();
|
|
338
|
-
});
|
|
339
|
-
const offPv = P() ? P().onChange(() => thread.refreshCards()) : () => {};
|
|
340
|
-
const offTrials = getAppEffects()?.subscribeTrial(() => thread.render(store.state.msgs[name] || [])) ?? (() => {});
|
|
341
|
-
const offPanel = panelStore.subscribe(() => thread.refreshCards()); // a card shows whether the panel has its block
|
|
439
|
+
if (kept) thread.refreshCards();
|
|
440
|
+
const unfollow = follow(name, thread);
|
|
342
441
|
const onScroll = () => { store.state.scrollMemo[name] = thread.onScroll(); };
|
|
343
|
-
const sc = scroller.current, ls = list.current;
|
|
344
442
|
sc.addEventListener("scroll", onScroll, { passive: true });
|
|
345
443
|
ls.addEventListener("click", thread.onClick);
|
|
346
444
|
return () => {
|
|
347
|
-
|
|
348
|
-
off(); offPv(); offPanel(); offTrials(); offEmojis(); offMentions();
|
|
445
|
+
unfollow();
|
|
349
446
|
sc.removeEventListener("scroll", onScroll);
|
|
350
447
|
ls.removeEventListener("click", thread.onClick);
|
|
351
|
-
|
|
448
|
+
e.sink = null;
|
|
352
449
|
th.current = null;
|
|
450
|
+
if (keptThreads.get(name) === e) { thread.park(); ls.remove(); } // kept for a later visit: the chat left
|
|
451
|
+
else letGo(e, false); // let go meanwhile (a new language)
|
|
353
452
|
};
|
|
354
453
|
}, [name]);
|
|
355
454
|
return html`<div class="scroller" ref=${scroller}>
|
|
356
|
-
<div class="thread" ref=${
|
|
455
|
+
<div class="thread-host" ref=${host}></div>
|
|
357
456
|
${empty ? html`<${Empty} icon="chat" title=${t("chat.noMessages")} />` : null}
|
|
358
|
-
|
|
359
|
-
<${Icon} name="down" size=${16} />${jump.unseen > 0 ? t("chat.newBelow", jump.unseen) : t("chat.jumpLatest")}</button>` : null}
|
|
457
|
+
<div class="jump-dock">${jump.show ? html`<button data-trust="thread-control" type="button" class="jump-latest" onClick=${() => th.current && th.current.jumpLatest()}>
|
|
458
|
+
<${Icon} name="down" size=${16} />${jump.unseen > 0 ? t("chat.newBelow", jump.unseen) : t("chat.jumpLatest")}</button>` : null}</div>
|
|
360
459
|
</div>`;
|
|
361
460
|
}
|
|
362
461
|
|
|
@@ -373,7 +472,8 @@ function Dock({ name, inst, lease, exec, awaiting, workingLabel }) {
|
|
|
373
472
|
<button type="button" class="btn btn-primary btn-sm" onClick=${() => store.putBack(name)}>${t("chat.putBack")}</button>
|
|
374
473
|
<button type="button" class="btn btn-sm" onClick=${() => store.discardFailed(name)}>${t("chat.discard")}</button></div>` : null}
|
|
375
474
|
<${Composer} name=${name} lease=${lease} busy=${R().isBusy(exec)} />
|
|
376
|
-
<p class="hint" title=${t("chat.composerMore")}>${t("chat.composerHint")}</p>
|
|
475
|
+
<p class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t("chat.composerHint")}</p>
|
|
476
|
+
${guestPage() ? html`<p class="guest-note">${t("chat.guestNote")}</p>` : html`<${AuthorizeChip} />`}
|
|
377
477
|
</div></div>`;
|
|
378
478
|
}
|
|
379
479
|
|
|
@@ -393,7 +493,8 @@ function CommandCard({ name, lease }) {
|
|
|
393
493
|
: c.compact === "lost" ? t("chat.compactLost") : c.compact === "not_compacted" ? t("chat.compactNotCompacted") : t("chat.compactUnknown")}</p>` : null}
|
|
394
494
|
${c.choices && c.choices.prompt ? html`<pre class="cmd-text">${c.choices.prompt}</pre>` : null}
|
|
395
495
|
${c.choices ? html`<div class="cmd-choices" role="group" aria-label=${t("chat.cmdChoose")}>${c.choices.options.map((o) => html`<button key=${o.id} type="button"
|
|
396
|
-
class=${`btn btn-sm${o.id === c.choices.current ? " current" : ""}`} aria-current=${o.id === c.choices.current ? "true" : undefined}
|
|
496
|
+
class=${`btn btn-sm${o.id === c.choices.current ? " current" : ""}`} aria-current=${o.id === c.choices.current ? "true" : undefined}
|
|
497
|
+
disabled=${!!o.disabled} title=${o.disabled || undefined} onClick=${() => { if (!o.disabled) run(o.id); }}>${o.label}</button>`)}</div>` : null}
|
|
397
498
|
</div>`;
|
|
398
499
|
}
|
|
399
500
|
|
|
@@ -449,8 +550,10 @@ function Composer({ name, lease, busy }) {
|
|
|
449
550
|
const value = s.drafts[name] || "";
|
|
450
551
|
const files = s.pendingFiles[name] || [];
|
|
451
552
|
const content = !!value.trim() || files.length > 0;
|
|
452
|
-
// A file waiting: what goes is a message with its file, never a command (no palette, no command line).
|
|
453
|
-
|
|
553
|
+
// A file waiting: what goes is a message with its file, never a command (no palette, no command line). A guest never
|
|
554
|
+
// runs one (#1920: the server refuses its slash text), so it has no palette either.
|
|
555
|
+
const guest = guestPage();
|
|
556
|
+
const palette = guest || files.length || closedFor === value ? null : paletteFor(value);
|
|
454
557
|
const pick = palette && palette.length ? palette[Math.min(sel, palette.length - 1)] : null;
|
|
455
558
|
// Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300).
|
|
456
559
|
useLayoutEffect(() => { const el = input.current; if (!el) return; el.style.height = "auto"; el.style.height = `${Math.min(el.scrollHeight, 240)}px`; }, [value]);
|
|
@@ -471,6 +574,8 @@ function Composer({ name, lease, busy }) {
|
|
|
471
574
|
else complete(pick);
|
|
472
575
|
return;
|
|
473
576
|
}
|
|
577
|
+
// #1920: a guest's "/…" would be refused as a command — said here, the draft kept to edit.
|
|
578
|
+
if (guest && /^\s*\//.test(value)) { toast(t("chat.guestNoCommands"), false); return; }
|
|
474
579
|
const cmd = files.length ? null : parseCommandLine(value);
|
|
475
580
|
if (cmd && needsArgument(cmd.command) && !cmd.args) { setValue(`/${cmd.command} `); return; }
|
|
476
581
|
if (cmd) { runLine(cmd); return; }
|
package/dist/ui/panel-details.js
CHANGED
|
@@ -14,6 +14,7 @@ import { PanelHeader, setTitle, statusClass, statusLabel, openDrawer } from "/as
|
|
|
14
14
|
import { navigate } from "/assets/app-nav.js";
|
|
15
15
|
import { settingsPath } from "/assets/app-route.js";
|
|
16
16
|
import { InstanceSwitch } from "/assets/instance-switch.js";
|
|
17
|
+
import { modelText } from "/assets/instance-nav.js";
|
|
17
18
|
import { HeaderTools } from "/assets/header-tools.js";
|
|
18
19
|
import { Menu } from "/assets/ui-menu.js";
|
|
19
20
|
import { themeMenuItems, themesStore } from "/assets/app-themes.js";
|
|
@@ -143,7 +144,7 @@ function DetailsView({ name, inst, navKey, exec, awaiting }) {
|
|
|
143
144
|
<button type="button" class="btn btn-sm d-edit-settings" onClick=${editInSettings}>${t("chat.dEditInSettings")}</button></div></section>
|
|
144
145
|
<section class="card"><h3>${t("chat.dRuntime")}</h3>
|
|
145
146
|
${row(t("chat.dBackend"), d.backend || "--")}
|
|
146
|
-
${row(t("chat.dModel"), `${d.model || sl.model?.display_name || "--"}${d.model_source === "live" ? "" : src(d.model_source)}`)}
|
|
147
|
+
${row(t("chat.dModel"), `${(d.model && modelText(d)) || sl.model?.display_name || "--"}${d.model_source === "live" ? "" : src(d.model_source)}`)}
|
|
147
148
|
${d.effort ? row(t("chat.dEffort"), `${d.effort}${src(d.effort_source)}`) : null}
|
|
148
149
|
${row(t("chat.dCost"), `$${cost.toFixed(2)} ${t("chat.dCostNote")}`)}
|
|
149
150
|
${row(t("chat.dContext"), html`<span class="pct">${ctx != null ? `${Math.round(ctx)}%` : "--"}</span>${ctx != null ? bar(ctx, ctx > 80 ? "warn" : "") : null}`)}
|
package/dist/ui/panel-fleet.js
CHANGED
|
@@ -11,22 +11,33 @@ import { useLease } from "/assets/app-ctx.js";
|
|
|
11
11
|
import { PanelHeader, setTitle } from "/assets/app-shell.js";
|
|
12
12
|
import { HeaderTools } from "/assets/header-tools.js";
|
|
13
13
|
import { renavigate } from "/assets/app-nav.js";
|
|
14
|
-
import { chatPath, fleetPath, FLEET_TABS } from "/assets/app-route.js";
|
|
14
|
+
import { chatPath, fleetPath, FLEET_TABS, rememberFleetTab } from "/assets/app-route.js";
|
|
15
15
|
import { Dialog } from "/assets/ui-dialog.js";
|
|
16
16
|
import { Empty, ErrorState, Skeleton } from "/assets/ui-states.js";
|
|
17
17
|
import { Icon } from "/assets/ui-icons.js";
|
|
18
18
|
import { toast } from "/assets/ui-toast.js";
|
|
19
19
|
import { confirmDialog } from "/assets/ui-confirm.js";
|
|
20
20
|
import { confirmedWrite } from "./settings-confirm.js";
|
|
21
|
-
import {
|
|
21
|
+
import { OrgCanvas } from "./fleet-canvas.js";
|
|
22
22
|
import { labelOf } from "/assets/instance-nav.js";
|
|
23
23
|
import { CacheReport } from "./fleet-cache.js";
|
|
24
|
+
import { UsageTab } from "./fleet-usage.js";
|
|
25
|
+
import { BackendSelect, ModelSelect } from "./settings-dialogs.js";
|
|
26
|
+
|
|
27
|
+
/** #2015: one of the largest processes as text: "python3 18.1 GB (dev-a)". */
|
|
28
|
+
export function memoryHogText(p) {
|
|
29
|
+
const size = p.rssBytes >= 1024 ** 3 ? `${(p.rssBytes / 1024 ** 3).toFixed(1)} GB` : `${Math.round(p.rssBytes / 1024 ** 2)} MB`;
|
|
30
|
+
const owner = p.owner === "instance" && p.instance ? p.instance
|
|
31
|
+
: t(p.owner === "fleet" ? "fleet.hostMemory_ownerFleet" : p.owner === "tmux" ? "fleet.hostMemory_ownerTmux" : "fleet.hostMemory_ownerOther");
|
|
32
|
+
return `${p.name} ${size} (${owner})`;
|
|
33
|
+
}
|
|
24
34
|
|
|
25
35
|
register("fleet", {
|
|
26
36
|
en: {
|
|
27
37
|
hostMemory_elevated: "Host memory pressure: elevated — new agents start one at a time, at least 5 s apart",
|
|
28
38
|
hostMemory_critical: "Host memory pressure: critical — new agents wait for memory to recover",
|
|
29
|
-
|
|
39
|
+
hostMemory_top: "Largest: {0}", hostMemory_ownerFleet: "the fleet", hostMemory_ownerTmux: "tmux", hostMemory_ownerOther: "not an instance",
|
|
40
|
+
title: "Fleet", tasks: "Tasks", schedules: "Schedules", teams: "Teams", org: "Org chart", cache: "Cache", usage: "Usage", config: "Config",
|
|
30
41
|
docs: "Docs", docsNewTab: "Docs (opens in a new tab)",
|
|
31
42
|
newTask: "New task", newSchedule: "New schedule", newTeam: "New team",
|
|
32
43
|
noTasks: "No tasks yet", noTasksHint: "Create one to coordinate fleet work.", noSchedules: "No schedules yet", noSchedulesHint: "Automate recurring work with cron.",
|
|
@@ -52,12 +63,13 @@ register("fleet", {
|
|
|
52
63
|
newInstance: "New instance", directory: "Directory", directoryHint: "Optional — a workspace is created if empty", topic: "Name", topicHint: "From the directory name",
|
|
53
64
|
topicRequired: "Name is required when Directory is empty", description: "Description", descriptionHint: "What this instance does", model: "Model", modelHint: "e.g. sonnet, opus, gpt-5",
|
|
54
65
|
branch: "Branch (git worktree)", branchHint: "e.g. feature-x", tags: "Tags", tagsHint: "comma-separated, e.g. dev, review", systemPrompt: "System prompt", systemPromptHint: "Custom instructions for this instance",
|
|
55
|
-
fleetDefault: "Use the fleet default", notInstalled: "(not installed)", deprecated: "(deprecated)", instanceCreated: "Instance created",
|
|
66
|
+
fleetDefault: "Use the fleet default", notInstalled: "(not installed)", notSignedIn: "(not signed in — use /login)", tooOld: "(needs {0} or newer)", unchecked: "(not checked yet)", deprecated: "(deprecated)", instanceCreated: "Instance created",
|
|
56
67
|
},
|
|
57
68
|
"zh-TW": {
|
|
58
69
|
hostMemory_elevated: "主機記憶體壓力偏高:新的 agent 一次啟動一個,至少間隔 5 秒",
|
|
59
70
|
hostMemory_critical: "主機記憶體壓力嚴重:新的 agent 會等記憶體恢復再啟動",
|
|
60
|
-
|
|
71
|
+
hostMemory_top: "最大:{0}", hostMemory_ownerFleet: "fleet 本身", hostMemory_ownerTmux: "tmux", hostMemory_ownerOther: "不屬於任何 instance",
|
|
72
|
+
title: "Fleet", tasks: "Tasks", schedules: "排程", teams: "Teams", org: "組織圖", cache: "快取", usage: "用量", config: "設定",
|
|
61
73
|
docs: "文件", docsNewTab: "文件(在新分頁開啟)",
|
|
62
74
|
newTask: "新增 task", newSchedule: "新增排程", newTeam: "新增 team",
|
|
63
75
|
noTasks: "還沒有 task", noTasksHint: "建立一個來協調 fleet 的工作。", noSchedules: "還沒有排程", noSchedulesHint: "用 cron 自動執行例行工作。",
|
|
@@ -82,7 +94,7 @@ register("fleet", {
|
|
|
82
94
|
newInstance: "新增 instance", directory: "目錄", directoryHint: "可留空——留空會自動建立工作區", topic: "名稱", topicHint: "預設取自目錄名稱",
|
|
83
95
|
topicRequired: "目錄留空時必須填寫名稱", description: "說明", descriptionHint: "這個 instance 做什麼", model: "模型", modelHint: "例如 sonnet、opus、gpt-5",
|
|
84
96
|
branch: "分支(git worktree)", branchHint: "例如 feature-x", tags: "標籤", tagsHint: "以逗號分隔,例如 dev, review", systemPrompt: "System prompt", systemPromptHint: "這個 instance 的自訂指示",
|
|
85
|
-
fleetDefault: "使用 fleet 預設", notInstalled: "(未安裝)", deprecated: "(已淘汰)", instanceCreated: "Instance 已建立",
|
|
97
|
+
fleetDefault: "使用 fleet 預設", notInstalled: "(未安裝)", notSignedIn: "(未登入,請用 /login)", tooOld: "(需要 {0} 以上版本)", unchecked: "(尚未檢查)", deprecated: "(已淘汰)", instanceCreated: "Instance 已建立",
|
|
86
98
|
},
|
|
87
99
|
});
|
|
88
100
|
|
|
@@ -91,7 +103,7 @@ const DOCS_SITE = "https://songsid.github.io/AgEnD";
|
|
|
91
103
|
export function docsUrl(language = lang()) {
|
|
92
104
|
return `${DOCS_SITE}${language === "zh-TW" ? "/zh-tw" : ""}/docs/web-guide/`;
|
|
93
105
|
}
|
|
94
|
-
const ICONS = { tasks: "tasks", schedules: "clock", teams: "team", org: "org", cache: "chart", config: "sliders" };
|
|
106
|
+
const ICONS = { tasks: "tasks", schedules: "clock", teams: "team", org: "org", cache: "chart", usage: "chart", config: "sliders" };
|
|
95
107
|
const BACKENDS = ["claude-code", "codex", "opencode", "kiro-cli", "antigravity", "grok", "muse"];
|
|
96
108
|
|
|
97
109
|
async function api(method, path, body, lease) {
|
|
@@ -130,15 +142,18 @@ export function FleetPanel({ route, navKey }) {
|
|
|
130
142
|
const lease = useLease(navKey);
|
|
131
143
|
const tab = route.tab;
|
|
132
144
|
useEffect(() => { setTitle(`${t("fleet.title")} · ${t(`fleet.${tab}`)}`); }, [tab, navKey]);
|
|
133
|
-
|
|
145
|
+
useEffect(() => { rememberFleetTab(tab); }, [tab]); // #2183: the sidebar's and the phone's Fleet reopen it
|
|
146
|
+
const Body = { tasks: Tasks, schedules: Schedules, teams: Teams, org: Org, cache: Cache, usage: UsageTab, config: Config }[tab] || Tasks;
|
|
134
147
|
// #1685: host memory pressure slowing or holding new agents — shown here, where macOS's routine "elevated" is not a chat notice.
|
|
135
|
-
const { hostMemory } = useStore(appStore);
|
|
136
|
-
|
|
148
|
+
const { hostMemory, hostMemoryTop } = useStore(appStore);
|
|
149
|
+
// #2015: and which processes hold the most, and whose — text only (never chat, #1712).
|
|
150
|
+
const top = hostMemory && hostMemoryTop?.length ? t("fleet.hostMemory_top", hostMemoryTop.map(memoryHogText).join(" · ")) : "";
|
|
151
|
+
const sub = hostMemory ? html`<span class=${`status host-mem ${hostMemory}`} role="status"><span class=${`dot ${hostMemory === "critical" ? "bad" : "warn"}`} aria-hidden="true"></span>${t(`fleet.hostMemory_${hostMemory}`)}${top ? html`<span class="host-mem-top">${top}</span>` : null}</span>` : null;
|
|
137
152
|
return html`<div data-effect-panel="fleet" class="panel p-fleet">
|
|
138
153
|
<${PanelHeader} title=${t("fleet.title")} sub=${sub}><${DocsLink} /><${HeaderTools} navKey=${navKey} /></${PanelHeader}>
|
|
139
154
|
<nav data-trust="panel-navigation" class="seg" aria-label=${t("fleet.title")}>${FLEET_TABS.map(k => html`<a key=${k} href=${fleetPath(k)} class=${`seg-item${k === tab ? " active" : ""}`}
|
|
140
155
|
aria-current=${k === tab ? "page" : undefined}><${Icon} name=${ICONS[k]} size=${16} /><span>${t(`fleet.${k}`)}</span></a>`)}</nav>
|
|
141
|
-
<div data-trust="panel-content" class="panel-body"><div class=${`col${tab === "org" || tab === "cache" ? " col-wide" : ""}`}><${Body} lease=${lease} /></div></div>
|
|
156
|
+
<div data-trust="panel-content" class="panel-body"><div class=${`col${tab === "org" || tab === "cache" || tab === "usage" ? " col-wide" : ""}`}><${Body} lease=${lease} /></div></div>
|
|
142
157
|
</div>`;
|
|
143
158
|
}
|
|
144
159
|
|
|
@@ -255,11 +270,11 @@ function Teams({ lease }) {
|
|
|
255
270
|
${editing ? html`<${CreateTeam} team=${editing} onClose=${() => setEditing(null)} onDone=${() => setVersion(v => v + 1)} />` : null}`;
|
|
256
271
|
}
|
|
257
272
|
|
|
258
|
-
/** #1389: the org chart — its structure read once per opening, its live state from the app store (fleet-
|
|
273
|
+
/** #1389: the org chart — its structure read once per opening, its live state from the app store. #1918: a canvas, or the list (fleet-canvas.js). */
|
|
259
274
|
function Org({ lease }) {
|
|
260
275
|
const state = useLoad(lease, "/ui/org", 0);
|
|
261
276
|
const app = useStore(appStore);
|
|
262
|
-
return html`<${Loaded} state=${state}>${(org) => html`<${
|
|
277
|
+
return html`<${Loaded} state=${state}>${(org) => html`<${OrgCanvas} org=${org} app=${app} lease=${lease} />`}</${Loaded}>`;
|
|
263
278
|
}
|
|
264
279
|
|
|
265
280
|
/** #1468: the prompt-cache analysis — one read per opening and per window chosen; Refresh reads again (fleet-cache.js). */
|
|
@@ -322,8 +337,7 @@ function Config({ lease }) {
|
|
|
322
337
|
<div class="field"><label for="cfg-users">${t("fleet.allowedUsers")}</label><${Secret} id="cfg-users" value=${form.users} onInput=${set("users")} label=${t("fleet.allowedUsers")} />
|
|
323
338
|
<p class="note">${t("fleet.allowedUsersNote")}</p></div></section>
|
|
324
339
|
<section class="card"><h3>${t("fleet.defaults")}</h3>
|
|
325
|
-
<
|
|
326
|
-
${[...(BACKENDS.includes(form.backend) ? [] : [form.backend]), ...BACKENDS].map(b => html`<option key=${b} value=${b}>${b}</option>`)}</select></label></section>
|
|
340
|
+
<div class="field"><label for="cfg-be">${t("fleet.backend")}</label><${BackendSelect} id="cfg-be" value=${form.backend} onChange=${(v) => setForm({ ...form, backend: v })} /></div></section>
|
|
327
341
|
<section class="card"><h3>${t("fleet.roots")}</h3>
|
|
328
342
|
${form.roots.map((r, i) => html`<div key=${i} class="field-row"><input value=${r} placeholder="/path/to/projects" aria-label=${t("fleet.roots")}
|
|
329
343
|
onInput=${(e) => { const roots = [...form.roots]; roots[i] = e.target.value; setForm({ ...form, roots }); }} />
|
|
@@ -510,6 +524,8 @@ function CreateTeam({ onClose, onDone, team = null }) {
|
|
|
510
524
|
export function CreateInstanceDialog({ onClose }) {
|
|
511
525
|
const lease = useLease("create-instance");
|
|
512
526
|
const [backends, setBackends] = useState(null);
|
|
527
|
+
// The backend "fleet default" means (#2098 review P3-3): its models are the ones the model list offers then.
|
|
528
|
+
const [defaultBackend, setDefaultBackend] = useState("claude-code");
|
|
513
529
|
const [f, setF] = useState({ dir: "", topic: "", desc: "", backend: "", model: "", branch: "", tags: "", prompt: "" });
|
|
514
530
|
useEffect(() => {
|
|
515
531
|
(async () => {
|
|
@@ -518,6 +534,7 @@ export function CreateInstanceDialog({ onClose }) {
|
|
|
518
534
|
if (!lease.current()) return;
|
|
519
535
|
const list = bd.backends || [];
|
|
520
536
|
const first = list.find(b => b.installed && !b.deprecated) || list.find(b => b.installed);
|
|
537
|
+
if (typeof bd.defaultBackend === "string" && bd.defaultBackend) setDefaultBackend(bd.defaultBackend);
|
|
521
538
|
setBackends(list);
|
|
522
539
|
setF(x => ({ ...x, backend: first ? first.name : "" }));
|
|
523
540
|
} catch {
|
|
@@ -547,14 +564,23 @@ export function CreateInstanceDialog({ onClose }) {
|
|
|
547
564
|
${field(`${t("fleet.topic")}${f.dir.trim() ? "" : " *"}`, html`<input value=${f.topic} onInput=${set("topic")} placeholder=${t("fleet.topicHint")} />`)}
|
|
548
565
|
${field(t("fleet.description"), html`<input value=${f.desc} onInput=${set("desc")} placeholder=${t("fleet.descriptionHint")} />`)}
|
|
549
566
|
${field(t("fleet.backend"), backends ? html`<select value=${f.backend} onChange=${set("backend")}><option value="">${t("fleet.fleetDefault")}</option>
|
|
550
|
-
${backends.map(b => html`<option key=${b.name} value=${b.name} disabled=${
|
|
551
|
-
${field(t("fleet.model"), html
|
|
567
|
+
${backends.map(b => html`<option key=${b.name} value=${b.name} disabled=${backendBlocked(b)}>${b.name}${b.deprecated ? ` ${t("fleet.deprecated")}` : ""}${backendNote(b)}</option>`)}</select>` : html`<${Skeleton} lines=${1} />`)}
|
|
568
|
+
${field(t("fleet.model"), html`<${ModelSelect} value=${f.model} onChange=${(v) => setF(x => ({ ...x, model: v }))} backend=${f.backend || defaultBackend} inherit=${t("fleet.fleetDefault")} />`)}
|
|
552
569
|
${field(t("fleet.branch"), html`<input value=${f.branch} onInput=${set("branch")} placeholder=${t("fleet.branchHint")} />`)}
|
|
553
570
|
${field(t("fleet.tags"), html`<input value=${f.tags} onInput=${set("tags")} placeholder=${t("fleet.tagsHint")} />`)}
|
|
554
571
|
${field(t("fleet.systemPrompt"), html`<textarea rows="3" value=${f.prompt} onInput=${set("prompt")} placeholder=${t("fleet.systemPromptHint")}></textarea>`)}
|
|
555
572
|
</${FormDialog}>`;
|
|
556
573
|
}
|
|
557
574
|
|
|
575
|
+
// #2072: a backend this host cannot run is shown, disabled, with why; one not checked yet stays selectable.
|
|
576
|
+
const backendState = (b) => b.state ?? (b.installed ? "usable" : b.unknown ? "unknown" : "not_installed");
|
|
577
|
+
function backendBlocked(b) { return ["not_installed", "not_signed_in", "too_old"].includes(backendState(b)); }
|
|
578
|
+
function backendNote(b) {
|
|
579
|
+
const s = backendState(b);
|
|
580
|
+
return s === "not_installed" ? ` ${t("fleet.notInstalled")}` : s === "not_signed_in" ? ` ${t("fleet.notSignedIn")}`
|
|
581
|
+
: s === "too_old" ? ` ${t("fleet.tooOld", b.minVersion ?? "?")}` : s === "unknown" ? ` ${t("fleet.unchecked")}` : "";
|
|
582
|
+
}
|
|
583
|
+
|
|
558
584
|
// ── Cron in words (#1724: in the page's language) ──
|
|
559
585
|
/**
|
|
560
586
|
* A cron expression as a person says it — "Weekdays 18:00", 「每週一到五 18:00」, "Every 15 minutes" — or the expression
|