This repo is a compact public demo for collecting OpenTelemetry traces from a search agent and reviewing those traces with HALO. It is the TypeScript twin of context-labs/SearchAgentDemo and behaves identically: same tools, same deliberate flaws, same dataset, same trace shape.
The agent uses the OpenAI Agents SDK (@openai/agents), Inference's OpenAI-compatible model endpoint, Tavily search, and Inference.net tracing. It is intentionally smaller than a deep research harness, but it still has enough tool use, scratchpad state, source extraction, and prompt surface area to make trace analysis useful.
- A single OpenAI Agents SDK agent with multi-turn tool calling.
- Inference.net tracing with
setup(), an outeragentSpan(), and manual child spans inside search tools. - Tavily web search and page extraction behind low default result caps.
- A deterministic mock search mode for local testing without Tavily usage.
- A 50-query starter dataset for collecting comparable traces.
- Documented non-critical flaws that HALO should be able to critique.
src/
agent.ts Agent definition and instructions
tools.ts Scratchpad, search, extract, source scoring, and claim comparison tools
searchClients.ts Tavily and mock search clients
cli.ts Single-query traced runner
batch.ts Dataset traced runner
data/
queries.jsonl 50 starter queries
search-agent-demo-traces.jsonl.gz ~1,000 pre-run traces (gzipped, ~20 MB)
docs/ HALO notes and known limitations
tests/ Unit tests that avoid network and model calls
If you just want to see HALO without running the agent, this repo bundles ~1,000 pre-run traces (generated from this exact repo) as a gzipped OTLP JSONL file. Decompress it and upload it to your project:
gunzip -k data/search-agent-demo-traces.jsonl.gz
inf trace upload ./data/search-agent-demo-traces.jsonl --name search-agent-demoYou can also upload data/search-agent-demo-traces.jsonl directly from the dashboard
(Observe → Traces → Upload). The traces keep their original timestamps, so widen the
time range to "all time" when viewing them or running HALO.
Install dependencies with Bun:
bun installCreate .env from .env.example and paste in your Inference API key:
INFERENCE_API_KEY=sk-... # one key: powers BOTH model calls and tracing
MODEL_ID=gpt-4.1-mini # any tool-capable model; this is the default
TAVILY_API_KEY=tvly-... # optional — real web search; omit for --mock-searchThat single INFERENCE_API_KEY is all you need. Bun auto-loads .env. The key powers the agent's model calls through Inference's OpenAI-compatible endpoint (https://api.inference.net/v1) and is also copied into CATALYST_OTLP_TOKEN to send traces to Catalyst (default endpoint https://telemetry.inference.net).
To use a different OpenAI-compatible provider (e.g. OpenAI directly), set INFERENCE_BASE_URL and INFERENCE_API_KEY to that provider's values.
Use real Tavily search:
bun run search-agent "What are the latest CISA recommendations for defending against ransomware?"Use mock search for a cheap smoke test:
bun run search-agent "What changed in the latest Python release?" --mock-searchThe command prints the final answer and the trace session_id. The same session_id appears in Inference.net so runs can be grouped in the dashboard.
Start with a small limit to control model and search spend:
bun run search-agent-batch --limit 3Use mock search when testing the runner shape:
bun run search-agent-batch --limit 3 --mock-searchEach dataset row gets a stable session ID like dataset-q001 and trace attributes for demo.query_id, demo.category, and demo.dataset.
scratchpad_write: stores short planning notes and observations.scratchpad_read: reads recent notes for final synthesis.web_search: calls Tavily search with capped results and a retriever span.extract_page: calls Tavily extract with a character cap and a retriever span.assess_source: applies a simple source-quality heuristic.compare_claims: does a deliberately shallow lexical comparison between two claims.
Tests avoid network and model calls:
bun test- Default Tavily search depth is
basic. - Search results are capped at five by default.
- Extracted page content is capped at 6000 characters.
- Dataset runs default to three queries.
- Mock search is available for smoke tests and CI.
- Run a representative sample from
data/queries.jsonl. - Inspect grouped traces in the Inference.net dashboard.
- Export or point HALO at the trace set.
- Let HALO identify repeated failure modes in planning, search choice, source quality, or final synthesis.
- Apply harness changes, collect another trace batch, and compare reports.
See docs/halo.md and docs/known_limitations.md for more detail.