@chatcops/server 0.5.0 → 0.7.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 (81) hide show
  1. package/README.md +4 -4
  2. package/dist/adapters/ai-sdk.d.ts +12 -0
  3. package/dist/adapters/ai-sdk.d.ts.map +1 -0
  4. package/dist/adapters/ai-sdk.js +27 -0
  5. package/dist/adapters/ai-sdk.js.map +1 -0
  6. package/dist/adapters/cloudflare.d.ts.map +1 -1
  7. package/dist/adapters/cloudflare.js +9 -83
  8. package/dist/adapters/cloudflare.js.map +1 -1
  9. package/dist/adapters/express.d.ts +6 -2
  10. package/dist/adapters/express.d.ts.map +1 -1
  11. package/dist/adapters/express.js +102 -30
  12. package/dist/adapters/express.js.map +1 -1
  13. package/dist/adapters/index.d.ts +1 -0
  14. package/dist/adapters/index.d.ts.map +1 -1
  15. package/dist/adapters/index.js +1 -0
  16. package/dist/adapters/index.js.map +1 -1
  17. package/dist/adapters/vercel.d.ts.map +1 -1
  18. package/dist/adapters/vercel.js +7 -74
  19. package/dist/adapters/vercel.js.map +1 -1
  20. package/dist/client-ip.d.ts +37 -0
  21. package/dist/client-ip.d.ts.map +1 -0
  22. package/dist/client-ip.js +57 -0
  23. package/dist/client-ip.js.map +1 -0
  24. package/dist/config.d.ts +136 -8
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +15 -1
  27. package/dist/config.js.map +1 -1
  28. package/dist/examples/cloudflare-worker.js +1 -1
  29. package/dist/examples/cloudflare-worker.js.map +1 -1
  30. package/dist/handler.d.ts +52 -1
  31. package/dist/handler.d.ts.map +1 -1
  32. package/dist/handler.js +417 -136
  33. package/dist/handler.js.map +1 -1
  34. package/dist/http.d.ts +69 -0
  35. package/dist/http.d.ts.map +1 -0
  36. package/dist/http.js +262 -0
  37. package/dist/http.js.map +1 -0
  38. package/dist/index.d.ts +4 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/knowledge.d.ts +18 -0
  43. package/dist/knowledge.d.ts.map +1 -0
  44. package/dist/knowledge.js +32 -0
  45. package/dist/knowledge.js.map +1 -0
  46. package/dist/rate-limit.d.ts +5 -0
  47. package/dist/rate-limit.d.ts.map +1 -1
  48. package/dist/rate-limit.js +5 -0
  49. package/dist/rate-limit.js.map +1 -1
  50. package/dist/security/origin-policy.d.ts +19 -0
  51. package/dist/security/origin-policy.d.ts.map +1 -0
  52. package/dist/security/origin-policy.js +60 -0
  53. package/dist/security/origin-policy.js.map +1 -0
  54. package/dist/security/origin.d.ts +5 -0
  55. package/dist/security/origin.d.ts.map +1 -0
  56. package/dist/security/origin.js +47 -0
  57. package/dist/security/origin.js.map +1 -0
  58. package/dist/security/session-token.d.ts +65 -0
  59. package/dist/security/session-token.d.ts.map +1 -0
  60. package/dist/security/session-token.js +338 -0
  61. package/dist/security/session-token.js.map +1 -0
  62. package/dist/security/sessions.d.ts +72 -0
  63. package/dist/security/sessions.d.ts.map +1 -0
  64. package/dist/security/sessions.js +216 -0
  65. package/dist/security/sessions.js.map +1 -0
  66. package/dist/security/startup-checks.d.ts +45 -0
  67. package/dist/security/startup-checks.d.ts.map +1 -0
  68. package/dist/security/startup-checks.js +124 -0
  69. package/dist/security/startup-checks.js.map +1 -0
  70. package/dist/security/turnstile.d.ts +24 -0
  71. package/dist/security/turnstile.d.ts.map +1 -0
  72. package/dist/security/turnstile.js +93 -0
  73. package/dist/security/turnstile.js.map +1 -0
  74. package/dist/visitor-context.d.ts.map +1 -1
  75. package/dist/visitor-context.js +7 -0
  76. package/dist/visitor-context.js.map +1 -1
  77. package/dist/webhook.d.ts +3 -2
  78. package/dist/webhook.d.ts.map +1 -1
  79. package/dist/webhook.js +5 -2
  80. package/dist/webhook.js.map +1 -1
  81. package/package.json +22 -5
@@ -0,0 +1,57 @@
1
+ import { consoleLogger } from '@chatcops/core';
2
+ const DOCS = 'https://chat.codercops.com/server/fetch-runtimes/#the-visitors-ip';
3
+ // IPv4, IPv6 (including IPv4-mapped) and nothing else, so a header can't
4
+ // carry an arbitrary counter key.
5
+ const IP_PATTERN = /^[0-9A-Fa-f:.]{2,45}$/;
6
+ export function cleanIp(value) {
7
+ const ip = value?.split(',')[0]?.trim();
8
+ return ip && IP_PATTERN.test(ip) ? ip : undefined;
9
+ }
10
+ /** Vercel overwrites these headers, so a visitor can't set them there. */
11
+ export function onVercel() {
12
+ return typeof process !== 'undefined' && Boolean(process.env?.VERCEL);
13
+ }
14
+ const warned = new Set();
15
+ export function warnOnce(key, message, logger = consoleLogger()) {
16
+ if (warned.has(key))
17
+ return;
18
+ warned.add(key);
19
+ logger.warn('client_ip', message, { key });
20
+ }
21
+ /** For tests. */
22
+ export function resetClientIpWarnings() {
23
+ warned.clear();
24
+ }
25
+ /** The fetch handler for Vercel and other Fetch API runtimes. */
26
+ export function fetchClientIp(request, resolver, logger) {
27
+ if (resolver)
28
+ return cleanIp(resolver(request));
29
+ if (onVercel()) {
30
+ const headers = request.headers;
31
+ return cleanIp(headers.get('x-vercel-forwarded-for')) ?? cleanIp(headers.get('x-real-ip')) ?? cleanIp(headers.get('x-forwarded-for'));
32
+ }
33
+ warnOnce('fetch', `Not running on Vercel, so the visitor's IP isn't read from X-Forwarded-For, which a visitor can set themselves. Per-IP rate limits and budgets are off until you set \`clientIp\`. See ${DOCS}`, logger);
34
+ return undefined;
35
+ }
36
+ /** Cloudflare sets CF-Connecting-IP on every request that reaches a Worker. */
37
+ export function cloudflareClientIp(request, resolver, logger) {
38
+ if (resolver)
39
+ return cleanIp(resolver(request));
40
+ const ip = cleanIp(request.headers.get('cf-connecting-ip'));
41
+ if (!ip) {
42
+ warnOnce('cloudflare', 'A request had no CF-Connecting-IP header, so per-IP rate limits and budgets were skipped for it.', logger);
43
+ }
44
+ return ip;
45
+ }
46
+ /**
47
+ * Express's `req.ip` follows its `trust proxy` setting. Behind a proxy with
48
+ * that setting off, every visitor gets the proxy's address and shares one
49
+ * rate limit and budget, so that case gets a warning.
50
+ */
51
+ export function expressClientIp(req, logger) {
52
+ if (req.headers?.['x-forwarded-for'] !== undefined && !req.app?.get?.('trust proxy')) {
53
+ warnOnce('express', "A request had X-Forwarded-For, but Express's `trust proxy` setting is off, so the connection's IP is used. Behind a proxy that means every visitor gets the proxy's IP and shares one rate limit and budget. With one proxy in front, call app.set('trust proxy', 1).", logger);
54
+ }
55
+ return cleanIp(req.ip ?? req.socket?.remoteAddress);
56
+ }
57
+ //# sourceMappingURL=client-ip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-ip.js","sourceRoot":"","sources":["../src/client-ip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAe,MAAM,gBAAgB,CAAC;AAW5D,MAAM,IAAI,GAAG,mEAAmE,CAAC;AAEjF,yEAAyE;AACzE,kCAAkC;AAClC,MAAM,UAAU,GAAG,uBAAuB,CAAC;AAE3C,MAAM,UAAU,OAAO,CAAC,KAAgC;IACtD,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IACxC,OAAO,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACpD,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,QAAQ;IACtB,OAAO,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;AAEjC,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,OAAe,EAAE,SAAiB,aAAa,EAAE;IACrF,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO;IAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,iBAAiB;AACjB,MAAM,UAAU,qBAAqB;IACnC,MAAM,CAAC,KAAK,EAAE,CAAC;AACjB,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,QAAsC,EAAE,MAAe;IACrG,IAAI,QAAQ;QAAE,OAAO,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,IAAI,QAAQ,EAAE,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACxI,CAAC;IACD,QAAQ,CACN,OAAO,EACP,0LAA0L,IAAI,EAAE,EAChM,MAAM,CACP,CAAC;IACF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,OAAgB,EAAE,QAAsC,EAAE,MAAe;IAC1G,IAAI,QAAQ;QAAE,OAAO,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC5D,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,QAAQ,CAAC,YAAY,EAAE,kGAAkG,EAAE,MAAM,CAAC,CAAC;IACrI,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AASD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAuB,EAAE,MAAe;IACtE,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC,iBAAiB,CAAC,KAAK,SAAS,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;QACrF,QAAQ,CACN,SAAS,EACT,uQAAuQ,EACvQ,MAAM,CACP,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AACtD,CAAC"}
package/dist/config.d.ts CHANGED
@@ -1,11 +1,23 @@
1
1
  import { z } from 'zod';
