chatkit-bun 0.0.2

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 (52) hide show
  1. package/README.md +202 -0
  2. package/package.json +40 -0
  3. package/src/actions.ts +39 -0
  4. package/src/agents/accumulate.ts +43 -0
  5. package/src/agents/annotations.ts +157 -0
  6. package/src/agents/context.ts +190 -0
  7. package/src/agents/converter.ts +290 -0
  8. package/src/agents/index.ts +25 -0
  9. package/src/agents/stream.ts +1053 -0
  10. package/src/agents/types.ts +30 -0
  11. package/src/agents/workflows.ts +220 -0
  12. package/src/errors.ts +19 -0
  13. package/src/http.ts +60 -0
  14. package/src/index.ts +11 -0
  15. package/src/serialization.ts +75 -0
  16. package/src/server.ts +874 -0
  17. package/src/sqlite-store.ts +400 -0
  18. package/src/store.ts +98 -0
  19. package/src/types/core.ts +322 -0
  20. package/src/types/server.ts +396 -0
  21. package/src/widgets/components.ts +188 -0
  22. package/src/widgets/diff.ts +151 -0
  23. package/src/widgets/index.ts +6 -0
  24. package/src/widgets/serialization.ts +46 -0
  25. package/src/widgets/stream.ts +104 -0
  26. package/src/widgets/template.ts +180 -0
  27. package/src/widgets/types.ts +52 -0
  28. package/types/actions.d.ts +19 -0
  29. package/types/agents/accumulate.d.ts +4 -0
  30. package/types/agents/annotations.d.ts +21 -0
  31. package/types/agents/context.d.ts +35 -0
  32. package/types/agents/converter.d.ts +60 -0
  33. package/types/agents/index.d.ts +9 -0
  34. package/types/agents/stream.d.ts +4 -0
  35. package/types/agents/types.d.ts +26 -0
  36. package/types/agents/workflows.d.ts +34 -0
  37. package/types/errors.d.ts +11 -0
  38. package/types/http.d.ts +6 -0
  39. package/types/index.d.ts +11 -0
  40. package/types/serialization.d.ts +8 -0
  41. package/types/server.d.ts +73 -0
  42. package/types/sqlite-store.d.ts +43 -0
  43. package/types/store.d.ts +45 -0
  44. package/types/types/core.d.ts +1220 -0
  45. package/types/types/server.d.ts +5841 -0
  46. package/types/widgets/components.d.ts +144 -0
  47. package/types/widgets/diff.d.ts +7 -0
  48. package/types/widgets/index.d.ts +6 -0
  49. package/types/widgets/serialization.d.ts +2 -0
  50. package/types/widgets/stream.d.ts +10 -0
  51. package/types/widgets/template.d.ts +19 -0
  52. package/types/widgets/types.d.ts +24 -0
