@songsid/agend 2.3.0-beta.24 → 2.3.0-beta.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-world.js.map +1 -1
- package/dist/agend-home.d.ts +5 -0
- package/dist/agend-home.js +7 -0
- package/dist/agend-home.js.map +1 -0
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +8 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-file-read.js +1 -1
- package/dist/agent-file-read.js.map +1 -1
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/apply-job.js.map +1 -1
- package/dist/atomic-write.js.map +1 -1
- package/dist/auth-api.d.ts +5 -0
- package/dist/auth-api.js +21 -0
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/authority-state.d.ts +4 -0
- package/dist/authority/authority-state.js +17 -8
- package/dist/authority/authority-state.js.map +1 -1
- package/dist/authority/session-authorizations.js.map +1 -1
- package/dist/authority/web-authority.d.ts +14 -6
- package/dist/authority/web-authority.js +19 -5
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/backend/antigravity.d.ts +2 -2
- package/dist/backend/antigravity.js +2 -1
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-package.js.map +1 -1
- package/dist/backend/codex-session-lookup.js.map +1 -1
- package/dist/backend/codex.js +1 -1
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/effort-metadata.js.map +1 -1
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro-engine-ledger.js.map +1 -1
- package/dist/backend/kiro-kas-store.js.map +1 -1
- package/dist/backend/kiro-v2-store.js.map +1 -1
- package/dist/backend/kiro-v3-identity.js.map +1 -1
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/launch-effort.js.map +1 -1
- package/dist/backend/mock.js.map +1 -1
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/opencode-permission.js.map +1 -1
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/session-signals.js.map +1 -1
- package/dist/backend-availability.js.map +1 -1
- package/dist/backend-command.js.map +1 -1
- package/dist/backend-outage.js.map +1 -1
- package/dist/binary-probe.js.map +1 -1
- package/dist/bug-report.js.map +1 -1
- package/dist/bundled-skills.js.map +1 -1
- package/dist/cache-service.js +1 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.js +29 -4
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +6 -0
- package/dist/channel/adapters/telegram.js +54 -14
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-server.js +10 -1
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.js +2 -2
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-queue.js.map +1 -1
- package/dist/channel/sticker-download.js.map +1 -1
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/classic-channel-manager.js +3 -2
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli-entry.js.map +1 -1
- package/dist/cli-flags.js.map +1 -1
- package/dist/cli.js +15 -9
- package/dist/cli.js.map +1 -1
- package/dist/command-table.js.map +1 -1
- package/dist/compact-watch.js.map +1 -1
- package/dist/completion-install.js.map +1 -1
- package/dist/completion.js.map +1 -1
- package/dist/config-validator.js +20 -6
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +11 -3
- package/dist/config.js.map +1 -1
- package/dist/connection-secrets.js.map +1 -1
- package/dist/context-guardian.js.map +1 -1
- package/dist/coredump-filter.js.map +1 -1
- package/dist/cpu-profile.js.map +1 -1
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.js +73 -3
- package/dist/daemon.js.map +1 -1
- package/dist/daily-summary.js.map +1 -1
- package/dist/darwin-memory.js.map +1 -1
- package/dist/dashboard-live.d.ts +8 -0
- package/dist/dashboard-live.js +35 -0
- package/dist/dashboard-live.js.map +1 -0
- package/dist/dashboard-permissions.d.ts +69 -0
- package/dist/dashboard-permissions.js +138 -0
- package/dist/dashboard-permissions.js.map +1 -0
- package/dist/decision-list.js.map +1 -1
- package/dist/delivery-consumed-watch.js.map +1 -1
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-outbox.test.js.map +1 -1
- package/dist/delivery-reconciliation.test.js.map +1 -1
- package/dist/diagnostic-redaction.js.map +1 -1
- package/dist/doctor.js.map +1 -1
- package/dist/e2e-isolation.js.map +1 -1
- package/dist/event-log.js.map +1 -1
- package/dist/export-import.js +3 -2
- package/dist/export-import.js.map +1 -1
- package/dist/fleet-control-audit.js.map +1 -1
- package/dist/fleet-label.js.map +1 -1
- package/dist/fleet-lock.js +3 -2
- package/dist/fleet-lock.js.map +1 -1
- package/dist/fleet-manager.d.ts +77 -6
- package/dist/fleet-manager.js +488 -147
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-process-handlers.js +3 -2
- package/dist/fleet-process-handlers.js.map +1 -1
- package/dist/fleet-startup.d.ts +14 -0
- package/dist/fleet-startup.js +30 -0
- package/dist/fleet-startup.js.map +1 -0
- package/dist/fleet-traffic.js.map +1 -1
- package/dist/forged-envelope-scan.d.ts +0 -20
- package/dist/forged-envelope-scan.js +3 -2
- package/dist/forged-envelope-scan.js.map +1 -1
- package/dist/full-restart.js.map +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +2 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +2 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +36 -12
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +45 -14
- package/dist/host-memory.js.map +1 -1
- package/dist/instance-config-impact.js +6 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.js +1 -1
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +2 -0
- package/dist/instructions.js +6 -2
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-compact-pane.js.map +1 -1
- package/dist/kiro-db-reader.js.map +1 -1
- package/dist/kiro-engine-status.js.map +1 -1
- package/dist/kiro-log-gate.js.map +1 -1
- package/dist/kiro-transcript-lane.js.map +1 -1
- package/dist/locale.js +22 -0
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js.map +1 -1
- package/dist/log-steps.js.map +1 -1
- package/dist/logger.js +3 -2
- package/dist/logger.js.map +1 -1
- package/dist/login-controller.js.map +1 -1
- package/dist/login-manager.js.map +1 -1
- package/dist/login-shell.js.map +1 -1
- package/dist/memory-pressure.js.map +1 -1
- package/dist/muse-usage-relay.js.map +1 -1
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.js.map +1 -1
- package/dist/network-family.js.map +1 -1
- package/dist/operator-control-paths.js +3 -4
- package/dist/operator-control-paths.js.map +1 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +68 -68
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/outbox-open-error.js.map +1 -1
- package/dist/pane-context-cache.js.map +1 -1
- package/dist/paths.js.map +1 -1
- package/dist/private-dir.js.map +1 -1
- package/dist/probe-worker-pool.js.map +1 -1
- package/dist/profile-control.js.map +1 -1
- package/dist/prompt-file-ref.js.map +1 -1
- package/dist/provider-probe.js.map +1 -1
- package/dist/provider-secret-registry.js.map +1 -1
- package/dist/public-link-auto-open.d.ts +32 -0
- package/dist/public-link-auto-open.js +86 -0
- package/dist/public-link-auto-open.js.map +1 -0
- package/dist/public-link-owner.d.ts +10 -0
- package/dist/public-link-owner.js +36 -0
- package/dist/public-link-owner.js.map +1 -0
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.js.map +1 -1
- package/dist/public-web-gateway.js +4 -2
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +35 -1
- package/dist/public-web-link.js +142 -43
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +5 -0
- package/dist/quickstart.js +25 -16
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/reply-dedup.js.map +1 -1
- package/dist/resource-report.js.map +1 -1
- package/dist/restart-guard.js.map +1 -1
- package/dist/restart-progress.js.map +1 -1
- package/dist/rollout-index.js.map +1 -1
- package/dist/runtime-cpu-profile.js.map +1 -1
- package/dist/scheduler/db.js.map +1 -1
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/task-board-text.js +16 -11
- package/dist/scheduler/task-board-text.js.map +1 -1
- package/dist/scheduler/task-board.d.ts +50 -10
- package/dist/scheduler/task-board.js +138 -44
- package/dist/scheduler/task-board.js.map +1 -1
- package/dist/scheduler/types.d.ts +1 -1
- package/dist/sd-notify.js.map +1 -1
- package/dist/search/search-index.js.map +1 -1
- package/dist/secret-file.js.map +1 -1
- package/dist/secret-store.js.map +1 -1
- package/dist/service-installer.d.ts +7 -1
- package/dist/service-installer.js +13 -1
- package/dist/service-installer.js.map +1 -1
- package/dist/service-plan.d.ts +33 -4
- package/dist/service-plan.js +177 -8
- package/dist/service-plan.js.map +1 -1
- package/dist/session-oom.d.ts +21 -0
- package/dist/session-oom.js +139 -0
- package/dist/session-oom.js.map +1 -0
- package/dist/settings-api.js +6 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-baseline.js.map +1 -1
- package/dist/settings-change.d.ts +5 -0
- package/dist/settings-change.js +71 -1
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-confirmation.d.ts +4 -0
- package/dist/settings-confirmation.js +2 -0
- package/dist/settings-confirmation.js.map +1 -1
- package/dist/settings-control.js.map +1 -1
- package/dist/settings-effect.js +6 -1
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +1 -0
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/settings-instance-patch.js +13 -1
- package/dist/settings-instance-patch.js.map +1 -1
- package/dist/settings-transaction.js.map +1 -1
- package/dist/setup-auth.js.map +1 -1
- package/dist/setup-host.d.ts +5 -1
- package/dist/setup-host.js +8 -1
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +5 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/spawn-gate.d.ts +4 -0
- package/dist/spawn-gate.js +15 -5
- package/dist/spawn-gate.js.map +1 -1
- package/dist/startup-memory-deadline.d.ts +23 -0
- package/dist/startup-memory-deadline.js +55 -0
- package/dist/startup-memory-deadline.js.map +1 -0
- package/dist/statusline-watcher.js.map +1 -1
- package/dist/storm-window.js.map +1 -1
- package/dist/systemd-peer-bus.js.map +1 -1
- package/dist/target-queue-worker.js.map +1 -1
- package/dist/task-board-summary.d.ts +4 -4
- package/dist/task-board-summary.js +4 -4
- package/dist/task-board-summary.js.map +1 -1
- package/dist/task-board-tool.js +3 -2
- package/dist/task-board-tool.js.map +1 -1
- package/dist/task-board-waker.js +8 -4
- package/dist/task-board-waker.js.map +1 -1
- package/dist/tips.js.map +1 -1
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-exec-helper.js.map +1 -1
- package/dist/tmux-manager.d.ts +1 -0
- package/dist/tmux-manager.js +2 -1
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.js +1 -1
- package/dist/tmux-read.js.map +1 -1
- package/dist/tool-permissions.js.map +1 -1
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-archiver.js.map +1 -1
- package/dist/topic-commands.js.map +1 -1
- package/dist/topic-link.js.map +1 -1
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/lease.js +3 -2
- package/dist/tunnel/lease.js.map +1 -1
- package/dist/tunnel/manager.js.map +1 -1
- package/dist/tunnel/public-fetch.js.map +1 -1
- package/dist/tunnel/purpose-lane.js.map +1 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/types.d.ts +26 -2
- package/dist/ui/chat-render.js +1 -15
- package/dist/ui/chat-store.js +171 -61
- package/dist/ui/chat-strings.js +7 -2
- package/dist/ui/chat-thread.js +48 -1
- package/dist/ui/fleet-canvas-layout.js +3 -0
- package/dist/ui/fleet-canvas.js +23 -20
- package/dist/ui/fleet-office-editor.js +149 -0
- package/dist/ui/fleet-office-model.js +358 -0
- package/dist/ui/fleet-office-pack.js +199 -0
- package/dist/ui/fleet-office-skin.js +474 -0
- package/dist/ui/fleet-office-store.js +49 -0
- package/dist/ui/fleet-office-zones.js +177 -0
- package/dist/ui/fleet-office.js +434 -0
- package/dist/ui/fleet-org.js +18 -5
- package/dist/ui/guest-refused.html +41 -0
- package/dist/ui/image-lightbox.js +9 -3
- package/dist/ui/panel-chat.js +14 -13
- package/dist/ui/panel-settings.js +30 -12
- package/dist/ui/settings-dialogs.js +15 -6
- package/dist/ui/settings-model.js +40 -3
- package/dist/ui/settings-strings.js +38 -0
- package/dist/ui/settings-web-authority.js +7 -2
- package/dist/ui/shared/agend-auth.js +2 -0
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-prefs.js +2 -0
- package/dist/ui/shared/app-shell.js +12 -0
- package/dist/ui/shared/app-store.js +1 -0
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +132 -3
- package/dist/ui/shared/guest-refused.js +35 -0
- package/dist/ui/shared/header-tools.js +2 -2
- package/dist/ui/shared/instance-nav.js +25 -8
- package/dist/ui/shared/ui-dialog.js +2 -2
- package/dist/ui/shared/usage-dialog.js +152 -6
- package/dist/ui/shared/view-strings.js +20 -0
- package/dist/ui/shared/web-authorize.js +3 -1
- package/dist/update-check.js.map +1 -1
- package/dist/update-dispatch.js.map +1 -1
- package/dist/update-install.js.map +1 -1
- package/dist/update-launch.js.map +1 -1
- package/dist/update-marker.js.map +1 -1
- package/dist/update-preflight.js.map +1 -1
- package/dist/usage/format-rich.js.map +1 -1
- package/dist/usage/providers.d.ts +8 -0
- package/dist/usage/providers.js +6 -2
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/usage/usage-api.js +2 -1
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/usage-report.js.map +1 -1
- package/dist/usage-rollup.js +1 -1
- package/dist/usage-rollup.js.map +1 -1
- package/dist/user-systemd.js.map +1 -1
- package/dist/view-api.js +1 -1
- package/dist/view-api.js.map +1 -1
- package/dist/wake-coordinator.js.map +1 -1
- package/dist/web-ambient.js.map +1 -1
- package/dist/web-api.d.ts +15 -2
- package/dist/web-api.js +174 -25
- package/dist/web-api.js.map +1 -1
- package/dist/web-app-icon.js.map +1 -1
- package/dist/web-appearance-policy.js.map +1 -1
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-auth.js +5 -4
- package/dist/web-auth.js.map +1 -1
- package/dist/web-backend-login.js.map +1 -1
- package/dist/web-card.js.map +1 -1
- package/dist/web-channel-echo.js.map +1 -1
- package/dist/web-chat-history.d.ts +41 -11
- package/dist/web-chat-history.js +93 -22
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.js.map +1 -1
- package/dist/web-effect-bundle.d.ts +2 -2
- package/dist/web-effect-library.js.map +1 -1
- package/dist/web-effect-playback.js.map +1 -1
- package/dist/web-effect-profiles.d.ts +16 -16
- package/dist/web-effect-profiles.js +3 -2
- package/dist/web-effect-profiles.js.map +1 -1
- package/dist/web-effect-service.js.map +1 -1
- package/dist/web-effect-tools.js.map +1 -1
- package/dist/web-effect-trials.js.map +1 -1
- package/dist/web-effect-upload.js.map +1 -1
- package/dist/web-effect.d.ts +7 -7
- package/dist/web-effect.js.map +1 -1
- package/dist/web-experimental.js.map +1 -1
- package/dist/web-guest-access.js +1 -1
- package/dist/web-guest-access.js.map +1 -1
- package/dist/web-guest-rooms.js.map +1 -1
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-icons.js.map +1 -1
- package/dist/web-identity-color.js.map +1 -1
- package/dist/web-images.js.map +1 -1
- package/dist/web-libavoid.js.map +1 -1
- package/dist/web-login.js.map +1 -1
- package/dist/web-mentions.js.map +1 -1
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-office-store.d.ts +104 -0
- package/dist/web-office-store.js +236 -0
- package/dist/web-office-store.js.map +1 -0
- package/dist/web-owner-css-store.js.map +1 -1
- package/dist/web-owner-css.js.map +1 -1
- package/dist/web-png.js.map +1 -1
- package/dist/web-prefs.js +2 -0
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-profile.js.map +1 -1
- package/dist/web-reactions.d.ts +2 -1
- package/dist/web-request-context.d.ts +5 -0
- package/dist/web-request-context.js.map +1 -1
- package/dist/web-returning-device.js +1 -1
- package/dist/web-returning-device.js.map +1 -1
- package/dist/web-schedules.js.map +1 -1
- package/dist/web-send-dedupe.d.ts +36 -0
- package/dist/web-send-dedupe.js +69 -0
- package/dist/web-send-dedupe.js.map +1 -0
- package/dist/web-session-authorization.js.map +1 -1
- package/dist/web-session.js +1 -1
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +1 -0
- package/dist/web-settings-patch.js +6 -4
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.js.map +1 -1
- package/dist/web-terminal-http.js.map +1 -1
- package/dist/web-terminal.js.map +1 -1
- package/dist/web-theme-store.js.map +1 -1
- package/dist/web-upload.d.ts +5 -1
- package/dist/web-upload.js +6 -4
- package/dist/web-upload.js.map +1 -1
- package/dist/web-widget.js.map +1 -1
- package/dist/webhook-emitter.js.map +1 -1
- package/dist/windows-shortcuts.js +1 -1
- package/dist/windows-shortcuts.js.map +1 -1
- package/dist/workflow-templates/default.md +1 -1
- package/dist/ws-server.js.map +1 -1
- package/dist/yaml-shared-nodes.js.map +1 -1
- package/launcher/agend-install.cjs +281 -6
- package/launcher/private-home.cjs +43 -0
- package/package.json +3 -2
- package/templates/windows-shortcuts.ps1 +1 -1
package/dist/web-api.js
CHANGED
|
@@ -53,6 +53,7 @@ import { resolveTz } from "./tz-utils.js";
|
|
|
53
53
|
import { filesPlayInChat } from "./web-effect.js";
|
|
54
54
|
import { QUERY_MAX_CHARS, RESULT_MAX, SEARCH_SOURCES } from "./search/search-store.js";
|
|
55
55
|
import { xmsgShown } from "./search/search-docs.js";
|
|
56
|
+
import { WebSendDedupe } from "./web-send-dedupe.js";
|
|
56
57
|
// ── Strict public-facing schemas ────────────────────────────────────────────
|
|
57
58
|
// web-api endpoints must reject unknown fields so the dashboard cannot inject
|
|
58
59
|
// internal-only flags that would reach handleCreate/scheduler/config writers.
|
|
@@ -169,6 +170,9 @@ const SendMessageSchema = z.object({
|
|
|
169
170
|
attachments: z.array(z.string().regex(/^[0-9a-f]{32}$/)).max(UPLOAD_LIMITS.maxFiles).optional(),
|
|
170
171
|
/** #2272: the message of this chat this one replies to — its row only; the server reads what it quotes. */
|
|
171
172
|
replyTo: z.object({ boot: z.string().regex(/^[0-9a-f]{1,32}$/), id: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER) }).strict().optional(),
|
|
173
|
+
/** The sending page's own id for its pending bubble: echoed on the live event only (never recorded), so that page
|
|
174
|
+
* swaps its "sending" bubble for the real one even when the stream beats this answer. */
|
|
175
|
+
clientId: z.string().regex(/^[A-Za-z0-9_-]{8,64}$/).optional(),
|
|
172
176
|
}).strict().refine(v => v.message.trim().length > 0 || (v.attachments?.length ?? 0) > 0, { message: "a message or at least one file", path: ["message"] });
|
|
173
177
|
/** An instance name that is safe as one path segment (no separator, no NUL, not a dot name). */
|
|
174
178
|
const safeInstanceSegment = (name) => /^[^\\/\x00]+$/.test(name) && name !== "." && name !== "..";
|
|
@@ -1304,7 +1308,14 @@ function webAuthorityOf(req, url, ctx, instance) {
|
|
|
1304
1308
|
return { authority: noAuthority(0), refusal: "not_session" };
|
|
1305
1309
|
const v = evaluateWebRequest(req, url, ctx.webToken ?? null, ctx.webSessions, { touch: false });
|
|
1306
1310
|
return ctx.webAuthority({ session: v.kind === "allow" && v.via === "session" ? v.session : null, gateway: !!gatewayRequestContext(req),
|
|
1307
|
-
peer:
|
|
1311
|
+
peer: webPeerOf(req), ...(instance !== undefined ? { instance } : {}) });
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* #1939 §3.4 / #2453 §0.2: where this request came from — the admin's named link by the gateway's own provenance (its
|
|
1315
|
+
* exact hostname), else what the TCP peer and Host say (a quick tunnel's request is `lan`, and refused as public_link).
|
|
1316
|
+
*/
|
|
1317
|
+
export function webPeerOf(req) {
|
|
1318
|
+
return gatewayRequestContext(req)?.named === true ? "named_link" : classifyWebPeer(req);
|
|
1308
1319
|
}
|
|
1309
1320
|
/**
|
|
1310
1321
|
* #2122: what let this request in, asked now — its session (by handle, under the token it was signed in with) or the
|
|
@@ -1626,15 +1637,12 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
1626
1637
|
const after = url.searchParams.get("after");
|
|
1627
1638
|
const fresh = after === null || after === ""; // a cursor that is not ours in shape still gets nothing
|
|
1628
1639
|
if (guest) {
|
|
1629
|
-
// #2085 review:
|
|
1640
|
+
// #2085 review: with what became the channel's after its cursor (an older row, its copy sent since), once each —
|
|
1641
|
+
// replayFor counts a row where it was seen (seenSeq), as for every reader (#1774).
|
|
1630
1642
|
json(res, 200, guestPollBody(ctx.getUiStatus(), guest.scope(), Date.now(), guest, after, () => {
|
|
1631
1643
|
if (!history)
|
|
1632
1644
|
return [];
|
|
1633
|
-
|
|
1634
|
-
return history.after(0);
|
|
1635
|
-
const cursor = parseLastEventId(after);
|
|
1636
|
-
const rows = [...history.replayFor(cursor), ...history.seenAfter(cursor)];
|
|
1637
|
-
return rows.filter((m, i) => rows.findIndex((x) => x.boot === m.boot && x.id === m.id) === i);
|
|
1645
|
+
return fresh ? history.after(0) : history.replayFor(parseLastEventId(after));
|
|
1638
1646
|
}));
|
|
1639
1647
|
return true;
|
|
1640
1648
|
}
|
|
@@ -1972,6 +1980,100 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
1972
1980
|
});
|
|
1973
1981
|
return true;
|
|
1974
1982
|
}
|
|
1983
|
+
// ── Fleet → Org chart → Office: the offices people made (web-offices.json, web-office-store.ts) ──
|
|
1984
|
+
// GET /ui/offices: { rev, offices, readable } for whoever may open the org chart (the public link too: it reads the
|
|
1985
|
+
// chart; a guest's routes do not include it). PUT /ui/offices { offices, rev? }: a person's change, as a theme's reset
|
|
1986
|
+
// or lock is — an admin session only, never the CLI token, never the public link (absent from its manifest, and
|
|
1987
|
+
// refused here too); asked again once the body is in. Every page hears `offices` on the stream and reads again.
|
|
1988
|
+
if (method === "GET" && path === "/ui/offices") {
|
|
1989
|
+
const store = ctx.officeStore;
|
|
1990
|
+
if (!store) {
|
|
1991
|
+
json(res, 404, { error: "Offices are not kept on this fleet" });
|
|
1992
|
+
return true;
|
|
1993
|
+
}
|
|
1994
|
+
json(res, 200, store.list());
|
|
1995
|
+
return true;
|
|
1996
|
+
}
|
|
1997
|
+
if (method === "PUT" && path === "/ui/offices") {
|
|
1998
|
+
const store = ctx.officeStore;
|
|
1999
|
+
if (!store) {
|
|
2000
|
+
json(res, 404, { error: "Offices are not kept on this fleet" });
|
|
2001
|
+
return true;
|
|
2002
|
+
}
|
|
2003
|
+
if (gatewayRequestContext(req)) {
|
|
2004
|
+
json(res, 403, { error: "offices_not_over_public_link" });
|
|
2005
|
+
return true;
|
|
2006
|
+
}
|
|
2007
|
+
// The person (an admin session's account), or the status that refuses the request.
|
|
2008
|
+
const who = () => {
|
|
2009
|
+
const verdict = evaluateWebRequest(req, url, ctx.webToken ?? null, ctx.webSessions, { touch: false });
|
|
2010
|
+
if (verdict.kind !== "allow")
|
|
2011
|
+
return { status: 401 };
|
|
2012
|
+
return verdict.via === "session" && verdict.session?.tier === "admin" ? { person: verdict.session.owner ? webPersonKey(verdict.session.owner) : "admin-session" } : { status: 403 };
|
|
2013
|
+
};
|
|
2014
|
+
const refuse = (status) => json(res, status, { error: status === 401 ? WEB_TOKEN_INVALID_MESSAGE : "offices_need_an_admin_session" });
|
|
2015
|
+
const first = who();
|
|
2016
|
+
if ("status" in first) {
|
|
2017
|
+
refuse(first.status);
|
|
2018
|
+
return true;
|
|
2019
|
+
}
|
|
2020
|
+
(async () => {
|
|
2021
|
+
let body;
|
|
2022
|
+
try {
|
|
2023
|
+
body = await parseBody(req);
|
|
2024
|
+
}
|
|
2025
|
+
catch {
|
|
2026
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
if (!permitWebContinuation(req, res, ctx))
|
|
2030
|
+
return;
|
|
2031
|
+
const now = who();
|
|
2032
|
+
if ("status" in now) {
|
|
2033
|
+
refuse(now.status);
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
2037
|
+
json(res, 400, { error: "offices required" });
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
const extra = Object.keys(body).find(k => k !== "offices" && k !== "rev");
|
|
2041
|
+
if (extra !== undefined) {
|
|
2042
|
+
json(res, 400, { error: "offices_invalid", detail: `unknown key "${extra}"` });
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
// Office review: the rev the page read is required — a save without one would overwrite another person's unseen.
|
|
2046
|
+
if (body.rev === undefined) {
|
|
2047
|
+
json(res, 428, { error: "offices_rev_required", detail: "send the rev you read (GET /ui/offices)" });
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
if (!Number.isSafeInteger(body.rev)) {
|
|
2051
|
+
json(res, 400, { error: "offices_invalid", detail: "rev: an integer" });
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
const limiter = ctx.officeWrites, allowed = limiter ? limiter.check(now.person) : { ok: true };
|
|
2055
|
+
if (!allowed.ok) {
|
|
2056
|
+
res.setHeader("Retry-After", String(allowed.retry_after_s));
|
|
2057
|
+
json(res, 429, { error: "offices_rate_limited", retry_after_s: allowed.retry_after_s });
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
const r = store.replace(body.offices, body.rev);
|
|
2061
|
+
if (!r.ok) {
|
|
2062
|
+
json(res, r.status, { error: r.error, ...(r.detail ? { detail: r.detail } : {}) });
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
limiter?.record(now.person);
|
|
2066
|
+
ctx.emitSseEvent("offices", { rev: r.rev });
|
|
2067
|
+
json(res, 200, { rev: r.rev, offices: r.offices });
|
|
2068
|
+
})().catch(err => {
|
|
2069
|
+
ctx.logger.error({ err: err.message }, "Saving offices failed");
|
|
2070
|
+
try {
|
|
2071
|
+
json(res, 500, { error: "Saving offices failed" });
|
|
2072
|
+
}
|
|
2073
|
+
catch { /* already answered */ }
|
|
2074
|
+
});
|
|
2075
|
+
return true;
|
|
2076
|
+
}
|
|
1975
2077
|
// ── #1630: avatars and Discord server emoji, same-origin (the page never loads a platform's CDN; img-src stays 'self') ──
|
|
1976
2078
|
const imageMatch = path.match(/^\/ui\/img\/(bot|avatar|emoji)\/([^/]+)$/);
|
|
1977
2079
|
if (method === "GET" && imageMatch) {
|
|
@@ -2373,7 +2475,7 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
2373
2475
|
if (path === "/ui/authorization" && (method === "GET" || method === "POST")) {
|
|
2374
2476
|
const judge = () => {
|
|
2375
2477
|
const v = evaluateWebRequest(req, url, ctx.webToken ?? null, ctx.webSessions, { touch: false });
|
|
2376
|
-
return { session: v.kind === "allow" && v.via === "session" ? v.session : null, gateway: !!gatewayRequestContext(req), peer:
|
|
2478
|
+
return { session: v.kind === "allow" && v.via === "session" ? v.session : null, gateway: !!gatewayRequestContext(req), peer: webPeerOf(req) };
|
|
2377
2479
|
};
|
|
2378
2480
|
res.setHeader("Cache-Control", "no-store");
|
|
2379
2481
|
if (method === "GET") {
|
|
@@ -3435,6 +3537,14 @@ function webDeliveryFailure(err) {
|
|
|
3435
3537
|
return { reason: "delivery_unavailable", error: "The instance could not receive this message. Check its status on the host, then retry." };
|
|
3436
3538
|
}
|
|
3437
3539
|
/** Handle POST /ui/send — extracted for readability. */
|
|
3540
|
+
/** Each web server's own memory of the sends it accepted by clientId (web-send-dedupe.ts). */
|
|
3541
|
+
const webSendDedupes = new WeakMap();
|
|
3542
|
+
function webSendDedupeOf(ctx) {
|
|
3543
|
+
let d = webSendDedupes.get(ctx);
|
|
3544
|
+
if (!d)
|
|
3545
|
+
webSendDedupes.set(ctx, d = new WebSendDedupe());
|
|
3546
|
+
return d;
|
|
3547
|
+
}
|
|
3438
3548
|
function handleSendMessage(req, res, ctx) {
|
|
3439
3549
|
const arrival = webAuthorityOf(req, new URL(req.url ?? "/", "http://localhost"), ctx); // #1939 §3: again after the body
|
|
3440
3550
|
let body = "";
|
|
@@ -3451,6 +3561,9 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3451
3561
|
req.on("end", async () => {
|
|
3452
3562
|
if (!permitWebContinuation(req, res, ctx))
|
|
3453
3563
|
return;
|
|
3564
|
+
// A claimed clientId is settled however this ends: with the messageId once accepted, else forgotten.
|
|
3565
|
+
let claim;
|
|
3566
|
+
let acceptedId = null;
|
|
3454
3567
|
try {
|
|
3455
3568
|
const raw = JSON.parse(body);
|
|
3456
3569
|
const parsed = SendMessageSchema.safeParse(raw);
|
|
@@ -3459,7 +3572,7 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3459
3572
|
json(res, 400, { error: `${issue.path.join(".") || "body"}: ${issue.message}` });
|
|
3460
3573
|
return;
|
|
3461
3574
|
}
|
|
3462
|
-
const { instance, message: typed, attachments = [], replyTo } = parsed.data;
|
|
3575
|
+
const { instance, message: typed, attachments = [], replyTo, clientId } = parsed.data;
|
|
3463
3576
|
// #1920 step 2b: a guest writes only to an in-scope instance, while guests may chat, and never a command.
|
|
3464
3577
|
const guest = guestRequestOf(req);
|
|
3465
3578
|
const refused = guestChatRefusal(guest, instance);
|
|
@@ -3486,6 +3599,33 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3486
3599
|
json(res, 404, { error: `Instance not found: ${instance}` });
|
|
3487
3600
|
return;
|
|
3488
3601
|
}
|
|
3602
|
+
// #1827: who is writing — the name agents and chats see, and the id they tell people apart by. Asked now that the
|
|
3603
|
+
// body is in: a session revoked meanwhile sends nothing (not as the operator).
|
|
3604
|
+
const person = webPersonOf(req, new URL(req.url ?? "/", "http://localhost"), ctx);
|
|
3605
|
+
if (!person) {
|
|
3606
|
+
json(res, 401, { error: WEB_TOKEN_INVALID_MESSAGE });
|
|
3607
|
+
return;
|
|
3608
|
+
}
|
|
3609
|
+
// #2438 follow-up: the page's Retry of a send that already went (its answer and the live copy lost) is that send:
|
|
3610
|
+
// answered with its messageId, delivered once. One still going is waited for; one that failed is sent anew.
|
|
3611
|
+
if (clientId) {
|
|
3612
|
+
const key = WebSendDedupe.key(person.key, instance, clientId);
|
|
3613
|
+
for (;;) {
|
|
3614
|
+
const seen = webSendDedupeOf(ctx).claim(key);
|
|
3615
|
+
if (seen.kind === "going") {
|
|
3616
|
+
await seen.settled;
|
|
3617
|
+
if (res.destroyed)
|
|
3618
|
+
return;
|
|
3619
|
+
continue;
|
|
3620
|
+
}
|
|
3621
|
+
if (seen.kind === "accepted") {
|
|
3622
|
+
json(res, 200, { sent: true, messageId: seen.messageId, repeated: true });
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3625
|
+
claim = seen;
|
|
3626
|
+
break;
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3489
3629
|
let files = [];
|
|
3490
3630
|
if (attachments.length > 0) {
|
|
3491
3631
|
const taken = ctx.webFiles?.takeForMessage(instance, attachments);
|
|
@@ -3512,14 +3652,6 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3512
3652
|
&& (!guest || (guest.current() && !guestChatRefusal(guest, instance)))
|
|
3513
3653
|
&& (registered ? registeredChatInstance(ctx, instance) : ctx.instanceIpcClients.has(instance));
|
|
3514
3654
|
const message = typed;
|
|
3515
|
-
// #1827: who is writing — the name agents and chats see, and the id they tell people apart by. Asked now that the
|
|
3516
|
-
// body is in: a session revoked meanwhile sends nothing (not as the operator).
|
|
3517
|
-
const person = webPersonOf(req, new URL(req.url ?? "/", "http://localhost"), ctx);
|
|
3518
|
-
if (!person) {
|
|
3519
|
-
ctx.webFiles?.release(files);
|
|
3520
|
-
json(res, 401, { error: WEB_TOKEN_INVALID_MESSAGE });
|
|
3521
|
-
return;
|
|
3522
|
-
}
|
|
3523
3655
|
// #1939 S1: what the receipt records about who authorized it (observe only: agents still read web as recognition).
|
|
3524
3656
|
const { authority } = confirmAfterBody(arrival, webAuthorityOf(req, new URL(req.url ?? "/", "http://localhost"), ctx, instance));
|
|
3525
3657
|
const ts = new Date().toISOString();
|
|
@@ -3537,13 +3669,18 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3537
3669
|
// Add no runtime credentials or local attachment paths to the display copy. A reply opens with the line it
|
|
3538
3670
|
// answers (#2272) — quoted only if that message reached the channel; a web-only one is named, never copied out.
|
|
3539
3671
|
const quoteLine = quoted ? webReplyEchoLine(quoted) : "";
|
|
3540
|
-
const
|
|
3672
|
+
const plainPreview = webEchoPreview(message, files.map(f => f.name));
|
|
3673
|
+
const preview = (quoteLine ? `${quoteLine}\n` : "") + plainPreview;
|
|
3674
|
+
// #2281: where the quoted message is on a platform. An echo to the same place is a native reply to it there, and
|
|
3675
|
+
// then needs no quoted line; anywhere else (or a message with no known post) keeps the line, as before.
|
|
3676
|
+
const quotedPosts = quoted ? ctx.webChatHistory?.postsOf(quoted) ?? [] : [];
|
|
3677
|
+
const echoText = (p) => formatWebChannelEcho(person.name, p, t("web.echo_full_text"), { guest: !!guest });
|
|
3541
3678
|
// ClassicBot echo (#1320 part B): same ordering lane, each room's setting (#2054: on unless it says false, or
|
|
3542
3679
|
// the fleet-wide switch is off for a room that is unset) resolved at send time inside sendClassicWebEcho.
|
|
3543
3680
|
const canClassicEcho = ctx.isClassicInstance?.(instance) === true && !!ctx.sendClassicWebEcho;
|
|
3544
3681
|
const settleClassicEcho = canClassicEcho ? ctx.reserveWebChannelEcho?.(instance, async () => {
|
|
3545
3682
|
const copies = [];
|
|
3546
|
-
const posted = await ctx.sendClassicWebEcho(instance,
|
|
3683
|
+
const posted = await ctx.sendClassicWebEcho(instance, echoText(preview), person.name, copies, quotedPosts.length ? { posts: quotedPosts, text: echoText(plainPreview) } : undefined);
|
|
3547
3684
|
if (posted > 0)
|
|
3548
3685
|
ctx.webMessageInChannel?.(instance, messageId, copies); // #1920 §5.2: the room saw it (#2305: its copies)
|
|
3549
3686
|
}) : undefined;
|
|
@@ -3553,12 +3690,13 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3553
3690
|
|| (ctx.getGroupIdForInstance?.(instance) ?? String(ctx.fleetConfig?.channel?.group_id ?? "")) !== groupId
|
|
3554
3691
|
|| ctx.fleetConfig?.instances[instance]?.topic_id !== topicId)
|
|
3555
3692
|
return;
|
|
3556
|
-
const
|
|
3693
|
+
const native = quotedPosts.find(p => p.adapterId === syncAdapter.id && p.chatId === String(groupId) && (p.threadId ?? "") === String(topicId));
|
|
3694
|
+
const sent = await syncAdapter.sendText(String(groupId), echoText(native ? plainPreview : preview), { threadId: String(topicId), format: "text", allowedMentions: { parse: [] }, ...(native ? { replyTo: native.messageId } : {}) });
|
|
3557
3695
|
const copyId = sent && typeof sent === "object" && typeof sent.messageId === "string" ? sent.messageId : "";
|
|
3558
|
-
// #1920 §5.2: the topic saw it. #2305: and where it is, for a reaction to the web message.
|
|
3696
|
+
// #1920 §5.2: the topic saw it. #2305: and where it is, for a reaction to the web message (#2281: and a reply).
|
|
3559
3697
|
const reacting = syncAdapter;
|
|
3560
|
-
ctx.webMessageInChannel?.(instance, messageId, copyId
|
|
3561
|
-
? [{ adapter: reacting, chatId: String(groupId), threadId: String(topicId), messageId: copyId }] : []);
|
|
3698
|
+
ctx.webMessageInChannel?.(instance, messageId, copyId
|
|
3699
|
+
? [{ adapter: reacting, adapterId: syncAdapter.id, chatId: String(groupId), threadId: String(topicId), messageId: copyId }] : []);
|
|
3562
3700
|
return sent;
|
|
3563
3701
|
}) : undefined;
|
|
3564
3702
|
let delivered;
|
|
@@ -3604,6 +3742,9 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3604
3742
|
json(res, 503, { reason: "delivery_changed", error: "The session or instance changed before this message was sent. Sign in again if needed, then retry." });
|
|
3605
3743
|
return;
|
|
3606
3744
|
}
|
|
3745
|
+
// The agent has it: from here on a repeat of this clientId is this send, whatever fails after (the echo, the
|
|
3746
|
+
// stream's copy) — a Retry then gets this messageId, never a second delivery.
|
|
3747
|
+
acceptedId = messageId;
|
|
3607
3748
|
settleEcho?.(true);
|
|
3608
3749
|
settleClassicEcho?.(true);
|
|
3609
3750
|
// #1666: the same cancel/progress bubble a platform inbound starts, in the topic the echo went to — a second turn
|
|
@@ -3618,11 +3759,19 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3618
3759
|
ctx.eventLog?.logActivity("message", person.name, (message || `[${files.length} file(s)]`).slice(0, 200), instance);
|
|
3619
3760
|
// #1920: the account that wrote it (a guest sees its own); channel-visible once its copy was sent (above).
|
|
3620
3761
|
ctx.emitSseEvent("message", { instance, sender: person.name, role: "user", text: message, ts, attachments: files.map(publicAttachment), messageId, ...(person.bound ? { from: person.voterKey } : {}),
|
|
3621
|
-
...(quoted ? { replyTo: webChatReplyRef(quoted) } : {}) });
|
|
3762
|
+
...(quoted ? { replyTo: webChatReplyRef(quoted) } : {}), ...(clientId ? { clientId } : {}) });
|
|
3622
3763
|
json(res, 200, { sent: true, messageId });
|
|
3623
3764
|
}
|
|
3624
3765
|
catch {
|
|
3625
|
-
|
|
3766
|
+
// A failure after the agent has it is no failed send: the page is told it went (a Retry would only find it).
|
|
3767
|
+
if (res.headersSent) { /* answered already: the failure was in or after the answer */ }
|
|
3768
|
+
else if (acceptedId)
|
|
3769
|
+
json(res, 200, { sent: true, messageId: acceptedId });
|
|
3770
|
+
else
|
|
3771
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
3772
|
+
}
|
|
3773
|
+
finally {
|
|
3774
|
+
claim?.settle(acceptedId);
|
|
3626
3775
|
}
|
|
3627
3776
|
});
|
|
3628
3777
|
}
|