@chatcops/server 0.5.0 → 0.6.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 (71) 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 +101 -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 +36 -0
  21. package/dist/client-ip.d.ts.map +1 -0
  22. package/dist/client-ip.js +62 -0
  23. package/dist/client-ip.js.map +1 -0
  24. package/dist/config.d.ts +110 -8
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +5 -1
  27. package/dist/config.js.map +1 -1
  28. package/dist/handler.d.ts +50 -1
  29. package/dist/handler.d.ts.map +1 -1
  30. package/dist/handler.js +384 -130
  31. package/dist/handler.js.map +1 -1
  32. package/dist/http.d.ts +66 -0
  33. package/dist/http.d.ts.map +1 -0
  34. package/dist/http.js +262 -0
  35. package/dist/http.js.map +1 -0
  36. package/dist/index.d.ts +3 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/rate-limit.d.ts +5 -0
  41. package/dist/rate-limit.d.ts.map +1 -1
  42. package/dist/rate-limit.js +5 -0
  43. package/dist/rate-limit.js.map +1 -1
  44. package/dist/security/origin-policy.d.ts +19 -0
  45. package/dist/security/origin-policy.d.ts.map +1 -0
  46. package/dist/security/origin-policy.js +58 -0
  47. package/dist/security/origin-policy.js.map +1 -0
  48. package/dist/security/origin.d.ts +5 -0
  49. package/dist/security/origin.d.ts.map +1 -0
  50. package/dist/security/origin.js +47 -0
  51. package/dist/security/origin.js.map +1 -0
  52. package/dist/security/session-token.d.ts +65 -0
  53. package/dist/security/session-token.d.ts.map +1 -0
  54. package/dist/security/session-token.js +338 -0
  55. package/dist/security/session-token.js.map +1 -0
  56. package/dist/security/sessions.d.ts +49 -0
  57. package/dist/security/sessions.d.ts.map +1 -0
  58. package/dist/security/sessions.js +159 -0
  59. package/dist/security/sessions.js.map +1 -0
  60. package/dist/security/startup-checks.d.ts +44 -0
  61. package/dist/security/startup-checks.d.ts.map +1 -0
  62. package/dist/security/startup-checks.js +134 -0
  63. package/dist/security/startup-checks.js.map +1 -0
  64. package/dist/security/turnstile.d.ts +24 -0
  65. package/dist/security/turnstile.d.ts.map +1 -0
  66. package/dist/security/turnstile.js +93 -0
  67. package/dist/security/turnstile.js.map +1 -0
  68. package/dist/visitor-context.d.ts.map +1 -1
  69. package/dist/visitor-context.js +1 -0
  70. package/dist/visitor-context.js.map +1 -1
  71. package/package.json +22 -5
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The visitor's IP, for per-IP rate limits and budgets. Only headers a
3
+ * platform sets itself are read, never ones a visitor can send. When the IP
4
+ * can't be told, per-IP limits are skipped for that request (with one
5
+ * warning) instead of every visitor sharing one bucket.
6
+ */
7
+ const DOCS = 'https://chat.codercops.com/server/fetch-runtimes/#the-visitors-ip';
8
+ // IPv4, IPv6 (including IPv4-mapped) and nothing else, so a header can't
9
+ // carry an arbitrary counter key.
10
+ const IP_PATTERN = /^[0-9A-Fa-f:.]{2,45}$/;
11
+ export function cleanIp(value) {
12
+ const ip = value?.split(',')[0]?.trim();
13
+ return ip && IP_PATTERN.test(ip) ? ip : undefined;
14
+ }
15
+ /** Vercel overwrites these headers, so a visitor can't set them there. */
16
+ export function onVercel() {
17
+ return typeof process !== 'undefined' && Boolean(process.env?.VERCEL);
18
+ }
19
+ const warned = new Set();
20
+ export function warnOnce(key, message) {
21
+ if (warned.has(key))
22
+ return;
23
+ warned.add(key);
24
+ console.warn(message);
25
+ }
26
+ /** For tests. */
27
+ export function resetClientIpWarnings() {
28
+ warned.clear();
29
+ }
30
+ /** The fetch handler for Vercel and other Fetch API runtimes. */
31
+ export function fetchClientIp(request, resolver) {
32
+ if (resolver)
33
+ return cleanIp(resolver(request));
34
+ if (onVercel()) {
35
+ const headers = request.headers;
36
+ return cleanIp(headers.get('x-vercel-forwarded-for')) ?? cleanIp(headers.get('x-real-ip')) ?? cleanIp(headers.get('x-forwarded-for'));
37
+ }
38
+ warnOnce('fetch', `[chatcops] 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}`);
39
+ return undefined;
40
+ }
41
+ /** Cloudflare sets CF-Connecting-IP on every request that reaches a Worker. */
42
+ export function cloudflareClientIp(request, resolver) {
43
+ if (resolver)
44
+ return cleanIp(resolver(request));
45
+ const ip = cleanIp(request.headers.get('cf-connecting-ip'));
46
+ if (!ip) {
47
+ warnOnce('cloudflare', '[chatcops] A request had no CF-Connecting-IP header, so per-IP rate limits and budgets were skipped for it.');
48
+ }
49
+ return ip;
50
+ }
51
+ /**
52
+ * Express's `req.ip` follows its `trust proxy` setting. Behind a proxy with
53
+ * that setting off, every visitor gets the proxy's address and shares one
54
+ * rate limit and budget, so that case gets a warning.
55
+ */
56
+ export function expressClientIp(req) {
57
+ if (req.headers?.['x-forwarded-for'] !== undefined && !req.app?.get?.('trust proxy')) {
58
+ warnOnce('express', "[chatcops] 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).");
59
+ }
60
+ return cleanIp(req.ip ?? req.socket?.remoteAddress);
61
+ }
62
+ //# sourceMappingURL=client-ip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-ip.js","sourceRoot":"","sources":["../src/client-ip.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,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;IACnD,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO;IAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACxB,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;IACpF,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,qMAAqM,IAAI,EAAE,CAC5M,CAAC;IACF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,OAAgB,EAAE,QAAsC;IACzF,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,6GAA6G,CAAC,CAAC;IACxI,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AASD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAuB;IACrD,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,kRAAkR,CACnR,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,17 @@
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, 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;
6
12
  tools?: ChatTool[];
7
13
  toolTimeoutMs?: number;
8
- /** Most tokens in one reply. Defaults to 1024. */
14
+ /** Most tokens in one reply. Defaults to 4096 (`DEFAULT_MAX_TOKENS` in @chatcops/core). */
9
15
  maxTokens?: number;
10
16
  /** Sampling temperature. Defaults to the provider's own; the allowed range depends on the provider. */
11
17
  temperature?: number;
@@ -14,12 +20,94 @@ export interface ChatCopsServerConfig {
14
20
  maxRequests: number;
15
21
  windowMs: number;
16
22
  };
23
+ /**
24
+ * Token, reply and dollar limits per conversation, per IP per day and
25
+ * overall per day, counted in `storage` so they hold across instances. A
26
+ * reply that would go over ends with an error instead of calling the model.
27
+ * On by default (`DEFAULT_BUDGETS`: 200 replies per conversation, 2M tokens
28
+ * per IP per day). A scope you set replaces its default, `false` turns a
29
+ * scope off, and `budgets: false` turns them all off.
30
+ */
31
+ budgets?: Budgets | false;
32
+ /**
33
+ * The pre-chat form fields to keep (the `name`s in the widget's
34
+ * `preChatForm.fields`). Other fields a request sends are dropped. Unset
35
+ * keeps every field. Answers are stored with the conversation from its
36
+ * first message and can't change after that.
37
+ */
38
+ preChatFields?: string[];
17
39
  webhooks?: WebhookConfig[];
18
40
  analytics?: boolean;
19
- /** Storage backend for conversation history. Defaults to an in-memory store (`MemoryStore`). */
41
+ /** Bearer token required to read the analytics endpoint. When unset, the endpoint is public. */
42
+ analyticsToken?: string;
43
+ /**
44
+ * Where conversations (and, with stores that have them, counters, turn
45
+ * locks and events) live: `postgresStorage(...)`, `redisStorage(...)` and
46
+ * so on. Defaults to memory, which doesn't survive a restart and isn't
47
+ * shared between serverless instances.
48
+ */
49
+ storage?: ChatCopsStorage;
50
+ /**
51
+ * @deprecated Use `storage`. A conversation store on its own, as in 0.4.
52
+ * Setting both is an error.
53
+ */
20
54
  conversationStore?: ConversationStore;
21
- /** Allowed CORS origin. Defaults to `'*'`. */
55
+ /**
56
+ * How long to keep conversations. `purge()` on the handler deletes older
57
+ * ones; call it on a schedule (a cron job, a Workers cron trigger).
58
+ */
59
+ retention?: {
60
+ conversationsDays?: number;
61
+ };
62
+ /**
63
+ * The sites that can use the chat: exact origins (`'https://acme.com'`) or
64
+ * subdomain wildcards (`'https://*.acme.com'`). A browser request from any
65
+ * other page gets 403 `origin_not_allowed` before the model is called.
66
+ * Requests without an `Origin` header (curl, your own server) pass: this
67
+ * stops other sites embedding your bot, not scripts. Defaults to `['*']`.
68
+ */
69
+ allowedOrigins?: string[];
70
+ /** @deprecated Use `allowedOrigins`. Read as a one-item `allowedOrigins`. */
22
71
  cors?: string;
72
+ /**
73
+ * Reads the visitor's IP for per-IP rate limits and budgets, in the fetch
74
+ * handlers (`chatcopsVercelHandler`, `chatcopsCloudflareHandler`). Needed
75
+ * where the handler can't tell on its own: any host other than Vercel or
76
+ * Cloudflare. Read only a header your platform or proxy sets, never one a
77
+ * visitor can send. Return `undefined` to skip per-IP limits for a request.
78
+ * Express uses `req.ip`, which follows its `trust proxy` setting.
79
+ */
80
+ clientIp?: ClientIpResolver;
81
+ /**
82
+ * Turns on session tokens. `POST <chat url>/session` gives the widget a
83
+ * token bound to its origin and one conversation, and chat requests send
84
+ * it as `Authorization: Bearer <token>`, so a guessed conversation id is
85
+ * useless. Sign with the first secret and verify with all of them, so a
86
+ * new secret goes first and the old one stays until its tokens expire.
87
+ * Each needs at least 32 bytes (`openssl rand -base64 32`). Keep them
88
+ * server side, never in a client bundle.
89
+ */
90
+ sessionSecrets?: string[];
91
+ /** How long a session token lasts, in seconds. Defaults to 900. */
92
+ sessionTtlSec?: number;
93
+ /**
94
+ * Refuse chat requests without a session token (401 `session_required`).
95
+ * Defaults to true when `sessionSecrets` is set. Set false while widgets
96
+ * older than 0.6, which don't send tokens, are still out there.
97
+ */
98
+ requireSession?: boolean;
99
+ /**
100
+ * Cloudflare Turnstile when a session token is issued: `/session` needs a
101
+ * Turnstile token and checks it with Cloudflare before signing. Refreshing
102
+ * a session with its previous token skips it. Needs `sessionSecrets`, and
103
+ * the widget's `turnstileSiteKey`.
104
+ */
105
+ turnstile?: TurnstileConfig;
106
+ /**
107
+ * Adds the reply's token usage to the `finish` part of protocol v1
108
+ * streams. Off by default, since visitors can read the stream.
109
+ */
110
+ exposeUsage?: boolean;
23
111
  /** @deprecated The server ignores this. Set the widget's text with its `strings` option. */
