@songsid/agend 2.3.0-beta.22 → 2.3.0-beta.24
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 +2 -2
- package/README.zh-TW.md +2 -2
- package/dist/agent-cli.js +4 -2
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.d.ts +1 -0
- package/dist/agent-endpoint.js +4 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.d.ts +5 -0
- package/dist/agent-whats-new.js +16 -1
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +7 -0
- package/dist/auth-api.js +54 -21
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/codex-package.d.ts +30 -0
- package/dist/backend/codex-package.js +133 -0
- package/dist/backend/codex-package.js.map +1 -0
- package/dist/backend/codex.d.ts +2 -0
- package/dist/backend/codex.js +9 -2
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/grok.js +3 -1
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +9 -1
- package/dist/backend/kiro.js +102 -1
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/opencode-permission.js +1 -1
- package/dist/backend/opencode-permission.js.map +1 -1
- package/dist/backend/opencode.js +2 -1
- package/dist/backend/opencode.js.map +1 -1
- package/dist/binary-probe.js +1 -1
- package/dist/bug-report-cli.js +11 -10
- package/dist/bug-report-cli.js.map +1 -1
- package/dist/bug-report.js +11 -10
- package/dist/bug-report.js.map +1 -1
- package/dist/bundled-skills.d.ts +7 -3
- package/dist/bundled-skills.js +16 -7
- package/dist/bundled-skills.js.map +1 -1
- package/dist/channel/adapters/discord.js +3 -3
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-server.js +12 -4
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +3 -1
- package/dist/channel/mcp-tools.js +36 -0
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/child-process.d.ts +21 -0
- package/dist/child-process.js +54 -0
- package/dist/child-process.js.map +1 -0
- package/dist/cli.js +188 -204
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +1 -1
- package/dist/command-table.js +1 -1
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +20 -1
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +3 -1
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +8 -0
- package/dist/daemon.js +31 -8
- package/dist/daemon.js.map +1 -1
- package/dist/darwin-memory.js +1 -1
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.js +42 -0
- package/dist/doctor.js.map +1 -1
- package/dist/export-import.js +4 -3
- package/dist/export-import.js.map +1 -1
- package/dist/fleet-level-config.d.ts +1 -1
- package/dist/fleet-level-config.js +2 -0
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +45 -0
- package/dist/fleet-manager.js +324 -36
- package/dist/fleet-manager.js.map +1 -1
- package/dist/full-restart.d.ts +5 -5
- package/dist/full-restart.js +5 -5
- package/dist/full-restart.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +48 -10
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -1
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +7 -8
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +319 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +529 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +17 -4
- package/dist/general-knowledge/steering/core-rules.md +1 -1
- package/dist/instance-config-impact.js +10 -3
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.js +4 -4
- package/dist/locale.js +296 -4
- package/dist/locale.js.map +1 -1
- package/dist/login-flows.js +1 -1
- package/dist/login-shell.js +3 -1
- package/dist/login-shell.js.map +1 -1
- package/dist/ls-rows.js +1 -1
- package/dist/needs-you-hub.d.ts +98 -1
- package/dist/needs-you-hub.js +313 -44
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.d.ts +5 -2
- package/dist/needs-you-live.js +6 -3
- package/dist/needs-you-live.js.map +1 -1
- package/dist/needs-you.d.ts +58 -3
- package/dist/needs-you.js +36 -3
- package/dist/needs-you.js.map +1 -1
- package/dist/outbound-schemas.d.ts +3 -3
- package/dist/public-link-progress.d.ts +12 -1
- package/dist/public-link-progress.js +5 -10
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-tunnel-config.d.ts +5 -0
- package/dist/public-tunnel-config.js +45 -1
- package/dist/public-tunnel-config.js.map +1 -1
- package/dist/public-web-link.js +6 -5
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart.js +15 -9
- package/dist/quickstart.js.map +1 -1
- package/dist/resource-report.js +1 -1
- package/dist/scheduler/task-board.d.ts +41 -1
- package/dist/scheduler/task-board.js +152 -5
- package/dist/scheduler/task-board.js.map +1 -1
- package/dist/sd-notify.js +2 -1
- package/dist/sd-notify.js.map +1 -1
- package/dist/service-installer.d.ts +69 -0
- package/dist/service-installer.js +182 -0
- package/dist/service-installer.js.map +1 -1
- package/dist/service-owner.d.ts +44 -0
- package/dist/service-owner.js +48 -0
- package/dist/service-owner.js.map +1 -0
- package/dist/service-plan.d.ts +8 -0
- package/dist/service-plan.js +13 -0
- package/dist/service-plan.js.map +1 -1
- package/dist/settings-api.d.ts +4 -0
- package/dist/settings-api.js +21 -3
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +88 -4
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-confirmation.d.ts +2 -0
- package/dist/settings-confirmation.js +14 -8
- package/dist/settings-confirmation.js.map +1 -1
- package/dist/settings-control.js +2 -1
- package/dist/settings-control.js.map +1 -1
- package/dist/settings-effect.js +2 -0
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.d.ts +2 -0
- package/dist/settings-http-confirmation.js +3 -2
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/settings-request-capability.d.ts +4 -1
- package/dist/settings-request-capability.js +11 -3
- package/dist/settings-request-capability.js.map +1 -1
- package/dist/setup-auth.d.ts +1 -0
- package/dist/setup-auth.js +2 -1
- package/dist/setup-auth.js.map +1 -1
- package/dist/setup-host.d.ts +1 -0
- package/dist/setup-host.js +7 -3
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +1 -1
- package/dist/setup-wizard.js.map +1 -1
- package/dist/task-board-summary.d.ts +101 -0
- package/dist/task-board-summary.js +177 -0
- package/dist/task-board-summary.js.map +1 -0
- package/dist/task-board-waker.d.ts +8 -1
- package/dist/task-board-waker.js +15 -6
- package/dist/task-board-waker.js.map +1 -1
- package/dist/tmux-control.js +1 -1
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-exec-helper.d.ts +13 -3
- package/dist/tmux-exec-helper.js +34 -29
- package/dist/tmux-exec-helper.js.map +1 -1
- package/dist/tmux-manager.js +1 -1
- package/dist/tmux-read.js +1 -1
- package/dist/tmux-read.js.map +1 -1
- package/dist/topic-commands.d.ts +3 -0
- package/dist/topic-commands.js +29 -18
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-monitor.js +5 -0
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +2 -0
- package/dist/transcript-sources.js +4 -0
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared-install.js +3 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +1 -1
- package/dist/tunnel/cloudflared.js +51 -10
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/named-diagnostics.d.ts +13 -0
- package/dist/tunnel/named-diagnostics.js +52 -0
- package/dist/tunnel/named-diagnostics.js.map +1 -0
- package/dist/tunnel/types.d.ts +2 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/ui/app-effects.js +10 -4
- package/dist/ui/chat-strings.js +3 -0
- package/dist/ui/chat-thread.js +14 -7
- package/dist/ui/effect-trial-offer.js +2 -31
- package/dist/ui/effects-waker.js +2 -70
- package/dist/ui/fleet-task-card.js +5 -5
- package/dist/ui/fleet-task-common.js +43 -9
- package/dist/ui/fleet-task-work.js +107 -0
- package/dist/ui/fleet-tasks.js +46 -12
- package/dist/ui/image-lightbox.js +39 -1
- package/dist/ui/image-zoom.js +242 -0
- package/dist/ui/panel-chat.js +24 -13
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +6 -2
- package/dist/ui/panel-needs.js +21 -6
- package/dist/ui/panel-settings.js +35 -13
- package/dist/ui/preview-panel.js +2 -1
- package/dist/ui/search-dialog.js +2 -1
- package/dist/ui/settings-appearance.js +44 -10
- package/dist/ui/settings-dialogs.js +33 -17
- package/dist/ui/settings-discord.js +6 -2
- package/dist/ui/settings-guests.js +2 -1
- package/dist/ui/settings-model.js +2 -11
- package/dist/ui/settings-mods.js +8 -3
- package/dist/ui/settings-strings.js +32 -0
- package/dist/ui/settings-token.js +3 -1
- package/dist/ui/settings-web-authority.js +6 -3
- package/dist/ui/settings-wizard.js +13 -7
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-needs.js +2 -2
- package/dist/ui/shared/app-store.js +2 -2
- package/dist/ui/shared/app.css +93 -32
- package/dist/ui/shared/app.js +5 -1
- package/dist/ui/shared/effect-app-runtime.js +7 -5
- package/dist/ui/shared/effect-chat-runtime.js +2 -7
- package/dist/ui/shared/effect-cursor.js +3 -2
- package/dist/ui/shared/effect-display-metadata.js +39 -0
- package/dist/ui/shared/effect-registry.js +4 -2
- package/dist/ui/shared/effect-runtime-loader.js +3 -1
- package/dist/ui/shared/effect-stock-trust.js +5 -5
- package/dist/ui/shared/feature-flags.js +127 -0
- package/dist/ui/shared/identity-color.js +6 -3
- package/dist/ui/shared/instance-nav.js +3 -2
- package/dist/ui/shared/panel-view.js +14 -29
- package/dist/ui/shared/text-undo.js +210 -0
- package/dist/ui/shared/ui-dialog.js +4 -3
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/shared/usage-dialog.js +29 -8
- package/dist/ui/shared/view-strings.js +2 -4
- package/dist/ui/web-terminal/terminal.html +10 -10
- package/dist/ui/web-terminal/terminal.js +58 -12
- package/dist/update-dispatch.js +1 -1
- package/dist/update-launch.d.ts +1 -1
- package/dist/update-launch.js +1 -2
- package/dist/update-launch.js.map +1 -1
- package/dist/update-preflight.js +1 -1
- package/dist/usage/providers.js +1 -1
- package/dist/usage/providers.js.map +1 -1
- package/dist/view-api.js +1 -1
- package/dist/web-api.d.ts +6 -0
- package/dist/web-api.js +163 -21
- package/dist/web-api.js.map +1 -1
- package/dist/web-app-icon-worker.js +2 -1
- package/dist/web-app-icon-worker.js.map +1 -1
- package/dist/web-app-icon.d.ts +10 -8
- package/dist/web-app-icon.js +14 -13
- package/dist/web-app-icon.js.map +1 -1
- package/dist/web-appearance-tools.d.ts +1 -0
- package/dist/web-appearance-tools.js +7 -3
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance.d.ts +4 -10
- package/dist/web-appearance.js +4 -3
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-auth.d.ts +8 -4
- package/dist/web-auth.js +28 -17
- package/dist/web-auth.js.map +1 -1
- package/dist/web-chat-history.d.ts +14 -0
- package/dist/web-chat-history.js +26 -1
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-cookie-names.d.ts +5 -0
- package/dist/web-cookie-names.js +12 -0
- package/dist/web-cookie-names.js.map +1 -0
- package/dist/web-experimental-http.d.ts +2 -0
- package/dist/web-experimental-http.js +12 -0
- package/dist/web-experimental-http.js.map +1 -0
- package/dist/web-experimental.d.ts +11 -0
- package/dist/web-experimental.js +49 -0
- package/dist/web-experimental.js.map +1 -0
- package/dist/web-guest-access.d.ts +1 -0
- package/dist/web-guest-access.js +4 -2
- package/dist/web-guest-access.js.map +1 -1
- package/dist/web-mod-store.d.ts +2 -0
- package/dist/web-mod-store.js +10 -0
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-png.d.ts +4 -0
- package/dist/web-png.js +10 -2
- package/dist/web-png.js.map +1 -1
- package/dist/web-preview.d.ts +2 -0
- package/dist/web-preview.js +2 -0
- package/dist/web-preview.js.map +1 -1
- package/dist/web-returning-device.d.ts +4 -2
- package/dist/web-returning-device.js +12 -5
- package/dist/web-returning-device.js.map +1 -1
- package/dist/web-settings-patch.d.ts +2 -1
- package/dist/web-settings-patch.js +5 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.d.ts +1 -0
- package/dist/web-settings-tools.js +14 -10
- package/dist/web-settings-tools.js.map +1 -1
- package/dist/web-terminal.js +1 -1
- package/dist/web-theme-store.d.ts +2 -2
- package/dist/web-theme-store.js +6 -3
- package/dist/web-theme-store.js.map +1 -1
- package/package.json +11 -11
package/dist/fleet-manager.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { PUBLIC_TUNNEL_TOKEN_ENV,
|
|
1
|
+
import { PUBLIC_TUNNEL_TOKEN_ENV, PUBLIC_TUNNEL_TOKEN_HINT, normalizePublicTunnelToken } from "./public-tunnel-config.js";
|
|
2
2
|
import { humanOwnerObjectId, webHumanOrigin } from "./human-delivery-receipts.js";
|
|
3
3
|
import { SettingsBaselines } from "./settings-baseline.js";
|
|
4
|
-
import { SettingsConfirmationStore } from "./settings-confirmation.js";
|
|
4
|
+
import { SettingsConfirmationStore, settingsSourceLabel } from "./settings-confirmation.js";
|
|
5
5
|
import { SettingsHttpConfirmation } from "./settings-http-confirmation.js";
|
|
6
6
|
import { SettingsControlServer } from "./settings-control.js";
|
|
7
7
|
import { SettingsExecution, settingsRevision, noteSettingsWrite, settingsFileResource, assertSettingsLease, trySettingsLease, waitSettingsLease, settingsUndo, undoSettingsPaths } from "./settings-transaction.js";
|
|
@@ -77,10 +77,12 @@ import { routeToolCall } from "./channel/tool-router.js";
|
|
|
77
77
|
import { Scheduler } from "./scheduler/index.js";
|
|
78
78
|
import { boardActivity, boardCardLimit, boardToolAction } from "./task-board-tool.js";
|
|
79
79
|
import { TaskBoardWaker } from "./task-board-waker.js";
|
|
80
|
+
import { TaskBoardError, wrapUpGap } from "./scheduler/task-board.js";
|
|
80
81
|
import { escapeTelegramHtml, scheduleClock, scheduleRetryLabel } from "./scheduler/retry-label.js";
|
|
81
82
|
import { DEFAULT_SCHEDULER_CONFIG } from "./scheduler/index.js";
|
|
82
83
|
import { TopicCommands, saveCommandForBackend, parseSaveFilename, parsePauseWakeCommand, parseCompactCommand, SAVE_FILENAME_RE, resolveInstanceContext, forgetInstanceContext, readStatuslineJson, statuslineModel } from "./topic-commands.js";
|
|
83
84
|
import { DailySummary } from "./daily-summary.js";
|
|
85
|
+
import { TaskBoardSummaryClock, buildSummary, isQuiet, renderSummary, summaryMarkFile, summaryTime } from "./task-board-summary.js";
|
|
84
86
|
import { WebhookEmitter } from "./webhook-emitter.js";
|
|
85
87
|
import { TmuxControlClient } from "./tmux-control.js";
|
|
86
88
|
import { safeHandler } from "./safe-async.js";
|
|
@@ -234,7 +236,7 @@ import { agyLegacyAgentsFile, danglingSymlink, migrateAgyAgentsFile } from "./ba
|
|
|
234
236
|
import { claudeConfigDirFor, claudeProjectKey } from "./backend/claude-code.js";
|
|
235
237
|
import { sharedRolloutIndex } from "./rollout-index.js";
|
|
236
238
|
import { mayUseTool, resolveToolSet, scheduleOpRefusal, toolForIpcType, toolRefusedMessage, } from "./tool-permissions.js";
|
|
237
|
-
import { authorizeSession, decideWebGate, loadOrCreateWebToken, readWebToken } from "./web-auth.js";
|
|
239
|
+
import { authorizeSession, decideWebGate, loadOrCreateWebToken, readWebToken, sessionCookieUpgrade } from "./web-auth.js";
|
|
238
240
|
import { bypassesWebGate, handleAuthRequest, serveSigninPage } from "./auth-api.js";
|
|
239
241
|
import { isWebPageNavigation } from "./web-shell-routes.js";
|
|
240
242
|
import { guestSessionPolicy, localSessionPolicy, tokenEpoch, WebSessionStore } from "./web-session.js";
|
|
@@ -247,6 +249,9 @@ import { decisionListAnswer } from "./decision-list.js";
|
|
|
247
249
|
import { CodeRequestLimiter, ReturningDevices } from "./web-returning-device.js";
|
|
248
250
|
import { allowedHostNames, applyWebSecurityHeaders, hostnameOf, isHostAllowed, LOOPBACK_HOST_NAMES, WEB_HOST_REJECTED_MESSAGE } from "./web-host-guard.js";
|
|
249
251
|
import { createPreviewListener, normalizeOrigin, previewAvailability, previewSettings } from "./web-preview.js";
|
|
252
|
+
import { canvasWorkerEnabled, isCanvasWorkerTool, CANVAS_WORKER_EXPERIMENTAL_MESSAGE } from "./web-experimental.js";
|
|
253
|
+
import { isCanvasWorkerHttpPath } from "./web-experimental-http.js";
|
|
254
|
+
import { bundledSkillContents } from "./bundled-skills.js";
|
|
250
255
|
import { createEffectBootstrap } from "./web-effect-bootstrap.js";
|
|
251
256
|
import { fleetLevelDifferences, fleetLevelSignature, appendedConnections } from "./fleet-level-config.js";
|
|
252
257
|
import { checkSelfRestartAllowance, recordSelfRestartAttempt } from "./self-restart-limit.js";
|
|
@@ -263,6 +268,7 @@ import { mergeBackendOptions, profileKey, profileName, profileOf } from "./class
|
|
|
263
268
|
import { classifyInstanceChange, CLASSIC_HOT_CONFIG_KEYS, HOT_INSTANCE_CONFIG_KEYS, hotConfigUpdate, } from "./instance-config-impact.js";
|
|
264
269
|
import { cleanupWatchVerdict, formatRestartProgressCompletion, RestartProgress, } from "./restart-progress.js";
|
|
265
270
|
import { launchFullRestartHelper } from "./full-restart.js";
|
|
271
|
+
import { fullReloadExitCode } from "./service-owner.js";
|
|
266
272
|
import { SYSTEMD_RESTART_INDETERMINATE_EXIT_CODE } from "./service-restart-selection.js";
|
|
267
273
|
import { collectRedundantInstanceDefaultPaths } from "./fleet-yaml-slim.js";
|
|
268
274
|
import { StormWindow } from "./storm-window.js";
|
|
@@ -673,6 +679,8 @@ export class FleetManager {
|
|
|
673
679
|
costGuard = null;
|
|
674
680
|
statuslineWatcher;
|
|
675
681
|
dailySummary = null;
|
|
682
|
+
/** #2246 step 3: the task board's daily summary for the person (task-board-summary.ts). */
|
|
683
|
+
taskBoardSummary = null;
|
|
676
684
|
dailyTipScheduler = null;
|
|
677
685
|
/** The task board's wakes (step 2): an instance hears when someone else changes a card it owns or handed out. */
|
|
678
686
|
taskBoardWaker = null;
|
|
@@ -849,6 +857,7 @@ export class FleetManager {
|
|
|
849
857
|
effectBootstrap = createEffectBootstrap();
|
|
850
858
|
/** Fixed trusted document coherence, independent of the HTML preview listener; never a resource grant. */
|
|
851
859
|
get effectBoot() { return this.effectBootstrap.boot; }
|
|
860
|
+
canvasWorkerEnabled() { return canvasWorkerEnabled(this.fleetConfig?.web); }
|
|
852
861
|
healthServerDrain = null;
|
|
853
862
|
/** #1306: the preview listener (health_port + 1 by default), and whether it is listening. */
|
|
854
863
|
previewListener = null;
|
|
@@ -1095,6 +1104,7 @@ export class FleetManager {
|
|
|
1095
1104
|
/** #1629 r4 §1: get_web_appearance / preview_web_appearance — the caller's own look, resolved as the page resolves it. */
|
|
1096
1105
|
get appearanceTools() {
|
|
1097
1106
|
return (this.appearanceToolsService ??= new AppearanceTools({
|
|
1107
|
+
canvasWorkerEnabled: () => this.canvasWorkerEnabled(),
|
|
1098
1108
|
frame: (name) => (this.getUiStatus().instances.find(i => i.name === name) ?? { name }),
|
|
1099
1109
|
theme: (name) => {
|
|
1100
1110
|
const r = this.themeStore.get(name);
|
|
@@ -1139,6 +1149,7 @@ export class FleetManager {
|
|
|
1139
1149
|
prefs: this.webPrefs,
|
|
1140
1150
|
ownerCss: this.ownerCss,
|
|
1141
1151
|
effects: this.effectLibrary,
|
|
1152
|
+
canvasWorkerEnabled: () => this.canvasWorkerEnabled(),
|
|
1142
1153
|
context: () => ({ agendVersion: agendVersion(), limits: this.themeStore.limits(), effects: cw1EffectChecker() }),
|
|
1143
1154
|
isKnownInstance: (name) => this.isKnownInstance(name),
|
|
1144
1155
|
prefsIdentity: OPERATOR,
|
|
@@ -1194,6 +1205,7 @@ export class FleetManager {
|
|
|
1194
1205
|
*/
|
|
1195
1206
|
get webSettings() {
|
|
1196
1207
|
return (this.webSettingsToolsService ??= new WebSettingsTools({
|
|
1208
|
+
canvasWorkerEnabled: () => this.canvasWorkerEnabled(),
|
|
1197
1209
|
prefs: this.webPrefs, ownerCss: this.ownerCss, mods: this.mods, effects: this.effectService,
|
|
1198
1210
|
instances: () => [...Object.keys(this.fleetConfig?.instances ?? {}),
|
|
1199
1211
|
...(this.classicChannels?.getAll() ?? []).map((ch) => ch.instanceName).filter((n) => !Object.hasOwn(this.fleetConfig?.instances ?? {}, n))],
|
|
@@ -1311,6 +1323,7 @@ export class FleetManager {
|
|
|
1311
1323
|
return null;
|
|
1312
1324
|
const record = auth.session, sessions = this.webSessions, exposure = gatewayRequestContext(req);
|
|
1313
1325
|
return { id: record.idHash, label: record.label, source: exposure ? "public_link" : "web_session",
|
|
1326
|
+
localAdmin: () => record.tier === "admin" && record.surface === "local" && !exposure,
|
|
1314
1327
|
current: () => current() && sessions === this.webSessions && !!this.webToken
|
|
1315
1328
|
&& sessions.isCurrent(record, tokenEpoch(this.webToken)) && (!exposure || exposure.isCurrent()) };
|
|
1316
1329
|
}, baseline: () => baselines.read(), snapshot: () => baselines.snapshot(),
|
|
@@ -1370,7 +1383,7 @@ export class FleetManager {
|
|
|
1370
1383
|
return null;
|
|
1371
1384
|
const name = this.webPerson({ adapterId: actor.adapter_id, userId: actor.user_id }).name;
|
|
1372
1385
|
return {
|
|
1373
|
-
label:
|
|
1386
|
+
label: t("settings_confirm.web_verified_label", name),
|
|
1374
1387
|
// admin() asks chat.current() too; a General a card could go to must still be there (else the host CLI's).
|
|
1375
1388
|
current: () => !!verified() && chat.admin(actor.user_id) && this.settingsNoticeGenerals(affected, chat.platform).length > 0,
|
|
1376
1389
|
done: view => this.settingsWebConfirmedNotice(view, affected, name, chat.platform),
|
|
@@ -1397,7 +1410,8 @@ export class FleetManager {
|
|
|
1397
1410
|
*/
|
|
1398
1411
|
async settingsWebConfirmedNotice(view, affected, who, platform) {
|
|
1399
1412
|
const outcome = view.outcome ? view.outcome.message : view.state;
|
|
1400
|
-
const text =
|
|
1413
|
+
const text = [t("settings_confirm.web_confirmed", who), t("settings_confirm.source", settingsSourceLabel(view.source)),
|
|
1414
|
+
t("settings_confirm.requester", view.requested_by), ...view.summary, t("settings_confirm.outcome", outcome)].join("\n");
|
|
1401
1415
|
for (const { adapterId, adapter, chatId, topic } of this.settingsNoticeGenerals(affected, platform)) {
|
|
1402
1416
|
try {
|
|
1403
1417
|
await adapter.sendText(chatId, text, { threadId: topic, allowedMentions: { parse: [] } });
|
|
@@ -1479,9 +1493,10 @@ export class FleetManager {
|
|
|
1479
1493
|
try {
|
|
1480
1494
|
const posted = this.postNonceButtonPromptOrThrow({ prefix: "settings-confirm:", alertType: "clear_confirm",
|
|
1481
1495
|
instanceName: name, adapterId, adapter, chatId, threadId: topic,
|
|
1482
|
-
message:
|
|
1483
|
-
|
|
1484
|
-
|
|
1496
|
+
message: [t("settings_confirm.prompt_title"), t("settings_confirm.source", settingsSourceLabel(view.source)),
|
|
1497
|
+
t("settings_confirm.requester", view.requested_by), ...view.summary].join("\n"),
|
|
1498
|
+
choices: [{ action: "confirm", label: t("settings_confirm.confirm_button") }, { action: "reject", label: t("settings_confirm.reject_button") }],
|
|
1499
|
+
expiredText: t("settings_confirm.no_longer_pending"), timeoutMs: view.remaining_ms,
|
|
1485
1500
|
extra: { pendingChangeId: view.id, confirmationCurrent: current, confirmationAdmin: userId => current() && authority.admin(userId) },
|
|
1486
1501
|
});
|
|
1487
1502
|
void posted.then(nonce => {
|
|
@@ -1492,7 +1507,7 @@ export class FleetManager {
|
|
|
1492
1507
|
return;
|
|
1493
1508
|
this.pendingNonceButtons.delete(nonce);
|
|
1494
1509
|
clearTimeout(entry.timer);
|
|
1495
|
-
void this.retireNonceButtons(entry, entry.messageId ?? "", "
|
|
1510
|
+
void this.retireNonceButtons(entry, entry.messageId ?? "", t("settings_confirm.delivery_expired"));
|
|
1496
1511
|
}, () => { });
|
|
1497
1512
|
const nonce = await withinBudget(posted, postingDeadline);
|
|
1498
1513
|
attached = true;
|
|
@@ -1502,7 +1517,7 @@ export class FleetManager {
|
|
|
1502
1517
|
return;
|
|
1503
1518
|
this.pendingNonceButtons.delete(nonce);
|
|
1504
1519
|
clearTimeout(entry.timer);
|
|
1505
|
-
void this.retireNonceButtons(entry, entry.messageId ?? "", "
|
|
1520
|
+
void this.retireNonceButtons(entry, entry.messageId ?? "", t("settings_confirm.no_longer_pending"));
|
|
1506
1521
|
};
|
|
1507
1522
|
if (!current()) {
|
|
1508
1523
|
retire();
|
|
@@ -1528,14 +1543,14 @@ export class FleetManager {
|
|
|
1528
1543
|
const { entry, action } = result;
|
|
1529
1544
|
try {
|
|
1530
1545
|
const view = await this.settingsGate().store.decide(entry.pendingChangeId, action, {
|
|
1531
|
-
label:
|
|
1546
|
+
label: t("settings_confirm.admin_label", data.userId ?? ""),
|
|
1532
1547
|
current: () => entry.confirmationCurrent?.() === true && this.getAdapterForInstance(entry.instanceName) === entry.adapter
|
|
1533
1548
|
&& !!data.userId && entry.confirmationAdmin?.(data.userId) === true,
|
|
1534
1549
|
});
|
|
1535
|
-
await this.retireNonceButtons(entry, entry.messageId ?? data.messageId, view.outcome?.message ?? "
|
|
1550
|
+
await this.retireNonceButtons(entry, entry.messageId ?? data.messageId, view.outcome?.message ?? t("settings_confirm.applying"));
|
|
1536
1551
|
}
|
|
1537
1552
|
catch {
|
|
1538
|
-
await this.retireNonceButtons(entry, entry.messageId ?? data.messageId, "
|
|
1553
|
+
await this.retireNonceButtons(entry, entry.messageId ?? data.messageId, t("settings_confirm.rejected_or_expired"));
|
|
1539
1554
|
}
|
|
1540
1555
|
return true;
|
|
1541
1556
|
}
|
|
@@ -2659,6 +2674,10 @@ export class FleetManager {
|
|
|
2659
2674
|
this.savedFleetConfigSnapshot = structuredClone(this.fleetConfig);
|
|
2660
2675
|
this.warnAboutRemovedLoginMode(loaded);
|
|
2661
2676
|
this.enforceGuestSwitch();
|
|
2677
|
+
// Child tool/guidance readers must follow this fleet's actual config, including --config.
|
|
2678
|
+
// Scope the path to its owning data directory so another inherited home's override cannot enable CW.
|
|
2679
|
+
process.env.AGEND_FLEET_DATA_DIR = resolvePath(this.dataDir);
|
|
2680
|
+
process.env.AGEND_FLEET_CONFIG_PATH = resolvePath(configPath);
|
|
2662
2681
|
return this.fleetConfig;
|
|
2663
2682
|
}
|
|
2664
2683
|
relayLoginModeWarned = false;
|
|
@@ -2838,8 +2857,17 @@ export class FleetManager {
|
|
|
2838
2857
|
this.logger.info(`Received SIGUSR1, initiating full restart (process reload)... ${describeSignalSource(this.dataDir, "SIGUSR1")}`);
|
|
2839
2858
|
this.gracefulShutdownForReload()
|
|
2840
2859
|
.then(() => {
|
|
2841
|
-
|
|
2842
|
-
|
|
2860
|
+
// #2356: under an AgEnD systemd unit a clean exit is a stop (Restart=on-failure); exit so the unit comes back.
|
|
2861
|
+
let ownCgroup = null;
|
|
2862
|
+
try {
|
|
2863
|
+
ownCgroup = readFileSync("/proc/self/cgroup", "utf-8");
|
|
2864
|
+
}
|
|
2865
|
+
catch { /* not Linux */ }
|
|
2866
|
+
const code = fullReloadExitCode(ownCgroup);
|
|
2867
|
+
this.logger.info(code === 0
|
|
2868
|
+
? "Full restart: shutdown complete, exiting for reload"
|
|
2869
|
+
: `Full restart: shutdown complete, exiting ${code} so the systemd unit restarts the fleet (#2356)`);
|
|
2870
|
+
process.exit(code);
|
|
2843
2871
|
})
|
|
2844
2872
|
.catch(err => {
|
|
2845
2873
|
this.logger.error({ err }, "Full restart: graceful shutdown failed");
|
|
@@ -2858,7 +2886,7 @@ export class FleetManager {
|
|
|
2858
2886
|
const chatId = String(this.getChannelConfig(adapterId)?.group_id ?? data.channelId);
|
|
2859
2887
|
this.beginUpdateProgress(adapter, chatId, data.channelId, messageId);
|
|
2860
2888
|
}
|
|
2861
|
-
const { spawn } = await import("
|
|
2889
|
+
const { spawn } = await import("./child-process.js");
|
|
2862
2890
|
// Plain `agend update`: the CLI picks the channel from the version it is about to replace (a beta install
|
|
2863
2891
|
// stays on beta). Deciding here, from the package.json next to THIS code, read a source checkout's 1.22.0 as
|
|
2864
2892
|
// "not a beta" and sent a beta install to @latest. #1450 C5: the INSTALLED agend, verified, by absolute path.
|
|
@@ -4304,6 +4332,18 @@ export class FleetManager {
|
|
|
4304
4332
|
async dispatchSlashCore(slash, adapterId, adapter) {
|
|
4305
4333
|
// #1569: an alias (/web) is its command (/dashboard) from here on — the door, the table and the handler see one name.
|
|
4306
4334
|
const data = { ...slash, command: canonicalCommand(slash.command) };
|
|
4335
|
+
// #2359: /dashboard stays listed; prefix its reply with the deprecation notice, then run /web's handler.
|
|
4336
|
+
const noteDeprecatedDashboard = () => {
|
|
4337
|
+
if (slash.command !== "dashboard")
|
|
4338
|
+
return;
|
|
4339
|
+
const prefix = `${t("dashboard.deprecated")}\n\n`;
|
|
4340
|
+
const respond = data.respond;
|
|
4341
|
+
data.respond = async (text) => respond(prefix + text);
|
|
4342
|
+
if (data.respondButtons) {
|
|
4343
|
+
const respondButtons = data.respondButtons;
|
|
4344
|
+
data.respondButtons = async (text, choices) => respondButtons(prefix + text, choices);
|
|
4345
|
+
}
|
|
4346
|
+
};
|
|
4307
4347
|
const door = this.slashDoor(data, adapterId);
|
|
4308
4348
|
if ("refusal" in door) {
|
|
4309
4349
|
await data.respond(t(door.refusal)).catch(() => { });
|
|
@@ -4314,6 +4354,7 @@ export class FleetManager {
|
|
|
4314
4354
|
// — before the table, which keeps /web for fleet admins. Per account: it touches no instance binding.
|
|
4315
4355
|
// Decided synchronously, so an admin's /web still reaches the table with no await after the door (#1399).
|
|
4316
4356
|
if (data.command === "dashboard" && this.isGuestWebAsk(adapterId, data.userId)) {
|
|
4357
|
+
noteDeprecatedDashboard();
|
|
4317
4358
|
await this.guestWebCommand({ adapterId, userId: data.userId, username: data.username,
|
|
4318
4359
|
arg: String(data.options?.action ?? "").trim().toLowerCase(), replyHere: text => data.respond(text), replyPrivately: text => data.respond(text) });
|
|
4319
4360
|
return;
|
|
@@ -4342,6 +4383,7 @@ export class FleetManager {
|
|
|
4342
4383
|
await data.respond(reply).catch(() => { });
|
|
4343
4384
|
return;
|
|
4344
4385
|
}
|
|
4386
|
+
noteDeprecatedDashboard();
|
|
4345
4387
|
if (data.command === "start") {
|
|
4346
4388
|
await this.handleClassicStartSlash(data, adapterId);
|
|
4347
4389
|
}
|
|
@@ -7158,6 +7200,17 @@ export class FleetManager {
|
|
|
7158
7200
|
return DailySummary.generateText(this.eventLog, instances, costMap, this.costGuard?.getFleetTotalCents() ?? 0);
|
|
7159
7201
|
});
|
|
7160
7202
|
this.dailySummary.start();
|
|
7203
|
+
// #2246 step 3: the task board's daily summary — its own time (defaults.task_board.summary_at, read at every look),
|
|
7204
|
+
// in the fleet's zone; a summary missed while the fleet was down goes out when it is back.
|
|
7205
|
+
this.taskBoardSummary = new TaskBoardSummaryClock({
|
|
7206
|
+
at: () => summaryTime(this.fleetConfig?.defaults?.task_board?.summary_at) ?? null,
|
|
7207
|
+
zone: () => this.usageZone(),
|
|
7208
|
+
now: () => new Date(),
|
|
7209
|
+
mark: summaryMarkFile(join(this.dataDir, "task-board-summary.json")),
|
|
7210
|
+
send: (since, until) => this.postTaskBoardSummary(since, until),
|
|
7211
|
+
failed: (err) => this.logger.warn({ err }, "The task board's daily summary failed; the next minute tries again"),
|
|
7212
|
+
});
|
|
7213
|
+
this.taskBoardSummary.start();
|
|
7161
7214
|
// Rotate classic channel chat logs daily (piggyback on daily summary timer)
|
|
7162
7215
|
this.classicChannels?.rotateLogs();
|
|
7163
7216
|
this.rotateInboxes();
|
|
@@ -7552,7 +7605,8 @@ export class FleetManager {
|
|
|
7552
7605
|
}, 60 * 60 * 1000);
|
|
7553
7606
|
const onRestart = () => this.onGracefulRestartSignal(() => process.once("SIGUSR2", onRestart));
|
|
7554
7607
|
process.once("SIGUSR2", onRestart);
|
|
7555
|
-
// SIGUSR1: full process reload (graceful stop → exit
|
|
7608
|
+
// SIGUSR1: full process reload (graceful stop → exit). Nothing in this AgEnD sends it any more (#2356); an older
|
|
7609
|
+
// CLI or a hand-sent signal still may, and the exit code then lets the owning manager start the next fleet.
|
|
7556
7610
|
process.once("SIGUSR1", () => this.onFullRestartSignal());
|
|
7557
7611
|
// A SIGHUP may arrive after the PID/general is available but before the
|
|
7558
7612
|
// rest of startup finishes. Replay one coalesced reload only after all
|
|
@@ -8443,7 +8497,7 @@ export class FleetManager {
|
|
|
8443
8497
|
// means we cannot verify the pane, which is treated as dead (the same
|
|
8444
8498
|
// conclusion the old code reached only by throwing).
|
|
8445
8499
|
try {
|
|
8446
|
-
const { execFile } = await import("
|
|
8500
|
+
const { execFile } = await import("./child-process.js");
|
|
8447
8501
|
const { promisify } = await import("node:util");
|
|
8448
8502
|
const { getTmuxSocketName } = await import("./paths.js");
|
|
8449
8503
|
// Honour socket isolation: without -L this queried the user's default
|
|
@@ -8993,6 +9047,22 @@ export class FleetManager {
|
|
|
8993
9047
|
this.logger.debug({ adapterId: msg.adapterId, threadId: threadId ?? null, messageId: msg.messageId, reason: drop }, "Inbound message dropped before the dedup claim — key not consumed");
|
|
8994
9048
|
return;
|
|
8995
9049
|
}
|
|
9050
|
+
// #2246 step 3 (#2341 review r2): a reply to a Needs you Send back / Answer prompt is the card's reason or answer, not
|
|
9051
|
+
// input for an instance — judged by the PROMPT's world (its admins, its instance, its room as it is now; the hub's
|
|
9052
|
+
// handleReply), before the room's generic access below: in a shared group the room may be another bot's, whose
|
|
9053
|
+
// policy has no say over that world's prompt. Only that exact reply, by the admin who clicked, while the prompt
|
|
9054
|
+
// stands. Taken once: another bot's copy of a taken reply is dropped here. All else flows on.
|
|
9055
|
+
if (!msg.isBotMessage && msg.replyTo && this.needsYou) {
|
|
9056
|
+
if (this.needsYou.wasTakenReply(msg.chatId, msg.messageId)) {
|
|
9057
|
+
this.logger.debug({ adapterId: msg.adapterId, messageId: msg.messageId }, "Another bot's copy of a Needs you answer already taken — dropped");
|
|
9058
|
+
return;
|
|
9059
|
+
}
|
|
9060
|
+
if (this.needsYou.handleReply({
|
|
9061
|
+
adapterId: msg.adapterId, chatId: msg.chatId, ...(threadId !== undefined ? { threadId } : {}), replyTo: msg.replyTo,
|
|
9062
|
+
messageId: msg.messageId, userId: msg.userId, ...(msg.username ? { username: msg.username } : {}), text: msg.text ?? "",
|
|
9063
|
+
}))
|
|
9064
|
+
return;
|
|
9065
|
+
}
|
|
8996
9066
|
// Access control — classic channels are open to all, others require an allowed
|
|
8997
9067
|
// user. This runs before the dedup claim, and is judged by the adapter that
|
|
8998
9068
|
// owns the topic rather than the one that happened to receive the message:
|
|
@@ -9103,6 +9173,10 @@ export class FleetManager {
|
|
|
9103
9173
|
// #1920 step 4b-3: /web typed in a ClassicBot room (a group: `/web@ThisBot`) by someone who is not a fleet admin,
|
|
9104
9174
|
// while guest sign-in is on: their guest code, privately — as anywhere else. An admin's goes on as before.
|
|
9105
9175
|
if (/^\/(?:web|dashboard)(?:\s|$)/i.test(text) && msgAdapter && this.isGuestWebAsk(msg.adapterId, msg.userId)) {
|
|
9176
|
+
// #2359: this door must not skip the deprecation notice TopicCommands shows on /dashboard.
|
|
9177
|
+
if (/^\/dashboard(?:\s|$)/i.test(text)) {
|
|
9178
|
+
await msgAdapter.sendText(chatId, t("dashboard.deprecated")).catch(() => { });
|
|
9179
|
+
}
|
|
9106
9180
|
await this.guestWebCommand({ adapterId: msg.adapterId, userId: msg.userId, username: msg.username,
|
|
9107
9181
|
arg: text.replace(/^\/(?:web|dashboard)/i, "").trim().toLowerCase(), replyHere: body => msgAdapter.sendText(chatId, body) });
|
|
9108
9182
|
return;
|
|
@@ -9653,6 +9727,8 @@ export class FleetManager {
|
|
|
9653
9727
|
}
|
|
9654
9728
|
}
|
|
9655
9729
|
checkToolPermission(sink, instanceName, tool) {
|
|
9730
|
+
if (isCanvasWorkerTool(tool) && !this.canvasWorkerEnabled())
|
|
9731
|
+
return { allowed: false, message: CANVAS_WORKER_EXPERIMENTAL_MESSAGE };
|
|
9656
9732
|
const profile = resolveToolSet(this.fleetConfig?.instances[instanceName], instanceName);
|
|
9657
9733
|
const allowed = mayUseTool(profile, tool);
|
|
9658
9734
|
if (!allowed) {
|
|
@@ -10337,13 +10413,14 @@ export class FleetManager {
|
|
|
10337
10413
|
if (!ipc || !fleetRequestId)
|
|
10338
10414
|
return;
|
|
10339
10415
|
const type = String(msg.type);
|
|
10340
|
-
const responseType = type.
|
|
10341
|
-
: type.startsWith("
|
|
10342
|
-
: type
|
|
10343
|
-
: type === "
|
|
10344
|
-
: type === "
|
|
10345
|
-
|
|
10346
|
-
|
|
10416
|
+
const responseType = isCanvasWorkerTool(type.replace(/^fleet_/, "")) ? "fleet_web_theme_response"
|
|
10417
|
+
: type.startsWith("fleet_schedule_") ? "fleet_schedule_response"
|
|
10418
|
+
: type.startsWith("fleet_decision_") ? "fleet_decision_response"
|
|
10419
|
+
: type === "fleet_task" ? "fleet_task_response"
|
|
10420
|
+
: type === "fleet_set_display_name" ? "fleet_display_name_response"
|
|
10421
|
+
: type === "fleet_list_emojis" || type === "fleet_set_persona_emoji" || type === "fleet_preview_emojis"
|
|
10422
|
+
|| type === "fleet_list_stickers" || type === "fleet_preview_stickers" ? "fleet_persona_emoji_response"
|
|
10423
|
+
: "fleet_description_response";
|
|
10347
10424
|
ipc.send({ type: responseType, fleetRequestId, error: message });
|
|
10348
10425
|
}
|
|
10349
10426
|
dispatchTypedIpc(name, msg) {
|
|
@@ -10836,6 +10913,8 @@ export class FleetManager {
|
|
|
10836
10913
|
}
|
|
10837
10914
|
/** #1629: `agend-agent theme …` — the same service as the tools; `instance` is the token's. */
|
|
10838
10915
|
async handleWebThemeHttp(instance, tool, args) {
|
|
10916
|
+
if (isCanvasWorkerTool(tool) && !this.canvasWorkerEnabled())
|
|
10917
|
+
return { error: "canvas_worker_experimental", message: CANVAS_WORKER_EXPERIMENTAL_MESSAGE };
|
|
10839
10918
|
if (WEB_EFFECT_TOOLS.has(tool)) {
|
|
10840
10919
|
if (!this.isKnownInstance(instance))
|
|
10841
10920
|
return { error: "unknown_instance" };
|
|
@@ -12728,7 +12807,7 @@ export class FleetManager {
|
|
|
12728
12807
|
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
|
12729
12808
|
const backend = this.fleetConfig?.defaults?.backend || "claude-code";
|
|
12730
12809
|
try {
|
|
12731
|
-
const { execFile } = await import("
|
|
12810
|
+
const { execFile } = await import("./child-process.js");
|
|
12732
12811
|
const { promisify } = await import("node:util");
|
|
12733
12812
|
// execFile with an argv array — no shell, so the backend name cannot be
|
|
12734
12813
|
// interpreted as a command even if config is malformed.
|
|
@@ -12954,6 +13033,51 @@ export class FleetManager {
|
|
|
12954
13033
|
}
|
|
12955
13034
|
return { chatId, opts: {} };
|
|
12956
13035
|
}
|
|
13036
|
+
/**
|
|
13037
|
+
* #2246 step 3: the task board's daily summary of [since, until) (task-board-summary.ts). Each chat world's General
|
|
13038
|
+
* gets its own instances' cards — a card that no world's instance holds (nobody's, a person's) goes to the primary
|
|
13039
|
+
* one — and each of that world's Generals shows the same text in the web chat. A world with nothing to say gets
|
|
13040
|
+
* nothing. A web-only fleet: everything, in the web chat.
|
|
13041
|
+
*/
|
|
13042
|
+
postTaskBoardSummary(since, until) {
|
|
13043
|
+
const board = this.scheduler?.db.board;
|
|
13044
|
+
if (!board)
|
|
13045
|
+
return;
|
|
13046
|
+
const read = board.summaryRead(since.toISOString(), until.toISOString());
|
|
13047
|
+
const projects = board.listProjects(true);
|
|
13048
|
+
const stats = board.projectStats();
|
|
13049
|
+
const names = new Map(projects.map((p) => [p.id, p.name]));
|
|
13050
|
+
const configured = this.getPrimaryAdapterId();
|
|
13051
|
+
const primary = configured !== undefined && this.worlds.has(configured) ? configured : this.worlds.keys().next().value;
|
|
13052
|
+
const worldOf = (assignee) => {
|
|
13053
|
+
const owner = assignee ? this.getInstanceAdapterId(assignee) : undefined;
|
|
13054
|
+
return owner !== undefined && this.worlds.has(owner) ? owner : primary;
|
|
13055
|
+
};
|
|
13056
|
+
const zone = this.usageZone();
|
|
13057
|
+
for (const world of this.worlds.size ? [...this.worlds.keys()] : [undefined]) {
|
|
13058
|
+
const summary = buildSummary(read, since, until, { inChat: (assignee) => worldOf(assignee) === world, projects, stats });
|
|
13059
|
+
if (isQuiet(summary))
|
|
13060
|
+
continue;
|
|
13061
|
+
const text = renderSummary(summary, { zone, t: (key, ...args) => t(key, ...args), projectOf: (c) => (c.projectId === null ? null : names.get(c.projectId) ?? null) });
|
|
13062
|
+
const cards = [...new Set([...summary.review, ...summary.blocked, ...summary.done, ...summary.created].map((c) => c.code))];
|
|
13063
|
+
if (world !== undefined) {
|
|
13064
|
+
const adapter = this.worlds.get(world)?.adapter;
|
|
13065
|
+
const target = this.fleetNoticeTarget(world);
|
|
13066
|
+
if (adapter && target) {
|
|
13067
|
+
adapter.sendText(target.chatId, text, target.opts).catch(e => this.logger.warn({ err: e, world }, "Failed to send the task board's daily summary"));
|
|
13068
|
+
}
|
|
13069
|
+
else {
|
|
13070
|
+
this.logger.warn({ world }, "The task board's daily summary has no postable target — set a General topic or channel.options.general_channel_id");
|
|
13071
|
+
}
|
|
13072
|
+
}
|
|
13073
|
+
for (const [name, cfg] of Object.entries(this.fleetConfig?.instances ?? {})) {
|
|
13074
|
+
if (cfg.general_topic !== true || (world !== undefined && this.getInstanceAdapterId(name) !== world))
|
|
13075
|
+
continue;
|
|
13076
|
+
// #2341 review: with the cards it names, so the public link withholds it while one of them is hidden there.
|
|
13077
|
+
this.emitSseEvent("message", { instance: name, sender: name, role: "status", text, ts: new Date().toISOString(), cards });
|
|
13078
|
+
}
|
|
13079
|
+
}
|
|
13080
|
+
}
|
|
12957
13081
|
/**
|
|
12958
13082
|
* Post the daily summary where fleet-wide notices go.
|
|
12959
13083
|
*
|
|
@@ -13662,6 +13786,8 @@ export class FleetManager {
|
|
|
13662
13786
|
return String(topic);
|
|
13663
13787
|
return this.classicChannels?.getChannelIdByInstance(instance) ?? undefined;
|
|
13664
13788
|
},
|
|
13789
|
+
laneTarget: instance => this.instanceChannelTarget(instance),
|
|
13790
|
+
taskRoute: code => this.needsYouRoute(code),
|
|
13665
13791
|
isFleetAdmin: (userId, world) => this.isFleetAdmin(userId, world),
|
|
13666
13792
|
fleetAdmins: world => (this.getChannelConfig(world)?.access?.allowed_users ?? []).map(String),
|
|
13667
13793
|
emitSse: (event, data) => this.emitSseEvent(event, data),
|
|
@@ -13670,6 +13796,8 @@ export class FleetManager {
|
|
|
13670
13796
|
throw new Error("the delivery outbox is not open");
|
|
13671
13797
|
return this.deliveryOutbox.acknowledge(deliveryId, by);
|
|
13672
13798
|
},
|
|
13799
|
+
tasks: () => this.needsYouTasks(),
|
|
13800
|
+
taskAction: action => this.taskActionFromChat(action),
|
|
13673
13801
|
settings: () => ({
|
|
13674
13802
|
liveMessage: this.fleetConfig?.needs_you?.live_message !== false,
|
|
13675
13803
|
dm: this.fleetConfig?.needs_you?.dm === true,
|
|
@@ -13680,6 +13808,98 @@ export class FleetManager {
|
|
|
13680
13808
|
});
|
|
13681
13809
|
this.needsYou.start();
|
|
13682
13810
|
}
|
|
13811
|
+
/**
|
|
13812
|
+
* #2352: where an instance's own notices go — its topic in its world's group, or its ClassicBot room — as a Needs you
|
|
13813
|
+
* lane's place; null when it has neither (or its world has no group).
|
|
13814
|
+
*/
|
|
13815
|
+
instanceChannelTarget(instance) {
|
|
13816
|
+
const cfg = this.fleetConfig?.instances && Object.hasOwn(this.fleetConfig.instances, instance) ? this.fleetConfig.instances[instance] : undefined;
|
|
13817
|
+
if (!cfg)
|
|
13818
|
+
return null;
|
|
13819
|
+
const groupId = this.getChannelConfig(this.getInstanceAdapterId(instance))?.group_id;
|
|
13820
|
+
if (cfg.topic_id != null && groupId != null)
|
|
13821
|
+
return { chatId: String(groupId), threadId: String(cfg.topic_id) };
|
|
13822
|
+
const classic = this.classicChannels?.getChannelIdByInstance(instance);
|
|
13823
|
+
return classic ? { chatId: classic } : null;
|
|
13824
|
+
}
|
|
13825
|
+
/**
|
|
13826
|
+
* #2352: the instance whose own channel lists a waiting card — the instance that handed it out, else its project's
|
|
13827
|
+
* lead — when that channel can be used now (a fleet instance of a live world, with a topic or room). undefined: its
|
|
13828
|
+
* assignee's world's General, as before.
|
|
13829
|
+
*/
|
|
13830
|
+
needsYouLane(card, leads) {
|
|
13831
|
+
const usable = (name) => {
|
|
13832
|
+
if (!name || !this.fleetConfig?.instances || !Object.hasOwn(this.fleetConfig.instances, name))
|
|
13833
|
+
return false;
|
|
13834
|
+
const world = this.getInstanceAdapterId(name);
|
|
13835
|
+
return world !== undefined && this.worlds.has(world) && this.instanceChannelTarget(name) !== null;
|
|
13836
|
+
};
|
|
13837
|
+
const dispatcher = card.dispatcher?.kind === "instance" && card.dispatcher.name !== card.assignee ? card.dispatcher.name : null;
|
|
13838
|
+
if (dispatcher)
|
|
13839
|
+
return usable(dispatcher) ? dispatcher : undefined;
|
|
13840
|
+
const lead = card.projectId != null ? leads.get(card.projectId) : null;
|
|
13841
|
+
return usable(lead) ? lead : undefined;
|
|
13842
|
+
}
|
|
13843
|
+
/**
|
|
13844
|
+
* #2355 review: where a card's Needs you buttons belong now — read from the board and the fleet at the moment of a click
|
|
13845
|
+
* or a reply, never from the last render. null: it no longer waits for the person.
|
|
13846
|
+
*/
|
|
13847
|
+
needsYouRoute(code) {
|
|
13848
|
+
const board = this.scheduler?.db.board;
|
|
13849
|
+
const card = board?.findCard(code);
|
|
13850
|
+
if (!board || !card || !(card.status === "blocked" || (card.status === "review" && card.needsReview)))
|
|
13851
|
+
return null;
|
|
13852
|
+
const leads = new Map(board.listProjects(true).map((p) => [p.id, p.leadInstance]));
|
|
13853
|
+
const lane = this.needsYouLane(card, leads);
|
|
13854
|
+
return lane ? { lane } : {};
|
|
13855
|
+
}
|
|
13856
|
+
/**
|
|
13857
|
+
* #2246 step 3: the task board's cards that wait for the person, for Needs you — under each card's assignee, listed in
|
|
13858
|
+
* its lane's channel (#2352: the instance that handed it out, else the project's lead), else in General.
|
|
13859
|
+
*/
|
|
13860
|
+
needsYouTasks() {
|
|
13861
|
+
const board = this.scheduler?.db.board;
|
|
13862
|
+
if (!board)
|
|
13863
|
+
return [];
|
|
13864
|
+
const leads = new Map(board.listProjects(true).map((p) => [p.id, p.leadInstance]));
|
|
13865
|
+
return board.waitingForPerson().map(({ card, since }) => {
|
|
13866
|
+
const gap = card.status === "review" ? wrapUpGap(card) : null;
|
|
13867
|
+
const lane = this.needsYouLane(card, leads);
|
|
13868
|
+
return {
|
|
13869
|
+
uuid: card.uuid, code: card.code, title: card.title, status: card.status, version: card.version,
|
|
13870
|
+
assignee: card.assignee, since: Date.parse(since) || 0, ...(gap ? { gap } : {}), ...(lane ? { lane } : {}),
|
|
13871
|
+
note: card.status === "blocked" ? card.blockedReason : gap ? t("needs.task_gap", [
|
|
13872
|
+
gap.unchecked ? t("needs.task_gap_unchecked", gap.unchecked) : "", gap.noResult ? t("needs.task_gap_no_result") : "",
|
|
13873
|
+
].filter(Boolean).join(t("needs.task_gap_join"))) : "",
|
|
13874
|
+
};
|
|
13875
|
+
});
|
|
13876
|
+
}
|
|
13877
|
+
/**
|
|
13878
|
+
* #2246 step 3: the person's move on a card from a chat's Needs you list — a fleet admin of that world (checked by the
|
|
13879
|
+
* hub), acting as the person, through the board's rules, at the version the button showed.
|
|
13880
|
+
*/
|
|
13881
|
+
taskActionFromChat(action) {
|
|
13882
|
+
const board = this.scheduler?.db.board;
|
|
13883
|
+
if (!board)
|
|
13884
|
+
return { ok: false, message: t("needs.task_board_closed") };
|
|
13885
|
+
const person = { kind: "person", name: action.by.username || `${action.by.platform}:${action.by.userId}` };
|
|
13886
|
+
try {
|
|
13887
|
+
if (action.verb === "confirm")
|
|
13888
|
+
board.moveCard(person, action.code, "done", { version: action.version });
|
|
13889
|
+
else if (action.verb === "back")
|
|
13890
|
+
board.moveCard(person, action.code, "claimed", { version: action.version, reason: action.text });
|
|
13891
|
+
else
|
|
13892
|
+
board.moveCard(person, action.code, "claimed", { version: action.version, comment: action.text });
|
|
13893
|
+
const done = { confirm: "needs.task_confirmed", back: "needs.task_sent_back", answer: "needs.task_answered" }[action.verb];
|
|
13894
|
+
this.logger.info({ code: action.code, verb: action.verb, by: `${action.by.platform}:${action.by.userId}` }, "Task board: the person moved a card from the chat");
|
|
13895
|
+
return { ok: true, message: t(done, action.code) };
|
|
13896
|
+
}
|
|
13897
|
+
catch (err) {
|
|
13898
|
+
if (err instanceof TaskBoardError && err.code === "conflict")
|
|
13899
|
+
return { ok: false, message: t("needs.task_changed", action.code) };
|
|
13900
|
+
return { ok: false, message: t("needs.task_refused", action.code, err.message) };
|
|
13901
|
+
}
|
|
13902
|
+
}
|
|
13683
13903
|
/** The prompts open on the dashboard right now (a page that loads after one was posted asks for them). */
|
|
13684
13904
|
listWebPrompts() {
|
|
13685
13905
|
const open = [];
|
|
@@ -16338,6 +16558,7 @@ export class FleetManager {
|
|
|
16338
16558
|
channel: m.channel,
|
|
16339
16559
|
from: m.from,
|
|
16340
16560
|
replyTo: m.replyTo,
|
|
16561
|
+
cards: m.cards,
|
|
16341
16562
|
});
|
|
16342
16563
|
broadcastSseEvent(this.sseClients, event, recorded, onError, this.webChatHistory.cursorOf(recorded));
|
|
16343
16564
|
this.searchIndex?.record(chatDoc(recorded));
|
|
@@ -16369,11 +16590,25 @@ export class FleetManager {
|
|
|
16369
16590
|
isInstance: (name) => this.fleetConfig?.instances?.[name] != null,
|
|
16370
16591
|
holds: (wake) => db.board.wakeHolds(wake),
|
|
16371
16592
|
card: (uuid) => db.board.findCard(uuid),
|
|
16593
|
+
// #2352: the card's Needs you buttons are in this instance's own channel (it is the card's lane).
|
|
16594
|
+
buttonsHere: (instance, uuid) => {
|
|
16595
|
+
// #2355 review: no live message, no buttons — the old words then.
|
|
16596
|
+
if (this.fleetConfig?.needs_you?.live_message === false)
|
|
16597
|
+
return false;
|
|
16598
|
+
const card = db.board.findCard(uuid);
|
|
16599
|
+
if (!card)
|
|
16600
|
+
return false;
|
|
16601
|
+
const leads = new Map(db.board.listProjects(true).map((p) => [p.id, p.leadInstance]));
|
|
16602
|
+
return this.needsYouLane(card, leads) === instance;
|
|
16603
|
+
},
|
|
16372
16604
|
log: (level, message, extra) => this.logger[level](extra ?? {}, message),
|
|
16373
16605
|
});
|
|
16374
16606
|
db.board.onChange(({ rev, ids, projects, wakes }) => {
|
|
16375
16607
|
this.emitSseEvent("tasks", { rev, ids, projects });
|
|
16376
16608
|
waker.note(wakes);
|
|
16609
|
+
// step 3: cards waiting for the person are Needs you items; #2355 review: a project's lead routes them too.
|
|
16610
|
+
if (ids.length || projects)
|
|
16611
|
+
this.needsYou?.poke();
|
|
16377
16612
|
});
|
|
16378
16613
|
}
|
|
16379
16614
|
/** A task board notice, as AgEnD's own (not a person's words): through the delivery outbox, held until delivered. */
|
|
@@ -16587,6 +16822,27 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
16587
16822
|
this.syncRoleSkills(workDir, backend, "general", knowledgeDir);
|
|
16588
16823
|
this.logger.debug({ knowledgeDir, workDir, backend }, "Synced general knowledge files");
|
|
16589
16824
|
}
|
|
16825
|
+
/** Update only AgEnD-managed skill copies; no launch or restart is needed for an experimental switch. */
|
|
16826
|
+
canvasWorkerSettingChanged() {
|
|
16827
|
+
this.refreshExperimentalSkills();
|
|
16828
|
+
this.emitSseEvent("status", this.getUiStatus());
|
|
16829
|
+
}
|
|
16830
|
+
refreshExperimentalSkills() {
|
|
16831
|
+
for (const [name, daemon] of this.daemons) {
|
|
16832
|
+
try {
|
|
16833
|
+
const cfg = daemon.getConfigSnapshot();
|
|
16834
|
+
const backend = cfg.backend ?? this.fleetConfig?.defaults?.backend ?? "claude-code";
|
|
16835
|
+
const workDir = this.resolveKnowledgeWorkDir(cfg.working_directory, backend, name);
|
|
16836
|
+
const classic = this.classicChannels?.getAll().some(ch => ch.instanceName === name) === true;
|
|
16837
|
+
if (classic && !insideManagedWorkspaces(workDir))
|
|
16838
|
+
continue;
|
|
16839
|
+
this.syncRoleSkills(workDir, backend, classic ? "classic" : cfg.general_topic ? "general" : "worker");
|
|
16840
|
+
}
|
|
16841
|
+
catch (err) {
|
|
16842
|
+
this.logger.warn({ name, err }, "Could not refresh experimental skill visibility");
|
|
16843
|
+
}
|
|
16844
|
+
}
|
|
16845
|
+
}
|
|
16590
16846
|
/** Publish only the bundled skills eligible for an instance role. */
|
|
16591
16847
|
syncRoleSkills(workDir, backend, role, knowledgeDir) {
|
|
16592
16848
|
const skillSegments = FleetManager.SKILLS_DIR_SEGMENTS[backend];
|
|
@@ -16751,7 +17007,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
16751
17007
|
continue;
|
|
16752
17008
|
}
|
|
16753
17009
|
managed.push(name);
|
|
16754
|
-
const newContent =
|
|
17010
|
+
const newContent = bundledSkillContents(join(srcSkills, name), this.canvasWorkerEnabled());
|
|
16755
17011
|
if (refuse(dest))
|
|
16756
17012
|
continue;
|
|
16757
17013
|
try {
|
|
@@ -19192,14 +19448,14 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
19192
19448
|
*/
|
|
19193
19449
|
stopAll() {
|
|
19194
19450
|
this.stopAllInFlight ??= this.doStopAll().finally(() => {
|
|
19195
|
-
this.stopAllInFlight = null;
|
|
19196
19451
|
// #1956: after the stop's own tmux work; anything later goes direct. A later startAll starts it again.
|
|
19197
19452
|
if (this.tmuxExecHelperTimer) {
|
|
19198
19453
|
clearInterval(this.tmuxExecHelperTimer);
|
|
19199
19454
|
this.tmuxExecHelperTimer = null;
|
|
19200
19455
|
}
|
|
19201
|
-
|
|
19202
|
-
|
|
19456
|
+
// #2364: and the stop waits (bounded) for the helper to leave on its own, before the caller exits the process.
|
|
19457
|
+
return tmuxExecHelper.stop();
|
|
19458
|
+
}).finally(() => { this.stopAllInFlight = null; });
|
|
19203
19459
|
return this.stopAllInFlight;
|
|
19204
19460
|
}
|
|
19205
19461
|
tmuxExecHelperTimer = null;
|
|
@@ -19274,6 +19530,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
19274
19530
|
this.clearStatuslineWatchers();
|
|
19275
19531
|
this.costGuard?.stop();
|
|
19276
19532
|
this.dailySummary?.stop();
|
|
19533
|
+
this.taskBoardSummary?.stop();
|
|
19277
19534
|
this.dailyTipScheduler?.stop();
|
|
19278
19535
|
this.dailyTipScheduler = null;
|
|
19279
19536
|
if (this.updateCheckTimer) {
|
|
@@ -19610,6 +19867,9 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
19610
19867
|
// mark every row done and tell the user "changes applied".
|
|
19611
19868
|
return { rejected: why };
|
|
19612
19869
|
}
|
|
19870
|
+
if (canvasWorkerEnabled(oldConfig?.web) !== this.canvasWorkerEnabled()) {
|
|
19871
|
+
this.canvasWorkerSettingChanged();
|
|
19872
|
+
}
|
|
19613
19873
|
// Classic behavior settings share the fleet defaults but are not entries
|
|
19614
19874
|
// in fleet.yaml. Snapshot the old effective chain before reloading the
|
|
19615
19875
|
// Classic file so SIGHUP can hot-apply either source without waiting for
|
|
@@ -19859,6 +20119,20 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
19859
20119
|
return false;
|
|
19860
20120
|
}
|
|
19861
20121
|
/** Non-null when startup found the running config and fleet.yaml disagreeing. */
|
|
20122
|
+
/**
|
|
20123
|
+
* #2346 (settings schema): whether a connection added now would start at once — what canHotAddConnections would decide
|
|
20124
|
+
* for the current config with one more connection appended: another one is running, and nothing else this process took
|
|
20125
|
+
* at startup has changed since (a pending locale or access change, say, needs AgEnD restarted anyway).
|
|
20126
|
+
*/
|
|
20127
|
+
connectionsStartLive() {
|
|
20128
|
+
if (!this.fleetConfig)
|
|
20129
|
+
return false;
|
|
20130
|
+
const current = this.fleetConfig.channels ?? (this.fleetConfig.channel ? [this.fleetConfig.channel] : []);
|
|
20131
|
+
const probe = { id: "\u0000probe", type: "telegram", mode: "topic", bot_token_env: "\u0000PROBE" };
|
|
20132
|
+
const next = { ...this.fleetConfig, channels: [...current, probe] };
|
|
20133
|
+
delete next.channel;
|
|
20134
|
+
return this.canHotAddConnections(next);
|
|
20135
|
+
}
|
|
19862
20136
|
fleetSignatureMismatchKeys() {
|
|
19863
20137
|
return this.fleetSignatureMismatch;
|
|
19864
20138
|
}
|
|
@@ -20113,9 +20387,10 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
20113
20387
|
const tunnel = spec.kind === "tunnel_token";
|
|
20114
20388
|
if (!tunnel && !spec.verifier)
|
|
20115
20389
|
return { ok: false, status: "unsupported_verifier", error: "this provider has no supported verifier" };
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20390
|
+
const secret = tunnel && !input.clear ? normalizePublicTunnelToken(input.secret) : input.secret;
|
|
20391
|
+
if (input.clear && (!tunnel || input.secret !== "")
|
|
20392
|
+
|| !input.clear && (!secret || secret.length > 4096 || /[\r\n\0]/.test(secret) || /[^\x20-\x7e]/.test(secret))) {
|
|
20393
|
+
return { ok: false, status: "invalid", error: tunnel ? PUBLIC_TUNNEL_TOKEN_HINT : "secret is invalid" };
|
|
20119
20394
|
}
|
|
20120
20395
|
const challengeKey = `${input.sessionBinding}:api_key:${spec.id}:${spec.envKey}:${input.idempotencyKey}`;
|
|
20121
20396
|
const existingId = this.providerSecretChallengesByKey.get(challengeKey);
|
|
@@ -20143,7 +20418,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
20143
20418
|
operation: "provider-secret.apply",
|
|
20144
20419
|
idempotencyKey: input.idempotencyKey,
|
|
20145
20420
|
expiresAt, deadline: performance.now() + SECRET_CHALLENGE_TTL_MS,
|
|
20146
|
-
secret:
|
|
20421
|
+
secret: secret,
|
|
20147
20422
|
};
|
|
20148
20423
|
this.providerSecretChallenges.set(challenge.id, challenge);
|
|
20149
20424
|
this.providerSecretChallengesByKey.set(challengeKey, challenge.id);
|
|
@@ -21287,7 +21562,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
21287
21562
|
// up to 15s each, and on a beta build BOTH ran — 30s with no WATCHDOG ping,
|
|
21288
21563
|
// past WatchdogSec's half-interval and enough for systemd to SIGABRT the fleet
|
|
21289
21564
|
// for a background version check.
|
|
21290
|
-
const { execFile } = await import("
|
|
21565
|
+
const { execFile } = await import("./child-process.js");
|
|
21291
21566
|
const { promisify } = await import("node:util");
|
|
21292
21567
|
const execFileP = promisify(execFile);
|
|
21293
21568
|
const npmVersion = async (spec) => {
|
|
@@ -21579,6 +21854,11 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
21579
21854
|
return;
|
|
21580
21855
|
}
|
|
21581
21856
|
const requestPath = new URL(req.url ?? "/", `http://localhost:${port}`).pathname;
|
|
21857
|
+
if (!this.canvasWorkerEnabled() && isCanvasWorkerHttpPath(requestPath)) {
|
|
21858
|
+
res.writeHead(403, { "Content-Type": "application/json", "Cache-Control": "no-store" });
|
|
21859
|
+
res.end(JSON.stringify({ error: "canvas_worker_experimental", message: CANVAS_WORKER_EXPERIMENTAL_MESSAGE }));
|
|
21860
|
+
return;
|
|
21861
|
+
}
|
|
21582
21862
|
if (this.effectBootstrap.handle(req, res))
|
|
21583
21863
|
return;
|
|
21584
21864
|
// Branding is public, after Host admission: automatic favicon requests
|
|
@@ -21647,6 +21927,12 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
21647
21927
|
// #1920 §5.1: guests chat unless the host made them read-only (web.guests.chat: false).
|
|
21648
21928
|
chat: () => this.fleetConfig?.web?.guests?.chat !== false });
|
|
21649
21929
|
}
|
|
21930
|
+
if (decision.via === "session" && this.webToken) {
|
|
21931
|
+
const upgrade = sessionCookieUpgrade(req, this.webToken, decision.session);
|
|
21932
|
+
// No cookie header is set before this gate; auth routes own their headers separately.
|
|
21933
|
+
if (upgrade)
|
|
21934
|
+
res.setHeader("Set-Cookie", upgrade);
|
|
21935
|
+
}
|
|
21650
21936
|
}
|
|
21651
21937
|
if (req.method === "GET" && req.url === "/health") {
|
|
21652
21938
|
const health = this.getFleetHealth();
|
|
@@ -22028,6 +22314,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
22028
22314
|
return;
|
|
22029
22315
|
}
|
|
22030
22316
|
const listener = createPreviewListener({ settings, healthPort: boundPort, config: this.fleetConfig,
|
|
22317
|
+
canvasWorkerEnabled: () => this.canvasWorkerEnabled(),
|
|
22031
22318
|
// #1723: with a web.preview_origin, the open public link's dashboard frames this listener's preview too.
|
|
22032
22319
|
extraOrigins: () => { const o = settings.origin ? this.publicWebLink?.openDashboardOrigin() : null; return o ? [o] : []; } });
|
|
22033
22320
|
this.previewListener = listener;
|
|
@@ -22205,7 +22492,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
22205
22492
|
return {};
|
|
22206
22493
|
} })(),
|
|
22207
22494
|
// #2118: only this Chat's placement content; guest projection drops global revisions.
|
|
22208
|
-
effect_chat_v: createHash("sha256").update(JSON.stringify(this.themeStore.get(name).placements.effects)).digest("hex").slice(0, 32),
|
|
22495
|
+
effect_chat_v: this.canvasWorkerEnabled() ? createHash("sha256").update(JSON.stringify(this.themeStore.get(name).placements.effects)).digest("hex").slice(0, 32) : null,
|
|
22209
22496
|
// #1958: the identity colour's tiers (the page resolves them: the agent's only while it shows agents' themes).
|
|
22210
22497
|
color: { ...(colors.get(name)?.color ? { person: colors.get(name).color } : {}), ...(image.agentColor ? { agent: image.agentColor } : {}),
|
|
22211
22498
|
...(image.roleColor ? { role: image.roleColor } : {}), auto: autoIdentityColor(name) },
|
|
@@ -22232,6 +22519,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
22232
22519
|
};
|
|
22233
22520
|
});
|
|
22234
22521
|
return {
|
|
22522
|
+
canvas_worker: this.canvasWorkerEnabled(),
|
|
22235
22523
|
instances,
|
|
22236
22524
|
publicLink: this.getPublicWebStatus(),
|
|
22237
22525
|
uptime: Math.floor((Date.now() - this.startedAt) / 1000),
|