{"version":3,"file":"capability-ceiling.d.ts","sourceRoot":"","sources":["../../../../src/runs/shared/capability-ceiling.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\n\nexport const SUBAGENT_CAPABILITY_CEILING_VERSION = 1 as const;\nexport const SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY = \"pi-subagents.capability-ceiling.v1\";\nexport const SUBAGENT_CAPABILITY_CEILING_ENV = \"PI_SUBAGENT_CAPABILITY_CEILING_V1\";\n\nexport type SubagentCapabilityCeiling =\n\t| { allowedTools: readonly string[]; allowedAgents?: readonly string[]; denyExtensions?: boolean }\n\t| { allowedTools?: readonly string[]; allowedAgents?: readonly string[]; denyExtensions: boolean }\n\t| { allowedTools?: readonly string[]; allowedAgents: readonly string[]; denyExtensions?: boolean };\n\nexport interface ResolvedSubagentCapabilityCeiling {\n\tversion: typeof SUBAGENT_CAPABILITY_CEILING_VERSION;\n\tallowedTools?: string[];\n\tallowedAgents?: string[];\n\tdenyExtensions: boolean;\n\tsources: string[];\n}\n\nexport interface SubagentCapabilityAudit {\n\tceiling: ResolvedSubagentCapabilityCeiling;\n\trequestedTools?: string[];\n\teffectiveTools: string[];\n\tremovedTools: string[];\n\tinternalTools: string[];\n\textensionsDenied: boolean;\n\tremovedExtensionCount: number;\n\trequestedMcpToolCount: number;\n\teffectiveMcpTools: string[];\n\tagentAllowed: boolean;\n\tagentRestrictionSources?: string[];\n}\n\nexport interface RegisterSubagentCapabilityCeilingOptions {\n\tsessionId: string;\n\tsource: string;\n\tceiling: SubagentCapabilityCeiling;\n}\n\nexport interface SubagentCapabilityCeilingHandle {\n\tupdate(ceiling: SubagentCapabilityCeiling): void;\n\tdispose(): void;\n}\n\ntype Registration = { source: string; ceiling: ResolvedSubagentCapabilityCeiling };\ntype Registry = Map<string, Map<symbol, Registration>>;\n\nfunction registry(): Registry {\n\tconst key = Symbol.for(SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY);\n\tconst store = globalThis as typeof globalThis & { [key: symbol]: unknown };\n\tconst existing = store[key];\n\tif (existing instanceof Map) return existing as Registry;\n\tconst created: Registry = new Map();\n\tstore[key] = created;\n\treturn created;\n}\n\nfunction validateText(value: unknown, field: string): string {\n\tif (\n\t\ttypeof value !== \"string\" ||\n\t\t!value.trim() ||\n\t\t/[\\u0000-\\u001f\\u007f]/u.test(value) ||\n\t\tBuffer.byteLength(value.trim(), \"utf8\") > 256\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid capability ceiling ${field}; expected a non-empty string without control characters (max 256 UTF-8 bytes).`,\n\t\t);\n\t}\n\treturn value.trim();\n}\n\nfunction normalizeCeiling(ceiling: SubagentCapabilityCeiling): ResolvedSubagentCapabilityCeiling {\n\tif (!ceiling || typeof ceiling !== \"object\" || Array.isArray(ceiling))\n\t\tthrow new Error(\"Invalid capability ceiling; expected an object.\");\n\tconst hasAllowedTools = Object.hasOwn(ceiling, \"allowedTools\");\n\tconst hasAllowedAgents = Object.hasOwn(ceiling, \"allowedAgents\");\n\tconst hasDenyExtensions = Object.hasOwn(ceiling, \"denyExtensions\");\n\tif (!hasAllowedTools && !hasAllowedAgents && !hasDenyExtensions)\n\t\tthrow new Error(\"Invalid capability ceiling; expected allowedTools, allowedAgents, or denyExtensions.\");\n\tif (hasDenyExtensions && typeof ceiling.denyExtensions !== \"boolean\")\n\t\tthrow new Error(\"Invalid capability ceiling denyExtensions; expected a boolean.\");\n\tconst normalizeList = (field: \"allowedTools\" | \"allowedAgents\", pattern: RegExp): string[] | undefined => {\n\t\tif (!Object.hasOwn(ceiling, field)) return undefined;\n\t\tconst values = ceiling[field];\n\t\tif (!Array.isArray(values)) throw new Error(`Invalid capability ceiling ${field}; expected an array.`);\n\t\tif (values.length > 256) throw new Error(`Invalid capability ceiling ${field}; expected at most 256 names.`);\n\t\treturn [\n\t\t\t...new Set(\n\t\t\t\tvalues.map((entry) => {\n\t\t\t\t\tconst name = validateText(entry, `${field} entry`);\n\t\t\t\t\tif (!pattern.test(name)) throw new Error(`Invalid capability ceiling ${field} entry '${name}'.`);\n\t\t\t\t\tif (Buffer.byteLength(name, \"utf8\") > 128)\n\t\t\t\t\t\tthrow new Error(`Invalid capability ceiling ${field} entry '${name}'; max 128 UTF-8 bytes.`);\n\t\t\t\t\treturn name;\n\t\t\t\t}),\n\t\t\t),\n\t\t].sort();\n\t};\n\tconst allowedTools = normalizeList(\"allowedTools\", /^[A-Za-z0-9_.:-]+$/u);\n\tconst allowedAgents = normalizeList(\"allowedAgents\", /^[A-Za-z0-9_.:-]+$/u);\n\treturn {\n\t\tversion: SUBAGENT_CAPABILITY_CEILING_VERSION,\n\t\t...(allowedTools !== undefined ? { allowedTools } : {}),\n\t\t...(allowedAgents !== undefined ? { allowedAgents } : {}),\n\t\tdenyExtensions: ceiling.denyExtensions === true,\n\t\tsources: [],\n\t};\n}\n\nexport function parseSubagentCapabilityCeiling(\n\tvalue: unknown,\n\tfield = \"capability ceiling\",\n): ResolvedSubagentCapabilityCeiling {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value))\n\t\tthrow new Error(`Invalid ${field}; expected an object.`);\n\tconst record = value as Record<string, unknown>;\n\tif (record.version !== SUBAGENT_CAPABILITY_CEILING_VERSION) throw new Error(`Invalid ${field} version.`);\n\tconst normalized = normalizeCeiling(record as SubagentCapabilityCeiling);\n\tconst sources = record.sources;\n\tif (!Array.isArray(sources) || sources.some((source) => typeof source !== \"string\"))\n\t\tthrow new Error(`Invalid ${field} sources; expected an array of strings.`);\n\tnormalized.sources = [...new Set(sources.map((source) => validateText(source, `${field} source`)))].sort();\n\treturn normalized;\n}\n\nexport function registerSubagentCapabilityCeiling(\n\toptions: RegisterSubagentCapabilityCeilingOptions,\n): SubagentCapabilityCeilingHandle {\n\tconst sessionId = validateText(options.sessionId, \"sessionId\");\n\tconst source = validateText(options.source, \"source\");\n\tlet normalized = normalizeCeiling(options.ceiling);\n\tconst token = Symbol(source);\n\tconst store = registry();\n\tlet session = store.get(sessionId);\n\tif (!session) {\n\t\tsession = new Map();\n\t\tstore.set(sessionId, session);\n\t}\n\tconst setRegistration = () => {\n\t\tnormalized = normalizeCeiling(normalized);\n\t\tnormalized.sources = [source];\n\t\tsession!.set(token, { source, ceiling: normalized });\n\t};\n\tsetRegistration();\n\tlet disposed = false;\n\treturn {\n\t\tupdate(ceiling) {\n\t\t\tif (disposed) throw new Error(\"Cannot update a disposed capability ceiling handle.\");\n\t\t\tnormalized = normalizeCeiling(ceiling);\n\t\t\tnormalized.sources = [source];\n\t\t\tsession!.set(token, { source, ceiling: normalized });\n\t\t},\n\t\tdispose() {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\t\t\tsession!.delete(token);\n\t\t\tif (session!.size === 0) store.delete(sessionId);\n\t\t},\n\t};\n}\n\nexport function intersectSubagentCapabilityCeilings(\n\t...ceilings: Array<ResolvedSubagentCapabilityCeiling | undefined>\n): ResolvedSubagentCapabilityCeiling | undefined {\n\tconst active = ceilings.filter((ceiling): ceiling is ResolvedSubagentCapabilityCeiling => ceiling !== undefined);\n\tif (active.length === 0) return undefined;\n\tconst intersectLists = (field: \"allowedTools\" | \"allowedAgents\"): string[] | undefined => {\n\t\tconst definedLists = active\n\t\t\t.filter((ceiling) => ceiling[field] !== undefined)\n\t\t\t.map((ceiling) => new Set(ceiling[field]));\n\t\tif (definedLists.length === 0) return undefined;\n\t\treturn [...definedLists[0]!].filter((entry) => definedLists.every((list) => list.has(entry))).sort();\n\t};\n\tconst allowedTools = intersectLists(\"allowedTools\");\n\tconst allowedAgents = intersectLists(\"allowedAgents\");\n\treturn {\n\t\tversion: SUBAGENT_CAPABILITY_CEILING_VERSION,\n\t\t...(allowedTools !== undefined ? { allowedTools } : {}),\n\t\t...(allowedAgents !== undefined ? { allowedAgents } : {}),\n\t\tdenyExtensions: active.some((ceiling) => ceiling.denyExtensions),\n\t\tsources: [...new Set(active.flatMap((ceiling) => ceiling.sources))].sort(),\n\t};\n}\n\nexport function resolveSubagentCapabilityCeiling(\n\tsessionId: string | undefined,\n\tinherited?: ResolvedSubagentCapabilityCeiling,\n): ResolvedSubagentCapabilityCeiling | undefined {\n\tconst active: ResolvedSubagentCapabilityCeiling[] = [];\n\tif (sessionId) {\n\t\tconst registrations = registry().get(sessionId);\n\t\tif (registrations) active.push(...Array.from(registrations.values(), ({ ceiling }) => ceiling));\n\t}\n\treturn intersectSubagentCapabilityCeilings(inherited, ...active);\n}\n\nexport function resolveCurrentSubagentCapabilityCeiling(\n\tsessionId: string | undefined,\n): ResolvedSubagentCapabilityCeiling | undefined {\n\treturn resolveSubagentCapabilityCeiling(\n\t\tsessionId,\n\t\tdecodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),\n\t);\n}\n\nexport function isAgentAllowedByCapabilityCeiling(\n\tagentName: string,\n\tceiling: ResolvedSubagentCapabilityCeiling | undefined,\n): boolean {\n\treturn ceiling?.allowedAgents === undefined || ceiling.allowedAgents.includes(agentName);\n}\n\nexport function capabilityCeilingAgentRestrictionMessage(\n\tagentName: string,\n\tceiling: ResolvedSubagentCapabilityCeiling | undefined,\n): string | undefined {\n\tif (isAgentAllowedByCapabilityCeiling(agentName, ceiling)) return undefined;\n\tconst sources = ceiling?.sources.length ? ceiling.sources.join(\", \") : \"unknown source\";\n\tconst allowed = ceiling?.allowedAgents?.length ? ceiling.allowedAgents.join(\", \") : \"(none)\";\n\treturn `Capability ceiling from ${sources} does not allow agent '${agentName}'. Allowed agents: ${allowed}.`;\n}\n\nexport function assertAgentAllowedByCapabilityCeiling(\n\tagentName: string,\n\tceiling: ResolvedSubagentCapabilityCeiling | undefined,\n): void {\n\tconst message = capabilityCeilingAgentRestrictionMessage(agentName, ceiling);\n\tif (message) throw new Error(message);\n}\n\nexport function capabilityCeilingAgentRestrictionSources(\n\tceiling: ResolvedSubagentCapabilityCeiling | undefined,\n): string[] | undefined {\n\treturn ceiling?.allowedAgents === undefined ? undefined : [...ceiling.sources];\n}\n\nexport function encodeSubagentCapabilityCeiling(\n\tceiling: ResolvedSubagentCapabilityCeiling | undefined,\n): string | undefined {\n\tif (!ceiling) return undefined;\n\treturn Buffer.from(JSON.stringify(ceiling), \"utf8\").toString(\"base64url\");\n}\n\nexport function decodeSubagentCapabilityCeiling(\n\tvalue: string | undefined,\n): ResolvedSubagentCapabilityCeiling | undefined {\n\tif (value === undefined || value === \"\") return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(Buffer.from(value, \"base64url\").toString(\"utf8\"));\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Invalid inherited capability ceiling: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n\tif (\n\t\t!parsed ||\n\t\ttypeof parsed !== \"object\" ||\n\t\tArray.isArray(parsed) ||\n\t\t(parsed as { version?: unknown }).version !== SUBAGENT_CAPABILITY_CEILING_VERSION\n\t) {\n\t\tthrow new Error(\"Invalid inherited capability ceiling version.\");\n\t}\n\treturn parseSubagentCapabilityCeiling(parsed, \"inherited capability ceiling\");\n}\n"]}