AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Resonate Basic Ephemeral World Usage Typescript

skill-resonatehq-resonate-skills-resonate-basic-ephemeral-world-usage-typescript · by resonatehq

Core patterns for using Resonate Client APIs in the Ephemeral World - initialization, registration, top-level invocations, promise management, and dependency injection. Use this for application entry points and orchestration code outside of durable functions.

— No reviews yet
0 installs
42 views
0.0% view→install

Install

$ agentstack add skill-resonatehq-resonate-skills-resonate-basic-ephemeral-world-usage-typescript

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ● Environment & secrets Used
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-resonatehq-resonate-skills-resonate-basic-ephemeral-world-usage-typescript)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Resonate Basic Ephemeral World Usage Typescript? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Resonate Basic Ephemeral World Usage

> SDK version: This skill reflects @resonatehq/sdk v0.10.2 (current on npm).

Overview

The Ephemeral World is where your application code lives that is NOT inside generator functions. This is where you:

  • Initialize the Resonate Client
  • Register functions
  • Start top-level executions
  • Manage promises externally
  • Set dependencies

Key distinction: Ephemeral World code uses resonate (the Client). Durable World code uses ctx (the Context).

Mental Model

┌─────────────────────────────────────────┐
│      EPHEMERAL WORLD (Stateless)        │
│                                         │
│  • Resonate Client initialization      │
│  • Function registration                │
│  • Top-level run/rpc calls             │
│  • Promise management (create/resolve)  │
│  • Dependency injection                 │
│                                         │
│  Uses: resonate.run(), resonate.rpc()  │
└─────────────────────────────────────────┘
              ↓ invokes ↓
┌─────────────────────────────────────────┐
│       DURABLE WORLD (Stateful)          │
│                                         │
│  • Generator functions with yield*      │
│  • Context APIs for sub-invocations    │
│  • Durable coordination and recovery    │
│                                         │
│  Uses: ctx.run(), ctx.rpc()            │
└─────────────────────────────────────────┘

Core Rule

You CANNOT use Context APIs in the ephemeral world, and you CANNOT use Client APIs in durable functions.

Initialization Patterns

Local Development Mode (Zero Dependencies)

import { Resonate } from "@resonatehq/sdk";

// No URL = local in-memory mode
const resonate = new Resonate();

Key behaviors:

  • In-memory storage (no persistence across restarts)
  • No external dependencies required
  • Perfect for testing and development
  • CRITICAL: When using new Resonate() without a URL, function arguments are wrapped in an array

Connected to Resonate Server

import { Resonate } from "@resonatehq/sdk";

const resonate = new Resonate({
  url: "http://localhost:8001",
  group: "workers",
  auth: {
    username: "user",
    password: "pass"
  }
});

When to use:

  • Multiple worker processes
  • Persistence across restarts
  • Distributed execution
  • Production deployments

Environment Variables

export RESONATE_URL="http://localhost:8001"
export RESONATE_USERNAME="user"
export RESONATE_PASSWORD="pass"
const resonate = new Resonate(); // Picks up env vars automatically

Resolution order:

  1. Constructor arguments (highest priority)
  2. Environment variables
  3. Built-in defaults (local mode)

Registration Patterns

Correct: Register with Function Reference

function* myWorkflow(ctx: Context, arg: string) {
  return `Hello ${arg}`;
}

// ✅ CORRECT
const stub = resonate.register(myWorkflow);

Wrong: Custom String Names

// ❌ WRONG - Don't use custom string names
resonate.register("my_workflow", myWorkflow);

// ❌ WRONG - Don't use snake_case
resonate.register("db_get_user", dbGetUser);

Why this matters:

  • Resonate uses function names for routing
  • Custom names break RPC resolution
  • Snake_case violates naming conventions

Using the Returned Stub

const workflowStub = resonate.register(myWorkflow);

// Later, invoke directly via stub
const result = await workflowStub.run("execution-1", "World");

Top-Level Invocation Patterns

Run (Local Execution)

// Blocks until result is ready
const result = await resonate.run(
  "execution-id",
  myWorkflow,
  "arg1",
  "arg2"
);

Use when:

  • Running in the same process
  • Need the result immediately

Begin Run (Non-Blocking Local)

// Returns immediately with handle
const handle = await resonate.beginRun(
  "execution-id",
  myWorkflow,
  "arg1"
);

// Do other work...

// Get result later
const result = await handle.result();

Use when:

  • Starting work but not waiting immediately
  • Running multiple executions concurrently

RPC (Remote Execution)

// Blocks until remote execution completes
const result = await resonate.rpc(
  "execution-id",
  "myWorkflow", // String name!
  "arg1",
  resonate.options({
    target: "poll://any@workers"
  })
);

Critical differences from run:

  • Uses string function name (not function reference)
  • Requires target specification
  • Executes in different process/group

Begin RPC (Non-Blocking Remote)

const handle = await resonate.beginRpc(
  "execution-id",
  "myWorkflow",
  "arg1",
  resonate.options({
    target: "poll://any@workers"
  })
);

const result = await handle.result();

Options Pattern

await resonate.run(
  "execution-id",
  myWorkflow,
  "arg1",
  resonate.options({
    timeout: 60_000,        // 60 seconds in ms
    tags: { userId: "123" },
    version: 1
  })
);

