@cms.ai/brand_brain 0.0.1 → 0.1.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/src/types.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  // This package is deliberately self-contained: it imports NO types from
2
2
  // @navless/types. That package is a barrel (`export *` over dozens of modules),
3
3
  // so importing any symbol drags the whole surface into the bundled .d.ts and the
4
- // dts bundler chokes on it. The wire-protocol enums below are stable contracts,
5
- // hand-mirrored here so the published package has zero @navless/* runtime deps
6
- // and a self-contained declaration file.
4
+ // dts bundler chokes on it. The wire-protocol enums and payload interfaces below
5
+ // are stable contracts, hand-mirrored here so the published package has zero
6
+ // @navless/* runtime deps and a self-contained declaration file. Authoritative
7
+ // server sources: `guide-runtime/sse-adapter.ts` (wire frames) and
8
+ // `@navless/types/guide.types.ts` (payload shapes).
7
9
 
8
10
  /** AG-UI protocol frame types written by the guide SSE stream. */
9
11
  export const SSEMessageType = {
@@ -17,7 +19,11 @@ export const SSEMessageType = {
17
19
  } as const;
18
20
  export type SSEMessageTypeName = (typeof SSEMessageType)[keyof typeof SSEMessageType];
19
21
 
20
- /** The `name` on `CUSTOM` guide events (skill payloads). */
22
+ /**
23
+ * The `name` on `CUSTOM` guide events (skill payloads). `SkillGate` and
24
+ * `SkillError` never surface as `skill` stream events — the SDK re-maps them to
25
+ * the dedicated `gate` / `error` {@link GuideStreamEvent} variants.
26
+ */
21
27
  export const GuideEvent = {
22
28
  SkillStart: "skill_start",
23
29
  SkillContent: "skill_content",
@@ -38,7 +44,6 @@ export const GuideEvent = {
38
44
  SkillEnd: "skill_end",
39
45
  SkillError: "skill_error",
40
46
  SkillGate: "skill_gate",
41
- InputSuggestions: "input_suggestions",
42
47
  } as const;
43
48
  export type GuideEventName = (typeof GuideEvent)[keyof typeof GuideEvent];
44
49
 
@@ -111,10 +116,77 @@ export interface GuideGraphSnapshot {
111
116
  edges: Array<{ from: string; to: string; relationship: string; strength: number }>;
112
117
  }
113
118
 
119
+ // ============================================================================
120
+ // Brand theme
121
+ // ============================================================================
122
+
123
+ /**
124
+ * The guide's brand theme — shadcn-style design tokens as CSS color strings
125
+ * (hex or `oklch(...)`). Every property is optional: absent tokens mean "keep
126
+ * your default". Map them onto your CSS variables / Tailwind theme so the
127
+ * guide UI matches the brand:
128
+ *
129
+ * ```css
130
+ * :root {
131
+ * --primary: <theme.primary>;
132
+ * --background: <theme.background>;
133
+ * }
134
+ * ```
135
+ */
136
+ export interface BrandKitTheme {
137
+ /** Main brand color (buttons, focus rings) */
138
+ primary?: string;
139
+ /** Text on primary backgrounds */
140
+ primaryForeground?: string;
141
+ /** Secondary surface color */
142
+ secondary?: string;
143
+ /** Text on secondary surfaces */
144
+ secondaryForeground?: string;
145
+ /** Main page background */
146
+ background?: string;
147
+ /** Default body text */
148
+ foreground?: string;
149
+ /** Muted areas, borders, inputs */
150
+ muted?: string;
151
+ /** Muted/secondary text */
152
+ mutedForeground?: string;
153
+ /** Border color */
154
+ border?: string;
155
+ /** Input border color */
156
+ input?: string;
157
+ /** Focus ring color */
158
+ ring?: string;
159
+ }
160
+
161
+ /** How a brand kit colors the guide's skill icons. */
162
+ export const SkillIconColorMode = {
163
+ /** Keep the built-in per-skill colors. */
164
+ Default: "default",
165
+ /** Apply a single brand color to every skill icon. */
166
+ Mono: "mono",
167
+ /** Pick a color per skill; skills left unset fall back to the default. */
168
+ PerSkill: "per_skill",
169
+ } as const;
170
+ export type SkillIconColorModeType = (typeof SkillIconColorMode)[keyof typeof SkillIconColorMode];
171
+
114
172
  /**
115
- * Resolved guide metadata returned by `guides.resolve`. `theme` / `skillIconStyle`
116
- * are left as `unknown` in v1 read them defensively or narrow as needed.
173
+ * Per-brand-kit skill icon color customization, discriminated on `mode`.
174
+ * Colors are CSS color strings (same format as {@link BrandKitTheme}).
117
175
  */
176
+ export type SkillIconStyle =
177
+ | { mode: typeof SkillIconColorMode.Default }
178
+ | {
179
+ mode: typeof SkillIconColorMode.Mono;
180
+ /** Single color applied to every skill icon. */
181
+ monoColor: string;
182
+ }
183
+ | {
184
+ mode: typeof SkillIconColorMode.PerSkill;
185
+ /** Color per skill; missing keys fall back to the default. */
186
+ colors: Partial<Record<SkillResponseTypeType, string>>;
187
+ };
188
+
189
+ /** Resolved guide metadata returned by `guides.resolve`. */
118
190
  export interface ResolvedGuide {
119
191
  id: string;
120
192
  name: string;
@@ -125,8 +197,9 @@ export interface ResolvedGuide {
125
197
  companyName: string;
126
198
  gdprEnabled: boolean;
127
199
  advancedSettings: { gatePdfDownload: boolean };
128
- theme: unknown;
129
- skillIconStyle: unknown;
200
+ /** Brand design tokens — apply to your UI so the guide matches the brand. */
201
+ theme: BrandKitTheme | null;
202
+ skillIconStyle: SkillIconStyle | null;
130
203
  faviconUrl: string | null;
131
204
  logoUrl: string | null;
132
205
  heroImageUrl: string | null;
@@ -164,7 +237,12 @@ export interface ConversationWithMessages {
164
237
  messages: ConversationMessage[];
165
238
  }
166
239
 
167
- /** A persisted skill deliverable. `data` shape varies by `type`. */
240
+ /**
241
+ * A persisted skill deliverable. `data` shape varies by `type` — it is the
242
+ * skill's completed output (e.g. for `howto` a `{ title, steps: HowToStep[] }`
243
+ * record); the streamed payload interfaces in this file describe the same
244
+ * building blocks.
245
+ */
168
246
  export interface SkillInstance {
169
247
  id: string;
170
248
  skillOutputId: string;
@@ -265,8 +343,10 @@ export interface InitGuideConfig {
265
343
  /** Guide slug to resolve (e.g. "default"). */
266
344
  slug: string;
267
345
  /**
268
- * Host domain used for account resolution + attribution. Defaults to
269
- * `window.location.hostname`. Required when running without a browser window.
346
+ * Optional override for the host used in account resolution + attribution.
347
+ * Defaults to the `baseUrl` hostname (lowercased, `www.`/port stripped), which
348
+ * is the registered account domain. Only set this if resolution must key off a
349
+ * different registered domain than the one requests are sent to.
270
350
  */
271
351
  domain?: string;
272
352
  /**
@@ -300,17 +380,424 @@ export interface AskOptions {
300
380
  signal?: AbortSignal;
301
381
  }
302
382
 
383
+ // ============================================================================
384
+ // Skill payload building blocks (mirrored from @navless/types/guide.types.ts)
385
+ // ============================================================================
386
+
387
+ /** Kind of content a document/playlist item points at. */
388
+ export const ContentType = {
389
+ PDF: "pdf",
390
+ DOCX: "docx",
391
+ PPTX: "pptx",
392
+ Image: "image",
393
+ VideoNative: "video_native",
394
+ VideoYouTube: "video_youtube",
395
+ VideoWistia: "video_wistia",
396
+ Document: "document",
397
+ URL: "url",
398
+ Text: "text",
399
+ Markdown: "markdown",
400
+ } as const;
401
+ export type ContentTypeName = (typeof ContentType)[keyof typeof ContentType];
402
+
403
+ /**
404
+ * Document reference streamed with a reply — the sources behind the answer.
405
+ * Render as source cards/citations: title, optional link, optional thumbnail.
406
+ */
407
+ export interface GuideDocument {
408
+ id: string;
409
+ title: string;
410
+ tag?: string;
411
+ /** Lucide icon name. */
412
+ icon?: string;
413
+ url?: string;
414
+ contentType?: ContentTypeName;
415
+ body?: string;
416
+ thumbnailUrl?: string;
417
+ /** 1-based citation number matching the [N] references in the reply text. Undefined if the document was not part of the reply context. */
418
+ citationIndex?: number;
419
+ }
420
+
421
+ /** Confidence level attached to a content recommendation. */
422
+ export const Confidence = {
423
+ High: "high",
424
+ Medium: "medium",
425
+ Low: "low",
426
+ } as const;
427
+ export type ConfidenceName = (typeof Confidence)[keyof typeof Confidence];
428
+
429
+ /**
430
+ * Follow-up recommendation to invoke a skill — render as a pill button that
431
+ * calls `ask(prompt, { forcedSkill: skill })`.
432
+ */
433
+ export interface SkillRecommendation {
434
+ id: string;
435
+ skill: SkillResponseTypeType;
436
+ label: string;
437
+ prompt: string;
438
+ }
439
+
440
+ /**
441
+ * A content recommendation generated by the recommend skill — render as a card
442
+ * whose click calls `ask(prompt, { forcedSkill: targetSkill })`.
443
+ */
444
+ export interface ContentRecommendation {
445
+ id: string;
446
+ targetSkill: Exclude<SkillResponseTypeType, "recommend">;
447
+ title: string;
448
+ rationale: string;
449
+ prompt: string;
450
+ confidence: ConfidenceName;
451
+ }
452
+
453
+ /** The render kind of a surfaced call-to-action. */
454
+ export const SkillCtaKind = {
455
+ Url: "url",
456
+ Form: "form",
457
+ Product: "product",
458
+ Scheduler: "scheduler",
459
+ } as const;
460
+ export type SkillCtaKindName = (typeof SkillCtaKind)[keyof typeof SkillCtaKind];
461
+
462
+ /** Where a surfaced CTA renders. One slot today: below the skill response. */
463
+ export const CtaPlacement = {
464
+ ResponseFooter: "response_footer",
465
+ } as const;
466
+ export type CtaPlacementName = (typeof CtaPlacement)[keyof typeof CtaPlacement];
467
+
468
+ /** Which scheduler embed a scheduler CTA loads. */
469
+ export const SchedulerProvider = {
470
+ HubSpot: "hubspot",
471
+ Calendly: "calendly",
472
+ } as const;
473
+ export type SchedulerProviderName = (typeof SchedulerProvider)[keyof typeof SchedulerProvider];
474
+
475
+ /**
476
+ * A call-to-action surfaced for a turn, discriminated on `kind`:
477
+ * - `url`: external link — render a button that opens `href`.
478
+ * - `form`: in-guide form — render a button/banner for the form `formId`.
479
+ * - `product`: product recommendation — render a link/card to `href`.
480
+ * - `scheduler`: meeting embed — render a button that opens `url` in the
481
+ * provider's scheduling widget (or a new tab).
482
+ *
483
+ * Only render the first CTA in the list; the wire carries a list for forward
484
+ * compatibility. `ctaInstanceId` (when present) should be echoed on CTA
485
+ * analytics events so impressions and clicks join up.
486
+ */
487
+ export type SkillCTA =
488
+ | {
489
+ kind: typeof SkillCtaKind.Url;
490
+ placement: CtaPlacementName;
491
+ ctaId: string | null;
492
+ label: string;
493
+ href: string;
494
+ /** Custom banner heading; absent → use a default like "Recommended next step". */
495
+ bannerText?: string;
496
+ /** Lucide icon name; absent → a default lightbulb. */
497
+ bannerIcon?: string;
498
+ ctaInstanceId?: string;
499
+ }
500
+ | {
501
+ kind: typeof SkillCtaKind.Form;
502
+ placement: CtaPlacementName;
503
+ ctaId: string;
504
+ label: string;
505
+ formId: string;
506
+ bannerText?: string;
507
+ bannerIcon?: string;
508
+ ctaInstanceId?: string;
509
+ }
510
+ | {
511
+ kind: typeof SkillCtaKind.Product;
512
+ placement: CtaPlacementName;
513
+ productId: string;
514
+ label: string;
515
+ href: string;
516
+ ctaInstanceId?: string;
517
+ }
518
+ | {
519
+ kind: typeof SkillCtaKind.Scheduler;
520
+ placement: CtaPlacementName;
521
+ ctaId: string;
522
+ label: string;
523
+ provider: SchedulerProviderName;
524
+ /** The meeting/scheduling link the embed loads. */
525
+ url: string;
526
+ bannerText?: string;
527
+ bannerIcon?: string;
528
+ ctaInstanceId?: string;
529
+ };
530
+
531
+ /** Diagnose skill mode. */
532
+ export const DiagnoseSubtype = {
533
+ /** A scored self-assessment rendered with results at the end. */
534
+ Standalone: "standalone",
535
+ /** Questions gather context for a follow-up skill (see `followUpSkill`). */
536
+ ContextGathering: "context_gathering",
537
+ } as const;
538
+ export type DiagnoseSubtypeName = (typeof DiagnoseSubtype)[keyof typeof DiagnoseSubtype];
539
+
540
+ /** Answer option labels for diagnose questions. */
541
+ export const DiagnoseOptionLabel = {
542
+ A: "A",
543
+ B: "B",
544
+ C: "C",
545
+ D: "D",
546
+ } as const;
547
+ export type DiagnoseOptionLabelName = (typeof DiagnoseOptionLabel)[keyof typeof DiagnoseOptionLabel];
548
+
549
+ /** Header for a diagnostic assessment — render as the assessment intro. */
550
+ export interface DiagnosticHeader {
551
+ targetTopic: string;
552
+ subtitle: string;
553
+ /** Total number of questions the assessment will stream. */
554
+ questionCount: number;
555
+ subtype?: DiagnoseSubtypeName;
556
+ /** For context_gathering mode — the skill to invoke after the user submits answers. */
557
+ followUpSkill?: string;
558
+ /** For context_gathering mode — the prompt to send to `followUpSkill` afterwards. */
559
+ followUpPrompt?: string;
560
+ }
561
+
562
+ /**
563
+ * A self-assessment question (no correct answer). Render the options as a
564
+ * single-choice list; `score` supports a results chart in standalone mode.
565
+ */
566
+ export interface DiagnosticQuestion {
567
+ id: string;
568
+ /** Short category name, usable as a results-chart axis label. */
569
+ category: string;
570
+ questionText: string;
571
+ options: Array<{ label: DiagnoseOptionLabelName; text: string; score?: number }>;
572
+ prerequisiteConceptId: string | null;
573
+ prerequisiteConceptName: string | null;
574
+ }
575
+
576
+ /** A step title in a how-to outline (details stream later as {@link HowToStep}). */
577
+ export interface HowToStepOutline {
578
+ id: string;
579
+ title: string;
580
+ }
581
+
582
+ /** How-to outline — render immediately as a skeleton; steps fill in after. */
583
+ export interface HowToOutline {
584
+ title: string;
585
+ steps: HowToStepOutline[];
586
+ }
587
+
588
+ /** Content reference attached to a how-to step — render as a source card. */
589
+ export interface HowToStepContent {
590
+ contentId: string;
591
+ title: string;
592
+ url: string | null;
593
+ contentType: ContentTypeName | null;
594
+ /** The matching excerpt that demonstrates relevance. */
595
+ excerpt: string;
596
+ thumbnailUrl?: string;
597
+ }
598
+
599
+ /** A fully detailed how-to step — replaces the outline entry with the same `id`. */
600
+ export interface HowToStep {
601
+ id: string;
602
+ title: string;
603
+ description: string;
604
+ content: HowToStepContent | null;
605
+ /** Lucide icon name. */
606
+ icon?: string;
607
+ }
608
+
609
+ /** Header for a playlist — render as the playlist title/description. */
610
+ export interface PlaylistHeader {
611
+ title: string;
612
+ description: string;
613
+ }
614
+
615
+ /** A single item in a curated playlist — render as a content card. */
616
+ export interface PlaylistItem {
617
+ id: string;
618
+ title: string;
619
+ description: string | null;
620
+ body?: string | null;
621
+ contentType: ContentTypeName;
622
+ url: string | null;
623
+ thumbnailUrl?: string;
624
+ /** Why this item is included — streams in later via `skill_playlist_rationale`. */
625
+ rationale?: string;
626
+ }
627
+
628
+ /** Business case format sub-types. */
629
+ export const BusinessCaseSubType = {
630
+ RoiAnalysis: "roi-analysis",
631
+ StakeholderBrief: "stakeholder-brief",
632
+ VendorComparison: "vendor-comparison",
633
+ } as const;
634
+ export type BusinessCaseSubTypeName = (typeof BusinessCaseSubType)[keyof typeof BusinessCaseSubType];
635
+
636
+ /** Format option in the business case chooser — render as a selectable card. */
637
+ export interface BusinessCaseFormatOption {
638
+ subType: BusinessCaseSubTypeName;
639
+ label: string;
640
+ description: string;
641
+ /** Lucide icon name. */
642
+ icon: string;
643
+ }
644
+
645
+ /** Reference to a pre-generated business case draft, keyed by sub-type. */
646
+ export interface BusinessCaseDraftRef {
647
+ draftId: string;
648
+ skillInstanceId: string;
649
+ }
650
+
651
+ /** Node types in a solution design diagram. */
652
+ export const SolutionDesignNodeType = {
653
+ Process: "process",
654
+ Decision: "decision",
655
+ Datastore: "datastore",
656
+ External: "external",
657
+ Trigger: "trigger",
658
+ Start: "start",
659
+ End: "end",
660
+ } as const;
661
+ export type SolutionDesignNodeTypeName = (typeof SolutionDesignNodeType)[keyof typeof SolutionDesignNodeType];
662
+
663
+ /** A node in a solution design diagram — render as a flowchart node. */
664
+ export interface SolutionDesignNode {
665
+ id: string;
666
+ label: string;
667
+ description: string;
668
+ nodeType: SolutionDesignNodeTypeName;
669
+ /** Lucide icon name. */
670
+ icon: string;
671
+ }
672
+
673
+ /** A directed edge between two solution design nodes (by node `id`). */
674
+ export interface SolutionDesignEdge {
675
+ id: string;
676
+ source: string;
677
+ target: string;
678
+ label?: string;
679
+ }
680
+
681
+ /** Header for a solution design — render as the diagram title/summary. */
682
+ export interface SolutionDesignHeader {
683
+ title: string;
684
+ summary: string;
685
+ }
686
+
687
+ /** Suggested retry attached to a recoverable skill error. */
688
+ export interface SkillErrorRetryHint {
689
+ /** Re-ask with this prompt… */
690
+ prompt: string;
691
+ /** …optionally forcing this skill. */
692
+ forcedSkill?: SkillResponseTypeType;
693
+ }
694
+
695
+ // ============================================================================
696
+ // The `ask()` event stream
697
+ // ============================================================================
698
+
699
+ /**
700
+ * A structured skill event, discriminated on `name`. Every turn runs one skill
701
+ * (plain answers are the `answer` skill): `skill_start` announces which,
702
+ * payload events stream the deliverable, and `skill_end` closes it. Use
703
+ * {@link reduceSkillEvent} to fold these into renderable state instead of
704
+ * handling each variant by hand.
705
+ */
706
+ export type GuideSkillEvent =
707
+ | {
708
+ type: "skill";
709
+ name: typeof GuideEvent.SkillStart;
710
+ /** Which skill this turn runs, and the assistant message it belongs to. */
711
+ value: { skill: SkillResponseTypeType; messageId: string; diagnoseSubtype?: DiagnoseSubtypeName; followUpSkill?: string };
712
+ }
713
+ | {
714
+ type: "skill";
715
+ name: typeof GuideEvent.SkillContent;
716
+ /** Markdown token chunk of the skill's long-form body (business case drafts, etc.). */
717
+ value: { delta: string };
718
+ }
719
+ | {
720
+ type: "skill";
721
+ name: typeof GuideEvent.SkillDocuments;
722
+ /** Source documents behind the reply — render as citation/source cards. */
723
+ value: { documents: GuideDocument[] };
724
+ }
725
+ | {
726
+ type: "skill";
727
+ name: typeof GuideEvent.SkillRecommendations;
728
+ /**
729
+ * Either the recommend skill's content cards ({@link ContentRecommendation},
730
+ * has `targetSkill`) or post-answer follow-up pills ({@link SkillRecommendation},
731
+ * has `skill`). Discriminate by which of those keys is present.
732
+ */
733
+ value: { recommendations: ContentRecommendation[] | SkillRecommendation[] };
734
+ }
735
+ | {
736
+ type: "skill";
737
+ name: typeof GuideEvent.SkillCTAs;
738
+ /** Calls-to-action for this turn — render the first below the response. */
739
+ value: { ctas: SkillCTA[] };
740
+ }
741
+ | { type: "skill"; name: typeof GuideEvent.SkillDiagnoseHeader; value: { header: DiagnosticHeader } }
742
+ | { type: "skill"; name: typeof GuideEvent.SkillDiagnoseQuestion; value: { question: DiagnosticQuestion } }
743
+ | { type: "skill"; name: typeof GuideEvent.SkillPlaylistItems; value: { items: PlaylistItem[] } }
744
+ | { type: "skill"; name: typeof GuideEvent.SkillPlaylistHeader; value: { header: PlaylistHeader } }
745
+ | {
746
+ type: "skill";
747
+ name: typeof GuideEvent.SkillPlaylistRationale;
748
+ /** Late-streamed rationale for the playlist item with this `itemId`. */
749
+ value: { itemId: string; rationale: string };
750
+ }
751
+ | {
752
+ type: "skill";
753
+ name: typeof GuideEvent.SkillHowToOutline;
754
+ /** Render the outline immediately; detailed steps stream in `skill_howto_steps`. */
755
+ value: { outline: HowToOutline; skillInstanceId?: string };
756
+ }
757
+ | { type: "skill"; name: typeof GuideEvent.SkillHowToSteps; value: { steps: HowToStep[] } }
758
+ | {
759
+ type: "skill";
760
+ name: typeof GuideEvent.SkillBusinessCaseChooser;
761
+ /** Format options to choose from; `drafts` maps subType → pre-generated draft. */
762
+ value: { formats: BusinessCaseFormatOption[]; drafts: Record<string, BusinessCaseDraftRef> };
763
+ }
764
+ | {
765
+ type: "skill";
766
+ name: typeof GuideEvent.SkillBusinessCaseDelta;
767
+ /** Markdown chunk of the draft for one business case format. */
768
+ value: { subType: BusinessCaseSubTypeName; delta: string };
769
+ }
770
+ | { type: "skill"; name: typeof GuideEvent.SkillSolutionDesignHeader; value: { header: SolutionDesignHeader } }
771
+ | {
772
+ type: "skill";
773
+ name: typeof GuideEvent.SkillSolutionDesignDiagram;
774
+ /** Flowchart nodes + edges — render with any diagram/graph component. */
775
+ value: { nodes: SolutionDesignNode[]; edges: SolutionDesignEdge[] };
776
+ }
777
+ | {
778
+ type: "skill";
779
+ name: typeof GuideEvent.SkillEnd;
780
+ /** `skillInstanceId` is the persisted deliverable's id (for `skills.get`/`update`). */
781
+ value: { skillInstanceId?: string; skillResponseId?: string };
782
+ };
783
+
303
784
  /**
304
- * A single event yielded by {@link GuideClient.ask}. The common case is `text`
305
- * (the streamed chat reply); `skill` carries every structured skill payload,
306
- * discriminated by its {@link GuideEventName} `name`.
785
+ * A single event yielded by {@link GuideClient.ask}.
786
+ *
787
+ * - `text` streams the chat reply; `skill` carries the structured deliverable
788
+ * (see {@link GuideSkillEvent}).
789
+ * - `gate` means the guide wants an email before continuing — collect one,
790
+ * call `submitGate(email)`, then re-send the message.
791
+ * - `skill-unknown` is a forward-compatibility escape hatch: a structured
792
+ * event this SDK version doesn't know. Safe to ignore.
307
793
  */
308
794
  export type GuideStreamEvent =
309
795
  | { type: "run-started"; runId: string }
310
796
  | { type: "message-start"; messageId: string; role: string }
311
797
  | { type: "text"; messageId: string; delta: string }
312
798
  | { type: "message-end"; messageId: string }
313
- | { type: "skill"; name: GuideEventName; value: Record<string, unknown> }
799
+ | GuideSkillEvent
800
+ | { type: "skill-unknown"; name: string; value: Record<string, unknown> }
314
801
  | { type: "gate"; skill: SkillResponseTypeType; prompt?: string; skillInstanceId?: string }
315
- | { type: "error"; message: string; code?: string }
802
+ | { type: "error"; message: string; code?: string; retryable?: boolean; retryHint?: SkillErrorRetryHint }
316
803
  | { type: "run-finished"; finishReason?: string };