2
- import type { ChatTool, KnowledgeSource, WebhookConfig, ProviderConfig, LocaleStrings, ConversationStore } from '@chatcops/core';
2
+ import type { ClientIpResolver } from './client-ip.js';
3
+ import type { TurnstileConfig } from './security/turnstile.js';
4
+ import type { AIProvider, Budgets, ChatCopsStorage, ChatTool, ConversationStore, KnowledgeSource, LocaleStrings, Logger, ProviderConfig, WebhookConfig } from '@chatcops/core';
3
5
  export interface ChatCopsServerConfig {
4
- provider: ProviderConfig;
6
+ /**
7
+ * Which model to use: a config like `{ type: 'claude', apiKey }`, or a
8
+ * provider object you built yourself (anything with `step()` or `chat()`).
9
+ */
10
+ provider: ProviderConfig | AIProvider;
5
11
  systemPrompt: string;
12
+ /**
13
+ * Where the server writes startup notes, refused requests and every error
14
+ * it handles instead of throwing, each with a stable `event` name. Default
15
+ * `consoleLogger()`; `jsonLogger()` writes one JSON object per line.
16
+ */
17
+ logger?: Logger;
6
18
  tools?: ChatTool[];
7
19
  toolTimeoutMs?: number;
8
- /** Most tokens in one reply. Defaults to 1024. */
20
+ /** Most tokens in one reply. Defaults to 4096 (`DEFAULT_MAX_TOKENS` in @chatcops/core). */
9
21
  maxTokens?: number;
10
22
  /** Sampling temperature. Defaults to the provider's own; the allowed range depends on the provider. */
11
23
  temperature?: number;
@@ -14,12 +26,105 @@ export interface ChatCopsServerConfig {
14
26
  maxRequests: number;
15
27
  windowMs: number;
16
28
  };
29
+ /**
30
+ * Token, reply and dollar limits per conversation, per IP per day and
31
+ * overall per day, counted in `storage` so they hold across instances. A
32
+ * reply that would go over ends with an error instead of calling the model.
33
+ * On by default (`DEFAULT_BUDGETS`: 200 replies per conversation, 2M tokens
34
+ * per IP per day). A scope you set replaces its default, `false` turns a
35
+ * scope off, and `budgets: false` turns them all off.
36
+ */
37
+ budgets?: Budgets | false;
38
+ /**
39
+ * The pre-chat form fields to keep (the `name`s in the widget's
40
+ * `preChatForm.fields`). Other fields a request sends are dropped. Unset
41
+ * keeps every field. Answers are stored with the conversation from its
42
+ * first message and can't change after that.
43
+ */
44
+ preChatFields?: string[];
17
45
  webhooks?: WebhookConfig[];
18
46
  analytics?: boolean;
19
- /** Storage backend for conversation history. Defaults to an in-memory store (`MemoryStore`). */
47
+ /** Bearer token required to read the analytics endpoint. When unset, the endpoint is public. */
48
+ analyticsToken?: string;
49
+ /**
50
+ * Where conversations (and, with stores that have them, counters, turn
51
+ * locks and events) live: `postgresStorage(...)`, `redisStorage(...)` and
52
+ * so on. Defaults to memory, which doesn't survive a restart and isn't
53
+ * shared between serverless instances.
54
+ */
55
+ storage?: ChatCopsStorage;
56
+ /**
57
+ * @deprecated Use `storage`. A conversation store on its own, as in 0.4.
58
+ * Setting both is an error.
59
+ */
20
60
  conversationStore?: ConversationStore;
21
- /** Allowed CORS origin. Defaults to `'*'`. */
61
+ /**
62
+ * How long to keep conversations. `purge()` on the handler deletes older
63
+ * ones; call it on a schedule (a cron job, a Workers cron trigger).
64
+ */
65
+ retention?: {
66
+ conversationsDays?: number;
67
+ };
68
+ /**
69
+ * The sites that can use the chat: exact origins (`'https://acme.com'`) or
70
+ * subdomain wildcards (`'https://*.acme.com'`). A browser request from any
71
+ * other page gets 403 `origin_not_allowed` before the model is called.
72
+ * Requests without an `Origin` header (curl, your own server) pass: this
73
+ * stops other sites embedding your bot, not scripts. Defaults to `['*']`.
74
+ */
75
+ allowedOrigins?: string[];
76
+ /** @deprecated Use `allowedOrigins`. Read as a one-item `allowedOrigins`. */
22
77
  cors?: string;
78
+ /**
79
+ * Reads the visitor's IP for per-IP rate limits and budgets, in the fetch
80
+ * handlers (`chatcopsVercelHandler`, `chatcopsCloudflareHandler`). Needed
81
+ * where the handler can't tell on its own: any host other than Vercel or
82
+ * Cloudflare. Read only a header your platform or proxy sets, never one a
83
+ * visitor can send. Return `undefined` to skip per-IP limits for a request.
84
+ * Express uses `req.ip`, which follows its `trust proxy` setting.
85
+ */
86
+ clientIp?: ClientIpResolver;
87
+ /**
88
+ * Turns on session tokens. `POST <chat url>/session` gives the widget a
89
+ * token bound to its origin and one conversation, and chat requests send
90
+ * it as `Authorization: Bearer <token>`, so a guessed conversation id is
91
+ * useless. Sign with the first secret and verify with all of them, so a
92
+ * new secret goes first and the old one stays until its tokens expire.
93
+ * Each needs at least 32 bytes (`openssl rand -base64 32`). Keep them
94
+ * server side, never in a client bundle.
95
+ */
96
+ sessionSecrets?: string[];
97
+ /** How long a session token lasts, in seconds. Defaults to 900. */
98
+ sessionTtlSec?: number;
99
+ /**
100
+ * Refuse chat requests without a session token (401 `session_required`).
101
+ * Defaults to true when `sessionSecrets` is set. Set false while widgets
102
+ * older than 0.6, which don't send tokens, are still out there.
103
+ */
104
+ requireSession?: boolean;
105
+ /**
106
+ * Cloudflare Turnstile when a session token is issued: `/session` needs a
107
+ * Turnstile token and checks it with Cloudflare before signing. Refreshing
108
+ * a session with its previous token skips it. Needs `sessionSecrets`, and
109
+ * the widget's `turnstileSiteKey`.
110
+ */
111
+ turnstile?: TurnstileConfig;
112
+ /**
113
+ * Signed-in visitors. Your site signs a \`userToken\` (an HS256 JWT with
114
+ * \`sub\`, \`exp\` and an optional \`tier\`) with \`secret\`, and the widget
115
+ * sends it with \`/session\`. The user id is stored on the conversation, so
116
+ * the same user can resume it with a fresh token, and \`tiers\` gives a
117
+ * tier its own budgets (merged over \`budgets\`). Needs \`sessionSecrets\`.
118
+ */
119
+ identity?: {
120
+ secret: string;
121
+ tiers?: Record<string, Budgets>;
122
+ };
123
+ /**
124
+ * Adds the reply's token usage to the `finish` part of protocol v1
125
+ * streams. Off by default, since visitors can read the stream.
126
+ */
127
+ exposeUsage?: boolean;
23
128
  /** @deprecated The server ignores this. Set the widget's text with its `strings` option. */
24
129
  i18n?: {
25
130
  defaultLocale: string;
@@ -27,6 +132,10 @@ export interface ChatCopsServerConfig {
27
132
  };
28
133
  }
29
134
  export declare const MAX_USER_DATA_FIELDS = 50;
135
+ /** `pageContext.data`, what a site adds with the widget's setContext(). */
136
+ export declare const MAX_PAGE_DATA_FIELDS = 20;
137
+ export declare const MAX_PAGE_DATA_KEY_LENGTH = 40;
138
+ export declare const MAX_PAGE_DATA_VALUE_LENGTH = 500;
30
139
  export declare const MAX_USER_DATA_KEY_LENGTH = 100;
31
140
  /** Longer page URLs are still accepted, but only this much reaches the prompt. */
32
141
  export declare const MAX_PAGE_URL_LENGTH = 2000;
@@ -34,22 +143,35 @@ export declare const chatRequestSchema: z.ZodObject<{
34
143
  conversationId: z.ZodString;
35
144
  message: z.ZodString;
36
145
  messageId: z.ZodOptional<z.ZodString>;
37
- regenerate: z.ZodOptional<z.ZodBoolean>;
146
+ /**
147
+ * `{ assistantMessageId }` replaces that reply. `true` (the 0.5 form)
148
+ * replaces the reply to `messageId`, or the latest one.
149
+ */
150
+ regenerate: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
151
+ assistantMessageId: z.ZodString;
152
+ }, "strip", z.ZodTypeAny, {
153
+ assistantMessageId: string;
154
+ }, {
155
+ assistantMessageId: string;
156
+ }>]>>;
38
157
  pageContext: z.ZodOptional<z.ZodObject<{
39
158
  url: z.ZodString;
40
159
  title: z.ZodString;
41
160
  description: z.ZodOptional<z.ZodString>;
42
161
  contentSnippet: z.ZodOptional<z.ZodString>;
162
+ data: z.ZodOptional<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodString>, Record<string, string>, Record<string, string>>>;
43
163
  }, "strip", z.ZodTypeAny, {
44
164
  url: string;
45
165
  title: string;
46
166
  description?: string | undefined;
47
167
  contentSnippet?: string | undefined;
168
+ data?: Record<string, string> | undefined;
48
169
  }, {
49
170
  url: string;
50
171
  title: string;
51
172
  description?: string | undefined;
52
173
  contentSnippet?: string | undefined;
174
+ data?: Record<string, string> | undefined;
53
175
  }>>;
54
176
  locale: z.ZodOptional<z.ZodString>;
55
177
  userData: z.ZodOptional<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodString>, Record<string, string>, Record<string, string>>>;
@@ -57,12 +179,15 @@ export declare const chatRequestSchema: z.ZodObject<{
57
179
  conversationId: string;
58
180
  message: string;
59
181
  messageId?: string | undefined;
60
- regenerate?: boolean | undefined;
182
+ regenerate?: boolean | {
183
+ assistantMessageId: string;
184
+ } | undefined;
61
185
  pageContext?: {
62
186
  url: string;
63
187
  title: string;
64
188
  description?: string | undefined;
65
189
  contentSnippet?: string | undefined;
190
+ data?: Record<string, string> | undefined;
66
191
  } | undefined;
67
192
  locale?: string | undefined;
68
193
  userData?: Record<string, string> | undefined;
@@ -70,12 +195,15 @@ export declare const chatRequestSchema: z.ZodObject<{
70
195
  conversationId: string;
71
196
  message: string;
72
197
  messageId?: string | undefined;
73
- regenerate?: boolean | undefined;
198
+ regenerate?: boolean | {
199
+ assistantMessageId: string;
200
+ } | undefined;
74
201
  pageContext?: {
75
202
  url: string;
76
203
  title: string;
77
204
  description?: string | undefined;
78
205
  contentSnippet?: string | undefined;
206
+ data?: Record<string, string> | undefined;
79
207
  } | undefined;
80
208
  locale?: string | undefined;
81
209
  userData?: Record<string, string> | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEjI,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uGAAuG;IACvG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4FAA4F;IAC5F,IAAI,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;KAAE,CAAC;CAC1E;AAGD,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkB5B,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  // Everything in a request can end up in the system prompt, so every field has a ceiling.
3
3
  export const MAX_USER_DATA_FIELDS = 50;
4
+ /** `pageContext.data`, what a site adds with the widget's setContext(). */
5
+ export const MAX_PAGE_DATA_FIELDS = 20;
6
+ export const MAX_PAGE_DATA_KEY_LENGTH = 40;
7
+ export const MAX_PAGE_DATA_VALUE_LENGTH = 500;
4
8
  export const MAX_USER_DATA_KEY_LENGTH = 100;
5
9
  /** Longer page URLs are still accepted, but only this much reaches the prompt. */
6
10
  export const MAX_PAGE_URL_LENGTH = 2000;
@@ -8,12 +12,22 @@ export const chatRequestSchema = z.object({
8
12
  conversationId: z.string().min(1).max(128),
9
13
  message: z.string().min(1).max(10000),
10
14
  messageId: z.string().min(1).max(128).optional(),
11
- regenerate: z.boolean().optional(),
15
+ /**
16
+ * `{ assistantMessageId }` replaces that reply. `true` (the 0.5 form)
17
+ * replaces the reply to `messageId`, or the latest one.
18
+ */
19
+ regenerate: z.union([z.boolean(), z.object({ assistantMessageId: z.string().min(1).max(128) })]).optional(),
12
20
  pageContext: z.object({
13
21
  url: z.string().url(),
14
22
  title: z.string().max(500),
15
23
  description: z.string().max(1000).optional(),
16
24
  contentSnippet: z.string().max(2000).optional(),
25
+ data: z
26
+ .record(z.string().max(MAX_PAGE_DATA_KEY_LENGTH), z.string().max(MAX_PAGE_DATA_VALUE_LENGTH))
27
+ .refine((data) => Object.keys(data).length <= MAX_PAGE_DATA_FIELDS, {
28
+ message: `pageContext.data can have at most ${MAX_PAGE_DATA_FIELDS} fields`,
29
+ })
30
+ .optional(),
17
31
  }).optional(),
18
32
  locale: z.string().max(10).optional(),
19
33
  userData: z
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAwBxB,yFAAyF;AACzF,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AACvC,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAC5C,kFAAkF;AAClF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IAC1C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAChD,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAClC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;QACrB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;QAC1B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;QAC5C,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;KAChD,CAAC,CAAC,QAAQ,EAAE;IACb,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACrC,QAAQ,EAAE,CAAC;SACR,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SACrE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,oBAAoB,EAAE;QAClE,OAAO,EAAE,6BAA6B,oBAAoB,SAAS;KACpE,CAAC;SACD,QAAQ,EAAE;CACd,CAAC,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuIxB,yFAAyF;AACzF,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AACvC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AACvC,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAC3C,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAC9C,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAC5C,kFAAkF;AAClF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IAC1C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAChD;;;OAGG;IACH,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC3G,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;QACrB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;QAC1B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;QAC5C,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;QAC/C,IAAI,EAAE,CAAC;aACJ,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;aAC5F,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,oBAAoB,EAAE;YAClE,OAAO,EAAE,qCAAqC,oBAAoB,SAAS;SAC5E,CAAC;aACD,QAAQ,EAAE;KACd,CAAC,CAAC,QAAQ,EAAE;IACb,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACrC,QAAQ,EAAE,CAAC;SACR,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SACrE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,oBAAoB,EAAE;QAClE,OAAO,EAAE,6BAA6B,oBAAoB,SAAS;KACpE,CAAC;SACD,QAAQ,EAAE;CACd,CAAC,CAAC"}
@@ -15,7 +15,7 @@ function getHandler(env) {
15
15
  apiKey: env.ANTHROPIC_API_KEY,
16
16
  },
17
17
  systemPrompt: 'You are a helpful assistant.',
18
- cors: '*',
18
+ allowedOrigins: ['*'],
19
19
  });
20
20
  return handler;
21
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare-worker.js","sourceRoot":"","sources":["../../src/examples/cloudflare-worker.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,6EAA6E;AAC7E,+EAA+E;AAC/E,2EAA2E;AAC3E,IAAI,OAAiE,CAAC;AACtE,SAAS,UAAU,CAAC,GAA2B;IAC7C,OAAO,KAAK,yBAAyB,CAAC;QACpC,QAAQ,EAAE;YACR,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,GAAG,CAAC,iBAAiB;SAC9B;QACD,YAAY,EAAE,8BAA8B;QAC5C,IAAI,EAAE,GAAG;KACV,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,eAAe;IACb,KAAK,CAAC,KAAK,CAAC,OAAgB,EAAE,GAA2B;QACvD,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"cloudflare-worker.js","sourceRoot":"","sources":["../../src/examples/cloudflare-worker.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,6EAA6E;AAC7E,+EAA+E;AAC/E,2EAA2E;AAC3E,IAAI,OAAiE,CAAC;AACtE,SAAS,UAAU,CAAC,GAA2B;IAC7C,OAAO,KAAK,yBAAyB,CAAC;QACpC,QAAQ,EAAE;YACR,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,GAAG,CAAC,iBAAiB;SAC9B;QACD,YAAY,EAAE,8BAA8B;QAC5C,cAAc,EAAE,CAAC,GAAG,CAAC;KACtB,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,eAAe;IACb,KAAK,CAAC,KAAK,CAAC,OAAgB,EAAE,GAA2B;QACvD,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;CACF,CAAC"}
package/dist/handler.d.ts CHANGED
@@ -1,9 +1,60 @@
1
+ import { type AnalyticsStats, type BudgetScope, type Budgets, type LegacyChunk, type WireErrorBody, type WirePart } from '@chatcops/core';
1
2
  import type { ChatCopsServerConfig } from './config.js';
2
3
  export declare function createChatHandler(config: ChatCopsServerConfig): {
3
4
  handleChat: (body: unknown, clientIp?: string, options?: {
4
5
  signal?: AbortSignal;
5
6
  }) => AsyncGenerator<string>;
6
- getAnalytics: () => import("@chatcops/core").AnalyticsStats | null;
7
+ startChat: (body: unknown, clientIp?: string, options?: StartChatOptions) => Promise<ChatStart>;
8
+ purge: (now?: number) => Promise<number | null>;
9
+ /** How many requests asked for each stream format, to tell when 0.5 widgets are gone. */
10
+ /** Answers `POST /session`, or `undefined` when session tokens are off. */
11
+ startSession: ((body: unknown, origin: string | null, clientIp?: string) => Promise<import("./security/sessions.js").SessionReply>) | undefined;
12
+ protocolStats: () => {
13
+ v1: number;
14
+ legacy: number;
15
+ "ai-sdk": number;
16
+ };
17
+ /** Stats from this process only. `analyticsStats()` reads the storage's event sink. */
18
+ getAnalytics: () => AnalyticsStats | null;
19
+ analyticsStats: (range?: {
20
+ from?: number;
21
+ to?: number;
22
+ }) => Promise<AnalyticsStats | null>;
7
23
  cleanup: () => void;
24
+ /** The logger the handler writes to, for adapters to use as well. */
25
+ logger: import("@chatcops/core").Logger;
8
26
  };
27
+ /** The stream a caller writes: protocol v1, the 0.5 chunks, or the AI SDK's UI message stream. */
28
+ export type ChatFormat = 'v1' | 'legacy' | 'ai-sdk';
29
+ export interface StartChatOptions {
30
+ signal?: AbortSignal;
31
+ /** Which stream the caller writes, for `protocolStats()`. Defaults to `'v1'`. */
32
+ format?: ChatFormat;
33
+ /** The request's `Authorization` header, for session tokens. */
34
+ authorization?: string | null;
35
+ /** The request's `Origin` header, which a session token is bound to. */
36
+ origin?: string | null;
37
+ }
38
+ /** A request the handler won't answer, or the reply's parts. */
39
+ export type ChatStart = {
40
+ ok: true;
41
+ parts: AsyncGenerator<WirePart, void>;
42
+ } | {
43
+ ok: false;
44
+ /** The HTTP status protocol v1 answers with. */
45
+ status: number;
46
+ error: WireErrorBody['error'];
47
+ /** The chunk a 0.5 widget gets instead, inside a 200 stream. */
48
+ legacy: LegacyChunk & {
49
+ details?: unknown;
50
+ };
51
+ };
52
+ export type ChatHandler = ReturnType<typeof createChatHandler>;
53
+ /**
54
+ * Protocol v1 parts as 0.5 chunks (JSON strings). An error goes out last,
55
+ * after the turn has ended and lead webhooks have finished, as in 0.5.
56
+ */
57
+ export declare function toLegacyStream(parts: AsyncIterable<WirePart>): AsyncGenerator<string>;
58
+ /** The startup line, so the limits in force are never a surprise. */
59
+ export declare function describeBudgets(budgets: Budgets, defaulted?: BudgetScope[]): string;
9
60
  //# sourceMappingURL=handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,oBAAoB,EAAwB,MAAM,aAAa,CAAC;AAM9E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB;uBAsBpD,OAAO,aACF,MAAM,YACR;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAChC,cAAc,CAAC,MAAM,CAAC;;;EAiN1B"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,cAAc,EAgBnB,KAAK,WAAW,EAChB,KAAK,OAAO,EAIZ,KAAK,WAAW,EAChB,KAAK,aAAa,EAElB,KAAK,QAAQ,EACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,oBAAoB,EAAwB,MAAM,aAAa,CAAC;AAS9E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB;uBAqMpD,OAAO,aACF,MAAM,YACR;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAChC,cAAc,CAAC,MAAM,CAAC;sBAtGM,OAAO,aAAa,MAAM,YAAW,gBAAgB,KAAQ,OAAO,CAAC,SAAS,CAAC;6BAiTtE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAc5D,yFAAyF;IACzF,2EAA2E;0BAEhE,OAAO,UAAU,MAAM,GAAG,IAAI,aAAa,MAAM;;;;;;IAG5D,uFAAuF;;6BAhPnD;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;;IAoPnG,qEAAqE;;EAGxE;AAED,kGAAkG;AAClG,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAQpD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,iFAAiF;IACjF,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,gEAAgE;AAChE,MAAM,MAAM,SAAS,GACjB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;CAAE,GACnD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9B,gEAAgE;IAChE,MAAM,EAAE,WAAW,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7C,CAAC;AAEN,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAU/D;;;GAGG;AACH,wBAAuB,cAAc,CAAC,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAc5F;AA+CD,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,GAAE,WAAW,EAAO,GAAG,MAAM,CAkBvF"}