@superblocksteam/gateway 2.0.155-next.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 (183) hide show
  1. package/LICENSE.txt +87 -0
  2. package/README.md +170 -0
  3. package/dist/agents/resolve-orchestrator-url.d.ts +22 -0
  4. package/dist/agents/resolve-orchestrator-url.js +38 -0
  5. package/dist/agents/resolve-orchestrator-url.js.map +1 -0
  6. package/dist/capabilities/import-prompt.d.ts +11 -0
  7. package/dist/capabilities/import-prompt.js +96 -0
  8. package/dist/capabilities/import-prompt.js.map +1 -0
  9. package/dist/capabilities/lifecycle.d.ts +73 -0
  10. package/dist/capabilities/lifecycle.js +2531 -0
  11. package/dist/capabilities/lifecycle.js.map +1 -0
  12. package/dist/capabilities/persisted-progress.d.ts +46 -0
  13. package/dist/capabilities/persisted-progress.js +246 -0
  14. package/dist/capabilities/persisted-progress.js.map +1 -0
  15. package/dist/capabilities/types.d.ts +446 -0
  16. package/dist/capabilities/types.js +47 -0
  17. package/dist/capabilities/types.js.map +1 -0
  18. package/dist/capture/browser-contract.d.ts +36 -0
  19. package/dist/capture/browser-contract.js +24 -0
  20. package/dist/capture/browser-contract.js.map +1 -0
  21. package/dist/capture/browser-instructions.d.ts +9 -0
  22. package/dist/capture/browser-instructions.js +17 -0
  23. package/dist/capture/browser-instructions.js.map +1 -0
  24. package/dist/capture/capture-library-probe.d.ts +55 -0
  25. package/dist/capture/capture-library-probe.js +177 -0
  26. package/dist/capture/capture-library-probe.js.map +1 -0
  27. package/dist/capture/capture-library.d.ts +52 -0
  28. package/dist/capture/capture-library.js +249 -0
  29. package/dist/capture/capture-library.js.map +1 -0
  30. package/dist/capture/classify-library-paint.d.ts +21 -0
  31. package/dist/capture/classify-library-paint.js +13 -0
  32. package/dist/capture/classify-library-paint.js.map +1 -0
  33. package/dist/capture/host-mcp-scan.d.ts +16 -0
  34. package/dist/capture/host-mcp-scan.js +64 -0
  35. package/dist/capture/host-mcp-scan.js.map +1 -0
  36. package/dist/capture/library-parent-script.d.ts +34 -0
  37. package/dist/capture/library-parent-script.js +82 -0
  38. package/dist/capture/library-parent-script.js.map +1 -0
  39. package/dist/capture/mode.d.ts +22 -0
  40. package/dist/capture/mode.js +45 -0
  41. package/dist/capture/mode.js.map +1 -0
  42. package/dist/config.d.ts +141 -0
  43. package/dist/config.js +109 -0
  44. package/dist/config.js.map +1 -0
  45. package/dist/debug.d.ts +2 -0
  46. package/dist/debug.js +9 -0
  47. package/dist/debug.js.map +1 -0
  48. package/dist/deps.d.ts +36 -0
  49. package/dist/deps.js +2 -0
  50. package/dist/deps.js.map +1 -0
  51. package/dist/events/cursor.d.ts +43 -0
  52. package/dist/events/cursor.js +78 -0
  53. package/dist/events/cursor.js.map +1 -0
  54. package/dist/events/memory-event-store.d.ts +34 -0
  55. package/dist/events/memory-event-store.js +110 -0
  56. package/dist/events/memory-event-store.js.map +1 -0
  57. package/dist/events/merge.d.ts +23 -0
  58. package/dist/events/merge.js +97 -0
  59. package/dist/events/merge.js.map +1 -0
  60. package/dist/events/normalized-collector.d.ts +62 -0
  61. package/dist/events/normalized-collector.js +156 -0
  62. package/dist/events/normalized-collector.js.map +1 -0
  63. package/dist/events/schema.d.ts +9 -0
  64. package/dist/events/schema.js +93 -0
  65. package/dist/events/schema.js.map +1 -0
  66. package/dist/events/snapshot.d.ts +32 -0
  67. package/dist/events/snapshot.js +57 -0
  68. package/dist/events/snapshot.js.map +1 -0
  69. package/dist/events/stream-key.d.ts +2 -0
  70. package/dist/events/stream-key.js +31 -0
  71. package/dist/events/stream-key.js.map +1 -0
  72. package/dist/events/types.d.ts +179 -0
  73. package/dist/events/types.js +66 -0
  74. package/dist/events/types.js.map +1 -0
  75. package/dist/health/readiness.d.ts +99 -0
  76. package/dist/health/readiness.js +202 -0
  77. package/dist/health/readiness.js.map +1 -0
  78. package/dist/index.d.ts +10 -0
  79. package/dist/index.js +10 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/integrations/filters.d.ts +8 -0
  82. package/dist/integrations/filters.js +40 -0
  83. package/dist/integrations/filters.js.map +1 -0
  84. package/dist/integrations/map.d.ts +26 -0
  85. package/dist/integrations/map.js +43 -0
  86. package/dist/integrations/map.js.map +1 -0
  87. package/dist/integrations/profile-key.d.ts +19 -0
  88. package/dist/integrations/profile-key.js +35 -0
  89. package/dist/integrations/profile-key.js.map +1 -0
  90. package/dist/main.d.ts +2 -0
  91. package/dist/main.js +5 -0
  92. package/dist/main.js.map +1 -0
  93. package/dist/orchestrator/client.d.ts +62 -0
  94. package/dist/orchestrator/client.js +160 -0
  95. package/dist/orchestrator/client.js.map +1 -0
  96. package/dist/playwright/ensure-chromium.d.ts +26 -0
  97. package/dist/playwright/ensure-chromium.js +60 -0
  98. package/dist/playwright/ensure-chromium.js.map +1 -0
  99. package/dist/preview/capture-screenshot.d.ts +52 -0
  100. package/dist/preview/capture-screenshot.js +158 -0
  101. package/dist/preview/capture-screenshot.js.map +1 -0
  102. package/dist/preview/viewer-url.d.ts +82 -0
  103. package/dist/preview/viewer-url.js +128 -0
  104. package/dist/preview/viewer-url.js.map +1 -0
  105. package/dist/process/fault-barrier.d.ts +55 -0
  106. package/dist/process/fault-barrier.js +113 -0
  107. package/dist/process/fault-barrier.js.map +1 -0
  108. package/dist/resume/memory-progress-store.d.ts +39 -0
  109. package/dist/resume/memory-progress-store.js +82 -0
  110. package/dist/resume/memory-progress-store.js.map +1 -0
  111. package/dist/resume/memory-recent-app-store.d.ts +14 -0
  112. package/dist/resume/memory-recent-app-store.js +27 -0
  113. package/dist/resume/memory-recent-app-store.js.map +1 -0
  114. package/dist/resume/memory-turn-store.d.ts +18 -0
  115. package/dist/resume/memory-turn-store.js +73 -0
  116. package/dist/resume/memory-turn-store.js.map +1 -0
  117. package/dist/resume/progress-key.d.ts +21 -0
  118. package/dist/resume/progress-key.js +58 -0
  119. package/dist/resume/progress-key.js.map +1 -0
  120. package/dist/resume/stores.d.ts +14 -0
  121. package/dist/resume/stores.js +18 -0
  122. package/dist/resume/stores.js.map +1 -0
  123. package/dist/resume/types.d.ts +124 -0
  124. package/dist/resume/types.js +13 -0
  125. package/dist/resume/types.js.map +1 -0
  126. package/dist/sabs/agent-facing-text.d.ts +33 -0
  127. package/dist/sabs/agent-facing-text.js +125 -0
  128. package/dist/sabs/agent-facing-text.js.map +1 -0
  129. package/dist/sabs/browser-context.d.ts +53 -0
  130. package/dist/sabs/browser-context.js +91 -0
  131. package/dist/sabs/browser-context.js.map +1 -0
  132. package/dist/sabs/editor-client-methods.d.ts +147 -0
  133. package/dist/sabs/editor-client-methods.js +715 -0
  134. package/dist/sabs/editor-client-methods.js.map +1 -0
  135. package/dist/sabs/recent-tool-runs.d.ts +14 -0
  136. package/dist/sabs/recent-tool-runs.js +61 -0
  137. package/dist/sabs/recent-tool-runs.js.map +1 -0
  138. package/dist/sabs/session-peer.d.ts +351 -0
  139. package/dist/sabs/session-peer.js +2 -0
  140. package/dist/sabs/session-peer.js.map +1 -0
  141. package/dist/sabs/turn-collector.d.ts +152 -0
  142. package/dist/sabs/turn-collector.js +573 -0
  143. package/dist/sabs/turn-collector.js.map +1 -0
  144. package/dist/sabs/websocket-session-peer.d.ts +514 -0
  145. package/dist/sabs/websocket-session-peer.js +2487 -0
  146. package/dist/sabs/websocket-session-peer.js.map +1 -0
  147. package/dist/security/constant-time.d.ts +9 -0
  148. package/dist/security/constant-time.js +13 -0
  149. package/dist/security/constant-time.js.map +1 -0
  150. package/dist/server/client.d.ts +371 -0
  151. package/dist/server/client.js +572 -0
  152. package/dist/server/client.js.map +1 -0
  153. package/dist/server/identity-reads.d.ts +76 -0
  154. package/dist/server/identity-reads.js +209 -0
  155. package/dist/server/identity-reads.js.map +1 -0
  156. package/dist/start.d.ts +14 -0
  157. package/dist/start.js +75 -0
  158. package/dist/start.js.map +1 -0
  159. package/dist/telemetry/metrics.d.ts +32 -0
  160. package/dist/telemetry/metrics.js +57 -0
  161. package/dist/telemetry/metrics.js.map +1 -0
  162. package/dist/transports/mcp/admin-tools.d.ts +47 -0
  163. package/dist/transports/mcp/admin-tools.js +164 -0
  164. package/dist/transports/mcp/admin-tools.js.map +1 -0
  165. package/dist/transports/mcp/app-status-html.d.ts +15 -0
  166. package/dist/transports/mcp/app-status-html.js +563 -0
  167. package/dist/transports/mcp/app-status-html.js.map +1 -0
  168. package/dist/transports/mcp/decision-elicitation.d.ts +74 -0
  169. package/dist/transports/mcp/decision-elicitation.js +233 -0
  170. package/dist/transports/mcp/decision-elicitation.js.map +1 -0
  171. package/dist/transports/mcp/dispatch-probe.d.ts +24 -0
  172. package/dist/transports/mcp/dispatch-probe.js +48 -0
  173. package/dist/transports/mcp/dispatch-probe.js.map +1 -0
  174. package/dist/transports/mcp/format-tool-content.d.ts +25 -0
  175. package/dist/transports/mcp/format-tool-content.js +48 -0
  176. package/dist/transports/mcp/format-tool-content.js.map +1 -0
  177. package/dist/transports/mcp/mount.d.ts +9 -0
  178. package/dist/transports/mcp/mount.js +469 -0
  179. package/dist/transports/mcp/mount.js.map +1 -0
  180. package/dist/transports/mcp/progress-notifier.d.ts +39 -0
  181. package/dist/transports/mcp/progress-notifier.js +52 -0
  182. package/dist/transports/mcp/progress-notifier.js.map +1 -0
  183. package/package.json +72 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Page source that makes a top-level pitcherURL visit look like the Code Mode
3
+ * iframe: mock `window.parent`, answer `sb-ready` / bootstrap / API auth in
4
+ * page JS so the host driver never sees the scoped JWT.
5
+ *
6
+ * Source text rather than a function handed to `addInitScript` because
7
+ * Playwright serializes functions and can drag bundler helpers (`__name`)
8
+ * into the page. Same rule as `buildEmbedHandshakeScript`.
9
+ */
10
+ export function buildLibraryParentScript(payload) {
11
+ return `(function () {
12
+ if (window.parent !== window) return;
13
+ var payload = ${JSON.stringify(payload)};
14
+ var init = payload.initData;
15
+ var bootstrap = payload.bootstrapData;
16
+ window.__sbGatewayCaptureEvents = [];
17
+ function record(message) {
18
+ if (message && typeof message.type === "string") {
19
+ window.__sbGatewayCaptureEvents.push({ type: message.type });
20
+ }
21
+ }
22
+ function send(message) {
23
+ window.postMessage(message, window.location.origin);
24
+ }
25
+ var mockParent = {
26
+ postMessage: function (message) {
27
+ record(message);
28
+ if (message && message.type === "sb-ready" && init) {
29
+ send({
30
+ payload: {
31
+ appId: init.appId,
32
+ devServerAuthorization: init.devServerAuthorization,
33
+ peerId: init.peerId,
34
+ userId: init.userId,
35
+ windowOriginUrl: init.windowOriginUrl,
36
+ },
37
+ startTime: Date.now(),
38
+ type: "sb-init",
39
+ });
40
+ }
41
+ if (message && message.type === "sb-editor-request-bootstrap" && bootstrap) {
42
+ send({
43
+ payload: bootstrap,
44
+ startTime: Date.now(),
45
+ type: "sb-bootstrap-response",
46
+ });
47
+ if (bootstrap.profiles) {
48
+ send({
49
+ payload: { global: { profiles: bootstrap.profiles } },
50
+ startTime: Date.now(),
51
+ type: "sb-global-sync",
52
+ });
53
+ }
54
+ }
55
+ if (message && message.type === "authenticate-api-request") {
56
+ var callbackId = message.payload && message.payload.callbackId;
57
+ if (callbackId) {
58
+ send({
59
+ callbackId: callbackId,
60
+ payload: {
61
+ tokens: {
62
+ accessToken: bootstrap.accessToken,
63
+ token: bootstrap.token,
64
+ },
65
+ },
66
+ type: "resolve-promise",
67
+ });
68
+ }
69
+ }
70
+ },
71
+ document: document,
72
+ location: window.location,
73
+ window: window,
74
+ };
75
+ Object.defineProperty(window, "parent", {
76
+ configurable: true,
77
+ value: mockParent,
78
+ writable: false,
79
+ });
80
+ })();`;
81
+ }
82
+ //# sourceMappingURL=library-parent-script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"library-parent-script.js","sourceRoot":"","sources":["../../src/capture/library-parent-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6BH,MAAM,UAAU,wBAAwB,CACtC,OAA6B;IAE7B,OAAO;;oBAEW,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAmErC,CAAC;AACP,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * How this Gateway can (or cannot) capture a live canvas or complete IdP.
3
+ *
4
+ * `host_mcp_configured` is a setup-time scan of the MCP client config. That
5
+ * file can lie: the listed server may fail to start or miss PATH. Tool results
6
+ * therefore keep hostBrowserTools as "unknown" rather than claiming this turn
7
+ * has Playwright.
8
+ */
9
+ export declare const CAPTURE_MODES: readonly ["gateway_chromium", "host_mcp_configured", "urls_only"];
10
+ export type CaptureMode = (typeof CAPTURE_MODES)[number];
11
+ export declare function resolveCaptureMode(input: {
12
+ gatewayChromium: boolean;
13
+ hostMcpConfigured: boolean;
14
+ }): CaptureMode;
15
+ /**
16
+ * Human + machine summary for `gateway setup` stderr. Info goes to stderr so
17
+ * a redirected stdout report stays clean.
18
+ */
19
+ export declare function formatCaptureModeSummary(input: {
20
+ isolated: boolean;
21
+ mode: CaptureMode;
22
+ }): string;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * How this Gateway can (or cannot) capture a live canvas or complete IdP.
3
+ *
4
+ * `host_mcp_configured` is a setup-time scan of the MCP client config. That
5
+ * file can lie: the listed server may fail to start or miss PATH. Tool results
6
+ * therefore keep hostBrowserTools as "unknown" rather than claiming this turn
7
+ * has Playwright.
8
+ */
9
+ export const CAPTURE_MODES = [
10
+ "gateway_chromium",
11
+ "host_mcp_configured",
12
+ "urls_only",
13
+ ];
14
+ export function resolveCaptureMode(input) {
15
+ if (input.hostMcpConfigured) {
16
+ return "host_mcp_configured";
17
+ }
18
+ if (input.gatewayChromium) {
19
+ return "gateway_chromium";
20
+ }
21
+ return "urls_only";
22
+ }
23
+ /**
24
+ * Human + machine summary for `gateway setup` stderr. Info goes to stderr so
25
+ * a redirected stdout report stays clean.
26
+ */
27
+ export function formatCaptureModeSummary(input) {
28
+ const lines = [`capture.mode: ${input.mode}`];
29
+ switch (input.mode) {
30
+ case "host_mcp_configured":
31
+ lines.push("A host browser MCP is listed in this client config. Config on disk can lie if that server fails to start or misses PATH.");
32
+ break;
33
+ case "gateway_chromium":
34
+ lines.push("Gateway Playwright Chromium is present for preview screenshots. No host browser MCP was found. Config on disk can lie if a listed server fails to start. IdP step-up still returns needs_browser_driver until a host browser MCP (@playwright/mcp) is configured. Gateway Chromium cannot complete IdP.");
35
+ break;
36
+ case "urls_only":
37
+ lines.push("URLs only: no host browser MCP and no Gateway Chromium. Config on disk can lie if a listed server fails to start. Live-canvas capture will return needs_browser_driver; superblocks gateway setup --with-screenshots may help live-canvas capture and does not clear IdP. Add @playwright/mcp for IdP step-up.");
38
+ break;
39
+ }
40
+ if (input.isolated) {
41
+ lines.push("Warning: a scanned browser MCP includes --isolated. IdP cookies will not survive browser_close.");
42
+ }
43
+ return lines.join("\n");
44
+ }
45
+ //# sourceMappingURL=mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.js","sourceRoot":"","sources":["../../src/capture/mode.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,kBAAkB;IAClB,qBAAqB;IACrB,WAAW;CACH,CAAC;AAIX,MAAM,UAAU,kBAAkB,CAAC,KAGlC;IACC,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC5B,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IACD,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC1B,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAGxC;IACC,MAAM,KAAK,GAAG,CAAC,iBAAiB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,qBAAqB;YACxB,KAAK,CAAC,IAAI,CACR,0HAA0H,CAC3H,CAAC;YACF,MAAM;QACR,KAAK,kBAAkB;YACrB,KAAK,CAAC,IAAI,CACR,ySAAyS,CAC1S,CAAC;YACF,MAAM;QACR,KAAK,WAAW;YACd,KAAK,CAAC,IAAI,CACR,gTAAgT,CACjT,CAAC;YACF,MAAM;IACV,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CACR,iGAAiG,CAClG,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,141 @@
1
+ export type GatewayConfig = {
2
+ deploymentEnvironmentName: string;
3
+ /** Cloud-Prem laptop agent: single-user process on a corp machine. */
4
+ localAgentMode: boolean;
5
+ /**
6
+ * Cloud-Prem Admin laptop agent: register Admin tools (integrations, apps,
7
+ * users, deploy_application, ...) only, and skip the Builder surface
8
+ * (start_app, edit_app, ...) that needs Playwright Chromium for preview
9
+ * screenshots and a live-edit session it never opens on this machine.
10
+ */
11
+ adminToolsOnly: boolean;
12
+ /**
13
+ * Where `import_app` looks for an archive it was handed the name of.
14
+ *
15
+ * A file attached to a chat lives in the model's own sandbox, so the path the
16
+ * model can quote is unreadable here even though the user downloaded the file
17
+ * to this machine. Only the file name is taken from that path, and only these
18
+ * directories are searched, so a caller cannot walk the filesystem with it.
19
+ */
20
+ importSearchDirs: string[];
21
+ /**
22
+ * How long the held socket's liveness beat must stay lost before the turn
23
+ * running on it is given up on. Exposed because this is the one knob that can
24
+ * end a turn the sandbox might still have finished: raising it far past the
25
+ * turn budget disables the behavior without a deploy.
26
+ */
27
+ beatLostGraceMs: number;
28
+ /**
29
+ * How long to hold an editor websocket after the last prompt before
30
+ * releasing it. Headless MCP needs the socket kept open so the sandbox
31
+ * startup watchdog does not tear the session down.
32
+ */
33
+ heldSessionIdleMs: number;
34
+ /**
35
+ * Grace period between a Clark turn settling and the held editor socket
36
+ * being dropped. Keeping it short leaves the user's own editor tab as the
37
+ * sandbox's only editor peer, which is the peer Clark's API calls run on.
38
+ */
39
+ heldSessionReleaseAfterTurnMs: number;
40
+ /**
41
+ * How long `start_app` / `edit_app` wait for Clark's generation turn after
42
+ * the prompt is acknowledged.
43
+ */
44
+ clarkTurnTimeoutMs: number;
45
+ /**
46
+ * Quiet window after Clark emits completed/idle before treating the job as
47
+ * done, so an acknowledgment that resumes generating is coalesced.
48
+ */
49
+ clarkSettleMs: number;
50
+ /**
51
+ * After Clark's job settles, how long to wait for the dev server to flush
52
+ * Clark's files before giving up on checkpointing them.
53
+ */
54
+ clarkReadyWaitMs: number;
55
+ /**
56
+ * Opt-in: hands Clark a browser context so its Playwright test tools are
57
+ * registered on headless turns. Off by default because that launches
58
+ * Chromium inside the live-edit pod (3Gi), which OOMKills generation.
59
+ * Gateway preview screenshots use laptop Chromium and ignore this flag.
60
+ * Set GATEWAY_CLARK_TESTING_ENABLED=true only when the sandbox has
61
+ * Chromium headroom and you want Clark to drive browser_* tools.
62
+ */
63
+ clarkTestingEnabled: boolean;
64
+ /**
65
+ * How long a single `check_app_progress` call waits for new Clark activity
66
+ * before answering with what it has. Sized to stay inside a client's tool
67
+ * timeout so the caller can poll again rather than lose the turn.
68
+ */
69
+ clarkProgressWaitMs: number;
70
+ /**
71
+ * How long to wait between `check_app_progress` polls once a plan is
72
+ * approved and the app is being built. Longer than
73
+ * {@link GatewayConfig.clarkProgressWaitMs} so a multi-minute build does not
74
+ * burn through a host's tool-call budget; the plan phase still uses the
75
+ * shorter progress wait so a decision surfaces quickly.
76
+ */
77
+ clarkBuildPollAfterMs: number;
78
+ /**
79
+ * How long a build may run without saying anything before a poll warns that
80
+ * it has gone quiet. Nonterminal and well under
81
+ * {@link GatewayConfig.clarkTurnTimeoutMs}, so the caller hears about the
82
+ * silence long before the turn is given up on.
83
+ */
84
+ clarkStallHintAfterMs: number;
85
+ /**
86
+ * Ceiling on any single host-facing call, whatever it is waiting for.
87
+ *
88
+ * Hosts abandon a tool call after their own timeout, and a host that
89
+ * advertises progress notifications is under no obligation to act on them —
90
+ * so a call cannot be allowed to run past this on the strength of having
91
+ * sent progress. Anything still unfinished here returns pacing and a cursor
92
+ * instead of holding the request open.
93
+ */
94
+ hostCallCeilingMs: number;
95
+ /**
96
+ * How long `publish_app` polls a queued deploy before giving up.
97
+ */
98
+ publishWaitTimeoutMs: number;
99
+ /**
100
+ * Interval between deploy-status polls while waiting for a queued publish.
101
+ */
102
+ publishPollIntervalMs: number;
103
+ /**
104
+ * Preferred integration profile key. When unset or `default`, Gateway
105
+ * picks a real org profile (staging → dev → production → first).
106
+ */
107
+ profileKey: string;
108
+ serverUrl: string;
109
+ turnTtlMs: number;
110
+ uiBaseUrl: string;
111
+ /**
112
+ * Base URL of the deployed-app shell / CDN used for JWT preview links.
113
+ * Local default is the csb-mock CDN. May include `__SB_APP_ID`.
114
+ */
115
+ deployedShellBaseUrl: string | undefined;
116
+ /**
117
+ * Optional https front door for the deployed shell (viewer/CDN tunnel).
118
+ * When set, Claude Desktop can openLink preview URLs. Stdio Gateway does
119
+ * not bind HTTP, so this is never a gateway origin.
120
+ */
121
+ publicViewerBaseUrl: string | undefined;
122
+ /**
123
+ * When true, always attempt a headless screenshot even if previewUrl is
124
+ * already https (demo / debugging).
125
+ */
126
+ forcePreviewScreenshot: boolean;
127
+ /**
128
+ * Setup-time scan: a host browser MCP (@playwright/mcp, chrome-devtools,
129
+ * puppeteer, agent-browser) was listed in the client config. Config on disk
130
+ * can lie; tool results keep hostBrowserTools as "unknown".
131
+ */
132
+ captureHostMcp: boolean;
133
+ /** Scanned host browser MCP args included --isolated (IdP cookies die). */
134
+ captureHostIsolated: boolean;
135
+ /**
136
+ * Test override for whether Playwright Chromium is on disk. Unset in
137
+ * production: capabilities probe without installing.
138
+ */
139
+ gatewayChromiumPresent?: boolean;
140
+ };
141
+ export declare function loadConfig(env?: NodeJS.ProcessEnv): GatewayConfig;
package/dist/config.js ADDED
@@ -0,0 +1,109 @@
1
+ import { homedir } from "node:os";
2
+ import path from "node:path";
3
+ import { DEFAULT_TURN_TTL_MS } from "./resume/types.js";
4
+ /**
5
+ * Reads an env var, treating blank as absent.
6
+ *
7
+ * Every optional var the Tilt profile passes is written as
8
+ * `os.getenv(name, '')`, and `FOO= cmd` sets an empty string too. Without this,
9
+ * `??` happily accepts `""` and it reaches config as a real value — a zero
10
+ * turn TTL, an empty UI URL that then fails URL construction.
11
+ */
12
+ function readEnv(env, name) {
13
+ return env[name]?.trim() || undefined;
14
+ }
15
+ function required(env, name, fallback) {
16
+ const value = readEnv(env, name) ?? fallback;
17
+ if (!value) {
18
+ throw new Error(`Missing required env var: ${name}`);
19
+ }
20
+ return value;
21
+ }
22
+ function explicitEnvironmentName(env) {
23
+ return (readEnv(env, "DEPLOYMENT_ENVIRONMENT_NAME") ?? readEnv(env, "NODE_ENV"));
24
+ }
25
+ /**
26
+ * Where a file the user just got hold of actually sits: the browser's download
27
+ * folder, the desktop, or a temp directory, which is where Claude's own
28
+ * downloads land. `/tmp` and `/private/tmp` cover the macOS `/tmp` symlink both
29
+ * ways; `/private/var/folders` covers Claude's realpath'd temp dirs without
30
+ * opening the rest of `/private`. Anything outside these is refused, so an
31
+ * operator who needs more sets the env var.
32
+ */
33
+ function resolveImportSearchDirs(env) {
34
+ const configured = readEnv(env, "GATEWAY_IMPORT_SEARCH_DIRS");
35
+ if (configured) {
36
+ return configured
37
+ .split(",")
38
+ .map((dir) => dir.trim())
39
+ .filter(Boolean);
40
+ }
41
+ const home = homedir();
42
+ return [
43
+ path.join(home, "Downloads"),
44
+ path.join(home, "Desktop"),
45
+ "/tmp",
46
+ "/private/tmp",
47
+ "/private/var/folders",
48
+ ];
49
+ }
50
+ function isTruthyFlag(value) {
51
+ if (!value) {
52
+ return false;
53
+ }
54
+ const normalized = value.toLowerCase();
55
+ return normalized === "1" || normalized === "true" || normalized === "yes";
56
+ }
57
+ export function loadConfig(env = process.env) {
58
+ const explicitEnvironment = explicitEnvironmentName(env);
59
+ const localAgentMode = isTruthyFlag(readEnv(env, "GATEWAY_LOCAL_AGENT"));
60
+ const adminToolsOnly = isTruthyFlag(readEnv(env, "GATEWAY_ADMIN_TOOLS_ONLY"));
61
+ // Never default the label to "local". A deployment that forgot to set the
62
+ // variable is not local, and anything reading the label would otherwise
63
+ // relax itself on the strength of a default.
64
+ const deploymentEnvironmentName = explicitEnvironment ?? "unknown";
65
+ // One origin standing in for both the server and the UI, for environments
66
+ // that serve them together - an ee deployment, a shared dev box. The local
67
+ // stack splits them across ports and sets each explicitly, and those still
68
+ // win: this is only the fallback beneath them.
69
+ const baseUrl = readEnv(env, "GATEWAY_BASE_URL");
70
+ return {
71
+ deploymentEnvironmentName,
72
+ localAgentMode,
73
+ adminToolsOnly,
74
+ importSearchDirs: resolveImportSearchDirs(env),
75
+ beatLostGraceMs: Number(readEnv(env, "GATEWAY_BEAT_LOST_GRACE_MS") ?? String(60_000)),
76
+ heldSessionIdleMs: Number(readEnv(env, "GATEWAY_HELD_SESSION_IDLE_MS") ?? String(30 * 60 * 1000)),
77
+ heldSessionReleaseAfterTurnMs: Number(readEnv(env, "GATEWAY_HELD_SESSION_RELEASE_AFTER_TURN_MS") ??
78
+ String(30_000)),
79
+ clarkTurnTimeoutMs: Number(readEnv(env, "GATEWAY_CLARK_TURN_TIMEOUT_MS") ?? String(10 * 60 * 1000)),
80
+ clarkSettleMs: Number(readEnv(env, "GATEWAY_CLARK_SETTLE_MS") ?? String(12_000)),
81
+ clarkReadyWaitMs: Number(readEnv(env, "GATEWAY_CLARK_READY_WAIT_MS") ?? String(30_000)),
82
+ clarkTestingEnabled: readEnv(env, "GATEWAY_CLARK_TESTING_ENABLED") === "true" ||
83
+ readEnv(env, "GATEWAY_CLARK_TESTING_ENABLED") === "1",
84
+ // Ceiling on one progress poll, not a floor: a poll returns the moment
85
+ // Clark says anything. Keep this around 10s so the plan phase stays
86
+ // responsive; the post-approve build cadence is clarkBuildPollAfterMs.
87
+ clarkProgressWaitMs: Number(readEnv(env, "GATEWAY_CLARK_PROGRESS_WAIT_MS") ?? String(10_000)),
88
+ // Suggested gap between polls once a plan is approved. ~30s keeps a
89
+ // 2-4 minute build inside Claude Desktop's tool-call budget.
90
+ clarkBuildPollAfterMs: Number(readEnv(env, "GATEWAY_CLARK_BUILD_POLL_AFTER_MS") ?? String(30_000)),
91
+ clarkStallHintAfterMs: Number(readEnv(env, "GATEWAY_CLARK_STALL_HINT_AFTER_MS") ?? String(120_000)),
92
+ // Under the 60s most MCP hosts allow a tool call, with room for the
93
+ // response to be written and read.
94
+ hostCallCeilingMs: Number(readEnv(env, "GATEWAY_HOST_CALL_CEILING_MS") ?? String(50_000)),
95
+ publishWaitTimeoutMs: Number(readEnv(env, "GATEWAY_PUBLISH_WAIT_TIMEOUT_MS") ?? String(2 * 60 * 1000)),
96
+ publishPollIntervalMs: Number(readEnv(env, "GATEWAY_PUBLISH_POLL_INTERVAL_MS") ?? "2000"),
97
+ profileKey: required(env, "GATEWAY_PROFILE_KEY", "default"),
98
+ serverUrl: required(env, "SUPERBLOCKS_SERVER_URL", readEnv(env, "GATEWAY_SERVER_URL") ?? baseUrl ?? "http://localhost:8080"),
99
+ turnTtlMs: Number(readEnv(env, "GATEWAY_TURN_TTL_MS") ?? DEFAULT_TURN_TTL_MS),
100
+ uiBaseUrl: required(env, "GATEWAY_UI_BASE_URL", baseUrl ?? "http://localhost:3000"),
101
+ deployedShellBaseUrl: readEnv(env, "GATEWAY_DEPLOYED_SHELL_BASE_URL"),
102
+ publicViewerBaseUrl: readEnv(env, "GATEWAY_PUBLIC_VIEWER_BASE_URL"),
103
+ forcePreviewScreenshot: readEnv(env, "GATEWAY_FORCE_PREVIEW_SCREENSHOT") === "true" ||
104
+ readEnv(env, "GATEWAY_FORCE_PREVIEW_SCREENSHOT") === "1",
105
+ captureHostMcp: isTruthyFlag(readEnv(env, "GATEWAY_CAPTURE_HOST_MCP")),
106
+ captureHostIsolated: isTruthyFlag(readEnv(env, "GATEWAY_CAPTURE_HOST_ISOLATED")),
107
+ };
108
+ }
109
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const gatewayDebug: import("util").DebugLogger;
2
+ export declare function gatewayDebugStack(message: string, attributes?: Record<string, unknown>): void;
package/dist/debug.js ADDED
@@ -0,0 +1,9 @@
1
+ import { debuglog } from "node:util";
2
+ export const gatewayDebug = debuglog("gateway");
3
+ export function gatewayDebugStack(message, attributes = {}) {
4
+ if (!gatewayDebug.enabled) {
5
+ return;
6
+ }
7
+ gatewayDebug(message, { ...attributes, stack: new Error().stack });
8
+ }
9
+ //# sourceMappingURL=debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;AAEhD,MAAM,UAAU,iBAAiB,CAC/B,OAAe,EACf,aAAsC,EAAE;IAExC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IACD,YAAY,CAAC,OAAO,EAAE,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,IAAI,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;AACrE,CAAC"}
package/dist/deps.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ import type { GatewayConfig } from "./config.js";
2
+ import type { EventStore } from "./events/types.js";
3
+ import type { ProgressStore, RecentAppStore, TurnStore } from "./resume/types.js";
4
+ import type { SessionPeer } from "./sabs/session-peer.js";
5
+ import type { SuperblocksServerClient } from "./server/client.js";
6
+ /**
7
+ * Runtime collaborators for one Gateway process. Stdio MCP is the only
8
+ * production transport; tests build the same bag and connect an in-memory
9
+ * client.
10
+ */
11
+ export type AppDeps = {
12
+ config: GatewayConfig;
13
+ /**
14
+ * Personal API key of the already-logged-in Superblocks CLI user.
15
+ * Gateway tools call the control plane as this identity.
16
+ */
17
+ cliApiKey: string;
18
+ /**
19
+ * Superblocks user already resolved for this CLI session. When present,
20
+ * capabilities skip `/users/me` (ENG-5806). When absent, the first tool
21
+ * call resolves it through the guarded identity read.
22
+ */
23
+ cliIdentity?: {
24
+ email?: string;
25
+ organizationId: string;
26
+ userId: string;
27
+ };
28
+ /** Durable, bounded history of what happened in each live-edit session. */
29
+ events: EventStore;
30
+ /** Each caller's place in a build's event stream, kept between calls. */
31
+ progressCursors: ProgressStore;
32
+ recentApps: RecentAppStore;
33
+ server: SuperblocksServerClient;
34
+ sessionPeer: SessionPeer;
35
+ turns: TurnStore;
36
+ };
package/dist/deps.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.js","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
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;
@@ -0,0 +1,78 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,34 @@
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
+ }