import type { ConfigFilePort } from "../app/config-storage.js"; import { readScopedConfig } from "../app/config-storage.js"; import type { ConfigDiagnostic } from "../domain/config-diagnostics.js"; import { resolveConfigDetailed } from "../domain/config-normalization.js"; export interface SessionFlagReader { getFlag(name: string): unknown; } export interface SessionAuthorization { core: boolean; assistant: boolean; specialBlocks: boolean; tools: boolean; ascii: boolean; } export interface ProductPolicy { readonly corePatchGate: "omitted" | "allow" | "deny"; } export interface ConfigSourceAdapter { setSession(cwd: string, trusted: boolean): void; load(): Promise<{ config: unknown; diagnostics: readonly ConfigDiagnostic[]; sources: Readonly>; rawSources?: { defaults?: unknown; global?: unknown; project?: unknown; environment?: Record; }; productPolicy?: ProductPolicy; }>; } function sessionOverrides(_pi: SessionFlagReader): Record { return {}; } /** Product policy is deny-only: any explicit false wins; true never grants Tier C. */ export function resolveProductGate( global: unknown, project: unknown, session: unknown, projectTrusted: boolean, ): ProductPolicy { const values = [global, projectTrusted ? project : undefined, session]; for (const value of values) { if (!value || typeof value !== "object" || Array.isArray(value)) continue; const compatibility = (value as Record).compatibility; if (!compatibility || typeof compatibility !== "object" || Array.isArray(compatibility)) continue; if (!Object.hasOwn(compatibility, "allowCorePatches")) continue; const leaf = (compatibility as Record).allowCorePatches; if (leaf === false) return { corePatchGate: "deny" }; } return { corePatchGate: "omitted" }; } export function readSessionAuthorization(pi: SessionFlagReader): SessionAuthorization { return { core: pi.getFlag("pi-style-core-patches") === true, assistant: pi.getFlag("pi-style-message-assistant") === true, specialBlocks: pi.getFlag("pi-style-message-special-blocks") === true, tools: pi.getFlag("pi-style-tools") === true, ascii: pi.getFlag("pi-style-ascii") === true, }; } export function createConfigSourceAdapter( pi: SessionFlagReader, port: ConfigFilePort, paths: (cwd: string) => { globalPath: string; projectPath: string }, ): ConfigSourceAdapter { let trusted = true; let currentCwd = process.cwd(); return { setSession(nextCwd, nextTrusted) { currentCwd = nextCwd; trusted = nextTrusted; }, async load() { const storage = paths(currentCwd); const global = await readScopedConfig(port, storage.globalPath); const project = trusted ? await readScopedConfig(port, storage.projectPath) : undefined; const resolved = resolveConfigDetailed({ global: global.value, project: project?.value, projectTrusted: trusted, environment: process.env, session: sessionOverrides(pi), }); const productPolicy = resolveProductGate(global.value, project?.value, sessionOverrides(pi), trusted); return { config: resolved.config, diagnostics: [...global.diagnostics, ...(project?.diagnostics ?? []), ...resolved.diagnostics], sources: resolved.sources, rawSources: { global: global.value, project: project?.value, environment: process.env }, productPolicy, }; }, }; } export { sessionOverrides };