Skip to content
Source
Sign inSign up

providers.md

174 lines12.4 KB

1# Provider constraints
2​
3Orchestration records intent and state without knowing which provider runs a thread. Provider
4protocols, account ownership, permissions, and capabilities belong at the
5[adapter boundary](../../packages/provider-core/src/server/ProviderAdapter.ts). Normalize there
6instead of spreading provider checks through orchestration and clients.
7​
8A driver kind identifies an integration; an instance identifies one configuration and account
9lifecycle. Route work by instance, so two accounts using the same driver do not share mutable
10session or catalog state. For a new driver, start with [adding a provider](./adding-a-provider.md).
11​
12## Process and account isolation
13​
14The `opencode` driver probes the installed version and runs the 1.x or 2.x runtime. OpenCode's MCP
15registrations are directory-scoped, while T3's MCP connection is thread-scoped, so threads in one
16directory must not share one T3 MCP entry.
17​
18- **1.x** uses one T3-managed chat server per thread, so threads cannot replace each other's
19 connection. Catalog and text-generation work can share the
20 [instance-owned helper](../../packages/provider-opencode/src/server/OpenCodeServerOwner.ts), which closes
21 after an idle period. See the [1.x adapter](../../packages/provider-opencode/src/server/adapter.ts).
22- **2.x** serves every directory from one
23 [server per instance](../../packages/provider-opencode/src/server/v2/OpenCode2Server.ts). Each thread
24 registers its own `t3-code-<thread>` MCP entry, and session permission rules deny every other
25 thread's entry. See the [2.x adapter](../../packages/provider-opencode/src/server/v2/adapter.ts).
26​
27External OpenCode servers remain externally owned and can require an external restart to pick up
28configuration changes. OpenCode stores "always" approval grants for the whole project. Automatic
29full-access replies use `once` so they cannot widen a supervised thread's permissions on a shared
30server. On 2.x, a session-wide approval also replies `once` and becomes T3's own rule on that session.
31​
32Pi runs the user's own `pi` install in RPC mode and owns native extension, package, and project
33trust discovery. T3 injects only its namespaced MCP bridge, so a Pi session behaves as it does in
34the Pi TUI. Pi session files back native resume, rollback, and same-instance thread forks.
35Forks use Pi's CLI in the destination directory because RPC session switching retains the source
36session's cwd. Provider switches still use portable handoff summaries.
37See the [adapter](../../packages/provider-pi/src/server/adapter.ts).
38​
39Antigravity separates account profiles per instance while sharing installed executables across the
40environment. It forces file-based credential storage because the native macOS keychain entry would
41otherwise be shared across instances. The launch environment removes ambient Google credentials,
42so an instance cannot silently use another account or billing project. The agent also resolves
43its user-global skill directories under that profile, so the profile links those two directories
44back to the user's real `~/.gemini`; MCP servers, hooks, and rules there stay out of the profile.
45See [profile isolation](../../apps/server/src/provider/antigravityAuthSupport.ts).
46​
47The [Antigravity installer](../../apps/server/src/provider/AntigravityInstallation.ts) outlives
48client connections and provider-instance rebuilds. Releases are immutable, with an atomic pointer
49selecting the version for new processes. Running processes hold leases on their version. Updates
50and removal must respect those leases instead of replacing executables under a running agent.
51​
52## Setup must not happen as a health-check side effect
53​
54Opening a provider session can start MCP servers, run hooks, or launch a login browser.
55[Grok probes](../../packages/provider-grok/src/server/status.ts) avoid authentication and
56session creation for this reason. Antigravity likewise reserves authenticated catalog sessions for
57explicit setup or model refresh; background checks use initialization only.
58​
59[Antigravity sign-in](../../apps/server/src/provider/AntigravityAuth.ts) belongs to the initiating
60T3 auth session. The client carries the return URL back to the environment because the provider's
61loopback listener may be on another machine. Forward only the callback for the owned pending flow;
62a successful callback HTTP request is not proof that provider authentication finished. The native
63process owns token exchange and storage.
64​
65Managed ChatGPT sign-in for a remote environment can finish on a local primary. The
66[primary handoff](../../apps/server/src/provider/CodexChatGptHandoff.ts) uses an ephemeral
67credential store and the destination's environment ID. It exchanges and verifies the code before
68transferring the issued client registration and tokens. Only the destination persists and refreshes
69that session; retaining a primary refresh session would race refresh-token rotation. Without a local
70primary, the client uses the remote callback completion flow.
71​
72Antigravity sign-out closes admission to new processes and stops existing processes before clearing account
73metadata. Otherwise a helper or resumed session could retain the old account. Cached model lists
74do not establish current access, and an authoritative empty catalog must clear the old list.
75​
76Antigravity text-generation helpers deny tool requests, but native hooks and MCP configuration can
77run before the prompt. They reject profiles with such configuration before launch. Prompt
78instructions and tool denial do not create a native sandbox.
79See [helper constraints](../../apps/server/src/textGeneration/AntigravityTextGeneration.ts).
80​
81## Provider updates run only through the owning installer
82​
83A package manager runs only when the resolved executable's path proves it owns the install. Homebrew
84and npm are proven by the real path (symlinks followed): a versioned keg or cask under
85`brew --prefix`, or `<prefix>/lib/node_modules/<pkg>/` (Windows: the shim beside `node_modules`).
86Native installer layouts and the global directories of pnpm, Bun, Yarn, and Vite+ may match on
87either the resolved path or its real target, since those installers place real files or their own
88symlinks there. Volta is proven by its `volta-shim` link plus the package's image directory. When
89nothing is proven, Pi's and Grok's own updaters (`pi update --self`, `grok update`) run instead,
90because each detects its installer itself; the runner's version check catches an updater that exits
910 without updating. Claude, Codex, and OpenCode updaters only act on their own native layout, so
92those providers stay manual-only unless that layout is proven. Mise is checked
93first, since a `mise x` wrapper can sit at a native installer's path, and `MISE_DATA_DIR` can put
94installs anywhere, so `mise which` is asked whenever mise is on `PATH`. Each driver names its fixed
95mise tool; mise must resolve the binary and list it in an active install of that tool requested as
96`latest`, which gets `mise upgrade <tool>`. T3 Code keeps launching the configured path, so that
97path must be a shim or under the tool's `latest` link, which the upgrade repoints: a version
98directory (including one `mise activate` put on `PATH`) would keep running the old binary until mise
99prunes it. Everything else mise touches (fixed version paths, pinned tools, wrappers, OpenCode 1.x,
100whose mise tool crosses into 2.x) stays manual-only, so T3 Code never edits mise config. Nix store installs stay manual-only too, since the store is read-only. `mise latest` supplies the version, because settings such as
101`minimum_release_age` can hold mise behind npm. A Grok cask whose Homebrew prefix
102cannot be verified also stays manual-only: `grok update` would install a second copy. npm updates pin `--prefix` because the
103`npm` on `PATH` can belong to a different Node than the one that owns the provider. Homebrew
104compares against `brew info` since casks trail npm by hours; native installs share npm's version
105train, so the registry stays authoritative for them. A Chocolatey install of Claude stays
106manual-only: its package directory is writable only by administrators, and `claude update` through
107the shim installs a native copy elsewhere. The latest version comes from `choco search`, and the
108card shows `choco upgrade claude-code` without running it.
109See the [resolver](../../apps/server/src/provider/providerMaintenanceRunner.ts).
110​
111Ownership is cached per instance and re-read immediately before an update runs. The
112[runner](../../apps/server/src/provider/providerMaintenanceRunner.ts) refuses when the lock key
113changed since the advisory, and reports success only when the refreshed provider is still installed
114with a readable, current version.
115​
116## Protocol traps
117​
118Codex async questions arrive as notifications and are answered with a new user message. There is
119no pending RPC response to send. The
120[adapter](../../apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts) persists them as
121`user_input_request` turn items and runtime requests with `responseCapability: { type: "message" }`.
122Their execution nodes do not block the run. Web, desktop, and mobile use their normal question
123panels, and requests remain pending after a turn finishes, a provider exits, or the server restarts.
124​
125`runtime-request.respond` reads the persisted request and question item, validates required
126answers, and commits the resolution and a user message in one transaction. Repeating the same
127command returns its receipt without posting the answer twice. The normal message path starts or
128resumes a run, queues behind active work, or steers when the adapter supports it. Blocking questions
129retain the provider's live response path. Do not infer that a request has disappeared merely because
130it is outside the recent history window.
131​
132Native `/goal` state belongs to the provider and is mirrored on the provider thread. Codex starts
133the next goal turn on its own milliseconds after the last one completes, so the
134[adapter](../../apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts) keeps the run open
135and adds that turn to it. One run can therefore own several native turns. `/goal` commands that
136start no Codex turn settle on a provider turn without a native ref, which native rollback must
137not count. Claude's SDK mode emits no goal events; its adapter reads goal state from the
138synthetic command output and Stop hook feedback in the transcript.
139​
140Capabilities must describe what the provider can actually do. Antigravity can capture workspace
141checkpoints but cannot roll back its conversation. The [checkpoint boundary](./overview.md#turn-completion-and-checkpoints)
142therefore rejects revert before touching files. Native permission and question option IDs must
143also survive normalization; a display label is not necessarily a valid reply.
144​
145## Attachments and stored history
146​
147Attachments live outside the project workspace. The
148[attachment boundary](../../apps/server/src/orchestration-v2/AttachmentClaims.ts) validates and claims
149uploads for a thread; adapters choose native input formats for those environment-local files.
150A path in the prompt does not grant filesystem access. Keep provider sandbox and approval rules
151in force; copying uploads into the project to bypass them changes that boundary.
152​
153File attachments introduced a replay compatibility limit. Image-only clients cannot decode
154file-bearing messages, and an image-only server can fail the entire environment's startup when
155replaying one such event. Rollouts and downgrades must account for persisted history as well as
156current client support.
157​
158## Provider diagnostics
159​
160Native event logs retain lifecycle events, responses, and failures. Token deltas and duplicate raw
161frames are filtered before adapters copy or redact payloads. The filter accepts both legacy native
162events and v2 protocol envelopes; decode failures remain visible through diagnostic frames.
163​
164Log payloads have a 64 KiB encoded budget. Large or deeply nested payloads become structural
165summaries that retain routing identifiers, methods, status, and error fields. Traversal is bounded
166before redaction and serialization, so logging a large response does not require several full
167copies. These limits apply to diagnostics; provider event handling is unchanged.
168​
169Codex resumes with metadata-only reads when it needs a thread's identity and update time. Its
170initialization capabilities opt out of `turn/diff/updated`: T3 derives diffs from checkpoints.
171The logger filters those notifications before traversal when an older provider still sends them.
172​
173Model classification has its own [manifest constraints](./model-manifest.md). Assistant-reference
174handling is documented under [citations](./assistant-citations.md).