@songsid/agend 2.2.0-alpha.9 → 2.2.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/README.md +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/agent-cli-instructions.md +16 -9
- package/dist/agent-cli.js +15 -7
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +21 -9
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.d.ts +47 -0
- package/dist/agent-whats-new.js +147 -0
- package/dist/agent-whats-new.js.map +1 -0
- package/dist/auth-api.d.ts +2 -2
- package/dist/auth-api.js +4 -2
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/antigravity.d.ts +3 -0
- package/dist/backend/antigravity.js +9 -0
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-carry.d.ts +22 -0
- package/dist/backend/claude-carry.js +170 -0
- package/dist/backend/claude-carry.js.map +1 -0
- package/dist/backend/claude-code.d.ts +65 -2
- package/dist/backend/claude-code.js +226 -33
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-metadata.d.ts +33 -0
- package/dist/backend/codex-metadata.js +122 -18
- package/dist/backend/codex-metadata.js.map +1 -1
- package/dist/backend/codex.d.ts +3 -0
- package/dist/backend/codex.js +17 -1
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/credential-profile.d.ts +40 -3
- package/dist/backend/credential-profile.js +113 -5
- package/dist/backend/credential-profile.js.map +1 -1
- package/dist/backend/effort-metadata.d.ts +1 -1
- package/dist/backend/effort-metadata.js +3 -2
- package/dist/backend/effort-metadata.js.map +1 -1
- package/dist/backend/grok.d.ts +4 -0
- package/dist/backend/grok.js +14 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro-engine-ledger.d.ts +5 -0
- package/dist/backend/kiro-engine-ledger.js +20 -0
- package/dist/backend/kiro-engine-ledger.js.map +1 -1
- package/dist/backend/kiro-identity.d.ts +6 -0
- package/dist/backend/kiro-identity.js +19 -5
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +39 -3
- package/dist/backend/kiro.js +143 -19
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/launch-effort.d.ts +33 -0
- package/dist/backend/launch-effort.js +123 -0
- package/dist/backend/launch-effort.js.map +1 -0
- package/dist/backend/muse.d.ts +84 -0
- package/dist/backend/muse.js +449 -53
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +33 -4
- package/dist/backend/types.js +2 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-command.d.ts +91 -0
- package/dist/backend-command.js +122 -0
- package/dist/backend-command.js.map +1 -0
- package/dist/background-work-ended.d.ts +18 -0
- package/dist/background-work-ended.js +30 -0
- package/dist/background-work-ended.js.map +1 -0
- package/dist/cache-service.d.ts +2 -0
- package/dist/cache-service.js +2 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +26 -1
- package/dist/channel/adapters/discord.js +98 -3
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +26 -2
- package/dist/channel/adapters/telegram.js +123 -19
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-server.js +9 -1
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +11 -0
- package/dist/channel/mcp-tools.js +24 -13
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-edit-errors.d.ts +5 -0
- package/dist/channel/message-edit-errors.js +24 -0
- package/dist/channel/message-edit-errors.js.map +1 -0
- package/dist/channel/telegram-markdown.d.ts +6 -0
- package/dist/channel/telegram-markdown.js +104 -0
- package/dist/channel/telegram-markdown.js.map +1 -0
- package/dist/channel/tool-router.js +9 -3
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +41 -3
- package/dist/cli.js +133 -28
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +2 -2
- package/dist/command-table.js +6 -3
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +59 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/cross-instance-envelope.d.ts +15 -2
- package/dist/cross-instance-envelope.js +29 -5
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +156 -1
- package/dist/daemon.js +764 -89
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +62 -7
- package/dist/delivery-outbox.js +309 -22
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +18 -1
- package/dist/delivery-reconciliation.js +67 -2
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/fleet-context.d.ts +10 -1
- package/dist/fleet-manager.d.ts +352 -10
- package/dist/fleet-manager.js +1925 -175
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/backend-providers/SKILL.md +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +92 -21
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +27 -3
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +8 -8
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +5 -5
- package/dist/general-knowledge/skills/fleet-health/SKILL.md +4 -4
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +13 -13
- package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +3 -2
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +6 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +9 -9
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +8 -2
- package/dist/general-knowledge/skills/scheduling/SKILL.md +5 -3
- package/dist/general-knowledge/skills/session-management/SKILL.md +6 -5
- package/dist/general-knowledge/skills/tui-effort/SKILL.md +2 -2
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +8 -3
- package/dist/general-knowledge/steering/core-rules.md +8 -7
- package/dist/host-memory.d.ts +10 -0
- package/dist/host-memory.js +33 -1
- package/dist/host-memory.js.map +1 -1
- package/dist/human-delivery-receipts.d.ts +83 -0
- package/dist/human-delivery-receipts.js +194 -0
- package/dist/human-delivery-receipts.js.map +1 -0
- package/dist/instance-avatar.d.ts +36 -0
- package/dist/instance-avatar.js +57 -0
- package/dist/instance-avatar.js.map +1 -0
- package/dist/instance-config-impact.js +6 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +29 -6
- package/dist/instance-lifecycle.js +79 -28
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +3 -1
- package/dist/instructions.js +11 -8
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-log-gate.d.ts +42 -0
- package/dist/kiro-log-gate.js +39 -0
- package/dist/kiro-log-gate.js.map +1 -0
- package/dist/locale.js +204 -44
- package/dist/locale.js.map +1 -1
- package/dist/log-service.d.ts +99 -0
- package/dist/log-service.js +243 -0
- package/dist/log-service.js.map +1 -0
- package/dist/log-steps.d.ts +169 -0
- package/dist/log-steps.js +858 -0
- package/dist/log-steps.js.map +1 -0
- package/dist/login-controller.d.ts +20 -3
- package/dist/login-controller.js +71 -19
- package/dist/login-controller.js.map +1 -1
- package/dist/login-flows.d.ts +5 -2
- package/dist/login-flows.js +5 -4
- package/dist/login-flows.js.map +1 -1
- package/dist/memory-pressure.d.ts +13 -1
- package/dist/memory-pressure.js +50 -10
- package/dist/memory-pressure.js.map +1 -1
- package/dist/needs-you-hub.js +2 -0
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.d.ts +6 -0
- package/dist/needs-you-live.js +23 -3
- package/dist/needs-you-live.js.map +1 -1
- package/dist/next-steps.js +1 -1
- package/dist/next-steps.js.map +1 -1
- package/dist/outbound-handlers.d.ts +100 -0
- package/dist/outbound-handlers.js +263 -96
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +26 -15
- package/dist/outbound-schemas.js +24 -7
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-link-progress.d.ts +1 -1
- package/dist/public-link-progress.js +8 -2
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.d.ts +101 -0
- package/dist/public-preview.js +236 -0
- package/dist/public-preview.js.map +1 -0
- package/dist/public-web-gateway.js +11 -4
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +24 -1
- package/dist/public-web-link.js +63 -9
- package/dist/public-web-link.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +3 -0
- package/dist/reply-buttons-controller.js +3 -2
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/restart-guard.d.ts +6 -3
- package/dist/restart-guard.js +6 -3
- package/dist/restart-guard.js.map +1 -1
- package/dist/restart-progress.d.ts +33 -0
- package/dist/restart-progress.js +30 -0
- package/dist/restart-progress.js.map +1 -1
- package/dist/service-activation.d.ts +15 -7
- package/dist/service-activation.js +46 -29
- package/dist/service-activation.js.map +1 -1
- package/dist/service-installer.d.ts +31 -0
- package/dist/service-installer.js +41 -2
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.js +85 -25
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +72 -1
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +28 -4
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +7 -0
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +1 -1
- package/dist/spawn-gate.js +5 -1
- package/dist/spawn-gate.js.map +1 -1
- package/dist/spawn-hold-reply.d.ts +12 -0
- package/dist/spawn-hold-reply.js +39 -0
- package/dist/spawn-hold-reply.js.map +1 -0
- package/dist/status-emojis.d.ts +10 -3
- package/dist/status-emojis.js +11 -2
- package/dist/status-emojis.js.map +1 -1
- package/dist/systemd-peer-bus.d.ts +95 -0
- package/dist/systemd-peer-bus.js +430 -0
- package/dist/systemd-peer-bus.js.map +1 -0
- package/dist/systemd-runtime.d.ts +27 -1
- package/dist/systemd-runtime.js +69 -7
- package/dist/systemd-runtime.js.map +1 -1
- package/dist/tips.js +4 -4
- package/dist/tips.js.map +1 -1
- package/dist/tool-progress.js +3 -2
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-commands.d.ts +42 -2
- package/dist/topic-commands.js +92 -7
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-delta-reader.d.ts +3 -3
- package/dist/transcript-delta-reader.js +8 -8
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/transcript-jsonl.d.ts +80 -3
- package/dist/transcript-jsonl.js +147 -10
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.d.ts +19 -0
- package/dist/transcript-monitor.js +87 -15
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +78 -0
- package/dist/transcript-sources.js +375 -64
- package/dist/transcript-sources.js.map +1 -1
- package/dist/transcript-turns.d.ts +15 -2
- package/dist/transcript-turns.js +15 -2
- package/dist/transcript-turns.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +7 -15
- package/dist/tunnel/cloudflared.js +36 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/purpose-lane.d.ts +5 -1
- package/dist/tunnel/purpose-lane.js +14 -7
- package/dist/tunnel/purpose-lane.js.map +1 -1
- package/dist/tunnel/types.d.ts +5 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/turn-resume.d.ts +2 -0
- package/dist/turn-resume.js +1 -1
- package/dist/turn-resume.js.map +1 -1
- package/dist/types.d.ts +25 -0
- package/dist/ui/chat-commands.js +1 -0
- package/dist/ui/chat-emojis.js +33 -0
- package/dist/ui/chat-mentions.js +63 -0
- package/dist/ui/chat-render.js +42 -2
- package/dist/ui/chat-store.js +2 -2
- package/dist/ui/chat-strings.js +38 -20
- package/dist/ui/chat-thread.js +69 -12
- package/dist/ui/chat-time.js +80 -0
- package/dist/ui/panel-chat.js +39 -10
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +199 -57
- package/dist/ui/panel-log.js +428 -0
- package/dist/ui/panel-needs.js +6 -2
- package/dist/ui/panel-settings.js +137 -32
- package/dist/ui/preview-panel.js +55 -15
- package/dist/ui/preview-status.js +28 -0
- package/dist/ui/preview.js +44 -3
- package/dist/ui/settings-dialogs.js +7 -2
- package/dist/ui/settings-model.js +2 -1
- package/dist/ui/settings-strings.js +46 -4
- package/dist/ui/settings-token.js +1 -1
- package/dist/ui/shared/app-i18n.js +43 -17
- package/dist/ui/shared/app-prefs.js +250 -0
- package/dist/ui/shared/app-route.js +7 -2
- package/dist/ui/shared/app-shell.js +165 -8
- package/dist/ui/shared/app-store.js +5 -1
- package/dist/ui/shared/app-stream.js +23 -5
- package/dist/ui/shared/app.css +159 -23
- package/dist/ui/shared/app.js +11 -1
- package/dist/ui/shared/fonts/README.md +1 -1
- package/dist/ui/shared/header-tools.js +55 -5
- package/dist/ui/shared/instance-nav.js +241 -45
- package/dist/ui/shared/instance-switch.js +4 -4
- package/dist/ui/shared/panel-view.js +6 -0
- package/dist/ui/shared/signin.js +7 -7
- package/dist/ui/shared/theme.js +39 -7
- package/dist/ui/shared/tokens.css +10 -1
- package/dist/ui/shared/ui-icons.js +1 -0
- package/dist/ui/shared/usage-dialog.js +23 -5
- package/dist/ui/shared/view-strings.js +32 -4
- package/dist/ui/signin.html +1 -1
- package/dist/unrecognised-prompt.d.ts +23 -0
- package/dist/unrecognised-prompt.js +74 -0
- package/dist/unrecognised-prompt.js.map +1 -0
- package/dist/update-install.d.ts +28 -2
- package/dist/update-install.js +22 -4
- package/dist/update-install.js.map +1 -1
- package/dist/update-marker.d.ts +51 -0
- package/dist/update-marker.js +61 -0
- package/dist/update-marker.js.map +1 -1
- package/dist/update-preflight.d.ts +100 -0
- package/dist/update-preflight.js +264 -0
- package/dist/update-preflight.js.map +1 -0
- package/dist/updater-caller.d.ts +31 -0
- package/dist/updater-caller.js +109 -0
- package/dist/updater-caller.js.map +1 -0
- package/dist/usage/i18n-keys.d.ts +1 -1
- package/dist/usage/i18n-keys.js +1 -1
- package/dist/usage/i18n-keys.js.map +1 -1
- package/dist/usage/providers.d.ts +34 -5
- package/dist/usage/providers.js +160 -27
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/statusline-usage.d.ts +6 -2
- package/dist/usage/statusline-usage.js +10 -4
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/view-api.d.ts +1 -1
- package/dist/view-api.js +38 -8
- package/dist/view-api.js.map +1 -1
- package/dist/wake-coordinator.d.ts +11 -0
- package/dist/wake-coordinator.js +38 -4
- package/dist/wake-coordinator.js.map +1 -1
- package/dist/web-api.d.ts +49 -2
- package/dist/web-api.js +421 -31
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +2 -2
- package/dist/web-auth.js +9 -4
- package/dist/web-auth.js.map +1 -1
- package/dist/web-commands.d.ts +27 -4
- package/dist/web-commands.js +19 -4
- package/dist/web-commands.js.map +1 -1
- package/dist/web-host-guard.d.ts +3 -11
- package/dist/web-host-guard.js +3 -12
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-images.d.ts +47 -0
- package/dist/web-images.js +169 -0
- package/dist/web-images.js.map +1 -0
- package/dist/web-mentions.d.ts +44 -0
- package/dist/web-mentions.js +75 -0
- package/dist/web-mentions.js.map +1 -0
- package/dist/web-prefs.d.ts +63 -0
- package/dist/web-prefs.js +265 -0
- package/dist/web-prefs.js.map +1 -0
- package/dist/web-preview.d.ts +14 -1
- package/dist/web-preview.js +35 -13
- package/dist/web-preview.js.map +1 -1
- package/dist/web-reply-files.d.ts +45 -0
- package/dist/web-reply-files.js +223 -0
- package/dist/web-reply-files.js.map +1 -0
- package/dist/web-schedules.d.ts +12 -0
- package/dist/web-schedules.js +34 -0
- package/dist/web-schedules.js.map +1 -0
- package/dist/web-server-drain.d.ts +10 -0
- package/dist/web-server-drain.js +50 -0
- package/dist/web-server-drain.js.map +1 -0
- package/dist/web-session.d.ts +36 -1
- package/dist/web-session.js +53 -6
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +23 -0
- package/dist/web-settings-patch.js +77 -0
- package/dist/web-settings-patch.js.map +1 -0
- package/dist/web-shell-routes.d.ts +5 -1
- package/dist/web-shell-routes.js +10 -1
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-upload.d.ts +17 -4
- package/dist/web-upload.js +64 -13
- package/dist/web-upload.js.map +1 -1
- package/dist/workflow-templates/default.md +1 -1
- package/package.json +1 -1
- package/templates/launchd.plist.ejs +3 -1
- package/templates/systemd.service.ejs +2 -1
|
@@ -43,7 +43,7 @@ import { startTour } from "./chat-tour.js";
|
|
|
43
43
|
import "./chat-strings.js";
|
|
44
44
|
|
|
45
45
|
const tn = (k, ...v) => t(`settings.${k}`, ...v);
|
|
46
|
-
const ICONS = { device: "user", agents: "bot", bots: "plug", classic: "room", general: "sliders", advanced: "code" };
|
|
46
|
+
const ICONS = { device: "user", agents: "bot", bots: "plug", classic: "room", general: "sliders", web: "globe", advanced: "code" };
|
|
47
47
|
const RELEASES = "https://github.com/songsid/AgEnD/releases";
|
|
48
48
|
|
|
49
49
|
/** GET under the panel's lease → { ok, status, body }. */
|
|
@@ -229,7 +229,8 @@ export function SettingsPanel({ route, navKey }) {
|
|
|
229
229
|
: section === "agents" ? html`<${Agents} ctx=${ctx} search=${search} openDialog=${(d) => setDialog({ ...d, key: navKey })} />`
|
|
230
230
|
: section === "bots" ? html`<${Bots} ctx=${ctx} search=${search} openDialog=${(d) => setDialog({ ...d, key: navKey })} />`
|
|
231
231
|
: section === "classic" ? html`<${Classic} ctx=${ctx} search=${search} openDialog=${(d) => setDialog({ ...d, key: navKey })} />`
|
|
232
|
-
: section === "general" ? html`<${General} key=${data.loadedAt} ctx=${ctx}
|
|
232
|
+
: section === "general" ? html`<${General} key=${data.loadedAt} ctx=${ctx} />`
|
|
233
|
+
: section === "web" ? html`<${WebSection} key=${data.loadedAt} ctx=${ctx} staged=${staged} />`
|
|
233
234
|
: html`<${Developer} ctx=${ctx} />`;
|
|
234
235
|
// A dialog belongs to the navigation that opened it (its draft names one object, never another).
|
|
235
236
|
const open = dialog && dialog.key === navKey ? dialog : null;
|
|
@@ -257,9 +258,10 @@ export function SettingsPanel({ route, navKey }) {
|
|
|
257
258
|
}
|
|
258
259
|
|
|
259
260
|
/**
|
|
260
|
-
* #1604:
|
|
261
|
-
*
|
|
262
|
-
* the chat tour (replayed on the
|
|
261
|
+
* #1604, #1665: Preferences. Synced across the person's devices (kept on the host, app-prefs.js): theme (theme.js),
|
|
262
|
+
* language (app-i18n.js) and HTML previews (preview.js, through the app's confirm). On the public link they are
|
|
263
|
+
* read-only: a change there stays in that browser, and it says so. This device only: the chat tour (replayed on the
|
|
264
|
+
* chat it points at), and the layout choices named there (their controls are where they always were).
|
|
263
265
|
*/
|
|
264
266
|
export function DeviceSection() {
|
|
265
267
|
const P = () => globalThis.AgendPreview;
|
|
@@ -290,11 +292,15 @@ export function DeviceSection() {
|
|
|
290
292
|
};
|
|
291
293
|
setTimeout(go, 0);
|
|
292
294
|
};
|
|
293
|
-
|
|
294
|
-
|
|
295
|
+
const publicLink = typeof document !== "undefined" && document.body && document.body.dataset.publicLink === "1";
|
|
296
|
+
return html`<section class="card s-device s-synced"><h3>${tn("syncedTitle")}</h3>
|
|
297
|
+
<p class="sub">${publicLink ? tn("syncedPublicLink") : tn("syncedLead")}</p>
|
|
295
298
|
<${DevicePrefs} labels=${true} />
|
|
296
299
|
${P() ? html`<label class="check"><input type="checkbox" checked=${on} onChange=${(e) => { const want = e.target.checked; setPreviewOptIn(want).then(setOn); }} />
|
|
297
300
|
<span>${t("chat.previews")}</span></label>` : null}
|
|
301
|
+
</section>
|
|
302
|
+
<section class="card s-device s-local"><h3>${tn("deviceTitle")}</h3>
|
|
303
|
+
<p class="sub">${tn("deviceLead")}</p>
|
|
298
304
|
<div class="s-device-row"><button type="button" id="tourBtn" class="btn" title=${t("chat.tourBtnTitle")} onClick=${replay}><${Icon} name="info" size=${16} />${t("chat.tour")}</button></div>
|
|
299
305
|
</section>`;
|
|
300
306
|
}
|
|
@@ -366,11 +372,13 @@ function makeCtx(data, setData, stage, unstage, reload, reloadLive, alive) {
|
|
|
366
372
|
async pauseWake(name, action) {
|
|
367
373
|
const res = await api(`/api/settings/instances/${encodeURIComponent(name)}/${action}`, { method: "POST" }).catch(() => ({ ok: false, body: {} }));
|
|
368
374
|
if (!res.ok) toast((res.body && res.body.error) || tn("actionFailed", action), false);
|
|
375
|
+
else if (res.body && res.body.held) toast(res.body.message);
|
|
369
376
|
await reloadLive();
|
|
370
377
|
},
|
|
371
378
|
async startStop(name, running) {
|
|
372
379
|
const res = await api(running ? `/stop/${encodeURIComponent(name)}` : `/api/instance/${encodeURIComponent(name)}/start`, { method: "POST" }).catch(() => ({ ok: false, body: {} }));
|
|
373
380
|
if (!res.ok) toast(`${tn("actionFailed", running ? tn("stop") : tn("start"))}: ${(res.body && res.body.error) || res.status || ""}`, false);
|
|
381
|
+
else if (res.body && res.body.held) toast(res.body.message);
|
|
374
382
|
await new Promise((r) => setTimeout(r, 800));
|
|
375
383
|
await reloadLive();
|
|
376
384
|
},
|
|
@@ -570,17 +578,13 @@ function Classic({ ctx, search, openDialog }) {
|
|
|
570
578
|
* ClassicBot defaults, web) — each only with what differs from what this form showed, so changing one hot default
|
|
571
579
|
* never also stages untouched settings as a fleet restart.
|
|
572
580
|
*/
|
|
573
|
-
function General({ ctx
|
|
581
|
+
function General({ ctx }) {
|
|
574
582
|
const { schema } = ctx;
|
|
575
583
|
const d = ctx.fleet.defaults || {}, c = (ctx.classic && ctx.classic.defaults) || {};
|
|
576
584
|
const hang = d.hang_detector || {}, startup = d.startup || {};
|
|
577
585
|
const primary = channelsOf(ctx.fleet)[0];
|
|
578
|
-
const
|
|
579
|
-
const originalPublic = useMemo(() => ({ allow_public: (web.public_link && web.public_link.allow_public) ?? true, ttl_minutes: (web.public_link && web.public_link.ttl_minutes) ?? 120, protocol: (web.public_link && web.public_link.protocol) ?? "http2" }), []);
|
|
580
|
-
const originalEcho = web.echo_to_channel ?? true;
|
|
581
|
-
const original = useMemo(() => ({ c: structuredClone(c), web: structuredClone(ctx.fleet.web) }), []);
|
|
586
|
+
const original = useMemo(() => ({ c: structuredClone(c) }), []);
|
|
582
587
|
const [f, setF] = useState(() => ({
|
|
583
|
-
echo: originalEcho, pub: originalPublic.allow_public, ttl: String(originalPublic.ttl_minutes), protocol: originalPublic.protocol,
|
|
584
588
|
backend: d.backend || "claude-code", model: d.model || "", locale: d.locale || "", auto: String(d.auto_pause_after ?? 0),
|
|
585
589
|
agent_mode: d.agent_mode || "mcp", tool_set: d.tool_set || "full", tool_progress: d.tool_progress ?? "off",
|
|
586
590
|
visibility: visibilityDefault(d), reply: d.reply_completion_guard ?? true, log_level: d.log_level || "info",
|
|
@@ -598,7 +602,6 @@ function General({ ctx, staged }) {
|
|
|
598
602
|
auto: nonNegative(f.auto), hang: positive(f.hang),
|
|
599
603
|
concurrency: !Number.isInteger(num(f.concurrency)) || num(f.concurrency) < 1 || num(f.concurrency) > 20 ? "concurrencyRange" : "",
|
|
600
604
|
stagger: !Number.isFinite(num(f.stagger)) || f.stagger.trim() === "" || num(f.stagger) < 0 || num(f.stagger) > 30000 ? "staggerRange" : "",
|
|
601
|
-
ttl: !Number.isInteger(num(f.ttl)) || num(f.ttl) < 1 || num(f.ttl) > 480 ? "publicTtl" : "",
|
|
602
605
|
};
|
|
603
606
|
const warnings = { concurrency: num(f.concurrency) > 4 ? "concurrencyWarning" : "", stagger: num(f.stagger) < 100 ? "staggerWarning" : "" };
|
|
604
607
|
const ready = !Object.values(errors).some(Boolean);
|
|
@@ -638,29 +641,12 @@ function General({ ctx, staged }) {
|
|
|
638
641
|
ctx.stage("defaults:classic", { label: tn("updateClassicDefaults"), impact: hotOnly ? "now" : "fleet",
|
|
639
642
|
request: { method: "PUT", url: "/api/settings/classic/defaults", body: classicPatch } });
|
|
640
643
|
}
|
|
641
|
-
const publicPatch = changedFields({ allow_public: f.pub, ttl_minutes: num(f.ttl), protocol: f.protocol }, originalPublic);
|
|
642
|
-
const webPatch = { ...(f.echo !== originalEcho ? { echo_to_channel: f.echo } : {}), ...(Object.keys(publicPatch).length ? { public_link: publicPatch } : {}) };
|
|
643
|
-
if (Object.keys(webPatch).length) {
|
|
644
|
-
ctx.stage("web:echo", { label: tn("publicLink"),
|
|
645
|
-
impact: batchImpact(schema, [...(webPatch.echo_to_channel !== undefined ? ["web.echo_to_channel"] : []), ...Object.keys(publicPatch).map((k) => `web.public_link.${k}`)], (k) => impactOf(schema, k)),
|
|
646
|
-
request: { method: "PUT", url: "/api/settings/fleet/web", body: webPatch } });
|
|
647
|
-
} else if (staged.has("web:echo")) ctx.unstage("web:echo");
|
|
648
644
|
setMsg(tn("readyApply"));
|
|
649
645
|
};
|
|
650
646
|
const L = (text, field, id) => html`<${Label} text=${text} schema=${schema} field=${field} htmlFor=${id} />`;
|
|
651
647
|
const fleetReply = replyGuardSupported(f.backend, f.agent_mode, d.kiro_ui) ? tn("replyGuardSupported") : tn("replyGuardUnsupported", f.backend, f.agent_mode);
|
|
652
648
|
const classicReply = replyGuardSupported(f.classicBackend, f.agent_mode, d.kiro_ui) ? tn("replyGuardSupported") : tn("replyGuardUnsupported", f.classicBackend, f.agent_mode);
|
|
653
649
|
return html`<div class="form s-general">
|
|
654
|
-
<section class="card"><h3>${tn("webChat")}</h3>
|
|
655
|
-
<label class="check"><input id="webEchoToChannel" type="checkbox" checked=${f.echo} onChange=${(e) => set("echo")(e.target.checked)} /><span>${tn("webEcho")}</span><${Impact} schema=${schema} field="web.echo_to_channel" /></label>
|
|
656
|
-
<p class="note">${tn("webEchoHint")}</p>
|
|
657
|
-
<label class="check"><input id="publicWebLink" type="checkbox" checked=${f.pub} onChange=${(e) => set("pub")(e.target.checked)} /><span>${tn("publicLink")}</span><${Impact} schema=${schema} field="web.public_link.allow_public" /></label>
|
|
658
|
-
<p class="note">${tn("publicLinkHint")}</p>
|
|
659
|
-
<div class="grid2">
|
|
660
|
-
<div class="field"><label for="publicWebTtl">${tn("publicTtl")}</label><input id="publicWebTtl" type="number" min="1" max="480" class=${errors.ttl ? "invalid" : ""} value=${f.ttl} onInput=${(e) => set("ttl")(e.target.value)} /><${Feedback} error=${errors.ttl} /></div>
|
|
661
|
-
<div class="field"><label for="publicWebProtocol">${tn("publicProtocol")}</label><${Select} id="publicWebProtocol" value=${f.protocol} onChange=${set("protocol")} options=${["http2", "quic", "auto"]} /></div>
|
|
662
|
-
</div>
|
|
663
|
-
</section>
|
|
664
650
|
<section class="card"><h3>${tn("defaultsTitle")}</h3>
|
|
665
651
|
<div class="grid2">
|
|
666
652
|
<div class="field">${L(tn("language"), "defaults.locale", "g-locale")}<${Select} id="g-locale" value=${f.locale} onChange=${set("locale")}
|
|
@@ -714,6 +700,125 @@ function General({ ctx, staged }) {
|
|
|
714
700
|
</div>`;
|
|
715
701
|
}
|
|
716
702
|
|
|
703
|
+
/**
|
|
704
|
+
* #1620: Settings → Web — the dashboard's own settings, in one write (PUT /api/settings/fleet/web). Editable: the local
|
|
705
|
+
* session limits, the browser terminal (on/off, its minutes), web echo and the public link's menu; everything else
|
|
706
|
+
* under web: / web_terminal: is listed read-only (it decides what the dashboard listens on and exposes: fleet.yaml, then
|
|
707
|
+
* agend reload). The server judges each change: loosening one waits for a fleet admin in General, tightening applies at
|
|
708
|
+
* once. Over
|
|
709
|
+
* the temporary public link none of it can be changed — the form says so and stays disabled (the server refuses too).
|
|
710
|
+
*/
|
|
711
|
+
export const WEB_READ_ONLY = [
|
|
712
|
+
"web.allowed_hosts", "web.view_access", "web.notify_login", "web.preview", "web.preview_port", "web.preview_origin", "web.preview_on_public_link",
|
|
713
|
+
"web.usage_panel", "web.provider_secrets", "web_terminal.bind", "web_terminal.tunnel",
|
|
714
|
+
];
|
|
715
|
+
function WebSection({ ctx, staged }) {
|
|
716
|
+
const { schema } = ctx;
|
|
717
|
+
const web = ctx.fleet.web || {}, local = (web.session && web.session.local) || {}, term = ctx.fleet.web_terminal || {};
|
|
718
|
+
const overPublicLink = globalThis.document?.body?.dataset?.publicLink === "1";
|
|
719
|
+
const original = useMemo(() => ({
|
|
720
|
+
echo: web.echo_to_channel ?? true,
|
|
721
|
+
log: web.log_tab === true, // #1218: off by default
|
|
722
|
+
pub: { allow_public: (web.public_link && web.public_link.allow_public) ?? true, ttl_minutes: (web.public_link && web.public_link.ttl_minutes) ?? 120, protocol: (web.public_link && web.public_link.protocol) ?? "http2" },
|
|
723
|
+
session: { absolute_hours: local.absolute_hours ?? null, idle_hours: local.idle_hours ?? null },
|
|
724
|
+
terminal: { enabled: term.enabled ?? null, ttl_minutes: term.ttl_minutes ?? null },
|
|
725
|
+
}), []);
|
|
726
|
+
const text = (v) => (v === null || v === undefined ? "" : String(v));
|
|
727
|
+
const [f, setF] = useState(() => ({
|
|
728
|
+
echo: original.echo, log: original.log, pub: original.pub.allow_public, ttl: String(original.pub.ttl_minutes), protocol: original.pub.protocol,
|
|
729
|
+
abs: text(original.session.absolute_hours), idle: text(original.session.idle_hours),
|
|
730
|
+
termOn: original.terminal.enabled !== false, termTtl: text(original.terminal.ttl_minutes),
|
|
731
|
+
}));
|
|
732
|
+
const [msg, setMsg] = useState(null);
|
|
733
|
+
const set = (k) => (v) => setF((x) => ({ ...x, [k]: v }));
|
|
734
|
+
// Empty = the default (null in the write: the key is removed from fleet.yaml).
|
|
735
|
+
const value = (v) => (v.trim() === "" ? null : Number(v));
|
|
736
|
+
const hours = (v) => { const n = value(v); return n !== null && (!Number.isFinite(n) || n < 1 || n > 720) ? "sessionRange" : ""; };
|
|
737
|
+
// The defaults (#1670: 48 h / 48 h, DEFAULT_SESSION_POLICY); an idle time left blank follows a shorter session, as
|
|
738
|
+
// the store and the validator apply it — so only an idle time that is typed can exceed the session length.
|
|
739
|
+
const abs = value(f.abs) ?? 48, idle = value(f.idle) ?? Math.min(48, abs);
|
|
740
|
+
const errors = {
|
|
741
|
+
ttl: !Number.isInteger(Number(f.ttl)) || Number(f.ttl) < 1 || Number(f.ttl) > 480 ? "publicTtl" : "",
|
|
742
|
+
abs: hours(f.abs),
|
|
743
|
+
idle: hours(f.idle) || (!hours(f.abs) && idle > abs ? "sessionIdleOver" : ""),
|
|
744
|
+
termTtl: value(f.termTtl) !== null && (!Number.isInteger(value(f.termTtl)) || value(f.termTtl) < 1 || value(f.termTtl) > 20) ? "webTerminalTtlRange" : "",
|
|
745
|
+
};
|
|
746
|
+
const ready = !overPublicLink && !Object.values(errors).some(Boolean);
|
|
747
|
+
const review = () => {
|
|
748
|
+
if (!ready) return;
|
|
749
|
+
const publicPatch = changedFields({ allow_public: f.pub, ttl_minutes: Number(f.ttl), protocol: f.protocol }, original.pub);
|
|
750
|
+
const sessionPatch = changedFields({ absolute_hours: value(f.abs), idle_hours: value(f.idle) }, original.session);
|
|
751
|
+
// The checkbox shows the default (on) for an unset key: only a real toggle is written; on again = back to the default.
|
|
752
|
+
const terminalPatch = changedFields({ enabled: f.termOn === (original.terminal.enabled !== false) ? original.terminal.enabled : f.termOn ? null : false,
|
|
753
|
+
ttl_minutes: value(f.termTtl) }, original.terminal);
|
|
754
|
+
const body = {
|
|
755
|
+
...(f.echo !== original.echo ? { echo_to_channel: f.echo } : {}),
|
|
756
|
+
...(f.log !== original.log ? { log_tab: f.log } : {}),
|
|
757
|
+
...(Object.keys(publicPatch).length ? { public_link: publicPatch } : {}),
|
|
758
|
+
...(Object.keys(sessionPatch).length ? { session: { local: sessionPatch } } : {}),
|
|
759
|
+
...(Object.keys(terminalPatch).length ? { web_terminal: terminalPatch } : {}),
|
|
760
|
+
};
|
|
761
|
+
const fields = [
|
|
762
|
+
...(body.echo_to_channel !== undefined ? ["web.echo_to_channel"] : []),
|
|
763
|
+
...(body.log_tab !== undefined ? ["web.log_tab"] : []),
|
|
764
|
+
...Object.keys(publicPatch).map((k) => `web.public_link.${k}`),
|
|
765
|
+
...Object.keys(sessionPatch).map((k) => `web.session.local.${k}`),
|
|
766
|
+
...Object.keys(terminalPatch).map((k) => `web_terminal.${k}`),
|
|
767
|
+
];
|
|
768
|
+
if (fields.length) {
|
|
769
|
+
ctx.stage("web", { label: tn("sec_web"), impact: batchImpact(schema, fields, (k) => impactOf(schema, k)),
|
|
770
|
+
request: { method: "PUT", url: "/api/settings/fleet/web", body } });
|
|
771
|
+
} else if (staged.has("web")) ctx.unstage("web");
|
|
772
|
+
setMsg(tn("readyApply"));
|
|
773
|
+
};
|
|
774
|
+
const shown = (path) => {
|
|
775
|
+
const [top, key] = path.split(".");
|
|
776
|
+
const v = (top === "web" ? web : term)[key];
|
|
777
|
+
return v === undefined ? tn("notSet") : typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
778
|
+
};
|
|
779
|
+
return html`<div class="form s-web">
|
|
780
|
+
<p class="sub">${tn("webLead")}</p>
|
|
781
|
+
${overPublicLink ? html`<p class="note" role="status" id="webPublicRefused">${tn("webPublicRefused")}</p>` : null}
|
|
782
|
+
<fieldset class="s-web-fields" disabled=${overPublicLink}>
|
|
783
|
+
<section class="card"><h3>${tn("webSessionTitle")}</h3>
|
|
784
|
+
<p class="note">${tn("webSessionHint")}</p>
|
|
785
|
+
<div class="grid2">
|
|
786
|
+
<div class="field"><label for="webSessionAbsolute">${tn("sessionAbsolute")}</label><input id="webSessionAbsolute" type="number" min="1" max="720" step="any" placeholder=${tn("defaultHours", 48)}
|
|
787
|
+
class=${errors.abs ? "invalid" : ""} value=${f.abs} onInput=${(e) => set("abs")(e.target.value)} /><${Feedback} error=${errors.abs} /></div>
|
|
788
|
+
<div class="field"><label for="webSessionIdle">${tn("sessionIdle")}</label><input id="webSessionIdle" type="number" min="1" max="720" step="any" placeholder=${tn("defaultHours", 48)}
|
|
789
|
+
class=${errors.idle ? "invalid" : ""} value=${f.idle} onInput=${(e) => set("idle")(e.target.value)} /><${Feedback} error=${errors.idle} /></div>
|
|
790
|
+
</div>
|
|
791
|
+
</section>
|
|
792
|
+
<section class="card"><h3>${tn("webTerminalTitle")}</h3>
|
|
793
|
+
<p class="note">${tn("webTerminalHint")}</p>
|
|
794
|
+
<label class="check"><input id="webTerminalEnabled" type="checkbox" checked=${f.termOn} onChange=${(e) => set("termOn")(e.target.checked)} /><span>${tn("webTerminalEnabled")}</span><${Impact} schema=${schema} field="web_terminal.enabled" /></label>
|
|
795
|
+
<div class="grid2">
|
|
796
|
+
<div class="field"><label for="webTerminalTtl">${tn("webTerminalTtl")}</label><input id="webTerminalTtl" type="number" min="1" max="20" step="1" placeholder=${tn("defaultHours", 10)}
|
|
797
|
+
class=${errors.termTtl ? "invalid" : ""} value=${f.termTtl} onInput=${(e) => set("termTtl")(e.target.value)} /><${Feedback} error=${errors.termTtl} /></div>
|
|
798
|
+
</div>
|
|
799
|
+
</section>
|
|
800
|
+
<section class="card"><h3>${tn("webChat")}</h3>
|
|
801
|
+
<label class="check"><input id="webEchoToChannel" type="checkbox" checked=${f.echo} onChange=${(e) => set("echo")(e.target.checked)} /><span>${tn("webEcho")}</span><${Impact} schema=${schema} field="web.echo_to_channel" /></label>
|
|
802
|
+
<p class="note">${tn("webEchoHint")}</p>
|
|
803
|
+
<label class="check"><input id="webLogTab" type="checkbox" checked=${f.log} onChange=${(e) => set("log")(e.target.checked)} /><span>${tn("webLogTab")}</span><${Impact} schema=${schema} field="web.log_tab" /></label>
|
|
804
|
+
<p class="note">${tn("webLogTabHint")}</p>
|
|
805
|
+
<label class="check"><input id="publicWebLink" type="checkbox" checked=${f.pub} onChange=${(e) => set("pub")(e.target.checked)} /><span>${tn("publicLink")}</span><${Impact} schema=${schema} field="web.public_link.allow_public" /></label>
|
|
806
|
+
<p class="note">${tn("publicLinkHint")}</p>
|
|
807
|
+
<div class="grid2">
|
|
808
|
+
<div class="field"><label for="publicWebTtl">${tn("publicTtl")}</label><input id="publicWebTtl" type="number" min="1" max="480" class=${errors.ttl ? "invalid" : ""} value=${f.ttl} onInput=${(e) => set("ttl")(e.target.value)} /><${Feedback} error=${errors.ttl} /></div>
|
|
809
|
+
<div class="field"><label for="publicWebProtocol">${tn("publicProtocol")}</label><${Select} id="publicWebProtocol" value=${f.protocol} onChange=${set("protocol")} options=${["http2", "quic", "auto"]} /></div>
|
|
810
|
+
</div>
|
|
811
|
+
</section>
|
|
812
|
+
</fieldset>
|
|
813
|
+
<section class="card s-web-readonly"><h3>${tn("webReadOnlyTitle")}</h3>
|
|
814
|
+
<p class="note">${tn("webReadOnlyLead")}</p>
|
|
815
|
+
<dl class="s-web-ro">${WEB_READ_ONLY.map((k) => html`<div key=${k}><dt><code>${k}</code></dt><dd>${shown(k)}</dd></div>`)}</dl>
|
|
816
|
+
</section>
|
|
817
|
+
<div class="save-row"><button type="button" class="btn btn-primary" disabled=${!ready} onClick=${review}>${tn("reviewChanges")}</button>
|
|
818
|
+
${msg ? html`<span class="note" role="status">${msg}</span>` : null}</div>
|
|
819
|
+
</div>`;
|
|
820
|
+
}
|
|
821
|
+
|
|
717
822
|
/** Developer: the fleet model as YAML or JSON, to read, copy, download — or edit and apply as one operation. */
|
|
718
823
|
function Developer({ ctx }) {
|
|
719
824
|
const [fmt, setFmt] = useState("yaml");
|
|
@@ -749,7 +854,7 @@ function Developer({ ctx }) {
|
|
|
749
854
|
<button type="button" class="btn btn-sm" title=${tn("copy")} onClick=${copy}><${Icon} name="copy" size=${14} /><span class="lbl">${tn("copy")}</span></button>
|
|
750
855
|
<button type="button" class="btn btn-sm" title=${tn("devDownload")} onClick=${download}><${Icon} name="download" size=${14} /><span class="lbl">${tn("devDownload")}</span></button>
|
|
751
856
|
</div>
|
|
752
|
-
${editing ? html`<textarea class="s-yaml" aria-label=${tn("devEditor")} spellcheck
|
|
857
|
+
${editing ? html`<textarea class="s-yaml" aria-label=${tn("devEditor")} spellcheck=${false} wrap="off" value=${draft} onInput=${(e) => setDraft(e.target.value)}></textarea>
|
|
753
858
|
<div class="save-row"><button type="button" class="btn btn-primary" onClick=${save}>${tn("devApply")}</button><span class="note">${tn("devApplyHint")}</span></div>`
|
|
754
859
|
: html`<pre class="s-yaml">${text}</pre>`}
|
|
755
860
|
${msg ? html`<p class=${`feedback${msg.error ? " error" : ""}`} role="status">${msg.error || msg.text}</p>` : null}
|
package/dist/ui/preview-panel.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// #1481: an agent's HTML preview (#1306) in a panel beside the conversation — a split with a draggable divider on a
|
|
2
2
|
// wide screen, a full-screen sheet on a narrow one. Nothing about the preview itself changes here: the frame is the one
|
|
3
3
|
// preview.js makes (mountPreview: the preview origin, sandbox allow-scripts only), started by AgendPreview.start like a
|
|
4
|
-
// card's, only told to fill the panel. It runs on a click, as a card's does:
|
|
5
|
-
// -
|
|
4
|
+
// card's, only told to fill the panel. It runs on a person's click, as a card's does — and "Open in panel" is that click:
|
|
5
|
+
// - on an idle card it opens the panel and starts the preview there, as if Preview were clicked in the panel (#1704);
|
|
6
|
+
// a device that has not allowed previews gets the "Allow previews" offer instead, and allowing it starts the run;
|
|
7
|
+
// where previews cannot run (web.preview: false, a public link), the panel opens with nothing running;
|
|
6
8
|
// - on a card whose preview is running, the same button moves that run to the panel (stopped in the card, started in
|
|
7
9
|
// the panel: a moved frame reloads anyway);
|
|
8
|
-
// -
|
|
10
|
+
// - nothing runs by itself: not when the page loads, not when the panel is shown again, and a newer version of the
|
|
11
|
+
// block in a later reply is offered, never swapped in, and shown with Preview to click.
|
|
9
12
|
// Closing the panel or leaving the chat stops its frame; one preview per page still holds (start() stops any other).
|
|
10
13
|
import { html, useEffect, useRef, useState } from "/assets/app-html.js";
|
|
11
14
|
import { t } from "/assets/app-i18n.js";
|
|
@@ -13,24 +16,36 @@ import { Icon } from "/assets/ui-icons.js";
|
|
|
13
16
|
import "./chat-render.js";
|
|
14
17
|
import "./preview.js";
|
|
15
18
|
import { loadHtmlAttachment } from "./html-attachment.js";
|
|
19
|
+
import { chatTime, onDayChange } from "./chat-time.js";
|
|
16
20
|
|
|
17
21
|
const R = () => globalThis.AgendChatRender;
|
|
18
22
|
const P = () => globalThis.AgendPreview;
|
|
19
23
|
|
|
20
24
|
// ── The panel's state, one per page (the chat shows one instance at a time) ──
|
|
21
25
|
|
|
22
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* open: { key, instance, msgKey, n | att, code, sender, ts } | null (`att`: an attached .html file, #1306 Q4);
|
|
28
|
+
* run: start it on mount — the card's click, as a token no later open ever reuses (0: nothing to run).
|
|
29
|
+
*/
|
|
23
30
|
let state = { open: null, run: 0 };
|
|
31
|
+
let runs = 0, version = 0, claims = 0;
|
|
24
32
|
const subs = new Set();
|
|
25
|
-
function set(next) { state = next; for (const fn of [...subs]) { try { fn(state); } catch { /* one listener's error stops no other */ } } }
|
|
33
|
+
function set(next) { state = next; version++; for (const fn of [...subs]) { try { fn(state); } catch { /* one listener's error stops no other */ } } }
|
|
26
34
|
export const panelStore = { get: () => state, subscribe(fn) { subs.add(fn); return () => { subs.delete(fn); }; } };
|
|
35
|
+
/** Moves on every open and close, and on every claim: what a card's click compares when its HTML arrives late. */
|
|
36
|
+
export const panelVersion = () => version;
|
|
37
|
+
/** A click that will show something in the panel (an attachment's HTML still to read): from now on it is the newest —
|
|
38
|
+
* any earlier click still reading is dropped when its answer comes. Returns the version the click owns. */
|
|
39
|
+
export function claimPanel() { claims++; return ++version; }
|
|
40
|
+
/** How many clicks have claimed: an inline Preview still reading asks only whether a newer click came (not a close). */
|
|
41
|
+
export const panelClaims = () => claims;
|
|
27
42
|
|
|
28
43
|
/** The frame's key in preview.js: its own, so the card that opened it keeps its own state. */
|
|
29
44
|
export const frameKey = (key) => `panel:${key}`;
|
|
30
|
-
/** Show `spec` in the panel; `run`: start it there (
|
|
45
|
+
/** Show `spec` in the panel; `run`: start it there (the card's "Open in panel" click). The block shown before stops. */
|
|
31
46
|
export function openPanel(spec, { run = false } = {}) {
|
|
32
47
|
if (state.open && P()) P().stop(frameKey(state.open.key), "stopped", "");
|
|
33
|
-
set({ open: { ...spec }, run: run ?
|
|
48
|
+
set({ open: { ...spec }, run: run ? ++runs : 0 });
|
|
34
49
|
}
|
|
35
50
|
/** Close the panel: its frame stops first. */
|
|
36
51
|
export function closePanel() {
|
|
@@ -98,14 +113,15 @@ export function clampWidth(w, total) {
|
|
|
98
113
|
return Math.round(Math.min(max, Math.max(MIN_PANEL, w)));
|
|
99
114
|
}
|
|
100
115
|
|
|
101
|
-
|
|
116
|
+
/** #1770: as the thread shows it — the fleet's zone, with the day when it is not today. */
|
|
117
|
+
const timeOf = (ts) => (ts ? chatTime(ts, { yesterday: (hm) => t("chat.yesterdayAt", hm) }) : "");
|
|
102
118
|
|
|
103
119
|
/**
|
|
104
120
|
* The panel, inside the chat's split (`split`: that element — the divider sets its width there, through CSSOM).
|
|
105
121
|
* props: name (the instance), msgs() → its messages, subscribe(fn) → unsubscribe (the chat store's), keyOf(x),
|
|
106
122
|
* reveal(msgKey) (the thread shows that message), download(code).
|
|
107
123
|
*/
|
|
108
|
-
/** allow(): this device's opt-in through the chat's one path (setPreviewOptIn: the confirm, then setOptIn) → the choice. */
|
|
124
|
+
/** allow(commit): this device's opt-in through the chat's one path (setPreviewOptIn: the confirm, then commit(setOptIn)) → the choice. */
|
|
109
125
|
export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, download, allow }) {
|
|
110
126
|
const [st, setSt] = useState(state);
|
|
111
127
|
const [view, setView] = useState("preview");
|
|
@@ -113,15 +129,23 @@ export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, down
|
|
|
113
129
|
const [newer, setNewer] = useState(null);
|
|
114
130
|
const [newerNote, setNewerNote] = useState("");
|
|
115
131
|
const reading = useRef(null); // the newer version whose file is being read (one at a time)
|
|
132
|
+
const pending = useRef(0); // #1704: the card's click, waiting for this device's Allow (the st.run it came with)
|
|
133
|
+
const ownAllow = useRef(0); // while this panel's own Allow commits its choice: the click it answers (not cancelled by it)
|
|
116
134
|
const holder = useRef(null), divider = useRef(null), panel = useRef(null);
|
|
117
135
|
const open = st.open && st.open.instance === name ? st.open : null;
|
|
118
136
|
|
|
119
137
|
useEffect(() => panelStore.subscribe(setSt), []);
|
|
120
138
|
// This device's choice (here or in another tab) decides whether Preview is offered: re-read it when it changes.
|
|
139
|
+
// A click waiting for Allow is cancelled by any other change of that choice (Never, a card's menu, another tab):
|
|
140
|
+
// after it, running needs a new click — only this panel's own Allow runs the click it was offered for.
|
|
121
141
|
const [, choice] = useState(0);
|
|
122
|
-
useEffect(() => (P() ? P().onChange(() => choice(n => n + 1)) : undefined), []);
|
|
142
|
+
useEffect(() => (P() ? P().onChange(() => { if (pending.current && ownAllow.current !== pending.current) pending.current = 0; choice(n => n + 1); }) : undefined), []);
|
|
123
143
|
// Leaving the chat (another instance, another panel) closes the panel: its frame stops with the thread's.
|
|
124
144
|
useEffect(() => () => closePanel(), []);
|
|
145
|
+
// #1770: the source and newer-version labels say the day as the thread does — drawn again when the fleet's day
|
|
146
|
+
// changes, while a block is open. A render: the frame and the run are left as they are.
|
|
147
|
+
const [, dayTick] = useState(0);
|
|
148
|
+
useEffect(() => (open ? onDayChange(() => dayTick(n => n + 1)) : undefined), [!!open]);
|
|
125
149
|
|
|
126
150
|
const ui = { state: (s, reason) => setRun({ state: s === "starting" || s === "running" ? s : "idle", reason: reason || "" }) };
|
|
127
151
|
const start = () => {
|
|
@@ -130,11 +154,17 @@ export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, down
|
|
|
130
154
|
P().start(frameKey(open.key), holder.current, open.code, ui, { fill: true });
|
|
131
155
|
};
|
|
132
156
|
const stop = () => { if (open) P().stop(frameKey(open.key), "stopped", ""); };
|
|
133
|
-
// A new block in the panel: what ran before is gone (openPanel stopped it);
|
|
157
|
+
// A new block in the panel: what ran before is gone (openPanel stopped it); the card's click starts it here — or,
|
|
158
|
+
// where it cannot run, waits as `pending`: only the Allow below can use it (offered to a device that has not opted
|
|
159
|
+
// in), only while the panel shows what that click opened (st.run is a token no later open reuses), and any other
|
|
160
|
+
// change of this device's choice cancels it.
|
|
134
161
|
useEffect(() => {
|
|
135
162
|
setRun({ state: "idle", reason: "" });
|
|
136
163
|
setView("preview");
|
|
137
|
-
if (open && st.run)
|
|
164
|
+
if (open && st.run) {
|
|
165
|
+
if (P().availability().ok) start();
|
|
166
|
+
else pending.current = st.run;
|
|
167
|
+
}
|
|
138
168
|
// Focus goes to the panel's close control that is on screen (Back on a phone, × beside the thread).
|
|
139
169
|
if (panel.current) { const f = [...panel.current.querySelectorAll(".pv-panel-close")].find(b => b.offsetParent !== null); if (f) f.focus(); }
|
|
140
170
|
}, [open && open.key, st.run]);
|
|
@@ -204,11 +234,21 @@ export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, down
|
|
|
204
234
|
if (!open) return null;
|
|
205
235
|
const going = run.state === "starting" || run.state === "running";
|
|
206
236
|
const a = P().availability();
|
|
207
|
-
// #1554: a device that has not allowed previews is offered the same "Allow previews" here (the same confirm)
|
|
208
|
-
// is then enabled — still a click. Why a run stopped
|
|
237
|
+
// #1554: a device that has not allowed previews is offered the same "Allow previews" here (the same confirm). Allowing
|
|
238
|
+
// runs the block the card's click opened (#1704); otherwise Preview is then enabled — still a click. Why a run stopped
|
|
239
|
+
// (e.g. switched off in another tab) is still said first.
|
|
209
240
|
const askOptIn = !going && !a.ok && a.why === "optin" && typeof allow === "function";
|
|
210
241
|
const note = run.reason || (going ? (run.state === "starting" ? t("chat.pvStarting") : "") : a.ok ? t("chat.pvPanelIdle") : askOptIn ? t("chat.pvWhyOptinPanel") : a.reason);
|
|
211
|
-
|
|
242
|
+
// Allowed here: the click that opened the panel runs now — if it is still waiting (no other change of the choice
|
|
243
|
+
// cancelled it, even while the confirm was open: only the change this Allow commits is its own) and the panel still
|
|
244
|
+
// shows what it opened (the confirm is a wait: the panel may have moved on meanwhile).
|
|
245
|
+
const allowHere = async () => {
|
|
246
|
+
const want = pending.current;
|
|
247
|
+
const yes = await allow((apply) => { ownAllow.current = want; try { apply(); } finally { ownAllow.current = 0; } });
|
|
248
|
+
if (!yes) return;
|
|
249
|
+
setRun({ state: "idle", reason: "" });
|
|
250
|
+
if (want && pending.current === want && panelStore.get().run === want) start();
|
|
251
|
+
};
|
|
212
252
|
const source = t("chat.pvSource", open.sender || name, timeOf(open.ts));
|
|
213
253
|
return html`<div class="pv-divider" ref=${divider} role="separator" aria-orientation="vertical" tabindex="0" aria-label=${t("chat.pvResize")}
|
|
214
254
|
aria-valuemin=${String(MIN_PANEL)} onKeyDown=${onKey} onPointerDown=${onDown}></div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// #1723: the public link's preview tunnel starts beside the link and is never waited for, so a page may load while it
|
|
2
|
+
// is still starting. This asks the page's own server (/ui/preview-status) until it is up or failed, and hands each answer
|
|
3
|
+
// to preview.js — which takes only what it may use, and itself reaches no network (#1306).
|
|
4
|
+
|
|
5
|
+
/** Every 2 s, for at most 5 minutes. */
|
|
6
|
+
export const POLL_MS = 2000;
|
|
7
|
+
export const POLL_MAX = 150;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Ask while `P.waitingForPublicPreview()`; stop once `P.applyPublicPreviewStatus(answer)` says there is nothing more to
|
|
11
|
+
* wait for, or after POLL_MAX asks. Returns a stop function.
|
|
12
|
+
*/
|
|
13
|
+
export function pollPublicPreview(P, deps = {}) {
|
|
14
|
+
const get = deps.fetch ?? ((...a) => fetch(...a));
|
|
15
|
+
const later = deps.setTimeout ?? ((fn, ms) => setTimeout(fn, ms));
|
|
16
|
+
if (!P || typeof P.waitingForPublicPreview !== "function" || !P.waitingForPublicPreview()) return () => {};
|
|
17
|
+
let attempts = 0, stopped = false;
|
|
18
|
+
const again = () => { if (!stopped && attempts < POLL_MAX) later(tick, POLL_MS); };
|
|
19
|
+
const tick = () => {
|
|
20
|
+
if (stopped || !P.waitingForPublicPreview()) return;
|
|
21
|
+
attempts++;
|
|
22
|
+
get("/ui/preview-status", { credentials: "same-origin", cache: "no-store" })
|
|
23
|
+
.then((r) => (r.ok ? r.json() : null))
|
|
24
|
+
.then((j) => { if (!stopped && !P.applyPublicPreviewStatus(j)) again(); }, again);
|
|
25
|
+
};
|
|
26
|
+
later(tick, POLL_MS);
|
|
27
|
+
return () => { stopped = true; };
|
|
28
|
+
}
|
package/dist/ui/preview.js
CHANGED
|
@@ -28,7 +28,14 @@
|
|
|
28
28
|
var BANNER_ACK_KEY = "agend_html_preview_banner_ack";
|
|
29
29
|
var BANNER = "Previews run the agent's HTML in an isolated frame. It cannot use your login, but it may be able to send data out. Only preview content you trust. A preview can slow or freeze this tab.";
|
|
30
30
|
|
|
31
|
-
var cfg = { dashboardOrigin: "", previewOrigin: "", boot: "", reason: "", reasonCode: "" };
|
|
31
|
+
var cfg = { dashboardOrigin: "", previewOrigin: "", boot: "", reason: "", reasonCode: "", detail: "" };
|
|
32
|
+
// Why it failed, in English when the app's dictionary has no word for it (chat-strings.js pvFail_*).
|
|
33
|
+
var FAIL_EN = { exited: "its address closed unexpectedly", "spawn-failed": "cloudflared could not start for it", "no-address": "no address was given for it",
|
|
34
|
+
unreachable: "its address could not be reached", "lease-held": "an earlier preview tunnel is still being cleaned up", "gateway-failed": "its local listener could not start",
|
|
35
|
+
// #1767: what cloudflared's exit before an address really was.
|
|
36
|
+
"rate-limited": "Cloudflare is limiting new temporary links from this machine — try again in a while",
|
|
37
|
+
"provisioning-failed": "Cloudflare refused to create its address right now", "process-exited": "cloudflared stopped before it gave an address",
|
|
38
|
+
"bad-address": "Cloudflare gave an address that could not be used" };
|
|
32
39
|
// #1554: the page's words. `say(key, english, …values)` asks the app's dictionary (init's opts.text) and keeps the
|
|
33
40
|
// English here when it has none — so this file still works, and reads the same, without the app.
|
|
34
41
|
var text = null;
|
|
@@ -66,6 +73,7 @@
|
|
|
66
73
|
boot: String((data && data.previewBoot) || ""),
|
|
67
74
|
reason: String((data && data.previewReason) || ""),
|
|
68
75
|
reasonCode: String((data && data.previewReasonCode) || ""),
|
|
76
|
+
detail: String((data && data.previewDetail) || ""),
|
|
69
77
|
};
|
|
70
78
|
if (opts && typeof opts.text === "function") text = opts.text;
|
|
71
79
|
if (!listening && typeof root.addEventListener === "function") {
|
|
@@ -76,6 +84,29 @@
|
|
|
76
84
|
listening = true;
|
|
77
85
|
}
|
|
78
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* #1723: the public link's own preview tunnel was still starting when this page loaded; preview-status.js asks the
|
|
89
|
+
* page's server and hands each answer here (this file reaches no network). Ready → previews run at the origin it
|
|
90
|
+
* names — only an https quick-tunnel origin other than this page's is ever taken: the page's frame-src allows no
|
|
91
|
+
* other; failed or off → the page says why. Returns true once there is nothing more to wait for.
|
|
92
|
+
*/
|
|
93
|
+
function waitingForPublicPreview() { return cfg.reasonCode === "publicLinkStarting"; }
|
|
94
|
+
function applyPublicPreviewStatus(j) {
|
|
95
|
+
if (cfg.reasonCode !== "publicLinkStarting") return true;
|
|
96
|
+
var at = root.location && root.location.origin;
|
|
97
|
+
if (j && typeof j.previewOrigin === "string" && /^https:\/\/[a-z0-9-]+\.trycloudflare\.com$/.test(j.previewOrigin) && j.previewOrigin !== at
|
|
98
|
+
&& typeof j.boot === "string" && /^[0-9a-f]{32}$/.test(j.boot)) {
|
|
99
|
+
cfg.previewOrigin = j.previewOrigin; cfg.boot = j.boot; cfg.reasonCode = ""; cfg.reason = "";
|
|
100
|
+
notify();
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
if (j && (j.code === "publicLinkPreviewFailed" || j.code === "publicLink" || j.code === "fleetOff")) {
|
|
104
|
+
cfg.reasonCode = j.code; cfg.reason = ""; cfg.detail = typeof j.detail === "string" ? j.detail : "";
|
|
105
|
+
notify();
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
79
110
|
function onStorage(event) {
|
|
80
111
|
// #1568: the banner acknowledged in another tab of this sign-in: hide it here too (nothing starts or stops).
|
|
81
112
|
if (event && event.key === BANNER_ACK_KEY) { notify(); return; }
|
|
@@ -159,7 +190,11 @@
|
|
|
159
190
|
function availability() {
|
|
160
191
|
if (!cfg.previewOrigin || !cfg.boot) {
|
|
161
192
|
// The server's reason, in the page's language when the server named it (a code), else as the server wrote it.
|
|
162
|
-
|
|
193
|
+
// #1723: a failed public-link preview names why ({0} is the reason, in the page's words).
|
|
194
|
+
var arg = cfg.reasonCode === "publicLinkPreviewFailed" ? say("pvFail_" + (cfg.detail || "unknown"), Object.prototype.hasOwnProperty.call(FAIL_EN, cfg.detail) ? FAIL_EN[cfg.detail] : "an unexpected error") : cfg.dashboardOrigin;
|
|
195
|
+
var english = cfg.reasonCode === "publicLinkPreviewFailed" ? "Previews are unavailable on this public link: {0}. The link itself keeps working."
|
|
196
|
+
: cfg.reason || "Previews are not available here.";
|
|
197
|
+
var server = cfg.reasonCode ? say("pvServer_" + cfg.reasonCode, english, arg) : "";
|
|
163
198
|
return { ok: false, why: "server", reason: server || cfg.reason || say("pvWhyServer", "Previews are not available here.") };
|
|
164
199
|
}
|
|
165
200
|
var at = root.location && root.location.origin;
|
|
@@ -197,11 +232,15 @@
|
|
|
197
232
|
* opts.fill (#1481, the side panel): the frame fills its holder — the same frame from mountPreview, only sized by the
|
|
198
233
|
* panel instead of by the frame's height messages, which it then ignores.
|
|
199
234
|
*/
|
|
235
|
+
/** How many previews this page has started: a click whose HTML arrives late asks whether another started meanwhile. */
|
|
236
|
+
var starts = 0;
|
|
237
|
+
function startCount() { return starts; }
|
|
200
238
|
function start(key, holder, html, ui, opts) {
|
|
201
239
|
var a = availability();
|
|
202
240
|
if (!a.ok) { ui.state("unavailable", a.reason); return false; }
|
|
203
241
|
if (typeof html !== "string" || utf8Bytes(html) > LIMITS.maxBytes) { ui.state("unavailable", say("pvHtmlOver", "This HTML is over 1 MiB; it is not previewed.")); return false; }
|
|
204
242
|
stopAll("another");
|
|
243
|
+
starts++;
|
|
205
244
|
var doc = holder.ownerDocument;
|
|
206
245
|
var iframe = mountPreview(doc);
|
|
207
246
|
var card = {
|
|
@@ -330,7 +369,7 @@
|
|
|
330
369
|
|
|
331
370
|
return {
|
|
332
371
|
init: init, availability: availability, optedIn: optedIn, setOptIn: setOptIn, never: never, setNever: setNever, onChange: onChange, listeners: listeners, onStorage: onStorage,
|
|
333
|
-
start: start, stop: stop, stopAll: stopAll, stopIn: stopIn, running: running, liveCount: liveCount, liveFrame: liveFrame,
|
|
372
|
+
start: start, startCount: startCount, stop: stop, stopAll: stopAll, stopIn: stopIn, running: running, liveCount: liveCount, liveFrame: liveFrame,
|
|
334
373
|
mountPreview: mountPreview, onMessage: onMessage, BANNER: BANNER, LIMITS: LIMITS,
|
|
335
374
|
/** The banner every running preview carries, in the page's language (#1554); BANNER is its English. */
|
|
336
375
|
banner: function () { return say("pvBanner", BANNER); },
|
|
@@ -338,5 +377,7 @@
|
|
|
338
377
|
bannerAckLabel: function () { return say("pvBannerAck", "I understand"); },
|
|
339
378
|
bannerAckTitle: function () { return say("pvBannerAckTitle", "Hide this notice until you sign in again"); },
|
|
340
379
|
bannerAcked: bannerAcked, ackBanner: ackBanner, setBannerScope: setBannerScope,
|
|
380
|
+
/** #1723: the public link's preview, while it starts (preview-status.js asks; this only takes the answer). */
|
|
381
|
+
waitingForPublicPreview: waitingForPublicPreview, applyPublicPreviewStatus: applyPublicPreviewStatus,
|
|
341
382
|
};
|
|
342
383
|
});
|
|
@@ -108,8 +108,13 @@ export function orderedStatusMap(map, keys) {
|
|
|
108
108
|
for (const key of keys) { const v = map && map[key]; if (typeof v === "string" && v.trim()) out[key] = v.trim(); }
|
|
109
109
|
return Object.keys(out).length ? out : null;
|
|
110
110
|
}
|
|
111
|
-
//
|
|
112
|
-
|
|
111
|
+
// #1630: from the dashboard's own route (/ui/img/emoji/<id>). An emoji the fleet's servers do not have is not served
|
|
112
|
+
// there: then its :name: as text, never a broken image.
|
|
113
|
+
const EmojiImg = ({ url, alt }) => {
|
|
114
|
+
const [broken, setBroken] = useState("");
|
|
115
|
+
return broken === url ? html`<span class="se-alt">${alt}</span>`
|
|
116
|
+
: html`<img src=${url} alt=${alt} referrerpolicy="no-referrer" loading="lazy" onError=${() => setBroken(url)} />`;
|
|
117
|
+
};
|
|
113
118
|
|
|
114
119
|
/**
|
|
115
120
|
* One status_emojis map. `platform` and `channel` say where it applies; `previewBody(map)` is what the preview resolves.
|
|
@@ -24,8 +24,9 @@ export function changedFields(next, baseline) {
|
|
|
24
24
|
/** #1302: cross-instance notices. Unset, or a value the fleet does not know, means full. */
|
|
25
25
|
export const visibilityDefault = (defaults) => (VISIBILITY_MODES.includes(defaults && defaults.cross_instance_visibility) ? defaults.cross_instance_visibility : "full");
|
|
26
26
|
|
|
27
|
+
/** The backends whose turn end the reply guard can see (backend replyCompletionGuard: claude-code, codex and muse since #1510, kiro legacy/tui), with the MCP server it needs. */
|
|
27
28
|
export const replyGuardSupported = (backend, mode, kiroUi = "legacy") =>
|
|
28
|
-
mode === "mcp" && (
|
|
29
|
+
mode === "mcp" && (["claude-code", "codex", "muse"].includes(backend) || (backend === "kiro-cli" && ["legacy", "tui"].includes(kiroUi || "legacy")));
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* The backend picker's options. A current value it does not offer (the removed gemini-cli, #1280 review) stays first
|