@songsid/agend 2.2.0 → 2.3.0-beta.1

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 (271) hide show
  1. package/dist/agent-cli.js +248 -2
  2. package/dist/agent-cli.js.map +1 -1
  3. package/dist/agent-endpoint.d.ts +2 -0
  4. package/dist/agent-endpoint.js +25 -0
  5. package/dist/agent-endpoint.js.map +1 -1
  6. package/dist/agent-file-read.d.ts +30 -0
  7. package/dist/agent-file-read.js +129 -0
  8. package/dist/agent-file-read.js.map +1 -0
  9. package/dist/agent-whats-new.js +13 -0
  10. package/dist/agent-whats-new.js.map +1 -1
  11. package/dist/atomic-write.js +9 -1
  12. package/dist/atomic-write.js.map +1 -1
  13. package/dist/auth-api.js +17 -0
  14. package/dist/auth-api.js.map +1 -1
  15. package/dist/backend/antigravity.js +3 -3
  16. package/dist/backend/antigravity.js.map +1 -1
  17. package/dist/backend/launch-effort.js +1 -1
  18. package/dist/backend/launch-effort.js.map +1 -1
  19. package/dist/backend/muse.js +10 -2
  20. package/dist/backend/muse.js.map +1 -1
  21. package/dist/channel/adapters/discord.d.ts +2 -0
  22. package/dist/channel/adapters/discord.js +17 -2
  23. package/dist/channel/adapters/discord.js.map +1 -1
  24. package/dist/channel/adapters/telegram.d.ts +8 -1
  25. package/dist/channel/adapters/telegram.js +74 -4
  26. package/dist/channel/adapters/telegram.js.map +1 -1
  27. package/dist/channel/mcp-tools.js +34 -0
  28. package/dist/channel/mcp-tools.js.map +1 -1
  29. package/dist/channel/types.d.ts +15 -0
  30. package/dist/config-validator.js +17 -1
  31. package/dist/config-validator.js.map +1 -1
  32. package/dist/daemon.js +244 -20
  33. package/dist/daemon.js.map +1 -1
  34. package/dist/fleet-context.d.ts +5 -0
  35. package/dist/fleet-manager.d.ts +89 -0
  36. package/dist/fleet-manager.js +401 -25
  37. package/dist/fleet-manager.js.map +1 -1
  38. package/dist/general-knowledge/skills/web-appearance/SKILL.md +258 -0
  39. package/dist/instance-avatar.js +3 -0
  40. package/dist/instance-avatar.js.map +1 -1
  41. package/dist/instance-config-impact.js +3 -0
  42. package/dist/instance-config-impact.js.map +1 -1
  43. package/dist/instance-lifecycle.js +9 -4
  44. package/dist/instance-lifecycle.js.map +1 -1
  45. package/dist/needs-you-hub.d.ts +4 -0
  46. package/dist/needs-you-hub.js +31 -6
  47. package/dist/needs-you-hub.js.map +1 -1
  48. package/dist/needs-you.d.ts +1 -13
  49. package/dist/needs-you.js +3 -37
  50. package/dist/needs-you.js.map +1 -1
  51. package/dist/outbound-handlers.d.ts +5 -0
  52. package/dist/outbound-handlers.js +30 -9
  53. package/dist/outbound-handlers.js.map +1 -1
  54. package/dist/outbound-schemas.d.ts +64 -0
  55. package/dist/outbound-schemas.js +50 -0
  56. package/dist/outbound-schemas.js.map +1 -1
  57. package/dist/public-web-gateway.js +14 -0
  58. package/dist/public-web-gateway.js.map +1 -1
  59. package/dist/repo-worktree-guard.d.ts +36 -0
  60. package/dist/repo-worktree-guard.js +143 -0
  61. package/dist/repo-worktree-guard.js.map +1 -0
  62. package/dist/scheduler/scheduler.d.ts +58 -1
  63. package/dist/scheduler/scheduler.js +176 -10
  64. package/dist/scheduler/scheduler.js.map +1 -1
  65. package/dist/settings-api.js +74 -1
  66. package/dist/settings-api.js.map +1 -1
  67. package/dist/settings-change.js +8 -0
  68. package/dist/settings-change.js.map +1 -1
  69. package/dist/settings-effect.js +16 -2
  70. package/dist/settings-effect.js.map +1 -1
  71. package/dist/settings-http-confirmation.js +5 -0
  72. package/dist/settings-http-confirmation.js.map +1 -1
  73. package/dist/tool-permissions.js +50 -0
  74. package/dist/tool-permissions.js.map +1 -1
  75. package/dist/topic-commands.d.ts +10 -6
  76. package/dist/topic-commands.js +21 -17
  77. package/dist/topic-commands.js.map +1 -1
  78. package/dist/topic-link.d.ts +128 -0
  79. package/dist/topic-link.js +269 -0
  80. package/dist/topic-link.js.map +1 -0
  81. package/dist/types.d.ts +13 -0
  82. package/dist/ui/app-effects.js +187 -0
  83. package/dist/ui/chat-effect-posters.js +42 -0
  84. package/dist/ui/chat-effects.js +72 -0
  85. package/dist/ui/chat-live-replies.js +58 -0
  86. package/dist/ui/chat-render.js +4 -4
  87. package/dist/ui/chat-store.js +9 -3
  88. package/dist/ui/chat-strings.js +15 -0
  89. package/dist/ui/chat-thread.js +47 -21
  90. package/dist/ui/chat-tour.js +1 -1
  91. package/dist/ui/effect-trial-controller.js +119 -0
  92. package/dist/ui/effect-trial-offer.js +31 -0
  93. package/dist/ui/effect-trial-panel.js +37 -0
  94. package/dist/ui/image-lightbox.js +1 -1
  95. package/dist/ui/panel-chat.js +85 -17
  96. package/dist/ui/panel-details.js +18 -5
  97. package/dist/ui/panel-fleet.js +3 -3
  98. package/dist/ui/panel-log.js +6 -6
  99. package/dist/ui/panel-needs.js +3 -3
  100. package/dist/ui/panel-settings.js +8 -5
  101. package/dist/ui/preview-panel.js +25 -9
  102. package/dist/ui/settings-appearance.js +325 -0
  103. package/dist/ui/settings-effects.js +201 -0
  104. package/dist/ui/settings-mods.js +257 -0
  105. package/dist/ui/settings-owner-effects.js +80 -0
  106. package/dist/ui/settings-strings.js +245 -0
  107. package/dist/ui/shared/agend-auth.js +27 -3
  108. package/dist/ui/shared/agent-widget.js +248 -0
  109. package/dist/ui/shared/app-i18n.js +2 -0
  110. package/dist/ui/shared/app-prefs.js +30 -10
  111. package/dist/ui/shared/app-route.js +23 -1
  112. package/dist/ui/shared/app-session.js +1 -1
  113. package/dist/ui/shared/app-shell.js +29 -16
  114. package/dist/ui/shared/app-store.js +2 -0
  115. package/dist/ui/shared/app-stream.js +12 -7
  116. package/dist/ui/shared/app-themes.js +188 -0
  117. package/dist/ui/shared/app.css +214 -1
  118. package/dist/ui/shared/app.js +6 -0
  119. package/dist/ui/shared/card-strings.js +28 -0
  120. package/dist/ui/shared/effect-app-runtime.js +248 -0
  121. package/dist/ui/shared/effect-baseline.js +64 -0
  122. package/dist/ui/shared/effect-chat-runtime.js +154 -0
  123. package/dist/ui/shared/effect-cursor.js +125 -0
  124. package/dist/ui/shared/effect-frame.js +127 -0
  125. package/dist/ui/shared/effect-geometry.js +122 -0
  126. package/dist/ui/shared/effect-host-style.js +10 -0
  127. package/dist/ui/shared/effect-owner-regions.js +74 -0
  128. package/dist/ui/shared/effect-owner-source.js +115 -0
  129. package/dist/ui/shared/effect-policy.js +137 -0
  130. package/dist/ui/shared/effect-poster-reader.js +98 -0
  131. package/dist/ui/shared/effect-posters.js +117 -0
  132. package/dist/ui/shared/effect-profiler.js +162 -0
  133. package/dist/ui/shared/effect-profiles.js +65 -0
  134. package/dist/ui/shared/effect-registry.js +496 -0
  135. package/dist/ui/shared/effect-runtime-loader.js +234 -0
  136. package/dist/ui/shared/effect-safety.js +153 -0
  137. package/dist/ui/shared/effect-stock-snapshot.js +34 -0
  138. package/dist/ui/shared/effect-stock-trust.js +165 -0
  139. package/dist/ui/shared/effect-trial-source.js +103 -0
  140. package/dist/ui/shared/effect-trial-stage.js +147 -0
  141. package/dist/ui/shared/header-tools.js +1 -1
  142. package/dist/ui/shared/identity-card.js +202 -0
  143. package/dist/ui/shared/identity-color.js +261 -0
  144. package/dist/ui/shared/instance-nav.js +22 -14
  145. package/dist/ui/shared/owner-css.js +221 -0
  146. package/dist/ui/shared/owner-effect-prefs.js +117 -0
  147. package/dist/ui/shared/panel-view.js +81 -8
  148. package/dist/ui/shared/signin.js +2 -0
  149. package/dist/ui/shared/ui-dialog.js +1 -1
  150. package/dist/ui/shared/ui-icons.js +2 -0
  151. package/dist/ui/shared/ui-menu.js +1 -1
  152. package/dist/ui/shared/ui-toast.js +1 -1
  153. package/dist/ui/shared/view-strings.js +30 -0
  154. package/dist/ui/signin.html +1 -0
  155. package/dist/view-api.d.ts +29 -0
  156. package/dist/view-api.js +188 -8
  157. package/dist/view-api.js.map +1 -1
  158. package/dist/web-api.d.ts +42 -0
  159. package/dist/web-api.js +896 -36
  160. package/dist/web-api.js.map +1 -1
  161. package/dist/web-appearance-limits.d.ts +82 -0
  162. package/dist/web-appearance-limits.js +83 -0
  163. package/dist/web-appearance-limits.js.map +1 -0
  164. package/dist/web-appearance-policy.d.ts +66 -0
  165. package/dist/web-appearance-policy.js +94 -0
  166. package/dist/web-appearance-policy.js.map +1 -0
  167. package/dist/web-appearance-tools.d.ts +50 -0
  168. package/dist/web-appearance-tools.js +105 -0
  169. package/dist/web-appearance-tools.js.map +1 -0
  170. package/dist/web-appearance-view.d.ts +99 -0
  171. package/dist/web-appearance-view.js +163 -0
  172. package/dist/web-appearance-view.js.map +1 -0
  173. package/dist/web-appearance.d.ts +79 -0
  174. package/dist/web-appearance.js +298 -0
  175. package/dist/web-appearance.js.map +1 -0
  176. package/dist/web-asset-parts.d.ts +98 -0
  177. package/dist/web-asset-parts.js +133 -0
  178. package/dist/web-asset-parts.js.map +1 -0
  179. package/dist/web-auth.js +17 -1
  180. package/dist/web-auth.js.map +1 -1
  181. package/dist/web-card.d.ts +96 -0
  182. package/dist/web-card.js +232 -0
  183. package/dist/web-card.js.map +1 -0
  184. package/dist/web-chat-history.d.ts +13 -0
  185. package/dist/web-chat-history.js +23 -2
  186. package/dist/web-chat-history.js.map +1 -1
  187. package/dist/web-effect-bundle.d.ts +28 -0
  188. package/dist/web-effect-bundle.js +126 -0
  189. package/dist/web-effect-bundle.js.map +1 -0
  190. package/dist/web-effect-canonical.d.ts +8 -0
  191. package/dist/web-effect-canonical.js +27 -0
  192. package/dist/web-effect-canonical.js.map +1 -0
  193. package/dist/web-effect-library.d.ts +114 -0
  194. package/dist/web-effect-library.js +530 -0
  195. package/dist/web-effect-library.js.map +1 -0
  196. package/dist/web-effect-poster.d.ts +8 -0
  197. package/dist/web-effect-poster.js +52 -0
  198. package/dist/web-effect-poster.js.map +1 -0
  199. package/dist/web-effect-profiles.d.ts +118 -0
  200. package/dist/web-effect-profiles.js +180 -0
  201. package/dist/web-effect-profiles.js.map +1 -0
  202. package/dist/web-effect-service.d.ts +86 -0
  203. package/dist/web-effect-service.js +312 -0
  204. package/dist/web-effect-service.js.map +1 -0
  205. package/dist/web-effect-tools.d.ts +47 -0
  206. package/dist/web-effect-tools.js +126 -0
  207. package/dist/web-effect-tools.js.map +1 -0
  208. package/dist/web-effect-trials.d.ts +86 -0
  209. package/dist/web-effect-trials.js +204 -0
  210. package/dist/web-effect-trials.js.map +1 -0
  211. package/dist/web-effect-upload.d.ts +29 -0
  212. package/dist/web-effect-upload.js +190 -0
  213. package/dist/web-effect-upload.js.map +1 -0
  214. package/dist/web-effect-worker.d.ts +8 -0
  215. package/dist/web-effect-worker.js +170 -0
  216. package/dist/web-effect-worker.js.map +1 -0
  217. package/dist/web-effect.d.ts +89 -0
  218. package/dist/web-effect.js +132 -0
  219. package/dist/web-effect.js.map +1 -0
  220. package/dist/web-identity-color.d.ts +87 -0
  221. package/dist/web-identity-color.js +209 -0
  222. package/dist/web-identity-color.js.map +1 -0
  223. package/dist/web-identity-palette.d.ts +113 -0
  224. package/dist/web-identity-palette.js +84 -0
  225. package/dist/web-identity-palette.js.map +1 -0
  226. package/dist/web-image-meta.d.ts +25 -0
  227. package/dist/web-image-meta.js +200 -0
  228. package/dist/web-image-meta.js.map +1 -0
  229. package/dist/web-mod-agent.d.ts +50 -0
  230. package/dist/web-mod-agent.js +203 -0
  231. package/dist/web-mod-agent.js.map +1 -0
  232. package/dist/web-mod-bundle.d.ts +144 -0
  233. package/dist/web-mod-bundle.js +464 -0
  234. package/dist/web-mod-bundle.js.map +1 -0
  235. package/dist/web-mod-export.d.ts +59 -0
  236. package/dist/web-mod-export.js +79 -0
  237. package/dist/web-mod-export.js.map +1 -0
  238. package/dist/web-mod-store.d.ts +283 -0
  239. package/dist/web-mod-store.js +624 -0
  240. package/dist/web-mod-store.js.map +1 -0
  241. package/dist/web-org.d.ts +3 -3
  242. package/dist/web-org.js +11 -17
  243. package/dist/web-org.js.map +1 -1
  244. package/dist/web-owner-css-store.d.ts +180 -0
  245. package/dist/web-owner-css-store.js +384 -0
  246. package/dist/web-owner-css-store.js.map +1 -0
  247. package/dist/web-owner-css.d.ts +58 -0
  248. package/dist/web-owner-css.js +552 -0
  249. package/dist/web-owner-css.js.map +1 -0
  250. package/dist/web-prefs.d.ts +48 -0
  251. package/dist/web-prefs.js +209 -33
  252. package/dist/web-prefs.js.map +1 -1
  253. package/dist/web-preview.d.ts +9 -1
  254. package/dist/web-preview.js +24 -7
  255. package/dist/web-preview.js.map +1 -1
  256. package/dist/web-settings-patch.d.ts +18 -0
  257. package/dist/web-settings-patch.js +51 -0
  258. package/dist/web-settings-patch.js.map +1 -1
  259. package/dist/web-shell-routes.d.ts +1 -1
  260. package/dist/web-shell-routes.js +1 -1
  261. package/dist/web-shell-routes.js.map +1 -1
  262. package/dist/web-theme-store.d.ts +218 -0
  263. package/dist/web-theme-store.js +516 -0
  264. package/dist/web-theme-store.js.map +1 -0
  265. package/dist/web-theme.d.ts +121 -0
  266. package/dist/web-theme.js +407 -0
  267. package/dist/web-theme.js.map +1 -0
  268. package/dist/web-widget.d.ts +72 -0
  269. package/dist/web-widget.js +245 -0
  270. package/dist/web-widget.js.map +1 -0
  271. package/package.json +1 -1
@@ -0,0 +1,83 @@
1
+ /**
2
+ * #1629 r4.1 Q3 (b): the identity card's upload limits are fleet configuration (`web.appearance`), under hard bounds
3
+ * no configuration passes. r4's numbers are the defaults. A value outside its bound is clamped and said so (the config
4
+ * validator's warnings: `agend doctor`, the fleet log at startup, Settings); one out-of-range number never stops the
5
+ * fleet. A limit applies to new uploads only: nothing stored is ever checked again or deleted because a limit moved.
6
+ */
7
+ const KIB = 1024, MIB = 1024 * 1024;
8
+ /** r4 §1's numbers, in `web.appearance`'s own units. */
9
+ export const APPEARANCE_DEFAULTS = {
10
+ portrait: { max_width: 1024, max_height: 1536, max_kib: 1536 },
11
+ banner: { max_width: 1600, max_height: 600, max_kib: 768 },
12
+ instance_quota: { files: 8, mib: 4 },
13
+ };
14
+ /** [floor, ceiling] per setting (r4.1 Q3 (b)). */
15
+ export const APPEARANCE_BOUNDS = {
16
+ portrait: { max_width: [64, 2048], max_height: [64, 3072], max_kib: [32, 4096] },
17
+ banner: { max_width: [64, 3200], max_height: [64, 1200], max_kib: [32, 2048] },
18
+ instance_quota: { files: [2, 16], mib: [1, 16] },
19
+ };
20
+ /**
21
+ * The limits in force for `config` (the fleet's `web.appearance`, possibly absent or malformed), and why any value
22
+ * differs from what was written. Unknown keys and wrong types are warned about and their defaults used.
23
+ */
24
+ export function effectiveAppearanceLimits(config) {
25
+ const warnings = [];
26
+ const root = isObj(config) ? config : {};
27
+ if (config !== undefined && config !== null && !isObj(config))
28
+ warnings.push({ path: "web.appearance", message: "must be a mapping — the defaults are used" });
29
+ for (const k of Object.keys(root))
30
+ if (!Object.hasOwn(APPEARANCE_DEFAULTS, k))
31
+ warnings.push({ path: `web.appearance.${k}`, message: `unknown key (known: ${Object.keys(APPEARANCE_DEFAULTS).join(", ")}) — ignored` });
32
+ const read = (group) => {
33
+ const defaults = APPEARANCE_DEFAULTS[group];
34
+ const bounds = APPEARANCE_BOUNDS[group];
35
+ const raw = Object.hasOwn(root, group) ? root[group] : undefined;
36
+ const given = isObj(raw) ? raw : {};
37
+ if (raw !== undefined && !isObj(raw))
38
+ warnings.push({ path: `web.appearance.${group}`, message: "must be a mapping — the defaults are used" });
39
+ for (const k of Object.keys(given))
40
+ if (!Object.hasOwn(defaults, k))
41
+ warnings.push({ path: `web.appearance.${group}.${k}`, message: `unknown key (known: ${Object.keys(defaults).join(", ")}) — ignored` });
42
+ const out = {};
43
+ for (const [k, def] of Object.entries(defaults)) {
44
+ const path = `web.appearance.${group}.${k}`;
45
+ const v = Object.hasOwn(given, k) ? given[k] : undefined;
46
+ if (v === undefined) {
47
+ out[k] = def;
48
+ continue;
49
+ }
50
+ if (typeof v !== "number" || !Number.isInteger(v)) {
51
+ warnings.push({ path, message: `must be a whole number — the default ${def} is used` });
52
+ out[k] = def;
53
+ continue;
54
+ }
55
+ const [floor, ceiling] = bounds[k];
56
+ if (v < floor) {
57
+ warnings.push({ path, message: `${v} is below the floor ${floor} — ${floor} is used` });
58
+ out[k] = floor;
59
+ continue;
60
+ }
61
+ if (v > ceiling) {
62
+ warnings.push({ path, message: `${v} is above the ceiling ${ceiling} — ${ceiling} is used` });
63
+ out[k] = ceiling;
64
+ continue;
65
+ }
66
+ out[k] = v;
67
+ }
68
+ return out;
69
+ };
70
+ const p = read("portrait"), b = read("banner"), q = read("instance_quota");
71
+ return {
72
+ limits: {
73
+ portrait: { maxWidth: p.max_width, maxHeight: p.max_height, maxBytes: p.max_kib * KIB },
74
+ banner: { maxWidth: b.max_width, maxHeight: b.max_height, maxBytes: b.max_kib * KIB },
75
+ quota: { files: q.files, bytes: q.mib * MIB },
76
+ },
77
+ warnings,
78
+ };
79
+ }
80
+ function isObj(v) {
81
+ return typeof v === "object" && v !== null && !Array.isArray(v);
82
+ }
83
+ //# sourceMappingURL=web-appearance-limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-appearance-limits.js","sourceRoot":"","sources":["../src/web-appearance-limits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * #1629: the one rate limiter for every agent appearance write — theme, effect, widget and mod (r2 (c), r4) — and the
3
+ * fleet-wide switch. Shared by the theme tools (B1) and the effect tools (B2): one budget per instance, not one each.
4
+ *
5
+ * - change: at least 30 s between accepted changes and at most 10 in a rolling hour (set_web_theme, apply/remove of an
6
+ * effect or widget, an agent's own reset);
7
+ * - upload: at most 3 in a rolling hour (theme assets, effects and widgets, proposed mods together);
8
+ * - data: a placed widget's new values (r4-4) — at least 10 s apart and at most 180 in a rolling hour, a budget of its
9
+ * own, so a widget showing progress never spends the theme's changes.
10
+ *
11
+ * Only accepted calls count: a handler asks `check` before it validates, and `record`s only after its write is on
12
+ * disk, all in one synchronous stretch (a refused or invalid call, or a failed write, costs nothing). The clock is
13
+ * monotonic, so a wall-clock jump neither opens nor closes the window; a fleet restart clears it (documented).
14
+ * The human's reset, lock and removals are never limited.
15
+ */
16
+ export type AppearanceBucket = "change" | "upload" | "data";
17
+ export declare const APPEARANCE_LIMITS: {
18
+ readonly changeGapMs: 30000;
19
+ readonly changesPerHour: 10;
20
+ readonly uploadsPerHour: 3;
21
+ readonly dataGapMs: 10000;
22
+ readonly dataPerHour: 180;
23
+ };
24
+ export type LimitVerdict = {
25
+ ok: true;
26
+ } | {
27
+ ok: false;
28
+ error: "rate_limited";
29
+ retry_after_s: number;
30
+ };
31
+ export declare class AppearanceLimiter {
32
+ private readonly now;
33
+ private readonly limits;
34
+ private readonly changes;
35
+ private readonly uploads;
36
+ private readonly data;
37
+ constructor(now?: () => number, limits?: {
38
+ readonly changeGapMs: 30000;
39
+ readonly changesPerHour: 10;
40
+ readonly uploadsPerHour: 3;
41
+ readonly dataGapMs: 10000;
42
+ readonly dataPerHour: 180;
43
+ });
44
+ private recent;
45
+ private waitMs;
46
+ /** Would these buckets admit a call now? Changes nothing. */
47
+ check(instance: string, buckets: readonly AppearanceBucket[]): LimitVerdict;
48
+ /** Count an accepted call — only after its write succeeded. */
49
+ record(instance: string, buckets: readonly AppearanceBucket[]): void;
50
+ /** What an agent sees in get_web_theme (and B2's tools): what is left, and when the next change may come. */
51
+ remaining(instance: string): {
52
+ changes: number;
53
+ uploads: number;
54
+ retry_after_s: number;
55
+ widget_data: number;
56
+ widget_data_retry_after_s: number;
57
+ };
58
+ /** An instance removed: its counters go with it. */
59
+ forget(instance: string): void;
60
+ }
61
+ /** `web.themes.agent_edit` (default true): false turns every agent appearance write off, fleet-wide; stored choices stay. */
62
+ export declare function agentAppearanceEditAllowed(web: {
63
+ themes?: {
64
+ agent_edit?: boolean;
65
+ };
66
+ } | undefined | null): boolean;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * #1629: the one rate limiter for every agent appearance write — theme, effect, widget and mod (r2 (c), r4) — and the
3
+ * fleet-wide switch. Shared by the theme tools (B1) and the effect tools (B2): one budget per instance, not one each.
4
+ *
5
+ * - change: at least 30 s between accepted changes and at most 10 in a rolling hour (set_web_theme, apply/remove of an
6
+ * effect or widget, an agent's own reset);
7
+ * - upload: at most 3 in a rolling hour (theme assets, effects and widgets, proposed mods together);
8
+ * - data: a placed widget's new values (r4-4) — at least 10 s apart and at most 180 in a rolling hour, a budget of its
9
+ * own, so a widget showing progress never spends the theme's changes.
10
+ *
11
+ * Only accepted calls count: a handler asks `check` before it validates, and `record`s only after its write is on
12
+ * disk, all in one synchronous stretch (a refused or invalid call, or a failed write, costs nothing). The clock is
13
+ * monotonic, so a wall-clock jump neither opens nor closes the window; a fleet restart clears it (documented).
14
+ * The human's reset, lock and removals are never limited.
15
+ */
16
+ export const APPEARANCE_LIMITS = { changeGapMs: 30_000, changesPerHour: 10, uploadsPerHour: 3, dataGapMs: 10_000, dataPerHour: 180 };
17
+ const HOUR_MS = 60 * 60 * 1000;
18
+ export class AppearanceLimiter {
19
+ now;
20
+ limits;
21
+ changes = new Map();
22
+ uploads = new Map();
23
+ data = new Map();
24
+ constructor(now = () => performance.now(), limits = APPEARANCE_LIMITS) {
25
+ this.now = now;
26
+ this.limits = limits;
27
+ }
28
+ recent(map, instance, now) {
29
+ const kept = (map.get(instance) ?? []).filter(t => now - t < HOUR_MS);
30
+ if (kept.length)
31
+ map.set(instance, kept);
32
+ else
33
+ map.delete(instance);
34
+ return kept;
35
+ }
36
+ waitMs(instance, buckets, now) {
37
+ const waits = [];
38
+ if (buckets.includes("change")) {
39
+ const c = this.recent(this.changes, instance, now);
40
+ const last = c.at(-1);
41
+ if (last !== undefined && now - last < this.limits.changeGapMs)
42
+ waits.push(this.limits.changeGapMs - (now - last));
43
+ if (c.length >= this.limits.changesPerHour)
44
+ waits.push(HOUR_MS - (now - c[0]));
45
+ }
46
+ if (buckets.includes("upload")) {
47
+ const u = this.recent(this.uploads, instance, now);
48
+ if (u.length >= this.limits.uploadsPerHour)
49
+ waits.push(HOUR_MS - (now - u[0]));
50
+ }
51
+ if (buckets.includes("data")) {
52
+ const d = this.recent(this.data, instance, now);
53
+ const last = d.at(-1);
54
+ if (last !== undefined && now - last < this.limits.dataGapMs)
55
+ waits.push(this.limits.dataGapMs - (now - last));
56
+ if (d.length >= this.limits.dataPerHour)
57
+ waits.push(HOUR_MS - (now - d[0]));
58
+ }
59
+ return waits.length ? Math.max(...waits) : 0;
60
+ }
61
+ /** Would these buckets admit a call now? Changes nothing. */
62
+ check(instance, buckets) {
63
+ const wait = this.waitMs(instance, buckets, this.now());
64
+ return wait > 0 ? { ok: false, error: "rate_limited", retry_after_s: Math.ceil(wait / 1000) } : { ok: true };
65
+ }
66
+ /** Count an accepted call — only after its write succeeded. */
67
+ record(instance, buckets) {
68
+ const now = this.now();
69
+ if (buckets.includes("change"))
70
+ this.changes.set(instance, [...this.recent(this.changes, instance, now), now]);
71
+ if (buckets.includes("upload"))
72
+ this.uploads.set(instance, [...this.recent(this.uploads, instance, now), now]);
73
+ if (buckets.includes("data"))
74
+ this.data.set(instance, [...this.recent(this.data, instance, now), now]);
75
+ }
76
+ /** What an agent sees in get_web_theme (and B2's tools): what is left, and when the next change may come. */
77
+ remaining(instance) {
78
+ const now = this.now();
79
+ return {
80
+ changes: Math.max(0, this.limits.changesPerHour - this.recent(this.changes, instance, now).length),
81
+ uploads: Math.max(0, this.limits.uploadsPerHour - this.recent(this.uploads, instance, now).length),
82
+ retry_after_s: Math.ceil(this.waitMs(instance, ["change"], now) / 1000),
83
+ widget_data: Math.max(0, this.limits.dataPerHour - this.recent(this.data, instance, now).length),
84
+ widget_data_retry_after_s: Math.ceil(this.waitMs(instance, ["data"], now) / 1000),
85
+ };
86
+ }
87
+ /** An instance removed: its counters go with it. */
88
+ forget(instance) { this.changes.delete(instance); this.uploads.delete(instance); this.data.delete(instance); }
89
+ }
90
+ /** `web.themes.agent_edit` (default true): false turns every agent appearance write off, fleet-wide; stored choices stay. */
91
+ export function agentAppearanceEditAllowed(web) {
92
+ return web?.themes?.agent_edit !== false;
93
+ }
94
+ //# sourceMappingURL=web-appearance-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-appearance-policy.js","sourceRoot":"","sources":["../src/web-appearance-policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { type AppearanceInput, type FrameEntry } from "./web-appearance-view.js";
2
+ import type { ResolvedCard } from "./web-card.js";
3
+ import type { Theme } from "./web-theme.js";
4
+ type Picture = {
5
+ bytes: Buffer;
6
+ mime: string;
7
+ } | null;
8
+ export interface AppearanceDeps {
9
+ frame(name: string): FrameEntry;
10
+ theme(name: string): {
11
+ theme: Theme;
12
+ locked: boolean;
13
+ widgets?: AppearanceInput["widgets"];
14
+ };
15
+ card(name: string): ResolvedCard;
16
+ /** The resolved card's picture for a slot (the person's, else the agent's). */
17
+ cardPicture(name: string, slot: "portrait" | "banner"): Picture;
18
+ themeAsset(name: string, id: string): Picture;
19
+ profileAvatar(name: string): Picture;
20
+ botAvatar(name: string, key: string): Picture;
21
+ /** get_web_theme's agent_edit and limits (one source for both tools). */
22
+ themeAnswer(name: string): {
23
+ agent_edit?: unknown;
24
+ limits?: unknown;
25
+ };
26
+ /** #1958: the host's identity-colour settings (web prefs color.show / color.off), for the colour view. */
27
+ colorPrefs?(): {
28
+ show?: unknown;
29
+ off?: boolean;
30
+ };
31
+ clock?: () => number;
32
+ }
33
+ /** A preview whose files (base64) fit one message is sent whole; a larger one is fetched in parts. */
34
+ export declare const PREVIEW_INLINE_BYTES: number;
35
+ export declare const PREVIEW_IDLE_MS = 60000;
36
+ export declare class AppearanceTools {
37
+ private readonly deps;
38
+ private readonly previews;
39
+ constructor(deps: AppearanceDeps);
40
+ private now;
41
+ private input;
42
+ /** The face drawn, as bytes of this instance's: the theme's picture, the person's upload, or its own bot's. */
43
+ private avatar;
44
+ handle(name: string, tool: string, args: Record<string, unknown>): Record<string, unknown>;
45
+ /** One part of the caller's own preview in progress (its id, a file it holds, an index in range, not gone stale). */
46
+ private part;
47
+ /** An instance was removed. */
48
+ forget(name: string): void;
49
+ }
50
+ export {};
@@ -0,0 +1,105 @@
1
+ /**
2
+ * #1629 r4 §1: get_web_appearance and preview_web_appearance, on the fleet's side. Both read only the caller's own
3
+ * record (no target), through resolvers the fleet passes in — the status frame's entry, the theme, the card, this
4
+ * instance's pictures. The preview's files are bytes for the daemon or the CLI to write in the agent's working
5
+ * directory (the fleet never names a path); a preview larger than one message is handed over in parts.
6
+ */
7
+ import { createHash, randomBytes } from "node:crypto";
8
+ import { appearanceView, PICTURE_ROLES, pictureFileName, previewSvg } from "./web-appearance-view.js";
9
+ import { ASSET_PART_BYTES } from "./web-asset-parts.js";
10
+ /** A preview whose files (base64) fit one message is sent whole; a larger one is fetched in parts. */
11
+ export const PREVIEW_INLINE_BYTES = 600 * 1024;
12
+ export const PREVIEW_IDLE_MS = 60_000;
13
+ const MODES = ["light", "dark"];
14
+ export class AppearanceTools {
15
+ deps;
16
+ previews = new Map();
17
+ constructor(deps) {
18
+ this.deps = deps;
19
+ }
20
+ now() { return (this.deps.clock ?? (() => performance.now()))(); }
21
+ input(name) {
22
+ const entry = this.deps.frame(name);
23
+ const t = this.deps.theme(name);
24
+ const answer = this.deps.themeAnswer(name);
25
+ return { entry, theme: t.theme, card: this.deps.card(name), locked: t.locked, agentEdit: answer.agent_edit !== false, limits: (answer.limits ?? {}), widgets: t.widgets,
26
+ colorPrefs: this.deps.colorPrefs?.() ?? {} };
27
+ }
28
+ /** The face drawn, as bytes of this instance's: the theme's picture, the person's upload, or its own bot's. */
29
+ avatar(name, input) {
30
+ const e = input.entry;
31
+ if (e.theme_avatar?.kind === "image" && input.theme.avatar)
32
+ return this.deps.themeAsset(name, input.theme.avatar);
33
+ const src = e.avatar?.kind === "image" ? e.avatar.src ?? "" : "";
34
+ if (src.startsWith("/ui/img/avatar/"))
35
+ return this.deps.profileAvatar(name);
36
+ const bot = /^\/ui\/img\/bot\/([^/?#]+)/.exec(src);
37
+ if (bot) {
38
+ try {
39
+ return this.deps.botAvatar(name, decodeURIComponent(bot[1]));
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ }
45
+ return null;
46
+ }
47
+ handle(name, tool, args) {
48
+ const now = this.now();
49
+ for (const [k, b] of this.previews)
50
+ if (now - b.last > PREVIEW_IDLE_MS)
51
+ this.previews.delete(k); // nothing stale stays
52
+ if (tool === "get_web_appearance")
53
+ return appearanceView(this.input(name));
54
+ if (tool !== "preview_web_appearance")
55
+ return { error: "unknown_tool" };
56
+ if (args.part !== undefined)
57
+ return this.part(name, args.part, now);
58
+ const modes = args.modes === undefined ? MODES : args.modes;
59
+ if (!Array.isArray(modes) || !modes.length || modes.length > 2 || !modes.every(m => m === "light" || m === "dark") || new Set(modes).size !== modes.length) {
60
+ return { error: "modes", message: "modes: light and/or dark" };
61
+ }
62
+ const input = this.input(name);
63
+ const bytes = {
64
+ avatar: this.avatar(name, input),
65
+ portrait: input.card.portrait ? this.deps.cardPicture(name, "portrait") : null,
66
+ banner: input.card.banner ? this.deps.cardPicture(name, "banner") : null,
67
+ background: input.theme.background ? this.deps.themeAsset(name, input.theme.background.image) : null,
68
+ };
69
+ // Each picture is its own file next to the SVGs (#1890 review: inside, they outgrew what an agent's Read takes).
70
+ const names = {};
71
+ const files = [];
72
+ const add = (fileName, data) => files.push({ name: fileName, data, sha256: createHash("sha256").update(data).digest("hex") });
73
+ for (const role of PICTURE_ROLES) {
74
+ const p = bytes[role], fileName = p ? pictureFileName(role, p.mime) : null;
75
+ names[role] = fileName;
76
+ if (p && fileName)
77
+ add(fileName, p.bytes);
78
+ }
79
+ for (const mode of modes)
80
+ add(`${mode}.svg`, Buffer.from(previewSvg(input, mode, names), "utf8"));
81
+ const total = files.reduce((n, f) => n + f.data.length, 0);
82
+ if (total <= PREVIEW_INLINE_BYTES)
83
+ return { files: files.map(f => ({ name: f.name, data_b64: f.data.toString("base64") })) };
84
+ const built = { id: randomBytes(8).toString("hex"), files, last: now };
85
+ this.previews.set(name, built); // one per instance: a new preview replaces it
86
+ return { preview: built.id, files: files.map(f => ({ name: f.name, bytes: f.data.length, sha256: f.sha256, parts: Math.ceil(f.data.length / ASSET_PART_BYTES) })) };
87
+ }
88
+ /** One part of the caller's own preview in progress (its id, a file it holds, an index in range, not gone stale). */
89
+ part(name, raw, now) {
90
+ const p = raw;
91
+ const b = this.previews.get(name);
92
+ if (!p || typeof p !== "object" || !b || p.preview !== b.id)
93
+ return { error: "preview", message: "No such preview: ask for it again." };
94
+ const f = b.files.find(x => x.name === p.name);
95
+ const count = f ? Math.ceil(f.data.length / ASSET_PART_BYTES) : 0;
96
+ if (!f || !Number.isSafeInteger(p.index) || p.index < 0 || p.index >= count)
97
+ return { error: "part", message: "No such part." };
98
+ b.last = now; // a preview being fetched stays while it is
99
+ const i = p.index;
100
+ return { data_b64: f.data.subarray(i * ASSET_PART_BYTES, (i + 1) * ASSET_PART_BYTES).toString("base64") };
101
+ }
102
+ /** An instance was removed. */
103
+ forget(name) { this.previews.delete(name); }
104
+ }
105
+ //# sourceMappingURL=web-appearance-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-appearance-tools.js","sourceRoot":"","sources":["../src/web-appearance-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * #1629 r4 §1: the agent's read-only view of how it appears on the dashboard — `get_web_appearance` (what the page
3
+ * draws now, resolved exactly as the status frame resolves it) and `preview_web_appearance` (an SVG per mode, built
4
+ * from the page's own layout numbers). Pure: the fleet passes in what it already resolved; nothing here reads a file,
5
+ * a path or another instance.
6
+ *
7
+ * The SVG names only this instance's pictures, as side files next to it (`avatar.png`, …: #1890 review — an SVG with a
8
+ * picture inside outgrew what an agent's Read takes); text is XML-escaped; colours are checked again; fonts are generic
9
+ * families. No browser, no live session, no other instance's content.
10
+ */
11
+ import { type IdentityColorSource, type IdentityColorTiers, type Placement, type PlacementSource, type PlacementTiers } from "./web-identity-color.js";
12
+ import { type Palette, type Theme, type ThemeMode } from "./web-theme.js";
13
+ import type { ResolvedCard } from "./web-card.js";
14
+ export type AvatarSource = "profile" | "theme" | "bot" | "emoji" | "initial";
15
+ /** The status frame's entry for one instance (getUiStatus): the fields the page draws a face and a ring from. */
16
+ export interface FrameEntry {
17
+ name: string;
18
+ display_name?: string;
19
+ avatar?: {
20
+ kind: string;
21
+ src?: string;
22
+ text?: string;
23
+ alt?: string;
24
+ };
25
+ accent?: string;
26
+ theme_avatar?: {
27
+ kind: string;
28
+ src?: string;
29
+ };
30
+ theme_accent?: string;
31
+ /** #1958: the identity colour's tiers and its placements' (getUiStatus). */
32
+ color?: IdentityColorTiers;
33
+ color_show?: PlacementTiers;
34
+ }
35
+ /**
36
+ * #1958: the identity colour drawn for this instance while agents' themes are shown, where it comes from, its value on
37
+ * a light and a dark page, where it shows (and whose choice that is), and whether the host turned colours off — what
38
+ * the agent needs to know: a person's choice wins over its own (theme.identity), field by field.
39
+ */
40
+ export declare function resolvedIdentityColor(e: FrameEntry, host?: {
41
+ show?: unknown;
42
+ off?: boolean;
43
+ }): {
44
+ color: string;
45
+ source: IdentityColorSource;
46
+ light: string;
47
+ dark: string;
48
+ show: Placement[];
49
+ show_source: PlacementSource;
50
+ off: boolean;
51
+ };
52
+ /** The face the page draws while agents' themes are shown (the default), and where it comes from. */
53
+ export declare function resolvedAvatar(e: FrameEntry): {
54
+ source: AvatarSource;
55
+ url?: string;
56
+ text?: string;
57
+ };
58
+ /** The ring the page draws round a picture face: the theme's colour, else an own bot's; none for an emoji or initial. */
59
+ export declare function resolvedRing(e: FrameEntry): {
60
+ color: string;
61
+ source: "theme" | "bot";
62
+ } | null;
63
+ /** The effective look per mode: the palette over the token defaults, and the fixed choices. */
64
+ export declare function effectiveTheme(theme: Theme): Record<ThemeMode, Required<Palette> & {
65
+ font: string;
66
+ bubbleRadius: number;
67
+ bubbleBorder: string;
68
+ }>;
69
+ export interface AppearanceInput {
70
+ entry: FrameEntry;
71
+ theme: Theme;
72
+ card: ResolvedCard;
73
+ locked: boolean;
74
+ agentEdit: boolean;
75
+ limits: Record<string, unknown>;
76
+ /** #1958: the host's identity-colour settings (color.show / color.off). */
77
+ colorPrefs?: {
78
+ show?: unknown;
79
+ off?: boolean;
80
+ };
81
+ /** r4-4: its placed widgets, by slot. */
82
+ widgets?: Partial<Record<"chat_header" | "details", {
83
+ spec: unknown;
84
+ data: unknown;
85
+ by: string;
86
+ }>>;
87
+ }
88
+ /** get_web_appearance's answer. Effects: none can be applied yet (they wait on the effects runtime). */
89
+ export declare function appearanceView(i: AppearanceInput): Record<string, unknown>;
90
+ export declare const PICTURE_ROLES: readonly ["avatar", "portrait", "banner", "background"];
91
+ export type PictureRole = typeof PICTURE_ROLES[number];
92
+ /** This instance's pictures by role, as the side files' names (null: none). */
93
+ export type PreviewPictures = Record<PictureRole, string | null>;
94
+ /** A side file's name for a picture of this role and type, or null for a type no page shows. */
95
+ export declare function pictureFileName(role: PictureRole, mime: string): string | null;
96
+ /** Every name a preview writes: an SVG per mode, and the pictures it names. */
97
+ export declare const PREVIEW_FILE: RegExp;
98
+ /** One mode's SVG. Every picture is one of `pics` (this instance's), inline; nothing else is referenced. */
99
+ export declare function previewSvg(i: AppearanceInput, mode: ThemeMode, pics: PreviewPictures): string;