refactor(auth-storage): cleanup auth.json mentions
- Migrated authentication storage from JSON-based (auth.json) to database-based (agent.db) format across test files and configuration. - Simplified auth storage discovery in sdk.ts by removing manual path construction and fallback logic in favor of centralized getAgentDbPath() function. - Removed dbPath instance property from AuthStorage class as database path is now managed centrally. - Updated environment variable precedence documentation to reflect agent.db instead of auth.json as the lowest priority source. - Removed OAuth provider section header comments from multiple test files for cleaner test organization. - Added support for JSON and JSONC configuration file formats without requiring migration to YAML.
This commit is contained in:
@@ -349,7 +349,7 @@ Our fork has architectural decisions that differ from upstream. **Do not port th
|
||||
|
||||
| Upstream | Our Fork | Notes |
|
||||
| ------------------------------- | ------------------------------------------- | ----------------------------------------------------- |
|
||||
| `proper-lockfile` + `auth.json` | `agent.db` (bun:sqlite) | Legacy `auth.json` is migrated; do not reintroduce it |
|
||||
| `proper-lockfile` + `auth.json` | `agent.db` (bun:sqlite) | Credentials stored exclusively in `agent.db` |
|
||||
| Single credential per provider | Multi-credential with round-robin selection | Session affinity and backoff logic preserved |
|
||||
|
||||
### Extensions
|
||||
|
||||
@@ -983,7 +983,7 @@ bunx @oh-my-pi/pi-ai login anthropic # login to specific provider
|
||||
bunx @oh-my-pi/pi-ai list # list available providers
|
||||
```
|
||||
|
||||
Credentials are saved to `auth.json` in the current directory.
|
||||
Credentials are saved to `agent.db` in the agent directory.
|
||||
|
||||
### Programmatic OAuth
|
||||
|
||||
@@ -1036,7 +1036,7 @@ const credentials = await loginGitHubCopilot({
|
||||
|
||||
// Store credentials yourself
|
||||
const auth = { "github-copilot": { type: "oauth", ...credentials } };
|
||||
fs.writeFileSync("auth.json", JSON.stringify(auth, null, 2));
|
||||
fs.writeFileSync("credentials.json", JSON.stringify(auth, null, 2));
|
||||
```
|
||||
|
||||
### Using OAuth Tokens
|
||||
@@ -1048,7 +1048,7 @@ import { getModel, complete, getOAuthApiKey } from "@oh-my-pi/pi-ai";
|
||||
import * as fs from "node:fs";
|
||||
|
||||
// Load your stored credentials
|
||||
const auth = JSON.parse(fs.readFileSync("auth.json", "utf-8"));
|
||||
const auth = JSON.parse(fs.readFileSync("credentials.json", "utf-8"));
|
||||
|
||||
// Get API key (refreshes if expired)
|
||||
const result = await getOAuthApiKey("github-copilot", auth);
|
||||
@@ -1056,7 +1056,7 @@ if (!result) throw new Error("Not logged in");
|
||||
|
||||
// Save refreshed credentials
|
||||
auth["github-copilot"] = { type: "oauth", ...result.newCredentials };
|
||||
fs.writeFileSync("auth.json", JSON.stringify(auth, null, 2));
|
||||
fs.writeFileSync("credentials.json", JSON.stringify(auth, null, 2));
|
||||
|
||||
// Use the API key
|
||||
const model = getModel("github-copilot", "gpt-4o");
|
||||
|
||||
@@ -465,10 +465,6 @@ describe("AI Providers Empty Message Tests", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// OAuth-based providers (credentials from ~/.pi/agent/oauth.json)
|
||||
// =========================================================================
|
||||
|
||||
describe("Anthropic OAuth Provider Empty Messages", () => {
|
||||
const llm = getModel("anthropic", "claude-3-5-haiku-20241022");
|
||||
|
||||
|
||||
@@ -321,10 +321,6 @@ describe("Tool Results with Images", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// OAuth-based providers (credentials from ~/.pi/agent/oauth.json)
|
||||
// =========================================================================
|
||||
|
||||
describe("Anthropic OAuth Provider (claude-sonnet-4-5)", () => {
|
||||
const model = getModel("anthropic", "claude-sonnet-4-5");
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Test helper for resolving API keys from ~/.pi/agent/auth.json
|
||||
* Test helper for resolving API keys from ~/.pi/agent/testauth.db
|
||||
*
|
||||
* Supports both API key and OAuth credentials.
|
||||
* OAuth tokens are automatically refreshed if expired and saved back to auth.json.
|
||||
* OAuth tokens are automatically refreshed if expired and saved back to testauth.db.
|
||||
*
|
||||
* E2E tests are disabled by default. Set E2E=1 environment variable to enable.
|
||||
*/
|
||||
@@ -28,7 +28,7 @@ export function e2eApiKey(envVar: string): string | undefined {
|
||||
return Bun.env[envVar];
|
||||
}
|
||||
|
||||
const AUTH_PATH = path.join(os.homedir(), ".pi", "agent", "auth.json");
|
||||
const AUTH_PATH = path.join(os.homedir(), ".pi", "agent", "testauth.db");
|
||||
|
||||
type ApiKeyCredential = {
|
||||
type: "api_key";
|
||||
@@ -59,7 +59,7 @@ async function saveAuthStorage(storage: AuthStorage): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve API key for a provider from ~/.pi/agent/auth.json
|
||||
* Resolve API key for a provider from ~/.pi/agent/testauth.db
|
||||
*
|
||||
* For API key credentials, returns the key directly.
|
||||
* For OAuth credentials, returns the access token (refreshing if expired and saving back).
|
||||
@@ -91,7 +91,7 @@ export async function resolveApiKey(provider: string): Promise<string | undefine
|
||||
const result = await getOAuthApiKey(provider as OAuthProvider, oauthCredentials);
|
||||
if (!result) return undefined;
|
||||
|
||||
// Save refreshed credentials back to auth.json
|
||||
// Save refreshed credentials back to testauth.db
|
||||
storage[provider] = { type: "oauth", ...result.newCredentials };
|
||||
await saveAuthStorage(storage);
|
||||
|
||||
|
||||
@@ -980,11 +980,6 @@ describe("Generate E2E Tests", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// OAuth-based providers (credentials from ~/.pi/agent/oauth.json)
|
||||
// Tokens are resolved at module level (see oauthTokens above)
|
||||
// =========================================================================
|
||||
|
||||
describe("Anthropic OAuth Provider (claude-sonnet-4-20250514)", () => {
|
||||
const model = getModel("anthropic", "claude-sonnet-4-20250514");
|
||||
|
||||
|
||||
@@ -180,10 +180,6 @@ describe("Token Statistics on Abort", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// OAuth-based providers (credentials from ~/.pi/agent/oauth.json)
|
||||
// =========================================================================
|
||||
|
||||
describe("Anthropic OAuth Provider", () => {
|
||||
const llm = getModel("anthropic", "claude-3-5-haiku-20241022");
|
||||
|
||||
|
||||
@@ -206,10 +206,6 @@ describe("Tool Call Without Result Tests", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// OAuth-based providers (credentials from ~/.pi/agent/oauth.json)
|
||||
// =========================================================================
|
||||
|
||||
describe("Anthropic OAuth Provider", () => {
|
||||
const model = getModel("anthropic", "claude-3-5-haiku-20241022");
|
||||
|
||||
|
||||
@@ -389,10 +389,6 @@ describe("AI Providers Unicode Surrogate Pair Tests", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// OAuth-based providers (credentials from ~/.pi/agent/oauth.json)
|
||||
// =========================================================================
|
||||
|
||||
describe("Anthropic OAuth Provider Unicode Handling", () => {
|
||||
const llm = getModel("anthropic", "claude-3-5-haiku-20241022");
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added `omp commit` command to generate commit messages and update changelogs with `--push`, `--dry-run`, `--no-changelog`, and model override flags
|
||||
@@ -29,8 +28,7 @@
|
||||
- Changed Perplexity search context size from 'high' to 'medium' and added search classifier, reasoning effort, and language preference settings
|
||||
- Increased Perplexity default max tokens from 4096 to 8192 for more comprehensive responses
|
||||
- Updated Anthropic and Gemini search providers to support `max_tokens` and `temperature` parameters for finer control over response generation
|
||||
- Simplified `AuthStorage.create()` to accept direct agent.db path instead of legacy auth.json path with fallback resolution
|
||||
- Updated `discoverAuthStorage()` to skip JSON-to-SQLite migration step, improving startup performance
|
||||
- Simplified `AuthStorage.create()` to accept direct agent.db path
|
||||
- Renamed web search types and exports for consistency: `WebSearchProvider` → `SearchProviderId`, `WebSearchResponse` → `SearchResponse`, `WebSearchTool` → `SearchTool`, and related functions
|
||||
- Refactored web search provider system to use centralized provider registry with `getSearchProvider()` and `resolveProviderChain()` for improved provider management
|
||||
- Updated web search system prompt to emphasize comprehensive, detailed answers with concrete data and specific examples over brevity
|
||||
@@ -49,8 +47,7 @@
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed legacy auth.json migration system—credentials are now stored exclusively in agent.db
|
||||
- Removed `getAuthPath()` configuration function—use `getAgentDbPath()` for credential storage location
|
||||
- Removed legacy auth.json file—credentials are stored exclusively in agent.db
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -1703,6 +1700,8 @@
|
||||
|
||||
- Fixed editor border rendering glitch after canceling slash command autocomplete
|
||||
- Fixed login/logout credential path message to reference agent.db
|
||||
- Removed legacy auth.json file—credentials are stored exclusively in agent.db
|
||||
- Removed legacy auth.json file—credentials are stored exclusively in agent.db
|
||||
|
||||
## [4.2.0] - 2026-01-10
|
||||
|
||||
|
||||
@@ -370,7 +370,7 @@ When disabled, neither case triggers automatic compaction (use `/compact` manual
|
||||
- Automatically sets environment variables when the application starts
|
||||
- Only sets variables that aren't already present in `Bun.env`
|
||||
- Supports any environment variable, not just API keys
|
||||
- Order of precedence: existing env vars > settings.json env vars > auth.json env vars
|
||||
- Order of precedence: existing env vars > settings.json env vars > agent.db
|
||||
|
||||
> **Note:** Compaction is lossy. The agent loses full conversation access afterward. Size tasks to avoid context limits when possible. For critical context, ask the agent to write a summary to a file, iterate on it until it covers everything, then start a new session with that file. The full session history is preserved in the JSONL file; use `/tree` to revisit any previous point.
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ Many modules now use the **capability/discovery system** (`discovery/builtin.ts`
|
||||
| `session/agent-session.ts` | `getAgentDbPath` | `~/.omp/agent/agent.db` | Database path |
|
||||
| `session/session-manager.ts` | `getAgentDir` | `~/.omp/agent/sessions/` | Session storage |
|
||||
| `session/agent-storage.ts` | `getAgentDbPath` | `~/.omp/agent/agent.db` | Settings/auth storage |
|
||||
| `session/auth-storage.ts` | `getAgentDbPath`, `getAuthPath` | agent.db, auth.json | Auth credential storage |
|
||||
| `session/auth-storage.ts` | `getAgentDbPath` | agent.db | Auth credential storage |
|
||||
| `session/history-storage.ts` | `getAgentDir` | `~/.omp/agent/` | Command history |
|
||||
| `session/storage-migration.ts` | `getAgentDbPath` | `~/.omp/agent/agent.db` | JSON→SQLite migration |
|
||||
| `modes/theme/theme.ts` | `getCustomThemesDir` | `~/.omp/agent/themes/` | Custom themes |
|
||||
@@ -78,12 +78,12 @@ These use helpers to check `.omp`, `.pi`, `.claude`, `.codex`, `.gemini` directo
|
||||
| File | Helper Used | Subpath(s) | Levels |
|
||||
| ---------------------------------------- | ------------------------------------------------------ | --------------------------- | ------------ |
|
||||
| `main.ts` | `findConfigFile` | `SYSTEM.md`, `APPEND_SYSTEM.md` | user+project |
|
||||
| `sdk.ts` | `getConfigDirPaths` | `auth.json`, `models.yml`, `models.json` | user |
|
||||
| `sdk.ts` | `getConfigDirPaths` | `models.yml`, `models.json` | user |
|
||||
| `lsp/config.ts` | `getConfigDirPaths` | `lsp.json`, `.lsp.json` | user+project |
|
||||
| `task/discovery.ts` | `getConfigDirs`, `findAllNearestProjectConfigDirs` | `agents/` | user+project |
|
||||
| `extensibility/plugins/paths.ts` | `getConfigDirPaths` | `plugin-overrides.json` | project |
|
||||
| `extensibility/custom-commands/loader.ts`| `getConfigDirs` | `commands/` | user+project |
|
||||
| `web/search/auth.ts` | `getConfigDirPaths`, `getAgentDbPath` | auth.json, agent.db | user |
|
||||
| `web/search/auth.ts` | `getConfigDirPaths`, `getAgentDbPath` | agent.db | user |
|
||||
| `web/search/providers/codex.ts` | `getConfigDirPaths`, `getAgentDbPath` | auth config | user |
|
||||
| `web/search/providers/gemini.ts` | `getConfigDirPaths`, `getAgentDbPath` | auth config | user |
|
||||
|
||||
@@ -110,7 +110,6 @@ These modules use `discovery/builtin.ts` which has its own config directory reso
|
||||
```
|
||||
User-level (~/.omp/agent/, ~/.pi/agent/, ~/.claude/, ~/.codex/, ~/.gemini/):
|
||||
├── agent.db ← SQLite storage (settings, auth)
|
||||
├── auth.json ← Legacy auth (migrated to agent.db)
|
||||
├── models.yml ← Model configuration (preferred)
|
||||
├── models.json ← Model configuration (legacy)
|
||||
├── config.yml ← Settings (alternative to agent.db)
|
||||
|
||||
@@ -272,7 +272,7 @@ const { session } = await createAgentSession({
|
||||
`agentDir` is used for:
|
||||
|
||||
- Global settings (`config.yml` + `agent.db`)
|
||||
- Primary auth/models locations (`auth.json`, `models.yml`, `models.json`)
|
||||
- Primary auth/models locations (`agent.db`, `models.yml`, `models.json`)
|
||||
- Prompt templates (`prompts/`)
|
||||
- Custom TS commands (`commands/`)
|
||||
|
||||
@@ -328,12 +328,13 @@ API key resolution priority (handled by AuthStorage):
|
||||
3. Environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.)
|
||||
4. Fallback resolver (for custom provider keys from `models.yml`)
|
||||
|
||||
`discoverAuthStorage` also migrates legacy `auth.json` from user config directories (`.pi`, `.claude`, `.codex`, `.gemini`) into `agent.db`.
|
||||
|
||||
`discoverAuthStorage` opens the `agent.db` SQLite database in the agent directory.
|
||||
|
||||
```typescript
|
||||
import { AuthStorage, ModelRegistry, discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent";
|
||||
|
||||
// Default: uses agentDir/auth.json → agent.db and agentDir/models.yml (with legacy fallbacks)
|
||||
// Default: uses agentDir/agent.db and agentDir/models.yml
|
||||
const authStorage = await discoverAuthStorage();
|
||||
const modelRegistry = discoverModels(authStorage);
|
||||
|
||||
@@ -347,7 +348,7 @@ const { session } = await createAgentSession({
|
||||
authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key");
|
||||
|
||||
// Custom auth storage location (use create(), constructor is private)
|
||||
const customAuth = await AuthStorage.create("/my/app/auth.json");
|
||||
const customAuth = await AuthStorage.create("/my/app/agent.db");
|
||||
const customRegistry = new ModelRegistry(customAuth, "/my/app/models.yml");
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
@@ -788,7 +789,7 @@ import {
|
||||
} from "@oh-my-pi/pi-coding-agent";
|
||||
|
||||
// Auth and Models
|
||||
const authStorage = await discoverAuthStorage(); // <agentDir>/auth.json → agent.db (with fallbacks)
|
||||
const authStorage = await discoverAuthStorage(); // <agentDir>/agent.db
|
||||
const modelRegistry = discoverModels(authStorage); // + <agentDir>/models.yml (or models.json)
|
||||
const allModels = modelRegistry.getAll(); // All models (built-in + custom)
|
||||
const available = modelRegistry.getAvailable(); // Only models with API keys
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
SessionManager,
|
||||
} from "@oh-my-pi/pi-coding-agent";
|
||||
|
||||
// Default: discoverAuthStorage() uses ~/.omp/agent/auth.json
|
||||
// Default: discoverAuthStorage() uses ~/.omp/agent/agent.db
|
||||
// discoverModels() loads built-in + custom models from ~/.omp/agent/models.json
|
||||
const authStorage = await discoverAuthStorage();
|
||||
const modelRegistry = await discoverModels(authStorage);
|
||||
@@ -25,7 +25,7 @@ await createAgentSession({
|
||||
console.log("Session with default auth storage and model registry");
|
||||
|
||||
// Custom auth storage location
|
||||
const customAuthStorage = new AuthStorage("/tmp/my-app/auth.json");
|
||||
const customAuthStorage = await AuthStorage.create("/tmp/my-app/agent.db");
|
||||
const customModelRegistry = new ModelRegistry(customAuthStorage, "/tmp/my-app/models.json");
|
||||
|
||||
await createAgentSession({
|
||||
|
||||
@@ -77,7 +77,7 @@ const { session } = await createAgentSession({
|
||||
});
|
||||
|
||||
// Full control
|
||||
const customAuth = new AuthStorage("/my/app/auth.json");
|
||||
const customAuth = await AuthStorage.create("/my/app/agent.db");
|
||||
customAuth.setRuntimeApiKey("anthropic", Bun.env.MY_KEY!);
|
||||
const customRegistry = new ModelRegistry(customAuth);
|
||||
|
||||
|
||||
@@ -159,6 +159,8 @@ export class ConfigFile<T> implements IConfigFile<T> {
|
||||
} else if (configPath.endsWith(".yaml")) {
|
||||
const jsonPath = `${configPath.slice(0, -5)}.json`;
|
||||
migrateJsonToYml(jsonPath, configPath);
|
||||
} else if (configPath.endsWith(".json") || configPath.endsWith(".jsonc")) {
|
||||
// JSON configs are still supported without migration.
|
||||
} else {
|
||||
throw new Error(`Invalid config file path: ${configPath}`);
|
||||
}
|
||||
|
||||
@@ -255,6 +255,7 @@ export class ModelRegistry {
|
||||
* Reload models from disk (built-in + custom from models.json).
|
||||
*/
|
||||
refresh(): void {
|
||||
this.modelsConfigFile.invalidate();
|
||||
this.customProviderApiKeys.clear();
|
||||
this.configError = undefined;
|
||||
this.loadModels();
|
||||
|
||||
@@ -23,7 +23,6 @@ import { Settings, settings } from "./config/settings";
|
||||
import { initializeWithSettings } from "./discovery";
|
||||
import { exportFromFile } from "./export/html";
|
||||
import type { ExtensionUIContext } from "./extensibility/extensions/types";
|
||||
import { runMigrations, showDeprecationWarnings } from "./migrations";
|
||||
import { InteractiveMode, runPrintMode, runRpcMode } from "./modes";
|
||||
import { initTheme, stopThemeWatcher } from "./modes/theme/theme";
|
||||
import { type CreateAgentSessionOptions, createAgentSession, discoverAuthStorage } from "./sdk";
|
||||
@@ -488,13 +487,6 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
||||
|
||||
const notifs: (InteractiveModeNotify | null)[] = [];
|
||||
|
||||
// Run migrations (pass cwd for project-local migrations)
|
||||
const { migratedAuthProviders: migratedProviders, deprecationWarnings } = await runMigrations(process.cwd());
|
||||
debugStartup("main:runMigrations");
|
||||
if (migratedProviders.length > 0) {
|
||||
notifs.push({ kind: "warn", message: `Migrated credentials to agent.db: ${migratedProviders.join(", ")}` });
|
||||
}
|
||||
|
||||
// Create AuthStorage and ModelRegistry upfront
|
||||
const authStorage = await discoverAuthStorage();
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
@@ -564,11 +556,6 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
||||
debugStartup("main:initTheme2");
|
||||
time("initTheme");
|
||||
|
||||
// Show deprecation warnings in interactive mode
|
||||
if (isInteractive && deprecationWarnings.length > 0) {
|
||||
await showDeprecationWarnings(deprecationWarnings);
|
||||
}
|
||||
|
||||
let scopedModels: ScopedModel[] = [];
|
||||
const modelPatterns = parsedArgs.models ?? settings.get("enabledModels");
|
||||
const modelMatchPreferences = {
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
/**
|
||||
* One-time migrations that run on startup.
|
||||
*/
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
||||
import chalk from "chalk";
|
||||
import { getAgentDbPath, getAgentDir } from "./config";
|
||||
import { AgentStorage } from "./session/agent-storage";
|
||||
import type { AuthCredential } from "./session/auth-storage";
|
||||
|
||||
type SessionHeader = {
|
||||
type: "session";
|
||||
cwd: string;
|
||||
};
|
||||
|
||||
function isSessionHeader(value: unknown): value is SessionHeader {
|
||||
if (!value || typeof value !== "object") return false;
|
||||
const record = value as Record<string, unknown>;
|
||||
return record.type === "session" && typeof record.cwd === "string" && record.cwd.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate legacy oauth.json and settings.json apiKeys to agent.db.
|
||||
*
|
||||
* @returns Array of provider names that were migrated
|
||||
*/
|
||||
export async function migrateAuthToAgentDb(): Promise<string[]> {
|
||||
const agentDir = getAgentDir();
|
||||
const oauthPath = path.join(agentDir, "oauth.json");
|
||||
const settingsPath = path.join(agentDir, "settings.json");
|
||||
const storage = await AgentStorage.open(getAgentDbPath(agentDir));
|
||||
|
||||
const migrated: Record<string, AuthCredential[]> = {};
|
||||
const providers: string[] = [];
|
||||
|
||||
try {
|
||||
const oauth = await Bun.file(oauthPath).json();
|
||||
try {
|
||||
for (const [provider, cred] of Object.entries(oauth)) {
|
||||
if (storage.listAuthCredentials(provider).length > 0) {
|
||||
continue;
|
||||
}
|
||||
migrated[provider] = [{ type: "oauth", ...(cred as object) } as AuthCredential];
|
||||
providers.push(provider);
|
||||
}
|
||||
await fs.promises.rename(oauthPath, `${oauthPath}.migrated`);
|
||||
} catch (error) {
|
||||
logger.warn("Failed to migrate oauth.json", { path: oauthPath, error: String(error) });
|
||||
}
|
||||
} catch (err) {
|
||||
if (!isEnoent(err)) {
|
||||
logger.warn("Failed to read oauth.json", { path: oauthPath, error: String(err) });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const settings = await Bun.file(settingsPath).json();
|
||||
try {
|
||||
if (settings.apiKeys && typeof settings.apiKeys === "object") {
|
||||
for (const [provider, key] of Object.entries(settings.apiKeys)) {
|
||||
if (typeof key !== "string") continue;
|
||||
if (migrated[provider]) continue;
|
||||
if (storage.listAuthCredentials(provider).length > 0) continue;
|
||||
migrated[provider] = [{ type: "api_key", key }];
|
||||
providers.push(provider);
|
||||
}
|
||||
delete settings.apiKeys;
|
||||
await Bun.write(settingsPath, JSON.stringify(settings, null, 2));
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn("Failed to migrate settings.json apiKeys", { path: settingsPath, error: String(error) });
|
||||
}
|
||||
} catch (err) {
|
||||
if (!isEnoent(err)) {
|
||||
logger.warn("Failed to read settings.json", { path: settingsPath, error: String(err) });
|
||||
}
|
||||
}
|
||||
|
||||
for (const [provider, credentials] of Object.entries(migrated)) {
|
||||
storage.replaceAuthCredentialsForProvider(provider, credentials);
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate sessions from ~/.omp/agent/*.jsonl to proper session directories.
|
||||
*
|
||||
* Bug in v0.30.0: Sessions were saved to ~/.omp/agent/ instead of
|
||||
* ~/.omp/agent/sessions/<encoded-cwd>/. This migration moves them
|
||||
* to the correct location based on the cwd in their session header.
|
||||
*
|
||||
* See: https://github.com/badlogic/pi-mono/issues/320
|
||||
*/
|
||||
export async function migrateSessionsFromAgentRoot(): Promise<void> {
|
||||
const agentDir = getAgentDir();
|
||||
|
||||
// Find all .jsonl files directly in agentDir (not in subdirectories)
|
||||
let files: string[];
|
||||
try {
|
||||
const entries = await fs.promises.readdir(agentDir);
|
||||
files = entries.filter(f => f.endsWith(".jsonl")).map(f => path.join(agentDir, f));
|
||||
} catch (error) {
|
||||
logger.warn("Failed to read agent directory for session migration", { path: agentDir, error: String(error) });
|
||||
return;
|
||||
}
|
||||
|
||||
if (files.length === 0) return;
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
// Read first line to get session header
|
||||
let content: string;
|
||||
try {
|
||||
content = await Bun.file(file).text();
|
||||
} catch (err) {
|
||||
if (isEnoent(err)) continue;
|
||||
throw err;
|
||||
}
|
||||
const entries: unknown[] = Bun.JSONL.parse(content);
|
||||
const header = entries[0];
|
||||
if (!isSessionHeader(header)) continue;
|
||||
|
||||
const cwd = header.cwd;
|
||||
|
||||
// Compute the correct session directory (same encoding as session-manager.ts)
|
||||
const safePath = `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
||||
const correctDir = path.join(agentDir, "sessions", safePath);
|
||||
|
||||
// Create directory if needed
|
||||
await fs.promises.mkdir(correctDir, { recursive: true });
|
||||
|
||||
// Move the file
|
||||
const fileName = file.split("/").pop() || file.split("\\").pop();
|
||||
const newPath = path.join(correctDir, fileName!);
|
||||
|
||||
if (fs.existsSync(newPath)) continue; // Skip if target exists
|
||||
|
||||
await fs.promises.rename(file, newPath);
|
||||
} catch (error) {
|
||||
logger.warn("Failed to migrate session file", { path: file, error: String(error) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all migrations. Called once on startup.
|
||||
*
|
||||
* @param _cwd - Current working directory (reserved for future project-local migrations)
|
||||
* @returns Object with migration results
|
||||
*/
|
||||
export async function runMigrations(_cwd: string): Promise<{
|
||||
migratedAuthProviders: string[];
|
||||
deprecationWarnings: string[];
|
||||
}> {
|
||||
// Then: run data migrations
|
||||
const migratedAuthProviders = await migrateAuthToAgentDb();
|
||||
await migrateSessionsFromAgentRoot();
|
||||
|
||||
return { migratedAuthProviders, deprecationWarnings: [] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Display deprecation warnings to the user in interactive mode.
|
||||
*
|
||||
* @param warnings - Array of deprecation warning messages
|
||||
*/
|
||||
export async function showDeprecationWarnings(warnings: string[]): Promise<void> {
|
||||
console.log(chalk.yellow("\n⚠ Deprecation Warnings:"));
|
||||
for (const warning of warnings) {
|
||||
console.log(chalk.yellow(` • ${warning}`));
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as path from "node:path";
|
||||
import { Agent, type AgentEvent, type AgentMessage, type AgentTool, type ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
||||
import { type Message, type Model, supportsXhigh } from "@oh-my-pi/pi-ai";
|
||||
import type { Component } from "@oh-my-pi/pi-tui";
|
||||
@@ -6,7 +5,7 @@ import { $env, logger, postmortem } from "@oh-my-pi/pi-utils";
|
||||
import chalk from "chalk";
|
||||
import { loadCapability } from "./capability";
|
||||
import { type Rule, ruleCapability } from "./capability/rule";
|
||||
import { getAgentDir, getConfigDirPaths } from "./config";
|
||||
import { getAgentDbPath, getAgentDir } from "./config";
|
||||
import { ModelRegistry } from "./config/model-registry";
|
||||
import { formatModelString, parseModelString } from "./config/model-resolver";
|
||||
import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
|
||||
@@ -229,13 +228,10 @@ function getDefaultAgentDir(): string {
|
||||
* Reads from primary path first, then falls back to legacy paths (.pi, .claude).
|
||||
*/
|
||||
export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir()): Promise<AuthStorage> {
|
||||
const primaryPath = path.join(agentDir, "auth.json");
|
||||
// Get all auth.json paths (user-level only), excluding the primary
|
||||
const allPaths = getConfigDirPaths("auth.json", { project: false });
|
||||
const fallbackPaths = allPaths.filter(p => p !== primaryPath);
|
||||
logger.debug("discoverAuthStorage", { agentDir, primaryPath, allPaths, fallbackPaths });
|
||||
const dbPath = getAgentDbPath(agentDir);
|
||||
logger.debug("discoverAuthStorage", { agentDir, dbPath });
|
||||
|
||||
const storage = await AuthStorage.create(primaryPath);
|
||||
const storage = await AuthStorage.create(dbPath);
|
||||
await storage.reload();
|
||||
return storage;
|
||||
}
|
||||
|
||||
@@ -329,6 +329,7 @@ export class AgentSession {
|
||||
private _streamingEditAbortTriggered = false;
|
||||
private _streamingEditCheckedLineCounts = new Map<string, number>();
|
||||
private _streamingEditFileCache = new Map<string, string>();
|
||||
private _promptInFlight = false;
|
||||
|
||||
constructor(config: AgentSessionConfig) {
|
||||
this.agent = config.agent;
|
||||
@@ -693,7 +694,11 @@ export class AgentSession {
|
||||
.map(line => line.slice(1));
|
||||
if (removedLines.length > 0) {
|
||||
const resolvedPath = resolveToCwd(path, this.sessionManager.getCwd());
|
||||
const cachedContent = this._streamingEditFileCache.get(resolvedPath);
|
||||
let cachedContent = this._streamingEditFileCache.get(resolvedPath);
|
||||
if (cachedContent === undefined) {
|
||||
this._ensureFileCache(resolvedPath);
|
||||
cachedContent = this._streamingEditFileCache.get(resolvedPath);
|
||||
}
|
||||
if (cachedContent !== undefined) {
|
||||
const missing = removedLines.find(line => !cachedContent.includes(normalizeToLF(line)));
|
||||
if (missing) {
|
||||
@@ -898,7 +903,7 @@ export class AgentSession {
|
||||
|
||||
/** Whether agent is currently streaming a response */
|
||||
get isStreaming(): boolean {
|
||||
return this.agent.state.isStreaming;
|
||||
return this.agent.state.isStreaming || this._promptInFlight;
|
||||
}
|
||||
|
||||
/** Current retry attempt (0 if not retrying) */
|
||||
@@ -1246,95 +1251,100 @@ export class AgentSession {
|
||||
expandedText: string,
|
||||
options?: Pick<PromptOptions, "toolChoice" | "images">,
|
||||
): Promise<void> {
|
||||
// Flush any pending bash messages before the new prompt
|
||||
this._flushPendingBashMessages();
|
||||
this._flushPendingPythonMessages();
|
||||
this._promptInFlight = true;
|
||||
try {
|
||||
// Flush any pending bash messages before the new prompt
|
||||
this._flushPendingBashMessages();
|
||||
this._flushPendingPythonMessages();
|
||||
|
||||
// Reset todo reminder count on new user prompt
|
||||
this._todoReminderCount = 0;
|
||||
// Reset todo reminder count on new user prompt
|
||||
this._todoReminderCount = 0;
|
||||
|
||||
// Validate model
|
||||
if (!this.model) {
|
||||
throw new Error(
|
||||
"No model selected.\n\n" +
|
||||
`Use /login, set an API key environment variable, or create ${getAgentDbPath()}\n\n` +
|
||||
"Then use /model to select a model.",
|
||||
);
|
||||
}
|
||||
// Validate model
|
||||
if (!this.model) {
|
||||
throw new Error(
|
||||
"No model selected.\n\n" +
|
||||
`Use /login, set an API key environment variable, or create ${getAgentDbPath()}\n\n` +
|
||||
"Then use /model to select a model.",
|
||||
);
|
||||
}
|
||||
|
||||
// Validate API key
|
||||
const apiKey = await this._modelRegistry.getApiKey(this.model, this.sessionId);
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
`No API key found for ${this.model.provider}.\n\n` +
|
||||
`Use /login, set an API key environment variable, or create ${getAgentDbPath()}`,
|
||||
);
|
||||
}
|
||||
// Validate API key
|
||||
const apiKey = await this._modelRegistry.getApiKey(this.model, this.sessionId);
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
`No API key found for ${this.model.provider}.\n\n` +
|
||||
`Use /login, set an API key environment variable, or create ${getAgentDbPath()}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if we need to compact before sending (catches aborted responses)
|
||||
const lastAssistant = this._findLastAssistantMessage();
|
||||
if (lastAssistant) {
|
||||
await this._checkCompaction(lastAssistant, false);
|
||||
}
|
||||
// Check if we need to compact before sending (catches aborted responses)
|
||||
const lastAssistant = this._findLastAssistantMessage();
|
||||
if (lastAssistant) {
|
||||
await this._checkCompaction(lastAssistant, false);
|
||||
}
|
||||
|
||||
// Build messages array (custom messages if any, then user message)
|
||||
const messages: AgentMessage[] = [];
|
||||
const planReferenceMessage = await this._buildPlanReferenceMessage?.();
|
||||
if (planReferenceMessage) {
|
||||
messages.push(planReferenceMessage);
|
||||
}
|
||||
const planModeMessage = await this._buildPlanModeMessage();
|
||||
if (planModeMessage) {
|
||||
messages.push(planModeMessage);
|
||||
}
|
||||
// Build messages array (custom messages if any, then user message)
|
||||
const messages: AgentMessage[] = [];
|
||||
const planReferenceMessage = await this._buildPlanReferenceMessage?.();
|
||||
if (planReferenceMessage) {
|
||||
messages.push(planReferenceMessage);
|
||||
}
|
||||
const planModeMessage = await this._buildPlanModeMessage();
|
||||
if (planModeMessage) {
|
||||
messages.push(planModeMessage);
|
||||
}
|
||||
|
||||
messages.push(message);
|
||||
messages.push(message);
|
||||
|
||||
// Inject any pending "nextTurn" messages as context alongside the user message
|
||||
for (const msg of this._pendingNextTurnMessages) {
|
||||
messages.push(msg);
|
||||
}
|
||||
this._pendingNextTurnMessages = [];
|
||||
// Inject any pending "nextTurn" messages as context alongside the user message
|
||||
for (const msg of this._pendingNextTurnMessages) {
|
||||
messages.push(msg);
|
||||
}
|
||||
this._pendingNextTurnMessages = [];
|
||||
|
||||
// Auto-read @filepath mentions
|
||||
const fileMentions = extractFileMentions(expandedText);
|
||||
if (fileMentions.length > 0) {
|
||||
const fileMentionMessages = await generateFileMentionMessages(fileMentions, this.sessionManager.getCwd(), {
|
||||
autoResizeImages: this.settings.get("images.autoResize"),
|
||||
});
|
||||
messages.push(...fileMentionMessages);
|
||||
}
|
||||
// Auto-read @filepath mentions
|
||||
const fileMentions = extractFileMentions(expandedText);
|
||||
if (fileMentions.length > 0) {
|
||||
const fileMentionMessages = await generateFileMentionMessages(fileMentions, this.sessionManager.getCwd(), {
|
||||
autoResizeImages: this.settings.get("images.autoResize"),
|
||||
});
|
||||
messages.push(...fileMentionMessages);
|
||||
}
|
||||
|
||||
// Emit before_agent_start extension event
|
||||
if (this._extensionRunner) {
|
||||
const result = await this._extensionRunner.emitBeforeAgentStart(
|
||||
expandedText,
|
||||
options?.images,
|
||||
this._baseSystemPrompt,
|
||||
);
|
||||
if (result?.messages) {
|
||||
for (const msg of result.messages) {
|
||||
messages.push({
|
||||
role: "custom",
|
||||
customType: msg.customType,
|
||||
content: msg.content,
|
||||
display: msg.display,
|
||||
details: msg.details,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
// Emit before_agent_start extension event
|
||||
if (this._extensionRunner) {
|
||||
const result = await this._extensionRunner.emitBeforeAgentStart(
|
||||
expandedText,
|
||||
options?.images,
|
||||
this._baseSystemPrompt,
|
||||
);
|
||||
if (result?.messages) {
|
||||
for (const msg of result.messages) {
|
||||
messages.push({
|
||||
role: "custom",
|
||||
customType: msg.customType,
|
||||
content: msg.content,
|
||||
display: msg.display,
|
||||
details: msg.details,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (result?.systemPrompt !== undefined) {
|
||||
this.agent.setSystemPrompt(result.systemPrompt);
|
||||
} else {
|
||||
this.agent.setSystemPrompt(this._baseSystemPrompt);
|
||||
}
|
||||
}
|
||||
|
||||
if (result?.systemPrompt !== undefined) {
|
||||
this.agent.setSystemPrompt(result.systemPrompt);
|
||||
} else {
|
||||
this.agent.setSystemPrompt(this._baseSystemPrompt);
|
||||
}
|
||||
const agentPromptOptions = options?.toolChoice ? { toolChoice: options.toolChoice } : undefined;
|
||||
await this.agent.prompt(messages, agentPromptOptions);
|
||||
await this.waitForRetry();
|
||||
} finally {
|
||||
this._promptInFlight = false;
|
||||
}
|
||||
|
||||
const agentPromptOptions = options?.toolChoice ? { toolChoice: options.toolChoice } : undefined;
|
||||
await this.agent.prompt(messages, agentPromptOptions);
|
||||
await this.waitForRetry();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -137,7 +137,7 @@ class AuthStorageUsageCache implements UsageCache {
|
||||
|
||||
/**
|
||||
* Credential storage backed by agent.db.
|
||||
* Reads from SQLite and migrates legacy auth.json paths.
|
||||
* Reads from SQLite (agent.db).
|
||||
*/
|
||||
export class AuthStorage {
|
||||
private static readonly defaultBackoffMs = 60_000; // Default backoff when no reset time available
|
||||
@@ -159,7 +159,6 @@ export class AuthStorage {
|
||||
private fallbackResolver?: (provider: string) => string | undefined;
|
||||
|
||||
private constructor(
|
||||
private dbPath: string,
|
||||
private storage: AgentStorage,
|
||||
options: AuthStorageOptions = {},
|
||||
) {
|
||||
@@ -181,7 +180,7 @@ export class AuthStorage {
|
||||
*/
|
||||
static async create(dbPath: string, options: AuthStorageOptions = {}): Promise<AuthStorage> {
|
||||
const storage = await AgentStorage.open(dbPath);
|
||||
return new AuthStorage(dbPath, storage, options);
|
||||
return new AuthStorage(storage, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +192,6 @@ export class AuthStorage {
|
||||
const storage = await AgentStorage.open(dbPath);
|
||||
|
||||
const instance = Object.create(AuthStorage.prototype) as AuthStorage;
|
||||
instance.dbPath = dbPath;
|
||||
instance.storage = storage;
|
||||
instance.data = new Map();
|
||||
instance.runtimeOverrides = new Map();
|
||||
@@ -252,7 +250,6 @@ export class AuthStorage {
|
||||
return {
|
||||
credentials,
|
||||
runtimeOverrides: Object.keys(runtimeOverrides).length > 0 ? runtimeOverrides : undefined,
|
||||
dbPath: this.dbPath,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -281,7 +278,7 @@ export class AuthStorage {
|
||||
|
||||
/**
|
||||
* Reload credentials from agent.db.
|
||||
* Migrates legacy auth.json/settings.json on first load.
|
||||
* Reloads credentials from the database.
|
||||
*/
|
||||
async reload(): Promise<void> {
|
||||
const records = this.storage.listAuthCredentials();
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
* 3. OAuth credentials in ~/.omp/agent/agent.db (with expiry check)
|
||||
* 4. ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL fallback
|
||||
*/
|
||||
import * as path from "node:path";
|
||||
import { buildAnthropicHeaders as buildProviderAnthropicHeaders, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
||||
import { $env, logger } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDbPath, getConfigDirPaths } from "../../config";
|
||||
import { AgentStorage } from "../../session/agent-storage";
|
||||
import type { AuthCredential, AuthCredentialEntry, AuthStorageData } from "../../session/auth-storage";
|
||||
import type { AuthCredential } from "../../session/auth-storage";
|
||||
import type { AnthropicAuthConfig, AnthropicOAuthCredential, ModelsJson } from "./types";
|
||||
|
||||
const DEFAULT_BASE_URL = "https://api.anthropic.com";
|
||||
@@ -59,26 +58,8 @@ function toAnthropicOAuthCredential(credential: AuthCredential): AnthropicOAuthC
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeAuthEntry(entry: AuthCredentialEntry | undefined): AuthCredential[] {
|
||||
if (!entry) return [];
|
||||
return Array.isArray(entry) ? entry : [entry];
|
||||
}
|
||||
|
||||
async function readLegacyAnthropicOAuthCredentials(configDir: string): Promise<AnthropicOAuthCredential[]> {
|
||||
const authJson = await readJson<AuthStorageData>(path.join(configDir, "auth.json"));
|
||||
if (!authJson) return [];
|
||||
const entry = authJson.anthropic as AuthCredentialEntry | undefined;
|
||||
const credentials = normalizeAuthEntry(entry);
|
||||
const results: AnthropicOAuthCredential[] = [];
|
||||
for (const credential of credentials) {
|
||||
const mapped = toAnthropicOAuthCredential(credential);
|
||||
if (mapped) results.push(mapped);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads Anthropic OAuth credentials from agent.db, migrating from legacy auth.json if needed.
|
||||
* Reads Anthropic OAuth credentials from agent.db.
|
||||
* @param configDir - Path to the config directory containing agent.db
|
||||
* @returns Array of valid Anthropic OAuth credentials
|
||||
*/
|
||||
@@ -93,10 +74,6 @@ async function readAnthropicOAuthCredentials(configDir: string): Promise<Anthrop
|
||||
}
|
||||
}
|
||||
|
||||
if (credentials.length === 0) {
|
||||
return readLegacyAnthropicOAuthCredentials(configDir);
|
||||
}
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
@@ -125,7 +102,7 @@ export async function findAnthropicAuth(): Promise<AnthropicAuthConfig | null> {
|
||||
|
||||
// 2. Provider with api="anthropic-messages" in models.json (check all config dirs)
|
||||
for (const configDir of configDirs) {
|
||||
const modelsJson = await readJson<ModelsJson>(path.join(configDir, "models.json"));
|
||||
const modelsJson = await readJson<ModelsJson>(`${configDir}/models.json`);
|
||||
if (modelsJson?.providers) {
|
||||
// First pass: look for providers with actual API keys
|
||||
for (const [_name, provider] of Object.entries(modelsJson.providers)) {
|
||||
|
||||
@@ -88,7 +88,7 @@ export interface ModelsJson {
|
||||
>;
|
||||
}
|
||||
|
||||
/** auth.json structure for OAuth credentials */
|
||||
/** OAuth credential for Anthropic API access */
|
||||
export interface AnthropicOAuthCredential {
|
||||
type: "oauth";
|
||||
access: string;
|
||||
@@ -97,12 +97,6 @@ export interface AnthropicOAuthCredential {
|
||||
expires: number;
|
||||
}
|
||||
|
||||
export type AnthropicAuthJsonEntry = AnthropicOAuthCredential | AnthropicOAuthCredential[];
|
||||
|
||||
export interface AuthJson {
|
||||
anthropic?: AnthropicAuthJsonEntry;
|
||||
}
|
||||
|
||||
/** Anthropic API response types */
|
||||
export interface AnthropicSearchResult {
|
||||
type: "web_search_result";
|
||||
|
||||
@@ -64,8 +64,8 @@ describe.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))("AgentSession branching", () =>
|
||||
|
||||
sessionManager = noSession ? SessionManager.inMemory() : SessionManager.create(tempDir);
|
||||
const settings = Settings.isolated();
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
const modelRegistry = new ModelRegistry(authStorage, tempDir);
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
const modelRegistry = new ModelRegistry(authStorage, path.join(tempDir, "models.yml"));
|
||||
|
||||
session = new AgentSession({
|
||||
agent,
|
||||
|
||||
@@ -68,7 +68,7 @@ describe.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))("AgentSession compaction e2e",
|
||||
|
||||
sessionManager = inMemory ? SessionManager.inMemory() : SessionManager.create(tempDir);
|
||||
const settings = Settings.isolated({ "compaction.keepRecentTokens": 1 });
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
|
||||
session = new AgentSession({
|
||||
|
||||
@@ -89,8 +89,8 @@ describe("AgentSession concurrent prompt guard", () => {
|
||||
|
||||
const sessionManager = SessionManager.inMemory();
|
||||
const settings = Settings.isolated();
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
const modelRegistry = new ModelRegistry(authStorage, tempDir);
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
const modelRegistry = new ModelRegistry(authStorage, path.join(tempDir, "models.yml"));
|
||||
authStorage.setRuntimeApiKey("anthropic", "test-key");
|
||||
|
||||
session = new AgentSession({
|
||||
@@ -179,8 +179,8 @@ describe("AgentSession concurrent prompt guard", () => {
|
||||
|
||||
const sessionManager = SessionManager.inMemory();
|
||||
const settings = Settings.isolated();
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
const modelRegistry = new ModelRegistry(authStorage, tempDir);
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
const modelRegistry = new ModelRegistry(authStorage, path.join(tempDir, "models.yml"));
|
||||
authStorage.setRuntimeApiKey("anthropic", "test-key");
|
||||
|
||||
session = new AgentSession({
|
||||
|
||||
@@ -104,7 +104,7 @@ describe.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))("Compaction hooks", () => {
|
||||
|
||||
const sessionManager = SessionManager.create(tempDir);
|
||||
const settings = Settings.isolated();
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
|
||||
hookRunner = new HookRunner(hooks, tempDir, sessionManager, modelRegistry);
|
||||
|
||||
@@ -76,7 +76,7 @@ describe.skipIf(!HAS_ANTIGRAVITY_AUTH)("Compaction with thinking models (Antigra
|
||||
const sessionManager = SessionManager.inMemory();
|
||||
const settings = Settings.isolated();
|
||||
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
|
||||
session = new AgentSession({
|
||||
@@ -179,7 +179,7 @@ describe.skipIf(!HAS_ANTHROPIC_AUTH)("Compaction with thinking models (Anthropic
|
||||
const sessionManager = SessionManager.inMemory();
|
||||
const settings = Settings.isolated();
|
||||
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
|
||||
session = new AgentSession({
|
||||
|
||||
@@ -23,7 +23,7 @@ describe("ExtensionRunner", () => {
|
||||
extensionsDir = path.join(tempDir.path(), ".omp", "extensions");
|
||||
fs.mkdirSync(extensionsDir, { recursive: true });
|
||||
sessionManager = SessionManager.inMemory();
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir.path(), "auth.json"));
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir.path(), "testauth.db"));
|
||||
modelRegistry = new ModelRegistry(authStorage);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ describe("ModelRegistry", () => {
|
||||
tempDir = path.join(os.tmpdir(), `pi-test-model-registry-${Snowflake.next()}`);
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
modelsJsonPath = path.join(tempDir, "models.json");
|
||||
authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -93,9 +93,9 @@ async function createSession(tempDir: string, streamFn: Agent["streamFn"], tool:
|
||||
|
||||
const sessionManager = SessionManager.inMemory(tempDir);
|
||||
const settings = Settings.isolated({ "edit.streamingAbort": true });
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
authStorage.setRuntimeApiKey("anthropic", "test-key");
|
||||
const modelRegistry = new ModelRegistry(authStorage, tempDir);
|
||||
const modelRegistry = new ModelRegistry(authStorage, path.join(tempDir, "models.yml"));
|
||||
|
||||
return new AgentSession({
|
||||
agent,
|
||||
|
||||
@@ -99,8 +99,8 @@ export async function createTestSession(options: TestSessionOptions = {}): Promi
|
||||
const sessionManager = options.inMemory ? SessionManager.inMemory() : SessionManager.create(tempDir);
|
||||
const settings = Settings.isolated(options.settingsOverrides);
|
||||
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "auth.json"));
|
||||
const modelRegistry = new ModelRegistry(authStorage, tempDir);
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir, "testauth.db"));
|
||||
const modelRegistry = new ModelRegistry(authStorage, path.join(tempDir, "models.yml"));
|
||||
const session = new AgentSession({
|
||||
agent,
|
||||
sessionManager,
|
||||
|
||||
Reference in New Issue
Block a user