Skip to content

feat(web): start and stop managed VM instances through hooks - #1

Closed
IAmJSD wants to merge 9 commits into
mainfrom
t3code/68b67c19
Closed

feat(web): start and stop managed VM instances through hooks#1
IAmJSD wants to merge 9 commits into
mainfrom
t3code/68b67c19

Conversation

@IAmJSD

@IAmJSD IAmJSD commented Aug 10, 2026

Copy link
Copy Markdown
Member

Problem

VM management solutions have no way to boot or shut down the machine hosting a T3 server from inside T3 Code — users must open the management app before and after every remote session.

Solution

Instance lifecycle hooks, configured by the management solution in the server's settings (startHookUrl / stopHookUrl, nullable, null by default). Clients receive them inside ServerConfig.settings, which is already cached per environment, so the start hook URL stays known while the instance is off.

Start hook (client-side, on Settings → Connections where the Connect button lives): clicking Connect first POSTs the hook URL with no content.

  • 200 with { "poll_url": ..., "retry_secs": ... } → poll until a 204 says the instance is up, then run the normal connect flow.
  • 400 with a component form → a dialog renders the components (informational text, selects, regex-validated text inputs), then POSTs the resolved values back as a JSON array; re-prompts are handled.
  • Polling is clamped to 1–60s intervals with a 10-minute deadline, and the row button becomes "Cancel start". Only an explicit Connect click runs the hook — supervisor auto-retries never boot a stopped VM.

Stop hook (server-side): connected environments whose settings carry a stop hook show a Stop button backed by the new server.runStopHook RPC (orchestration:operate). The server DELETEs the endpoint: 204 reports the instance as stopping; 404 clears the setting, which streams to clients and removes the button. The saved environment keeps its pairing and cached config, so the next Connect starts it again.

Protocol reference for management-solution authors: docs/internals/instance-hooks.md. User docs in docs/user/remote-access.md.

Notes

  • The management endpoint is called from the browser, so it must allow CORS for the app origin.
  • Desktop gets both hooks by wrapping web. Mobile can already dispatch the stop RPC through shared client-runtime but has no start-hook gate in its Connections screen yet (noted as follow-up in the internals doc).

Testing

  • vp test run apps/server/src/instanceHooks.test.ts apps/web/src/components/settings/startHook.test.ts — 14 tests covering the DELETE flow (204/404/500/unconfigured) and the client protocol (poll state, form parsing, immediate 204, JSON array submission, polling loop, regex validation).
  • Targeted typecheck and lint clean on contracts, client-runtime, server, and web.

Done by Claude Fable 5 on Claude Code.

🤖 Generated with Claude Code

t3-code Bot and others added 9 commits August 7, 2026 14:29
Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>
Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>
The settings panels read and write server settings through the primary
environment, which only resolves via a `PrimaryConnectionTarget`. The
hosted app never has one — every device is paired remotely — so the
panels degraded silently there:

- `primaryServerProvidersAtom` returned no providers, leaving the text
  generation model picker with nothing to offer ("No models found"), so
  the model backing thread titles, commit messages, change request
  content and branch names could not be changed at all.
- `useUpdatePrimarySettings` resolved to a null environment id, and
  `useUpdateSettingsTarget` no-ops on null, so every write from these
  panels was dropped without an error.
- Reads fell back to `DEFAULT_SERVER_SETTINGS`, presenting the schema
  default as if it were the user's configuration.

Resolve the settings target to the primary device when there is one and
the first connected device otherwise. Sessions with a primary device are
unaffected. Rename the hook pair to `useGlobalSettings` /
`useUpdateGlobalSettings`, since the module header documents the
primary-only scoping as deliberate and it no longer holds.

Left alone: the primary-scoped atoms behind the primary-device update
notification, sidebar and command palette, and the diagnostics panel,
whose RPCs target the primary environment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two consumers were left on the primary environment after the settings
panels moved to the resolved settings environment, so on a hosted session
with no primary device they disagreed with the panels that write them.

- New-thread defaults (`defaultThreadEnvMode`,
  `newWorktreesStartFromOrigin`) read `primaryServerSettingsAtom` in
  `useNewThreadHandler` and `ChatView`, so the General controls saved and
  re-displayed while new drafts kept the schema defaults. Both now read
  `settingsServerSettingsAtom` — the environment those controls write.
- `SourceControlSettingsPanel` still resolved its discovery target and
  gated `SourceControlWritingSettingsSection` on `usePrimaryEnvironment`,
  so the writer-model and fetch-interval controls never mounted there.
  It now uses `useSettingsEnvironmentId`, matching the section's own hooks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
VM management solutions had no way to boot or shut down the machine
hosting a T3 server from inside T3 Code, so users had to open the
management app before every remote session.

The server's settings gain nullable startHookUrl/stopHookUrl, delivered
to clients inside the cached server config. On Settings -> Connections,
Connect now POSTs the start hook first, renders its component form when
the endpoint asks for input, polls until the instance reports ready with
a 204, then runs the normal connect flow. Connected environments with a
stop hook show a Stop button backed by a new server.runStopHook RPC; the
server DELETEs the endpoint and clears the setting on a 404 so clients
drop the control.

Done by Claude Fable 5 on Claude Code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Aug 10, 2026
@IAmJSD

IAmJSD commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

Closing — reopening this against the upstream repo pingdotgg/t3code.

@IAmJSD IAmJSD closed this Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants