AI chat architecture
newsence chat is a Cloudflare ChatAgent Durable Object. The Web app owns
authentication, product data, and billing storage; the chat Worker owns model
orchestration and AI cost policy; the Core Worker owns corpus reads and search.
Request flow
Section titled “Request flow”flowchart LR
UI["Web chat UI\nuseAgentChat"]
Agent["ChatAgent Durable Object"]
Engine["engine.ts\nprompt + tool gating"]
Stream["stream.ts\nAI SDK streamText"]
Core["Core service binding\ncorpus search/read"]
Domain["App DOMAIN binding\ndocuments/workspaces/billing"]
Model["AI Gateway providers"]
UI <--> Agent
Agent --> Engine
Engine --> Stream
Engine <--> Domain
Stream <--> Core
Stream <--> Domain
Stream <--> Model
web-tanstack/src/components/chat/useChatRuntime.tsconnects withuseAgentanduseAgentChat. The request carries the selected model, language, optional workspace, and attached-context keys. Skill shortcuts send ordinary user messages through the same path as typed prompts.workers/chat-worker/src/chat/agent.tsauthenticates the app-injected user and coordinates Durable Object state, history, reconnects, and title sync.workers/chat-worker/src/chat/engine.tsgets the authoritative billing snapshot throughDOMAINand builds the prompt and allowed tool set.workers/chat-worker/src/chat/stream.tspasses them to AI SDKstreamText.search-newsand resource reads go through Core service-binding RPC. Document, workspace, resource-link, and billing writes go through the AppDOMAINbinding. The chat Worker does not own those tables.- Tool parts and transient progress data stream to the UI. Final ordered message parts are persisted for history reconstruction; transient parts are excluded by AI SDK.
workers/chat-worker/src/chat/tools/registry.ts is the only tool registry. It
constructs request-scoped AI SDK tools and applies product feature gates.
| Tool | Responsibility |
|---|---|
search-news |
Filter-aware bilingual retrieval from the canonical corpus |
search-web |
Exa fallback when corpus search is insufficient |
read-context |
Batch-read resources, documents, collections, or saved URLs |
create-workspace |
Create an app-owned workspace when the plan allows it |
create-document |
Generate Markdown, convert it to Tiptap JSON, and persist a document in a workspace |
edit-document |
Edit an existing app-owned document |
add-resource |
Pin cited resource IDs or newly ingested URLs to a workspace |
generate-image |
Generate, meter, and persist an image resource |
create-podcast |
Start the workspace podcast workflow |
Daily report, summary, rewrite, social, and podcast shortcuts are regular user
messages. AI SDK’s standard multi-step streamText loop selects and executes
the same request-scoped tools used by manually typed prompts.
Tools return raw JSON-serializable results. They throw on failure so AI SDK
emits an error tool result. Progress previews use typed data-* parts through
the stream writer; live-only parts must be transient.
Resource links and citations
Section titled “Resource links and citations”Search results expose canonical resources.id values. read-context can load
their full text through Core. After a document is created, add-resource sends
the cited resource IDs and external URLs to the App DOMAIN binding and pins
the resolved resources to the returned workspace ID.
These pins are workspace context, not document-private citation rows. Markdown
citations remain ordinary links in generated content and are rendered by the
Streamdown-based chat renderer. Relational ownership uses resource_links;
display citations do not create another resource identity or read model.
Web rendering
Section titled “Web rendering”The current UI is intentionally small:
| File | Responsibility |
|---|---|
components/chat/ChatPanel.tsx |
Composes session state, conversation, prompt composer, and page/rail layouts |
components/chat/useChatRuntime.ts |
Agent connection, session switching, send/retry, cache invalidation |
components/chat/ChatConversation.tsx |
Empty-state presets, message list, errors, retry, and streaming state |
components/chat/ChatMessageContent.tsx |
Renders ordered text, reasoning, tools, files, and typed data parts |
components/chat/AgentSteps.tsx |
Tool progress/results, including corpus and web search |
components/chat/MarkdownStreamContent.tsx |
Lazy boundary for streaming/static Markdown rendering |
components/chat/MarkdownStreamContentImpl.tsx |
Streamdown rendering, CJK plugin, links, and prose component overrides |
components/chat/PodcastCard.tsx |
Podcast workflow status and audio |
components/chat/PromptComposer.tsx |
Input, model selection, context picker, audio shortcut, submit/stop |
There is no separate citation component or document-progress component. Tool
results render through AgentSteps, and citations remain links in Markdown.
Billing ownership
Section titled “Billing ownership”web-tanstack/src/lib/billing/plans.tsowns plan and product policy.web-tanstack/src/server/domain/billing.tsowns subscription, credit account, ledger, and atomic deductions.workers/chat-worker/src/chat/billing/owns model cost estimates, preflight, and settlement calls throughDOMAIN.
See Prompt and billing for the detailed policy flow.