@intelligo-dev/chat 1.0.0-beta.13

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 (94) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +6 -0
  3. package/README.md +116 -0
  4. package/dist/artifact-writer.d.ts +45 -0
  5. package/dist/artifact-writer.d.ts.map +1 -0
  6. package/dist/artifact-writer.js +77 -0
  7. package/dist/artifact-writer.js.map +1 -0
  8. package/dist/attachments.d.ts +56 -0
  9. package/dist/attachments.d.ts.map +1 -0
  10. package/dist/attachments.js +204 -0
  11. package/dist/attachments.js.map +1 -0
  12. package/dist/body.d.ts +72 -0
  13. package/dist/body.d.ts.map +1 -0
  14. package/dist/body.js +174 -0
  15. package/dist/body.js.map +1 -0
  16. package/dist/client.d.ts +65 -0
  17. package/dist/client.d.ts.map +1 -0
  18. package/dist/client.js +61 -0
  19. package/dist/client.js.map +1 -0
  20. package/dist/config.d.ts +322 -0
  21. package/dist/config.d.ts.map +1 -0
  22. package/dist/config.js +11 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/errors.d.ts +23 -0
  25. package/dist/errors.d.ts.map +1 -0
  26. package/dist/errors.js +41 -0
  27. package/dist/errors.js.map +1 -0
  28. package/dist/feedback.d.ts +22 -0
  29. package/dist/feedback.d.ts.map +1 -0
  30. package/dist/feedback.js +46 -0
  31. package/dist/feedback.js.map +1 -0
  32. package/dist/generation.d.ts +104 -0
  33. package/dist/generation.d.ts.map +1 -0
  34. package/dist/generation.js +85 -0
  35. package/dist/generation.js.map +1 -0
  36. package/dist/handler.d.ts +30 -0
  37. package/dist/handler.d.ts.map +1 -0
  38. package/dist/handler.js +913 -0
  39. package/dist/handler.js.map +1 -0
  40. package/dist/index.d.ts +31 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +20 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/messages.d.ts +19 -0
  45. package/dist/messages.d.ts.map +1 -0
  46. package/dist/messages.js +34 -0
  47. package/dist/messages.js.map +1 -0
  48. package/dist/parts.d.ts +117 -0
  49. package/dist/parts.d.ts.map +1 -0
  50. package/dist/parts.js +13 -0
  51. package/dist/parts.js.map +1 -0
  52. package/dist/quota.d.ts +32 -0
  53. package/dist/quota.d.ts.map +1 -0
  54. package/dist/quota.js +81 -0
  55. package/dist/quota.js.map +1 -0
  56. package/dist/share.d.ts +24 -0
  57. package/dist/share.d.ts.map +1 -0
  58. package/dist/share.js +78 -0
  59. package/dist/share.js.map +1 -0
  60. package/dist/testing.d.ts +36 -0
  61. package/dist/testing.d.ts.map +1 -0
  62. package/dist/testing.js +82 -0
  63. package/dist/testing.js.map +1 -0
  64. package/dist/title.d.ts +7 -0
  65. package/dist/title.d.ts.map +1 -0
  66. package/dist/title.js +15 -0
  67. package/dist/title.js.map +1 -0
  68. package/dist/usage.d.ts +21 -0
  69. package/dist/usage.d.ts.map +1 -0
  70. package/dist/usage.js +35 -0
  71. package/dist/usage.js.map +1 -0
  72. package/dist/windowing.d.ts +38 -0
  73. package/dist/windowing.d.ts.map +1 -0
  74. package/dist/windowing.js +82 -0
  75. package/dist/windowing.js.map +1 -0
  76. package/package.json +78 -0
  77. package/src/artifact-writer.ts +114 -0
  78. package/src/attachments.ts +262 -0
  79. package/src/body.ts +236 -0
  80. package/src/client.ts +133 -0
  81. package/src/config.ts +376 -0
  82. package/src/errors.ts +80 -0
  83. package/src/feedback.ts +62 -0
  84. package/src/generation.ts +150 -0
  85. package/src/handler.ts +1164 -0
  86. package/src/index.ts +93 -0
  87. package/src/messages.ts +39 -0
  88. package/src/parts.ts +143 -0
  89. package/src/quota.ts +105 -0
  90. package/src/share.ts +103 -0
  91. package/src/testing.ts +150 -0
  92. package/src/title.ts +15 -0
  93. package/src/usage.ts +46 -0
  94. package/src/windowing.ts +110 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * A deterministic language model (from `ai/test`) that streams end to end
