feat(coding-agent): replace model registry with model runtime

Move provider auth and OAuth flows onto pi-ai Models, compose models.json and extension overlays through ModelRuntime, and retain ModelRegistry as an extension compatibility facade.
This commit is contained in:
Mario Zechner
2026-07-14 17:48:45 +02:00
parent 6731a0ba9e
commit 9993c96907
133 changed files with 5103 additions and 4340 deletions
@@ -46,7 +46,7 @@ import {
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
// =============================================================================
// OAuth Implementation (copied from packages/ai/src/utils/oauth/anthropic.ts)
// OAuth implementation adapted for the legacy extension compatibility interface.
// =============================================================================
const decode = (s: string) => atob(s);
@@ -5,11 +5,9 @@
*/
import { getModel } from "@earendil-works/pi-ai/compat";
import { AuthStorage, createAgentSession, ModelRegistry } from "@earendil-works/pi-coding-agent";
import { createAgentSession, ModelRuntime } from "@earendil-works/pi-coding-agent";
// Set up auth storage and model registry
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
const modelRuntime = await ModelRuntime.create();
// Option 1: Find a specific built-in model by provider/id
const opus = getModel("anthropic", "claude-opus-4-5");
@@ -18,13 +16,13 @@ if (opus) {
}
// Option 2: Find model via registry (includes custom models from models.json)
const customModel = modelRegistry.find("my-provider", "my-model");
const customModel = modelRuntime.getModel("my-provider", "my-model");
if (customModel) {
console.log(`Found custom model: ${customModel.provider}/${customModel.id}`);
}
// Option 3: Pick from available models (have valid API keys)
const available = await modelRegistry.getAvailable();
const available = await modelRuntime.getAvailable();
console.log(
"Available models:",
available.map((m) => `${m.provider}/${m.id}`),
@@ -34,8 +32,7 @@ if (available.length > 0) {
const { session } = await createAgentSession({
model: available[0],
thinkingLevel: "medium", // off, low, medium, high
authStorage,
modelRegistry,
modelRuntime,
});
try {
@@ -1,52 +1,34 @@
/**
* API Keys and OAuth
*
* Configure API key resolution via AuthStorage and ModelRegistry.
* Configure provider auth through ModelRuntime.
*/
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "@earendil-works/pi-coding-agent";
// Default: AuthStorage uses ~/.pi/agent/auth.json
// ModelRegistry loads built-in + custom models from ~/.pi/agent/models.json
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
import { createAgentSession, ModelRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
const modelRuntime = await ModelRuntime.create();
const { session: defaultAuthSession } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry,
modelRuntime,
});
console.log("Session with default auth storage and model registry");
console.log("Session with default model runtime");
defaultAuthSession.dispose();
// Custom auth storage location
const customAuthStorage = AuthStorage.create("/tmp/my-app/auth.json");
const customModelRegistry = ModelRegistry.create(customAuthStorage, "/tmp/my-app/models.json");
const customRuntime = await ModelRuntime.create({
authPath: "/tmp/my-app/auth.json",
modelsPath: "/tmp/my-app/models.json",
});
const { session: customAuthSession } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage: customAuthStorage,
modelRegistry: customModelRegistry,
modelRuntime: customRuntime,
});
console.log("Session with custom auth storage location");
console.log("Session with custom auth and models locations");
customAuthSession.dispose();
// Runtime API key override (not persisted to disk)
authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key");
modelRuntime.setRuntimeApiKey("anthropic", "sk-my-temp-key");
const { session: runtimeKeySession } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry,
modelRuntime,
});
console.log("Session with runtime API key override");
runtimeKeySession.dispose();
// No models.json - only built-in models
const simpleRegistry = ModelRegistry.inMemory(authStorage);
const { session: builtInModelsSession } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry: simpleRegistry,
});
console.log("Session with only built-in models");
builtInModelsSession.dispose();
@@ -6,26 +6,22 @@
import { getModel } from "@earendil-works/pi-ai/compat";
import {
AuthStorage,
createAgentSession,
createExtensionRuntime,
ModelRegistry,
ModelRuntime,
type ResourceLoader,
SessionManager,
SettingsManager,
} from "@earendil-works/pi-coding-agent";
// Custom auth storage location
const authStorage = AuthStorage.create("/tmp/my-agent/auth.json");
// Runtime API key override (not persisted)
const modelRuntime = await ModelRuntime.create({
authPath: "/tmp/my-agent/auth.json",
modelsPath: "/tmp/my-agent/models.json",
});
if (process.env.MY_ANTHROPIC_KEY) {
authStorage.setRuntimeApiKey("anthropic", process.env.MY_ANTHROPIC_KEY);
modelRuntime.setRuntimeApiKey("anthropic", process.env.MY_ANTHROPIC_KEY);
}
// Model registry with no custom models.json
const modelRegistry = ModelRegistry.inMemory(authStorage);
const model = getModel("anthropic", "claude-sonnet-4-5");
if (!model) throw new Error("Model not found");
@@ -55,8 +51,7 @@ const { session } = await createAgentSession({
agentDir: "/tmp/my-agent",
model,
thinkingLevel: "off",
authStorage,
modelRegistry,
modelRuntime,
resourceLoader,
tools: ["read", "bash"],
sessionManager: SessionManager.inMemory(cwd),
+14 -18
View File
@@ -34,46 +34,44 @@ npx tsx examples/sdk/01-minimal.ts
```typescript
import { getModel } from "@earendil-works/pi-ai";
import {
AuthStorage,
createAgentSession,
DefaultResourceLoader,
ModelRegistry,
ModelRuntime,
SessionManager,
SettingsManager,
} from "@earendil-works/pi-coding-agent";
// Auth and models setup
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
const modelRuntime = await ModelRuntime.create();
// Minimal
const { session } = await createAgentSession({ authStorage, modelRegistry });
const { session } = await createAgentSession({ modelRuntime });
// Custom model
const model = getModel("anthropic", "claude-opus-4-5");
const { session } = await createAgentSession({ model, thinkingLevel: "high", authStorage, modelRegistry });
const { session } = await createAgentSession({ model, thinkingLevel: "high", modelRuntime });
// Modify prompt
const loader = new DefaultResourceLoader({
systemPromptOverride: (base) => `${base}\n\nBe concise.`,
});
await loader.reload();
const { session } = await createAgentSession({ resourceLoader: loader, authStorage, modelRegistry });
const { session } = await createAgentSession({ resourceLoader: loader, modelRuntime });
// Read-only
const { session } = await createAgentSession({ tools: ["read", "grep", "find", "ls"], authStorage, modelRegistry });
const { session } = await createAgentSession({ tools: ["read", "grep", "find", "ls"], modelRuntime });
// In-memory
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry,
modelRuntime,
});
// Full control
const customAuth = AuthStorage.create("/my/app/auth.json");
customAuth.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
const customRegistry = ModelRegistry.create(customAuth);
const customRuntime = await ModelRuntime.create({
authPath: "/my/app/auth.json",
modelsPath: "/my/app/models.json",
});
customRuntime.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
const resourceLoader = new DefaultResourceLoader({
systemPromptOverride: () => "You are helpful.",
@@ -86,8 +84,7 @@ await resourceLoader.reload();
const { session } = await createAgentSession({
model,
authStorage: customAuth,
modelRegistry: customRegistry,
modelRuntime: customRuntime,
resourceLoader,
tools: ["read", "bash", "my_tool"],
customTools: [myTool],
@@ -108,8 +105,7 @@ await session.prompt("Hello");
| Option | Default | Description |
|--------|---------|-------------|
| `authStorage` | `AuthStorage.create()` | Credential storage |
| `modelRegistry` | `ModelRegistry.create(authStorage)` | Model registry |
| `modelRuntime` | Runtime using `agentDir/auth.json` and `models.json` | Canonical model and authentication runtime |
| `cwd` | `process.cwd()` | Working directory |
| `agentDir` | `~/.pi/agent` | Config directory |
| `model` | From settings/first available | Model to use |