Skip to content

feat(tasks): add durable run-scoped event mailboxes - #2287

Open
AntoniTok wants to merge 6 commits into
mainfrom
feat/tasks-event-mailbox
Open

AntoniTok wants to merge 6 commits into
mainfrom
feat/tasks-event-mailbox

Conversation

@AntoniTok

@AntoniTok AntoniTok commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add durable external events to agents/tasks.

  • Tasks.sendEvent() sends run-scoped, optionally idempotent events.
  • step.waitForEvent() waits for one event, indefinitely or with a timeout.
  • step.takeEvents() drains currently buffered events in bounded FIFO batches.
  • Event consumption and step journaling commit atomically.
  • Event batches choose a byte-bounded prefix before loading payload rows.
  • Added schema v2 with a crash-safe v1-to-v2 migration.
  • Indefinite event waits retain no per-run wake job.
  • Routed Task wakes use a collision-free namespace, migrate legacy rows, and
    recover root alarm ownership across eviction.
  • Added public types, errors, observability events, documentation, and recovery tests.

Input timing

The run mailbox supports input regardless of handler timing.

If a run is already waiting for an event, delivery wakes it immediately. If
the run is pending or actively working, the event remains buffered until a
later waitForEvent() or takeEvents() step consumes it.

New input must still target a known, non-terminal run. This is not a
general-purpose inbox. An identical idempotent retry may recover a retained
receipt after the run settles.

Example

const receipt = await tasks.run("process-order", input);

await tasks.sendEvent(
  receipt.runId,
  "approval",
  { approved: true },
  { idempotencyKey: `approval:${requestId}` }
);
const approval = await step.waitForEvent<{ approved: boolean }>(
  "wait-for-approval",
  "approval",
  { timeout: "1 day" }
);

Architecture

External caller
      |
      | Tasks.sendEvent()
      v
Validate and serialize
      |
      v
Event acceptance transaction
      |
      +-- insert or deduplicate event
      |
      +-- matching wait exists?
              |
         +----+----+
         |         |
        yes        no
         |         |
         v         v
   make run due   leave buffered
         |
         v
Lifecycle job queue
         |
         v
Replay Task handler
         |
         v
waitForEvent() / takeEvents()
         |
         v
Atomic transaction
         |
         +-- select matching events FIFO
         +-- mark events consumed
         +-- complete step journal
         |
         v
Return event or batch

Consumption and journaling either commit together or do not commit. After
process loss, replay returns the journaled event instead of consuming another
mailbox entry.

Facet startup ordering

Task runs may live on routed sub-agent facets, but only the root Lifecycle owns
the physical alarm. On a cold start, Lifecycle starts capabilities before the
Agent's asynchronous host-startup hook. Previously that hook also restored
persisted facet identity, so Tasks could temporarily see no route source,
mistake the facet for an alarm-owning root, and reconcile its wake on the wrong
object. Because a facet cannot drive a physical alarm, that ordering could
leave work stranded after eviction.

The fix is deliberately split across two integration points:

  • dynamic-agents.ts restores the persisted facet marker, name, and parent
    path synchronously.
  • index.ts calls that restoration from the Agent constructor before route
    transport and Lifecycle capability startup.

Virtual WebSocket connection hydration remains asynchronous because Tasks only
needs routing identity at startup. The final implementation reads the marker
once for roots and new agents, and reads all three identity keys once for cold
facets. This changes no dynamic-agent route format or persisted key name.

Behavior

  • Events are scoped to one run.
  • Types match exactly and case-sensitively.
  • Events sent before or during execution remain buffered.
  • Indefinite waits keep no per-run Lifecycle wake job.
  • Timed waits persist their first deadline and return null on expiry.
  • Events accepted after a timed-wait deadline remain available.
  • takeEvents() defaults to 100 events and allows at most 1,000.
  • Batches may return fewer events to stay below the 1 MiB journal limit.
  • Idempotency keys are scoped to the run and limited to 256 characters.
  • Reusing a key with different content throws TaskEventIdempotencyConflictError.
  • Missing runs and new events for terminal runs are rejected explicitly.
  • A failed delivery may have committed before wake synchronization failed;
    retrying the same retained event and idempotency key returns the original
    receipt with accepted: false.
  • Non-finite payload numbers are rejected instead of silently becoming null.
  • Local wake IDs remain task:${runId}. Routed wakes use a separate internal
    namespace and preserve stable identity while refreshing transport data.

Correctness

  • Event insertion is durable before sendEvent() resolves, but may also have
    committed when later wake synchronization rejects.
  • Generation fencing prevents superseded attempts from consuming events.
  • FIFO selection, consumption, and journal completion happen in one synchronous SQLite transaction.
  • Batch selection uses event IDs and sizes before loading payload rows into
    JavaScript memory.
  • Completed event steps replay the original result.
  • A park-time mailbox recheck closes the arrival-versus-park race.
  • Retried event delivery can repair a missing wake without inserting a duplicate.
  • Routed wake migration is push-before-delete and idempotent. Startup restores
    facet identity before Tasks reconciles wakes, then coalesces alarm rearming.
  • Deleting a retained run also deletes its events.

Storage

Adds cf_agents_task_events for durable mailbox entries.

Write amplification and intended use

The mailbox is optimized for durable approval and message delivery, not as a
high-throughput message queue. Cloudflare bills writes to indexed columns as a
table-row write plus an additional row write for each affected index. An
individually consumed event therefore costs approximately:

  • Acceptance: four row writes, comprising the event row and three indexes.
  • Consumption: two row writes, comprising the event row and availability index.
  • Journal completion: at least one step-row write.

That is roughly seven row writes per individually consumed event before
AUTOINCREMENT metadata and run/wake bookkeeping. Batching amortizes the
journal write, but does not remove event and index writes.

Under the current Durable Objects SQLite pricing,
the Workers Free plan allows 100,000 rows written per day. Seven writes per
event gives a theoretical ceiling of roughly 14,000 individually consumed
events per day if nothing else writes; real Tasks workloads have less headroom.
The Workers Paid plan includes 50 million rows written per month and then
charges $1 per million rows, or roughly $7 per million individually consumed
events beyond the included allocation.

The indexes avoid full mailbox scans and keep exact-type FIFO lookup bounded.
That tradeoff is reasonable for the intended approval/message workload, but
callers needing event-stream throughput should use a queue-oriented primitive.

The existing step journal gains wait_event and take_events step kinds plus
event_type for replay validation. Existing v1 step rows migrate without
modification. Rolling back to older SDK code after writing event steps is
unsupported.

Deep dive

See Tasks event mailbox
for the complete storage model, delivery and wait flows, replay behavior, and
module responsibilities.

Testing

  • Repository exports, formatting, lint, and diff whitespace validation: passed.
  • Agents TypeScript projects: 5/5 passed.
  • Agents package build: passed.
  • Full Workers suite: 1,960 passed across 125 files.
  • Process-recovery E2E suite: 20 passed across 9 files.
  • Repository typecheck: 119/122 projects passed. The remaining existing
    failures are agent-think/tsconfig.json,
    agent-think/tests-e2e/tsconfig.json, and
    examples/context-overflow-recovery/tsconfig.json.

The SIGKILL coverage verifies both an indefinite wait surviving restart and a
consumed event replaying from its journal after another process loss.

@changeset-bot

changeset-bot Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b01ea98

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
agents Minor
@cloudflare/agent-think Patch

Not sure what this means? Click here to learn what changesets are.

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

# Conflicts:
#	packages/agents/src/tasks/tasks.ts
#	packages/agents/src/tests/capabilities/tasks.ts
@pkg-pr-new

pkg-pr-new Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

agents

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

@cloudflare/ai-chat

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

@cloudflare/codemode

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

hono-agents

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

@cloudflare/shell

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

@cloudflare/think

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

@cloudflare/voice

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

@cloudflare/worker-bundler

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

commit: b01ea98

@agent-think

agent-think Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

🔴 agents import sizes

Measured 346 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
1 56 0 286 3 0

Compared c076e4c9 with b01ea988. Open workflow run.

Changed imports (60)
Status Import Base gzip Head gzip Delta
🔴 agents/tasks#Tasks 8.9 KiB 11.9 KiB +3.0 KiB (+33.79%)
🟡 agents#routeAgentRequest 261.7 KiB 265.0 KiB +3.2 KiB (+1.24%)
🟡 agents/chat-sdk#ChatSdkStateAgent 262.8 KiB 266.1 KiB +3.3 KiB (+1.24%)
🟡 agents#unstable_callable 261.2 KiB 264.4 KiB +3.2 KiB (+1.23%)
🟡 agents#createHeaderBasedEmailResolver 261.3 KiB 264.5 KiB +3.2 KiB (+1.23%)
🟡 agents/workflows#AgentWorkflow 262.4 KiB 265.6 KiB +3.2 KiB (+1.23%)
🟡 agents#getSubAgentByName 261.4 KiB 264.6 KiB +3.2 KiB (+1.23%)
🟡 agents#AGENT_TOOL_PROGRESS_PART 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#MCP_SERVER_ID_MAX_LENGTH 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#AGENT_TOOL_MILESTONE_PART 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#MessageType 261.2 KiB 264.4 KiB +3.2 KiB (+1.23%)
🟡 agents#buildAgentUrl 261.8 KiB 265.0 KiB +3.2 KiB (+1.23%)
🟡 agents#SUB_PREFIX 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#__DO_NOT_USE_WILL_BREAK__withInvocationScope 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#buildAgentPath 261.7 KiB 264.9 KiB +3.2 KiB (+1.23%)
🟡 agents#callable 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents/chat-sdk#ChatSdkStateAdapter 263.5 KiB 266.7 KiB +3.2 KiB (+1.23%)
🟡 agents#isDurableObjectMemoryLimitReset 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#parseSubAgentPath 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#isPlatformTransientError 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#DEFAULT_AGENT_STATIC_OPTIONS 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#normalizeServerId 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents/chat-sdk#createChatSdkState 263.5 KiB 266.7 KiB +3.2 KiB (+1.23%)
🟡 agents#DurableObjectOAuthClientProvider 261.1 KiB 264.3 KiB +3.2 KiB (+1.23%)
🟡 agents#getAgentByName 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents#camelCaseToKebabCase 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents#__DO_NOT_USE_WILL_BREAK__agentContext 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents#getCurrentAgent 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents#Agent 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents#StreamingResponse 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents#SqlError 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents/chat-sdk#defaultThreadShard 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents#routeSubAgentRequest 261.3 KiB 264.5 KiB +3.2 KiB (+1.22%)
🟡 agents#isDurableObjectCodeUpdateReset 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents/workflows#WorkflowRejectedError 261.2 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents/chat-sdk#defaultKeyShard 261.2 KiB 264.4 KiB +3.2 KiB (+1.22%)
🟡 agents#isDurableObjectStorageReset 261.1 KiB 264.3 KiB +3.2 KiB (+1.22%)
🟡 agents#routeAgentEmail 261.4 KiB 264.6 KiB +3.2 KiB (+1.22%)
🟡 agents/tasks#TaskReplayDivergedError 341 B 345 B +4 B (+1.17%)
🟡 agents/mcp#SSEEdgeClientTransport 345.2 KiB 348.4 KiB +3.2 KiB (+0.93%)
🟡 agents/mcp#MCP_SERVER_ID_MAX_LENGTH 345.1 KiB 348.3 KiB +3.2 KiB (+0.93%)
🟡 agents/mcp#RPC_DO_PREFIX 345.1 KiB 348.3 KiB +3.2 KiB (+0.93%)
🟡 agents/mcp#normalizeServerId 345.1 KiB 348.3 KiB +3.2 KiB (+0.93%)
🟡 agents/mcp#RPCClientTransport 345.1 KiB 348.3 KiB +3.2 KiB (+0.93%)
🟡 agents/mcp#RPCServerTransport 345.1 KiB 348.3 KiB +3.2 KiB (+0.93%)
🟡 agents/mcp#getMcpAuthContext 345.1 KiB 348.3 KiB +3.2 KiB (+0.92%)
🟡 agents/mcp#McpAgent 345.1 KiB 348.3 KiB +3.2 KiB (+0.92%)
🟡 agents/mcp#DurableObjectEventStore 345.1 KiB 348.3 KiB +3.2 KiB (+0.92%)
🟡 agents/mcp#StreamableHTTPEdgeClientTransport 345.2 KiB 348.4 KiB +3.2 KiB (+0.92%)
🟡 agents/mcp#ElicitRequestSchema 345.1 KiB 348.3 KiB +3.2 KiB (+0.92%)
🟡 agents/mcp#WorkerTransport 348.5 KiB 351.7 KiB +3.2 KiB (+0.9%)
🟡 agents/mcp#experimental_createMcpHandler 378.7 KiB 382.0 KiB +3.2 KiB (+0.85%)
🟡 agents/mcp#createLegacyMcpHandler 378.6 KiB 381.8 KiB +3.2 KiB (+0.84%)
🟡 agents/mcp#createMcpHandler 390.9 KiB 394.1 KiB +3.1 KiB (+0.8%)
🟡 agents/tasks#NonRetryableError 238 B 239 B +1 B (+0.42%)
🟡 agents/tasks#TaskSerializationError 258 B 259 B +1 B (+0.39%)
🟡 agents/tasks#MissingTaskDefinitionError 358 B 359 B +1 B (+0.28%)
⚪ agents/tasks#TaskEventIdempotencyConflictError — 314 B —
⚪ agents/tasks#TaskRunNotFoundError — 267 B —
⚪ agents/tasks#TaskRunTerminalError — 273 B —
All 346 current runtime imports
Status Import Gzip Raw minified
🟡 agents#__DO_NOT_USE_WILL_BREAK__agentContext 264.3 KiB 1154.8 KiB
🟡 agents#__DO_NOT_USE_WILL_BREAK__withInvocationScope 264.3 KiB 1154.8 KiB
🟡 agents#Agent 264.3 KiB 1154.8 KiB
🟡 agents#AGENT_TOOL_MILESTONE_PART 264.3 KiB 1154.8 KiB
🟡 agents#AGENT_TOOL_PROGRESS_PART 264.3 KiB 1154.8 KiB
🟡 agents#buildAgentPath 264.9 KiB 1157.1 KiB
🟡 agents#buildAgentUrl 265.0 KiB 1157.5 KiB
🟡 agents#callable 264.3 KiB 1154.8 KiB
🟡 agents#camelCaseToKebabCase 264.3 KiB 1154.8 KiB
🟡 agents#createHeaderBasedEmailResolver 264.5 KiB 1155.2 KiB
🟡 agents#DEFAULT_AGENT_STATIC_OPTIONS 264.3 KiB 1154.8 KiB
🟡 agents#DurableObjectOAuthClientProvider 264.3 KiB 1154.8 KiB
🟡 agents#getAgentByName 264.3 KiB 1154.8 KiB
🟡 agents#getCurrentAgent 264.3 KiB 1154.8 KiB
🟡 agents#getSubAgentByName 264.6 KiB 1155.4 KiB
🟡 agents#isDurableObjectCodeUpdateReset 264.3 KiB 1154.8 KiB
🟡 agents#isDurableObjectMemoryLimitReset 264.3 KiB 1154.8 KiB
🟡 agents#isDurableObjectStorageReset 264.3 KiB 1154.8 KiB
🟡 agents#isPlatformTransientError 264.3 KiB 1154.8 KiB
🟡 agents#MCP_SERVER_ID_MAX_LENGTH 264.3 KiB 1154.8 KiB
🟡 agents#MessageType 264.4 KiB 1155.1 KiB
🟡 agents#normalizeServerId 264.3 KiB 1154.8 KiB
🟡 agents#parseSubAgentPath 264.3 KiB 1154.8 KiB
🟡 agents#routeAgentEmail 264.6 KiB 1155.5 KiB
🟡 agents#routeAgentRequest 265.0 KiB 1156.7 KiB
🟡 agents#routeSubAgentRequest 264.5 KiB 1155.3 KiB
🟡 agents#SqlError 264.3 KiB 1154.8 KiB
🟡 agents#StreamingResponse 264.3 KiB 1154.8 KiB
🟡 agents#SUB_PREFIX 264.3 KiB 1154.8 KiB
🟡 agents#unstable_callable 264.4 KiB 1155.0 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 147.0 KiB 632.8 KiB
⚪ agents/browser/ai#createBrowserTools 147.0 KiB 632.9 KiB
⚪ agents/browser/ai#createQuickActionTools 122.5 KiB 554.3 KiB
⚪ agents/browser/tanstack-ai#createBrowserTools 162.7 KiB 701.7 KiB
⚪ agents/channels#ChannelHost 3.3 KiB 9.1 KiB
⚪ agents/channels#consumeChunks 237 B 321 B
⚪ agents/channels#createUserIdentityStore 1.3 KiB 3.2 KiB
⚪ agents/channels#fallback 155 B 160 B
⚪ agents/channels#fallbackChannel 893 B 1.9 KiB
⚪ agents/channels#fanout 151 B 156 B
⚪ agents/channels#fanoutChannel 875 B 1.9 KiB
⚪ agents/channels#identityKey 178 B 223 B
⚪ agents/channels#isChannelMessageSurface 271 B 452 B
⚪ agents/channels#linkChannelIdentities 113 B 99 B
⚪ agents/channels#matchesPath 110 B 96 B
⚪ agents/channels#routes 307 B 507 B
⚪ agents/channels#UserIdentityConflictError 248 B 335 B
⚪ agents/channels/ai-sdk#createSendMessageTool 112.2 KiB 537.4 KiB
⚪ agents/channels/ai-sdk#toChannelChunks 112.5 KiB 538.4 KiB
⚪ agents/channels/email#email 23.5 KiB 75.4 KiB
⚪ agents/channels/email#inboundEmail 22.3 KiB 72.3 KiB
⚪ agents/channels/slack#slack 5.5 KiB 14.6 KiB
⚪ agents/channels/slack#slackWebhook 2.2 KiB 5.1 KiB
⚪ agents/channels/tanstack-ai#createSendMessageTool 16.2 KiB 68.4 KiB
⚪ agents/channels/telegram#telegram 3.8 KiB 10.3 KiB
⚪ agents/channels/telegram#telegramWebhook 1.6 KiB 3.6 KiB
⚪ agents/channels/voice#browserVoice 1010 B 2.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.2 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#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 6.4 KiB 22.1 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 5.1 KiB 16.8 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_RESUME_NONE_REASONS 2.3 KiB 8.2 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 266.7 KiB 1166.4 KiB
🟡 agents/chat-sdk#ChatSdkStateAgent 266.1 KiB 1163.8 KiB
🟡 agents/chat-sdk#createChatSdkState 266.7 KiB 1166.4 KiB
🟡 agents/chat-sdk#defaultKeyShard 264.4 KiB 1155.0 KiB
🟡 agents/chat-sdk#defaultThreadShard 264.3 KiB 1154.8 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 21.8 KiB 75.2 KiB
⚪ agents/client#AgentConnectionError 16.3 KiB 57.8 KiB
⚪ agents/client#agentFetch 19.7 KiB 69.0 KiB
⚪ agents/client#createStubProxy 16.4 KiB 57.8 KiB
⚪ agents/client#DEFAULT_CALL_TIMEOUT_MS 16.2 KiB 57.6 KiB
⚪ agents/client#isTerminalCloseEvent 16.3 KiB 57.6 KiB
⚪ agents/client#nativeCall 16.5 KiB 58.3 KiB
⚪ agents/client#NativeCallQueue 16.7 KiB 58.9 KiB
⚪ agents/client#splitCallOptions 16.3 KiB 57.7 KiB
⚪ 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.4 KiB 26.1 KiB
⚪ agents/lifecycle#LifecycleCapability 497 B 999 B
🟡 agents/mcp#createLegacyMcpHandler 381.8 KiB 1597.1 KiB
🟡 agents/mcp#createMcpHandler 394.1 KiB 1642.4 KiB
🟡 agents/mcp#DurableObjectEventStore 348.3 KiB 1455.7 KiB
🟡 agents/mcp#ElicitRequestSchema 348.3 KiB 1455.7 KiB
🟡 agents/mcp#experimental_createMcpHandler 382.0 KiB 1597.5 KiB
🟡 agents/mcp#getMcpAuthContext 348.3 KiB 1455.7 KiB
🟡 agents/mcp#MCP_SERVER_ID_MAX_LENGTH 348.3 KiB 1455.7 KiB
🟡 agents/mcp#McpAgent 348.3 KiB 1455.7 KiB
🟡 agents/mcp#normalizeServerId 348.3 KiB 1455.7 KiB
🟡 agents/mcp#RPC_DO_PREFIX 348.3 KiB 1455.7 KiB
🟡 agents/mcp#RPCClientTransport 348.3 KiB 1455.7 KiB
🟡 agents/mcp#RPCServerTransport 348.3 KiB 1455.7 KiB
🟡 agents/mcp#SSEEdgeClientTransport 348.4 KiB 1455.9 KiB
🟡 agents/mcp#StreamableHTTPEdgeClientTransport 348.4 KiB 1456.0 KiB
🟡 agents/mcp#WorkerTransport 351.7 KiB 1472.5 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/queue#Queue 3.1 KiB 9.6 KiB
⚪ agents/react#_testUtils 19.3 KiB 66.4 KiB
⚪ agents/react#useAgent 27.1 KiB 89.9 KiB
⚪ agents/react#useAgentToolEvents 21.1 KiB 73.7 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#COMPACTION_PREFIX 147 B 160 B
⚪ agents/sessions#createCompactFunction 1.7 KiB 4.0 KiB
⚪ agents/sessions#isCompactionMessage 177 B 200 B
⚪ agents/sessions#Session 2.0 KiB 6.3 KiB
⚪ agents/sessions#Sessions 9.6 KiB 34.3 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/state#State 1.1 KiB 2.4 KiB
⚪ agents/streams#DEFAULT_MAX_CHUNK_BYTES 94 B 96 B
⚪ agents/streams#sseResponse 857 B 1.6 KiB
⚪ agents/streams#StreamClosedError 175 B 212 B
⚪ agents/streams#StreamNotFoundError 215 B 276 B
⚪ agents/streams#Streams 4.3 KiB 13.9 KiB
⚪ agents/streams#StreamSerializationError 171 B 201 B
⚪ agents/tasks#DuplicateTaskStepError 328 B 463 B
⚪ agents/tasks#MAX_SERIALIZED_BYTES 190 B 232 B
🟡 agents/tasks#MissingTaskDefinitionError 359 B 536 B
🟡 agents/tasks#NonRetryableError 239 B 308 B
⚪ agents/tasks#TaskEventIdempotencyConflictError 314 B 445 B
🟡 agents/tasks#TaskReplayDivergedError 345 B 483 B
⚪ agents/tasks#TaskRunNotFoundError 267 B 351 B
⚪ agents/tasks#TaskRunTerminalError 273 B 367 B
🔴 agents/tasks#Tasks 11.9 KiB 45.5 KiB
🟡 agents/tasks#TaskSerializationError 259 B 339 B
⚪ agents/types#MessageType 211 B 365 B
⚪ agents/vite#default 353.8 KiB 1356.1 KiB
⚪ agents/voice#addSFUTracks 324 B 424 B
⚪ agents/voice#createSFUSession 255 B 306 B
⚪ agents/voice#createSFUWebSocketAdapter 331 B 429 B
⚪ agents/voice#decodeVarint 157 B 160 B
⚪ agents/voice#downsample48kStereoTo16kMono 238 B 324 B
⚪ agents/voice#encodePayloadToProtobuf 189 B 279 B
⚪ agents/voice#encodeVarint 129 B 122 B
⚪ agents/voice#extractPayloadFromProtobuf 271 B 425 B
⚪ agents/voice#iterateText 1.6 KiB 3.8 KiB
⚪ agents/voice#renegotiateSFUSession 329 B 428 B
⚪ agents/voice#SentenceChunker 550 B 1.1 KiB
⚪ agents/voice#sfuFetch 294 B 358 B
⚪ agents/voice#upsample16kMonoTo48kStereo 211 B 272 B
⚪ agents/voice#VOICE_PROTOCOL_VERSION 51 B 31 B
⚪ agents/voice#withVoice 9.5 KiB 32.7 KiB
⚪ agents/voice#withVoiceInput 4.8 KiB 16.0 KiB
⚪ agents/voice#WorkersAIFluxSTT 1.5 KiB 4.3 KiB
⚪ agents/voice#WorkersAINova3STT 1.6 KiB 4.3 KiB
⚪ agents/voice#WorkersAITTS 682 B 1.4 KiB
⚪ agents/voice/client#VOICE_PROTOCOL_VERSION 473 B 768 B
⚪ agents/voice/client#VoiceClient 10.1 KiB 31.6 KiB
⚪ agents/voice/client#WebSocketVoiceTransport 4.6 KiB 13.3 KiB
⚪ agents/voice/errors#logVoiceError 132 B 173 B
⚪ agents/voice/errors#toVoiceError 94 B 80 B
⚪ agents/voice/errors#voiceErrorMessage 125 B 111 B
⚪ agents/voice/errors#VoiceProviderError 190 B 345 B
⚪ agents/voice/react#useVoiceAgent 13.6 KiB 42.5 KiB
⚪ agents/voice/react#useVoiceInput 13.3 KiB 41.4 KiB
⚪ agents/voice/react#WebSocketVoiceTransport 7.4 KiB 21.3 KiB
⚪ agents/voice/sfu#addSFUTracks 323 B 424 B
⚪ agents/voice/sfu#createSFUSession 254 B 306 B
⚪ agents/voice/sfu#createSFUWebSocketAdapter 329 B 429 B
⚪ agents/voice/sfu#decodeVarint 155 B 160 B
⚪ agents/voice/sfu#downsample48kStereoTo16kMono 236 B 324 B
⚪ agents/voice/sfu#encodePayloadToProtobuf 188 B 279 B
⚪ agents/voice/sfu#encodeVarint 129 B 122 B
⚪ agents/voice/sfu#extractPayloadFromProtobuf 270 B 425 B
⚪ agents/voice/sfu#renegotiateSFUSession 329 B 428 B
⚪ agents/voice/sfu#sfuFetch 292 B 358 B
⚪ agents/voice/sfu#upsample16kMonoTo48kStereo 209 B 272 B
⚪ agents/voice/text#iterateText 1.6 KiB 3.8 KiB
⚪ agents/voice/text#SentenceChunker 549 B 1.1 KiB
⚪ agents/voice/types#VOICE_PROTOCOL_VERSION 51 B 31 B
⚪ agents/voice/workers-ai#WorkersAIFluxSTT 1.5 KiB 4.3 KiB
⚪ agents/voice/workers-ai#WorkersAINova3STT 1.6 KiB 4.3 KiB
⚪ agents/voice/workers-ai#WorkersAITTS 682 B 1.4 KiB
⚪ agents/websockets#CAPNWEB_TRANSPORT_QUERY 12.4 KiB 43.3 KiB
⚪ agents/websockets#CAPNWEB_TRANSPORT_VALUE 12.4 KiB 43.3 KiB
⚪ agents/websockets#capnWebTransportUrl 12.5 KiB 43.5 KiB
⚪ agents/websockets#CF_NO_PROTOCOL_KEY 12.4 KiB 43.3 KiB
⚪ agents/websockets#CF_READONLY_KEY 12.4 KiB 43.3 KiB
⚪ agents/websockets#isCapnWebTransportUpgrade 12.5 KiB 43.4 KiB
⚪ agents/websockets#registerInternalConnectionKeys 12.4 KiB 43.4 KiB
⚪ agents/websockets#WebSockets 20.0 KiB 69.3 KiB
🟡 agents/workflows#AgentWorkflow 265.6 KiB 1159.5 KiB
🟡 agents/workflows#WorkflowRejectedError 264.3 KiB 1155.0 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].

@AntoniTok
AntoniTok marked this pull request as ready for review September 24, 2026 12:27

@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 potential issues.

Devin Review

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.

🔴 Slow routed failures strand pending runs

When routed onJob dispatch exceeds five seconds and later fails, it completes the wake before observing the failure. trackAlarmWork does not retry ordinary RPC failures, leaving an unclaimed facet run without a root wake.

(Refers to this code)

Learn more

A routed run lives on a facet, but its alarm job lives on the root. The root races the dispatch RPC against a five-second timer. When the timer wins, the root returns an empty job outcome; applyOutcome deletes the job unless the facet has already pushed a newer wake. If the RPC then fails before claiming the run, trackAlarmWork observes the rejection but only applies recovery for memory-limit resets. The pending run remains durable without a wake.

Example: Root dispatch to a cold facet takes six seconds during startup, then the facet's startup hook rejects. The five-second budget deletes its root wake. No claim or replacement wake was written, so the pending run never starts.

Recommended fix: Preserve or defer the root job until the facet confirms it has claimed the run or pushed its own successor wake. For a detached RPC failure, explicitly restore a wake from the authoritative facet state; do not treat a timed-out dispatch as completed solely because its RPC remains outstanding.

Devin Review


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

Comment on lines +32 to +35
Agent restores persisted facet routing identity synchronously during
construction, before these capability hooks run. Its host startup phase then
hydrates facet connection state without changing the route identity capabilities
already observed.

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.

🔍 Alarm design retains obsolete contribution model

The Lifecycle design still describes getNextAlarm() contributions, while the updated Tasks design uses queue jobs. Its scheduling guidance needs reconciliation.

Devin Review


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

Comment on lines +43 to +52
## Public API

```ts
await tasks.sendEvent(
runId,
"approval",
{ approved: true },
{ idempotencyKey: "approval:1" }
);
```

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.

🔍 Design record duplicates API reference

The new design record repeats API usage and parameter details already covered in the user guide. Consider keeping its focus on architecture and tradeoffs.

Devin Review


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

@AntoniTok AntoniTok self-assigned this Sep 25, 2026
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