@songsid/agend 2.3.3-beta.3 → 2.3.3-beta.5

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.
Files changed (238) hide show
  1. package/dist/agent-cli.js +13 -112
  2. package/dist/agent-cli.js.map +1 -1
  3. package/dist/agent-endpoint.d.ts +0 -1
  4. package/dist/agent-endpoint.js +1 -9
  5. package/dist/agent-endpoint.js.map +1 -1
  6. package/dist/agent-whats-new.d.ts +3 -6
  7. package/dist/agent-whats-new.js +7 -18
  8. package/dist/agent-whats-new.js.map +1 -1
  9. package/dist/auth-api.d.ts +0 -7
  10. package/dist/auth-api.js +1 -15
  11. package/dist/auth-api.js.map +1 -1
  12. package/dist/backend/codex.d.ts +1 -0
  13. package/dist/backend/codex.js +106 -15
  14. package/dist/backend/codex.js.map +1 -1
  15. package/dist/backend/types.d.ts +17 -0
  16. package/dist/backend/types.js.map +1 -1
  17. package/dist/bundled-skills.d.ts +4 -6
  18. package/dist/bundled-skills.js +10 -14
  19. package/dist/bundled-skills.js.map +1 -1
  20. package/dist/channel/mcp-server.js +5 -5
  21. package/dist/channel/mcp-server.js.map +1 -1
  22. package/dist/channel/mcp-tools.d.ts +1 -3
  23. package/dist/channel/mcp-tools.js +4 -58
  24. package/dist/channel/mcp-tools.js.map +1 -1
  25. package/dist/cli.js +3 -1
  26. package/dist/cli.js.map +1 -1
  27. package/dist/config-validator.js +6 -3
  28. package/dist/config-validator.js.map +1 -1
  29. package/dist/daemon.d.ts +11 -3
  30. package/dist/daemon.js +59 -107
  31. package/dist/daemon.js.map +1 -1
  32. package/dist/dashboard-live.d.ts +1 -1
  33. package/dist/dashboard-live.js +3 -3
  34. package/dist/dashboard-live.js.map +1 -1
  35. package/dist/dashboard-permissions.d.ts +1 -1
  36. package/dist/dashboard-permissions.js +1 -3
  37. package/dist/dashboard-permissions.js.map +1 -1
  38. package/dist/fleet-manager.d.ts +26 -44
  39. package/dist/fleet-manager.js +70 -226
  40. package/dist/fleet-manager.js.map +1 -1
  41. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +11 -1
  42. package/dist/general-knowledge/skills/session-management/SKILL.md +6 -0
  43. package/dist/general-knowledge/skills/web-appearance/SKILL.md +13 -204
  44. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +26 -63
  45. package/dist/instance-lifecycle.js +1 -1
  46. package/dist/instance-lifecycle.js.map +1 -1
  47. package/dist/locale.js +2 -0
  48. package/dist/locale.js.map +1 -1
  49. package/dist/outbound-handlers.js +19 -7
  50. package/dist/outbound-handlers.js.map +1 -1
  51. package/dist/outbound-schemas.d.ts +0 -62
  52. package/dist/outbound-schemas.js +2 -41
  53. package/dist/outbound-schemas.js.map +1 -1
  54. package/dist/public-web-gateway.js +0 -9
  55. package/dist/public-web-gateway.js.map +1 -1
  56. package/dist/public-web-link.d.ts +2 -0
  57. package/dist/public-web-link.js +5 -0
  58. package/dist/public-web-link.js.map +1 -1
  59. package/dist/search/search-docs.d.ts +1 -1
  60. package/dist/search/search-docs.js +2 -2
  61. package/dist/search/search-docs.js.map +1 -1
  62. package/dist/settings-api.d.ts +6 -0
  63. package/dist/settings-api.js +14 -2
  64. package/dist/settings-api.js.map +1 -1
  65. package/dist/settings-change.js +3 -1
  66. package/dist/settings-change.js.map +1 -1
  67. package/dist/task-board-waker.d.ts +4 -1
  68. package/dist/task-board-waker.js +13 -2
  69. package/dist/task-board-waker.js.map +1 -1
  70. package/dist/tool-permissions.js +3 -10
  71. package/dist/tool-permissions.js.map +1 -1
  72. package/dist/topic-commands.d.ts +6 -2
  73. package/dist/topic-commands.js +2 -0
  74. package/dist/topic-commands.js.map +1 -1
  75. package/dist/types.d.ts +2 -1
  76. package/dist/ui/chat-ambient.js +4 -4
  77. package/dist/ui/chat-commands.js +3 -2
  78. package/dist/ui/chat-store.js +4 -1
  79. package/dist/ui/chat-strings.js +6 -42
  80. package/dist/ui/chat-thread.js +6 -44
  81. package/dist/ui/panel-chat.js +21 -50
  82. package/dist/ui/preview-panel.js +3 -15
  83. package/dist/ui/search-dialog.js +30 -14
  84. package/dist/ui/settings-appearance.js +1 -3
  85. package/dist/ui/settings-guests.js +13 -4
  86. package/dist/ui/settings-learn.js +1 -1
  87. package/dist/ui/settings-mods.js +5 -12
  88. package/dist/ui/settings-registry.js +0 -1
  89. package/dist/ui/settings-strings.js +6 -6
  90. package/dist/ui/shared/app-prefs.js +17 -17
  91. package/dist/ui/shared/app-shell.js +12 -8
  92. package/dist/ui/shared/app-store.js +6 -2
  93. package/dist/ui/shared/app.css +4 -36
  94. package/dist/ui/shared/app.js +5 -4
  95. package/dist/ui/shared/instance-nav.js +9 -9
  96. package/dist/ui/shared/instance-switch.js +8 -2
  97. package/dist/ui/shared/panel-view.js +22 -8
  98. package/dist/ui/shared/view-strings.js +6 -0
  99. package/dist/usage/usage-api.d.ts +3 -0
  100. package/dist/usage/usage-api.js +5 -1
  101. package/dist/usage/usage-api.js.map +1 -1
  102. package/dist/view-api.d.ts +2 -0
  103. package/dist/view-api.js +64 -0
  104. package/dist/view-api.js.map +1 -1
  105. package/dist/web-api.d.ts +9 -17
  106. package/dist/web-api.js +255 -371
  107. package/dist/web-api.js.map +1 -1
  108. package/dist/web-appearance-tools.d.ts +0 -2
  109. package/dist/web-appearance-tools.js +2 -5
  110. package/dist/web-appearance-tools.js.map +1 -1
  111. package/dist/web-appearance-view.d.ts +0 -6
  112. package/dist/web-appearance-view.js +0 -1
  113. package/dist/web-appearance-view.js.map +1 -1
  114. package/dist/web-appearance.d.ts +14 -7
  115. package/dist/web-appearance.js +5 -6
  116. package/dist/web-appearance.js.map +1 -1
  117. package/dist/web-asset-parts.d.ts +1 -17
  118. package/dist/web-asset-parts.js +1 -15
  119. package/dist/web-asset-parts.js.map +1 -1
  120. package/dist/web-auth.js +1 -7
  121. package/dist/web-auth.js.map +1 -1
  122. package/dist/web-chat-history.d.ts +3 -7
  123. package/dist/web-chat-history.js +10 -17
  124. package/dist/web-chat-history.js.map +1 -1
  125. package/dist/web-commands.d.ts +9 -1
  126. package/dist/web-commands.js +13 -0
  127. package/dist/web-commands.js.map +1 -1
  128. package/dist/web-experimental.d.ts +2 -11
  129. package/dist/web-experimental.js +8 -36
  130. package/dist/web-experimental.js.map +1 -1
  131. package/dist/web-guest-access.d.ts +40 -2
  132. package/dist/web-guest-access.js +50 -14
  133. package/dist/web-guest-access.js.map +1 -1
  134. package/dist/web-mod-bundle.d.ts +2 -45
  135. package/dist/web-mod-bundle.js +15 -63
  136. package/dist/web-mod-bundle.js.map +1 -1
  137. package/dist/web-mod-store.d.ts +3 -48
  138. package/dist/web-mod-store.js +30 -97
  139. package/dist/web-mod-store.js.map +1 -1
  140. package/dist/web-prefs.d.ts +13 -0
  141. package/dist/web-prefs.js +38 -0
  142. package/dist/web-prefs.js.map +1 -1
  143. package/dist/web-preview.d.ts +1 -11
  144. package/dist/web-preview.js +6 -20
  145. package/dist/web-preview.js.map +1 -1
  146. package/dist/web-session.d.ts +11 -3
  147. package/dist/web-session.js +22 -11
  148. package/dist/web-session.js.map +1 -1
  149. package/dist/web-settings-patch.d.ts +1 -0
  150. package/dist/web-settings-patch.js +6 -3
  151. package/dist/web-settings-patch.js.map +1 -1
  152. package/dist/web-settings-tools.d.ts +0 -13
  153. package/dist/web-settings-tools.js +12 -84
  154. package/dist/web-settings-tools.js.map +1 -1
  155. package/launcher/fleet-service-owner.cjs +4 -1
  156. package/package.json +1 -1
  157. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +0 -376
  158. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +0 -672
  159. package/dist/ui/app-effects.js +0 -251
  160. package/dist/ui/chat-effect-posters.js +0 -42
  161. package/dist/ui/chat-effects.js +0 -167
  162. package/dist/ui/effect-trial-controller.js +0 -124
  163. package/dist/ui/effect-trial-offer.js +0 -2
  164. package/dist/ui/effect-trial-panel.js +0 -40
  165. package/dist/ui/effects-waker.js +0 -2
  166. package/dist/ui/settings-effects.js +0 -202
  167. package/dist/ui/settings-owner-effects.js +0 -82
  168. package/dist/ui/shared/effect-app-runtime.js +0 -299
  169. package/dist/ui/shared/effect-baseline.js +0 -64
  170. package/dist/ui/shared/effect-chat-runtime.js +0 -162
  171. package/dist/ui/shared/effect-cursor.js +0 -126
  172. package/dist/ui/shared/effect-display-metadata.js +0 -39
  173. package/dist/ui/shared/effect-frame.js +0 -146
  174. package/dist/ui/shared/effect-geometry.js +0 -137
  175. package/dist/ui/shared/effect-host-style.js +0 -10
  176. package/dist/ui/shared/effect-owner-regions.js +0 -74
  177. package/dist/ui/shared/effect-owner-source.js +0 -122
  178. package/dist/ui/shared/effect-policy.js +0 -151
  179. package/dist/ui/shared/effect-poster-reader.js +0 -98
  180. package/dist/ui/shared/effect-posters.js +0 -117
  181. package/dist/ui/shared/effect-profiler.js +0 -184
  182. package/dist/ui/shared/effect-profiles.js +0 -65
  183. package/dist/ui/shared/effect-registry.js +0 -545
  184. package/dist/ui/shared/effect-runtime-loader.js +0 -236
  185. package/dist/ui/shared/effect-safety.js +0 -153
  186. package/dist/ui/shared/effect-stock-snapshot.js +0 -43
  187. package/dist/ui/shared/effect-stock-trust.js +0 -217
  188. package/dist/ui/shared/effect-trial-shadow.js +0 -62
  189. package/dist/ui/shared/effect-trial-source.js +0 -103
  190. package/dist/ui/shared/effect-trial-stage.js +0 -170
  191. package/dist/ui/shared/effect-trial.css +0 -40
  192. package/dist/ui/shared/feature-flags.js +0 -127
  193. package/dist/ui/shared/owner-effect-prefs.js +0 -117
  194. package/dist/web-effect-bootstrap.d.ts +0 -12
  195. package/dist/web-effect-bootstrap.js +0 -49
  196. package/dist/web-effect-bootstrap.js.map +0 -1
  197. package/dist/web-effect-bundle.d.ts +0 -28
  198. package/dist/web-effect-bundle.js +0 -171
  199. package/dist/web-effect-bundle.js.map +0 -1
  200. package/dist/web-effect-canonical.d.ts +0 -8
  201. package/dist/web-effect-canonical.js +0 -27
  202. package/dist/web-effect-canonical.js.map +0 -1
  203. package/dist/web-effect-library.d.ts +0 -114
  204. package/dist/web-effect-library.js +0 -530
  205. package/dist/web-effect-library.js.map +0 -1
  206. package/dist/web-effect-playback.d.ts +0 -30
  207. package/dist/web-effect-playback.js +0 -71
  208. package/dist/web-effect-playback.js.map +0 -1
  209. package/dist/web-effect-poster.d.ts +0 -8
  210. package/dist/web-effect-poster.js +0 -52
  211. package/dist/web-effect-poster.js.map +0 -1
  212. package/dist/web-effect-profiles.d.ts +0 -118
  213. package/dist/web-effect-profiles.js +0 -181
  214. package/dist/web-effect-profiles.js.map +0 -1
  215. package/dist/web-effect-service.d.ts +0 -93
  216. package/dist/web-effect-service.js +0 -335
  217. package/dist/web-effect-service.js.map +0 -1
  218. package/dist/web-effect-tools.d.ts +0 -48
  219. package/dist/web-effect-tools.js +0 -129
  220. package/dist/web-effect-tools.js.map +0 -1
  221. package/dist/web-effect-trials.d.ts +0 -86
  222. package/dist/web-effect-trials.js +0 -204
  223. package/dist/web-effect-trials.js.map +0 -1
  224. package/dist/web-effect-upload.d.ts +0 -27
  225. package/dist/web-effect-upload.js +0 -185
  226. package/dist/web-effect-upload.js.map +0 -1
  227. package/dist/web-effect-worker.d.ts +0 -8
  228. package/dist/web-effect-worker.js +0 -170
  229. package/dist/web-effect-worker.js.map +0 -1
  230. package/dist/web-effect.d.ts +0 -103
  231. package/dist/web-effect.js +0 -153
  232. package/dist/web-effect.js.map +0 -1
  233. package/dist/web-experimental-http.d.ts +0 -2
  234. package/dist/web-experimental-http.js +0 -12
  235. package/dist/web-experimental-http.js.map +0 -1
  236. package/dist/web-mod-agent.d.ts +0 -50
  237. package/dist/web-mod-agent.js +0 -207
  238. package/dist/web-mod-agent.js.map +0 -1
