Wealth Advisor — System Overview

A multi-agent equity research tool for wealth advisors. Combines official primary-source data (SEC + market quotes) with unverified social signal (Reddit, Stocktwits, news), in clearly separated sections.

Live: wealth-advisor.pages.dev Version: v0.2.0 — multi-agent Date: 2026-05-25

TL;DR for a customer

Type a US-listed ticker, get a one-screen briefing in ~60 seconds: live quote, business snapshot, recent 8-K material events, top 10-K risks (all cited to the filing), plus a separately-labeled "social signal" section showing what people are saying on Reddit, Stocktwits, and recent news — with explicit unverified tags, pump-bot detection, and an explicit divergences section when the two sources disagree.

Built for compliance: never gives buy/sell recommendations, scopes to US public equities only, and shows the advisor the agent's work (every tool call) for audit.

01 · The Wealth Advisor

the user

"What does this thing actually do for me before my 9:30am client call?"

The 60-second workflow

  1. Open wealth-advisor.pages.dev in a browser.
  2. Type a US-listed ticker (e.g. F, AAPL, NVDA) and hit Send.
  3. Watch the agent's work stream in real time — you can see exactly which SEC filings and social sources it's consulting.
  4. Read the briefing. Ask follow-ups ("what's the debt load?", "what changed in the latest 10-Q?").

What you get

SectionSourceWhat it tells you
SnapshotYahoo FinancePrice, change, day range, market cap, sector — live.
Business snapshotLatest 10-K Item 11–2 sentence company description, cited.
Material eventsLast 5 filings (10-K + 8-K)What was announced and when, cited per filing.
Top risksLatest 10-K Item 1AThe risk factors the company itself discloses, cited.
Social signalReddit + Stocktwits + News + volume anomalyWhat's being said, with an unverified tag and pump-bot detection.
DivergencesSynthesisCases where social claims contradict the filings — surfaced, not suppressed.

What you do not get

02 · System Architect

build the boxes & arrows

"What's the shape of the system and why?"

Pattern: sub-agents-as-tools

One Coordinator agent exposes two macro-tools: research_official and research_social. Each macro-tool internally runs its own focused sub-agent with its own system prompt and its own tool set. The coordinator sees clean structured Reports back, never raw tool output, and synthesizes a single briefing.

┌─────────────────────────────────────────────────┐
│  Coordinator Agent (Sonnet 4.6)                 │
│    tools: research_official, research_social    │
│       │                                         │
│       ├──► Official sub-agent (Sonnet 4.6)      │
│       │     prompt: strict primary-source       │
│       │     tools: resolve_ticker, quote,       │
│       │            filings, 10-K sections,      │
│       │            8-K summary  (6 tools)       │
│       │                                         │
│       └──► Social sub-agent (Sonnet 4.6)        │
│             prompt: skeptical forum analyst     │
│             tools: reddit, stocktwits, news,    │
│                    volume anomaly  (4 tools)    │
└─────────────────────────────────────────────────┘

Why this pattern, not "one big agent"

Tech stack

LayerChoice
RuntimeCloudflare Pages + Pages Functions (Workers V8 isolates at the edge)
LanguageTypeScript (strict, noUncheckedIndexedAccess)
LLM@anthropic-ai/sdk, Claude Sonnet 4.6 across all three agents
Tool-use loopHand-rolled (the Agent SDK doesn't run in Workers — no subprocess)
HTML parsingnode-html-parser (Workers-compatible)
Front endSingle static index.html, vanilla JS, no build step
Server stateNone. Browser holds conversation history, posts it back each turn.
Testsvitest — unit, integration, golden behavior, contract

03 · Security Architect

how can this hurt us?

"What's the threat surface and what does each layer defend?"

Layered defenses (in defense-in-depth order)

LayerDefenseThreat addressed
Input2,000-char cap on user messages; JSON.parse validationPrompt-injection payload size; malformed bodies
Inputresolve_ticker rejects non-US tickers (containing .) before any other callScope abuse; non-US listings
PromptSystem prompt forbids stating facts without a tool resultHallucinated "facts"
PromptHard refusal for investment advice questionsRegulatory exposure (SEC/FINRA "investment advice" rules)
ToolDiscriminated-union returns; errors-as-data, not exceptionsSilent failures; partial data passing as complete
Outputmarked + DOMPurify sanitization before innerHTMLXSS from filing content or LLM output
OutputTool-trace UI built with textContent onlyXSS from tool names / parameters
LoopMax 10 iterations per sub-agent; max 4k tokens/callRunaway cost; infinite tool loops

Secrets management

Known gaps (intentional for v1)

04 · UX Architect

design for trust under time pressure

"An advisor has 60 seconds before a client call. The UI must earn trust fast."

Key design decisions

What the advisor sees stream by stream

  1. "Resolving ticker..." ✓ (39 ms)
  2. "Fetching live quote..." ✓ (121 ms)
  3. "Pulling latest 5 filings..." ✓ (137 ms)
  4. "Reading 10-K Item 1A (Risk Factors, 9778 words)..." ✓ (73 ms)
  5. "Checking Reddit / Stocktwits / News in parallel..." (with per-source results)
  6. "Detecting volume anomaly..." ✓ (117 ms — 0.88σ, no flag)
  7. Final markdown briefing renders with tables.

05 · Test Architect

how do we know it works?

"Each commit followed test → red → implement → green → commit. The spec is encoded in the tests."

Test pyramid

LayerFilesCountWhat it covers
Unit (tools)tests/tools/*.test.ts ×7~25Each external API integration mocked at fetch
Promptstests/prompts.test.ts14Sanity assertions that key behavior clauses survive edits
Agent looptests/agent/loop.test.ts2Generic runSubAgent with a mock Anthropic client
Sub-agentstests/agent/{official,social}.test.ts4JSON parsing, graceful failure when output is non-JSON
Coordinatortests/agent/coordinator.test.ts2Parallel sub-agent dispatch + event nesting
APItests/api/chat.test.ts3SSE stream shape; 400 on malformed input
Golden behaviortests/golden.test.ts10End-to-end behavior contract (no advice, citations, scope, divergence handling, anomaly handling, graceful degradation)
Contracttests/contract/contract.test.ts5API↔UI event registry sync; SSE wire format

The contract test is the differentiator

It catches drift in both directions:

Live verification

Final smoke driven via Playwright MCP against the deployed URL with ticker F (Ford). Verified: tables rendered, no JSON leakage, 3 [UNVERIFIED] tags including a flagged pump-promotion bot, explicit divergence section.

06 · Integration Architect

5 third-party systems, zero hard dependencies

"What happens when an upstream is down? Define it explicitly."

Upstream integrations

SystemAuthUsed forFailure mode
SEC EDGAR data.sec.govnone (User-Agent required)Filings list, 10-K sections, 8-K itemsReturns sec_unavailable → official report carries a warning, briefing continues
Yahoo Finance query1/v8/chartnone (undocumented)Live quote, 30-day volume baselineReturns quote_unavailable → snapshot table replaced by "Live quote unavailable"
Reddit OAuthclient-credentialsr/wallstreetbets, r/stocks, r/investing (whitelisted)Missing keys → missing_credentials warning; rate-limited → reddit_unavailable
Stocktwits /streams/symbolnoneAggregate Bullish/Bearish countsstocktwits_unavailable → row shows "unavailable"
NewsAPI /v2/everythingAPI keyRecent headlines (14-day window)Missing key → missing_credentials; over-quota → news_unavailable
Anthropic Messages APIAPI keyAll three agentsNetwork error → SSE emits {type:"error"} event; UI surfaces it

Failure philosophy

07 · Performance Architect

edge-deployed, parallel where possible

"Where does the time actually go, and what's the headroom?"

Latency budget (observed on AAPL/F runs)

PhaseTimeNotes
Cold start (Workers V8 isolate)< 100 msEdge isolates; no container spin-up
Coordinator turn 1 (LLM)~3 sDecides to call both macro-tools in parallel
Official sub-agent (full loop)~40–60 s5–6 internal iterations; tool calls dominate
Social sub-agent (full loop)~15–25 s4 tools in parallel within one assistant turn
Coordinator turn 2 (synthesis)~10–20 sGenerates markdown brief from two Reports
End-to-end~60–90 sSub-agents run sequentially in v1; could be in parallel via Promise.all

Parallelism actually used

Cost / wallclock controls

08 · Compliance / Regulatory Architect

"Not in this section" — the financial-services version

"Investment advice triggers SEC / FINRA regulation. Stay on the right side."

What the system asserts about itself

Audit trail

Every tool call emits a structured SSE event with: tool name, input, latency, ok/error status, and result summary. The same stream that drives the UI can be teed to a log sink for retention. Audit is a free byproduct of inspectability.

What this design does not claim

09 · AI / Agent Architect

model + pattern + eval

"Why these models, why this topology, how do you know it's stable?"

Pattern choice: sub-agents-as-tools (orchestrator-workers variant)

From Anthropic's "Building effective agents" taxonomy. Coordinator dispatches; workers execute; each worker has a focused tool surface. We chose this over:

Model selection: Sonnet 4.6 for all three agents

ModelConsidered forWhy not chosen
Haiku 4.5Sub-agents (cost)Misreads financial document language under tool-chain pressure; risk too high for a wealth-advisory product.
Sonnet 4.6All three agentsProduction-grade reasoning, ~3× faster and ~5× cheaper than Opus.
Opus 4.7Overkill — the bottleneck is tool latency, not reasoning depth.

Evals

Production hardening hooks (deliberately deferred)

10 · Data Architect

two trust regimes, well-defined schemas

"Where does data come from, what shape is it, and who can touch it?"

Two epistemic regimes

RegimeSourcesProvenanceUI treatment
OfficialSEC EDGAR, Yahoo FinanceFiled, signed, audit-requiredCited inline; treated as fact
SocialReddit, Stocktwits, NewsAPI, volume statsAnonymous, may include bots / pumpsAlways tagged unverified; in its own section with a disclaimer

Tool return shapes

Every tool returns a discriminated union: T | { ok: false; error: string; reason?: string }. Callers must branch on the discriminator. This makes "I forgot to handle the failure" a TypeScript error at compile time.

Report schemas (the sub-agent contract)

OfficialReport = {
  findings: { claim: string; citation: string }[]
  quote:    Quote | null
  warnings: string[]
}

SocialReport = {
  sentiment: { reddit_breakdown, stocktwits, news_tone }
  themes:        { topic, supporting_posts[] }[]
  claimed_facts: { claim, source_url, tag: "[UNVERIFIED]" }[]
  anomalies:     { kind, evidence }[]
  warnings:      string[]
}

Persistence

None. Server is stateless per request. Browser holds the conversation array. There is no PII storage problem because there is no storage. Production hooks: Cloudflare KV for 15-min cache on Reddit/Stocktwits/News (cost + rate-limit relief).

11 · Observability / SRE Architect

if you can't see it, you can't run it

"Every tool call leaves a trace. The same stream that renders the UI is the audit log."

Event-driven instrumentation

The agent loop emits an AgentEvent for every meaningful action:

type AgentEvent =
  | { type: "token"; text: string }
  | { type: "tool_call"; name; input; iteration; ts; parent? }
  | { type: "tool_result"; name; ok; latency_ms; result_summary; ts; parent? }
  | { type: "sub_agent_start"; agent; ts }
  | { type: "sub_agent_end"; agent; report_summary; ts }
  | { type: "done" }
  | { type: "error"; reason }

How this becomes ops

Cloudflare ops surface

Reliability posture

12 · FinOps / Cost Architect

where does the money go?

"The LLM call is the cost. Everything else is rounding."

Per-briefing cost (rough estimate, Sonnet 4.6 list pricing)

ComponentTokens (in / out)Est. cost / brief
Official sub-agent (5–6 iterations)~25k in / ~3k out~$0.12
Social sub-agent (1–2 iterations)~8k in / ~2k out~$0.05
Coordinator (2 iterations)~10k in / ~1.5k out~$0.05
Total~$0.20–$0.30 per ticker

Non-LLM costs

ServicePlanCost
Cloudflare Pages + FunctionsFree tier$0 (well under 100k requests/day)
SEC EDGARFree$0
Yahoo FinanceUndocumented (rate-limited)$0
Stocktwits public APIFree$0
Reddit OAuthFree (rate-limited)$0
NewsAPIFree tier (100 req/day)$0 — caps at 100 briefs/day for the social section

Cost levers (production)