Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
3a4000d
update docs index
threepointone Jan 30, 2026
039121c
Add interval scheduling support to Agent API,
threepointone Jan 30, 2026
ab6abf1
Add options-based API for addMcpServer and update docs
threepointone Jan 30, 2026
5a186bb
Add state management docs and tests
threepointone Jan 30, 2026
4cb3a49
Add guide for integrating agents into existing projects
threepointone Jan 30, 2026
97c2127
Add custom routing and identity enhancements for agents
threepointone Jan 30, 2026
ee893ae
Add HTTP & WebSockets documentation and update references
threepointone Jan 30, 2026
e10826b
Enhance callable RPC system with streaming, timeout, and introspection
threepointone Jan 30, 2026
b2a9de6
Support legacy and new call options in AgentClient.call()
threepointone Jan 30, 2026
ec82d28
Fix interval hangs, state recovery, and callable method lookup
threepointone Jan 30, 2026
3d2aa90
Refactor camelCaseToKebabCase to shared utility module
threepointone Jan 30, 2026
a601a9d
Reset ready state on close and add lifecycle tests
threepointone Jan 30, 2026
6090969
Reject pending calls on disconnect; use UUIDs
threepointone Jan 30, 2026
e0f9209
Add human-in-the-loop docs and forward onClose event
threepointone Jan 30, 2026
32e9feb
Revamp agents README with examples and guide
threepointone Jan 30, 2026
5124af2
docs(agents): add Workflows Integration
threepointone Jan 30, 2026
938bb66
Handle stream errors, migrate columns, and state hooks
threepointone Jan 30, 2026
0fcfa9f
Add WS message tests and schedule simulation helpers
threepointone Jan 30, 2026
2d75f40
Improve identity warnings and timeout behavior
threepointone Jan 30, 2026
2b0d45e
Add webhooks docs and GitHub webhook example
threepointone Jan 31, 2026
6986ee1
Update configuration docs for env, assets, and AI
threepointone Jan 31, 2026
c0a68ac
Merge branch 'main' into docs
threepointone Jan 31, 2026
ea9b859
Revamp playground; add email utils; fix types
threepointone Feb 1, 2026
24f49d7
Merge branch 'main' into docs
threepointone Feb 2, 2026
71252fc
Destructure getWorkflows results & add docs links
threepointone Feb 2, 2026
a6e6ad4
MCP docs: /mcp endpoints and new default
threepointone Feb 2, 2026
10597eb
Merge branch 'main' into docs
threepointone Feb 2, 2026
6e762c1
Update workflow imports to agents/workflows
threepointone Feb 2, 2026
106412d
Merge branch 'main' into docs
threepointone Feb 2, 2026
5f458ab
Split email imports into agents/email
threepointone Feb 2, 2026
8f6ea1a
Make setState synchronous and add beforeStateChange
threepointone Feb 3, 2026
6659bba
Rename validation hook and simplify auto-reply detection
threepointone Feb 3, 2026
625ff5e
Remove email header helpers, keep isAutoReplyEmail
threepointone Feb 3, 2026
9ad10c1
Rename Agent.agentOptions to options
threepointone Feb 3, 2026
3e8f63a
Make AgentStaticOptions a Partial type
threepointone Feb 3, 2026
88c4cc8
Log streaming errors and limit alarm interval
threepointone Feb 3, 2026
ab50410
Document @callable usage and warn on closed stream
threepointone Feb 3, 2026
a471587
Await routeAgentRequest and remove duplicate JSDoc
threepointone Feb 3, 2026
202fda1
Update README examples for API changes
threepointone Feb 3, 2026
fe6de73
Improve auto-reply detection and tests
threepointone Feb 3, 2026
29b9fdd
Clear corrupted state and route errors to onError
threepointone Feb 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .changeset/bug-fixes-three.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
"agents": patch
---

# Bug Fixes

This release includes three bug fixes:

## 1. Hung Callback Detection in scheduleEvery()

Fixed a deadlock where if an interval callback hung indefinitely, all future interval executions would be skipped forever.

**Fix:** Track execution start time and force reset after 30 seconds of inactivity. If a previous execution appears hung (started more than 30s ago), it is force-reset and re-executed.

```typescript
// Now safe - hung callbacks won't block future executions
await this.scheduleEvery(60, "myCallback");
```

## 2. Corrupted State Recovery

Fixed a crash when the database contains malformed JSON state.

**Fix:** Wrapped `JSON.parse` in try-catch with fallback to `initialState`. If parsing fails, the agent logs an error and recovers gracefully.

```typescript
// Agent now survives corrupted state
class MyAgent extends Agent {
initialState = { count: 0 }; // Used as fallback if DB state is corrupted
}
```

## 3. getCallableMethods() Prototype Chain Traversal

Fixed `getCallableMethods()` to find `@callable` methods from parent classes, not just the immediate class.

**Fix:** Walk the full prototype chain using `Object.getPrototypeOf()` loop.

```typescript
class BaseAgent extends Agent {
@callable()
parentMethod() {
return "parent";
}
}

class ChildAgent extends BaseAgent {
@callable()
childMethod() {
return "child";
}
}

// Now correctly returns both parentMethod and childMethod
const methods = childAgent.getCallableMethods();
```
77 changes: 77 additions & 0 deletions .changeset/callable-improvements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
"agents": patch
---

# Callable System Improvements

This release includes several improvements to the `@callable` decorator and RPC system:

## New Features

### Client-side RPC Timeout

You can now specify a timeout for RPC calls that will reject if the call doesn't complete in time:

```typescript
await agent.call("slowMethod", [], { timeout: 5000 });
```

### StreamingResponse.error()

New method to gracefully signal an error during streaming and close the stream:

```typescript
@callable({ streaming: true })
async processItems(stream: StreamingResponse, items: string[]) {
for (const item of items) {
try {
const result = await this.process(item);
stream.send(result);
} catch (e) {
stream.error(`Failed to process ${item}: ${e.message}`);
return;
}
}
stream.end();
}
```

### getCallableMethods() API

New method on the Agent class to introspect all callable methods and their metadata:

```typescript
const methods = agent.getCallableMethods();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@craigsdennis you wanted this

// Returns Map<string, CallableMetadata>

for (const [name, meta] of methods) {
console.log(`${name}: ${meta.description || "(no description)"}`);
}
```

### Connection Close Handling

Pending RPC calls are now automatically rejected with a "Connection closed" error when the WebSocket connection closes unexpectedly.

## Internal Improvements

- **WeakMap for metadata storage**: Changed `callableMetadata` from `Map` to `WeakMap` to prevent memory leaks when function references are garbage collected.
- **UUID for RPC IDs**: Replaced `Math.random().toString(36)` with `crypto.randomUUID()` for more robust and unique RPC call identifiers.
- **Streaming observability**: Added observability events for streaming RPC calls.

## API Enhancements

The `agent.call()` method now accepts a unified `CallOptions` object with timeout support:

```typescript
// New format (preferred, supports timeout)
await agent.call("method", [args], {
timeout: 5000,
stream: { onChunk, onDone, onError }
});

// Legacy format (still fully supported for backward compatibility)
await agent.call("method", [args], { onChunk, onDone, onError });
```

Both formats work seamlessly - the client auto-detects which format you're using.
47 changes: 47 additions & 0 deletions .changeset/cool-intervals-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
"agents": patch
---

feat: Add `scheduleEvery` method for fixed-interval scheduling

Adds a new `scheduleEvery(intervalSeconds, callback, payload?)` method to the Agent class for scheduling recurring tasks at fixed intervals.

### Features

- **Fixed interval execution**: Schedule a callback to run every N seconds
- **Overlap prevention**: If a callback is still running when the next interval fires, the next execution is skipped
- **Error resilience**: If a callback throws, the schedule persists and continues on the next interval
- **Cancellable**: Use `cancelSchedule(id)` to stop the recurring schedule

### Usage

```typescript
class MyAgent extends Agent {
async onStart() {
// Run cleanup every 60 seconds
await this.scheduleEvery(60, "cleanup");

// With payload
await this.scheduleEvery(300, "syncData", { source: "api" });
}

cleanup() {
// Runs every 60 seconds
}

syncData(payload: { source: string }) {
// Runs every 300 seconds with payload
}
}
```

### Querying interval schedules

```typescript
// Get all interval schedules
const intervals = await this.getSchedules({ type: "interval" });
```

### Schema changes

Adds `intervalSeconds` and `running` columns to `cf_agents_schedules` table (auto-migrated for existing agents).
38 changes: 38 additions & 0 deletions .changeset/email-header-utilities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
"agents": patch
---

Add email header parsing utilities for working with postal-mime

New utilities to simplify working with email headers from postal-mime:

- `parseEmailHeaders(headers)` - Converts postal-mime headers array to a simple `Record<string, string>` object
- `getEmailHeader(headers, name)` - Gets a specific header value (case-insensitive)
- `hasEmailHeader(headers, name)` - Checks if a header exists (case-insensitive)
- `hasAnyEmailHeader(headers, names)` - Checks if any of the specified headers exist
- `getAllEmailHeaders(headers, name)` - Gets all values for headers that appear multiple times (e.g., `Received`)
- `isAutoReplyEmail(headers, subject?)` - Detects auto-reply emails based on common headers and subject patterns

Example usage:

```typescript
import { parseEmailHeaders, isAutoReplyEmail } from "agents";
import PostalMime from "postal-mime";

async onEmail(email: AgentEmail) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);

// Convert headers to simple object
const headers = parseEmailHeaders(parsed.headers);
console.log(headers["content-type"]);

// Detect and skip auto-reply emails
if (isAutoReplyEmail(parsed.headers, parsed.subject)) {
console.log("Skipping auto-reply");
return;
}

// Process the email...
}
```
36 changes: 36 additions & 0 deletions .changeset/fix-serializable-interface-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
"agents": patch
---

fix: improve type inference for RPC methods returning custom interfaces

Previously, `RPCMethod` used `{ [key: string]: SerializableValue }` to check if return types were serializable. This didn't work with TypeScript interfaces that have named properties (like `interface CoreState { counter: number; name: string; }`), causing those methods to be incorrectly excluded from typed RPC calls.

Now uses a recursive `CanSerialize<T>` type that checks if all properties of an object are serializable, properly supporting:

- Custom interfaces with named properties
- Nested object types
- Arrays of objects
- Optional and nullable properties
- Union types

Also expanded `NonSerializable` to explicitly exclude non-JSON-serializable types like `Date`, `RegExp`, `Map`, `Set`, `Error`, and typed arrays.

```typescript
// Before: these methods were NOT recognized as callable
interface MyState {
counter: number;
items: string[];
}

class MyAgent extends Agent<Env, MyState> {
@callable()
getState(): MyState {
return this.state;
} // ❌ Not typed
}

// After: properly recognized and typed
const agent = useAgent<MyAgent, MyState>({ agent: "my-agent" });
agent.call("getState"); // ✅ Typed as Promise<MyState>
```
47 changes: 47 additions & 0 deletions .changeset/friendly-mcp-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
"agents": patch
---

feat: Add options-based API for `addMcpServer`

Adds a cleaner options-based overload for `addMcpServer()` that avoids passing `undefined` for unused positional parameters.

### Before (still works)

```typescript
// Awkward when you only need transport options
await this.addMcpServer("server", url, undefined, undefined, {
transport: { headers: { Authorization: "Bearer ..." } }
});
```

### After (preferred)

```typescript
// Clean options object
await this.addMcpServer("server", url, {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattzcarey better, no?

Copy link
Contributor

@whoiskatrin whoiskatrin Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am actually a fan of this, i like it, it's clean

transport: { headers: { Authorization: "Bearer ..." } }
});

// With callback host
await this.addMcpServer("server", url, {
callbackHost: "https://my-worker.workers.dev",
transport: { type: "sse" }
});
```

### Options

```typescript
type AddMcpServerOptions = {
callbackHost?: string; // OAuth callback host (auto-derived if omitted)
agentsPrefix?: string; // Routing prefix (default: "agents")
client?: ClientOptions; // MCP client options
transport?: {
headers?: HeadersInit; // Custom headers for auth
type?: "sse" | "streamable-http" | "auto";
};
};
```

The legacy 5-parameter signature remains fully supported for backward compatibility.
Loading
Loading