-
Notifications
You must be signed in to change notification settings - Fork 367
docs/tests/enhancements #812
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 30 commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
3a4000d
update docs index
threepointone 039121c
Add interval scheduling support to Agent API,
threepointone ab6abf1
Add options-based API for addMcpServer and update docs
threepointone 5a186bb
Add state management docs and tests
threepointone 4cb3a49
Add guide for integrating agents into existing projects
threepointone 97c2127
Add custom routing and identity enhancements for agents
threepointone ee893ae
Add HTTP & WebSockets documentation and update references
threepointone e10826b
Enhance callable RPC system with streaming, timeout, and introspection
threepointone b2a9de6
Support legacy and new call options in AgentClient.call()
threepointone ec82d28
Fix interval hangs, state recovery, and callable method lookup
threepointone 3d2aa90
Refactor camelCaseToKebabCase to shared utility module
threepointone a601a9d
Reset ready state on close and add lifecycle tests
threepointone 6090969
Reject pending calls on disconnect; use UUIDs
threepointone e0f9209
Add human-in-the-loop docs and forward onClose event
threepointone 32e9feb
Revamp agents README with examples and guide
threepointone 5124af2
docs(agents): add Workflows Integration
threepointone 938bb66
Handle stream errors, migrate columns, and state hooks
threepointone 0fcfa9f
Add WS message tests and schedule simulation helpers
threepointone 2d75f40
Improve identity warnings and timeout behavior
threepointone 2b0d45e
Add webhooks docs and GitHub webhook example
threepointone 6986ee1
Update configuration docs for env, assets, and AI
threepointone c0a68ac
Merge branch 'main' into docs
threepointone ea9b859
Revamp playground; add email utils; fix types
threepointone 24f49d7
Merge branch 'main' into docs
threepointone 71252fc
Destructure getWorkflows results & add docs links
threepointone a6e6ad4
MCP docs: /mcp endpoints and new default
threepointone 10597eb
Merge branch 'main' into docs
threepointone 6e762c1
Update workflow imports to agents/workflows
threepointone 106412d
Merge branch 'main' into docs
threepointone 5f458ab
Split email imports into agents/email
threepointone 8f6ea1a
Make setState synchronous and add beforeStateChange
threepointone 6659bba
Rename validation hook and simplify auto-reply detection
threepointone 625ff5e
Remove email header helpers, keep isAutoReplyEmail
threepointone 9ad10c1
Rename Agent.agentOptions to options
threepointone 3e8f63a
Make AgentStaticOptions a Partial type
threepointone 88c4cc8
Log streaming errors and limit alarm interval
threepointone ab50410
Document @callable usage and warn on closed stream
threepointone a471587
Await routeAgentRequest and remove duplicate JSDoc
threepointone 202fda1
Update README examples for API changes
threepointone fe6de73
Improve auto-reply detection and tests
threepointone 29b9fdd
Clear corrupted state and route errors to onError
threepointone File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(); | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(); | ||
| // 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. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"; | ||
threepointone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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... | ||
| } | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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> | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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, { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mattzcarey better, no?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@craigsdennis you wanted this