Skip to content

prototype: share a Databricks connection with Goose and Pi agents - #1036

Closed
salman1993 wants to merge 21 commits into
mainfrom
codex/shared-databricks-provider
Closed

salman1993 wants to merge 21 commits into
mainfrom
codex/shared-databricks-provider

Conversation

@salman1993

@salman1993 salman1993 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Why

Goose and Pi can each use several inference providers, so today a user signs in to Databricks separately for each harness. provider-plugins-exploration.md proposes one shared provider plugin that owns sign-in and model discovery, while each agent plugin keeps its own inference code. This PR implements that doc's first two slices: one Databricks connection, used by both Goose and Pi agents.

What

  • A bundled buzz.databricks plugin adds Settings → Providers → Databricks. It signs in to Block's Databricks workspace in the browser, stores credentials privately, refreshes tokens, and lists models along with the gateway routes each one serves.
  • Goose agents can choose Databricks in Buzz and one of nine verified models in their provider dialog or Settings tab: GPT-6 Sol/Luna/Astra, GPT-6.1 Sol, Claude Opus/Sonnet/Haiku 5.5, GLM 5.3, and Grok 4.7. Goose's model now shows in New agent's Model field, which follows main's Streamline agent creation and library controls #966.
  • Pi agents get Databricks in Buzz as the first entry in their provider list. They sign in through the Databricks connection instead of an API key. Claude models run on Anthropic Messages; other families run on OpenAI Responses. Gemini and models that only have a Chat route are not offered.
  • The shared OAuth callback host gains an opt-in localhost callback host and a bare-origin path. The databricks-cli OAuth client only accepts http://localhost:{port}.

How

Shared plugin

  • Credentials never reach the renderer, Goose's config, or Pi's auth.json. They live in Buzz's local data directory, at providers/databricks/credential.json (folder 0700, file 0600). A file lock serializes token refresh across Buzz and every agent process.
  • The catalog lists ready endpoints that support tool calls and AI Gateway v2, with every route each one serves (apis). Agent plugins filter those routes. The plugin also gives agents the token helper: the Buzz binary run with --databricks-provider-token, which prints a fresh access token and exits before the app starts. The helper refreshes a token 60 seconds before it expires.

Goose

  • Goose's native Databricks adapter cannot take an external token, so Buzz writes a Goose custom provider (custom_providers/buzz_databricks.json) with command auth that runs the helper.
  • Goose still owns request formatting, streaming, and history. The provider file uses the gateway's Responses route (ai-gateway/mlflow/v1/responses). The Chat route rejects reasoning effort alongside tools for GPT-6 models, and GPT-6.1 Sol always requires one. With Responses, the user's Goose thinking effort still applies.
  • The model list is an allowlist in src/bundled/goose/shared-provider.ts, limited to models the catalog says serve the Responses route. Gemini fails on Goose's stream_options, and Gemma rejects minimum in a tool schema, so neither is offered.

Pi

  • Buzz does not load Pi packages, so Pi's host registers the provider with pi-ai itself instead of loading pi-databricks. It copies that extension's routes and gateway compatibility settings, including dropping Responses item IDs longer than 64 characters.
  • The plugin maps the catalog to a binding (src/bundled/pi/shared-provider.ts). The host asks for it with a new host→plugin provider call: when the provider is first needed, and again each time it lists that provider's models, in case the user signed in or the catalog changed. Pi's own providers never need this call.
  • Context windows, output limits, and thinking levels come from Pi's built-in model with the same canonical name. Each request runs the helper, which then serves requests for the next 30 seconds.
  • Pi still owns request formatting, streaming, and history.

Risk

