@ggui-ai/mcp-server 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +48 -0
- package/dist/admin-blueprints-transport.d.ts +114 -0
- package/dist/admin-blueprints-transport.d.ts.map +1 -0
- package/dist/admin-blueprints-transport.js +118 -0
- package/dist/admin-oauth-providers-transport.d.ts +40 -0
- package/dist/admin-oauth-providers-transport.d.ts.map +1 -0
- package/dist/admin-oauth-providers-transport.js +263 -0
- package/dist/auth.d.ts +39 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +75 -0
- package/dist/build-mcp.d.ts +128 -0
- package/dist/build-mcp.d.ts.map +1 -0
- package/dist/build-mcp.js +113 -0
- package/dist/code-store-fs.d.ts +19 -0
- package/dist/code-store-fs.d.ts.map +1 -0
- package/dist/code-store-fs.js +98 -0
- package/dist/console-auth.d.ts +139 -0
- package/dist/console-auth.d.ts.map +1 -0
- package/dist/console-auth.js +102 -0
- package/dist/console-cache.d.ts +78 -0
- package/dist/console-cache.d.ts.map +1 -0
- package/dist/console-cache.js +105 -0
- package/dist/console-headers.d.ts +124 -0
- package/dist/console-headers.d.ts.map +1 -0
- package/dist/console-headers.js +49 -0
- package/dist/console-llm-trace.d.ts +66 -0
- package/dist/console-llm-trace.d.ts.map +1 -0
- package/dist/console-llm-trace.js +105 -0
- package/dist/console-payloads.d.ts +67 -0
- package/dist/console-payloads.d.ts.map +1 -0
- package/dist/console-payloads.js +105 -0
- package/dist/console-theme-routes.d.ts +111 -0
- package/dist/console-theme-routes.d.ts.map +1 -0
- package/dist/console-theme-routes.js +202 -0
- package/dist/console-timeline.d.ts +45 -0
- package/dist/console-timeline.d.ts.map +1 -0
- package/dist/console-timeline.js +169 -0
- package/dist/console-validator.d.ts +67 -0
- package/dist/console-validator.d.ts.map +1 -0
- package/dist/console-validator.js +105 -0
- package/dist/console-welcome.d.ts +7 -0
- package/dist/console-welcome.d.ts.map +1 -0
- package/dist/console-welcome.js +221 -0
- package/dist/csrf-middleware.d.ts +55 -0
- package/dist/csrf-middleware.d.ts.map +1 -0
- package/dist/csrf-middleware.js +138 -0
- package/dist/email-login.d.ts +174 -0
- package/dist/email-login.d.ts.map +1 -0
- package/dist/email-login.js +254 -0
- package/dist/email-resend.d.ts +29 -0
- package/dist/email-resend.d.ts.map +1 -0
- package/dist/email-resend.js +71 -0
- package/dist/email-sender-from-env.d.ts +34 -0
- package/dist/email-sender-from-env.d.ts.map +1 -0
- package/dist/email-sender-from-env.js +112 -0
- package/dist/email-smtp.d.ts +42 -0
- package/dist/email-smtp.d.ts.map +1 -0
- package/dist/email-smtp.js +81 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +122 -0
- package/dist/instructions-presets.d.ts +112 -0
- package/dist/instructions-presets.d.ts.map +1 -0
- package/dist/instructions-presets.js +195 -0
- package/dist/llm-backed-negotiator.d.ts +178 -0
- package/dist/llm-backed-negotiator.d.ts.map +1 -0
- package/dist/llm-backed-negotiator.js +579 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +41 -0
- package/dist/mcp-apps-inbound.d.ts +86 -0
- package/dist/mcp-apps-inbound.d.ts.map +1 -0
- package/dist/mcp-apps-inbound.js +278 -0
- package/dist/mcp-apps-outbound.d.ts +448 -0
- package/dist/mcp-apps-outbound.d.ts.map +1 -0
- package/dist/mcp-apps-outbound.js +1163 -0
- package/dist/mcp-mounts.d.ts +239 -0
- package/dist/mcp-mounts.d.ts.map +1 -0
- package/dist/mcp-mounts.js +222 -0
- package/dist/oauth-login-types.d.ts +160 -0
- package/dist/oauth-login-types.d.ts.map +1 -0
- package/dist/oauth-login-types.js +9 -0
- package/dist/oauth-login.d.ts +77 -0
- package/dist/oauth-login.d.ts.map +1 -0
- package/dist/oauth-login.js +455 -0
- package/dist/oauth-providers/github.d.ts +17 -0
- package/dist/oauth-providers/github.d.ts.map +1 -0
- package/dist/oauth-providers/github.js +89 -0
- package/dist/oauth-providers/google.d.ts +18 -0
- package/dist/oauth-providers/google.d.ts.map +1 -0
- package/dist/oauth-providers/google.js +59 -0
- package/dist/oauth-providers-store.d.ts +32 -0
- package/dist/oauth-providers-store.d.ts.map +1 -0
- package/dist/oauth-providers-store.js +291 -0
- package/dist/oauth.d.ts +347 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +686 -0
- package/dist/pairing-transport.d.ts +99 -0
- package/dist/pairing-transport.d.ts.map +1 -0
- package/dist/pairing-transport.js +223 -0
- package/dist/rate-limit-middleware.d.ts +36 -0
- package/dist/rate-limit-middleware.d.ts.map +1 -0
- package/dist/rate-limit-middleware.js +57 -0
- package/dist/render-gate.d.ts +87 -0
- package/dist/render-gate.d.ts.map +1 -0
- package/dist/render-gate.js +77 -0
- package/dist/render-rate-limit.d.ts +59 -0
- package/dist/render-rate-limit.d.ts.map +1 -0
- package/dist/render-rate-limit.js +73 -0
- package/dist/render-signing.d.ts +98 -0
- package/dist/render-signing.d.ts.map +1 -0
- package/dist/render-signing.js +113 -0
- package/dist/request-context.d.ts +113 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +154 -0
- package/dist/reserved-validators.d.ts +22 -0
- package/dist/reserved-validators.d.ts.map +1 -0
- package/dist/reserved-validators.js +101 -0
- package/dist/schema-compat.d.ts +167 -0
- package/dist/schema-compat.d.ts.map +1 -0
- package/dist/schema-compat.js +187 -0
- package/dist/security-headers-middleware.d.ts +38 -0
- package/dist/security-headers-middleware.d.ts.map +1 -0
- package/dist/security-headers-middleware.js +30 -0
- package/dist/server.d.ts +2060 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +6338 -0
- package/dist/session-channel.d.ts +651 -0
- package/dist/session-channel.d.ts.map +1 -0
- package/dist/session-channel.js +1756 -0
- package/dist/storage.d.ts +89 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +171 -0
- package/dist/thread-transport.d.ts +118 -0
- package/dist/thread-transport.d.ts.map +1 -0
- package/dist/thread-transport.js +478 -0
- package/dist/user-session-auth.d.ts +167 -0
- package/dist/user-session-auth.d.ts.map +1 -0
- package/dist/user-session-auth.js +148 -0
- package/package.json +76 -0
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OSS live channel — live session plane over WebSocket.
|
|
3
|
+
*
|
|
4
|
+
* The live channel is where the typed-channel contract is enforced on
|
|
5
|
+
* live traffic between the server and the user. It co-hosts on the
|
|
6
|
+
* same Express server as `/mcp` and reuses the same
|
|
7
|
+
* `@ggui-ai/mcp-server-handlers/session-mutations` helpers that the
|
|
8
|
+
* closed hosted server consumes.
|
|
9
|
+
*
|
|
10
|
+
* Scope:
|
|
11
|
+
*
|
|
12
|
+
* - `subscribe` → auth, resolve-or-create session, register subscriber,
|
|
13
|
+
* reply `ack` with the session's current stack + sequence.
|
|
14
|
+
* - `action` → inbound user action carried as an {@link ActionEnvelope}.
|
|
15
|
+
* Gated through `assertEventAllowed` (allowlist) +
|
|
16
|
+
* `assertActionContract` (payload, for data:submit). Persisted to
|
|
17
|
+
* SessionStore as a typed session event.
|
|
18
|
+
* - `ping`/`pong` → heartbeat parity with hosted.
|
|
19
|
+
* - `close`/socket-close → clean subscriber teardown.
|
|
20
|
+
* - `sendToSession(sessionId, data)` → outbound fan-out API for
|
|
21
|
+
* mutation handlers (ggui_emit / connector `ctx.send`). Validated
|
|
22
|
+
* through `assertStreamContract` before delivery.
|
|
23
|
+
*
|
|
24
|
+
* `props_update`: mount handlers dispatched through the wired-action
|
|
25
|
+
* router can call `ctx.sendPropsUpdate(stackItemId, props)` to fan a
|
|
26
|
+
* `{type:'props_update'}` frame to live subscribers without going
|
|
27
|
+
* through a refresh-stream path. Reaches the renderer's existing
|
|
28
|
+
* `props_update` branch in `iframe-runtime` and applies new props
|
|
29
|
+
* in-place. This seam is scoped to mount tools; the agent-driven
|
|
30
|
+
* `ggui_update` path is handled separately.
|
|
31
|
+
*
|
|
32
|
+
* Not handled here:
|
|
33
|
+
*
|
|
34
|
+
* - Pattern-B daemon-agent notification stream (GET /mcp SSE).
|
|
35
|
+
* - Short-lived session-token mint/consume — that's ggui_push-gated.
|
|
36
|
+
* Dev-mode auth (any bearer via existing AuthAdapter) matches the
|
|
37
|
+
* `/mcp` endpoint's shape and is operator-replaceable.
|
|
38
|
+
*/
|
|
39
|
+
import type { IncomingMessage } from 'node:http';
|
|
40
|
+
import type { Duplex } from 'node:stream';
|
|
41
|
+
import type { JsonObject, ReservedChannelValidator, SanitizeCausedBy, SessionStackEntry } from '@ggui-ai/protocol';
|
|
42
|
+
import type { AuthAdapter, SessionStore, SessionStreamBuffer, StreamEnvelopeInput, StreamFanout, TelemetrySink } from '@ggui-ai/mcp-server-core';
|
|
43
|
+
import type { Logger } from './logger.js';
|
|
44
|
+
/** Default URL path for the channel endpoint. Operators can override. */
|
|
45
|
+
export declare const DEFAULT_SESSION_CHANNEL_PATH = "/ws";
|
|
46
|
+
/**
|
|
47
|
+
* Opt-in plumbing for the `channel_subscribe` polling loop. When this
|
|
48
|
+
* field is set on {@link SessionChannelOptions}, channel subscribes
|
|
49
|
+
* whose `source.tool` is in {@link allowlist} are accepted and the
|
|
50
|
+
* server begins polling. When absent, every `channel_subscribe`
|
|
51
|
+
* returns `CHANNEL_NOT_LOCAL` so the iframe falls back to direct
|
|
52
|
+
* polling via the MCP host proxy.
|
|
53
|
+
*/
|
|
54
|
+
export interface SessionChannelLocalToolsOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Whitelist of `source.tool` names this channel can poll. Must mirror
|
|
57
|
+
* the value the host advertises on
|
|
58
|
+
* `handshake.serverCapabilities.streamWebSocketLocalTools` so the
|
|
59
|
+
* iframe + server agree on which channels use the WS fan-out path.
|
|
60
|
+
* Tools NOT in this list are rejected with `CHANNEL_NOT_LOCAL` (the
|
|
61
|
+
* iframe falls back to direct polling).
|
|
62
|
+
*/
|
|
63
|
+
readonly allowlist: readonly string[];
|
|
64
|
+
/**
|
|
65
|
+
* Synchronous resolver invoked at poll time. Returns the tool's
|
|
66
|
+
* structured output (validated against `streamSpec[ch].schema`
|
|
67
|
+
* client-side; server-side schema validation is deferred to the
|
|
68
|
+
* future `validateContract` slice). Implementations typically
|
|
69
|
+
* delegate to the same in-process tool registry that backs `/mcp`.
|
|
70
|
+
*
|
|
71
|
+
* Throwing surfaces `POLL_FAILED` on the subscriber's
|
|
72
|
+
* `channel_error` channel without canceling the poll loop —
|
|
73
|
+
* transient tool failures are recoverable.
|
|
74
|
+
*/
|
|
75
|
+
invoke(name: string, input: unknown): Promise<unknown>;
|
|
76
|
+
/**
|
|
77
|
+
* Optional poll cadence policy. `defaultMs` applies when the client
|
|
78
|
+
* doesn't supply a `pollIntervalMs`; `floorMs`/`ceilingMs` clamp
|
|
79
|
+
* client-supplied values. Defaults:
|
|
80
|
+
* `{floorMs: 1000, ceilingMs: 60000, defaultMs: 10000}`.
|
|
81
|
+
*/
|
|
82
|
+
readonly pollCadence?: {
|
|
83
|
+
readonly floorMs?: number;
|
|
84
|
+
readonly ceilingMs?: number;
|
|
85
|
+
readonly defaultMs?: number;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Bootstrap-auth plumbing for the live-channel endpoint.
|
|
90
|
+
*
|
|
91
|
+
* The channel accepts a bootstrap credential on the `subscribe`
|
|
92
|
+
* message (`SubscribePayload.bootstrap`). When present:
|
|
93
|
+
*
|
|
94
|
+
* 1. `verify(token)` is called. Must return the bound
|
|
95
|
+
* `{sessionId, appId}` on success, or `null` on any failure
|
|
96
|
+
* (invalid sig, expired, wrong kind, replayed, etc.).
|
|
97
|
+
* 2. The bound `sessionId` MUST match the one on the subscribe
|
|
98
|
+
* payload. Mismatches are rejected with a clean error.
|
|
99
|
+
* 3. On success, the server mints a reconnect credential via
|
|
100
|
+
* `issueSessionToken(sessionId, appId)` and returns it in
|
|
101
|
+
* `AckPayload.sessionToken`. The iframe stores this for WS
|
|
102
|
+
* reconnects via the normal bearer path.
|
|
103
|
+
*
|
|
104
|
+
* Bootstrap auth is MUTUALLY EXCLUSIVE with the upstream `AuthAdapter`
|
|
105
|
+
* bearer path at subscribe time — when a bootstrap token is present,
|
|
106
|
+
* the identity resolved at the HTTP upgrade is IGNORED in favor of
|
|
107
|
+
* the bootstrap-derived identity. This is intentional: MCP Apps
|
|
108
|
+
* iframes don't have a long-lived bearer; the bootstrap IS the auth.
|
|
109
|
+
*/
|
|
110
|
+
/**
|
|
111
|
+
* Verify failure shape — distinguished so the channel server can map
|
|
112
|
+
* `'expired'` to `BOOTSTRAP_EXPIRED` (client SHOULD refresh) vs
|
|
113
|
+
* `'invalid'` to `BOOTSTRAP_INVALID` (client MUST re-handshake).
|
|
114
|
+
*
|
|
115
|
+
* G14 (2026-05-23): bootstrap envelopes are no longer single-use. A
|
|
116
|
+
* signature-valid + unexpired token authenticates EVERY subscribe
|
|
117
|
+
* within the TTL window; transient WS drops reconnect without a fresh
|
|
118
|
+
* handshake. Past expiry, the iframe MAY refresh via the
|
|
119
|
+
* {@link refresh} surface; past the refresh window, fresh handshake.
|
|
120
|
+
*/
|
|
121
|
+
export type SessionChannelBootstrapVerifyResult = {
|
|
122
|
+
readonly ok: true;
|
|
123
|
+
readonly sessionId: string;
|
|
124
|
+
readonly appId: string;
|
|
125
|
+
} | {
|
|
126
|
+
readonly ok: false;
|
|
127
|
+
readonly reason: 'expired' | 'invalid';
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Result of {@link SessionChannelBootstrap.refresh}.
|
|
131
|
+
*
|
|
132
|
+
* - `ok: true`: caller swaps the old envelope for `token` and resumes.
|
|
133
|
+
* - `ok: false`: caller MUST re-handshake (refresh window closed,
|
|
134
|
+
* tampered envelope, etc.).
|
|
135
|
+
*/
|
|
136
|
+
export type SessionChannelBootstrapRefreshResult = {
|
|
137
|
+
readonly ok: true;
|
|
138
|
+
readonly token: string;
|
|
139
|
+
readonly expiresAt: string;
|
|
140
|
+
} | {
|
|
141
|
+
readonly ok: false;
|
|
142
|
+
readonly reason: 'window_closed' | 'invalid';
|
|
143
|
+
};
|
|
144
|
+
export interface SessionChannelBootstrap {
|
|
145
|
+
/**
|
|
146
|
+
* Verify a `SubscribePayload.bootstrap` token.
|
|
147
|
+
*
|
|
148
|
+
* Returns the bound identity on success, or a discriminated failure.
|
|
149
|
+
* The channel server maps `'expired'` to `BOOTSTRAP_EXPIRED` so the
|
|
150
|
+
* iframe can branch on refresh-vs-rehandshake, and `'invalid'` to
|
|
151
|
+
* `BOOTSTRAP_INVALID` for tamper / format / kind failures (no
|
|
152
|
+
* refresh on those).
|
|
153
|
+
*/
|
|
154
|
+
verify(token: string): SessionChannelBootstrapVerifyResult;
|
|
155
|
+
/**
|
|
156
|
+
* Mint a longer-lived reconnect credential to return in
|
|
157
|
+
* `AckPayload.sessionToken`. Called only after a successful
|
|
158
|
+
* `verify()` on a bootstrap subscribe.
|
|
159
|
+
*/
|
|
160
|
+
issueSessionToken(sessionId: string, appId: string): string;
|
|
161
|
+
/**
|
|
162
|
+
* Refresh a (possibly-expired-but-signature-valid) bootstrap envelope
|
|
163
|
+
* into a new envelope with a fresh TTL. Used by the
|
|
164
|
+
* `ggui_runtime_refresh_bootstrap` MCP tool — iframes that see their
|
|
165
|
+
* bootstrap drift out of the TTL window swap in the refreshed
|
|
166
|
+
* envelope without going back through `ggui_push`.
|
|
167
|
+
*
|
|
168
|
+
* Stateless: verifies HMAC against the same secret used at mint,
|
|
169
|
+
* checks the refresh window against the ORIGINAL `iat`, and mints
|
|
170
|
+
* a fresh bootstrap envelope bound to the SAME `(sessionId, appId)`.
|
|
171
|
+
* Past the refresh window the result is `{ok:false, reason:
|
|
172
|
+
* 'window_closed'}`; tampered envelopes are `{ok:false, reason:
|
|
173
|
+
* 'invalid'}`.
|
|
174
|
+
*/
|
|
175
|
+
refresh(token: string): SessionChannelBootstrapRefreshResult;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Default timeout for a single wired-tool invocation, in ms. Operators
|
|
179
|
+
* override via {@link SessionChannelOptions.wiredActionTimeoutMs}; the
|
|
180
|
+
* 30 s ceiling is a honest non-promise: long-running tools MUST design
|
|
181
|
+
* their own completion path (streaming, polling).
|
|
182
|
+
*/
|
|
183
|
+
export declare const DEFAULT_WIRED_TOOL_TIMEOUT_MS = 30000;
|
|
184
|
+
/**
|
|
185
|
+
* Opt-in wired-action dispatch surface. When present on a session
|
|
186
|
+
* channel, `data:submit` envelopes whose declared `actionSpec
|
|
187
|
+
* [name].tool` resolves against this router fire the named tool
|
|
188
|
+
* in-process after validation, and the router's return value flows
|
|
189
|
+
* back onto the session through a refresh-stream emission (see the
|
|
190
|
+
* {@link import('@ggui-ai/protocol').StreamChannelEntry.tool} field).
|
|
191
|
+
*
|
|
192
|
+
* This is the agent-free contract-execution path — "zero agent code"
|
|
193
|
+
* lands here. In OSS `ggui serve`, the CLI composes a router that
|
|
194
|
+
* delegates to the same handler bundle `/mcp` uses (ggui-native +
|
|
195
|
+
* mounts). Hosted deployments that want the same behavior compose
|
|
196
|
+
* their own.
|
|
197
|
+
*
|
|
198
|
+
* Intentionally minimal surface:
|
|
199
|
+
* - `has(name)` separates "tool absent" (TOOL_NOT_FOUND envelope,
|
|
200
|
+
* recoverable) from "tool handler threw" (TOOL_THREW envelope,
|
|
201
|
+
* isolated). The channel server uses the split to pick the error
|
|
202
|
+
* code BEFORE invoking.
|
|
203
|
+
* - `invoke(name, input)` returns `unknown` — the router doesn't
|
|
204
|
+
* impose a shape, the refresh emission validates against the
|
|
205
|
+
* declared `streamSpec[*].schema`.
|
|
206
|
+
*
|
|
207
|
+
* Thread-safety: implementations MUST tolerate concurrent invocations
|
|
208
|
+
* for the same or different tools. The session channel fires no retry;
|
|
209
|
+
* the router's handler is the sole execution.
|
|
210
|
+
*/
|
|
211
|
+
/**
|
|
212
|
+
* Per-invocation context the wired-action dispatcher hands the router.
|
|
213
|
+
* The session-channel server constructs this at dispatch time from
|
|
214
|
+
* the active session + stack item, then closes
|
|
215
|
+
* `sendPropsUpdate` over the channel's outbound fan-out so the mount
|
|
216
|
+
* handler can push a `props_update` frame to live subscribers without
|
|
217
|
+
* routing through a refresh-stream tool.
|
|
218
|
+
*
|
|
219
|
+
* Why this is its own type, not threaded through `HandlerContext`:
|
|
220
|
+
* `HandlerContext` (in `@ggui-ai/mcp-server-handlers`) is the canonical
|
|
221
|
+
* shape every shared handler — ggui-native AND mounted — accepts. It
|
|
222
|
+
* stays narrow on purpose (`appId`, `requestId`, optional `apiKeyHash`)
|
|
223
|
+
* so the surface a host implements is stable. Wired-action runtime
|
|
224
|
+
* fields (`sessionId`, `stackItemId`, `sendPropsUpdate`) are dispatcher-
|
|
225
|
+
* specific — only mount tools invoked through the wired-action router
|
|
226
|
+
* see them, only at dispatch time. Passing them as a third arg to
|
|
227
|
+
* `invoke` keeps the canonical handler shape untouched and makes
|
|
228
|
+
* "this code reaches a wired dispatch path" syntactically obvious.
|
|
229
|
+
*
|
|
230
|
+
* The composer in `mcp-mounts.ts::composeWiredActionRouterFromMounts`
|
|
231
|
+
* synthesizes a runtime ctx for the mount handler that satisfies
|
|
232
|
+
* `HandlerContext` AND structurally carries these wired fields, so a
|
|
233
|
+
* mount fixture can read `ctx.sendPropsUpdate` / `ctx.stackItemId` from the
|
|
234
|
+
* same `ctx` argument the canonical `HandlerContext` sig types — no
|
|
235
|
+
* cast, no widening of the static type.
|
|
236
|
+
*/
|
|
237
|
+
export interface WiredActionContext {
|
|
238
|
+
/** The session this dispatch is bound to. Sourced from the live
|
|
239
|
+
* subscriber + the action envelope's spoof-guarded `sessionId`. */
|
|
240
|
+
readonly sessionId: string;
|
|
241
|
+
/** The active stack item's stackItemId at dispatch time. Mounts that
|
|
242
|
+
* fire `sendPropsUpdate` typically pass this verbatim — only a
|
|
243
|
+
* mount that intentionally targets a sibling stack entry would
|
|
244
|
+
* pick a different value. */
|
|
245
|
+
readonly stackItemId: string;
|
|
246
|
+
/**
|
|
247
|
+
* Push a `{type:'props_update', payload:{stackItemId, props}}` frame to
|
|
248
|
+
* every live subscriber bound to this dispatcher's `sessionId`. The
|
|
249
|
+
* call closes over the `SessionChannelServer.sendPropsUpdate` method,
|
|
250
|
+
* scoped to the active session for safety — even if a buggy mount
|
|
251
|
+
* picks a `stackItemId` that doesn't exist on the session, the channel
|
|
252
|
+
* server's own validation no-ops the fan-out (same posture as
|
|
253
|
+
* `notifyStackPush` orphan handling).
|
|
254
|
+
*
|
|
255
|
+
* Best-effort: per-subscriber send failures are swallowed; a closed
|
|
256
|
+
* socket is a no-op.
|
|
257
|
+
*/
|
|
258
|
+
sendPropsUpdate(stackItemId: string, props: JsonObject): void;
|
|
259
|
+
}
|
|
260
|
+
export interface WiredActionRouter {
|
|
261
|
+
/** Returns `true` when the named tool has a registered handler. Used
|
|
262
|
+
* to emit a clean `TOOL_NOT_FOUND` envelope before invoking — an
|
|
263
|
+
* "invoke unknown tool" would throw through router internals, but
|
|
264
|
+
* the error surface would be less specific. */
|
|
265
|
+
has(toolName: string): boolean;
|
|
266
|
+
/**
|
|
267
|
+
* Invoke the named tool with the given input + per-dispatch wired
|
|
268
|
+
* context. The channel server wraps this call in a timeout +
|
|
269
|
+
* try/catch; implementations SHOULD NOT add their own retry or
|
|
270
|
+
* timeout layer on top.
|
|
271
|
+
*
|
|
272
|
+
* `ctx.sendPropsUpdate` is closed over the active session — mounts
|
|
273
|
+
* fire it to push props to live subscribers without an extra refresh
|
|
274
|
+
* round-trip. Refresh-stream invocations (the post-action pass that
|
|
275
|
+
* fires every declared `streamSpec[*].tool`) reuse the SAME ctx —
|
|
276
|
+
* a refresh tool that wants to emit a props_update can do so, though
|
|
277
|
+
* the canonical surface is the action tool itself.
|
|
278
|
+
*/
|
|
279
|
+
invoke(toolName: string, input: Record<string, unknown>, ctx: WiredActionContext): Promise<unknown>;
|
|
280
|
+
}
|
|
281
|
+
export interface SessionChannelOptions {
|
|
282
|
+
/** Required — the session backing store (typically `InMemorySessionStore`). */
|
|
283
|
+
readonly sessionStore: SessionStore;
|
|
284
|
+
/**
|
|
285
|
+
* Required — the same `AuthAdapter` the `/mcp` endpoint uses. Any
|
|
286
|
+
* failure during `subscribe` rejects the upgrade with HTTP 401.
|
|
287
|
+
*/
|
|
288
|
+
readonly auth: AuthAdapter;
|
|
289
|
+
/**
|
|
290
|
+
* Maps resolved identity → tenant appId. Defaults to
|
|
291
|
+
* `defaultAppIdFromIdentity` — same mapping the `/mcp` endpoint uses.
|
|
292
|
+
*/
|
|
293
|
+
/** Structured logger. */
|
|
294
|
+
readonly logger: Logger;
|
|
295
|
+
/** URL path to mount on. Defaults to `/ws`. */
|
|
296
|
+
readonly path?: string;
|
|
297
|
+
/**
|
|
298
|
+
* Outbound stream replay buffer. Defaults to a fresh
|
|
299
|
+
* `InMemorySessionStreamBuffer` when omitted — fine for OSS
|
|
300
|
+
* zero-config / dev. Persistent adapters bind via the same
|
|
301
|
+
* `SessionStreamBuffer` interface when they land.
|
|
302
|
+
*
|
|
303
|
+
* Each channel instance owns its own seq cursor space; sharing a
|
|
304
|
+
* buffer across two channels in the same process would couple their
|
|
305
|
+
* sequences in confusing ways.
|
|
306
|
+
*/
|
|
307
|
+
readonly streamBuffer?: SessionStreamBuffer;
|
|
308
|
+
/**
|
|
309
|
+
* Live-tail pub/sub for outbound live-channel frames. Defaults to a
|
|
310
|
+
* fresh `InProcessStreamFanout` (in-memory, single-process). Hosted
|
|
311
|
+
* deployments bind a `RedisPubSubFanout` here for multi-process
|
|
312
|
+
* fan-out.
|
|
313
|
+
*
|
|
314
|
+
* The channel server uses the seam to publish every fanout-eligible
|
|
315
|
+
* envelope and to subscribe one async iterator per WebSocket
|
|
316
|
+
* subscriber — no in-process Map walk; the seam owns routing.
|
|
317
|
+
*/
|
|
318
|
+
readonly streamFanout?: StreamFanout;
|
|
319
|
+
/**
|
|
320
|
+
* Optional bootstrap-auth plumbing. When present, the channel
|
|
321
|
+
* accepts `SubscribePayload.bootstrap` and issues reconnect
|
|
322
|
+
* credentials in `AckPayload.sessionToken`. When absent, bootstrap
|
|
323
|
+
* tokens are rejected with `BOOTSTRAP_NOT_SUPPORTED`.
|
|
324
|
+
*/
|
|
325
|
+
readonly bootstrap?: SessionChannelBootstrap;
|
|
326
|
+
/**
|
|
327
|
+
* Optional console cookie-auth plumbing. When present, the
|
|
328
|
+
* channel upgrade looks for the configured cookie on the incoming
|
|
329
|
+
* request. A valid cookie binds the identity as a `builder` and
|
|
330
|
+
* scopes the subscriber to the cookie's `sessionId` — any
|
|
331
|
+
* `subscribe.sessionId` mismatch is rejected with
|
|
332
|
+
* `DEVTOOL_COOKIE_SESSION_MISMATCH`.
|
|
333
|
+
*
|
|
334
|
+
* Absent = cookie auth disabled on this channel. Cookies are never
|
|
335
|
+
* auto-enabled; the server's console composition decides.
|
|
336
|
+
*
|
|
337
|
+
* Design boundary: this auth plane is MUTUALLY EXCLUSIVE with
|
|
338
|
+
* bootstrap auth at upgrade time. When both are configured, the
|
|
339
|
+
* bootstrap path (via `?bootstrap=` query) wins; cookie is only
|
|
340
|
+
* consulted for standard upgrades.
|
|
341
|
+
*/
|
|
342
|
+
readonly cookieAuth?: SessionChannelCookieAuth;
|
|
343
|
+
/**
|
|
344
|
+
* Opt-in wired-action dispatch router. When present, validated
|
|
345
|
+
* `data:submit` envelopes whose declared `actionSpec[name]
|
|
346
|
+
* .tool` names a tool the router knows fire the tool in-process and
|
|
347
|
+
* emit any declared refresh on the session. See
|
|
348
|
+
* {@link WiredActionRouter}.
|
|
349
|
+
*
|
|
350
|
+
* Absent (the default) = the server relays inbound actions to the
|
|
351
|
+
* session store for agent pickup and emits no synthetic stream
|
|
352
|
+
* frames. Matches pre-Slice-11.5 behavior — no regression surface.
|
|
353
|
+
*/
|
|
354
|
+
readonly wiredActionRouter?: WiredActionRouter;
|
|
355
|
+
/**
|
|
356
|
+
* Per-call timeout for wired-tool invocations, in milliseconds.
|
|
357
|
+
* Defaults to {@link DEFAULT_WIRED_TOOL_TIMEOUT_MS} (30 s). Applied
|
|
358
|
+
* identically to the initial action tool AND to each refresh-stream
|
|
359
|
+
* tool. On timeout, a `TOOL_TIMEOUT` envelope emits on
|
|
360
|
+
* `_ggui:contract-error` and the session channel keeps running.
|
|
361
|
+
*/
|
|
362
|
+
readonly wiredActionTimeoutMs?: number;
|
|
363
|
+
/**
|
|
364
|
+
* Override the default sanitizer applied to the stringified original
|
|
365
|
+
* error before it's written to `ContractErrorPayload.error.causedBy`.
|
|
366
|
+
*
|
|
367
|
+
* Defaults to `@ggui-ai/protocol::sanitizeCausedBy` (redacts Bearer
|
|
368
|
+
* tokens, query-param secrets, common env-var patterns, truncates at
|
|
369
|
+
* 2KB). Operators running in locked-down environments can pass a
|
|
370
|
+
* stricter function — e.g., one that returns an empty string to
|
|
371
|
+
* disable `causedBy` entirely, or one that layers additional
|
|
372
|
+
* patterns on top of the defaults.
|
|
373
|
+
*
|
|
374
|
+
* The contract-error envelope flows on `_ggui:contract-error` with
|
|
375
|
+
* `replay: 'all'`, so anything that lands in `causedBy` persists in
|
|
376
|
+
* the session ring buffer and surfaces in SessionInspector. Accepting
|
|
377
|
+
* raw `err.stack` verbatim is a credential-leak footgun; the default
|
|
378
|
+
* sanitizer is load-bearing.
|
|
379
|
+
*/
|
|
380
|
+
readonly sanitizeCausedBy?: SanitizeCausedBy;
|
|
381
|
+
/**
|
|
382
|
+
* Reserved-channel payload validators for channels whose shape is
|
|
383
|
+
* NOT protocol-owned (Item 4 injection pattern). The primary
|
|
384
|
+
* consumer is `_ggui:preview` — the server composes a
|
|
385
|
+
* {@link ReservedChannelValidator} adapting
|
|
386
|
+
* `@ggui-ai/preview-a2ui::parseServerMessage` so malformed A2UI
|
|
387
|
+
* frames emitted on the preview channel reject at the fan-out
|
|
388
|
+
* boundary instead of landing in the subscriber's renderer.
|
|
389
|
+
*
|
|
390
|
+
* Lookup inside {@link validateStreamData} consults this map FIRST,
|
|
391
|
+
* then the protocol-shipped `BUILTIN_RESERVED_VALIDATORS` (which
|
|
392
|
+
* validates `_ggui:contract-error`), then falls through to
|
|
393
|
+
* `{valid: true}` when a known reserved channel has no validator.
|
|
394
|
+
*
|
|
395
|
+
* Absent = no `_ggui:preview` validation (documented degradation for
|
|
396
|
+
* implementations without the preview package); `_ggui:contract-
|
|
397
|
+
* error` is always validated via the built-in.
|
|
398
|
+
*/
|
|
399
|
+
readonly extraReservedValidators?: ReadonlyMap<string, ReservedChannelValidator>;
|
|
400
|
+
/**
|
|
401
|
+
* Optional {@link TelemetrySink} for live-channel operational signals
|
|
402
|
+
* (C12). When present, the channel emits `wired-tool.invoked` events
|
|
403
|
+
* on successful wired-tool dispatches — operational counts +
|
|
404
|
+
* durations for OTLP / CloudWatch / Datadog forwarders. Defaults
|
|
405
|
+
* to {@link NoopTelemetrySink} (swallow silently).
|
|
406
|
+
*
|
|
407
|
+
* Deliberately separate from the renderer's client-side
|
|
408
|
+
* `ObservabilityEvent` surface: same event name, two independent
|
|
409
|
+
* consumers (backend metrics vs host inspector UI). See C12 plan +
|
|
410
|
+
* the TelemetrySink docstring for the sync/lossy contract.
|
|
411
|
+
*/
|
|
412
|
+
readonly telemetry?: TelemetrySink;
|
|
413
|
+
/**
|
|
414
|
+
* Opt-in `channel_subscribe` plumbing for `streamSpec[*].source.tool`
|
|
415
|
+
* fan-out. When present, channel subscribes whose
|
|
416
|
+
* `source.tool` is in `allowlist` are accepted and the server begins
|
|
417
|
+
* polling. When absent, every `channel_subscribe` returns
|
|
418
|
+
* `CHANNEL_NOT_LOCAL` so the iframe falls back to direct polling via
|
|
419
|
+
* the MCP host proxy.
|
|
420
|
+
*
|
|
421
|
+
* Same `allowlist` MUST be advertised on
|
|
422
|
+
* `handshake.serverCapabilities.streamWebSocketLocalTools` so iframe
|
|
423
|
+
* + server agree on which channels use the WS fan-out path.
|
|
424
|
+
*/
|
|
425
|
+
readonly streamWebSocketLocalTools?: SessionChannelLocalToolsOptions;
|
|
426
|
+
/**
|
|
427
|
+
* Protocol-version handshake policy. Governs server behavior when a
|
|
428
|
+
* subscribe declares a `supportedVersions` list that does NOT
|
|
429
|
+
* contain this server's {@link PROTOCOL_SCHEMA_VERSION}.
|
|
430
|
+
*
|
|
431
|
+
* - `'reject'` (default): server emits
|
|
432
|
+
* `{type:'error', payload.code: 'UPGRADE_REQUIRED'}` AND closes
|
|
433
|
+
* the underlying WebSocket. The caller cannot accidentally
|
|
434
|
+
* proceed against a version-mismatched session. This is the
|
|
435
|
+
* canonical posture for first-party servers.
|
|
436
|
+
* - `'advisory'` (opt-out): server emits `UPGRADE_REQUIRED`
|
|
437
|
+
* but keeps the connection open — the subscribe stops (no ack,
|
|
438
|
+
* no stack, no replay). Existing clients that ignore the error
|
|
439
|
+
* code continue to interoperate exactly as pre-handshake.
|
|
440
|
+
* Use only for controlled migration windows during which
|
|
441
|
+
* legacy-version clients must remain attached.
|
|
442
|
+
*
|
|
443
|
+
* Absent `payload.supportedVersions` always passes through — the
|
|
444
|
+
* handshake is fully opt-in on the client side. `serverVersion` is
|
|
445
|
+
* stamped into every successful ack regardless of policy.
|
|
446
|
+
*
|
|
447
|
+
* Switching between `'advisory'` and `'reject'` is a config change,
|
|
448
|
+
* not a schema change — the wire fields and error code ship
|
|
449
|
+
* identically in both modes.
|
|
450
|
+
*/
|
|
451
|
+
readonly versionPolicy?: 'advisory' | 'reject';
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Cookie-based authentication for the live-channel upgrade. Used
|
|
455
|
+
* exclusively by the same-origin console viewer; see
|
|
456
|
+
* `console-auth.ts` for the single consumer today.
|
|
457
|
+
*/
|
|
458
|
+
export interface SessionChannelCookieAuth {
|
|
459
|
+
/**
|
|
460
|
+
* Read the raw cookie value for THIS server's console cookie
|
|
461
|
+
* from the incoming request headers. Returns `null` when the
|
|
462
|
+
* cookie is absent or malformed.
|
|
463
|
+
*/
|
|
464
|
+
readCookie(headers: import('node:http').IncomingHttpHeaders): string | null;
|
|
465
|
+
/**
|
|
466
|
+
* Verify a cookie value and return the bound session/app. Returns
|
|
467
|
+
* `null` on any failure (signature, expiry, wrong kind). Never
|
|
468
|
+
* throws.
|
|
469
|
+
*/
|
|
470
|
+
verify(cookieValue: string): {
|
|
471
|
+
sessionId: string;
|
|
472
|
+
appId: string;
|
|
473
|
+
} | null;
|
|
474
|
+
}
|
|
475
|
+
export interface SessionChannelServer {
|
|
476
|
+
/** The URL path the channel accepts upgrade requests on. */
|
|
477
|
+
readonly path: string;
|
|
478
|
+
/**
|
|
479
|
+
* Wire this into the HTTP server's `upgrade` event. Rejects with 401
|
|
480
|
+
* on auth failure; otherwise completes the WS handshake and wires
|
|
481
|
+
* the subscriber.
|
|
482
|
+
*/
|
|
483
|
+
handleUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer): void;
|
|
484
|
+
/**
|
|
485
|
+
* Deliver a stream envelope to every subscriber of `delivery.sessionId`.
|
|
486
|
+
*
|
|
487
|
+
* Outbound fan-out enforcement point — the delivery's `payload` is
|
|
488
|
+
* validated against the active stack item's streamSpec via
|
|
489
|
+
* `assertStreamContract` before any subscriber receives it.
|
|
490
|
+
*
|
|
491
|
+
* Sequencing + replay behavior:
|
|
492
|
+
* 1. Payload validated against the active stack item's streamSpec.
|
|
493
|
+
* 2. The replay buffer assigns a session-scoped monotonic `seq` and
|
|
494
|
+
* (conditionally) stores the stamped envelope per the channel's
|
|
495
|
+
* replay policy (`'none'` skip / `'latest'` single-slot /
|
|
496
|
+
* `'all'` FIFO ring).
|
|
497
|
+
* 3. The stamped envelope fans out to every subscriber for the
|
|
498
|
+
* session, skipping any whose initial replay already covered
|
|
499
|
+
* this seq (prevents double delivery on reconnect).
|
|
500
|
+
*
|
|
501
|
+
* The caller supplies `StreamEnvelopeInput` (no seq); the server
|
|
502
|
+
* stamps. Returns the stamped `seq` for callers that need to thread
|
|
503
|
+
* ordering back to their own response (e.g., `ggui_emit`'s wire
|
|
504
|
+
* output). When the channel's replay policy was `'none'`, seq is
|
|
505
|
+
* still assigned (so fan-out has a stable cursor) but nothing is
|
|
506
|
+
* stored — the seq still surfaces here so the caller has the same
|
|
507
|
+
* shape regardless of replay policy.
|
|
508
|
+
*
|
|
509
|
+
* Throws `ContractViolationError` on payload mismatch; transport
|
|
510
|
+
* errors are logged but not propagated (per-subscriber best-effort).
|
|
511
|
+
*/
|
|
512
|
+
sendToSession(delivery: StreamEnvelopeInput): Promise<{
|
|
513
|
+
seq: number;
|
|
514
|
+
}>;
|
|
515
|
+
/**
|
|
516
|
+
* Fan a `{type:'push', payload:{stackItem, matchType?}}` wire frame
|
|
517
|
+
* to every subscriber currently bound to `sessionId`. Use this to
|
|
518
|
+
* notify already-subscribed clients about a stack mutation that
|
|
519
|
+
* happened AFTER they subscribed — the initial `ack.stack` snapshot
|
|
520
|
+
* covers state at subscribe time only, so without an explicit notify
|
|
521
|
+
* a second-turn `appendStackItem` is invisible to the live client.
|
|
522
|
+
*
|
|
523
|
+
* The `B1` regression context (2026-04-22 QA pass): the chat surface
|
|
524
|
+
* in `/chat` reuses one session across turns. The first turn's push
|
|
525
|
+
* subscribed AFTER `appendStackItem` ran, so the ack carried the
|
|
526
|
+
* entry. The second turn's push appended on the live session — the
|
|
527
|
+
* subscriber never heard about the new entry, the inline UI slot
|
|
528
|
+
* stayed in "Waiting for session channel replay…" indefinitely.
|
|
529
|
+
* `notifyStackPush` closes that gap. Best-effort: per-subscriber
|
|
530
|
+
* send failures are swallowed (same posture as `sendToSession`).
|
|
531
|
+
*
|
|
532
|
+
* NOT durable. Frames are not stamped through the replay buffer —
|
|
533
|
+
* fresh subscribers still get the current stack via `ack.stack` on
|
|
534
|
+
* subscribe. A new tab opening mid-session reads the latest stack
|
|
535
|
+
* from the snapshot; live tabs read the delta from this notify.
|
|
536
|
+
*
|
|
537
|
+
* Subscribers received via `register()` are tracked in
|
|
538
|
+
* `subscribersBySession`; this helper iterates the bound set and
|
|
539
|
+
* skips closed sockets via the `send()` helper's existing guard.
|
|
540
|
+
* Callers ARE responsible for ordering — call after the underlying
|
|
541
|
+
* `sessionStore.appendStackItem` resolves so the snapshot a
|
|
542
|
+
* concurrent fresh subscriber observes still includes the entry.
|
|
543
|
+
*/
|
|
544
|
+
notifyStackPush(sessionId: string, stackItem: SessionStackEntry, matchType?: string): void;
|
|
545
|
+
/**
|
|
546
|
+
* Prime every declared streamSpec channel on `stackItem` that carries a
|
|
547
|
+
* `tool` refresh hint. Invokes each refresh tool via the bound
|
|
548
|
+
* wiredActionRouter with the empty refresh input, validates the result
|
|
549
|
+
* against the channel's schema, and fans it out so subscribers see an
|
|
550
|
+
* initial value instead of `latest = undefined`.
|
|
551
|
+
*
|
|
552
|
+
* Intended for seed-a-session mount paths (console try-live, agent-
|
|
553
|
+
* initiated bootstraps that append a stack item without a driving
|
|
554
|
+
* action). Without this, blueprints with `streamSpec[channel].tool`
|
|
555
|
+
* render their empty-state branch because the channel has no live
|
|
556
|
+
* delivery — operators see "waiting for data" UI even when the
|
|
557
|
+
* refresh tool would have produced initial content.
|
|
558
|
+
*
|
|
559
|
+
* Same isolation posture as the action-driven refresh pass: one
|
|
560
|
+
* broken refresh MUST NOT block others; per-channel failures log a
|
|
561
|
+
* warning but don't throw. No-op when no wiredActionRouter is
|
|
562
|
+
* configured, when `stackItem.streamSpec` is absent, or when every
|
|
563
|
+
* channel lacks a `.tool` hint.
|
|
564
|
+
*
|
|
565
|
+
* Ordering: callers should invoke AFTER the stack item is persisted
|
|
566
|
+
* so `sendToSession`'s active-stack-item lookup resolves. The
|
|
567
|
+
* `try-live` endpoint awaits this before returning the shortCode so
|
|
568
|
+
* the viewer SPA subscribes with the initial envelope already
|
|
569
|
+
* buffered on the session's stream-buffer replay state.
|
|
570
|
+
*/
|
|
571
|
+
primeStreams(sessionId: string, stackItem: SessionStackEntry): Promise<void>;
|
|
572
|
+
/**
|
|
573
|
+
* Fan a `{type:'props_update', payload:{stackItemId, props}}` wire frame
|
|
574
|
+
* to every subscriber currently bound to `sessionId`. Mount tools
|
|
575
|
+
* dispatched through {@link WiredActionRouter} call this via
|
|
576
|
+
* `WiredActionContext.sendPropsUpdate` so a wired action that
|
|
577
|
+
* mutates server-side state can replace renderer props in-place
|
|
578
|
+
* without going through a refresh-stream tool.
|
|
579
|
+
*
|
|
580
|
+
* Validation posture (mirrors `notifyStackPush`'s "best-effort orphan
|
|
581
|
+
* no-op"):
|
|
582
|
+
* 1. Look up the session via `sessionStore.get`. Absent → log
|
|
583
|
+
* `session_channel_props_update_orphan` and return — the wire
|
|
584
|
+
* validator on the renderer side would reject a frame for an
|
|
585
|
+
* unknown session anyway.
|
|
586
|
+
* 2. Look up the target stack entry by `stackItemId` in the loaded
|
|
587
|
+
* session's stack. Absent → log
|
|
588
|
+
* `session_channel_props_update_pageid_unknown` and return.
|
|
589
|
+
* 3. Iterate the flat WS-subscriber set, filter to subscribers
|
|
590
|
+
* whose `sessionId` matches, and `send()` the frame. Closed
|
|
591
|
+
* sockets are skipped silently by `send()`.
|
|
592
|
+
*
|
|
593
|
+
* NOT routed through StreamFanout — `type: 'props_update'` is a
|
|
594
|
+
* distinct WebSocket message type, not a stream envelope. Stream
|
|
595
|
+
* envelopes flow on `data` frames and have a `seq` cursor; props
|
|
596
|
+
* updates are ephemeral and follow `notifyStackPush`'s pattern
|
|
597
|
+
* (live-only, no replay-buffer stamping). A new subscriber that
|
|
598
|
+
* connects mid-session reads current `props` from the stack
|
|
599
|
+
* snapshot delivered in `ack.stack`.
|
|
600
|
+
*
|
|
601
|
+
* Schema validation against `propsSpec`: NOT enforced server-side
|
|
602
|
+
* here. The renderer validates inbound props via
|
|
603
|
+
* `validateInboundPropsPayload` against the cached
|
|
604
|
+
* `stackItem.propsSpec` before applying — defense-in-depth at the
|
|
605
|
+
* receiving boundary. Server-side enforcement is reserved for the
|
|
606
|
+
* future agent-driven `ggui_update` path; the mount-tool seam is
|
|
607
|
+
* trusted-runtime today (mounts execute in-process, same trust
|
|
608
|
+
* boundary as ggui-native handlers).
|
|
609
|
+
*/
|
|
610
|
+
sendPropsUpdate(sessionId: string, stackItemId: string, props: JsonObject): Promise<void>;
|
|
611
|
+
/**
|
|
612
|
+
* Fan a `{type:'drain_ack', payload:{sessionId, appId, stackItemId,
|
|
613
|
+
* eventId, drainedAt}}` wire frame to every subscriber currently
|
|
614
|
+
* bound to `sessionId`.
|
|
615
|
+
*
|
|
616
|
+
* Fired by `createGguiConsumeHandler` once per drained
|
|
617
|
+
* `PendingEvent` so the iframe-runtime can cancel the matching
|
|
618
|
+
* per-action 10s claim timer + resolve the toast as `consumed`.
|
|
619
|
+
* Implements the `DrainAckNotifier` contract from
|
|
620
|
+
* `@ggui-ai/mcp-server-handlers`.
|
|
621
|
+
*
|
|
622
|
+
* Same posture as `notifyStackPush` / `sendPropsUpdate` — live-only,
|
|
623
|
+
* no replay-buffer stamping. Subscribers that connect AFTER the
|
|
624
|
+
* drain see the next consume's snapshot rather than the missed
|
|
625
|
+
* frame; the iframe's claim timer + atomic-pop primitive backstop
|
|
626
|
+
* any frame loss.
|
|
627
|
+
*/
|
|
628
|
+
sendDrainAck(args: {
|
|
629
|
+
readonly sessionId: string;
|
|
630
|
+
readonly appId: string;
|
|
631
|
+
readonly stackItemId: string;
|
|
632
|
+
readonly eventId: string;
|
|
633
|
+
readonly drainedAt: string;
|
|
634
|
+
}): void;
|
|
635
|
+
/** Number of live subscribers. Useful for health / debug introspection. */
|
|
636
|
+
readonly subscriberCount: number;
|
|
637
|
+
/** Number of distinct sessions with at least one subscriber. */
|
|
638
|
+
readonly sessionCount: number;
|
|
639
|
+
/**
|
|
640
|
+
* Close every live subscriber + the underlying ws server. Idempotent.
|
|
641
|
+
*/
|
|
642
|
+
close(): Promise<void>;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Build an OSS live-channel server. The returned object is designed to be
|
|
646
|
+
* composed into `createGguiServer` — see `server.ts` for the wire-up.
|
|
647
|
+
*/
|
|
648
|
+
export declare function createSessionChannelServer(opts: SessionChannelOptions): SessionChannelServer;
|
|
649
|
+
/** Fabricate a request id for live-channel ops so logs correlate. */
|
|
650
|
+
export declare function newRequestId(): string;
|
|
651
|
+
//# sourceMappingURL=session-channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-channel.d.ts","sourceRoot":"","sources":["../src/session-channel.ts"],"names":[],"mappings":"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"}
|