@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 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 conversation features: compaction and message feedback.
13
- - **`Assistant`** — per-user shell over `ChatAgent`: create/list/delete conversations, titles, summaries, retention.
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 conversation
18
- └─ Assistant ← one user, many conversations
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 ai
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
- `jose` is an optional peer dependency, needed only for JWT auth.
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 conversation; address one per user, ticket, or whatever fits.
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 conversation's SQLite (`assistant_messages_feedback`, created automatically):
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 conversation, keyed by message id. |
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 conversations — a sidebar, titles, summaries — add an `Assistant`. It owns the chat list and routes to a `ChatAgent` per conversation.
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 conversation:
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 conversation and returns its id. |
200
- | `getChats()` | `Chat[]` | The user's conversations, most recently updated first. |
201
- | `deleteChat(id)` | `void` | Deletes a conversation and its record. |
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 conversation grows).
204
- - **Retention** — inactive conversations are deleted after 90 days, via a Durable Object alarm.
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
- A skill bundles markdown `instructions` with optional tools. Only `description` sits in the system prompt; the model loads the instructions and tools on demand via the built-in `load_context` tool.
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. Return keys from `getRemoteSkills()`:
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
- protected getRemoteSkills() {
302
- return ["billing.md", "tax.md"];
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
- Loaded from `SKILLS_BUCKET` under the `skills/` prefix the object's `description` metadata shows up front, the body loads on demand. Skipped (with a logged error) if `SKILLS_BUCKET` isn't bound.
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` conversations. |
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 conversation management. |
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-DmwIOBJS.mjs";
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, Session, StepConfig, Think, ToolCallContext, ToolCallDecision, TurnConfig, TurnContext } from "@cloudflare/think";
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: Context) => boolean;
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 in the current turn.
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<StepConfig | void>;
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 _getAuthorizedSkills;
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-DKDvDYnR.mjs";
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 registerAgent(db, input) {
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
- let configuredSession = session.withContext("soul", { provider: { get: async () => {
153
- return this.getSystemPrompt(this._requestContext !== void 0 ? this._buildToolContext() : void 0);
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._requestContext = ctx.body ?? {};
149
+ this._toolContextForCurrentTurn = {
150
+ ...ctx.body ?? {},
151
+ _userContext: this._userContext
152
+ };
169
153
  await this.session.refreshSystemPrompt();
170
- const { tools, activeTools } = await this._getAuthorizedTools();
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._buildToolContext()),
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: ["load_context"]
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 in the current turn.
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 { activeTools } = await this._getAuthorizedTools();
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._buildToolContext()
201
+ experimental_context: this._toolContextForCurrentTurn ?? {}
214
202
  };
215
203
  }
216
204
  async beforeToolCall(ctx) {
217
- if (ctx.toolName === "load_context" && ctx.input["label"] === "local-skills") {
218
- if (!this._getAuthorizedSkills().some((skill) => skill.name === ctx.input["key"])) return {
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 sessionTools = await this.session.tools();
246
- const agentTools = this.getTools();
247
- const tools = {
248
- ...sessionTools,
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 (activeSkills.has(`local-skills:${skill.name}`)) activeTools.push(...Object.keys(skill.tools));
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._buildToolContext()) !== false)
238
+ activeTools: activeTools.filter((toolName) => tools[toolName]?.authorize?.(this._toolContextForCurrentTurn) !== false)
262
239
  };
263
240
  }
264
- _getAuthorizedSkills() {
265
- return this.getSkills().filter((skill) => skill.authorize?.(this._buildToolContext()) !== false);
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 registerAgent(this.env.AGENTS_DB, {
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(-CHAT_RECENT_MESSAGES_COUNT)),
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 registerAgent(this.env.AGENTS_DB, {
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 definition;
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-DmwIOBJS.mjs";
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-DKDvDYnR.mjs";
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(-SUMMARY_CONTEXT_MESSAGES));
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.2.4",
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.7.2",
31
- "@cloudflare/think": "^0.7.3",
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.13.3",
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
- "ai": "^6.0.0",
48
- "jose": "^6.0.0"
49
- },
50
- "peerDependenciesMeta": {
51
- "jose": {
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
  }