/** * Packaged engine method-material seam (#356 T1 / ADR 0069 / #376). * Engine names are owner pool-directive labels — not a closed material catalog. * Material body is optional data for the LLM, not a code contract. * Only path-safety syntax is checked at real I/O seams. */ import { existsSync, readdirSync } from "node:fs"; import { join } from "node:path"; const ENGINE_MATERIAL_RELATIVE_ROOT = "resources/engines" as const; export type EngineSessionMaterial = Readonly<{ name: string; /** Present only when a packaged notes file exists for this name. */ materialPath?: string; /** * Optional owner pool-directive model id for multi-model engines (#883). * Opaque pass-through coordinate for the seat — not validated against a catalog. */ model?: string; }>; /** Project engine name + optional model for env / request / material spreads. */ export function pickEngineAxis(source: { readonly engine?: string | undefined; readonly engineModel?: string | undefined; }): { engine?: string; engineModel?: string } { return { ...(source.engine === undefined ? {} : { engine: source.engine }), ...(source.engineModel === undefined ? {} : { engineModel: source.engineModel }), }; } /** Non-empty trimmed opaque engine model label; empty/whitespace rejected. */ export function assertLegalEngineModel(model: string): string { if (typeof model !== "string" || model.trim() === "" || model.trim() !== model) { throw new Error(`illegal engine model: ${JSON.stringify(model)}`); } return model; } /** Non-empty, trimmed; reject only real path hazards at the I/O seam. */ export function isEngineNameSyntax(name: string): boolean { if (typeof name !== "string") return false; if (name.length === 0 || name.trim() !== name) return false; // Exact "." / ".." are directory aliases; consecutive dots inside a label are not. if (name === "." || name === "..") return false; if (name.includes("/") || name.includes("\\") || name.includes("\0")) return false; return true; } export function engineMaterialRelativeDirectory(): string { return ENGINE_MATERIAL_RELATIVE_ROOT; } export function resolveEngineMaterialDirectory(packageRoot: string): string { return join(packageRoot, ENGINE_MATERIAL_RELATIVE_ROOT); } /** * Enumerate packaged engine notes stems (discovery only — not a legal-name gate). * Only `*.md` stems that pass name syntax are listed. */ export function listEngineMaterialNames(packageRoot: string): readonly string[] { const dir = resolveEngineMaterialDirectory(packageRoot); if (!existsSync(dir)) return Object.freeze([]); const names = readdirSync(dir) .filter((entry) => entry.endsWith(".md")) .map((entry) => entry.slice(0, -".md".length)) .filter((stem) => isEngineNameSyntax(stem)) .sort(); return Object.freeze([...names]); } /** * Build the packaged notes path for a syntax-legal engine name. * Does not require the file to exist (material is optional data). */ export function resolveEngineMaterialPath( packageRoot: string, name: string, ): string { const legal = assertLegalEngineName(name); return join(resolveEngineMaterialDirectory(packageRoot), `${legal}.md`); } /** * Path-safety syntax gate for engine labels at real I/O seams. * Returns the canonical name on success; throws Error on illegal syntax. * Does not consult any material catalog (ADR 0069: 引擎权威是 owner 池令;能力通用可插拔). */ export function assertLegalEngineName(name: string): string { if (!isEngineNameSyntax(name)) { throw new Error(`illegal engine name: ${name}`); } return name; } /** * Resolve optional engine options into session material coordinates. * No engine → undefined (caller keeps default prompt bytes). * Engine with packaged notes → name + absolute material path. * Engine without notes → name only (pass-through; no warning). */ export function engineSessionMaterialFromOptions(options: { engine?: string; /** Optional pool-directive model id (#883); ignored when engine is absent. */ engineModel?: string; packageRoot?: string; }): EngineSessionMaterial | undefined { if (options.engine === undefined) return undefined; if (options.packageRoot === undefined || options.packageRoot.trim() === "") { throw new Error("packageRoot is required when engine is configured"); } const name = assertLegalEngineName(options.engine); const model = options.engineModel === undefined ? undefined : assertLegalEngineModel(options.engineModel); const materialPath = resolveEngineMaterialPath(options.packageRoot, name); const modelField = model === undefined ? {} : { model }; if (existsSync(materialPath)) { return Object.freeze({ name, materialPath, ...modelField }); } return Object.freeze({ name, ...modelField }); } /** * Append engine method-material delivery to session initial material lines. * No engine → identity copy (byte-stable when joined the same way). * With notes → Chinese neutral handbook header + engine name + absolute material path. * Name only → engine name coordinate only (no handbook header, no path, no warning). * Never delivers material body. */ export function appendEngineSessionMaterial( lines: readonly string[], engineMaterial?: EngineSessionMaterial, ): string[] { if (engineMaterial === undefined) { return [...lines]; } const out = [...lines]; out.push(""); if (engineMaterial.materialPath !== undefined) { out.push("本次配置的劳务引擎及其手册:"); out.push(`- engine: ${engineMaterial.name}`); if (engineMaterial.model !== undefined) { out.push(`- engineModel: ${engineMaterial.model}`); } out.push(`- ${engineMaterial.materialPath}`); } else { // Name-only pass-through: no packaged bytes to claim as handbook. out.push(`- engine: ${engineMaterial.name}`); if (engineMaterial.model !== undefined) { out.push(`- engineModel: ${engineMaterial.model}`); } } return out; }