@economic/agents 2.2.4 → 2.3.1
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 +198 -27
- package/dist/index.d.mts +35 -25
- package/dist/index.mjs +83 -69
- package/dist/providers.d.mts +37 -0
- package/dist/providers.mjs +46 -0
- package/dist/v1.d.mts +3 -3
- package/dist/v1.mjs +16 -16
- package/package.json +14 -13
- /package/dist/{route-agent-request-DmwIOBJS.d.mts → route-agent-request-CbjgNl2B.d.mts} +0 -0
- /package/dist/{route-agent-request-DKDvDYnR.mjs → route-agent-request-lVm3eus2.mjs} +0 -0
package/README.md
CHANGED
|
@@ -4,27 +4,87 @@ Our agents SDK for building AI agents on Cloudflare Workers. Each agent is a Dur
|
|
|
4
4
|
|
|
5
5
|
React client: [`@economic/agents-react`](../react/README.md).
|
|
6
6
|
|
|
7
|
-
> The v1 (`@economic/agents/v1`) API (`ChatAgentHarness`, `buildLLMParams`) is deprecated, kept only for migration, and removed in v3.
|
|
7
|
+
> The v1 (`@economic/agents/v1`) API (`ChatAgentHarness`, `buildLLMParams`) is deprecated, kept only for migration, and will be removed in v3.
|
|
8
8
|
|
|
9
9
|
## The three classes
|
|
10
10
|
|
|
11
11
|
- **`Agent`** — the core. Runs the agent loop and keeps message history. Drive it over a WebSocket or programmatically (schedule, alarm, RPC). Most agents stop here.
|
|
12
|
-
- **`ChatAgent`** — `Agent` plus
|
|
13
|
-
- **`Assistant`** — per-user shell over `ChatAgent`: create/list/delete
|
|
12
|
+
- **`ChatAgent`** — `Agent` plus chat features: compaction and message feedback.
|
|
13
|
+
- **`Assistant`** — per-user shell over `ChatAgent`: create/list/delete chats, titles, summaries, retention.
|
|
14
14
|
|
|
15
15
|
```
|
|
16
16
|
Agent ← LLM + tools + skills (most agents stop here)
|
|
17
|
-
└─ ChatAgent ← one persistent
|
|
18
|
-
└─ Assistant ← one user, many
|
|
17
|
+
└─ ChatAgent ← one persistent chat
|
|
18
|
+
└─ Assistant ← one user, many chats
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Install
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
|
-
npm install @economic/agents
|
|
24
|
+
npm install @economic/agents @cloudflare/think agents
|
|
25
|
+
npm install -D wrangler vite @cloudflare/vite-plugin @cloudflare/worker-bundler
|
|
25
26
|
```
|
|
26
27
|
|
|
27
|
-
`
|
|
28
|
+
`@economic/agents` provides the agent runtime. Worker apps install the Cloudflare/Agents host packages they import directly, including the Vite plugin stack used by native skills.
|
|
29
|
+
|
|
30
|
+
## Providers
|
|
31
|
+
|
|
32
|
+
`@economic/agents/providers` ships two pre-configured AI SDK providers that route through **Cloudflare AI Gateway** to Google Vertex AI and Google AI Studio. Both providers require `@ai-sdk/anthropic` and `@ai-sdk/google` to be installed.
|
|
33
|
+
|
|
34
|
+
### Anthropic via Vertex AI
|
|
35
|
+
|
|
36
|
+
Routes Claude models through Cloudflare AI Gateway → Google Vertex AI.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { createAnthropicVertexProvider } from "@economic/agents/providers";
|
|
40
|
+
|
|
41
|
+
const anthropic = createAnthropicVertexProvider({
|
|
42
|
+
cloudflareAccountId: env.CLOUDFLARE_ACCOUNT_ID,
|
|
43
|
+
cloudflareAiGatewayId: env.AI_GATEWAY_ID,
|
|
44
|
+
cloudflareApiToken: env.CLOUDFLARE_API_TOKEN,
|
|
45
|
+
googleCloudProjectId: env.GOOGLE_CLOUD_PROJECT_ID,
|
|
46
|
+
location: "europe-west1", // optional, defaults to "europe-west1"
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
getModel() {
|
|
50
|
+
return anthropic("claude-3-7-sonnet-20250219");
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Gemini via Vertex AI
|
|
55
|
+
|
|
56
|
+
Routes Gemini models through Cloudflare AI Gateway → Google Vertex AI.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { createGeminiProvider } from "@economic/agents/providers";
|
|
60
|
+
|
|
61
|
+
const gemini = createGeminiProvider({
|
|
62
|
+
cloudflareAccountId: env.CLOUDFLARE_ACCOUNT_ID,
|
|
63
|
+
cloudflareAiGatewayId: env.AI_GATEWAY_ID,
|
|
64
|
+
cloudflareApiToken: env.CLOUDFLARE_API_TOKEN,
|
|
65
|
+
googleCloudProjectId: env.GOOGLE_CLOUD_PROJECT_ID,
|
|
66
|
+
location: "europe-west1", // optional, defaults to "europe-west1"
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
getModel() {
|
|
70
|
+
return gemini("gemini-2.0-flash");
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Both together
|
|
75
|
+
|
|
76
|
+
`createAiGatewayVertexProviders` returns both providers from a single options object:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { createAiGatewayVertexProviders } from "@economic/agents/providers";
|
|
80
|
+
|
|
81
|
+
const { anthropic, gemini } = createAiGatewayVertexProviders({
|
|
82
|
+
cloudflareAccountId: env.CLOUDFLARE_ACCOUNT_ID,
|
|
83
|
+
cloudflareAiGatewayId: env.AI_GATEWAY_ID,
|
|
84
|
+
cloudflareApiToken: env.CLOUDFLARE_API_TOKEN,
|
|
85
|
+
googleCloudProjectId: env.GOOGLE_CLOUD_PROJECT_ID,
|
|
86
|
+
});
|
|
87
|
+
```
|
|
28
88
|
|
|
29
89
|
## Quick start: an agent
|
|
30
90
|
|
|
@@ -125,7 +185,7 @@ You can also drive turns server-side — call `saveMessages` from a schedule or
|
|
|
125
185
|
|
|
126
186
|
## A chat agent
|
|
127
187
|
|
|
128
|
-
For a chat UI, extend `ChatAgent` instead of `Agent`. It adds compaction and message feedback, and connects to a browser with [`useChat`](../react/README.md#usechat) — no `Assistant` required. The DO name is the
|
|
188
|
+
For a chat UI, extend `ChatAgent` instead of `Agent`. It adds compaction and message feedback, and connects to a browser with [`useChat`](../react/README.md#usechat) — no `Assistant` required. The DO name is the chat; address one per user, ticket, or whatever fits.
|
|
129
189
|
|
|
130
190
|
```typescript
|
|
131
191
|
import { openai } from "@ai-sdk/openai";
|
|
@@ -158,18 +218,18 @@ const { chat } = useChat({
|
|
|
158
218
|
What `ChatAgent` adds over `Agent`:
|
|
159
219
|
|
|
160
220
|
- **Compaction** — past 100,000 tokens, older messages are summarised (with `getModel()`) while recent ones are kept verbatim. Storage keeps the full history.
|
|
161
|
-
- **Message feedback** — thumbs up/down with an optional comment, in the
|
|
221
|
+
- **Message feedback** — thumbs up/down with an optional comment, in the chat's SQLite (`assistant_messages_feedback`, created automatically):
|
|
162
222
|
|
|
163
223
|
| Method | Description |
|
|
164
224
|
| ---------------------------------------------------- | ------------------------------------------------------------ |
|
|
165
225
|
| `submitMessageFeedback(messageId, rating, comment?)` | `rating` is `1` (up) or `-1` (down). Upserts on the message. |
|
|
166
|
-
| `getMessageFeedback()` | All feedback for the
|
|
226
|
+
| `getMessageFeedback()` | All feedback for the chat, keyed by message id. |
|
|
167
227
|
|
|
168
228
|
Surfaced by the client as `chat.submitMessageFeedback` / `chat.getMessageFeedback`.
|
|
169
229
|
|
|
170
230
|
## Many chats per user: Assistant
|
|
171
231
|
|
|
172
|
-
When one user needs a list of
|
|
232
|
+
When one user needs a list of chats — a sidebar, titles, summaries — add an `Assistant`. It owns the chat list and routes to a `ChatAgent` per chat.
|
|
173
233
|
|
|
174
234
|
```typescript
|
|
175
235
|
import { openai } from "@ai-sdk/openai";
|
|
@@ -182,7 +242,7 @@ export class MyAssistant extends Assistant {
|
|
|
182
242
|
}
|
|
183
243
|
```
|
|
184
244
|
|
|
185
|
-
Bind both classes (binding name = class name) with a `new_sqlite_classes` migration each. On the client, use [`useAssistant`](../react/README.md#useassistant) — it manages the chat list and connects to the active
|
|
245
|
+
Bind both classes (binding name = class name) with a `new_sqlite_classes` migration each. On the client, use [`useAssistant`](../react/README.md#useassistant) — it manages the chat list and connects to the active chat:
|
|
186
246
|
|
|
187
247
|
```tsx
|
|
188
248
|
const { status, chats, assistant, chat } = useAssistant({
|
|
@@ -194,14 +254,14 @@ const { status, chats, assistant, chat } = useAssistant({
|
|
|
194
254
|
|
|
195
255
|
The `Assistant` keeps the chat list in its own SQLite (a `chats` table, created automatically) and exposes three callable methods:
|
|
196
256
|
|
|
197
|
-
| Method | Returns | Description
|
|
198
|
-
| ---------------- | -------- |
|
|
199
|
-
| `createChat()` | `string` | Creates a
|
|
200
|
-
| `getChats()` | `Chat[]` | The user's
|
|
201
|
-
| `deleteChat(id)` | `void` | Deletes a
|
|
257
|
+
| Method | Returns | Description |
|
|
258
|
+
| ---------------- | -------- | ---------------------------------------------- |
|
|
259
|
+
| `createChat()` | `string` | Creates a chat and returns its id. |
|
|
260
|
+
| `getChats()` | `Chat[]` | The user's chats, most recently updated first. |
|
|
261
|
+
| `deleteChat(id)` | `void` | Deletes a chat and its record. |
|
|
202
262
|
|
|
203
|
-
- **Titles and summaries** — generated with `fastModel` after each turn (on the first turn, then refreshed as the
|
|
204
|
-
- **Retention** — inactive
|
|
263
|
+
- **Titles and summaries** — generated with `fastModel` after each turn (on the first turn, then refreshed as the chat grows).
|
|
264
|
+
- **Retention** — inactive chats are deleted after 90 days, via a Durable Object alarm.
|
|
205
265
|
|
|
206
266
|
## Bindings
|
|
207
267
|
|
|
@@ -212,6 +272,7 @@ Checked in `Agent.onStart` when a connection opens:
|
|
|
212
272
|
| `AGENTS_AUDIT_LOGS` | R2 | Yes | Connection rejected if missing. One [audit log](#observability) per turn. |
|
|
213
273
|
| `AGENTS_ANALYTICS` | Analytics Engine | No | Per-turn/per-tool [analytics](#observability). Warns if missing. |
|
|
214
274
|
| `SKILLS_BUCKET` | R2 | No | Source for [remote skills](#remote-skills). |
|
|
275
|
+
| `LOADER` | Worker Loader | No | Enables [native skill scripts](#skill-scripts) when bound. |
|
|
215
276
|
|
|
216
277
|
## Tools
|
|
217
278
|
|
|
@@ -261,7 +322,16 @@ const call_api = tool({
|
|
|
261
322
|
|
|
262
323
|
## Skills
|
|
263
324
|
|
|
264
|
-
|
|
325
|
+
The SDK supports two skill styles:
|
|
326
|
+
|
|
327
|
+
- **Code-defined skills** with the local `skill()` helper.
|
|
328
|
+
- **Native Agent Skills** loaded from `SKILL.md` folders via `agents:skills` or remote sources.
|
|
329
|
+
|
|
330
|
+
Use skills when a capability should be loaded on demand instead of living in the always-on system prompt.
|
|
331
|
+
|
|
332
|
+
### Code-defined skills
|
|
333
|
+
|
|
334
|
+
A code-defined skill bundles markdown `instructions` with optional tools. Only `description` sits in the system prompt; the model loads the instructions and tools on demand.
|
|
265
335
|
|
|
266
336
|
```typescript
|
|
267
337
|
import { skill, tool } from "@economic/agents";
|
|
@@ -293,17 +363,119 @@ Use this skill whenever the user asks about current conditions or a forecast.
|
|
|
293
363
|
|
|
294
364
|
Return skills from `getSkills()`. Add `authorize(ctx)` to gate a skill (and its tools). `skill()` throws if `description` or `instructions` is missing.
|
|
295
365
|
|
|
366
|
+
### Native Agent Skills
|
|
367
|
+
|
|
368
|
+
Native Agent Skills are folders containing a `SKILL.md` file, with optional references and scripts:
|
|
369
|
+
|
|
370
|
+
```text
|
|
371
|
+
src/skills/top-customers-by-revenue/
|
|
372
|
+
├── SKILL.md
|
|
373
|
+
└── scripts/
|
|
374
|
+
└── top-customers.py
|
|
375
|
+
|
|
376
|
+
src/skills/vat-code-review/
|
|
377
|
+
├── SKILL.md
|
|
378
|
+
├── references/
|
|
379
|
+
│ └── accounting-rules.md
|
|
380
|
+
└── scripts/
|
|
381
|
+
└── review-invoice-vat.ts
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Load bundled skills with the Agents Vite plugin:
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import bundledSkills from "agents:skills";
|
|
388
|
+
|
|
389
|
+
getSkills() {
|
|
390
|
+
return [bundledSkills];
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
`SKILL.md` should describe the business workflow, not the implementation mechanism:
|
|
395
|
+
|
|
396
|
+
```md
|
|
397
|
+
---
|
|
398
|
+
name: top-customers-by-revenue
|
|
399
|
+
description: Find the top customers by revenue from customer and sales data.
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
# Top Customers By Revenue
|
|
403
|
+
|
|
404
|
+
1. Call `request` with `{ "endpoint": "customers" }`.
|
|
405
|
+
2. Call `request` with `{ "endpoint": "sales" }`.
|
|
406
|
+
3. Run `scripts/top-customers.py` with the returned customer and sales arrays.
|
|
407
|
+
4. Summarize the top customers by revenue.
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Skill scripts
|
|
411
|
+
|
|
412
|
+
Skill scripts are enabled when a Worker Loader binding named `LOADER` is present:
|
|
413
|
+
|
|
414
|
+
```jsonc
|
|
415
|
+
{
|
|
416
|
+
"worker_loaders": [{ "binding": "LOADER" }],
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
`Agent` automatically creates a script runner when `env.LOADER` is bound. Without the binding, script execution is unavailable.
|
|
421
|
+
|
|
422
|
+
TypeScript scripts use the function-style contract:
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
import type { SkillRunContext } from "@cloudflare/think";
|
|
426
|
+
|
|
427
|
+
export default async function run(input: unknown, ctx: SkillRunContext) {
|
|
428
|
+
const rules = ctx.files["references/accounting-rules.md"];
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
ok: true,
|
|
432
|
+
rulesLoaded: Boolean(rules),
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Python scripts use the path-based Dynamic Workers contract:
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
import json
|
|
441
|
+
from pathlib import Path
|
|
442
|
+
|
|
443
|
+
input_data = json.loads(Path("/input.json").read_text())
|
|
444
|
+
|
|
445
|
+
print(json.dumps({
|
|
446
|
+
"ok": True,
|
|
447
|
+
"result": input_data,
|
|
448
|
+
}))
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Use tools for I/O and scripts for deterministic processing. For example, a skill can call a shared `request` tool to fetch customers and sales, then pass the returned arrays into a Python script that ranks customers by revenue.
|
|
452
|
+
|
|
296
453
|
### Remote skills
|
|
297
454
|
|
|
298
|
-
Store skills in R2 to edit without redeploying and share across agents.
|
|
455
|
+
Store skills in R2 to edit without redeploying and share complex skills across agents. Add an R2 skill source from `getSkills()`:
|
|
299
456
|
|
|
300
457
|
```typescript
|
|
301
|
-
|
|
302
|
-
|
|
458
|
+
import { skills } from "@cloudflare/think";
|
|
459
|
+
|
|
460
|
+
getSkills() {
|
|
461
|
+
return [
|
|
462
|
+
skills.r2(this.env.SKILLS_BUCKET, {
|
|
463
|
+
skills: ["top-customers-by-revenue", "vat-code-review"],
|
|
464
|
+
}),
|
|
465
|
+
];
|
|
303
466
|
}
|
|
304
467
|
```
|
|
305
468
|
|
|
306
|
-
|
|
469
|
+
Remote skills use the same folder shape as bundled skills under the configured R2 prefix:
|
|
470
|
+
|
|
471
|
+
```text
|
|
472
|
+
skills/top-customers-by-revenue/
|
|
473
|
+
├── SKILL.md
|
|
474
|
+
└── scripts/
|
|
475
|
+
└── top-customers.py
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Source order matters: if two sources define the same skill name, the first source wins.
|
|
307
479
|
|
|
308
480
|
## Authentication
|
|
309
481
|
|
|
@@ -359,7 +531,7 @@ Imported from `@economic/agents` unless noted.
|
|
|
359
531
|
| ----------- | -------------------------------------------------------- |
|
|
360
532
|
| `Agent` | Core agent base. Implement `getModel`/`getSystemPrompt`. |
|
|
361
533
|
| `ChatAgent` | `Agent` + compaction and message feedback. |
|
|
362
|
-
| `Assistant` | Per-user manager of `ChatAgent`
|
|
534
|
+
| `Assistant` | Per-user manager of `ChatAgent` chats. |
|
|
363
535
|
|
|
364
536
|
### Helpers and types
|
|
365
537
|
|
|
@@ -379,13 +551,12 @@ Imported from `@economic/agents` unless noted.
|
|
|
379
551
|
| `getSystemPrompt(ctx?)` | `Agent` | Required. System prompt. |
|
|
380
552
|
| `getTools()` | `Agent` | Always-on tools (default `{}`). |
|
|
381
553
|
| `getSkills()` | `Agent` | Local skills (default `[]`). |
|
|
382
|
-
| `getRemoteSkills()` | `Agent` | R2 skill keys (default `[]`). |
|
|
383
554
|
| `static getJwtAuthConfig(env)` | `Agent` | Optional JWT verification config. |
|
|
384
555
|
| `getUserContext(jwtToken)` | `Agent` | Optional per-user context after auth. |
|
|
385
556
|
| `submitMessageFeedback` / `getMessageFeedback` | `ChatAgent` | Callable message feedback. |
|
|
386
557
|
| `agent` | `Assistant` | Required. Your `ChatAgent` subclass. |
|
|
387
558
|
| `fastModel` | `Assistant` | Required. Cheap model for titles/summaries. |
|
|
388
|
-
| `createChat` / `getChats` / `deleteChat` | `Assistant` | Callable
|
|
559
|
+
| `createChat` / `getChats` / `deleteChat` | `Assistant` | Callable chat management. |
|
|
389
560
|
|
|
390
561
|
### Package root (`@economic/agents`)
|
|
391
562
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { n as JwtAuthConfig, t as routeAgentRequest } from "./route-agent-request-
|
|
1
|
+
import { n as JwtAuthConfig, t as routeAgentRequest } from "./route-agent-request-CbjgNl2B.mjs";
|
|
2
2
|
import { Agent as Agent$1, Connection, ConnectionContext, SubAgentClass } from "agents";
|
|
3
|
-
import { ChatResponseResult,
|
|
3
|
+
import { ChatResponseResult, PrepareStepContext, Session, Think, ToolCallContext, ToolCallDecision, TurnConfig, TurnContext, skills } from "@cloudflare/think";
|
|
4
4
|
import { JSONValue, LanguageModel, Tool as Tool$1, UIMessage } from "ai";
|
|
5
|
+
import { createCompactFunction } from "agents/experimental/memory/utils";
|
|
6
|
+
import { SkillSource as SkillSource$1 } from "agents/skills";
|
|
7
|
+
|
|
5
8
|
//#region src/server/types.d.ts
|
|
6
9
|
/**
|
|
7
10
|
* The context object available throughout an agent's lifetime — passed via
|
|
@@ -31,20 +34,10 @@ type AgentConnectionState = {
|
|
|
31
34
|
//#region src/server/util/tools.d.ts
|
|
32
35
|
type ToolSet = Record<string, Tool>;
|
|
33
36
|
type Tool<Context extends Record<string, unknown> = Record<string, unknown>, INPUT extends JSONValue | unknown | never = any, OUTPUT extends JSONValue | unknown | never = any> = Tool$1<INPUT, OUTPUT> & {
|
|
34
|
-
authorize?: (ctx
|
|
37
|
+
authorize?: (ctx?: Context) => boolean;
|
|
35
38
|
};
|
|
36
39
|
declare function tool<Context extends Record<string, unknown> = Record<string, unknown>, INPUT extends JSONValue | unknown | never = any, OUTPUT extends JSONValue | unknown | never = any>(tool: Tool<Context, INPUT, OUTPUT>): Tool$1<INPUT, OUTPUT>;
|
|
37
40
|
//#endregion
|
|
38
|
-
//#region src/server/util/skills.d.ts
|
|
39
|
-
interface Skill<Context extends Record<string, unknown> = Record<string, unknown>> {
|
|
40
|
-
name: string;
|
|
41
|
-
description: string;
|
|
42
|
-
instructions: string;
|
|
43
|
-
tools?: Record<string, Tool<Context>>;
|
|
44
|
-
authorize?: (ctx: Context) => boolean;
|
|
45
|
-
}
|
|
46
|
-
declare function skill<Context extends Record<string, unknown> = Record<string, unknown>>(definition: Skill<Context>): Skill<Context>;
|
|
47
|
-
//#endregion
|
|
48
41
|
//#region src/server/agents/Agent.d.ts
|
|
49
42
|
declare abstract class Agent<RequestContext extends Record<string, unknown> = Record<string, unknown>, UserContext extends Record<string, unknown> = Record<string, unknown>> extends Think<Cloudflare.Env & AgentEnv, AgentConnectionState> {
|
|
50
43
|
initialState: AgentConnectionState;
|
|
@@ -61,6 +54,8 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
61
54
|
onClose(): Promise<void>;
|
|
62
55
|
onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
|
|
63
56
|
configureSession(session: Session): Session;
|
|
57
|
+
private _toolsForCurrentTurn;
|
|
58
|
+
private _toolContextForCurrentTurn?;
|
|
64
59
|
/**
|
|
65
60
|
* Merges the client request `body` into `experimental_context` for tools
|
|
66
61
|
* returned from {@link getTools} only (Think-internal tools are unchanged).
|
|
@@ -70,28 +65,29 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
70
65
|
*/
|
|
71
66
|
beforeTurn(ctx: TurnContext): Promise<void | TurnConfig>;
|
|
72
67
|
/**
|
|
73
|
-
* Sets active tools based on skills that might be loaded
|
|
68
|
+
* Sets active tools based on skills that might be loaded during the current turn.
|
|
74
69
|
*
|
|
75
70
|
* @param ctx - The prepare step context.
|
|
76
71
|
* @returns The step config.
|
|
77
72
|
*/
|
|
78
|
-
beforeStep(): Promise<
|
|
73
|
+
beforeStep(ctx: PrepareStepContext): Promise<{
|
|
74
|
+
experimental_context: {};
|
|
75
|
+
activeTools?: undefined;
|
|
76
|
+
} | {
|
|
77
|
+
activeTools: string[];
|
|
78
|
+
experimental_context: {};
|
|
79
|
+
}>;
|
|
79
80
|
beforeToolCall(ctx: ToolCallContext): Promise<ToolCallDecision | void>;
|
|
80
|
-
private _buildToolContext;
|
|
81
81
|
getTools(): ToolSet;
|
|
82
|
+
workspaceBash: boolean;
|
|
83
|
+
getSkillScriptRunner(): skills.SkillScriptRunner | null;
|
|
82
84
|
/**
|
|
83
85
|
* Returns the remote skills to be loaded from SKILLS_BUCKET.
|
|
84
86
|
* @returns The remote skills to be loaded from SKILLS_BUCKET.
|
|
85
87
|
*/
|
|
86
|
-
|
|
87
|
-
* Returns the skills to load for the agent.
|
|
88
|
-
* @returns The skills to load for the agent.
|
|
89
|
-
*/
|
|
90
|
-
protected getSkills(): Skill[];
|
|
88
|
+
protected getRemoteSkills(): string[];
|
|
91
89
|
private _getAuthorizedTools;
|
|
92
|
-
private
|
|
93
|
-
/** Store the pending user context request to defer awaiting it until after the connection is established */
|
|
94
|
-
private _requestContext?;
|
|
90
|
+
private _getLastActivatedSkillName;
|
|
95
91
|
/**
|
|
96
92
|
* Override to enable JWT authentication on WebSocket connections.
|
|
97
93
|
* Return the auth config based on the incoming request, or undefined to skip auth.
|
|
@@ -272,7 +268,21 @@ declare abstract class Assistant extends Agent$1<Cloudflare.Env & AgentEnv, Agen
|
|
|
272
268
|
private runMigration;
|
|
273
269
|
}
|
|
274
270
|
//#endregion
|
|
271
|
+
//#region src/server/util/skills.d.ts
|
|
272
|
+
interface Skill<Context extends Record<string, unknown> = Record<string, unknown>> {
|
|
273
|
+
name: string;
|
|
274
|
+
description: string;
|
|
275
|
+
instructions: string;
|
|
276
|
+
tools?: Record<string, Tool<Context>>;
|
|
277
|
+
authorize?: (ctx?: Context) => boolean;
|
|
278
|
+
}
|
|
279
|
+
interface SkillSource<Context extends Record<string, unknown> = Record<string, unknown>> extends SkillSource$1 {
|
|
280
|
+
tools?: Record<string, Tool<Context>>;
|
|
281
|
+
authorize?: (ctx?: Context | undefined) => boolean;
|
|
282
|
+
}
|
|
283
|
+
declare function skill<Context extends Record<string, unknown> = Record<string, unknown>>(definition: Skill<Context>): SkillSource<Context>;
|
|
284
|
+
//#endregion
|
|
275
285
|
//#region src/server/index.d.ts
|
|
276
286
|
declare function getCurrentToolContext(): any;
|
|
277
287
|
//#endregion
|
|
278
|
-
export { Agent, type AgentConnectionState, type AgentConnectionStatus, type AgentConnectionType, type AgentEnv, Assistant, ChatAgent, type FacetStub, type LegacyChatStub, type LegacyMessageFeedback, type MigrationDeps, type Skill, type Tool, type ToolContext, type ToolSet, getCurrentToolContext, migrateUserFromV1, routeAgentRequest, skill, tool };
|
|
288
|
+
export { Agent, type AgentConnectionState, type AgentConnectionStatus, type AgentConnectionType, type AgentEnv, Assistant, ChatAgent, type FacetStub, type LegacyChatStub, type LegacyMessageFeedback, type MigrationDeps, type Skill, type SkillSource, type Tool, type ToolContext, type ToolSet, getCurrentToolContext, migrateUserFromV1, routeAgentRequest, skill, tool };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as verifyJwt, n as createAgentTracer, r as extractTokenFromConnectRequest, t as routeAgentRequest } from "./route-agent-request-
|
|
1
|
+
import { i as verifyJwt, n as createAgentTracer, r as extractTokenFromConnectRequest, t as routeAgentRequest } from "./route-agent-request-lVm3eus2.mjs";
|
|
2
2
|
import { Agent as Agent$1, callable, getCurrentAgent } from "agents";
|
|
3
|
-
import { Think } from "@cloudflare/think";
|
|
3
|
+
import { Think, skills } from "@cloudflare/think";
|
|
4
4
|
import { Output, convertToModelMessages, generateText, jsonSchema, pruneMessages, tool as tool$1 } from "ai";
|
|
5
5
|
import { nanoid } from "nanoid";
|
|
6
6
|
import { createCompactFunction } from "agents/experimental/memory/utils";
|
|
@@ -23,24 +23,6 @@ const TURN_PROTOCOL_RULES_PROMPT = `These rules are specific for responding to u
|
|
|
23
23
|
- Prefer direct tools over code execution. Never use code execution for a single API call — that always belongs in the direct request tool. A small number of sequential direct calls (typically one to three) is also preferred over code execution. Use code execution only when direct tools cannot satisfy the request, for example variable-length loops, pagination across many calls, substantial joining/grouping/calculation, or transformation that would be unreliable to do mentally.
|
|
24
24
|
- Default to making a fresh data call for every new data request, even if similar data was just fetched — data may have changed and freshness matters more than saving a call. Only reuse earlier tool results when the user is explicitly referring back to a specific prior result, for example asking to summarize it or asking about a specific value inside it ("what was the third one called?", "show that as a table"). Any rephrasing, filter, count, or related follow-up that asks for data is a new data request and requires a fresh call.`;
|
|
25
25
|
//#endregion
|
|
26
|
-
//#region src/server/features/session.ts
|
|
27
|
-
var LocalSkillProvider = class {
|
|
28
|
-
skills;
|
|
29
|
-
constructor(skills) {
|
|
30
|
-
this.skills = new Map(skills.map((skill) => [skill.name, skill]));
|
|
31
|
-
}
|
|
32
|
-
async get() {
|
|
33
|
-
const entries = [...this.skills.values()].map((skill) => `- ${skill.name}: ${skill.description}`);
|
|
34
|
-
if (entries.length === 0) return null;
|
|
35
|
-
return entries.join("\n");
|
|
36
|
-
}
|
|
37
|
-
async load(key) {
|
|
38
|
-
const skill = this.skills.get(key);
|
|
39
|
-
if (!skill) return null;
|
|
40
|
-
return skill.instructions;
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
//#endregion
|
|
44
26
|
//#region src/server/features/registry.ts
|
|
45
27
|
/**
|
|
46
28
|
* Registers a top-level DO in the global registry.
|
|
@@ -48,7 +30,7 @@ var LocalSkillProvider = class {
|
|
|
48
30
|
* Idempotent: keyed on `(agent_name, durable_object_name)`, so repeated calls
|
|
49
31
|
* across cold starts no-op and `created_at` reflects the first registration.
|
|
50
32
|
*/
|
|
51
|
-
async function
|
|
33
|
+
async function registerAgentInstance(db, input) {
|
|
52
34
|
await db.prepare(`INSERT INTO agent_registry (agent_name, durable_object_name, actor_id, created_at)
|
|
53
35
|
VALUES (?, ?, ?, ?)
|
|
54
36
|
ON CONFLICT (agent_name, durable_object_name) DO NOTHING`).bind(input.agentName, input.durableObjectName, input.actorId, input.createdAt).run();
|
|
@@ -149,13 +131,12 @@ var Agent = class extends Think {
|
|
|
149
131
|
});
|
|
150
132
|
}
|
|
151
133
|
configureSession(session) {
|
|
152
|
-
|
|
153
|
-
return this.getSystemPrompt(this.
|
|
154
|
-
} } }).withContext("critical-rules", { provider: { get: async () => SECURITY_RULES_PROMPT } }).withContext("turn-protocol-rules", { provider: { get: async () => TURN_PROTOCOL_RULES_PROMPT } });
|
|
155
|
-
const localSkills = this.getSkills();
|
|
156
|
-
if (localSkills.length) configuredSession = configuredSession.withContext("local-skills", { provider: new LocalSkillProvider(localSkills) });
|
|
157
|
-
return configuredSession.withCachedPrompt();
|
|
134
|
+
return session.withContext("soul", { provider: { get: async () => {
|
|
135
|
+
return this.getSystemPrompt(this._toolContextForCurrentTurn !== void 0 ? this._toolContextForCurrentTurn : void 0);
|
|
136
|
+
} } }).withContext("critical-rules", { provider: { get: async () => SECURITY_RULES_PROMPT } }).withContext("turn-protocol-rules", { provider: { get: async () => TURN_PROTOCOL_RULES_PROMPT } }).withCachedPrompt();
|
|
158
137
|
}
|
|
138
|
+
_toolsForCurrentTurn = {};
|
|
139
|
+
_toolContextForCurrentTurn;
|
|
159
140
|
/**
|
|
160
141
|
* Merges the client request `body` into `experimental_context` for tools
|
|
161
142
|
* returned from {@link getTools} only (Think-internal tools are unchanged).
|
|
@@ -165,11 +146,16 @@ var Agent = class extends Think {
|
|
|
165
146
|
*/
|
|
166
147
|
async beforeTurn(ctx) {
|
|
167
148
|
if (this._pendingUserContextRequest) await this._pendingUserContextRequest;
|
|
168
|
-
this.
|
|
149
|
+
this._toolContextForCurrentTurn = {
|
|
150
|
+
...ctx.body ?? {},
|
|
151
|
+
_userContext: this._userContext
|
|
152
|
+
};
|
|
169
153
|
await this.session.refreshSystemPrompt();
|
|
170
|
-
|
|
154
|
+
this._toolsForCurrentTurn = ctx.tools;
|
|
155
|
+
const activeSkillName = this._getLastActivatedSkillName(ctx.messages);
|
|
156
|
+
const { tools, activeTools } = await this._getAuthorizedTools(ctx.tools, activeSkillName);
|
|
171
157
|
return {
|
|
172
|
-
model: this.getModel(this.
|
|
158
|
+
model: this.getModel(this._toolContextForCurrentTurn),
|
|
173
159
|
messages: pruneMessages({
|
|
174
160
|
messages: ctx.messages,
|
|
175
161
|
toolCalls: [{
|
|
@@ -177,7 +163,7 @@ var Agent = class extends Think {
|
|
|
177
163
|
tools: []
|
|
178
164
|
}, {
|
|
179
165
|
type: "before-last-5-messages",
|
|
180
|
-
tools: ["
|
|
166
|
+
tools: ["activate_skill"]
|
|
181
167
|
}]
|
|
182
168
|
}),
|
|
183
169
|
tools,
|
|
@@ -201,72 +187,81 @@ var Agent = class extends Think {
|
|
|
201
187
|
};
|
|
202
188
|
}
|
|
203
189
|
/**
|
|
204
|
-
* Sets active tools based on skills that might be loaded
|
|
190
|
+
* Sets active tools based on skills that might be loaded during the current turn.
|
|
205
191
|
*
|
|
206
192
|
* @param ctx - The prepare step context.
|
|
207
193
|
* @returns The step config.
|
|
208
194
|
*/
|
|
209
|
-
async beforeStep() {
|
|
210
|
-
const
|
|
195
|
+
async beforeStep(ctx) {
|
|
196
|
+
const activeSkillName = this._getLastActivatedSkillName(ctx.steps);
|
|
197
|
+
if (!activeSkillName) return { experimental_context: this._toolContextForCurrentTurn ?? {} };
|
|
198
|
+
const { activeTools } = await this._getAuthorizedTools(this._toolsForCurrentTurn, activeSkillName);
|
|
211
199
|
return {
|
|
212
200
|
activeTools,
|
|
213
|
-
experimental_context: this.
|
|
201
|
+
experimental_context: this._toolContextForCurrentTurn ?? {}
|
|
214
202
|
};
|
|
215
203
|
}
|
|
216
204
|
async beforeToolCall(ctx) {
|
|
217
|
-
if (ctx.toolName === "
|
|
218
|
-
if (
|
|
205
|
+
if (ctx.toolName === "activate_skill") {
|
|
206
|
+
if ((await this.getSkills()).find((source) => source.id === ctx.input["name"])?.authorize?.(this._toolContextForCurrentTurn ?? {}) === false) return {
|
|
219
207
|
action: "block",
|
|
220
208
|
reason: "Unauthorized skill"
|
|
221
209
|
};
|
|
222
210
|
}
|
|
223
211
|
}
|
|
224
|
-
_buildToolContext() {
|
|
225
|
-
return {
|
|
226
|
-
...this._requestContext,
|
|
227
|
-
_userContext: this._userContext
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
212
|
getTools() {
|
|
231
213
|
return {};
|
|
232
214
|
}
|
|
215
|
+
workspaceBash = false;
|
|
216
|
+
getSkillScriptRunner() {
|
|
217
|
+
if (!("LOADER" in this.env)) return null;
|
|
218
|
+
return skills.runner({ loader: this.env.LOADER });
|
|
219
|
+
}
|
|
233
220
|
/**
|
|
234
221
|
* Returns the remote skills to be loaded from SKILLS_BUCKET.
|
|
235
222
|
* @returns The remote skills to be loaded from SKILLS_BUCKET.
|
|
236
223
|
*/
|
|
237
|
-
|
|
238
|
-
* Returns the skills to load for the agent.
|
|
239
|
-
* @returns The skills to load for the agent.
|
|
240
|
-
*/
|
|
241
|
-
getSkills() {
|
|
224
|
+
getRemoteSkills() {
|
|
242
225
|
return [];
|
|
243
226
|
}
|
|
244
|
-
async _getAuthorizedTools() {
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
...agentTools
|
|
250
|
-
};
|
|
251
|
-
const activeTools = [...Object.keys(sessionTools), ...Object.keys(agentTools)];
|
|
252
|
-
const skills = this._getAuthorizedSkills();
|
|
253
|
-
const activeSkills = await this.session.getLoadedSkillKeys();
|
|
254
|
-
for (const skill of skills) {
|
|
227
|
+
async _getAuthorizedTools(availableTools, activeSkillName) {
|
|
228
|
+
const tools = { ...availableTools };
|
|
229
|
+
const activeTools = Object.keys(availableTools);
|
|
230
|
+
const authorizedSkills = (await this.getSkills()).filter((skill) => !skill.authorize || skill.authorize?.(this._toolContextForCurrentTurn) !== false);
|
|
231
|
+
for (const skill of authorizedSkills) {
|
|
255
232
|
if (!skill.tools || Object.keys(skill.tools).length === 0) continue;
|
|
256
233
|
Object.assign(tools, skill.tools);
|
|
257
|
-
if (
|
|
234
|
+
if (skill.id === activeSkillName) activeTools.push(...Object.keys(skill.tools));
|
|
258
235
|
}
|
|
259
236
|
return {
|
|
260
237
|
tools,
|
|
261
|
-
activeTools: activeTools.filter((toolName) => tools[toolName]?.authorize?.(this.
|
|
238
|
+
activeTools: activeTools.filter((toolName) => tools[toolName]?.authorize?.(this._toolContextForCurrentTurn) !== false)
|
|
262
239
|
};
|
|
263
240
|
}
|
|
264
|
-
|
|
265
|
-
|
|
241
|
+
_getLastActivatedSkillName(items = []) {
|
|
242
|
+
for (let index = items.length - 1; index >= 0; index--) {
|
|
243
|
+
const item = items[index];
|
|
244
|
+
if (!item || typeof item !== "object") continue;
|
|
245
|
+
const record = item;
|
|
246
|
+
const input = record.input;
|
|
247
|
+
if ((record.toolName === "activate_skill" || record.type === "tool-activate_skill") && input && typeof input === "object") {
|
|
248
|
+
const { name } = input;
|
|
249
|
+
if (typeof name === "string") return name;
|
|
250
|
+
}
|
|
251
|
+
for (const key of [
|
|
252
|
+
"toolResults",
|
|
253
|
+
"parts",
|
|
254
|
+
"content"
|
|
255
|
+
]) {
|
|
256
|
+
const nestedItems = record[key];
|
|
257
|
+
if (Array.isArray(nestedItems)) {
|
|
258
|
+
const skillName = this._getLastActivatedSkillName(nestedItems);
|
|
259
|
+
if (skillName) return skillName;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
266
263
|
}
|
|
267
264
|
/** Store the pending user context request to defer awaiting it until after the connection is established */
|
|
268
|
-
_requestContext;
|
|
269
|
-
/** Store the pending user context request to defer awaiting it until after the connection is established */
|
|
270
265
|
_pendingUserContextRequest;
|
|
271
266
|
/**
|
|
272
267
|
* The user context for the session.
|
|
@@ -285,7 +280,7 @@ var Agent = class extends Think {
|
|
|
285
280
|
async registerInstance() {
|
|
286
281
|
if (this.parentPath.length > 0) return;
|
|
287
282
|
try {
|
|
288
|
-
await
|
|
283
|
+
await registerAgentInstance(this.env.AGENTS_DB, {
|
|
289
284
|
agentName: this.constructor.name,
|
|
290
285
|
durableObjectName: this.name,
|
|
291
286
|
actorId: this.getActorIdFromDurableObjectName(),
|
|
@@ -367,7 +362,7 @@ async function summariseChatWithAI(sql, durableObjectName, messages, model) {
|
|
|
367
362
|
const { output: { title, summary } } = await generateText({
|
|
368
363
|
model,
|
|
369
364
|
system: systemPrompt,
|
|
370
|
-
messages: await convertToModelMessages(messages.slice(-
|
|
365
|
+
messages: await convertToModelMessages(messages.slice(-20)),
|
|
371
366
|
output: Output.object({ schema: jsonSchema({
|
|
372
367
|
type: "object",
|
|
373
368
|
properties: {
|
|
@@ -597,7 +592,7 @@ var Assistant = class extends Agent$1 {
|
|
|
597
592
|
*/
|
|
598
593
|
async registerInstance() {
|
|
599
594
|
try {
|
|
600
|
-
await
|
|
595
|
+
await registerAgentInstance(this.env.AGENTS_DB, {
|
|
601
596
|
agentName: this.constructor.name,
|
|
602
597
|
durableObjectName: this.name,
|
|
603
598
|
actorId: this.name,
|
|
@@ -772,7 +767,26 @@ function skill(definition) {
|
|
|
772
767
|
if (!definition.name) throw new Error("Skill name is required");
|
|
773
768
|
if (!definition.description) throw new Error("Skill description is required");
|
|
774
769
|
if (!definition.instructions) throw new Error("Skill content is required");
|
|
775
|
-
return
|
|
770
|
+
return {
|
|
771
|
+
id: definition.name,
|
|
772
|
+
fingerprint: definition.name,
|
|
773
|
+
async list() {
|
|
774
|
+
return [{
|
|
775
|
+
name: definition.name,
|
|
776
|
+
description: definition.description
|
|
777
|
+
}];
|
|
778
|
+
},
|
|
779
|
+
async load(name) {
|
|
780
|
+
if (name !== definition.name) return null;
|
|
781
|
+
return {
|
|
782
|
+
name: definition.name,
|
|
783
|
+
description: definition.description,
|
|
784
|
+
body: definition.instructions
|
|
785
|
+
};
|
|
786
|
+
},
|
|
787
|
+
tools: definition.tools,
|
|
788
|
+
authorize: definition.authorize
|
|
789
|
+
};
|
|
776
790
|
}
|
|
777
791
|
//#endregion
|
|
778
792
|
//#region src/server/index.ts
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as _$_ai_sdk_anthropic_internal0 from "@ai-sdk/anthropic/internal";
|
|
2
|
+
import { AnthropicMessagesLanguageModel } from "@ai-sdk/anthropic/internal";
|
|
3
|
+
import * as _$_ai_sdk_google0 from "@ai-sdk/google";
|
|
4
|
+
import { GoogleGenerativeAIProvider } from "@ai-sdk/google";
|
|
5
|
+
import { AnthropicProvider } from "@ai-sdk/anthropic";
|
|
6
|
+
|
|
7
|
+
//#region src/providers/anthropic.d.ts
|
|
8
|
+
type AnthropicVertexModelId = Parameters<AnthropicProvider>[0];
|
|
9
|
+
interface AnthropicVertexProviderOptions {
|
|
10
|
+
cloudflareAccountId: string;
|
|
11
|
+
cloudflareAiGatewayId: string;
|
|
12
|
+
cloudflareApiToken: string;
|
|
13
|
+
googleCloudProjectId: string;
|
|
14
|
+
location?: string;
|
|
15
|
+
anthropicVersion?: string;
|
|
16
|
+
}
|
|
17
|
+
declare function createAnthropicVertexProvider(options: AnthropicVertexProviderOptions): (modelId: AnthropicVertexModelId) => AnthropicMessagesLanguageModel;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/providers/gemini.d.ts
|
|
20
|
+
type GeminiModelId = Parameters<GoogleGenerativeAIProvider>[0];
|
|
21
|
+
interface GeminiProviderOptions {
|
|
22
|
+
cloudflareAccountId: string;
|
|
23
|
+
cloudflareAiGatewayId: string;
|
|
24
|
+
cloudflareApiToken: string;
|
|
25
|
+
googleCloudProjectId: string;
|
|
26
|
+
location?: string;
|
|
27
|
+
}
|
|
28
|
+
declare function createGeminiProvider(options: GeminiProviderOptions): GoogleGenerativeAIProvider;
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/providers/index.d.ts
|
|
31
|
+
interface AiGatewayVertexProvidersOptions extends AnthropicVertexProviderOptions, GeminiProviderOptions {}
|
|
32
|
+
declare function createAiGatewayVertexProviders(options: AiGatewayVertexProvidersOptions): {
|
|
33
|
+
anthropic: (modelId: AnthropicVertexModelId) => _$_ai_sdk_anthropic_internal0.AnthropicMessagesLanguageModel;
|
|
34
|
+
gemini: _$_ai_sdk_google0.GoogleGenerativeAIProvider;
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { AiGatewayVertexProvidersOptions, type AnthropicVertexModelId, type AnthropicVertexProviderOptions, type GeminiModelId, type GeminiProviderOptions, createAiGatewayVertexProviders, createAnthropicVertexProvider, createGeminiProvider };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { AnthropicMessagesLanguageModel } from "@ai-sdk/anthropic/internal";
|
|
2
|
+
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
3
|
+
//#region src/providers/anthropic.ts
|
|
4
|
+
function createAnthropicVertexProvider(options) {
|
|
5
|
+
const { anthropicVersion = "vertex-2023-10-16", cloudflareAccountId, cloudflareAiGatewayId, cloudflareApiToken, googleCloudProjectId, location = "europe-west1" } = options;
|
|
6
|
+
const anthropicBaseURL = `${`${`https://gateway.ai.cloudflare.com/v1/${cloudflareAccountId}/${cloudflareAiGatewayId}/`}google-vertex-ai/v1/projects/${googleCloudProjectId}/locations/${location}/publishers/`}anthropic/models/`;
|
|
7
|
+
return function anthropic(modelId) {
|
|
8
|
+
return new AnthropicMessagesLanguageModel(modelId, {
|
|
9
|
+
provider: "vertex.anthropic.messages",
|
|
10
|
+
baseURL: anthropicBaseURL,
|
|
11
|
+
headers: { "cf-aig-authorization": `Bearer ${cloudflareApiToken}` },
|
|
12
|
+
buildRequestUrl: (baseURL, isStreaming) => `${baseURL}${modelId}:${isStreaming ? "streamRawPredict" : "rawPredict"}`,
|
|
13
|
+
transformRequestBody: (args) => {
|
|
14
|
+
const { model, ...rest } = args;
|
|
15
|
+
return {
|
|
16
|
+
...rest,
|
|
17
|
+
anthropic_version: anthropicVersion
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
supportedUrls: () => ({}),
|
|
21
|
+
supportsNativeStructuredOutput: false,
|
|
22
|
+
supportsStrictTools: false
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/providers/gemini.ts
|
|
28
|
+
function createGeminiProvider(options) {
|
|
29
|
+
const { cloudflareAccountId, cloudflareAiGatewayId, cloudflareApiToken, googleCloudProjectId, location = "europe-west1" } = options;
|
|
30
|
+
return createGoogleGenerativeAI({
|
|
31
|
+
apiKey: "unused",
|
|
32
|
+
baseURL: `https://gateway.ai.cloudflare.com/v1/${cloudflareAccountId}/${cloudflareAiGatewayId}/google-vertex-ai/v1/projects/${googleCloudProjectId}/locations/${location}/publishers/google/models`,
|
|
33
|
+
headers: { "cf-aig-authorization": `Bearer ${cloudflareApiToken}` },
|
|
34
|
+
name: "gateway.google.vertex-ai"
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/providers/index.ts
|
|
39
|
+
function createAiGatewayVertexProviders(options) {
|
|
40
|
+
return {
|
|
41
|
+
anthropic: createAnthropicVertexProvider(options),
|
|
42
|
+
gemini: createGeminiProvider(options)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { createAiGatewayVertexProviders, createAnthropicVertexProvider, createGeminiProvider };
|
package/dist/v1.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { n as JwtAuthConfig, t as routeAgentRequest } from "./route-agent-request-
|
|
1
|
+
import { n as JwtAuthConfig, t as routeAgentRequest } from "./route-agent-request-CbjgNl2B.mjs";
|
|
2
2
|
import { Agent as Agent$1, Connection, ConnectionContext } from "agents";
|
|
3
3
|
import { LanguageModel, StreamTextOnFinishCallback, ToolSet, UIMessage, generateText, streamText } from "ai";
|
|
4
4
|
import { AIChatAgent, ChatResponseResult, OnChatMessageOptions } from "@cloudflare/ai-chat";
|
|
5
5
|
|
|
6
|
-
//#region src/server/features/skills/index.d.ts
|
|
6
|
+
//#region src/server/v1/features/skills/index.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* A named group of related tools that can be loaded together on demand.
|
|
9
9
|
*
|
|
@@ -25,7 +25,7 @@ interface Skill {
|
|
|
25
25
|
tools: ToolSet;
|
|
26
26
|
}
|
|
27
27
|
//#endregion
|
|
28
|
-
//#region src/server/util/llm.d.ts
|
|
28
|
+
//#region src/server/v1/util/llm.d.ts
|
|
29
29
|
type LLMParams = Parameters<typeof streamText>[0] & Parameters<typeof generateText>[0];
|
|
30
30
|
type BuildLLMParamsConfig = Omit<LLMParams, "prompt"> & {
|
|
31
31
|
/** Skill names loaded in previous turns. Pass `await this.getLoadedSkills()`. */activeSkills?: string[]; /** Skills available for on-demand loading this turn. */
|
package/dist/v1.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { i as verifyJwt, n as createAgentTracer, r as extractTokenFromConnectRequest, t as routeAgentRequest } from "./route-agent-request-
|
|
1
|
+
import { i as verifyJwt, n as createAgentTracer, r as extractTokenFromConnectRequest, t as routeAgentRequest } from "./route-agent-request-lVm3eus2.mjs";
|
|
2
2
|
import { Agent as Agent$1, callable, getCurrentAgent } from "agents";
|
|
3
3
|
import { Output, convertToModelMessages, generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
|
|
4
4
|
import { AIChatAgent } from "@cloudflare/ai-chat";
|
|
5
|
-
//#region src/server/features/skills/index.ts
|
|
6
|
-
const TOOL_NAME_ACTIVATE_SKILL = "activate_skill";
|
|
7
|
-
const TOOL_NAME_LIST_CAPABILITIES = "list_capabilities";
|
|
5
|
+
//#region src/server/v1/features/skills/index.ts
|
|
6
|
+
const TOOL_NAME_ACTIVATE_SKILL$1 = "activate_skill";
|
|
7
|
+
const TOOL_NAME_LIST_CAPABILITIES$1 = "list_capabilities";
|
|
8
8
|
const SKILL_STATE_SENTINEL = "\n__SKILLS_STATE__:";
|
|
9
9
|
function buildActivateSkillDescription(skills) {
|
|
10
10
|
return [
|
|
@@ -37,8 +37,8 @@ function createSkills(config) {
|
|
|
37
37
|
for (const skill of skills) Object.assign(allTools, skill.tools);
|
|
38
38
|
function getActiveToolNames() {
|
|
39
39
|
const names = [
|
|
40
|
-
TOOL_NAME_ACTIVATE_SKILL,
|
|
41
|
-
TOOL_NAME_LIST_CAPABILITIES,
|
|
40
|
+
TOOL_NAME_ACTIVATE_SKILL$1,
|
|
41
|
+
TOOL_NAME_LIST_CAPABILITIES$1,
|
|
42
42
|
...Object.keys(alwaysOnTools)
|
|
43
43
|
];
|
|
44
44
|
for (const skillName of loadedSkills) {
|
|
@@ -57,7 +57,7 @@ function createSkills(config) {
|
|
|
57
57
|
if (sections.length === 0) return "";
|
|
58
58
|
return sections.join("\n\n");
|
|
59
59
|
}
|
|
60
|
-
allTools[TOOL_NAME_ACTIVATE_SKILL] = tool({
|
|
60
|
+
allTools[TOOL_NAME_ACTIVATE_SKILL$1] = tool({
|
|
61
61
|
description: buildActivateSkillDescription(skills),
|
|
62
62
|
inputSchema: jsonSchema({
|
|
63
63
|
type: "object",
|
|
@@ -83,7 +83,7 @@ function createSkills(config) {
|
|
|
83
83
|
return "All requested skills were already loaded.";
|
|
84
84
|
}
|
|
85
85
|
});
|
|
86
|
-
allTools[TOOL_NAME_LIST_CAPABILITIES] = tool({
|
|
86
|
+
allTools[TOOL_NAME_LIST_CAPABILITIES$1] = tool({
|
|
87
87
|
description: LIST_CAPABILITIES_DESCRIPTION,
|
|
88
88
|
inputSchema: jsonSchema({
|
|
89
89
|
type: "object",
|
|
@@ -115,11 +115,6 @@ function createSkills(config) {
|
|
|
115
115
|
}
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
-
function isEphemeralSkillToolPart(part) {
|
|
119
|
-
if (!part || typeof part !== "object" || !("toolCallId" in part) || !("type" in part)) return false;
|
|
120
|
-
const { type } = part;
|
|
121
|
-
return type === `tool-list_capabilities` || type === `tool-activate_skill`;
|
|
122
|
-
}
|
|
123
118
|
/** Creates the `skill_state` table in DO SQLite if it does not exist yet. */
|
|
124
119
|
function ensureSkillTable(sql) {
|
|
125
120
|
sql`CREATE TABLE IF NOT EXISTS skill_state (id INTEGER PRIMARY KEY, active_skills TEXT NOT NULL DEFAULT '[]')`;
|
|
@@ -161,7 +156,7 @@ function saveSkillStateFromMessages(sql, messages) {
|
|
|
161
156
|
sql`INSERT OR REPLACE INTO skill_state(id, active_skills) VALUES(1, ${JSON.stringify(latestSkillState)})`;
|
|
162
157
|
}
|
|
163
158
|
//#endregion
|
|
164
|
-
//#region src/server/util/llm.ts
|
|
159
|
+
//#region src/server/v1/util/llm.ts
|
|
165
160
|
function buildSystemPromptWithSkills(basePrompt, availableSkillList, loadedGuidance) {
|
|
166
161
|
let prompt = `${basePrompt}
|
|
167
162
|
|
|
@@ -556,7 +551,7 @@ async function updateConversationSummary(db, durableObjectName, title, summary)
|
|
|
556
551
|
* prompt bounded regardless of total conversation length.
|
|
557
552
|
*/
|
|
558
553
|
async function generateTitleAndSummary(messages, model, existingSummary) {
|
|
559
|
-
const recentMessages = await convertToModelMessages(messages.slice(-
|
|
554
|
+
const recentMessages = await convertToModelMessages(messages.slice(-15));
|
|
560
555
|
const previousContext = existingSummary ? `Previous summary:\n${existingSummary}\n\nMost recent messages:` : "Conversation:";
|
|
561
556
|
const { output } = await generateText({
|
|
562
557
|
model,
|
|
@@ -610,7 +605,12 @@ function getDeleteConversationScheduleIds(schedules) {
|
|
|
610
605
|
return schedules.filter((schedule) => schedule.callback === DELETE_CONVERSATION_CALLBACK).map((schedule) => schedule.id);
|
|
611
606
|
}
|
|
612
607
|
//#endregion
|
|
613
|
-
//#region src/server/util/messages.ts
|
|
608
|
+
//#region src/server/v1/util/messages.ts
|
|
609
|
+
function isEphemeralSkillToolPart(part) {
|
|
610
|
+
if (!part || typeof part !== "object" || !("toolCallId" in part) || !("type" in part)) return false;
|
|
611
|
+
const { type } = part;
|
|
612
|
+
return type === `tool-list_capabilities` || type === `tool-activate_skill`;
|
|
613
|
+
}
|
|
614
614
|
function filterEphemeralMessages(messages) {
|
|
615
615
|
return messages.flatMap((message) => {
|
|
616
616
|
if (message.role !== "assistant" || !message.parts?.length) return [message];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@economic/agents",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "A starter for creating a TypeScript package.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"exports": {
|
|
16
16
|
".": "./dist/index.mjs",
|
|
17
17
|
"./cli": "./dist/cli.mjs",
|
|
18
|
+
"./providers": "./dist/providers.mjs",
|
|
18
19
|
"./v1": "./dist/v1.mjs",
|
|
19
20
|
"./package.json": "./package.json"
|
|
20
21
|
},
|
|
@@ -26,30 +27,30 @@
|
|
|
26
27
|
"prepublishOnly": "npm run build"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
30
|
+
"@ai-sdk/anthropic": "^3.0.77",
|
|
31
|
+
"@ai-sdk/google": "^3.0.73",
|
|
29
32
|
"@clack/prompts": "^1.2.0",
|
|
30
|
-
"@cloudflare/ai-chat": "^0.
|
|
31
|
-
"@cloudflare/think": "^0.
|
|
33
|
+
"@cloudflare/ai-chat": "^0.8.3",
|
|
34
|
+
"@cloudflare/think": "^0.8.4",
|
|
32
35
|
"@opentelemetry/sdk-trace-base": "^2.7.1",
|
|
33
|
-
"agents": "^0.
|
|
36
|
+
"agents": "^0.14.3",
|
|
37
|
+
"ai": "^6.0.197",
|
|
38
|
+
"jose": "^6.2.3",
|
|
34
39
|
"nanoid": "^5.1.11"
|
|
35
40
|
},
|
|
36
41
|
"devDependencies": {
|
|
37
42
|
"@cloudflare/workers-types": "^4.20260527.1",
|
|
38
43
|
"@types/node": "^25.6.0",
|
|
39
44
|
"@typescript/native-preview": "7.0.0-dev.20260412.1",
|
|
40
|
-
"ai": "^6.0.175",
|
|
41
|
-
"jose": "^6.2.2",
|
|
42
45
|
"tsdown": "^0.22.0",
|
|
43
46
|
"typescript": "^6.0.2",
|
|
44
47
|
"vitest": "^4.1.4"
|
|
45
48
|
},
|
|
46
49
|
"peerDependencies": {
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"optional": true
|
|
53
|
-
}
|
|
50
|
+
"@cloudflare/think": "^0.8.4",
|
|
51
|
+
"@cloudflare/vite-plugin": "^1.40.0",
|
|
52
|
+
"@cloudflare/worker-bundler": "^0.2.0",
|
|
53
|
+
"agents": "^0.14.3",
|
|
54
|
+
"vite": "^8.0.0"
|
|
54
55
|
}
|
|
55
56
|
}
|
|
File without changes
|
|
File without changes
|