@holostaff/sdk 0.3.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.
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Fast-path signals — Wave 3b (per runtime doc §6.2).
3
+ *
4
+ * Lightweight deterministic context the SDK gathers in the browser and
5
+ * forwards to the per-stage monitoring agent:
6
+ *
7
+ * - Current SPA route (location.pathname). Tracked via popstate and
8
+ * wrapped history.pushState / history.replaceState.
9
+ * - Time-of-last-user-interaction. Tracked via window-level
10
+ * mousemove / scroll / keydown / touchstart listeners (passive).
11
+ *
12
+ * Posted to `POST /api/runtime/sessions/:sessionId/fastpath` on:
13
+ * - Initial mount (so the agent has a starting route even before any
14
+ * activity).
15
+ * - Each route change (debounced — coalesces rapid back-to-back
16
+ * navigations into a single emit ~80ms later).
17
+ * - A low-frequency heartbeat (HEARTBEAT_MS) so the server's view of
18
+ * the user's idle/active state stays current even on a static page.
19
+ *
20
+ * Iframe / shadow-DOM / canvas / native-webview caveats from the doc
21
+ * apply — for hosts where window-level listeners don't catch user
22
+ * activity, idle detection falls back to vision-only (the timer simply
23
+ * never updates).
24
+ */
25
+ import { type TransportConfig } from './transport.js';
26
+ export interface SignalsBindingDeps {
27
+ cfg: TransportConfig;
28
+ sessionId: string;
29
+ tenantId: string;
30
+ sourceId: string;
31
+ /** Builds the common body fields (deviceId, hostUserId, tenant/source). */
32
+ buildBody: () => Record<string, unknown>;
33
+ }
34
+ export interface SignalsBinding {
35
+ /** Detach all listeners and cancel pending timers. */
36
+ dispose(): void;
37
+ }
38
+ /**
39
+ * Start the signals subsystem. Idempotent across calls is the caller's
40
+ * responsibility — the holostaff singleton ensures one binding per
41
+ * session via _destroy on re-init.
42
+ */
43
+ export declare function startSignals(deps: SignalsBindingDeps): SignalsBinding;
44
+ //# sourceMappingURL=signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAS/D,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,eAAe,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACzC;AAED,MAAM,WAAW,cAAc;IAC7B,sDAAsD;IACtD,OAAO,IAAI,IAAI,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CA4IrE"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Fast-path signals — Wave 3b (per runtime doc §6.2).
3
+ *
4
+ * Lightweight deterministic context the SDK gathers in the browser and
5
+ * forwards to the per-stage monitoring agent:
6
+ *
7
+ * - Current SPA route (location.pathname). Tracked via popstate and
8
+ * wrapped history.pushState / history.replaceState.
9
+ * - Time-of-last-user-interaction. Tracked via window-level
10
+ * mousemove / scroll / keydown / touchstart listeners (passive).
11
+ *
12
+ * Posted to `POST /api/runtime/sessions/:sessionId/fastpath` on:
13
+ * - Initial mount (so the agent has a starting route even before any
14
+ * activity).
15
+ * - Each route change (debounced — coalesces rapid back-to-back
16
+ * navigations into a single emit ~80ms later).
17
+ * - A low-frequency heartbeat (HEARTBEAT_MS) so the server's view of
18
+ * the user's idle/active state stays current even on a static page.
19
+ *
20
+ * Iframe / shadow-DOM / canvas / native-webview caveats from the doc
21
+ * apply — for hosts where window-level listeners don't catch user
22
+ * activity, idle detection falls back to vision-only (the timer simply
23
+ * never updates).
24
+ */
25
+ import { postJson } from './transport.js';
26
+ /** Heartbeat cadence. ~30s matches the runtime doc's expected
27
+ * per-cycle cadence (§7) and keeps cost low for static-page sessions. */
28
+ const HEARTBEAT_MS = 30000;
29
+ /** Route-change debounce — coalesces rapid navigations. */
30
+ const ROUTE_DEBOUNCE_MS = 80;
31
+ /**
32
+ * Start the signals subsystem. Idempotent across calls is the caller's
33
+ * responsibility — the holostaff singleton ensures one binding per
34
+ * session via _destroy on re-init.
35
+ */
36
+ export function startSignals(deps) {
37
+ if (typeof window === 'undefined') {
38
+ return { dispose: () => { } };
39
+ }
40
+ // -------- State ---------------------------------------------------------
41
+ let lastReportedRoute = null;
42
+ let lastActivityAt = new Date().toISOString();
43
+ let pendingRouteTimer = null;
44
+ let heartbeatTimer = null;
45
+ let disposed = false;
46
+ const currentRoute = () => {
47
+ try {
48
+ return window.location.pathname + window.location.search;
49
+ }
50
+ catch {
51
+ return '/';
52
+ }
53
+ };
54
+ const send = (route) => {
55
+ if (disposed)
56
+ return;
57
+ void postJson(deps.cfg, `/api/runtime/sessions/${deps.sessionId}/fastpath`, {
58
+ ...deps.buildBody(),
59
+ route,
60
+ lastActivityAt,
61
+ });
62
+ };
63
+ const flushRoute = () => {
64
+ pendingRouteTimer = null;
65
+ const cur = currentRoute();
66
+ if (cur === lastReportedRoute)
67
+ return;
68
+ lastReportedRoute = cur;
69
+ send(cur);
70
+ };
71
+ const scheduleRouteFlush = () => {
72
+ if (pendingRouteTimer)
73
+ return;
74
+ pendingRouteTimer = setTimeout(flushRoute, ROUTE_DEBOUNCE_MS);
75
+ };
76
+ // -------- Activity listeners --------------------------------------------
77
+ const onActivity = () => {
78
+ lastActivityAt = new Date().toISOString();
79
+ // Do NOT send immediately on every mousemove — the heartbeat will
80
+ // carry the updated lastActivityAt. We only emit eagerly on route
81
+ // changes; activity ticks are cheap to batch.
82
+ };
83
+ // -------- Route change listeners ----------------------------------------
84
+ const onPopstate = () => { scheduleRouteFlush(); };
85
+ const onHashchange = () => { scheduleRouteFlush(); };
86
+ const originalPushState = window.history.pushState;
87
+ const originalReplaceState = window.history.replaceState;
88
+ const wrappedPushState = function (...args) {
89
+ const result = originalPushState.apply(window.history, args);
90
+ scheduleRouteFlush();
91
+ return result;
92
+ };
93
+ const wrappedReplaceState = function (...args) {
94
+ const result = originalReplaceState.apply(window.history, args);
95
+ scheduleRouteFlush();
96
+ return result;
97
+ };
98
+ window.history.pushState = wrappedPushState;
99
+ window.history.replaceState = wrappedReplaceState;
100
+ // -------- Wire it up ----------------------------------------------------
101
+ // Passive activity listeners — won't block scroll perf.
102
+ const PASSIVE = { passive: true };
103
+ window.addEventListener('mousemove', onActivity, PASSIVE);
104
+ window.addEventListener('scroll', onActivity, PASSIVE);
105
+ window.addEventListener('keydown', onActivity, PASSIVE);
106
+ window.addEventListener('touchstart', onActivity, PASSIVE);
107
+ window.addEventListener('popstate', onPopstate);
108
+ window.addEventListener('hashchange', onHashchange);
109
+ // Initial emit — captures the landing route before any nav happens.
110
+ const initialRoute = currentRoute();
111
+ lastReportedRoute = initialRoute;
112
+ send(initialRoute);
113
+ // Heartbeat — fixed cadence. We don't skip when "idle" because the
114
+ // server uses the heartbeat itself as evidence the tab is still alive;
115
+ // idle vs. active is derived from lastActivityAt.
116
+ heartbeatTimer = setInterval(() => {
117
+ if (disposed)
118
+ return;
119
+ // Emit current route too — cheap insurance against missed route
120
+ // change events on exotic SPAs.
121
+ send(currentRoute());
122
+ }, HEARTBEAT_MS);
123
+ // -------- Dispose -------------------------------------------------------
124
+ return {
125
+ dispose() {
126
+ if (disposed)
127
+ return;
128
+ disposed = true;
129
+ window.removeEventListener('mousemove', onActivity);
130
+ window.removeEventListener('scroll', onActivity);
131
+ window.removeEventListener('keydown', onActivity);
132
+ window.removeEventListener('touchstart', onActivity);
133
+ window.removeEventListener('popstate', onPopstate);
134
+ window.removeEventListener('hashchange', onHashchange);
135
+ // Restore the original History API methods. Safe: even if some
136
+ // other lib has further-wrapped them in the meantime, restoring
137
+ // ours keeps the chain intact (no-op if equal).
138
+ if (window.history.pushState === wrappedPushState) {
139
+ window.history.pushState = originalPushState;
140
+ }
141
+ if (window.history.replaceState === wrappedReplaceState) {
142
+ window.history.replaceState = originalReplaceState;
143
+ }
144
+ if (heartbeatTimer != null) {
145
+ clearInterval(heartbeatTimer);
146
+ heartbeatTimer = null;
147
+ }
148
+ if (pendingRouteTimer != null) {
149
+ clearTimeout(pendingRouteTimer);
150
+ pendingRouteTimer = null;
151
+ }
152
+ },
153
+ };
154
+ }
155
+ //# sourceMappingURL=signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signals.js","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Transport — fetch + sendBeacon wrappers for runtime endpoints.
3
+ *
4
+ * Design rules:
5
+ * - Fail-soft: transport never throws into customer code. Errors
6
+ * route to the `onError` callback if the caller provided one.
7
+ * - Best-effort: no retry queue in Wave 1c. Network blips are
8
+ * tolerated; the next call carries the state forward.
9
+ * - sendBeacon: used for the pagehide/close path so the request
10
+ * survives navigation (Beacon API uses POST with keepalive).
11
+ */
12
+ import type { InitOptions } from './types.js';
13
+ export interface TransportConfig {
14
+ baseUrl: string;
15
+ apiKey?: string;
16
+ onError?: InitOptions['onError'];
17
+ }
18
+ export declare function resolveTransportConfig(opts: InitOptions): TransportConfig;
19
+ /**
20
+ * POST JSON to the runtime. Returns true on 2xx, false on transport
21
+ * or HTTP error. Errors route to onError; never throws.
22
+ */
23
+ export declare function postJson(cfg: TransportConfig, path: string, body: Record<string, unknown>): Promise<boolean>;
24
+ /**
25
+ * POST via sendBeacon — survives page navigation. Used by the close
26
+ * path on pagehide/beforeunload. Falls back to fetch with keepalive
27
+ * if sendBeacon is unavailable (older browsers, Node).
28
+ */
29
+ export declare function postBeacon(cfg: TransportConfig, path: string, body: Record<string, unknown>): boolean;
30
+ /**
31
+ * Open an SSE channel for intervention commands. Returns the
32
+ * EventSource so the caller can attach listeners + close it.
33
+ *
34
+ * Wave 1c: the channel opens but the server doesn't dispatch any
35
+ * `fire_intervention` events yet (Wave 4). We still establish it now
36
+ * so customer apps wire it up once and reap commands automatically
37
+ * when the firing engine lands.
38
+ */
39
+ export declare function openCommandStream(cfg: TransportConfig, sessionId: string, tenantId: string, sourceId: string): EventSource | null;
40
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAI7C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;CACjC;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,CAMzE;AAQD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC,CAwBlB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CA2BT;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,WAAW,GAAG,IAAI,CAapB"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Transport — fetch + sendBeacon wrappers for runtime endpoints.
3
+ *
4
+ * Design rules:
5
+ * - Fail-soft: transport never throws into customer code. Errors
6
+ * route to the `onError` callback if the caller provided one.
7
+ * - Best-effort: no retry queue in Wave 1c. Network blips are
8
+ * tolerated; the next call carries the state forward.
9
+ * - sendBeacon: used for the pagehide/close path so the request
10
+ * survives navigation (Beacon API uses POST with keepalive).
11
+ */
12
+ const DEFAULT_BASE_URL = 'https://holostaff-vision-1008066443043.us-central1.run.app';
13
+ export function resolveTransportConfig(opts) {
14
+ return {
15
+ baseUrl: opts.baseUrl ?? DEFAULT_BASE_URL,
16
+ apiKey: opts.apiKey,
17
+ onError: opts.onError,
18
+ };
19
+ }
20
+ function buildHeaders(cfg) {
21
+ const headers = { 'content-type': 'application/json' };
22
+ if (cfg.apiKey)
23
+ headers['authorization'] = `Bearer ${cfg.apiKey}`;
24
+ return headers;
25
+ }
26
+ /**
27
+ * POST JSON to the runtime. Returns true on 2xx, false on transport
28
+ * or HTTP error. Errors route to onError; never throws.
29
+ */
30
+ export async function postJson(cfg, path, body) {
31
+ try {
32
+ const res = await fetch(`${cfg.baseUrl}${path}`, {
33
+ method: 'POST',
34
+ headers: buildHeaders(cfg),
35
+ body: JSON.stringify(body),
36
+ // The runtime endpoints accept anonymous CORS. credentials='omit'
37
+ // keeps host-app cookies out of cross-origin requests by default.
38
+ credentials: 'omit',
39
+ mode: 'cors',
40
+ });
41
+ if (!res.ok) {
42
+ const text = await res.text().catch(() => '');
43
+ cfg.onError?.(new Error(`HTTP ${res.status} ${res.statusText}${text ? ` — ${text}` : ''}`), { method: 'POST', path });
44
+ return false;
45
+ }
46
+ return true;
47
+ }
48
+ catch (err) {
49
+ cfg.onError?.(err instanceof Error ? err : new Error(String(err)), { method: 'POST', path });
50
+ return false;
51
+ }
52
+ }
53
+ /**
54
+ * POST via sendBeacon — survives page navigation. Used by the close
55
+ * path on pagehide/beforeunload. Falls back to fetch with keepalive
56
+ * if sendBeacon is unavailable (older browsers, Node).
57
+ */
58
+ export function postBeacon(cfg, path, body) {
59
+ const url = `${cfg.baseUrl}${path}`;
60
+ const payload = JSON.stringify(body);
61
+ if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
62
+ try {
63
+ const blob = new Blob([payload], { type: 'application/json' });
64
+ return navigator.sendBeacon(url, blob);
65
+ }
66
+ catch (err) {
67
+ cfg.onError?.(err instanceof Error ? err : new Error(String(err)), { method: 'BEACON', path });
68
+ return false;
69
+ }
70
+ }
71
+ // Fallback: keepalive fetch (also designed to survive unload).
72
+ try {
73
+ void fetch(url, {
74
+ method: 'POST',
75
+ headers: buildHeaders(cfg),
76
+ body: payload,
77
+ keepalive: true,
78
+ credentials: 'omit',
79
+ mode: 'cors',
80
+ });
81
+ return true;
82
+ }
83
+ catch (err) {
84
+ cfg.onError?.(err instanceof Error ? err : new Error(String(err)), { method: 'BEACON', path });
85
+ return false;
86
+ }
87
+ }
88
+ /**
89
+ * Open an SSE channel for intervention commands. Returns the
90
+ * EventSource so the caller can attach listeners + close it.
91
+ *
92
+ * Wave 1c: the channel opens but the server doesn't dispatch any
93
+ * `fire_intervention` events yet (Wave 4). We still establish it now
94
+ * so customer apps wire it up once and reap commands automatically
95
+ * when the firing engine lands.
96
+ */
97
+ export function openCommandStream(cfg, sessionId, tenantId, sourceId) {
98
+ if (typeof EventSource === 'undefined')
99
+ return null;
100
+ const qs = new URLSearchParams({ tenantId, sourceId });
101
+ const url = `${cfg.baseUrl}/api/runtime/sessions/${encodeURIComponent(sessionId)}/commands?${qs}`;
102
+ try {
103
+ return new EventSource(url);
104
+ }
105
+ catch (err) {
106
+ cfg.onError?.(err instanceof Error ? err : new Error(String(err)), {
107
+ method: 'SSE',
108
+ path: `/api/runtime/sessions/${sessionId}/commands`,
109
+ });
110
+ return null;
111
+ }
112
+ }
113
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Public types for the SDK.
3
+ *
4
+ * `BowtieStage` is duplicated here from the server schema (4th mirror,
5
+ * see CLAUDE.md memory `project_stage_decomposition_mirrors`). The
6
+ * SDK ships to customer codebases and can't depend on server internals.
7
+ * If a stage is added, all four mirrors update in lockstep.
8
+ */
9
+ export type BowtieStage = 'awareness' | 'education' | 'selection' | 'mutual_commit' | 'onboarding' | 'adoption' | 'expansion';
10
+ export declare const BOWTIE_STAGES: readonly BowtieStage[];
11
+ export interface InitOptions {
12
+ /** The knowledge source id (e.g. 'cli-source-abc'). Provided by the deploy PR. */
13
+ sourceId: string;
14
+ /**
15
+ * Tenant id. Wave 1: SDK callers pass this explicitly. Future work
16
+ * derives it from a signed source token so customer code only needs
17
+ * sourceId.
18
+ */
19
+ tenantId: string;
20
+ /** Base URL of the holostaff runtime. Defaults to the public Cloud Run URL. */
21
+ baseUrl?: string;
22
+ /**
23
+ * Optional API key. Reserved for the per-source auth model that
24
+ * lands once SDK auth ships (runtime doc §11). Wave 1 callers can
25
+ * omit; the runtime endpoints are dev-token gated server-side.
26
+ */
27
+ apiKey?: string;
28
+ /**
29
+ * Optional callback fired on transport errors (network failures,
30
+ * non-2xx responses). The SDK never throws into customer code; this
31
+ * is the only way to observe failures.
32
+ */
33
+ onError?: (err: Error, ctx: {
34
+ method: string;
35
+ path: string;
36
+ }) => void;
37
+ }
38
+ export type InterventionOutcome = 'pending' | 'dismissed' | 'engaged' | 'progressed' | 'ignored' | 'errored';
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,YAAY,GACZ,UAAU,GACV,WAAW,CAAA;AAEf,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAQtC,CAAA;AAEV,MAAM,WAAW,WAAW;IAC1B,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE;AAED,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,WAAW,GACX,SAAS,GACT,YAAY,GACZ,SAAS,GACT,SAAS,CAAA"}
package/dist/types.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Public types for the SDK.
3
+ *
4
+ * `BowtieStage` is duplicated here from the server schema (4th mirror,
5
+ * see CLAUDE.md memory `project_stage_decomposition_mirrors`). The
6
+ * SDK ships to customer codebases and can't depend on server internals.
7
+ * If a stage is added, all four mirrors update in lockstep.
8
+ */
9
+ export const BOWTIE_STAGES = [
10
+ 'awareness',
11
+ 'education',
12
+ 'selection',
13
+ 'mutual_commit',
14
+ 'onboarding',
15
+ 'adoption',
16
+ 'expansion',
17
+ ];
18
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,WAAW;IACX,WAAW;IACX,WAAW;IACX,eAAe;IACf,YAAY;IACZ,UAAU;IACV,WAAW;CACH,CAAA"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Widget — Wave 3e.
3
+ *
4
+ * Listens to the SSE command channel for `fire_intervention` events
5
+ * and renders a minimal text pill in the customer's host page. Wires
6
+ * dismiss / engage user actions back to /outcome.
7
+ *
8
+ * Wave 3e scope:
9
+ * - `text` modality only. Other modalities (voice / email / sms /
10
+ * phone / screen_automation) are logged and ignored — Wave 6.
11
+ * - Single visible widget at a time. A new event while one is open
12
+ * replaces it (and reports 'ignored' for the prior).
13
+ * - No theming. Inline-styled floating pill, fixed lower-right.
14
+ *
15
+ * The widget keeps zero dependencies on host CSS — it injects its own
16
+ * styles into the shadow root of a top-level container so host page
17
+ * rules can't bleed in or out.
18
+ */
19
+ import { type TransportConfig } from './transport.js';
20
+ export interface WidgetBindingDeps {
21
+ cfg: TransportConfig;
22
+ /** Resolves the current session id at event time (may rotate after clearIdentity). */
23
+ getSessionId: () => string;
24
+ /** Common body builder — tenantId/sourceId/deviceId/hostUserId. */
25
+ buildBody: () => Record<string, unknown>;
26
+ /** SSE source — the same EventSource the SDK already opened. */
27
+ source: EventSource | null;
28
+ }
29
+ export interface WidgetBinding {
30
+ dispose(): void;
31
+ }
32
+ /**
33
+ * Start the widget subsystem. Subscribes to the SSE `fire_intervention`
34
+ * event and shows the text pill when one arrives. Idempotent in the
35
+ * sense that the SDK singleton ensures one binding per session.
36
+ */
37
+ export declare function startWidget(deps: WidgetBindingDeps): WidgetBinding;
38
+ //# sourceMappingURL=widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"widget.d.ts","sourceRoot":"","sources":["../src/widget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAS/D,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,eAAe,CAAA;IACpB,sFAAsF;IACtF,YAAY,EAAE,MAAM,MAAM,CAAA;IAC1B,mEAAmE;IACnE,SAAS,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,gEAAgE;IAChE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,IAAI,IAAI,CAAA;CAChB;AAUD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,iBAAiB,GAAG,aAAa,CAiDlE"}