@cosmoops/connect-sdk 0.30.1 → 0.33.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/CHANGELOG.md +267 -0
- package/dist/cjs/client/components/visitor-chat.js +387 -0
- package/dist/cjs/client/components/visitor-stream.js +124 -0
- package/dist/cjs/client/index.js +23 -3
- package/dist/cjs/client/provider.js +24 -0
- package/dist/cjs/client/use-push.js +139 -0
- package/dist/cjs/server/client.js +15 -1
- package/dist/cjs/server/handler.js +196 -0
- package/dist/cjs/server/resources/ai.js +85 -0
- package/dist/cjs/server/resources/enrichment.js +107 -0
- package/dist/cjs/server/resources/push.js +92 -0
- package/dist/cjs/server/resources/visitor.js +227 -0
- package/dist/esm/client/components/visitor-chat.js +384 -0
- package/dist/esm/client/components/visitor-stream.js +120 -0
- package/dist/esm/client/index.js +17 -1
- package/dist/esm/client/provider.js +23 -0
- package/dist/esm/client/use-push.js +136 -0
- package/dist/esm/server/client.js +15 -1
- package/dist/esm/server/handler.js +196 -0
- package/dist/esm/server/resources/ai.js +85 -0
- package/dist/esm/server/resources/enrichment.js +107 -0
- package/dist/esm/server/resources/push.js +88 -0
- package/dist/esm/server/resources/visitor.js +222 -0
- package/dist/types/client/components/visitor-chat.d.ts +120 -0
- package/dist/types/client/components/visitor-stream.d.ts +42 -0
- package/dist/types/client/index.d.ts +13 -1
- package/dist/types/client/provider.d.ts +21 -0
- package/dist/types/client/use-push.d.ts +56 -0
- package/dist/types/server/client.d.ts +13 -1
- package/dist/types/server/handler.d.ts +0 -24
- package/dist/types/server/resources/ai.d.ts +75 -1
- package/dist/types/server/resources/enrichment.d.ts +102 -1
- package/dist/types/server/resources/push.d.ts +73 -0
- package/dist/types/server/resources/visitor.d.ts +200 -0
- package/dist/types/types.d.ts +683 -0
- package/package.json +3 -2
package/dist/types/types.d.ts
CHANGED
|
@@ -534,6 +534,160 @@ export interface WriteLogResponse {
|
|
|
534
534
|
attributed: boolean;
|
|
535
535
|
}
|
|
536
536
|
/** One turn of a RAG chat. Matches the AI SDK's `UIMessage` loosely enough to pass one straight through. */
|
|
537
|
+
/** Where a knowledge item's text came from. */
|
|
538
|
+
export type VisitorKnowledgeSource = "website" | "authored" | "upload";
|
|
539
|
+
/**
|
|
540
|
+
* How a published item reaches the model.
|
|
541
|
+
*
|
|
542
|
+
* `inline` is the **normal and better** state for a small knowledge base, not a
|
|
543
|
+
* degraded one: the model is shown every published item rather than a top-k
|
|
544
|
+
* slice, with no chunk boundaries and no retrieval round trip. The other three
|
|
545
|
+
* only occur once a knowledge base has outgrown the prompt.
|
|
546
|
+
*/
|
|
547
|
+
export type VisitorKnowledgeIndexState = "inline" | "indexing" | "indexed" | "failed";
|
|
548
|
+
/**
|
|
549
|
+
* One thing a workspace's website chat is allowed to tell strangers.
|
|
550
|
+
*
|
|
551
|
+
* **Not the workspace's Context Index.** That corpus is fed by every Drive
|
|
552
|
+
* upload; this store holds only what somebody explicitly published, which is
|
|
553
|
+
* what makes it safe to answer an anonymous visitor from. Nothing arrives here
|
|
554
|
+
* as a side effect of anything else.
|
|
555
|
+
*/
|
|
556
|
+
export interface VisitorKnowledgeItem {
|
|
557
|
+
id: string;
|
|
558
|
+
title: string;
|
|
559
|
+
body: string;
|
|
560
|
+
source: VisitorKnowledgeSource;
|
|
561
|
+
/** The crawled URL, or the uploaded file's name. `null` for authored text. */
|
|
562
|
+
sourceRef: string | null;
|
|
563
|
+
status: "draft" | "published";
|
|
564
|
+
indexState: VisitorKnowledgeIndexState;
|
|
565
|
+
/** Why the last index attempt failed, for your own screen to show. */
|
|
566
|
+
indexError: string | null;
|
|
567
|
+
/** ISO-8601, or `null` for a draft. */
|
|
568
|
+
publishedAt: string | null;
|
|
569
|
+
createdAt: string;
|
|
570
|
+
updatedAt: string;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* The knowledge base, and how its published half reaches the model.
|
|
574
|
+
*
|
|
575
|
+
* `mode` is a property of the *set*, which is why it is here and not on each
|
|
576
|
+
* item - and why a screen should read it from here rather than inferring it.
|
|
577
|
+
*/
|
|
578
|
+
export interface VisitorKnowledgeList {
|
|
579
|
+
/** Drafts included: this is the list a customer audits. */
|
|
580
|
+
items: VisitorKnowledgeItem[];
|
|
581
|
+
mode: "none" | "inline" | "retrieval";
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* What the widget should ask for next, decided by Connect.
|
|
585
|
+
*
|
|
586
|
+
* `"message"` is the ordinary composer. The others mean render that field's own
|
|
587
|
+
* input instead - and the reason this is a server decision rather than something
|
|
588
|
+
* the widget infers from the assistant's sentence is the phone: an email asked
|
|
589
|
+
* for in prose gets typed into a general text box with autocorrect on, where
|
|
590
|
+
* `type="email" inputmode="email" autocomplete="email"` brings up a keyboard
|
|
591
|
+
* with an `@` on it. A six-digit code wants `autocomplete="one-time-code"`,
|
|
592
|
+
* which is what lets iOS offer the code straight from the notification.
|
|
593
|
+
*/
|
|
594
|
+
export type VisitorField = "message" | "email" | "code" | "name";
|
|
595
|
+
/** One stored message in a visitor conversation. `at` is ISO-8601. */
|
|
596
|
+
export interface VisitorMessage {
|
|
597
|
+
role: "visitor" | "assistant";
|
|
598
|
+
text: string;
|
|
599
|
+
at: string;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* An appointment this conversation booked, as a visitor is allowed to see it.
|
|
603
|
+
*
|
|
604
|
+
* Deliberately not the booking: no host ids, no workspace ids, no internal
|
|
605
|
+
* reference - the same narrowing Connect's own public booking page makes.
|
|
606
|
+
*/
|
|
607
|
+
export interface VisitorBooking {
|
|
608
|
+
startAt: string;
|
|
609
|
+
endAt: string;
|
|
610
|
+
/** The workspace's zone, not the visitor's - the zone the confirmation uses. */
|
|
611
|
+
timezone: string;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* A conversation, as `visitor.startSession` opens or reopens it.
|
|
615
|
+
*
|
|
616
|
+
* **It carries no greeting and no suggestions.** Those are your own widget
|
|
617
|
+
* settings, rendered from your own store - so your bubble paints its first frame
|
|
618
|
+
* with no call to Connect at all, and this exists only to mint or recover the
|
|
619
|
+
* conversation Connect holds.
|
|
620
|
+
*/
|
|
621
|
+
export interface VisitorSession {
|
|
622
|
+
/**
|
|
623
|
+
* Hold this and send it with every turn.
|
|
624
|
+
*
|
|
625
|
+
* Treat it as a credential: it is the only thing identifying the conversation,
|
|
626
|
+
* and anyone holding it can continue it. Keep it in `sessionStorage` - it
|
|
627
|
+
* should survive a reload, which is what lets somebody follow a link out of
|
|
628
|
+
* the chat and come back, and it should not travel to another tab or outlive
|
|
629
|
+
* the visit.
|
|
630
|
+
*/
|
|
631
|
+
sessionId: string;
|
|
632
|
+
/** The transcript, oldest first. Empty on a new conversation. */
|
|
633
|
+
messages: VisitorMessage[];
|
|
634
|
+
field: VisitorField;
|
|
635
|
+
turnsRemaining: number;
|
|
636
|
+
booking: VisitorAppointment | null;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* The appointment `visitor.book` placed, and the one a reopened conversation
|
|
640
|
+
* reports.
|
|
641
|
+
*
|
|
642
|
+
* `VisitorBooking` plus `label`, and the extra field is the point: the words are
|
|
643
|
+
* produced by the same formatter the confirmation email uses, so the chat and
|
|
644
|
+
* the email cannot read differently about the same appointment. Render it rather
|
|
645
|
+
* than formatting `startAt` yourself - the visitor's browser is in their zone and
|
|
646
|
+
* the appointment is in the host's, and `Intl`'s month names differ between
|
|
647
|
+
* engines besides.
|
|
648
|
+
*/
|
|
649
|
+
export interface VisitorAppointment extends VisitorBooking {
|
|
650
|
+
label: string;
|
|
651
|
+
}
|
|
652
|
+
/** One time the assistant may offer, as its slot lookup returned it. */
|
|
653
|
+
export interface VisitorSlot {
|
|
654
|
+
/** ISO-8601. Pass this back verbatim to book it. */
|
|
655
|
+
startAt: string;
|
|
656
|
+
/** Already written out in the workspace's own timezone - render this, don't reformat. */
|
|
657
|
+
label: string;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* The times a turn looked up, lifted out of the tool result.
|
|
661
|
+
*
|
|
662
|
+
* **Draw the buttons from this, never from the assistant's sentence.** A time the
|
|
663
|
+
* visitor taps is then one the server offered, which is what makes
|
|
664
|
+
* `visitor.book` unable to take a time nobody was given.
|
|
665
|
+
*/
|
|
666
|
+
export interface VisitorSlotOffer {
|
|
667
|
+
eventType: string;
|
|
668
|
+
durationMinutes: number;
|
|
669
|
+
timezone: string;
|
|
670
|
+
slots: VisitorSlot[];
|
|
671
|
+
}
|
|
672
|
+
/** What a tool did this turn. `result` is the tool's own payload - switch on `name`. */
|
|
673
|
+
export interface VisitorToolCall {
|
|
674
|
+
name: string;
|
|
675
|
+
result: unknown;
|
|
676
|
+
}
|
|
677
|
+
/** The `done` event at the end of a streamed turn. */
|
|
678
|
+
export interface VisitorTurnDone {
|
|
679
|
+
toolCalls: VisitorToolCall[];
|
|
680
|
+
field: VisitorField;
|
|
681
|
+
turnsRemaining: number;
|
|
682
|
+
/**
|
|
683
|
+
* Whether the reply was grounded in published knowledge.
|
|
684
|
+
*
|
|
685
|
+
* `false` means nothing published covered the question, so the assistant said
|
|
686
|
+
* so and offered a call. Worth recording: a run of them is a knowledge base
|
|
687
|
+
* with a gap in it, which is the most useful signal this endpoint produces.
|
|
688
|
+
*/
|
|
689
|
+
grounded: boolean;
|
|
690
|
+
}
|
|
537
691
|
export interface ChatMessage {
|
|
538
692
|
role: "user" | "assistant" | "system";
|
|
539
693
|
[field: string]: unknown;
|
|
@@ -586,6 +740,54 @@ export interface AiConversationDetail {
|
|
|
586
740
|
conversation: AiConversation;
|
|
587
741
|
turns: AiConversationTurn[];
|
|
588
742
|
}
|
|
743
|
+
/**
|
|
744
|
+
* One question in an assessment, as the platform defines it. Echoed back on a
|
|
745
|
+
* draft so the questions can be rendered without a second round trip.
|
|
746
|
+
*/
|
|
747
|
+
export interface AssessmentSection {
|
|
748
|
+
/** Stable identifier - the key answers are filed under. */
|
|
749
|
+
key: string;
|
|
750
|
+
/** The question as a person reads it. */
|
|
751
|
+
question: string;
|
|
752
|
+
/** What belongs here. Written to steer generation, but useful as helper text. */
|
|
753
|
+
hint?: string;
|
|
754
|
+
/** Fewest answers a draft offers. */
|
|
755
|
+
min?: number;
|
|
756
|
+
/** Most answers a draft offers, and the cap worth enforcing on edits. */
|
|
757
|
+
max?: number;
|
|
758
|
+
}
|
|
759
|
+
/** Answers, keyed by section key. */
|
|
760
|
+
export type AssessmentAnswers = Record<string, string[]>;
|
|
761
|
+
export interface AssessmentDraft {
|
|
762
|
+
assessmentKey: string;
|
|
763
|
+
title: string;
|
|
764
|
+
/** The definition's sections, in the order they should be asked. */
|
|
765
|
+
sections: AssessmentSection[];
|
|
766
|
+
/** Suggested answers - a strawman to edit, never a finished answer. */
|
|
767
|
+
answers: AssessmentAnswers;
|
|
768
|
+
}
|
|
769
|
+
export type AssessmentFindingSeverity = "critical" | "warning" | "note";
|
|
770
|
+
export interface AssessmentFinding {
|
|
771
|
+
severity: AssessmentFindingSeverity;
|
|
772
|
+
/** Section keys this finding concerns - usually two, since the useful findings are tensions between sections. */
|
|
773
|
+
sections: string[];
|
|
774
|
+
title: string;
|
|
775
|
+
detail: string;
|
|
776
|
+
/** The specific change to make. */
|
|
777
|
+
fix: string;
|
|
778
|
+
}
|
|
779
|
+
export interface AssessmentReview {
|
|
780
|
+
assessmentKey: string;
|
|
781
|
+
findings: AssessmentFinding[];
|
|
782
|
+
/** The one assumption everything rests on, and a cheap way to test it. Lead with this. */
|
|
783
|
+
riskiestAssumption: {
|
|
784
|
+
section: string;
|
|
785
|
+
assumption: string;
|
|
786
|
+
experiment: string;
|
|
787
|
+
};
|
|
788
|
+
/** 0-100, advisory and coarse - it shows movement between revisions. */
|
|
789
|
+
score: number;
|
|
790
|
+
}
|
|
589
791
|
export interface WorkspaceApp {
|
|
590
792
|
applicationId: string;
|
|
591
793
|
/** Stable, human-readable key - branch on this, not `applicationId`, which is per-environment. */
|
|
@@ -1698,6 +1900,428 @@ export type CompanyEnrichmentResult = {
|
|
|
1698
1900
|
status: "skipped";
|
|
1699
1901
|
reason: EnrichmentSkipReason;
|
|
1700
1902
|
};
|
|
1903
|
+
/**
|
|
1904
|
+
* Where a brand claim was read. Its own union rather than `EnrichmentSource`,
|
|
1905
|
+
* which has no notion of a stylesheet or a rendered page.
|
|
1906
|
+
*/
|
|
1907
|
+
export type BrandCitationSource = "website" | "stylesheet" | "render";
|
|
1908
|
+
export interface BrandCitation {
|
|
1909
|
+
source: BrandCitationSource;
|
|
1910
|
+
url: string;
|
|
1911
|
+
/** ISO-8601. */
|
|
1912
|
+
retrievedAt: string;
|
|
1913
|
+
}
|
|
1914
|
+
/** Whether the company sells a thing, an engagement, or both. */
|
|
1915
|
+
export type BusinessModel = "product" | "service" | "hybrid";
|
|
1916
|
+
/**
|
|
1917
|
+
* Where an official name came from.
|
|
1918
|
+
*
|
|
1919
|
+
* `website-legal` is a legal disclosure the company published itself - a
|
|
1920
|
+
* footer copyright line, or the entity named on its Terms page - matched by
|
|
1921
|
+
* code against a corporate suffix and cited. `registry` is a statutory
|
|
1922
|
+
* register and outranks it. Neither is a model's recall.
|
|
1923
|
+
*/
|
|
1924
|
+
export type OfficialNameSource = "registry" | "website-legal";
|
|
1925
|
+
export interface Offering {
|
|
1926
|
+
name: string;
|
|
1927
|
+
/** The company's own grouping when it has one. */
|
|
1928
|
+
category: string | null;
|
|
1929
|
+
description: string | null;
|
|
1930
|
+
url: string | null;
|
|
1931
|
+
}
|
|
1932
|
+
export type LogoKind = "primary" | "mark" | "wordmark" | "favicon" | "social";
|
|
1933
|
+
export interface BrandLogo {
|
|
1934
|
+
/**
|
|
1935
|
+
* A **short-lived signed URL**, minted fresh on every read.
|
|
1936
|
+
*
|
|
1937
|
+
* Do not store it and do not bake it into a cached page - it will expire
|
|
1938
|
+
* and become a broken image with a plausible-looking address. Render it
|
|
1939
|
+
* directly, or re-read the profile, which on a cache hit is one document
|
|
1940
|
+
* read. `null` when Connect holds no cached copy, in which case `sourceUrl`
|
|
1941
|
+
* is all there is.
|
|
1942
|
+
*/
|
|
1943
|
+
url: string | null;
|
|
1944
|
+
/** Where it lives on the company's own site. Stable, and the citation. */
|
|
1945
|
+
sourceUrl: string;
|
|
1946
|
+
kind: LogoKind;
|
|
1947
|
+
/** Lowercase extension without the dot: `svg`, `png`, `webp`. */
|
|
1948
|
+
format: string;
|
|
1949
|
+
width: number | null;
|
|
1950
|
+
height: number | null;
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* How a colour was obtained, which is how much it is worth.
|
|
1954
|
+
*
|
|
1955
|
+
* `css-var` is the company naming its own colour in its own tokens - the
|
|
1956
|
+
* strongest signal short of a brand manual. `computed` comes from a rendered
|
|
1957
|
+
* page. `frequency` is a ranked count of declarations and is the weakest.
|
|
1958
|
+
*/
|
|
1959
|
+
export type PaletteSource = "css-var" | "computed" | "frequency";
|
|
1960
|
+
export type PaletteRole = "primary" | "secondary" | "accent" | "background" | "surface" | "text" | "muted";
|
|
1961
|
+
export interface PaletteEntry {
|
|
1962
|
+
/** Lowercase `#rrggbb`. */
|
|
1963
|
+
hex: string;
|
|
1964
|
+
/** Assigned by a model over measured values - never invented by one. */
|
|
1965
|
+
role: PaletteRole | null;
|
|
1966
|
+
source: PaletteSource;
|
|
1967
|
+
/** The CSS custom property it came from, when `source` is `css-var`. */
|
|
1968
|
+
variableName: string | null;
|
|
1969
|
+
occurrences: number;
|
|
1970
|
+
}
|
|
1971
|
+
export type TypefaceRole = "display" | "heading" | "body" | "mono";
|
|
1972
|
+
export interface TypefaceEntry {
|
|
1973
|
+
/** The first named family in the stack - the one the company chose, not the fallbacks. */
|
|
1974
|
+
family: string;
|
|
1975
|
+
role: TypefaceRole | null;
|
|
1976
|
+
weights: number[];
|
|
1977
|
+
webfontUrl: string | null;
|
|
1978
|
+
occurrences: number;
|
|
1979
|
+
}
|
|
1980
|
+
/** One step of a measured scale. */
|
|
1981
|
+
export interface ScaleStep {
|
|
1982
|
+
value: number;
|
|
1983
|
+
/** `px`, `rem`, `em`, `%`. */
|
|
1984
|
+
unit: string;
|
|
1985
|
+
occurrences: number;
|
|
1986
|
+
}
|
|
1987
|
+
/**
|
|
1988
|
+
* A CSS framework detected by signature.
|
|
1989
|
+
*
|
|
1990
|
+
* Worth checking first, because it tells you how to read everything else: a
|
|
1991
|
+
* Tailwind site's spacing scale is Tailwind's defaults rather than a
|
|
1992
|
+
* considered choice, and a palette full of `--bs-*` is Bootstrap's.
|
|
1993
|
+
*/
|
|
1994
|
+
export type CssFramework = "tailwind" | "bootstrap" | "material" | "chakra" | "bulma";
|
|
1995
|
+
export interface BrandKit {
|
|
1996
|
+
logos: BrandLogo[];
|
|
1997
|
+
palette: PaletteEntry[];
|
|
1998
|
+
typography: TypefaceEntry[];
|
|
1999
|
+
voice: {
|
|
2000
|
+
tagline: string | null;
|
|
2001
|
+
/** A few adjectives for the register the copy is written in. */
|
|
2002
|
+
tone: string[];
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
export interface DesignSystem {
|
|
2006
|
+
radiusScale: ScaleStep[];
|
|
2007
|
+
spacingScale: ScaleStep[];
|
|
2008
|
+
/** Raw `box-shadow` values, most used first. */
|
|
2009
|
+
shadows: string[];
|
|
2010
|
+
/** `@media` breakpoint widths as written, ascending. */
|
|
2011
|
+
breakpoints: string[];
|
|
2012
|
+
/** Every `--*` declaration on `:root` - the company's own token vocabulary, verbatim. */
|
|
2013
|
+
customProperties: {
|
|
2014
|
+
name: string;
|
|
2015
|
+
value: string;
|
|
2016
|
+
}[];
|
|
2017
|
+
framework: CssFramework | null;
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* What a company looks like, measured off its own website.
|
|
2021
|
+
*
|
|
2022
|
+
* The important property: **the numbers and the colours here were parsed, not
|
|
2023
|
+
* generated.** Every hex came out of a stylesheet and every scale step is a
|
|
2024
|
+
* count of real declarations. A model wrote only the prose (`about`,
|
|
2025
|
+
* `industry`, `businessModel`, `offerings`, `voice`) and assigned `role` to
|
|
2026
|
+
* values it was shown. Ask any model directly for a brand colour and it
|
|
2027
|
+
* answers `#3B82F6`; that is the failure this shape exists to avoid.
|
|
2028
|
+
*/
|
|
2029
|
+
export interface BrandProfile {
|
|
2030
|
+
/** Normalised - lowercase, no scheme, no `www.`. */
|
|
2031
|
+
domain: string;
|
|
2032
|
+
/** The trading name, as the company writes it. */
|
|
2033
|
+
knownAs: string | null;
|
|
2034
|
+
officialName: string | null;
|
|
2035
|
+
officialNameSource: OfficialNameSource | null;
|
|
2036
|
+
about: string | null;
|
|
2037
|
+
industry: string | null;
|
|
2038
|
+
businessModel: BusinessModel | null;
|
|
2039
|
+
offerings: Offering[];
|
|
2040
|
+
brandKit: BrandKit;
|
|
2041
|
+
designSystem: DesignSystem;
|
|
2042
|
+
/** Derived from how many aspects the sources actually filled - not a model's self-report. */
|
|
2043
|
+
confidence: "high" | "medium" | "low";
|
|
2044
|
+
citations: BrandCitation[];
|
|
2045
|
+
/** ISO-8601. Age the profile against this. */
|
|
2046
|
+
retrievedAt: string;
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* Why there is nothing to report. A normal 200, not an error.
|
|
2050
|
+
*
|
|
2051
|
+
* `consumer_domain` and `blocked_domain` will never succeed on retry.
|
|
2052
|
+
* `unreachable` may. `no_stylesheet` means the site answered but shipped no
|
|
2053
|
+
* CSS Connect could measure - a real outcome for a page built entirely in
|
|
2054
|
+
* inline styles.
|
|
2055
|
+
*/
|
|
2056
|
+
export type BrandSkipReason = "no_domain" | "consumer_domain" | "blocked_domain" | "unreachable" | "no_signal" | "no_stylesheet";
|
|
2057
|
+
export type BrandExtractionResult = {
|
|
2058
|
+
status: "extracted";
|
|
2059
|
+
profile: BrandProfile;
|
|
2060
|
+
/** Whether Connect served this from its platform-wide cache. The profile is the same either way. */
|
|
2061
|
+
cached: boolean;
|
|
2062
|
+
} | {
|
|
2063
|
+
status: "skipped";
|
|
2064
|
+
reason: BrandSkipReason;
|
|
2065
|
+
};
|
|
2066
|
+
/** The five questions a readiness report answers. */
|
|
2067
|
+
export type ReadinessDimension =
|
|
2068
|
+
/** Can an agent *act* here? MCP, OpenAPI, agent cards. */
|
|
2069
|
+
"agent_interfaces"
|
|
2070
|
+
/** Can an agent get the content at all? JavaScript dependency, bot walls, semantic structure. */
|
|
2071
|
+
| "retrievability"
|
|
2072
|
+
/** Can an agent *read* it? llms.txt, markdown twins, structured data. */
|
|
2073
|
+
| "machine_readable"
|
|
2074
|
+
/** Is an agent *allowed*, and does the site say so deliberately? */
|
|
2075
|
+
| "crawl_consent"
|
|
2076
|
+
/** Is the site safe and identifiable to transact with? */
|
|
2077
|
+
| "trust";
|
|
2078
|
+
/**
|
|
2079
|
+
* How a check came out.
|
|
2080
|
+
*
|
|
2081
|
+
* `error` is the one to handle deliberately: it means Connect **could not
|
|
2082
|
+
* measure** this - a timeout, a refused connection - and it is excluded from
|
|
2083
|
+
* the score rather than counted as a failure. Render it as unknown, never as a
|
|
2084
|
+
* problem. `absent` ("you do not have this") and `fail` ("you have it and it is
|
|
2085
|
+
* broken") are likewise distinct and want different words.
|
|
2086
|
+
*/
|
|
2087
|
+
export type ReadinessCheckStatus = "pass" | "partial" | "fail" | "absent" | "error";
|
|
2088
|
+
/**
|
|
2089
|
+
* Whether a check can cost points or only earn them.
|
|
2090
|
+
*
|
|
2091
|
+
* A `bonus` check raises the score when it passes and leaves it alone when it
|
|
2092
|
+
* does not - so a site is never marked down for missing a convention almost
|
|
2093
|
+
* nobody has adopted yet. Worth surfacing differently in a UI: a failed `core`
|
|
2094
|
+
* check is a gap, an absent `bonus` one is an opportunity.
|
|
2095
|
+
*/
|
|
2096
|
+
export type ReadinessCheckKind = "core" | "bonus";
|
|
2097
|
+
/** One measured fact, with the evidence that settles it. */
|
|
2098
|
+
export interface ReadinessCheck {
|
|
2099
|
+
/** Stable dotted id, e.g. `mcp.server`. Safe to key on; findings cite it. */
|
|
2100
|
+
id: string;
|
|
2101
|
+
dimension: ReadinessDimension;
|
|
2102
|
+
kind: ReadinessCheckKind;
|
|
2103
|
+
title: string;
|
|
2104
|
+
status: ReadinessCheckStatus;
|
|
2105
|
+
/** Relative importance inside its dimension. Only ratios are meaningful. */
|
|
2106
|
+
weight: number;
|
|
2107
|
+
/** What was actually observed, in one line - specific enough to show the user verbatim. */
|
|
2108
|
+
observed: string;
|
|
2109
|
+
/** The URL that proves it, where there is one. */
|
|
2110
|
+
evidence: string | null;
|
|
2111
|
+
}
|
|
2112
|
+
export interface ReadinessDimensionScore {
|
|
2113
|
+
dimension: ReadinessDimension;
|
|
2114
|
+
/** 0-100. */
|
|
2115
|
+
score: number;
|
|
2116
|
+
/** This dimension's share of the overall score. */
|
|
2117
|
+
weight: number;
|
|
2118
|
+
/** Core checks that could not be measured. Show this rather than implying full coverage. */
|
|
2119
|
+
unmeasured: number;
|
|
2120
|
+
}
|
|
2121
|
+
export type ReadinessGrade = "A" | "B" | "C" | "D" | "F";
|
|
2122
|
+
/** One thing to do about it, anchored in the checks that motivated it. */
|
|
2123
|
+
export interface ReadinessFinding {
|
|
2124
|
+
severity: "critical" | "warning" | "note";
|
|
2125
|
+
/** The checks this is about. Every id here appears in `checks`. */
|
|
2126
|
+
checkIds: string[];
|
|
2127
|
+
title: string;
|
|
2128
|
+
detail: string;
|
|
2129
|
+
fix: string;
|
|
2130
|
+
effort: "low" | "medium" | "high";
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* Whether an assistant could answer a buyer's question from the site's own pages.
|
|
2134
|
+
*
|
|
2135
|
+
* The one judged rather than measured part of the report - a model read the
|
|
2136
|
+
* pages and decided. Everything else in `checks` was parsed from real responses.
|
|
2137
|
+
*/
|
|
2138
|
+
export interface ReadinessAnswerability {
|
|
2139
|
+
offering: boolean;
|
|
2140
|
+
pricing: boolean;
|
|
2141
|
+
howToBuy: boolean;
|
|
2142
|
+
contact: boolean;
|
|
2143
|
+
summary: string;
|
|
2144
|
+
gaps: string[];
|
|
2145
|
+
}
|
|
2146
|
+
/**
|
|
2147
|
+
* How ready a site is for AI agents.
|
|
2148
|
+
*
|
|
2149
|
+
* The important property, and the one that makes this worth showing a customer:
|
|
2150
|
+
* **the score was computed from the checks, and the checks were measured.**
|
|
2151
|
+
* Whether an MCP server exists is a completed JSON-RPC handshake, not a file
|
|
2152
|
+
* that happened to answer 200; whether assistants are blocked is that site's
|
|
2153
|
+
* `robots.txt` evaluated by its real rules. A model contributed only
|
|
2154
|
+
* `answerability` and the prose in `findings`, and it cannot alter a status or
|
|
2155
|
+
* the score.
|
|
2156
|
+
*/
|
|
2157
|
+
export interface ReadinessReport {
|
|
2158
|
+
/** Normalised - lowercase, no scheme, no `www.`. */
|
|
2159
|
+
domain: string;
|
|
2160
|
+
/** 0-100, weighted across dimensions. */
|
|
2161
|
+
score: number;
|
|
2162
|
+
grade: ReadinessGrade;
|
|
2163
|
+
dimensions: ReadinessDimensionScore[];
|
|
2164
|
+
checks: ReadinessCheck[];
|
|
2165
|
+
/** Ordered by consequence, most serious first. */
|
|
2166
|
+
findings: ReadinessFinding[];
|
|
2167
|
+
answerability: ReadinessAnswerability | null;
|
|
2168
|
+
/** The one thing to do first. Drawn from `findings`, so it never disagrees with them. */
|
|
2169
|
+
topFix: {
|
|
2170
|
+
checkId: string;
|
|
2171
|
+
title: string;
|
|
2172
|
+
fix: string;
|
|
2173
|
+
} | null;
|
|
2174
|
+
/** ISO-8601. Age the report against this. */
|
|
2175
|
+
assessedAt: string;
|
|
2176
|
+
}
|
|
2177
|
+
/**
|
|
2178
|
+
* Why there is nothing to report. A normal 200, not an error.
|
|
2179
|
+
*
|
|
2180
|
+
* The distinction that matters for what you show and whether you retry:
|
|
2181
|
+
* `unreachable` is about the **site** - it did not answer - and may succeed
|
|
2182
|
+
* later. `provider_unavailable` is about **Connect** - our assessment service
|
|
2183
|
+
* is down or unconfigured - and is worth retrying without telling the user
|
|
2184
|
+
* anything about their own site. `no_domain` and `blocked_domain` never succeed
|
|
2185
|
+
* on retry.
|
|
2186
|
+
*/
|
|
2187
|
+
export type ReadinessSkipReason = "no_domain" | "blocked_domain" | "consumer_domain" | "unreachable" | "provider_unavailable";
|
|
2188
|
+
export type ReadinessAssessmentResult = {
|
|
2189
|
+
status: "assessed";
|
|
2190
|
+
report: ReadinessReport;
|
|
2191
|
+
/** Whether Connect served this from its platform-wide cache. The report is the same either way. */
|
|
2192
|
+
cached: boolean;
|
|
2193
|
+
} | {
|
|
2194
|
+
status: "skipped";
|
|
2195
|
+
reason: ReadinessSkipReason;
|
|
2196
|
+
};
|
|
2197
|
+
/** The five things a presence is scored on. */
|
|
2198
|
+
export type PresenceCategory = "findability" | "identity" | "social" | "contactability" | "trust";
|
|
2199
|
+
/**
|
|
2200
|
+
* How a check came out.
|
|
2201
|
+
*
|
|
2202
|
+
* `partial` is a real third state rather than a rounded failure: a site with
|
|
2203
|
+
* `og:title` but no `og:image` has done most of the work, and rendering that as
|
|
2204
|
+
* a failure is both wrong and discouraging.
|
|
2205
|
+
*/
|
|
2206
|
+
export type PresenceCriterionStatus = "pass" | "partial" | "fail";
|
|
2207
|
+
/** Where a score sits, coarsely. Advisory - use it to pick wording, not to make a decision. */
|
|
2208
|
+
export type PresenceBand = "absent" | "emerging" | "established" | "strong";
|
|
2209
|
+
/** The networks a scan recognises. `other` is a profile that matched no known pattern, kept rather than dropped. */
|
|
2210
|
+
export type PresenceNetwork = "linkedin" | "instagram" | "facebook" | "youtube" | "google_business" | "crunchbase" | "x" | "tiktok" | "github" | "other";
|
|
2211
|
+
/**
|
|
2212
|
+
* One check, its verdict and the evidence behind it.
|
|
2213
|
+
*
|
|
2214
|
+
* `weight` and `earned` are both published so the total can be reconstructed:
|
|
2215
|
+
* a `partial` that cost two points and one that cost six look identical
|
|
2216
|
+
* without them, and a score nobody can reconstruct is a score nobody trusts.
|
|
2217
|
+
*/
|
|
2218
|
+
export interface PresenceCriterion {
|
|
2219
|
+
/** Stable across scans, so two reports of one domain can be diffed. */
|
|
2220
|
+
key: string;
|
|
2221
|
+
category: PresenceCategory;
|
|
2222
|
+
label: string;
|
|
2223
|
+
status: PresenceCriterionStatus;
|
|
2224
|
+
/** The check's full weight in the rubric. */
|
|
2225
|
+
weight: number;
|
|
2226
|
+
/** The share of `weight` actually earned. */
|
|
2227
|
+
earned: number;
|
|
2228
|
+
/** What was seen, in one line. */
|
|
2229
|
+
observed: string;
|
|
2230
|
+
/** The URL that settles it, when there is one. */
|
|
2231
|
+
citation: string | null;
|
|
2232
|
+
/** What to change. `null` when the check passed. */
|
|
2233
|
+
fix: string | null;
|
|
2234
|
+
}
|
|
2235
|
+
export interface PresenceCategoryScore {
|
|
2236
|
+
category: PresenceCategory;
|
|
2237
|
+
/** 0-100 within the category, so a weak one is legible beside a strong one. */
|
|
2238
|
+
score: number;
|
|
2239
|
+
weight: number;
|
|
2240
|
+
earned: number;
|
|
2241
|
+
}
|
|
2242
|
+
/**
|
|
2243
|
+
* A profile the subject's own site linked.
|
|
2244
|
+
*
|
|
2245
|
+
* **Claimed, never verified.** Nothing in a scan proves the account belongs to
|
|
2246
|
+
* that business - that needs a credential for each network. Render it as
|
|
2247
|
+
* "linked from their site", never as a confirmed account.
|
|
2248
|
+
*/
|
|
2249
|
+
export interface PresenceSocialProfile {
|
|
2250
|
+
network: PresenceNetwork;
|
|
2251
|
+
url: string;
|
|
2252
|
+
handle: string | null;
|
|
2253
|
+
/**
|
|
2254
|
+
* Whether the site declared it in `schema.org` `sameAs` markup rather than
|
|
2255
|
+
* only as a link.
|
|
2256
|
+
*
|
|
2257
|
+
* The one to read closely. `false` means a real presence with none of the
|
|
2258
|
+
* discoverability - a search engine has to infer the connection from a
|
|
2259
|
+
* footer icon - and it is among the most common findings a scan produces.
|
|
2260
|
+
*/
|
|
2261
|
+
machineReadable: boolean;
|
|
2262
|
+
}
|
|
2263
|
+
/** One prioritised thing to do. `fix` is Connect's own reviewed text; `why` is written for this business. */
|
|
2264
|
+
export interface PresenceRecommendation {
|
|
2265
|
+
/** Names the check this came from, so advice is always traceable to evidence. */
|
|
2266
|
+
criterionKey: string;
|
|
2267
|
+
category: PresenceCategory;
|
|
2268
|
+
/** 1 is what to do first. Always 1..n with no gaps. */
|
|
2269
|
+
priority: number;
|
|
2270
|
+
title: string;
|
|
2271
|
+
/** What it costs this business specifically. Empty when the narrative could not be generated. */
|
|
2272
|
+
why: string;
|
|
2273
|
+
fix: string;
|
|
2274
|
+
}
|
|
2275
|
+
/**
|
|
2276
|
+
* How visible a business is online.
|
|
2277
|
+
*
|
|
2278
|
+
* The important property, and the one that makes this worth showing a
|
|
2279
|
+
* customer: **the score was computed from the checks, and the checks were
|
|
2280
|
+
* measured.** Every point comes from something a fetcher observed on the site.
|
|
2281
|
+
* A model contributed only the ordering of `recommendations` and the prose in
|
|
2282
|
+
* `why` and `summary`; it cannot alter a status, a weight or the score.
|
|
2283
|
+
*/
|
|
2284
|
+
export interface PresenceScan {
|
|
2285
|
+
/** Normalised - lowercase, no scheme, no `www.`. */
|
|
2286
|
+
domain: string;
|
|
2287
|
+
/** 0-100. */
|
|
2288
|
+
score: number;
|
|
2289
|
+
band: PresenceBand;
|
|
2290
|
+
categories: PresenceCategoryScore[];
|
|
2291
|
+
/** Every check, passed and failed. The audit trail behind the number. */
|
|
2292
|
+
criteria: PresenceCriterion[];
|
|
2293
|
+
/** Every profile found, including networks the rubric does not weight individually. */
|
|
2294
|
+
social: PresenceSocialProfile[];
|
|
2295
|
+
/** Two or three sentences on the overall state. Empty when it could not be generated - the scan is complete without it. */
|
|
2296
|
+
summary: string;
|
|
2297
|
+
/** What to do, in order. Render this before `criteria`. */
|
|
2298
|
+
recommendations: PresenceRecommendation[];
|
|
2299
|
+
/** ISO-8601. Age the scan against this. */
|
|
2300
|
+
scannedAt: string;
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Why there is nothing to report. A normal 200, not an error.
|
|
2304
|
+
*
|
|
2305
|
+
* The distinction that matters for what you show and whether you retry:
|
|
2306
|
+
* `unreachable` is about the **site** - the home page did not answer - and may
|
|
2307
|
+
* succeed later. `provider_unavailable` is about **Connect** - our scanner is
|
|
2308
|
+
* down or unconfigured - and is worth retrying without telling the user
|
|
2309
|
+
* anything about their own site. `no_domain`, `consumer_domain` and
|
|
2310
|
+
* `blocked_domain` never succeed on retry.
|
|
2311
|
+
*
|
|
2312
|
+
* Note what is **not** here: a low score. Every check failing is a successful
|
|
2313
|
+
* scan and the most useful report this produces.
|
|
2314
|
+
*/
|
|
2315
|
+
export type PresenceSkipReason = "no_domain" | "consumer_domain" | "blocked_domain" | "unreachable" | "provider_unavailable";
|
|
2316
|
+
export type PresenceScanResult = {
|
|
2317
|
+
status: "scanned";
|
|
2318
|
+
scan: PresenceScan;
|
|
2319
|
+
/** Whether Connect served this from its platform-wide cache. The scan is the same either way. */
|
|
2320
|
+
cached: boolean;
|
|
2321
|
+
} | {
|
|
2322
|
+
status: "skipped";
|
|
2323
|
+
reason: PresenceSkipReason;
|
|
2324
|
+
};
|
|
1701
2325
|
/**
|
|
1702
2326
|
* What kind of conversation a channel carries. `voice` is the only kind
|
|
1703
2327
|
* Connect stocks inventory for today - `whatsapp` and `meeting` are modelled
|
|
@@ -1952,3 +2576,62 @@ export interface ConversationsPage {
|
|
|
1952
2576
|
nextCursor: string | null;
|
|
1953
2577
|
hasMore: boolean;
|
|
1954
2578
|
}
|
|
2579
|
+
/**
|
|
2580
|
+
* One notification, to members of one workspace.
|
|
2581
|
+
*
|
|
2582
|
+
* Hand-written here rather than imported from `@connect/data`, like every wire
|
|
2583
|
+
* type in this file: the published package carries none of Connect's
|
|
2584
|
+
* internals, and `conformance.test.ts` type-checks these against the server's
|
|
2585
|
+
* own DTOs so drift fails the build in Connect rather than in a partner's
|
|
2586
|
+
* integration.
|
|
2587
|
+
*/
|
|
2588
|
+
export interface SendPushInput {
|
|
2589
|
+
workspaceId: string;
|
|
2590
|
+
/**
|
|
2591
|
+
* Connect user ids. Ids naming somebody who is not an active member are
|
|
2592
|
+
* dropped rather than refused - a partner legitimately holds a stale id for
|
|
2593
|
+
* somebody who has left, and failing the whole call for one of them would
|
|
2594
|
+
* mean a reminder nobody gets.
|
|
2595
|
+
*/
|
|
2596
|
+
userIds: string[];
|
|
2597
|
+
title: string;
|
|
2598
|
+
body: string;
|
|
2599
|
+
/** Absolute, and on an origin this application registered as an SSO redirect URI. */
|
|
2600
|
+
url: string;
|
|
2601
|
+
}
|
|
2602
|
+
/**
|
|
2603
|
+
* What a fan-out actually did.
|
|
2604
|
+
*
|
|
2605
|
+
* Reported honestly rather than collapsed to a boolean, because the
|
|
2606
|
+
* interesting number is usually `silenced`: a partner seeing zero delivered
|
|
2607
|
+
* needs to know whether nobody has a device or everybody muted them, and those
|
|
2608
|
+
* call for opposite responses.
|
|
2609
|
+
*/
|
|
2610
|
+
export interface SendPushResponse {
|
|
2611
|
+
/** Members reached after preference filtering. */
|
|
2612
|
+
recipients: number;
|
|
2613
|
+
/** Members dropped because they silenced notifications from their apps. */
|
|
2614
|
+
silenced: number;
|
|
2615
|
+
/** Devices the notification actually reached, across every recipient. */
|
|
2616
|
+
delivered: number;
|
|
2617
|
+
failed: number;
|
|
2618
|
+
}
|
|
2619
|
+
export interface PushKeyResponse {
|
|
2620
|
+
/** `false` when the platform has no VAPID keys - say so rather than prompting. */
|
|
2621
|
+
configured: boolean;
|
|
2622
|
+
/** The `applicationServerKey` for `pushManager.subscribe()`. Empty when unconfigured. */
|
|
2623
|
+
publicKey: string;
|
|
2624
|
+
}
|
|
2625
|
+
/** Exactly what `PushSubscription.toJSON()` produces, plus the agent string for the device list. */
|
|
2626
|
+
export interface RegisterPushDeviceInput {
|
|
2627
|
+
endpoint: string;
|
|
2628
|
+
keys: {
|
|
2629
|
+
p256dh: string;
|
|
2630
|
+
auth: string;
|
|
2631
|
+
};
|
|
2632
|
+
userAgent: string;
|
|
2633
|
+
}
|
|
2634
|
+
export interface RegisterPushDeviceResponse {
|
|
2635
|
+
/** Hand this to `revokeDevice` to turn the same browser off again. */
|
|
2636
|
+
id: string;
|
|
2637
|
+
}
|