/** * 控制面(A4):身份、策略、凭据、确认、审计五块绑在一起的入口。 * * 它们不是五个独立功能,而是同一条链上的五道关: * 身份(以谁的身份调) → 策略(这次调用允不允许) → 凭据(拿什么去调) * → 确认(写操作要不要人点头) → 审计(发生后留痕) * 任何一块缺失都会让「可控」变成空话,所以绑在一起注入 Core。 * * 这一层与引擎无关:Pi 还是 Codex,走的都是 Core.invoke 这一个出口。 */ import type { Capability } from '@aps/provider-sdk'; import type { AuditLog } from '../audit/log.js'; import type { CredentialBroker } from '../credentials/broker.js'; import type { IdentityContext } from '../identity/context.js'; import type { PendingActionStore } from '../approval/store.js'; import type { PolicyEngine } from '../policy/engine.js'; import { createRulePolicy } from '../policy/engine.js'; export interface ControlPlaneOptions { identity: IdentityContext; credentials: CredentialBroker; approvals: PendingActionStore; audit: AuditLog; /** 能力可见性(A3.5):能力清单与这里必须是同一次过滤 */ isVisible: (capability: Capability, subject: { userId: string; tenantId: string }) => boolean; policy?: PolicyEngine; /** 确认卡有效期;超过就失效,必须重新发起 */ approvalTtlMs?: number; now?: () => number; } export interface ControlPlane { identity: IdentityContext; credentials: CredentialBroker; approvals: PendingActionStore; audit: AuditLog; policy: PolicyEngine; isVisible: (capability: Capability, subject: { userId: string; tenantId: string }) => boolean; approvalTtlMs: number; now(): number; } const DEFAULT_APPROVAL_TTL_MS = 15 * 60_000; export function createControlPlane(options: ControlPlaneOptions): ControlPlane { return { identity: options.identity, credentials: options.credentials, approvals: options.approvals, audit: options.audit, policy: options.policy ?? createRulePolicy(), isVisible: options.isVisible, approvalTtlMs: options.approvalTtlMs ?? DEFAULT_APPROVAL_TTL_MS, now: options.now ?? Date.now, }; }