@@ -0,0 +1,30 @@
1
+ import type { Store } from "../store";
2
+ import type { JsonValue } from "../serialization";
3
+ import type { ThreadMetadata } from "../types/core";
4
+ import type { ResponseStreamConverter } from "./annotations";
5
+
6
+ export interface AgentContextOptions<TContext> {
7
+ thread: ThreadMetadata;
8
+ store: Store<TContext>;
9
+ context: TContext;
10
+ previousResponseId?: string | null;
11
+ now?: () => Date | string;
12
+ }
13
+
14
+ export interface AgentStreamInput {
15
+ [Symbol.asyncIterator]?: () => AsyncIterator<unknown>;
16
+ toStream?: () => AsyncIterable<unknown>;
17
+ }
18
+
19
+ export interface StreamAgentResponseOptions {
20
+ converter?: ResponseStreamConverter;
21
+ }
22
+
23
+ export interface ToolCallMetadata {
24
+ itemId: string | null;
25
+ callId: string | null;
26
+ }
27
+
28
+ export type { JsonValue } from "../serialization";
29
+
30
+ export type JsonObject = { [key: string]: JsonValue };
@@ -0,0 +1,220 @@
1
+ import {
2
+ TaskSchema,
3
+ WorkflowSchema,
4
+ type Task,
5
+ type ThreadItem,
6
+ type Workflow,
7
+ type WorkflowSummary,
8
+ } from "../types/core";
9
+ import type { ThreadStreamEvent } from "../types/server";
10
+ import type { AgentContext } from "./context";
11
+
12
+ export type WorkflowItem = Extract<ThreadItem, { type: "workflow" }>;
13
+ export type ThoughtTask = Extract<WorkflowItem["workflow"]["tasks"][number], { type: "thought" }>;
14
+ export type ThreadItemAddedEvent = Extract<ThreadStreamEvent, { type: "thread.item.added" }>;
15
+ export type ThreadItemDoneEvent = Extract<ThreadStreamEvent, { type: "thread.item.done" }>;
16
+ export type ThreadItemUpdatedEvent = Extract<ThreadStreamEvent, { type: "thread.item.updated" }>;
17
+
18
+ export function isWorkflowItem(item: ThreadItem | undefined): item is WorkflowItem {
19
+ return item?.type === "workflow";
20
+ }
21
+
22
+ export function createWorkflowItem<TContext>(
23
+ context: AgentContext<TContext>,
24
+ workflow: Workflow,
25
+ ): WorkflowItem {
26
+ const parsedWorkflow = WorkflowSchema.parse(workflow);
27
+
28
+ return {
29
+ id: context.store.generateItemId("workflow", context.thread, context.context),
30
+ thread_id: context.thread.id,
31
+ created_at: context.createdAt(),
32
+ type: "workflow",
33
+ workflow: parsedWorkflow,
34
+ };
35
+ }
36
+
37
+ export function createReasoningWorkflowItem<TContext>(
38
+ context: AgentContext<TContext>,
39
+ ): WorkflowItem {
40
+ return createWorkflowItem(context, {
41
+ type: "reasoning",
42
+ tasks: [],
43
+ expanded: false,
44
+ });
45
+ }
46
+
47
+ export function shouldEmitWorkflowAdded(workflow: Workflow): boolean {
48
+ return workflow.type === "reasoning" || workflow.tasks.length > 0;
49
+ }
50
+
51
+ export function workflowAddedEvent(workflow: WorkflowItem): ThreadItemAddedEvent {
52
+ return {
53
+ type: "thread.item.added",
54
+ item: workflow,
55
+ };
56
+ }
57
+
58
+ export function resumeWorkflowFromThreadItems<TContext>(
59
+ context: AgentContext<TContext>,
60
+ items: readonly ThreadItem[],
61
+ ): void {
62
+ const latest = items[0];
63
+ const secondLatest = items[1];
64
+
65
+ if (isWorkflowItem(latest)) {
66
+ context.workflowItem = latest;
67
+ return;
68
+ }
69
+
70
+ if (latest?.type === "client_tool_call" && isWorkflowItem(secondLatest)) {
71
+ context.workflowItem = secondLatest;
72
+ }
73
+ }
74
+
75
+ export function shouldAutoEndWorkflowForItem<TContext>(
76
+ context: AgentContext<TContext>,
77
+ item: ThreadItem,
78
+ ): boolean {
79
+ const workflow = context.workflowItem;
80
+
81
+ return (
82
+ workflow !== null &&
83
+ item.id !== workflow.id &&
84
+ item.type !== "client_tool_call" &&
85
+ item.type !== "hidden_context_item" &&
86
+ item.type !== "sdk_hidden_context"
87
+ );
88
+ }
89
+
90
+ export function createThoughtTask(content: string): ThoughtTask {
91
+ return {
92
+ type: "thought",
93
+ content,
94
+ status_indicator: "none",
95
+ };
96
+ }
97
+
98
+ export function workflowTaskAddedEvent(
99
+ workflow: WorkflowItem,
100
+ task: Task,
101
+ taskIndex: number,
102
+ ): ThreadItemUpdatedEvent {
103
+ return {
104
+ type: "thread.item.updated",
105
+ item_id: workflow.id,
106
+ update: {
107
+ type: "workflow.task.added",
108
+ task_index: taskIndex,
109
+ task,
110
+ },
111
+ };
112
+ }
113
+
114
+ export function workflowTaskUpdatedEvent(
115
+ workflow: WorkflowItem,
116
+ task: Task,
117
+ taskIndex: number,
118
+ ): ThreadItemUpdatedEvent {
119
+ return {
120
+ type: "thread.item.updated",
121
+ item_id: workflow.id,
122
+ update: {
123
+ type: "workflow.task.updated",
124
+ task_index: taskIndex,
125
+ task,
126
+ },
127
+ };
128
+ }
129
+
130
+ export function normalizeWorkflowTask(task: Task): Task {
131
+ return TaskSchema.parse(task);
132
+ }
133
+
134
+ export function appendWorkflowTask(workflow: WorkflowItem, task: Task): ThreadItemUpdatedEvent {
135
+ const parsedTask = normalizeWorkflowTask(task);
136
+ workflow.workflow.tasks.push(parsedTask);
137
+ return workflowTaskAddedEvent(workflow, parsedTask, workflow.workflow.tasks.length - 1);
138
+ }
139
+
140
+ export function updateWorkflowTaskEvent(
141
+ workflow: WorkflowItem,
142
+ task: Task,
143
+ taskIndex: number,
144
+ ): ThreadItemUpdatedEvent {
145
+ if (
146
+ !Number.isInteger(taskIndex) ||
147
+ taskIndex < 0 ||
148
+ taskIndex >= workflow.workflow.tasks.length
149
+ ) {
150
+ throw new RangeError("Workflow task index is out of range");
151
+ }
152
+
153
+ const parsedTask = normalizeWorkflowTask(task);
154
+ workflow.workflow.tasks[taskIndex] = parsedTask;
155
+ return workflowTaskUpdatedEvent(workflow, parsedTask, taskIndex);
156
+ }
157
+
158
+ export function durationSeconds(startedAt: string, endedAt: string): number {
159
+ const started = Date.parse(startedAt);
160
+ const ended = Date.parse(endedAt);
161
+
162
+ if (!Number.isFinite(started) || !Number.isFinite(ended)) {
163
+ return 0;
164
+ }
165
+
166
+ return Math.max(0, Math.floor((ended - started) / 1000));
167
+ }
168
+
169
+ export function finishWorkflow<TContext>(
170
+ context: AgentContext<TContext>,
171
+ summary?: WorkflowSummary,
172
+ expanded = false,
173
+ ): ThreadItemDoneEvent | null {
174
+ const workflow = context.workflowItem;
175
+
176
+ if (!workflow) {
177
+ return null;
178
+ }
179
+
180
+ if (workflow.workflow.type !== "reasoning" && workflow.workflow.tasks.length === 0) {
181
+ context.workflowItem = null;
182
+ return null;
183
+ }
184
+
185
+ const endedAt = context.createdAt();
186
+ const doneItem: WorkflowItem = {
187
+ ...workflow,
188
+ workflow: {
189
+ ...workflow.workflow,
190
+ summary:
191
+ summary ??
192
+ workflow.workflow.summary ??
193
+ { duration: durationSeconds(workflow.created_at, endedAt) },
194
+ expanded,
195
+ },
196
+ };
197
+ context.workflowItem = null;
198
+
199
+ return {
200
+ type: "thread.item.done",
201
+ item: doneItem,
202
+ };
203
+ }
204
+
205
+ export async function persistOpenWorkflow<TContext>(
206
+ context: AgentContext<TContext>,
207
+ ): Promise<void> {
208
+ const workflow = context.workflowItem;
209
+
210
+ if (!workflow) {
211
+ return;
212
+ }
213
+
214
+ await context.store.saveItem(
215
+ context.thread.id,
216
+ { ...workflow, created_at: context.createdAt() },
217
+ context.context,
218
+ );
219
+ context.workflowItem = null;
220
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,19 @@
1
+ export class ChatKitError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = new.target.name;
5
+ }
6
+ }
7
+
8
+ export class NotFoundError extends ChatKitError {}
9
+
10
+ export class UnsupportedOperationError extends ChatKitError {}
11
+
12
+ export class ValidationError extends ChatKitError {
13
+ constructor(
14
+ message: string,
15
+ override readonly cause?: unknown,
16
+ ) {
17
+ super(message);
18
+ }
19
+ }
package/src/http.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { NonStreamingResult, StreamingResult, type ChatKitServer } from "./server";
2
+
3
+ export interface ChatKitHandlerOptions<TContext> {
4
+ getContext?: (request: Request) => TContext | Promise<TContext>;
5
+ }
6
+
7
+ export type ChatKitHandler = (request: Request) => Promise<Response>;
8
+
9
+ export function createChatKitHandler<TContext = undefined>(
10
+ server: ChatKitServer<TContext>,
11
+ options: ChatKitHandlerOptions<TContext> = {},
12
+ ): ChatKitHandler {
13
+ return async (request) => {
14
+ const context = options.getContext
15
+ ? await options.getContext(request)
16
+ : (undefined as TContext);
17
+ const result = await server.process(await request.arrayBuffer(), context);
18
+
19
+ if (result instanceof NonStreamingResult) {
20
+ return new Response(result.json, {
21
+ headers: {
22
+ "content-type": "application/json",
23
+ },
24
+ });
25
+ }
26
+
27
+ if (result instanceof StreamingResult) {
28
+ return new Response(toReadableStream(result), {
29
+ headers: {
30
+ "content-type": "text/event-stream",
31
+ "cache-control": "no-cache",
32
+ },
33
+ });
34
+ }
35
+
36
+ const _exhaustive: never = result;
37
+ return _exhaustive;
38
+ };
39
+ }
40
+
41
+ function toReadableStream(iterable: AsyncIterable<Uint8Array>): ReadableStream<Uint8Array> {
42
+ let iterator: AsyncIterator<Uint8Array> | undefined;
43
+
44
+ return new ReadableStream<Uint8Array>({
45
+ async pull(controller) {
46
+ iterator ??= iterable[Symbol.asyncIterator]();
47
+ const next = await iterator.next();
48
+
49
+ if (next.done) {
50
+ controller.close();
51
+ return;
52
+ }
53
+
54
+ controller.enqueue(next.value);
55
+ },
56
+ async cancel() {
57
+ await iterator?.return?.();
58
+ },
59
+ });
60
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export * from "./actions";
2
+ export * from "./agents";
3
+ export * from "./errors";
4
+ export * from "./http";
5
+ export * from "./serialization";
6
+ export * from "./server";
7
+ export * from "./sqlite-store";
8
+ export * from "./store";
9
+ export * from "./types/core";
10
+ export * from "./types/server";
11
+ export * from "./widgets";
@@ -0,0 +1,75 @@
1
+ import { ValidationError } from "./errors";
2
+
3
+ const encoder = new TextEncoder();
4
+ const decoder = new TextDecoder("utf-8", { fatal: true });
5
+
6
+ export type JsonValue =
7
+ | string
8
+ | number
9
+ | boolean
10
+ | null
11
+ | JsonValue[]
12
+ | { [key: string]: JsonValue };
13
+
14
+ export function encodeJsonBytes(value: unknown): Uint8Array {
15
+ let json: string | undefined;
16
+ try {
17
+ json = JSON.stringify(omitUndefinedDeep(value));
18
+ } catch (error) {
19
+ throw new ValidationError("Invalid JSON payload", error);
20
+ }
21
+ if (typeof json !== "string") {
22
+ throw new ValidationError("Invalid JSON payload");
23
+ }
24
+ return encoder.encode(json);
25
+ }
26
+
27
+ export function decodeJsonBytes(input: string | Uint8Array | ArrayBuffer): unknown {
28
+ let text: string;
29
+ try {
30
+ text =
31
+ typeof input === "string"
32
+ ? input
33
+ : input instanceof Uint8Array
34
+ ? decoder.decode(input)
35
+ : decoder.decode(new Uint8Array(input));
36
+ } catch (error) {
37
+ throw new ValidationError("Invalid JSON payload", error);
38
+ }
39
+
40
+ try {
41
+ return JSON.parse(text);
42
+ } catch (error) {
43
+ throw new ValidationError("Invalid JSON payload", error);
44
+ }
45
+ }
46
+
47
+ export function omitUndefinedDeep(value: unknown): unknown {
48
+ if (Array.isArray(value)) {
49
+ return value.map((item) => omitUndefinedDeep(item));
50
+ }
51
+
52
+ if (value && typeof value === "object" && !(value instanceof Date)) {
53
+ const result: Record<string, unknown> = {};
54
+ for (const [key, child] of Object.entries(value)) {
55
+ if (child !== undefined) {
56
+ result[key] = omitUndefinedDeep(child);
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+
62
+ return value;
63
+ }
64
+
65
+ export function parseDate(value: string | Date): Date {
66
+ const date = value instanceof Date ? value : new Date(value);
67
+ if (Number.isNaN(date.valueOf())) {
68
+ throw new ValidationError(`Invalid datetime: ${String(value)}`);
69
+ }
70
+ return date;
71
+ }
72
+
73
+ export function serializeDate(value: string | Date): string {
74
+ return parseDate(value).toISOString();
75
+ }