From 7a6d2d86d76d967dc81e59b357b17e7fba1e00b9 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 2 Jul 2026 08:23:49 +0200 Subject: [PATCH] Name the layers: agent providers and model providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record the naming decision in the glossary: bare "provider" splits into agent provider (the harness: Claude Code, Codex, Paseo Agent) and model provider (a model API attached to Paseo Agent). Bare "Agent" is the runnable identity — an agent provider today, a named agent definition tomorrow — resolved through one CLI namespace (--agent). Agent session stays the running instance. Retire "inference provider" across docs, UI strings, and identifiers (PaseoAgentModelProvider, paseoAgentModelProviders). Labels and internal names only — wire and config keys are untouched. --- CLAUDE.md | 2 +- docs/glossary.md | 13 +++-- docs/paseo-agent.md | 8 +-- packages/app/e2e/helpers/paseo-agent.ts | 2 +- .../e2e/paseo-agent-provider-config.spec.ts | 10 ++-- .../components/paseo-agent-settings-sheet.tsx | 2 +- .../src/server/agent/provider-registry.ts | 2 +- .../agent/providers/paseo-agent/agent.test.ts | 2 +- .../agent/providers/paseo-agent/agent.ts | 19 +++---- .../providers/paseo-agent/config.test.ts | 22 ++++---- .../agent/providers/paseo-agent/config.ts | 52 +++++++++---------- .../providers/paseo-agent/pi-services.test.ts | 22 ++++---- .../providers/paseo-agent/pi-services.ts | 14 ++--- .../src/server/persisted-config.test.ts | 2 +- 14 files changed, 84 insertions(+), 88 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 836042b9c0..c92a4c5469 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,7 +37,7 @@ At the start of non-trivial work, list `docs/` and skim anything relevant to the | [docs/file-icons.md](docs/file-icons.md) | Material icon theme integration for the file explorer | | [docs/providers.md](docs/providers.md) | Adding a new agent provider end-to-end | | [docs/service-proxy.md](docs/service-proxy.md) | Service proxy: exposing workspace scripts at public URLs, DNS setup, reverse proxy config | -| [docs/paseo-agent.md](docs/paseo-agent.md) | Paseo Agent provider (id `paseo`): in-process Pi harness, `agents.paseo` typed inference providers, auth, OAuth limitation | +| [docs/paseo-agent.md](docs/paseo-agent.md) | Paseo Agent provider (id `paseo`): in-process Pi harness, `agents.paseo` typed model providers, auth, OAuth limitation | | [docs/development.md](docs/development.md) | Dev server, build sync gotchas, CLI reference, agent state, Playwright MCP | | [docs/rpc-namespacing.md](docs/rpc-namespacing.md) | WebSocket RPC naming convention — dotted namespaces and `.request`/`.response` pairs | | [docs/terminal-performance.md](docs/terminal-performance.md) | Terminal latency pipeline, coalescing/backpressure invariants, benchmark + perf spec usage | diff --git a/docs/glossary.md b/docs/glossary.md index 5a9d14b990..5d07894888 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -6,7 +6,7 @@ Authoritative terminology. UI label wins. Don't invent synonyms; use what's here - **Workspace** — One concrete `cwd` on one daemon, with git state; belongs to exactly one project. Its `id` is opaque workspace identity; its `cwd` is the filesystem directory. UI: "Workspace". Code: `WorkspaceDescriptorPayload` (`packages/protocol/src/messages.ts:2178`). Don't confuse with: Branch (one branch can back many workspaces via worktrees). Forbidden: "Folder", "Directory" as UI label. - **Workspace kind** — `"directory" | "local_checkout" | "worktree"`. The git-derived, persisted property of a workspace, used across its lifetime (archive safety, sidebar, grouping). Derived from the cwd's git reality (`deriveWorkspaceKind`, `packages/server/src/server/workspace-registry-model.ts:158`), not stored from a user choice. Code: `PersistedWorkspaceKind` (`packages/server/src/server/workspace-registry-model.ts:8`). Don't confuse with **Isolation** (the create-time intent). - **Isolation** — Create-time choice for a new workspace: reuse the existing checkout (**Local**) or cut a dedicated git worktree (**New worktree**). A transient setup input, also remembered as a create-form preference; it is not a workspace property. UI: "Isolation" control on the New Workspace screen. Code: `isolation` (`"local" | "worktree"`), `useWorkspaceIsolation` (`packages/app/src/screens/new-workspace-screen.tsx`); persisted as `FormPreferences.isolation` (`packages/app/src/create-agent-preferences/preferences.ts`). Distinct from **Workspace kind**, which is the git-derived property the intent produces (Local → `local_checkout` or `directory` by git-ness; New worktree → `worktree`). On the wire it is the create request's `source.kind` (`directory | worktree`, `packages/protocol/src/messages.ts:1693`). -- **Agent** — See **Agent session**. UI still says "Agent" / "New Agent" in places, but moving toward **Agent session** as the canonical term. Code: `AgentSnapshotPayload` (`packages/protocol/src/messages.ts:608`). Forbidden: "Task", "Job", "Run". +- **Agent** — A runnable identity the user picks to start work: today that is an **Agent provider** (Claude Code, Codex, Paseo Agent); direction: also a named **Agent definition** (`reviewer`) that pins provider, model, prompt, and tools. One CLI namespace: `paseo run --agent ` resolves agent provider ids first, then agent definition ids. The running instance is an **Agent session** — "New Agent" starts a new agent session. Forbidden: "Task", "Job", "Run". - **Daemon** — Local Paseo server process; identified by `serverId`. UI: "Daemon" (system contexts only). Code: `serverId` in `ServerInfoStatusPayloadSchema` (`packages/protocol/src/messages.ts:1936`), `DaemonClient` (`packages/client/src/daemon-client.ts`). - **Host** — Client-side connection profile pointing at a daemon; bundles one or more `HostConnection`s. UI: "Host" / "Add host" / "Switch host". Code: `HostProfile` (`packages/app/src/types/host-connection.ts:37`). Forbidden: "Connection" (means `HostConnection`, not host). - **Project host entry** — One row in a project for a single (project, daemon) pair, aggregating that daemon's workspaces in the project. Internal. Code: `ProjectHostEntry` (`packages/app/src/utils/projects.ts:11`). Don't introduce "Checkout" as a synonym. @@ -17,11 +17,13 @@ Authoritative terminology. UI label wins. Don't invent synonyms; use what's here - **Directory-backed surface** — A right-sidebar surface whose content is determined by the workspace's `cwd`, so two workspaces on the same directory see identical content: git diff/status, GitHub PR info, file preview/explorer contents. Keyed by `(serverId, cwd)`, never `workspaceId`. See [architecture.md](architecture.md#right-sidebar-boundary-directory-backed-vs-workspace-owned). - **Workspace-owned state** — Per-workspace state that never leaks to a same-`cwd` sibling: tabs, agents, terminals, panes, title, plus review drafts, diff-mode overrides, composer attachments, and file-explorer open/expand state. Keyed by `workspaceId` (`cwd` only as a fallback for old payloads). See [architecture.md](architecture.md#right-sidebar-boundary-directory-backed-vs-workspace-owned). - **Workspace status bucket** — Aggregate activity signal for a workspace row. Same-`cwd` workspaces intentionally share agent and terminal status buckets, while tab, agent, and terminal visibility remains scoped by `workspaceId`. -- **Agent session** — One running instance of an agent inside a workspace (one provider, one model, one cwd, one timeline). The conceptual unit; in the UI this opens as a tab. Moving toward this as the canonical term over "Agent". Code: `AgentSnapshotPayload` (`packages/protocol/src/messages.ts:608`). +- **Agent session** — One running instance of an agent inside a workspace (one agent provider, one model, one cwd, one timeline). The conceptual unit; in the UI this opens as a tab. Canonical term for the running instance (bare "Agent" is the identity being run, see **Agent**). Code: `AgentSnapshotPayload` (`packages/protocol/src/messages.ts:608`). +- **Agent definition** — A Paseo-owned markdown persona in `$PASEO_HOME/agents/*.md`: frontmatter `name`, `prompt`, `model`, `tools`, `permissions`; body is the prompt. Selected via `agents.paseo.defaultAgent` (`defaultProfile` is a legacy alias). Direction: definitions join the `--agent` namespace (`paseo run --agent reviewer`), so a definition id must not shadow an agent provider id. Code: `loadAgentDefinition` (`packages/server/src/server/agent/providers/paseo-agent/prompt-profiles.ts:62`), config keys (`packages/server/src/server/agent/providers/paseo-agent/config.ts:174`). Forbidden: "Profile", "Prompt profile" as synonyms. - **Session** — Two senses: (a) per-client connection to a daemon, internal; (b) user-facing agent session, see **Agent session**. Code: `Session` (`packages/server/src/server/session.ts`) for (a). Don't confuse with: provider-side agent session log. - **Profile** — Internal name for the persisted shape of a host. Code: `HostProfile` (`packages/app/src/types/host-connection.ts:37`). Never user-facing. -- **Provider** — Agent backend (Claude Code, Codex, Copilot, OpenCode, Pi, OMP). UI: "Provider". Code: `ProviderSnapshotEntry` (`packages/protocol/src/messages.ts:198`). -- **Model** — A specific LLM offered by a provider. UI: "Model" / "Select model". Code: `AgentModelDefinition` (`packages/protocol/src/messages.ts:187`). +- **Agent provider** — The harness/backend that runs agents (Claude Code, Codex, Copilot, OpenCode, Pi, OMP, Paseo Agent with id `paseo`). UI: "Agent provider" (target label; surfaces still saying bare "Provider" migrate as touched). Wire and config keep `provider` — labels move, the protocol does not. Code: `AgentProvider` (`packages/protocol/src/agent-types.ts:3`), `ProviderSnapshotEntrySchema` (`packages/protocol/src/messages.ts:262`). Don't confuse with: **Model provider**. Forbidden: bare "Provider" as a new UI label — always qualify which layer. +- **Model provider** — A model API attached to the Paseo Agent provider: a type (`openrouter`, `openai`, `anthropic`, `openai-codex`, …) plus key/OAuth and models, keyed by instance name (`openrouter-main`, `chatgpt`). UI: "Model providers" (Paseo Agent settings). CLI: `paseo provider add` — the CLI `provider` noun means model provider. Config: `agents.paseo.providers` (`packages/server/src/server/agent/providers/paseo-agent/config.ts:169`); wire: `PaseoAgentProviderTypeSchema` (`packages/protocol/src/messages.ts:1924`). Don't confuse with: **Agent provider**. Forbidden: "Inference provider" (old docs term), bare "Provider" as a new UI label. +- **Model** — A specific LLM offered by a provider; for the Paseo Agent provider, addressed as `/`. UI: "Model" / "Select model". Code: `AgentModelDefinition` (`packages/protocol/src/messages.ts:187`). - **Tab** — UI surface representing one session inside a workspace. Not a conceptual unit; use **Agent session** when talking about the model. Code: `WorkspaceTabDescriptor` (`packages/app/src/screens/workspace/workspace-tabs-types.ts`). - **Terminal** — Workspace-scoped PTY shell streamed over the binary mux channel. UI: "Terminal". Code: `TerminalStreamFrame` (`packages/protocol/src/terminal-stream-protocol.ts`). - **Schedule** — Cron-style trigger that creates new agents. UI: CLI/MCP (`paseo schedule`, `create_schedule`). Don't confuse with: Heartbeat (cron prompt back into the same agent) or Loop (iterative re-execution of one agent). @@ -39,5 +41,8 @@ Authoritative terminology. UI label wins. Don't invent synonyms; use what's here ## Inconsistencies (documented, not papered over) +- Bare "Provider" survives as a UI label in the provider picker and settings; target is "Agent provider" / "Model providers" per the entries above. Migrate labels as surfaces are touched — no big-bang rename. +- CLI `paseo provider` describes itself as "Manage agent providers" (`packages/cli/src/commands/provider/index.ts:11`) but manages **model providers**. Fix the description; keep the noun. +- CLI `paseo run --provider ` selects an agent provider. Direction: add `--agent ` as the canonical flag resolving one namespace (agent provider ids, then agent definition ids), keep `--provider` as a COMPAT alias. - CLI `--host ` description `"Daemon host target"` (`packages/cli/src/utils/command-options.ts:5`) blurs daemon/host; the app keeps them distinct. - `WorkspaceDescriptorPayloadSchema.workspaceKind` accepts legacy `"checkout"` on the wire (`packages/protocol/src/messages.ts:2187`) while `PersistedWorkspaceKind` does not (`packages/server/src/server/workspace-registry-model.ts:8`). diff --git a/docs/paseo-agent.md b/docs/paseo-agent.md index fc78538e0a..26b350fbd3 100644 --- a/docs/paseo-agent.md +++ b/docs/paseo-agent.md @@ -1,8 +1,8 @@ # Paseo Agent provider -Paseo Agent is a built-in provider that runs Pi's coding-agent harness **in process** (no `pi` CLI, no `~/.pi` discovery). It is configured entirely by Paseo-owned config under `agents.paseo` in `$PASEO_HOME/config.json`. The model backends are "inference providers": one or more typed entries, each pointing at an API with its own key and models. +Paseo Agent is a built-in agent provider that runs Pi's coding-agent harness **in process** (no `pi` CLI, no `~/.pi` discovery). It is configured entirely by Paseo-owned config under `agents.paseo` in `$PASEO_HOME/config.json`. The model backends are **model providers** (see [glossary.md](glossary.md)): one or more typed entries, each pointing at an API with its own key and models. -The provider id is **`paseo`** (the display name is "Paseo Agent"). Use it like any other provider, e.g. `paseo run --provider paseo --model / ...`. +The provider id is **`paseo`** (the display name is "Paseo Agent"). Use it like any other agent provider, e.g. `paseo run --provider paseo --model / ...`. This is a prototype. There is no app UI yet. OpenRouter and ChatGPT setup have CLI paths; other provider setup is still config-file based. @@ -102,7 +102,7 @@ is not present in the session's `mcpServers`, Paseo Agent logs a warning and con { "agents": { "paseo": { - // Optional. "/". Used when an agent is + // Optional. "/". Used when an agent is // started without an explicit model. "defaultModel": "openrouter-main/anthropic/claude-3.7-sonnet", @@ -113,7 +113,7 @@ is not present in the session's `mcpServers`, Paseo Agent logs a warning and con // Legacy alias for defaultAgent. Still accepted for old configs. "defaultProfile": "orchestrator", - // Inference providers, keyed by instance name. Names are free-form; you may + // Model providers, keyed by instance name. Names are free-form; you may // run several entries of the same type against different APIs/keys/models. "providers": { "openrouter-main": { diff --git a/packages/app/e2e/helpers/paseo-agent.ts b/packages/app/e2e/helpers/paseo-agent.ts index 65a297674e..e085978146 100644 --- a/packages/app/e2e/helpers/paseo-agent.ts +++ b/packages/app/e2e/helpers/paseo-agent.ts @@ -58,7 +58,7 @@ export async function addOpenRouterProvider( await expect(page.getByText(provider.apiKey, { exact: true })).toHaveCount(0); } -export async function expectInferenceProviderListed( +export async function expectModelProviderListed( page: Page, expected: ExpectedProvider, ): Promise { diff --git a/packages/app/e2e/paseo-agent-provider-config.spec.ts b/packages/app/e2e/paseo-agent-provider-config.spec.ts index 784857eef7..142b7ed6ee 100644 --- a/packages/app/e2e/paseo-agent-provider-config.spec.ts +++ b/packages/app/e2e/paseo-agent-provider-config.spec.ts @@ -2,7 +2,7 @@ import { test } from "./fixtures"; import { addOpenRouterProvider, cleanupPaseoAgentProviders, - expectInferenceProviderListed, + expectModelProviderListed, openPaseoAgentSettings, seedChatGptProvider, } from "./helpers/paseo-agent"; @@ -18,7 +18,7 @@ test.describe("Paseo Agent provider configuration", () => { providerNamesToCleanup.clear(); }); - test("adds an OpenRouter inference provider from Settings", async ({ page }) => { + test("adds an OpenRouter model provider from Settings", async ({ page }) => { providerNamesToCleanup.add(OPENROUTER_PROVIDER); await openPaseoAgentSettings(page); @@ -28,7 +28,7 @@ test.describe("Paseo Agent provider configuration", () => { models: ["openai/gpt-4o-mini", "anthropic/claude-3.7-sonnet"], }); - await expectInferenceProviderListed(page, { + await expectModelProviderListed(page, { name: OPENROUTER_PROVIDER, providerType: "openrouter", modelCount: 2, @@ -36,13 +36,13 @@ test.describe("Paseo Agent provider configuration", () => { }); }); - test("shows a stored ChatGPT login as a read-only inference provider row", async ({ page }) => { + test("shows a stored ChatGPT login as a read-only model provider row", async ({ page }) => { providerNamesToCleanup.add(CHATGPT_PROVIDER); await seedChatGptProvider(CHATGPT_PROVIDER); await openPaseoAgentSettings(page); - await expectInferenceProviderListed(page, { + await expectModelProviderListed(page, { name: CHATGPT_PROVIDER, providerType: "openai-codex", modelCount: 1, diff --git a/packages/app/src/components/paseo-agent-settings-sheet.tsx b/packages/app/src/components/paseo-agent-settings-sheet.tsx index f599ae84bb..01091618a4 100644 --- a/packages/app/src/components/paseo-agent-settings-sheet.tsx +++ b/packages/app/src/components/paseo-agent-settings-sheet.tsx @@ -246,7 +246,7 @@ export function PaseoAgentSettingsSheet({ } else if (providers.length === 0) { body = ( - No inference providers configured yet. + No model providers configured yet. ); } else { diff --git a/packages/server/src/server/agent/provider-registry.ts b/packages/server/src/server/agent/provider-registry.ts index f7a18cbe19..26069176a5 100644 --- a/packages/server/src/server/agent/provider-registry.ts +++ b/packages/server/src/server/agent/provider-registry.ts @@ -83,7 +83,7 @@ export interface BuildProviderRegistryOptions { paseoHome?: string; /** * Opaque Paseo Agent config blob. The registry only forwards it to the - * paseo-agent client factory; it never reads the nested inference providers. + * paseo-agent client factory; it never reads the nested model providers. */ paseoAgentConfig?: PaseoAgentConfig; } diff --git a/packages/server/src/server/agent/providers/paseo-agent/agent.test.ts b/packages/server/src/server/agent/providers/paseo-agent/agent.test.ts index d6afe523ee..a9a636166b 100644 --- a/packages/server/src/server/agent/providers/paseo-agent/agent.test.ts +++ b/packages/server/src/server/agent/providers/paseo-agent/agent.test.ts @@ -163,7 +163,7 @@ describe("PaseoAgentClient", () => { } }); - it("is available only when config has a usable inference provider", async () => { + it("is available only when config has a usable model provider", async () => { const withConfig = new PaseoAgentClient({ logger: createTestLogger(), config: makeConfig() }); expect(await withConfig.isAvailable()).toBe(true); diff --git a/packages/server/src/server/agent/providers/paseo-agent/agent.ts b/packages/server/src/server/agent/providers/paseo-agent/agent.ts index 36b02fd61f..f97e6b5191 100644 --- a/packages/server/src/server/agent/providers/paseo-agent/agent.ts +++ b/packages/server/src/server/agent/providers/paseo-agent/agent.ts @@ -30,7 +30,7 @@ import { type PaseoAgentConfig, listPaseoAgentModels, paseoAgentHasUsableModel, - paseoAgentInferenceProviders, + paseoAgentModelProviders, parsePaseoAgentModelId, resolvePaseoAgentModel, } from "./config.js"; @@ -534,22 +534,17 @@ export class PaseoAgentClient implements AgentClient { config: AgentSessionConfig, _launchContext?: AgentLaunchContext, ): Promise { - const inferenceProviders = await paseoAgentInferenceProviders(this.config); - if (inferenceProviders.length === 0) { + const modelProviders = await paseoAgentModelProviders(this.config); + if (modelProviders.length === 0) { throw new Error( - "Paseo Agent has no configured inference providers. Add agents.paseo.providers to your Paseo config.", + "Paseo Agent has no configured model providers. Add agents.paseo.providers to your Paseo config.", ); } const availableAgents = this.loadAvailableAgentModes(); const agent = this.loadSelectedAgent(config.modeId); this.verifyExpectedMcpServers(agent, config.mcpServers); - const model = resolvePaseoAgentModel( - this.config, - config.model, - inferenceProviders, - agent?.model, - ); + const model = resolvePaseoAgentModel(this.config, config.model, modelProviders, agent?.model); const thinkingLevel = normalizeThinkingLevel(config.thinkingOptionId) ?? undefined; const composedPrompt = composePromptParts({ agent, @@ -567,7 +562,7 @@ export class PaseoAgentClient implements AgentClient { // OAuth providers (ChatGPT/Codex) use a Paseo-owned, file-backed AuthStorage so Pi // reads the stored credential and persists refreshed tokens (rotation) back to it. - const usesOAuth = inferenceProviders.some((provider) => provider.oauth); + const usesOAuth = modelProviders.some((provider) => provider.oauth); const authStorage = usesOAuth ? createPaseoAgentAuthStorage(envForPaseoHome(this.paseoHome)) : undefined; @@ -583,7 +578,7 @@ export class PaseoAgentClient implements AgentClient { const handle = await createPaseoAgentSession({ cwd: config.cwd, agentDir: resolveIsolatedAgentDir(), - inferenceProviders, + modelProviders, ...(model ? { model } : {}), ...(thinkingLevel ? { thinkingLevel } : {}), ...(authStorage ? { authStorage } : {}), diff --git a/packages/server/src/server/agent/providers/paseo-agent/config.test.ts b/packages/server/src/server/agent/providers/paseo-agent/config.test.ts index 92e279f53f..a2715ad4ca 100644 --- a/packages/server/src/server/agent/providers/paseo-agent/config.test.ts +++ b/packages/server/src/server/agent/providers/paseo-agent/config.test.ts @@ -5,7 +5,7 @@ import { encodePaseoAgentModelId, listPaseoAgentModels, paseoAgentHasUsableModel, - paseoAgentInferenceProviders, + paseoAgentModelProviders, parsePaseoAgentModelId, resolvePaseoAgentModel, type PaseoAgentConfig, @@ -34,7 +34,7 @@ describe("PaseoAgentConfigSchema", () => { expect(() => PaseoAgentConfigSchema.parse({ providers: {}, unexpected: true })).toThrow(); }); - it("rejects an unknown inference provider type", () => { + it("rejects an unknown model provider type", () => { expect(() => PaseoAgentConfigSchema.parse({ providers: { p: { type: "mystery", options: { models: [{ id: "m" }] } } }, @@ -42,7 +42,7 @@ describe("PaseoAgentConfigSchema", () => { ).toThrow(); }); - it("requires at least one model per inference provider", () => { + it("requires at least one model per model provider", () => { expect(() => PaseoAgentConfigSchema.parse({ providers: { p: { type: "openrouter", options: { models: [] } } }, @@ -114,9 +114,9 @@ describe("listPaseoAgentModels", () => { }); }); -describe("paseoAgentInferenceProviders (per-type defaults)", () => { +describe("paseoAgentModelProviders (per-type defaults)", () => { it("applies openrouter defaults (base url, api, model fields)", async () => { - const [provider] = await paseoAgentInferenceProviders(configWith()); + const [provider] = await paseoAgentModelProviders(configWith()); expect(provider.name).toBe("openrouter-main"); expect(provider.config.baseUrl).toBe("https://openrouter.ai/api/v1"); expect(provider.config.apiKey).toBe("sk-test"); @@ -136,7 +136,7 @@ describe("paseoAgentInferenceProviders (per-type defaults)", () => { anthropic: { type: "anthropic", options: { models: [{ id: "claude-x" }] } }, }, }); - const [provider] = await paseoAgentInferenceProviders(config); + const [provider] = await paseoAgentModelProviders(config); expect(provider.config.baseUrl).toBe("https://api.anthropic.com"); expect(provider.config.apiKey).toBe("$ANTHROPIC_API_KEY"); expect(provider.config.models?.[0]?.api).toBe("anthropic-messages"); @@ -155,7 +155,7 @@ describe("paseoAgentInferenceProviders (per-type defaults)", () => { }, }, }); - const [provider] = await paseoAgentInferenceProviders(config); + const [provider] = await paseoAgentModelProviders(config); expect(provider.config.baseUrl).toBe("https://opencode.ai/zen/v1"); expect(provider.config.models?.[0]?.api).toBe("openai-completions"); expect(provider.config.models?.[1]?.api).toBe("anthropic-messages"); @@ -177,7 +177,7 @@ describe("paseoAgentInferenceProviders (per-type defaults)", () => { }, }, }); - const [provider] = await paseoAgentInferenceProviders(config); + const [provider] = await paseoAgentModelProviders(config); expect(provider.config.api).toBe("google-generative-ai"); expect(provider.config.authHeader).toBe(true); expect(provider.config.headers).toEqual({ "x-extra": "1" }); @@ -288,8 +288,8 @@ describe("openai-codex (ChatGPT subscription) provider", () => { ).toThrow(); }); - it("maps to a codex inference provider with an oauth marker and no api key", async () => { - const [provider] = await paseoAgentInferenceProviders(codexConfig({}), {}); + it("maps to a codex model provider with an oauth marker and no api key", async () => { + const [provider] = await paseoAgentModelProviders(codexConfig({}), {}); expect(provider.name).toBe("chatgpt"); expect(provider.oauth).toEqual({ kind: "openai-codex" }); expect(provider.config.apiKey).toBeUndefined(); @@ -300,7 +300,7 @@ describe("openai-codex (ChatGPT subscription) provider", () => { it("carries an advanced self-supplied refresh token resolved from an env var", async () => { const config = codexConfig({ refreshToken: "$CODEX_REFRESH_TOKEN" }); - const [provider] = await paseoAgentInferenceProviders(config, { + const [provider] = await paseoAgentModelProviders(config, { CODEX_REFRESH_TOKEN: "rt-env", }); expect(provider.oauth).toEqual({ kind: "openai-codex", refreshToken: "rt-env" }); diff --git a/packages/server/src/server/agent/providers/paseo-agent/config.ts b/packages/server/src/server/agent/providers/paseo-agent/config.ts index a1d2f55bec..eb9d9afa6c 100644 --- a/packages/server/src/server/agent/providers/paseo-agent/config.ts +++ b/packages/server/src/server/agent/providers/paseo-agent/config.ts @@ -5,18 +5,18 @@ import { isRefreshTokenExpressionConfigured, resolveRefreshTokenExpression, } from "./oauth-credentials.js"; -import type { PaseoAgentInferenceProvider, PaseoAgentModelReference } from "./pi-services.js"; +import type { PaseoAgentModelProvider, PaseoAgentModelReference } from "./pi-services.js"; import { findEnvReferences } from "./env-references.js"; export const PASEO_AGENT_PROVIDER = "paseo"; // Dedicated Paseo-owned config for the Paseo Agent provider. This is the single -// schema for `agents.paseo`; inference-provider fields are intentionally NOT +// schema for `agents.paseo`; model-provider fields are intentionally NOT // merged into the shared strict ProviderOverrideSchema, and only this provider // (via the helpers below) consumes it. This module imports no Pi runtime code so // it stays cheap to load from persisted-config parsing. // -// Inference providers are typed by `type`. Each known type carries sensible +// Model providers are typed by `type`. Each known type carries sensible // defaults (base URL, Pi wire `api`, and the env var its API key is read from), // so a user only needs to give an `apiKey` (or the env var) and one or more model // ids. `openai-compatible` covers any OpenAI Chat Completions endpoint (incl. @@ -139,7 +139,7 @@ const PaseoAgentProviderOptionsSchema = z }) .strict(); -const PaseoAgentInferenceProviderSchema = z +const PaseoAgentModelProviderSchema = z .object({ type: z.enum(PROVIDER_TYPES), options: PaseoAgentProviderOptionsSchema, @@ -151,14 +151,14 @@ const PaseoAgentInferenceProviderSchema = z ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["options", "baseUrl"], - message: `Inference provider type "${entry.type}" requires options.baseUrl.`, + message: `Model provider type "${entry.type}" requires options.baseUrl.`, }); } if (!defaults.api && !entry.options.api) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["options", "api"], - message: `Inference provider type "${entry.type}" requires options.api.`, + message: `Model provider type "${entry.type}" requires options.api.`, }); } // `openai-codex` needs no credential field here: auth comes from the Paseo-owned @@ -168,20 +168,20 @@ const PaseoAgentInferenceProviderSchema = z export const PaseoAgentConfigSchema = z .object({ - // Optional default model as "/". + // Optional default model as "/". defaultModel: z.string().min(1).optional(), // Optional default agent definition from $PASEO_HOME/agents/.md. defaultAgent: z.string().min(1).optional(), // Legacy alias for defaultAgent. defaultProfile: z.string().min(1).optional(), - // Inference providers keyed by instance name. Multiple entries may share a + // Model providers keyed by instance name. Multiple entries may share a // type while pointing at different APIs/base URLs/models. - providers: z.record(z.string(), PaseoAgentInferenceProviderSchema).optional(), + providers: z.record(z.string(), PaseoAgentModelProviderSchema).optional(), }) .strict(); export type PaseoAgentConfig = z.infer; -type PaseoAgentInferenceProviderEntry = z.infer; +type PaseoAgentModelProviderEntry = z.infer; const DEFAULT_CONTEXT_WINDOW = 128_000; const DEFAULT_MAX_TOKENS = 16_384; @@ -194,14 +194,12 @@ interface ResolvedProviderSettings { authHeader?: boolean; } -function entries(config: PaseoAgentConfig): [string, PaseoAgentInferenceProviderEntry][] { +function entries(config: PaseoAgentConfig): [string, PaseoAgentModelProviderEntry][] { return Object.entries(config.providers ?? {}); } -/** Apply per-type defaults to a raw inference provider entry. */ -function resolveProviderSettings( - entry: PaseoAgentInferenceProviderEntry, -): ResolvedProviderSettings { +/** Apply per-type defaults to a raw model provider entry. */ +function resolveProviderSettings(entry: PaseoAgentModelProviderEntry): ResolvedProviderSettings { const defaults = resolvePaseoAgentProviderTypeDefaults(entry.type); const apiKey = entry.options.apiKey ?? (defaults.envVar ? `$${defaults.envVar}` : undefined); return { @@ -232,12 +230,12 @@ function isAuthConfigured(value: string | undefined, env: NodeJS.ProcessEnv): bo return referencedVars.every((name) => Boolean(env[name])); } -/** Encode the Paseo-facing model id from an inference provider + Pi model id. */ +/** Encode the Paseo-facing model id from an model provider + Pi model id. */ export function encodePaseoAgentModelId(providerName: string, modelId: string): string { return `${providerName}/${modelId}`; } -/** Parse a Paseo-facing model id back into its inference provider + Pi model id. */ +/** Parse a Paseo-facing model id back into its model provider + Pi model id. */ export function parsePaseoAgentModelId(modelId: string): PaseoAgentModelReference | null { const slash = modelId.indexOf("/"); if (slash <= 0 || slash === modelId.length - 1) { @@ -246,7 +244,7 @@ export function parsePaseoAgentModelId(modelId: string): PaseoAgentModelReferenc return { provider: modelId.slice(0, slash), id: modelId.slice(slash + 1) }; } -function toPiModels(entry: PaseoAgentInferenceProviderEntry, settings: ResolvedProviderSettings) { +function toPiModels(entry: PaseoAgentModelProviderEntry, settings: ResolvedProviderSettings) { return entry.options.models.map((model) => { const api = model.api ?? settings.api; return { @@ -263,16 +261,16 @@ function toPiModels(entry: PaseoAgentInferenceProviderEntry, settings: ResolvedP } /** - * Map Paseo-owned config into the in-memory inference providers the Pi seam expects. + * Map Paseo-owned config into the in-memory model providers the Pi seam expects. * `openai-codex` entries carry an oauth marker instead of an API key. Their credential * normally lives in the Paseo-owned store (populated by `paseo login chatgpt`); an * advanced `options.refreshToken` may supply the user's own token instead. */ -export async function paseoAgentInferenceProviders( +export async function paseoAgentModelProviders( config: PaseoAgentConfig, env: NodeJS.ProcessEnv = process.env, -): Promise { - const providers: PaseoAgentInferenceProvider[] = []; +): Promise { + const providers: PaseoAgentModelProvider[] = []; for (const [name, entry] of entries(config)) { const settings = resolveProviderSettings(entry); @@ -329,7 +327,7 @@ export function listPaseoAgentModels(config: PaseoAgentConfig): AgentModelDefini } /** - * An inference provider is usable when it has at least one model and auth is configured. + * An model provider is usable when it has at least one model and auth is configured. * For API-key types that means a resolvable key (literal, set env var, or command). For * `openai-codex`, auth comes from the Paseo-owned store (checked via `isOAuthAuthed`, * keyed by provider instance name) or, as an advanced override, a resolvable @@ -364,7 +362,7 @@ export function paseoAgentHasUsableModel( export function resolvePaseoAgentModel( config: PaseoAgentConfig, requestedModelId: string | null | undefined, - registeredProviders: PaseoAgentInferenceProvider[] = paseoAgentModelInventory(config), + registeredProviders: PaseoAgentModelProvider[] = paseoAgentModelInventory(config), agentDefaultModelId?: string | null, ): PaseoAgentModelReference | undefined { if (requestedModelId) { @@ -384,7 +382,7 @@ export function resolvePaseoAgentModel( return firstRegisteredModel(registeredProviders); } -function paseoAgentModelInventory(config: PaseoAgentConfig): PaseoAgentInferenceProvider[] { +function paseoAgentModelInventory(config: PaseoAgentConfig): PaseoAgentModelProvider[] { return entries(config).map(([name, entry]) => { const settings = resolveProviderSettings(entry); return { name, config: { models: toPiModels(entry, settings) } }; @@ -402,7 +400,7 @@ function firstModelId(config: PaseoAgentConfig): string | undefined { } function hasRegisteredModel( - providers: PaseoAgentInferenceProvider[], + providers: PaseoAgentModelProvider[], model: PaseoAgentModelReference, ): boolean { return providers.some( @@ -413,7 +411,7 @@ function hasRegisteredModel( } function firstRegisteredModel( - providers: PaseoAgentInferenceProvider[], + providers: PaseoAgentModelProvider[], ): PaseoAgentModelReference | undefined { for (const provider of providers) { const first = provider.config.models?.[0]; diff --git a/packages/server/src/server/agent/providers/paseo-agent/pi-services.test.ts b/packages/server/src/server/agent/providers/paseo-agent/pi-services.test.ts index 6d3bdefb32..41262f83e0 100644 --- a/packages/server/src/server/agent/providers/paseo-agent/pi-services.test.ts +++ b/packages/server/src/server/agent/providers/paseo-agent/pi-services.test.ts @@ -8,11 +8,11 @@ import { createToolPermissionPolicy } from "./agent-permissions.js"; import { type CreatePaseoAgentSessionOptions, - type PaseoAgentInferenceProvider, + type PaseoAgentModelProvider, createPaseoAgentSession, } from "./pi-services.js"; -function codexInferenceProvider(): PaseoAgentInferenceProvider { +function codexModelProvider(): PaseoAgentModelProvider { return { name: "chatgpt", config: { @@ -46,7 +46,7 @@ function toolCallContext(toolName: string): BeforeToolCallContext { } as BeforeToolCallContext; } -function fakeInferenceProvider(): PaseoAgentInferenceProvider { +function fakeModelProvider(): PaseoAgentModelProvider { return { name: FAKE_PROVIDER, config: { @@ -101,12 +101,12 @@ describe("createPaseoAgentSession (no-discovery spike)", () => { return { cwd, agentDir, - inferenceProviders: [fakeInferenceProvider()], + modelProviders: [fakeModelProvider()], model: { provider: FAKE_PROVIDER, id: FAKE_MODEL_ID }, }; } - it("creates a session from an in-memory inference provider and selects its model", async () => { + it("creates a session from an in-memory model provider and selects its model", async () => { const { session, modelRegistry } = await createPaseoAgentSession(baseOptions()); expect(session).toBeDefined(); @@ -160,11 +160,11 @@ describe("createPaseoAgentSession (no-discovery spike)", () => { expect(existsSync(join(cwd, ".pi"))).toBe(false); }); - it("rejects a model that no inference provider registered", async () => { + it("rejects a model that no model provider registered", async () => { await expect( createPaseoAgentSession({ ...baseOptions(), - inferenceProviders: [], + modelProviders: [], }), ).rejects.toThrow(/not registered/); }); @@ -261,14 +261,12 @@ describe("createPaseoAgentSession (no-discovery spike)", () => { }); it("registers a codex provider and seeds the advanced refresh-token override", async () => { - const codex = codexInferenceProvider(); + const codex = codexModelProvider(); const { session, modelRegistry } = await createPaseoAgentSession({ cwd, agentDir, model: { provider: "chatgpt", id: "gpt-5.3-codex" }, - inferenceProviders: [ - { ...codex, oauth: { kind: "openai-codex", refreshToken: "rt-test-only" } }, - ], + modelProviders: [{ ...codex, oauth: { kind: "openai-codex", refreshToken: "rt-test-only" } }], }); expect(session.model?.provider).toBe("chatgpt"); @@ -289,7 +287,7 @@ describe("createPaseoAgentSession (no-discovery spike)", () => { authStorage, model: { provider: "chatgpt", id: "gpt-5.3-codex" }, // No oauth.refreshToken marker — the credential comes from the Paseo store. - inferenceProviders: [{ ...codexInferenceProvider(), oauth: { kind: "openai-codex" } }], + modelProviders: [{ ...codexModelProvider(), oauth: { kind: "openai-codex" } }], }); const available = modelRegistry.getAvailable(); diff --git a/packages/server/src/server/agent/providers/paseo-agent/pi-services.ts b/packages/server/src/server/agent/providers/paseo-agent/pi-services.ts index 0f55835fd7..bff96d1d50 100644 --- a/packages/server/src/server/agent/providers/paseo-agent/pi-services.ts +++ b/packages/server/src/server/agent/providers/paseo-agent/pi-services.ts @@ -36,7 +36,7 @@ export type PiProviderConfig = Parameters[1]; type PiAuthData = Parameters[0]; type PiSettings = Parameters[0]; -/** OAuth wiring for an inference provider (currently only ChatGPT/Codex). */ +/** OAuth wiring for an model provider (currently only ChatGPT/Codex). */ export interface PaseoAgentOAuth { kind: "openai-codex"; /** @@ -47,7 +47,7 @@ export interface PaseoAgentOAuth { refreshToken?: string; } -export interface PaseoAgentInferenceProvider { +export interface PaseoAgentModelProvider { /** Instance name, e.g. "openrouter-main". Used as the Pi provider key. */ name: string; /** Typed Pi provider config: baseUrl, apiKey, models, api, etc. */ @@ -70,8 +70,8 @@ export interface CreatePaseoAgentSessionOptions { * from or written to it during creation. */ agentDir: string; - /** Inference providers (model backends) registered entirely in memory. */ - inferenceProviders: PaseoAgentInferenceProvider[]; + /** Model providers (model backends) registered entirely in memory. */ + modelProviders: PaseoAgentModelProvider[]; /** Explicit model selection. When omitted, Pi falls back to its own resolution. */ model?: PaseoAgentModelReference; thinkingLevel?: ThinkingLevel; @@ -187,7 +187,7 @@ export async function createPaseoAgentSession( // Seed any oauth marker that carries a refresh token (the advanced/manual override). // The product path leaves this empty — the credential is already in the Paseo store. // Empty `access` + `expires: 0` forces a refresh on the first request. - for (const provider of options.inferenceProviders) { + for (const provider of options.modelProviders) { if (provider.oauth?.kind === "openai-codex" && provider.oauth.refreshToken) { authStorage.set(provider.name, { type: "oauth", @@ -200,7 +200,7 @@ export async function createPaseoAgentSession( const modelRegistry = ModelRegistry.inMemory(authStorage); - for (const provider of options.inferenceProviders) { + for (const provider of options.modelProviders) { const config = provider.oauth?.kind === "openai-codex" ? { ...provider.config, oauth: openaiCodexOAuthProvider } @@ -222,7 +222,7 @@ export async function createPaseoAgentSession( : undefined; if (options.model && !model) { throw new Error( - `Paseo Agent: model ${options.model.provider}/${options.model.id} is not registered by any inference provider`, + `Paseo Agent: model ${options.model.provider}/${options.model.id} is not registered by any model provider`, ); } diff --git a/packages/server/src/server/persisted-config.test.ts b/packages/server/src/server/persisted-config.test.ts index a6a018a4f1..e7d1f41b0a 100644 --- a/packages/server/src/server/persisted-config.test.ts +++ b/packages/server/src/server/persisted-config.test.ts @@ -242,7 +242,7 @@ describe("PersistedConfigSchema agent provider runtime settings", () => { }); }); - test("accepts the dedicated agents.paseo inference provider config", () => { + test("accepts the dedicated agents.paseo model provider config", () => { const parsed = PersistedConfigSchema.parse({ agents: { paseo: {