whoiskatrin
kreznykova@cloudflare.com
90d · built 2026-05-28
90-day totals
- Commits
- 64
- Grow
- 7.6
- Maintenance
- 8.2
- Fixes
- 12.3
- Total ETV
- 28.1
Where this dev ranks
Percentile against the global top-100 leaderboard (all-time totals).
- By commits
- Top 89 %
- By Growth share
- Top 52 %
30-day trajectory
Last 30 days vs. the 30 days before. Up arrows on Growth and ETV mean improvement; up arrow on Fixes share means more time on fixes (worse).
Daily performance
Daily ETV, stacked by Growth, Maintenance and Fixes.
Work-mix over time
Share of Growth / Maintenance / Fixes over a rolling 7-day window. Reads as 'where is effort flowing right now'.
Bug flow over time
Monthly bug flow attributed to this developer. The left bar (red) is bug impact this dev authored that was addressed in the given month — combining bugs others fixed for them and bugs they fixed themselves. The right bar is fixes they personally shipped that month, split between self-fixes (overlap with the red bar) and fixes done for someone else. X-axis is fix-time, not introduction-time — the Navigara API attributes bugs backward to the author at the moment the fix lands.
- Self-fix share
- 32%
- Bugs you introduced
- 11.8
- Bugs you fixed
- 15.7
Repository spread
Where this developer's commits land. Concentrated work (top1 > 80%) vs polymath spread (top1 < 30%).
| Repo | Commits | ETV |
|---|---|---|
| agents | 28 | 16.0 |
| sandbox-sdk | 30 | 11.8 |
| cloudflare-docs | 6 | 0.3 |
Most impactful commits
Top 20 by ETV in the 90-day window.
- 3.2ETVWIP - Add Telnyx voice provider (#1461) * Add Telnyx voice provider * Add empty changeset for Telnyx provider * Address Telnyx provider review feedback * Fix Telnyx example typecheck * Update voice-providers/telnyx/src/transport/phone-transport.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Refocus Telnyx example on phone bridge * dependency version pinning * port telnyx tests * Bump deps, add aria-label, adjust tests Update dependency versions in examples/telnyx-voice-agent/package.json (bumping @cloudflare/kumo, ai, react, and several devDependencies) to newer releases for compatibility. Add an aria-label ("Text message") to the text input in the Telnyx voice agent UI to improve accessibility. Harden durable-chat recovery tests by checking scheduled callback counts before running retry/continue tasks and waiting for the agent to become idle, reducing flakiness in CI. * Harden Telnyx voice provider lifecycle and exports Keep the Telnyx package root server-safe by moving browser WebRTC helpers to the /browser entrypoint, and update the example/docs to use the clearer root-plus-browser import model. This avoids pulling @telnyx/webrtc into Worker bundles while keeping STT, TTS, and JWT helpers available from server-safe paths. Tighten the phone bridge and credential lifecycle so reconnects preserve the preferred audio format, stale async bridge setup is cancelled without leaking browser audio resources, in-flight starts settle on stop, late STT WebSockets are closed, and failed JWT token creation cleans up orphaned Telnyx credentials. The example now warns about local-only unauthenticated token minting, handles overlapping connect/disconnect attempts, and documents the live browser bridge requirement. Co-authored-by: Cursor <cursoragent@cursor.com> * Notify on websocket close error Ensure the TTS stream is unblocked if websocket.close() throws by calling notify() in the close handler catch. Add a test that simulates close() throwing (via MockWebSocket) and verifies aborting the request immediately resolves the synthesize promise to null. --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Christopher Little-Savage <clittle-savage@cloudflare.com> Co-authored-by: Sunil Pai <spai@cloudflare.com> Co-authored-by: Cursor <cursoragent@cursor.com>github.com-cloudflare-agents · d44f59ad · 2026-05-22
- 2.5ETVfix(ai-chat): preserve assistant messages across chained continuations (#1162) * Bump core deps: agents, hono, AI providers Upgrade various dependencies across the repo: bump @openai/agents and @openai/agents-extensions to ^0.8.0, workers-ai-provider to ^3.1.7, hono to ^4.12.9, and ai-gateway-provider to ^3.1.2. Update package-lock.json to reflect these new versions (including updated optional provider sub-dependencies). Also apply minor formatting/whitespace cleanup in ai-chat and codemode CHANGELOGs and update example and site package.json files to use the new versions. * fix(ai-chat): preserve assistant messages across chained continuations * Clear _pendingAutoContinuation when continuation response has no body * fix(ai-chat): reuse resume handlers for tool continuations * Update packages/ai-chat/src/index.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ai-chat): close stream controller when resume request send fails --------- Co-authored-by: Sunil Pai <spai@cloudflare.com> Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>github.com-cloudflare-agents · 7053b495 · 2026-03-24
- 2.2ETVfeat: add file watching capabilities with inotify support (#324) * feat: add file watching capabilities with inotify support * Create empty-poets-serve.md * Potential fix for code scanning alert no. 42: Incomplete string escaping or encoding Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fixes for claude review * update tests to verify regex format for default and custom excludes * Fix error handling and type safety in WatchService and FileWatch classes. Update tests to validate new event parsing logic and ensure proper handling of inotifywait output. * Refactor WatchService tests to validate combined regex patterns for default and custom excludes * Added timeouts for event propagation * Refactored and cleaned * Small ws transport related fixes * Timing changes help account for the additional buffering * Fix WebSocket blocking issue for SSE streaming responses Streaming responses (like file watch events) were blocking the WebSocket message handler because handleStreamingResponse was awaited. This prevented other messages from being processed while a stream was active. Run streaming response handlers in the background with error logging, allowing the message handler to return immediately and process subsequent messages. * Add debug logging and stream tracking for WebSocket streaming Add detailed logging to trace streaming response handling and track active streams to prevent potential garbage collection of Response objects. * Acquire stream reader synchronously before async execution The WebSocket message handler needs to capture the Response body reader before any await points. When getReader() was called inside the async handleStreamingResponse method, Bun's WebSocket handler would return before the reader was acquired, potentially invalidating the Response body stream. By getting the reader synchronously in handleRequest before the promise starts executing, we ensure the stream remains valid throughout the async streaming loop. * Wait for inotifywait watches to be established before signaling ready The watching SSE event was sent immediately when the stream started, before inotifywait finished setting up watches. This caused flaky tests because file operations could occur before the watch was truly ready. Now we read stderr and wait for the 'Watches established' message from inotifywait before sending the watching event to clients. * Add timeout to waitForWatchesEstablished to prevent hanging If inotifywait fails to output 'Watches established' within 10 seconds, the function will return and allow the stream to proceed. This prevents indefinite hangs if inotifywait behaves unexpectedly. * Wait for first message before returning WebSocket stream For WebSocket streaming, errors were deferred until stream consumption. This caused issues where watchStream() would return successfully even when the server returned an error response. Now requestStream() waits for the first message before returning: - If it's a stream chunk, return the stream (success case) - If it's an error response, throw immediately (error case) This makes WebSocket streaming behavior match HTTP streaming, where errors are thrown immediately rather than deferred. * Address code review issues in file watching Replace empty catch blocks with debug/warn logging throughout WatchService and FileWatch to make failures visible. Fix FileWatch.established() to reject on AbortSignal during establishment, preventing indefinite hangs. Strengthen the SSE event type guard to validate required fields per event type. Add WATCH_STOP_ERROR code, integrate watch cleanup into server shutdown, and rewrite changeset for end users. * Fix file watch E2E test timeout race condition The watchWithActions timeout started at stream creation but blocked for ~6.5s inside the event handler (pre-action delay + file ops + post-action delay), leaving insufficient time to read events in CI. Reset the timeout after actions complete so the full window is available for event collection. * Fix exclude test timeout in file watch E2E The combined wait time (~18s) approached the 30s Vitest timeout. With excludes filtering most events, only 2-4 arrive, so the high stopAfterEvents threshold was never reached and the test always fell through to the full 12s reader timeout. * Remove low-level watch API to simplify public interface Remove watchStream(), stopWatch(), and listWatches() methods from Sandbox class. The handle-based API via watch() is sufficient for all use cases and prevents resource management confusion. Keep the internal WatchService methods for container use but don't expose them through the SDK public API. * Harden file watch stream lifecycle * Stabilize explicit watch stop e2e test * Isolate file-watch e2e sessions per test * Stabilize file-watch error and stop e2e cases * Stabilize websocket e2e flake handling Treat watch stop as idempotent when a watcher is already gone and make OpenCode proxy health checks resilient to transient startup failures. Relax the foreground timing threshold to reduce transport-related CI jitter without masking blocking behavior. * Treat ESRCH as success when stopping already-gone watch process Handle the race where a watch process exits before stopWatch() is called. When process.kill() throws ESRCH (no such process), clean up the watch entry and return success instead of an error. * Isolate file watch e2e sandbox * Route watch E2E through SDK surface * Use SDK watch bridge without new public API * Remove unused watch stop endpoint and reduce stream logging verbosity Remove /api/watch/stop endpoint and WatchClient.stop() method as watch lifecycle is now managed through handle-based API. Clean up verbose debug logging in WebSocket stream handler, keeping only completion summary. Clear setup timeout in watch service to prevent leak. * Scope stream cancellation to owning WebSocket connection Track connectionId for each active stream and only allow cancellation from the connection that initiated it. Prevent cross-connection cancel messages and ensure onClose only cancels streams owned by the closing connection. * Address review: parseSSEStream abort, watch readiness, include/exclude validation - Fix parseSSEStream to register abort listener that calls reader.cancel(), unblocking idle streams instead of polling signal.aborted between reads - Make watch() block until the watching event is received so callers can immediately perform actions that depend on the watcher being active - Reject requests that specify both include and exclude with a clear validation error since inotifywait does not support both simultaneously - Add timeout-race tests for WebSocket transport stream requests - Simplify E2E watch helper: use parseSSEStream + AbortSignal.timeout, remove all hardcoded delays - Remove test worker workaround that silently dropped exclude when include was present * Simplify non-existent path error test watch() now throws on establishment failure, so the test worker returns a clear error response. The 3-path defensive logic is no longer necessary. * Simplify stop-watch test: remove manual watching detection watch() now blocks until established, so the manual loop scanning for the watching event is redundant. Read one chunk to confirm the stream is live, then cancel. * chore: retrigger CI * Update .changeset/empty-poets-serve.md Co-authored-by: Naresh <ghostwriternr@gmail.com> * FIxed small review comments --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Naresh <ghostwriternr@gmail.com>github.com-cloudflare-sandbox-sdk · 2af3c283 · 2026-03-03
- 1.8ETVfix(ai-chat): serialize chat turns and saveMessages (#1142) * fix(ai-chat): serialize chat turns and saveMessages * fix(ai-chat): add pending interaction helpers * Skip stale continuations after chat clear Introduce a monotonic _chatEpoch (incremented on CF_AGENT_CHAT_CLEAR) so queued continuations or save operations enqueued under an older epoch are skipped after a chat clear. Wire apply/approval promises as prerequisites for auto-continuations (catching rejections) so continuations only run if the tool result/approval was applied, and check epoch before running exclusive chat turns. Snap clientTools/body in saveMessages and avoid running them if the epoch changed. Minor cleanup: remove an unnecessary _getAbortSignal call in cancel flow. Add tests and test helpers (isChatTurnActiveForTest, waitForIdleForTest, persistToolCallMessage, getMessageCount) to cover tool-result continuations, chat-clear skipping, and saveMessages behavior; replace some fixed delays with waitForIdleForTest. --------- Co-authored-by: Sunil Pai <spai@cloudflare.com>github.com-cloudflare-agents · 5651eced · 2026-03-22
- 1.5ETVFix null pointer in shellExitedPromise during concurrent session destroy (#452) * Fix null pointer in shellExitedPromise during concurrent session destroy Session.destroy() sets shellExitedPromise to null, but execStream's polling loop accesses it via a non-null assertion after resuming from sleep. When deleteSession() runs concurrently without holding the per-session mutex, this causes a TypeError crash. Guard the nullable field, capture mutable instance fields into locals before any await points, acquire the session lock in deleteSession, and add a SESSION_DESTROYED error code (HTTP 410) so callers get a typed error instead of an unhandled crash. * Add changeset for shellExitedPromise fix * Add tests for destroy-during-streaming race condition Test the SESSION_DESTROYED error path when deleteSession races with background streaming and foreground exec. Also pass sessionDir as a parameter to buildFIFOScript rather than having it reach through this.sessionDir, and document why shellExitedPromise is intentionally read fresh in the polling loop rather than captured into a local. * Refine session-destroyed detection and race tests * Preserve shell-termination error propagation Keep shell-termination failures from being rewritten as session teardown errors so exit-command behavior is reported accurately. * Handle explicit exit commands as shell termination Treat direct exit commands as shell termination errors so they keep the expected shell-terminated message and exit code details. * Trigger CI rerun * Add typed errors for shell exit classification * Fix remaining race conditions in session destruction shellExitedPromise could hang forever if destroy() ran while a streaming command was awaiting it, because the callback returned without settling the promise. Session destruction also bypassed per-session locks in the graceful shutdown path. Error classification now uses typed errors instead of regex on message strings, and the labelers-done loop short-circuits on destroy. * Update changeset to reflect actual user-facing behavior --------- Co-authored-by: Naresh <naresh@cloudflare.com>github.com-cloudflare-sandbox-sdk · 5cce0343 · 2026-03-05
- 1.3ETVPreview URLs survive container restarts (#600) * Preview URLs survive container restarts Preview URLs were invalidated on every container stop. Two problems: onStop() unconditionally deleted portTokens from DO storage, and even after the container came back up the runtime had no memory of which ports had been exposed, so validatePortToken() would 404 every request. Persist portTokens across onStop() (cleared only on explicit unexposePort() or full destroy()), and re-expose saved ports on container start via a new restoreExposedPorts() called from onStart() under blockConcurrencyWhile so concurrent requests queue behind restore. Storage shape changes from { portStr: token } to { portStr: { token, name? } } so the friendly name passed to exposePort() also survives restart. readPortTokens() migrates the legacy string format on read, so existing stored state is honored without an explicit migration step. All other consumers (exposePort, unexposePort, getExposedPorts, validatePortToken, desktop fallback path) go through the same helper. Added seven focused unit tests covering restore with names, legacy migration on restore, skip-if-exposed, per-port failure isolation, empty-storage no-op, onStop() not deleting portTokens, and exposePort() persisting the name. * Add E2E test and exposePort() JSDoc for restart survival Documents the new behavior on the SDK's authoritative reference point (exposePort JSDoc) instead of bloating the README, keeping the README as the minimal quick-start surface the project prefers. Adds tests/e2e/preview-url-restart-workflow.test.ts that exposes a port with a custom token, stops the container via a new test-only /api/container/stop endpoint, restarts the user process in a fresh container, and asserts the preview URL still responds 200 without re-exposing. Covers the end-to-end story (storage persistence + onStart restore + blockConcurrencyWhile race guard) that the unit tests exercise in isolation. * destroy() clears portTokens from storage onStop() was changed in this PR to preserve portTokens so preview URLs survive transient restarts. The old cleanup relied on destroy() flowing through onStop() to clear tokens; with onStop() preserving them, destroy() must delete portTokens itself or a reused DO id would let restoreExposedPorts() bring back tokens and ports from a previously destroyed sandbox. Delete portTokens explicitly in destroy() before super.destroy(). Add a regression test that stubs Container.destroy and asserts the storage key is deleted on teardown. * Fix changeset scope and trim release-note copy Beta SDK bugfix — `patch`, not `minor`. Drop the implementation details (blockConcurrencyWhile, validatePortToken) from the body; they belong in code comments, not release notes. * Simplify onStart restore and batch the exposed-port check The base @cloudflare/containers class already wraps onStart() in blockConcurrencyWhile, so nesting another one is redundant. Make onStart async and await restoreExposedPorts() directly. Fetch getExposedPorts once per restore instead of calling isPortExposed() per port. Drops cold-start container round trips from N+1 to 1 and removes a duplicate ensureDefaultSession() on the first iteration. * Test onStart error handling and snapshot restore Lock in two behaviors from the previous commit: - onStart swallows restoreExposedPorts rejections; an unhandled error inside the base class's blockConcurrencyWhile would reset the DO. - getExposedPorts is called once per restore; a rejection falls back to attempting exposePort for every saved port. * Lock in destroy() ordering against stale-read race super.destroy() is not serialized, so other DO RPCs can run during the await. Deleting portTokens first closes the window where a concurrent validatePortToken() would read stale tokens or a concurrent start path would rehydrate them. See the expanded comment at the call site for the full rationale. Also switch the existing destroy() test from direct prototype mutation to vi.spyOn — the old form leaked between tests. * Trim redundant port-restoration tests and docblock Remove the empty-storage no-op test — trivial cover for a one-line early return that other tests exercise implicitly. Rewrite the E2E docblock to describe what the test asserts (preview URL works after a full restart) without referencing internal mechanisms (blockConcurrencyWhile, queueing behind restore) the test does not actually exercise. --------- Co-authored-by: Naresh <naresh@cloudflare.com>github.com-cloudflare-sandbox-sdk · 63e6a898 · 2026-04-21
- 1.3ETVAdd message concurrency controls to AIChatAgent (#1192) Add `AIChatAgent.messageConcurrency` with queue, latest, merge, drop, and debounce strategies for overlapping sendMessage() submits. Enhance `saveMessages()` to accept a functional form for deriving messages from the latest transcript, and return { requestId, status } so callers can detect skipped turns. Test coverage for all strategies, onChatResponse interaction with each strategy, queued programmatic turns, and clear/epoch behavior. Made-with: Cursor Co-authored-by: Sunil Pai <spai@cloudflare.com>github.com-cloudflare-agents · 28925b60 · 2026-03-29
- 1.2ETVAdd disconnected file change checks (#493) * Add persistent file watch state Keep file watches usable for hibernating Durable Objects by separating live SSE delivery from retained watch state in the container. Add owner-scoped acknowledgement and idle expiry so background consumers can reconcile safely without sharing a global dirty bit. * Forward persistent watch owner IDs Pass ownerId through Sandbox.ensureWatch so persistent watches keep their ownership metadata and the reconnect workflow can validate the same consumer across ack and stop operations. * Refine persistent watch leases Replace the initial ownership-flavoured watch API with a cleaner checkpoint and lease model for background consumers. Use `changed`, `checkpointWatch()`, and returned lease tokens for the public flow, while `resumeToken` keeps `ensureWatch()` retryable without exposing another consumer's lease. * Polish persistent watch compatibility Clarify stopWatch token validation, remove redundant key normalization work, and normalize legacy watch responses so clients still see `changed` while older paths return `dirty`. * Refocus file watches on change checks Background consumers only need to know whether a path changed while disconnected. Replace the lease-based persistent watch API with checkChanges() so callers store one version token and choose whether to skip work, sync incrementally, or rescan. * Use canonical log events in WatchService --------- Co-authored-by: Naresh <naresh@cloudflare.com>github.com-cloudflare-sandbox-sdk · fdd3efa4 · 2026-04-01
- 0.8ETVfix(ai-chat): keep other tabs streaming during continuations (#1178) * fix(ai-chat): avoid stalling other tabs during continuations * test(ai-chat): stabilize multi-tab approval regressiongithub.com-cloudflare-agents · 253345e2 · 2026-03-24
- 0.7ETVSurface SESSION_TERMINATED when a session's shell exits (#633) * Surface SESSION_TERMINATED when a session's shell exits Previously a session whose underlying shell exited (user ran `exit`, shell crashed, or a child process took the shell down) lingered in the map with ready=false. Every subsequent call hit the dead handle and failed with "Session is not ready or shell has died", and createSession refused to replace it. The only recovery was sandbox.destroy(), which loses /workspace state. Model shell death as a first-class error instead of silently papering over it: - Add SESSION_TERMINATED / SessionTerminatedError (HTTP 410), distinct from SESSION_DESTROYED, carrying the observed exit code. This matches the existing ShellTerminatedError vs SessionDestroyedError split in the container runtime. - The command that kills the shell returns SESSION_TERMINATED so the caller learns session-local state (env vars, cwd, shell functions) is gone, rather than running against a fresh shell that pretends nothing happened. - The dead handle is evicted as part of surfacing the error, so the next call on the same session id creates a fresh session through the normal path. The caller gets auto-recovery without a silent semantic swap. - createSession on a dead session id now replaces the dead handle instead of returning SESSION_ALREADY_EXISTS, giving callers an explicit recovery API. Healthy duplicates still yield SESSION_ALREADY_EXISTS. Eviction runs under the per-session lock: the getOrCreateSession fast path is already called under the lock held by executeInSession / withSession / executeStreamInSession, and the createSession recovery branch takes the lock itself (the public HTTP handler does not). * Close two follow-up gaps in the SESSION_TERMINATED fix 1. withSession's catch block only unwrapped errors with a `code` field matching ErrorCode. ShellTerminatedError and SessionDestroyedError are plain Error subclasses with no `code`, so they fell through to the generic branch and were wrapped as INTERNAL_ERROR. withSession also did not call evictDeadSession. Impact: setEnvVars / writeFile / readFile / git clone callers got INTERNAL_ERROR instead of SESSION_TERMINATED on a shell death, and recovery was deferred to the next call. Match the typed errors at the top of the catch block, evict under the held lock, return the proper service error. Mirrors executeInSession. 2. createSession's dead-replace branch held the per-session lock only for the eviction; `new Session` + initialize() + sessions.set ran outside the lock. A concurrent executeInSession could interleave between evict and set, create its own Session via getOrCreateSession, and be silently overwritten when createSession resumed -- orphaning a live bash PTY and session directory. A second race existed on the fresh-create path where two concurrent createSession calls both saw an empty map and both constructed. Hold the lock across the entire check -> evict -> construct -> initialize -> set sequence so the concurrency contract the existing comment already claimed becomes true, and both races close. Tests added to session-manager-dead-session.test.ts: - surfaces SESSION_TERMINATED from inside withSession callbacks - surfaces SESSION_TERMINATED from setEnvVars after a prior shell death - createSession holds the lock across evict, create, and set - two concurrent createSession calls on the same fresh id serialize * Align shell-exit e2e tests with SESSION_TERMINATED The test worker's RPC error map lacked SessionTerminatedError and SessionDestroyedError, so shell-death errors fell through to a generic 500 instead of the documented 410. The build-test workflow still asserted the pre-refactor 500 / "shell terminated unexpectedly" shape. Map the new errors and update the assertions to the 410 / SESSION_TERMINATED contract. * Update .changeset/session-terminated.md Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com> * Update packages/sandbox-container/tests/services/session-manager-dead-session.test.ts Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com> * Update packages/sandbox-container/src/services/session-manager.ts Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com> * Update packages/sandbox-container/src/services/session-manager.ts Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com> * Update packages/sandbox-container/src/services/session-manager.ts Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com> * Update packages/sandbox-container/src/services/session-manager.ts Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com> * Update packages/sandbox-container/src/services/session-manager.ts Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com> * Align session-terminated naming Keep the changeset user-facing and align the container and test naming with SESSION_TERMINATED so the behavior reads consistently. * Fix code interpreter model typecheck The Workers AI provider still accepts the older BaseAiTextGeneration model set, so use a compatible model id to keep the example and pre-push hook green. * Export SessionTerminatedError Re-export the new error from the package entry point so the documented @cloudflare/sandbox import works. --------- Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com>github.com-cloudflare-sandbox-sdk · 4e628ae9 · 2026-04-24
- 0.7ETVFix duplicate assistant messages during overlapping submits (#1232) * fix(ai-chat): avoid duplicate assistant messages on overlap * Improve streaming protection and tests Refactor streaming-protection logic and add robust tests for edge cases. Renamed moveMessageById to moveMessageToEnd and simplified its behavior to always move a message to the end. Introduced anchorMessageId in protectedStreamingAssistant to remember the message that an in-progress assistant should be anchored to, and updated restore logic to reinsert the assistant either after the anchor or at the front if no anchor exists. Consolidated local response ID handling inside the effect (localResponseIds) and ensure IDs are cleared on CF_AGENT_CHAT_CLEAR; also clear protectedStreamingAssistant on cleanup. Tests: wrap sendMessage calls in act, and add multiple new tests covering multiple server broadcasts during a stream, CF_AGENT_CHAT_CLEAR mid-stream, server-inserted messages that require anchor-based restoration, handling done without a start chunk, and verifying no protection is activated when not streaming. * Update use-agent-chat.test.tsx --------- Co-authored-by: Sunil Pai <spai@cloudflare.com>github.com-cloudflare-agents · 2713c45b · 2026-03-31
- 0.6ETVfeat(backup): add gitignore and excludes options to createBackup (#474) * Add backup exclude options and e2e coverage This adds end-to-end support for backup exclude patterns so users can skip dependency and build directories during createBackup. It also adds an excludeDefaults option for common paths and covers both exclude and default excludes with backup workflow E2E tests. * Deduplicate backup exclude patterns * Trigger CI * Trigger CI * Tighten backup exclude validation and logging Use regex for control character detection in the SDK layer, matching the pattern already used for backup name validation. Add the same control character check to the container handler for defense-in-depth. Log warnings on exclude file cleanup failures instead of silently swallowing errors. * refactor(backup): use gitignore defaults * fix(backup): normalize prefix matching in gitignore resolution * fix(backup): list ignored files without collapsing dirs * fix(backup): scope gitignore excludes to backup dir * fix(backup): use absolute paths in mksquashfs exclude file mksquashfs -ef matches patterns containing '/' against the full absolute source path, not relative to the source directory root. The git ls-files output was relative, so no files were excluded. * Use absolute paths in mksquashfs excludes Co-authored-by: whoiskatrin <whoiskatrin@users.noreply.github.com> * fix(backup): use wildcard mode for gitignore excludes * fix(backup): restore relative exclude patterns * Fix backup exclude path matching Emit both direct and sticky mksquashfs exclude patterns so .gitignore-derived paths are excluded regardless of how the source directory is rooted in the archive. * fix(backup): use null-delimited git ls-files output Switch from newline to null-byte delimiters when listing gitignored files to handle paths containing spaces, quotes, and non-ASCII characters. Remove .git from hardcoded excludes since git ls-files already omits it. * fix(backup): preserve special chars in gitignore paths without breaking mksquashfs format * feat(backup): change useGitignore default to false and require git when enabled Switch useGitignore default from true to false across BackupService, BackupClient, and Sandbox. When useGitignore is explicitly true, throw GitRequiredForGitignoreError if git is unavailable instead of silently falling back. Escape wildcard metacharacters ([, ], *, ?, \) in gitignored paths to prevent mksquashfs from treating them as patterns. Move exclude file cleanup to finally block to ensure it runs even when mksquashfs throws. * Update .changeset/backup-exclude-patterns.md Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Update tests/e2e/backup-workflow.test.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(backup): rename useGitignore to gitignore and add excludes option Replace useGitignore with gitignore and add excludes array for explicit glob patterns. When git is unavailable and gitignore is true, log a warning and skip git-based exclusions instead of throwing an error. Both options default to off/empty to preserve existing behavior. * Update packages/sandbox-container/src/services/backup-service.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Update packages/sandbox-container/src/services/backup-service.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com> Co-authored-by: whoiskatrin <whoiskatrin@users.noreply.github.com> Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>github.com-cloudflare-sandbox-sdk · 5b0ce89e · 2026-03-10
- 0.6ETVFix git clone timeout controls (#573) * Fix git clone timeout controls Raise the default clone timeout to 10 minutes and let callers override the git clone subprocess timeout per checkout. Thread the override through the SDK and container layers so validation, transport behavior, and timeout reporting stay aligned. * Update packages/sandbox-container/src/services/git-service.ts Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com> * Update packages/sandbox-container/src/services/git-service.ts Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com> --------- Co-authored-by: Aron <263346377+aron-cf@users.noreply.github.com>github.com-cloudflare-sandbox-sdk · cc14fc7e · 2026-04-10
- 0.6ETVFix WS stream timeout killing long-running streams (#400) * Fix WS stream timeout killing long-running streams The WebSocket transport applied a fixed 2-minute total-duration timeout to streaming requests, breaking execStream and process log streams that run longer than 2 minutes. Convert to an inactivity-based idle timeout (default 5 minutes) that resets on every chunk, and make both the request and stream idle timeouts user-configurable via SandboxOptions or environment variables. Closes cloudflare/sandbox-sdk#398 * Fix transport timeout handling to avoid connection resets Mutate transport config in-place instead of recreating the WebSocket client when setting timeouts. Prevents killing in-flight requests and active streams on every getSandbox() call with transportTimeouts. Also fixes a race condition where the idle timeout could fire before the stream controller was set up, and adds validation for timeout values. * Remove transport timeout config from public API Transport-level timeout tuning shouldn't be exposed in the public API. This removes the 6 new user-facing knobs (options, env vars, RPC method) and keeps the idle timeout fix internal. Adds test coverage for the core idle-reset-on-chunk behavior and try/catch guards on all streamController.error() call sites. --------- Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com> Co-authored-by: Naresh <naresh@cloudflare.com>github.com-cloudflare-sandbox-sdk · 92e1fdab · 2026-03-10
- 0.5ETVFix transport retry budget exceeding before container startup completes (#449) * Tie transport retry budget to startup timeouts The transport layer's 503 retry budget was hard-coded at 120s, independent of the configurable container startup timeouts. Customers with large images who set longer startup timeouts still saw failures because the transport gave up before the container finished starting. The retry budget now scales to the sum of instanceGetTimeoutMS and portReadyTimeoutMS plus a 30s margin, with a 120s floor to preserve existing behavior at default settings. * Add changeset * Add retry timeout setter to avoid client recreation The transport retry budget can now be updated in place via setRetryTimeoutMs(), so timeout changes no longer require recreating the entire client and interpreter graph. This removes the risk of stale references when components snapshot the client at construction time. * Add retry timeout setter; stop recreating client. Co-authored-by: ghostwriternr <ghostwriternr@users.noreply.github.com> --------- Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com> Co-authored-by: ghostwriternr <ghostwriternr@users.noreply.github.com>github.com-cloudflare-sandbox-sdk · 909e8c55 · 2026-03-05
- 0.5ETVfix(ai-chat): preserve assistant state during continuations (#1116)github.com-cloudflare-agents · dcc3bb20 · 2026-03-17
- 0.5ETVFix stop() for tool continuation streams; fix orphaned continuation hibernation (#1234) * fix(ai-chat): cancel tool continuations on stop * Make abortActiveToolContinuation more robust Always call transport.abortActiveToolContinuation when stopping (wrap stop() in finally) and enhance WebSocket transport abort logic to handle pre-handshake aborts and keep request IDs for server cleanup. Adds an abortRequested flag and closes the stream if the handshake hasn't completed so late STREAM_RESUMING is ignored, and keeps the requestId in activeIds until server signals done. Also adds tests covering: keeping requestId for cleanup, abort semantics when no continuation or already completed, and abort-before-handshake behavior to prevent late resumes. --------- Co-authored-by: Sunil Pai <spai@cloudflare.com>github.com-cloudflare-agents · 809f4ddd · 2026-03-31
- 0.5ETVfix(ai-chat): prevent hibernation from dropping tool continuations (#1161) Wrap _queueAutoContinuation in keepAliveWhile so the DO stays alive from queueing through streaming. Without this, the DO could hibernate between receiving a tool result and starting the continuation stream, silently dropping the LLM follow-up. Also adds test coverage for continuation edge cases: empty body, chat clear during pending continuation, transport send failure, and event-based test helpers replacing flaky sleep-based waits. Made-with: Cursor Co-authored-by: Sunil Pai <spai@cloudflare.com>github.com-cloudflare-agents · c1319231 · 2026-03-24
- 0.5ETVfix(ai-chat): coalesce rapid auto-continuation turns (#1179) * Bump core deps: agents, hono, AI providers Upgrade various dependencies across the repo: bump @openai/agents and @openai/agents-extensions to ^0.8.0, workers-ai-provider to ^3.1.7, hono to ^4.12.9, and ai-gateway-provider to ^3.1.2. Update package-lock.json to reflect these new versions (including updated optional provider sub-dependencies). Also apply minor formatting/whitespace cleanup in ai-chat and codemode CHANGELOGs and update example and site package.json files to use the new versions. * fix(ai-chat): coalesce rapid auto-continuation turns Batch adjacent client tool results and approvals into one continuation turn so ai-chat does not double-run the model or stream duplicate follow-up output. * fix(ai-chat): preserve origin-tab continuation resume Register the originating connection as soon as auto-continuation is queued and keep other tabs on live broadcast even after the continuation stream starts, avoiding flaky resume handshakes. * fix(ai-chat): queue late continuation follow-ups Preserve auto-continued tool results that arrive after the coalesce window but before the current continuation starts streaming, and cover the pre-stream race with a regression test. * Fix missing _activeAutoContinuationConnectionId cleanup on connection close * ai-chat: fix auto-continuation queuing Initialize _pendingAutoContinuationConnectionId and change auto-continuation behavior so the exclusive chat turn is queued synchronously. keepAlive() is now invoked inside the queued turn (returning a dispose) and disposed in a finally block to prevent hibernation during prerequisites/streaming and ensure proper cleanup. Also apply minor formatting cleanup to the package changelog. --------- Co-authored-by: Sunil Pai <spai@cloudflare.com>github.com-cloudflare-agents · adc86f80 · 2026-03-24
- 0.5ETVAdd durable cancellation mode for useAgentChat (#1484) * Add durable chat cancellation policy * Make client abort cancellation opt-in Co-authored-by: Cursor <cursoragent@cursor.com> * Respect client abort policy for tool continuations Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Sunil Pai <spai@cloudflare.com> Co-authored-by: Cursor <cursoragent@cursor.com>github.com-cloudflare-agents · 364a45da · 2026-05-11