feat(agent): per-upstream pi Models with isolated credential stores

- PiUpstreamClient: one Models per upstream holding only its provider; credential
  store adapter maps to that upstream's secret, so several accounts of one provider
  coexist; host env is only read for authKind none (ambient cloud creds)
- provider catalog from builtinProviders() plus static oauth modes, env fields and
  subscription terms notes; openai-compatible custom endpoints
- fake upstream provider with mock OAuth (browser paste / device approval)
- PiAgentRuntime accepts an upstream; thinking level clamped to the model

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 02:23:09 +08:00
1 parent b9dbd54e37
commit 743fe2486d
8 files changed
+1085 -5

No files matched your search

+20 -2
View File
@@ -27,11 +27,22 @@ import {
import { type ResolvedAgentSettings, resolveAgentSettings } from "../settings.ts";
import { compactionFromSettings, type PiModelResolver, resolveModelFromSettings } from "./model.ts";
import { openRunSession } from "./session.ts";
import {
PiUpstreamClient,
type UpstreamClientOptions,
type UpstreamCredentialStore,
type UpstreamSpec,
} from "./upstream.ts";
export interface PiAgentRuntimeOptions {
registry: CapabilityRegistry;
/** 默认按设置构造真实上游;测试用 FakeModelProvider 覆盖 */
resolveModel?: PiModelResolver;
/**
* 按上游实例装配(W7-A):独立 Models + 只映射本上游 secret 的凭据存储(OAuth 按需刷新、锁内写回)。
* 给了 resolveModel 时以 resolveModel 为准。
*/
upstream?: { spec: UpstreamSpec; credentials: UpstreamCredentialStore; options?: UpstreamClientOptions };
/**
* 每个事件产生时回调(server 用它写 agent_run_event 再推 SSE)。
* 同一 run 内按 seq 顺序串行调用(前一个 Promise 结束才调下一个);回调抛错只记入 onEventError,不影响运行。
@@ -56,7 +67,14 @@ export class PiAgentRuntime implements AgentRuntimePort {
const ctx = BACKGROUND_CONTEXT;
const settings = resolveAgentSettings(spec.settings);
const capabilities = this.options.registry.resolve(spec.capabilities);
const { models, model } = (this.options.resolveModel ?? resolveModelFromSettings)(settings);
const upstream = this.options.upstream;
const resolved = this.options.resolveModel
? await this.options.resolveModel(settings)
: upstream
? await new PiUpstreamClient(upstream.spec, upstream.credentials, upstream.options).resolveRunModel(settings)
: resolveModelFromSettings(settings);
const { models, model } = resolved;
const thinkingLevel = resolved.thinkingLevel ?? settings.thinkingLevel;
const opened = await openRunSession(spec.sessionDir, spec.runId, ctx);
const run = new PiAgentRun(spec, settings, capabilities, this.options, opened.path, opened.repo);
@@ -69,7 +87,7 @@ export class PiAgentRuntime implements AgentRuntimePort {
systemPrompt: spec.systemPrompt,
tools: capabilities.map((c) => run.toTool(c)),
toolContext: {},
thinkingLevel: settings.thinkingLevel,
thinkingLevel,
compaction: compactionFromSettings(settings, model.contextWindow),
retry: { enabled: settings.maxRetries > 0, maxRetries: settings.maxRetries, baseDelayMs: 1000 },
streamOptions: { timeoutMs: settings.requestTimeoutMs, maxRetries: settings.maxRetries },