Skip to content

feat(examples): add Worker-native Codex harness - #2208

Open
mattzcarey wants to merge 15 commits into
cloudflare:mainfrom
mattzcarey:feat/codex-harness-example
Open

feat(examples): add Worker-native Codex harness#2208
mattzcarey wants to merge 15 commits into
cloudflare:mainfrom
mattzcarey:feat/codex-harness-example

Conversation

@mattzcarey

@mattzcarey mattzcarey commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds examples/next/harnesses/codex, a Worker-native Codex harness composed as CodexHarness extends LifecycleCapability on a plain DurableObject. Example only: no package export, no changeset.

  • A static Codex-derived Rust/Wasm transition kernel runs in the owning isolate. Tasks delivers wakes and journals each model and tool effect with step.do, Streams records the kernel events, and a Shell Workspace holds the files.
  • The harness accepts an AI SDK LanguageModelV4 directly and runs Kimi K2.7 Code through workers-ai-provider@4, Workers AI, and AI Gateway. Every model-emitted tool call is preserved and settled sequentially before the next round.
  • The UI is served over the WebSockets capability. The client connects with useAgent from agents/react; a useCodexSession hook replays-then-tails each operation's Streams log, so the transcript and events reload from durable state on reconnect. A "Restart and verify" action aborts the Durable Object and shows the operation and file recovered.
  • Two RFCs record the target Codex architecture and the v1 model boundary (LanguageModelV4 for Codex, pi-ai for Pi).
  • OpenAI Codex source provenance and the pinned Apache-2.0 license are retained.

Known gaps

  • The harness uses the framework-internal Tasks apertures for a capability-owned driver; a public path is still to be designed.
  • A model step interrupted between completion and journal write is re-issued and the rare duplicate call accepted; the RFC records the policy.
  • The routes are unauthenticated, as with the other examples.

Run

Rust 1.95 with the wasm32-unknown-unknown target is required locally; pnpm run start builds the kernel first. CI does not build the kernel.

@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: dba8aec

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 8 potential issues.

4 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment thread examples/next/harnesses/codex/src/codex-harness.ts
Comment on lines +346 to +366
const existing = this.#effect(operation.operation_id, action.effect_id);
if (existing?.status === "completed" && existing.result !== null) {
return parseEffectResult(existing.result);
}
if (!existing) {
this.lifecycle.storage.sql.exec(
`INSERT INTO cf_codex_effects
(operation_id, effect_id, kind, status, request, created_at)
VALUES (?, ?, ?, 'pending', ?, ?)`,
operation.operation_id,
action.effect_id,
action.type,
JSON.stringify(action),
Date.now()
);
}

const result =
action.type === "model"
? await completeCodexModel(this.model, action)
: await performWorkspaceTool(this.workspace, action);

@devin-ai-integration devin-ai-integration Bot Sep 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Interrupted model rounds replay inconsistently

If an attempt stops after transcript.record but before Tasks journals it, retrying can produce a different response. Stored history and replayed results then diverge.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread examples/next/harnesses/codex/src/codex-harness.ts Outdated
Comment thread examples/next/harnesses/codex/src/codex-harness.ts
Comment thread examples/next/harnesses/codex/src/codex-harness.ts
Comment thread examples/next/harnesses/codex/src/client.tsx Outdated
Comment thread examples/next/harnesses/codex/src/language-model-v4.ts Outdated
Comment on lines +182 to +186
const [root, session] = url.pathname.split("/").filter(Boolean);
if (root !== "sessions" || session === undefined) {
return json({ error: "use /sessions/:session" }, { status: 404 });
}
return env.Coder.getByName(session).fetch(request);

@devin-ai-integration devin-ai-integration Bot Sep 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟥 Public session routes expose workspaces

Unauthenticated callers can read any named session's files, submit paid model work, cancel turns, and restart its Durable Object.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@agent-think

agent-think Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

⚪ agents import sizes

Measured 267 runtime imports as minified bundles. The primary size is gzip; raw minified size is included for diagnosis. An existing import growing by more than 10% is marked red. This report is informational.

Red Yellow Green Unchanged New Removed
0 0 0 267 0 0

Compared ec93caf6 with dba8aec6. Open workflow run.

No import sizes changed.

