Skip to content

Prompt construction and credits

The chat Worker owns model orchestration and local cost calculations. Product plan state, credit balances, and the append-only ledger remain app-domain data accessed through the DOMAIN service binding.

buildMessages() in workers/chat-worker/src/chat/ai/prompts.ts composes the canonical system prompt and the enriched content injected into the latest user message.

The system prompt contains:

  1. role and response rules;
  2. Markdown and citation format contracts;
  3. multi-step tool and error-recovery rules;
  4. an optional workspace catalog or workspace-capability block;
  5. the output-language directive.

Tool-specific invocation policy belongs in each AI SDK tool’s description and input schema. Do not duplicate the registry in a second system-prompt section.

buildAttachedContextBlock() adds metadata keys, not full resource bodies:

# Attached Context
| Key | Type | Name |
|-----|------|------|
| resource:abc-123 | resource | Article title |
| document:def-456 | document | Research report |

The model calls read-context with all relevant keys in one batch. Resource bodies come from Core corpus RPC; document bodies come from the app Worker’s DOMAIN.readDocuments RPC. This keeps the initial prompt bounded and avoids loading attachments the model does not need.

When a chat is bound to a Workspace, the engine may also add the app-domain workspace summary. In scope-free chats, it can add the user’s Workspace catalog and the authoritative capability to create another Workspace.

If an answer uses tool results, each factual claim must cite a numbered Markdown link such as [1](https://example.com), and the response must end with a matching numbered ## Sources list. The prompt forbids bare numeric markers and fabricated URLs.

The UI renders these as ordinary Markdown links. Citations are display content; they are not resource_links rows.

workers/chat-worker/src/chat/billing/cost.ts defines the pure estimation functions.

  • English text uses an estimate of roughly 4 characters per token.
  • CJK-heavy text interpolates toward roughly 1.8 characters per token.
  • Chat preflight assumes a chat-sized output and applies 1.3× headroom.
  • Content creation uses a larger fixed output estimate.
  • One credit represents USD 0.001 of model cost.

These values protect prepaid balance before execution. Text settlement uses actual provider usage when available; image and audio settlement use the catalog’s per-generation or per-episode price.

  • web-tanstack/src/lib/billing/plans.ts owns plans, feature gates, monthly grants, allowed models, and product limits.
  • web-tanstack/src/server/domain/billing.ts owns subscription snapshots, credit accounts, the append-only ledger, idempotent settlement, and atomic deduction.
  • workers/chat-worker/src/chat/ai/model-catalog.ts owns model identity and local pricing metadata.
  • workers/chat-worker/src/chat/billing/ owns cost estimates, preflight checks, and settlement calls through DOMAIN.

The chat Worker must not query subscription or credit tables directly.

workers/chat-worker/src/chat/billing/server.ts exposes:

Method Responsibility
assertChatQuota(...) Preflight a chat turn against an already-loaded snapshot
checkText(...) Fresh balance check before an in-stream document generation
checkImage(...) Fresh feature and balance check before image generation
checkAudio(...) Fresh balance check before podcast synthesis
trackText(...) Settle actual text usage through DOMAIN
trackImage(...) Settle one image generation through DOMAIN
trackAudio(...) Settle one audio episode through DOMAIN

Settlement requests include stable idempotency keys derived from turn, resource, document, or podcast identifiers. Durable Workflow callers receive settlement failures and own their retry policy; user-facing non-durable paths use the billing facade’s bounded retry behavior.

  • Insufficient balance throws QuotaExceededError and maps to the frontend’s upgrade flow.
  • A plan-disabled feature throws FeatureNotAvailableError.
  • A snapshot or capability RPC failure must surface as an error; it must not be converted into a forged free-plan limit.
  • Tools throw on operational failure so AI SDK marks the tool result as an error.

See AI chat architecture for the full request and tool flow.