@ggui-ai/mcp-server 0.2.0-alpha.4 → 0.4.0-rc.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/dist/admin-blueprints-transport.d.ts.map +1 -1
- package/dist/admin-blueprints-transport.js +2 -1
- package/dist/admin-oauth-providers-transport.d.ts.map +1 -1
- package/dist/admin-oauth-providers-transport.js +7 -5
- package/dist/api-renders-routes.d.ts +85 -0
- package/dist/api-renders-routes.d.ts.map +1 -0
- package/dist/api-renders-routes.js +372 -0
- package/dist/build-mcp.d.ts +2 -2
- package/dist/build-mcp.d.ts.map +1 -1
- package/dist/build-mcp.js +64 -5
- package/dist/code-routes.d.ts +47 -0
- package/dist/code-routes.d.ts.map +1 -0
- package/dist/code-routes.js +81 -0
- package/dist/code-store-fs.js +2 -2
- package/dist/console-auth.d.ts +10 -10
- package/dist/console-auth.d.ts.map +1 -1
- package/dist/console-auth.js +5 -5
- package/dist/console-blueprint-routes.d.ts +71 -0
- package/dist/console-blueprint-routes.d.ts.map +1 -0
- package/dist/console-blueprint-routes.js +348 -0
- package/dist/console-chat-routes.d.ts +80 -0
- package/dist/console-chat-routes.d.ts.map +1 -0
- package/dist/console-chat-routes.js +204 -0
- package/dist/console-config-routes.d.ts +37 -0
- package/dist/console-config-routes.d.ts.map +1 -0
- package/dist/console-config-routes.js +91 -0
- package/dist/console-headers.d.ts +1 -1
- package/dist/console-info-routes.d.ts +84 -0
- package/dist/console-info-routes.d.ts.map +1 -0
- package/dist/console-info-routes.js +135 -0
- package/dist/console-keys-routes.d.ts +50 -0
- package/dist/console-keys-routes.d.ts.map +1 -0
- package/dist/console-keys-routes.js +222 -0
- package/dist/console-llm-keys-routes.d.ts +47 -0
- package/dist/console-llm-keys-routes.d.ts.map +1 -0
- package/dist/console-llm-keys-routes.js +443 -0
- package/dist/console-mcp-tools-routes.d.ts +41 -0
- package/dist/console-mcp-tools-routes.d.ts.map +1 -0
- package/dist/console-mcp-tools-routes.js +60 -0
- package/dist/console-registry-routes.d.ts +66 -0
- package/dist/console-registry-routes.d.ts.map +1 -0
- package/dist/console-registry-routes.js +276 -0
- package/dist/console-session-routes.d.ts +89 -0
- package/dist/console-session-routes.d.ts.map +1 -0
- package/dist/console-session-routes.js +385 -0
- package/dist/console-sessions-routes.d.ts +52 -0
- package/dist/console-sessions-routes.d.ts.map +1 -0
- package/dist/console-sessions-routes.js +106 -0
- package/dist/console-static-routes.d.ts +54 -0
- package/dist/console-static-routes.d.ts.map +1 -0
- package/dist/console-static-routes.js +190 -0
- package/dist/console-theme-routes.d.ts +3 -3
- package/dist/console-theme-routes.js +1 -1
- package/dist/console-timeline.d.ts +5 -5
- package/dist/console-timeline.d.ts.map +1 -1
- package/dist/console-timeline.js +27 -26
- package/dist/console-welcome.js +2 -2
- package/dist/email-login.d.ts.map +1 -1
- package/dist/email-login.js +2 -3
- package/dist/ggui-session-channel/action-ingress.d.ts +54 -0
- package/dist/ggui-session-channel/action-ingress.d.ts.map +1 -0
- package/dist/ggui-session-channel/action-ingress.js +228 -0
- package/dist/ggui-session-channel/channel-subscriptions.d.ts +97 -0
- package/dist/ggui-session-channel/channel-subscriptions.d.ts.map +1 -0
- package/dist/ggui-session-channel/channel-subscriptions.js +224 -0
- package/dist/ggui-session-channel/internal-types.d.ts +102 -0
- package/dist/ggui-session-channel/internal-types.d.ts.map +1 -0
- package/dist/ggui-session-channel/internal-types.js +6 -0
- package/dist/ggui-session-channel/outbound.d.ts +81 -0
- package/dist/ggui-session-channel/outbound.d.ts.map +1 -0
- package/dist/ggui-session-channel/outbound.js +174 -0
- package/dist/ggui-session-channel/socket-router.d.ts +38 -0
- package/dist/ggui-session-channel/socket-router.d.ts.map +1 -0
- package/dist/ggui-session-channel/socket-router.js +213 -0
- package/dist/ggui-session-channel/subscribe.d.ts +165 -0
- package/dist/ggui-session-channel/subscribe.d.ts.map +1 -0
- package/dist/ggui-session-channel/subscribe.js +370 -0
- package/dist/ggui-session-channel/subscriber-lifecycle.d.ts +40 -0
- package/dist/ggui-session-channel/subscriber-lifecycle.d.ts.map +1 -0
- package/dist/ggui-session-channel/subscriber-lifecycle.js +123 -0
- package/dist/ggui-session-channel.d.ts +425 -0
- package/dist/ggui-session-channel.d.ts.map +1 -0
- package/dist/ggui-session-channel.js +262 -0
- package/dist/health-routes.d.ts +76 -0
- package/dist/health-routes.d.ts.map +1 -0
- package/dist/health-routes.js +145 -0
- package/dist/index.d.ts +10 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -9
- package/dist/instructions-presets.d.ts +3 -3
- package/dist/instructions-presets.js +24 -24
- package/dist/llm-backed-negotiator.d.ts +68 -67
- package/dist/llm-backed-negotiator.d.ts.map +1 -1
- package/dist/llm-backed-negotiator.js +82 -221
- package/dist/mcp-apps-outbound.d.ts +47 -48
- package/dist/mcp-apps-outbound.d.ts.map +1 -1
- package/dist/mcp-apps-outbound.js +154 -177
- package/dist/mcp-endpoint-routes.d.ts +88 -0
- package/dist/mcp-endpoint-routes.d.ts.map +1 -0
- package/dist/mcp-endpoint-routes.js +359 -0
- package/dist/mcp-mounts.d.ts +2 -76
- package/dist/mcp-mounts.d.ts.map +1 -1
- package/dist/mcp-mounts.js +0 -76
- package/dist/oauth-as-routes.d.ts +60 -0
- package/dist/oauth-as-routes.d.ts.map +1 -0
- package/dist/oauth-as-routes.js +82 -0
- package/dist/oauth-clients-routes.d.ts +39 -0
- package/dist/oauth-clients-routes.d.ts.map +1 -0
- package/dist/oauth-clients-routes.js +87 -0
- package/dist/oauth-login-types.d.ts +1 -20
- package/dist/oauth-login-types.d.ts.map +1 -1
- package/dist/oauth-login-types.js +30 -7
- package/dist/oauth-login.d.ts.map +1 -1
- package/dist/oauth-login.js +3 -2
- package/dist/oauth-providers-store.d.ts.map +1 -1
- package/dist/oauth-providers-store.js +5 -5
- package/dist/oauth.d.ts +9 -8
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +41 -19
- package/dist/pairing-transport.d.ts.map +1 -1
- package/dist/pairing-transport.js +2 -1
- package/dist/request-context.d.ts +2 -2
- package/dist/request-context.js +2 -2
- package/dist/reserved-validators.d.ts.map +1 -1
- package/dist/reserved-validators.js +9 -1
- package/dist/route-param.d.ts +9 -0
- package/dist/route-param.d.ts.map +1 -0
- package/dist/route-param.js +10 -0
- package/dist/runtime-bundle-route.d.ts +43 -0
- package/dist/runtime-bundle-route.d.ts.map +1 -0
- package/dist/runtime-bundle-route.js +80 -0
- package/dist/schema-compat.d.ts +64 -62
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +23 -51
- package/dist/server.d.ts +179 -193
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +644 -3759
- package/dist/storage.d.ts +5 -5
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +5 -5
- package/dist/thread-transport.d.ts.map +1 -1
- package/dist/thread-transport.js +4 -3
- package/dist/user-session-auth.d.ts +7 -21
- package/dist/user-session-auth.d.ts.map +1 -1
- package/dist/user-session-auth.js +7 -28
- package/package.json +16 -15
- package/dist/mcp-apps-inbound.d.ts +0 -86
- package/dist/mcp-apps-inbound.d.ts.map +0 -1
- package/dist/mcp-apps-inbound.js +0 -283
- package/dist/render-channel.d.ts +0 -694
- package/dist/render-channel.d.ts.map +0 -1
- package/dist/render-channel.js +0 -1775
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WS subscriber lifecycle for the live channel — registration into the
|
|
3
|
+
* shared subscriber set, the per-subscriber live-tail pump loop, and
|
|
4
|
+
* the symmetric teardown path (`unregister`) that ends the pump,
|
|
5
|
+
* unhooks the StreamFanout subscription, and clears every
|
|
6
|
+
* `channel_subscribe` polling loop the subscriber owned.
|
|
7
|
+
*
|
|
8
|
+
* Owns the per-render subscriber counter that drives the
|
|
9
|
+
* `onFirstSubscriber` / `onLastSubscriberGone` 0↔1 transition hooks —
|
|
10
|
+
* no other module reads it.
|
|
11
|
+
*/
|
|
12
|
+
export function createSubscriberLifecycle(deps) {
|
|
13
|
+
/**
|
|
14
|
+
* Per-render local subscriber count. Drives the
|
|
15
|
+
* `onFirstSubscriber` / `onLastSubscriberGone` 0↔1 transition hooks
|
|
16
|
+
* multi-process deployments use for per-render cross-process pub/sub
|
|
17
|
+
* channel scoping. Distinct from the channel server's `renderCount`
|
|
18
|
+
* getter — that walks `wsSubscribers` on demand; this map is the
|
|
19
|
+
* registration-time counter the hooks key off.
|
|
20
|
+
*/
|
|
21
|
+
const renderCountById = new Map();
|
|
22
|
+
/**
|
|
23
|
+
* Pump live frames from the StreamFanout iterator out to this
|
|
24
|
+
* subscriber's WS. Started fire-and-forget by `register`; ends when
|
|
25
|
+
* the iterator yields done (close() on the seam) OR `unregister`
|
|
26
|
+
* calls `iter.return()`. Per-subscriber seq filter applied here:
|
|
27
|
+
* frames with `seq <= replayCompletedSeq` were (or will be)
|
|
28
|
+
* delivered via the replay path on subscribe.
|
|
29
|
+
*
|
|
30
|
+
* The pump's first action is `await iter.next()`, which yields
|
|
31
|
+
* control back to the event loop. This is what preserves the
|
|
32
|
+
* subscribe-handler ordering invariant: ack → replay frames →
|
|
33
|
+
* live frames. The replay-frame send loop completes synchronously
|
|
34
|
+
* before the pump can ever send anything, regardless of fanout
|
|
35
|
+
* timing.
|
|
36
|
+
*/
|
|
37
|
+
async function pumpSubscriber(sub) {
|
|
38
|
+
try {
|
|
39
|
+
for (;;) {
|
|
40
|
+
const { value, done } = await sub.iter.next();
|
|
41
|
+
if (done)
|
|
42
|
+
return;
|
|
43
|
+
if (value.seq <= sub.replayCompletedSeq)
|
|
44
|
+
continue;
|
|
45
|
+
if (sub.ws.readyState !== sub.ws.OPEN) {
|
|
46
|
+
await sub.iter.return?.();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
deps.send(sub.ws, { type: "data", payload: value });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
deps.logger.warn("render_channel_pump_failed", {
|
|
54
|
+
sessionId: sub.sessionId,
|
|
55
|
+
error: String(err),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function register(sub) {
|
|
60
|
+
deps.wsSubscribers.add(sub);
|
|
61
|
+
deps.subscribersByWs.set(sub.ws, sub);
|
|
62
|
+
// Per-render count bookkeeping + 0→1 hook for cloud pubsub
|
|
63
|
+
// adapter scoping. Increment FIRST so the hook sees the up-to-date
|
|
64
|
+
// state; hook fires only on the transition (prevCount === 0).
|
|
65
|
+
const prevCount = renderCountById.get(sub.sessionId) ?? 0;
|
|
66
|
+
renderCountById.set(sub.sessionId, prevCount + 1);
|
|
67
|
+
if (prevCount === 0 && deps.onFirstSubscriber) {
|
|
68
|
+
try {
|
|
69
|
+
deps.onFirstSubscriber(sub.sessionId);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
// Best-effort: a thrown hook MUST NOT corrupt the
|
|
73
|
+
// wsSubscribers set vs the real socket lifecycle.
|
|
74
|
+
deps.logger.warn("render_channel_on_first_subscriber_threw", {
|
|
75
|
+
sessionId: sub.sessionId,
|
|
76
|
+
error: String(err),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Start the pump loop. Fire-and-forget — pump errors are logged
|
|
81
|
+
// inside pumpSubscriber, never propagated.
|
|
82
|
+
void pumpSubscriber(sub);
|
|
83
|
+
}
|
|
84
|
+
function unregister(ws) {
|
|
85
|
+
const sub = deps.subscribersByWs.get(ws);
|
|
86
|
+
if (!sub)
|
|
87
|
+
return;
|
|
88
|
+
deps.subscribersByWs.delete(ws);
|
|
89
|
+
deps.wsSubscribers.delete(sub);
|
|
90
|
+
// Per-render count bookkeeping + 1→0 hook (symmetric with register).
|
|
91
|
+
const prevCount = renderCountById.get(sub.sessionId) ?? 0;
|
|
92
|
+
if (prevCount <= 1) {
|
|
93
|
+
renderCountById.delete(sub.sessionId);
|
|
94
|
+
if (prevCount === 1 && deps.onLastSubscriberGone) {
|
|
95
|
+
try {
|
|
96
|
+
deps.onLastSubscriberGone(sub.sessionId);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
deps.logger.warn("render_channel_on_last_subscriber_gone_threw", {
|
|
100
|
+
sessionId: sub.sessionId,
|
|
101
|
+
error: String(err),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
renderCountById.set(sub.sessionId, prevCount - 1);
|
|
108
|
+
}
|
|
109
|
+
// Ending the iter terminates pumpSubscriber AND unregisters this
|
|
110
|
+
// subscriber from the StreamFanout. Idempotent on the seam side
|
|
111
|
+
// (close-after-return is a no-op).
|
|
112
|
+
void sub.iter.return?.();
|
|
113
|
+
// Tear down every `channel_subscribe` polling loop owned
|
|
114
|
+
// by this subscriber. Symmetric with stream-iterator teardown
|
|
115
|
+
// above. clearInterval is idempotent on already-cleared handles,
|
|
116
|
+
// so a concurrent channel_unsubscribe + WS close is safe.
|
|
117
|
+
for (const state of sub.channelSubs.values()) {
|
|
118
|
+
clearInterval(state.timer);
|
|
119
|
+
}
|
|
120
|
+
sub.channelSubs.clear();
|
|
121
|
+
}
|
|
122
|
+
return { register, unregister };
|
|
123
|
+
}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OSS live channel — live render 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
|
|
7
|
+
* `@ggui-ai/mcp-server-handlers/renders` helpers, so every
|
|
8
|
+
* deployment of this server family enforces the same contracts.
|
|
9
|
+
*
|
|
10
|
+
* Scope:
|
|
11
|
+
*
|
|
12
|
+
* - `subscribe` → auth, resolve-or-create render, register subscriber,
|
|
13
|
+
* reply `ack` with the render's current snapshot + sequence.
|
|
14
|
+
* - `action` → inbound user action carried as an {@link ActionEnvelope}.
|
|
15
|
+
* Gated through `assertActionContract` (payload, for data:submit).
|
|
16
|
+
* Persisted to GguiSessionStore as a typed render event.
|
|
17
|
+
* - `ping`/`pong` → heartbeat parity with hosted.
|
|
18
|
+
* - `close`/socket-close → clean subscriber teardown.
|
|
19
|
+
* - `sendToGguiSession(sessionId, data)` → outbound fan-out API for
|
|
20
|
+
* mutation handlers (ggui_emit / connector `ctx.send`). Validated
|
|
21
|
+
* through `assertStreamContract` before delivery.
|
|
22
|
+
*
|
|
23
|
+
* `props_update`: the agent-driven `ggui_update` handler calls
|
|
24
|
+
* `channel.sendPropsUpdate(sessionId, props)` (wired as its
|
|
25
|
+
* `propsUpdateNotifier`) to fan a `{type:'props_update'}` frame to
|
|
26
|
+
* live subscribers. Reaches the renderer's existing `props_update`
|
|
27
|
+
* branch in `iframe-runtime` and applies new props in-place.
|
|
28
|
+
*
|
|
29
|
+
* Not handled here:
|
|
30
|
+
*
|
|
31
|
+
* - Pattern-B daemon-agent notification stream (GET /mcp SSE).
|
|
32
|
+
* - Short-lived render-token mint/consume — that's ggui_render-gated.
|
|
33
|
+
* Dev-mode auth (any bearer via existing AuthAdapter) matches the
|
|
34
|
+
* `/mcp` endpoint's shape and is operator-replaceable.
|
|
35
|
+
*
|
|
36
|
+
* Module layout: this file owns the public surface (options, server
|
|
37
|
+
* interface, composer). The handler families live in
|
|
38
|
+
* `./ggui-session-channel/` as factories taking explicit typed deps:
|
|
39
|
+
*
|
|
40
|
+
* - `outbound.ts` — send/fan-out primitives + the public fan-out
|
|
41
|
+
* surfaces (`sendToGguiSession`, `sendPropsUpdate`, …).
|
|
42
|
+
* - `subscriber-lifecycle.ts` — register / live-tail pump /
|
|
43
|
+
* unregister + the 0↔1 subscriber-count hooks.
|
|
44
|
+
* - `subscribe.ts` — upgrade-time identity resolution (bearer /
|
|
45
|
+
* bootstrap / console cookie) + the `subscribe` handler.
|
|
46
|
+
* - `action-ingress.ts` — `action` contract gate + ledger/pipe
|
|
47
|
+
* dual-write + ack.
|
|
48
|
+
* - `channel-subscriptions.ts` — `channel_subscribe` source-tool
|
|
49
|
+
* polling loops.
|
|
50
|
+
* - `socket-router.ts` — `connection` wiring with the per-socket
|
|
51
|
+
* inbound ordering chain + the message-type dispatcher.
|
|
52
|
+
*/
|
|
53
|
+
import type { AuthAdapter, AuthResult, PendingEventConsumer, GguiSessionStore, GguiSessionStreamBuffer, StreamEnvelopeInput, StreamFanout, TelemetrySink } from "@ggui-ai/mcp-server-core";
|
|
54
|
+
import type { JsonObject, GguiSession, ReservedChannelValidator } from "@ggui-ai/protocol";
|
|
55
|
+
import type { WebSocketMessage } from "@ggui-ai/protocol/transport/websocket";
|
|
56
|
+
import type { IncomingMessage } from "node:http";
|
|
57
|
+
import type { Duplex } from "node:stream";
|
|
58
|
+
import { type GguiSessionChannelLocalToolsOptions } from "./ggui-session-channel/channel-subscriptions.js";
|
|
59
|
+
import { type GguiSessionChannelBootstrap, type GguiSessionChannelCookieAuth } from "./ggui-session-channel/subscribe.js";
|
|
60
|
+
import type { Logger } from "./logger.js";
|
|
61
|
+
/** Default URL path for the channel endpoint. Operators can override. */
|
|
62
|
+
export declare const DEFAULT_RENDER_CHANNEL_PATH = "/ws";
|
|
63
|
+
export type { GguiSessionChannelLocalToolsOptions } from "./ggui-session-channel/channel-subscriptions.js";
|
|
64
|
+
export type { GguiSessionChannelBootstrap, GguiSessionChannelBootstrapRefreshResult, GguiSessionChannelBootstrapVerifyResult, GguiSessionChannelCookieAuth, } from "./ggui-session-channel/subscribe.js";
|
|
65
|
+
export interface GguiSessionChannelOptions {
|
|
66
|
+
/** Required — the render backing store (typically `InMemoryGguiSessionStore`). */
|
|
67
|
+
readonly renderStore: GguiSessionStore;
|
|
68
|
+
/**
|
|
69
|
+
* Optional pending-events pipe — the SAME `PendingEventConsumer`
|
|
70
|
+
* instance the `ggui_consume` handler drains and the
|
|
71
|
+
* `ggui_runtime_submit_action` relay appends to. When wired, the WS
|
|
72
|
+
* `action` ingress dual-writes every accepted `data:submit` envelope:
|
|
73
|
+
*
|
|
74
|
+
* 1. `renderStore.appendEvent({type:'user.submitted', …})` — the
|
|
75
|
+
* append-only retained ledger (load-bearing for the ack `seq`
|
|
76
|
+
* and reconnect resume, exactly as before).
|
|
77
|
+
* 2. `pendingEventConsumer.append(sessionId, {id, envelope,
|
|
78
|
+
* createdAt})` — the queue that wakes the agent's `ggui_consume`
|
|
79
|
+
* long-poll. The entry mirrors the relay's consume-entry shape
|
|
80
|
+
* (`ConsumeEventEntry`), so WS-originated gestures and
|
|
81
|
+
* tools/call-relayed gestures drain identically.
|
|
82
|
+
*
|
|
83
|
+
* A failed pipe append (e.g. the pipe was never opened because the
|
|
84
|
+
* render didn't come from `ggui_render`) degrades to ledger-only with
|
|
85
|
+
* a `render_channel_consume_append_failed` warn — ack semantics are
|
|
86
|
+
* unchanged either way.
|
|
87
|
+
*
|
|
88
|
+
* Absent → ledger-only ingress (audit-only deployments, conformance
|
|
89
|
+
* harnesses, and composers that registered no `ggui_consume`).
|
|
90
|
+
* `createGguiServer` threads its shared instance here whenever it
|
|
91
|
+
* composed the default handler set.
|
|
92
|
+
*/
|
|
93
|
+
readonly pendingEventConsumer?: PendingEventConsumer;
|
|
94
|
+
/**
|
|
95
|
+
* Required — the same `AuthAdapter` the `/mcp` endpoint uses. Any
|
|
96
|
+
* failure during `subscribe` rejects the upgrade with HTTP 401.
|
|
97
|
+
*/
|
|
98
|
+
readonly auth: AuthAdapter;
|
|
99
|
+
/**
|
|
100
|
+
* Maps resolved identity → tenant appId for subscribes that omit
|
|
101
|
+
* `payload.appId` (SPEC §12.2 identity-default resolution). Defaults
|
|
102
|
+
* to `defaultAppIdFromIdentity` — same mapping the `/mcp` endpoint
|
|
103
|
+
* uses. Token-bound subscribes (wsToken / console cookie) never
|
|
104
|
+
* consult this seam: their credential already binds the appId.
|
|
105
|
+
*/
|
|
106
|
+
readonly appIdFromIdentity?: (result: AuthResult) => string;
|
|
107
|
+
/** Structured logger. */
|
|
108
|
+
readonly logger: Logger;
|
|
109
|
+
/** URL path to mount on. Defaults to `/ws`. */
|
|
110
|
+
readonly path?: string;
|
|
111
|
+
/**
|
|
112
|
+
* Outbound stream replay buffer. Defaults to a fresh
|
|
113
|
+
* `InMemoryGguiSessionStreamBuffer` when omitted — fine for OSS
|
|
114
|
+
* zero-config / dev. Persistent adapters bind via the same
|
|
115
|
+
* `GguiSessionStreamBuffer` interface when they land.
|
|
116
|
+
*
|
|
117
|
+
* Each channel instance owns its own seq cursor space; sharing a
|
|
118
|
+
* buffer across two channels in the same process would couple their
|
|
119
|
+
* sequences in confusing ways.
|
|
120
|
+
*/
|
|
121
|
+
readonly streamBuffer?: GguiSessionStreamBuffer;
|
|
122
|
+
/**
|
|
123
|
+
* Live-tail pub/sub for outbound live-channel frames. Defaults to a
|
|
124
|
+
* fresh `InProcessStreamFanout` (in-memory, single-process).
|
|
125
|
+
* Multi-process deployments bind a pubsub-backed `StreamFanout`
|
|
126
|
+
* implementation (e.g. Redis) behind this seam for cross-process
|
|
127
|
+
* fan-out.
|
|
128
|
+
*
|
|
129
|
+
* The channel server uses the seam to publish every fanout-eligible
|
|
130
|
+
* envelope and to subscribe one async iterator per WebSocket
|
|
131
|
+
* subscriber — no in-process Map walk; the seam owns routing.
|
|
132
|
+
*/
|
|
133
|
+
readonly streamFanout?: StreamFanout;
|
|
134
|
+
/**
|
|
135
|
+
* Optional bootstrap-auth plumbing. When present, the channel
|
|
136
|
+
* accepts `SubscribePayload.wsToken` and issues reconnect
|
|
137
|
+
* credentials in `AckPayload.sessionToken`. When absent, bootstrap
|
|
138
|
+
* tokens are rejected with `BOOTSTRAP_NOT_SUPPORTED`.
|
|
139
|
+
*/
|
|
140
|
+
readonly bootstrap?: GguiSessionChannelBootstrap;
|
|
141
|
+
/**
|
|
142
|
+
* Optional console cookie-auth plumbing. When present, the
|
|
143
|
+
* channel upgrade looks for the configured cookie on the incoming
|
|
144
|
+
* request. A valid cookie binds the identity as a `builder` and
|
|
145
|
+
* scopes the subscriber to the cookie's `sessionId` — any
|
|
146
|
+
* `subscribe.sessionId` mismatch is rejected with
|
|
147
|
+
* `DEVTOOL_COOKIE_SESSION_MISMATCH`.
|
|
148
|
+
*
|
|
149
|
+
* Absent = cookie auth disabled on this channel. Cookies are never
|
|
150
|
+
* auto-enabled; the server's console composition decides.
|
|
151
|
+
*
|
|
152
|
+
* Design boundary: this auth plane is MUTUALLY EXCLUSIVE with
|
|
153
|
+
* bootstrap auth at upgrade time. When both are configured, the
|
|
154
|
+
* bootstrap path (via `?bootstrap=` query) wins; cookie is only
|
|
155
|
+
* consulted for standard upgrades.
|
|
156
|
+
*/
|
|
157
|
+
readonly cookieAuth?: GguiSessionChannelCookieAuth;
|
|
158
|
+
/**
|
|
159
|
+
* Reserved-channel payload validators for channels whose shape is
|
|
160
|
+
* NOT protocol-owned (Item 4 injection pattern). The primary
|
|
161
|
+
* consumer is `_ggui:preview` — the server composes a
|
|
162
|
+
* {@link ReservedChannelValidator} adapting
|
|
163
|
+
* `@ggui-ai/preview-a2ui::parseServerMessage` so malformed A2UI
|
|
164
|
+
* frames emitted on the preview channel reject at the fan-out
|
|
165
|
+
* boundary instead of landing in the subscriber's renderer.
|
|
166
|
+
*
|
|
167
|
+
* Lookup inside {@link validateStreamData} consults this map FIRST,
|
|
168
|
+
* then the protocol-shipped `BUILTIN_RESERVED_VALIDATORS` (which
|
|
169
|
+
* validates `_ggui:lifecycle`), then falls through to
|
|
170
|
+
* `{valid: true}` when a known reserved channel has no validator.
|
|
171
|
+
*
|
|
172
|
+
* Absent = no `_ggui:preview` validation (documented degradation for
|
|
173
|
+
* implementations without the preview package); `_ggui:lifecycle`
|
|
174
|
+
* is always validated via the built-in.
|
|
175
|
+
*/
|
|
176
|
+
readonly extraReservedValidators?: ReadonlyMap<string, ReservedChannelValidator>;
|
|
177
|
+
/**
|
|
178
|
+
* Optional {@link TelemetrySink} for live-channel operational
|
|
179
|
+
* signals (C12) — operational counts + durations for OTLP /
|
|
180
|
+
* CloudWatch / Datadog forwarders. Reserved seam: the channel
|
|
181
|
+
* defines the binding point but emits no signals of its own today;
|
|
182
|
+
* future operational counts (e.g. subscribe / poll failure rates)
|
|
183
|
+
* bind here so operator sink wiring composes uniformly with the
|
|
184
|
+
* server's `server.composed` signal.
|
|
185
|
+
*
|
|
186
|
+
* Deliberately separate from the renderer's client-side
|
|
187
|
+
* `ObservabilityEvent` surface — two independent consumers (backend
|
|
188
|
+
* metrics vs host inspector UI). See the TelemetrySink docstring
|
|
189
|
+
* for the sync/lossy contract.
|
|
190
|
+
*/
|
|
191
|
+
readonly telemetry?: TelemetrySink;
|
|
192
|
+
/**
|
|
193
|
+
* Opt-in `channel_subscribe` plumbing for `streamSpec[*].source.tool`
|
|
194
|
+
* fan-out. When present, channel subscribes whose
|
|
195
|
+
* `source.tool` is in `allowlist` are accepted and the server begins
|
|
196
|
+
* polling. When absent, every `channel_subscribe` returns
|
|
197
|
+
* `CHANNEL_NOT_LOCAL` so the iframe falls back to direct polling via
|
|
198
|
+
* the MCP host proxy.
|
|
199
|
+
*
|
|
200
|
+
* Same `allowlist` MUST be advertised on
|
|
201
|
+
* `handshake.serverCapabilities.streamWebSocketLocalTools` so iframe
|
|
202
|
+
* + server agree on which channels use the WS fan-out path.
|
|
203
|
+
*/
|
|
204
|
+
readonly streamWebSocketLocalTools?: GguiSessionChannelLocalToolsOptions;
|
|
205
|
+
/**
|
|
206
|
+
* Protocol-version handshake policy. Governs server behavior when a
|
|
207
|
+
* subscribe declares a `supportedVersions` list that does NOT
|
|
208
|
+
* contain this server's {@link PROTOCOL_SCHEMA_VERSION}.
|
|
209
|
+
*
|
|
210
|
+
* - `'reject'` (default): server emits
|
|
211
|
+
* `{type:'error', payload.code: 'UPGRADE_REQUIRED'}` AND closes
|
|
212
|
+
* the underlying WebSocket. The caller cannot accidentally
|
|
213
|
+
* proceed against a version-mismatched render. This is the
|
|
214
|
+
* canonical posture for first-party servers.
|
|
215
|
+
* - `'advisory'` (opt-out): server emits `UPGRADE_REQUIRED`
|
|
216
|
+
* but keeps the connection open — the subscribe stops (no ack,
|
|
217
|
+
* no snapshot, no replay). Existing clients that ignore the error
|
|
218
|
+
* code continue to interoperate exactly as pre-handshake.
|
|
219
|
+
* Use only for controlled migration windows during which
|
|
220
|
+
* legacy-version clients must remain attached.
|
|
221
|
+
*
|
|
222
|
+
* Absent `payload.supportedVersions` always passes through — the
|
|
223
|
+
* handshake is fully opt-in on the client side. `serverVersion` is
|
|
224
|
+
* stamped into every successful ack regardless of policy.
|
|
225
|
+
*
|
|
226
|
+
* Switching between `'advisory'` and `'reject'` is a config change,
|
|
227
|
+
* not a schema change — the wire fields and error code ship
|
|
228
|
+
* identically in both modes.
|
|
229
|
+
*/
|
|
230
|
+
readonly versionPolicy?: "advisory" | "reject";
|
|
231
|
+
/**
|
|
232
|
+
* Optional hook fired synchronously when the local subscriber count
|
|
233
|
+
* for `sessionId` transitions 0 → 1 (the first subscriber for that
|
|
234
|
+
* render connects to this server instance).
|
|
235
|
+
*
|
|
236
|
+
* Hosted deployments use this to lazily SUBSCRIBE to the per-render
|
|
237
|
+
* cross-pod broadcast channel (e.g. Redis pub/sub); OSS has no use
|
|
238
|
+
* for it (in-process broadcasts already route via
|
|
239
|
+
* {@link GguiSessionChannelServer.sendPropsUpdate}). Bounding pubsub
|
|
240
|
+
* fan-in to only renders a pod actually holds connections for is a
|
|
241
|
+
* correctness requirement, not an optimization — without it every
|
|
242
|
+
* pod receives every other pod's broadcast for every active render.
|
|
243
|
+
*
|
|
244
|
+
* Best-effort: a thrown callback is logged and swallowed.
|
|
245
|
+
* `register()` MUST NOT fail because of a hook error or the
|
|
246
|
+
* `wsSubscribers` set would drift out of sync with the real socket
|
|
247
|
+
* lifecycle.
|
|
248
|
+
*
|
|
249
|
+
* Concurrent register/unregister for the same sessionId are serialized
|
|
250
|
+
* by the channel's single-threaded WS event loop; hook implementations
|
|
251
|
+
* do not need their own mutex for the 0↔1 transition.
|
|
252
|
+
*/
|
|
253
|
+
readonly onFirstSubscriber?: (sessionId: string) => void;
|
|
254
|
+
/**
|
|
255
|
+
* Optional hook fired synchronously when the local subscriber count
|
|
256
|
+
* for `sessionId` transitions 1 → 0 (the last subscriber for that
|
|
257
|
+
* render disconnects).
|
|
258
|
+
*
|
|
259
|
+
* Symmetric with {@link onFirstSubscriber}; same best-effort posture
|
|
260
|
+
* and single-threaded serialization guarantee.
|
|
261
|
+
*/
|
|
262
|
+
readonly onLastSubscriberGone?: (sessionId: string) => void;
|
|
263
|
+
}
|
|
264
|
+
export interface GguiSessionChannelServer {
|
|
265
|
+
/** The URL path the channel accepts upgrade requests on. */
|
|
266
|
+
readonly path: string;
|
|
267
|
+
/**
|
|
268
|
+
* Wire this into the HTTP server's `upgrade` event. Rejects with 401
|
|
269
|
+
* on auth failure; otherwise completes the WS handshake and wires
|
|
270
|
+
* the subscriber.
|
|
271
|
+
*/
|
|
272
|
+
handleUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer): void;
|
|
273
|
+
/**
|
|
274
|
+
* Deliver a stream envelope to every subscriber of `delivery.sessionId`.
|
|
275
|
+
*
|
|
276
|
+
* Outbound fan-out enforcement point — the delivery's `payload` is
|
|
277
|
+
* validated against the active render's streamSpec via
|
|
278
|
+
* `assertStreamContract` before any subscriber receives it.
|
|
279
|
+
*
|
|
280
|
+
* Sequencing + replay behavior:
|
|
281
|
+
* 1. Payload validated against the active render's streamSpec.
|
|
282
|
+
* 2. The replay buffer assigns a render-scoped monotonic `seq` and
|
|
283
|
+
* (conditionally) stores the stamped envelope per the channel's
|
|
284
|
+
* replay policy (`'none'` skip / `'latest'` single-slot /
|
|
285
|
+
* `'all'` FIFO ring).
|
|
286
|
+
* 3. The stamped envelope fans out to every subscriber for the
|
|
287
|
+
* render, skipping any whose initial replay already covered
|
|
288
|
+
* this seq (prevents double delivery on reconnect).
|
|
289
|
+
*
|
|
290
|
+
* The caller supplies `StreamEnvelopeInput` (no seq); the server
|
|
291
|
+
* stamps. Returns the stamped `seq` for callers that need to thread
|
|
292
|
+
* ordering back to their own response (e.g., `ggui_emit`'s wire
|
|
293
|
+
* output). When the channel's replay policy was `'none'`, seq is
|
|
294
|
+
* still assigned (so fan-out has a stable cursor) but nothing is
|
|
295
|
+
* stored — the seq still surfaces here so the caller has the same
|
|
296
|
+
* shape regardless of replay policy.
|
|
297
|
+
*
|
|
298
|
+
* Throws `ContractViolationError` on payload mismatch; transport
|
|
299
|
+
* errors are logged but not propagated (per-subscriber best-effort).
|
|
300
|
+
*/
|
|
301
|
+
sendToGguiSession(delivery: StreamEnvelopeInput): Promise<{
|
|
302
|
+
seq: number;
|
|
303
|
+
}>;
|
|
304
|
+
/**
|
|
305
|
+
* Fan a `{type:'render', payload:{render, matchType?}}` wire frame
|
|
306
|
+
* to every subscriber currently bound to `sessionId`. Use this to
|
|
307
|
+
* notify already-subscribed clients about a render-commit that
|
|
308
|
+
* happened AFTER they subscribed — the initial `ack.render` snapshot
|
|
309
|
+
* covers state at subscribe time only, so without an explicit notify
|
|
310
|
+
* a second-turn `commit` is invisible to the live client.
|
|
311
|
+
*
|
|
312
|
+
* The `B1` regression context (2026-04-22 QA pass): the chat surface
|
|
313
|
+
* in `/chat` reuses one render across turns. The first turn's render
|
|
314
|
+
* subscribed AFTER `commit` ran, so the ack carried the
|
|
315
|
+
* entry. The second turn's commit landed on the live render — the
|
|
316
|
+
* subscriber never heard about the new entry, the inline UI slot
|
|
317
|
+
* stayed in "Waiting for render channel replay…" indefinitely.
|
|
318
|
+
* `notifyGguiSessionCommit` closes that gap. Best-effort: per-subscriber
|
|
319
|
+
* send failures are swallowed (same posture as `sendToGguiSession`).
|
|
320
|
+
*
|
|
321
|
+
* NOT durable. Frames are not stamped through the replay buffer —
|
|
322
|
+
* fresh subscribers still get the current render via `ack.render` on
|
|
323
|
+
* subscribe. A new tab opening mid-render reads the latest render
|
|
324
|
+
* from the snapshot; live tabs read the delta from this notify.
|
|
325
|
+
*
|
|
326
|
+
* Subscribers received via `register()` are tracked in
|
|
327
|
+
* `subscribersByRender`; this helper iterates the bound set and
|
|
328
|
+
* skips closed sockets via the `send()` helper's existing guard.
|
|
329
|
+
* Callers ARE responsible for ordering — call after the underlying
|
|
330
|
+
* `renderStore.commit` resolves so the snapshot a
|
|
331
|
+
* concurrent fresh subscriber observes still includes the entry.
|
|
332
|
+
*/
|
|
333
|
+
notifyGguiSessionCommit(sessionId: string, render: GguiSession, matchType?: string): void;
|
|
334
|
+
/**
|
|
335
|
+
* Fan a `{type:'props_update', payload:{sessionId, props}}` wire frame
|
|
336
|
+
* to every subscriber currently bound to `sessionId`. The agent-driven
|
|
337
|
+
* `ggui_update` handler calls this (wired as its `propsUpdateNotifier`)
|
|
338
|
+
* so a props patch replaces renderer props in-place on live
|
|
339
|
+
* subscribers without waiting for a resubscribe.
|
|
340
|
+
*
|
|
341
|
+
* Validation posture (mirrors `notifyGguiSessionCommit`'s "best-effort orphan
|
|
342
|
+
* no-op"):
|
|
343
|
+
* 1. Look up the render via `renderStore.get`. Absent → log
|
|
344
|
+
* `render_channel_props_update_orphan` and return — the wire
|
|
345
|
+
* validator on the renderer side would reject a frame for an
|
|
346
|
+
* unknown render anyway.
|
|
347
|
+
* 2. Iterate the flat WS-subscriber set, filter to subscribers
|
|
348
|
+
* whose `sessionId` matches, and `send()` the frame. Closed
|
|
349
|
+
* sockets are skipped silently by `send()`.
|
|
350
|
+
*
|
|
351
|
+
* NOT routed through StreamFanout — `type: 'props_update'` is a
|
|
352
|
+
* distinct WebSocket message type, not a stream envelope. Stream
|
|
353
|
+
* envelopes flow on `data` frames and have a `seq` cursor; props
|
|
354
|
+
* updates are ephemeral and follow `notifyGguiSessionCommit`'s pattern
|
|
355
|
+
* (live-only, no replay-buffer stamping). A new subscriber that
|
|
356
|
+
* connects mid-render reads current `props` from the render
|
|
357
|
+
* snapshot delivered in `ack.render`.
|
|
358
|
+
*
|
|
359
|
+
* Schema validation against `propsSpec`: NOT enforced server-side
|
|
360
|
+
* here. The `ggui_update` handler validates the patch against the
|
|
361
|
+
* render's `propsSpec` BEFORE persisting and notifying, and the
|
|
362
|
+
* renderer re-validates inbound props via
|
|
363
|
+
* `validateInboundPropsPayload` against the cached
|
|
364
|
+
* `render.propsSpec` before applying — defense-in-depth at the
|
|
365
|
+
* receiving boundary.
|
|
366
|
+
*/
|
|
367
|
+
sendPropsUpdate(sessionId: string, props: JsonObject): Promise<void>;
|
|
368
|
+
/**
|
|
369
|
+
* Fan a `{type:'drain_ack', payload:{sessionId, appId,
|
|
370
|
+
* eventId, drainedAt}}` wire frame to every subscriber currently
|
|
371
|
+
* bound to `sessionId`.
|
|
372
|
+
*
|
|
373
|
+
* Fired by `createGguiConsumeHandler` once per drained
|
|
374
|
+
* `PendingEvent` so the iframe-runtime can cancel the matching
|
|
375
|
+
* per-action 10s claim timer + resolve the toast as `consumed`.
|
|
376
|
+
* Implements the `DrainAckNotifier` contract from
|
|
377
|
+
* `@ggui-ai/mcp-server-handlers`.
|
|
378
|
+
*
|
|
379
|
+
* Same posture as `notifyGguiSessionCommit` / `sendPropsUpdate` — live-only,
|
|
380
|
+
* no replay-buffer stamping. Subscribers that connect AFTER the
|
|
381
|
+
* drain see the next consume's snapshot rather than the missed
|
|
382
|
+
* frame; the iframe's claim timer + atomic-pop primitive backstop
|
|
383
|
+
* any frame loss.
|
|
384
|
+
*/
|
|
385
|
+
sendDrainAck(args: {
|
|
386
|
+
readonly sessionId: string;
|
|
387
|
+
readonly appId: string;
|
|
388
|
+
readonly eventId: string;
|
|
389
|
+
readonly drainedAt: string;
|
|
390
|
+
}): void;
|
|
391
|
+
/**
|
|
392
|
+
* Fan a server-frame to every local WS subscriber bound to
|
|
393
|
+
* `sessionId`. Skips replay-buffer stamping, GguiSessionStore lookups,
|
|
394
|
+
* and contract validation — the caller is the one that originally
|
|
395
|
+
* validated + persisted the underlying mutation. This surface is the
|
|
396
|
+
* delivery path for already-validated frames that arrived via an
|
|
397
|
+
* external pubsub layer (e.g. Redis from another process of a
|
|
398
|
+
* multi-process deployment).
|
|
399
|
+
*
|
|
400
|
+
* Internal adapter use only. NOT part of the published ggui
|
|
401
|
+
* protocol, NOT stable across versions, NOT exposed to MCP / wire
|
|
402
|
+
* callers. The publisher is responsible for ensuring `frame` is
|
|
403
|
+
* wire-valid; this method does not re-validate.
|
|
404
|
+
*
|
|
405
|
+
* No-op when no local subscriber is bound to `sessionId`. Closed
|
|
406
|
+
* sockets are skipped silently by the underlying `send()` helper —
|
|
407
|
+
* same posture as `sendPropsUpdate` / `notifyGguiSessionCommit`. Per-
|
|
408
|
+
* subscriber send failures are logged but never propagated.
|
|
409
|
+
*/
|
|
410
|
+
externalBroadcast(sessionId: string, frame: WebSocketMessage): void;
|
|
411
|
+
/** Number of live subscribers. Useful for health / debug introspection. */
|
|
412
|
+
readonly subscriberCount: number;
|
|
413
|
+
/** Number of distinct renders with at least one subscriber. */
|
|
414
|
+
readonly renderCount: number;
|
|
415
|
+
/**
|
|
416
|
+
* Close every live subscriber + the underlying ws server. Idempotent.
|
|
417
|
+
*/
|
|
418
|
+
close(): Promise<void>;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Build an OSS live-channel server. The returned object is designed to be
|
|
422
|
+
* composed into `createGguiServer` — see `server.ts` for the wire-up.
|
|
423
|
+
*/
|
|
424
|
+
export declare function createGguiSessionChannelServer(opts: GguiSessionChannelOptions): GguiSessionChannelServer;
|
|
425
|
+
//# sourceMappingURL=ggui-session-channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ggui-session-channel.d.ts","sourceRoot":"","sources":["../src/ggui-session-channel.ts"],"names":[],"mappings":"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"}
|