24
112
  i18n?: {
25
113
  defaultLocale: string;
@@ -34,7 +122,17 @@ export declare const chatRequestSchema: z.ZodObject<{
34
122
  conversationId: z.ZodString;
35
123
  message: z.ZodString;
36
124
  messageId: z.ZodOptional<z.ZodString>;
37
- regenerate: z.ZodOptional<z.ZodBoolean>;
125
+ /**
126
+ * `{ assistantMessageId }` replaces that reply. `true` (the 0.5 form)
127
+ * replaces the reply to `messageId`, or the latest one.
128
+ */
129
+ regenerate: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
130
+ assistantMessageId: z.ZodString;
131
+ }, "strip", z.ZodTypeAny, {
132
+ assistantMessageId: string;
133
+ }, {
134
+ assistantMessageId: string;
135
+ }>]>>;
38
136
  pageContext: z.ZodOptional<z.ZodObject<{
39
137
  url: z.ZodString;
40
138
  title: z.ZodString;
@@ -57,7 +155,9 @@ export declare const chatRequestSchema: z.ZodObject<{
57
155
  conversationId: string;
58
156
  message: string;
59
157
  messageId?: string | undefined;
60
- regenerate?: boolean | undefined;
158
+ regenerate?: boolean | {
159
+ assistantMessageId: string;
160
+ } | undefined;
61
161
  pageContext?: {
62
162
  url: string;
63
163
  title: string;
@@ -70,7 +170,9 @@ export declare const chatRequestSchema: z.ZodObject<{
70
170
  conversationId: string;
71
171
  message: string;
72
172
  messageId?: string | undefined;
73
- regenerate?: boolean | undefined;
173
+ regenerate?: boolean | {
174
+ assistantMessageId: string;
175
+ } | undefined;
74
176
  pageContext?: {
75
177
  url: string;
76
178
  title: string;
@@ -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":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EACV,UAAU,EACV,OAAO,EACP,eAAe,EACf,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,cAAc,EACd,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,QAAQ,EAAE,cAAc,GAAG,UAAU,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2FAA2F;IAC3F,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;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC1B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gGAAgG;IAChG,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE;QAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,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;;;;IAI5B;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAeH,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
package/dist/config.js CHANGED
@@ -8,7 +8,11 @@ export const chatRequestSchema = z.object({
8
8
  conversationId: z.string().min(1).max(128),
9
9
  message: z.string().min(1).max(10000),
10
10
  messageId: z.string().min(1).max(128).optional(),
11
- regenerate: z.boolean().optional(),
11
+ /**
12
+ * `{ assistantMessageId }` replaces that reply. `true` (the 0.5 form)
13
+ * replaces the reply to `messageId`, or the latest one.
14
+ */
15
+ regenerate: z.union([z.boolean(), z.object({ assistantMessageId: z.string().min(1).max(128) })]).optional(),
12
16
  pageContext: z.object({
13
17
  url: z.string().url(),
14
18
  title: z.string().max(500),
@@ -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;AAwHxB,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;;;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;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"}
package/dist/handler.d.ts CHANGED
@@ -1,9 +1,58 @@
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;
8
24
  };
25
+ /** The stream a caller writes: protocol v1, the 0.5 chunks, or the AI SDK's UI message stream. */
26
+ export type ChatFormat = 'v1' | 'legacy' | 'ai-sdk';
27
+ export interface StartChatOptions {
28
+ signal?: AbortSignal;
29
+ /** Which stream the caller writes, for `protocolStats()`. Defaults to `'v1'`. */
30
+ format?: ChatFormat;
31
+ /** The request's `Authorization` header, for session tokens. */
32
+ authorization?: string | null;
33
+ /** The request's `Origin` header, which a session token is bound to. */
34
+ origin?: string | null;
35
+ }
36
+ /** A request the handler won't answer, or the reply's parts. */
37
+ export type ChatStart = {
38
+ ok: true;
39
+ parts: AsyncGenerator<WirePart, void>;
40
+ } | {
41
+ ok: false;
42
+ /** The HTTP status protocol v1 answers with. */
43
+ status: number;
44
+ error: WireErrorBody['error'];
45
+ /** The chunk a 0.5 widget gets instead, inside a 200 stream. */
46
+ legacy: LegacyChunk & {
47
+ details?: unknown;
48
+ };
49
+ };
50
+ export type ChatHandler = ReturnType<typeof createChatHandler>;
51
+ /**
52
+ * Protocol v1 parts as 0.5 chunks (JSON strings). An error goes out last,
53
+ * after the turn has ended and lead webhooks have finished, as in 0.5.
54
+ */
55
+ export declare function toLegacyStream(parts: AsyncIterable<WirePart>): AsyncGenerator<string>;
56
+ /** The startup line, so the limits in force are never a surprise. */
57
+ export declare function describeBudgets(budgets: Budgets, defaulted?: BudgetScope[]): string;
9
58
  //# 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,EAOL,KAAK,cAAc,EAenB,KAAK,WAAW,EAChB,KAAK,OAAO,EAGZ,KAAK,WAAW,EAChB,KAAK,aAAa,EAElB,KAAK,QAAQ,EACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,oBAAoB,EAAwB,MAAM,aAAa,CAAC;AAQ9E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB;uBAgLpD,OAAO,aACF,MAAM,YACR;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAChC,cAAc,CAAC,MAAM,CAAC;sBAlGM,OAAO,aAAa,MAAM,YAAW,gBAAgB,KAAQ,OAAO,CAAC,SAAS,CAAC;6BAwStE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAc5D,yFAAyF;IACzF,2EAA2E;0BAEhE,OAAO,UAAU,MAAM,GAAG,IAAI,aAAa,MAAM;;;;;;IAG5D,uFAAuF;;6BA3OnD;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;;EAgPtG;AAED,kGAAkG;AAClG,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpD,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"}