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

@cuylabs/agent-runtime-dapr

v5.0.0

Published

Dapr-backed workload runtime and host adapters for @cuylabs/agent-runtime

Downloads

3,124

Readme

@cuylabs/agent-runtime-dapr

Run AI agents with Dapr durability — crash-safe workflows, persistent state, and scheduled jobs. Built on @cuylabs/agent-core and @cuylabs/agent-runtime.

Package Boundary

Use @cuylabs/agent-runtime-dapr when you want Dapr-backed infrastructure for workloads or agents:

  • Dapr runtime driver for scheduled and manually triggered jobs
  • durable workflow decomposition for agent turns
  • persistent execution snapshots and checkpoints
  • hosted HTTP runners and multi-agent hosts
  • Dapr service invocation and workflow clients

This package does not redefine agent semantics or generic workload orchestration. It builds on:

  • agent-core for task and turn execution semantics
  • agent-runtime for the outer workload runtime contract

When paired with @cuylabs/agent-server, this package should sit behind the same session/turn surface rather than replacing it. Use createDaprAgentServerAdapter(runner) when you want agent-server transports like WebSocket to route turns, steering, follow-ups, and interactive requests through the same Dapr workflow runtime as the hosted HTTP routes.

Why This Package Is Bigger Than A Simple Driver

agent-runtime-dapr has two roles:

  1. It implements shared runtime contracts from @cuylabs/agent-runtime
  2. It exposes Dapr-native helpers that should stay outside the shared contract

The first category is the portability seam:

  • DaprRuntimeDriver implements RuntimeDriver
  • DaprOrchestratorRunStore implements OrchestratorRunStore
  • createDaprWorkloadRuntime(...) builds a WorkloadRuntime with those pieces

The second category is intentionally Dapr-specific:

  • workflow clients and workflow activities
  • HTTP host/runners
  • sidecar job callbacks
  • execution checkpoint persistence
  • cross-service invocation helpers

Those features are not drift in the base runtime contract. They are adapter surfaces that exist because Dapr offers more than a generic scheduler/store.

Why Dapr?

Dapr provides the durable infrastructure while your agent owns the intelligence:

Your code (agent + tools)  ←→  Dapr sidecar (state, workflows, jobs)
  • Crash recovery — if the process dies mid-turn, Dapr resumes from the last checkpoint.
  • Persistent state — execution history, sessions, and checkpoints survive restarts.
  • Scheduled jobs — trigger agent work on a cron schedule via Dapr Jobs API.
  • Zero vendor lock-in — Dapr runs anywhere: local Docker, Kubernetes, cloud.

Installation

pnpm add @cuylabs/agent-runtime-dapr @cuylabs/agent-core @dapr/dapr

Focused imports are also available when you want the package surface to mirror the internal modules:

import { createDaprExecutionObserver } from "@cuylabs/agent-runtime-dapr/execution";
import { createDaprAgentRunner } from "@cuylabs/agent-runtime-dapr/host";
import { DaprWorkflowClient } from "@cuylabs/agent-runtime-dapr/workflow";

Under the hood, this package now exposes two layers:

  • createDaprWorkloadRuntime(...) for any workload that fits the neutral agent-runtime contract
  • createDaprAgentRuntime(...) and createDaprAgentRunner(...) as the agent-core-specific adapters built on top of that

The rule is:

  • if your code only needs portable scheduling/orchestration, target agent-runtime
  • if your code wants Dapr durability or Dapr host capabilities, opt into this package explicitly

Tool Hosts And Durable Turns

ToolHost configuration still belongs on the agent, not on the Dapr runner.

import { WorkflowRuntime } from "@dapr/dapr";
import { createAgent } from "@cuylabs/agent-core";
import { dockerHost } from "@cuylabs/agent-sandbox-docker";
import { createDaprAgentRunner } from "@cuylabs/agent-runtime-dapr";

const agent = createAgent({
  model,
  host: dockerHost({ image: "node:22", workspaceDir: "/workspace" }),
  tools,
});

const runner = createDaprAgentRunner({
  agent,
  name: "my-agent",
  workflowRuntime: new WorkflowRuntime(),
});

In direct mode and durable mode, host-backed tools use the same agent-core execution seam. Dapr persists workflow state around the tool call, but the tool still executes through agent.getHost().

For the full explanation, see Tool Hosts In Durable Workflows.

Durable Context Compaction

Durable turns compact at the same model-step boundary as direct Agent.chat(), but they do it through workflow state instead of the in-process chat loop. Before a model-step activity runs, the workflow can call a context-compaction activity. That activity applies the agent's normal compaction policy to the serialized workflow messages, persists the compaction entry to session storage, returns the compacted message snapshots, and the workflow checkpoints context-compaction-finish before continuing.

This keeps the durable path crash-safe without importing direct-loop internals: direct execution uses ChatModelStepSnapshot; Dapr durable execution uses AgentWorkflowTurnState.messages and AgentWorkflowModelStepPlan.

Quick Start

Step 1: Define your agent

import { createAgent, Tool } from "@cuylabs/agent-core";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

const greetTool = Tool.define("greet", {
  description: "Greet someone by name.",
  parameters: z.object({ name: z.string() }),
  execute: async ({ name }) => ({
    title: `Greeted ${name}`,
    output: `Hello, ${name}!`,
    metadata: {},
  }),
});

const agent = createAgent({
  model: openai("gpt-4o"),
  cwd: process.cwd(),
  systemPrompt: "You are a helpful assistant. Use the greet tool when asked.",
  maxSteps: 10,
  tools: [greetTool],
});

Step 2: Create the runner

import { WorkflowRuntime } from "@dapr/dapr";
import { createDaprAgentRunner } from "@cuylabs/agent-runtime-dapr";

const runner = createDaprAgentRunner({
  agent,
  name: "my-agent",
  workflowRuntime: new WorkflowRuntime(),
});

await runner.serve({ port: 3000 });

Step 3: Start with Dapr

dapr run --app-id my-agent --dapr-grpc-port 50001 -- npx tsx my-agent.ts

Step 4: Send requests

# Direct execution (synchronous)
curl -s http://localhost:3000/agents/run \
  -H "Content-Type: application/json" \
  -d '{"message": "Greet Carlos"}' | jq

# Durable run (async, crash-recoverable)
curl -s http://localhost:3000/agents/run-durable \
  -H "Content-Type: application/json" \
  -d '{"message": "Greet Carlos"}' | jq

That's it. The runner handles workflow registration, HTTP server, runtime wiring, logging, and graceful shutdown.

Multi-Agent Host

Run multiple agents in a single process with createDaprMultiAgentRunner():

import { createDaprMultiAgentRunner } from "@cuylabs/agent-runtime-dapr";

const runner = createDaprMultiAgentRunner({
  agents: [
    { agent: greeter, name: "greeter" },
    { agent: calculator, name: "calculator" },
  ],
  workflowRuntime: new WorkflowRuntime(),
});

await runner.serve({ port: 3000 });

Each agent gets its own workflow definition, execution store, and logging — but they share a single Dapr sidecar, workflow worker, and HTTP port.

# Target a specific agent by URL path
curl -s http://localhost:3000/agents/greeter/run \
  -H "Content-Type: application/json" \
  -d '{"message": "Say hi to Alice"}' | jq

Two Execution Modes

Every agent host exposes two ways to run a turn:

| Mode | Endpoint | Behavior | |------|----------|----------| | Direct | POST /agents/run | Synchronous. Returns result in the HTTP response. State is persisted, but execution is not crash-recoverable. | | Durable | POST /agents/run-durable | Asynchronous. Returns 202 with an instanceId immediately. The turn runs as a Dapr workflow — crash-safe with activity-level checkpoints. |

The workflow decomposes each turn into five activities:

input-commit → model-step → tool-call → step-commit → output-commit

Each activity is a checkpoint. If the process crashes after tool-call, Dapr replays from that point — the model call and tool execution don't repeat.

Team Coordination

createDaprTeamRunner() applies the same split to multi-agent coordination:

  • Vocabulary:

    • run() = direct, in-process coordinator execution
    • runDurable() = start the durable root coordinator workflow
    • child workflow = one durable member task execution started by the root
    • waitForDurableRun() = external polling helper for the root workflow
  • run(prompt) keeps the coordinator loop in-process while using Dapr-backed stores.

  • runDurable(prompt, options?) starts a durable coordinator workflow and returns { teamId, workflowName, coordinatorSessionId, instanceId }.

  • getDurableRun(instanceId) reads workflow status and extracts the final coordinator result when present.

  • waitForDurableRun(instanceId, options?) is the explicit edge-level wait helper when you want to block for completion.

The HTTP surface mirrors that programmatic contract:

  • POST /team/run
  • POST /team/run-durable
  • GET /team/workflows/:instanceId

HTTP API Reference

| Method | Path | Description | |--------|------|-------------| | GET | /health | Liveness check | | GET | /healthz | Liveness alias | | GET | /ready | Readiness check | | GET | /readyz | Readiness alias | | GET | /agents | List registered agents | | POST | /agents/run | Run agent turn (direct) | | POST | /agents/run-durable | Run agent turn (durable) | | POST | /agents/:id/run | Run specific agent (direct) | | POST | /agents/:id/run-durable | Run specific agent (durable) | | GET | /agents/inputs | List durable human input requests for the single hosted agent | | GET | /agents/inputs/:requestId | Get durable human input request for the single hosted agent | | POST | /agents/inputs/:requestId/respond | Resolve durable human input request for the single hosted agent | | GET | /agents/approvals | List durable approval requests for the single hosted agent | | GET | /agents/approvals/:requestId | Get durable approval request for the single hosted agent | | POST | /agents/approvals/:requestId/respond | Resolve durable approval for the single hosted agent | | GET | /agents/:id/inputs | List durable human input requests | | GET | /agents/:id/inputs/:requestId | Get durable human input request | | POST | /agents/:id/inputs/:requestId/respond | Resolve durable human input request | | GET | /agents/:id/approvals | List durable approval requests | | GET | /agents/:id/approvals/:requestId | Get durable approval request | | POST | /agents/:id/approvals/:requestId/respond | Resolve durable approval with allow, deny, or remember | | GET | /agents/:id/executions/:sessionId | Get execution details | | GET | /agents/:id/executions/:sessionId/checkpoints | Get execution checkpoints | | GET | /agents/:id/workflows/:instanceId | Get workflow state | | POST | /agents/:id/workflows/:instanceId/terminate | Terminate a running workflow | | POST | /agents/steer | Inject steering message (single-agent host) | | POST | /agents/:id/steer | Inject steering message into running workflow | | POST | /agents/follow-up | Queue follow-up message (single-agent host) | | POST | /agents/:id/follow-up | Queue follow-up for after current turn | | GET | /agents/follow-ups | List follow-up requests (single-agent host) | | GET | /agents/:id/follow-ups | List follow-up requests | | GET | /agents/:id/events/:sessionId | SSE stream of agent events | | GET | /dapr/subscribe | Dapr pub/sub subscription declaration | | POST | /dapr/:topic | Dapr pub/sub event delivery callback | | POST | /job/:name | Handle Dapr scheduled job trigger |

Runner Options

createDaprAgentRunner() accepts these options:

| Option | Required | Default | Description | |--------|----------|---------|-------------| | agent | Yes | — | The Agent instance | | name | No | agent.name | Agent ID in the Dapr ecosystem | | workflowRuntime | Yes | — | new WorkflowRuntime() from @dapr/dapr | | daprHttpEndpoint | No | http://$DAPR_HOST:$DAPR_HTTP_PORT | Sidecar HTTP endpoint | | stateStoreName | No | "statestore" | Dapr state store component | | workflowComponent | No | "dapr" | Dapr workflow component name | | driverOptions | No | — | Advanced Dapr runtime driver options: API token, retries, timeouts, custom fetch, sidecar verification | | observers | No | [] | Extra execution lifecycle observers | | logging | No | true | Enable/disable console logging | | logPrefix | No | [${name}] | Log line prefix | | aliases | No | [] | Alternative names for agent lookup |

The runner returns an object with:

  • start() — start runtime and workflow worker
  • createHttpHandler(options?) — build the Dapr host HTTP handler for embedding in a custom server
  • agentServerCapabilities() — capabilities patch describing the Dapr-backed runtime
  • serve(options?) — start HTTP server, block on SIGINT/SIGTERM
  • run(message, options?) — run a task programmatically
  • runDurable(message, options?) — start a durable turn programmatically
  • stop() — graceful shutdown

serve(options?) also accepts lightweight UI-hosting options:

  • staticDir — serve static files before the built-in agent routes
  • indexFile — file served for / when staticDir is configured
  • extraRoutes — exact-match custom routes layered ahead of static assets and agent APIs

Runner startup is transactional: if the workflow worker fails to start, the runtime is stopped before the error is returned.

Retention and Cleanup

Durability needs explicit retention. The package exposes cleanup methods on both durable stores so hosts can trim history without reaching into raw Dapr state.

await runtimeBundle.executionStore.cleanup({
  maxAgeMs: 7 * 24 * 60 * 60 * 1000,
  maxCheckpointsPerExecution: 20,
});

await runStore.cleanup({
  maxAgeMs: 30 * 24 * 60 * 60 * 1000,
  maxRuns: 10_000,
});

The intended production pattern is to schedule cleanup as ordinary runtime work rather than relying on ad hoc scripts.

Going Deeper

For advanced use cases (custom workflow shapes, custom HTTP handlers, cross-service invocation), the package also exports the lower-level building blocks:

| Helper | Purpose | |--------|---------| | createDaprAgentWorkflowHost() | Wrap an Agent into a workflow host | | createDaprAgentServerAdapter() | Bridge @cuylabs/agent-server to the Dapr workflow runtime | | createDaprWorkflowWorker() | Register workflow hosts in a WorkflowRuntime | | createDaprWorkloadRuntime() | Dapr-backed runtime bundle for generic workloads | | createDaprAgentRuntime() | Create runtime bundle (scheduling + runner + store) | | startDaprHostHttpServer() | Start the HTTP control surface | | DaprWorkflowClient | Manage workflow instances via HTTP API | | DaprRuntimeDriver | Low-level RuntimeDriver for Dapr state | | DaprExecutionStore | Persistent execution snapshots and checkpoints | | createDaprExecutionObserver() | Persist execution events to the store | | createDaprLoggingObserver() | Console logging for execution lifecycle | | DaprServiceInvoker | Call agents across Dapr service boundaries | | invokeRemoteAgentRun() | Convenience wrapper for cross-service agent calls | | createRemoteAgentTool() | Create a tool that invokes a remote Dapr agent | | createDaprWorkflowApprovalRuntime() | Durable approval runtime | | createDaprWorkflowHumanInputRuntime() | Durable human-input runtime | | createDaprWorkflowSteerRuntime() | Durable steering runtime | | createDaprWorkflowFollowUpRuntime() | Durable follow-up runtime | | createDaprHostHttpHandler() | Build Request → Response handler for custom servers | | createEventBus() | In-process event bus for SSE streaming | | createDaprPubSubEventBridge() | Multi-instance event fan-out via Dapr pub/sub | | createDaprDispatchRuntime() | Dapr-backed async dispatch runtime | | createDaprTeamRunner() | Multi-agent team runner with durable coordination |

See the docs/ folder for detailed guides:

Runtime Boundary

The package layering is:

  • agent-core: agent turn/task semantics, EventBus interface, AgentSignal
  • agent-runtime: generic workload orchestration contract
  • agent-runtime-dapr: Dapr-backed implementation of that contract, plus DaprPubSubEventBridge for multi-instance event fan-out

agent-runtime-dapr integrates with those lower layers in two different ways:

  • outer workload path: it uses agent-runtime to schedule, dispatch, retry, and observe jobs
  • inner durable turn path: it uses agent-core execution primitives to split one agent turn into durable workflow activities such as model-step, tool-call, step-commit, and output-commit

So this package does not only sit "on top of" agent-runtime. It also reaches into the reusable turn/task surface exported by agent-core when it needs fine-grained durable execution.

If you are running ordinary jobs or non-agent workloads, use createDaprWorkloadRuntime(...).

If you are running agent-core tasks, use createDaprAgentRuntime(...) or the higher-level createDaprAgentRunner(...).

Examples

The examples/ directory has complete, runnable scripts:

| Script | Description | |--------|-------------| | 01-simple-agent.ts | Minimal agent with one tool | | 02-coding-agent.ts | File-system tools via @cuylabs/agent-code | | 03-multi-agent.ts | Two agents in one process | | 04-crash-recovery.ts | Process crash mid-turn, Dapr auto-resumes | | 05-tracing-zipkin.ts | OpenTelemetry tracing → Zipkin | | 06-tracing-phoenix.ts | OpenTelemetry tracing → Arize Phoenix | | 07-maintenance-host.ts | Retention jobs + Prometheus metrics |

See the examples README for step-by-step setup and usage.

Production Notes

  • DaprRuntimeDriver verifies the sidecar is reachable on startup (verifySidecarOnStart, default true)
  • Per-request timeout: 15s default (requestTimeoutMs)
  • Automatic retries for transient failures (maxRequestRetries, default 2)
  • Supports Dapr API token authentication (dapr-api-token header)
  • GET /ready and GET /readyz report runtime, worker, sidecar, and state-store readiness
  • Dapr Jobs API calls are isolated behind an internal adapter so scheduler changes stay local to the Dapr package
  • Use DaprExecutionStore.cleanup(...) and DaprOrchestratorRunStore.cleanup(...) to enforce retention budgets
  • For a concrete operational service, see examples/07-maintenance-host.ts
  • For containers: run one sidecar per app process, point daprHttpEndpoint at the local sidecar

License

Apache-2.0