An R-native implementation of STORM (Synthesis of Topic Outlines through Retrieval and Multi-perspective Question Asking) and Co-STORM from Stanford’s STORM project.
This package reproduces the core workflow primitives:
- Multi-perspective research with selected or automatically generated expert profiles
- Provisional evidence workspaces with citations and source attribution
- Pair-level claim support with exact claim, span, and source bindings
- Research manifests that identify configuration, programs, knowledge snapshots, traces, and deliverables
- Review-only Graft promotion with immutable acceptance receipts
- Resumable research state for scripted STORM and interactive Co-STORM
- Two-step outline refinement and at-a-glance decision synthesis
- Query decomposition and semantic fact retrieval
- Parallel section writing from already-grounded evidence (optional)
- Interactive multi-agent moderation with mind map (Co-STORM)
- Deterministic dialogue projections with an explicit expert roster
- Automated evaluation with simulated users
Built on the R AI ecosystem:
- ellmer — LLM orchestration: tool calling, structured output, streaming
- ragnar — RAG: chunking, embedding, semantic retrieval
- dsprrr — structured extraction/generation modules
- Deputy — persistent, permission-bounded Co-STORM agent execution
- graft — exact retained evidence and explicit review decisions
- shinymcp — interactive research cards and read-only MCP tools
- shinychat — interactive chat UI
- vitals — evaluation tasks
Try the ecosystem together
tempest::tempest_app(demo = TRUE)The offline demonstration runs actual deputy agents and dsprrr stages against synthetic evidence, shows a shinymcp research card, and lets you explicitly accept, correct, withdraw, and reopen exact evidence through graft. No model credentials or network requests are needed after installation. Install the optional shinymcp, graft, shiny, bslib, and callr packages to run it.
Use tempest_mcp_app(result) with your own completed STORM or Co-STORM research to give people and models the same read-only claim and evidence inspection. See the ecosystem walkthrough.
Reuse retained artifacts
tempest_artifact_knowledge(selection, contents) supplies exact retained evidence without a Graft view. The host checks current eligibility and resolves the bytes; Tempest checks complete selection coverage, SHA-256 digests, allowed evidence classes, and exact declared dependencies. STORM and Co-STORM retain this input in their research workspace and current save/resume formats. Saved evidence cannot register tools or bind executable procedures.
See artifact knowledge for the input contract and a local example. This is an evidence consumer API; it does not implement artifact storage, approval, withdrawal, or future acceptance.
Installation
# install.packages("pak")
pak::pak("JamesHWade/tempest")After installation, run vignette("tempest", package = "tempest") for the package tour and a first STORM workflow.
Setup
LLM provider credentials
By default, Tempest creates OpenAI clients with ellmer::chat_openai(). Which credential it needs depends on the installed ellmer:
-
Released ellmer (0.5.0 or later from CRAN) authenticates with an API key. Set it outside your R source files:
Sys.setenv(OPENAI_API_KEY = "<your key>") ellmer with the subscription-authentication port (
chat_openai(auth = ), tidyverse/ellmer#1067) lets Tempest passauth = "codex"and reuse the file-backed ChatGPT subscription managed by an installed Codex CLI; noOPENAI_API_KEYis required. If Codex has not stored file-backed credentials, runcodex login -c 'cli_auth_credentials_store="file"'.
Tempest detects which it has and tells you once per session when it falls back to the API key. Alternative providers and custom chat factories use the credential mechanism configured for that provider.
Search provider
By default, tempest uses native provider web search when available (OpenAI, Anthropic, Google). This leverages each provider’s built-in web search capabilities for better results.
To use alternative search providers:
cfg <- tempest_config(
search_provider = "wikipedia" # or "you", "bing", "serper", "brave", "duckduckgo", "tavily", "searxng", "google", "azure_ai_search"
)Provider-specific API keys for alternative search:
- Wikipedia: no API key required (fallback when native not available)
- You.com: set
YDC_API_KEY - Bing: set
BING_SEARCH_API_KEY - Serper: set
SERPER_API_KEY - Brave: set
BRAVE_API_KEY - DuckDuckGo: no API key required
- Tavily: set
TAVILY_API_KEY - SearXNG: set
SEARXNG_API_URL; optionally setSEARXNG_API_KEY - Google Custom Search: set
GOOGLE_SEARCH_API_KEYandGOOGLE_CSE_ID - Azure AI Search: set
AZURE_AI_SEARCH_API_KEY,AZURE_AI_SEARCH_ENDPOINT, andAZURE_AI_SEARCH_INDEX_NAME
Completed STORM products are read through tempest_report(), tempest_sources(), tempest_claims(), tempest_claim_supports(), and tempest_trajectory_review(). Internal manifests, mutable workspaces, retrievers, and state remain implementation details used to validate those reads. A Co-STORM session exposes only its supported operations and read-only projections. Retained artifact evidence enters through tempest_artifact_knowledge(); tempest_reuse_artifact_research() rechecks Graft decisions and host eligibility. New research remains provisional until the host explicitly reviews and accepts it.
Internally, every evidence value is one exact TempestResource. Retriever fetches, provider-native tools, caches, and restored products must agree on that shape. tempest_sources() returns a read-only presentation table rather than a writable evidence object, and Tempest provides no legacy list conversion path.
Co-STORM moderator and expert chats run through required persistent Deputy agents. Tempest disables ambient file, shell, R, and package-install authority at that boundary and allowlists only the Tempest tools already attached to each chat. Web authority is limited to that frozen tool surface, including an explicit provider-native search tool when configured. Each product-owned Deputy session identifier is passed to the Agent constructor and checked against the live runtime before use. Session persistence stores credential-safe opaque run, session, agent, stage, role, expert, and correlation references; it never serializes a Deputy Agent or provider credentials.
Verify and promote research evidence
Claim verification is authoritative at the exact claim-by-evidence-span pair. Publication replaces the complete support set atomically, and tempest_claim_supports() exposes each deterministic pair identity, claim text, evidence-span quote and location, source binding, status, score, and rationale. Claim-level status and citation-audit tables are derived views of those records; they are not separate evidence authority.
A succeeded STORM product can be packaged for explicit Graft review:
supports <- tempest_claim_supports(result)
bundle <- tempest_promotion_bundle(result)
trusted_bundle_id <- bundle@bundle_id
tempest_save_promotion_bundle(bundle, "promotion/grid-battery-recycling")
bundle <- tempest_read_promotion_bundle(
"promotion/grid-battery-recycling",
expected_bundle_id = trusted_bundle_id
)
store <- graft::graft_store("research-artifacts", create = TRUE)
selection <- tempest_publish_artifact_research(result, store)
# The host explicitly reviews the complete evidence and report.
accepted <- graft::graft_accept(
store, graft::graft_read_selection(store, selection), "battery-recycling",
expected = NULL, key = "review-1",
actor = "reviewer", reason = "Evidence reviewed", purpose = "research"
)Graft retains exact bytes and decision history. Tempest owns the scientific record contract and verifies report, source, span, claim, support, and program provenance. No graph schema compiler or native database is required.
Promotion accepts only a completed tempest_run() result or a succeeded, quiescent TempestSession. A loose Workspace, Manifest, or StageRecord tuple cannot reconstruct publication authority. The bundle contains the selected Sources, Claims, EvidenceSpans, ClaimSupports, and exact extraction and verification ProgramArtifacts. Its closed proof projection retains the exact resources, claims, spans, and supports needed to recompute each retained StageRecord digest. A selection must include every output bound by each retained extraction or verification record; Tempest rejects partial stage-output selection instead of packaging unselected evidence. Publishing retains a candidate: Tempest never records host acceptance itself. The promotion directory is a closed current-format bundle, its destination must not already exist, and any older or extra shape is rejected. Reading requires the original bundle id as an out-of-band trust pin; checksums stored inside the directory establish internal consistency but are not a signature. Research promotion never creates executable authority; stored program references are provenance only.
Review a completed run
tempest_trajectory_review() reconstructs a bounded, read-only review from one exact completed STORM result or succeeded, quiescent Co-STORM session:
review <- tempest_trajectory_review(result)
proposed_review <- tempest_trajectory_review(
result,
promotion_bundle = bundle
)
artifact_store <- graft::graft_store("research-artifacts", create = TRUE)
selection <- tempest_publish_artifact_research(result, artifact_store)
accepted <- graft::graft_accept(
artifact_store, graft::graft_read_selection(artifact_store, selection),
"research", expected = NULL, key = "review-1",
actor = "reviewer", reason = "Reviewed the evidence", purpose = "briefing"
)
accepted_review <- tempest_trajectory_review(
result, store = artifact_store, selection = selection,
stream = "research", decision = accepted@id
)The ten-field value identifies the product and contains ordered StageRecord summaries, safe terminal Deputy identities, the fixed ProgramSet references, input and optional promotion knowledge, evidence identities, explicit joins, and structural findings. Variable lanes retain at most 250 records and report the complete count and digest when rows are omitted. The review contains no prompts, responses, source text, paths, credentials, live objects, or capabilities, and it is reconstructable rather than persisted.
Every join names its relation and proof. Exact run, stage, program, artifact selection, bundle, publication, and recorded decision identities establish explicit bindings; correlation_id can establish only correlated_with with correlation_only proof. It never establishes authorship or causation. Mutable progress events are intentionally outside the review identity. A promotion bundle or publication is shown as proposed; an exact recorded accept decision is shown as historically accepted. Withdrawal does not erase that history or alter the original input selection. An input reported_decision is unverified host provenance, not authenticated acceptance; malformed metadata is omitted from the projection. Current reuse still requires fresh admission through tempest_reuse_artifact_research(). Schema 3 directly replaces native-snapshot and receipt-based reviews; cross-product publications are rejected.
The current persistence line accepts only ResearchWorkspace snapshot schema 7, Co-STORM snapshot and bundle schema 13, STORM bundle schema 10 with state schema 5, ProgramSet schema 2, research-manifest schema 5, StageRecord output-digest payload schema 3, and promotion-bundle schema 1. Readers reject every other version; missing fields, extra fields, and values that only become valid after coercion are errors.
Product boundary
Tempest 0.2 supports only the STORM and Co-STORM product APIs. Use tempest_run() for scripted research and tempest_session() for interactive research. The former application-neutral workflow, runtime, capability, connection, skill, deliverable, and artifact kernel and its symbols have been removed. There is no compatibility or generic-kernel migration layer.
Product bundles contain the exact research manifest, provisional workspace, product state, report, and retained artifact selection. Fixed scientific transformations carry dsprrr program identity, while open-ended agent calls carry Deputy execution identity. Those identities support correlation and audit joins only; they do not claim that an execution caused, authored, or validated report content.
Scripted STORM
library(tempest)
cfg <- tempest_config(
# search_provider = "native" is the default (uses OpenAI/Anthropic/Google native search)
models = list(
coordinator = "openai/gpt-5.6-sol",
expert = "openai/gpt-5.6-luna",
writer = "openai/gpt-5.6-sol",
judge = "openai/gpt-5.6-luna",
mindmap = "openai/gpt-5.6-luna"
)
)
res <- tempest_run("Life cycle assessment of lithium-ion batteries", config = cfg, verbose = TRUE)
cat(tempest_report(res))Hosts can supply the same immutable scientific profiles used by generated expert panels:
recycling_expert <- tempest_expert(
name = "Dr. Rivera",
title = "Battery recovery specialist",
description = "Studies process yield, safety, and industrial scale-up.",
instructions = "Separate demonstrated performance from projections.",
focus_areas = c("hydrometallurgy", "process safety"),
initial_questions = "Which recovery steps constrain full-scale yield?"
)
res <- tempest_run(
"Life cycle assessment of lithium-ion batteries",
config = cfg,
experts = list(recycling_expert)
)Generated and supplied experts use the same canonical constructor. Tempest derives expert_id and version from its six authored fields. Profiles do not contain runtime clients, tools, capabilities, or roster state.
Experimental OpenTelemetry traces
Tempest can project bounded STORM, stage-execution, and Co-STORM moderator-completion, turn-commit, warmup, and report traces through a provider configured by the host. Start R with the exact Tempest-only scope and generative AI content capture disabled:
OTEL_R_EMIT_SCOPES=io.github.jameshwade.tempest \
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=false \
RThen opt in for that R process before starting a run:
options(tempest.otel.enabled = TRUE)The integration is experimental and off by default. Tempest does not configure the OpenTelemetry provider, exporter, endpoint, credentials, or shutdown; the host owns those settings. The Tempest projection excludes prompts, responses, queries, evidence, source content, URLs, paths, identifiers, and progress payloads.
Set the privacy environment before starting R and any Mirai workers. The exact Tempest-only scope suppresses automatic Shiny, Mirai, ellmer, and httr2 spans, which are separate telemetry contracts that Tempest has not audited. In particular, httr2 records the complete request URL as url.full. Tempest’s Google Custom Search request places both GOOGLE_SEARCH_API_KEY and the research query in URL query parameters, so enabling the httr2 scope could expose a credential and research content. Do not broaden the supported scope without a separate privacy review.
Local OTLP/HTTP Collector
Install otelsdk in the host environment, then save this traces-only Collector configuration as otel-collector.yaml:
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
exporters:
debug:
verbosity: detailed
service:
pipelines:
traces:
receivers: [otlp]
exporters: [debug]Run the Collector with its OTLP/HTTP port bound only to the local interface:
docker run --rm --name tempest-otel-collector \
-p 127.0.0.1:4318:4318 \
-v "$PWD/otel-collector.yaml:/etc/otelcol/config.yaml:ro" \
otel/opentelemetry-collector:0.158.0Start a fresh R process configured to send only Tempest traces to that local Collector:
OTEL_TRACES_EXPORTER=http \
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318 \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_R_EMIT_SCOPES=io.github.jameshwade.tempest \
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=false \
OTEL_SERVICE_NAME=tempest-local \
RThe local example needs no exporter header or credential. For a short-lived smoke test, the host may also set OTEL_BSP_SCHEDULE_DELAY=100 so the SDK’s batch processor exports promptly. Tempest does not flush or shut down the provider.
Logfire is one optional operator-selected destination downstream of the Collector. It is not a Tempest dependency, API, default, endpoint, SDK, or schema. Keep any backend token and exporter configuration in operator-owned Collector secrets, never in Tempest configuration, source code, examples, snapshots, or bundles.
Report polishing is deterministic.
You can also use a single model for all roles:
cfg <- tempest_config(models = "anthropic/claude-sonnet-4-20250514")Parallel section writing
Perspective research is deliberately sequential so each open-ended expert turn can be synchronously bound to one terminal Deputy trace. Already-grounded report sections can be written in parallel using mirai:
res <- tempest_run(
"Quantum computing applications",
config = cfg,
parallel_writing = TRUE, # requires mirai
verbose = TRUE
)STORM persistence
Save intermediate STORM artifacts to disk and resume interrupted runs:
res <- tempest_run(
"Quantum computing applications",
config = cfg,
output_dir = "storm-runs"
)
# Later: loads completed stages from storm-runs/quantum-computing-applications
# and continues from the first incomplete stage.
res <- tempest_run(
"Quantum computing applications",
config = cfg,
output_dir = "storm-runs",
resume = TRUE
)Each run directory is an exact current schema-10 STORM product bundle with schema-5 state. It includes checksummed JSON state for perspectives, experts, sources, claims, outlines, and references; Markdown drafts; and the final Markdown report. Resume rejects older, future, missing, extra, coerced, or mismatched shapes rather than migrating them.
Every typed ProgramSet attempt also has a durable record of its stage, program, evaluator, trace, support decision, and execution or fallback path. Tempest evaluates a complete structured output before changing product state, so malformed output cannot leave a partial update. On resume, persisted running attempts become cancelled records. Final reports append a deterministic Execution review when an attempt failed, was cancelled, used a policy fallback, or produced a grounded result that was not verified.
Pipeline Details
tempest_run() executes five steps: perspectives, research, outline, write, polish. Key features:
-
Persistent staged runs – pass
output_dirto save run artifacts after each completed stage; passresume = TRUEto load saved artifacts and skip completed stages. - ToC-enriched perspective discovery – seed URLs are fetched and their table-of-contents headings are extracted to give the coordinator richer context for generating expert perspectives.
- Query decomposition – each expert research question is decomposed into 2-3 targeted search queries before retrieval.
- Semantic fact retrieval – when ragnar is configured, section writing uses semantic similarity to select the most relevant facts (falls back to keyword matching otherwise).
- Two-step outline – a draft outline is generated from the LLM’s parametric knowledge, then refined with research findings.
- At-a-glance decision brief – verified observations are kept distinct from evidence-bound assessments and review actions; no-change signals copy an explicit verified claim and are omitted when evidence is inconclusive.
-
Parallel section writing – pass
parallel_writing = TRUEto write report sections concurrently with mirai, then assemble the already-grounded sections and evidence in deterministic outline order. -
Governed dsprrr programs – every structured stage resolves an exact addressable program from a
TempestProgramSet, including its verified dsprrr artifact ID, stage contract, and evaluator identity.
Compiling dsprrr programs
Tempest resolves its builtin dsprrr programs internally. Program identity, contract version, and evaluator are recorded in the run manifest and rechecked before any program executes; runtime registries, model clients, credentials, and executable objects never enter that metadata.
Resuming a run recomputes every program artifact ID and continues only when they match the persisted run.
Keep stored knowledge separate from execution
The host selects the programs used by a research run. Accepted evidence and stored program references cannot select, replace, or authorize executable programs. Tempest no longer resolves GovernedProcedure records from Graft or passes live Graft views into stage execution. Retained procedure references in research manifests and trajectory reviews are inert provenance; they cannot be loaded into a live ProgramSet.
Accepted evidence uses exact artifact selections. The host recipe retains a reviewed decision and its complete evidence closure:
source(system.file("examples", "briefing-basis.R", package = "tempest"))
basis <- capture_briefing_basis(store, "battery-recycling", accepted@id, "research")
saveRDS(basis, "accepted-basis.rds")
historical <- read_briefing_basis(store, basis)
knowledge <- reuse_briefing_basis(store, basis, eligible = function(event) TRUE)The eligibility callback represents the host’s current access and purpose check. Historical inspection remains available after correction; it does not authorize new research or resume.
On later days, reopen the matching store and trusted checkpoint. Changes to the selected evidence require host review before another automatic run:
basis <- readRDS("accepted-basis.rds")
if (nrow(briefing_changes(store, basis)) > 0L) {
stop("Review the selected evidence before research.")
}
knowledge <- read_briefing_basis(store, basis)An unchanged day keeps the full selection. After accepting additional research, append its complete selection to basis$selections and capture a new checkpoint. For repeated record IDs, the newest selected receipt revision wins. If current records differ from those reviewed revisions, checkpoint creation stops for review. The recipe also stops above 1,000 records instead of dropping evidence. Historical inspection can still reconstruct the earlier pinned answer.
A report remains host-owned synthesis linked to its selected evidence, not a new source or an execution grant. The offline acceptance and correction example shows the complete restart and correction workflow. The daily briefing guide applies the same recipe to a scheduled host.
Accepted Claim records give the briefing its change signal: a verified claim that restates one of them is a no-change finding, and every other verified claim is something that changed.
Accepted record text is evidence, never instruction. It travels in a data channel and cannot change prompts, message roles, tools, or executable programs. A record Tempest cannot materialize exactly is rejected rather than truncated. The host selects programs independently, and Tempest verifies each program’s artifact identity and output contract before execution. Stored program and procedure references remain inspection data.
Configuration options
tempest_config() accepts the following parameters:
| Parameter | Default | Description |
|---|---|---|
models |
role-specific OpenAI defaults | Single model string or named list with coordinator, expert, writer, mindmap, judge
|
search_provider |
"native" |
Search backend: "native", "wikipedia", "you", "bing", "serper", "brave", "duckduckgo", "tavily", "searxng", "google", "azure_ai_search"
|
embed_fn |
NULL |
Embedding function for RAG (e.g., ragnar::embed_openai()) |
ragnar_store |
NULL |
Pre-built ragnar store; auto-created if embed_fn provided |
chat_fn |
NULL |
Custom chat factory: function(role, model, system_prompt, echo)
|
cache_dir |
NULL |
Cache directory; defaults to tempdir()/tempest-cache or TEMPEST_CACHE_DIR env var |
cache_enabled |
TRUE |
Whether search and fetch calls read from and write to the cache |
cache_ttl |
Inf |
Maximum cache age in seconds before search/fetch entries are refreshed |
max_search_results |
8 |
Maximum results per search query |
max_search_queries_per_turn |
3 |
Maximum decomposed search queries per research turn |
retrieve_top_k |
25 |
Maximum facts/chunks retrieved for each section |
max_sources |
24 |
Maximum sources to track per session |
max_active_experts |
5 |
Co-STORM: maximum concurrent active experts |
By default, coordinator and writer roles use openai/gpt-5.6-sol; expert, mind map, and judge roles use openai/gpt-5.6-luna. Tempest constructs these clients with ellmer::chat_openai(), passing auth = "codex" to reuse the ChatGPT subscription authenticated by Codex CLI when the installed ellmer supports it and using OPENAI_API_KEY otherwise (see LLM provider credentials). Built-in clients use lower reasoning effort for mind-map and judge calls; explicit params values override these defaults. tempest_app() limits individual provider requests to 120 seconds by default, configurable with tempest.shiny.provider_timeout_s. The bundled app’s warmup asks each active expert for one brief, bounded, evidence-backed orientation. Each expert reuses session evidence or runs one targeted search, inspects at most two results, and preserves at least one citation. Tempest then commits source-backed claims in stable expert order and updates the shared mind map once for the complete panel. The summary says explicitly when no citable evidence was collected. Warmup concurrency and its 120-second safety timeout are configurable with tempest.shiny.warmup_* options.
To set a personal default model, add the tempest.chat option to your .Rprofile, using the same provider/model format as ellmer::chat():
options(tempest.chat = "anthropic/claude-sonnet-4-20250514")You can also supply a configured ellmer Chat object:
options(
tempest.chat = ellmer::chat_ollama(model = "qwen3.5:9b")
)Tempest clones a configured Chat for each role, prepends its role-specific system prompt, and retains the Chat’s provider settings and existing system instructions. Explicit models and chat_fn arguments take precedence over the option. Use either override when API-key authentication or another provider is preferred.
RAG with ragnar
Enable semantic search over fetched sources by providing an embedding function:
library(tempest)
cfg <- tempest_config(
embed_fn = ragnar::embed_openai(), # or embed_ollama(), custom function
search_provider = "wikipedia"
)
res <- tempest_run("Quantum computing applications", config = cfg)When embed_fn is provided, tempest automatically:
- Chunks fetched web content using
ragnar::markdown_chunk() - Stores chunks with metadata (source_id, url, title, perspective)
- Registers a semantic retrieve tool with chat agents
You can also provide a pre-built ragnar store:
store <- tempest_create_ragnar_store(
embed_fn = ragnar::embed_openai(),
cache_dir = "~/.tempest_cache"
)
cfg <- tempest_config(ragnar_store = store)Custom chat functions
Use custom LLM providers (e.g., internal APIs) with the chat_fn parameter:
cfg <- tempest_config(
chat_fn = function(role, model, system_prompt, echo) {
my_company_chat(
model = model,
system_prompt = system_prompt,
echo = echo
)
}
)Deputy execution identity
Co-STORM moderator and expert calls run through Deputy with product-owned tools. The product-owned session identifier is the live Deputy Agent’s constructor identity, not parallel adapter metadata. Tempest persists only credential-safe terminal execution identities for correlation and audit joins. These references do not grant authority and do not assert causal content provenance.
Interactive Co-STORM
Co-STORM provides an interactive multi-expert research experience with selected or automatically generated expert profiles.
Console Usage
library(tempest)
# Create a session - expert profiles are generated automatically
session <- tempest_session("AI safety and alignment", n_experts = 3)
# See who's on the panel
session$get_expert_names()
#> [1] "Dr. Sarah Chen" "Prof. Marcus Webb" "Dr. Aisha Patel"
# Optional: Run warmup phase (experts research their initial questions)
session$warmup(verbose = TRUE)
#> ℹ Warmup: Dr. Sarah Chen (3 questions)
#> Q: What are the current technical approaches to AI alignment?
#> ℹ Warmup: Prof. Marcus Webb (3 questions)
#> Q: What governance frameworks exist for AI safety?
#> ℹ Warmup complete: 18 facts, 12 sources
# Interactive Q&A - moderator delegates to experts via tools
result <- session$step("What are the main risks from advanced AI systems?")
cat(result$answer)
# Generate and commit a report (styles: "technical" or "executive")
session$publish(style = "technical", include_references = TRUE)
committed_report <- tempest_report(session)
cat(committed_report)session$publish() validates and commits the canonical Co-STORM report. tempest_report() only reads those exact committed bytes from a succeeded, quiescent session; it does not generate or repair a report. There is no separate public renderer that can finalize a product or grant promotion authority.
Expert delegation
The moderator receives one scoped delegate_to_expert(expert_id, question) tool. It resolves the active roster by content-derived expert id, so duplicate display names cannot redirect work. Each expert:
- Has one persistent Deputy-backed chat session with conversation continuity
- Receives only the role-specific tools needed for that run
- Can use host-provided web or evidence tools to find and cite sources
- Extracts claims automatically after each response
- Keeps an opaque, host-inaccessible chat session binding for continuity
The moderator uses the same bounded Deputy runtime. Each completed moderator or expert run records an opaque terminal trace that is carried through Co-STORM snapshot and bundle persistence.
Co-STORM save, snapshot, restore, and resume accept only the exact current schema-13 product. Expert, transcript, mind-map, StageRecord, Workspace, report, suggested-question, and artifact selection state must pass integrity checks. Live chats, tools, credentials, clients, callbacks, and Shiny reactives are recreated rather than serialized.
Warmup Phase
The optional warmup phase has each expert research their initial questions before interactive Q&A begins. This primes the knowledge base with foundational research:
# Skip warmup - jump straight into Q&A
session <- tempest_session("Quantum computing")
session$step("What is quantum supremacy?")
# With warmup - experts research first
session <- tempest_session("Quantum computing")
session$warmup() # Each expert answers 2-4 initial questions
session$step("What is quantum supremacy?") # Benefits from prior researchDynamic Expert Roster
Experts can be added or retired during a session:
error_correction_expert <- tempest_expert(
name = "Dr. Sarah Chen",
title = "Quantum error-correction researcher",
description = "Studies fault-tolerant quantum architectures.",
instructions = "Distinguish demonstrated thresholds from projections."
)
session <- tempest_session(
"Quantum computing",
experts = list(error_correction_expert)
)
session$add_expert("quantum networking")
session$retire_expert(error_correction_expert@expert_id)
session$expertsExpert profiles remain immutable. Retirement is manager-owned session-roster state, persisted separately from the profile. The roster respects max_active_experts (default 5), and retired experts’ tools return a notice that the expert is no longer available.
Report Generation
session$reorganize_mindmap()
report <- session$publish(style = "technical", include_references = TRUE)Shiny App
Untouched model fields inherit options(tempest.chat = ); otherwise the app uses Tempest’s GPT-5.6 Sol/Luna defaults with ChatGPT subscription authentication where the installed ellmer supports it, else OPENAI_API_KEY. Editing any model field switches the app to those explicit per-role model selections.
The app provides:
-
Chat tab: Multi-expert conversation with selected or generated experts, shinychat
chat_server()streaming, cancellation, and greeting support - STORM tab: Scripted research through a responsive asynchronous worker
- Mind Map tab: Real-time knowledge graph visualization
- Sources tab: Table of all retrieved sources with metadata
- Facts tab: Extracted facts with citations and confidence
- Transcript tab: Ordered public Co-STORM turns
- Report tab: Rendered markdown report with footnotes
- Run review tab: Bounded authoritative StageRecord review beside clearly labeled, untrusted live progress observations
Features:
- Configurable number of experts and optional warmup phase
- Report style selection (technical or executive)
- Bounded Co-STORM session download and upload with archive validation
- Polite live status for progress, persistence, and publication, with alerts for validation, cancellation, and publication failures
- Dark mode toggle
Session archives are explicit downloads and uploads. The app does not claim browser-temporary autosave, and its STORM panel does not expose unsupported parallel perspective research. STORM runs in a shiny::ExtendedTask backed by Mirai so the Shiny session remains responsive.
The Shiny modules and their store are Tempest implementation details. The bundled application is reachable only through tempest_app(), and there is no supported contract for embedding the panels in a host app.
The app currently depends on the shinychat development version that provides chat_server(), pinned in DESCRIPTION.
Notes
- This package is designed as a starting point and reference implementation.
- For production use, you may want to:
- use ragnar with domain-specific content (internal docs, databases)
- add guardrails (policy, bias checks) and more systematic evaluations
- customize prompts in
inst/prompts/
References
Papers:
- STORM: Shao, Y., et al. (2024). Assisting in Writing Wikipedia-like Articles From Scratch with Large Language Models. NAACL 2024.
- Co-STORM: Jiang, Y., et al. (2024). Into the Unknown Unknowns: Engaged Human Learning through Participation in Language Model Agent Conversations. EMNLP 2024.
- DSPy: Khattab, O., et al. (2024). DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines. ICLR 2024.
Code that inspired tempest:
- stanford-oval/storm — the reference STORM and Co-STORM implementation this package ports to R.
- stanfordnlp/dspy — the DSPy framework that dsprrr brings to R for tempest’s optimizable modules.
- Stanford STORM project — live demo and project overview.
Retain reviewed research across runs
Use tempest_publish_artifact_research() to preserve a completed report and its full evidence through Graft’s artifact store. After explicit host acceptance, tempest_reuse_artifact_research() checks the current decision and host policy before research admission. Historical inspection uses tempest_read_artifact_research() and grants no current permission. See retained artifact evidence for an executable offline example and session resume rules.