All 267 current runtime imports
Status Import Gzip Raw minified
agents#__DO_NOT_USE_WILL_BREAK__agentContext 258.6 KiB 1130.0 KiB
agents#__DO_NOT_USE_WILL_BREAK__withInvocationScope 258.6 KiB 1130.0 KiB
agents#Agent 258.6 KiB 1130.0 KiB
agents#AGENT_TOOL_MILESTONE_PART 258.6 KiB 1130.0 KiB
agents#AGENT_TOOL_PROGRESS_PART 258.6 KiB 1130.0 KiB
agents#buildAgentPath 259.1 KiB 1132.3 KiB
agents#buildAgentUrl 259.3 KiB 1132.7 KiB
agents#callable 258.6 KiB 1130.1 KiB
agents#camelCaseToKebabCase 258.6 KiB 1130.0 KiB
agents#createHeaderBasedEmailResolver 258.8 KiB 1130.4 KiB
agents#DEFAULT_AGENT_STATIC_OPTIONS 258.6 KiB 1130.0 KiB
agents#DurableObjectOAuthClientProvider 258.6 KiB 1130.0 KiB
agents#getAgentByName 258.6 KiB 1130.0 KiB
agents#getCurrentAgent 258.6 KiB 1130.0 KiB
agents#getSubAgentByName 258.9 KiB 1130.7 KiB
agents#isDurableObjectCodeUpdateReset 258.6 KiB 1130.0 KiB
agents#isDurableObjectMemoryLimitReset 258.6 KiB 1130.0 KiB
agents#isDurableObjectStorageReset 258.6 KiB 1130.1 KiB
agents#isPlatformTransientError 258.6 KiB 1130.0 KiB
agents#MCP_SERVER_ID_MAX_LENGTH 258.6 KiB 1130.0 KiB
agents#MessageType 258.8 KiB 1130.3 KiB
agents#normalizeServerId 258.6 KiB 1130.0 KiB
agents#parseSubAgentPath 258.6 KiB 1130.0 KiB
agents#routeAgentEmail 258.9 KiB 1130.7 KiB
agents#routeAgentRequest 259.2 KiB 1131.9 KiB
agents#routeSubAgentRequest 258.8 KiB 1130.6 KiB
agents#SqlError 258.6 KiB 1130.0 KiB
agents#StreamingResponse 258.6 KiB 1130.0 KiB
agents#SUB_PREFIX 258.6 KiB 1130.0 KiB
agents#unstable_callable 258.7 KiB 1130.2 KiB
agents/agent-tools#agentTool 112.5 KiB 538.2 KiB
agents/browser#BrowserConnector 50.5 KiB 176.6 KiB
agents/browser#browserContent 36.3 KiB 127.4 KiB
agents/browser#browserExtract 36.3 KiB 127.4 KiB
agents/browser#browserLinks 36.3 KiB 127.4 KiB
agents/browser#browserMarkdown 36.3 KiB 127.4 KiB
agents/browser#browserPdf 36.3 KiB 127.3 KiB
agents/browser#BrowserRenderingError 36.0 KiB 126.7 KiB
agents/browser#browserScrape 36.3 KiB 127.4 KiB
agents/browser#browserScreenshot 36.3 KiB 127.3 KiB
agents/browser#browserSnapshot 36.3 KiB 127.4 KiB
agents/browser#CdpSession 37.2 KiB 129.8 KiB
agents/browser#CodemodeRuntime 39.6 KiB 139.0 KiB
agents/browser#connectBrowser 37.8 KiB 131.4 KiB
agents/browser#connectBrowserSession 37.5 KiB 130.4 KiB
agents/browser#connectUrl 37.6 KiB 130.5 KiB
agents/browser#createBrowserSession 36.3 KiB 127.5 KiB
agents/browser#DEFAULT_EXEC_SWEEP_IDLE_MS 36.0 KiB 126.6 KiB
agents/browser#DEFAULT_SWEEP_IDLE_MS 36.0 KiB 126.6 KiB
agents/browser#deleteBrowserSession 36.1 KiB 126.9 KiB
agents/browser#DurableBrowserSessionStore 36.4 KiB 127.6 KiB
agents/browser#getBrowserRecording 36.2 KiB 127.1 KiB
agents/browser#listBrowserTargets 36.1 KiB 126.9 KiB
agents/browser#loadCdpSpec 36.6 KiB 128.3 KiB
agents/browser#runQuickAction 36.0 KiB 126.6 KiB
agents/browser/ai#createBrowserRuntime 146.0 KiB 630.3 KiB
agents/browser/ai#createBrowserTools 146.0 KiB 630.3 KiB
agents/browser/ai#createQuickActionTools 122.5 KiB 554.3 KiB
agents/browser/tanstack-ai#createBrowserTools 161.7 KiB 699.2 KiB
agents/chat#AbortRegistry 2.5 KiB 8.9 KiB
agents/chat#AGENT_TOOL_STREAM_PROGRESS_BUMP_THROTTLE_MS 2.3 KiB 8.2 KiB
agents/chat#AgentToolProgressEmitter 2.7 KiB 9.5 KiB
agents/chat#AgentToolStreamProgressThrottle 2.4 KiB 8.3 KiB
agents/chat#aiSdkRecoveryCodec 2.3 KiB 8.2 KiB
agents/chat#applyAgentToolEvent 3.2 KiB 11.0 KiB
agents/chat#applyChunkToParts 2.3 KiB 8.2 KiB
agents/chat#applyToolUpdate 2.4 KiB 8.4 KiB
agents/chat#AutoContinuationController 2.3 KiB 8.2 KiB
agents/chat#awaitWithDeadline 2.4 KiB 8.4 KiB
agents/chat#broadcastTransition 3.1 KiB 11.4 KiB
agents/chat#buildChatRecoveringFrame 2.4 KiB 8.4 KiB
agents/chat#buildInClauseStrings 2.4 KiB 8.4 KiB
agents/chat#bumpChatRecoveryProgress 2.4 KiB 8.3 KiB
agents/chat#byteLength 2.5 KiB 8.5 KiB
agents/chat#CHAT_LAST_TERMINAL_KEY 2.3 KiB 8.2 KiB
agents/chat#CHAT_MESSAGE_TYPES 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERING_FLAG_TTL_MS 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERING_KEY 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_ALARM_DEBOUNCE_MS 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_INCIDENT_KEY_PREFIX 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_INCIDENT_TTL_MS 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_PROGRESS_KEY 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_STABLE_RETRY_DELAY_SECONDS 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_TASK_NAME 2.3 KiB 8.2 KiB
agents/chat#CHAT_STREAM_PROGRESS_CREDIT_THROTTLE_MS 2.3 KiB 8.2 KiB
agents/chat#ChatRecoveryEngine 4.4 KiB 15.3 KiB
agents/chat#chatRecoveryTaskRunOptions 2.4 KiB 8.6 KiB
agents/chat#ChatStreamStalledError 2.4 KiB 8.3 KiB
agents/chat#classifyAgentToolChildRecovery 2.4 KiB 8.5 KiB
agents/chat#cleanupStreamBuffers 2.3 KiB 8.2 KiB
agents/chat#clearChatTerminal 2.3 KiB 8.3 KiB
agents/chat#clientResolvableToolNames 2.3 KiB 8.3 KiB
agents/chat#ContinuationState 2.7 KiB 9.8 KiB
agents/chat#createAgentToolEventState 2.3 KiB 8.3 KiB
agents/chat#createChatFiberSnapshot 2.5 KiB 8.6 KiB
agents/chat#createChatRecoveryTaskDefinition 2.6 KiB 9.0 KiB
agents/chat#createChatStreams 5.5 KiB 19.3 KiB
agents/chat#createChatTurnTaskDefinition 2.7 KiB 8.9 KiB
agents/chat#createToolsFromClientSchemas 114.3 KiB 545.4 KiB
agents/chat#crossMessageToolResultUpdate 2.4 KiB 8.6 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_MAX_ATTEMPTS 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_MAX_OOM_RETRIES 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_MAX_WORK 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_NO_PROGRESS_TIMEOUT_MS 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_STABLE_TIMEOUT_MS 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_TERMINAL_MESSAGE 2.4 KiB 8.3 KiB
agents/chat#dispatchChatRecoveryToHandoff 3.0 KiB 9.9 KiB
agents/chat#drainInteractionApplies 2.3 KiB 8.3 KiB
agents/chat#enforceRowSizeLimit 3.5 KiB 11.2 KiB
agents/chat#hasIncompleteToolBatch 2.4 KiB 8.6 KiB
agents/chat#interceptAgentToolBroadcast 2.5 KiB 8.7 KiB
agents/chat#isPlatformFailure 2.6 KiB 8.9 KiB
agents/chat#isReplayChunk 2.4 KiB 8.7 KiB
agents/chat#iterateWithStallWatchdog 2.6 KiB 8.8 KiB
agents/chat#KV_DELETE_MAX_KEYS 2.3 KiB 8.2 KiB
agents/chat#listActiveChatRecoveryIncidents 2.4 KiB 8.4 KiB
agents/chat#MAX_BOUND_PARAMS 2.3 KiB 8.2 KiB
agents/chat#MessageType 2.4 KiB 9.0 KiB
agents/chat#normalizeToolInput 2.3 KiB 8.2 KiB
agents/chat#parseProtocolMessage 2.5 KiB 9.0 KiB
agents/chat#partAwaitsClientInteraction 2.4 KiB 8.6 KiB
agents/chat#pausedExecutionUpdate 2.4 KiB 8.4 KiB
agents/chat#pendingChatTerminal 2.3 KiB 8.3 KiB
agents/chat#persistReconstructedOrphan 3.1 KiB 11.0 KiB
agents/chat#PreStreamTurns 2.6 KiB 9.3 KiB
agents/chat#readChatRecoveryProgress 2.3 KiB 8.3 KiB
agents/chat#reconcileMessages 2.8 KiB 9.6 KiB
agents/chat#reconcileOrphanPartial 2.4 KiB 8.5 KiB
agents/chat#recordChatTerminal 2.4 KiB 8.3 KiB
agents/chat#repairInterruptedToolParts 2.6 KiB 9.1 KiB
agents/chat#resolveChatRecoveryConfig 2.6 KiB 8.9 KiB
agents/chat#resolveToolMergeId 2.4 KiB 8.5 KiB
agents/chat#ResumableStream 4.6 KiB 15.3 KiB
agents/chat#ResumeHandshake 3.0 KiB 10.4 KiB
agents/chat#ROW_MAX_BYTES 2.3 KiB 8.2 KiB
agents/chat#runChatRecoveryExhaustion 2.6 KiB 8.9 KiB
agents/chat#sanitizeMessage 2.5 KiB 8.8 KiB
agents/chat#sendIfOpen 2.4 KiB 8.4 KiB
agents/chat#setChatRecovering 2.5 KiB 8.5 KiB
agents/chat#shouldCreditStreamProgress 2.4 KiB 8.3 KiB
agents/chat#STREAM_CLEANUP_DELAY_SECONDS 2.3 KiB 8.2 KiB
agents/chat#STREAM_RESUME_NONE_REASONS 2.3 KiB 8.3 KiB
agents/chat#StreamAccumulator 2.9 KiB 10.7 KiB
agents/chat#StreamProgressCreditThrottle 2.4 KiB 8.3 KiB
agents/chat#SubmitConcurrencyController 2.9 KiB 10.3 KiB
agents/chat#sweepStaleChatRecoveryIncidents 2.4 KiB 8.5 KiB
agents/chat#TextSegmentJoiner 2.7 KiB 9.2 KiB
agents/chat#TIMED_OUT 2.3 KiB 8.2 KiB
agents/chat#toolApprovalUpdate 2.4 KiB 8.5 KiB
agents/chat#toolPartHasSettledResult 2.3 KiB 8.4 KiB
agents/chat#toolResultUpdate 2.4 KiB 8.5 KiB
agents/chat#truncateOlderMessages 3.2 KiB 10.4 KiB
agents/chat#TurnQueue 2.6 KiB 9.2 KiB
agents/chat#unwrapChatFiberSnapshot 2.4 KiB 8.5 KiB
agents/chat#wrapChatFiberSnapshot 2.3 KiB 8.2 KiB
agents/chat-sdk#ChatSdkStateAdapter 261.0 KiB 1141.6 KiB
agents/chat-sdk#ChatSdkStateAgent 260.4 KiB 1139.1 KiB
agents/chat-sdk#createChatSdkState 261.0 KiB 1141.6 KiB
agents/chat-sdk#defaultKeyShard 258.8 KiB 1130.2 KiB
agents/chat-sdk#defaultThreadShard 258.7 KiB 1130.1 KiB
agents/chat/react#detectToolsRequiringConfirmation 3.3 KiB 8.3 KiB
agents/chat/react#extractClientToolSchemas 3.2 KiB 8.3 KiB
agents/chat/react#getAgentMessages 3.4 KiB 8.6 KiB
agents/chat/react#getToolApproval 3.1 KiB 8.0 KiB
agents/chat/react#getToolCallId 3.1 KiB 8.0 KiB
agents/chat/react#getToolInput 3.1 KiB 8.0 KiB
agents/chat/react#getToolOutput 3.1 KiB 8.0 KiB
agents/chat/react#getToolPartState 3.2 KiB 8.2 KiB
agents/chat/react#useAgentChat 132.9 KiB 609.7 KiB
agents/chat/react#WebSocketChatTransport 5.7 KiB 17.1 KiB
agents/chat/transport#WebSocketChatTransport 2.8 KiB 9.2 KiB
agents/client#AgentClient 5.7 KiB 16.6 KiB
agents/client#AgentConnectionError 582 B 993 B
agents/client#agentFetch 4.2 KiB 12.3 KiB
agents/client#createStubProxy 638 B 1.0 KiB
agents/client#DEFAULT_CALL_TIMEOUT_MS 473 B 770 B
agents/client#isTerminalCloseEvent 509 B 822 B
agents/context#AgentContextProvider 412 B 792 B
agents/context#AgentSearchProvider 640 B 1.3 KiB
agents/context#ContextBlocks 87.4 KiB 429.7 KiB
agents/email#createAddressBasedEmailResolver 193 B 227 B
agents/email#createCatchAllEmailResolver 110 B 97 B
agents/email#createHeaderBasedEmailResolver 334 B 492 B
agents/email#createSecureReplyEmailResolver 718 B 1.3 KiB
agents/email#DEFAULT_MAX_AGE_SECONDS 56 B 39 B
agents/email#isAutoReplyEmail 201 B 249 B
agents/email#signAgentHeaders 424 B 812 B
agents/experimental/webmcp#registerWebMcp 85.2 KiB 295.8 KiB
agents/lifecycle#getCurrentAgent 376 B 798 B
agents/lifecycle#Lifecycle 8.3 KiB 25.8 KiB
agents/lifecycle#LifecycleCapability 484 B 975 B
agents/mcp#createLegacyMcpHandler 375.9 KiB 1572.2 KiB
agents/mcp#createMcpHandler 388.2 KiB 1617.4 KiB
agents/mcp#DurableObjectEventStore 342.5 KiB 1430.7 KiB
agents/mcp#ElicitRequestSchema 342.5 KiB 1430.7 KiB
agents/mcp#experimental_createMcpHandler 376.1 KiB 1572.5 KiB
agents/mcp#getMcpAuthContext 342.5 KiB 1430.8 KiB
agents/mcp#MCP_SERVER_ID_MAX_LENGTH 342.5 KiB 1430.7 KiB
agents/mcp#McpAgent 342.5 KiB 1430.7 KiB
agents/mcp#normalizeServerId 342.5 KiB 1430.7 KiB
agents/mcp#RPC_DO_PREFIX 342.5 KiB 1430.7 KiB
agents/mcp#RPCClientTransport 342.5 KiB 1430.7 KiB
agents/mcp#RPCServerTransport 342.5 KiB 1430.7 KiB
agents/mcp#SSEEdgeClientTransport 342.6 KiB 1431.0 KiB
agents/mcp#StreamableHTTPEdgeClientTransport 342.6 KiB 1431.0 KiB
agents/mcp#WorkerTransport 345.8 KiB 1447.6 KiB
agents/mcp/client#getNamespacedData 62.9 KiB 240.0 KiB
agents/mcp/client#MCP_SERVER_ID_MAX_LENGTH 62.9 KiB 239.9 KiB
agents/mcp/client#MCPClientManager 158.6 KiB 702.6 KiB
agents/mcp/client#normalizeServerId 63.0 KiB 240.2 KiB
agents/mcp/do-oauth-client-provider#DurableObjectOAuthClientProvider 2.1 KiB 6.6 KiB
agents/mcp/server#createMcpHandler 80.5 KiB 307.2 KiB
agents/mcp/server#getMcpAuthContext 64.0 KiB 245.5 KiB
agents/observability#channels 259 B 549 B
agents/observability#genericObservability 470 B 1.2 KiB
agents/observability#subscribe 324 B 668 B
agents/observability/ai#wrapAISDK 8.8 KiB 30.5 KiB
agents/react#_testUtils 3.8 KiB 9.5 KiB
agents/react#useAgent 10.8 KiB 31.1 KiB
agents/react#useAgentToolEvents 5.6 KiB 16.8 KiB
agents/routing#getAgentByName 795 B 1.7 KiB
agents/routing#routeAgentRequest 1.6 KiB 3.6 KiB
agents/routing#RoutedAgents 2.4 KiB 6.2 KiB
agents/schedule#getSchedulePrompt 85.8 KiB 424.7 KiB
agents/schedule#scheduleSchema 85.3 KiB 423.6 KiB
agents/schedule#unstable_getSchedulePrompt 85.9 KiB 424.9 KiB
agents/schedule#unstable_scheduleSchema 85.3 KiB 423.6 KiB
agents/schedules#Scheduler 6.8 KiB 22.0 KiB
agents/schedules/parser#getSchedulePrompt 85.8 KiB 424.7 KiB
agents/schedules/parser#scheduleSchema 85.3 KiB 423.6 KiB
agents/sessions#createCompactFunction 1.7 KiB 4.0 KiB
agents/sessions#Session 1.9 KiB 5.4 KiB
agents/sessions#Sessions 8.8 KiB 31.2 KiB
agents/skills#fromManifest 309.8 KiB 1084.0 KiB
agents/skills#parseSkillFrontmatter 328.4 KiB 1146.2 KiB
agents/skills#parseSkillMarkdown 328.6 KiB 1146.5 KiB
agents/skills#r2 330.2 KiB 1150.4 KiB
agents/skills#runner 369.0 KiB 1297.8 KiB
agents/skills#SkillRegistry 416.4 KiB 1581.7 KiB
agents/skills/compile#compileSkillScript 15.4 KiB 43.4 KiB
agents/skills/compile#isCompilableSkillScript 15.4 KiB 43.3 KiB
agents/streams#DEFAULT_MAX_CHUNK_BYTES 83 B 81 B
agents/streams#sseResponse 843 B 1.6 KiB
agents/streams#StreamClosedError 161 B 197 B
agents/streams#StreamNotFoundError 201 B 261 B
agents/streams#Streams 3.4 KiB 11.1 KiB
agents/streams#StreamSerializationError 158 B 186 B
agents/tasks#DuplicateTaskStepError 328 B 463 B
agents/tasks#MAX_SERIALIZED_BYTES 190 B 232 B
agents/tasks#MissingTaskDefinitionError 358 B 536 B
agents/tasks#NonRetryableError 238 B 308 B
agents/tasks#TaskReplayDivergedError 341 B 483 B
agents/tasks#Tasks 8.9 KiB 31.8 KiB
agents/tasks#TaskSerializationError 258 B 339 B
agents/types#MessageType 211 B 365 B
agents/vite#default 353.8 KiB 1356.1 KiB
agents/websockets#CALLABLES_RPC_QUERY 12.4 KiB 43.3 KiB
agents/websockets#CALLABLES_RPC_VALUE 12.4 KiB 43.3 KiB
agents/websockets#callablesFromDecorated 12.7 KiB 44.3 KiB
agents/websockets#callablesRpcUrl 12.5 KiB 43.5 KiB
agents/websockets#isCallablesRpcUpgrade 12.4 KiB 43.4 KiB
agents/websockets#WebSockets 17.7 KiB 62.2 KiB
agents/workflows#AgentWorkflow 260.0 KiB 1134.8 KiB
agents/workflows#WorkflowRejectedError 258.7 KiB 1130.2 KiB
agents/x402#normalizeNetwork 14.7 KiB 61.1 KiB
agents/x402#withX402 23.0 KiB 89.2 KiB
agents/x402#withX402Client 104.1 KiB 346.5 KiB

