@songsid/agend 2.2.0-beta.2 → 2.2.0-beta.20
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 +40 -24
- package/README.zh-TW.md +40 -24
- package/dist/agent-cli-instructions.md +6 -2
- package/dist/agent-cli.js +15 -7
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +3 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.js +16 -4
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +3 -1
- package/dist/auth-api.js +17 -1
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/antigravity.d.ts +33 -0
- package/dist/backend/antigravity.js +126 -18
- 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 +37 -1
- package/dist/backend/effort-metadata.js +86 -15
- 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.d.ts +27 -1
- package/dist/backend/kiro.js +63 -10
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/launch-effort.d.ts +61 -0
- package/dist/backend/launch-effort.js +170 -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 +48 -2
- package/dist/backend/types.js +12 -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 +103 -3
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +35 -3
- package/dist/channel/adapters/telegram.js +122 -23
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +5 -3
- 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.d.ts +2 -0
- package/dist/channel/tool-router.js +6 -5
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +46 -4
- package/dist/cli.js +184 -34
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +2 -2
- package/dist/command-table.js +5 -2
- package/dist/command-table.js.map +1 -1
- package/dist/compact-watch.d.ts +86 -0
- package/dist/compact-watch.js +288 -0
- package/dist/compact-watch.js.map +1 -0
- package/dist/config-validator.js +55 -5
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/cross-instance-envelope.js +2 -0
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +129 -1
- package/dist/daemon.js +754 -94
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +64 -8
- package/dist/delivery-outbox.js +315 -24
- 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/doctor.js +7 -0
- package/dist/doctor.js.map +1 -1
- package/dist/fleet-context.d.ts +12 -1
- package/dist/fleet-manager.d.ts +392 -19
- package/dist/fleet-manager.js +2030 -275
- package/dist/fleet-manager.js.map +1 -1
- package/dist/forged-envelope-scan.d.ts +4 -3
- package/dist/forged-envelope-scan.js +4 -3
- package/dist/forged-envelope-scan.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +84 -16
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +16 -1
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +4 -1
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +4 -0
- 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/install-lock.d.ts +18 -1
- package/dist/install-lock.js +30 -3
- package/dist/install-lock.js.map +1 -1
- package/dist/instance-avatar.d.ts +5 -4
- package/dist/instance-avatar.js +12 -8
- package/dist/instance-avatar.js.map +1 -1
- package/dist/instance-config-impact.js +1 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +25 -6
- package/dist/instance-lifecycle.js +76 -27
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +3 -3
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-compact-pane.d.ts +38 -0
- package/dist/kiro-compact-pane.js +57 -0
- package/dist/kiro-compact-pane.js.map +1 -0
- 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/launchd-handoff.d.ts +43 -0
- package/dist/launchd-handoff.js +56 -0
- package/dist/launchd-handoff.js.map +1 -0
- package/dist/locale.js +268 -42
- 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/login-shell.d.ts +21 -0
- package/dist/login-shell.js +98 -0
- package/dist/login-shell.js.map +1 -0
- 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/outbound-handlers.d.ts +100 -0
- package/dist/outbound-handlers.js +277 -93
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +30 -15
- package/dist/outbound-schemas.js +23 -5
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/provider-probe.d.ts +29 -0
- package/dist/provider-probe.js +198 -2
- package/dist/provider-probe.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.d.ts +2 -0
- package/dist/public-web-gateway.js +20 -6
- 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/quickstart-api.d.ts +1 -1
- package/dist/quickstart-api.js +8 -6
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +12 -0
- package/dist/quickstart.js +36 -6
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +89 -5
- package/dist/reply-buttons-controller.js +273 -8
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/reply-buttons.d.ts +48 -3
- package/dist/reply-buttons.js +105 -10
- package/dist/reply-buttons.js.map +1 -1
- package/dist/reply-polls.d.ts +85 -0
- package/dist/reply-polls.js +154 -0
- package/dist/reply-polls.js.map +1 -0
- 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 -2
- package/dist/restart-progress.js +30 -2
- package/dist/restart-progress.js.map +1 -1
- package/dist/sd-notify.d.ts +22 -1
- package/dist/sd-notify.js +63 -10
- package/dist/sd-notify.js.map +1 -1
- package/dist/service-activation.d.ts +15 -7
- package/dist/service-activation.js +42 -26
- package/dist/service-activation.js.map +1 -1
- package/dist/service-installer.d.ts +55 -0
- package/dist/service-installer.js +90 -3
- package/dist/service-installer.js.map +1 -1
- package/dist/service-plan.d.ts +11 -0
- package/dist/service-plan.js +23 -0
- package/dist/service-plan.js.map +1 -1
- package/dist/settings-api.js +14 -4
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +4 -2
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +2 -0
- package/dist/settings-effect.js.map +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/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 +1 -1
- package/dist/tips.js.map +1 -1
- package/dist/tool-permissions.js +3 -3
- package/dist/tool-permissions.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 +71 -4
- package/dist/topic-commands.js +167 -34
- 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 +85 -3
- package/dist/transcript-jsonl.js +156 -13
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.d.ts +11 -0
- package/dist/transcript-monitor.js +61 -13
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +24 -0
- package/dist/transcript-sources.js +211 -56
- package/dist/transcript-sources.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 +22 -0
- package/dist/ui/chat-commands.js +1 -0
- package/dist/ui/chat-mentions.js +63 -0
- package/dist/ui/chat-render.js +80 -10
- package/dist/ui/chat-store.js +70 -6
- package/dist/ui/chat-strings.js +62 -32
- package/dist/ui/chat-thread.js +199 -24
- package/dist/ui/chat-time.js +80 -0
- package/dist/ui/chat-tour.js +17 -14
- package/dist/ui/panel-chat.js +45 -21
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +213 -58
- package/dist/ui/panel-log.js +428 -0
- package/dist/ui/panel-needs.js +14 -10
- package/dist/ui/panel-settings.js +82 -17
- 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 +11 -6
- package/dist/ui/settings-model.js +3 -1
- package/dist/ui/settings-strings.js +32 -12
- package/dist/ui/settings-token.js +8 -1
- package/dist/ui/settings-wizard.js +5 -3
- package/dist/ui/shared/app-i18n.js +41 -15
- package/dist/ui/shared/app-prefs.js +250 -0
- package/dist/ui/shared/app-route.js +6 -1
- 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 +26 -5
- package/dist/ui/shared/app.css +170 -30
- package/dist/ui/shared/app.js +11 -1
- package/dist/ui/shared/fonts/README.md +1 -1
- package/dist/ui/shared/header-tools.js +83 -13
- package/dist/ui/shared/instance-nav.js +178 -51
- package/dist/ui/shared/instance-switch.js +8 -5
- package/dist/ui/shared/panel-view.js +21 -4
- package/dist/ui/shared/signin.js +15 -9
- package/dist/ui/shared/theme.js +39 -7
- package/dist/ui/shared/tokens.css +1 -1
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/shared/usage-dialog.js +23 -5
- package/dist/ui/shared/view-strings.js +40 -4
- package/dist/ui/signin.html +8 -3
- 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-check.d.ts +31 -0
- package/dist/update-check.js +47 -0
- package/dist/update-check.js.map +1 -1
- package/dist/update-install.d.ts +13 -1
- package/dist/update-install.js +16 -4
- package/dist/update-install.js.map +1 -1
- package/dist/update-launch.d.ts +48 -1
- package/dist/update-launch.js +159 -5
- package/dist/update-launch.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/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 +62 -13
- package/dist/usage/providers.js +256 -68
- 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.js +31 -9
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +82 -4
- package/dist/web-api.js +632 -88
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +5 -2
- package/dist/web-auth.js.map +1 -1
- package/dist/web-chat-history.d.ts +26 -1
- package/dist/web-chat-history.js +47 -8
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +41 -5
- package/dist/web-commands.js +25 -7
- package/dist/web-commands.js.map +1 -1
- package/dist/web-login.d.ts +14 -0
- package/dist/web-login.js +4 -2
- package/dist/web-login.js.map +1 -1
- 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 +81 -0
- package/dist/web-prefs.js +297 -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-profile.d.ts +32 -0
- package/dist/web-profile.js +132 -0
- package/dist/web-profile.js.map +1 -0
- 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 +14 -0
- package/dist/web-session.js +11 -1
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +3 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -0
- package/dist/web-shell-routes.js +9 -0
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-upload.d.ts +135 -7
- package/dist/web-upload.js +315 -51
- package/dist/web-upload.js.map +1 -1
- package/launcher/install-admission.cjs +61 -6
- package/package.json +3 -3
- package/templates/launchd.plist.ejs +3 -1
- package/templates/systemd.service.ejs +2 -1
|
@@ -24,7 +24,7 @@ import "./settings-strings.js";
|
|
|
24
24
|
import { api, attach, confirmedWrite, newKey } from "./settings-confirm.js";
|
|
25
25
|
import { dismissOperation, operationActive, restartFleet, startOperation, takeLeftover } from "./settings-apply.js";
|
|
26
26
|
import {
|
|
27
|
-
ACCESS_MODES, AGENT_MODES, DEFAULT_SCHEMA, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS, VISIBILITY_MODES, batchImpact,
|
|
27
|
+
ACCESS_MODES, AGENT_MODES, DEFAULT_SCHEMA, HANG_TIMEOUT_DEFAULT_MINUTES, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS, VISIBILITY_MODES, batchImpact,
|
|
28
28
|
changedFields, channelId, channelsOf, chLabel, connectionState, effectiveSummary, fleetModel, fromYaml, fullModelRequests, groupAgents,
|
|
29
29
|
hasOwn, impactOf, localeOptions, defaultsImpact, requestImpact, nonNegative, positive, replyGuardSupported, shortName, STATUS_ORDER, toYaml,
|
|
30
30
|
visibilityDefault, worstImpact,
|
|
@@ -258,9 +258,10 @@ export function SettingsPanel({ route, navKey }) {
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
/**
|
|
261
|
-
* #1604:
|
|
262
|
-
*
|
|
263
|
-
* 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).
|
|
264
265
|
*/
|
|
265
266
|
export function DeviceSection() {
|
|
266
267
|
const P = () => globalThis.AgendPreview;
|
|
@@ -269,7 +270,11 @@ export function DeviceSection() {
|
|
|
269
270
|
const replay = () => {
|
|
270
271
|
let last = null;
|
|
271
272
|
try { last = localStorage.getItem("agend_last_instance"); } catch { /* none */ }
|
|
272
|
-
|
|
273
|
+
// #1854: the tour is about an agent's chat — with no agent to open (none remembered, or it is gone), say so instead
|
|
274
|
+
// of opening an empty chat that waits ~3 s and shows nothing.
|
|
275
|
+
const { instances } = appStore.get();
|
|
276
|
+
if (!last || !(instances || []).some((i) => i && i.name === last)) { toast(t("chat.tourNoAgent"), false); return; }
|
|
277
|
+
navigate(chatPath(last));
|
|
273
278
|
// This replay belongs to the navigation it just made — that one, by its sequence number, not by URL (A → B → A, or
|
|
274
279
|
// the same path again, is a new navigation). It did not happen (a leave guard said no) or it is not the chat:
|
|
275
280
|
// nothing. Left meanwhile (another page, another navigation): nothing (#1609 review).
|
|
@@ -284,22 +289,73 @@ export function DeviceSection() {
|
|
|
284
289
|
let tries = 0;
|
|
285
290
|
const go = () => {
|
|
286
291
|
if (!current()) return;
|
|
287
|
-
if (!document.getElementById("sendBtn")) { if (++tries <= 30) setTimeout(go, 100); return; }
|
|
292
|
+
if (!document.getElementById("sendBtn")) { if (++tries <= 30) setTimeout(go, 100); else toast(t("chat.tourNoAgent"), false); return; }
|
|
288
293
|
const here = document.getElementById("msgIn") || document.getElementById("main");
|
|
289
294
|
if (here && typeof here.focus === "function") here.focus();
|
|
290
295
|
if (current()) startTour();
|
|
291
296
|
};
|
|
292
297
|
setTimeout(go, 0);
|
|
293
298
|
};
|
|
294
|
-
|
|
295
|
-
|
|
299
|
+
const publicLink = typeof document !== "undefined" && document.body && document.body.dataset.publicLink === "1";
|
|
300
|
+
return html`<${ProfileCard} />
|
|
301
|
+
<section class="card s-device s-synced"><h3>${tn("syncedTitle")}</h3>
|
|
302
|
+
<p class="sub">${publicLink ? tn("syncedPublicLink") : tn("syncedLead")}</p>
|
|
296
303
|
<${DevicePrefs} labels=${true} />
|
|
297
304
|
${P() ? html`<label class="check"><input type="checkbox" checked=${on} onChange=${(e) => { const want = e.target.checked; setPreviewOptIn(want).then(setOn); }} />
|
|
298
305
|
<span>${t("chat.previews")}</span></label>` : null}
|
|
306
|
+
</section>
|
|
307
|
+
<section class="card s-device s-local"><h3>${tn("deviceTitle")}</h3>
|
|
308
|
+
<p class="sub">${tn("deviceLead")}</p>
|
|
299
309
|
<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>
|
|
300
310
|
</section>`;
|
|
301
311
|
}
|
|
302
312
|
|
|
313
|
+
/**
|
|
314
|
+
* #1827: who agents see when this person writes from the web. Signed in with a code sent to their Discord or Telegram
|
|
315
|
+
* account, they are that account — its user id, and its name unless they choose another (it follows them to every
|
|
316
|
+
* device, the public link included). Otherwise "web-user", as before. The host checks a name in full and says why it
|
|
317
|
+
* refuses one; this only shows what it says.
|
|
318
|
+
*/
|
|
319
|
+
const view = (b) => ({ phase: "ready", name: b.name || "", shown: b.shown || "", id: b.id || "", bound: b.bound === true, platform: b.platform_name || "", error: "" });
|
|
320
|
+
export function ProfileCard() {
|
|
321
|
+
const [state, setState] = useState({ phase: "loading", name: "", shown: "", id: "", bound: false, platform: "", error: "" });
|
|
322
|
+
const [draft, setDraft] = useState("");
|
|
323
|
+
const lease = useLease();
|
|
324
|
+
useEffect(() => {
|
|
325
|
+
let live = true;
|
|
326
|
+
api("/ui/profile").then((r) => {
|
|
327
|
+
if (!live) return;
|
|
328
|
+
if (!r.ok || !r.body) { setState((s) => ({ ...s, phase: "unavailable" })); return; }
|
|
329
|
+
setState(view(r.body));
|
|
330
|
+
setDraft(r.body.name || "");
|
|
331
|
+
}).catch(() => { if (live) setState((s) => ({ ...s, phase: "unavailable" })); });
|
|
332
|
+
return () => { live = false; };
|
|
333
|
+
}, []);
|
|
334
|
+
if (state.phase === "unavailable") return null;
|
|
335
|
+
const save = (clear) => {
|
|
336
|
+
setState((s) => ({ ...s, phase: "saving", error: "" }));
|
|
337
|
+
api("/ui/profile", { method: "POST", body: JSON.stringify({ name: clear ? null : draft }) }).then((r) => {
|
|
338
|
+
if (lease && !lease.current()) return;
|
|
339
|
+
if (!r.ok) { setState((s) => ({ ...s, phase: "ready", error: (r.body && r.body.error) || tn("profileFailed") })); return; }
|
|
340
|
+
setState(view(r.body));
|
|
341
|
+
setDraft(r.body.name || "");
|
|
342
|
+
toast(tn("profileSaved", r.body.shown || "web-user"));
|
|
343
|
+
}).catch(() => setState((s) => ({ ...s, phase: "ready", error: tn("profileFailed") })));
|
|
344
|
+
};
|
|
345
|
+
const busy = state.phase !== "ready";
|
|
346
|
+
const changed = draft.trim() !== state.name;
|
|
347
|
+
return html`<section class="card s-device s-profile"><h3>${tn("profileTitle")}</h3>
|
|
348
|
+
<p class="sub">${state.bound ? tn("profileLeadBound") : tn("profileLeadUnbound")}</p>
|
|
349
|
+
<div class="field"><label for="profile-name">${tn("profileName")}</label>
|
|
350
|
+
<input id="profile-name" type="text" maxlength="200" autocomplete="nickname" value=${draft} disabled=${busy}
|
|
351
|
+
placeholder=${state.platform || "web-user"} aria-invalid=${state.error ? "true" : "false"} aria-describedby="profile-help"
|
|
352
|
+
onInput=${(e) => setDraft(e.target.value)} onKeyDown=${(e) => { if (e.key === "Enter" && changed && draft.trim()) save(false); }} />
|
|
353
|
+
<p class="sub" id="profile-help">${state.error ? html`<span class="err" role="alert">${state.error}</span>` : tn("profileHelp", state.shown || "web-user", state.id)}</p></div>
|
|
354
|
+
<div class="s-device-row"><button type="button" class="btn primary" disabled=${busy || !changed || !draft.trim()} onClick=${() => save(false)}>${tn("profileSave")}</button>
|
|
355
|
+
${state.name ? html`<button type="button" class="btn" disabled=${busy} onClick=${() => save(true)}>${state.bound ? tn("profileUseAccount", state.platform || "web-user") : tn("profileClear")}</button>` : null}</div>
|
|
356
|
+
</section>`;
|
|
357
|
+
}
|
|
358
|
+
|
|
303
359
|
/** The operation in hand still owns Apply: its writes and job, or a restart of AgEnD from the press to its end. */
|
|
304
360
|
const opBusy = (op) => !!op && (["writing", "posting", "watching"].includes(op.phase) || op.restart === "busy");
|
|
305
361
|
function dropSecrets(staged) { for (const c of staged.values()) if (c.connectionSecret) c.connectionSecret.secret = ""; }
|
|
@@ -367,11 +423,13 @@ function makeCtx(data, setData, stage, unstage, reload, reloadLive, alive) {
|
|
|
367
423
|
async pauseWake(name, action) {
|
|
368
424
|
const res = await api(`/api/settings/instances/${encodeURIComponent(name)}/${action}`, { method: "POST" }).catch(() => ({ ok: false, body: {} }));
|
|
369
425
|
if (!res.ok) toast((res.body && res.body.error) || tn("actionFailed", action), false);
|
|
426
|
+
else if (res.body && res.body.held) toast(res.body.message);
|
|
370
427
|
await reloadLive();
|
|
371
428
|
},
|
|
372
429
|
async startStop(name, running) {
|
|
373
430
|
const res = await api(running ? `/stop/${encodeURIComponent(name)}` : `/api/instance/${encodeURIComponent(name)}/start`, { method: "POST" }).catch(() => ({ ok: false, body: {} }));
|
|
374
431
|
if (!res.ok) toast(`${tn("actionFailed", running ? tn("stop") : tn("start"))}: ${(res.body && res.body.error) || res.status || ""}`, false);
|
|
432
|
+
else if (res.body && res.body.held) toast(res.body.message);
|
|
375
433
|
await new Promise((r) => setTimeout(r, 800));
|
|
376
434
|
await reloadLive();
|
|
377
435
|
},
|
|
@@ -581,7 +639,7 @@ function General({ ctx }) {
|
|
|
581
639
|
backend: d.backend || "claude-code", model: d.model || "", locale: d.locale || "", auto: String(d.auto_pause_after ?? 0),
|
|
582
640
|
agent_mode: d.agent_mode || "mcp", tool_set: d.tool_set || "full", tool_progress: d.tool_progress ?? "off",
|
|
583
641
|
visibility: visibilityDefault(d), reply: d.reply_completion_guard ?? true, log_level: d.log_level || "info",
|
|
584
|
-
hangOn: hang.enabled !== false, hang: String(hang.timeout_minutes ??
|
|
642
|
+
hangOn: hang.enabled !== false, hang: String(hang.timeout_minutes ?? HANG_TIMEOUT_DEFAULT_MINUTES),
|
|
585
643
|
concurrency: String(startup.concurrency ?? 10), stagger: String(startup.stagger_delay_ms ?? 500),
|
|
586
644
|
classicBackend: c.backend || d.backend || "claude-code",
|
|
587
645
|
classicToolProgress: c.tool_progress ?? d.tool_progress ?? "off", classicToolProgressInherit: !hasOwn(c, "tool_progress"),
|
|
@@ -610,7 +668,7 @@ function General({ ctx }) {
|
|
|
610
668
|
backend: d.backend || "claude-code", model: d.model || undefined, locale: d.locale || null, auto_pause_after: d.auto_pause_after ?? 0,
|
|
611
669
|
agent_mode: d.agent_mode || "mcp", tool_set: d.tool_set || "full", tool_progress: d.tool_progress ?? "off",
|
|
612
670
|
cross_instance_visibility: visibilityDefault(d), reply_completion_guard: d.reply_completion_guard ?? true, log_level: d.log_level || "info",
|
|
613
|
-
hang_detector: { enabled: hang.enabled !== false, timeout_minutes: hang.timeout_minutes ??
|
|
671
|
+
hang_detector: { enabled: hang.enabled !== false, timeout_minutes: hang.timeout_minutes ?? HANG_TIMEOUT_DEFAULT_MINUTES },
|
|
614
672
|
startup: { concurrency: startup.concurrency ?? 10, stagger_delay_ms: startup.stagger_delay_ms ?? 500 },
|
|
615
673
|
});
|
|
616
674
|
const oc = original.c;
|
|
@@ -702,8 +760,8 @@ function General({ ctx }) {
|
|
|
702
760
|
* the temporary public link none of it can be changed — the form says so and stays disabled (the server refuses too).
|
|
703
761
|
*/
|
|
704
762
|
export const WEB_READ_ONLY = [
|
|
705
|
-
"web.allowed_hosts", "web.view_access", "web.notify_login", "web.preview", "web.preview_port", "web.preview_origin",
|
|
706
|
-
"web.usage_panel", "web.provider_secrets", "web_terminal.bind", "web_terminal.tunnel",
|
|
763
|
+
"web.allowed_hosts", "web.view_access", "web.notify_login", "web.preview", "web.preview_port", "web.preview_origin", "web.preview_on_public_link",
|
|
764
|
+
"web.usage_panel", "web.provider_secrets", "web.upload_max_file_mb", "web.upload_max_total_mb", "web_terminal.bind", "web_terminal.tunnel",
|
|
707
765
|
];
|
|
708
766
|
function WebSection({ ctx, staged }) {
|
|
709
767
|
const { schema } = ctx;
|
|
@@ -711,13 +769,14 @@ function WebSection({ ctx, staged }) {
|
|
|
711
769
|
const overPublicLink = globalThis.document?.body?.dataset?.publicLink === "1";
|
|
712
770
|
const original = useMemo(() => ({
|
|
713
771
|
echo: web.echo_to_channel ?? true,
|
|
772
|
+
log: web.log_tab === true, // #1218: off by default
|
|
714
773
|
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" },
|
|
715
774
|
session: { absolute_hours: local.absolute_hours ?? null, idle_hours: local.idle_hours ?? null },
|
|
716
775
|
terminal: { enabled: term.enabled ?? null, ttl_minutes: term.ttl_minutes ?? null },
|
|
717
776
|
}), []);
|
|
718
777
|
const text = (v) => (v === null || v === undefined ? "" : String(v));
|
|
719
778
|
const [f, setF] = useState(() => ({
|
|
720
|
-
echo: original.echo, pub: original.pub.allow_public, ttl: String(original.pub.ttl_minutes), protocol: original.pub.protocol,
|
|
779
|
+
echo: original.echo, log: original.log, pub: original.pub.allow_public, ttl: String(original.pub.ttl_minutes), protocol: original.pub.protocol,
|
|
721
780
|
abs: text(original.session.absolute_hours), idle: text(original.session.idle_hours),
|
|
722
781
|
termOn: original.terminal.enabled !== false, termTtl: text(original.terminal.ttl_minutes),
|
|
723
782
|
}));
|
|
@@ -726,7 +785,9 @@ function WebSection({ ctx, staged }) {
|
|
|
726
785
|
// Empty = the default (null in the write: the key is removed from fleet.yaml).
|
|
727
786
|
const value = (v) => (v.trim() === "" ? null : Number(v));
|
|
728
787
|
const hours = (v) => { const n = value(v); return n !== null && (!Number.isFinite(n) || n < 1 || n > 720) ? "sessionRange" : ""; };
|
|
729
|
-
|
|
788
|
+
// The defaults (#1670: 48 h / 48 h, DEFAULT_SESSION_POLICY); an idle time left blank follows a shorter session, as
|
|
789
|
+
// the store and the validator apply it — so only an idle time that is typed can exceed the session length.
|
|
790
|
+
const abs = value(f.abs) ?? 48, idle = value(f.idle) ?? Math.min(48, abs);
|
|
730
791
|
const errors = {
|
|
731
792
|
ttl: !Number.isInteger(Number(f.ttl)) || Number(f.ttl) < 1 || Number(f.ttl) > 480 ? "publicTtl" : "",
|
|
732
793
|
abs: hours(f.abs),
|
|
@@ -743,12 +804,14 @@ function WebSection({ ctx, staged }) {
|
|
|
743
804
|
ttl_minutes: value(f.termTtl) }, original.terminal);
|
|
744
805
|
const body = {
|
|
745
806
|
...(f.echo !== original.echo ? { echo_to_channel: f.echo } : {}),
|
|
807
|
+
...(f.log !== original.log ? { log_tab: f.log } : {}),
|
|
746
808
|
...(Object.keys(publicPatch).length ? { public_link: publicPatch } : {}),
|
|
747
809
|
...(Object.keys(sessionPatch).length ? { session: { local: sessionPatch } } : {}),
|
|
748
810
|
...(Object.keys(terminalPatch).length ? { web_terminal: terminalPatch } : {}),
|
|
749
811
|
};
|
|
750
812
|
const fields = [
|
|
751
813
|
...(body.echo_to_channel !== undefined ? ["web.echo_to_channel"] : []),
|
|
814
|
+
...(body.log_tab !== undefined ? ["web.log_tab"] : []),
|
|
752
815
|
...Object.keys(publicPatch).map((k) => `web.public_link.${k}`),
|
|
753
816
|
...Object.keys(sessionPatch).map((k) => `web.session.local.${k}`),
|
|
754
817
|
...Object.keys(terminalPatch).map((k) => `web_terminal.${k}`),
|
|
@@ -771,9 +834,9 @@ function WebSection({ ctx, staged }) {
|
|
|
771
834
|
<section class="card"><h3>${tn("webSessionTitle")}</h3>
|
|
772
835
|
<p class="note">${tn("webSessionHint")}</p>
|
|
773
836
|
<div class="grid2">
|
|
774
|
-
<div class="field"><label for="webSessionAbsolute">${tn("sessionAbsolute")}</label><input id="webSessionAbsolute" type="number" min="1" max="720" step="any" placeholder=${tn("defaultHours",
|
|
837
|
+
<div class="field"><label for="webSessionAbsolute">${tn("sessionAbsolute")}</label><input id="webSessionAbsolute" type="number" min="1" max="720" step="any" placeholder=${tn("defaultHours", 48)}
|
|
775
838
|
class=${errors.abs ? "invalid" : ""} value=${f.abs} onInput=${(e) => set("abs")(e.target.value)} /><${Feedback} error=${errors.abs} /></div>
|
|
776
|
-
<div class="field"><label for="webSessionIdle">${tn("sessionIdle")}</label><input id="webSessionIdle" type="number" min="1" max="720" step="any" placeholder=${tn("defaultHours",
|
|
839
|
+
<div class="field"><label for="webSessionIdle">${tn("sessionIdle")}</label><input id="webSessionIdle" type="number" min="1" max="720" step="any" placeholder=${tn("defaultHours", 48)}
|
|
777
840
|
class=${errors.idle ? "invalid" : ""} value=${f.idle} onInput=${(e) => set("idle")(e.target.value)} /><${Feedback} error=${errors.idle} /></div>
|
|
778
841
|
</div>
|
|
779
842
|
</section>
|
|
@@ -788,6 +851,8 @@ function WebSection({ ctx, staged }) {
|
|
|
788
851
|
<section class="card"><h3>${tn("webChat")}</h3>
|
|
789
852
|
<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>
|
|
790
853
|
<p class="note">${tn("webEchoHint")}</p>
|
|
854
|
+
<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>
|
|
855
|
+
<p class="note">${tn("webLogTabHint")}</p>
|
|
791
856
|
<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>
|
|
792
857
|
<p class="note">${tn("publicLinkHint")}</p>
|
|
793
858
|
<div class="grid2">
|
|
@@ -840,7 +905,7 @@ function Developer({ ctx }) {
|
|
|
840
905
|
<button type="button" class="btn btn-sm" title=${tn("copy")} onClick=${copy}><${Icon} name="copy" size=${14} /><span class="lbl">${tn("copy")}</span></button>
|
|
841
906
|
<button type="button" class="btn btn-sm" title=${tn("devDownload")} onClick=${download}><${Icon} name="download" size=${14} /><span class="lbl">${tn("devDownload")}</span></button>
|
|
842
907
|
</div>
|
|
843
|
-
${editing ? html`<textarea class="s-yaml" aria-label=${tn("devEditor")} spellcheck
|
|
908
|
+
${editing ? html`<textarea class="s-yaml" aria-label=${tn("devEditor")} spellcheck=${false} wrap="off" value=${draft} onInput=${(e) => setDraft(e.target.value)}></textarea>
|
|
844
909
|
<div class="save-row"><button type="button" class="btn btn-primary" onClick=${save}>${tn("devApply")}</button><span class="note">${tn("devApplyHint")}</span></div>`
|
|
845
910
|
: html`<pre class="s-yaml">${text}</pre>`}
|
|
846
911
|
${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
|
});
|
|
@@ -10,11 +10,11 @@ import { Icon } from "/assets/ui-icons.js";
|
|
|
10
10
|
import { toast } from "/assets/ui-toast.js";
|
|
11
11
|
import { confirmDialog } from "/assets/ui-confirm.js";
|
|
12
12
|
import { api, confirmedWrite, newKey } from "./settings-confirm.js";
|
|
13
|
-
import { botHandle, TokenEnvNote, TokenField, verifyBotToken } from "./settings-token.js";
|
|
13
|
+
import { adminUserIdError, adminUserIdPattern, botHandle, TokenEnvNote, TokenField, verifyBotToken } from "./settings-token.js";
|
|
14
14
|
import { DiscordServerPicker } from "./settings-discord.js";
|
|
15
15
|
import { startOperation } from "./settings-apply.js";
|
|
16
16
|
import {
|
|
17
|
-
ACCESS_MODES, AGENT_FIELDS, AGENT_MODES, BOT_FIELDS, CH_TYPES, CLASSIC_FIELDS, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS,
|
|
17
|
+
ACCESS_MODES, AGENT_FIELDS, AGENT_MODES, BOT_FIELDS, CH_TYPES, CLASSIC_FIELDS, HANG_TIMEOUT_DEFAULT_MINUTES, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS,
|
|
18
18
|
VISIBILITY_MODES, accessConfirmations, agentForm, agentPatch, backendOptions, changedFields, channelId, channelsOf,
|
|
19
19
|
chLabel, hasOwn, impactOf, nonNegative, nonNegativeInteger, positive, replyGuardSupported, sameValue, visibilityDefault,
|
|
20
20
|
webEchoIsGroup, worstImpact,
|
|
@@ -242,7 +242,7 @@ export function AgentDialog({ name, inst, ctx, onClose }) {
|
|
|
242
242
|
<div class="field">${L(`${tn("hangTimeout")} (${tn("minutes")})`, "instance.hang_detector", "ag-hang")}
|
|
243
243
|
<${Override} inherit=${f.inherit.hang} onInherit=${setInherit("hang")}>
|
|
244
244
|
<input id="ag-hang" type="number" min="0.1" step="0.5" class=${errors.hang ? "invalid" : ""} disabled=${f.inherit.hang}
|
|
245
|
-
value=${shown("hang", f.hang, String((defaults.hang_detector && defaults.hang_detector.timeout_minutes) ??
|
|
245
|
+
value=${shown("hang", f.hang, String((defaults.hang_detector && defaults.hang_detector.timeout_minutes) ?? HANG_TIMEOUT_DEFAULT_MINUTES))} onInput=${(e) => set("hang")(e.target.value)} /></${Override}>
|
|
246
246
|
<${Feedback} error=${errors.hang} /></div>
|
|
247
247
|
${[["agent_mode", "agentMode", AGENT_MODES, defaults.agent_mode || "mcp"], ["tool_set", "toolSet", TOOL_SETS, defaults.tool_set || "full"],
|
|
248
248
|
["tool_progress", "toolProgress", TOOL_PROGRESS, defaults.tool_progress ?? "off"],
|
|
@@ -466,17 +466,18 @@ export function BotDialog({ id, ctx, onClose }) {
|
|
|
466
466
|
*/
|
|
467
467
|
export function NewBotDialog({ ctx, onClose }) {
|
|
468
468
|
const lease = useLease("new-bot");
|
|
469
|
-
const [f, setF] = useState({ type: "discord", id: "", token: "", identity: null, guilds: [], group: "", general: "" });
|
|
469
|
+
const [f, setF] = useState({ type: "discord", id: "", token: "", identity: null, guilds: [], group: "", general: "", admin: "" });
|
|
470
470
|
const [plan, setPlan] = useState(null);
|
|
471
471
|
const [busy, setBusy] = useState(false);
|
|
472
472
|
const [err, setErr] = useState("");
|
|
473
473
|
// The form's revision (#1529 review): any change to the token, platform, id or group moves it; a Verify or a plan
|
|
474
474
|
// that began before is dropped when it lands — never restoring an old token, naming it, or planning for it.
|
|
475
475
|
const rev = useRef(0);
|
|
476
|
-
const body = (x) => ({ platform: x.type, connection_only: true, ...(x.id.trim() ? { channel_id: x.id.trim() } : {}),
|
|
476
|
+
const body = (x) => ({ platform: x.type, connection_only: true, admin_user_id: x.admin.trim(), ...(x.id.trim() ? { channel_id: x.id.trim() } : {}),
|
|
477
477
|
...(x.group.trim() ? (x.type === "discord" ? { guild_id: x.group.trim() } : { group_id: x.group.trim() }) : {}),
|
|
478
478
|
...(x.type === "discord" && x.general.trim() ? { general_channel_id: x.general.trim() } : {}) });
|
|
479
479
|
const planFor = async (x) => {
|
|
480
|
+
if (adminUserIdError(x.type, x.admin.trim())) { setPlan(null); return null; }
|
|
480
481
|
const at = rev.current;
|
|
481
482
|
const res = await api("/api/settings/quickstart/plan", { method: "POST", body: JSON.stringify(body(x)) }).catch(() => ({ ok: false, body: {} }));
|
|
482
483
|
if (!lease.current() || rev.current !== at) return null;
|
|
@@ -501,6 +502,8 @@ export function NewBotDialog({ ctx, onClose }) {
|
|
|
501
502
|
};
|
|
502
503
|
const save = async () => {
|
|
503
504
|
if (!f.identity || !f.identity.valid) { setErr(tn("wizardNeedVerify")); return; }
|
|
505
|
+
const adminError = adminUserIdError(f.type, f.admin.trim());
|
|
506
|
+
if (adminError) { setErr(adminError); return; }
|
|
504
507
|
const x = f;
|
|
505
508
|
setBusy(true);
|
|
506
509
|
const p = await planFor(x);
|
|
@@ -514,7 +517,7 @@ export function NewBotDialog({ ctx, onClose }) {
|
|
|
514
517
|
};
|
|
515
518
|
// A change of platform or id is another connection: what was verified and planned no longer applies. The server or
|
|
516
519
|
// group does not change the id or the token's name, so the plan (and its name under Advanced) stays.
|
|
517
|
-
const set = (k) => (v) => { rev.current++; setF((x) => ({ ...x, [k]: v, ...(k === "type" ? { identity: null, guilds: [], group: "", general: "" } : {}) })); if (k !== "group" && k !== "general") setPlan(null); };
|
|
520
|
+
const set = (k) => (v) => { rev.current++; setF((x) => ({ ...x, [k]: v, ...(k === "type" ? { identity: null, guilds: [], group: "", general: "", admin: "" } : {}) })); if (k !== "group" && k !== "general") setPlan(null); };
|
|
518
521
|
return html`<${Dialog} title=${tn("newBot")} onClose=${onClose} busy=${busy}
|
|
519
522
|
actions=${html`<button type="button" class="btn" disabled=${busy} onClick=${onClose}>${tn("cancel")}</button>
|
|
520
523
|
<button type="button" class="btn btn-primary" disabled=${busy || !f.identity || !f.identity.valid} onClick=${save}>${tn("save")}</button>`}>
|
|
@@ -528,6 +531,8 @@ export function NewBotDialog({ ctx, onClose }) {
|
|
|
528
531
|
guild=${f.group} onGuild=${set("group")} channel=${f.general} onChannel=${set("general")} />`
|
|
529
532
|
: html`<div class="field"><label for="nb-group">${tn("groupIdField")}</label>
|
|
530
533
|
<input id="nb-group" type="text" value=${f.group} onInput=${(e) => set("group")(e.target.value.trim())} /></div>`}
|
|
534
|
+
<div class="field"><label for="nb-user">${tn("wizardAdminUser")}</label>
|
|
535
|
+
<input id="nb-user" type="text" required=${true} inputmode="numeric" pattern=${adminUserIdPattern(f.type)} value=${f.admin} onInput=${(e) => set("admin")(e.target.value.trim())} /></div>
|
|
531
536
|
<${Drawer} title=${tn("advancedSection")}>
|
|
532
537
|
<div class="field"><label for="nb-id">${tn("connectionIdOptional")}</label><input id="nb-id" type="text" placeholder=${plan ? plan.channel_id : f.type}
|
|
533
538
|
value=${f.id} onInput=${(e) => set("id")(e.target.value)} onBlur=${() => { if (f.identity && f.identity.valid) planFor(f); }} /></div>
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Ported from settings.html with their reasons: every rule here was a fix (the review or issue named beside it).
|
|
3
3
|
// No DOM, no fetch; the panel (panel-settings.js) and the tests call these directly.
|
|
4
4
|
|
|
5
|
+
/** #1854: the stuck timeout when none is set — the daemon's DEFAULT_STUCK_TIMEOUT_MS (src/daemon.ts), in minutes. */
|
|
6
|
+
export const HANG_TIMEOUT_DEFAULT_MINUTES = 10;
|
|
5
7
|
export const BACKENDS = ["claude-code", "kiro-cli", "codex", "opencode", "antigravity", "grok", "muse"];
|
|
6
8
|
export const CH_TYPES = ["discord", "telegram"];
|
|
7
9
|
export const LOCALES = [["en", "English"], ["zh-TW", "繁體中文 (zh-TW)"]];
|
|
@@ -166,7 +168,7 @@ export function agentForm(inst, defaults) {
|
|
|
166
168
|
description: inst.description || "", systemPrompt: inst.systemPrompt || "", tags: [...(inst.tags || [])],
|
|
167
169
|
general_topic: !!inst.general_topic,
|
|
168
170
|
auto_pause_after: String(inst.auto_pause_after ?? defaults.auto_pause_after ?? 0),
|
|
169
|
-
hang: String(hang.timeout_minutes ?? (defaults.hang_detector && defaults.hang_detector.timeout_minutes) ??
|
|
171
|
+
hang: String(hang.timeout_minutes ?? (defaults.hang_detector && defaults.hang_detector.timeout_minutes) ?? HANG_TIMEOUT_DEFAULT_MINUTES),
|
|
170
172
|
agent_mode: inst.agent_mode || defaults.agent_mode || "mcp",
|
|
171
173
|
tool_set: inst.tool_set || defaults.tool_set || "full",
|
|
172
174
|
tool_progress: inst.tool_progress ?? defaults.tool_progress ?? "off",
|