@songsid/agend 2.3.0-beta.1 → 2.3.0-beta.10
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 +476 -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 +5 -2
- package/dist/ui/chat-strings.js +59 -6
- package/dist/ui/chat-thread.js +149 -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 +127 -49
- package/dist/ui/panel-details.js +2 -1
- package/dist/ui/panel-fleet.js +41 -16
- 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 +1 -1
- package/dist/ui/shared/app-session.js +2 -0
- package/dist/ui/shared/app-shell.js +75 -9
- 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 +364 -16
- 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 +46 -0
- package/dist/web-chat-history.js +80 -3
- 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-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/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 } 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,118 @@ 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 THREAD_KEEP lets one go.
|
|
326
|
+
*/
|
|
327
|
+
export const THREAD_KEEP = 4;
|
|
328
|
+
const keptThreads = new Map(); // name → { thread, list, sink, lang }
|
|
329
|
+
/** Tests only: let every kept thread go. */
|
|
330
|
+
export function forgetKeptThreads() { for (const e of keptThreads.values()) letGo(e, true); keptThreads.clear(); }
|
|
331
|
+
function letGo(e, own) { e.thread.dispose(own); e.list.remove(); }
|
|
332
|
+
|
|
333
|
+
/** A chat's thread: its own list element and renderer, and a sink for the view that shows it. */
|
|
334
|
+
function threadEntry(name, doc) {
|
|
335
|
+
const list = doc.createElement("div");
|
|
336
|
+
list.className = "thread";
|
|
337
|
+
list.setAttribute("role", "log");
|
|
338
|
+
list.setAttribute("aria-live", "off");
|
|
339
|
+
list.setAttribute("aria-label", t("chat.conversationWith", name));
|
|
340
|
+
const e = { list, sink: null, lang: lang(), thread: null };
|
|
341
|
+
const thread = createThread(list, doc.createElement("div"), {
|
|
342
|
+
t, tf: t, isUser: (x) => store.isUser(x, name), onJump: (j) => e.sink?.setJump(j), onEmpty: (v) => e.sink?.setEmpty(v),
|
|
343
|
+
setPreviewOptIn, copyText, download: downloadHtml, toggleWrap,
|
|
344
|
+
effectTrialStatus: x => getAppEffects()?.trialStatus(x) ?? "localAdmin",
|
|
345
|
+
effectTrialFailure: x => getAppEffects()?.trialFailure(x),
|
|
346
|
+
fixEffectTrial: (x, reason) => {
|
|
347
|
+
if (reason === "reducedMotion") setFollowsReducedMotion(false);
|
|
348
|
+
else if (reason === "busy") getAppEffects()?.stopTrial();
|
|
349
|
+
else if (reason === "transport") location.reload();
|
|
350
|
+
else if (reason === "localAdmin") location.href = "/signin?next=" + encodeURIComponent(location.pathname + location.search);
|
|
351
|
+
else navigate("/settings/appearance");
|
|
352
|
+
},
|
|
353
|
+
canTryEffect: x => getAppEffects()?.canTry(x) === true,
|
|
354
|
+
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")); }); },
|
|
355
|
+
clickReplyButton: (id, index) => store.clickReplyButton(name, id, index), // #1266
|
|
356
|
+
replyButtonBusy: (id) => store.state.rbBusy.has(id),
|
|
357
|
+
pollMine: (id) => store.state.pollMine[id], // #1823: this device's own vote
|
|
358
|
+
serverNow: () => Date.now() + store.state.clockSkew, // #1823: the deadline is the server's
|
|
359
|
+
panel: { shown: shownKey, open: (spec, o) => openPanel(spec, o), version: panelVersion, claim: claimPanel, claims: panelClaims }, // #1481, #1704
|
|
360
|
+
openImage: openLightbox, // an image, full size, in the page
|
|
361
|
+
emojis: (id) => !!emojiIndex && emojiIndex.known(id), // #1630: the fleet's server emoji
|
|
362
|
+
mentions: (kind, id) => (mentionIndex ? mentionIndex.name(kind, id) : null), // #1682: mention names
|
|
363
|
+
});
|
|
364
|
+
e.thread = thread;
|
|
365
|
+
return e;
|
|
366
|
+
}
|
|
367
|
+
/** While a thread is shown: what it follows. Returns what lets it all go. */
|
|
368
|
+
function follow(name, thread) {
|
|
369
|
+
// #1630: the server-emoji list is read once a message shows a tag; when it arrives, those messages are drawn again.
|
|
370
|
+
// #1682: the same for mention names — asked for the ids the messages carry; when names arrive, drawn again.
|
|
371
|
+
const wantEmojis = (msgs) => { if (emojiIndex && emojiIndex.wants(msgs)) emojiIndex.ensure(); if (mentionIndex) mentionIndex.ensure(msgs); };
|
|
372
|
+
const offs = [];
|
|
373
|
+
if (emojiIndex) offs.push(emojiIndex.subscribe(() => thread.render(store.state.msgs[name] || [])));
|
|
374
|
+
if (mentionIndex) offs.push(mentionIndex.subscribe(() => thread.render(store.state.msgs[name] || [])));
|
|
375
|
+
wantEmojis(store.state.msgs[name]);
|
|
376
|
+
offs.push(store.subscribe((instance, kind) => {
|
|
377
|
+
if (instance !== name) return;
|
|
378
|
+
if (kind === "msgs") { thread.render(store.state.msgs[name] || []); wantEmojis(store.state.msgs[name]); }
|
|
379
|
+
else if (kind === "sent") thread.jumpLatest();
|
|
380
|
+
}));
|
|
381
|
+
offs.push(P() ? P().onChange(() => thread.refreshCards()) : () => {});
|
|
382
|
+
offs.push(getAppEffects()?.subscribeTrial(() => thread.render(store.state.msgs[name] || [])) ?? (() => {}));
|
|
383
|
+
offs.push(panelStore.subscribe(() => thread.refreshCards())); // a card shows whether the panel has its block
|
|
384
|
+
return () => { for (const off of offs) off(); };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** The thread's view: Preact owns the scroller and the jump pill; the kept list (chat-thread.js) is put inside it. */
|
|
306
388
|
function Thread({ name, th }) {
|
|
307
|
-
const scroller = useRef(null),
|
|
389
|
+
const scroller = useRef(null), host = useRef(null);
|
|
308
390
|
const [jump, setJump] = useState({ show: false, unseen: 0 });
|
|
309
391
|
const [empty, setEmpty] = useState(false);
|
|
310
392
|
useLayoutEffect(() => {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
393
|
+
const doc = host.current.ownerDocument;
|
|
394
|
+
let e = keptThreads.get(name);
|
|
395
|
+
// Kept only while it can be shown as it is: the same language, and this page's own document.
|
|
396
|
+
if (e && (e.lang !== lang() || e.list.ownerDocument !== doc)) { letGo(e, true); keptThreads.delete(name); e = undefined; }
|
|
397
|
+
const kept = !!e;
|
|
398
|
+
if (e) keptThreads.delete(name);
|
|
399
|
+
else e = threadEntry(name, doc);
|
|
400
|
+
keptThreads.set(name, e); // the most recent last
|
|
401
|
+
for (const [k, old] of keptThreads) {
|
|
402
|
+
if (keptThreads.size <= THREAD_KEEP) break;
|
|
403
|
+
if (k !== name) { letGo(old, true); keptThreads.delete(k); }
|
|
404
|
+
}
|
|
405
|
+
const thread = e.thread, sc = scroller.current, ls = e.list;
|
|
406
|
+
e.sink = { setJump, setEmpty };
|
|
407
|
+
host.current.append(ls);
|
|
408
|
+
thread.attach(sc);
|
|
326
409
|
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] || [])) : () => {};
|
|
410
|
+
// 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
411
|
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
|
|
412
|
+
if (kept) thread.refreshCards();
|
|
413
|
+
const unfollow = follow(name, thread);
|
|
342
414
|
const onScroll = () => { store.state.scrollMemo[name] = thread.onScroll(); };
|
|
343
|
-
const sc = scroller.current, ls = list.current;
|
|
344
415
|
sc.addEventListener("scroll", onScroll, { passive: true });
|
|
345
416
|
ls.addEventListener("click", thread.onClick);
|
|
346
417
|
return () => {
|
|
347
|
-
|
|
348
|
-
off(); offPv(); offPanel(); offTrials(); offEmojis(); offMentions();
|
|
418
|
+
unfollow();
|
|
349
419
|
sc.removeEventListener("scroll", onScroll);
|
|
350
420
|
ls.removeEventListener("click", thread.onClick);
|
|
351
|
-
|
|
421
|
+
e.sink = null;
|
|
352
422
|
th.current = null;
|
|
423
|
+
if (keptThreads.get(name) === e) { thread.park(); ls.remove(); } // kept for a later visit: the chat left
|
|
424
|
+
else letGo(e, false); // let go meanwhile (a new language)
|
|
353
425
|
};
|
|
354
426
|
}, [name]);
|
|
355
427
|
return html`<div class="scroller" ref=${scroller}>
|
|
356
|
-
<div class="thread" ref=${
|
|
428
|
+
<div class="thread-host" ref=${host}></div>
|
|
357
429
|
${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}
|
|
430
|
+
<div class="jump-dock">${jump.show ? html`<button data-trust="thread-control" type="button" class="jump-latest" onClick=${() => th.current && th.current.jumpLatest()}>
|
|
431
|
+
<${Icon} name="down" size=${16} />${jump.unseen > 0 ? t("chat.newBelow", jump.unseen) : t("chat.jumpLatest")}</button>` : null}</div>
|
|
360
432
|
</div>`;
|
|
361
433
|
}
|
|
362
434
|
|
|
@@ -373,7 +445,8 @@ function Dock({ name, inst, lease, exec, awaiting, workingLabel }) {
|
|
|
373
445
|
<button type="button" class="btn btn-primary btn-sm" onClick=${() => store.putBack(name)}>${t("chat.putBack")}</button>
|
|
374
446
|
<button type="button" class="btn btn-sm" onClick=${() => store.discardFailed(name)}>${t("chat.discard")}</button></div>` : null}
|
|
375
447
|
<${Composer} name=${name} lease=${lease} busy=${R().isBusy(exec)} />
|
|
376
|
-
<p class="hint" title=${t("chat.composerMore")}>${t("chat.composerHint")}</p>
|
|
448
|
+
<p class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t("chat.composerHint")}</p>
|
|
449
|
+
${guestPage() ? html`<p class="guest-note">${t("chat.guestNote")}</p>` : html`<${AuthorizeChip} />`}
|
|
377
450
|
</div></div>`;
|
|
378
451
|
}
|
|
379
452
|
|
|
@@ -393,7 +466,8 @@ function CommandCard({ name, lease }) {
|
|
|
393
466
|
: c.compact === "lost" ? t("chat.compactLost") : c.compact === "not_compacted" ? t("chat.compactNotCompacted") : t("chat.compactUnknown")}</p>` : null}
|
|
394
467
|
${c.choices && c.choices.prompt ? html`<pre class="cmd-text">${c.choices.prompt}</pre>` : null}
|
|
395
468
|
${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}
|
|
469
|
+
class=${`btn btn-sm${o.id === c.choices.current ? " current" : ""}`} aria-current=${o.id === c.choices.current ? "true" : undefined}
|
|
470
|
+
disabled=${!!o.disabled} title=${o.disabled || undefined} onClick=${() => { if (!o.disabled) run(o.id); }}>${o.label}</button>`)}</div>` : null}
|
|
397
471
|
</div>`;
|
|
398
472
|
}
|
|
399
473
|
|
|
@@ -449,8 +523,10 @@ function Composer({ name, lease, busy }) {
|
|
|
449
523
|
const value = s.drafts[name] || "";
|
|
450
524
|
const files = s.pendingFiles[name] || [];
|
|
451
525
|
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
|
-
|
|
526
|
+
// A file waiting: what goes is a message with its file, never a command (no palette, no command line). A guest never
|
|
527
|
+
// runs one (#1920: the server refuses its slash text), so it has no palette either.
|
|
528
|
+
const guest = guestPage();
|
|
529
|
+
const palette = guest || files.length || closedFor === value ? null : paletteFor(value);
|
|
454
530
|
const pick = palette && palette.length ? palette[Math.min(sel, palette.length - 1)] : null;
|
|
455
531
|
// Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300).
|
|
456
532
|
useLayoutEffect(() => { const el = input.current; if (!el) return; el.style.height = "auto"; el.style.height = `${Math.min(el.scrollHeight, 240)}px`; }, [value]);
|
|
@@ -471,6 +547,8 @@ function Composer({ name, lease, busy }) {
|
|
|
471
547
|
else complete(pick);
|
|
472
548
|
return;
|
|
473
549
|
}
|
|
550
|
+
// #1920: a guest's "/…" would be refused as a command — said here, the draft kept to edit.
|
|
551
|
+
if (guest && /^\s*\//.test(value)) { toast(t("chat.guestNoCommands"), false); return; }
|
|
474
552
|
const cmd = files.length ? null : parseCommandLine(value);
|
|
475
553
|
if (cmd && needsArgument(cmd.command) && !cmd.args) { setValue(`/${cmd.command} `); return; }
|
|
476
554
|
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
|
@@ -18,15 +18,26 @@ 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,17 @@ 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
|
-
const Body = { tasks: Tasks, schedules: Schedules, teams: Teams, org: Org, cache: Cache, config: Config }[tab] || Tasks;
|
|
145
|
+
const Body = { tasks: Tasks, schedules: Schedules, teams: Teams, org: Org, cache: Cache, usage: UsageTab, config: Config }[tab] || Tasks;
|
|
134
146
|
// #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
|
-
|
|
147
|
+
const { hostMemory, hostMemoryTop } = useStore(appStore);
|
|
148
|
+
// #2015: and which processes hold the most, and whose — text only (never chat, #1712).
|
|
149
|
+
const top = hostMemory && hostMemoryTop?.length ? t("fleet.hostMemory_top", hostMemoryTop.map(memoryHogText).join(" · ")) : "";
|
|
150
|
+
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
151
|
return html`<div data-effect-panel="fleet" class="panel p-fleet">
|
|
138
152
|
<${PanelHeader} title=${t("fleet.title")} sub=${sub}><${DocsLink} /><${HeaderTools} navKey=${navKey} /></${PanelHeader}>
|
|
139
153
|
<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
154
|
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>
|
|
155
|
+
<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
156
|
</div>`;
|
|
143
157
|
}
|
|
144
158
|
|
|
@@ -255,11 +269,11 @@ function Teams({ lease }) {
|
|
|
255
269
|
${editing ? html`<${CreateTeam} team=${editing} onClose=${() => setEditing(null)} onDone=${() => setVersion(v => v + 1)} />` : null}`;
|
|
256
270
|
}
|
|
257
271
|
|
|
258
|
-
/** #1389: the org chart — its structure read once per opening, its live state from the app store (fleet-
|
|
272
|
+
/** #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
273
|
function Org({ lease }) {
|
|
260
274
|
const state = useLoad(lease, "/ui/org", 0);
|
|
261
275
|
const app = useStore(appStore);
|
|
262
|
-
return html`<${Loaded} state=${state}>${(org) => html`<${
|
|
276
|
+
return html`<${Loaded} state=${state}>${(org) => html`<${OrgCanvas} org=${org} app=${app} lease=${lease} />`}</${Loaded}>`;
|
|
263
277
|
}
|
|
264
278
|
|
|
265
279
|
/** #1468: the prompt-cache analysis — one read per opening and per window chosen; Refresh reads again (fleet-cache.js). */
|
|
@@ -322,8 +336,7 @@ function Config({ lease }) {
|
|
|
322
336
|
<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
337
|
<p class="note">${t("fleet.allowedUsersNote")}</p></div></section>
|
|
324
338
|
<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>
|
|
339
|
+
<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
340
|
<section class="card"><h3>${t("fleet.roots")}</h3>
|
|
328
341
|
${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
342
|
onInput=${(e) => { const roots = [...form.roots]; roots[i] = e.target.value; setForm({ ...form, roots }); }} />
|
|
@@ -510,6 +523,8 @@ function CreateTeam({ onClose, onDone, team = null }) {
|
|
|
510
523
|
export function CreateInstanceDialog({ onClose }) {
|
|
511
524
|
const lease = useLease("create-instance");
|
|
512
525
|
const [backends, setBackends] = useState(null);
|
|
526
|
+
// The backend "fleet default" means (#2098 review P3-3): its models are the ones the model list offers then.
|
|
527
|
+
const [defaultBackend, setDefaultBackend] = useState("claude-code");
|
|
513
528
|
const [f, setF] = useState({ dir: "", topic: "", desc: "", backend: "", model: "", branch: "", tags: "", prompt: "" });
|
|
514
529
|
useEffect(() => {
|
|
515
530
|
(async () => {
|
|
@@ -518,6 +533,7 @@ export function CreateInstanceDialog({ onClose }) {
|
|
|
518
533
|
if (!lease.current()) return;
|
|
519
534
|
const list = bd.backends || [];
|
|
520
535
|
const first = list.find(b => b.installed && !b.deprecated) || list.find(b => b.installed);
|
|
536
|
+
if (typeof bd.defaultBackend === "string" && bd.defaultBackend) setDefaultBackend(bd.defaultBackend);
|
|
521
537
|
setBackends(list);
|
|
522
538
|
setF(x => ({ ...x, backend: first ? first.name : "" }));
|
|
523
539
|
} catch {
|
|
@@ -547,14 +563,23 @@ export function CreateInstanceDialog({ onClose }) {
|
|
|
547
563
|
${field(`${t("fleet.topic")}${f.dir.trim() ? "" : " *"}`, html`<input value=${f.topic} onInput=${set("topic")} placeholder=${t("fleet.topicHint")} />`)}
|
|
548
564
|
${field(t("fleet.description"), html`<input value=${f.desc} onInput=${set("desc")} placeholder=${t("fleet.descriptionHint")} />`)}
|
|
549
565
|
${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
|
|
566
|
+
${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} />`)}
|
|
567
|
+
${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
568
|
${field(t("fleet.branch"), html`<input value=${f.branch} onInput=${set("branch")} placeholder=${t("fleet.branchHint")} />`)}
|
|
553
569
|
${field(t("fleet.tags"), html`<input value=${f.tags} onInput=${set("tags")} placeholder=${t("fleet.tagsHint")} />`)}
|
|
554
570
|
${field(t("fleet.systemPrompt"), html`<textarea rows="3" value=${f.prompt} onInput=${set("prompt")} placeholder=${t("fleet.systemPromptHint")}></textarea>`)}
|
|
555
571
|
</${FormDialog}>`;
|
|
556
572
|
}
|
|
557
573
|
|
|
574
|
+
// #2072: a backend this host cannot run is shown, disabled, with why; one not checked yet stays selectable.
|
|
575
|
+
const backendState = (b) => b.state ?? (b.installed ? "usable" : b.unknown ? "unknown" : "not_installed");
|
|
576
|
+
function backendBlocked(b) { return ["not_installed", "not_signed_in", "too_old"].includes(backendState(b)); }
|
|
577
|
+
function backendNote(b) {
|
|
578
|
+
const s = backendState(b);
|
|
579
|
+
return s === "not_installed" ? ` ${t("fleet.notInstalled")}` : s === "not_signed_in" ? ` ${t("fleet.notSignedIn")}`
|
|
580
|
+
: s === "too_old" ? ` ${t("fleet.tooOld", b.minVersion ?? "?")}` : s === "unknown" ? ` ${t("fleet.unchecked")}` : "";
|
|
581
|
+
}
|
|
582
|
+
|
|
558
583
|
// ── Cron in words (#1724: in the page's language) ──
|
|
559
584
|
/**
|
|
560
585
|
* A cron expression as a person says it — "Weekdays 18:00", 「每週一到五 18:00」, "Every 15 minutes" — or the expression
|