@kibee/contracts 0.2.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,588 @@
1
+ /**
2
+ * Frozen Sprint 1 contract. Edits require joint sign-off from Track A
3
+ * (broker) and Track B (macOS) leads.
4
+ *
5
+ * Spec: docs/superpowers/specs/2026-04-28-bee-mesh-sprint-plan-design.md
6
+ * Section 2 (Architecture — mesh of bees), Section 3 (Source-of-truth
7
+ * contracts).
8
+ *
9
+ * Mirrored to Swift in apps/macos-kibee (Sprint 1 T5/T7 — BridgeEnvelope
10
+ * Swift port lands in Sprint 4 convergence).
11
+ *
12
+ * Wire-stable note: the string raw values for `SurfaceId` and
13
+ * `BridgeEventType` (e.g. "presence:hello") are part of the wire format.
14
+ * Do not rename or re-shape them in the Swift mirror — they must JSON-encode
15
+ * to the exact same strings.
16
+ */
17
+ /**
18
+ * Surfaces that participate in the bee mesh. Every BridgeEnvelope declares
19
+ * its source and target by surface id.
20
+ */
21
+ type SurfaceId = "web" | "extension" | "desktop";
22
+ /**
23
+ * The frozen event-type union for the cross-surface bus. Adding a new event
24
+ * type after Sprint 1 requires joint sign-off from both track leads.
25
+ */
26
+ type BridgeEventType = "presence:hello" | "presence:bye" | "flow:handoff" | "flow:resume" | "pointer:show" | "memory:share" | "voice:state" | "capture:promote" | "tenant:attach" | "tenant:detach";
27
+ /**
28
+ * The wire format for every cross-surface message. `tenantId` is `null`
29
+ * when the surface is in personal mode (not attached to a workspace).
30
+ */
31
+ interface BridgeEnvelope<T = unknown> {
32
+ /** Protocol version. Bump on any breaking change. */
33
+ v: 1;
34
+ source: SurfaceId;
35
+ /** A specific surface or "broadcast" to all subscribers in the same scope. */
36
+ target: SurfaceId | "broadcast";
37
+ /** Null in personal mode; required for tenant-attached and admin-attached modes. */
38
+ tenantId: string | null;
39
+ workspaceId: string | null;
40
+ /** Stable across surfaces for the same visitor. Drives the fan-out scope. */
41
+ visitorId: string;
42
+ sessionId: string | null;
43
+ type: BridgeEventType;
44
+ /** Wall-clock millis at emit. */
45
+ ts: number;
46
+ payload: T;
47
+ }
48
+ /**
49
+ * Type guard usable at runtime when receiving an envelope from the wire.
50
+ * Validates every field strictly — unknown surface ids, unknown event types,
51
+ * missing fields, or arrays are all rejected. Use at the trust boundary
52
+ * (broker, SDK receive handler, Swift port) before treating data as a
53
+ * `BridgeEnvelope`.
54
+ */
55
+ declare function isBridgeEnvelope(value: unknown): value is BridgeEnvelope;
56
+ /**
57
+ * Claims extracted from the bridge JWT after `authenticateBridgeUpgrade`
58
+ * succeeds. Shared between the broker and the route to keep the trust
59
+ * boundary consistent.
60
+ */
61
+ interface BridgeClaims {
62
+ visitorId: string;
63
+ tenantId: string | null;
64
+ workspaceId: string | null;
65
+ }
66
+
67
+ /**
68
+ * Frozen Sprint 1 contract. Edits require joint sign-off from Track A
69
+ * (broker) and Track B (macOS) leads.
70
+ *
71
+ * Spec: docs/superpowers/specs/2026-04-28-bee-mesh-sprint-plan-design.md
72
+ * Section 3 (Source-of-truth contracts).
73
+ *
74
+ * Mirrored to Swift in apps/macos-kibee/Sources/KiBee/BeeBuddyState.swift
75
+ * (Sprint 1 T5). The Swift `allowedTransitions` table must match
76
+ * BUDDY_TRANSITIONS below exactly.
77
+ */
78
+ /**
79
+ * The macOS Buddy state machine. Frozen end of Sprint 1 — additions require
80
+ * joint sign-off from both track leads.
81
+ *
82
+ * Transition table (15 transitions, including cancel/escape paths to ambient):
83
+ *
84
+ * ambient → listening (voice hotkey)
85
+ * ambient → actionRing (hover 400ms over bee)
86
+ *
87
+ * listening → thinking (transcript finalized)
88
+ * listening → ambient (cancel / hotkey released / silence timeout)
89
+ *
90
+ * thinking → speaking (Claude responds)
91
+ * thinking → ambient (cancel / response error)
92
+ *
93
+ * speaking → pointing (POINT tag parsed)
94
+ * speaking → followUp (TTS finishes, no point tag)
95
+ * speaking → ambient (cancel / TTS interrupted)
96
+ *
97
+ * pointing → followUp (TTS finishes after point)
98
+ * pointing → ambient (cancel / target lost)
99
+ *
100
+ * followUp → ambient (silence timeout)
101
+ * followUp → listening (auto re-listen)
102
+ *
103
+ * actionRing → ambient (mouse leaves bee region)
104
+ * actionRing → listening (tap mic icon)
105
+ *
106
+ * Self-transitions are not allowed. The Swift mirror in
107
+ * apps/macos-kibee/Sources/KiBee/BeeBuddyState.swift uses the same table.
108
+ */
109
+ type BuddyState = "ambient" | "listening" | "thinking" | "speaking" | "pointing" | "followUp" | "actionRing";
110
+ /**
111
+ * Allowed transitions, expressed as `from → to[]`. Used by tests and by the
112
+ * Swift state machine to reject illegal transitions.
113
+ */
114
+ declare const BUDDY_TRANSITIONS: Record<BuddyState, BuddyState[]>;
115
+ declare function canTransition(from: BuddyState, to: BuddyState): boolean;
116
+
117
+ /**
118
+ * Frozen Sprint 2 contract. Edits require joint sign-off.
119
+ *
120
+ * Spec: docs/superpowers/specs/2026-04-28-bee-mesh-sprint-plan-design.md
121
+ * Section 4 Sprint 2 row.
122
+ *
123
+ * The macOS Buddy calls `GET /v1/identity` after Auth0 sign-in to obtain
124
+ * the canonical visitor record. Web SDK gets the same `visitorId` from
125
+ * the existing visitor cookie path (no contract change there).
126
+ */
127
+ /**
128
+ * The canonical visitor identity returned to any surface authenticating
129
+ * a real human. Stable for the lifetime of the user account.
130
+ *
131
+ * Note: structurally identical to `BridgeClaims` (bridge.ts) by design —
132
+ * same shape, different trust boundaries (HTTP response vs JWT claims).
133
+ * Do not unify; their lifecycles diverge in S3+.
134
+ */
135
+ interface VisitorIdentity {
136
+ /** Stable identifier for the authenticated user across all surfaces. */
137
+ visitorId: string;
138
+ /**
139
+ * Same value as `workspaceId` today (workspaces ARE tenants in the
140
+ * current schema). Kept as a separate field so the two can diverge
141
+ * without a contract break in future sprints.
142
+ */
143
+ tenantId: string | null;
144
+ /**
145
+ * For users with multiple workspace memberships, this is the OLDEST
146
+ * by `createdAt`. Workspace switching is a future feature (Sprint 6+
147
+ * tenant attachment). `null` means the user is in personal mode.
148
+ */
149
+ workspaceId: string | null;
150
+ }
151
+ /**
152
+ * Optional payload on `GET /v1/identity` (sent in body for first-time
153
+ * users only — typically the macOS app on first launch). Web surfaces
154
+ * never send this since they already have a visitor cookie.
155
+ */
156
+ interface IdentityRequest {
157
+ contactHint?: {
158
+ email?: string;
159
+ auth0Sub?: string;
160
+ };
161
+ }
162
+ /**
163
+ * Response envelope. `issuedAt` lets clients age out the local cache.
164
+ */
165
+ interface IdentityResponse {
166
+ identity: VisitorIdentity;
167
+ /** Wall-clock millis at issue (matches BridgeEnvelope.ts convention). */
168
+ issuedAt: number;
169
+ }
170
+
171
+ type HighlightVariant = "pulse" | "ring" | "soft";
172
+ interface TargetRef {
173
+ kibeeId?: string;
174
+ selector?: string;
175
+ textHints?: string[];
176
+ /**
177
+ * Legacy compatibility for older prototypes.
178
+ */
179
+ beeId?: string;
180
+ }
181
+ interface RegisteredTarget {
182
+ id: string;
183
+ label: string;
184
+ target: TargetRef;
185
+ description?: string;
186
+ phrases?: string[];
187
+ }
188
+ interface VisibleTarget extends RegisteredTarget {
189
+ matchedText?: string;
190
+ }
191
+ interface FlowMoveOptions {
192
+ duration?: number;
193
+ offsetX?: number;
194
+ offsetY?: number;
195
+ }
196
+ type FlowCommand = {
197
+ type: "fly_to";
198
+ target: TargetRef;
199
+ message?: string;
200
+ highlight?: HighlightVariant;
201
+ moveTo?: FlowMoveOptions;
202
+ } | {
203
+ type: "highlight";
204
+ target: TargetRef;
205
+ variant?: HighlightVariant;
206
+ } | {
207
+ type: "wait_for_click";
208
+ target: TargetRef;
209
+ timeoutMs?: number;
210
+ } | {
211
+ type: "wait_for_input";
212
+ target: TargetRef;
213
+ timeoutMs?: number;
214
+ } | {
215
+ type: "speak";
216
+ message: string;
217
+ } | {
218
+ type: "celebrate";
219
+ message?: string;
220
+ } | {
221
+ type: "end_flow";
222
+ status: "completed" | "abandoned";
223
+ message?: string;
224
+ };
225
+ interface FlowDefinition {
226
+ id: string;
227
+ title: string;
228
+ pageId: string;
229
+ goal: string;
230
+ audience?: string;
231
+ introMessage?: string;
232
+ phrases?: string[];
233
+ steps: FlowCommand[];
234
+ /**
235
+ * Optional cross-site continuation steps executed by the extension on external pages.
236
+ * Each step targets a page the SaaS does not control (e.g. stripe.com, meta.com).
237
+ */
238
+ crossSiteSteps?: CrossSiteFlowStep[];
239
+ /**
240
+ * External hostnames this flow may navigate to.
241
+ * Must be a subset of the tenant's allowedExternalHosts.
242
+ */
243
+ externalHosts?: string[];
244
+ /**
245
+ * The surface this flow is pinned to. Drives cross-surface `flow:handoff`
246
+ * routing — if the visitor's current surface != `surface` (and `surface`
247
+ * is not "both"), the broker emits a handoff so the other surface's bee
248
+ * can pick the flow up. Defaults to `"web"` at the DB layer.
249
+ */
250
+ surface?: "web" | "desktop" | "both";
251
+ }
252
+ interface SessionState {
253
+ id: string;
254
+ visitorId: string;
255
+ pageId: string;
256
+ flowId?: string;
257
+ currentStepIndex: number;
258
+ status: "idle" | "active" | "completed" | "abandoned";
259
+ startedAt: string;
260
+ updatedAt: string;
261
+ /** Present when session is linked to a CRM contact (e.g. admin session detail). */
262
+ contactId?: string | null;
263
+ }
264
+ interface IntentMatch {
265
+ flowId: string | null;
266
+ confidence: number;
267
+ message: string;
268
+ /**
269
+ * Surface the matched flow is pinned to, or undefined when no flow matched.
270
+ * Used by the route to decide whether to emit a `flow:handoff` envelope on
271
+ * the bridge. The literal union mirrors `FlowDefinition.surface` — the
272
+ * runtime emitter still tolerates unknown strings (it no-ops outside this
273
+ * union), but the type narrowing prevents accidental writes of values like
274
+ * `"mobile"` or `"extension"` typechecking silently.
275
+ */
276
+ flowSurface?: "web" | "desktop" | "both";
277
+ /** Present only in dev/debug mode — stripped before client delivery in production. */
278
+ _debug?: {
279
+ flowScore: number;
280
+ journeyScore: number;
281
+ pageCtxScore: number;
282
+ frictionPenalty: number;
283
+ fusedScore: number;
284
+ tier: "high" | "medium" | "low" | "none";
285
+ method: "fusion" | "phrase" | "none";
286
+ };
287
+ }
288
+ /**
289
+ * Discriminated union of known event metadata shapes.
290
+ * The catch-all `Record<string, unknown>` arm preserves forward-compatibility
291
+ * for unknown event types — all existing callers that pass untyped objects
292
+ * remain valid.
293
+ */
294
+ type EventMetadata = {
295
+ eventType: "page_identified";
296
+ title?: string;
297
+ url?: string;
298
+ referrer?: string;
299
+ } | {
300
+ eventType: "element_clicked";
301
+ kibeeId?: string;
302
+ selector?: string;
303
+ label?: string;
304
+ /** X / Y relative to viewport as 0–1 fractions */
305
+ relX?: number;
306
+ relY?: number;
307
+ } | {
308
+ eventType: "scroll_depth";
309
+ /** 0–1 fraction of page scrolled when threshold was crossed */
310
+ depth: number;
311
+ /** Milliseconds since page load when the threshold was first crossed */
312
+ dwellMs: number;
313
+ } | {
314
+ eventType: "bee_dismissed";
315
+ flowId?: string;
316
+ stepIndex?: number;
317
+ reason?: "manual" | "timeout" | "page_nav";
318
+ } | {
319
+ eventType: "flow_step_completed";
320
+ flowId: string;
321
+ stepIndex: number;
322
+ dwellMs?: number;
323
+ } | {
324
+ eventType: "intent_submitted";
325
+ input: string;
326
+ resolvedFlowId?: string;
327
+ confidence?: number;
328
+ } | {
329
+ eventType: "pairing_started";
330
+ /** Public tenant ID of the SaaS that initiated pairing */
331
+ tenantPublicId: string;
332
+ /** Hostname of the SaaS page where pairing was established */
333
+ originHost: string;
334
+ /** Shared session ID adopted for the pairing */
335
+ sessionId: string;
336
+ } | {
337
+ eventType: "pairing_ended";
338
+ reason: "tab_closed" | "tab_navigated" | "ttl_expired" | "user_request" | "session_expired";
339
+ /** Number of cross-site steps completed before pairing ended */
340
+ stepsCompleted: number;
341
+ } | {
342
+ eventType: "cross_site_step_completed";
343
+ /** Hostname where the step was executed, e.g. "stripe.com" */
344
+ externalHost: string;
345
+ stepIndex: number;
346
+ flowId: string;
347
+ dwellMs?: number;
348
+ } | Record<string, unknown>;
349
+ interface AnalyticsEvent {
350
+ type: string;
351
+ pageId: string;
352
+ timestamp: string;
353
+ visitorId?: string;
354
+ sessionId?: string;
355
+ flowId?: string;
356
+ targetId?: string;
357
+ metadata?: EventMetadata;
358
+ }
359
+ interface StartSessionRequest {
360
+ visitorId: string;
361
+ pageId: string;
362
+ flowId?: string;
363
+ }
364
+ interface ResolveIntentRequest {
365
+ input: string;
366
+ pageId: string;
367
+ visibleTargets: VisibleTarget[];
368
+ /** Optional: passed through to inference_outcomes for outcome tracking */
369
+ sessionId?: string;
370
+ /**
371
+ * Optional: stable visitor identifier shared across surfaces. Required for
372
+ * cross-surface `flow:handoff` emission. When omitted, no handoff is emitted
373
+ * (back-compat with older SDKs that haven't been updated for Sprint 4).
374
+ */
375
+ visitorId?: string;
376
+ /**
377
+ * Optional: the surface the request originated from ("web" | "desktop" |
378
+ * "extension"). Required for cross-surface `flow:handoff` emission. When
379
+ * omitted, no handoff is emitted.
380
+ */
381
+ currentSource?: "web" | "desktop" | "extension";
382
+ }
383
+ interface RecoveryRequest {
384
+ pageId: string;
385
+ target: TargetRef;
386
+ visibleTargets: VisibleTarget[];
387
+ }
388
+ interface RecoveryResponse {
389
+ target: TargetRef | null;
390
+ confidence: number;
391
+ message: string;
392
+ }
393
+ interface TrackEventRequest {
394
+ event: AnalyticsEvent;
395
+ }
396
+ type DockPositionPreset = "bottom-right" | "bottom-left" | "top-right" | "top-left";
397
+ interface DockInlineAnchor {
398
+ /** CSS selector or HTMLElement for the container to render inside */
399
+ container: string | HTMLElement;
400
+ /** Alignment within container */
401
+ align?: "left" | "center" | "right";
402
+ }
403
+ type DockPositionConfig = DockPositionPreset | {
404
+ preset: DockPositionPreset;
405
+ offsetX?: number;
406
+ offsetY?: number;
407
+ } | {
408
+ custom: {
409
+ x: number;
410
+ y: number;
411
+ };
412
+ } | {
413
+ inline: DockInlineAnchor;
414
+ };
415
+ interface AdminSessionSummary {
416
+ id: string;
417
+ pageId: string;
418
+ flowId?: string;
419
+ status: SessionState["status"];
420
+ startedAt: string;
421
+ updatedAt: string;
422
+ eventCount: number;
423
+ }
424
+ /** Who produced a message in the chat thread.
425
+ * - `customer`: visitor's own input (echoed in their panel, primary in admin)
426
+ * - `agent`: human support agent
427
+ * - `ai`: KiBee bee's freeform reply (Claude Haiku) or matched flow message
428
+ * - `system`: thread-level annotation (escalations, take-overs, releases)
429
+ */
430
+ type ChatSender = "agent" | "customer" | "ai" | "system";
431
+ /** Whether the conversation is currently driven by the AI or a human agent.
432
+ * Set per-session; flips on take-over / release. The visitor SDK uses this
433
+ * to hide its "talk to a human" chip when an agent is already on. */
434
+ type ChatMode = "ai" | "human";
435
+ interface ChatMessage {
436
+ id: string;
437
+ sessionId: string;
438
+ sender: ChatSender;
439
+ text: string;
440
+ timestamp: string;
441
+ }
442
+ interface VisitorPollResponse {
443
+ messages: ChatMessage[];
444
+ mode: ChatMode;
445
+ /** True when an agent has taken over (assigned_agent_id IS NOT NULL). */
446
+ hasAgent: boolean;
447
+ /** Page-level friction score (0-100), used by the SDK to surface the
448
+ * proactive "want a guided tour?" prompt. */
449
+ frictionScore: number;
450
+ /** The session's last_message_at — clients echo it back as `?since=` on
451
+ * the next poll. */
452
+ lastMessageAt: string | null;
453
+ }
454
+ interface MessageTemplate {
455
+ id: string;
456
+ label: string;
457
+ body: string;
458
+ kind: "greeting" | "ack" | "handoff" | "custom";
459
+ createdAt: string;
460
+ updatedAt: string;
461
+ }
462
+ interface PushFlowRequest {
463
+ flowId: string;
464
+ /** Optional cross-site continuation steps, executed by the extension on external pages */
465
+ crossSiteSteps?: CrossSiteFlowStep[];
466
+ }
467
+ /**
468
+ * A flow step that executes on an external page not controlled by the SaaS.
469
+ * Must use textHints only — kibeeId and selector are unavailable on third-party pages.
470
+ */
471
+ interface CrossSiteFlowStep {
472
+ /** Hostname the step executes on, e.g. "stripe.com" */
473
+ externalHost: string;
474
+ /** Path glob pattern, e.g. "/dashboard/apikeys*" — matches any path if absent */
475
+ pathPattern?: string;
476
+ /** Message shown to user before they navigate to the external page */
477
+ navigationMessage?: string;
478
+ /** The flow command to execute. Target must use textHints only. */
479
+ command: {
480
+ type: FlowCommand["type"];
481
+ message?: string;
482
+ /** textHints only — kibeeId and selector are disallowed for cross-site steps */
483
+ target?: {
484
+ textHints: string[];
485
+ };
486
+ timeoutMs?: number;
487
+ status?: "completed" | "abandoned";
488
+ variant?: HighlightVariant;
489
+ };
490
+ }
491
+ /** An item in the cross-site flow queue delivered to the extension via polling */
492
+ interface CrossSiteFlowQueueItem {
493
+ flowId: string;
494
+ flowTitle: string;
495
+ crossSiteSteps: CrossSiteFlowStep[];
496
+ /** Unix ms — item expires if not picked up by the extension */
497
+ expiresAt: number;
498
+ }
499
+ /** Sent by the extension content script to the embedded SDK page to initiate pairing */
500
+ interface KiBeePairRequest {
501
+ type: "KIBEE_PAIR_REQUEST";
502
+ /** chrome.runtime.id of the extension */
503
+ extensionId: string;
504
+ /** Manifest version string of the extension */
505
+ version: string;
506
+ }
507
+ /** Sent by the embedded SDK back to the extension content script to accept pairing */
508
+ interface KiBeePairAccept {
509
+ type: "KIBEE_PAIR_ACCEPT";
510
+ /** Public tenant identifier (same as websiteId) */
511
+ tenantPublicId: string;
512
+ /**
513
+ * The SaaS session ID — extension adopts this for all subordinate API calls.
514
+ * Analytics for both surfaces land in one session timeline.
515
+ */
516
+ sessionId: string;
517
+ /** Domains the extension may activate on in subordinate mode, server-enforced by the API */
518
+ allowedExternalHosts: string[];
519
+ /** Pre-queued cross-site flows to execute on allowed external hosts */
520
+ flowQueue: CrossSiteFlowQueueItem[];
521
+ /** Pairing validity window in milliseconds, e.g. 1_800_000 (30 min) */
522
+ ttlMs: number;
523
+ /**
524
+ * Short-lived server-issued credential for the extension to authenticate
525
+ * subordinate polling requests. Scoped to this session only.
526
+ */
527
+ pairingToken: string;
528
+ }
529
+ /** Sent by the extension content script to the embedded SDK to reject pairing */
530
+ interface KiBeePairReject {
531
+ type: "KIBEE_PAIR_REJECT";
532
+ reason: string;
533
+ }
534
+ /**
535
+ * Pairing state persisted in chrome.storage.local under key "kibee_pairing".
536
+ * Cleared on unpair. extensionVisitorId is NEVER overwritten by pairing.
537
+ */
538
+ interface PairingState {
539
+ tenantPublicId: string;
540
+ /** The SaaS session ID shared for this pairing — used for all subordinate API calls */
541
+ sessionId: string;
542
+ /** Hostname of the SaaS page where the handshake started, e.g. "acme.com" */
543
+ originHostname: string;
544
+ /** Chrome tab ID where the handshake started — monitored for close/navigate-away */
545
+ originTabId: number;
546
+ /** Domains the extension may operate on in subordinate mode */
547
+ allowedExternalHosts: string[];
548
+ /** Cross-site flow steps cached from the last poll response */
549
+ flowQueue: CrossSiteFlowQueueItem[];
550
+ /** Short-lived token for authenticating subordinate polling requests */
551
+ pairingToken: string;
552
+ /** Unix ms when pairing was established */
553
+ pairedAt: number;
554
+ /** How long the pairing is valid in milliseconds */
555
+ ttlMs: number;
556
+ /**
557
+ * Index of the cross-site flow step currently executing.
558
+ * null = idle (no step in progress).
559
+ * Used to decide immediate vs deferred unpair when origin tab closes.
560
+ */
561
+ activeFlowStepIndex: number | null;
562
+ /**
563
+ * Set to true when origin tab closes while a step is in progress.
564
+ * Content script detects this after step completion and clears pairing.
565
+ */
566
+ pendingUnpair: boolean;
567
+ /**
568
+ * The extension's own permanent visitor ID.
569
+ * Preserved independently of pairing — NEVER replaced by the SaaS visitor ID.
570
+ * Used for event attribution even in subordinate mode.
571
+ */
572
+ extensionVisitorId: string;
573
+ }
574
+ interface PushTargetsRequest {
575
+ targets: TargetRef[];
576
+ highlight?: HighlightVariant;
577
+ }
578
+ interface WidgetConfig {
579
+ position: DockPositionConfig;
580
+ size: number;
581
+ theme: {
582
+ primary: string;
583
+ tooltipBg: string;
584
+ tooltipColor: string;
585
+ };
586
+ }
587
+
588
+ export { type AdminSessionSummary, type AnalyticsEvent, BUDDY_TRANSITIONS, type BridgeClaims, type BridgeEnvelope, type BridgeEventType, type BuddyState, type ChatMessage, type ChatMode, type ChatSender, type CrossSiteFlowQueueItem, type CrossSiteFlowStep, type DockInlineAnchor, type DockPositionConfig, type DockPositionPreset, type EventMetadata, type FlowCommand, type FlowDefinition, type FlowMoveOptions, type HighlightVariant, type IdentityRequest, type IdentityResponse, type IntentMatch, type KiBeePairAccept, type KiBeePairReject, type KiBeePairRequest, type MessageTemplate, type PairingState, type PushFlowRequest, type PushTargetsRequest, type RecoveryRequest, type RecoveryResponse, type RegisteredTarget, type ResolveIntentRequest, type SessionState, type StartSessionRequest, type SurfaceId, type TargetRef, type TrackEventRequest, type VisibleTarget, type VisitorIdentity, type VisitorPollResponse, type WidgetConfig, canTransition, isBridgeEnvelope };
@@ -0,0 +1,588 @@
1
+ /**
2
+ * Frozen Sprint 1 contract. Edits require joint sign-off from Track A
3
+ * (broker) and Track B (macOS) leads.
4
+ *
5
+ * Spec: docs/superpowers/specs/2026-04-28-bee-mesh-sprint-plan-design.md
6
+ * Section 2 (Architecture — mesh of bees), Section 3 (Source-of-truth
7
+ * contracts).
8
+ *
9
+ * Mirrored to Swift in apps/macos-kibee (Sprint 1 T5/T7 — BridgeEnvelope
10
+ * Swift port lands in Sprint 4 convergence).
11
+ *
12
+ * Wire-stable note: the string raw values for `SurfaceId` and
13
+ * `BridgeEventType` (e.g. "presence:hello") are part of the wire format.
14
+ * Do not rename or re-shape them in the Swift mirror — they must JSON-encode
15
+ * to the exact same strings.
16
+ */
17
+ /**
18
+ * Surfaces that participate in the bee mesh. Every BridgeEnvelope declares
19
+ * its source and target by surface id.
20
+ */
21
+ type SurfaceId = "web" | "extension" | "desktop";
22
+ /**
23
+ * The frozen event-type union for the cross-surface bus. Adding a new event
24
+ * type after Sprint 1 requires joint sign-off from both track leads.
25
+ */
26
+ type BridgeEventType = "presence:hello" | "presence:bye" | "flow:handoff" | "flow:resume" | "pointer:show" | "memory:share" | "voice:state" | "capture:promote" | "tenant:attach" | "tenant:detach";
27
+ /**
28
+ * The wire format for every cross-surface message. `tenantId` is `null`
29
+ * when the surface is in personal mode (not attached to a workspace).
30
+ */
31
+ interface BridgeEnvelope<T = unknown> {
32
+ /** Protocol version. Bump on any breaking change. */
33
+ v: 1;
34
+ source: SurfaceId;
35
+ /** A specific surface or "broadcast" to all subscribers in the same scope. */
36
+ target: SurfaceId | "broadcast";
37
+ /** Null in personal mode; required for tenant-attached and admin-attached modes. */
38
+ tenantId: string | null;
39
+ workspaceId: string | null;
40
+ /** Stable across surfaces for the same visitor. Drives the fan-out scope. */
41
+ visitorId: string;
42
+ sessionId: string | null;
43
+ type: BridgeEventType;
44
+ /** Wall-clock millis at emit. */
45
+ ts: number;
46
+ payload: T;
47
+ }
48
+ /**
49
+ * Type guard usable at runtime when receiving an envelope from the wire.
50
+ * Validates every field strictly — unknown surface ids, unknown event types,
51
+ * missing fields, or arrays are all rejected. Use at the trust boundary
52
+ * (broker, SDK receive handler, Swift port) before treating data as a
53
+ * `BridgeEnvelope`.
54
+ */
55
+ declare function isBridgeEnvelope(value: unknown): value is BridgeEnvelope;
56
+ /**
57
+ * Claims extracted from the bridge JWT after `authenticateBridgeUpgrade`
58
+ * succeeds. Shared between the broker and the route to keep the trust
59
+ * boundary consistent.
60
+ */
61
+ interface BridgeClaims {
62
+ visitorId: string;
63
+ tenantId: string | null;
64
+ workspaceId: string | null;
65
+ }
66
+
67
+ /**
68
+ * Frozen Sprint 1 contract. Edits require joint sign-off from Track A
69
+ * (broker) and Track B (macOS) leads.
70
+ *
71
+ * Spec: docs/superpowers/specs/2026-04-28-bee-mesh-sprint-plan-design.md
72
+ * Section 3 (Source-of-truth contracts).
73
+ *
74
+ * Mirrored to Swift in apps/macos-kibee/Sources/KiBee/BeeBuddyState.swift
75
+ * (Sprint 1 T5). The Swift `allowedTransitions` table must match
76
+ * BUDDY_TRANSITIONS below exactly.
77
+ */
78
+ /**
79
+ * The macOS Buddy state machine. Frozen end of Sprint 1 — additions require
80
+ * joint sign-off from both track leads.
81
+ *
82
+ * Transition table (15 transitions, including cancel/escape paths to ambient):
83
+ *
84
+ * ambient → listening (voice hotkey)
85
+ * ambient → actionRing (hover 400ms over bee)
86
+ *
87
+ * listening → thinking (transcript finalized)
88
+ * listening → ambient (cancel / hotkey released / silence timeout)
89
+ *
90
+ * thinking → speaking (Claude responds)
91
+ * thinking → ambient (cancel / response error)
92
+ *
93
+ * speaking → pointing (POINT tag parsed)
94
+ * speaking → followUp (TTS finishes, no point tag)
95
+ * speaking → ambient (cancel / TTS interrupted)
96
+ *
97
+ * pointing → followUp (TTS finishes after point)
98
+ * pointing → ambient (cancel / target lost)
99
+ *
100
+ * followUp → ambient (silence timeout)
101
+ * followUp → listening (auto re-listen)
102
+ *
103
+ * actionRing → ambient (mouse leaves bee region)
104
+ * actionRing → listening (tap mic icon)
105
+ *
106
+ * Self-transitions are not allowed. The Swift mirror in
107
+ * apps/macos-kibee/Sources/KiBee/BeeBuddyState.swift uses the same table.
108
+ */
109
+ type BuddyState = "ambient" | "listening" | "thinking" | "speaking" | "pointing" | "followUp" | "actionRing";
110
+ /**
111
+ * Allowed transitions, expressed as `from → to[]`. Used by tests and by the
112
+ * Swift state machine to reject illegal transitions.
113
+ */
114
+ declare const BUDDY_TRANSITIONS: Record<BuddyState, BuddyState[]>;
115
+ declare function canTransition(from: BuddyState, to: BuddyState): boolean;
116
+
117
+ /**
118
+ * Frozen Sprint 2 contract. Edits require joint sign-off.
119
+ *
120
+ * Spec: docs/superpowers/specs/2026-04-28-bee-mesh-sprint-plan-design.md
121
+ * Section 4 Sprint 2 row.
122
+ *
123
+ * The macOS Buddy calls `GET /v1/identity` after Auth0 sign-in to obtain
124
+ * the canonical visitor record. Web SDK gets the same `visitorId` from
125
+ * the existing visitor cookie path (no contract change there).
126
+ */
127
+ /**
128
+ * The canonical visitor identity returned to any surface authenticating
129
+ * a real human. Stable for the lifetime of the user account.
130
+ *
131
+ * Note: structurally identical to `BridgeClaims` (bridge.ts) by design —
132
+ * same shape, different trust boundaries (HTTP response vs JWT claims).
133
+ * Do not unify; their lifecycles diverge in S3+.
134
+ */
135
+ interface VisitorIdentity {
136
+ /** Stable identifier for the authenticated user across all surfaces. */
137
+ visitorId: string;
138
+ /**
139
+ * Same value as `workspaceId` today (workspaces ARE tenants in the
140
+ * current schema). Kept as a separate field so the two can diverge
141
+ * without a contract break in future sprints.
142
+ */
143
+ tenantId: string | null;
144
+ /**
145
+ * For users with multiple workspace memberships, this is the OLDEST
146
+ * by `createdAt`. Workspace switching is a future feature (Sprint 6+
147
+ * tenant attachment). `null` means the user is in personal mode.
148
+ */
149
+ workspaceId: string | null;
150
+ }
151
+ /**
152
+ * Optional payload on `GET /v1/identity` (sent in body for first-time
153
+ * users only — typically the macOS app on first launch). Web surfaces
154
+ * never send this since they already have a visitor cookie.
155
+ */
156
+ interface IdentityRequest {
157
+ contactHint?: {
158
+ email?: string;
159
+ auth0Sub?: string;
160
+ };
161
+ }
162
+ /**
163
+ * Response envelope. `issuedAt` lets clients age out the local cache.
164
+ */
165
+ interface IdentityResponse {
166
+ identity: VisitorIdentity;
167
+ /** Wall-clock millis at issue (matches BridgeEnvelope.ts convention). */
168
+ issuedAt: number;
169
+ }
170
+
171
+ type HighlightVariant = "pulse" | "ring" | "soft";
172
+ interface TargetRef {
173
+ kibeeId?: string;
174
+ selector?: string;
175
+ textHints?: string[];
176
+ /**
177
+ * Legacy compatibility for older prototypes.
178
+ */
179
+ beeId?: string;
180
+ }
181
+ interface RegisteredTarget {
182
+ id: string;
183
+ label: string;
184
+ target: TargetRef;
185
+ description?: string;
186
+ phrases?: string[];
187
+ }
188
+ interface VisibleTarget extends RegisteredTarget {
189
+ matchedText?: string;
190
+ }
191
+ interface FlowMoveOptions {
192
+ duration?: number;
193
+ offsetX?: number;
194
+ offsetY?: number;
195
+ }
196
+ type FlowCommand = {
197
+ type: "fly_to";
198
+ target: TargetRef;
199
+ message?: string;
200
+ highlight?: HighlightVariant;
201
+ moveTo?: FlowMoveOptions;
202
+ } | {
203
+ type: "highlight";
204
+ target: TargetRef;
205
+ variant?: HighlightVariant;
206
+ } | {
207
+ type: "wait_for_click";
208
+ target: TargetRef;
209
+ timeoutMs?: number;
210
+ } | {
211
+ type: "wait_for_input";
212
+ target: TargetRef;
213
+ timeoutMs?: number;
214
+ } | {
215
+ type: "speak";
216
+ message: string;
217
+ } | {
218
+ type: "celebrate";
219
+ message?: string;
220
+ } | {
221
+ type: "end_flow";
222
+ status: "completed" | "abandoned";
223
+ message?: string;
224
+ };
225
+ interface FlowDefinition {
226
+ id: string;
227
+ title: string;
228
+ pageId: string;
229
+ goal: string;
230
+ audience?: string;
231
+ introMessage?: string;
232
+ phrases?: string[];
233
+ steps: FlowCommand[];
234
+ /**
235
+ * Optional cross-site continuation steps executed by the extension on external pages.
236
+ * Each step targets a page the SaaS does not control (e.g. stripe.com, meta.com).
237
+ */
238
+ crossSiteSteps?: CrossSiteFlowStep[];
239
+ /**
240
+ * External hostnames this flow may navigate to.
241
+ * Must be a subset of the tenant's allowedExternalHosts.
242
+ */
243
+ externalHosts?: string[];
244
+ /**
245
+ * The surface this flow is pinned to. Drives cross-surface `flow:handoff`
246
+ * routing — if the visitor's current surface != `surface` (and `surface`
247
+ * is not "both"), the broker emits a handoff so the other surface's bee
248
+ * can pick the flow up. Defaults to `"web"` at the DB layer.
249
+ */
250
+ surface?: "web" | "desktop" | "both";
251
+ }
252
+ interface SessionState {
253
+ id: string;
254
+ visitorId: string;
255
+ pageId: string;
256
+ flowId?: string;
257
+ currentStepIndex: number;
258
+ status: "idle" | "active" | "completed" | "abandoned";
259
+ startedAt: string;
260
+ updatedAt: string;
261
+ /** Present when session is linked to a CRM contact (e.g. admin session detail). */
262
+ contactId?: string | null;
263
+ }
264
+ interface IntentMatch {
265
+ flowId: string | null;
266
+ confidence: number;
267
+ message: string;
268
+ /**
269
+ * Surface the matched flow is pinned to, or undefined when no flow matched.
270
+ * Used by the route to decide whether to emit a `flow:handoff` envelope on
271
+ * the bridge. The literal union mirrors `FlowDefinition.surface` — the
272
+ * runtime emitter still tolerates unknown strings (it no-ops outside this
273
+ * union), but the type narrowing prevents accidental writes of values like
274
+ * `"mobile"` or `"extension"` typechecking silently.
275
+ */
276
+ flowSurface?: "web" | "desktop" | "both";
277
+ /** Present only in dev/debug mode — stripped before client delivery in production. */
278
+ _debug?: {
279
+ flowScore: number;
280
+ journeyScore: number;
281
+ pageCtxScore: number;
282
+ frictionPenalty: number;
283
+ fusedScore: number;
284
+ tier: "high" | "medium" | "low" | "none";
285
+ method: "fusion" | "phrase" | "none";
286
+ };
287
+ }
288
+ /**
289
+ * Discriminated union of known event metadata shapes.
290
+ * The catch-all `Record<string, unknown>` arm preserves forward-compatibility
291
+ * for unknown event types — all existing callers that pass untyped objects
292
+ * remain valid.
293
+ */
294
+ type EventMetadata = {
295
+ eventType: "page_identified";
296
+ title?: string;
297
+ url?: string;
298
+ referrer?: string;
299
+ } | {
300
+ eventType: "element_clicked";
301
+ kibeeId?: string;
302
+ selector?: string;
303
+ label?: string;
304
+ /** X / Y relative to viewport as 0–1 fractions */
305
+ relX?: number;
306
+ relY?: number;
307
+ } | {
308
+ eventType: "scroll_depth";
309
+ /** 0–1 fraction of page scrolled when threshold was crossed */
310
+ depth: number;
311
+ /** Milliseconds since page load when the threshold was first crossed */
312
+ dwellMs: number;
313
+ } | {
314
+ eventType: "bee_dismissed";
315
+ flowId?: string;
316
+ stepIndex?: number;
317
+ reason?: "manual" | "timeout" | "page_nav";
318
+ } | {
319
+ eventType: "flow_step_completed";
320
+ flowId: string;
321
+ stepIndex: number;
322
+ dwellMs?: number;
323
+ } | {
324
+ eventType: "intent_submitted";
325
+ input: string;
326
+ resolvedFlowId?: string;
327
+ confidence?: number;
328
+ } | {
329
+ eventType: "pairing_started";
330
+ /** Public tenant ID of the SaaS that initiated pairing */
331
+ tenantPublicId: string;
332
+ /** Hostname of the SaaS page where pairing was established */
333
+ originHost: string;
334
+ /** Shared session ID adopted for the pairing */
335
+ sessionId: string;
336
+ } | {
337
+ eventType: "pairing_ended";
338
+ reason: "tab_closed" | "tab_navigated" | "ttl_expired" | "user_request" | "session_expired";
339
+ /** Number of cross-site steps completed before pairing ended */
340
+ stepsCompleted: number;
341
+ } | {
342
+ eventType: "cross_site_step_completed";
343
+ /** Hostname where the step was executed, e.g. "stripe.com" */
344
+ externalHost: string;
345
+ stepIndex: number;
346
+ flowId: string;
347
+ dwellMs?: number;
348
+ } | Record<string, unknown>;
349
+ interface AnalyticsEvent {
350
+ type: string;
351
+ pageId: string;
352
+ timestamp: string;
353
+ visitorId?: string;
354
+ sessionId?: string;
355
+ flowId?: string;
356
+ targetId?: string;
357
+ metadata?: EventMetadata;
358
+ }
359
+ interface StartSessionRequest {
360
+ visitorId: string;
361
+ pageId: string;
362
+ flowId?: string;
363
+ }
364
+ interface ResolveIntentRequest {
365
+ input: string;
366
+ pageId: string;
367
+ visibleTargets: VisibleTarget[];
368
+ /** Optional: passed through to inference_outcomes for outcome tracking */
369
+ sessionId?: string;
370
+ /**
371
+ * Optional: stable visitor identifier shared across surfaces. Required for
372
+ * cross-surface `flow:handoff` emission. When omitted, no handoff is emitted
373
+ * (back-compat with older SDKs that haven't been updated for Sprint 4).
374
+ */
375
+ visitorId?: string;
376
+ /**
377
+ * Optional: the surface the request originated from ("web" | "desktop" |
378
+ * "extension"). Required for cross-surface `flow:handoff` emission. When
379
+ * omitted, no handoff is emitted.
380
+ */
381
+ currentSource?: "web" | "desktop" | "extension";
382
+ }
383
+ interface RecoveryRequest {
384
+ pageId: string;
385
+ target: TargetRef;
386
+ visibleTargets: VisibleTarget[];
387
+ }
388
+ interface RecoveryResponse {
389
+ target: TargetRef | null;
390
+ confidence: number;
391
+ message: string;
392
+ }
393
+ interface TrackEventRequest {
394
+ event: AnalyticsEvent;
395
+ }
396
+ type DockPositionPreset = "bottom-right" | "bottom-left" | "top-right" | "top-left";
397
+ interface DockInlineAnchor {
398
+ /** CSS selector or HTMLElement for the container to render inside */
399
+ container: string | HTMLElement;
400
+ /** Alignment within container */
401
+ align?: "left" | "center" | "right";
402
+ }
403
+ type DockPositionConfig = DockPositionPreset | {
404
+ preset: DockPositionPreset;
405
+ offsetX?: number;
406
+ offsetY?: number;
407
+ } | {
408
+ custom: {
409
+ x: number;
410
+ y: number;
411
+ };
412
+ } | {
413
+ inline: DockInlineAnchor;
414
+ };
415
+ interface AdminSessionSummary {
416
+ id: string;
417
+ pageId: string;
418
+ flowId?: string;
419
+ status: SessionState["status"];
420
+ startedAt: string;
421
+ updatedAt: string;
422
+ eventCount: number;
423
+ }
424
+ /** Who produced a message in the chat thread.
425
+ * - `customer`: visitor's own input (echoed in their panel, primary in admin)
426
+ * - `agent`: human support agent
427
+ * - `ai`: KiBee bee's freeform reply (Claude Haiku) or matched flow message
428
+ * - `system`: thread-level annotation (escalations, take-overs, releases)
429
+ */
430
+ type ChatSender = "agent" | "customer" | "ai" | "system";
431
+ /** Whether the conversation is currently driven by the AI or a human agent.
432
+ * Set per-session; flips on take-over / release. The visitor SDK uses this
433
+ * to hide its "talk to a human" chip when an agent is already on. */
434
+ type ChatMode = "ai" | "human";
435
+ interface ChatMessage {
436
+ id: string;
437
+ sessionId: string;
438
+ sender: ChatSender;
439
+ text: string;
440
+ timestamp: string;
441
+ }
442
+ interface VisitorPollResponse {
443
+ messages: ChatMessage[];
444
+ mode: ChatMode;
445
+ /** True when an agent has taken over (assigned_agent_id IS NOT NULL). */
446
+ hasAgent: boolean;
447
+ /** Page-level friction score (0-100), used by the SDK to surface the
448
+ * proactive "want a guided tour?" prompt. */
449
+ frictionScore: number;
450
+ /** The session's last_message_at — clients echo it back as `?since=` on
451
+ * the next poll. */
452
+ lastMessageAt: string | null;
453
+ }
454
+ interface MessageTemplate {
455
+ id: string;
456
+ label: string;
457
+ body: string;
458
+ kind: "greeting" | "ack" | "handoff" | "custom";
459
+ createdAt: string;
460
+ updatedAt: string;
461
+ }
462
+ interface PushFlowRequest {
463
+ flowId: string;
464
+ /** Optional cross-site continuation steps, executed by the extension on external pages */
465
+ crossSiteSteps?: CrossSiteFlowStep[];
466
+ }
467
+ /**
468
+ * A flow step that executes on an external page not controlled by the SaaS.
469
+ * Must use textHints only — kibeeId and selector are unavailable on third-party pages.
470
+ */
471
+ interface CrossSiteFlowStep {
472
+ /** Hostname the step executes on, e.g. "stripe.com" */
473
+ externalHost: string;
474
+ /** Path glob pattern, e.g. "/dashboard/apikeys*" — matches any path if absent */
475
+ pathPattern?: string;
476
+ /** Message shown to user before they navigate to the external page */
477
+ navigationMessage?: string;
478
+ /** The flow command to execute. Target must use textHints only. */
479
+ command: {
480
+ type: FlowCommand["type"];
481
+ message?: string;
482
+ /** textHints only — kibeeId and selector are disallowed for cross-site steps */
483
+ target?: {
484
+ textHints: string[];
485
+ };
486
+ timeoutMs?: number;
487
+ status?: "completed" | "abandoned";
488
+ variant?: HighlightVariant;
489
+ };
490
+ }
491
+ /** An item in the cross-site flow queue delivered to the extension via polling */
492
+ interface CrossSiteFlowQueueItem {
493
+ flowId: string;
494
+ flowTitle: string;
495
+ crossSiteSteps: CrossSiteFlowStep[];
496
+ /** Unix ms — item expires if not picked up by the extension */
497
+ expiresAt: number;
498
+ }
499
+ /** Sent by the extension content script to the embedded SDK page to initiate pairing */
500
+ interface KiBeePairRequest {
501
+ type: "KIBEE_PAIR_REQUEST";
502
+ /** chrome.runtime.id of the extension */
503
+ extensionId: string;
504
+ /** Manifest version string of the extension */
505
+ version: string;
506
+ }
507
+ /** Sent by the embedded SDK back to the extension content script to accept pairing */
508
+ interface KiBeePairAccept {
509
+ type: "KIBEE_PAIR_ACCEPT";
510
+ /** Public tenant identifier (same as websiteId) */
511
+ tenantPublicId: string;
512
+ /**
513
+ * The SaaS session ID — extension adopts this for all subordinate API calls.
514
+ * Analytics for both surfaces land in one session timeline.
515
+ */
516
+ sessionId: string;
517
+ /** Domains the extension may activate on in subordinate mode, server-enforced by the API */
518
+ allowedExternalHosts: string[];
519
+ /** Pre-queued cross-site flows to execute on allowed external hosts */
520
+ flowQueue: CrossSiteFlowQueueItem[];
521
+ /** Pairing validity window in milliseconds, e.g. 1_800_000 (30 min) */
522
+ ttlMs: number;
523
+ /**
524
+ * Short-lived server-issued credential for the extension to authenticate
525
+ * subordinate polling requests. Scoped to this session only.
526
+ */
527
+ pairingToken: string;
528
+ }
529
+ /** Sent by the extension content script to the embedded SDK to reject pairing */
530
+ interface KiBeePairReject {
531
+ type: "KIBEE_PAIR_REJECT";
532
+ reason: string;
533
+ }
534
+ /**
535
+ * Pairing state persisted in chrome.storage.local under key "kibee_pairing".
536
+ * Cleared on unpair. extensionVisitorId is NEVER overwritten by pairing.
537
+ */
538
+ interface PairingState {
539
+ tenantPublicId: string;
540
+ /** The SaaS session ID shared for this pairing — used for all subordinate API calls */
541
+ sessionId: string;
542
+ /** Hostname of the SaaS page where the handshake started, e.g. "acme.com" */
543
+ originHostname: string;
544
+ /** Chrome tab ID where the handshake started — monitored for close/navigate-away */
545
+ originTabId: number;
546
+ /** Domains the extension may operate on in subordinate mode */
547
+ allowedExternalHosts: string[];
548
+ /** Cross-site flow steps cached from the last poll response */
549
+ flowQueue: CrossSiteFlowQueueItem[];
550
+ /** Short-lived token for authenticating subordinate polling requests */
551
+ pairingToken: string;
552
+ /** Unix ms when pairing was established */
553
+ pairedAt: number;
554
+ /** How long the pairing is valid in milliseconds */
555
+ ttlMs: number;
556
+ /**
557
+ * Index of the cross-site flow step currently executing.
558
+ * null = idle (no step in progress).
559
+ * Used to decide immediate vs deferred unpair when origin tab closes.
560
+ */
561
+ activeFlowStepIndex: number | null;
562
+ /**
563
+ * Set to true when origin tab closes while a step is in progress.
564
+ * Content script detects this after step completion and clears pairing.
565
+ */
566
+ pendingUnpair: boolean;
567
+ /**
568
+ * The extension's own permanent visitor ID.
569
+ * Preserved independently of pairing — NEVER replaced by the SaaS visitor ID.
570
+ * Used for event attribution even in subordinate mode.
571
+ */
572
+ extensionVisitorId: string;
573
+ }
574
+ interface PushTargetsRequest {
575
+ targets: TargetRef[];
576
+ highlight?: HighlightVariant;
577
+ }
578
+ interface WidgetConfig {
579
+ position: DockPositionConfig;
580
+ size: number;
581
+ theme: {
582
+ primary: string;
583
+ tooltipBg: string;
584
+ tooltipColor: string;
585
+ };
586
+ }
587
+
588
+ export { type AdminSessionSummary, type AnalyticsEvent, BUDDY_TRANSITIONS, type BridgeClaims, type BridgeEnvelope, type BridgeEventType, type BuddyState, type ChatMessage, type ChatMode, type ChatSender, type CrossSiteFlowQueueItem, type CrossSiteFlowStep, type DockInlineAnchor, type DockPositionConfig, type DockPositionPreset, type EventMetadata, type FlowCommand, type FlowDefinition, type FlowMoveOptions, type HighlightVariant, type IdentityRequest, type IdentityResponse, type IntentMatch, type KiBeePairAccept, type KiBeePairReject, type KiBeePairRequest, type MessageTemplate, type PairingState, type PushFlowRequest, type PushTargetsRequest, type RecoveryRequest, type RecoveryResponse, type RegisteredTarget, type ResolveIntentRequest, type SessionState, type StartSessionRequest, type SurfaceId, type TargetRef, type TrackEventRequest, type VisibleTarget, type VisitorIdentity, type VisitorPollResponse, type WidgetConfig, canTransition, isBridgeEnvelope };
package/dist/index.js ADDED
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ BUDDY_TRANSITIONS: () => BUDDY_TRANSITIONS,
24
+ canTransition: () => canTransition,
25
+ isBridgeEnvelope: () => isBridgeEnvelope
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/bridge.ts
30
+ var SURFACE_IDS = /* @__PURE__ */ new Set(["web", "extension", "desktop"]);
31
+ var BRIDGE_TARGETS = /* @__PURE__ */ new Set([
32
+ "web",
33
+ "extension",
34
+ "desktop",
35
+ "broadcast"
36
+ ]);
37
+ var BRIDGE_EVENT_TYPES = /* @__PURE__ */ new Set([
38
+ "presence:hello",
39
+ "presence:bye",
40
+ "flow:handoff",
41
+ "flow:resume",
42
+ "pointer:show",
43
+ "memory:share",
44
+ "voice:state",
45
+ "capture:promote",
46
+ "tenant:attach",
47
+ "tenant:detach"
48
+ ]);
49
+ function isBridgeEnvelope(value) {
50
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
51
+ return false;
52
+ }
53
+ const e = value;
54
+ const isStringOrNull = (v) => v === null || typeof v === "string";
55
+ return e.v === 1 && typeof e.source === "string" && SURFACE_IDS.has(e.source) && typeof e.target === "string" && BRIDGE_TARGETS.has(e.target) && isStringOrNull(e.tenantId) && isStringOrNull(e.workspaceId) && typeof e.visitorId === "string" && isStringOrNull(e.sessionId) && typeof e.type === "string" && BRIDGE_EVENT_TYPES.has(e.type) && typeof e.ts === "number" && Number.isFinite(e.ts) && "payload" in e;
56
+ }
57
+
58
+ // src/buddy.ts
59
+ var BUDDY_TRANSITIONS = {
60
+ ambient: ["listening", "actionRing"],
61
+ listening: ["thinking", "ambient"],
62
+ thinking: ["speaking", "ambient"],
63
+ speaking: ["pointing", "followUp", "ambient"],
64
+ pointing: ["followUp", "ambient"],
65
+ followUp: ["ambient", "listening"],
66
+ actionRing: ["ambient", "listening"]
67
+ };
68
+ function canTransition(from, to) {
69
+ return BUDDY_TRANSITIONS[from].includes(to);
70
+ }
71
+ // Annotate the CommonJS export names for ESM import in node:
72
+ 0 && (module.exports = {
73
+ BUDDY_TRANSITIONS,
74
+ canTransition,
75
+ isBridgeEnvelope
76
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,47 @@
1
+ // src/bridge.ts
2
+ var SURFACE_IDS = /* @__PURE__ */ new Set(["web", "extension", "desktop"]);
3
+ var BRIDGE_TARGETS = /* @__PURE__ */ new Set([
4
+ "web",
5
+ "extension",
6
+ "desktop",
7
+ "broadcast"
8
+ ]);
9
+ var BRIDGE_EVENT_TYPES = /* @__PURE__ */ new Set([
10
+ "presence:hello",
11
+ "presence:bye",
12
+ "flow:handoff",
13
+ "flow:resume",
14
+ "pointer:show",
15
+ "memory:share",
16
+ "voice:state",
17
+ "capture:promote",
18
+ "tenant:attach",
19
+ "tenant:detach"
20
+ ]);
21
+ function isBridgeEnvelope(value) {
22
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
23
+ return false;
24
+ }
25
+ const e = value;
26
+ const isStringOrNull = (v) => v === null || typeof v === "string";
27
+ return e.v === 1 && typeof e.source === "string" && SURFACE_IDS.has(e.source) && typeof e.target === "string" && BRIDGE_TARGETS.has(e.target) && isStringOrNull(e.tenantId) && isStringOrNull(e.workspaceId) && typeof e.visitorId === "string" && isStringOrNull(e.sessionId) && typeof e.type === "string" && BRIDGE_EVENT_TYPES.has(e.type) && typeof e.ts === "number" && Number.isFinite(e.ts) && "payload" in e;
28
+ }
29
+
30
+ // src/buddy.ts
31
+ var BUDDY_TRANSITIONS = {
32
+ ambient: ["listening", "actionRing"],
33
+ listening: ["thinking", "ambient"],
34
+ thinking: ["speaking", "ambient"],
35
+ speaking: ["pointing", "followUp", "ambient"],
36
+ pointing: ["followUp", "ambient"],
37
+ followUp: ["ambient", "listening"],
38
+ actionRing: ["ambient", "listening"]
39
+ };
40
+ function canTransition(from, to) {
41
+ return BUDDY_TRANSITIONS[from].includes(to);
42
+ }
43
+ export {
44
+ BUDDY_TRANSITIONS,
45
+ canTransition,
46
+ isBridgeEnvelope
47
+ };
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@kibee/contracts",
3
+ "version": "0.2.0",
4
+ "main": "./dist/index.js",
5
+ "module": "./dist/index.mjs",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": ["dist"],
15
+ "scripts": {
16
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.build.json",
17
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
18
+ },
19
+ "devDependencies": {
20
+ "tsup": "^8.5.1"
21
+ }
22
+ }