@@ -176,13 +176,6 @@ import { OfficeSkinStore } from "./web-office-skins.js";
176
176
  import { OfficeActs } from "./web-office-acts.js";
177
177
  import { OFFICE_PACK_TOOLS, OfficePackTools } from "./web-office-pack-tools.js";
178
178
  import { WebAppearance } from "./web-appearance.js";
179
- import { WebEffectLibrary } from "./web-effect-library.js";
180
- import { WebEffectTrials } from "./web-effect-trials.js";
181
- import { WebEffectService } from "./web-effect-service.js";
182
- import { WebEffectProfiles } from "./web-effect-profiles.js";
183
- import { WebEffectTools, WEB_EFFECT_TOOLS } from "./web-effect-tools.js";
184
- import { resolveEffectParams } from "./web-effect.js";
185
- import { cw1EffectChecker } from "./web-mod-bundle.js";
186
179
  import { effectiveAppearanceLimits } from "./web-appearance-limits.js";
187
180
  import { WEB_NAME_FALLBACK, safeWebName, validateWebName, webNameInUse, webPersonKey } from "./web-profile.js";
188
181
  import { ReplyButtonStore, parseReplyButtons, replyButtonClickText, replyButtonsFallbackText, REPLY_BUTTON_PREFIX } from "./reply-buttons.js";
