@superblocksteam/gateway 2.0.161 → 2.0.162-next.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 (235) hide show
  1. package/README.md +135 -39
  2. package/dist/agents/resolve-orchestrator-url.d.ts +18 -0
  3. package/dist/agents/resolve-orchestrator-url.js +64 -1
  4. package/dist/agents/resolve-orchestrator-url.js.map +1 -1
  5. package/dist/capabilities/lifecycle.d.ts +46 -29
  6. package/dist/capabilities/lifecycle.js +1731 -843
  7. package/dist/capabilities/lifecycle.js.map +1 -1
  8. package/dist/capabilities/query-integration.d.ts +14 -0
  9. package/dist/capabilities/query-integration.js +172 -0
  10. package/dist/capabilities/query-integration.js.map +1 -0
  11. package/dist/capabilities/source-files-archive.d.ts +11 -0
  12. package/dist/capabilities/source-files-archive.js +54 -0
  13. package/dist/capabilities/source-files-archive.js.map +1 -0
  14. package/dist/capabilities/types.d.ts +197 -119
  15. package/dist/capabilities/types.js +45 -6
  16. package/dist/capabilities/types.js.map +1 -1
  17. package/dist/capture/browser-contract.d.ts +15 -5
  18. package/dist/capture/browser-contract.js +13 -3
  19. package/dist/capture/browser-contract.js.map +1 -1
  20. package/dist/capture/browser-instructions.d.ts +3 -2
  21. package/dist/capture/browser-instructions.js +3 -2
  22. package/dist/capture/browser-instructions.js.map +1 -1
  23. package/dist/capture/mode.d.ts +8 -1
  24. package/dist/capture/mode.js +22 -2
  25. package/dist/capture/mode.js.map +1 -1
  26. package/dist/config.d.ts +22 -23
  27. package/dist/config.js +13 -12
  28. package/dist/config.js.map +1 -1
  29. package/dist/deps.d.ts +7 -7
  30. package/dist/index.d.ts +2 -4
  31. package/dist/index.js +2 -4
  32. package/dist/index.js.map +1 -1
  33. package/dist/integrations/read-only-postgres-query.d.ts +2 -0
  34. package/dist/integrations/read-only-postgres-query.js +164 -0
  35. package/dist/integrations/read-only-postgres-query.js.map +1 -0
  36. package/dist/main.js +1 -1
  37. package/dist/main.js.map +1 -1
  38. package/dist/playwright/ensure-chromium.d.ts +5 -7
  39. package/dist/playwright/ensure-chromium.js +13 -13
  40. package/dist/playwright/ensure-chromium.js.map +1 -1
  41. package/dist/preview/capture-screenshot.d.ts +30 -4
  42. package/dist/preview/capture-screenshot.js +67 -17
  43. package/dist/preview/capture-screenshot.js.map +1 -1
  44. package/dist/preview/viewer-url.js +3 -1
  45. package/dist/preview/viewer-url.js.map +1 -1
  46. package/dist/process/fault-barrier.js +1 -1
  47. package/dist/process/fault-barrier.js.map +1 -1
  48. package/dist/sabs/agent-facing-text.d.ts +2 -0
  49. package/dist/sabs/agent-facing-text.js +56 -2
  50. package/dist/sabs/agent-facing-text.js.map +1 -1
  51. package/dist/sabs/app-state.d.ts +126 -0
  52. package/dist/sabs/app-state.js +332 -0
  53. package/dist/sabs/app-state.js.map +1 -0
  54. package/dist/sabs/awaited-decision.d.ts +49 -0
  55. package/dist/sabs/awaited-decision.js +129 -0
  56. package/dist/sabs/awaited-decision.js.map +1 -0
  57. package/dist/sabs/editor-client-methods.d.ts +23 -5
  58. package/dist/sabs/editor-client-methods.js +45 -4
  59. package/dist/sabs/editor-client-methods.js.map +1 -1
  60. package/dist/sabs/editor-socket.d.ts +85 -0
  61. package/dist/sabs/editor-socket.js +67 -0
  62. package/dist/sabs/editor-socket.js.map +1 -0
  63. package/dist/sabs/session-peer.d.ts +119 -82
  64. package/dist/sabs/session-peer.js +10 -1
  65. package/dist/sabs/session-peer.js.map +1 -1
  66. package/dist/sabs/streamed-reply.d.ts +45 -0
  67. package/dist/sabs/streamed-reply.js +125 -0
  68. package/dist/sabs/streamed-reply.js.map +1 -0
  69. package/dist/sabs/turn-collector.d.ts +50 -35
  70. package/dist/sabs/turn-collector.js +110 -176
  71. package/dist/sabs/turn-collector.js.map +1 -1
  72. package/dist/sabs/websocket-session-peer.d.ts +104 -69
  73. package/dist/sabs/websocket-session-peer.js +1333 -477
  74. package/dist/sabs/websocket-session-peer.js.map +1 -1
  75. package/dist/server/client.d.ts +41 -9
  76. package/dist/server/client.js +126 -21
  77. package/dist/server/client.js.map +1 -1
  78. package/dist/start.d.ts +1 -1
  79. package/dist/start.js +30 -8
  80. package/dist/start.js.map +1 -1
  81. package/dist/stores/memory.d.ts +46 -0
  82. package/dist/stores/memory.js +163 -0
  83. package/dist/stores/memory.js.map +1 -0
  84. package/dist/stores/types.d.ts +92 -0
  85. package/dist/stores/types.js +11 -0
  86. package/dist/stores/types.js.map +1 -0
  87. package/dist/telemetry/mcp-client.d.ts +6 -1
  88. package/dist/telemetry/mcp-client.js +100 -4
  89. package/dist/telemetry/mcp-client.js.map +1 -1
  90. package/dist/telemetry/metrics.d.ts +12 -3
  91. package/dist/telemetry/metrics.js +60 -14
  92. package/dist/telemetry/metrics.js.map +1 -1
  93. package/dist/telemetry/runtime.js +4 -6
  94. package/dist/telemetry/runtime.js.map +1 -1
  95. package/dist/transports/mcp/admin-tools.d.ts +8 -0
  96. package/dist/transports/mcp/admin-tools.js +115 -4
  97. package/dist/transports/mcp/admin-tools.js.map +1 -1
  98. package/dist/transports/mcp/app-status-html.d.ts +4 -8
  99. package/dist/transports/mcp/app-status-html.js +336 -128
  100. package/dist/transports/mcp/app-status-html.js.map +1 -1
  101. package/dist/transports/mcp/client-presentation.d.ts +16 -0
  102. package/dist/transports/mcp/client-presentation.js +13 -0
  103. package/dist/transports/mcp/client-presentation.js.map +1 -0
  104. package/dist/transports/mcp/cowork-editor-url.d.ts +6 -0
  105. package/dist/transports/mcp/cowork-editor-url.js +10 -0
  106. package/dist/transports/mcp/cowork-editor-url.js.map +1 -0
  107. package/dist/transports/mcp/decision-card-html.d.ts +26 -0
  108. package/dist/transports/mcp/decision-card-html.js +876 -0
  109. package/dist/transports/mcp/decision-card-html.js.map +1 -0
  110. package/dist/transports/mcp/decision-elicitation.d.ts +42 -0
  111. package/dist/transports/mcp/decision-elicitation.js +59 -0
  112. package/dist/transports/mcp/decision-elicitation.js.map +1 -1
  113. package/dist/transports/mcp/editor-document-probe.d.ts +4 -0
  114. package/dist/transports/mcp/editor-document-probe.js +35 -0
  115. package/dist/transports/mcp/editor-document-probe.js.map +1 -0
  116. package/dist/transports/mcp/editor-integration-setup-url.d.ts +14 -0
  117. package/dist/transports/mcp/editor-integration-setup-url.js +29 -0
  118. package/dist/transports/mcp/editor-integration-setup-url.js.map +1 -0
  119. package/dist/transports/mcp/format-tool-content.d.ts +13 -18
  120. package/dist/transports/mcp/format-tool-content.js +172 -13
  121. package/dist/transports/mcp/format-tool-content.js.map +1 -1
  122. package/dist/transports/mcp/instructions/index.d.ts +23 -0
  123. package/dist/transports/mcp/instructions/index.js +81 -0
  124. package/dist/transports/mcp/instructions/index.js.map +1 -0
  125. package/dist/transports/mcp/instructions/result.d.ts +18 -0
  126. package/dist/transports/mcp/instructions/result.js +58 -0
  127. package/dist/transports/mcp/instructions/result.js.map +1 -0
  128. package/dist/transports/mcp/instructions/tools/ask-user.d.ts +2 -0
  129. package/dist/transports/mcp/instructions/tools/ask-user.js +20 -0
  130. package/dist/transports/mcp/instructions/tools/ask-user.js.map +1 -0
  131. package/dist/transports/mcp/instructions/tools/build-app.d.ts +4 -0
  132. package/dist/transports/mcp/instructions/tools/build-app.js +54 -0
  133. package/dist/transports/mcp/instructions/tools/build-app.js.map +1 -0
  134. package/dist/transports/mcp/instructions/tools/check-app-progress.d.ts +3 -0
  135. package/dist/transports/mcp/instructions/tools/check-app-progress.js +79 -0
  136. package/dist/transports/mcp/instructions/tools/check-app-progress.js.map +1 -0
  137. package/dist/transports/mcp/instructions/tools/check-publish-progress.d.ts +3 -0
  138. package/dist/transports/mcp/instructions/tools/check-publish-progress.js +23 -0
  139. package/dist/transports/mcp/instructions/tools/check-publish-progress.js.map +1 -0
  140. package/dist/transports/mcp/instructions/tools/copy.d.ts +18 -0
  141. package/dist/transports/mcp/instructions/tools/copy.js +49 -0
  142. package/dist/transports/mcp/instructions/tools/copy.js.map +1 -0
  143. package/dist/transports/mcp/instructions/tools/create-integration.d.ts +4 -0
  144. package/dist/transports/mcp/instructions/tools/create-integration.js +41 -0
  145. package/dist/transports/mcp/instructions/tools/create-integration.js.map +1 -0
  146. package/dist/transports/mcp/instructions/tools/edit-app.d.ts +3 -0
  147. package/dist/transports/mcp/instructions/tools/edit-app.js +18 -0
  148. package/dist/transports/mcp/instructions/tools/edit-app.js.map +1 -0
  149. package/dist/transports/mcp/instructions/tools/get-app.d.ts +3 -0
  150. package/dist/transports/mcp/instructions/tools/get-app.js +49 -0
  151. package/dist/transports/mcp/instructions/tools/get-app.js.map +1 -0
  152. package/dist/transports/mcp/instructions/tools/get-integration-metadata.d.ts +2 -0
  153. package/dist/transports/mcp/instructions/tools/get-integration-metadata.js +13 -0
  154. package/dist/transports/mcp/instructions/tools/get-integration-metadata.js.map +1 -0
  155. package/dist/transports/mcp/instructions/tools/index.d.ts +7 -0
  156. package/dist/transports/mcp/instructions/tools/index.js +31 -0
  157. package/dist/transports/mcp/instructions/tools/index.js.map +1 -0
  158. package/dist/transports/mcp/instructions/tools/publish-app.d.ts +3 -0
  159. package/dist/transports/mcp/instructions/tools/publish-app.js +23 -0
  160. package/dist/transports/mcp/instructions/tools/publish-app.js.map +1 -0
  161. package/dist/transports/mcp/instructions/tools/start-app.d.ts +3 -0
  162. package/dist/transports/mcp/instructions/tools/start-app.js +22 -0
  163. package/dist/transports/mcp/instructions/tools/start-app.js.map +1 -0
  164. package/dist/transports/mcp/instructions/tools/upload-artifact.d.ts +2 -0
  165. package/dist/transports/mcp/instructions/tools/upload-artifact.js +13 -0
  166. package/dist/transports/mcp/instructions/tools/upload-artifact.js.map +1 -0
  167. package/dist/transports/mcp/mcp-app-brand-css.d.ts +1 -0
  168. package/dist/transports/mcp/mcp-app-brand-css.js +180 -0
  169. package/dist/transports/mcp/mcp-app-brand-css.js.map +1 -0
  170. package/dist/transports/mcp/mount.d.ts +14 -1
  171. package/dist/transports/mcp/mount.js +729 -172
  172. package/dist/transports/mcp/mount.js.map +1 -1
  173. package/dist/transports/mcp/native-browser-presence.d.ts +45 -0
  174. package/dist/transports/mcp/native-browser-presence.js +158 -0
  175. package/dist/transports/mcp/native-browser-presence.js.map +1 -0
  176. package/dist/transports/mcp/plan-approval.d.ts +44 -0
  177. package/dist/transports/mcp/plan-approval.js +179 -0
  178. package/dist/transports/mcp/plan-approval.js.map +1 -0
  179. package/dist/transports/mcp/session-directory.d.ts +7 -0
  180. package/dist/transports/mcp/session-directory.js +18 -0
  181. package/dist/transports/mcp/session-directory.js.map +1 -0
  182. package/dist/transports/mcp/tool-names.d.ts +2 -0
  183. package/dist/transports/mcp/tool-names.js +2 -0
  184. package/dist/transports/mcp/tool-names.js.map +1 -0
  185. package/package.json +16 -7
  186. package/skills/superblocks-build/SKILL.md +59 -0
  187. package/skills/superblocks-import/SKILL.md +125 -0
  188. package/dist/capabilities/import-prompt.d.ts +0 -11
  189. package/dist/capabilities/import-prompt.js +0 -96
  190. package/dist/capabilities/import-prompt.js.map +0 -1
  191. package/dist/capabilities/persisted-progress.d.ts +0 -46
  192. package/dist/capabilities/persisted-progress.js +0 -246
  193. package/dist/capabilities/persisted-progress.js.map +0 -1
  194. package/dist/events/cursor.d.ts +0 -43
  195. package/dist/events/cursor.js +0 -78
  196. package/dist/events/cursor.js.map +0 -1
  197. package/dist/events/memory-event-store.d.ts +0 -34
  198. package/dist/events/memory-event-store.js +0 -110
  199. package/dist/events/memory-event-store.js.map +0 -1
  200. package/dist/events/merge.d.ts +0 -23
  201. package/dist/events/merge.js +0 -97
  202. package/dist/events/merge.js.map +0 -1
  203. package/dist/events/normalized-collector.d.ts +0 -62
  204. package/dist/events/normalized-collector.js +0 -156
  205. package/dist/events/normalized-collector.js.map +0 -1
  206. package/dist/events/schema.d.ts +0 -9
  207. package/dist/events/schema.js +0 -93
  208. package/dist/events/schema.js.map +0 -1
  209. package/dist/events/snapshot.d.ts +0 -32
  210. package/dist/events/snapshot.js +0 -57
  211. package/dist/events/snapshot.js.map +0 -1
  212. package/dist/events/stream-key.d.ts +0 -2
  213. package/dist/events/stream-key.js +0 -31
  214. package/dist/events/stream-key.js.map +0 -1
  215. package/dist/events/types.d.ts +0 -179
  216. package/dist/events/types.js +0 -66
  217. package/dist/events/types.js.map +0 -1
  218. package/dist/resume/memory-progress-store.d.ts +0 -39
  219. package/dist/resume/memory-progress-store.js +0 -82
  220. package/dist/resume/memory-progress-store.js.map +0 -1
  221. package/dist/resume/memory-recent-app-store.d.ts +0 -14
  222. package/dist/resume/memory-recent-app-store.js +0 -27
  223. package/dist/resume/memory-recent-app-store.js.map +0 -1
  224. package/dist/resume/memory-turn-store.d.ts +0 -18
  225. package/dist/resume/memory-turn-store.js +0 -73
  226. package/dist/resume/memory-turn-store.js.map +0 -1
  227. package/dist/resume/progress-key.d.ts +0 -21
  228. package/dist/resume/progress-key.js +0 -58
  229. package/dist/resume/progress-key.js.map +0 -1
  230. package/dist/resume/stores.d.ts +0 -14
  231. package/dist/resume/stores.js +0 -18
  232. package/dist/resume/stores.js.map +0 -1
  233. package/dist/resume/types.d.ts +0 -124
  234. package/dist/resume/types.js +0 -13
  235. package/dist/resume/types.js.map +0 -1
@@ -1,43 +0,0 @@
1
- import { type EventProducer, type LiveEditEvent } from "./types.js";
2
- export declare const EVENT_CURSOR_VERSION = 1;
3
- /**
4
- * How far one caller has read a live edit's history.
5
- *
6
- * There is deliberately no single number here. SABS and the dev server mint
7
- * their own sequence counters and cannot see each other's, so a global
8
- * position would either have to be invented by the gateway — making it a
9
- * third counter that two producers can race — or would have to pretend one
10
- * producer's 7 is comparable with the other's. Naming each producer's own
11
- * position is the smallest contract that stays true to how the events are
12
- * produced, and it degrades gracefully: a producer this cursor has never
13
- * heard from simply reads from the start of its stream.
14
- *
15
- * Keys are the wire producer names (lowercase snake_case) and the field name
16
- * is lowerCamelCase, so the cursor survives a round trip through a tool
17
- * result unchanged.
18
- */
19
- export type EventCursor = {
20
- streams: Partial<Record<EventProducer, number>>;
21
- version: typeof EVENT_CURSOR_VERSION;
22
- };
23
- export declare const EMPTY_EVENT_CURSOR: EventCursor;
24
- /** Last sequence this cursor consumed from a producer; 0 when never read. */
25
- export declare function cursorPosition(cursor: EventCursor | undefined, producer: EventProducer): number;
26
- /**
27
- * Moves a cursor past the given events.
28
- *
29
- * Only ever forwards. Events arrive out of order between producers, and a
30
- * re-read of an older event must not rewind a caller that has already gone
31
- * further — that is the loop where a caller replays the same events forever.
32
- */
33
- export declare function advanceEventCursor(cursor: EventCursor | undefined, events: readonly LiveEditEvent[]): EventCursor;
34
- /**
35
- * Reads a cursor a caller handed back.
36
- *
37
- * Anything unrecognized reads as "no cursor" rather than as an error: a caller
38
- * resuming with a cursor from an older contract should re-read the retained
39
- * history, not be refused. Positions are clamped to safe non-negative
40
- * integers, so a hand-edited cursor cannot skip a stream to infinity or wind
41
- * one backwards into a replay loop.
42
- */
43
- export declare function parseEventCursor(raw: unknown): EventCursor | undefined;
@@ -1,78 +0,0 @@
1
- import { EVENT_PRODUCERS, } from "./types.js";
2
- export const EVENT_CURSOR_VERSION = 1;
3
- export const EMPTY_EVENT_CURSOR = {
4
- streams: {},
5
- version: EVENT_CURSOR_VERSION,
6
- };
7
- /** Last sequence this cursor consumed from a producer; 0 when never read. */
8
- export function cursorPosition(cursor, producer) {
9
- return cursor?.streams[producer] ?? 0;
10
- }
11
- /**
12
- * Moves a cursor past the given events.
13
- *
14
- * Only ever forwards. Events arrive out of order between producers, and a
15
- * re-read of an older event must not rewind a caller that has already gone
16
- * further — that is the loop where a caller replays the same events forever.
17
- */
18
- export function advanceEventCursor(cursor, events) {
19
- const streams = {
20
- ...(cursor?.streams ?? {}),
21
- };
22
- for (const event of events) {
23
- const seen = streams[event.producer] ?? 0;
24
- if (event.sequence > seen) {
25
- streams[event.producer] = event.sequence;
26
- }
27
- }
28
- return { streams: sortStreams(streams), version: EVENT_CURSOR_VERSION };
29
- }
30
- /**
31
- * Producer keys in a fixed order, so two cursors holding the same positions
32
- * serialize to the same bytes. A cursor travels through JSON in a tool result
33
- * and back again; an unstable key order would make two equal cursors compare
34
- * unequal wherever they are compared as text.
35
- */
36
- function sortStreams(streams) {
37
- const sorted = {};
38
- for (const producer of [...EVENT_PRODUCERS].sort()) {
39
- const position = streams[producer];
40
- if (position !== undefined) {
41
- sorted[producer] = position;
42
- }
43
- }
44
- return sorted;
45
- }
46
- /**
47
- * Reads a cursor a caller handed back.
48
- *
49
- * Anything unrecognized reads as "no cursor" rather than as an error: a caller
50
- * resuming with a cursor from an older contract should re-read the retained
51
- * history, not be refused. Positions are clamped to safe non-negative
52
- * integers, so a hand-edited cursor cannot skip a stream to infinity or wind
53
- * one backwards into a replay loop.
54
- */
55
- export function parseEventCursor(raw) {
56
- if (typeof raw !== "object" || raw === null) {
57
- return undefined;
58
- }
59
- const candidate = raw;
60
- if (candidate.version !== EVENT_CURSOR_VERSION) {
61
- return undefined;
62
- }
63
- if (typeof candidate.streams !== "object" || candidate.streams === null) {
64
- return undefined;
65
- }
66
- const source = candidate.streams;
67
- const streams = {};
68
- for (const producer of EVENT_PRODUCERS) {
69
- const position = source[producer];
70
- if (typeof position === "number" &&
71
- Number.isSafeInteger(position) &&
72
- position >= 0) {
73
- streams[producer] = position;
74
- }
75
- }
76
- return { streams: sortStreams(streams), version: EVENT_CURSOR_VERSION };
77
- }
78
- //# sourceMappingURL=cursor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/events/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,GAGhB,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAuBtC,MAAM,CAAC,MAAM,kBAAkB,GAAgB;IAC7C,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,oBAAoB;CAC9B,CAAC;AAEF,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAC5B,MAA+B,EAC/B,QAAuB;IAEvB,OAAO,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA+B,EAC/B,MAAgC;IAEhC,MAAM,OAAO,GAA2C;QACtD,GAAG,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC;KAC3B,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAK,CAAC,QAAQ,GAAG,IAAI,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC3C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAClB,OAA+C;IAE/C,MAAM,MAAM,GAA2C,EAAE,CAAC;IAC1D,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,eAAe,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,SAAS,GAAG,GAA+C,CAAC;IAClE,IAAI,SAAS,CAAC,OAAO,KAAK,oBAAoB,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,SAAS,CAAC,OAAO,KAAK,QAAQ,IAAI,SAAS,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QACxE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,SAAS,CAAC,OAAkC,CAAC;IAC5D,MAAM,OAAO,GAA2C,EAAE,CAAC;IAC3D,KAAK,MAAM,QAAQ,IAAI,eAAe,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,IACE,OAAO,QAAQ,KAAK,QAAQ;YAC5B,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC;YAC9B,QAAQ,IAAI,CAAC,EACb,CAAC;YACD,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAC1E,CAAC"}
@@ -1,34 +0,0 @@
1
- import type { EventCursor } from "./cursor.js";
2
- import { type AppendOutcome, type EventProducer, type EventScope, type EventSnapshot, type EventStore, type LiveEditEvent } from "./types.js";
3
- /**
4
- * Local-only fallback for a laptop stack with no Redis, matching the same
5
- * fallback the turn store makes. It is not a second source of truth: a
6
- * multi-replica deployment would need an external store, because two
7
- * processes with private maps cannot agree on what was already delivered.
8
- *
9
- * A shared test suite runs both implementations against the same behaviour, so
10
- * "works locally, breaks on Redis" shows up here rather than in production.
11
- */
12
- export declare class MemoryEventStore implements EventStore {
13
- private readonly streams;
14
- append(event: LiveEditEvent): Promise<AppendOutcome>;
15
- list(input: EventScope & {
16
- afterSequence?: number;
17
- limit?: number;
18
- producer?: EventProducer;
19
- }): Promise<LiveEditEvent[]>;
20
- snapshot(input: EventScope & {
21
- cursor?: EventCursor;
22
- limit?: number;
23
- }): Promise<EventSnapshot>;
24
- /** One producer's retained stream in sequence order, as Redis reads it. */
25
- private ordered;
26
- private streamFor;
27
- /**
28
- * Drops the lowest-sequence events past the bound, which takes their
29
- * de-duplication records with them: keeping every id forever would make the
30
- * bound on events pointless.
31
- */
32
- private trim;
33
- private evictExpired;
34
- }
@@ -1,110 +0,0 @@
1
- import { PRODUCERS_IN_ORDER, sortEvents } from "./merge.js";
2
- import { buildSnapshot } from "./snapshot.js";
3
- import { streamSuffix } from "./stream-key.js";
4
- import { EVENT_STREAM_TTL_MS, MAX_EVENTS_PER_STREAM, } from "./types.js";
5
- /**
6
- * Local-only fallback for a laptop stack with no Redis, matching the same
7
- * fallback the turn store makes. It is not a second source of truth: a
8
- * multi-replica deployment would need an external store, because two
9
- * processes with private maps cannot agree on what was already delivered.
10
- *
11
- * A shared test suite runs both implementations against the same behaviour, so
12
- * "works locally, breaks on Redis" shows up here rather than in production.
13
- */
14
- export class MemoryEventStore {
15
- streams = new Map();
16
- async append(event) {
17
- const stream = this.streamFor(event, event.producer);
18
- stream.expiresAtMs = Date.now() + EVENT_STREAM_TTL_MS;
19
- // Presence is the de-duplication record, so it cannot outlive the event
20
- // or be evicted separately from it.
21
- if (stream.byEventId.has(event.eventId)) {
22
- return "duplicate";
23
- }
24
- stream.byEventId.set(event.eventId, event);
25
- this.trim(stream);
26
- return "accepted";
27
- }
28
- async list(input) {
29
- this.evictExpired();
30
- const producers = input.producer ? [input.producer] : PRODUCERS_IN_ORDER;
31
- const collected = [];
32
- for (const producer of producers) {
33
- const stream = this.streams.get(key(input, producer));
34
- for (const event of stream?.byEventId.values() ?? []) {
35
- if (input.afterSequence !== undefined &&
36
- event.sequence <= input.afterSequence) {
37
- continue;
38
- }
39
- collected.push(event);
40
- }
41
- }
42
- const ordered = sortEvents(collected);
43
- return input.limit === undefined ? ordered : ordered.slice(0, input.limit);
44
- }
45
- async snapshot(input) {
46
- this.evictExpired();
47
- return await buildSnapshot(input, {
48
- firstRetainedSequence: async (producer) => this.ordered(input, producer)[0]?.sequence,
49
- readAfter: async ({ afterSequence, limit, producer }) => {
50
- const after = this.ordered(input, producer).filter((event) => event.sequence > afterSequence);
51
- return limit === undefined ? after : after.slice(0, limit);
52
- },
53
- readTail: async ({ count, producer }) => this.ordered(input, producer).slice(-count),
54
- });
55
- }
56
- /** One producer's retained stream in sequence order, as Redis reads it. */
57
- ordered(scope, producer) {
58
- const stream = this.streams.get(key(scope, producer));
59
- return [...(stream?.byEventId.values() ?? [])].sort((left, right) => left.sequence - right.sequence ||
60
- (left.eventId < right.eventId
61
- ? -1
62
- : left.eventId > right.eventId
63
- ? 1
64
- : 0));
65
- }
66
- streamFor(scope, producer) {
67
- this.evictExpired();
68
- const streamKey = key(scope, producer);
69
- const existing = this.streams.get(streamKey);
70
- if (existing) {
71
- return existing;
72
- }
73
- const created = {
74
- byEventId: new Map(),
75
- expiresAtMs: Date.now() + EVENT_STREAM_TTL_MS,
76
- };
77
- this.streams.set(streamKey, created);
78
- return created;
79
- }
80
- /**
81
- * Drops the lowest-sequence events past the bound, which takes their
82
- * de-duplication records with them: keeping every id forever would make the
83
- * bound on events pointless.
84
- */
85
- trim(stream) {
86
- if (stream.byEventId.size <= MAX_EVENTS_PER_STREAM) {
87
- return;
88
- }
89
- const oldestFirst = [...stream.byEventId.values()].sort((left, right) => left.sequence - right.sequence);
90
- for (const dropped of oldestFirst.slice(0, stream.byEventId.size - MAX_EVENTS_PER_STREAM)) {
91
- stream.byEventId.delete(dropped.eventId);
92
- }
93
- }
94
- evictExpired() {
95
- const now = Date.now();
96
- for (const [streamKey, stream] of this.streams) {
97
- if (stream.expiresAtMs <= now) {
98
- this.streams.delete(streamKey);
99
- }
100
- }
101
- }
102
- }
103
- /**
104
- * The same addressing the Redis store uses, from the same builder, so the two
105
- * cannot disagree about which events share a stream.
106
- */
107
- function key(scope, producer) {
108
- return streamSuffix(scope, producer);
109
- }
110
- //# sourceMappingURL=memory-event-store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"memory-event-store.js","sourceRoot":"","sources":["../../src/events/memory-event-store.ts"],"names":[],"mappings":"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"}
@@ -1,23 +0,0 @@
1
- import type { EventProducer, LiveEditEvent } from "./types.js";
2
- export declare const PRODUCERS_IN_ORDER: readonly EventProducer[];
3
- /**
4
- * Merges each producer's stream into one order.
5
- *
6
- * This cannot be a comparator. "Sequence within a producer, timestamp across
7
- * producers" has no total order behind it: if one producer's clock steps
8
- * backwards between two of its own events (NTP correction, container skew),
9
- * its first event can be later than another producer's event that is itself
10
- * later than its second. Sorting on such a rule is a cycle, and `Array#sort`
11
- * answers a cycle differently depending on the order it read the input — so
12
- * two Gateway replicas holding the same events could disagree about what the
13
- * turn looked like.
14
- *
15
- * A merge has no such freedom. Each producer is walked strictly in sequence
16
- * order, so its own events can never be reordered against each other whatever
17
- * its clock did. Only the choice of which producer to take from next depends
18
- * on time, and that is decided by a monotone clock — a producer's event is
19
- * never treated as earlier than one already emitted from the same producer —
20
- * with producer name and then sequence settling a dead heat. The result
21
- * depends on the set of events alone, never on the order they were read.
22
- */
23
- export declare function sortEvents(events: LiveEditEvent[]): LiveEditEvent[];
@@ -1,97 +0,0 @@
1
- export const PRODUCERS_IN_ORDER = [
2
- "dev_server",
3
- "sabs",
4
- ];
5
- /**
6
- * Merges each producer's stream into one order.
7
- *
8
- * This cannot be a comparator. "Sequence within a producer, timestamp across
9
- * producers" has no total order behind it: if one producer's clock steps
10
- * backwards between two of its own events (NTP correction, container skew),
11
- * its first event can be later than another producer's event that is itself
12
- * later than its second. Sorting on such a rule is a cycle, and `Array#sort`
13
- * answers a cycle differently depending on the order it read the input — so
14
- * two Gateway replicas holding the same events could disagree about what the
15
- * turn looked like.
16
- *
17
- * A merge has no such freedom. Each producer is walked strictly in sequence
18
- * order, so its own events can never be reordered against each other whatever
19
- * its clock did. Only the choice of which producer to take from next depends
20
- * on time, and that is decided by a monotone clock — a producer's event is
21
- * never treated as earlier than one already emitted from the same producer —
22
- * with producer name and then sequence settling a dead heat. The result
23
- * depends on the set of events alone, never on the order they were read.
24
- */
25
- export function sortEvents(events) {
26
- const byProducer = new Map();
27
- for (const event of events) {
28
- const stream = byProducer.get(event.producer);
29
- if (stream) {
30
- stream.push(event);
31
- }
32
- else {
33
- byProducer.set(event.producer, [event]);
34
- }
35
- }
36
- const cursors = [...byProducer.entries()]
37
- .sort(([left], [right]) => compareProducers(left, right))
38
- .map(([producer, stream]) => ({
39
- emittedThroughMs: Number.NEGATIVE_INFINITY,
40
- // Sequence is the only authority within a producer. The event id settles
41
- // the case of one sequence claimed twice, so even that stays stable.
42
- events: [...stream].sort((left, right) => left.sequence - right.sequence ||
43
- compareStrings(left.eventId, right.eventId)),
44
- position: 0,
45
- producer,
46
- }));
47
- const merged = [];
48
- for (;;) {
49
- const next = takeNext(cursors);
50
- if (!next) {
51
- return merged;
52
- }
53
- merged.push(next);
54
- }
55
- }
56
- /**
57
- * Removes and returns the event that comes next across all producers, or
58
- * undefined once every stream is exhausted.
59
- */
60
- function takeNext(cursors) {
61
- let chosen;
62
- let chosenAtMs = Number.POSITIVE_INFINITY;
63
- for (const cursor of cursors) {
64
- const head = cursor.events[cursor.position];
65
- if (!head) {
66
- continue;
67
- }
68
- const atMs = Math.max(head.occurredAtMs, cursor.emittedThroughMs);
69
- // Cursors are already in producer order, so `<` alone keeps the earlier
70
- // producer on a tie.
71
- if (atMs < chosenAtMs) {
72
- chosen = cursor;
73
- chosenAtMs = atMs;
74
- }
75
- }
76
- if (!chosen) {
77
- return undefined;
78
- }
79
- chosen.emittedThroughMs = chosenAtMs;
80
- const head = chosen.events[chosen.position];
81
- chosen.position += 1;
82
- return head;
83
- }
84
- function compareProducers(left, right) {
85
- const leftRank = PRODUCERS_IN_ORDER.indexOf(left);
86
- const rightRank = PRODUCERS_IN_ORDER.indexOf(right);
87
- return leftRank !== rightRank
88
- ? leftRank - rightRank
89
- : compareStrings(left, right);
90
- }
91
- function compareStrings(left, right) {
92
- if (left === right) {
93
- return 0;
94
- }
95
- return left < right ? -1 : 1;
96
- }
97
- //# sourceMappingURL=merge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/events/merge.ts"],"names":[],"mappings":"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"}
@@ -1,62 +0,0 @@
1
- import type { ClarkPeerEvent } from "../sabs/editor-client-methods.js";
2
- import type { TurnCollector } from "../sabs/turn-collector.js";
3
- import type { LiveEditEvent, SESSION_STATUSES } from "./types.js";
4
- /**
5
- * How many recent events are remembered for cross-transport de-duplication.
6
- * A long build pushes far more than this; forgetting the oldest is safe
7
- * because the overlap between the socket and the push path is measured in
8
- * seconds, not in thousands of events.
9
- */
10
- export declare const MAX_TRACKED_FINGERPRINTS = 256;
11
- export type NormalizedTurnCollectorDeps = {
12
- collector: TurnCollector;
13
- /**
14
- * Clark needs an interactive authorization before it can continue. Reported
15
- * rather than collected: it is not something Clark did, and a caller that
16
- * only reads the turn's reply would never learn the build is stuck.
17
- */
18
- onAuthRequired?: (reason: string) => void;
19
- /** The live-edit session changed state underneath the turn. */
20
- onSessionStatus?: (status: (typeof SESSION_STATUSES)[number]) => void;
21
- /** The turn id Gateway minted with the prompt this collector is driving. */
22
- turnId: string | undefined;
23
- };
24
- /**
25
- * One door into a turn's collector for both transports.
26
- *
27
- * Clark's activity reaches Gateway two ways during the transition: directly on
28
- * the held editor socket, and relayed through SABS over the push channel. They
29
- * overlap, so this applies each underlying fact once, and it drops anything
30
- * carrying a turn id other than the one it is collecting for.
31
- *
32
- * Session lifecycle and auth-required are not turn activity. They are recorded
33
- * here for the caller to read rather than pushed into the collector, which
34
- * only understands what Clark did.
35
- */
36
- export declare class NormalizedTurnCollector {
37
- private readonly deps;
38
- private readonly seen;
39
- private authReason;
40
- private session;
41
- constructor(deps: NormalizedTurnCollectorDeps);
42
- /** An event observed on the held editor socket. */
43
- fromSocket(event: ClarkPeerEvent): void;
44
- /** An event relayed through SABS over the push channel. */
45
- fromPush(event: LiveEditEvent): void;
46
- /** Reason Clark cannot proceed without an interactive authorization. */
47
- authRequired(): string | undefined;
48
- sessionStatus(): (typeof SESSION_STATUSES)[number] | undefined;
49
- /** Exposed so the bound on remembered events can be asserted. */
50
- trackedFingerprintCount(): number;
51
- /**
52
- * A session-scoped event carries no turn id and belongs to every turn on the
53
- * live edit; a turn-scoped one belongs only to the turn that minted the id.
54
- */
55
- private belongsToThisTurn;
56
- /**
57
- * Records a fingerprint, returning false when it was already known.
58
- * Insertion order is iteration order for a Set, so evicting the first entry
59
- * drops the oldest.
60
- */
61
- private remember;
62
- }
@@ -1,156 +0,0 @@
1
- /**
2
- * How many recent events are remembered for cross-transport de-duplication.
3
- * A long build pushes far more than this; forgetting the oldest is safe
4
- * because the overlap between the socket and the push path is measured in
5
- * seconds, not in thousands of events.
6
- */
7
- export const MAX_TRACKED_FINGERPRINTS = 256;
8
- /**
9
- * One door into a turn's collector for both transports.
10
- *
11
- * Clark's activity reaches Gateway two ways during the transition: directly on
12
- * the held editor socket, and relayed through SABS over the push channel. They
13
- * overlap, so this applies each underlying fact once, and it drops anything
14
- * carrying a turn id other than the one it is collecting for.
15
- *
16
- * Session lifecycle and auth-required are not turn activity. They are recorded
17
- * here for the caller to read rather than pushed into the collector, which
18
- * only understands what Clark did.
19
- */
20
- export class NormalizedTurnCollector {
21
- deps;
22
- seen = new Set();
23
- authReason;
24
- session;
25
- constructor(deps) {
26
- this.deps = deps;
27
- }
28
- /** An event observed on the held editor socket. */
29
- fromSocket(event) {
30
- const fingerprint = socketFingerprint(event);
31
- if (fingerprint && !this.remember(fingerprint)) {
32
- return;
33
- }
34
- this.deps.collector.handle(event);
35
- }
36
- /** An event relayed through SABS over the push channel. */
37
- fromPush(event) {
38
- if (!this.belongsToThisTurn(event)) {
39
- return;
40
- }
41
- // The event id is authoritative for a redelivery of the same push; the
42
- // content fingerprint is what catches the same fact arriving on the other
43
- // transport, which has no event id to compare against.
44
- if (!this.remember(`event:${event.eventId}`)) {
45
- return;
46
- }
47
- const fingerprint = pushFingerprint(event);
48
- if (fingerprint && !this.remember(fingerprint)) {
49
- return;
50
- }
51
- switch (event.payload.kind) {
52
- case "auth_required": {
53
- this.authReason = event.payload.reason ?? "auth_required";
54
- this.deps.onAuthRequired?.(this.authReason);
55
- return;
56
- }
57
- case "session": {
58
- this.session = event.payload.status;
59
- this.deps.onSessionStatus?.(this.session);
60
- return;
61
- }
62
- case "chat_message": {
63
- this.deps.collector.handle({
64
- kind: "message",
65
- message: {
66
- content: event.payload.text,
67
- ...(event.payload.messageId ? { id: event.payload.messageId } : {}),
68
- role: event.payload.role,
69
- type: event.payload.messageType ?? "text",
70
- },
71
- });
72
- return;
73
- }
74
- case "generation_state": {
75
- this.deps.collector.handle({
76
- ...(event.payload.hasError === undefined
77
- ? {}
78
- : { hasError: event.payload.hasError }),
79
- ...(event.payload.hasSuggestions === undefined
80
- ? {}
81
- : { hasSuggestions: event.payload.hasSuggestions }),
82
- kind: "generation_state",
83
- state: event.payload.state,
84
- });
85
- return;
86
- }
87
- case "progress": {
88
- this.deps.collector.handle({
89
- kind: "task_status",
90
- label: event.payload.label,
91
- });
92
- }
93
- }
94
- }
95
- /** Reason Clark cannot proceed without an interactive authorization. */
96
- authRequired() {
97
- return this.authReason;
98
- }
99
- sessionStatus() {
100
- return this.session;
101
- }
102
- /** Exposed so the bound on remembered events can be asserted. */
103
- trackedFingerprintCount() {
104
- return this.seen.size;
105
- }
106
- /**
107
- * A session-scoped event carries no turn id and belongs to every turn on the
108
- * live edit; a turn-scoped one belongs only to the turn that minted the id.
109
- */
110
- belongsToThisTurn(event) {
111
- if (!event.gatewayTurnId) {
112
- return true;
113
- }
114
- return event.gatewayTurnId === this.deps.turnId;
115
- }
116
- /**
117
- * Records a fingerprint, returning false when it was already known.
118
- * Insertion order is iteration order for a Set, so evicting the first entry
119
- * drops the oldest.
120
- */
121
- remember(fingerprint) {
122
- if (this.seen.has(fingerprint)) {
123
- return false;
124
- }
125
- if (this.seen.size >= MAX_TRACKED_FINGERPRINTS) {
126
- const oldest = this.seen.values().next();
127
- if (!oldest.done) {
128
- this.seen.delete(oldest.value);
129
- }
130
- }
131
- this.seen.add(fingerprint);
132
- return true;
133
- }
134
- }
135
- /**
136
- * Identity of a fact as seen on the socket, in the same terms
137
- * {@link pushFingerprint} uses, so the two transports collide on it.
138
- *
139
- * Only events with a stable identity are fingerprinted. A state or label with
140
- * no id genuinely recurs during a build, and suppressing a real repeat is
141
- * worse than letting a transitional duplicate through: the collector already
142
- * drops consecutive identical progress lines.
143
- */
144
- function socketFingerprint(event) {
145
- if (event.kind === "message" && typeof event.message.id === "string") {
146
- return `message:${event.message.id}`;
147
- }
148
- return undefined;
149
- }
150
- function pushFingerprint(event) {
151
- if (event.payload.kind === "chat_message" && event.payload.messageId) {
152
- return `message:${event.payload.messageId}`;
153
- }
154
- return undefined;
155
- }
156
- //# sourceMappingURL=normalized-collector.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"normalized-collector.js","sourceRoot":"","sources":["../../src/events/normalized-collector.ts"],"names":[],"mappings":"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"}
@@ -1,9 +0,0 @@
1
- import { type ParseResult } from "./types.js";
2
- /**
3
- * Validates one inbound event against the contract.
4
- *
5
- * SABS validates the same shape before pushing it, which is not a reason to
6
- * skip it here: a validated producer is not a trusted one, and this is the
7
- * process boundary where the bytes become Gateway's problem.
8
- */
9
- export declare function parseLiveEditEvent(input: unknown): ParseResult;