Available options:

  • timeout: Max execution time in milliseconds
  • tags: Metadata for filtering/searching
  • version: Function version for schema evolution
  • target: RPC routing (poll://any@group-name)

Promise Management

Create Promise

await resonate.promises.create(
  "approval-123",
  Date.now() + 30000 // timeout 30s from now
);

Get Promise

const promise = await resonate.promises.get("approval-123");

Resolve Promise (Human-in-the-Loop)

// Elsewhere (webhook, UI, CLI):
await resonate.promises.resolve("approval-123", {
  data: JSON.stringify({
    approved: true,
    approver: "alice@example.com",
  }),
});

Reject Promise

await resonate.promises.reject("approval-123", {
  data: JSON.stringify({
    reason: "Insufficient funds",
  }),
});

Cancel Promise

await resonate.promises.cancel("approval-123");

> Migration note (v0.10.0 → v0.10.1+): The single resonate.promises.settle(id, state, value) method was split into resolve, reject, and cancel in v0.10.1. The old settle() is private in v0.10.2. Update any code that calls .settle() directly.

Dependency Injection

Set Dependencies

import { createClient } from "@supabase/supabase-js";

const db = createClient(url, key);

// Set in ephemeral world
resonate.setDependency("db", db);
resonate.setDependency("config", { apiKey: "..." });

Rules:

  • Only set dependencies in ephemeral world
  • Set before registering functions that use them
  • Pass non-serializable objects (DB connections, etc.)

Access in Durable World

function* myWorkflow(ctx: Context, userId: string) {
  // Get in durable world
  const db = ctx.getDependency("db");

  const user = yield* ctx.run(async (_ctx, id) => {
    return await db.from("users").select().eq("id", id).single();
  }, userId);

  return user;
}

Scheduling

const schedule = await resonate.schedule(
  "daily-report",
  "0 8 * * *",      // Every day at 8am
  generateReport,
  "arg1"
);

// Later, delete the schedule
await schedule.delete();

Subscription (Get Existing Execution)

// Subscribe to existing execution
const handle = await resonate.get("execution-id");

// Wait for result
const result = await handle.result();

// Check if done
const isDone = await handle.done();

Use when:

  • Checking status of long-running executions
  • Reconnecting to executions after restart
  • Monitoring from external systems

Common Pitfalls

1. Mixing Ephemeral and Durable APIs

// ❌ WRONG - Using ctx in ephemeral world
async function main() {
  const result = await ctx.run(myFunc);  // ctx doesn't exist here!
}

// ✅ CORRECT
async function main() {
  const result = await resonate.run("id", myFunc);
}

2. Using Client APIs Inside Generators

// ❌ WRONG
function* myWorkflow(ctx: Context) {
  const result = await resonate.run("id", otherFunc);  // Wrong API!
}

// ✅ CORRECT
function* myWorkflow(ctx: Context) {
  const result = yield* ctx.run(otherFunc);
}

3. Missing Target on RPC

// ❌ WRONG - RPC needs target
await resonate.rpc("id", "funcName", "arg");

// ✅ CORRECT
await resonate.rpc(
  "id",
  "funcName",
  "arg",
  resonate.options({ target: "poll://any@workers" })
);

4. Forgetting In-Memory Mode Wraps Args in Array

// In-memory mode (no URL)
const resonate = new Resonate();

function* workflow(ctx: Context, state: MyState) {
  // ❌ WRONG - state will be [MyState], not MyState!
  console.log(state.someField);  // undefined!
}

// ✅ CORRECT - Handle array wrapping in local mode
function* workflow(ctx: Context, state: any) {
  const actualState = Array.isArray(state) ? state[0] : state;
  console.log(actualState.someField);  // Works!
}

5. Using Async Instead of Generators

// ❌ WRONG - Durable functions must be generators
async function* workflow(ctx: Context) { }

// ✅ CORRECT
function* workflow(ctx: Context) { }

Complete Example

import "dotenv/config";
import { Resonate, type Context } from "@resonatehq/sdk";

// Initialize client (ephemeral world)
const resonate = new Resonate({
  url: process.env.RESONATE_URL,
  group: "workers"
});

// Set dependencies (ephemeral world)
resonate.setDependency("apiKey", process.env.API_KEY);

// Register functions (ephemeral world)
const workflowStub = resonate.register(myWorkflow);

// Start execution (ephemeral world)
async function main() {
  const handle = await resonate.beginRun(
    "workflow-1",
    myWorkflow,
    { userId: "123" }
  );

  console.log("Started:", handle.id);

  const result = await handle.result();
  console.log("Result:", result);
}

// Durable function (durable world - uses Context APIs)
function* myWorkflow(ctx: Context, input: any) {
  const apiKey = ctx.getDependency("apiKey");

  const data = yield* ctx.run(fetchData, input.userId);
  const processed = yield* ctx.run(processData, data);

  return processed;
}

async function fetchData(_ctx: Context, userId: string) {
  // Fetch logic
  return { userId, data: "..." };
}

async function processData(_ctx: Context, data: any) {
  // Process logic
  return { processed: true };
}

main().catch(console.error);

Decision Tree

Where am I?

  • In main() or app entry point → Ephemeral World (use Client APIs)
  • In function* with ctx parameter → Durable World (use Context APIs)

What do I need to do?

  • Initialize Resonate → new Resonate()
  • Register a function → resonate.register(func)
  • Start a workflow → resonate.run() or resonate.beginRun()
  • Start remote work → resonate.rpc() or resonate.beginRpc()
  • Manage promises → resonate.promises.*()
  • Share resources → resonate.setDependency()
  • Schedule cron → resonate.schedule()
  • Check execution status → resonate.get()

Summary

The Ephemeral World is your control plane:

  • One Client per process
  • Stateless orchestration
  • Entry and exit points
  • External promise resolution
  • Dependency setup

Keep it simple, keep it separate from durable functions.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.