@@ -195,7 +188,6 @@ import { autoIdentityColor } from "./web-identity-color.js";
195
188
  import { AppearanceTools } from "./web-appearance-tools.js";
196
189
  import { cardVersion, resolveCard } from "./web-card.js";
197
190
  import { ModStore } from "./web-mod-store.js";
198
- import { AgentMods } from "./web-mod-agent.js";
199
191
  import { downloadStickerImage } from "./channel/sticker-download.js";
200
192
  import { WebImageCache, imageMimeForFile, WEB_IMAGE_MAX_AGE_MS, WEB_IMAGE_MAX_BYTES, WEB_IMAGE_RETRY_MS } from "./web-images.js";
201
193
  import { accentColor, avatarFor, ownBotConnections } from "./instance-avatar.js";
@@ -214,7 +206,7 @@ import { crossInstanceVisibility, runVisibilityCommand, xmsgVisibility } from ".
214
206
  import { SEARCH_RETENTION_DAYS, SearchIndex } from "./search/search-index.js";
215
207
  import { chatDoc, taskDoc, xmsgDoc } from "./search/search-docs.js";
216
208
  import { WebSettingsTools } from "./web-settings-tools.js";
217
- import { bindGuestRequest, guestRouteDecision } from "./web-guest-access.js";
209
+ import { GUEST_COMMAND_RUNS, GUEST_PREF_WRITES, GUEST_SEARCHES, bindGuestRequest, guestRouteDecision } from "./web-guest-access.js";
218
210
  import { GUEST_ROOM_TICK_MS, GuestRoomMembership, guestGroupRooms } from "./web-guest-rooms.js";
219
211
  import { Delegations, TrafficLog, trafficItem } from "./fleet-traffic.js";
220
212
  import { installedChannel, updateNoticeKey } from "./update-check.js";
@@ -259,14 +251,14 @@ import { buildOrgChart } from "./web-org.js";
259
251
  import { topMemoryHogs } from "./memory-hogs.js";
260
252
  import { GENERAL_POINTING_REFUSALS, SNOWFLAKE, hasPlaceRefs, placeRef, webRoute, webTextLinks, placeSuffix, renderPlaceRefs, stripPlaceRefs, topicUrl, worldPlaceOf } from "./topic-link.js";
261
253
  import { CacheService, cacheWindow } from "./cache-service.js";
262
- import { runWebCommand } from "./web-commands.js";
254
+ import { guestWebCommands, runWebCommand } from "./web-commands.js";
263
255
  import { CompactWatch, launchIdentity } from "./compact-watch.js";
264
256
  import { agyLegacyAgentsFile, danglingSymlink, migrateAgyAgentsFile } from "./backend/antigravity.js";
265
257
  import { claudeConfigDirFor, claudeProjectKey } from "./backend/claude-code.js";
266
258
  import { sharedRolloutIndex } from "./rollout-index.js";
267
259
  import { TASK_BOARD_ON_NOTICE, dashboardRefusalIn, taskBoardSwitchedOn } from "./dashboard-permissions.js";
268
260
  import { mayUseTool, resolveToolSet, scheduleOpRefusal, toolForIpcType, toolRefusedMessage, } from "./tool-permissions.js";
269
- import { authorizeSession, decideWebGate, loadOrCreateWebToken, readWebToken, sessionCookieUpgrade } from "./web-auth.js";
261
+ import { authorizeSession, decideWebGate, loadOrCreateWebToken, readSessionCookies, readWebToken, sessionCookieUpgrade } from "./web-auth.js";
270
262
  import { bypassesWebGate, handleAuthRequest, serveGuestRefusedPage, serveSigninPage } from "./auth-api.js";
271
263
  import { isWebPageNavigation } from "./web-shell-routes.js";
272
264
  import { guestSessionPolicy, localSessionPolicy, tokenEpoch, WebSessionStore } from "./web-session.js";
@@ -279,10 +271,8 @@ import { decisionListAnswer } from "./decision-list.js";
279
271
  import { CodeRequestLimiter, ReturningDevices } from "./web-returning-device.js";
280
272
  import { allowedHostNames, applyWebSecurityHeaders, hostnameOf, isHostAllowed, LOOPBACK_HOST_NAMES, WEB_HOST_REJECTED_MESSAGE } from "./web-host-guard.js";
281
273
  import { createPreviewListener, normalizeOrigin, previewAvailability, previewSettings } from "./web-preview.js";
282
- import { canvasWorkerEnabled, isCanvasWorkerTool, CANVAS_WORKER_EXPERIMENTAL_MESSAGE } from "./web-experimental.js";
283
- import { isCanvasWorkerHttpPath } from "./web-experimental-http.js";
274
+ import { isCanvasWorkerTool, CANVAS_WORKER_EXPERIMENTAL_MESSAGE } from "./web-experimental.js";
284
275
  import { bundledSkillContents } from "./bundled-skills.js";
285
- import { createEffectBootstrap } from "./web-effect-bootstrap.js";
286
276
  import { fleetLevelDifferences, fleetLevelSignature, appendedConnections } from "./fleet-level-config.js";
287
277
  import { checkSelfRestartAllowance, recordSelfRestartAttempt } from "./self-restart-limit.js";
288
278
  import { SecretStore } from "./secret-store.js";
@@ -988,11 +978,6 @@ export class FleetManager {
988
978
  collabInstances = new Set();
989
979
  // Health endpoint
990
980
  healthServer = null;
991
- /** #2118: independent of HTML preview availability/port; fixed bootstrap only, no effect resources. */
992
- effectBootstrap = createEffectBootstrap();
993
- /** Fixed trusted document coherence, independent of the HTML preview listener; never a resource grant. */
994
- get effectBoot() { return this.effectBootstrap.boot; }
995
- canvasWorkerEnabled() { return canvasWorkerEnabled(this.fleetConfig?.web); }
996
981
  healthServerDrain = null;
997
982
  /** #1306: the preview listener (health_port + 1 by default), and whether it is listening. */
998
983
  previewListener = null;
@@ -1171,7 +1156,7 @@ export class FleetManager {
1171
1156
  /** #1629: every instance's theme, placements, assets and lock (web-themes.json). */
1172
1157
  get themeStore() {
1173
1158
  // r4.1 Q3 (b): the card's limits are read from fleet.yaml at each upload (a change applies without a restart).
1174
- return (this.themeStoreInstance ??= new ThemeStore(this.dataDir, { limits: () => effectiveAppearanceLimits(this.fleetConfig?.web?.appearance).limits, effectsDropped: ids => this.effectLibrary.collectUnreferenced(ids) }));
1159
+ return (this.themeStoreInstance ??= new ThemeStore(this.dataDir, { limits: () => effectiveAppearanceLimits(this.fleetConfig?.web?.appearance).limits }));
1175
1160
  }
1176
1161
  officeStoreInstance;
1177
1162
  /** Fleet → Org chart → Office: the offices people made (web-offices.json, beside web-themes.json). */
@@ -1183,6 +1168,12 @@ export class FleetManager {
1183
1168
  officeSkinWrites = new OfficeWriteLimiter(undefined, { perWindow: 10, windowMs: 60_000 });
1184
1169
  /** How often a person may save the offices (PUT /ui/offices); memory only, as the appearance limiter. */
1185
1170
  officeWrites = new OfficeWriteLimiter();
1171
+ /** #2855: a guest's preference saves, per account. */
1172
+ guestPrefWrites = new OfficeWriteLimiter(undefined, GUEST_PREF_WRITES);
1173
+ /** #2855: a guest's commands, per account. */
1174
+ guestCommandRuns = new OfficeWriteLimiter(undefined, GUEST_COMMAND_RUNS);
1175
+ /** #2855: a trusted guest's searches, per account. */
1176
+ guestSearches = new OfficeWriteLimiter(undefined, { perWindow: GUEST_SEARCHES.perWindow, windowMs: GUEST_SEARCHES.windowMs });
1186
1177
  officeSharesInstance;
1187
1178
  /**
1188
1179
  * #2636: Office share links — the live Office view for people outside the fleet, read-only, on a quick tunnel of
@@ -1378,96 +1369,16 @@ export class FleetManager {
1378
1369
  }
1379
1370
  catch { /* the next frame carries the rev */ } }));
1380
1371
  }
1381
- effectLibraryInstance;
1382
- effectTrialsInstance;
1383
- /** Lazy, single-owner storage. Register every host reference source before any operation can collect. */
1384
- get effectLibrary() {
1385
- if (!this.effectLibraryInstance) {
1386
- const library = new WebEffectLibrary(this.dataDir);
1387
- // Providers are lazy; publish only after every registration succeeds.
1388
- library.registerReferenceSource("themes", () => {
1389
- if (!this.themeStore.readable())
1390
- throw new Error("themes_unreadable");
1391
- return this.themeStore.placedEffects().flatMap(row => row.ids.map(id => ({ id, holder: `instance:${row.name}` })));
1392
- });
1393
- library.registerReferenceSource("prefs", () => this.webPrefs.ownerEffectReferences());
1394
- library.registerReferenceSource("mods", () => {
1395
- const mods = this.mods.list();
1396
- if (mods.unreadable)
1397
- throw new Error("mods_unreadable");
1398
- return mods.mods.flatMap(mod => mod.effects.map(effect => ({ id: effect.id, holder: `mod:${mod.id}` })));
1399
- });
1400
- this.effectTrialsInstance = new WebEffectTrials({ library, known: name => this.isKnownInstance(name) });
1401
- this.effectLibraryInstance = library;
1402
- }
1403
- return this.effectLibraryInstance;
1404
- }
1405
- /** Volatile Try offers and session-bound runs share this library. */
1406
- get effectTrials() { if (!this.effectTrialsInstance)
1407
- void this.effectLibrary; return this.effectTrialsInstance; }
1408
- effectProfilesInstance;
1409
- get effectProfiles() { return (this.effectProfilesInstance ??= new WebEffectProfiles(this.dataDir)); }
1410
- effectServiceInstance;
1411
- get effectService() {
1412
- return (this.effectServiceInstance ??= new WebEffectService({ library: this.effectLibrary, themes: this.themeStore, profiles: this.effectProfiles, prefs: this.webPrefs, trials: this.effectTrials,
1413
- known: name => this.isKnownInstance(name), changed: () => { try {
1414
- this.emitSseEvent("status", this.getUiStatus());
1415
- }
1416
- catch { /* next status carries revision */ } } }));
1417
- }
1418
- effectToolsService;
1419
- /** Caller identity is supplied only by the owned IPC connection / verified agent endpoint token. */
1420
- get effectTools() {
1421
- return (this.effectToolsService ??= new WebEffectTools({
1422
- library: this.effectLibrary, limiter: this.webAppearance.limiter,
1423
- authorize: (name, buckets) => !this.isKnownInstance(name) ? { error: "unknown_instance" } : this.webAppearance.authorizeEffect(name, buckets),
1424
- placed: name => this.themeStore.get(name).placements.effects,
1425
- place: (name, effects) => {
1426
- const before = this.themeStore.get(name).placements.effects;
1427
- const explicit = new Set(effects.filter(e => typeof e !== "string").map(e => e.id));
1428
- const w = this.themeStore.setPlacements(name, { effects }, "agent", what => {
1429
- // An unchanged human/private placement may be retained, never exposed or newly acquired by an agent.
1430
- const r = explicit.has(what.id) ? this.effectLibrary.read(what.id, { kind: "agent", id: name })
1431
- : this.effectLibrary.readReferenced(what.id, `instance:${name}`);
1432
- if (!r.ok)
1433
- return r.error;
1434
- const params = explicit.has(what.id) ? what.params : before.find(e => e.id === what.id)?.params;
1435
- return resolveEffectParams(r.entry.manifest, params ?? {}).ok ? null : "effect_params";
1436
- });
1437
- return w.ok ? { ok: true, rev: w.rev } : { ok: false, error: w.error };
1438
- },
1439
- changed: () => { try {
1440
- this.emitSseEvent("status", this.getUiStatus());
1441
- }
1442
- catch { /* later status carries placements */ } },
1443
- // A nonmutating own/shared offer is not an appearance write. Only an explicit non-public admin-session click may start it.
1444
- preview: async (instance, id) => {
1445
- const result = this.effectTrials.offer(instance, id);
1446
- if (!result.ok)
1447
- return { error: "not_found" };
1448
- const present = this.webChatHistory.list(instance).some(row => row.effectTrial?.offer === result.offer.offer);
1449
- if (!present && !this.publishEffectTrial(instance, result.offer))
1450
- return { error: "trial_unavailable" };
1451
- return { id, status: "offered", expiresAt: result.offer.expiresAt, message: "A trial card is in your web chat. A signed-in admin outside the public link can start it; nothing was applied." };
1452
- },
1453
- profiles: id => this.effectProfiles.read(id),
1454
- profileSummary: id => this.effectService.profileSummary(id),
1455
- profileSummaries: ids => this.effectService.profileSummaries(ids),
1456
- playbackSummary: (name, id) => this.effectService.playbackSummary(name, id),
1457
- }));
1458
- }
1459
1372
  appearanceToolsService;
1460
1373
  /** #1629 r4 §1: get_web_appearance / preview_web_appearance — the caller's own look, resolved as the page resolves it. */
1461
1374
  get appearanceTools() {
1462
1375
  return (this.appearanceToolsService ??= new AppearanceTools({
1463
- canvasWorkerEnabled: () => this.canvasWorkerEnabled(),
1464
1376
  frame: (name) => (this.getUiStatus().instances.find(i => i.name === name) ?? { name }),
1465
1377
  theme: (name) => {
1466
1378
  const r = this.themeStore.get(name);
1467
1379
  return { theme: r.theme, locked: r.locked, widgets: r.placements.widgets, ambient: r.placements.ambient,
1468
1380
  glyphAsset: (id) => this.themeStore.asset(name, id) };
1469
1381
  },
1470
- effects: name => this.themeStore.readable() ? this.themeStore.get(name).placements.effects.map(p => ({ id: p.id, by: p.by, ...(p.params ? { params: p.params } : {}), playback: this.effectService.playbackSummary(name, p.id) })) : [],
1471
1382
  card: (name) => resolveCard(name, humanCards(this.dataDir).get(name), this.themeStore.get(name).theme.card),
1472
1383
  // #1958: the host's identity-colour settings, as the page reads them (an unreadable prefs file: the defaults).
1473
1384
  colorPrefs: () => { try {
@@ -1496,49 +1407,17 @@ export class FleetManager {
1496
1407
  return Object.hasOwn(this.fleetConfig?.instances ?? {}, name) || (this.classicChannels?.getAll() ?? []).some((ch) => ch.instanceName === name);
1497
1408
  }
1498
1409
  modStore;
1499
- /**
1500
- * #1629 r4-5 / B2: installed mods use the same validated library and scoped holder claims as direct uploads.
1501
- */
1410
+ /** #1629 r4-5: installed mods (#2799: a bundle that carries Canvas Worker effects is refused). */
1502
1411
  get mods() {
1503
1412
  return (this.modStore ??= new ModStore(this.dataDir, {
1504
1413
  themes: this.themeStore,
1505
1414
  prefs: this.webPrefs,
1506
1415
  ownerCss: this.ownerCss,
1507
- effects: this.effectLibrary,
1508
- canvasWorkerEnabled: () => this.canvasWorkerEnabled(),
1509
- context: () => ({ agendVersion: agendVersion(), limits: this.themeStore.limits(), effects: cw1EffectChecker() }),
1416
+ context: () => ({ agendVersion: agendVersion(), limits: this.themeStore.limits() }),
1510
1417
  isKnownInstance: (name) => this.isKnownInstance(name),
1511
1418
  prefsIdentity: OPERATOR,
1512
1419
  }));
1513
1420
  }
1514
- agentModsService;
1515
- /** #1629 r4-5b: export_web_mod / propose_web_mod — the caller's own look as a file, and a proposal for a person. */
1516
- get agentMods() {
1517
- return (this.agentModsService ??= new AgentMods({
1518
- themes: this.themeStore, mods: this.mods, limiter: this.webAppearance.limiter,
1519
- webConfig: () => this.fleetConfig?.web ?? null,
1520
- // The mod store's own: a proposal is checked exactly as the person's import of it will be.
1521
- context: () => this.mods.checkContext(),
1522
- 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; },
1523
- notice: (instance, text) => { try {
1524
- this.emitSseEvent("message", { instance, sender: instance, role: "status", text, ts: new Date().toISOString() });
1525
- }
1526
- catch { /* the page reads it from Settings too */ } },
1527
- changed: () => { try {
1528
- this.emitSseEvent("status", this.getUiStatus());
1529
- }
1530
- catch { /* the next frame */ } },
1531
- }));
1532
- }
1533
- /**
1534
- * #1629 r4-5c: an effect as this person may carry it away in an export — their own claim, or one a person shared —
1535
- * read with their principal (Sol's ACL). Being able to view or run a placed effect never grants packaging someone
1536
- * else's private claim: the library's reference read is runtime access, not an export path. Null: not theirs.
1537
- */
1538
- modPlacedEffect(person, id) {
1539
- const r = this.effectLibrary.read(id, { kind: "human", id: person });
1540
- return r.ok ? { name: r.entry.manifest.name, files: r.entry.files } : null;
1541
- }
1542
1421
  /** #1629 r4-5: an instance's card text as every viewer sees it (a person's fields over the agent's). */
1543
1422
  modCardText(name) {
1544
1423
  const card = resolveCard(name, humanCards(this.dataDir).get(name), this.themeStore.get(name).theme.card);
@@ -1561,8 +1440,7 @@ export class FleetManager {
1561
1440
  */
1562
1441
  get webSettings() {
1563
1442
  return (this.webSettingsToolsService ??= new WebSettingsTools({
1564
- canvasWorkerEnabled: () => this.canvasWorkerEnabled(),
1565
- prefs: this.webPrefs, ownerCss: this.ownerCss, mods: this.mods, effects: this.effectService,
1443
+ prefs: this.webPrefs, ownerCss: this.ownerCss,
1566
1444
  instances: () => [...Object.keys(this.fleetConfig?.instances ?? {}),
1567
1445
  ...(this.classicChannels?.getAll() ?? []).map((ch) => ch.instanceName).filter((n) => !Object.hasOwn(this.fleetConfig?.instances ?? {}, n))],
1568
1446
  colour: (name) => personIdentityColour(this.dataDir, name),
@@ -1571,7 +1449,6 @@ export class FleetManager {
1571
1449
  bannerFocus: (name) => personBannerFocus(this.dataDir, name),
1572
1450
  setBannerFocus: (name, focus) => { const w = setPersonBannerFocus(this.dataDir, name, focus); if (w.ok)
1573
1451
  this.identityChanged(); return w; },
1574
- placements: (name) => this.themeStore.get(name).placements.effects.map((e) => (typeof e === "string" ? { id: e } : e)),
1575
1452
  ambient: (name) => Object.fromEntries(Object.entries(this.themeStore.get(name).placements.ambient).flatMap(([k, a]) => a ? [[k, { spec: a.spec, by: a.by }]] : [])),
1576
1453
  setAmbient: (name, spec, which) => this.webAppearance.humanSetAmbient(name, spec, which),
1577
1454
  announce: () => { try {
@@ -6652,10 +6529,12 @@ export class FleetManager {
6652
6529
  dataDir: this.dataDir,
6653
6530
  onWarn: message => this.logger.warn(message),
6654
6531
  // #1620: fleet.yaml's web.session.local, read at every check — a Settings save or a reload applies at once
6655
- // (shortening ends the sessions it now outlives). The public link's policy stays fixed.
6532
+ // (shortening ends the sessions it now outlives). The public link's own cap stays fixed.
6656
6533
  localPolicy: () => localSessionPolicy(this.fleetConfig?.web?.session?.local),
6657
6534
  // #1920 §5.4: a guest's, from web.guests.session (never longer than the surface's own).
6658
6535
  guestPolicy: () => guestSessionPolicy(this.fleetConfig?.web?.guests?.session),
6536
+ // A session over the temporary link ends with the link (its TTL, at most 8 h on a quick tunnel).
6537
+ exposureEnd: id => this.publicWebLink?.endOf(id) ?? null,
6659
6538
  });
6660
6539
  this.enforceGuestSwitch(); // #1920: guests off while the fleet was down
6661
6540
  }
@@ -7299,7 +7178,7 @@ export class FleetManager {
7299
7178
  return { ...a, name: badge.name, platform: badge.platform, scope: [...this.guestScope(owner)].sort(), reach: [...this.guestReach(owner)].sort(),
7300
7179
  devices: devices.get(k) ?? 0, narrow: own(cfg?.narrow, `${a.adapterId}:${a.userId}`) ?? null,
7301
7180
  // #2348: the grant as fleet.yaml has it (null: none), and the last code an admin sent from Settings.
7302
- grant: grant ? { ...(grant.name ? { name: grant.name } : {}), instances: [...grant.instances] } : null,
7181
+ grant: grant ? { ...(grant.name ? { name: grant.name } : {}), instances: [...grant.instances], ...(grant.level === "trusted" ? { level: "trusted" } : {}) } : null,
7303
7182
  ...(sent ? { code: { at: sent.at, outcome: sent.outcome.kind, ...(sent.outcome.kind === "sent" ? { expiresAt: sent.at + sent.outcome.expiresInMs } : {}),
7304
7183
  ...(sent.outcome.kind === "dm_failed" ? { reason: sent.outcome.reason, platform: sent.outcome.platform, ...(sent.outcome.startUrl ? { startUrl: sent.outcome.startUrl } : {}) } : {}) } } : {}) };
7305
7184
  }).sort((x, y) => (y.lastSeen ?? -1) - (x.lastSeen ?? -1));
@@ -7347,7 +7226,7 @@ export class FleetManager {
7347
7226
  const when = Date.parse(r.created_at.includes("T") ? r.created_at : `${r.created_at.replace(" ", "T")}Z`);
7348
7227
  return r.event_type === "guest_grant_changed"
7349
7228
  ? [{ kind: "grant", at: when, account, name, via: p.via === "confirmed" ? "confirmed" : "direct", added: names(p.added), removed: names(p.removed),
7350
- endedSessions: typeof p.endedSessions === "number" ? p.endedSessions : 0 }]
7229
+ endedSessions: typeof p.endedSessions === "number" ? p.endedSessions : 0, ...(p.level === "chat" || p.level === "trusted" ? { level: p.level } : {}) }]
7351
7230
  : [{ kind: "code", at: when, account, name, outcome: typeof p.outcome === "string" ? p.outcome : "unknown" }];
7352
7231
  });
7353
7232
  }
@@ -7386,7 +7265,7 @@ export class FleetManager {
7386
7265
  * confirmed in General, the agents added and taken away, and the sessions a removal ended. Never message content.
7387
7266
  */
7388
7267
  noteGuestGrantChange(change) {
7389
- this.logger.info({ account: change.account, via: change.via, added: change.added.length, removed: change.removed.length }, "Guest access changed in Settings");
7268
+ this.logger.info({ account: change.account, via: change.via, added: change.added.length, removed: change.removed.length, ...(change.level ? { level: change.level } : {}) }, "Guest access changed in Settings");
7390
7269
  try {
7391
7270
  this.eventLog?.insert("fleet", "guest_grant_changed", { ...change });
7392
7271
  }
@@ -7471,6 +7350,8 @@ export class FleetManager {
7471
7350
  * #1920 §5.4: a guest's own `/web revoke` — its sessions and its outstanding code; nobody else's (an admin's revokes
7472
7351
  * everyone's, as before).
7473
7352
  */
7353
+ /** #2855: the admin removed a guest — its own preferences (theme, language, favourites, sort, filter) go too. */
7354
+ forgetGuestPrefs(owner) { this.webPrefs.forgetGuest(webPersonKey(owner)); }
7474
7355
  revokeGuestWebSessions(owner) {
7475
7356
  this.initializeWebSessions();
7476
7357
  const key = `${owner.adapterId}\u0000${owner.userId}`;
@@ -10838,8 +10719,8 @@ export class FleetManager {
10838
10719
  }
10839
10720
  }
10840
10721
  checkToolPermission(sink, instanceName, tool) {
10841
- if (isCanvasWorkerTool(tool) && !this.canvasWorkerEnabled())
10842
- return { allowed: false, message: CANVAS_WORKER_EXPERIMENTAL_MESSAGE };
10722
+ if (isCanvasWorkerTool(tool))
10723
+ return { allowed: false, message: CANVAS_WORKER_EXPERIMENTAL_MESSAGE }; // #2799: removed
10843
10724
  const profile = resolveToolSet(this.fleetConfig?.instances[instanceName], instanceName);
10844
10725
  const allowed = mayUseTool(profile, tool);
10845
10726
  if (!allowed) {
@@ -11572,19 +11453,6 @@ export class FleetManager {
11572
11453
  }
11573
11454
  dispatchTypedIpc(name, msg) {
11574
11455
  const type = String(msg.type);
11575
- if (type.startsWith("fleet_") && WEB_EFFECT_TOOLS.has(type.slice(6))) {
11576
- const ipc = this.instanceIpcClients.get(name), fleetRequestId = msg.fleetRequestId;
11577
- if (!ipc || typeof fleetRequestId !== "string")
11578
- return;
11579
- void this.handleWebThemeHttp(name, type.slice(6), (msg.payload ?? {})).then(result => {
11580
- if (this.instanceIpcClients.get(name) === ipc)
11581
- ipc.send({ type: "fleet_web_theme_response", fleetRequestId, result });
11582
- }, err => {
11583
- if (this.instanceIpcClients.get(name) === ipc)
11584
- ipc.send({ type: "fleet_web_theme_response", fleetRequestId, error: err instanceof Error ? err.message : "Effect operation failed" });
11585
- }).catch(err => this.logger.debug({ err }, "Effect tool response could not be written"));
11586
- return;
11587
- }
11588
11456
  if (type.startsWith("fleet_schedule_")) {
11589
11457
  this.handleScheduleCrud(name, msg);
11590
11458
  return;
@@ -11638,10 +11506,9 @@ export class FleetManager {
11638
11506
  const args = (msg.payload ?? {});
11639
11507
  const result = tool === "get_web_appearance" || tool === "preview_web_appearance"
11640
11508
  ? this.appearanceTools.handle(instanceName, tool, args)
11641
- : tool === "export_web_mod" || tool === "propose_web_mod" ? this.agentMods.handle(instanceName, tool, args)
11642
- : tool === "office_view" || tool === "office_set" ? this.officeActs.handle(instanceName, tool, args)
11643
- : OFFICE_PACK_IPC.has(`fleet_${tool}`) ? this.officePackTools.handle(instanceName, tool, args)
11644
- : this.webAppearance.handleTool(instanceName, tool, args);
11509
+ : tool === "office_view" || tool === "office_set" ? this.officeActs.handle(instanceName, tool, args)
11510
+ : OFFICE_PACK_IPC.has(`fleet_${tool}`) ? this.officePackTools.handle(instanceName, tool, args)
11511
+ : this.webAppearance.handleTool(instanceName, tool, args);
11645
11512
  ipc.send({ type: "fleet_web_theme_response", fleetRequestId, result });
11646
11513
  }
11647
11514
  catch (err) {
@@ -12066,17 +11933,10 @@ export class FleetManager {
12066
11933
  }
12067
11934
  /** #1629: `agend-agent theme …` — the same service as the tools; `instance` is the token's. */
12068
11935
  async handleWebThemeHttp(instance, tool, args) {
12069
- if (isCanvasWorkerTool(tool) && !this.canvasWorkerEnabled())
12070
- return { error: "canvas_worker_experimental", message: CANVAS_WORKER_EXPERIMENTAL_MESSAGE };
12071
- if (WEB_EFFECT_TOOLS.has(tool)) {
12072
- if (!this.isKnownInstance(instance))
12073
- return { error: "unknown_instance" };
12074
- return this.effectTools.handle(instance, tool, args);
12075
- }
11936
+ if (isCanvasWorkerTool(tool))
11937
+ return { error: "canvas_worker_experimental", message: CANVAS_WORKER_EXPERIMENTAL_MESSAGE }; // #2799: removed
12076
11938
  if (tool === "get_web_appearance" || tool === "preview_web_appearance")
12077
11939
  return this.appearanceTools.handle(instance, tool, args);
12078
- if (tool === "export_web_mod" || tool === "propose_web_mod")
12079
- return this.agentMods.handle(instance, tool, args);
12080
11940
  if (tool === "office_view" || tool === "office_set")
12081
11941
  return this.officeActs.handle(instance, tool, args); // #2578
12082
11942
  if (OFFICE_PACK_IPC.has(`fleet_${tool}`))
@@ -12960,9 +12820,7 @@ export class FleetManager {
12960
12820
  this.themeStore.forget(name);
12961
12821
  this.webAppearance.forget(name);
12962
12822
  this.appearanceTools.forget(name);
12963
- this.effectToolsService?.forget(name);
12964
12823
  this.mods.forgetInstance(name);
12965
- this.agentMods.forget(name);
12966
12824
  this.officeActsInstance?.forget(name);
12967
12825
  this.officePackToolsInstance?.forget(name);
12968
12826
  }
@@ -15855,6 +15713,16 @@ export class FleetManager {
15855
15713
  return result;
15856
15714
  }
15857
15715
  /** #1269: an instance's chat command from the web chat (web-commands.ts); `publicLink`: a gateway request. */
15716
+ /** #2855: the table scope of an instance the web chat may command (a fleet instance or a ClassicBot room), else null. */
15717
+ webCommandScope(instance) {
15718
+ // Own entries only: a name like "__proto__" or "constructor" must not be found on the prototype (#1476 review).
15719
+ const instances = this.fleetConfig?.instances;
15720
+ if (instances && Object.prototype.hasOwnProperty.call(instances, instance))
15721
+ return isGeneralInstance(this.fleetConfig, instance) ? "general" : "fleet";
15722
+ return this.classicChannels?.getChannelIdByInstance(instance) !== undefined ? "classic" : null;
15723
+ }
15724
+ /** #2855: the platform an instance's channel is on, for the command table's cell (a web-only fleet: Discord's). */
15725
+ commandPlatform(instance) { return this.getAdapterForInstance(instance)?.type === "telegram" ? "telegram" : "discord"; }
15858
15726
  webCommand(input, opts) {
15859
15727
  const webMeta = (instance) => {
15860
15728
  const topicId = this.fleetConfig?.instances[instance]?.topic_id;
@@ -15863,6 +15731,7 @@ export class FleetManager {
15863
15731
  chatId: this.getGroupIdForInstance(instance) || "", messageId: newWebMessageId(),
15864
15732
  username: opts.person?.name ?? WEB_NAME_FALLBACK, userId: opts.person?.userId ?? WEB_NAME_FALLBACK,
15865
15733
  threadId: topicId != null ? String(topicId) : undefined, adapterId: this.getAdapterForInstance(instance)?.id ?? "", source: "web", humanOrigin: webHumanOrigin(true, opts.authority),
15734
+ ...(opts.guest ? { webGuest: true } : {}), // #2855: a guest's /steer or /btw reaches the agent as a guest's
15866
15735
  };
15867
15736
  };
15868
15737
  const choicesFromCache = (instance) => {
@@ -15874,13 +15743,8 @@ export class FleetManager {
15874
15743
  return { current, options: this.withCurrentModel(cached.models, current).map(o => ({ id: o.id, label: this.modelChoiceLabel(o, current) })) };
15875
15744
  };
15876
15745
  return runWebCommand({
15877
- // Own entries only: a name like "__proto__" or "constructor" must not be found on the prototype (#1476 review).
15878
- scope: (instance) => {
15879
- const instances = this.fleetConfig?.instances;
15880
- if (instances && Object.prototype.hasOwnProperty.call(instances, instance))
15881
- return isGeneralInstance(this.fleetConfig, instance) ? "general" : "fleet";
15882
- return this.classicChannels?.getChannelIdByInstance(instance) !== undefined ? "classic" : null;
15883
- },
15746
+ scope: (instance) => this.webCommandScope(instance),
15747
+ platform: (instance) => this.commandPlatform(instance),
15884
15748
  ctx: (instance) => this.topicCommands.getCtxText(instance),
15885
15749
  // Board 003648ac: a watch for the paste, armed when the daemon says it was written (raw_paste_written); one
15886
15750
  // for a command that was not handed over is dropped.
@@ -18156,14 +18020,6 @@ export class FleetManager {
18156
18020
  const room = this.classicChannels?.getAll().find(c => c.instanceName === name);
18157
18021
  return room ? `room:${room.channelId}:${room.adapterId ?? ""}` : null;
18158
18022
  }
18159
- /** The only Try-card publisher: no arbitrary SSE sender/role can manufacture this metadata. */
18160
- publishEffectTrial(instance, offer) {
18161
- const row = this.webChatHistory.recordEffectTrial(instance, offer, new Date().toISOString());
18162
- if (!row)
18163
- return false;
18164
- broadcastSseEvent(this.sseClients, "message", row, err => this.logger.debug({ err }, "SSE client write failed; evicting"), this.webChatHistory.cursorOf(row));
18165
- return true;
18166
- }
18167
18023
  /** Push an SSE event to all connected Web UI clients. */
18168
18024
  emitSseEvent(event, data) {
18169
18025
  // #1386: a prompt opened or closed, or an instance's state moved — "Needs you" may have changed.
@@ -18471,27 +18327,6 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
18471
18327
  this.syncRoleSkills(workDir, backend, "general", knowledgeDir);
18472
18328
  this.logger.debug({ knowledgeDir, workDir, backend }, "Synced general knowledge files");
18473
18329
  }
18474
- /** Update only AgEnD-managed skill copies; no launch or restart is needed for an experimental switch. */
18475
- canvasWorkerSettingChanged() {
18476
- this.refreshExperimentalSkills();
18477
- this.emitSseEvent("status", this.getUiStatus());
18478
- }
18479
- refreshExperimentalSkills() {
18480
- for (const [name, daemon] of this.daemons) {
18481
- try {
18482
- const cfg = daemon.getConfigSnapshot();
18483
- const backend = cfg.backend ?? this.fleetConfig?.defaults?.backend ?? "claude-code";
18484
- const workDir = this.resolveKnowledgeWorkDir(cfg.working_directory, backend, name);
18485
- const classic = this.classicChannels?.getAll().some(ch => ch.instanceName === name) === true;
18486
- if (classic && !insideManagedWorkspaces(workDir))
18487
- continue;
18488
- this.syncRoleSkills(workDir, backend, classic ? "classic" : cfg.general_topic ? "general" : "worker");
18489
- }
18490
- catch (err) {
18491
- this.logger.warn({ name, err }, "Could not refresh experimental skill visibility");
18492
- }
18493
- }
18494
- }
18495
18330
  /** Publish only the bundled skills eligible for an instance role. */
18496
18331
  syncRoleSkills(workDir, backend, role, knowledgeDir) {
18497
18332
  const skillSegments = FleetManager.SKILLS_DIR_SEGMENTS[backend];
@@ -18696,7 +18531,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
18696
18531
  continue;
18697
18532
  }
18698
18533
  managed.push(name);
18699
- const newContent = bundledSkillContents(join(srcSkills, name), this.canvasWorkerEnabled());
18534
+ const newContent = bundledSkillContents(join(srcSkills, name));
18700
18535
  if (refuse(dest))
18701
18536
  continue;
18702
18537
  try {
@@ -21136,7 +20971,6 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
21136
20971
  try {
21137
20972
  this.themeStore.forget(ch.instanceName);
21138
20973
  this.webAppearance.limiter.forget(ch.instanceName);
21139
- this.effectToolsService?.forget(ch.instanceName);
21140
20974
  }
21141
20975
  catch (err) {
21142
20976
  this.logger.warn({ err: err.message, instanceName: ch.instanceName }, "Dropping a removed room's web theme failed");
@@ -21643,9 +21477,6 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
21643
21477
  this.getConfigHistory().accepted("fleet.yaml", this.configPath, this.loadedFleetSource);
21644
21478
  this.migrateOfficeStorage();
21645
21479
  this.migrateNoticeDeliveryStorage();
21646
- if (canvasWorkerEnabled(oldConfig?.web) !== this.canvasWorkerEnabled()) {
21647
- this.canvasWorkerSettingChanged();
21648
- }
21649
21480
  // Classic behavior settings share the fleet defaults but are not entries
21650
21481
  // in fleet.yaml. Snapshot the old effective chain before reloading the
21651
21482
  // Classic file so SIGHUP can hot-apply either source without waiting for
@@ -23512,6 +23343,21 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
23512
23343
  }
23513
23344
  return scope.size ? scope : NO_GUEST_SCOPE;
23514
23345
  }
23346
+ /** #2855: whether a request carries a guest's live session (its cookie; checked without counting as activity). */
23347
+ presentsGuestSession(req) {
23348
+ if (!this.webToken || !this.webSessions)
23349
+ return false;
23350
+ const epoch = tokenEpoch(this.webToken);
23351
+ return readSessionCookies(req, this.webToken).some(id => this.webSessions.authenticate(id, epoch, { touch: false })?.tier === "guest");
23352
+ }
23353
+ /** #2855: whether the admin made this guest Trusted — only an explicit grant can (web.guests.grants[account].level). */
23354
+ guestTrusted(owner) {
23355
+ const guests = this.fleetConfig?.web?.guests;
23356
+ if (guests?.enabled !== true || !guests.grants)
23357
+ return false;
23358
+ const key = `${owner.adapterId}:${owner.userId}`;
23359
+ return Object.hasOwn(guests.grants, key) && guests.grants[key]?.level === "trusted";
23360
+ }
23515
23361
  /**
23516
23362
  * #1920 (#2104 review): what an account reaches before an admin's narrowing — its connection's topics (an open one's
23517
23363
  * named agents), its own Telegram private room, its verified group rooms. Narrowing can only take from this set, so it
@@ -23690,13 +23536,6 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
23690
23536
  return;
23691
23537
  }
23692
23538
  const requestPath = new URL(req.url ?? "/", `http://localhost:${port}`).pathname;
23693
- if (!this.canvasWorkerEnabled() && isCanvasWorkerHttpPath(requestPath)) {
23694
- res.writeHead(403, { "Content-Type": "application/json", "Cache-Control": "no-store" });
23695
- res.end(JSON.stringify({ error: "canvas_worker_experimental", message: CANVAS_WORKER_EXPERIMENTAL_MESSAGE }));
23696
- return;
23697
- }
23698
- if (this.effectBootstrap.handle(req, res))
23699
- return;
23700
23539
  // Branding is public, after Host admission: automatic favicon requests
23701
23540
  // must not become noisy web-token 401s in the browser console.
23702
23541
  // #2308: the icon routes and the pages' icon links follow the app icon set in Settings.
@@ -23704,8 +23543,9 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
23704
23543
  if (handleWebIconRequest(req, res, requestPath))
23705
23544
  return;
23706
23545
  // Public: the health probe, /agent (instance-token auth of its own), the
23707
- // sign-in surface, and /view's reads unless web.view_access says otherwise.
23708
- if (bypassesWebGate(req, requestPath, this.fleetConfig, p => !gatewayRequestContext(req) && (isViewPath(p) || isUsagePath(p)))) {
23546
+ // sign-in surface, and /view's reads unless web.view_access says otherwise (#2855: never a guest's, which go
23547
+ // through the guest gate and its limits on either listener).
23548
+ if (bypassesWebGate(req, requestPath, this.fleetConfig, p => !gatewayRequestContext(req) && (isViewPath(p) || isUsagePath(p)) && !this.presentsGuestSession(req))) {
23709
23549
  // fall through to the handlers below
23710
23550
  }
23711
23551
  else {
@@ -23771,7 +23611,9 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
23771
23611
  streamOpened: () => { void this.refreshGuestRooms(owner, { mode: "force" }); },
23772
23612
  history: () => this.webChatHistory,
23773
23613
  // #1920 §5.1: guests chat unless the host made them read-only (web.guests.chat: false).
23774
- chat: () => this.fleetConfig?.web?.guests?.chat !== false });
23614
+ chat: () => this.fleetConfig?.web?.guests?.chat !== false,
23615
+ // #2855: the admin's level for this guest, read at each request (a change applies at once).
23616
+ trusted: () => this.guestTrusted(owner) });
23775
23617
  }
23776
23618
  if (decision.via === "session" && this.webToken) {
23777
23619
  const upgrade = sessionCookieUpgrade(req, this.webToken, decision.session);
@@ -24177,7 +24019,6 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
24177
24019
  return;
24178
24020
  }
24179
24021
  const listener = createPreviewListener({ settings, healthPort: boundPort, config: this.fleetConfig,
24180
- canvasWorkerEnabled: () => this.canvasWorkerEnabled(),
24181
24022
  // #1723: with a web.preview_origin, the open public link's dashboard frames this listener's preview too.
24182
24023
  extraOrigins: () => { const o = settings.origin ? this.publicWebLink?.openDashboardOrigin() : null; return o ? [o] : []; } });
24183
24024
  this.previewListener = listener;
@@ -24354,8 +24195,6 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
24354
24195
  catch {
24355
24196
  return {};
24356
24197
  } })(),
24357
- // #2118: only this Chat's placement content; guest projection drops global revisions.
24358
- effect_chat_v: this.canvasWorkerEnabled() ? createHash("sha256").update(JSON.stringify(this.themeStore.get(name).placements.effects)).digest("hex").slice(0, 32) : null,
24359
24198
  // #1958: the identity colour's tiers (the page resolves them: the agent's only while it shows agents' themes).
24360
24199
  color: { ...(colors.get(name)?.color ? { person: colors.get(name).color } : {}), ...(image.agentColor ? { agent: image.agentColor } : {}),
24361
24200
  ...(image.roleColor ? { role: image.roleColor } : {}), auto: autoIdentityColor(name) },
@@ -24366,6 +24205,12 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
24366
24205
  tags: identity.tags,
24367
24206
  teams: instanceTeams(this.fleetConfig?.teams, name), // #2172: the sidebar groups a team's members under it
24368
24207
  last_reply: this.webChatHistory.lastAgentReply(name) ?? undefined, // #2179: its last reply to a person (ISO)
24208
+ // #2855: its last reply that reached the channel — the only reply time a guest's sidebar gets (guestStatus).
24209
+ last_reply_channel: this.webChatHistory.lastAgentReply(name, { channel: true }) ?? undefined,
24210
+ // #2855: the web commands a guest may run here — the table's "anyone" cells for this channel (guestWebCommands);
24211
+ // only while guests are on (no guest reads it otherwise).
24212
+ ...(this.fleetConfig?.web?.guests?.enabled === true
24213
+ ? { guest_commands: (() => { const scope = this.webCommandScope(name); return scope ? guestWebCommands(scope, this.commandPlatform(name)) : []; })() } : {}),
24369
24214
  status: this.getInstanceStatus(name),
24370
24215
  // `state` (presentation: may be awaiting_input) and `execution_state` (working / idle / stuck, or null —
24371
24216
  // what the dashboard's activity events carry) come from instancePresentation (#1212).
@@ -24382,7 +24227,6 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
24382
24227
  };
24383
24228
  });
24384
24229
  return {
24385
- canvas_worker: this.canvasWorkerEnabled(),
24386
24230
  instances,
24387
24231
  startup: this.startupDashboard?.snapshot() ?? null,
24388
24232
  publicLink: this.getPublicWebStatus(),