Files
supabase/apps/studio/components
Charis cd6afdd8b2 refactor(studio): add tool contracts and migrate update_notebook (#51626)
## Summary

Each assistant tool's identity and contract is currently written in up
to nine places that nothing ties together at compile time. This PR adds
a contract/server split and migrates `update_notebook`, the tool with
the most copies, as the first proof of the design.

- **Contract** (`lib/ai/tools/update-notebook.ts`): isomorphic file with
the input and output schemas, approval flag, minimum opt-in level, and
`toModelOutput`, the single rule for what the model may see.
- **Server** (`update-notebook.server.ts`): the executable tool, built
with `approvalGatedToolFromContract`.
- **Registry** (`registry.ts`): collects contracts, derives a typed
`StudioUIMessage`, and provides `sanitizeContractPart` for history
replay.
- **Shared types** (`types.ts`): `ToolContract`, `ToolContext`,
`ToolFactory`, plus a single `getAuthHeaders` replacing three variants.
- **Approval gate** (`approval-gate.ts`): approval-gated tools get
`acceptUntrustedSql` / `acceptUntrustedLogsSql` through a gate passed to
`execute`, and `*.server.ts` files can't import the raw functions
(ESLint).
- **Opt-in level**: `update_notebook` is gated by its contract's
`minOptInLevel`. `TOOL_CATEGORY_MAP` is now typed against `ToolName` and
excludes migrated tools, which exposed `get_active_incidents` missing
from the name enum.

Removed duplicates: the `update_notebook` sanitizer in
`tool-sanitizer.ts`, the client-side `stripNotebookSnapshot`, and the
copied update schemas in `Message.utils.ts`.

## Behavior changes

- The client no longer strips `previous_content` from `update_notebook`
outputs before sending history, so requests carry the previous notebook
snapshot. The server still strips it before the model sees it.
- On history replay, an `update_notebook` output that doesn't match its
schema is dropped instead of passed through minus `previous_content`.

## Testing

- New `registry.test.ts` (typed parts, replay sanitizer) and a contract
case in `tool-filter.test.ts`.
- Studio typecheck, lint ratchet, knip, Prettier and the affected vitest
suites pass.
2026-10-14 10:44:38 -04:00
..

Writing components

Where to create your components

  • For components that declare the general structure and layout of a page:
    • /components/layouts/xxx
  • For components that are tightly coupled to a specific interface:
    • /components/interfaces/xxx
  • For components that are meant to be reusable across multiple pages:
    • /components/ui/xxx
  • Note: We're gradually moving files out of the to-be-cleaned folder into the respective folders as we refactor

Component structure

  • If a component has constants and utility methods that are tightly coupled to itself, keep them close to the component and enclose them in a folder with an index.tsx as an entry point
  • Otherwise it can just be a file on its own
  • For example:
    • components/ui
      - SampleComponentA
        - SampleComponentA.tsx
        - SampleComponentA.constants.ts
        - SampleComponentA.utils.ts
        - SampleComponentA.types.ts
        - index.ts
      - SampleComponentB.tsx
      

Template for building components


// Declare the prop types of your component
interface ComponentAProps {
  sampleProp: string
}

// Name your component accordingly — use a named export, not a default export
export const ComponentA = ({ sampleProp }: ComponentAProps) => {
  return <div>ComponentA: {sampleProp}</div>
}