3
+ * before any provider is configured; the transport's own tests use it too.
4
+ * `modelId` is a parameter, never a literal: the boundary bills whatever id
5
+ * the transport settles with, so it must be registered in the pricing table.
6
+ */
7
+ import { MockLanguageModelV3, simulateReadableStream } from "ai/test";
8
+ /** The last user message's text, from a provider-level prompt. */
9
+ export function lastUserTextOf(prompt) {
10
+ for (let i = prompt.length - 1; i >= 0; i--) {
11
+ const message = prompt[i];
12
+ if (message?.role !== "user")
13
+ continue;
14
+ const parts = Array.isArray(message.content) ? message.content : [];
15
+ return parts
16
+ .filter((part) => typeof part === "object" &&
17
+ part !== null &&
18
+ part.type === "text")
19
+ .map((part) => part.text)
20
+ .join(" ");
21
+ }
22
+ return "";
23
+ }
24
+ export function createStubLanguageModel(options) {
25
+ const delay = options.chunkDelayInMs ?? 15;
26
+ return new MockLanguageModelV3({
27
+ provider: "stub",
28
+ modelId: options.modelId,
29
+ doStream: async ({ prompt }) => {
30
+ const userText = lastUserTextOf(prompt);
31
+ const reply = await options.reply(userText, prompt);
32
+ const call = options.toolCall
33
+ ? await options.toolCall(userText, prompt)
34
+ : null;
35
+ const words = reply.split(" ");
36
+ const chunks = [
37
+ { type: "stream-start", warnings: [] },
38
+ { type: "text-start", id: "1" },
39
+ ...words.map((word, index) => ({
40
+ type: "text-delta",
41
+ id: "1",
42
+ delta: index === 0 ? word : ` ${word}`,
43
+ })),
44
+ { type: "text-end", id: "1" },
45
+ ...(call
46
+ ? [
47
+ {
48
+ type: "tool-call",
49
+ toolCallId: `stub-${Date.now()}`,
50
+ toolName: call.toolName,
51
+ input: JSON.stringify(call.input),
52
+ },
53
+ ]
54
+ : []),
55
+ {
56
+ type: "finish",
57
+ finishReason: {
58
+ unified: call ? "tool-calls" : "stop",
59
+ raw: undefined,
60
+ },
61
+ usage: {
62
+ inputTokens: {
63
+ total: Math.max(1, Math.ceil(JSON.stringify(prompt).length / 4)),
64
+ noCache: undefined,
65
+ cacheRead: undefined,
66
+ cacheWrite: undefined,
67
+ },
68
+ outputTokens: {
69
+ total: words.length,
70
+ text: words.length,
71
+ reasoning: undefined,
72
+ },
73
+ },
74
+ },
75
+ ];
76
+ return {
77
+ stream: simulateReadableStream({ chunkDelayInMs: delay, chunks }),
78
+ };
79
+ },
80
+ });
81
+ }
82
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The default conversation title: the first line of the opening
3
+ * message, truncated to something a sidebar can list.
4
+ */
5
+ /** First line, trimmed, truncated — enough to tell history rows apart. */
6
+ export declare function truncateTitle(firstUserText: string): string | null;
7
+ //# sourceMappingURL=title.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"title.d.ts","sourceRoot":"","sources":["../src/title.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMlE"}
package/dist/title.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The default conversation title: the first line of the opening
3
+ * message, truncated to something a sidebar can list.
4
+ */
5
+ const MAX_TITLE_LENGTH = 60;
6
+ /** First line, trimmed, truncated — enough to tell history rows apart. */
7
+ export function truncateTitle(firstUserText) {
8
+ const line = firstUserText.trim().split("\n")[0]?.trim();
9
+ if (!line)
10
+ return null;
11
+ return line.length <= MAX_TITLE_LENGTH
12
+ ? line
13
+ : `${line.slice(0, MAX_TITLE_LENGTH - 1).trimEnd()}…`;
14
+ }
15
+ //# sourceMappingURL=title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"title.js","sourceRoot":"","sources":["../src/title.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,aAAqB;IACjD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO,IAAI,CAAC,MAAM,IAAI,gBAAgB;QACpC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Token usage, in the one shape the execution boundary settles.
3
+ */
4
+ export type TokenUsage = {
5
+ inputTokens?: number;
6
+ outputTokens?: number;
7
+ totalTokens?: number;
8
+ };
9
+ /** The three fields settlement reads, and nothing else a provider adds. */
10
+ export declare function pickUsage(usage: TokenUsage | undefined): TokenUsage;
11
+ /**
12
+ * Whole-run usage from the steps that completed before an abort. A
13
+ * step still in flight is missed — under-counting by at most one step
14
+ * beats charging nothing and leaving the hold to expire.
15
+ */
16
+ export declare function sumStepUsage(steps: ReadonlyArray<{
17
+ usage?: TokenUsage;
18
+ }>): TokenUsage;
19
+ /** The sum of two usages; a total a provider omitted is input plus output. */
20
+ export declare function sumUsage(a: TokenUsage, b: TokenUsage): TokenUsage;
21
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,CAMnE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,aAAa,CAAC;IAAE,KAAK,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,GAC3C,UAAU,CAQZ;AAED,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,UAAU,CAQjE"}
package/dist/usage.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Token usage, in the one shape the execution boundary settles.
3
+ */
4
+ /** The three fields settlement reads, and nothing else a provider adds. */
5
+ export function pickUsage(usage) {
6
+ return {
7
+ inputTokens: usage?.inputTokens,
8
+ outputTokens: usage?.outputTokens,
9
+ totalTokens: usage?.totalTokens,
10
+ };
11
+ }
12
+ /**
13
+ * Whole-run usage from the steps that completed before an abort. A
14
+ * step still in flight is missed — under-counting by at most one step
15
+ * beats charging nothing and leaving the hold to expire.
16
+ */
17
+ export function sumStepUsage(steps) {
18
+ let inputTokens = 0;
19
+ let outputTokens = 0;
20
+ for (const step of steps) {
21
+ inputTokens += step.usage?.inputTokens ?? 0;
22
+ outputTokens += step.usage?.outputTokens ?? 0;
23
+ }
24
+ return { inputTokens, outputTokens, totalTokens: inputTokens + outputTokens };
25
+ }
26
+ /** The sum of two usages; a total a provider omitted is input plus output. */
27
+ export function sumUsage(a, b) {
28
+ const total = (u) => u.totalTokens ?? (u.inputTokens ?? 0) + (u.outputTokens ?? 0);
29
+ return {
30
+ inputTokens: (a.inputTokens ?? 0) + (b.inputTokens ?? 0),
31
+ outputTokens: (a.outputTokens ?? 0) + (b.outputTokens ?? 0),
32
+ totalTokens: total(a) + total(b),
33
+ };
34
+ }
35
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,2EAA2E;AAC3E,MAAM,UAAU,SAAS,CAAC,KAA6B;IACrD,OAAO;QACL,WAAW,EAAE,KAAK,EAAE,WAAW;QAC/B,YAAY,EAAE,KAAK,EAAE,YAAY;QACjC,WAAW,EAAE,KAAK,EAAE,WAAW;KAChC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,KAA4C;IAE5C,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,WAAW,IAAI,IAAI,CAAC,KAAK,EAAE,WAAW,IAAI,CAAC,CAAC;QAC5C,YAAY,IAAI,IAAI,CAAC,KAAK,EAAE,YAAY,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,GAAG,YAAY,EAAE,CAAC;AAChF,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,QAAQ,CAAC,CAAa,EAAE,CAAa;IACnD,MAAM,KAAK,GAAG,CAAC,CAAa,EAAE,EAAE,CAC9B,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC;IAChE,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC;QACxD,YAAY,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC;QAC3D,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;KACjC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Conversation windowing: what the model is shown of a long history.
3
+ *
4
+ * Works on `UIMessage`s, the AI SDK's transcript, not stored rows.
5
+ * The default `prepareMessages` applies it; an application that wants
6
+ * to summarise what was pruned does so in its own `prepareMessages`,
7
+ * with its own model, and hands the summary back as `system`.
8
+ */
9
+ import type { UIMessage } from "ai";
10
+ /** The text parts of a message, joined. Tool and file parts contribute nothing. */
11
+ export declare function extractText(parts: ReadonlyArray<unknown>): string;
12
+ /**
13
+ * A cheap token estimate that does not need a tokenizer.
14
+ *
15
+ * Latin text runs about four characters per token; Cyrillic — and
16
+ * most non-Latin scripts — closer to two, because their byte-pair
17
+ * vocabularies are smaller. A single divisor would under-count a
18
+ * Cyrillic transcript by half and window it far too late.
19
+ */
20
+ export declare function estimateTokenCount(text: string): number;
21
+ export declare function estimateConversationTokens(messages: ReadonlyArray<UIMessage>): number;
22
+ export type ConversationWindowOptions = {
23
+ /** Keep at most this many non-system messages. */
24
+ maxMessages: number;
25
+ /** Also drop from the front until the estimate fits, when set. */
26
+ maxTokens?: number;
27
+ };
28
+ export type ConversationWindow = {
29
+ windowed: UIMessage[];
30
+ pruned: UIMessage[];
31
+ };
32
+ /**
33
+ * Keep the most recent messages, always starting the window on a user
34
+ * turn so the model never sees an assistant reply without the message
35
+ * it answered. System messages are never pruned.
36
+ */
37
+ export declare function applyConversationWindow(messages: ReadonlyArray<UIMessage>, options: ConversationWindowOptions): ConversationWindow;
38
+ //# sourceMappingURL=windowing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windowing.d.ts","sourceRoot":"","sources":["../src/windowing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAapC,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,MAAM,CAKjE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUvD;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC,GACjC,MAAM,CAYR;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC,EAClC,OAAO,EAAE,yBAAyB,GACjC,kBAAkB,CAuBpB"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Conversation windowing: what the model is shown of a long history.
3
+ *
4
+ * Works on `UIMessage`s, the AI SDK's transcript, not stored rows.
5
+ * The default `prepareMessages` applies it; an application that wants
6
+ * to summarise what was pruned does so in its own `prepareMessages`,
7
+ * with its own model, and hands the summary back as `system`.
8
+ */
9
+ function isTextPart(part) {
10
+ return (typeof part === "object" &&
11
+ part !== null &&
12
+ part.type === "text" &&
13
+ typeof part.text === "string");
14
+ }
15
+ /** The text parts of a message, joined. Tool and file parts contribute nothing. */
16
+ export function extractText(parts) {
17
+ return parts
18
+ .filter(isTextPart)
19
+ .map((part) => part.text)
20
+ .join(" ");
21
+ }
22
+ /**
23
+ * A cheap token estimate that does not need a tokenizer.
24
+ *
25
+ * Latin text runs about four characters per token; Cyrillic — and
26
+ * most non-Latin scripts — closer to two, because their byte-pair
27
+ * vocabularies are smaller. A single divisor would under-count a
28
+ * Cyrillic transcript by half and window it far too late.
29
+ */
30
+ export function estimateTokenCount(text) {
31
+ // Stryker disable next-line ConditionalExpression: equivalent — the loop below counts nothing in an empty string and returns 0 either way.
32
+ if (!text)
33
+ return 0;
34
+ let nonLatin = 0;
35
+ for (const char of text) {
36
+ const code = char.codePointAt(0) ?? 0;
37
+ if (code > 0x024f)
38
+ nonLatin++;
39
+ }
40
+ const latin = text.length - nonLatin;
41
+ return Math.ceil(latin / 4 + nonLatin / 2);
42
+ }
43
+ export function estimateConversationTokens(messages) {
44
+ let total = 0;
45
+ for (const message of messages) {
46
+ // Tool payloads are not text but still cost tokens; count their
47
+ // serialised size at the Latin rate.
48
+ for (const part of message.parts) {
49
+ total += isTextPart(part)
50
+ ? estimateTokenCount(part.text)
51
+ : Math.ceil(JSON.stringify(part).length / 4);
52
+ }
53
+ }
54
+ return total;
55
+ }
56
+ /**
57
+ * Keep the most recent messages, always starting the window on a user
58
+ * turn so the model never sees an assistant reply without the message
59
+ * it answered. System messages are never pruned.
60
+ */
61
+ export function applyConversationWindow(messages, options) {
62
+ const system = messages.filter((message) => message.role === "system");
63
+ const turns = messages.filter((message) => message.role !== "system");
64
+ let start = Math.max(0, turns.length - options.maxMessages);
65
+ // Stryker disable next-line ConditionalExpression: equivalent — with no budget the comparison against undefined is false, so the loop exits on its first test.
66
+ if (options.maxTokens !== undefined) {
67
+ while (start < turns.length - 1 &&
68
+ estimateConversationTokens(turns.slice(start)) > options.maxTokens) {
69
+ start++;
70
+ }
71
+ }
72
+ // Open on a user message. If none follows, keep the last turn: a
73
+ // window of nothing is worse than one that opens mid-exchange.
74
+ // Stryker disable next-line OptionalChaining: equivalent — the condition to its left has already proved the index is in range.
75
+ while (start < turns.length - 1 && turns[start]?.role !== "user")
76
+ start++;
77
+ return {
78
+ windowed: [...system, ...turns.slice(start)],
79
+ pruned: turns.slice(0, start),
80
+ };
81
+ }
82
+ //# sourceMappingURL=windowing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windowing.js","sourceRoot":"","sources":["../src/windowing.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@intelligo-dev/chat",
3
+ "version": "1.0.0-beta.13",
4
+ "description": "The AI-SDK-native chat transport: one Route Handler with auth, rate limits, feature gates, credits and persistence.",
5
+ "keywords": [
6
+ "intelligo",
7
+ "ai-saas",
8
+ "saas",
9
+ "typescript",
10
+ "ai-sdk",
11
+ "chat",
12
+ "streaming",
13
+ "llm"
14
+ ],
15
+ "license": "Apache-2.0",
16
+ "author": "Turtuvshin Byambaa <toroo.byamba@gmail.com>",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/intelligo-dev/intelligo.git",
20
+ "directory": "packages/chat"
21
+ },
22
+ "homepage": "https://intelligo.dev/docs/packages/chat",
23
+ "bugs": {
24
+ "url": "https://github.com/intelligo-dev/intelligo/issues"
25
+ },
26
+ "type": "module",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "default": "./dist/index.js"
31
+ },
32
+ "./client": {
33
+ "types": "./dist/client.d.ts",
34
+ "default": "./dist/client.js"
35
+ },
36
+ "./testing": {
37
+ "types": "./dist/testing.d.ts",
38
+ "default": "./dist/testing.js"
39
+ }
40
+ },
41
+ "dependencies": {
42
+ "@intelligo-dev/auth": "1.0.0-beta.13",
43
+ "@intelligo-dev/billing": "1.0.0-beta.13",
44
+ "@intelligo-dev/core": "1.0.0-beta.13",
45
+ "@intelligo-dev/executions": "1.0.0-beta.13"
46
+ },
47
+ "peerDependencies": {
48
+ "ai": "^7.0.0"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^26.6.1",
52
+ "ai": "^7.0.103",
53
+ "typescript": "^5.9.3"
54
+ },
55
+ "files": [
56
+ "dist",
57
+ "src",
58
+ "!src/**/*.test.ts",
59
+ "!src/**/*.test.tsx",
60
+ "!src/**/__tests__/**",
61
+ "LICENSE",
62
+ "NOTICE",
63
+ "README.md",
64
+ "!dist/**/*.tsbuildinfo"
65
+ ],
66
+ "publishConfig": {
67
+ "access": "public"
68
+ },
69
+ "engines": {
70
+ "node": ">=22.14"
71
+ },
72
+ "sideEffects": false,
73
+ "scripts": {
74
+ "type-check": "tsc --noEmit",
75
+ "lint": "eslint .",
76
+ "build": "tsc -p tsconfig.build.json && node ../../scripts/fix-esm-extensions.mjs dist"
77
+ }
78
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Stream a document into the chat canvas from inside a tool, as three
3
+ * calls over one `data-chat-artifact` part:
4
+ *
5
+ * const doc = createArtifactWriter(turn, { kind: "text", title });
6
+ * for (const chunk of chunks) doc.append(chunk);
7
+ * const saved = await saveDocument(actor, { id: doc.id, title, content, kind });
8
+ * doc.finish({ documentId: saved.id });
9
+ *
10
+ * Deltas are transient parts under the writer's `id` — the client
11
+ * accumulates them and nothing is persisted mid-stream. The `ready`
12
+ * part is persisted, so the message's card reopens the document from
13
+ * `documentId` after a reload; `error` is persisted too, so a failed
14
+ * document does not reload as a blank card.
15
+ */
16
+
17
+ import type { ChatArtifactData, ChatDataChunk } from "./parts";
18
+
19
+ export type ArtifactWriterOptions = {
20
+ kind: string;
21
+ title: string;
22
+ /** The part id, and the default document id. A fresh UUID when omitted. */
23
+ id?: string;
24
+ };
25
+
26
+ export type ArtifactWriter = {
27
+ readonly id: string;
28
+ /** Stream one increment. The first call opens the canvas. */
29
+ append: (delta: string) => void;
30
+ /**
31
+ * Mark the document ready. `content` is persisted in the part only
32
+ * when passed — a document that lives in the documents table is
33
+ * reopened from `documentId` instead.
34
+ */
35
+ finish: (result?: {
36
+ documentId?: string;
37
+ version?: number;
38
+ content?: string;
39
+ title?: string;
40
+ }) => void;
41
+ fail: (error: unknown) => void;
42
+ /** Everything appended so far. */
43
+ readonly content: string;
44
+ };
45
+
46
+ export function createArtifactWriter(
47
+ turn: { write: (chunk: ChatDataChunk) => void },
48
+ options: ArtifactWriterOptions
49
+ ): ArtifactWriter {
50
+ const id = options.id ?? crypto.randomUUID();
51
+ let title = options.title;
52
+ let content = "";
53
+ let opened = false;
54
+ let closed = false;
55
+
56
+ const emit = (data: ChatArtifactData, transient: boolean) => {
57
+ turn.write({
58
+ type: "data-chat-artifact",
59
+ id,
60
+ data,
61
+ ...(transient ? { transient: true } : {}),
62
+ });
63
+ };
64
+
65
+ const base = (): ChatArtifactData => ({
66
+ id,
67
+ kind: options.kind,
68
+ title,
69
+ status: "streaming",
70
+ });
71
+
72
+ return {
73
+ id,
74
+ get content() {
75
+ return content;
76
+ },
77
+ append(delta) {
78
+ if (closed) return;
79
+ content += delta;
80
+ if (!opened) {
81
+ opened = true;
82
+ emit(base(), true);
83
+ }
84
+ emit({ ...base(), delta }, true);
85
+ },
86
+ finish(result = {}) {
87
+ if (closed) return;
88
+ closed = true;
89
+ if (result.title) title = result.title;
90
+ emit(
91
+ {
92
+ ...base(),
93
+ status: "ready",
94
+ ...(result.documentId ? { documentId: result.documentId } : {}),
95
+ ...(result.version !== undefined ? { version: result.version } : {}),
96
+ ...(result.content !== undefined ? { content: result.content } : {}),
97
+ },
98
+ false
99
+ );
100
+ },
101
+ fail(error) {
102
+ if (closed) return;
103
+ closed = true;
104
+ emit(
105
+ {
106
+ ...base(),
107
+ status: "error",
108
+ error: error instanceof Error ? error.message : String(error),
109
+ },
110
+ false
111
+ );
112
+ },
113
+ };
114
+ }