How Echo works

What Echo is, how it's built, and what it's doing right now.

What Echo is. Echo is a developer assistant for Resonate — retrieval-augmented answers grounded in the docs, the SDKs, and a corpus of real examples. You can reach it five ways: the web app, the API, the CLI, Discord, and MCP.

How Echo is built. Echo runs as two tiers. The query path is synchronous: a request embeds the question, runs a hybrid vector and full-text search over the corpus, reranks the hits, and asks Claude to write the answer — all on a single request, start to finish in a few seconds, most of that the model writing. The corpus those queries read is maintained separately, by a set of durable workflows on Resonate: nightly crawls of the docs, SDKs, and examples; a weekly pull from Discord; a daily examples-validation pass; and the briefing that produces this page. Both tiers share one Postgres database that holds the corpus as vector embeddings and a full-text index.

Echo's surfaces (web app, API, CLI, Discord, MCP) feed a synchronous query path that embeds, searches, reranks, and generates an answer with Claude. A separate tier of durable workflows on Resonate keeps the corpus fresh. Both tiers share one Postgres corpus (vector + full-text) and the same external model services (Cohere for embeddings and reranking, Claude for generation).SurfacesWeb app · API · CLI · Discord · MCPSynchronous query pathembed → hybrid search → rerank → Claudeanswered live on the request threadDurable workflows · Resonatecorpus refresh · examples validationteam briefing · transparency rollupscheduled · crash-safe · auto-retryPostgres corpusvector embeddings + full-text indexModel servicesCohere embeddings + rerankClaude generation + intent

Running the background work on a durable execution engine has a useful side effect: the Resonate server records the state and timing of every step as it goes. The pipeline numbers below are read straight from those records — and because each step is durable, a crashed run replays and finishes instead of failing.

Total queries
572
+9 last 7d
Unique visitors
62
lifetime
Latency 7d
5.1s
p95 7.5s
Helpful 30d
👍 of rated answers

Pipeline health

Each durable pipeline is a Resonate workflow, and the call graph under each tile is the live shape of its last run — the same picture resonate tree prints for a running workflow. The curated source beside each graph is the workflow that drew it: every ctx.run step is one node. Green nodes succeeded, red failed, amber is still in flight. Because the run is durable, a partial run retries on its own instead of counting against the success rate.

succeededin flightfailedno recent run

Durable workflows

Docs corpus refresh
Nightly · durable workflow
Healthy
Success 7d
100%
p50 duration
59.0s
Last run
within the last 6h
Call graph nodes: runIngest, crawlCorpus, docs · SDKs · examples · Discord, processStaged. Steps flow: runIngest → crawlCorpus; crawlCorpus → docs · SDKs · examples · Discord; docs · SDKs · examples · Discord → processStaged.ctx.runrunIngestcrawlCorpus8.1sdocs · SDKs · examples · DiscordprocessStaged48.8s
Nightly crawl → stage → chunk/embed/index. crawlCorpus fans out across every source; Discord forum threads fold in on a weekly cadence.
src/ingest/workflow.tstypescript
// echo-ingest · nightly
function* runIngest(ctx: Context) {
  const runId = new Date(yield* ctx.date.now())
    .toISOString().slice(0, 10);

  // crawl every source, then chunk + embed + index
  const staged = yield* ctx.run(crawlCorpus, runId);
  return yield* ctx.run(processStaged, staged, runId);
}

function* crawlCorpus(ctx: Context, runId: string) {
  // one durable child per source —
  // docs · SDKs · examples · Discord, and ~20 more
  const docs    = yield* ctx.beginRun(crawlAndStageDocs, runId);
  const sdks    = yield* ctx.beginRun(crawlAndStageTsSdk, runId);
  const skills  = yield* ctx.beginRun(crawlAndStageSkills, runId);
  const discord = yield* ctx.beginRun(crawlAndStageDiscordThreads, runId);

  return [...(yield* docs), ...(yield* sdks),
          ...(yield* skills), ...(yield* discord)];
}
Examples validation
Daily · durable workflow
Healthy
Success 7d
100%
p50 duration
900ms
Last run
within the last 6h
Call graph nodes: runExamplesHealthBrief, persistRunStep, fetchYesterdayStep, fetchTwoDaysAgoStep, postParentStep, fileNewIssuesStep. Steps flow: runExamplesHealthBrief → persistRunStep; persistRunStep → fetchYesterdayStep; fetchYesterdayStep → fetchTwoDaysAgoStep; fetchTwoDaysAgoStep → postParentStep; postParentStep → fileNewIssuesStep.ctx.runctx.runctx.runctx.runctx.runrunExamplesHealthBriefpersistRunStepfetchYesterdayStepfetchTwoDaysAgoSteppostParentStepfileNewIssuesStep
Ingests the daily examples-ci report, diffs against prior runs, files issues for two-strike failures.
src/examples-health/workflow.tstypescript
// echo-examples-health · ingests the daily examples-ci report
function* runExamplesHealthBrief(ctx: Context, report: ExamplesCiReport) {
  yield* ctx.run(persistRunStep, report);

  // diff today against the last two days
  const yesterday  = yield* ctx.run(fetchYesterdayStep, report.run_date);
  const twoDaysAgo = yield* ctx.run(fetchTwoDaysAgoStep, report.run_date);
  const diff = computeDiff(report, yesterday, twoDaysAgo);

  yield* ctx.run(postParentStep, report, diff);

  // two-strike failures get a GitHub issue
  yield* ctx.run(fileNewIssuesStep, diff.new_failing, report.run_date);
}
Daily team briefing
Daily · durable workflow
Healthy
Success 7d
100%
p50 duration
1.9s
Last run
within the last 6h
Call graph nodes: runBrief, briefFetchWindow, briefUpdateProfiles, briefGenerateCandidates, briefQualityGate, briefPost. Steps flow: runBrief → briefFetchWindow; briefFetchWindow → briefUpdateProfiles; briefUpdateProfiles → briefGenerateCandidates; briefGenerateCandidates → briefQualityGate; briefQualityGate → briefPost.ctx.runctx.runctx.runctx.runctx.runrunBriefbriefFetchWindow737msbriefUpdateProfiles75msbriefGenerateCandidates23msbriefQualityGate24msbriefPost21ms
Each step is a durable ctx.run. After a crash the workflow replays from the top, completed steps return instantly from their recorded results, and only the unfinished step runs again.
src/proactive/brief.tstypescript
// echo-proactive · posts the daily community brief
function* runBrief(ctx: Context) {
  const now = yield* ctx.date.now();
  const briefDate = new Date(now).toISOString().slice(0, 10);
  const since = new Date(now - 24 * 3600_000).toISOString();

  const window     = yield* ctx.run(briefFetchWindow, briefDate, since);
  const enriched   = yield* ctx.run(briefUpdateProfiles, window);
  const candidates = yield* ctx.run(briefGenerateCandidates, enriched);
  const scored     = yield* ctx.run(briefQualityGate, briefDate, candidates);
  yield* ctx.run(briefPost, briefDate, scored);

  return { posted: scored.length };
}

Synchronous query path

The three query surfaces aren't durable — they answer on the request thread and share one path.

Call graph nodes: web · API · CLI, rate-limit, embed, hybrid search, rerank, LLM, response. Steps flow: web · API · CLI → rate-limit; rate-limit → embed; embed → hybrid search; hybrid search → rerank; rerank → LLM; LLM → response.web · API · CLIrate-limitembedhybrid searchrerankLLMresponse
All three surfaces converge on one synchronous path — answered live on the request thread, no background work or queue.
Interactive query (API)
synchronous
Healthy
Answered 7d
100%
p50 latency
3.8s
Queries 7d
2
Echo web app
synchronous
Healthy
Answered 7d
100%
p50 latency
5.1s
Queries 7d
7
CLI query
synchronous
No recent data
Answered 7d
p50 latency
Queries 7d
0

Daily query volume

By surface, last 90 days.

API: 395 queries over 90dWeb app: 156 queries over 90dCLI: 18 queries over 90dDiscord: 2 queries over 90dMCP: 0 queries over 90d
APIWeb appCLIDiscordMCP

What people ask about

A lightweight classifier sorts each question into one of sixteen topics. This page shows only those bucket counts over the last 30 days — never the question text. Arrows compare the last 7 days to the 7 before.

Durable promises (concept)
64
Other / unclear
32
Getting started / hello world
19
Compatibility / version matrix
18
Retries / error handling
16
TypeScript SDK
14
Deployment / hosting
11
Local dev / CLI
9
Call graphs / agent loop (concept)
9
Comparison to other tools
8
Building agents on Resonate
8
Webhooks / external coordination
5
Telemetry / observability
4
Python SDK
3
Rust SDK
2
Pricing / OSS / licensing
2

Feedback

Last 30 days.

Helpful 👍
0
Not helpful 👎
2
Ratio
Coverage
0.9%
rated

Latency

p50 / p95, last 30 days.

peak 12763ms
p50p95