@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.
- package/LICENSE.txt +87 -0
- package/README.md +170 -0
- package/dist/agents/resolve-orchestrator-url.d.ts +22 -0
- package/dist/agents/resolve-orchestrator-url.js +38 -0
- package/dist/agents/resolve-orchestrator-url.js.map +1 -0
- package/dist/capabilities/import-prompt.d.ts +11 -0
- package/dist/capabilities/import-prompt.js +96 -0
- package/dist/capabilities/import-prompt.js.map +1 -0
- package/dist/capabilities/lifecycle.d.ts +73 -0
- package/dist/capabilities/lifecycle.js +2531 -0
- package/dist/capabilities/lifecycle.js.map +1 -0
- package/dist/capabilities/persisted-progress.d.ts +46 -0
- package/dist/capabilities/persisted-progress.js +246 -0
- package/dist/capabilities/persisted-progress.js.map +1 -0
- package/dist/capabilities/types.d.ts +446 -0
- package/dist/capabilities/types.js +47 -0
- package/dist/capabilities/types.js.map +1 -0
- package/dist/capture/browser-contract.d.ts +36 -0
- package/dist/capture/browser-contract.js +24 -0
- package/dist/capture/browser-contract.js.map +1 -0
- package/dist/capture/browser-instructions.d.ts +9 -0
- package/dist/capture/browser-instructions.js +17 -0
- package/dist/capture/browser-instructions.js.map +1 -0
- package/dist/capture/capture-library-probe.d.ts +55 -0
- package/dist/capture/capture-library-probe.js +177 -0
- package/dist/capture/capture-library-probe.js.map +1 -0
- package/dist/capture/capture-library.d.ts +52 -0
- package/dist/capture/capture-library.js +249 -0
- package/dist/capture/capture-library.js.map +1 -0
- package/dist/capture/classify-library-paint.d.ts +21 -0
- package/dist/capture/classify-library-paint.js +13 -0
- package/dist/capture/classify-library-paint.js.map +1 -0
- package/dist/capture/host-mcp-scan.d.ts +16 -0
- package/dist/capture/host-mcp-scan.js +64 -0
- package/dist/capture/host-mcp-scan.js.map +1 -0
- package/dist/capture/library-parent-script.d.ts +34 -0
- package/dist/capture/library-parent-script.js +82 -0
- package/dist/capture/library-parent-script.js.map +1 -0
- package/dist/capture/mode.d.ts +22 -0
- package/dist/capture/mode.js +45 -0
- package/dist/capture/mode.js.map +1 -0
- package/dist/config.d.ts +141 -0
- package/dist/config.js +109 -0
- package/dist/config.js.map +1 -0
- package/dist/debug.d.ts +2 -0
- package/dist/debug.js +9 -0
- package/dist/debug.js.map +1 -0
- package/dist/deps.d.ts +36 -0
- package/dist/deps.js +2 -0
- package/dist/deps.js.map +1 -0
- package/dist/events/cursor.d.ts +43 -0
- package/dist/events/cursor.js +78 -0
- package/dist/events/cursor.js.map +1 -0
- package/dist/events/memory-event-store.d.ts +34 -0
- package/dist/events/memory-event-store.js +110 -0
- package/dist/events/memory-event-store.js.map +1 -0
- package/dist/events/merge.d.ts +23 -0
- package/dist/events/merge.js +97 -0
- package/dist/events/merge.js.map +1 -0
- package/dist/events/normalized-collector.d.ts +62 -0
- package/dist/events/normalized-collector.js +156 -0
- package/dist/events/normalized-collector.js.map +1 -0
- package/dist/events/schema.d.ts +9 -0
- package/dist/events/schema.js +93 -0
- package/dist/events/schema.js.map +1 -0
- package/dist/events/snapshot.d.ts +32 -0
- package/dist/events/snapshot.js +57 -0
- package/dist/events/snapshot.js.map +1 -0
- package/dist/events/stream-key.d.ts +2 -0
- package/dist/events/stream-key.js +31 -0
- package/dist/events/stream-key.js.map +1 -0
- package/dist/events/types.d.ts +179 -0
- package/dist/events/types.js +66 -0
- package/dist/events/types.js.map +1 -0
- package/dist/health/readiness.d.ts +99 -0
- package/dist/health/readiness.js +202 -0
- package/dist/health/readiness.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/filters.d.ts +8 -0
- package/dist/integrations/filters.js +40 -0
- package/dist/integrations/filters.js.map +1 -0
- package/dist/integrations/map.d.ts +26 -0
- package/dist/integrations/map.js +43 -0
- package/dist/integrations/map.js.map +1 -0
- package/dist/integrations/profile-key.d.ts +19 -0
- package/dist/integrations/profile-key.js +35 -0
- package/dist/integrations/profile-key.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +5 -0
- package/dist/main.js.map +1 -0
- package/dist/orchestrator/client.d.ts +62 -0
- package/dist/orchestrator/client.js +160 -0
- package/dist/orchestrator/client.js.map +1 -0
- package/dist/playwright/ensure-chromium.d.ts +26 -0
- package/dist/playwright/ensure-chromium.js +60 -0
- package/dist/playwright/ensure-chromium.js.map +1 -0
- package/dist/preview/capture-screenshot.d.ts +52 -0
- package/dist/preview/capture-screenshot.js +158 -0
- package/dist/preview/capture-screenshot.js.map +1 -0
- package/dist/preview/viewer-url.d.ts +82 -0
- package/dist/preview/viewer-url.js +128 -0
- package/dist/preview/viewer-url.js.map +1 -0
- package/dist/process/fault-barrier.d.ts +55 -0
- package/dist/process/fault-barrier.js +113 -0
- package/dist/process/fault-barrier.js.map +1 -0
- package/dist/resume/memory-progress-store.d.ts +39 -0
- package/dist/resume/memory-progress-store.js +82 -0
- package/dist/resume/memory-progress-store.js.map +1 -0
- package/dist/resume/memory-recent-app-store.d.ts +14 -0
- package/dist/resume/memory-recent-app-store.js +27 -0
- package/dist/resume/memory-recent-app-store.js.map +1 -0
- package/dist/resume/memory-turn-store.d.ts +18 -0
- package/dist/resume/memory-turn-store.js +73 -0
- package/dist/resume/memory-turn-store.js.map +1 -0
- package/dist/resume/progress-key.d.ts +21 -0
- package/dist/resume/progress-key.js +58 -0
- package/dist/resume/progress-key.js.map +1 -0
- package/dist/resume/stores.d.ts +14 -0
- package/dist/resume/stores.js +18 -0
- package/dist/resume/stores.js.map +1 -0
- package/dist/resume/types.d.ts +124 -0
- package/dist/resume/types.js +13 -0
- package/dist/resume/types.js.map +1 -0
- package/dist/sabs/agent-facing-text.d.ts +33 -0
- package/dist/sabs/agent-facing-text.js +125 -0
- package/dist/sabs/agent-facing-text.js.map +1 -0
- package/dist/sabs/browser-context.d.ts +53 -0
- package/dist/sabs/browser-context.js +91 -0
- package/dist/sabs/browser-context.js.map +1 -0
- package/dist/sabs/editor-client-methods.d.ts +147 -0
- package/dist/sabs/editor-client-methods.js +715 -0
- package/dist/sabs/editor-client-methods.js.map +1 -0
- package/dist/sabs/recent-tool-runs.d.ts +14 -0
- package/dist/sabs/recent-tool-runs.js +61 -0
- package/dist/sabs/recent-tool-runs.js.map +1 -0
- package/dist/sabs/session-peer.d.ts +351 -0
- package/dist/sabs/session-peer.js +2 -0
- package/dist/sabs/session-peer.js.map +1 -0
- package/dist/sabs/turn-collector.d.ts +152 -0
- package/dist/sabs/turn-collector.js +573 -0
- package/dist/sabs/turn-collector.js.map +1 -0
- package/dist/sabs/websocket-session-peer.d.ts +514 -0
- package/dist/sabs/websocket-session-peer.js +2487 -0
- package/dist/sabs/websocket-session-peer.js.map +1 -0
- package/dist/security/constant-time.d.ts +9 -0
- package/dist/security/constant-time.js +13 -0
- package/dist/security/constant-time.js.map +1 -0
- package/dist/server/client.d.ts +371 -0
- package/dist/server/client.js +572 -0
- package/dist/server/client.js.map +1 -0
- package/dist/server/identity-reads.d.ts +76 -0
- package/dist/server/identity-reads.js +209 -0
- package/dist/server/identity-reads.js.map +1 -0
- package/dist/start.d.ts +14 -0
- package/dist/start.js +75 -0
- package/dist/start.js.map +1 -0
- package/dist/telemetry/metrics.d.ts +32 -0
- package/dist/telemetry/metrics.js +57 -0
- package/dist/telemetry/metrics.js.map +1 -0
- package/dist/transports/mcp/admin-tools.d.ts +47 -0
- package/dist/transports/mcp/admin-tools.js +164 -0
- package/dist/transports/mcp/admin-tools.js.map +1 -0
- package/dist/transports/mcp/app-status-html.d.ts +15 -0
- package/dist/transports/mcp/app-status-html.js +563 -0
- package/dist/transports/mcp/app-status-html.js.map +1 -0
- package/dist/transports/mcp/decision-elicitation.d.ts +74 -0
- package/dist/transports/mcp/decision-elicitation.js +233 -0
- package/dist/transports/mcp/decision-elicitation.js.map +1 -0
- package/dist/transports/mcp/dispatch-probe.d.ts +24 -0
- package/dist/transports/mcp/dispatch-probe.js +48 -0
- package/dist/transports/mcp/dispatch-probe.js.map +1 -0
- package/dist/transports/mcp/format-tool-content.d.ts +25 -0
- package/dist/transports/mcp/format-tool-content.js +48 -0
- package/dist/transports/mcp/format-tool-content.js.map +1 -0
- package/dist/transports/mcp/mount.d.ts +9 -0
- package/dist/transports/mcp/mount.js +469 -0
- package/dist/transports/mcp/mount.js.map +1 -0
- package/dist/transports/mcp/progress-notifier.d.ts +39 -0
- package/dist/transports/mcp/progress-notifier.js +52 -0
- package/dist/transports/mcp/progress-notifier.js.map +1 -0
- 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
|
+
}
|