Medium, for a prototype. This PR targets sm/goose-agent-plugin (#1096) and merges in the Pi branch (#1103), so the diff includes #1103's commits until that PR lands. Pi stays off by default.

The OAuth callback change is opt-in, so existing callers (hosted communities, builderlab) keep 127.0.0.1 and their own paths. The Databricks plugin is enabled by default and points at Block's workspace, which needs a decision before this stops being a prototype.

Testing

On this head (db09d64e2)

  • Typecheck, biome, clippy, and pre-push hooks pass. Vitest passes across src/bundled, src/features/providers, and src/features/agents: 2,760 tests.
  • Rust tests cover the catalog's route filtering, the helper op, and Goose rejecting a model without the Responses route.
  • New unit tests:
    • sharedBinding routes each model, and returns nothing for Pi's own providers.
    • The Pi transport answers the host's provider call.
    • In the Pi provider dialog, Databricks in Buzz shows the shared sign-in instead of a key field, asks Pi for no models while signed out, and saves the chosen model after sign-in.
  • Pi host live test (host.live.test.ts, run locally with PI_DEPS pointing at Pi's installed packages): the real host and pi-ai 1.1.0 ran against a fake local gateway and a fake token command.
    • Claude went to /ai-gateway/anthropic/v1/messages?beta=true, and o3 to /ai-gateway/openai/v1/responses. Both used Authorization: Bearer <token> with no x-api-key.
    • Thinking levels were borrowed from Pi's models.
    • One helper run served three requests.

From the Goose prototype commits (carried into this branch; not rerun on this head)

  • Live sign-in (native flow): a temporary #[ignore] Rust test (since removed) ran authorize, the callback host, connect, and models against the real workspace. Databricks accepted redirect_uri = http://localhost:62166, and discovery returned 57 models.
  • Token helper: --databricks-provider-token exited 0 and printed one token.
  • Model matrix: real goose run called a stubbed Buzz send tool with all nine models, each loaded with Buzz's 12 tool schemas and the developer tools, with GOOSE_THINKING_EFFORT: medium. All nine passed.

Not yet done

  • Running the app from this head: signing in through the UI, then the Goose and Pi provider dialogs.
  • Pi turns against the real gateway: Claude on Anthropic Messages and GPT on Responses, including tool calls, compatibility settings, and long item IDs.
  • A Goose agent and a Pi agent replying in a Buzz channel on one connection.
  • History after restart, and continued work after a token refresh in a long session.
  • Linux and packaged builds. An independent review. Human acceptance.

Bigger picture

OpenRouter can follow the same boundary. Known gaps:

  • A revoked refresh token still shows "Connected" until sign-out.
  • All Buzz builds on a machine share one connection.
  • Pi offers every catalog model on a supported route without verifying each one, unlike Goose's allowlist.

🤖 Generated with Claude Code

salman1993 and others added 21 commits October 13, 2026 17:59
A bundled `buzz.goose` plugin whose agents run as one `goose-acp` process
each, with one ACP session per conversation. The Buzz tools reach goose over
MCP-over-ACP (`mcp/message` on the same pipe), so the process holds no Buzz
key. Uses the Claude plugin's Buzz prompt, appended with
`_goose/unstable/session/system-prompt/set`; names sessions with
`_meta.sessionTitle`; steers mid-turn messages with
`_goose/unstable/session/steer`. Provider and model come from the owner's
goose config unless the agent names its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
- New agent: the Goose type menu offers goose's own default provider and
  model, or a dialog to choose a provider, save its key into goose, pick a
  model, and test the connection. The card body keeps only instructions.
- Settings: provider and model per agent, and Runtime diagnostics listing
  the agent's goose sessions with their transcripts, read through
  session/list and _goose/unstable/session/export.
- A turn that writes text without calling send is posted as the agent's
  reply rather than reported as a failure.
- A missing goose-acp says so; test connection deletes its session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
A bundled agent type for macOS and Linux whose agents run on Pi durable
(@earendil-works/pi-durable), with any provider built into Pi. One Node
host runs every Pi agent, each with its own SQLite storage; each Buzz
thread or channel is one durable Pi conversation.

- New agent offers Pi's default or a chosen provider and model, with
  keys saved to Pi's auth.json and a connection test. A provider that
  a Pi package adds, which Buzz does not load, is named as such.
- Mentions steer a working run; a final buzz_send ends it; an answer
  that never used send is posted; failed turns are reported. Runs cut
  off by a stop continue from Pi's storage.
- Watches and timers run as one-off conversations in their Interest's
  lane. The owner can pick a model and thinking level per turn.
- Settings → Runtime diagnostics lists the agent's Pi conversations
  and shows each transcript, read from Pi's storage.
- pi.sh installs Pi's packages once from a pinned lockfile with
  npm ci, on this computer's Node 22.19 or later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Follows #961: the Pi runtime takes each agent's AgentReads, as Claude's
does, for owner attestation, Me chats, channels, the conversation
before a turn, names and memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pi still ships with Buzz, but like Todos and Bestie it starts off.
Users turn it on in Settings → Plugins, and only then can they create
Pi agents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
pi-ai's credential stores treat a change that returns undefined as
"leave the entry", and resolve with the entry after the change. Its
OAuth refresh relies on that when another request or process refreshed
first. Buzz's AuthFile deleted the entry instead, so two agents, or Buzz
and the pi command, refreshing at once could sign the provider out of the
auth.json they share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
… add Activity tab

- A run that fails withdraws the requests queued to steer it, unanswered
  for the same reason, instead of leaving them for a later request.
- A steer into a working run changes only the model or effort its owner
  picked, not the agent's configuration.
- A request's answer is judged against the sends made after Pi placed it,
  not after its handover.
- A turn whose retried attempt succeeded no longer shows the earlier error.
- The provider dialog and Settings share one providers read with their
  fields, so Try again also updates what Use and Save allow.
- Enter in the API key field saves the key, not the settings form.
- A `!command` key that gave nothing runs again next time.
- Pi's conversations move from Settings' Runtime diagnostics to an
  Activity tab, as for Claude Code and Codex.
- Simplifications: useRead takes an abort signal and backs the transcript
  view; pi.sh has one entry point; effort labels and the once deadline come
  from Claude Code; unused props, fields, returns and exports are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
main's New agent dialog (#966) replaced the type menu with one compact
Model field per type, also shown on library cards. Pi's field lists its
provider's models, starting on Pi's default, with a Provider… button that
opens the existing provider dialog: provider, API key, model and Test
connection. CreateModelChoices gains an optional trailing action for it.
The menu, its open-state store and its trigger label are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
…MCP (#1096)

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…7284a' into claude/databricks-provider-goose-pi-a62d2a

Signed-off-by: Salman Mohammed <smohammed@squareup.com>

# Conflicts:
#	crates/plugin-manager/src/lib.rs
#	src/bundled/index.ts
Add a bundled Databricks provider plugin that owns browser sign-in,
private credential storage, token refresh, and model discovery for
Block's workspace. Goose agents choose "Databricks in Buzz" and an exact
served GPT model; Buzz writes a Goose command-auth provider whose helper
(the Buzz binary with --databricks-provider-token) prints fresh access
tokens. Request formatting and streaming stay in Goose.

The databricks-cli OAuth client accepts only http://localhost with an
ephemeral port, so the shared loopback callback host gains an opt-in
`localhost` callback host and bare-origin path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
The gateway's Chat Completions route rejects reasoning_effort when tools
are present ("Function tools with reasoning_effort are not supported for
gpt-6-luna"). A goose-wide GOOSE_THINKING_EFFORT in config.yaml reached
it, so tool-bearing turns and Test connection failed. Launch shared
Databricks agents with GOOSE_THINKING_EFFORT=none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Goose now reaches the shared Databricks connection through the gateway's
Responses route instead of Chat Completions. The Chat route rejects
reasoning effort alongside tools for GPT-6 models, and GPT-6.1 Sol always
requires one, so the Responses route keeps the user's goose thinking
effort and replaces the GOOSE_THINKING_EFFORT=none workaround.

Replace the GPT-only filter with the endpoints verified with Goose's and
Buzz's tools: GPT-6 and 6.1, Claude Opus/Sonnet/Haiku 5.5, GLM 5.3, and
Grok 4.7. A provider file written for an earlier route is rewritten
rather than reused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
main's #966 replaced CreateModelMenu and createModelLabel with a
compact CreateModel field. goose now offers its provider's models there,
with a Provider… button for its provider dialog, as Pi does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
The shared catalog now lists every AI Gateway API a model serves instead
of one route Goose chose, and hands agents the token helper command so a
host can fetch fresh access tokens itself. Goose keeps only models served
on the MLflow Responses route it configures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pi's provider list leads with Databricks in Buzz, signed in to with the
Databricks plugin's connection rather than a key. The plugin maps each
catalog model to its gateway route (Anthropic Messages for Claude, OpenAI
Responses otherwise); the Pi host asks for that binding when it first
needs the provider and registers it with pi-ai, borrowing limits and
thinking levels from Pi's own models and running Buzz's token helper for
each request's access token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pi registers the shared provider in its host rather than loading the
Databricks extension, since Buzz doesn't load Pi packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
@salman1993
salman1993 force-pushed the codex/shared-databricks-provider branch from 4471d7a to db09d64 Compare October 14, 2026 21:20
@salman1993 salman1993 changed the title prototype: share a Databricks connection with Goose agents prototype: share a Databricks connection with Goose and Pi agents Oct 14, 2026
Base automatically changed from sm/goose-agent-plugin to main October 15, 2026 02:38
@salman1993

Copy link
Copy Markdown
Contributor Author

this PR was a prototype to see if a provider plugin architecture made sense which could be shared among agentType plugins like goose and pi where you can configure many providers. also applicable for MeshLLM (Shared Compute).

TLDR:

  • Mic’s shipping shared compute / mesh as an “agent type” - basically a harness plugin. Like Claude, Codex, or Goose. He demoed that and it’s a nice experience.
  • We discussed separating providers from agent types (ie, Databricks or Jev) and agreed that’s a “maybe later” thing, that it most likely looks like some way of serving credentials to plugins with permission but it's not a priority at all to solve in the next month.
  • Also called out the need for clarity around plugin contracts, what we’re promising if anything.

@salman1993 salman1993 closed this Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant