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. For hard questions there's deep research, available on every surface: Echo breaks the question into sub-queries, retrieves them in parallel, and writes a single cited answer from the pooled evidence.
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. Deep research takes the second tier instead: the question becomes a durable workflow on Resonate, which gates it against a curated index of the product space, plans it into sub-queries, retrieves every sub-query in parallel, and synthesizes one answer from everything that came back. That same tier maintains the corpus both paths read: 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.
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.
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.
Scheduled workflows
- Success 7d
- 100%
- p50 duration
- 123.9s
- Last run
- ~24h ago
// 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)];
}- Success 7d
- 100%
- p50 duration
- 794ms
- Last run
- ~24h ago
// 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);
}- Success 7d
- 100%
- p50 duration
- 2.3s
- Last run
- ~24h ago
// 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 };
}Deep research
Deep research runs on the same Resonate engine as the pipelines above, but per question rather than on a schedule. The request gets a research ID back immediately and the run carries on server-side: a gate checks the question against the curated product-space index, the workflow fans out into sub-queries retrieved in parallel, and a single synthesis step writes the cited answer. Because the run is durable, disconnecting doesn't cancel it — any surface can come back with the same ID and the finished answer is waiting.
// echo-research · one durable run per question
function* runResearch(ctx: Context, input: ResearchInput) {
yield* ctx.run(initRunStep, ctx.id, input);
// gate + plan against the product-space index
const plan = yield* ctx.run(gateAndPlanStep, ctx.id, input);
// (out-of-scope questions deflect and finalize here)
// every sub-query retrieved in parallel
const pool = yield* ctx.run(fanOutSearchStep, ctx.id,
plan.sub_queries.slice(0, RESEARCH_MAX_SUBQUERIES),
input.req.source_filter);
// one synthesis over the pooled evidence
const synth = yield* ctx.run(synthStep, ctx.id, input, pool);
const reason = synth.grounded ? "answered" : "no_evidence";
yield* ctx.run(finalizeStep, ctx.id, input, synth,
reason, plan.sub_queries.length, synth.docCount);
return { research_id: ctx.id, answer: synth.answer,
sources: synth.sources, termination_reason: reason };
}Synchronous query path
With deep research off, the query surfaces answer on the request thread — no durable run, one shared path.
- Answered 7d
- —
- p50 latency
- —
- Queries 7d
- 0
- Answered 7d
- 100%
- p50 latency
- 5.5s
- Queries 7d
- 7
- Answered 7d
- —
- p50 latency
- —
- Queries 7d
- 0
Daily query volume
By surface, last 90 days.
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.
Feedback
Last 30 days.
Latency
p50 / p95, last 30 days.