@cuylabs/agent-http 0.1.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.
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # @cuylabs/agent-http
2
+
3
+ HTTP streaming adapter for [@cuylabs/agent-core](https://github.com/cuylabs-ai/agents-ts/tree/main/packages/agent-core). Bridges agent events to AI SDK v6 UIMessageStream for use with `useChat()`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @cuylabs/agent-http @cuylabs/agent-core
9
+ # or
10
+ pnpm add @cuylabs/agent-http @cuylabs/agent-core
11
+ ```
12
+
13
+ You'll also need at least one AI SDK provider:
14
+
15
+ ```bash
16
+ npm install @ai-sdk/openai
17
+ # or @ai-sdk/anthropic, @ai-sdk/google
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ // app/api/chat/route.ts (Next.js)
24
+ import { createAgent } from "@cuylabs/agent-core";
25
+ import { createAgentStreamResponse } from "@cuylabs/agent-http";
26
+ import { anthropic } from "@ai-sdk/anthropic";
27
+
28
+ const agent = createAgent({
29
+ model: anthropic("claude-sonnet-4-20250514"),
30
+ systemPrompt: "You are a helpful assistant.",
31
+ });
32
+
33
+ export async function POST(req: Request) {
34
+ const { messages, id } = await req.json();
35
+ const lastMessage = messages[messages.length - 1];
36
+
37
+ return createAgentStreamResponse(agent, {
38
+ sessionId: id,
39
+ message: lastMessage.content,
40
+ });
41
+ }
42
+ ```
43
+
44
+ Then use with AI SDK's `useChat()`:
45
+
46
+ ```tsx
47
+ "use client";
48
+ import { useChat } from "@ai-sdk/react";
49
+
50
+ export function Chat() {
51
+ const { messages, input, handleInputChange, handleSubmit } = useChat({
52
+ api: "/api/chat",
53
+ });
54
+
55
+ return (
56
+ <div>
57
+ {messages.map((m) => (
58
+ <div key={m.id}>
59
+ {m.role}: {m.content}
60
+ </div>
61
+ ))}
62
+ <form onSubmit={handleSubmit}>
63
+ <input value={input} onChange={handleInputChange} />
64
+ <button type="submit">Send</button>
65
+ </form>
66
+ </div>
67
+ );
68
+ }
69
+ ```
70
+
71
+ ## API
72
+
73
+ ### createAgentStreamResponse
74
+
75
+ Creates an HTTP Response with streaming agent events.
76
+
77
+ ```typescript
78
+ function createAgentStreamResponse(
79
+ agent: Agent,
80
+ options: AgentStreamOptions
81
+ ): Response;
82
+
83
+ interface AgentStreamOptions {
84
+ sessionId: string;
85
+ message: string;
86
+ abortSignal?: AbortSignal;
87
+ system?: string;
88
+ }
89
+ ```
90
+
91
+ ### createAgentStream
92
+
93
+ Lower-level API that returns a ReadableStream.
94
+
95
+ ```typescript
96
+ function createAgentStream(
97
+ agent: Agent,
98
+ options: AgentStreamOptions
99
+ ): ReturnType<typeof createUIMessageStream>;
100
+ ```
101
+
102
+ ## License
103
+
104
+ Apache-2.0
@@ -0,0 +1,89 @@
1
+ import { Agent } from '@cuylabs/agent-core';
2
+ export { AgentEvent } from '@cuylabs/agent-core';
3
+ import { createUIMessageStream } from 'ai';
4
+ export { createUIMessageStream, createUIMessageStreamResponse } from 'ai';
5
+
6
+ /**
7
+ * @cuylabs/agent-http
8
+ *
9
+ * HTTP streaming adapter for @cuylabs/agent-core.
10
+ * Bridges agent events to AI SDK v6 compatible UIMessageStream for use with useChat().
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // In a Next.js API route
15
+ * import { createAgent } from "@cuylabs/agent-core";
16
+ * import { createAgentStreamResponse } from "@cuylabs/agent-http";
17
+ * import { anthropic } from "@ai-sdk/anthropic";
18
+ *
19
+ * const agent = createAgent({
20
+ * model: anthropic("claude-sonnet-4-20250514"),
21
+ * systemPrompt: "You are a helpful assistant.",
22
+ * });
23
+ *
24
+ * export async function POST(req: Request) {
25
+ * const { messages, id } = await req.json();
26
+ * const lastMessage = messages[messages.length - 1];
27
+ *
28
+ * return createAgentStreamResponse(agent, {
29
+ * sessionId: id,
30
+ * message: lastMessage.content,
31
+ * });
32
+ * }
33
+ * ```
34
+ *
35
+ * @packageDocumentation
36
+ */
37
+
38
+ /**
39
+ * Options for creating an agent stream response
40
+ */
41
+ interface AgentStreamOptions {
42
+ /** Session ID for conversation history */
43
+ sessionId: string;
44
+ /** User message to send */
45
+ message: string;
46
+ /** Optional abort signal */
47
+ abortSignal?: AbortSignal;
48
+ /** Optional system prompt override for this message */
49
+ system?: string;
50
+ /** Callback when streaming completes - use for persistence */
51
+ onFinish?: (result: {
52
+ response: string;
53
+ messageId?: string;
54
+ }) => void | Promise<void>;
55
+ }
56
+ /**
57
+ * Create an HTTP streaming response from an agent.
58
+ *
59
+ * This bridges @cuylabs/agent-core's event-based streaming to the
60
+ * AI SDK v6 UIMessageStream format that useChat() understands.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // Next.js API route
65
+ * export async function POST(req: Request) {
66
+ * const { messages, id } = await req.json();
67
+ * const lastMessage = messages[messages.length - 1];
68
+ *
69
+ * return createAgentStreamResponse(agent, {
70
+ * sessionId: id,
71
+ * message: lastMessage.content,
72
+ * onFinish: async ({ response }) => {
73
+ * // Persist the response to database
74
+ * await saveMessage({ chatId: id, content: response, role: "assistant" });
75
+ * },
76
+ * });
77
+ * }
78
+ * ```
79
+ */
80
+ declare function createAgentStreamResponse(agent: Agent, options: AgentStreamOptions): Response;
81
+ /**
82
+ * Create a ReadableStream of UIMessageChunks from agent events.
83
+ *
84
+ * Lower-level than createAgentStreamResponse - useful when you need
85
+ * to compose streams or add custom middleware.
86
+ */
87
+ declare function createAgentStream(agent: Agent, options: AgentStreamOptions): ReturnType<typeof createUIMessageStream>;
88
+
89
+ export { type AgentStreamOptions, createAgentStream, createAgentStreamResponse };
package/dist/index.js ADDED
@@ -0,0 +1,167 @@
1
+ // src/index.ts
2
+ import {
3
+ createUIMessageStream,
4
+ createUIMessageStreamResponse
5
+ } from "ai";
6
+ var idCounter = 0;
7
+ function generatePartId() {
8
+ return `part-${Date.now()}-${++idCounter}`;
9
+ }
10
+ async function writeAgentEventsToStream(agent, options, writer) {
11
+ let currentTextId = null;
12
+ let currentReasoningId = null;
13
+ let fullResponse = "";
14
+ for await (const event of agent.chat(options.sessionId, options.message, {
15
+ abort: options.abortSignal,
16
+ system: options.system
17
+ })) {
18
+ switch (event.type) {
19
+ // Text streaming
20
+ case "text-start":
21
+ currentTextId = generatePartId();
22
+ writer.write({
23
+ type: "text-start",
24
+ id: currentTextId
25
+ });
26
+ break;
27
+ case "text-delta":
28
+ if (currentTextId) {
29
+ fullResponse += event.text;
30
+ writer.write({
31
+ type: "text-delta",
32
+ id: currentTextId,
33
+ delta: event.text
34
+ });
35
+ }
36
+ break;
37
+ case "text-end":
38
+ if (currentTextId) {
39
+ writer.write({
40
+ type: "text-end",
41
+ id: currentTextId
42
+ });
43
+ currentTextId = null;
44
+ }
45
+ break;
46
+ // Reasoning/thinking streaming
47
+ case "reasoning-start":
48
+ currentReasoningId = generatePartId();
49
+ writer.write({
50
+ type: "reasoning-start",
51
+ id: currentReasoningId
52
+ });
53
+ break;
54
+ case "reasoning-delta":
55
+ if (currentReasoningId) {
56
+ writer.write({
57
+ type: "reasoning-delta",
58
+ id: currentReasoningId,
59
+ delta: event.text
60
+ });
61
+ }
62
+ break;
63
+ case "reasoning-end":
64
+ if (currentReasoningId) {
65
+ writer.write({
66
+ type: "reasoning-end",
67
+ id: currentReasoningId
68
+ });
69
+ currentReasoningId = null;
70
+ }
71
+ break;
72
+ // Tool invocations
73
+ case "tool-start":
74
+ writer.write({
75
+ type: "tool-input-available",
76
+ toolCallId: event.toolCallId,
77
+ toolName: event.toolName,
78
+ input: event.input
79
+ });
80
+ break;
81
+ case "tool-result":
82
+ writer.write({
83
+ type: "tool-output-available",
84
+ toolCallId: event.toolCallId,
85
+ output: event.result
86
+ });
87
+ break;
88
+ case "tool-error":
89
+ writer.write({
90
+ type: "tool-output-available",
91
+ toolCallId: event.toolCallId,
92
+ output: { error: event.error, isError: true }
93
+ });
94
+ break;
95
+ // Error handling
96
+ case "error":
97
+ writer.write({
98
+ type: "error",
99
+ errorText: event.error.message
100
+ });
101
+ break;
102
+ // Step and message lifecycle events - no direct mapping in AI SDK v6
103
+ // These can be exposed via custom data parts if needed
104
+ case "step-start":
105
+ case "step-finish":
106
+ case "message":
107
+ case "complete":
108
+ break;
109
+ // Events we don't need to stream
110
+ case "status":
111
+ case "retry":
112
+ case "doom-loop":
113
+ case "context-overflow":
114
+ case "computer-call":
115
+ case "computer-result":
116
+ case "approval-request":
117
+ case "approval-resolved":
118
+ break;
119
+ }
120
+ }
121
+ return fullResponse;
122
+ }
123
+ function createAgentStreamResponse(agent, options) {
124
+ let fullResponse = "";
125
+ const stream = createUIMessageStream({
126
+ execute: async ({ writer }) => {
127
+ fullResponse = await writeAgentEventsToStream(agent, options, writer);
128
+ },
129
+ onFinish: async ({ responseMessage }) => {
130
+ if (options.onFinish) {
131
+ await options.onFinish({
132
+ response: fullResponse,
133
+ messageId: responseMessage?.id
134
+ });
135
+ }
136
+ },
137
+ onError: (error) => {
138
+ return error instanceof Error ? error.message : "Unknown error";
139
+ }
140
+ });
141
+ return createUIMessageStreamResponse({ stream });
142
+ }
143
+ function createAgentStream(agent, options) {
144
+ let fullResponse = "";
145
+ return createUIMessageStream({
146
+ execute: async ({ writer }) => {
147
+ fullResponse = await writeAgentEventsToStream(agent, options, writer);
148
+ },
149
+ onFinish: async ({ responseMessage }) => {
150
+ if (options.onFinish) {
151
+ await options.onFinish({
152
+ response: fullResponse,
153
+ messageId: responseMessage?.id
154
+ });
155
+ }
156
+ },
157
+ onError: (error) => {
158
+ return error instanceof Error ? error.message : "Unknown error";
159
+ }
160
+ });
161
+ }
162
+ export {
163
+ createAgentStream,
164
+ createAgentStreamResponse,
165
+ createUIMessageStream,
166
+ createUIMessageStreamResponse
167
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@cuylabs/agent-http",
3
+ "version": "0.1.0",
4
+ "description": "HTTP streaming adapter for @cuylabs/agent-core - bridges agent events to AI SDK compatible streams",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm --dts --clean",
21
+ "dev": "tsup src/index.ts --format esm --dts --watch",
22
+ "typecheck": "tsc --noEmit",
23
+ "clean": "rm -rf dist"
24
+ },
25
+ "dependencies": {
26
+ "@cuylabs/agent-core": "workspace:*"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^22.0.0",
30
+ "tsup": "^8.0.0",
31
+ "typescript": "^5.7.0"
32
+ },
33
+ "peerDependencies": {
34
+ "ai": "^6.0.0"
35
+ },
36
+ "keywords": [
37
+ "agent",
38
+ "ai",
39
+ "http",
40
+ "streaming",
41
+ "vercel-ai-sdk"
42
+ ],
43
+ "author": "cuylabs",
44
+ "license": "Apache-2.0",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/cuylabs-ai/agents-ts.git",
48
+ "directory": "packages/agent-http"
49
+ }
50
+ }