Reported by agent-think[bot].

The example is the demo: remove smoke.mjs, the node:test adapter suite and
its tsx tsconfig, the /health, results and abort routes, and the
"static-wasm" mode/metadata leftovers. Remove the nx `build` script so CI
does not need a Rust toolchain; `start` and `deploy` still build the kernel.

Fix recovery after a Durable Object restart: a stored terminal action now
settles the operation instead of failing it, a replayed terminal operation
closes its stream, and an identical resubmit of a still-queued operation
re-syncs its Tasks wake. Join text blocks before trimming so boundary
whitespace survives, and stop gating UI completion on the demo file.

Trim deployment URLs and smoke timing tables from the RFCs.

Claude-Session: https://claude.ai/code/session_01KEFnjoMnZBMewsuD9qxGrL
Drop the harness's own cf_codex_effects table and run each model or
Workspace effect as a named Tasks step keyed by its effect ID. Tasks replays
settled results on later attempts and hands the step's AbortSignal to the
model call, so cancellation reaches in-flight work. Document the interrupted
step policy in the RFC: tools re-run idempotently, model rounds re-issue and
accept a rare duplicate call.

Claude-Session: https://claude.ai/code/session_01KEFnjoMnZBMewsuD9qxGrL

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 2 new potential issues.

4 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment thread examples/next/harnesses/codex/src/codex-harness.ts
Comment on lines +202 to +205
if (operation.status === "completed" || operation.status === "failed") {
// Replayed after settling: make sure the stream is closed and finish.
await this.#flushEvents(operation, true);
return projectTaskResult(operation);

@devin-ai-integration devin-ai-integration Bot Sep 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Terminal recovery reopens closed streams

A restart after stream settlement makes replay call #flushEvents on a closed stream. Streams.open() throws, so the task cannot finish recovery.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Replace the HTTP polling routes with a WebSocket protocol on the
WebSockets capability: a session snapshot on connect, subscribe to
replay-then-tail an operation's Streams log, and submit and restart
commands. Operation state changes broadcast to every connection.

The client connects with useAgent from agents/react and a small
useCodexSession hook layers the protocol on that socket, so the whole
transcript and every operation's events reload from durable state on
reconnect. The worker routes with routeAgentRequest.

Claude-Session: https://claude.ai/code/session_01KEFnjoMnZBMewsuD9qxGrL

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 4 new potential issues.

4 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment thread examples/next/harnesses/codex/src/use-codex-session.ts Outdated
Comment on lines +419 to +443
if (block.type === "text") continue;
if (!block.complete) continue;
frames.push({
type: "response.output_item.done",
item: {
type: "function_call",
call_id: block.id,
name: block.name,
arguments: block.input
}
});
}
if (text.length > 0) {
frames.push(
{ type: "response.output_text.delta", delta: text },
{
type: "response.output_item.done",
item: {
type: "message",
role: "assistant",
content: [{ type: "output_text", text }]
}
}
);
}

