@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,514 @@
1
+ import type { RecentToolRun } from "../capabilities/types.js";
2
+ import { type OrchestratorClient } from "../orchestrator/client.js";
3
+ import type { SuperblocksServerClient } from "../server/client.js";
4
+ import type { HoldLiveEditInput, TurnState, ForwardPromptResult, PromptInput, SessionPeer, StartPromptResult, TurnDecision, TurnUpdate } from "./session-peer.js";
5
+ /**
6
+ * Consecutive upgrade rejections from one host before asking where the live
7
+ * edit went. More than one, so an ingress route that briefly answers for a pod
8
+ * that is not up yet is waited out rather than read as a move.
9
+ *
10
+ * Exported so a test can say "kept trying past the point where it asked"
11
+ * without restating the number.
12
+ */
13
+ export declare const HOST_REJECTIONS_BEFORE_RERESOLVE = 2;
14
+ /**
15
+ * How many times one connect may ask where the session went.
16
+ *
17
+ * Bounded because each ask is a Superblocks API round trip, and spaced by a
18
+ * fresh rejection streak so the asks land across the connect window rather
19
+ * than all at the start of it - SABS needs time to notice the pod is gone
20
+ * before it can answer with anywhere different.
21
+ */
22
+ export declare const MAX_RERESOLVES_PER_OPEN = 3;
23
+ /**
24
+ * Grace period between a turn settling and the gateway socket being dropped.
25
+ *
26
+ * Long enough for the checkpoint and any trailing pushes to land, short enough
27
+ * that the sandbox is not held open by a peer with nothing left to do.
28
+ *
29
+ * Must outlast {@link DEFAULT_SETTLE_MS}. The teardown this release leads to is
30
+ * read as a dead live edit by any turn whose `completed` had not settled yet,
31
+ * so releasing inside the settle window would report healthy builds as
32
+ * failures. `releases the socket only after a completed has had its settle
33
+ * window` holds this.
34
+ */
35
+ export declare const DEFAULT_RELEASE_AFTER_TURN_MS = 30000;
36
+ export type WebSocketSessionPeerOptions = {
37
+ /**
38
+ * How long a lost liveness beat must stay lost before the turn on that socket
39
+ * is given up on. See {@link DEFAULT_BEAT_LOST_GRACE_MS}.
40
+ */
41
+ beatLostGraceMs?: number;
42
+ connectAttemptTimeoutMs?: number;
43
+ coreModel?: string;
44
+ /**
45
+ * How long a turn that died before Superblocks saw it stays worth reporting.
46
+ * See {@link FailedStarts}; injectable so its bounds are testable without
47
+ * waiting them out.
48
+ */
49
+ failedStartTtlMs?: number;
50
+ /** Distinct apps holding a dead start at once. See {@link FailedStarts}. */
51
+ maxFailedStarts?: number;
52
+ /** Consecutive genuine misses before the beat is declared lost. */
53
+ heartbeatMaxMisses?: number;
54
+ /**
55
+ * Consecutive unanswered beats to tolerate before neither proof of life nor
56
+ * grace may excuse another.
57
+ */
58
+ heartbeatMaxUnanswered?: number;
59
+ /** How often the held socket proves the gateway is still alive. */
60
+ heartbeatMs?: number;
61
+ /** How long a single liveness beat waits before it counts as missed. */
62
+ heartbeatTimeoutMs?: number;
63
+ idleMs?: number;
64
+ keepaliveMs?: number;
65
+ /**
66
+ * Optional test override for the orchestrator HTTP client. Production
67
+ * discovers the agent URL from Server `/users/me` agents per session.
68
+ */
69
+ orchestrator?: OrchestratorClient;
70
+ /**
71
+ * Optional test override for the agent base URL handed to the sandbox
72
+ * browser as `agentUrls`. Production discovers this from Server agents.
73
+ */
74
+ orchestratorUrl?: string;
75
+ /**
76
+ * Integration profile key used when asking the orchestrator for metadata or
77
+ * executing APIs. Defaults to `default`.
78
+ */
79
+ profileKey?: string;
80
+ provider?: string;
81
+ pushWaitMs?: number;
82
+ readyWaitMs?: number;
83
+ /** Grace period between a turn settling and the gateway socket being dropped. */
84
+ releaseAfterTurnMs?: number;
85
+ reattachWaitMs?: number;
86
+ /** How many times one turn goes back for a live edit that keeps dropping it. */
87
+ reconnectAttempts?: number;
88
+ /** How long a turn keeps trying to get back onto its live edit. */
89
+ reconnectGraceMs?: number;
90
+ /**
91
+ * Superblocks API client used to list integrations for Clark mid-build.
92
+ */
93
+ server?: SuperblocksServerClient;
94
+ settleMs?: number;
95
+ /**
96
+ * Opt-in: lets Clark run browser tests during a headless turn. Requires
97
+ * `orchestratorUrl` and `uiBaseUrl`. Off by default so Gateway turns do
98
+ * not launch Chromium inside the live-edit pod.
99
+ */
100
+ testingEnabled?: boolean;
101
+ timeoutMs?: number;
102
+ turnTimeoutMs?: number;
103
+ /** Origin the sandbox app shell should report as its parent window. */
104
+ uiBaseUrl?: string;
105
+ };
106
+ export declare function buildSessionUrl(input: {
107
+ applicationId: string;
108
+ liveEditUrl: string;
109
+ peerId: string;
110
+ userId?: string;
111
+ }): URL;
112
+ export declare class WebSocketSessionPeer implements SessionPeer {
113
+ private readonly options;
114
+ private readonly held;
115
+ /**
116
+ * Every server -> editor method the dev server has called on this peer.
117
+ * Diagnostic only: it answers "did that push ever arrive" without guessing
118
+ * from what the collector chose to keep.
119
+ */
120
+ private readonly inboundMethods;
121
+ /**
122
+ * In-flight opens, so overlapping prompts for one app share a socket instead
123
+ * of each opening their own and orphaning all but the last.
124
+ */
125
+ private readonly connecting;
126
+ /**
127
+ * Turns keyed by application. An entry outlives the call that started it and
128
+ * is kept after finishing so a late poll still learns the outcome; the next
129
+ * prompt for the same app replaces it.
130
+ */
131
+ private readonly turns;
132
+ /**
133
+ * Last plan Clark proposed per application, so approve/refine can re-send
134
+ * planContext without the MCP client echoing the full markdown.
135
+ */
136
+ private readonly pendingPlans;
137
+ /**
138
+ * Last needs_decision payload (plan or multi_choice) per application, so
139
+ * approve/refine via edit_app can reuse the pending plan after
140
+ * check_app_progress drained the finished turn.
141
+ */
142
+ private readonly pendingDecisions;
143
+ /**
144
+ * Applications that have dispatched at least one BUILD turn. Survives
145
+ * clearing the pending plan, which happens as soon as an approved build
146
+ * starts — the point of the latch is the turns that come after it.
147
+ */
148
+ private readonly buildModeApplications;
149
+ /**
150
+ * Turns that died before Superblocks ever saw them, so no {@link ActiveTurn}
151
+ * was ever created to hold the outcome. Read by
152
+ * {@link WebSocketSessionPeer.turnState} and
153
+ * {@link WebSocketSessionPeer.awaitTurn} after the live map, and cleared when
154
+ * a prompt for the app is accepted.
155
+ */
156
+ private readonly failedStarts;
157
+ constructor(options?: WebSocketSessionPeerOptions);
158
+ /**
159
+ * Plan waiting on human approve/refine for this application, if any.
160
+ */
161
+ pendingPlan(applicationId: string): {
162
+ messageId?: string;
163
+ plan: string;
164
+ title?: string;
165
+ } | undefined;
166
+ /**
167
+ * Full needs_decision payload waiting on a human for this application.
168
+ */
169
+ pendingDecision(applicationId: string): {
170
+ decision: TurnDecision;
171
+ recentRuns?: RecentToolRun[];
172
+ reply: string;
173
+ } | undefined;
174
+ /**
175
+ * Remember a turn that never reached Superblocks. See
176
+ * {@link SessionPeer.recordFailedStart}.
177
+ */
178
+ recordFailedStart(input: {
179
+ applicationId: string;
180
+ organizationId: string;
181
+ reason: string;
182
+ userId: string;
183
+ }): void;
184
+ /**
185
+ * Where this application's turn stands for this caller, read without draining
186
+ * the activity an {@link WebSocketSessionPeer.awaitTurn} poll is owed.
187
+ *
188
+ * Order matters, and it is recency. A turn still *running* is live work and
189
+ * outranks everything - a second prompt failing to open its own session does
190
+ * not end it. Failing that, this caller's own dead start outranks a turn that
191
+ * already finished: `turns` keeps finished turns, so without this a follow-up
192
+ * whose connect failed would be answered by the previous turn's outcome and
193
+ * its reply, reporting an edit as landed that never ran. And it is read rather
194
+ * than superseded by deleting from `turns`, because that map is keyed by
195
+ * application while a dead start belongs to one caller - deleting would erase
196
+ * a colleague's outcome to answer this caller's question.
197
+ *
198
+ * Reading recency out of the order rather than out of a clock holds because
199
+ * of the two rules either side of this one: accepting a prompt clears every
200
+ * caller's record for the app, and no record is made while a turn is running.
201
+ * Together they leave every stored record newer than any finished turn it can
202
+ * be read beside, so "the record wins" is not a guess about which came first.
203
+ */
204
+ turnState(input: {
205
+ applicationId: string;
206
+ organizationId: string;
207
+ userId: string;
208
+ }): TurnState | undefined;
209
+ clearPendingPlan(applicationId: string): void;
210
+ clearPendingDecision(applicationId: string): void;
211
+ markBuildMode(applicationId: string): void;
212
+ buildMode(applicationId: string): boolean;
213
+ isHeld(applicationId: string): boolean;
214
+ libraryBrowserContext(applicationId: string): import("@superblocksteam/library-shared/types").BrowserContext | undefined;
215
+ holdLiveEdit(input: HoldLiveEditInput): Promise<void>;
216
+ release(applicationId: string): void;
217
+ closeAll(): void;
218
+ /**
219
+ * Sends the prompt and waits for Clark's whole turn.
220
+ *
221
+ * Kept for callers with nowhere to poll from — the HTTP capability routes
222
+ * answer a single request — and implemented on the same engine as
223
+ * {@link startPrompt} so there is one turn lifecycle, not two.
224
+ */
225
+ forwardPrompt(input: PromptInput): Promise<ForwardPromptResult>;
226
+ startPrompt(input: PromptInput): Promise<StartPromptResult>;
227
+ /**
228
+ * Sends the prompt on a session already holding a subscribed turn, and hands
229
+ * the turn to {@link runTurn} once the session confirms it took it on.
230
+ */
231
+ private sendPrompt;
232
+ awaitTurn(input: {
233
+ applicationId: string;
234
+ organizationId?: string;
235
+ signal?: AbortSignal;
236
+ userId?: string;
237
+ waitMs: number;
238
+ }): Promise<TurnUpdate>;
239
+ /**
240
+ * Follows the turn to its end on the held session: terminal state, then the
241
+ * checkpoint that commits Clark's draft. Never throws — a caller polling for
242
+ * news has to be told what happened, and the only place left to report a
243
+ * failure is the turn record itself.
244
+ */
245
+ private runTurn;
246
+ private pushWaitMs;
247
+ /**
248
+ * Attaches a turn to the events SABS relays for its live edit, returning the
249
+ * idempotent detach the turn calls when it ends.
250
+ *
251
+ * A turn with no live edit id, or a deployment with no push channel, gets a
252
+ * detach that does nothing: the socket remains its only source, which is the
253
+ * behavior from before the channel existed.
254
+ */
255
+ private subscribeToPushes;
256
+ /**
257
+ * Checks that the session took this prompt on as a turn belonging to this
258
+ * peer, and gives up on it now if it did not.
259
+ *
260
+ * A dev server that predates the gateway role accepts the socket, echoes the
261
+ * role it was offered, and then treats the connection as a library socket: the
262
+ * prompt is acknowledged and Clark's pushes go to the browser binding
263
+ * instead. The turn would spend its whole budget looking detached, and a
264
+ * rollout with one stale sandbox in it would look like Clark hanging. Only a
265
+ * server that bound the turn can name it back, so the missing handle is the
266
+ * mismatch, reported where it is still cheap to act on.
267
+ */
268
+ private assertHeadlessTurnBound;
269
+ /**
270
+ * The field the session reads to decide which turn a call belongs to. Sent on
271
+ * the calls that make up a turn's lifetime, so a turn keeps reporting into
272
+ * this caller across a reattach and no other session can be handed it.
273
+ */
274
+ private turnCorrelator;
275
+ /**
276
+ * Moves a turn onto a fresh socket while the one it is on is still open,
277
+ * because Clark has stopped pushing to it. A turn that was running unseen
278
+ * becomes visible again from where it got to.
279
+ *
280
+ * The socket it came in on is still the better of the two whenever the
281
+ * replacement fails to take the turn over: the sandbox is demonstrably alive,
282
+ * and it is still pushing this turn somewhere. A caller mid-turn has nothing
283
+ * to gain from an exception here, so there is no failure to report but the
284
+ * log line.
285
+ */
286
+ private reattach;
287
+ /**
288
+ * Whether this turn gets another go at its live edit, booking it if so.
289
+ *
290
+ * The allowance is for a live edit that cannot hold a socket, and a turn that
291
+ * has heard from Clark since its last reconnect is not that: it is getting
292
+ * somewhere, so its allowance starts over. Without that, a long build behind
293
+ * a proxy that reaps every few minutes runs out of goes and is failed for the
294
+ * very reason this path exists to survive.
295
+ */
296
+ private takeReconnectAllowance;
297
+ /**
298
+ * Opens a replacement connection and asks the live edit to hand `turn` over
299
+ * to it.
300
+ *
301
+ * Everything that belongs to the session rather than to the socket is carried
302
+ * across first: the credential the claim itself is made with is one of those
303
+ * things, and so is the orchestrator a mid-turn tool call needs. The turn's
304
+ * listener goes on the replacement before the claim, so a push that arrives
305
+ * while it is in flight is not dropped.
306
+ *
307
+ * The turn is claimed before the old socket is let go. The session releases a
308
+ * turn when the connection holding it closes, so closing first leaves the
309
+ * claim nothing to take over; claiming first makes the old socket's close a
310
+ * no-op scoped to a peer that is no longer the owner.
311
+ *
312
+ * Nothing has moved when this fails: the listener is back where it was and
313
+ * the replacement is closed, which leaves the caller free to decide what a
314
+ * socket it could not replace means.
315
+ */
316
+ private takeOverTurn;
317
+ private attemptTakeOver;
318
+ /**
319
+ * Carries onto a replacement socket everything that belongs to the session
320
+ * rather than to the connection: who the turn runs as, what it is allowed to
321
+ * do, and which orchestrator its tool calls go to. A replacement missing any
322
+ * of it is connected but cannot act for the caller.
323
+ */
324
+ private carryOver;
325
+ /**
326
+ * Makes one authenticated read on a freshly opened socket.
327
+ *
328
+ * The dev server trusts a peer's identity per connection and does not carry
329
+ * it over from the peer this one replaced, so it withholds the pushes it
330
+ * scopes to a caller — Clark's step-by-step task status — until the new
331
+ * connection has proven who it is. Reading the chat is the cheapest way to
332
+ * prove it, and it costs a turn nothing when the read fails.
333
+ *
334
+ * Naming `turnId` is what makes this call a reattach rather than just an
335
+ * authenticated read: the session only moves a turn onto a connection that
336
+ * can say which turn it is taking over.
337
+ *
338
+ * Answers whether it worked, because a caller mid-reattach has to decide
339
+ * between two sockets on it.
340
+ */
341
+ private proveIdentity;
342
+ private reconnect;
343
+ /** Lines this caller has not been handed yet. */
344
+ private drain;
345
+ private wake;
346
+ /** Adds a line to what the next poll on this turn is handed. */
347
+ private note;
348
+ /**
349
+ * Records the outcome of a turn from outside runTurn, and ends the collector
350
+ * waits so runTurn cannot overwrite it with a later timeout of its own. The
351
+ * first outcome written wins: it is the one callers have already been shown.
352
+ */
353
+ private settleTurn;
354
+ /**
355
+ * Mirrors the browser editor's auto-accept: wait for the dev server to flush
356
+ * Clark's files, then commit the draft. Failing to checkpoint costs the caller
357
+ * a committed app, not the generation itself, so this never throws.
358
+ */
359
+ private checkpoint;
360
+ private ensureHeld;
361
+ private connect;
362
+ /**
363
+ * The liveness beat for one held socket.
364
+ *
365
+ * Everything about pacing, deadlines and when to believe a silence lives in
366
+ * {@link SocketHeartbeat}; what belongs here is only what is specific to this
367
+ * peer - which call to make, what counts as unanswered on this client, and
368
+ * who to tell.
369
+ */
370
+ private buildHeartbeat;
371
+ private armIdle;
372
+ /**
373
+ * Ends a turn whose live edit is gone.
374
+ *
375
+ * The dev server exits on its own when its lock is lost, and it says nothing
376
+ * on the way out. A turn left waiting on that socket burns its entire budget
377
+ * — ten minutes — before the caller is told anything.
378
+ *
379
+ * Reached only once the live edit being gone is established, which a close on
380
+ * its own never does: either it named its own end in the application range,
381
+ * or {@link recoverTurn} could not get back to it for the whole reconnect
382
+ * grace. `live_edit_terminated` is a claim about the sandbox, so it is worth
383
+ * only as much as the evidence behind it. The close code and heartbeat
384
+ * account go in the reply, since they are the only account of the cause that
385
+ * reaches the caller.
386
+ */
387
+ private abandonTurnOnDisconnect;
388
+ /**
389
+ * Ends a turn SABS has just said has no live edit left to run on.
390
+ *
391
+ * A socket close is the other trigger for this same outcome, and it is the
392
+ * slower one: the dev server exits silently when it loses its lock, so the
393
+ * close can be minutes late or never arrive at all. This event is the control
394
+ * plane stating the session is gone, which is the same fact without waiting
395
+ * for a connection to notice it.
396
+ *
397
+ * Only for a turn that has not settled itself, though. Every healthy build
398
+ * ends with its session torn down too, and reading that as a dead live edit
399
+ * would report each finished app as a failure. A collector that already
400
+ * settled on a terminal state keeps it; the teardown is the outcome only for
401
+ * a turn still waiting for one. The disconnect path needs no such guard - an
402
+ * unsolicited close mid-turn is abnormal in its own right, where a teardown
403
+ * is what the end of every turn looks like.
404
+ *
405
+ * Settled, not merely reached: a terminal state is provisional until the
406
+ * quiet window closes, because a build that resumes after a false
407
+ * completed/idle clears it. A teardown inside that window therefore still
408
+ * ends the turn - which is the honest answer, since the checkpoint that
409
+ * would follow runs over the socket to the dev server that just died and
410
+ * could not have landed either way.
411
+ */
412
+ private abandonTurnOnSessionTerminated;
413
+ /**
414
+ * Ends a turn whose socket has stopped answering.
415
+ *
416
+ * Two gates, because they answer different questions. `SocketHeartbeat`
417
+ * declares the beat lost after consecutive genuine misses, having already
418
+ * excused a run of unanswered beats and every silence that inbound traffic
419
+ * explained - the right threshold for a diagnostic, and too eager for this: a
420
+ * sandbox that misses two beats and answers the next was recovering, and its
421
+ * turn has to survive that. So the loss also has to outlast
422
+ * {@link DEFAULT_BEAT_LOST_GRACE_MS}, and any beat that succeeds clears it.
423
+ *
424
+ * What is left is a request channel that has been silent for minutes on a
425
+ * socket that never closed, with a turn waiting on it - which without this
426
+ * waits out the whole turn budget.
427
+ *
428
+ * This is the only trigger that reaches a half-open socket. An unsolicited
429
+ * close is the ordinary one and never arrives for a connection that stays up
430
+ * with nothing behind it, and the SABS `session: terminated` event cannot
431
+ * reach a gateway with no inbound route - which is every gateway run on a
432
+ * laptop, where a loopback public URL leaves SABS no way to post at all.
433
+ *
434
+ * The socket is deliberately left in place. Whether a connection proven
435
+ * wedged should also be torn down rather than handed to the next prompt is a
436
+ * separate question from whether this turn should keep waiting on it.
437
+ */
438
+ private abandonTurnOnBeatLost;
439
+ /**
440
+ * Records a dead live edit as the turn's outcome and ends every wait on it.
441
+ * `evidence` is appended to the reply because it is the only account of the
442
+ * cause that reaches the caller.
443
+ */
444
+ private settleAsLiveEditTerminated;
445
+ /**
446
+ * Decides what a close the gateway did not ask for actually means, by going
447
+ * back for the live edit rather than inferring from the close.
448
+ *
449
+ * The reattach machinery exists for exactly this: a turn whose socket went
450
+ * away is one the gateway can move onto a replacement. Abandoning here is
451
+ * what used to make that unreachable, and it charged a recoverable blip the
452
+ * whole turn - the caller is told to send the prompt again, and the work
453
+ * already done in it is discarded.
454
+ *
455
+ * Each of the three outcomes is a different thing to tell the caller:
456
+ *
457
+ * - taken over: nothing to tell. The turn carries on where it got to.
458
+ * - unreachable for the whole grace: the termination the close only hinted
459
+ * at, now with the evidence to say so.
460
+ * - reached but refused the turn: the sandbox is alive and building where
461
+ * this gateway cannot watch it, which is `detached`. Calling that a death
462
+ * would send the caller to retry a build that is still running.
463
+ *
464
+ * A reconnect that fails while SABS is still relaying the turn settles
465
+ * nothing: the socket is one of two ways this turn reports, and the events
466
+ * still arriving are the better account of whether the build is alive.
467
+ *
468
+ * Never throws: nothing awaits this, and every outcome it can reach belongs
469
+ * on the turn rather than on the socket handler that started it.
470
+ */
471
+ private recoverTurn;
472
+ /**
473
+ * Ends a prior turn for `applicationId` before a newer prompt claims the map
474
+ * slot. Callers polling the old turn see `cancelled`; its runTurn exits via
475
+ * AbortSignal and must not keep listening or arming release on the held
476
+ * session.
477
+ */
478
+ private supersedeTurn;
479
+ private armPostTurnRelease;
480
+ private armKeepalive;
481
+ /**
482
+ * Clears the turn timers only. The beat is stopped separately on purpose: it
483
+ * belongs to the socket and outlives these, which the post-turn grace period
484
+ * resets.
485
+ */
486
+ private clearTimers;
487
+ private drop;
488
+ private editorContextFor;
489
+ /**
490
+ * Context the sandbox needs before it will let Clark drive a browser.
491
+ *
492
+ * Absent when headless testing is off or the session is missing something
493
+ * the app shell cannot boot without — Clark then runs the turn with its
494
+ * ordinary tools rather than failing it.
495
+ */
496
+ private browserContextFor;
497
+ /**
498
+ * Library iframe bootstrap from a held session. Used for Gateway's own
499
+ * live-canvas screenshot even when Clark's in-sandbox Playwright is off.
500
+ */
501
+ private buildHeldBrowserContext;
502
+ /**
503
+ * Resolves a real org profile key once per held session. Sending the legacy
504
+ * placeholder `default` makes the orchestrator fail every integration query
505
+ * with "Invalid profile specified" / HTTP 500.
506
+ */
507
+ private resolveHeldProfileKey;
508
+ /**
509
+ * Discovers the orchestrator / agent base URL the way the browser does:
510
+ * Server `/users/me` agents filtered by org agentType, Active status, and
511
+ * profile tags. Operators never set this URL.
512
+ */
513
+ private resolveHeldOrchestrator;
514
+ }