aicodeman 1.44.0 → 1.45.0

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 (211) hide show
  1. package/README.md +6 -2
  2. package/dist/case-rename.d.ts +65 -0
  3. package/dist/case-rename.d.ts.map +1 -0
  4. package/dist/case-rename.js +198 -0
  5. package/dist/case-rename.js.map +1 -0
  6. package/dist/cli-agent.d.ts +60 -1
  7. package/dist/cli-agent.d.ts.map +1 -1
  8. package/dist/cli-agent.js +225 -13
  9. package/dist/cli-agent.js.map +1 -1
  10. package/dist/config/auth-config.d.ts +16 -1
  11. package/dist/config/auth-config.d.ts.map +1 -1
  12. package/dist/config/auth-config.js +16 -1
  13. package/dist/config/auth-config.js.map +1 -1
  14. package/dist/host-sensors.d.ts +115 -0
  15. package/dist/host-sensors.d.ts.map +1 -0
  16. package/dist/host-sensors.js +457 -0
  17. package/dist/host-sensors.js.map +1 -0
  18. package/dist/remote-hosts.d.ts +7 -0
  19. package/dist/remote-hosts.d.ts.map +1 -1
  20. package/dist/remote-hosts.js +17 -3
  21. package/dist/remote-hosts.js.map +1 -1
  22. package/dist/remote-pane-history.d.ts.map +1 -1
  23. package/dist/remote-pane-history.js +2 -2
  24. package/dist/remote-pane-history.js.map +1 -1
  25. package/dist/session.d.ts.map +1 -1
  26. package/dist/session.js +13 -0
  27. package/dist/session.js.map +1 -1
  28. package/dist/tmux-manager.d.ts +7 -3
  29. package/dist/tmux-manager.d.ts.map +1 -1
  30. package/dist/tmux-manager.js +47 -15
  31. package/dist/tmux-manager.js.map +1 -1
  32. package/dist/tmux-prefix-table.d.ts +37 -5
  33. package/dist/tmux-prefix-table.d.ts.map +1 -1
  34. package/dist/tmux-prefix-table.js +40 -5
  35. package/dist/tmux-prefix-table.js.map +1 -1
  36. package/dist/tui/tui-app.d.ts +8 -1
  37. package/dist/tui/tui-app.d.ts.map +1 -1
  38. package/dist/tui/tui-app.js +49 -12
  39. package/dist/tui/tui-app.js.map +1 -1
  40. package/dist/tui/tui-client.d.ts +90 -3
  41. package/dist/tui/tui-client.d.ts.map +1 -1
  42. package/dist/tui/tui-client.js +195 -26
  43. package/dist/tui/tui-client.js.map +1 -1
  44. package/dist/web/agent-inbox-persistence.d.ts +52 -0
  45. package/dist/web/agent-inbox-persistence.d.ts.map +1 -0
  46. package/dist/web/agent-inbox-persistence.js +139 -0
  47. package/dist/web/agent-inbox-persistence.js.map +1 -0
  48. package/dist/web/agent-inbox.d.ts +140 -0
  49. package/dist/web/agent-inbox.d.ts.map +1 -0
  50. package/dist/web/agent-inbox.js +258 -0
  51. package/dist/web/agent-inbox.js.map +1 -0
  52. package/dist/web/middleware/auth.d.ts +2 -0
  53. package/dist/web/middleware/auth.d.ts.map +1 -1
  54. package/dist/web/middleware/auth.js +145 -52
  55. package/dist/web/middleware/auth.js.map +1 -1
  56. package/dist/web/public/admin-ui.js.gz +0 -0
  57. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  58. package/dist/web/public/app.42d0abae.js +43 -0
  59. package/dist/web/public/app.42d0abae.js.br +0 -0
  60. package/dist/web/public/app.42d0abae.js.gz +0 -0
  61. package/dist/web/public/approvals-ui.js.gz +0 -0
  62. package/dist/web/public/{constants.4866ecc3.js → constants.0b3cdde9.js} +11 -0
  63. package/dist/web/public/constants.0b3cdde9.js.br +0 -0
  64. package/dist/web/public/constants.0b3cdde9.js.gz +0 -0
  65. package/dist/web/public/cron-ui.js.gz +0 -0
  66. package/dist/web/public/entrance-animations.js.gz +0 -0
  67. package/dist/web/public/git-status-ui.js.gz +0 -0
  68. package/dist/web/public/header-divider.eb2d2463.js +1 -0
  69. package/dist/web/public/header-divider.eb2d2463.js.br +0 -0
  70. package/dist/web/public/header-divider.eb2d2463.js.gz +0 -0
  71. package/dist/web/public/home-sessions.js.gz +0 -0
  72. package/dist/web/public/host-wake-ui.js.gz +0 -0
  73. package/dist/web/public/i18n.1e68220d.js +2 -0
  74. package/dist/web/public/i18n.1e68220d.js.br +0 -0
  75. package/dist/web/public/i18n.1e68220d.js.gz +0 -0
  76. package/dist/web/public/image-input.941386e7.js.gz +0 -0
  77. package/dist/web/public/index.html +74 -11
  78. package/dist/web/public/index.html.br +0 -0
  79. package/dist/web/public/index.html.gz +0 -0
  80. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  81. package/dist/web/public/keyboard-accessory.4e8b20ac.js.gz +0 -0
  82. package/dist/web/public/{mobile-handlers.44adaa07.js → mobile-handlers.1c0cbcd8.js} +4 -2
  83. package/dist/web/public/mobile-handlers.1c0cbcd8.js.br +0 -0
  84. package/dist/web/public/mobile-handlers.1c0cbcd8.js.gz +0 -0
  85. package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
  86. package/dist/web/public/mobile-overview.js.gz +0 -0
  87. package/dist/web/public/mobile.12fb18de.css.gz +0 -0
  88. package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
  89. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  90. package/dist/web/public/{panels-ui.cbb24ba1.js → panels-ui.78c3defb.js} +76 -70
  91. package/dist/web/public/panels-ui.78c3defb.js.br +0 -0
  92. package/dist/web/public/panels-ui.78c3defb.js.gz +0 -0
  93. package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
  94. package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
  95. package/dist/web/public/readmymind-ui.js.gz +0 -0
  96. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  97. package/dist/web/public/respawn-ui.d7adcf6a.js.gz +0 -0
  98. package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
  99. package/dist/web/public/session-lineage.js.gz +0 -0
  100. package/dist/web/public/{session-ui.9c1f009b.js → session-ui.ea6b1994.js} +28 -26
  101. package/dist/web/public/session-ui.ea6b1994.js.br +0 -0
  102. package/dist/web/public/session-ui.ea6b1994.js.gz +0 -0
  103. package/dist/web/public/{settings-ui.fea9ddae.js → settings-ui.085dd202.js} +3 -3
  104. package/dist/web/public/settings-ui.085dd202.js.br +0 -0
  105. package/dist/web/public/settings-ui.085dd202.js.gz +0 -0
  106. package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
  107. package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
  108. package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
  109. package/dist/web/public/styles.3b59b41d.css +1 -0
  110. package/dist/web/public/styles.3b59b41d.css.br +0 -0
  111. package/dist/web/public/styles.3b59b41d.css.gz +0 -0
  112. package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
  113. package/dist/web/public/sw.js +2 -2
  114. package/dist/web/public/sw.js.br +0 -0
  115. package/dist/web/public/sw.js.gz +0 -0
  116. package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
  117. package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
  118. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
  119. package/dist/web/public/terminal-split.js.gz +0 -0
  120. package/dist/web/public/terminal-tile.js +8 -1
  121. package/dist/web/public/terminal-tile.js.br +0 -0
  122. package/dist/web/public/terminal-tile.js.gz +0 -0
  123. package/dist/web/public/terminal-ui.fb2a27f5.js +2 -0
  124. package/dist/web/public/terminal-ui.fb2a27f5.js.br +0 -0
  125. package/dist/web/public/terminal-ui.fb2a27f5.js.gz +0 -0
  126. package/dist/web/public/tile-grid.js.gz +0 -0
  127. package/dist/web/public/ultracode-panel.js.gz +0 -0
  128. package/dist/web/public/ultracode-windows.js.gz +0 -0
  129. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  130. package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
  131. package/dist/web/public/vendor/fflate.min.js.gz +0 -0
  132. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  133. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  134. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  135. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  136. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  137. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
  138. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
  139. package/dist/web/public/vendor/xterm.css.gz +0 -0
  140. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  141. package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
  142. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  143. package/dist/web/public/webview-tabs.js.gz +0 -0
  144. package/dist/web/public/whats-new.json +9 -9
  145. package/dist/web/route-helpers.d.ts +57 -6
  146. package/dist/web/route-helpers.d.ts.map +1 -1
  147. package/dist/web/route-helpers.js +72 -4
  148. package/dist/web/route-helpers.js.map +1 -1
  149. package/dist/web/routes/case-routes.d.ts.map +1 -1
  150. package/dist/web/routes/case-routes.js +183 -2
  151. package/dist/web/routes/case-routes.js.map +1 -1
  152. package/dist/web/routes/inbox-routes.d.ts +43 -0
  153. package/dist/web/routes/inbox-routes.d.ts.map +1 -0
  154. package/dist/web/routes/inbox-routes.js +148 -0
  155. package/dist/web/routes/inbox-routes.js.map +1 -0
  156. package/dist/web/routes/index.d.ts +1 -0
  157. package/dist/web/routes/index.d.ts.map +1 -1
  158. package/dist/web/routes/index.js +1 -0
  159. package/dist/web/routes/index.js.map +1 -1
  160. package/dist/web/routes/session-routes.d.ts.map +1 -1
  161. package/dist/web/routes/session-routes.js +25 -47
  162. package/dist/web/routes/session-routes.js.map +1 -1
  163. package/dist/web/routes/system-routes.d.ts.map +1 -1
  164. package/dist/web/routes/system-routes.js +23 -2
  165. package/dist/web/routes/system-routes.js.map +1 -1
  166. package/dist/web/schemas.d.ts +26 -0
  167. package/dist/web/schemas.d.ts.map +1 -1
  168. package/dist/web/schemas.js +48 -0
  169. package/dist/web/schemas.js.map +1 -1
  170. package/dist/web/sensitive-path.d.ts.map +1 -1
  171. package/dist/web/sensitive-path.js +2 -0
  172. package/dist/web/sensitive-path.js.map +1 -1
  173. package/dist/web/server.d.ts +2 -0
  174. package/dist/web/server.d.ts.map +1 -1
  175. package/dist/web/server.js +61 -3
  176. package/dist/web/server.js.map +1 -1
  177. package/dist/web/session-wait-registry.d.ts +48 -4
  178. package/dist/web/session-wait-registry.d.ts.map +1 -1
  179. package/dist/web/session-wait-registry.js +144 -6
  180. package/dist/web/session-wait-registry.js.map +1 -1
  181. package/dist/web/sse-events.d.ts +4 -1
  182. package/dist/web/sse-events.d.ts.map +1 -1
  183. package/dist/web/sse-events.js +4 -1
  184. package/dist/web/sse-events.js.map +1 -1
  185. package/package.json +1 -1
  186. package/skills/codeman/SKILL.md +7 -4
  187. package/skills/codeman/preamble.sh +3 -3
  188. package/skills/codeman/reference/endpoints.md +24 -3
  189. package/skills/codeman/reference/verbs.md +47 -0
  190. package/dist/web/public/app.9314ee60.js +0 -43
  191. package/dist/web/public/app.9314ee60.js.br +0 -0
  192. package/dist/web/public/app.9314ee60.js.gz +0 -0
  193. package/dist/web/public/constants.4866ecc3.js.br +0 -0
  194. package/dist/web/public/constants.4866ecc3.js.gz +0 -0
  195. package/dist/web/public/i18n.22ca902b.js +0 -2
  196. package/dist/web/public/i18n.22ca902b.js.br +0 -0
  197. package/dist/web/public/i18n.22ca902b.js.gz +0 -0
  198. package/dist/web/public/mobile-handlers.44adaa07.js.br +0 -0
  199. package/dist/web/public/mobile-handlers.44adaa07.js.gz +0 -0
  200. package/dist/web/public/panels-ui.cbb24ba1.js.br +0 -0
  201. package/dist/web/public/panels-ui.cbb24ba1.js.gz +0 -0
  202. package/dist/web/public/session-ui.9c1f009b.js.br +0 -0
  203. package/dist/web/public/session-ui.9c1f009b.js.gz +0 -0
  204. package/dist/web/public/settings-ui.fea9ddae.js.br +0 -0
  205. package/dist/web/public/settings-ui.fea9ddae.js.gz +0 -0
  206. package/dist/web/public/styles.e096fcd6.css +0 -1
  207. package/dist/web/public/styles.e096fcd6.css.br +0 -0
  208. package/dist/web/public/styles.e096fcd6.css.gz +0 -0
  209. package/dist/web/public/terminal-ui.68dbd352.js +0 -2
  210. package/dist/web/public/terminal-ui.68dbd352.js.br +0 -0
  211. package/dist/web/public/terminal-ui.68dbd352.js.gz +0 -0
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @fileoverview Opt-in snapshot of the agent inbox, so pending mail between agents
3
+ * survives a server restart (the agents themselves keep running in tmux across it).
4
+ *
5
+ * Off by default: an install that never turns on `agentInboxPersistEnabled` writes
6
+ * nothing new to disk. The rules (discussion #558):
7
+ * - Off means no file. Switching it off deletes the snapshot; the in-memory inboxes
8
+ * stay as they are.
9
+ * - Its own file under `dataPath()`, written 0600 through tmp + rename like
10
+ * `intents.json`, since messages carry the same kind of text prompts do. It is never
11
+ * read by `/api/search` and `sensitive-path.ts` keeps the file viewer off it.
12
+ * - Writes are debounced (a burst of posts is one write) and flushed on shutdown.
13
+ * - Boot pruning is NOT done here: the server calls `agentInbox.pruneExcept()` only
14
+ * after the session restore succeeded, so a failed restore leaves the mail alone.
15
+ *
16
+ * @module web/agent-inbox-persistence
17
+ */
18
+ import { type AgentInbox } from './agent-inbox.js';
19
+ export declare class AgentInboxPersistence {
20
+ private readonly inbox;
21
+ /** Resolved per call: test HOMEs change between files. */
22
+ private readonly filePath;
23
+ private readonly debounceMs;
24
+ private enabled;
25
+ private timer;
26
+ constructor(inbox?: AgentInbox,
27
+ /** Resolved per call: test HOMEs change between files. */
28
+ filePath?: () => string, debounceMs?: number);
29
+ get isEnabled(): boolean;
30
+ /**
31
+ * Boot: apply the setting and, when it is on, load the snapshot into the store.
32
+ * Returns how many messages were restored. A missing or unreadable file is an
33
+ * empty inbox, never a failed boot.
34
+ */
35
+ load(enabled: boolean): number;
36
+ /**
37
+ * The setting changed. On: write the current inboxes now. Off: drop any pending
38
+ * write and delete the snapshot; memory is untouched.
39
+ */
40
+ setEnabled(enabled: boolean): void;
41
+ /** Shutdown: write a pending change now instead of losing it with the timer. */
42
+ flush(): void;
43
+ /** Test hook: drop a pending write and detach from the store. */
44
+ dispose(): void;
45
+ private schedule;
46
+ private cancel;
47
+ private writeNow;
48
+ private removeFile;
49
+ }
50
+ /** Process-wide instance wired to `agentInbox`; the server and the settings route use it. */
51
+ export declare const agentInboxPersistence: AgentInboxPersistence;
52
+ //# sourceMappingURL=agent-inbox-persistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-inbox-persistence.d.ts","sourceRoot":"","sources":["../../src/web/agent-inbox-persistence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAK/D,qBAAa,qBAAqB;IAK9B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,0DAA0D;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAP7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAA+B;gBAGzB,KAAK,GAAE,UAAuB;IAC/C,0DAA0D;IACzC,QAAQ,GAAE,MAAM,MAA2C,EAC3D,UAAU,GAAE,MAA0B;IAKzD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM;IAiB9B;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAWlC,gFAAgF;IAChF,KAAK,IAAI,IAAI;IAMb,iEAAiE;IACjE,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,QAAQ;IAiBhB,OAAO,CAAC,UAAU;CASnB;AAED,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,uBAA8B,CAAC"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @fileoverview Opt-in snapshot of the agent inbox, so pending mail between agents
3
+ * survives a server restart (the agents themselves keep running in tmux across it).
4
+ *
5
+ * Off by default: an install that never turns on `agentInboxPersistEnabled` writes
6
+ * nothing new to disk. The rules (discussion #558):
7
+ * - Off means no file. Switching it off deletes the snapshot; the in-memory inboxes
8
+ * stay as they are.
9
+ * - Its own file under `dataPath()`, written 0600 through tmp + rename like
10
+ * `intents.json`, since messages carry the same kind of text prompts do. It is never
11
+ * read by `/api/search` and `sensitive-path.ts` keeps the file viewer off it.
12
+ * - Writes are debounced (a burst of posts is one write) and flushed on shutdown.
13
+ * - Boot pruning is NOT done here: the server calls `agentInbox.pruneExcept()` only
14
+ * after the session restore succeeded, so a failed restore leaves the mail alone.
15
+ *
16
+ * @module web/agent-inbox-persistence
17
+ */
18
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
19
+ import { dirname } from 'node:path';
20
+ import { dataPath } from '../config/instance.js';
21
+ import { agentInbox } from './agent-inbox.js';
22
+ /** A burst of posts (a coordinator fanning out to five workers) becomes one write. */
23
+ const WRITE_DEBOUNCE_MS = 250;
24
+ export class AgentInboxPersistence {
25
+ inbox;
26
+ filePath;
27
+ debounceMs;
28
+ enabled = false;
29
+ timer = null;
30
+ constructor(inbox = agentInbox,
31
+ /** Resolved per call: test HOMEs change between files. */
32
+ filePath = () => dataPath('agent-inbox.json'), debounceMs = WRITE_DEBOUNCE_MS) {
33
+ this.inbox = inbox;
34
+ this.filePath = filePath;
35
+ this.debounceMs = debounceMs;
36
+ this.inbox.onChange = () => this.schedule();
37
+ }
38
+ get isEnabled() {
39
+ return this.enabled;
40
+ }
41
+ /**
42
+ * Boot: apply the setting and, when it is on, load the snapshot into the store.
43
+ * Returns how many messages were restored. A missing or unreadable file is an
44
+ * empty inbox, never a failed boot.
45
+ */
46
+ load(enabled) {
47
+ this.enabled = enabled;
48
+ if (!enabled) {
49
+ // A file left from an earlier "on" is removed, so off really means no file.
50
+ this.removeFile();
51
+ return 0;
52
+ }
53
+ const path = this.filePath();
54
+ if (!existsSync(path))
55
+ return 0;
56
+ try {
57
+ return this.inbox.restore(JSON.parse(readFileSync(path, 'utf-8')));
58
+ }
59
+ catch (err) {
60
+ console.warn(`[AgentInbox] Failed to load ${path}, starting empty:`, err);
61
+ return 0;
62
+ }
63
+ }
64
+ /**
65
+ * The setting changed. On: write the current inboxes now. Off: drop any pending
66
+ * write and delete the snapshot; memory is untouched.
67
+ */
68
+ setEnabled(enabled) {
69
+ if (enabled === this.enabled)
70
+ return;
71
+ this.enabled = enabled;
72
+ if (enabled) {
73
+ this.writeNow();
74
+ }
75
+ else {
76
+ this.cancel();
77
+ this.removeFile();
78
+ }
79
+ }
80
+ /** Shutdown: write a pending change now instead of losing it with the timer. */
81
+ flush() {
82
+ if (!this.timer)
83
+ return;
84
+ this.cancel();
85
+ this.writeNow();
86
+ }
87
+ /** Test hook: drop a pending write and detach from the store. */
88
+ dispose() {
89
+ this.cancel();
90
+ if (this.inbox.onChange)
91
+ this.inbox.onChange = undefined;
92
+ }
93
+ schedule() {
94
+ if (!this.enabled || this.timer)
95
+ return;
96
+ this.timer = setTimeout(() => {
97
+ this.timer = null;
98
+ this.writeNow();
99
+ }, this.debounceMs);
100
+ // A pending write must not hold the process open; shutdown flushes it explicitly.
101
+ this.timer.unref();
102
+ }
103
+ cancel() {
104
+ if (this.timer)
105
+ clearTimeout(this.timer);
106
+ this.timer = null;
107
+ }
108
+ writeNow() {
109
+ if (!this.enabled)
110
+ return;
111
+ const path = this.filePath();
112
+ const tmpPath = `${path}.tmp`;
113
+ try {
114
+ mkdirSync(dirname(path), { recursive: true });
115
+ // 0600: agent messages can carry anything a prompt can, secrets included.
116
+ // `mode` applies only when the file is created, and the rename makes every
117
+ // write a fresh file, so the final file is always 0600.
118
+ rmSync(tmpPath, { force: true });
119
+ writeFileSync(tmpPath, JSON.stringify(this.inbox.snapshot()), { mode: 0o600 });
120
+ renameSync(tmpPath, path);
121
+ }
122
+ catch (err) {
123
+ console.warn(`[AgentInbox] Failed to persist ${path}:`, err);
124
+ }
125
+ }
126
+ removeFile() {
127
+ const path = this.filePath();
128
+ try {
129
+ rmSync(path, { force: true });
130
+ rmSync(`${path}.tmp`, { force: true });
131
+ }
132
+ catch (err) {
133
+ console.warn(`[AgentInbox] Failed to remove ${path}:`, err);
134
+ }
135
+ }
136
+ }
137
+ /** Process-wide instance wired to `agentInbox`; the server and the settings route use it. */
138
+ export const agentInboxPersistence = new AgentInboxPersistence();
139
+ //# sourceMappingURL=agent-inbox-persistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-inbox-persistence.js","sourceRoot":"","sources":["../../src/web/agent-inbox-persistence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @fileoverview Agent inbox: a per-session mailbox other agents (or the user) post to,
3
+ * and the receiving agent reads when it chooses to.
4
+ *
5
+ * The other agent-to-agent channel, `POST /api/sessions/:id/input`, types into the
6
+ * receiver's composer. That is right for a prompt and wrong for a message: every
7
+ * delivery starts or interrupts a turn, the receiver cannot defer it, and it is one
8
+ * line only. The inbox stores instead of typing. Nothing here ever reaches a pane.
9
+ *
10
+ * Invariants (each pinned in `test/agent-inbox.test.ts`):
11
+ * - **Read until ack.** A read hands out every pending message and removes nothing.
12
+ * A message goes only when the receiver acknowledges its id. "Read and dropped"
13
+ * and "done" must not look the same to the sender (discussion #558, the 09-23 case).
14
+ * - **Seen is the receiver's alone.** `read(…, { receiver: true })` marks what it
15
+ * handed out as seen; any other read is a peek and changes nothing. The route
16
+ * decides who the receiver is; this module only takes the flag.
17
+ * - **Bounded, and full refuses.** `MAX_MESSAGES_PER_INBOX`, `MAX_MESSAGE_BYTES` and
18
+ * `MAX_INBOX_BYTES`. A post that would cross any of them is refused so the sender
19
+ * learns. The oldest message is never dropped to make room.
20
+ * - **TTL from the message's own timestamp** (`MESSAGE_TTL_MS`). A restore keeps
21
+ * `createdAt`, so a restart does not restart the clock. Expiry is lazy: every
22
+ * accessor sweeps the inbox it touches, `summary()` and `snapshot()` sweep all.
23
+ * - **No IO, no `Session` import.** Persistence (`agent-inbox-persistence.ts`) hooks
24
+ * `onChange`; long-poll waiters live in `session-wait-registry.ts`.
25
+ *
26
+ * @consumedby web/routes/inbox-routes, web/agent-inbox-persistence, web/server
27
+ * @module web/agent-inbox
28
+ */
29
+ /** Messages one inbox holds before posts are refused. */
30
+ export declare const MAX_MESSAGES_PER_INBOX = 200;
31
+ /** UTF-8 bytes per message. A bigger payload belongs in the workspace; post its path. */
32
+ export declare const MAX_MESSAGE_BYTES = 16384;
33
+ /** UTF-8 bytes of message text one inbox holds before posts are refused. */
34
+ export declare const MAX_INBOX_BYTES: number;
35
+ /** A message older than this is gone, read or not (same span as the approvals inbox). */
36
+ export declare const MESSAGE_TTL_MS: number;
37
+ /** A message as it is stored (and persisted). */
38
+ interface StoredMessage {
39
+ id: string;
40
+ /** The posting session's full id, or null when no session could be resolved (the user). */
41
+ from: string | null;
42
+ text: string;
43
+ /** Epoch ms. The TTL counts from here. */
44
+ createdAt: number;
45
+ /** Epoch ms of the first receiver read that handed this message out; null while unseen. */
46
+ seenAt: number | null;
47
+ }
48
+ /** A message as a read hands it out. */
49
+ export interface InboxMessage {
50
+ id: string;
51
+ from: string | null;
52
+ text: string;
53
+ createdAt: number;
54
+ /** True when a receiver read had handed this message out BEFORE this read. */
55
+ seen: boolean;
56
+ }
57
+ export interface InboxReadResult {
58
+ /** Every pending message, oldest first: unseen and seen-but-unacked alike. */
59
+ messages: InboxMessage[];
60
+ pending: number;
61
+ /** Unseen after this read (0 after a receiver read; unchanged by a peek). */
62
+ unseen: number;
63
+ }
64
+ export type PostResult = {
65
+ ok: true;
66
+ message: InboxMessage;
67
+ pending: number;
68
+ unseen: number;
69
+ } | {
70
+ ok: false;
71
+ reason: 'empty' | 'message-too-large' | 'inbox-full-count' | 'inbox-full-bytes';
72
+ };
73
+ /** What `summary()` reports for one session. Counts and times only, never text. */
74
+ export interface InboxCounts {
75
+ pending: number;
76
+ unseen: number;
77
+ /** `createdAt` of the oldest unseen message, null when nothing is unseen. */
78
+ oldestUnseenAt: number | null;
79
+ }
80
+ /** On-disk shape. `version` lets a later format refuse an older file instead of misreading it. */
81
+ export interface InboxSnapshot {
82
+ version: 1;
83
+ inboxes: Record<string, StoredMessage[]>;
84
+ }
85
+ export declare class AgentInbox {
86
+ private readonly now;
87
+ private readonly inboxes;
88
+ /** Any mutation (post, seen, ack, expiry, drop, prune, restore excluded). Persistence hooks this. */
89
+ onChange: (() => void) | undefined;
90
+ constructor(now?: () => number);
91
+ /**
92
+ * Store a message for `sessionId`. `from` is the RESOLVED sender (the route's job),
93
+ * never a client-supplied label.
94
+ */
95
+ post(sessionId: string, from: string | null, text: string): PostResult;
96
+ /**
97
+ * Every pending message, oldest first. Removes nothing. With `receiver`, the
98
+ * messages handed out are marked seen (each keeps its first `seenAt`); without it
99
+ * this is a peek and changes nothing at all.
100
+ */
101
+ read(sessionId: string, options: {
102
+ receiver: boolean;
103
+ }): InboxReadResult;
104
+ /** Messages no receiver read has handed out yet. A `?wait=` read parks while this is 0. */
105
+ unseenCount(sessionId: string): number;
106
+ pendingCount(sessionId: string): number;
107
+ /**
108
+ * Remove the given messages. Idempotent: an id that is unknown or already gone is
109
+ * not an error, so a retry after a lost response succeeds. Returns how many went.
110
+ */
111
+ ack(sessionId: string, ids: readonly string[]): number;
112
+ /** Per-session counts for every inbox that holds anything. Never blocks. */
113
+ summary(): Map<string, InboxCounts>;
114
+ /** The session was DELETED: its mail goes with it. Not called on detach or shutdown. */
115
+ drop(sessionId: string): void;
116
+ /**
117
+ * Boot-time sweep, run only after the session restore succeeded: discard inboxes
118
+ * whose session did not come back. Every route needs a live session, so an orphan
119
+ * could otherwise never be removed. Returns how many inboxes were dropped.
120
+ */
121
+ pruneExcept(keep: Iterable<string>): number;
122
+ /** Whole-store snapshot for persistence, expired messages already swept. */
123
+ snapshot(): InboxSnapshot;
124
+ /**
125
+ * Replace the store from a snapshot (server boot). Malformed input is skipped entry
126
+ * by entry rather than thrown: a corrupt file must not stop the server. Expired
127
+ * messages are not restored, and the caps hold for what is. Does not fire
128
+ * `onChange`: writing back what was just read would be a no-op at best.
129
+ */
130
+ restore(snapshot: unknown): number;
131
+ /** Test hook: back to an empty store with no listener. */
132
+ resetForTests(): void;
133
+ /** The inbox with expired messages swept out (fires `onChange` when it swept any). */
134
+ private live;
135
+ private store;
136
+ }
137
+ /** Process-wide store used by the routes and the server wiring. Tests construct their own. */
138
+ export declare const agentInbox: AgentInbox;
139
+ export {};
140
+ //# sourceMappingURL=agent-inbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-inbox.d.ts","sourceRoot":"","sources":["../../src/web/agent-inbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,258 @@
1
+ /**
2
+ * @fileoverview Agent inbox: a per-session mailbox other agents (or the user) post to,
3
+ * and the receiving agent reads when it chooses to.
4
+ *
5
+ * The other agent-to-agent channel, `POST /api/sessions/:id/input`, types into the
6
+ * receiver's composer. That is right for a prompt and wrong for a message: every
7
+ * delivery starts or interrupts a turn, the receiver cannot defer it, and it is one
8
+ * line only. The inbox stores instead of typing. Nothing here ever reaches a pane.
9
+ *
10
+ * Invariants (each pinned in `test/agent-inbox.test.ts`):
11
+ * - **Read until ack.** A read hands out every pending message and removes nothing.
12
+ * A message goes only when the receiver acknowledges its id. "Read and dropped"
13
+ * and "done" must not look the same to the sender (discussion #558, the 09-23 case).
14
+ * - **Seen is the receiver's alone.** `read(…, { receiver: true })` marks what it
15
+ * handed out as seen; any other read is a peek and changes nothing. The route
16
+ * decides who the receiver is; this module only takes the flag.
17
+ * - **Bounded, and full refuses.** `MAX_MESSAGES_PER_INBOX`, `MAX_MESSAGE_BYTES` and
18
+ * `MAX_INBOX_BYTES`. A post that would cross any of them is refused so the sender
19
+ * learns. The oldest message is never dropped to make room.
20
+ * - **TTL from the message's own timestamp** (`MESSAGE_TTL_MS`). A restore keeps
21
+ * `createdAt`, so a restart does not restart the clock. Expiry is lazy: every
22
+ * accessor sweeps the inbox it touches, `summary()` and `snapshot()` sweep all.
23
+ * - **No IO, no `Session` import.** Persistence (`agent-inbox-persistence.ts`) hooks
24
+ * `onChange`; long-poll waiters live in `session-wait-registry.ts`.
25
+ *
26
+ * @consumedby web/routes/inbox-routes, web/agent-inbox-persistence, web/server
27
+ * @module web/agent-inbox
28
+ */
29
+ import { randomUUID } from 'node:crypto';
30
+ // ─── Bounds ──────────────────────────────────────────────────────────────────
31
+ /** Messages one inbox holds before posts are refused. */
32
+ export const MAX_MESSAGES_PER_INBOX = 200;
33
+ /** UTF-8 bytes per message. A bigger payload belongs in the workspace; post its path. */
34
+ export const MAX_MESSAGE_BYTES = 16_384;
35
+ /** UTF-8 bytes of message text one inbox holds before posts are refused. */
36
+ export const MAX_INBOX_BYTES = 512 * 1024;
37
+ /** A message older than this is gone, read or not (same span as the approvals inbox). */
38
+ export const MESSAGE_TTL_MS = 12 * 60 * 60 * 1000;
39
+ // ─── Store ───────────────────────────────────────────────────────────────────
40
+ export class AgentInbox {
41
+ now;
42
+ inboxes = new Map();
43
+ /** Any mutation (post, seen, ack, expiry, drop, prune, restore excluded). Persistence hooks this. */
44
+ onChange;
45
+ constructor(now = Date.now) {
46
+ this.now = now;
47
+ }
48
+ /**
49
+ * Store a message for `sessionId`. `from` is the RESOLVED sender (the route's job),
50
+ * never a client-supplied label.
51
+ */
52
+ post(sessionId, from, text) {
53
+ if (text.length === 0)
54
+ return { ok: false, reason: 'empty' };
55
+ const bytes = Buffer.byteLength(text, 'utf8');
56
+ if (bytes > MAX_MESSAGE_BYTES)
57
+ return { ok: false, reason: 'message-too-large' };
58
+ const queue = this.live(sessionId);
59
+ if (queue.length >= MAX_MESSAGES_PER_INBOX)
60
+ return { ok: false, reason: 'inbox-full-count' };
61
+ if (inboxBytes(queue) + bytes > MAX_INBOX_BYTES)
62
+ return { ok: false, reason: 'inbox-full-bytes' };
63
+ const stored = { id: randomUUID(), from, text, createdAt: this.now(), seenAt: null };
64
+ queue.push(stored);
65
+ this.inboxes.set(sessionId, queue);
66
+ this.onChange?.();
67
+ return { ok: true, message: view(stored), pending: queue.length, unseen: countUnseen(queue) };
68
+ }
69
+ /**
70
+ * Every pending message, oldest first. Removes nothing. With `receiver`, the
71
+ * messages handed out are marked seen (each keeps its first `seenAt`); without it
72
+ * this is a peek and changes nothing at all.
73
+ */
74
+ read(sessionId, options) {
75
+ const queue = this.live(sessionId);
76
+ const messages = queue.map(view);
77
+ if (options.receiver) {
78
+ let marked = false;
79
+ const at = this.now();
80
+ for (const m of queue) {
81
+ if (m.seenAt !== null)
82
+ continue;
83
+ m.seenAt = at;
84
+ marked = true;
85
+ }
86
+ if (marked)
87
+ this.onChange?.();
88
+ }
89
+ return { messages, pending: queue.length, unseen: countUnseen(queue) };
90
+ }
91
+ /** Messages no receiver read has handed out yet. A `?wait=` read parks while this is 0. */
92
+ unseenCount(sessionId) {
93
+ return countUnseen(this.live(sessionId));
94
+ }
95
+ pendingCount(sessionId) {
96
+ return this.live(sessionId).length;
97
+ }
98
+ /**
99
+ * Remove the given messages. Idempotent: an id that is unknown or already gone is
100
+ * not an error, so a retry after a lost response succeeds. Returns how many went.
101
+ */
102
+ ack(sessionId, ids) {
103
+ const queue = this.live(sessionId);
104
+ if (queue.length === 0 || ids.length === 0)
105
+ return 0;
106
+ const drop = new Set(ids);
107
+ const kept = queue.filter((m) => !drop.has(m.id));
108
+ const removed = queue.length - kept.length;
109
+ if (removed === 0)
110
+ return 0;
111
+ this.store(sessionId, kept);
112
+ this.onChange?.();
113
+ return removed;
114
+ }
115
+ /** Per-session counts for every inbox that holds anything. Never blocks. */
116
+ summary() {
117
+ const out = new Map();
118
+ for (const sessionId of [...this.inboxes.keys()]) {
119
+ const queue = this.live(sessionId);
120
+ if (queue.length === 0)
121
+ continue;
122
+ const unseen = queue.filter((m) => m.seenAt === null);
123
+ out.set(sessionId, {
124
+ pending: queue.length,
125
+ unseen: unseen.length,
126
+ oldestUnseenAt: unseen.length > 0 ? unseen[0].createdAt : null,
127
+ });
128
+ }
129
+ return out;
130
+ }
131
+ /** The session was DELETED: its mail goes with it. Not called on detach or shutdown. */
132
+ drop(sessionId) {
133
+ if (this.inboxes.delete(sessionId))
134
+ this.onChange?.();
135
+ }
136
+ /**
137
+ * Boot-time sweep, run only after the session restore succeeded: discard inboxes
138
+ * whose session did not come back. Every route needs a live session, so an orphan
139
+ * could otherwise never be removed. Returns how many inboxes were dropped.
140
+ */
141
+ pruneExcept(keep) {
142
+ const alive = new Set(keep);
143
+ let dropped = 0;
144
+ for (const sessionId of [...this.inboxes.keys()]) {
145
+ if (alive.has(sessionId))
146
+ continue;
147
+ this.inboxes.delete(sessionId);
148
+ dropped++;
149
+ }
150
+ if (dropped > 0)
151
+ this.onChange?.();
152
+ return dropped;
153
+ }
154
+ /** Whole-store snapshot for persistence, expired messages already swept. */
155
+ snapshot() {
156
+ const inboxes = {};
157
+ for (const sessionId of [...this.inboxes.keys()]) {
158
+ const queue = this.live(sessionId);
159
+ if (queue.length > 0)
160
+ inboxes[sessionId] = queue.map((m) => ({ ...m }));
161
+ }
162
+ return { version: 1, inboxes };
163
+ }
164
+ /**
165
+ * Replace the store from a snapshot (server boot). Malformed input is skipped entry
166
+ * by entry rather than thrown: a corrupt file must not stop the server. Expired
167
+ * messages are not restored, and the caps hold for what is. Does not fire
168
+ * `onChange`: writing back what was just read would be a no-op at best.
169
+ */
170
+ restore(snapshot) {
171
+ const data = snapshot;
172
+ this.inboxes.clear();
173
+ if (!data || data.version !== 1 || typeof data.inboxes !== 'object' || data.inboxes === null)
174
+ return 0;
175
+ const cutoff = this.now() - MESSAGE_TTL_MS;
176
+ let restored = 0;
177
+ for (const [sessionId, raw] of Object.entries(data.inboxes)) {
178
+ if (!Array.isArray(raw))
179
+ continue;
180
+ const queue = [];
181
+ let bytes = 0;
182
+ // Oldest first whatever order the file has, so the caps keep the oldest mail,
183
+ // which is the rule for posts too.
184
+ const entries = raw
185
+ .filter((entry) => isStoredMessage(entry) && entry.createdAt > cutoff)
186
+ .sort((a, b) => a.createdAt - b.createdAt);
187
+ for (const entry of entries) {
188
+ const size = Buffer.byteLength(entry.text, 'utf8');
189
+ if (queue.length >= MAX_MESSAGES_PER_INBOX || bytes + size > MAX_INBOX_BYTES)
190
+ break;
191
+ queue.push({ ...entry });
192
+ bytes += size;
193
+ }
194
+ if (queue.length === 0)
195
+ continue;
196
+ this.inboxes.set(sessionId, queue);
197
+ restored += queue.length;
198
+ }
199
+ return restored;
200
+ }
201
+ /** Test hook: back to an empty store with no listener. */
202
+ resetForTests() {
203
+ this.inboxes.clear();
204
+ this.onChange = undefined;
205
+ }
206
+ /** The inbox with expired messages swept out (fires `onChange` when it swept any). */
207
+ live(sessionId) {
208
+ const queue = this.inboxes.get(sessionId);
209
+ if (!queue)
210
+ return [];
211
+ const cutoff = this.now() - MESSAGE_TTL_MS;
212
+ // Every message, not only queue[0]: a clock stepping back can put an older
213
+ // createdAt behind a newer one. At most 200 entries, so the scan is cheap.
214
+ if (!queue.some((m) => m.createdAt <= cutoff))
215
+ return queue;
216
+ const kept = queue.filter((m) => m.createdAt > cutoff);
217
+ this.store(sessionId, kept);
218
+ this.onChange?.();
219
+ return kept;
220
+ }
221
+ store(sessionId, queue) {
222
+ if (queue.length === 0)
223
+ this.inboxes.delete(sessionId);
224
+ else
225
+ this.inboxes.set(sessionId, queue);
226
+ }
227
+ }
228
+ function view(m) {
229
+ return { id: m.id, from: m.from, text: m.text, createdAt: m.createdAt, seen: m.seenAt !== null };
230
+ }
231
+ function countUnseen(queue) {
232
+ let n = 0;
233
+ for (const m of queue)
234
+ if (m.seenAt === null)
235
+ n++;
236
+ return n;
237
+ }
238
+ function inboxBytes(queue) {
239
+ let total = 0;
240
+ for (const m of queue)
241
+ total += Buffer.byteLength(m.text, 'utf8');
242
+ return total;
243
+ }
244
+ function isStoredMessage(value) {
245
+ const m = value;
246
+ return (!!m &&
247
+ typeof m.id === 'string' &&
248
+ (m.from === null || typeof m.from === 'string') &&
249
+ typeof m.text === 'string' &&
250
+ m.text.length > 0 &&
251
+ Buffer.byteLength(m.text, 'utf8') <= MAX_MESSAGE_BYTES &&
252
+ typeof m.createdAt === 'number' &&
253
+ Number.isFinite(m.createdAt) &&
254
+ (m.seenAt === null || typeof m.seenAt === 'number'));
255
+ }
256
+ /** Process-wide store used by the routes and the server wiring. Tests construct their own. */
257
+ export const agentInbox = new AgentInbox();
258
+ //# sourceMappingURL=agent-inbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-inbox.js","sourceRoot":"","sources":["../../src/web/agent-inbox.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,8 @@ interface AuthState {
26
26
  hookSecretFailures: StaleExpirationMap<string, number> | null;
27
27
  /** Per-username Basic-auth failure bucket (multi-user only). */
28
28
  userFailures: StaleExpirationMap<string, number> | null;
29
+ /** Per-(username, client IP) Basic-auth failure bucket (multi-user only). */
30
+ userIpFailures: StaleExpirationMap<string, number> | null;
29
31
  }
30
32
  /**
31
33
  * The landing-page case of serveLostWebviewFrame, for the index route. Without
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/web/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAgD,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAoB1G,OAAO,EAAqC,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAIlF,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;KACrB;CACF;AAGD,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAElD,8EAA8E;AAC9E,UAAU,SAAS;IACjB,YAAY,EAAE,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,IAAI,CAAC;IACnE,YAAY,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACxD,cAAc,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC1D,kBAAkB,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC9D,gEAAgE;IAChE,YAAY,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CACzD;AAwLD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAInE;AAED,uFAAuF;AACvF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAI1E;AAgCD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAK,GAAG,SAAS,CAoKrG;AA8KD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,EAAE,QAAQ,SAAK,GAAG,IAAI,CAuBxG;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAK,GAAG,IAAI,CAwEjG"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/web/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAgD,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAqB1G,OAAO,EAAqC,KAAK,QAAQ,EAAmB,MAAM,gBAAgB,CAAC;AAInG,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;KACrB;CACF;AAGD,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAElD,8EAA8E;AAC9E,UAAU,SAAS;IACjB,YAAY,EAAE,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,IAAI,CAAC;IACnE,YAAY,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACxD,cAAc,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC1D,kBAAkB,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC9D,gEAAgE;IAChE,YAAY,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACxD,6EAA6E;IAC7E,cAAc,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CAC3D;AAwMD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAInE;AAED,uFAAuF;AACvF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAI1E;AAgCD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAK,GAAG,SAAS,CAyLrG;AAgQD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,EAAE,QAAQ,SAAK,GAAG,IAAI,CAuBxG;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAK,GAAG,IAAI,CAwEjG"}