@devin-ai-integration devin-ai-integration Bot Sep 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Model content order is reversed

blocksToFrames moves every text block behind all tool calls. Responses with interleaved text and tools enter the next model round in a different order.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +30 to +37
function isClientMessage(value: unknown): value is CodexClientMessage {
return (
typeof value === "object" &&
value !== null &&
"type" in value &&
typeof value.type === "string"
);
}

@devin-ai-integration devin-ai-integration Bot Sep 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟨 WebSocket payloads lack validation

isClientMessage validates only the discriminator. Oversized prompts and malformed identifiers reach storage and model execution, enabling resource exhaustion and unexpected failures.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +538 to +547
const path = action.arguments.path;
if (typeof path !== "string") {
return { type: "error", message: `${action.name} requires a path` };
}
if (action.name === "workspace_write") {
const content = action.arguments.content;
if (typeof content !== "string") {
return { type: "error", message: "workspace_write requires content" };
}
await workspace.writeFile(path, content);

@devin-ai-integration devin-ai-integration Bot Sep 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟨 Workspace tools lack path confinement

Model-provided paths reach readFile and writeFile without a permitted-root check. Adversarial prompts can access any path exposed by the workspace.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@2208

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@2208

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@2208

hono-agents

npm i https://pkg.pr.new/hono-agents@2208

@cloudflare/shell

npm i https://pkg.pr.new/@cloudflare/shell@2208

@cloudflare/think

npm i https://pkg.pr.new/@cloudflare/think@2208

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@2208

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@2208

commit: dba8aec

Deploy as codex-harness-example, give the Vite dev server its own inspector
port so it can run beside the other harness examples, and document setting
CLOUDFLARE_ACCOUNT_ID for logins with several accounts.
agents/react resolved a second React copy from the agents package, which
broke every hook call in the browser.
A closed stream ends as soon as it is replayed, and every stream end
asked for a snapshot that resubscribed to every operation. Subscribe to
each operation once per connection and refresh only after a live tail
ends, and scroll to the bottom only when content is added.
devin-ai-integration[bot]

This comment was marked as resolved.

Add a synthetic-model stress worker (src/stress) with a driver and a CDP
heap profiler (scripts/) so the kernel, Tasks, Streams, and SQLite paths
can be pushed without Workers AI.

Fix what the runs broke:
- the 16-transition cap failed any turn with more than six tool calls;
  cap model rounds at 24 and transitions at 256 instead
- the stored model action repeated the checkpoint's input, halving the
  transcript a turn could hold before SQLITE_TOOBIG; store it without
  the input and rehydrate on read
- a 1 MB tool argument could not be journaled as a Tasks step result;
  bound prompts, tool arguments, and tool outputs at 256 KB, replacing
  oversized arguments with an error the model sees
- session listings carried every checkpoint; omit kernel state from
  listings and let the UI read one checkpoint on demand

Record the results and limits in the README and RFC.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 1 new potential issue.

4 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment on lines +176 to +185
await this.streams.open(streamId, { tag: operationId });
this.lifecycle.storage.sql.exec(
`INSERT INTO cf_codex_operations
(operation_id, stream_id, status, prompt, started_at)
VALUES (?, ?, 'queued', ?, ?)`,
operationId,
streamId,
prompt,
Date.now()
);

@devin-ai-integration devin-ai-integration Bot Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Concurrent retries reject accepted operations

Concurrent submissions sharing an operationId can both pass the existing-row check before streams.open yields. The later insert throws instead of returning the receipt.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

…ample

# Conflicts:
#	design/AGENTS.md
#	examples/next/README.md
…e limits

The kernel checkpoint no longer carries the transcript. It is a cursor over
one turn: phase, round, and pending tool calls, a few hundred bytes however
long the conversation is. Everything with size moves to the SDK's durable
primitives:

- prompts, assistant messages, and tool outputs are Sessions messages, so
  large ones chunk across rows; each round hydrates a byte-budgeted window
  and Sessions compacts the branch past a token threshold
- tool calls reach the kernel as a pointer to the stored assistant message
  and tool outputs are stored as tool messages, so Tasks journals effects
  by id instead of by value
- events append to the operation's Streams log in the same transaction as
  the checkpoint; the journal table is gone
- workspace_read takes offset and max_bytes, and the Workspace spills large
  files to an R2 bucket
- the prompt shows a marker for any tool input or output over 64 KB, with
  a ranged read to page it back, so one large write cannot evict the rest
  of the turn from the context window

Remove the 256 KB payload caps and the transition cap; the round cap is a
configurable option. The stress suite now completes 8 MB prompts, 8 MB tool
payloads, 60-round turns, 200 calls in a round, 200 turns on one object,
and 32 concurrent objects with a 0.5 KB checkpoint throughout.
A transient Workers AI capacity error failed the whole turn. A round that
returns no usable response now throws inside its Tasks step, which retries
with backoff before the turn fails. Nothing is stored for a failed round,
so a retry starts clean.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 8 new potential issues.

4 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment on lines +212 to +217
await this.session.appendMessage({
id: userMessageId(operationId),
role: "user",
parts: [{ type: "text", text: prompt }],
metadata: { operationId }
});

@devin-ai-integration devin-ai-integration Bot Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Concurrent turns contaminate each other

When prompts overlap, appendMessage puts them on one shared branch. Each model can consume another operation’s prompt or output.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +735 to +743
if (action.name === "workspace_read") {
const content = await workspace.readFile(path);
if (content === null)
return { success: false, output: { path, found: false } };
// Files have no size limit; the model pages through big ones by range.
const bytes = new TextEncoder().encode(content);
const offset = clampInteger(input.offset, 0, bytes.byteLength);
const maxBytes = clampInteger(input.max_bytes, 1, DEFAULT_READ_BYTES);
const end = Math.min(bytes.byteLength, offset + maxBytes);

@devin-ai-integration devin-ai-integration Bot Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Ranged reads load whole files

workspace_read loads and re-encodes the entire file before slicing. A small range from a large R2 file can exhaust Worker memory.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +197 to +203
const existing = this.#operation(operationId);
if (existing) {
if (existing.prompt !== promptPreview(prompt)) {
throw new Error(
`Codex operation ${operationId} already exists with different input`
);
}

@devin-ai-integration devin-ai-integration Bot Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Long prompt conflicts go undetected

promptPreview compares only the first 4,096 characters of long prompts. Different requests sharing that prefix reuse the earlier operation.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +743 to +752
const end = Math.min(bytes.byteLength, offset + maxBytes);
return {
success: true,
output: {
path,
content: new TextDecoder().decode(bytes.subarray(offset, end)),
offset,
end,
total_bytes: bytes.byteLength,
...(end < bytes.byteLength ? { next_offset: end } : {})

@devin-ai-integration devin-ai-integration Bot Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Paged reads corrupt Unicode

When a byte range splits a multibyte character, decode replaces both fragments. Paged reads permanently corrupt that character.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +501 to +505
const text = blocks
.filter((block) => block.type === "text")
.map((block) => block.value)
.join("")
.trim();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Model output loses whitespace

trim removes intentional edge whitespace before events and final output are generated. Code blocks and whitespace-sensitive answers change.

Suggested change
const text = blocks
.filter((block) => block.type === "text")
.map((block) => block.value)
.join("")
.trim();
const text = blocks
.filter((block) => block.type === "text")
.map((block) => block.value)
.join("");
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +553 to +562
this.lifecycle.storage.transactionSync(() => {
for (const event of transition.events) {
if (event.seq < writer.cursor) continue;
if (event.seq !== writer.cursor) {
throw new Error(
`Codex event gap for ${operation.operation_id}: expected ${writer.cursor}, got ${event.seq}`
);
}
writer.append(event);
}

@devin-ai-integration devin-ai-integration Bot Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Large response blocks fail turns

When a text or reasoning block exceeds Streams’ 1 MiB limit, append throws. The otherwise valid model turn is marked failed.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +451 to +454
return step.do(
`effect:${action.effect_id}`,
{ retries: MODEL_ROUND_RETRIES, timeout: "10 minutes" },
({ signal }) => this.#runEffect(operation, checkpoint, action, signal)

@devin-ai-integration devin-ai-integration Bot Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Long tool IDs fail turns

A tool-call ID above 244 characters makes step.do reject the effect. Valid provider tool calls then fail the turn.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

submit: (input) => this.submit(input),
operation: (operationId) => this.snapshot(operationId),
message: (id) => this.message(id),
readFile: (path) => this.readFile(path),

@devin-ai-integration devin-ai-integration Bot Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟥 Clients can read arbitrary workspace files

A client-controlled path reaches readFile without an allowlist. Any connected client can retrieve every file stored in that session’s workspace.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 3 new potential issues.

5 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment on lines +444 to +450
if (
!(error instanceof Error) ||
error.name === "AttemptSupersededError"
) {
throw error;
}
this.#settleFailed(input.operationId, errorMessage(error));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Platform failures become terminal turns

When a step exhausts transient platform retries, #drive converts the propagated error into a terminal turn failure. Tasks cannot defer recovery to a fresh invocation.

Prompt for agents
Preserve Tasks control-flow and platform-failure semantics in CodexHarness.#drive. The catch in examples/next/harnesses/codex/src/codex-harness.ts currently rethrows non-Error controls and AttemptSupersededError only. ReplayStep also intentionally propagates code-update resets, memory-limit resets, and exhausted transient platform failures so Tasks can defer or apply breaker policy. Use the Tasks/retries predicates or a framework-supported error classifier to rethrow every control/platform failure, and settle the Codex operation only for terminal application errors.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +159 to +167
case "stream_end": {
// Only an operation we were tailing live needs its settled state
// and file; a replayed closed stream ends immediately.
const operation = stateRef.current.operations.find(
(candidate) => candidate.operationId === message.operationId
);
if (operation && isActive(operation)) {
sendRef.current({ type: "snapshot", id: crypto.randomUUID() });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Completed turns leave workspace view stale

After completion, isActive suppresses the snapshot request at stream end. The sidebar keeps the file state captured before the turn.

Suggested change
case "stream_end": {
// Only an operation we were tailing live needs its settled state
// and file; a replayed closed stream ends immediately.
const operation = stateRef.current.operations.find(
(candidate) => candidate.operationId === message.operationId
);
if (operation && isActive(operation)) {
sendRef.current({ type: "snapshot", id: crypto.randomUUID() });
}
case "stream_end": {
sendRef.current({ type: "snapshot", id: crypto.randomUUID() });
return;
}
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +423 to +434
const result = await this.#performEffect(
operation,
transition.checkpoint,
transition.action,
step
);
command = {
type: "resolve_effect",
checkpoint: transition.checkpoint,
effect_id: transition.action.effect_id,
result
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Round limit permits extra call

When the kernel emits the first disallowed round, #performEffect calls the model before checking the limit. The operation pays for an unused response.

Prompt for agents
Enforce maxRounds before dispatching each model action, including the model action emitted after the last tool in a batch. Do not call the provider or append an assistant message for a round that the next loop iteration will reject. Clarify whether maxRounds counts model calls or completed continuations and test small limits such as 1.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant