@economic/agents 0.0.1-alpha.26 → 0.0.1-alpha.27
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 +28 -0
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +20 -16
- package/dist/react.d.mts +25 -0
- package/dist/react.mjs +38 -0
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ Base class and utilities for building LLM chat agents on Cloudflare's Agents SDK
|
|
|
6
6
|
npm install @economic/agents ai @cloudflare/ai-chat agents
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
+
For React UIs that import `@economic/agents/react`, also install `react`. It is an **optional** peer of the package so Workers-only installs are not required to add React. The hook still needs `agents`, `ai`, and `@cloudflare/ai-chat` at runtime (same as the Cloudflare SDK imports it wraps).
|
|
10
|
+
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
## Overview
|
|
@@ -15,9 +17,35 @@ npm install @economic/agents ai @cloudflare/ai-chat agents
|
|
|
15
17
|
- **`AIChatAgent`** — an abstract Cloudflare Durable Object base class. Implement `onChatMessage`, call `this.buildLLMParams()`, and pass the result to `streamText` from the AI SDK.
|
|
16
18
|
- **`guard`** — optional TypeScript 5+ method decorator for `onChatMessage`. Runs your function with `options.body`; return a `Response` to short-circuit (e.g. auth), or nothing to continue.
|
|
17
19
|
- **`buildLLMParams`** — the standalone version of the above, for use outside of `AIChatAgent` or in custom agent implementations.
|
|
20
|
+
- **`useAIChatAgent`** (subpath `@economic/agents/react`) — React hook that wraps `useAgent` (`agents/react`) and `useAgentChat` (`@cloudflare/ai-chat/react`). Connection status is **callback-only** (`onConnectionStatusChange`). On WebSocket close codes **`>= 3000`**, the hook calls `agent.close()` to stop reconnection, then forwards `onClose` (use `event.reason` for the server message). Pass-through **`onOpen`**, **`onClose`**, **`onError`** are supported.
|
|
18
21
|
|
|
19
22
|
Skills and compaction are AI SDK concerns — they control what goes to the LLM. The CF layer is responsible for WebSockets, Durable Objects, and message persistence. These are kept separate.
|
|
20
23
|
|
|
24
|
+
### React client
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { useAIChatAgent, type AgentConnectionStatus } from "@economic/agents/react";
|
|
28
|
+
import { useState } from "react";
|
|
29
|
+
|
|
30
|
+
const [connectionStatus, setConnectionStatus] = useState<AgentConnectionStatus>("connecting");
|
|
31
|
+
|
|
32
|
+
const { agent, chat } = useAIChatAgent({
|
|
33
|
+
agent: "MyAgent",
|
|
34
|
+
host: "localhost:8787",
|
|
35
|
+
chatId: "session-id",
|
|
36
|
+
toolContext: {},
|
|
37
|
+
connectionParams: { userId: "…" },
|
|
38
|
+
onConnectionStatusChange: setConnectionStatus,
|
|
39
|
+
onOpen: (event) => {},
|
|
40
|
+
onClose: (event) => {},
|
|
41
|
+
onError: (event) => {},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const { messages, sendMessage, status, stop } = chat;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Server-side agent code still imports only from `@economic/agents`; the `/react` entry is a separate build output and does not pull Workers runtime code into the client bundle.
|
|
48
|
+
|
|
21
49
|
---
|
|
22
50
|
|
|
23
51
|
## Quick start
|
package/dist/index.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import { AIChatAgent as AIChatAgent$1, OnChatMessageOptions } from "@cloudflare/
|
|
|
2
2
|
import { LanguageModel, ToolSet, UIMessage, generateText, streamText } from "ai";
|
|
3
3
|
import { Connection, ConnectionContext } from "agents";
|
|
4
4
|
|
|
5
|
-
//#region src/features/skills/index.d.ts
|
|
5
|
+
//#region src/server/features/skills/index.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* A named group of related tools that can be loaded together on demand.
|
|
8
8
|
*
|
|
@@ -23,7 +23,7 @@ interface Skill {
|
|
|
23
23
|
tools: ToolSet;
|
|
24
24
|
}
|
|
25
25
|
//#endregion
|
|
26
|
-
//#region src/llm.d.ts
|
|
26
|
+
//#region src/server/llm.d.ts
|
|
27
27
|
type LLMParams = Parameters<typeof streamText>[0] & Parameters<typeof generateText>[0];
|
|
28
28
|
type BuildLLMParamsConfig = Omit<LLMParams, "messages" | "experimental_context" | "abortSignal"> & {
|
|
29
29
|
/** CF options object — extracts `abortSignal` and `experimental_context` (from `body`). */options: OnChatMessageOptions | undefined; /** Conversation history (`this.messages`). Converted to `ModelMessage[]` internally. */
|
|
@@ -66,7 +66,7 @@ type BuildLLMParamsConfig = Omit<LLMParams, "messages" | "experimental_context"
|
|
|
66
66
|
*/
|
|
67
67
|
declare function buildLLMParams(config: BuildLLMParamsConfig): Promise<LLMParams>;
|
|
68
68
|
//#endregion
|
|
69
|
-
//#region src/agents/AIChatAgent.d.ts
|
|
69
|
+
//#region src/server/agents/AIChatAgent.d.ts
|
|
70
70
|
/**
|
|
71
71
|
* Base class for Cloudflare Agents SDK chat agents with lazy skill loading
|
|
72
72
|
* and built-in audit logging.
|
|
@@ -98,13 +98,13 @@ declare abstract class AIChatAgent<Env extends Cloudflare.Env = Cloudflare.Env>
|
|
|
98
98
|
* to `buildLLMParams` rather than omitting or nulling out `fastModel`.
|
|
99
99
|
*/
|
|
100
100
|
protected abstract fastModel: LanguageModel;
|
|
101
|
+
onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
|
|
101
102
|
/**
|
|
102
103
|
* Resolves the D1 database binding and userId required for all D1 writes.
|
|
103
104
|
* Returns null and silently no-ops if AGENT_DB is not bound.
|
|
104
105
|
* Returns null and logs an error if userId is missing.
|
|
105
106
|
*/
|
|
106
107
|
private resolveD1Context;
|
|
107
|
-
onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
|
|
108
108
|
/**
|
|
109
109
|
* Returns all conversations for the current user.
|
|
110
110
|
*/
|
|
@@ -178,7 +178,7 @@ declare abstract class AIChatAgent<Env extends Cloudflare.Env = Cloudflare.Env>
|
|
|
178
178
|
}): Promise<void>;
|
|
179
179
|
}
|
|
180
180
|
//#endregion
|
|
181
|
-
//#region src/types.d.ts
|
|
181
|
+
//#region src/server/types.d.ts
|
|
182
182
|
/**
|
|
183
183
|
* The context object available throughout an agent's lifetime — passed via
|
|
184
184
|
* `experimental_context` to tool `execute` functions. Contains the typed
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AIChatAgent as AIChatAgent$1 } from "@cloudflare/ai-chat";
|
|
2
2
|
import { Output, convertToModelMessages, generateText, jsonSchema, stepCountIs, tool } from "ai";
|
|
3
3
|
import { callable } from "agents";
|
|
4
|
-
//#region src/features/skills/index.ts
|
|
4
|
+
//#region src/server/features/skills/index.ts
|
|
5
5
|
/** Creates the `skill_state` table in DO SQLite if it does not exist yet. */
|
|
6
6
|
function ensureSkillTable(sql) {
|
|
7
7
|
sql`CREATE TABLE IF NOT EXISTS skill_state (id INTEGER PRIMARY KEY, active_skills TEXT NOT NULL DEFAULT '[]')`;
|
|
@@ -279,7 +279,7 @@ async function compactIfNeeded(messages, model, tailSize) {
|
|
|
279
279
|
return compactMessages(messages, model, tailSize);
|
|
280
280
|
}
|
|
281
281
|
//#endregion
|
|
282
|
-
//#region src/llm.ts
|
|
282
|
+
//#region src/server/llm.ts
|
|
283
283
|
/**
|
|
284
284
|
* Builds the parameter object for a Vercel AI SDK `streamText` or `generateText` call.
|
|
285
285
|
*
|
|
@@ -328,7 +328,7 @@ async function buildLLMParams(config) {
|
|
|
328
328
|
};
|
|
329
329
|
}
|
|
330
330
|
//#endregion
|
|
331
|
-
//#region src/features/audit/index.ts
|
|
331
|
+
//#region src/server/features/audit/index.ts
|
|
332
332
|
/**
|
|
333
333
|
* Inserts a single audit event row into the shared `audit_events` D1 table.
|
|
334
334
|
*
|
|
@@ -370,7 +370,7 @@ function extractMessageText(msg) {
|
|
|
370
370
|
return msg.parts.filter((p) => p.type === "text").map((p) => p.text).join(" ").trim();
|
|
371
371
|
}
|
|
372
372
|
//#endregion
|
|
373
|
-
//#region src/features/conversations/index.ts
|
|
373
|
+
//#region src/server/features/conversations/index.ts
|
|
374
374
|
/**
|
|
375
375
|
* Records a conversation row in the `conversations` D1 table.
|
|
376
376
|
*
|
|
@@ -459,7 +459,7 @@ async function generateConversationSummary(db, durableObjectId, messages, model)
|
|
|
459
459
|
await updateConversationSummary(db, durableObjectId, title, summary);
|
|
460
460
|
}
|
|
461
461
|
//#endregion
|
|
462
|
-
//#region src/agents/AIChatAgent.ts
|
|
462
|
+
//#region src/server/agents/AIChatAgent.ts
|
|
463
463
|
/**
|
|
464
464
|
* Base class for Cloudflare Agents SDK chat agents with lazy skill loading
|
|
465
465
|
* and built-in audit logging.
|
|
@@ -478,6 +478,16 @@ var AIChatAgent = class extends AIChatAgent$1 {
|
|
|
478
478
|
* Undefined if the client did not include `userId`.
|
|
479
479
|
*/
|
|
480
480
|
_userId;
|
|
481
|
+
async onConnect(connection, ctx) {
|
|
482
|
+
super.onConnect(connection, ctx);
|
|
483
|
+
const userId = new URL(ctx.request.url).searchParams.get("userId");
|
|
484
|
+
if (!userId) {
|
|
485
|
+
console.error("[AIChatAgent] Connection rejected: userId query parameter is required");
|
|
486
|
+
connection.close(3e3, "userId query parameter is required");
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
this._userId = userId;
|
|
490
|
+
}
|
|
481
491
|
/**
|
|
482
492
|
* Resolves the D1 database binding and userId required for all D1 writes.
|
|
483
493
|
* Returns null and silently no-ops if AGENT_DB is not bound.
|
|
@@ -485,9 +495,12 @@ var AIChatAgent = class extends AIChatAgent$1 {
|
|
|
485
495
|
*/
|
|
486
496
|
resolveD1Context() {
|
|
487
497
|
const db = this.env.AGENT_DB;
|
|
488
|
-
if (!db)
|
|
498
|
+
if (!db) {
|
|
499
|
+
console.error("[AIChatAgent] Skipping logging: D1 database not found");
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
489
502
|
if (!this._userId) {
|
|
490
|
-
console.error("[AIChatAgent]
|
|
503
|
+
console.error("[AIChatAgent] Skipping logging: userId not provided in connection query params");
|
|
491
504
|
return null;
|
|
492
505
|
}
|
|
493
506
|
return {
|
|
@@ -495,15 +508,6 @@ var AIChatAgent = class extends AIChatAgent$1 {
|
|
|
495
508
|
userId: this._userId
|
|
496
509
|
};
|
|
497
510
|
}
|
|
498
|
-
async onConnect(connection, ctx) {
|
|
499
|
-
super.onConnect(connection, ctx);
|
|
500
|
-
const userId = new URL(ctx.request.url).searchParams.get("userId");
|
|
501
|
-
if (!userId) {
|
|
502
|
-
connection.close(3e3, "userId query parameter is required");
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
this._userId = userId;
|
|
506
|
-
}
|
|
507
511
|
/**
|
|
508
512
|
* Returns all conversations for the current user.
|
|
509
513
|
*/
|
package/dist/react.d.mts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { UIMessage } from "ai";
|
|
2
|
+
import { useAgentChat } from "@cloudflare/ai-chat/react";
|
|
3
|
+
import { useAgent } from "agents/react";
|
|
4
|
+
|
|
5
|
+
//#region src/client/index.d.ts
|
|
6
|
+
type AgentConnectionStatus = "connecting" | "connected" | "disconnected" | "unauthorized";
|
|
7
|
+
interface UseAIChatAgentOptions {
|
|
8
|
+
agent: string;
|
|
9
|
+
host: string;
|
|
10
|
+
basePath?: string;
|
|
11
|
+
chatId: string;
|
|
12
|
+
toolContext?: Record<string, unknown>;
|
|
13
|
+
connectionParams?: Record<string, string>;
|
|
14
|
+
onConnectionStatusChange?: (status: AgentConnectionStatus) => void;
|
|
15
|
+
onOpen?: (event: Event) => void;
|
|
16
|
+
onClose?: (event: CloseEvent) => void;
|
|
17
|
+
onError?: (event: ErrorEvent) => void;
|
|
18
|
+
}
|
|
19
|
+
type UseAIChatAgentResult = {
|
|
20
|
+
agent: ReturnType<typeof useAgent>;
|
|
21
|
+
chat: ReturnType<typeof useAgentChat<unknown, UIMessage>>;
|
|
22
|
+
};
|
|
23
|
+
declare function useAIChatAgent(options: UseAIChatAgentOptions): UseAIChatAgentResult;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { AgentConnectionStatus, UseAIChatAgentOptions, useAIChatAgent };
|
package/dist/react.mjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useAgentChat } from "@cloudflare/ai-chat/react";
|
|
2
|
+
import { useAgent } from "agents/react";
|
|
3
|
+
//#region src/client/index.ts
|
|
4
|
+
function useAIChatAgent(options) {
|
|
5
|
+
const { agent: agentName, host, basePath, chatId, toolContext, connectionParams, onConnectionStatusChange, onOpen: onOpenProp, onClose: onCloseProp, onError: onErrorProp } = options;
|
|
6
|
+
const agent = useAgent({
|
|
7
|
+
agent: agentName,
|
|
8
|
+
host,
|
|
9
|
+
basePath,
|
|
10
|
+
name: chatId,
|
|
11
|
+
query: connectionParams ?? {},
|
|
12
|
+
onOpen: (event) => {
|
|
13
|
+
onConnectionStatusChange?.("connected");
|
|
14
|
+
onOpenProp?.(event);
|
|
15
|
+
},
|
|
16
|
+
onClose: (event) => {
|
|
17
|
+
if (event.code >= 3e3) {
|
|
18
|
+
onConnectionStatusChange?.("unauthorized");
|
|
19
|
+
agent.close();
|
|
20
|
+
onCloseProp?.(event);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
onConnectionStatusChange?.("disconnected");
|
|
24
|
+
onCloseProp?.(event);
|
|
25
|
+
},
|
|
26
|
+
onError: onErrorProp
|
|
27
|
+
});
|
|
28
|
+
return {
|
|
29
|
+
agent,
|
|
30
|
+
chat: useAgentChat({
|
|
31
|
+
agent,
|
|
32
|
+
body: toolContext ?? {},
|
|
33
|
+
getInitialMessages: null
|
|
34
|
+
})
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { useAIChatAgent };
|
package/package.json
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@economic/agents",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.27",
|
|
4
4
|
"description": "A starter for creating a TypeScript package.",
|
|
5
|
-
"homepage": "https://github.com/author/library#readme",
|
|
6
|
-
"bugs": {
|
|
7
|
-
"url": "https://github.com/author/library/issues"
|
|
8
|
-
},
|
|
9
5
|
"license": "MIT",
|
|
10
|
-
"author": "Author Name <author.name@mail.com>",
|
|
11
|
-
"repository": {
|
|
12
|
-
"type": "git",
|
|
13
|
-
"url": "git+https://github.com/author/library.git"
|
|
14
|
-
},
|
|
15
6
|
"files": [
|
|
16
7
|
"dist",
|
|
17
8
|
"schema"
|
|
@@ -19,6 +10,7 @@
|
|
|
19
10
|
"type": "module",
|
|
20
11
|
"exports": {
|
|
21
12
|
".": "./dist/index.mjs",
|
|
13
|
+
"./react": "./dist/react.mjs",
|
|
22
14
|
"./package.json": "./package.json"
|
|
23
15
|
},
|
|
24
16
|
"scripts": {
|
|
@@ -33,9 +25,11 @@
|
|
|
33
25
|
"@cloudflare/ai-chat": "^0.1.9",
|
|
34
26
|
"@cloudflare/workers-types": "^4.20260317.1",
|
|
35
27
|
"@types/node": "^22.0.0",
|
|
28
|
+
"@types/react": "^19.0.0",
|
|
36
29
|
"@typescript/native-preview": "7.0.0-dev.20260316.1",
|
|
37
30
|
"ai": "^6.0.134",
|
|
38
31
|
"bumpp": "^11.0.1",
|
|
32
|
+
"react": "^19.0.0",
|
|
39
33
|
"tsdown": "^0.21.4",
|
|
40
34
|
"typescript": "^5.9.3",
|
|
41
35
|
"vitest": "^4.1.0"
|
|
@@ -43,6 +37,12 @@
|
|
|
43
37
|
"peerDependencies": {
|
|
44
38
|
"@cloudflare/ai-chat": "^0.1.0",
|
|
45
39
|
"agents": "^0.7.6",
|
|
46
|
-
"ai": "^6.0.0"
|
|
40
|
+
"ai": "^6.0.0",
|
|
41
|
+
"react": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"peerDependenciesMeta": {
|
|
44
|
+
"react": {
|
|
45
|
+
"optional": true
|
|
46
|
+
}
|
|
47
47
|
}
|
|
48
48
|
}
|