TurnStage
Debug the stream. Save the case. Check the next run.
TurnStage tests streaming LLM chat and agent APIs. Use it as a VS Code
extension or a standalone Web app. A Profile describes the endpoint and event
mapping; TurnStage keeps the conversation, HTTP requests, stream events,
timing, test results, and evidence together.

Synthetic local fixture in a VS Code-style Webview. No live API, credential,
customer endpoint, or production conversation is shown.
TurnStage is a public preview. It is a testing and evidence workbench, not a
model-safety certification service or an autonomous attack platform.
Inspect one conversation from message to network event
Inspect the rendered response beside its request, headers, timing, SSE or
NDJSON events, normalized mappings, errors, and correlation identifiers. Stop
or replay a turn, then reopen its evidence without reconstructing the session
from logs.
Click an Assistant response to compare its rendered preview with the complete
raw content assembled from the mapped stream events. Markdown and static HTML
share the same renderer in VS Code and Web. Profiles may map safe visual styles
to response class names and use bounded, response-scoped class selectors for
child, descendant, adjacent, and interaction-state styling. Scripts, handlers,
arbitrary CSS, remote styles, and unsafe URLs remain blocked.
Save and run general test cases
Save a conversation as a draft test, review its expected behavior, and run it
again. Create single- or multi-turn cases inline, or link JSONC/CSV files for
version control. Search and select cases in the Profile editor, run one or
several, and see why a case is unavailable before trying to run it. VS Code
Test Explorer and the headless CLI can also run ready cases.
Case lists, imports, linked suite files, and manual runs have no fixed total-case,
aggregate attempt, request, or suite-file size ceiling. Per-case
validation remains in place; browser storage, device capacity, and the target
API's rate limits are practical considerations for large Web runs. Web imports
large suites without blocking the case-list UI and saves completed-case
checkpoints. If a page closes during a run, review its history and explicitly
choose Run remaining cases; TurnStage does not automatically resend a
request whose outcome is unknown.
Use Pause tests to stop dispatching new cases. Cases already running finish
their turns and repetitions before the run becomes paused. Resume tests
continues the same run without repeating completed cases; Stop test run
aborts active requests and preserves completed results and unfinished-case
history. These controls work in Web and VS Code. Pausing requires keeping the
page or editor host open; reopening an interrupted run requires explicitly
running its remaining cases. Explicitly configured request/duration budgets
still apply. Copilot-triggered runs retain their separate execution budgets.

Results stay separate from case setup. Pick a previous run, compare it with an
accepted baseline, rerun cases that did not pass, or export that run. Open a
failed result to inspect its captured Chat, Network, Raw Events, or Normalized
Events. Configured reports and Evidence Bundles use a sanitized projection for
local review or CI. Explicitly downloaded general and red-team HTML reports are
separate, self-contained detailed artifacts with success and latency summaries,
every case and retained evidence, search, outcome and non-passing filters,
pagination, mobile layouts, expandable details, and complete print rendering; a
run export stays scoped to that run. Treat downloaded detailed HTML as
confidential when the underlying conversation or payload is confidential.

Keep red-team regressions reproducible
Red Team has its own case list and results, not a mixed view with general
tests. Replay bounded adversarial cases against observable rules such as
forbidden content, URLs, CTAs, tools, or normalized events. Repeat a case in
fresh conversations to see unstable behavior instead of relying on one sample.
Results distinguish Resisted, Attack succeeded, Indeterminate, and
Infrastructure error. A timeout or incomplete evidence never counts as a
pass.

All screenshots above use synthetic local fixtures; they do not show a live
service, real user data, or a production test outcome.
VS Code extension and standalone Web are separate builds
The Marketplace installs the VS Code extension. It uses a desktop or remote
Extension Host for workspace-linked suites, Test Explorer, and optional Copilot
integration. The repository also provides a standalone Web
build
as a static ZIP; it does not load or run the VSIX. Web users can choose
deployment-owned read-only Profile presets or make browser-local copies.
Browser-local Profiles can be edited as JSONC with syntax and schema diagnostics,
formatting, problem navigation, and recoverable unsaved drafts; deployment-owned
files remain read-only until copied.
Web users can export a Profile with its imported General tests and Red Team
suites as one portable file. Older Profile-only exports still import normally;
neither export includes run history or detailed evidence.
VS Code-only actions are disabled in Web. The browser calls the configured API
directly, so that API must allow the Web origin through CORS.
Why use TurnStage
- Provider-neutral Profiles describe request variants, streaming protocols,
event mappings, controls, openings, and response UI without provider code in
the Webview.
- Evidence before guesswork links messages and results to the exact Network
and event records that produced them.
- Separate general and red-team workflows share familiar case selection,
run history, and evidence navigation while retaining distinct outcomes.
- Performance and comparison checks cover TTFT, duration, visual baselines,
baseline/candidate differences, and controlled Fault Lab experiments.
- Local-first security uses Workspace Trust, VS Code SecretStorage, bounded
retention, explicit disclosures, and redacted diagnostics without automatic
TurnStage telemetry.
- Optional Copilot assistance can diagnose results, explain configuration,
draft a regression, or prepare a guarded Profile repair. Copilot output is
advisory and cannot relabel deterministic test results.
Five-minute start in VS Code
- Open the TurnStage Activity Bar view.
- Run TurnStage: Initialize Workspace and select a starter Profile.
- Open the Profile and send a message in Debug using your configured API,
or start the bundled local mock server to explore without credentials.
- Inspect the same turn in Network, Raw Events, and Normalized
Events.
- In General tests or Red Team, add or select a case, run it, and use
Results → Open evidence to inspect what happened.
For an existing API, use TurnStage: Create Profile from cURL. TurnStage
parses a bounded cURL subset without invoking a shell, excludes captured
messages and tools, and replaces detected credentials with SecretStorage
references before you save the Profile.
Copilot is optional
The @turnstage Chat participant and TurnStage language-model tools work when a
compatible VS Code language model is available. Use /diagnose, /run,
/compare, /configure, or /evidence from VS Code Chat. Core chat, replay,
debug, testing, red-team, CLI, and export features remain available without
GitHub Copilot.
Privacy, trust, and support
TurnStage has no operated cloud service and sends no automatic product
telemetry. Requests go to the endpoint selected by the active Profile. A
request-backed opening is allowed to run once when a trusted Profile editor
opens because that behavior is explicitly part of the Profile.
Read PRIVACY.md, SECURITY.md, and
SUPPORT.md before using TurnStage with a sensitive environment.
The full implementation boundary is documented in
docs/security.md.
Requirements and installation
The repository uses Node.js 24, npm (package-lock.json), and the npm scripts
in package.json. Install dependencies and build the extension bundle with:
npm install
npm run compile
The extension manifest targets VS Code ^1.106.0. Development and CI use Node
24. The Extension Host and headless CLI bundles target Node 20 for VS Code
runtime compatibility, and the Webview bundle targets ES2022.
For local development, open this folder in VS Code and run the extension from
an Extension Development Host. The production packaging command is:
npm run package
That command compiles and invokes vsce package --no-dependencies, producing
a VSIX in the project root (VSIX files are ignored by Git).
After compilation, the repository-local CLI can run linked profile tests against
a configured backend (secrets are resolved only from process environment
variables; .env files are never loaded):
./dist/cli.js run --workspace . --changed-file src/chat/client.ts --format junit
./dist/cli.js verify path/to/evidence/provenance.json
TurnStage currently targets desktop and remote VS Code Extension Hosts. It
does not declare a browser entry and therefore does not claim support for
vscode.dev or github.dev.
Standalone Web build
Build the static Web app (Node.js is needed to build, but not to serve it):
npm run package:web
Extract turnstage-web-<version>.zip and serve its contents with a static HTTP
server. For local development, run npm run web:dev. To preview the packaged
files, run python3 -m http.server 8000 --bind 127.0.0.1 from the extracted
directory and open http://127.0.0.1:8000/ (Python 3.6 or newer). Do not open
index.html directly. The Python server is for local preview, not production.
For a simple internal HTTP-by-IP deployment, the ZIP also includes serve.py.
Run it from the extracted directory and explicitly choose the Web listener and,
only when needed, the single API upstream:
python3 serve.py --port 9095 --bind 0.0.0.0 --upstream http://127.0.0.1:9098
The numbers above are deployment examples, not embedded company endpoints.
Without --upstream (or TURNSTAGE_API_UPSTREAM), the /api/ proxy is
disabled and requests to it return 503. Point a Web Profile at the Web
server's same-origin /api path only when that proxy is configured.
See the deployment steps
and plaintext-transport warning. A managed reverse proxy is recommended for
durable shared deployments.
To provide default Profiles, copy VS Code *.turnstage.jsonc files into the
extracted profiles/ folder. Nested directories appear as nested Web sidebar
folders. Copy any referenced *.environment.jsonc files into environments/,
then run python3 update_profiles.py from the extracted directory.
The included update_profiles.py generates turnstage-catalog.json; Python
is not needed while a separate static Web server runs. Users can duplicate a
default Profile or create, import, edit, and export their own browser-local
Profiles, including direct JSONC editing in the Web app. Default Profiles stay
read-only until duplicated. A portable Profile export includes its referenced
Environment and any plaintext credentials in those settings. Browser requests
pointed at the same-origin /api/ route use the fixed server-side proxy;
requests pointed directly at
other origins must be reachable from the user's device and allow the Web
origin through CORS. See the Web deployment guide for
administrator setup and security boundaries, and the
Web user guide for everyday use and backups.
First run in VS Code
- Open a workspace folder in VS Code.
- Open the TurnStage Activity Bar view.
- Run TurnStage: Initialize Workspace and choose a starter option.
- Select a profile in the
Profiles Tree View to open the Custom Editor.
Alternatively, use Create Profile from cURL in the Profiles view, paste
a supported OpenAI-compatible request, inspect the sanitized untitled draft,
and explicitly choose Create Sanitized Profile. TurnStage never executes
the pasted command.
- Use Configure Profile for the eight profile configuration sections, or
use Run Profile for the phone chat preview and Debug inspector. Recorded
runs are in the Debug panel's Runs tab.
- Open VS Code's Testing view to run any scenarios declared under
tests.scenarios. Conversation-contract tests make real profile requests
and therefore require a trusted workspace. TurnStage: Run Conversation
Contracts provides the same deterministic extension-owned entry point for
automation.
- Open the Profile's Tests tab to author and run conversation contracts,
inspect functional/comparison/performance results, and select several cases
together. Small cases can stay inline; use Link suite for an existing
.tests.jsonc, .tests.json, or CSV file without copying it into the
Profile. Report, advisory-review, and visual-regression defaults remain
under Configure Profile → Test settings. Run
TurnStage: Export Contract Test Report to save the latest sanitized
results manually.
- Add known red-team regressions under Red Team → Cases. Link a
workspace-relative JSONC or CSV source for portable Git-managed cases, or
explicitly select an external file when the suite must remain elsewhere.
External links are local, Profile-bound authorizations and are not portable
to another machine. Select Edit on a linked case to load and change only
that case with structured controls, or use Open source for unrestricted
source editing. Import/export copies remain available for spreadsheet
exchange; triage the four outcomes in Test Explorer or Red Team results.
- From a latest result, choose Diagnose with Copilot to explain timeout,
TTFT, stream, mapping, assertion, comparison, or repeat-stability evidence.
Use Profile Doctor for configuration-only diagnosis. Response-quality
review is a separate, explicit Advisory action and never replaces the four
deterministic adversarial outcomes. You can also open VS Code Chat and use
@turnstage /diagnose, /run, /compare, /configure, or /evidence with
an exact Run, Evidence, Profile, Case, Failure ID, or test selection.
- After a real request, open Configure Profile → Request → Connection
Doctor and choose Analyze latest response. The diagnosis reuses the
latest bounded evidence; it does not consume another request or model call.
Initialization is explicit. Merely installing the extension, opening a
workspace, or opening the sidebar does not create profile files. Existing
files are offered Skip, Create Copy, or Replace; replacement needs
an additional confirmation.
The built-in demo can also be opened without writing to the workspace by
running TurnStage: Run Profile with no selected profile. It uses the
bundled Basic SSE fixture and does not issue a network request.
Profile storage layers
The Profiles view separates Workspace and User profiles. Workspace
initialization writes this layout under the selected workspace folder:
.vscode/
└── turnstage/
├── profiles/
│ ├── basic-sse-chat.turnstage.jsonc
│ ├── agent-flow.turnstage.jsonc
│ └── enterprise-chat.turnstage.jsonc
├── environments/
│ └── local.environment.jsonc
└── fixtures/ # only for the mock-server option
├── basic-sse-chat.jsonl
├── agent-flow.jsonl
└── enterprise-chat.jsonl
The extension ships equivalent templates, schemas, and fixtures under
resources/. Profiles and environment files are ordinary workspace files and
can be reviewed and committed to Git. Secret values must not be committed.
TurnStage: Initialize User Profiles writes reusable profiles and
environments under the extension's globalStorageUri/configuration directory.
Those files are available to every workspace handled by the same Extension
Host. A Workspace profile resolves environments from its own workspace first
and falls back to the user definition when IDs match; a User profile uses User
environments, which avoids ambiguous overrides in multi-root workspaces.
Creating or importing a profile asks which layer should receive the file.
A Workspace profile with the same id is treated as the project's full-file
replacement for the User profile; the User item remains visible and is marked
Overridden so its reusable base can still be edited. Profile files are not
deep-merged because mapping-array order and security policy must remain
explicit.
Starter profiles
Basic SSE Chat
resources/templates/basic-sse-chat.turnstage.jsonc is a small POST + SSE
profile. It has a static opening, two starters, first-turn and continuation
request variants, a stop request, text/progress/title/done/error mappings, a
split chat/inspector layout, local run retention, and metrics. It intentionally
does not include tools, forms, citations, actions, or follow-ups.
Agent Flow
resources/templates/agent-flow.turnstage.jsonc demonstrates a request-backed
opening, dynamic starter options, fallback text, actor/model/debug controls,
first-turn and continuation variants, bearer-secret resolution, stop handling,
and mappings for progress, markdown, tools, citations, actions, forms,
follow-ups, diagnostics, usage, title, completion, and failure.
Enterprise Chat Contract
resources/templates/enterprise-chat.turnstage.jsonc is a synthetic contract
fixture for a common POST + SSE application flow. It covers a request-backed
opening with a 7021 fallback, optional opening choices, different first-turn and
continuation bodies, global actor selection, per-turn event counts, remote
stop, partial-content retention, error finalization, follow-ups, CTA payloads,
diagnostics, and reference-only remote history. Its unknown custom_card
fixture deliberately remains unmatched and inspectable in Raw Events instead
of becoming a built-in renderer. All identity and profile values are visibly
synthetic. See docs/enterprise-chat-profile-guide.md for setup and adaptation.
Local environment
resources/templates/local.environment.jsonc contains only non-secret values:
{
"version": 1,
"id": "local",
"name": "Local Mock Server",
"variables": { "baseUrl": "http://127.0.0.1:8787" },
"secretReferences": { "apiToken": "local-api-token" }
}
secretReferences maps a profile placeholder name to a SecretStorage key; it
does not store the value.
Profile-driven conversation
Opening
opening.mode is static, request, or disabled.
static displays the configured message immediately and makes no opening
request.
request starts automatically once when a trusted Profile editor opens,
resolves the response message and starter paths, and can use a configured
fallback for a network failure. Rehydrating the Webview or editing Profile
metadata does not silently repeat the request; a failed opening exposes an
explicit retry.
disabled leaves the session ready without an opening message.
Opening requests have a bounded timeout and expose retry, configured fallback,
and request-inspection actions on failure. Fallback rules are evaluated in
order against response data, HTTP status, a missing-message marker, or the
runtime error type; an unconditional fallback is the final catch-all.
First turn and continuation
conversation.send.variants are evaluated in order. The starter templates use
conversation.id to select first-turn when it does not exist and
continuation when it does. An interaction is carried in
turn.interaction, including starter, follow-up, response-action, form-submit,
and retry metadata.
HTTP streams
Network work is host-side. SSE uses fetch, an AbortController, UTF-8
decoding, chunk-safe parsing, event/id/retry fields, comments, multiline data,
CR/LF/CRLF, an optional leading BOM, blank-line dispatch, a partial final event,
and the [DONE] sentinel.
NDJSON uses a chunk-safe line buffer. HTTP status and content-type failures,
network failures, total timeout, idle timeout, user abort, and panel disposal
become runtime results.
Reconnect is opt-in and bounded. It is attempted only before the first raw
event, so a partially received response is never automatically replayed.
Redirects are processed manually with a bounded hop count; the default policy
allows same-origin redirects only. Explicit cross-origin following strips
credential headers and headers containing SecretStorage-resolved values.
json, text-stream, and fixture are accepted profile transport values.
text-stream emits decoded chunks as raw events without JSON parsing; json
buffers one bounded complete JSON document instead of treating lines as
records. dataFormat: "text" keeps SSE/NDJSON payloads as text. Fixture
profiles load .vscode/turnstage/fixtures/<profile-id>.jsonl and replay it
through the same mapping/reducer pipeline without a network request.
Mapping and chat content
Each stream mapping matches an SSE event name and/or a data path, extracts
{ "path": "$.field" } values, and emits a version-1 normalized event. The
reducer turns normalized events into the current SessionSnapshot:
- text and markdown deltas append to assistant parts;
- progress updates maintain a collapsible progress part;
- tool start/argument/result events maintain tool-call parts;
- citation, follow-up, and response-action events upsert message entities;
- forms, diagnostics, and usage become message parts;
- completion, failure, and abort terminal events finish the turn.
See docs/event-mapping.md for event types and matching semantics.
These are declarative event payloads. The Webview renders source lists,
follow-up chips, response-action buttons, and form fields. Citation opening and
VS Code command execution are mediated by the Extension Host. Forms validate
required values, maximum length, and patterns in the Webview before sending a
formSubmit interaction.
The current UI exposes built-in copy/retry/edit actions and does not execute
arbitrary backend JavaScript, HTML, CSS, shell commands, or unallowlisted VS
Code commands. Cancelling a rendered form clears its local values and marks it
cancelled without sending a request.
Stop, errors, and new conversations
Stop changes the turn to stopping, aborts the local fetch, optionally sends a
configured remote stop request, and finalizes the turn as aborted. Remote
stop failure is a non-blocking warning; local completion remains aborted.
Every normal terminal path calls the idempotent finalizeTurn path. A
completed, failed, or aborted run ends the assistant status, updates metrics,
and (unless disabled) records a local run. unexpectedEndPolicy defaults to
failure; completeWithWarning is also supported.
New conversation is disabled while a turn is active. It creates a new
session snapshot, clears conversation/event state, preserves configured
controls, and runs the opening flow again.
History, Replay, and Metrics
Local runs are stored in VS Code global storage under the profile ID, retained
according to history.localRuns.maxRuns or turnstage.runRetention, and can
be imported or exported as versioned *.turnstage-run.json files. Import also
accepts the legacy unversioned export, rejects mismatched profile IDs, and
creates a new run ID instead of overwriting a duplicate. A replay restores the
recorded conversation through the last user message, then feeds saved raw
events through the same Mapping Engine and reducer; it never calls the backend.
Runs recorded without raw events remain inspectable and exportable but are
clearly marked as unavailable for replay.
The Replay page can delete one local run or clear the current Profile's local
history after a native confirmation. Deletion never removes exported
*.turnstage-run.json files and is disabled while a request or replay is
active.
Profiles may also enable reference-only remote session history. It stores only
the conversation ID and metadata scoped by workspace/profile/actor/environment.
Applying one never fabricates history: TurnStage clears the visible chat and
states that previous messages were not loaded.
Replay preserves recorded event spacing and supports 0.25×, 0.5×, 1×, 2×, and
4× playback plus pause, resume, step, and stop. Single-run metrics include
headers latency, first chunk/event latency, TTFT, stream/total duration, event
and byte counts, event gaps, parse/mapping/unmatched counts, and abort reason.
No percentile statistics are produced.
Each chat message has VS Code-native icon actions for Copy, Retry,
Edit-and-resend, and Inspect. They are visible by default for discoverability;
profiles can opt into interaction-only visibility. Backends can also expose
arbitrary per-message measurements through message.metric.updated mappings,
including message correlation, display format, and first/last/sum/min/max/count
aggregation. Assistant messages also expose built-in TTFT and total-turn time,
measured by the Extension Host from the request start. The compact footer shows
only these two built-ins by default. metrics.messageEnabled can explicitly
opt into a mapped metric ID, but backend-reported duration or token values stay
out of the chat surface by default and remain available in Debug data. A
backend usage.updated message part is likewise hidden in Chat unless
ui.components.usage.visible is explicitly enabled.
The test workspace keeps Chat on the left and provides Debug, General tests,
Red Team, and Configure as four modes of the right pane. Each testing
mode has its own Cases and Results tabs. General and
Red Team judgments remain distinct while sharing selection and metadata-only
run history. Search and bounded paging keep large suites responsive. Configure exposes the same
eight profile sections as the command-driven configuration flow: General,
Opening & Flow, Request, Stream & Mapping, Chat UI, Test settings, History & Errors,
and Security. Every GUI edit is applied as a
structured WorkspaceEdit to the open .turnstage.jsonc document, so the
profile file remains the source of truth and VS Code Undo/Redo continues to
work. Configure shows pushed saved/dirty and validation state without polling,
and keeps Save, Open JSONC, Validate, and first-issue navigation in its compact
toolbar. TurnStage: Go to… provides native Quick Pick navigation to Chat,
Debug, General tests, Red Team, or common Configure destinations.
Debug's Network tab presents every Opening, Conversation Stream attempt,
retry, and Stop request as a compact request list. Selecting a row exposes
Chrome-style Headers, Payload, Response, and Timing views,
including status, first-chunk latency, total/idle timeout settings, transferred
bytes, event count, and a structured failure such as IdleTimeoutError. The
filter searches request kind, method, URL, status, state, and variant. Network
entries are live-session diagnostics: restarting the session clears them and
they are not added to Recorded Runs. The Network Headers view masks outgoing
Authorization and other structurally sensitive header values while preserving
useful metadata such as the authentication scheme.
Commands and settings
Commands are registered under the turnstage namespace:
| Command |
Purpose |
turnstage.initializeWorkspace |
Create starter workspace files with conflict handling |
turnstage.createProfile |
Create a duplicate-safe empty profile |
turnstage.importProfile |
Import a valid JSONC profile with duplicate-safe naming |
turnstage.initializeUser |
Initialize reusable user profiles and a user environment |
turnstage.duplicateProfile / turnstage.deleteProfile |
Copy a discovered profile or move it to Trash after confirmation |
turnstage.openProfile |
Open a profile in the custom editor |
turnstage.goTo |
Jump to Chat, Debug, Tests, Red Team, or Configure with a native Quick Pick |
turnstage.configureProfile |
Open Profile Configuration for the selected profile |
turnstage.runProfile |
Open/run a selected profile or the built-in Basic demo |
turnstage.startSession |
Explicitly execute a request-backed opening |
turnstage.abortRequest |
Stop an active turn |
turnstage.newConversation / turnstage.clearConversation |
Reset conversation state |
turnstage.validateProfile |
Publish Problems diagnostics |
turnstage.openAsText |
Open the same document in VS Code's text editor |
turnstage.selectEnvironment / turnstage.openEnvironment |
Select or edit an effective workspace or user environment |
turnstage.setSecret / turnstage.removeSecret / turnstage.listSecretNames |
Manage secret names and values |
turnstage.replayRun / turnstage.importRun / turnstage.exportRun |
Replay the latest run, or import/export versioned local-run files |
turnstage.openOutput |
Show the TurnStage Output Channel |
turnstage.changeDisplayLanguage |
Choose Auto, Traditional Chinese, Japanese, Korean, or English for TurnStage profile editors |
turnstage.migrateProfile |
Migrate a version-0 profile after confirmation, backup, and diff review |
turnstage.refreshProfiles |
Refresh discovery and cross-file duplicate-ID diagnostics |
The contributed settings are:
| Setting |
Default |
Effect |
turnstage.displayLanguage |
auto |
Application-wide language for TurnStage profile editors: follow VS Code, Traditional Chinese, Japanese, Korean, or English |
turnstage.profileGlob |
.vscode/turnstage/profiles/*.turnstage.jsonc |
Workspace-relative discovery glob |
turnstage.maxBufferedEvents |
5000 |
Maximum raw and normalized events kept in the live session |
turnstage.maxConversationMessages |
500 |
Maximum conversation messages kept in memory (50–5000) |
turnstage.maxBufferedBytes |
10485760 |
Maximum raw-buffer JSON bytes (10 MiB) |
turnstage.streamBatchIntervalMs |
32 |
Batching interval for Host-to-Webview session updates (16–100 ms); normal updates are deltas after a full checkpoint |
turnstage.runRetention |
20 |
Fallback local-run retention (1–100) |
turnstage.logLevel |
info |
Minimum output-channel level: error, warn, info, or debug |
turnstage.notifications.enabled |
true |
Show non-modal TurnStage notifications; selecting Do not show again sets it false at user scope |
For request failures, run TurnStage: Open Output. The default info level
records a correlated request timeline with the profile/environment, request
build time, method, URL without its query or fragment, selected variant,
header/body byte counts, configured timeouts, response status/content type,
safe server request/trace IDs, first-chunk latency, last event, terminal-event
state, maximum chunk gap, parser/mapping/drop counts, byte/event totals, and
retry count. Safe network-client error codes distinguish DNS, proxy, TLS,
connection, and TurnStage timeout failures. Set turnstage.logLevel to debug
to add each transport attempt, chunk, SSE event name, mapping result, retry
delay, and the exact timeout that fired.
Headers, request bodies, query values, SSE payloads, and known secrets are not
written to the Output Channel.
Connection Doctor also reports a bounded Network path assessment using
non-secret VS Code and environment configuration facts plus response metadata.
It labels routes as likely proxied, direct possible, or unknown rather than
claiming certainty. Proxy URLs, credentials, and NO_PROXY entries are never
copied to the Webview or Output. For development or company endpoints that
cannot present a trusted certificate, Configure → Request → Network
resilience can enable request-local invalid-certificate access. The unsafe
mode is explicit, remains visible in the UI and Output, never changes global
TLS state, and fails closed when a system/PAC proxy route cannot be preserved.
Use Debug → Network for the request and response view. For a timeout, first
check whether a row received an HTTP status, then compare Headers, First
chunk, Total, and Idle timeout under Timing. Output remains the more
durable correlated timeline; Network deliberately includes bounded, redacted
request/response previews for the current editor session only. Sensitive header
values remain masked, and Output never records them.
displayLanguage has VS Code application scope, so one User setting applies
across projects. profileGlob has VS Code resource scope. The runtime-limit settings use
explicit window scope, so they can be set once in User Settings and optionally
overridden by a workspace. Machine-specific reusable endpoint and command data
belongs in a User environment; credentials remain in SecretStorage.
Workspace Trust and secrets
In an untrusted workspace, profiles remain viewable/editable and bundled
fixture replay remains available, but session requests and request-backed
openings are blocked. The Webview displays a restricted-mode banner. Citation
opening also requires trust.
Request-backed openings run automatically when a Profile opens in a trusted
workspace, including Profiles opened from Downloads. Open only Profiles whose
configured destination and request content you trust. Requests that send known
secrets over non-loopback HTTP, or disable certificate verification, still ask
for consent and show the destination, method, secret usage, and TLS state.
Allow this Profile remembers a hashed workspace-local grant; Allow once
does not. Changing the destination, request definition, secret references, or
TLS mode asks again for these higher-risk requests.
Use TurnStage: Set Secret to store a value in VS Code SecretStorage. Only
the Extension Host resolves ${secret.name}. Request previews redact sensitive
headers and secret/token/password-like body fields before sending data to the
Webview. File and symbol citations accept only normalized workspace-relative
paths. See docs/security.md for the exact current policy and its boundaries.
Local mock server
The mock server uses Node's built-in HTTP module and listens only on
127.0.0.1:8787:
npm run mock-server
Endpoints used by the starter profiles:
POST /basic/chat/stream
POST /basic/chat/stop
POST /agent/opening
POST /agent/chat/stream
POST /agent/chat/stop
POST /v1/chat/opening
POST /v1/chat/stream
POST /v1/chat/stop
The server emits example SSE events and has deterministic modes selected by
x-turnstage-mode or body.mode: normal, rich-html, rich-markdown,
rich-mixed, rich-complex, slow, chunk-split,
malformed-json, unknown-event, partial-error, http-401, http-500,
idle-timeout, and disconnect. It does not call an LLM. Do not treat the
example endpoint or example.com citation as a production service.
Both starter profiles expose the streaming and failure modes as a Mock Scenario
control. The Agent Flow profile also exposes the four rich-content modes and
maps responses as Markdown, so static HTML and Markdown can be tested together.
The Basic SSE Chat profile maps responses as literal text and deliberately does
not render those rich-content modes.
The Enterprise Chat Contract profile additionally exposes contract-slow,
contract-error, contract-actions, and opening-options. Its mock API
validates trimmed input, first-turn versus continuation fields, stop IDs, and
the start → status → message → title → done terminal sequence. This server is
only a behavioral simulator; it never calls an internal service or an LLM.
Development checks
Available scripts are:
npm run typecheck
npm run lint
npm test
npm run test:sse
npm run test:integration
npm run benchmark
npm run compile
npm run package
npm run test:sse starts the real local HTTP server on an ephemeral port and
verifies incremental SSE delivery, split chunks, malformed and unknown events,
HTTP/idle failures, and partial-stream abort behavior. npm run test:integration
launches a clean VS Code Extension Host and verifies
activation, command registration, workspace discovery, and profile validation.
npm run benchmark measures parser, mapping, bounded-buffer, and reducer
scenarios. Observed results and their environment are recorded in
docs/performance.md.
Further reading
- Architecture and runtime lifecycle:
docs/turnstage-architecture.md
- Profile and environment schema:
docs/profile-schema.md
- Raw-event mapping and normalized events:
docs/event-mapping.md
- Security, trust, secrets, and redaction:
docs/security.md
- Performance budgets and measurement plan:
docs/performance.md
- Functional suites, linked CSV/JSONC, and Copilot selectors:
docs/automated-testing.md
- Adversarial suites, repetitions, campaigns, and evidence:
docs/adversarial-testing.md
- Required VS Code UI review standard:
docs/vscode-extension-ui-guidelines.md
- Current VS Code UI audit:
docs/vscode-ui-audit-2026-08-27.md
Scope boundaries
The current implementation does not provide a low-code drag-and-drop builder,
arbitrary user scripts/components/styles, provider SDKs, a general MCP server,
cloud sync, accounts, collaborative storage, automatic telemetry, or autonomous
backend actions. Its Copilot integration is limited to the contributed bounded
language-model tools and @turnstage participant; it does not grant Copilot
arbitrary Profile edits, shell access, or unconfirmed network execution.
Configured actions require a user interaction and VS Code command actions
require an allowlist.