npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@yansirplus/backend-cloudflare-do

v0.5.16

Published

<!-- generated by scripts/generate-docs.mjs; edit docs/surface.json and docs/packages/backend-cloudflare-do.md -->

Readme

@agent-os/backend-cloudflare-do

Purpose

Cloudflare Durable Object backend for agentOS: app facade, DO storage, transactions, alarms, SSE streaming, dispatch delivery, and Cloudflare binding materialization.

Public API Status

Backend package. It is the only substrate package that imports cloudflare:workers.

Invariant

Cloudflare-specific APIs stay in this backend. Shared kernel/runtime packages must not import Durable Object state, Worker bindings, or alarm APIs.

Application code registers app-owned durable triggers only through the facade configuration. defineAgentDO({ triggers }) is the Cloudflare DO construction surface for trigger registration. Pure triggers are registered as an array. Backend-bound triggers use triggers(ctx) where ctx exposes env, scope, and DO-local sql; they may close over ctx.sql for app-owned provider idempotency or custom tables. Materialized current state should use defineAgentDO({ projections }) instead of trigger-local projection writes. The backend builds registries, validates duplicate kinds, and uses those same registries for submit, drain, stream, and projection operations. Apps must not import runtime-core, due-work, SQL helpers, inserted-event helpers, or backend state internals.

agent.enqueueTrigger({ triggerKind, payload, at }) is the app-facing trigger submit primitive. It is a public projection of the backend durable-trigger commit path: registry lookup happens before any ledger, due-work, or alarm write.

defineAgentDO({ domains, tools }) boot-validates external tool execution domains. Deterministic tools need no domain declaration. External tools must reference a declared domain; duplicate declarations or host domains without explicit environment allowlists fail during lowering, before submit.

agent.cancelTrigger({ triggerKind, intentEventId, reason }) is the app-facing best-effort cancellation primitive. It is registry-closed: unknown trigger kind fails before ledger, due-work, or alarm mutation. Triggers declared with cancellation: "ignored" return ignored before reading or mutating due-work. Cooperative pending rows are terminally cancelled in one transaction. Running rows record a durable cancel request and abort the active in-isolate AbortController when present; if the acquire already crossed a non-cancellable external boundary, normal commit may still win. Expired claims redrive automatically, and a redrive that sees a durable cancel request starts with AcquireCtx.signal.aborted === true.

The backend adds claim/cancel columns to due_work, but the payload remains the intent pointer { intentEventId }. due_work is a mechanical buffer, not an audit source. Cancellation visibility comes from trigger-owned commitCancelled facts.

Production drain is alarm-owned. Deterministic drain helpers are package-local test fixtures only; production app routes must not call them and no public package subpath exports them.

Durable Object RPC named-stub helpers are exported from @agent-os/backend-cloudflare-do/do-rpc. Keep them out of the root backend barrel so browser/consumer protocol types can import the helper without pulling in the Durable Object implementation graph.

Backend primitives stop at append-only ledger order, trigger identity, tx-local ledger reads, and materialized projection tables. Custom product SQL tables and provider idempotency key mappings stay in application code until at least two independent apps or adapters prove the same shape should become substrate. Factories must be idempotent in resource acquisition: Durable Object eviction can cause triggers(ctx) to run again, and the backend does not promise a stable factory call count.

defineAgentDO({ projections }) registers materialized projection declarations. Every ledger insert applies matching projection reducers in the same Durable Object transaction. Reducer failure rolls back the ledger insert. projectionStatus reports version mismatch as needs_rebuild; projectionRebuild is explicit and replays rows from the ledger. There is no hidden auto-repair.

defineAgentDO({ streams }) registers attached stream handlers. attachStream returns WebSocket for mode: "bidi" and SSE for mode: "output_only"; cancelStream({ streamRef, reason }) is the explicit output-only cancel surface. WebSocket/SSE disconnect only detaches transport. It does not write a cancelled fact. v1 streams do not support reconnect/resume or hibernation; an active WebSocket pins the Durable Object alive and costs DO uptime. Long workspace sessions need a later durable stream log or hibernation substrate.

LLM routes are dispatched through the Effect AI transport. Submit provider-call timeouts pass runtime cancellation through the HTTP transport and still write one terminal ledger abort fact. Provider-side cancellation and billing semantics remain provider-specific; the ledger terminal fact is the substrate-owned bookkeeping boundary.

stuckTriggers(now) is observability over expired claims. It is not a repair entry point; ordinary drain redrives expired claims by claiming the same due row with acquireMode: "redrive".

Cross-DO strong consistency is not provided by this backend. Apps choose a single DO scope for strong transaction boundaries, or model cross-scope work as saga/compensation facts until a future coordination substrate exists.

Minimal Usage

Create a DO class from one app-facing facade config. bindings is the only material declaration surface; LLM routes reference symbolic ids.

import { credential, defineAgentDO, endpoint, openAIChat } from "@agent-os/backend-cloudflare-do";
import { defineTool } from "@agent-os/kernel/tools";
import { Schema } from "effect";

const lookup = defineTool({
  name: "lookup",
  description: "Look up a symbolic key.",
  args: Schema.Struct({ key: Schema.String }),
  authority: "read",
  admit: () => ({ ok: true }),
  execute: ({ key }) => ({ value: key }),
});

export const AgentDO = defineAgentDO<Env>({
  bindings: [
    endpoint("llm").from((env) => env.LLM_ENDPOINT),
    credential("llm-key").from((env) => env.LLM_KEY),
  ],
  llms: {
    default: openAIChat({
      model: "gpt-4.1-mini",
      endpoint: "llm",
      credential: "llm-key",
    }),
  },
  tools: [lookup],
});

App triggers use runtime trigger types and the same facade registration path as built-in triggers. Pure triggers pass as arrays:

import { defineAgentDO } from "@agent-os/backend-cloudflare-do";
import type { AnyDurableTrigger } from "@agent-os/runtime";

const appTriggers = [imageScanTrigger] satisfies ReadonlyArray<AnyDurableTrigger>;

export const AgentDO = defineAgentDO<Env>({
  bindings: [],
  triggers: appTriggers,
});

Materialized projections use runtime declarations and the facade registration path:

import { defineProjection } from "@agent-os/runtime";

const runs = defineProjection({
  kind: "run.workflow",
  version: 1,
  eventKinds: ["run.requested", "run.completed"],
  identity,
  state,
  identityKey,
  identify,
  initial,
  reduce,
});

export const AgentDO = defineAgentDO<Env>({
  bindings: [],
  projections: [runs],
});

Omit llms for event-only facades. They keep emit, schedule, dispatch, on, bindings, and extensions, but do not expose submit. Full SubmitSpec remains on the low-level createAgentDurableObject API.

External consumer apps should install the published internal npm packages, not source workspace package paths. Use the public package entrypoints only; backend source subpaths remain outside the contract. Published packages include dist JavaScript and declaration files for NodeNext and Bundler consumers.

Workspace jobs use Cloudflare host helpers instead of product-owned Sandbox glue. createCloudflareSandboxWorkspaceEnvResolver({ binding, transport, cwd, scopePrefix, cleanup, shellFileOperationTimeoutMs }) owns the Cloudflare Sandbox binding/client composition, validates the DO namespace and client shape, pins normalizeId: true, disables implicit default sessions, and returns one WorkspaceEnv lease per scope/runId. Products choose the binding and declare cleanup policy; they do not configure sandbox sessions/transports or mint workspace refs. Shell-derived file operation timeout policy comes from @agent-os/workspace-env-cloudflare, and the resolver only forwards the single declared override.

Consumers that want the complete Cloudflare workspace-job bridge can install installCloudflareWorkspaceJobProfile({ workspaceResolver, readProjection }). The profile only composes existing helpers: workspace resolver, workspace-op provider install, AG-UI SSE response helpers, and workspace-job HTTP response. Its default response reader expects the runtime-owned sanitized observability projection, so consumer responses do not expose raw submitRunId or runtime numeric run ids.

AG-UI SSE responses are also host composition. @agent-os/ag-ui projects ledger events into AG-UI frames, @agent-os/sse-http stays a generic SSE transport, and createCloudflareLedgerAgUiSseResponse combines them for Cloudflare callers. It accepts either an already chunked ledger SSE stream or a Web Response; products do not need local ledger-response chunk adapters.

Verification

cd packages/backends/cloudflare-do
bun run test