@cosmoops/connect-sdk 0.32.0 → 0.34.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +251 -0
  2. package/README.md +17 -5
  3. package/dist/cjs/client/components/access-gate.js +98 -2
  4. package/dist/cjs/client/components/visitor-chat.js +387 -0
  5. package/dist/cjs/client/components/visitor-stream.js +124 -0
  6. package/dist/cjs/client/index.js +16 -3
  7. package/dist/cjs/client/provider.js +29 -2
  8. package/dist/cjs/client/use-checkout.js +3 -1
  9. package/dist/cjs/helpers.js +46 -0
  10. package/dist/cjs/index.js +5 -1
  11. package/dist/cjs/next/index.js +10 -3
  12. package/dist/cjs/server/client.js +8 -1
  13. package/dist/cjs/server/handler.js +142 -0
  14. package/dist/cjs/server/resources/ai.js +50 -0
  15. package/dist/cjs/server/resources/billing.js +5 -0
  16. package/dist/cjs/server/resources/enrichment.js +59 -0
  17. package/dist/cjs/server/resources/entitlements.js +47 -0
  18. package/dist/cjs/server/resources/visitor.js +227 -0
  19. package/dist/esm/client/components/access-gate.js +99 -3
  20. package/dist/esm/client/components/visitor-chat.js +384 -0
  21. package/dist/esm/client/components/visitor-stream.js +120 -0
  22. package/dist/esm/client/index.js +11 -1
  23. package/dist/esm/client/provider.js +28 -2
  24. package/dist/esm/client/use-checkout.js +3 -1
  25. package/dist/esm/helpers.js +43 -0
  26. package/dist/esm/index.js +1 -0
  27. package/dist/esm/next/index.js +10 -3
  28. package/dist/esm/server/client.js +8 -1
  29. package/dist/esm/server/handler.js +142 -0
  30. package/dist/esm/server/resources/ai.js +50 -0
  31. package/dist/esm/server/resources/billing.js +5 -0
  32. package/dist/esm/server/resources/enrichment.js +59 -0
  33. package/dist/esm/server/resources/entitlements.js +47 -0
  34. package/dist/esm/server/resources/visitor.js +222 -0
  35. package/dist/types/client/components/access-gate.d.ts +9 -0
  36. package/dist/types/client/components/visitor-chat.d.ts +120 -0
  37. package/dist/types/client/components/visitor-stream.d.ts +42 -0
  38. package/dist/types/client/index.d.ts +6 -1
  39. package/dist/types/client/provider.d.ts +176 -51
  40. package/dist/types/client/use-checkout.d.ts +18 -1
  41. package/dist/types/helpers.d.ts +36 -1
  42. package/dist/types/index.d.ts +1 -0
  43. package/dist/types/server/client.d.ts +7 -1
  44. package/dist/types/server/handler.d.ts +0 -24
  45. package/dist/types/server/resources/ai.d.ts +48 -1
  46. package/dist/types/server/resources/billing.d.ts +4 -0
  47. package/dist/types/server/resources/enrichment.d.ts +57 -1
  48. package/dist/types/server/resources/entitlements.d.ts +40 -1
  49. package/dist/types/server/resources/visitor.d.ts +200 -0
  50. package/dist/types/types.d.ts +562 -0
  51. package/package.json +3 -2
@@ -1,5 +1,5 @@
1
1
  import { type TransportOptions } from "../http.js";
2
- import type { AiConversationDetail, AiConversationList, AssessmentAnswers, AssessmentDraft, AssessmentReview, ChatMessage, ConnectScreen } from "../../types.js";
2
+ import type { AiConversationDetail, AiConversationList, AssessmentAnswers, AssessmentDraft, AssessmentReview, ChatMessage, ConnectScreen, ReadinessAssessmentResult } from "../../types.js";
3
3
  /**
4
4
  * RAG chat and structured extraction against a workspace's data.
5
5
  *
@@ -114,4 +114,51 @@ export declare class AiResource {
114
114
  assessmentKey: string;
115
115
  answers: AssessmentAnswers;
116
116
  }): Promise<AssessmentReview>;
117
+ /**
118
+ * How ready the site at a domain is for AI agents.
119
+ *
120
+ * Probes for an MCP server, an OpenAPI description, `llms.txt`, structured
121
+ * data and a markdown twin; evaluates the site's `robots.txt` against the
122
+ * assistants that actually fetch pages; checks whether its content survives
123
+ * without JavaScript and whether a bot wall refuses agents outright. Then
124
+ * grades what it found and says what to fix first.
125
+ *
126
+ * Needs an API key scoped to **`ai:readiness`**, which is separate from every
127
+ * other AI scope because it is the widest outbound surface on the API - it
128
+ * POSTs to candidate MCP endpoints and fetches the home page under two user
129
+ * agents.
130
+ *
131
+ * ```ts
132
+ * const result = await connect.ai.readiness({ domain: "acme.com" });
133
+ * if (result.status === "assessed") {
134
+ * console.log(result.report.grade, result.report.topFix?.fix);
135
+ * }
136
+ * ```
137
+ *
138
+ * Four things to know before wiring it in:
139
+ *
140
+ * - **Set your client timeout to at least 90 seconds.** A cold assessment
141
+ * runs the whole probe before answering and can take up to a minute; many
142
+ * HTTP clients default to 30s and abandon a request that was about to
143
+ * succeed. Cached reads return in milliseconds and are the normal case -
144
+ * reports are held for a week.
145
+ * - **A week, not ninety days**, because a report is a to-do list somebody is
146
+ * working through. Pass `refresh` after shipping a fix to see it land.
147
+ * - **Handle `status: "error"` checks as unknown, not as failures.** They mean
148
+ * Connect could not measure that check, and they are excluded from the
149
+ * score rather than counted against it.
150
+ * - **`skipped` says whose problem it is.** `unreachable` means the site did
151
+ * not answer; `provider_unavailable` means Connect's own assessment service
152
+ * did. Only the second is worth retrying silently, and neither should be
153
+ * shown as a failing grade.
154
+ */
155
+ readiness(input: {
156
+ /** A domain or a URL in any form - `acme.com`, `www.acme.com`, `https://acme.com/pricing`. */
157
+ domain: string;
158
+ asUser?: string;
159
+ /** Re-assess even if Connect has a fresh report cached. For an operator who has just shipped a fix. */
160
+ refresh?: boolean;
161
+ }, options?: {
162
+ signal?: AbortSignal;
163
+ }): Promise<ReadinessAssessmentResult>;
117
164
  }
@@ -12,6 +12,10 @@ export declare class BillingResource {
12
12
  * independently of the instance it talks to. Without the coercion a partner
13
13
  * pointed at a Connect that has not upgraded yet renders a feature list of
14
14
  * blanks - which is exactly what happened. See `normalizePlanFeatures`.
15
+ *
16
+ * `limits` is defaulted for the same reason, one version later: an instance
17
+ * that predates limits sends no such field, and a pricing grid mapping over
18
+ * `plan.limits` would throw rather than simply show no limit rows.
15
19
  */
16
20
  listPlans(): Promise<PlansResponse>;
17
21
  /**
@@ -1,5 +1,5 @@
1
1
  import { type TransportOptions } from "../http.js";
2
- import type { BrandExtractionResult, CompanyEnrichmentResult } from "../../types.js";
2
+ import type { BrandExtractionResult, CompanyEnrichmentResult, PresenceScanResult } from "../../types.js";
3
3
  /**
4
4
  * What Connect knows about the company at a domain.
5
5
  *
@@ -86,4 +86,60 @@ export declare class EnrichmentResource {
86
86
  }, options?: {
87
87
  signal?: AbortSignal;
88
88
  }): Promise<BrandExtractionResult>;
89
+ /**
90
+ * How **visible** the business at a domain is online, scored out of 100.
91
+ *
92
+ * The third sibling of `company()` and `brand()`: that one answers what a
93
+ * business does, the other what it looks like, and this one answers how
94
+ * findable it is - by a search engine, by a customer, and by the assistants
95
+ * that increasingly answer questions about a business before anybody visits
96
+ * its site.
97
+ *
98
+ * Needs an API key scoped to **`enrichment:presence`** - separate from
99
+ * `enrichment:read` and `enrichment:brand` because it is the widest outbound
100
+ * read of the three: roughly twenty fetches across the site's well-known
101
+ * paths, and sometimes a headless browser.
102
+ *
103
+ * ```ts
104
+ * const result = await connect.enrichment.presence({ domain: "acme.com" });
105
+ * if (result.status === "scanned") {
106
+ * for (const next of result.scan.recommendations) {
107
+ * console.log(`${next.priority}. ${next.title} - ${next.fix}`);
108
+ * }
109
+ * }
110
+ * ```
111
+ *
112
+ * Four things to know before wiring it in:
113
+ *
114
+ * - **The score is computed, not generated.** Every point comes from a check
115
+ * run against something actually observed, and every check ships with its
116
+ * weight, what was seen and the URL that settles it - so the total is
117
+ * reconstructable. A model only orders `recommendations` and writes the
118
+ * prose; it cannot move the number. That is the whole reason this is worth
119
+ * showing a customer: ask a model how good a company's web presence is and
120
+ * it answers 72 with total confidence for a site it never read.
121
+ * - **`social` is claimed, never verified.** Those are URLs the site itself
122
+ * printed. Nothing proves the account belongs to that business - render it
123
+ * as "linked from their site". Check `machineReadable` too: `false` means
124
+ * a real profile that search engines have to infer from a footer link,
125
+ * which is a specific, common and easily fixed finding.
126
+ * - **A low score is a success, not a skip.** Every check failing is the most
127
+ * useful report this produces. Only `unreachable` means the site did not
128
+ * answer - and `provider_unavailable` means *Connect* did not, which is
129
+ * never something to tell your user about their own site.
130
+ * - **Set your client timeout to at least 90 seconds.** A cold scan runs the
131
+ * whole probe before answering; many HTTP clients default to 30s and
132
+ * abandon a request that was about to succeed. Cached scans return in
133
+ * milliseconds, and cached is the normal case - results are held for a
134
+ * fortnight, so you pay the cold path roughly once per domain.
135
+ */
136
+ presence(input: {
137
+ /** A domain or a URL in any form - `acme.com`, `www.acme.com`, `https://acme.com/pricing`. */
138
+ domain: string;
139
+ asUser?: string;
140
+ /** Re-scan even if Connect has a fresh result cached. For an operator who has just shipped a fix - not for automatic calls. */
141
+ refresh?: boolean;
142
+ }, options?: {
143
+ signal?: AbortSignal;
144
+ }): Promise<PresenceScanResult>;
89
145
  }
@@ -1,5 +1,5 @@
1
1
  import { type TransportOptions } from "../http.js";
2
- import type { ApplicationSubscriber, ReadinessResponse, SubscribersResponse, SubscriptionResponse } from "../../types.js";
2
+ import type { ApplicationSubscriber, PlanLimit, ReadinessResponse, SubscribersResponse, SubscriptionResponse } from "../../types.js";
3
3
  /**
4
4
  * The two checks worth making on every request: may this workspace be here at
5
5
  * all, and what is it paying for.
@@ -28,6 +28,45 @@ export declare class EntitlementsResource {
28
28
  * should not need a try/catch around it.
29
29
  */
30
30
  hasFeature(workspaceId: string, feature: string): Promise<boolean>;
31
+ /**
32
+ * How much of a metered resource this workspace may have - the numeric
33
+ * counterpart of `hasFeature`, for the resources your application counts
34
+ * itself (sites, projects, pipelines).
35
+ *
36
+ * **This is the effective ceiling, not the plan's own number.** Connect has
37
+ * already folded in whatever the add-ons on the subscription raise it by, so
38
+ * "Starter includes 1 site, they bought two 5-site packs" arrives here as
39
+ * `{ model: "limited", value: 11 }`. You never join the plan against the
40
+ * add-on list yourself, and a customer who buys another pack needs no change
41
+ * on your side.
42
+ *
43
+ * Returns `{ model: "none" }` rather than throwing when there is no active
44
+ * subscription, when the plan carries no answer for this key, and when the
45
+ * Connect instance predates limits entirely - the same reasoning
46
+ * `hasFeature` gives for answering `false`: "not entitled" is the same answer
47
+ * either way, and a limit check should not need a try/catch around it.
48
+ *
49
+ * Connect does not enforce this. It cannot see what you are counting, so it
50
+ * hands you the number and your code refuses the next row - `canAdd` is that
51
+ * comparison if you already have the count.
52
+ */
53
+ getLimit(workspaceId: string, key: string): Promise<PlanLimit>;
54
+ /**
55
+ * Whether this workspace may add `additional` more of a limited resource,
56
+ * given how many it already has.
57
+ *
58
+ * You supply the count, because only your application can produce it. This
59
+ * is `getLimit` plus `planLimitAllows`, which exists so the comparison is
60
+ * not rewritten per call site - see that helper for the three ways of
61
+ * getting it wrong.
62
+ *
63
+ * ```ts
64
+ * if (!(await connect.entitlements.canAdd(workspaceId, "sites", sites.length))) {
65
+ * return refuse("Your plan's site limit is reached.");
66
+ * }
67
+ * ```
68
+ */
69
+ canAdd(workspaceId: string, key: string, currentCount: number, additional?: number): Promise<boolean>;
31
70
  /**
32
71
  * One page of the workspaces subscribed to your application, newest first.
33
72
  *
@@ -0,0 +1,200 @@
1
+ import { type TransportOptions } from "../http.js";
2
+ import type { VisitorAppointment, VisitorKnowledgeItem, VisitorKnowledgeList, VisitorKnowledgeSource, VisitorSession } from "../../types.js";
3
+ /**
4
+ * What `book` takes.
5
+ *
6
+ * A named interface rather than an inline shape, so `conformance.test.ts` can
7
+ * pin it against the server's own request schema - the check that stops a
8
+ * required field being optional here, which is a 400 a partner only meets at
9
+ * runtime.
10
+ */
11
+ export interface VisitorBookInput {
12
+ sessionId: string;
13
+ workspaceId: string;
14
+ /** The same published slug the turn's slot lookup used. */
15
+ eventTypeSlug: string;
16
+ /** ISO-8601, or a `Date`. Must be one of the offered slots. */
17
+ startAt: string | Date;
18
+ name: string;
19
+ /** Required - it is how the host reaches them, and the second limiter key. */
20
+ phone: string;
21
+ email?: string | null;
22
+ notes?: string | null;
23
+ }
24
+ /** The per-call option every visitor method takes, so the limiter can see a visitor. */
25
+ export interface VisitorCallOptions {
26
+ /** The visitor's address, as your bridge saw it. See `VISITOR_IP_HEADER`. */
27
+ visitorIp?: string | null;
28
+ signal?: AbortSignal;
29
+ }
30
+ /**
31
+ * The public chat bubble on your own marketing site.
32
+ *
33
+ * The one resource here whose caller is **not** one of your users. There is no
34
+ * `asUser`, no `workspaceId` and no session anywhere in it: the counterpart is a
35
+ * stranger who has no account, and everything the assistant may say or do is
36
+ * configuration a platform admin set on your application record.
37
+ *
38
+ * That is why none of these methods takes an id naming anything. The workspace a
39
+ * call books into, the event type it books, the knowledge it answers from and the
40
+ * daily budget it spends are all read from your application - so nothing a
41
+ * visitor types, and nothing an injection attempt in your own chat window
42
+ * achieves, reaches a different tenant.
43
+ *
44
+ * **Never call these from a browser.** They take your API key, like every other
45
+ * resource on this client. `createConnectHandler` already exposes them on your
46
+ * own origin for `<ConnectVisitorChat>` to use, which is how the credential stays
47
+ * server-side.
48
+ */
49
+ export declare class VisitorResource {
50
+ private readonly transport;
51
+ /**
52
+ * The knowledge base behind the assistant - see `VisitorKnowledgeResource`.
53
+ *
54
+ * Nested rather than a top-level resource, because it is meaningless on its
55
+ * own: it exists to decide what *this* assistant may say.
56
+ */
57
+ readonly knowledge: VisitorKnowledgeResource;
58
+ constructor(transport: TransportOptions);
59
+ /**
60
+ * Open a conversation, or reopen one.
61
+ *
62
+ * Generates nothing - the greeting and the suggested questions come off the
63
+ * application record - so this is the one call in the flow that is always fast,
64
+ * which is what lets a bubble paint real copy instead of a spinner.
65
+ *
66
+ * Pass `resume` with a `sessionId` you kept and the transcript comes back with
67
+ * it. A `resume` that is expired, unknown or another application's silently
68
+ * starts a fresh conversation rather than failing: the token is the only thing
69
+ * being offered and it is being rejected, so there is nothing for the visitor
70
+ * to do about an error.
71
+ */
72
+ startSession(input: {
73
+ /** The customer whose website this is. Checked against your subscriptions. */
74
+ workspaceId: string;
75
+ /**
76
+ * Where the bubble was opened - a path or a page title.
77
+ *
78
+ * Worth passing: "how much is this?" asked from a pricing page and from a
79
+ * docs page are different questions, and this is the only signal of which.
80
+ * Treated as an untrusted hint and nothing else.
81
+ */
82
+ page?: string | null;
83
+ resume?: string | null;
84
+ }, options?: VisitorCallOptions): Promise<VisitorSession>;
85
+ /**
86
+ * One turn, streamed.
87
+ *
88
+ * Returns the raw `Response` rather than a parsed body, the same way
89
+ * `ai.chat` does: this answers server-sent events, and buffering them here
90
+ * would throw away the only reason to stream. The events are `delta`
91
+ * (`{text}`), `done` (`VisitorTurnDone`) and `error` (`{message}`).
92
+ *
93
+ * **The transcript is not a parameter**, which is the security property this
94
+ * endpoint is built around: the caller is ultimately a browser on a public
95
+ * page, and a client that can supply its own transcript can supply one in
96
+ * which anything has already been established. Only the conversation id and
97
+ * the new message travel.
98
+ */
99
+ chat(input: {
100
+ sessionId: string;
101
+ workspaceId: string;
102
+ message: string;
103
+ /** What the assistant calls itself, from your own widget settings. */
104
+ assistantName?: string;
105
+ /** A published event type slug in that workspace, for the booking exit. */
106
+ bookingEventTypeSlug?: string | null;
107
+ }, options?: VisitorCallOptions): Promise<Response>;
108
+ /**
109
+ * Book the call a visitor chose.
110
+ *
111
+ * `startAt` must be a time the turn's own slot lookup returned - an arbitrary
112
+ * instant is refused. That, plus a name and phone collected in real fields,
113
+ * is what makes it impossible to book an appointment at a time nobody offered
114
+ * or with a phone number a model parsed out of prose.
115
+ *
116
+ * A conversation books at most one appointment; a second call answers `409`.
117
+ */
118
+ book(input: VisitorBookInput, options?: VisitorCallOptions): Promise<VisitorAppointment>;
119
+ }
120
+ /**
121
+ * What `knowledge.save` takes.
122
+ *
123
+ * A named interface so `conformance.test.ts` can pin it against the server's own
124
+ * request schema - the check that stops a required field being optional here,
125
+ * which is a 400 a partner only meets at runtime.
126
+ */
127
+ export interface SaveVisitorKnowledgeInput {
128
+ workspaceId: string;
129
+ /** Omit to create. An id that does not exist is refused, never created under. */
130
+ itemId?: string | null;
131
+ title: string;
132
+ body: string;
133
+ source: VisitorKnowledgeSource;
134
+ /** The crawled URL or the uploaded file's name. */
135
+ sourceRef?: string | null;
136
+ }
137
+ /**
138
+ * A subscribing workspace's **public** knowledge base - what their website chat
139
+ * may tell strangers.
140
+ *
141
+ * ## It is deliberately not their Context Index
142
+ *
143
+ * `ai.chat` answers from a workspace's Drive-fed corpus, for a signed-in member
144
+ * of that workspace. This is a separate store that holds only what somebody
145
+ * explicitly published, and the separation is the reason a publishable widget
146
+ * credential is safe to publish: everything the widget can reach was already
147
+ * meant for the public.
148
+ *
149
+ * Do not try to fill it by uploading to Drive. Nothing reaches this store as a
150
+ * side effect of anything else - that is the invariant, not an inconvenience.
151
+ *
152
+ * ## Publishing is its own call
153
+ *
154
+ * `save` always writes a draft. `publish` is what makes text readable by
155
+ * strangers, so no edit can do it by accident, and `unpublish` can fail - which
156
+ * means the item is **still live**, not that the call was pointless.
157
+ */
158
+ export declare class VisitorKnowledgeResource {
159
+ private readonly transport;
160
+ constructor(transport: TransportOptions);
161
+ /** Everything, drafts included - the list a customer audits. */
162
+ list(workspaceId: string): Promise<VisitorKnowledgeList>;
163
+ /**
164
+ * Create or edit a draft.
165
+ *
166
+ * A body over the cap is **refused rather than truncated** - split a long page
167
+ * into sections, which is also what makes retrieval work.
168
+ */
169
+ save(input: SaveVisitorKnowledgeInput): Promise<VisitorKnowledgeItem>;
170
+ /**
171
+ * Make one item readable by strangers.
172
+ *
173
+ * May be slow: the publish that outgrows the prompt provisions a retrieval
174
+ * corpus and indexes everything published so far. An indexing failure still
175
+ * publishes and comes back with `indexState: "failed"` and `indexError` set,
176
+ * so a provider hiccup never makes your button look broken.
177
+ */
178
+ publish(input: {
179
+ workspaceId: string;
180
+ itemId: string;
181
+ }): Promise<VisitorKnowledgeItem>;
182
+ /**
183
+ * Take one item out of what strangers can read.
184
+ *
185
+ * **A failure means it is still live.** The retrievable copy is removed before
186
+ * the record is flipped, so a provider that refuses the delete aborts rather
187
+ * than leaving an item your screen shows as withdrawn that the widget goes on
188
+ * quoting. Surface the error and let them retry; do not grey the row out
189
+ * optimistically.
190
+ */
191
+ unpublish(input: {
192
+ workspaceId: string;
193
+ itemId: string;
194
+ }): Promise<VisitorKnowledgeItem>;
195
+ /** Remove it entirely. A published item is unpublished first. */
196
+ delete(input: {
197
+ workspaceId: string;
198
+ itemId: string;
199
+ }): Promise<void>;
200
+ }