Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions assistant/src/daemon/approval-generators.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
import { loadConfig } from "../config/loader.js";
import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
import { resolveDefaultProvider } from "../providers/connection-resolution.js";
import { listProviders } from "../providers/registry.js";
import type { Provider } from "../providers/types.js";
import {
APPROVAL_COPY_MAX_TOKENS,
Expand All @@ -16,6 +18,7 @@ import type {
ApprovalConversationResult,
ApprovalCopyGenerator,
} from "../runtime/http-types.js";
import { ProviderNotConfiguredError } from "../util/errors.js";

// ---------------------------------------------------------------------------
// Approval conversation generator constants
Expand Down Expand Up @@ -142,14 +145,13 @@ export function createApprovalConversationGenerator(): ApprovalConversationGener
// Connection-aware default + per-call routing. `resolveDefaultProvider`
// throws `ConnectionResolutionError` on hard config errors (missing /
// unknown / mismatched connection) and returns null on soft credential
// failures (vault miss, transient auth) — we treat null as "no
// provider available" and throw a domain-specific error below. We do
// not pre-gate on `listProviders()` because the default provider lives
// behind a `provider_connection` and never appears in the registry's
// initialization-time provider list.
// failures (missing credential, platform auth unavailable).
const baseProvider = await resolveDefaultProvider(config);
if (!baseProvider) {
throw new Error("No provider available for approval conversation");
const resolved = resolveCallSiteConfig("mainAgent", config.llm);
throw new ProviderNotConfiguredError(resolved.provider, listProviders(), {
connectionName: resolved.provider_connection,
});
}
const provider = wrapWithCallSiteRouting(baseProvider, config);

Expand Down
15 changes: 11 additions & 4 deletions assistant/src/daemon/conversation-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import { buildSystemPrompt } from "../prompts/system-prompt.js";
import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
import { resolveDefaultProvider } from "../providers/connection-resolution.js";
import { RateLimitProvider } from "../providers/ratelimit.js";
import { listProviders } from "../providers/registry.js";
import { getSubagentManager } from "../subagent/index.js";
import { ProviderNotConfiguredError } from "../util/errors.js";
import { getSandboxWorkingDir } from "../util/platform.js";
import { Conversation } from "./conversation.js";
import type { ConversationEvictor } from "./conversation-evictor.js";
Expand Down Expand Up @@ -226,12 +228,17 @@ export async function getOrCreateConversation(
// Connection-aware default-provider resolution. Throws
// `ConnectionResolutionError` when the default profile's
// `provider_connection` is unset / unknown / mismatched (config
// bugs). Returns null on soft credential failures (handled below
// as "default provider not registered").
// bugs). Returns null on soft credential failures (missing
// credential, platform auth unavailable).
const baseProvider = await resolveDefaultProvider(config);
if (!baseProvider) {
throw new Error(
`Conversation: default provider '${resolveCallSiteConfig("mainAgent", config.llm).provider}' is not registered`,
const resolved = resolveCallSiteConfig("mainAgent", config.llm);
throw new ProviderNotConfiguredError(
resolved.provider,
listProviders(),
{
connectionName: resolved.provider_connection,
},
);
}
// Per-call `callSite` routing layered on top, with connection-awareness
Expand Down
7 changes: 7 additions & 0 deletions assistant/src/providers/connection-resolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ export async function tryResolveProviderForConnectionName(
`provider_connection "${connectionName}" has provider="${connection.provider}" but resolving profile declared provider="${expectedProvider}" — set the profile's provider_connection to a row matching its provider`,
);
}
if (connection.status === "disabled") {
log.debug(
{ connectionName },
"provider_connection is disabled — returning null",
);
return null;
}
// `resolveProviderFromConnection` reaches into auth resolution (credential
// reads, managed-proxy context). A transient failure there is a soft
// miss — log and return null so the caller can treat it the same as
Expand Down
14 changes: 8 additions & 6 deletions assistant/src/subagent/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
import { resolveDefaultProvider } from "../providers/connection-resolution.js";
import { RateLimitProvider } from "../providers/ratelimit.js";
import { listProviders } from "../providers/registry.js";
import { createAbortReason } from "../util/abort-reasons.js";
import { ProviderNotConfiguredError } from "../util/errors.js";
import { getLogger } from "../util/logger.js";
import { getSandboxWorkingDir } from "../util/platform.js";
import {
Expand Down Expand Up @@ -186,14 +188,14 @@ export class SubagentManager {
// Connection-aware default-provider resolution. Throws
// `ConnectionResolutionError` if `llm.default.provider_connection` is
// unset or the connection row is missing/mismatched (config bugs).
// Returns null on soft credential failures (vault miss, transient
// auth) — handled below as "no provider available". Per-call
// `callSite` routing is layered next.
// Returns null on soft credential failures (missing credential,
// platform auth unavailable).
const baseProvider = await resolveDefaultProvider(appConfig);
if (!baseProvider) {
throw new Error(
`Subagent: default provider '${resolveCallSiteConfig("mainAgent", appConfig.llm).provider}' is not registered`,
);
const resolved = resolveCallSiteConfig("mainAgent", appConfig.llm);
throw new ProviderNotConfiguredError(resolved.provider, listProviders(), {
connectionName: resolved.provider_connection,
});
}
// Per-call `options.config.callSite` (e.g. `subagentSpawn`) can resolve
// to a profile that differs from `llm.default`. The shared wrapper
Expand Down
Loading