@songsid/agend 2.2.0 → 2.3.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-cli.js +248 -2
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.d.ts +2 -0
- package/dist/agent-endpoint.js +25 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-file-read.d.ts +35 -0
- package/dist/agent-file-read.js +144 -0
- package/dist/agent-file-read.js.map +1 -0
- package/dist/agent-whats-new.js +8 -0
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/atomic-write.js +9 -1
- package/dist/atomic-write.js.map +1 -1
- package/dist/auth-api.js +17 -0
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/antigravity.js +3 -3
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.js +5 -1
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/launch-effort.js +1 -1
- package/dist/backend/launch-effort.js.map +1 -1
- package/dist/backend/muse.js +10 -2
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +8 -0
- package/dist/backend/types.js +10 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/cache-analysis.d.ts +47 -1
- package/dist/cache-analysis.js +68 -8
- package/dist/cache-analysis.js.map +1 -1
- package/dist/cache-report-tool.d.ts +17 -0
- package/dist/cache-report-tool.js +40 -0
- package/dist/cache-report-tool.js.map +1 -0
- package/dist/cache-service.d.ts +15 -0
- package/dist/cache-service.js +26 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +2 -0
- package/dist/channel/adapters/discord.js +17 -2
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +8 -1
- package/dist/channel/adapters/telegram.js +74 -4
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +36 -0
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +15 -0
- package/dist/cli.js +7 -3
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +22 -2
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.js +244 -20
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-context.d.ts +5 -0
- package/dist/fleet-manager.d.ts +92 -0
- package/dist/fleet-manager.js +410 -29
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +7 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +367 -0
- package/dist/instance-avatar.js +3 -0
- package/dist/instance-avatar.js.map +1 -1
- package/dist/instance-config-impact.js +3 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.js +9 -4
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/needs-you-hub.d.ts +4 -0
- package/dist/needs-you-hub.js +31 -6
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +1 -13
- package/dist/needs-you.js +3 -37
- package/dist/needs-you.js.map +1 -1
- package/dist/outbound-handlers.d.ts +7 -0
- package/dist/outbound-handlers.js +65 -10
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +74 -0
- package/dist/outbound-schemas.js +56 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +14 -0
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/repo-worktree-guard.d.ts +36 -0
- package/dist/repo-worktree-guard.js +143 -0
- package/dist/repo-worktree-guard.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +70 -1
- package/dist/scheduler/scheduler.js +256 -17
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/service-installer.d.ts +2 -1
- package/dist/service-installer.js +18 -3
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.js +74 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +8 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +16 -2
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +5 -0
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/tool-permissions.d.ts +5 -0
- package/dist/tool-permissions.js +61 -2
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.d.ts +10 -6
- package/dist/topic-commands.js +21 -17
- package/dist/topic-commands.js.map +1 -1
- package/dist/topic-link.d.ts +128 -0
- package/dist/topic-link.js +269 -0
- package/dist/topic-link.js.map +1 -0
- package/dist/types.d.ts +13 -0
- package/dist/ui/app-effects.js +187 -0
- package/dist/ui/chat-effect-posters.js +42 -0
- package/dist/ui/chat-effects.js +72 -0
- package/dist/ui/chat-live-replies.js +58 -0
- package/dist/ui/chat-render.js +4 -4
- package/dist/ui/chat-store.js +9 -3
- package/dist/ui/chat-strings.js +15 -0
- package/dist/ui/chat-thread.js +47 -21
- package/dist/ui/chat-tour.js +1 -1
- package/dist/ui/effect-trial-controller.js +119 -0
- package/dist/ui/effect-trial-offer.js +31 -0
- package/dist/ui/effect-trial-panel.js +37 -0
- package/dist/ui/fleet-cache.js +23 -1
- package/dist/ui/image-lightbox.js +1 -1
- package/dist/ui/panel-chat.js +85 -17
- package/dist/ui/panel-details.js +18 -5
- package/dist/ui/panel-fleet.js +3 -3
- package/dist/ui/panel-log.js +6 -6
- package/dist/ui/panel-needs.js +3 -3
- package/dist/ui/panel-settings.js +8 -5
- package/dist/ui/preview-panel.js +25 -9
- package/dist/ui/settings-appearance.js +325 -0
- package/dist/ui/settings-effects.js +201 -0
- package/dist/ui/settings-mods.js +257 -0
- package/dist/ui/settings-owner-effects.js +80 -0
- package/dist/ui/settings-strings.js +245 -0
- package/dist/ui/shared/agend-auth.js +27 -3
- package/dist/ui/shared/agent-widget.js +248 -0
- package/dist/ui/shared/app-i18n.js +2 -0
- package/dist/ui/shared/app-prefs.js +30 -10
- package/dist/ui/shared/app-route.js +23 -1
- package/dist/ui/shared/app-session.js +1 -1
- package/dist/ui/shared/app-shell.js +29 -16
- package/dist/ui/shared/app-store.js +2 -0
- package/dist/ui/shared/app-stream.js +12 -7
- package/dist/ui/shared/app-themes.js +188 -0
- package/dist/ui/shared/app.css +225 -3
- package/dist/ui/shared/app.js +6 -0
- package/dist/ui/shared/card-strings.js +28 -0
- package/dist/ui/shared/effect-app-runtime.js +248 -0
- package/dist/ui/shared/effect-baseline.js +64 -0
- package/dist/ui/shared/effect-chat-runtime.js +154 -0
- package/dist/ui/shared/effect-cursor.js +125 -0
- package/dist/ui/shared/effect-frame.js +127 -0
- package/dist/ui/shared/effect-geometry.js +122 -0
- package/dist/ui/shared/effect-host-style.js +10 -0
- package/dist/ui/shared/effect-owner-regions.js +74 -0
- package/dist/ui/shared/effect-owner-source.js +115 -0
- package/dist/ui/shared/effect-policy.js +137 -0
- package/dist/ui/shared/effect-poster-reader.js +98 -0
- package/dist/ui/shared/effect-posters.js +117 -0
- package/dist/ui/shared/effect-profiler.js +162 -0
- package/dist/ui/shared/effect-profiles.js +65 -0
- package/dist/ui/shared/effect-registry.js +496 -0
- package/dist/ui/shared/effect-runtime-loader.js +234 -0
- package/dist/ui/shared/effect-safety.js +153 -0
- package/dist/ui/shared/effect-stock-snapshot.js +34 -0
- package/dist/ui/shared/effect-stock-trust.js +165 -0
- package/dist/ui/shared/effect-trial-source.js +103 -0
- package/dist/ui/shared/effect-trial-stage.js +147 -0
- package/dist/ui/shared/header-tools.js +1 -1
- package/dist/ui/shared/identity-card.js +202 -0
- package/dist/ui/shared/identity-color.js +261 -0
- package/dist/ui/shared/instance-nav.js +37 -19
- package/dist/ui/shared/owner-css.js +221 -0
- package/dist/ui/shared/owner-effect-prefs.js +117 -0
- package/dist/ui/shared/panel-view.js +81 -8
- package/dist/ui/shared/signin.js +2 -0
- package/dist/ui/shared/ui-dialog.js +1 -1
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/shared/ui-menu.js +1 -1
- package/dist/ui/shared/ui-toast.js +1 -1
- package/dist/ui/shared/usage-dialog.js +14 -2
- package/dist/ui/shared/view-strings.js +36 -2
- package/dist/ui/signin.html +1 -0
- package/dist/usage/providers.d.ts +3 -0
- package/dist/usage/providers.js +4 -3
- package/dist/usage/providers.js.map +1 -1
- package/dist/view-api.d.ts +29 -0
- package/dist/view-api.js +188 -8
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +42 -0
- package/dist/web-api.js +898 -36
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-limits.d.ts +82 -0
- package/dist/web-appearance-limits.js +83 -0
- package/dist/web-appearance-limits.js.map +1 -0
- package/dist/web-appearance-policy.d.ts +66 -0
- package/dist/web-appearance-policy.js +94 -0
- package/dist/web-appearance-policy.js.map +1 -0
- package/dist/web-appearance-tools.d.ts +50 -0
- package/dist/web-appearance-tools.js +105 -0
- package/dist/web-appearance-tools.js.map +1 -0
- package/dist/web-appearance-view.d.ts +99 -0
- package/dist/web-appearance-view.js +163 -0
- package/dist/web-appearance-view.js.map +1 -0
- package/dist/web-appearance.d.ts +79 -0
- package/dist/web-appearance.js +298 -0
- package/dist/web-appearance.js.map +1 -0
- package/dist/web-asset-parts.d.ts +93 -0
- package/dist/web-asset-parts.js +133 -0
- package/dist/web-asset-parts.js.map +1 -0
- package/dist/web-auth.js +17 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-card.d.ts +96 -0
- package/dist/web-card.js +232 -0
- package/dist/web-card.js.map +1 -0
- package/dist/web-chat-history.d.ts +13 -0
- package/dist/web-chat-history.js +23 -2
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-effect-bundle.d.ts +28 -0
- package/dist/web-effect-bundle.js +126 -0
- package/dist/web-effect-bundle.js.map +1 -0
- package/dist/web-effect-canonical.d.ts +8 -0
- package/dist/web-effect-canonical.js +27 -0
- package/dist/web-effect-canonical.js.map +1 -0
- package/dist/web-effect-library.d.ts +114 -0
- package/dist/web-effect-library.js +530 -0
- package/dist/web-effect-library.js.map +1 -0
- package/dist/web-effect-poster.d.ts +8 -0
- package/dist/web-effect-poster.js +52 -0
- package/dist/web-effect-poster.js.map +1 -0
- package/dist/web-effect-profiles.d.ts +118 -0
- package/dist/web-effect-profiles.js +180 -0
- package/dist/web-effect-profiles.js.map +1 -0
- package/dist/web-effect-service.d.ts +86 -0
- package/dist/web-effect-service.js +314 -0
- package/dist/web-effect-service.js.map +1 -0
- package/dist/web-effect-tools.d.ts +47 -0
- package/dist/web-effect-tools.js +129 -0
- package/dist/web-effect-tools.js.map +1 -0
- package/dist/web-effect-trials.d.ts +86 -0
- package/dist/web-effect-trials.js +204 -0
- package/dist/web-effect-trials.js.map +1 -0
- package/dist/web-effect-upload.d.ts +27 -0
- package/dist/web-effect-upload.js +185 -0
- package/dist/web-effect-upload.js.map +1 -0
- package/dist/web-effect-worker.d.ts +8 -0
- package/dist/web-effect-worker.js +170 -0
- package/dist/web-effect-worker.js.map +1 -0
- package/dist/web-effect.d.ts +103 -0
- package/dist/web-effect.js +153 -0
- package/dist/web-effect.js.map +1 -0
- package/dist/web-identity-color.d.ts +87 -0
- package/dist/web-identity-color.js +209 -0
- package/dist/web-identity-color.js.map +1 -0
- package/dist/web-identity-palette.d.ts +113 -0
- package/dist/web-identity-palette.js +84 -0
- package/dist/web-identity-palette.js.map +1 -0
- package/dist/web-image-meta.d.ts +25 -0
- package/dist/web-image-meta.js +200 -0
- package/dist/web-image-meta.js.map +1 -0
- package/dist/web-mod-agent.d.ts +50 -0
- package/dist/web-mod-agent.js +205 -0
- package/dist/web-mod-agent.js.map +1 -0
- package/dist/web-mod-bundle.d.ts +149 -0
- package/dist/web-mod-bundle.js +470 -0
- package/dist/web-mod-bundle.js.map +1 -0
- package/dist/web-mod-export.d.ts +59 -0
- package/dist/web-mod-export.js +79 -0
- package/dist/web-mod-export.js.map +1 -0
- package/dist/web-mod-store.d.ts +283 -0
- package/dist/web-mod-store.js +625 -0
- package/dist/web-mod-store.js.map +1 -0
- package/dist/web-org.d.ts +3 -3
- package/dist/web-org.js +11 -17
- package/dist/web-org.js.map +1 -1
- package/dist/web-owner-css-store.d.ts +180 -0
- package/dist/web-owner-css-store.js +384 -0
- package/dist/web-owner-css-store.js.map +1 -0
- package/dist/web-owner-css.d.ts +58 -0
- package/dist/web-owner-css.js +552 -0
- package/dist/web-owner-css.js.map +1 -0
- package/dist/web-prefs.d.ts +48 -0
- package/dist/web-prefs.js +209 -33
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +9 -1
- package/dist/web-preview.js +24 -7
- package/dist/web-preview.js.map +1 -1
- package/dist/web-settings-patch.d.ts +18 -0
- package/dist/web-settings-patch.js +51 -0
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +1 -1
- package/dist/web-shell-routes.js +1 -1
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-theme-store.d.ts +218 -0
- package/dist/web-theme-store.js +516 -0
- package/dist/web-theme-store.js.map +1 -0
- package/dist/web-theme.d.ts +121 -0
- package/dist/web-theme.js +407 -0
- package/dist/web-theme.js.map +1 -0
- package/dist/web-widget.d.ts +72 -0
- package/dist/web-widget.js +245 -0
- package/dist/web-widget.js.map +1 -0
- package/package.json +1 -1
- package/templates/systemd.service.ejs +8 -0
package/dist/fleet-manager.js
CHANGED
|
@@ -139,14 +139,31 @@ import { LogService } from "./log-service.js";
|
|
|
139
139
|
import { WebChatHistory, WEB_CHAT_TEXT_MAX, isWebMessageId, newWebMessageId } from "./web-chat-history.js";
|
|
140
140
|
import { WebChatDiskStore } from "./web-chat-store.js";
|
|
141
141
|
import { SeenUsers, resolveMentions } from "./web-mentions.js";
|
|
142
|
-
import { WebPrefsStore } from "./web-prefs.js";
|
|
142
|
+
import { OPERATOR, WebPrefsStore } from "./web-prefs.js";
|
|
143
|
+
import { agendVersion } from "./agent-whats-new.js";
|
|
144
|
+
import { OwnerCssStore } from "./web-owner-css-store.js";
|
|
145
|
+
import { ThemeStore } from "./web-theme-store.js";
|
|
146
|
+
import { WebAppearance } from "./web-appearance.js";
|
|
147
|
+
import { WebEffectLibrary } from "./web-effect-library.js";
|
|
148
|
+
import { WebEffectTrials } from "./web-effect-trials.js";
|
|
149
|
+
import { WebEffectService } from "./web-effect-service.js";
|
|
150
|
+
import { WebEffectProfiles } from "./web-effect-profiles.js";
|
|
151
|
+
import { WebEffectTools, WEB_EFFECT_TOOLS } from "./web-effect-tools.js";
|
|
152
|
+
import { resolveEffectParams } from "./web-effect.js";
|
|
153
|
+
import { cw1EffectChecker } from "./web-mod-bundle.js";
|
|
154
|
+
import { effectiveAppearanceLimits } from "./web-appearance-limits.js";
|
|
143
155
|
import { WEB_NAME_FALLBACK, safeWebName, validateWebName, webNameInUse, webPersonKey } from "./web-profile.js";
|
|
144
156
|
import { ReplyButtonStore, parseReplyButtons, replyButtonClickText, replyButtonsFallbackText, REPLY_BUTTON_PREFIX } from "./reply-buttons.js";
|
|
145
157
|
import { ReplyButtonsController } from "./reply-buttons-controller.js";
|
|
146
158
|
import { parsePoll } from "./reply-polls.js";
|
|
147
159
|
import { isUploadRequest, limitRequestReceive, publicAttachment, sweepOrphanedUploads, UPLOAD_REQUEST_TIMEOUT_MS, uploadLimits, WebFileLedger } from "./web-upload.js";
|
|
148
160
|
import { REPLY_FILES_MAX_BYTES, snapshotReplyFile, sweepReplySnapshots, webFilesDir } from "./web-reply-files.js";
|
|
149
|
-
import { handleViewRequest, isViewPath, profileIdentities, resolveInstanceIdentity } from "./view-api.js";
|
|
161
|
+
import { cardPicture, handleViewRequest, humanCards, identityColors, isViewPath, profileIdentities, resolveInstanceIdentity } from "./view-api.js";
|
|
162
|
+
import { autoIdentityColor } from "./web-identity-color.js";
|
|
163
|
+
import { AppearanceTools } from "./web-appearance-tools.js";
|
|
164
|
+
import { cardVersion, resolveCard } from "./web-card.js";
|
|
165
|
+
import { ModStore } from "./web-mod-store.js";
|
|
166
|
+
import { AgentMods } from "./web-mod-agent.js";
|
|
150
167
|
import { downloadStickerImage } from "./channel/sticker-download.js";
|
|
151
168
|
import { WebImageCache, imageMimeForFile, WEB_IMAGE_MAX_AGE_MS, WEB_IMAGE_MAX_BYTES, WEB_IMAGE_RETRY_MS } from "./web-images.js";
|
|
152
169
|
import { accentColor, avatarFor, ownBotConnections } from "./instance-avatar.js";
|
|
@@ -192,7 +209,8 @@ import { NeedsYouHub } from "./needs-you-hub.js";
|
|
|
192
209
|
import { WEB_CHAT_NOTICE, claimNotice, hasWebChat, releaseNotice, upgradeNoticesPath } from "./upgrade-notices.js";
|
|
193
210
|
import { GENERAL_PAUSE_ERROR, isGeneralInstance } from "./general-instance.js";
|
|
194
211
|
import { buildOrgChart } from "./web-org.js";
|
|
195
|
-
import {
|
|
212
|
+
import { GENERAL_POINTING_REFUSALS, SNOWFLAKE, hasPlaceRefs, placeRef, webRoute, webTextLinks, placeSuffix, renderPlaceRefs, stripPlaceRefs, topicUrl, worldPlaceOf } from "./topic-link.js";
|
|
213
|
+
import { CacheService, cacheWindow } from "./cache-service.js";
|
|
196
214
|
import { runWebCommand } from "./web-commands.js";
|
|
197
215
|
import { CompactWatch, launchIdentity } from "./compact-watch.js";
|
|
198
216
|
import { agyLegacyAgentsFile, danglingSymlink, migrateAgyAgentsFile } from "./backend/antigravity.js";
|
|
@@ -884,6 +902,183 @@ export class FleetManager {
|
|
|
884
902
|
return true;
|
|
885
903
|
return false;
|
|
886
904
|
}
|
|
905
|
+
themeStoreInstance;
|
|
906
|
+
webAppearanceService;
|
|
907
|
+
/** #1629: every instance's theme, placements, assets and lock (web-themes.json). */
|
|
908
|
+
get themeStore() {
|
|
909
|
+
// r4.1 Q3 (b): the card's limits are read from fleet.yaml at each upload (a change applies without a restart).
|
|
910
|
+
return (this.themeStoreInstance ??= new ThemeStore(this.dataDir, { limits: () => effectiveAppearanceLimits(this.fleetConfig?.web?.appearance).limits, effectsDropped: ids => this.effectLibrary.collectUnreferenced(ids) }));
|
|
911
|
+
}
|
|
912
|
+
/** #1629: the theme tools, a person's reset and lock, and the one appearance rate limiter (shared with effects). */
|
|
913
|
+
get webAppearance() {
|
|
914
|
+
return (this.webAppearanceService ??= new WebAppearance(this.themeStore, () => this.fleetConfig?.web ?? null, () => { try {
|
|
915
|
+
this.emitSseEvent("status", this.getUiStatus());
|
|
916
|
+
}
|
|
917
|
+
catch { /* the next frame carries the rev */ } }));
|
|
918
|
+
}
|
|
919
|
+
effectLibraryInstance;
|
|
920
|
+
effectTrialsInstance;
|
|
921
|
+
/** Lazy, single-owner storage. Register every host reference source before any operation can collect. */
|
|
922
|
+
get effectLibrary() {
|
|
923
|
+
if (!this.effectLibraryInstance) {
|
|
924
|
+
const library = new WebEffectLibrary(this.dataDir);
|
|
925
|
+
// Providers are lazy; publish only after every registration succeeds.
|
|
926
|
+
library.registerReferenceSource("themes", () => {
|
|
927
|
+
if (!this.themeStore.readable())
|
|
928
|
+
throw new Error("themes_unreadable");
|
|
929
|
+
return this.themeStore.placedEffects().flatMap(row => row.ids.map(id => ({ id, holder: `instance:${row.name}` })));
|
|
930
|
+
});
|
|
931
|
+
library.registerReferenceSource("prefs", () => this.webPrefs.ownerEffectReferences());
|
|
932
|
+
library.registerReferenceSource("mods", () => {
|
|
933
|
+
const mods = this.mods.list();
|
|
934
|
+
if (mods.unreadable)
|
|
935
|
+
throw new Error("mods_unreadable");
|
|
936
|
+
return mods.mods.flatMap(mod => mod.effects.map(effect => ({ id: effect.id, holder: `mod:${mod.id}` })));
|
|
937
|
+
});
|
|
938
|
+
this.effectTrialsInstance = new WebEffectTrials({ library, known: name => this.isKnownInstance(name) });
|
|
939
|
+
this.effectLibraryInstance = library;
|
|
940
|
+
}
|
|
941
|
+
return this.effectLibraryInstance;
|
|
942
|
+
}
|
|
943
|
+
/** Volatile Try offers and session-bound runs share this library. */
|
|
944
|
+
get effectTrials() { if (!this.effectTrialsInstance)
|
|
945
|
+
void this.effectLibrary; return this.effectTrialsInstance; }
|
|
946
|
+
effectProfilesInstance;
|
|
947
|
+
get effectProfiles() { return (this.effectProfilesInstance ??= new WebEffectProfiles(this.dataDir)); }
|
|
948
|
+
effectServiceInstance;
|
|
949
|
+
get effectService() {
|
|
950
|
+
return (this.effectServiceInstance ??= new WebEffectService({ library: this.effectLibrary, themes: this.themeStore, profiles: this.effectProfiles, prefs: this.webPrefs, trials: this.effectTrials,
|
|
951
|
+
known: name => this.isKnownInstance(name), changed: () => { try {
|
|
952
|
+
this.emitSseEvent("status", this.getUiStatus());
|
|
953
|
+
}
|
|
954
|
+
catch { /* next status carries revision */ } } }));
|
|
955
|
+
}
|
|
956
|
+
effectToolsService;
|
|
957
|
+
/** Caller identity is supplied only by the owned IPC connection / verified agent endpoint token. */
|
|
958
|
+
get effectTools() {
|
|
959
|
+
return (this.effectToolsService ??= new WebEffectTools({
|
|
960
|
+
library: this.effectLibrary, limiter: this.webAppearance.limiter,
|
|
961
|
+
authorize: (name, buckets) => !this.isKnownInstance(name) ? { error: "unknown_instance" } : this.webAppearance.authorizeEffect(name, buckets),
|
|
962
|
+
placed: name => this.themeStore.get(name).placements.effects,
|
|
963
|
+
place: (name, effects) => {
|
|
964
|
+
const before = this.themeStore.get(name).placements.effects;
|
|
965
|
+
const explicit = new Set(effects.filter(e => typeof e !== "string").map(e => e.id));
|
|
966
|
+
const w = this.themeStore.setPlacements(name, { effects }, "agent", what => {
|
|
967
|
+
// An unchanged human/private placement may be retained, never exposed or newly acquired by an agent.
|
|
968
|
+
const r = explicit.has(what.id) ? this.effectLibrary.read(what.id, { kind: "agent", id: name })
|
|
969
|
+
: this.effectLibrary.readReferenced(what.id, `instance:${name}`);
|
|
970
|
+
if (!r.ok)
|
|
971
|
+
return r.error;
|
|
972
|
+
const params = explicit.has(what.id) ? what.params : before.find(e => e.id === what.id)?.params;
|
|
973
|
+
return resolveEffectParams(r.entry.manifest, params ?? {}).ok ? null : "effect_params";
|
|
974
|
+
});
|
|
975
|
+
return w.ok ? { ok: true, rev: w.rev } : { ok: false, error: w.error };
|
|
976
|
+
},
|
|
977
|
+
changed: () => { try {
|
|
978
|
+
this.emitSseEvent("status", this.getUiStatus());
|
|
979
|
+
}
|
|
980
|
+
catch { /* later status carries placements */ } },
|
|
981
|
+
// A nonmutating own/shared offer is not an appearance write. Only an explicit local-admin click may start it.
|
|
982
|
+
preview: async (instance, id) => {
|
|
983
|
+
const result = this.effectTrials.offer(instance, id);
|
|
984
|
+
if (!result.ok)
|
|
985
|
+
return { error: "not_found" };
|
|
986
|
+
const present = this.webChatHistory.list(instance).some(row => row.effectTrial?.offer === result.offer.offer);
|
|
987
|
+
if (!present && !this.publishEffectTrial(instance, result.offer))
|
|
988
|
+
return { error: "trial_unavailable" };
|
|
989
|
+
return { id, status: "offered", expiresAt: result.offer.expiresAt, message: "A trial card is in your web chat. A signed-in local admin can start it; nothing was applied." };
|
|
990
|
+
},
|
|
991
|
+
profiles: id => this.effectProfiles.read(id),
|
|
992
|
+
profileSummary: id => this.effectService.profileSummary(id),
|
|
993
|
+
profileSummaries: ids => this.effectService.profileSummaries(ids),
|
|
994
|
+
}));
|
|
995
|
+
}
|
|
996
|
+
appearanceToolsService;
|
|
997
|
+
/** #1629 r4 §1: get_web_appearance / preview_web_appearance — the caller's own look, resolved as the page resolves it. */
|
|
998
|
+
get appearanceTools() {
|
|
999
|
+
return (this.appearanceToolsService ??= new AppearanceTools({
|
|
1000
|
+
frame: (name) => (this.getUiStatus().instances.find(i => i.name === name) ?? { name }),
|
|
1001
|
+
theme: (name) => { const r = this.themeStore.get(name); return { theme: r.theme, locked: r.locked, widgets: r.placements.widgets }; },
|
|
1002
|
+
card: (name) => resolveCard(name, humanCards(this.dataDir).get(name), this.themeStore.get(name).theme.card),
|
|
1003
|
+
// #1958: the host's identity-colour settings, as the page reads them (an unreadable prefs file: the defaults).
|
|
1004
|
+
colorPrefs: () => { try {
|
|
1005
|
+
const v = this.webPrefs.read(OPERATOR).values;
|
|
1006
|
+
return { show: v["color.show"], off: v["color.off"] === true };
|
|
1007
|
+
}
|
|
1008
|
+
catch {
|
|
1009
|
+
return {};
|
|
1010
|
+
} },
|
|
1011
|
+
cardPicture: (name, slot) => cardPicture(this, name, slot),
|
|
1012
|
+
themeAsset: (name, id) => this.themeStore.asset(name, id),
|
|
1013
|
+
profileAvatar: (name) => this.webImage("avatar", name),
|
|
1014
|
+
botAvatar: (_name, key) => this.webImage("bot", key),
|
|
1015
|
+
themeAnswer: (name) => this.webAppearance.getTheme(name),
|
|
1016
|
+
}));
|
|
1017
|
+
}
|
|
1018
|
+
/** #1958: a profile, card or colour write (view-api.ts) landed: every open page gets a fresh status frame. */
|
|
1019
|
+
identityChanged() {
|
|
1020
|
+
try {
|
|
1021
|
+
this.emitSseEvent("status", this.getUiStatus());
|
|
1022
|
+
}
|
|
1023
|
+
catch { /* the next frame carries it */ }
|
|
1024
|
+
}
|
|
1025
|
+
/** A fleet.yaml instance (own key) or a ClassicBot room, now (#1629: a person's reset/lock names one). */
|
|
1026
|
+
isKnownInstance(name) {
|
|
1027
|
+
return Object.hasOwn(this.fleetConfig?.instances ?? {}, name) || (this.classicChannels?.getAll() ?? []).some((ch) => ch.instanceName === name);
|
|
1028
|
+
}
|
|
1029
|
+
modStore;
|
|
1030
|
+
/**
|
|
1031
|
+
* #1629 r4-5 / B2: installed mods use the same validated library and scoped holder claims as direct uploads.
|
|
1032
|
+
*/
|
|
1033
|
+
get mods() {
|
|
1034
|
+
return (this.modStore ??= new ModStore(this.dataDir, {
|
|
1035
|
+
themes: this.themeStore,
|
|
1036
|
+
prefs: this.webPrefs,
|
|
1037
|
+
ownerCss: this.ownerCss,
|
|
1038
|
+
effects: this.effectLibrary,
|
|
1039
|
+
context: () => ({ agendVersion: agendVersion(), limits: this.themeStore.limits(), effects: cw1EffectChecker() }),
|
|
1040
|
+
isKnownInstance: (name) => this.isKnownInstance(name),
|
|
1041
|
+
prefsIdentity: OPERATOR,
|
|
1042
|
+
}));
|
|
1043
|
+
}
|
|
1044
|
+
agentModsService;
|
|
1045
|
+
/** #1629 r4-5b: export_web_mod / propose_web_mod — the caller's own look as a file, and a proposal for a person. */
|
|
1046
|
+
get agentMods() {
|
|
1047
|
+
return (this.agentModsService ??= new AgentMods({
|
|
1048
|
+
themes: this.themeStore, mods: this.mods, limiter: this.webAppearance.limiter,
|
|
1049
|
+
webConfig: () => this.fleetConfig?.web ?? null,
|
|
1050
|
+
// The mod store's own: a proposal is checked exactly as the person's import of it will be.
|
|
1051
|
+
context: () => this.mods.checkContext(),
|
|
1052
|
+
readOwnEffect: (instance, id) => { const r = this.effectLibrary.read(id, { kind: "agent", id: instance }); return r.ok ? { name: r.entry.manifest.name, files: r.entry.files } : null; },
|
|
1053
|
+
notice: (instance, text) => { try {
|
|
1054
|
+
this.emitSseEvent("message", { instance, sender: instance, role: "status", text, ts: new Date().toISOString() });
|
|
1055
|
+
}
|
|
1056
|
+
catch { /* the page reads it from Settings too */ } },
|
|
1057
|
+
changed: () => { try {
|
|
1058
|
+
this.emitSseEvent("status", this.getUiStatus());
|
|
1059
|
+
}
|
|
1060
|
+
catch { /* the next frame */ } },
|
|
1061
|
+
}));
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* #1629 r4-5c: an effect as this person may carry it away in an export — their own claim, or one a person shared —
|
|
1065
|
+
* read with their principal (Sol's ACL). Being able to view or run a placed effect never grants packaging someone
|
|
1066
|
+
* else's private claim: the library's reference read is runtime access, not an export path. Null: not theirs.
|
|
1067
|
+
*/
|
|
1068
|
+
modPlacedEffect(person, id) {
|
|
1069
|
+
const r = this.effectLibrary.read(id, { kind: "human", id: person });
|
|
1070
|
+
return r.ok ? { name: r.entry.manifest.name, files: r.entry.files } : null;
|
|
1071
|
+
}
|
|
1072
|
+
/** #1629 r4-5: an instance's card text as every viewer sees it (a person's fields over the agent's). */
|
|
1073
|
+
modCardText(name) {
|
|
1074
|
+
const card = resolveCard(name, humanCards(this.dataDir).get(name), this.themeStore.get(name).theme.card);
|
|
1075
|
+
return { ...(card.tagline ? { tagline: card.tagline.text } : {}), ...(card.bio ? { bio: card.bio.text } : {}) };
|
|
1076
|
+
}
|
|
1077
|
+
ownerCssStore;
|
|
1078
|
+
/** #1629 r4 §3: each person's own CSS; which version is in force is their `css.active` in the web preferences. */
|
|
1079
|
+
get ownerCss() {
|
|
1080
|
+
return (this.ownerCssStore ??= new OwnerCssStore(this.dataDir, this.webPrefs));
|
|
1081
|
+
}
|
|
887
1082
|
get webPrefs() {
|
|
888
1083
|
// #1664: a favorite must be an instance of this fleet now — fleet.yaml's (own keys) or a ClassicBot room's.
|
|
889
1084
|
return (this.webPrefsStore ??= new WebPrefsStore(this.dataDir, {
|
|
@@ -3704,7 +3899,11 @@ export class FleetManager {
|
|
|
3704
3899
|
// A command off for want of fleet admins names where they go (`{0}` in its disabled reply).
|
|
3705
3900
|
if (decision.reply === spec.disabled)
|
|
3706
3901
|
args.push(this.adminListKey(adapterId));
|
|
3707
|
-
|
|
3902
|
+
// #1990: a refusal that sends the person to General links to it — not in a ClassicBot room, whose readers need
|
|
3903
|
+
// not be in the fleet's guild.
|
|
3904
|
+
const toGeneral = GENERAL_POINTING_REFUSALS.has(key) && scope !== "classic" && scope !== "none";
|
|
3905
|
+
const reply = toGeneral ? this.renderPlaces(adapterId, t(key, ...args) + placeSuffix({ kind: "general" }, "General")).text : t(key, ...args);
|
|
3906
|
+
await data.respond(reply).catch(() => { });
|
|
3708
3907
|
return;
|
|
3709
3908
|
}
|
|
3710
3909
|
if (data.command === "start") {
|
|
@@ -5302,7 +5501,8 @@ export class FleetManager {
|
|
|
5302
5501
|
const owner = id ? this.dashboardOwner(msg.userId, id, msg.chatId, msg.threadId, msg.username) : null;
|
|
5303
5502
|
if (!owner) {
|
|
5304
5503
|
const adapter = id ? this.adapters.get(id) : undefined;
|
|
5305
|
-
|
|
5504
|
+
const out = this.renderPlaces(id, t("dashboard.public_general") + placeSuffix({ kind: "general" }, "General"), { threadId: msg.threadId });
|
|
5505
|
+
await adapter?.sendText(msg.chatId, out.text, out.opts);
|
|
5306
5506
|
return;
|
|
5307
5507
|
}
|
|
5308
5508
|
if (!await this.showDashboardMenu(owner)) {
|
|
@@ -6273,7 +6473,7 @@ export class FleetManager {
|
|
|
6273
6473
|
...DEFAULT_SCHEDULER_CONFIG,
|
|
6274
6474
|
...this.fleetConfig?.defaults.scheduler,
|
|
6275
6475
|
};
|
|
6276
|
-
this.scheduler = new Scheduler(join(this.dataDir, "scheduler.db"), (schedule, runId, retry) => this.handleScheduleTrigger(schedule, runId, retry), schedulerConfig, (name) => this.fleetConfig?.instances?.[name] != null || !!this.classicChannels?.getAll().some(ch => ch.instanceName === name), (schedule, retry, reason) => this.scheduleRetryDropped(schedule, retry, reason));
|
|
6476
|
+
this.scheduler = new Scheduler(join(this.dataDir, "scheduler.db"), (schedule, runId, retry) => this.handleScheduleTrigger(schedule, runId, retry), schedulerConfig, (name) => this.fleetConfig?.instances?.[name] != null || !!this.classicChannels?.getAll().some(ch => ch.instanceName === name), (schedule, retry, reason) => this.scheduleRetryDropped(schedule, retry, reason), this.logger);
|
|
6277
6477
|
this.scheduler.init();
|
|
6278
6478
|
this.logger.info("Scheduler initialized");
|
|
6279
6479
|
// Tips share the daily-report clock but remain an independent internal
|
|
@@ -7505,6 +7705,48 @@ export class FleetManager {
|
|
|
7505
7705
|
this.adapterRestarting.delete(id);
|
|
7506
7706
|
}
|
|
7507
7707
|
}
|
|
7708
|
+
/**
|
|
7709
|
+
* #1990: how a reader in world `worldId`'s group reaches a place token's target — undefined (plain text) for another
|
|
7710
|
+
* world's instance, an instance without a fleet topic (a ClassicBot room may be anywhere), or an id no link is built
|
|
7711
|
+
* from. Discord: the channel mention; Telegram: the topic URL (General's topic included).
|
|
7712
|
+
*/
|
|
7713
|
+
placeLink(worldId, target) {
|
|
7714
|
+
const world = this.worlds.get(worldId ?? "");
|
|
7715
|
+
if (!world)
|
|
7716
|
+
return undefined;
|
|
7717
|
+
let topic;
|
|
7718
|
+
if (target.kind === "general") {
|
|
7719
|
+
const notice = this.fleetNoticeTarget(world.id);
|
|
7720
|
+
if (!notice || notice.chatId !== world.groupId || notice.opts.threadId === undefined)
|
|
7721
|
+
return undefined;
|
|
7722
|
+
topic = String(notice.opts.threadId);
|
|
7723
|
+
}
|
|
7724
|
+
else {
|
|
7725
|
+
if (this.getInstanceAdapterId(target.name) !== world.id)
|
|
7726
|
+
return undefined;
|
|
7727
|
+
const topicId = this.fleetConfig?.instances[target.name]?.topic_id;
|
|
7728
|
+
if (topicId == null)
|
|
7729
|
+
return undefined;
|
|
7730
|
+
topic = String(topicId);
|
|
7731
|
+
}
|
|
7732
|
+
if (world.type === "discord")
|
|
7733
|
+
return SNOWFLAKE.test(topic) && SNOWFLAKE.test(world.groupId) ? { mention: `<#${topic}>` } : undefined;
|
|
7734
|
+
const url = topicUrl(worldPlaceOf(world), topic, { general: target.kind === "general" });
|
|
7735
|
+
return url ? { url } : undefined;
|
|
7736
|
+
}
|
|
7737
|
+
/** #1990: a message for world `worldId`'s group with its place tokens rendered (links as text or Telegram entities). */
|
|
7738
|
+
renderPlaces(worldId, text, opts) {
|
|
7739
|
+
if (!hasPlaceRefs(text))
|
|
7740
|
+
return { text, opts };
|
|
7741
|
+
const type = this.worlds.get(worldId ?? "")?.type;
|
|
7742
|
+
const surface = type === "discord" ? "discord" : type === "telegram" ? "telegram" : "plain";
|
|
7743
|
+
const out = renderPlaceRefs(text, surface, target => this.placeLink(worldId, target));
|
|
7744
|
+
return { text: out.text, opts: out.textLinks.length ? { ...opts, textLinks: out.textLinks } : opts };
|
|
7745
|
+
}
|
|
7746
|
+
/** #1990: renderPlaces for the world that owns `instanceName`'s topic. */
|
|
7747
|
+
renderPlacesFor(instanceName, text, opts) {
|
|
7748
|
+
return this.renderPlaces(this.getInstanceAdapterId(instanceName), text, opts);
|
|
7749
|
+
}
|
|
7508
7750
|
/** Handle inbound message — transcribe voice if present, then route */
|
|
7509
7751
|
findGeneralInstance(adapterId) {
|
|
7510
7752
|
if (!this.fleetConfig)
|
|
@@ -9177,6 +9419,19 @@ export class FleetManager {
|
|
|
9177
9419
|
}
|
|
9178
9420
|
dispatchTypedIpc(name, msg) {
|
|
9179
9421
|
const type = String(msg.type);
|
|
9422
|
+
if (type.startsWith("fleet_") && WEB_EFFECT_TOOLS.has(type.slice(6))) {
|
|
9423
|
+
const ipc = this.instanceIpcClients.get(name), fleetRequestId = msg.fleetRequestId;
|
|
9424
|
+
if (!ipc || typeof fleetRequestId !== "string")
|
|
9425
|
+
return;
|
|
9426
|
+
void this.handleWebThemeHttp(name, type.slice(6), (msg.payload ?? {})).then(result => {
|
|
9427
|
+
if (this.instanceIpcClients.get(name) === ipc)
|
|
9428
|
+
ipc.send({ type: "fleet_web_theme_response", fleetRequestId, result });
|
|
9429
|
+
}, err => {
|
|
9430
|
+
if (this.instanceIpcClients.get(name) === ipc)
|
|
9431
|
+
ipc.send({ type: "fleet_web_theme_response", fleetRequestId, error: err instanceof Error ? err.message : "Effect operation failed" });
|
|
9432
|
+
}).catch(err => this.logger.debug({ err }, "Effect tool response could not be written"));
|
|
9433
|
+
return;
|
|
9434
|
+
}
|
|
9180
9435
|
if (type.startsWith("fleet_schedule_")) {
|
|
9181
9436
|
this.handleScheduleCrud(name, msg);
|
|
9182
9437
|
return;
|
|
@@ -9202,6 +9457,36 @@ export class FleetManager {
|
|
|
9202
9457
|
this.handlePersonaEmoji(name, msg);
|
|
9203
9458
|
return;
|
|
9204
9459
|
}
|
|
9460
|
+
if (type === "fleet_get_web_theme" || type === "fleet_set_web_theme" || type === "fleet_set_web_theme_asset" || type === "fleet_reset_web_theme"
|
|
9461
|
+
|| type === "fleet_get_web_appearance" || type === "fleet_preview_web_appearance"
|
|
9462
|
+
|| type === "fleet_set_web_widget" || type === "fleet_remove_web_widget"
|
|
9463
|
+
|| type === "fleet_export_web_mod" || type === "fleet_propose_web_mod") {
|
|
9464
|
+
this.handleWebTheme(name, msg);
|
|
9465
|
+
return;
|
|
9466
|
+
}
|
|
9467
|
+
}
|
|
9468
|
+
/**
|
|
9469
|
+
* #1629: the caller's own web theme over IPC (the agent endpoint calls the same service). `name` is the instance this
|
|
9470
|
+
* IPC connection belongs to — never anything the message says. A refusal is a result the agent reads (its `error`
|
|
9471
|
+
* and what to do), not a transport error.
|
|
9472
|
+
*/
|
|
9473
|
+
handleWebTheme(instanceName, msg) {
|
|
9474
|
+
const fleetRequestId = msg.fleetRequestId;
|
|
9475
|
+
const ipc = this.instanceIpcClients.get(instanceName);
|
|
9476
|
+
if (!ipc)
|
|
9477
|
+
return;
|
|
9478
|
+
const tool = String(msg.type).replace(/^fleet_/, "");
|
|
9479
|
+
try {
|
|
9480
|
+
const args = (msg.payload ?? {});
|
|
9481
|
+
const result = tool === "get_web_appearance" || tool === "preview_web_appearance"
|
|
9482
|
+
? this.appearanceTools.handle(instanceName, tool, args)
|
|
9483
|
+
: tool === "export_web_mod" || tool === "propose_web_mod" ? this.agentMods.handle(instanceName, tool, args)
|
|
9484
|
+
: this.webAppearance.handleTool(instanceName, tool, args);
|
|
9485
|
+
ipc.send({ type: "fleet_web_theme_response", fleetRequestId, result });
|
|
9486
|
+
}
|
|
9487
|
+
catch (err) {
|
|
9488
|
+
ipc.send({ type: "fleet_web_theme_response", fleetRequestId, error: err.message });
|
|
9489
|
+
}
|
|
9205
9490
|
}
|
|
9206
9491
|
/** `list_emojis` / `set_persona_emoji` / `preview_emojis` / `list_stickers` / `preview_stickers` over IPC; the agent endpoint calls the same methods. */
|
|
9207
9492
|
handlePersonaEmoji(instanceName, msg) {
|
|
@@ -9606,6 +9891,19 @@ export class FleetManager {
|
|
|
9606
9891
|
async handleSetPersonaEmojiHttp(instance, args) {
|
|
9607
9892
|
return this.setPersonaEmoji(instance, args);
|
|
9608
9893
|
}
|
|
9894
|
+
/** #1629: `agend-agent theme …` — the same service as the tools; `instance` is the token's. */
|
|
9895
|
+
async handleWebThemeHttp(instance, tool, args) {
|
|
9896
|
+
if (WEB_EFFECT_TOOLS.has(tool)) {
|
|
9897
|
+
if (!this.isKnownInstance(instance))
|
|
9898
|
+
return { error: "unknown_instance" };
|
|
9899
|
+
return this.effectTools.handle(instance, tool, args);
|
|
9900
|
+
}
|
|
9901
|
+
if (tool === "get_web_appearance" || tool === "preview_web_appearance")
|
|
9902
|
+
return this.appearanceTools.handle(instance, tool, args);
|
|
9903
|
+
if (tool === "export_web_mod" || tool === "propose_web_mod")
|
|
9904
|
+
return this.agentMods.handle(instance, tool, args);
|
|
9905
|
+
return this.webAppearance.handleTool(instance, tool, args);
|
|
9906
|
+
}
|
|
9609
9907
|
async handlePreviewEmojisHttp(instance, args) {
|
|
9610
9908
|
return this.previewEmojis(instance, args);
|
|
9611
9909
|
}
|
|
@@ -10308,6 +10606,17 @@ export class FleetManager {
|
|
|
10308
10606
|
catch (err) {
|
|
10309
10607
|
this.logger.warn({ err: err.message, name }, "Dropping a removed instance from web favorites failed");
|
|
10310
10608
|
}
|
|
10609
|
+
try {
|
|
10610
|
+
this.themeStore.forget(name);
|
|
10611
|
+
this.webAppearance.forget(name);
|
|
10612
|
+
this.appearanceTools.forget(name);
|
|
10613
|
+
this.effectToolsService?.forget(name);
|
|
10614
|
+
this.mods.forgetInstance(name);
|
|
10615
|
+
this.agentMods.forget(name);
|
|
10616
|
+
}
|
|
10617
|
+
catch (err) {
|
|
10618
|
+
this.logger.warn({ err: err.message, name }, "Dropping a removed instance's web theme failed");
|
|
10619
|
+
}
|
|
10311
10620
|
// Clean up statusline watcher + instance directory
|
|
10312
10621
|
this.statuslineWatcher.unwatch(name);
|
|
10313
10622
|
// instanceDirToClean and effectiveBackend are captured before lifecycle.remove
|
|
@@ -10596,10 +10905,25 @@ export class FleetManager {
|
|
|
10596
10905
|
const avatar = avatarFor({ name, displayName, profileVersion, bot: bot && connection ? { connection, version: bot.version } : null,
|
|
10597
10906
|
personaEmoji: cfg?.status_emojis?.delivered });
|
|
10598
10907
|
// The ring belongs to an own bot's face: only when that is what is shown (a shared bot's would ring everyone alike).
|
|
10599
|
-
|
|
10908
|
+
// #1958: the own bot's colour is also the identity colour's role tier — whatever face is shown (never a shared bot's).
|
|
10909
|
+
const roleColor = ownConnection && owner
|
|
10600
10910
|
? this.webAccent(ownConnection, owner, cfg ? (cfg.topic_id == null ? undefined : String(cfg.topic_id)) : this.classicChannels?.getChannelIdByInstance(name))
|
|
10601
10911
|
: null;
|
|
10602
|
-
|
|
10912
|
+
const accent = !profileVersion && bot && ownConnection && owner ? roleColor : null;
|
|
10913
|
+
// #1629 (first design §5, r2 (d)): the agent's theme may name its own picture and ring colour. Both come below a
|
|
10914
|
+
// person's upload (never when there is one) and ride beside the theme-free face: a viewer who turned agents'
|
|
10915
|
+
// themes off (fx.show), or a safe-mode load, shows `avatar` and `accent` as before.
|
|
10916
|
+
let theme = { v: 1 };
|
|
10917
|
+
try {
|
|
10918
|
+
theme = this.themeStore.get(name).theme;
|
|
10919
|
+
}
|
|
10920
|
+
catch { /* unreadable themes: none */ }
|
|
10921
|
+
const themeAvatar = !profileVersion && theme.avatar ? { kind: "image", src: `/ui/img/theme/${encodeURIComponent(name)}/${theme.avatar}` } : undefined;
|
|
10922
|
+
const themeAccent = !profileVersion && theme.accent ? theme.accent : undefined;
|
|
10923
|
+
return { avatar, ...(accent ? { accent } : {}), ...(themeAvatar ? { themeAvatar } : {}), ...(themeAccent ? { themeAccent } : {}),
|
|
10924
|
+
// #1958: the agent's own identity colour (theme.identity.color, else its accent) and where it would show it.
|
|
10925
|
+
...(roleColor ? { roleColor } : {}), ...(theme.identity?.color ?? theme.accent ? { agentColor: theme.identity?.color ?? theme.accent } : {}),
|
|
10926
|
+
...(theme.identity?.show ? { agentShow: theme.identity.show } : {}) };
|
|
10603
10927
|
}
|
|
10604
10928
|
/**
|
|
10605
10929
|
* GET /ui/img/bot|avatar/<key>: what is cached, never a fetch. Only a connection some instance or room speaks
|
|
@@ -11686,7 +12010,8 @@ export class FleetManager {
|
|
|
11686
12010
|
// Discord, so every such fallback failed inside the catch handler.
|
|
11687
12011
|
const target = this.fleetNoticeTarget();
|
|
11688
12012
|
if (this.adapter && target) {
|
|
11689
|
-
this.
|
|
12013
|
+
const out = this.renderPlaces(this.getPrimaryAdapterId(), body, target.opts);
|
|
12014
|
+
this.adapter.sendText(target.chatId, out.text, out.opts)
|
|
11690
12015
|
.catch(err => this.logger.warn({ err }, "Failed to send fleet error notification"));
|
|
11691
12016
|
dispatched = true;
|
|
11692
12017
|
}
|
|
@@ -11698,7 +12023,7 @@ export class FleetManager {
|
|
|
11698
12023
|
// also suppressed the next ten minutes of identical ones. Leaving the key
|
|
11699
12024
|
// unspent lets the next caller, once an adapter is up, actually get
|
|
11700
12025
|
// through. The counter is not reset either: nothing was shown.
|
|
11701
|
-
this.logger.warn({ text: body }, "Fleet error could not be delivered (no adapter or no target yet) — not consuming the throttle window");
|
|
12026
|
+
this.logger.warn({ text: stripPlaceRefs(body) }, "Fleet error could not be delivered (no adapter or no target yet) — not consuming the throttle window");
|
|
11702
12027
|
return false;
|
|
11703
12028
|
}
|
|
11704
12029
|
if (!throttled)
|
|
@@ -11736,24 +12061,28 @@ export class FleetManager {
|
|
|
11736
12061
|
const groupId = channelCfg?.group_id;
|
|
11737
12062
|
// Fleet topic instance
|
|
11738
12063
|
const threadId = this.fleetConfig?.instances[instanceName]?.topic_id;
|
|
12064
|
+
const worldId = this.getInstanceAdapterId(instanceName);
|
|
11739
12065
|
if (threadId != null && groupId) {
|
|
11740
|
-
|
|
12066
|
+
const out = this.renderPlaces(worldId, text, { threadId: String(threadId), ...extraOpts });
|
|
12067
|
+
adapter.sendText(String(groupId), out.text, out.opts)
|
|
11741
12068
|
.catch(e => this.logger.warn({ err: e, instanceName }, "Failed to send instance topic notification"));
|
|
11742
12069
|
return true;
|
|
11743
12070
|
}
|
|
11744
12071
|
// Classic instance: find its channelId from the classic manager
|
|
11745
12072
|
const classicChatId = this.classicChannels?.getChannelIdByInstance(instanceName);
|
|
11746
12073
|
if (classicChatId) {
|
|
11747
|
-
|
|
12074
|
+
// #1990: a ClassicBot room's readers need not be in the fleet's group, so its notices name places as plain text.
|
|
12075
|
+
adapter.sendText(classicChatId, stripPlaceRefs(text), extraOpts)
|
|
11748
12076
|
.catch(e => this.logger.warn({ err: e, instanceName }, "Failed to send classic notification"));
|
|
11749
12077
|
return true;
|
|
11750
12078
|
}
|
|
11751
12079
|
// Fallback: the instance has neither a topic nor a classic channel, so post
|
|
11752
12080
|
// where fleet-wide notices go. Not the bare group id: on Discord that is a
|
|
11753
12081
|
// guild, and the send fails inside the catch handler.
|
|
11754
|
-
const target = this.fleetNoticeTarget(
|
|
12082
|
+
const target = this.fleetNoticeTarget(worldId);
|
|
11755
12083
|
if (target) {
|
|
11756
|
-
|
|
12084
|
+
const out = this.renderPlaces(worldId, text, { ...target.opts, ...extraOpts });
|
|
12085
|
+
adapter.sendText(target.chatId, out.text, out.opts)
|
|
11757
12086
|
.catch(e => this.logger.warn({ err: e, instanceName }, "Failed to send notification (no topic)"));
|
|
11758
12087
|
return true;
|
|
11759
12088
|
}
|
|
@@ -11824,12 +12153,14 @@ export class FleetManager {
|
|
|
11824
12153
|
this.pendingNonceButtons.set(nonce, entry);
|
|
11825
12154
|
try {
|
|
11826
12155
|
const choices = opts.choices.map(c => ({ id: `${opts.prefix}${nonce}:${c.action}`, label: c.label }));
|
|
12156
|
+
// #1990: place names link for the group the prompt is posted in; the dashboard's copy is plain (for now).
|
|
12157
|
+
const posted = this.renderPlaces(opts.adapterId, opts.message, opts.threadId ? { threadId: opts.threadId } : undefined);
|
|
11827
12158
|
const sent = opts.deliver ? await opts.deliver(choices) : await opts.adapter.notifyAlert(opts.chatId, {
|
|
11828
12159
|
type: opts.alertType,
|
|
11829
12160
|
instanceName: opts.instanceName,
|
|
11830
|
-
message:
|
|
12161
|
+
message: posted.text,
|
|
11831
12162
|
choices,
|
|
11832
|
-
},
|
|
12163
|
+
}, posted.opts);
|
|
11833
12164
|
// Bind the nonce to the provider's canonical delivery address, not the
|
|
11834
12165
|
// logical routing input. Telegram, for example, represents General as
|
|
11835
12166
|
// AgEnD topic "1" on input but omits message_thread_id on the wire and in
|
|
@@ -11847,8 +12178,15 @@ export class FleetManager {
|
|
|
11847
12178
|
// Offered on the dashboard only once it is live on the platform (a failed post is disarmed above),
|
|
11848
12179
|
// and only if nothing claimed or expired it meanwhile.
|
|
11849
12180
|
if (WEB_MIRRORED_PROMPT_PREFIXES.has(opts.prefix) && this.pendingNonceButtons.get(nonce) === entry) {
|
|
12181
|
+
// #1990: on the dashboard an instance's name opens its terminal View (where the prompt is answered by hand).
|
|
12182
|
+
const web = renderPlaceRefs(opts.message, "web", target => {
|
|
12183
|
+
const route = target.kind === "instance" && Object.hasOwn(this.fleetConfig?.instances ?? {}, target.name) ? webRoute("view", target.name) : undefined;
|
|
12184
|
+
return route ? { url: route } : undefined;
|
|
12185
|
+
});
|
|
12186
|
+
const links = webTextLinks(web.textLinks, web.text);
|
|
11850
12187
|
entry.web = {
|
|
11851
|
-
text:
|
|
12188
|
+
text: web.text,
|
|
12189
|
+
...(links ? { links } : {}),
|
|
11852
12190
|
actions: opts.choices.map(c => ({ id: c.action, label: c.label })),
|
|
11853
12191
|
expiresAt: Date.now() + (opts.timeoutMs ?? NONCE_BUTTON_TIMEOUT_MS),
|
|
11854
12192
|
};
|
|
@@ -12100,7 +12438,7 @@ export class FleetManager {
|
|
|
12100
12438
|
isClassic: name => classic.has(name),
|
|
12101
12439
|
place: name => {
|
|
12102
12440
|
const world = this.worlds.get(this.getInstanceAdapterId(name) ?? "");
|
|
12103
|
-
return world ?
|
|
12441
|
+
return world ? worldPlaceOf(world) : undefined;
|
|
12104
12442
|
},
|
|
12105
12443
|
// fleet.yaml's topic only. A ClassicBot room has none here, so no link: its channel may sit in any allowed
|
|
12106
12444
|
// guild, not necessarily its world's group.
|
|
@@ -12159,8 +12497,14 @@ export class FleetManager {
|
|
|
12159
12497
|
* (cache-service.ts) — never a vendor call, never a synchronous transcript read.
|
|
12160
12498
|
*/
|
|
12161
12499
|
cacheReport(window) {
|
|
12162
|
-
|
|
12163
|
-
|
|
12500
|
+
return this.cacheServiceFor().report(cacheWindow(window));
|
|
12501
|
+
}
|
|
12502
|
+
/** #2001: get_cache_report's — the same report, as last worked out (reused for a minute, until the next pass). */
|
|
12503
|
+
cacheReportPublished(window) {
|
|
12504
|
+
return this.cacheServiceFor().published(cacheWindow(window));
|
|
12505
|
+
}
|
|
12506
|
+
cacheServiceFor() {
|
|
12507
|
+
return this.cacheService ??= new CacheService({
|
|
12164
12508
|
instances: () => this.cacheInstances(),
|
|
12165
12509
|
claudeProjectsDir: () => join(process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude"), "projects"),
|
|
12166
12510
|
claudeKey: claudeProjectKey,
|
|
@@ -12169,7 +12513,6 @@ export class FleetManager {
|
|
|
12169
12513
|
metaPath: join(this.dataDir, "cache-codex-rollouts.json"),
|
|
12170
12514
|
log: (level, msg, extra) => this.logger[level](extra ?? {}, msg),
|
|
12171
12515
|
});
|
|
12172
|
-
return this.cacheService.report(w);
|
|
12173
12516
|
}
|
|
12174
12517
|
/** The web's Acknowledge (any item; a signed-in session is fleet-admin level). */
|
|
12175
12518
|
acknowledgeNeedsItem(id, principal) {
|
|
@@ -12265,9 +12608,7 @@ export class FleetManager {
|
|
|
12265
12608
|
finishedAt: Date.parse(d.finishedAt) || 0,
|
|
12266
12609
|
})),
|
|
12267
12610
|
ownerOf,
|
|
12268
|
-
worlds: () => [...this.worlds.values()].map((w) => ({
|
|
12269
|
-
id: w.id, adapter: w.adapter, place: { type: w.type, ...(w.groupId ? { groupId: w.groupId } : {}) },
|
|
12270
|
-
})),
|
|
12611
|
+
worlds: () => [...this.worlds.values()].map((w) => ({ id: w.id, adapter: w.adapter, place: worldPlaceOf(w) })),
|
|
12271
12612
|
noticeTarget: world => {
|
|
12272
12613
|
const target = this.fleetNoticeTarget(world);
|
|
12273
12614
|
return target ? { chatId: target.chatId, ...(target.opts.threadId !== undefined ? { threadId: String(target.opts.threadId) } : {}) } : null;
|
|
@@ -12919,12 +13260,12 @@ export class FleetManager {
|
|
|
12919
13260
|
* the operator's. Keep the instance notice passive and put the action in the
|
|
12920
13261
|
* same-world General topic where an administrator can make the choice. */
|
|
12921
13262
|
async notifyNormalExit(instanceName) {
|
|
12922
|
-
this.notifyInstanceTopic(instanceName, t("exit.instance_notice", instanceName));
|
|
13263
|
+
this.notifyInstanceTopic(instanceName, t("exit.instance_notice", instanceName) + placeSuffix({ kind: "general" }, "General"));
|
|
12923
13264
|
const web = this.webOnlyPromptPlace();
|
|
12924
13265
|
if (web) {
|
|
12925
13266
|
await this.postNonceButtonPrompt({
|
|
12926
13267
|
prefix: EXIT_RESTART_CALLBACK_PREFIX, alertType: "exit_restart", instanceName, ...web,
|
|
12927
|
-
message: t("exit.general_notice", instanceName),
|
|
13268
|
+
message: t("exit.general_notice", placeRef({ kind: "instance", name: instanceName }, instanceName)),
|
|
12928
13269
|
choices: [{ action: "restart", label: t("exit.restart") }, { action: "ignore", label: t("exit.ignore") }],
|
|
12929
13270
|
expiredText: t("exit.expired", instanceName),
|
|
12930
13271
|
});
|
|
@@ -12953,7 +13294,7 @@ export class FleetManager {
|
|
|
12953
13294
|
adapterId,
|
|
12954
13295
|
chatId,
|
|
12955
13296
|
threadId,
|
|
12956
|
-
message: t("exit.general_notice", instanceName),
|
|
13297
|
+
message: t("exit.general_notice", placeRef({ kind: "instance", name: instanceName }, instanceName)),
|
|
12957
13298
|
choices: [
|
|
12958
13299
|
{ action: "restart", label: t("exit.restart") },
|
|
12959
13300
|
{ action: "ignore", label: t("exit.ignore") },
|
|
@@ -13000,7 +13341,7 @@ export class FleetManager {
|
|
|
13000
13341
|
* contain usernames or secret-adjacent text and stays in daemon.log.
|
|
13001
13342
|
*/
|
|
13002
13343
|
async notifyInteractivePrompt(instanceName, kind) {
|
|
13003
|
-
this.notifyInstanceTopic(instanceName, t("interactive.instance_notice", instanceName));
|
|
13344
|
+
this.notifyInstanceTopic(instanceName, t("interactive.instance_notice", instanceName) + placeSuffix({ kind: "general" }, "General"));
|
|
13004
13345
|
// Multi-world fleets can have one General per bot/platform. Route the assist
|
|
13005
13346
|
// request to the General bound to the same adapter as the blocked instance.
|
|
13006
13347
|
const generalName = this.findGeneralInstance(this.getInstanceAdapterId(instanceName));
|
|
@@ -13011,7 +13352,7 @@ export class FleetManager {
|
|
|
13011
13352
|
const label = this.interactivePromptLabel(kind);
|
|
13012
13353
|
const prompt = {
|
|
13013
13354
|
prefix: INTERACTIVE_ASSIST_CALLBACK_PREFIX, alertType: "interactive_prompt", instanceName,
|
|
13014
|
-
message: t("interactive.general_notice", instanceName, label),
|
|
13355
|
+
message: t("interactive.general_notice", placeRef({ kind: "instance", name: instanceName }, instanceName), label),
|
|
13015
13356
|
choices: [{ action: "confirm", label: t("interactive.confirm") }, { action: "cancel", label: t("interactive.cancel") }],
|
|
13016
13357
|
expiredText: t("interactive.expired", instanceName),
|
|
13017
13358
|
extra: { generalName, promptKind: kind, assistFor: this.interactionWaitKey(instanceName) },
|
|
@@ -14610,6 +14951,14 @@ export class FleetManager {
|
|
|
14610
14951
|
const room = this.classicChannels?.getAll().find(c => c.instanceName === name);
|
|
14611
14952
|
return room ? `room:${room.channelId}:${room.adapterId ?? ""}` : null;
|
|
14612
14953
|
}
|
|
14954
|
+
/** The only Try-card publisher: no arbitrary SSE sender/role can manufacture this metadata. */
|
|
14955
|
+
publishEffectTrial(instance, offer) {
|
|
14956
|
+
const row = this.webChatHistory.recordEffectTrial(instance, offer, new Date().toISOString());
|
|
14957
|
+
if (!row)
|
|
14958
|
+
return false;
|
|
14959
|
+
broadcastSseEvent(this.sseClients, "message", row, err => this.logger.debug({ err }, "SSE client write failed; evicting"), this.webChatHistory.cursorOf(row));
|
|
14960
|
+
return true;
|
|
14961
|
+
}
|
|
14613
14962
|
/** Push an SSE event to all connected Web UI clients. */
|
|
14614
14963
|
emitSseEvent(event, data) {
|
|
14615
14964
|
// #1386: a prompt opened or closed, or an instance's state moved — "Needs you" may have changed.
|
|
@@ -14626,8 +14975,12 @@ export class FleetManager {
|
|
|
14626
14975
|
role: typeof m.role === "string" ? m.role : undefined,
|
|
14627
14976
|
buttons: m.buttons,
|
|
14628
14977
|
click: m.click,
|
|
14978
|
+
links: m.links,
|
|
14629
14979
|
});
|
|
14630
14980
|
broadcastSseEvent(this.sseClients, event, recorded, onError, this.webChatHistory.cursorOf(recorded));
|
|
14981
|
+
// #1629: live-only receipt after the recorded agent reply. Never persist/replay it or give it an SSE cursor.
|
|
14982
|
+
if (recorded.role === "agent")
|
|
14983
|
+
broadcastSseEvent(this.sseClients, "agent_reply_live", { v: 1, boot: recorded.boot, id: recorded.id, instance: recorded.instance }, onError);
|
|
14631
14984
|
return;
|
|
14632
14985
|
}
|
|
14633
14986
|
// #1266: a reply's buttons ended — the history shows it too (a later load, the public link's poll).
|
|
@@ -17295,6 +17648,14 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
17295
17648
|
catch (err) {
|
|
17296
17649
|
this.logger.warn({ err: err.message, instanceName: ch.instanceName }, "Dropping a removed room from web favorites failed");
|
|
17297
17650
|
}
|
|
17651
|
+
try {
|
|
17652
|
+
this.themeStore.forget(ch.instanceName);
|
|
17653
|
+
this.webAppearance.limiter.forget(ch.instanceName);
|
|
17654
|
+
this.effectToolsService?.forget(ch.instanceName);
|
|
17655
|
+
}
|
|
17656
|
+
catch (err) {
|
|
17657
|
+
this.logger.warn({ err: err.message, instanceName: ch.instanceName }, "Dropping a removed room's web theme failed");
|
|
17658
|
+
}
|
|
17298
17659
|
}
|
|
17299
17660
|
this.instanceWorldBinding.delete(ch.instanceName);
|
|
17300
17661
|
// #1716: say so in the reply when background work was ended with the stop.
|
|
@@ -20024,6 +20385,9 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
20024
20385
|
// /api/profiles (resolveInstanceIdentity) — the sidebar groups, searches and labels with it on every page.
|
|
20025
20386
|
const classicRooms = new Map((this.classicChannels?.getAll() ?? []).map(ch => [ch.instanceName, ch]));
|
|
20026
20387
|
const profiles = profileIdentities(this.dataDir);
|
|
20388
|
+
// #1629 r4 §1: the identity card's version per instance (the page reads the card when it moves).
|
|
20389
|
+
const cards = humanCards(this.dataDir);
|
|
20390
|
+
const colors = identityColors(this.dataDir);
|
|
20027
20391
|
// #1630: decorative — whatever goes wrong deciding an avatar never costs the frame (every instance gets its first character).
|
|
20028
20392
|
let bots = { own: new Map(), speaking: new Map() };
|
|
20029
20393
|
try {
|
|
@@ -20083,6 +20447,21 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
20083
20447
|
display_name: identity.display_name || undefined,
|
|
20084
20448
|
avatar: image.avatar,
|
|
20085
20449
|
...(image.accent ? { accent: image.accent } : {}),
|
|
20450
|
+
// #1629: the agent's theme's picture and ring colour, for viewers who show agents' themes.
|
|
20451
|
+
...(image.themeAvatar ? { theme_avatar: image.themeAvatar } : {}),
|
|
20452
|
+
...(image.themeAccent ? { theme_accent: image.themeAccent } : {}),
|
|
20453
|
+
...(() => { try {
|
|
20454
|
+
const v = cardVersion(resolveCard(name, cards.get(name), this.themeStore.get(name).theme.card));
|
|
20455
|
+
return v ? { card_v: v } : {};
|
|
20456
|
+
}
|
|
20457
|
+
catch {
|
|
20458
|
+
return {};
|
|
20459
|
+
} })(),
|
|
20460
|
+
// #1958: the identity colour's tiers (the page resolves them: the agent's only while it shows agents' themes).
|
|
20461
|
+
color: { ...(colors.get(name)?.color ? { person: colors.get(name).color } : {}), ...(image.agentColor ? { agent: image.agentColor } : {}),
|
|
20462
|
+
...(image.roleColor ? { role: image.roleColor } : {}), auto: autoIdentityColor(name) },
|
|
20463
|
+
// #1958 decision 2: where it shows — a person's choice for this instance, the agent's; else the viewer's host default.
|
|
20464
|
+
color_show: { ...(colors.get(name)?.show ? { person: colors.get(name).show } : {}), ...(image.agentShow ? { agent: image.agentShow } : {}) },
|
|
20086
20465
|
description: identity.description || undefined,
|
|
20087
20466
|
role: identity.role || undefined,
|
|
20088
20467
|
tags: identity.tags,
|
|
@@ -20109,6 +20488,8 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
20109
20488
|
connections: (this.fleetConfig?.channels ?? (this.fleetConfig?.channel ? [this.fleetConfig.channel] : [])).length,
|
|
20110
20489
|
// #1665: the host preferences' revision — a page that has another re-reads /ui/prefs (no values in this frame).
|
|
20111
20490
|
prefs_rev: this.webPrefs.rev(),
|
|
20491
|
+
// #1629: the themes' revision — the page re-reads GET /ui/themes when it moves.
|
|
20492
|
+
themes_rev: this.themeStore.rev(),
|
|
20112
20493
|
// #1685: host memory pressure while it slows or holds new agents (the dashboard's indicator; macOS elevated is
|
|
20113
20494
|
// shown here rather than in chat). null when normal, unknown or advisory.
|
|
20114
20495
|
host_memory: this.hostMemoryIndicator(),
|