@ai-agent-forge/plugin-sdk 0.88.5 → 0.88.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"capability-manifest.d.ts","sourceRoot":"","sources":["../src/capability-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAKX,cAAc,EACd,MAAM,iBAAiB,CAAC;AA+IzB,6EAA6E;AAC7E,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,SAAe,GAAG,cAAc,CAkFzG","sourcesContent":["/**\n * Capability plugin manifest schema (plugin.json) and its pure validation.\n *\n * Authority: `@agent-forge/plugin-sdk` (D-075 S2). The host re-exports these\n * symbols from its loader module; filesystem discovery, jiti loading, and\n * lifecycle dispatch stay host-side.\n */\n\nimport { validRange } from \"semver\";\nimport type {\n\tAgentDefinitionDeclaration,\n\tPluginAgentRole,\n\tPluginCapabilityDeclaration,\n\tPluginCapabilityRequirement,\n\tPluginManifest,\n} from \"./public-api.ts\";\nimport { EXPERIMENTAL_PUBLIC_API_VERSION } from \"./public-api.ts\";\n\nfunction isStringArray(value: unknown): value is string[] {\n\treturn Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isJsonConfigValue(value: unknown): boolean {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"string\") return true;\n\tif (typeof value === \"number\") return Number.isFinite(value);\n\tif (Array.isArray(value)) return value.every(isJsonConfigValue);\n\tif (!isObject(value)) return false;\n\treturn Object.values(value).every(isJsonConfigValue);\n}\n\nfunction assertPluginCapabilityId(value: unknown, field: string, manifestPath: string): asserts value is string {\n\tif (typeof value !== \"string\" || !value.trim()) {\n\t\tthrow new Error(`Capability plugin ${field} must be a non-empty string: ${manifestPath}`);\n\t}\n}\n\nfunction assertPluginCapabilityVersion(value: unknown, field: string, manifestPath: string): asserts value is number {\n\tif (typeof value !== \"number\" || !Number.isSafeInteger(value) || value < 1) {\n\t\tthrow new Error(`Capability plugin ${field} must be a positive safe integer: ${manifestPath}`);\n\t}\n}\n\nfunction capabilityKey(id: string, version: number): string {\n\treturn `${id}\\u0000${version}`;\n}\n\nfunction parsePluginAgentRoles(value: unknown, manifestPath: string): readonly PluginAgentRole[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) throw new Error(`Capability plugin roles must be an array: ${manifestPath}`);\n\tconst roles: PluginAgentRole[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, role] of value.entries()) {\n\t\tif (role !== \"root-agent-controller\" && role !== \"child-agent-controller\") {\n\t\t\tthrow new Error(`Capability plugin roles[${index}] is invalid: ${manifestPath}`);\n\t\t}\n\t\tif (seen.has(role)) throw new Error(`Capability plugin roles duplicates ${role}: ${manifestPath}`);\n\t\tseen.add(role);\n\t\troles.push(role);\n\t}\n\treturn roles;\n}\n\nfunction parsePluginAgentDeclarations(\n\tvalue: unknown,\n\troles: readonly PluginAgentRole[] | undefined,\n\tmanifestPath: string,\n): readonly AgentDefinitionDeclaration[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) throw new Error(`Capability plugin agents must be an array: ${manifestPath}`);\n\tconst declarations: AgentDefinitionDeclaration[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate))\n\t\t\tthrow new Error(`Capability plugin agents[${index}] must be an object: ${manifestPath}`);\n\t\tconst { id, version, role, entry } = candidate;\n\t\tassertPluginCapabilityId(id, `agents[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `agents[${index}].version`, manifestPath);\n\t\tif (role !== \"root-agent-controller\" && role !== \"child-agent-controller\") {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].role is invalid: ${manifestPath}`);\n\t\t}\n\t\tif (typeof entry !== \"string\" || !entry.trim()) {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].entry must be a non-empty string: ${manifestPath}`);\n\t\t}\n\t\tif (roles !== undefined && !roles.includes(role)) {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].role is not declared in roles: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) throw new Error(`Capability plugin agents duplicates ${id}@${version}: ${manifestPath}`);\n\t\tseen.add(key);\n\t\tdeclarations.push({ id, version, role, entry });\n\t}\n\treturn declarations;\n}\n\nfunction parsePluginCapabilityDeclarations(\n\tvalue: unknown,\n\tmanifestPath: string,\n): readonly PluginCapabilityDeclaration[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`Capability plugin provides must be an array: ${manifestPath}`);\n\t}\n\tconst declarations: PluginCapabilityDeclaration[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate)) {\n\t\t\tthrow new Error(`Capability plugin provides[${index}] must be an object: ${manifestPath}`);\n\t\t}\n\t\tconst { id, version, kind } = candidate;\n\t\tassertPluginCapabilityId(id, `provides[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `provides[${index}].version`, manifestPath);\n\t\tif (kind !== \"service\" && kind !== \"contribution\" && kind !== \"transform\") {\n\t\t\tthrow new Error(`Capability plugin provides[${index}].kind is invalid: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) {\n\t\t\tthrow new Error(`Capability plugin provides duplicates ${id}@${version}: ${manifestPath}`);\n\t\t}\n\t\tseen.add(key);\n\t\tdeclarations.push({ id, version, kind });\n\t}\n\treturn declarations;\n}\n\nfunction parsePluginCapabilityRequirements(\n\tvalue: unknown,\n\tmanifestPath: string,\n): readonly PluginCapabilityRequirement[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`Capability plugin requires must be an array: ${manifestPath}`);\n\t}\n\tconst requirements: PluginCapabilityRequirement[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate)) {\n\t\t\tthrow new Error(`Capability plugin requires[${index}] must be an object: ${manifestPath}`);\n\t\t}\n\t\tconst { id, version, optional } = candidate;\n\t\tassertPluginCapabilityId(id, `requires[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `requires[${index}].version`, manifestPath);\n\t\tif (optional !== undefined && typeof optional !== \"boolean\") {\n\t\t\tthrow new Error(`Capability plugin requires[${index}].optional must be a boolean: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) {\n\t\t\tthrow new Error(`Capability plugin requires duplicates ${id}@${version}: ${manifestPath}`);\n\t\t}\n\t\tseen.add(key);\n\t\trequirements.push(optional === undefined ? { id, version } : { id, version, optional });\n\t}\n\treturn requirements;\n}\n\n/** Parse a capability plugin manifest without accepting malformed fields. */\nexport function parseCapabilityPluginManifest(value: unknown, manifestPath = \"<manifest>\"): PluginManifest {\n\tif (!isObject(value)) throw new Error(`Capability plugin manifest must be an object: ${manifestPath}`);\n\tconst {\n\t\tid,\n\t\tversion,\n\t\tdescription,\n\t\thostVersion,\n\t\tapiVersion,\n\t\tentry,\n\t\trequiredCapabilities,\n\t\toptionalCapabilities,\n\t\tprovides,\n\t\trequires,\n\t\troles,\n\t\tagents,\n\t\tactivation,\n\t\tmanual,\n\t\tconfig,\n\t} = value;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof version !== \"string\" ||\n\t\ttypeof apiVersion !== \"string\" ||\n\t\ttypeof entry !== \"string\"\n\t) {\n\t\tthrow new Error(`Capability plugin manifest requires string id, version, apiVersion, and entry: ${manifestPath}`);\n\t}\n\tif (apiVersion !== EXPERIMENTAL_PUBLIC_API_VERSION) {\n\t\tthrow new Error(`Unsupported plugin API version: ${apiVersion}: ${manifestPath}`);\n\t}\n\tif (\n\t\thostVersion !== undefined &&\n\t\t(typeof hostVersion !== \"string\" || hostVersion.trim() === \"\" || validRange(hostVersion) === null)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Capability plugin hostVersion must be a non-empty string containing a valid semver range (e.g. \">=0.84.0\"): ${manifestPath}`,\n\t\t);\n\t}\n\tif (requiredCapabilities !== undefined && !isStringArray(requiredCapabilities)) {\n\t\tthrow new Error(`Capability plugin requiredCapabilities must be a string array: ${manifestPath}`);\n\t}\n\tif (optionalCapabilities !== undefined && !isStringArray(optionalCapabilities)) {\n\t\tthrow new Error(`Capability plugin optionalCapabilities must be a string array: ${manifestPath}`);\n\t}\n\tif (config !== undefined && (!isObject(config) || !isJsonConfigValue(config))) {\n\t\tthrow new Error(`Capability plugin config must be a JSON object: ${manifestPath}`);\n\t}\n\tif (description !== undefined && (typeof description !== \"string\" || !description.trim())) {\n\t\tthrow new Error(`Capability plugin description must be a non-empty string: ${manifestPath}`);\n\t}\n\t// Capability cards render one system-prompt line per plugin (按需挂载与能力手册设计 §4);\n\t// an unbounded description would let a manifest bloat every request.\n\tif (description !== undefined && description.length > 200) {\n\t\tthrow new Error(`Capability plugin description must be at most 200 characters: ${manifestPath}`);\n\t}\n\tif (activation !== undefined && activation !== \"always\" && activation !== \"on-demand\") {\n\t\tthrow new Error(`Capability plugin activation must be \"always\" or \"on-demand\": ${manifestPath}`);\n\t}\n\tif (manual !== undefined && (typeof manual !== \"string\" || !manual.trim())) {\n\t\tthrow new Error(`Capability plugin manual must be a non-empty string: ${manifestPath}`);\n\t}\n\tconst parsedProvides = parsePluginCapabilityDeclarations(provides, manifestPath);\n\tconst parsedRequires = parsePluginCapabilityRequirements(requires, manifestPath);\n\tconst parsedRoles = parsePluginAgentRoles(roles, manifestPath);\n\tconst parsedAgents = parsePluginAgentDeclarations(agents, parsedRoles, manifestPath);\n\treturn {\n\t\tid,\n\t\tversion,\n\t\t...(description === undefined ? {} : { description }),\n\t\tapiVersion: apiVersion as PluginManifest[\"apiVersion\"],\n\t\tentry,\n\t\trequiredCapabilities,\n\t\toptionalCapabilities,\n\t\t...(hostVersion === undefined ? {} : { hostVersion }),\n\t\t...(parsedProvides === undefined ? {} : { provides: parsedProvides }),\n\t\t...(parsedRequires === undefined ? {} : { requires: parsedRequires }),\n\t\t...(parsedRoles === undefined ? {} : { roles: parsedRoles }),\n\t\t...(parsedAgents === undefined ? {} : { agents: parsedAgents }),\n\t\t...(activation === undefined ? {} : { activation }),\n\t\t...(manual === undefined ? {} : { manual }),\n\t\t...(config === undefined ? {} : { config: config as PluginManifest[\"config\"] }),\n\t};\n}\n"]}
1
+ {"version":3,"file":"capability-manifest.d.ts","sourceRoot":"","sources":["../src/capability-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAKX,cAAc,EACd,MAAM,iBAAiB,CAAC;AA4JzB,6EAA6E;AAC7E,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,SAAe,GAAG,cAAc,CAyFzG","sourcesContent":["/**\n * Capability plugin manifest schema (plugin.json) and its pure validation.\n *\n * Authority: `@agent-forge/plugin-sdk` (D-075 S2). The host re-exports these\n * symbols from its loader module; filesystem discovery, jiti loading, and\n * lifecycle dispatch stay host-side.\n */\n\nimport { validRange } from \"semver\";\nimport type {\n\tAgentDefinitionDeclaration,\n\tPluginAgentRole,\n\tPluginCapabilityDeclaration,\n\tPluginCapabilityRequirement,\n\tPluginManifest,\n} from \"./public-api.ts\";\nimport { EXPERIMENTAL_PUBLIC_API_VERSION } from \"./public-api.ts\";\n\nfunction isStringArray(value: unknown): value is string[] {\n\treturn Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isJsonConfigValue(value: unknown): boolean {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"string\") return true;\n\tif (typeof value === \"number\") return Number.isFinite(value);\n\tif (Array.isArray(value)) return value.every(isJsonConfigValue);\n\tif (!isObject(value)) return false;\n\treturn Object.values(value).every(isJsonConfigValue);\n}\n\n/**\n * Manifest entries resolve via path.resolve(dirname(manifestPath), entry) in\n * the host loader, so a crafted entry could point outside the plugin\n * directory. Apply the install host's normalizedEntryName rule family\n * lexically: relative shapes only — no absolute path, drive letter,\n * backslash, \":\" or \"..\" segments. \"./\" prefixes and ordinary relative\n * segments stay allowed.\n */\nfunction isRelativeManifestEntry(entry: string): boolean {\n\tif (entry.trim() === \"\" || entry.includes(\"\\\\\") || entry.startsWith(\"/\") || entry.includes(\":\")) return false;\n\treturn !entry.split(\"/\").includes(\"..\");\n}\n\nfunction assertPluginCapabilityId(value: unknown, field: string, manifestPath: string): asserts value is string {\n\tif (typeof value !== \"string\" || !value.trim()) {\n\t\tthrow new Error(`Capability plugin ${field} must be a non-empty string: ${manifestPath}`);\n\t}\n}\n\nfunction assertPluginCapabilityVersion(value: unknown, field: string, manifestPath: string): asserts value is number {\n\tif (typeof value !== \"number\" || !Number.isSafeInteger(value) || value < 1) {\n\t\tthrow new Error(`Capability plugin ${field} must be a positive safe integer: ${manifestPath}`);\n\t}\n}\n\nfunction capabilityKey(id: string, version: number): string {\n\treturn `${id}\\u0000${version}`;\n}\n\nfunction parsePluginAgentRoles(value: unknown, manifestPath: string): readonly PluginAgentRole[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) throw new Error(`Capability plugin roles must be an array: ${manifestPath}`);\n\tconst roles: PluginAgentRole[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, role] of value.entries()) {\n\t\tif (role !== \"root-agent-controller\" && role !== \"child-agent-controller\") {\n\t\t\tthrow new Error(`Capability plugin roles[${index}] is invalid: ${manifestPath}`);\n\t\t}\n\t\tif (seen.has(role)) throw new Error(`Capability plugin roles duplicates ${role}: ${manifestPath}`);\n\t\tseen.add(role);\n\t\troles.push(role);\n\t}\n\treturn roles;\n}\n\nfunction parsePluginAgentDeclarations(\n\tvalue: unknown,\n\troles: readonly PluginAgentRole[] | undefined,\n\tmanifestPath: string,\n): readonly AgentDefinitionDeclaration[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) throw new Error(`Capability plugin agents must be an array: ${manifestPath}`);\n\tconst declarations: AgentDefinitionDeclaration[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate))\n\t\t\tthrow new Error(`Capability plugin agents[${index}] must be an object: ${manifestPath}`);\n\t\tconst { id, version, role, entry } = candidate;\n\t\tassertPluginCapabilityId(id, `agents[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `agents[${index}].version`, manifestPath);\n\t\tif (role !== \"root-agent-controller\" && role !== \"child-agent-controller\") {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].role is invalid: ${manifestPath}`);\n\t\t}\n\t\tif (typeof entry !== \"string\" || !entry.trim()) {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].entry must be a non-empty string: ${manifestPath}`);\n\t\t}\n\t\tif (roles !== undefined && !roles.includes(role)) {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].role is not declared in roles: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) throw new Error(`Capability plugin agents duplicates ${id}@${version}: ${manifestPath}`);\n\t\tseen.add(key);\n\t\tdeclarations.push({ id, version, role, entry });\n\t}\n\treturn declarations;\n}\n\nfunction parsePluginCapabilityDeclarations(\n\tvalue: unknown,\n\tmanifestPath: string,\n): readonly PluginCapabilityDeclaration[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`Capability plugin provides must be an array: ${manifestPath}`);\n\t}\n\tconst declarations: PluginCapabilityDeclaration[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate)) {\n\t\t\tthrow new Error(`Capability plugin provides[${index}] must be an object: ${manifestPath}`);\n\t\t}\n\t\tconst { id, version, kind } = candidate;\n\t\tassertPluginCapabilityId(id, `provides[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `provides[${index}].version`, manifestPath);\n\t\tif (kind !== \"service\" && kind !== \"contribution\" && kind !== \"transform\") {\n\t\t\tthrow new Error(`Capability plugin provides[${index}].kind is invalid: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) {\n\t\t\tthrow new Error(`Capability plugin provides duplicates ${id}@${version}: ${manifestPath}`);\n\t\t}\n\t\tseen.add(key);\n\t\tdeclarations.push({ id, version, kind });\n\t}\n\treturn declarations;\n}\n\nfunction parsePluginCapabilityRequirements(\n\tvalue: unknown,\n\tmanifestPath: string,\n): readonly PluginCapabilityRequirement[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`Capability plugin requires must be an array: ${manifestPath}`);\n\t}\n\tconst requirements: PluginCapabilityRequirement[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate)) {\n\t\t\tthrow new Error(`Capability plugin requires[${index}] must be an object: ${manifestPath}`);\n\t\t}\n\t\tconst { id, version, optional } = candidate;\n\t\tassertPluginCapabilityId(id, `requires[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `requires[${index}].version`, manifestPath);\n\t\tif (optional !== undefined && typeof optional !== \"boolean\") {\n\t\t\tthrow new Error(`Capability plugin requires[${index}].optional must be a boolean: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) {\n\t\t\tthrow new Error(`Capability plugin requires duplicates ${id}@${version}: ${manifestPath}`);\n\t\t}\n\t\tseen.add(key);\n\t\trequirements.push(optional === undefined ? { id, version } : { id, version, optional });\n\t}\n\treturn requirements;\n}\n\n/** Parse a capability plugin manifest without accepting malformed fields. */\nexport function parseCapabilityPluginManifest(value: unknown, manifestPath = \"<manifest>\"): PluginManifest {\n\tif (!isObject(value)) throw new Error(`Capability plugin manifest must be an object: ${manifestPath}`);\n\tconst {\n\t\tid,\n\t\tversion,\n\t\tdescription,\n\t\thostVersion,\n\t\tapiVersion,\n\t\tentry,\n\t\trequiredCapabilities,\n\t\toptionalCapabilities,\n\t\tprovides,\n\t\trequires,\n\t\troles,\n\t\tagents,\n\t\tactivation,\n\t\tmanual,\n\t\tconfig,\n\t} = value;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof version !== \"string\" ||\n\t\ttypeof apiVersion !== \"string\" ||\n\t\ttypeof entry !== \"string\"\n\t) {\n\t\tthrow new Error(`Capability plugin manifest requires string id, version, apiVersion, and entry: ${manifestPath}`);\n\t}\n\tassertPluginCapabilityId(id, \"id\", manifestPath);\n\tassertPluginCapabilityId(version, \"version\", manifestPath);\n\tif (!isRelativeManifestEntry(entry)) {\n\t\tthrow new Error(\n\t\t\t`Capability plugin manifest entry must be a relative path without drive letters, backslashes, \":\" or \"..\" segments: ${manifestPath}`,\n\t\t);\n\t}\n\tif (apiVersion !== EXPERIMENTAL_PUBLIC_API_VERSION) {\n\t\tthrow new Error(`Unsupported plugin API version: ${apiVersion}: ${manifestPath}`);\n\t}\n\tif (\n\t\thostVersion !== undefined &&\n\t\t(typeof hostVersion !== \"string\" || hostVersion.trim() === \"\" || validRange(hostVersion) === null)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Capability plugin hostVersion must be a non-empty string containing a valid semver range (e.g. \">=0.84.0\"): ${manifestPath}`,\n\t\t);\n\t}\n\tif (requiredCapabilities !== undefined && !isStringArray(requiredCapabilities)) {\n\t\tthrow new Error(`Capability plugin requiredCapabilities must be a string array: ${manifestPath}`);\n\t}\n\tif (optionalCapabilities !== undefined && !isStringArray(optionalCapabilities)) {\n\t\tthrow new Error(`Capability plugin optionalCapabilities must be a string array: ${manifestPath}`);\n\t}\n\tif (config !== undefined && (!isObject(config) || !isJsonConfigValue(config))) {\n\t\tthrow new Error(`Capability plugin config must be a JSON object: ${manifestPath}`);\n\t}\n\tif (description !== undefined && (typeof description !== \"string\" || !description.trim())) {\n\t\tthrow new Error(`Capability plugin description must be a non-empty string: ${manifestPath}`);\n\t}\n\t// Capability cards render one system-prompt line per plugin (按需挂载与能力手册设计 §4);\n\t// an unbounded description would let a manifest bloat every request.\n\tif (description !== undefined && description.length > 200) {\n\t\tthrow new Error(`Capability plugin description must be at most 200 characters: ${manifestPath}`);\n\t}\n\tif (activation !== undefined && activation !== \"always\" && activation !== \"on-demand\") {\n\t\tthrow new Error(`Capability plugin activation must be \"always\" or \"on-demand\": ${manifestPath}`);\n\t}\n\tif (manual !== undefined && (typeof manual !== \"string\" || !manual.trim())) {\n\t\tthrow new Error(`Capability plugin manual must be a non-empty string: ${manifestPath}`);\n\t}\n\tconst parsedProvides = parsePluginCapabilityDeclarations(provides, manifestPath);\n\tconst parsedRequires = parsePluginCapabilityRequirements(requires, manifestPath);\n\tconst parsedRoles = parsePluginAgentRoles(roles, manifestPath);\n\tconst parsedAgents = parsePluginAgentDeclarations(agents, parsedRoles, manifestPath);\n\treturn {\n\t\tid,\n\t\tversion,\n\t\t...(description === undefined ? {} : { description }),\n\t\tapiVersion: apiVersion as PluginManifest[\"apiVersion\"],\n\t\tentry,\n\t\trequiredCapabilities,\n\t\toptionalCapabilities,\n\t\t...(hostVersion === undefined ? {} : { hostVersion }),\n\t\t...(parsedProvides === undefined ? {} : { provides: parsedProvides }),\n\t\t...(parsedRequires === undefined ? {} : { requires: parsedRequires }),\n\t\t...(parsedRoles === undefined ? {} : { roles: parsedRoles }),\n\t\t...(parsedAgents === undefined ? {} : { agents: parsedAgents }),\n\t\t...(activation === undefined ? {} : { activation }),\n\t\t...(manual === undefined ? {} : { manual }),\n\t\t...(config === undefined ? {} : { config: config as PluginManifest[\"config\"] }),\n\t};\n}\n"]}
@@ -24,6 +24,19 @@ function isJsonConfigValue(value) {
24
24
  return false;
25
25
  return Object.values(value).every(isJsonConfigValue);
26
26
  }
27
+ /**
28
+ * Manifest entries resolve via path.resolve(dirname(manifestPath), entry) in
29
+ * the host loader, so a crafted entry could point outside the plugin
30
+ * directory. Apply the install host's normalizedEntryName rule family
31
+ * lexically: relative shapes only — no absolute path, drive letter,
32
+ * backslash, ":" or ".." segments. "./" prefixes and ordinary relative
33
+ * segments stay allowed.
34
+ */
35
+ function isRelativeManifestEntry(entry) {
36
+ if (entry.trim() === "" || entry.includes("\\") || entry.startsWith("/") || entry.includes(":"))
37
+ return false;
38
+ return !entry.split("/").includes("..");
39
+ }
27
40
  function assertPluginCapabilityId(value, field, manifestPath) {
28
41
  if (typeof value !== "string" || !value.trim()) {
29
42
  throw new Error(`Capability plugin ${field} must be a non-empty string: ${manifestPath}`);
@@ -150,6 +163,11 @@ export function parseCapabilityPluginManifest(value, manifestPath = "<manifest>"
150
163
  typeof entry !== "string") {
151
164
  throw new Error(`Capability plugin manifest requires string id, version, apiVersion, and entry: ${manifestPath}`);
152
165
  }
166
+ assertPluginCapabilityId(id, "id", manifestPath);
167
+ assertPluginCapabilityId(version, "version", manifestPath);
168
+ if (!isRelativeManifestEntry(entry)) {
169
+ throw new Error(`Capability plugin manifest entry must be a relative path without drive letters, backslashes, ":" or ".." segments: ${manifestPath}`);
170
+ }
153
171
  if (apiVersion !== EXPERIMENTAL_PUBLIC_API_VERSION) {
154
172
  throw new Error(`Unsupported plugin API version: ${apiVersion}: ${manifestPath}`);
155
173
  }
@@ -1 +1 @@
1
- {"version":3,"file":"capability-manifest.js","sourceRoot":"","sources":["../src/capability-manifest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability plugin manifest schema (plugin.json) and its pure validation.\n *\n * Authority: `@agent-forge/plugin-sdk` (D-075 S2). The host re-exports these\n * symbols from its loader module; filesystem discovery, jiti loading, and\n * lifecycle dispatch stay host-side.\n */\n\nimport { validRange } from \"semver\";\nimport type {\n\tAgentDefinitionDeclaration,\n\tPluginAgentRole,\n\tPluginCapabilityDeclaration,\n\tPluginCapabilityRequirement,\n\tPluginManifest,\n} from \"./public-api.ts\";\nimport { EXPERIMENTAL_PUBLIC_API_VERSION } from \"./public-api.ts\";\n\nfunction isStringArray(value: unknown): value is string[] {\n\treturn Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isJsonConfigValue(value: unknown): boolean {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"string\") return true;\n\tif (typeof value === \"number\") return Number.isFinite(value);\n\tif (Array.isArray(value)) return value.every(isJsonConfigValue);\n\tif (!isObject(value)) return false;\n\treturn Object.values(value).every(isJsonConfigValue);\n}\n\nfunction assertPluginCapabilityId(value: unknown, field: string, manifestPath: string): asserts value is string {\n\tif (typeof value !== \"string\" || !value.trim()) {\n\t\tthrow new Error(`Capability plugin ${field} must be a non-empty string: ${manifestPath}`);\n\t}\n}\n\nfunction assertPluginCapabilityVersion(value: unknown, field: string, manifestPath: string): asserts value is number {\n\tif (typeof value !== \"number\" || !Number.isSafeInteger(value) || value < 1) {\n\t\tthrow new Error(`Capability plugin ${field} must be a positive safe integer: ${manifestPath}`);\n\t}\n}\n\nfunction capabilityKey(id: string, version: number): string {\n\treturn `${id}\\u0000${version}`;\n}\n\nfunction parsePluginAgentRoles(value: unknown, manifestPath: string): readonly PluginAgentRole[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) throw new Error(`Capability plugin roles must be an array: ${manifestPath}`);\n\tconst roles: PluginAgentRole[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, role] of value.entries()) {\n\t\tif (role !== \"root-agent-controller\" && role !== \"child-agent-controller\") {\n\t\t\tthrow new Error(`Capability plugin roles[${index}] is invalid: ${manifestPath}`);\n\t\t}\n\t\tif (seen.has(role)) throw new Error(`Capability plugin roles duplicates ${role}: ${manifestPath}`);\n\t\tseen.add(role);\n\t\troles.push(role);\n\t}\n\treturn roles;\n}\n\nfunction parsePluginAgentDeclarations(\n\tvalue: unknown,\n\troles: readonly PluginAgentRole[] | undefined,\n\tmanifestPath: string,\n): readonly AgentDefinitionDeclaration[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) throw new Error(`Capability plugin agents must be an array: ${manifestPath}`);\n\tconst declarations: AgentDefinitionDeclaration[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate))\n\t\t\tthrow new Error(`Capability plugin agents[${index}] must be an object: ${manifestPath}`);\n\t\tconst { id, version, role, entry } = candidate;\n\t\tassertPluginCapabilityId(id, `agents[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `agents[${index}].version`, manifestPath);\n\t\tif (role !== \"root-agent-controller\" && role !== \"child-agent-controller\") {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].role is invalid: ${manifestPath}`);\n\t\t}\n\t\tif (typeof entry !== \"string\" || !entry.trim()) {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].entry must be a non-empty string: ${manifestPath}`);\n\t\t}\n\t\tif (roles !== undefined && !roles.includes(role)) {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].role is not declared in roles: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) throw new Error(`Capability plugin agents duplicates ${id}@${version}: ${manifestPath}`);\n\t\tseen.add(key);\n\t\tdeclarations.push({ id, version, role, entry });\n\t}\n\treturn declarations;\n}\n\nfunction parsePluginCapabilityDeclarations(\n\tvalue: unknown,\n\tmanifestPath: string,\n): readonly PluginCapabilityDeclaration[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`Capability plugin provides must be an array: ${manifestPath}`);\n\t}\n\tconst declarations: PluginCapabilityDeclaration[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate)) {\n\t\t\tthrow new Error(`Capability plugin provides[${index}] must be an object: ${manifestPath}`);\n\t\t}\n\t\tconst { id, version, kind } = candidate;\n\t\tassertPluginCapabilityId(id, `provides[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `provides[${index}].version`, manifestPath);\n\t\tif (kind !== \"service\" && kind !== \"contribution\" && kind !== \"transform\") {\n\t\t\tthrow new Error(`Capability plugin provides[${index}].kind is invalid: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) {\n\t\t\tthrow new Error(`Capability plugin provides duplicates ${id}@${version}: ${manifestPath}`);\n\t\t}\n\t\tseen.add(key);\n\t\tdeclarations.push({ id, version, kind });\n\t}\n\treturn declarations;\n}\n\nfunction parsePluginCapabilityRequirements(\n\tvalue: unknown,\n\tmanifestPath: string,\n): readonly PluginCapabilityRequirement[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`Capability plugin requires must be an array: ${manifestPath}`);\n\t}\n\tconst requirements: PluginCapabilityRequirement[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate)) {\n\t\t\tthrow new Error(`Capability plugin requires[${index}] must be an object: ${manifestPath}`);\n\t\t}\n\t\tconst { id, version, optional } = candidate;\n\t\tassertPluginCapabilityId(id, `requires[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `requires[${index}].version`, manifestPath);\n\t\tif (optional !== undefined && typeof optional !== \"boolean\") {\n\t\t\tthrow new Error(`Capability plugin requires[${index}].optional must be a boolean: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) {\n\t\t\tthrow new Error(`Capability plugin requires duplicates ${id}@${version}: ${manifestPath}`);\n\t\t}\n\t\tseen.add(key);\n\t\trequirements.push(optional === undefined ? { id, version } : { id, version, optional });\n\t}\n\treturn requirements;\n}\n\n/** Parse a capability plugin manifest without accepting malformed fields. */\nexport function parseCapabilityPluginManifest(value: unknown, manifestPath = \"<manifest>\"): PluginManifest {\n\tif (!isObject(value)) throw new Error(`Capability plugin manifest must be an object: ${manifestPath}`);\n\tconst {\n\t\tid,\n\t\tversion,\n\t\tdescription,\n\t\thostVersion,\n\t\tapiVersion,\n\t\tentry,\n\t\trequiredCapabilities,\n\t\toptionalCapabilities,\n\t\tprovides,\n\t\trequires,\n\t\troles,\n\t\tagents,\n\t\tactivation,\n\t\tmanual,\n\t\tconfig,\n\t} = value;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof version !== \"string\" ||\n\t\ttypeof apiVersion !== \"string\" ||\n\t\ttypeof entry !== \"string\"\n\t) {\n\t\tthrow new Error(`Capability plugin manifest requires string id, version, apiVersion, and entry: ${manifestPath}`);\n\t}\n\tif (apiVersion !== EXPERIMENTAL_PUBLIC_API_VERSION) {\n\t\tthrow new Error(`Unsupported plugin API version: ${apiVersion}: ${manifestPath}`);\n\t}\n\tif (\n\t\thostVersion !== undefined &&\n\t\t(typeof hostVersion !== \"string\" || hostVersion.trim() === \"\" || validRange(hostVersion) === null)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Capability plugin hostVersion must be a non-empty string containing a valid semver range (e.g. \">=0.84.0\"): ${manifestPath}`,\n\t\t);\n\t}\n\tif (requiredCapabilities !== undefined && !isStringArray(requiredCapabilities)) {\n\t\tthrow new Error(`Capability plugin requiredCapabilities must be a string array: ${manifestPath}`);\n\t}\n\tif (optionalCapabilities !== undefined && !isStringArray(optionalCapabilities)) {\n\t\tthrow new Error(`Capability plugin optionalCapabilities must be a string array: ${manifestPath}`);\n\t}\n\tif (config !== undefined && (!isObject(config) || !isJsonConfigValue(config))) {\n\t\tthrow new Error(`Capability plugin config must be a JSON object: ${manifestPath}`);\n\t}\n\tif (description !== undefined && (typeof description !== \"string\" || !description.trim())) {\n\t\tthrow new Error(`Capability plugin description must be a non-empty string: ${manifestPath}`);\n\t}\n\t// Capability cards render one system-prompt line per plugin (按需挂载与能力手册设计 §4);\n\t// an unbounded description would let a manifest bloat every request.\n\tif (description !== undefined && description.length > 200) {\n\t\tthrow new Error(`Capability plugin description must be at most 200 characters: ${manifestPath}`);\n\t}\n\tif (activation !== undefined && activation !== \"always\" && activation !== \"on-demand\") {\n\t\tthrow new Error(`Capability plugin activation must be \"always\" or \"on-demand\": ${manifestPath}`);\n\t}\n\tif (manual !== undefined && (typeof manual !== \"string\" || !manual.trim())) {\n\t\tthrow new Error(`Capability plugin manual must be a non-empty string: ${manifestPath}`);\n\t}\n\tconst parsedProvides = parsePluginCapabilityDeclarations(provides, manifestPath);\n\tconst parsedRequires = parsePluginCapabilityRequirements(requires, manifestPath);\n\tconst parsedRoles = parsePluginAgentRoles(roles, manifestPath);\n\tconst parsedAgents = parsePluginAgentDeclarations(agents, parsedRoles, manifestPath);\n\treturn {\n\t\tid,\n\t\tversion,\n\t\t...(description === undefined ? {} : { description }),\n\t\tapiVersion: apiVersion as PluginManifest[\"apiVersion\"],\n\t\tentry,\n\t\trequiredCapabilities,\n\t\toptionalCapabilities,\n\t\t...(hostVersion === undefined ? {} : { hostVersion }),\n\t\t...(parsedProvides === undefined ? {} : { provides: parsedProvides }),\n\t\t...(parsedRequires === undefined ? {} : { requires: parsedRequires }),\n\t\t...(parsedRoles === undefined ? {} : { roles: parsedRoles }),\n\t\t...(parsedAgents === undefined ? {} : { agents: parsedAgents }),\n\t\t...(activation === undefined ? {} : { activation }),\n\t\t...(manual === undefined ? {} : { manual }),\n\t\t...(config === undefined ? {} : { config: config as PluginManifest[\"config\"] }),\n\t};\n}\n"]}
1
+ {"version":3,"file":"capability-manifest.js","sourceRoot":"","sources":["../src/capability-manifest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability plugin manifest schema (plugin.json) and its pure validation.\n *\n * Authority: `@agent-forge/plugin-sdk` (D-075 S2). The host re-exports these\n * symbols from its loader module; filesystem discovery, jiti loading, and\n * lifecycle dispatch stay host-side.\n */\n\nimport { validRange } from \"semver\";\nimport type {\n\tAgentDefinitionDeclaration,\n\tPluginAgentRole,\n\tPluginCapabilityDeclaration,\n\tPluginCapabilityRequirement,\n\tPluginManifest,\n} from \"./public-api.ts\";\nimport { EXPERIMENTAL_PUBLIC_API_VERSION } from \"./public-api.ts\";\n\nfunction isStringArray(value: unknown): value is string[] {\n\treturn Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isJsonConfigValue(value: unknown): boolean {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"string\") return true;\n\tif (typeof value === \"number\") return Number.isFinite(value);\n\tif (Array.isArray(value)) return value.every(isJsonConfigValue);\n\tif (!isObject(value)) return false;\n\treturn Object.values(value).every(isJsonConfigValue);\n}\n\n/**\n * Manifest entries resolve via path.resolve(dirname(manifestPath), entry) in\n * the host loader, so a crafted entry could point outside the plugin\n * directory. Apply the install host's normalizedEntryName rule family\n * lexically: relative shapes only — no absolute path, drive letter,\n * backslash, \":\" or \"..\" segments. \"./\" prefixes and ordinary relative\n * segments stay allowed.\n */\nfunction isRelativeManifestEntry(entry: string): boolean {\n\tif (entry.trim() === \"\" || entry.includes(\"\\\\\") || entry.startsWith(\"/\") || entry.includes(\":\")) return false;\n\treturn !entry.split(\"/\").includes(\"..\");\n}\n\nfunction assertPluginCapabilityId(value: unknown, field: string, manifestPath: string): asserts value is string {\n\tif (typeof value !== \"string\" || !value.trim()) {\n\t\tthrow new Error(`Capability plugin ${field} must be a non-empty string: ${manifestPath}`);\n\t}\n}\n\nfunction assertPluginCapabilityVersion(value: unknown, field: string, manifestPath: string): asserts value is number {\n\tif (typeof value !== \"number\" || !Number.isSafeInteger(value) || value < 1) {\n\t\tthrow new Error(`Capability plugin ${field} must be a positive safe integer: ${manifestPath}`);\n\t}\n}\n\nfunction capabilityKey(id: string, version: number): string {\n\treturn `${id}\\u0000${version}`;\n}\n\nfunction parsePluginAgentRoles(value: unknown, manifestPath: string): readonly PluginAgentRole[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) throw new Error(`Capability plugin roles must be an array: ${manifestPath}`);\n\tconst roles: PluginAgentRole[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, role] of value.entries()) {\n\t\tif (role !== \"root-agent-controller\" && role !== \"child-agent-controller\") {\n\t\t\tthrow new Error(`Capability plugin roles[${index}] is invalid: ${manifestPath}`);\n\t\t}\n\t\tif (seen.has(role)) throw new Error(`Capability plugin roles duplicates ${role}: ${manifestPath}`);\n\t\tseen.add(role);\n\t\troles.push(role);\n\t}\n\treturn roles;\n}\n\nfunction parsePluginAgentDeclarations(\n\tvalue: unknown,\n\troles: readonly PluginAgentRole[] | undefined,\n\tmanifestPath: string,\n): readonly AgentDefinitionDeclaration[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) throw new Error(`Capability plugin agents must be an array: ${manifestPath}`);\n\tconst declarations: AgentDefinitionDeclaration[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate))\n\t\t\tthrow new Error(`Capability plugin agents[${index}] must be an object: ${manifestPath}`);\n\t\tconst { id, version, role, entry } = candidate;\n\t\tassertPluginCapabilityId(id, `agents[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `agents[${index}].version`, manifestPath);\n\t\tif (role !== \"root-agent-controller\" && role !== \"child-agent-controller\") {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].role is invalid: ${manifestPath}`);\n\t\t}\n\t\tif (typeof entry !== \"string\" || !entry.trim()) {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].entry must be a non-empty string: ${manifestPath}`);\n\t\t}\n\t\tif (roles !== undefined && !roles.includes(role)) {\n\t\t\tthrow new Error(`Capability plugin agents[${index}].role is not declared in roles: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) throw new Error(`Capability plugin agents duplicates ${id}@${version}: ${manifestPath}`);\n\t\tseen.add(key);\n\t\tdeclarations.push({ id, version, role, entry });\n\t}\n\treturn declarations;\n}\n\nfunction parsePluginCapabilityDeclarations(\n\tvalue: unknown,\n\tmanifestPath: string,\n): readonly PluginCapabilityDeclaration[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`Capability plugin provides must be an array: ${manifestPath}`);\n\t}\n\tconst declarations: PluginCapabilityDeclaration[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate)) {\n\t\t\tthrow new Error(`Capability plugin provides[${index}] must be an object: ${manifestPath}`);\n\t\t}\n\t\tconst { id, version, kind } = candidate;\n\t\tassertPluginCapabilityId(id, `provides[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `provides[${index}].version`, manifestPath);\n\t\tif (kind !== \"service\" && kind !== \"contribution\" && kind !== \"transform\") {\n\t\t\tthrow new Error(`Capability plugin provides[${index}].kind is invalid: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) {\n\t\t\tthrow new Error(`Capability plugin provides duplicates ${id}@${version}: ${manifestPath}`);\n\t\t}\n\t\tseen.add(key);\n\t\tdeclarations.push({ id, version, kind });\n\t}\n\treturn declarations;\n}\n\nfunction parsePluginCapabilityRequirements(\n\tvalue: unknown,\n\tmanifestPath: string,\n): readonly PluginCapabilityRequirement[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`Capability plugin requires must be an array: ${manifestPath}`);\n\t}\n\tconst requirements: PluginCapabilityRequirement[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, candidate] of value.entries()) {\n\t\tif (!isObject(candidate)) {\n\t\t\tthrow new Error(`Capability plugin requires[${index}] must be an object: ${manifestPath}`);\n\t\t}\n\t\tconst { id, version, optional } = candidate;\n\t\tassertPluginCapabilityId(id, `requires[${index}].id`, manifestPath);\n\t\tassertPluginCapabilityVersion(version, `requires[${index}].version`, manifestPath);\n\t\tif (optional !== undefined && typeof optional !== \"boolean\") {\n\t\t\tthrow new Error(`Capability plugin requires[${index}].optional must be a boolean: ${manifestPath}`);\n\t\t}\n\t\tconst key = capabilityKey(id, version);\n\t\tif (seen.has(key)) {\n\t\t\tthrow new Error(`Capability plugin requires duplicates ${id}@${version}: ${manifestPath}`);\n\t\t}\n\t\tseen.add(key);\n\t\trequirements.push(optional === undefined ? { id, version } : { id, version, optional });\n\t}\n\treturn requirements;\n}\n\n/** Parse a capability plugin manifest without accepting malformed fields. */\nexport function parseCapabilityPluginManifest(value: unknown, manifestPath = \"<manifest>\"): PluginManifest {\n\tif (!isObject(value)) throw new Error(`Capability plugin manifest must be an object: ${manifestPath}`);\n\tconst {\n\t\tid,\n\t\tversion,\n\t\tdescription,\n\t\thostVersion,\n\t\tapiVersion,\n\t\tentry,\n\t\trequiredCapabilities,\n\t\toptionalCapabilities,\n\t\tprovides,\n\t\trequires,\n\t\troles,\n\t\tagents,\n\t\tactivation,\n\t\tmanual,\n\t\tconfig,\n\t} = value;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof version !== \"string\" ||\n\t\ttypeof apiVersion !== \"string\" ||\n\t\ttypeof entry !== \"string\"\n\t) {\n\t\tthrow new Error(`Capability plugin manifest requires string id, version, apiVersion, and entry: ${manifestPath}`);\n\t}\n\tassertPluginCapabilityId(id, \"id\", manifestPath);\n\tassertPluginCapabilityId(version, \"version\", manifestPath);\n\tif (!isRelativeManifestEntry(entry)) {\n\t\tthrow new Error(\n\t\t\t`Capability plugin manifest entry must be a relative path without drive letters, backslashes, \":\" or \"..\" segments: ${manifestPath}`,\n\t\t);\n\t}\n\tif (apiVersion !== EXPERIMENTAL_PUBLIC_API_VERSION) {\n\t\tthrow new Error(`Unsupported plugin API version: ${apiVersion}: ${manifestPath}`);\n\t}\n\tif (\n\t\thostVersion !== undefined &&\n\t\t(typeof hostVersion !== \"string\" || hostVersion.trim() === \"\" || validRange(hostVersion) === null)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Capability plugin hostVersion must be a non-empty string containing a valid semver range (e.g. \">=0.84.0\"): ${manifestPath}`,\n\t\t);\n\t}\n\tif (requiredCapabilities !== undefined && !isStringArray(requiredCapabilities)) {\n\t\tthrow new Error(`Capability plugin requiredCapabilities must be a string array: ${manifestPath}`);\n\t}\n\tif (optionalCapabilities !== undefined && !isStringArray(optionalCapabilities)) {\n\t\tthrow new Error(`Capability plugin optionalCapabilities must be a string array: ${manifestPath}`);\n\t}\n\tif (config !== undefined && (!isObject(config) || !isJsonConfigValue(config))) {\n\t\tthrow new Error(`Capability plugin config must be a JSON object: ${manifestPath}`);\n\t}\n\tif (description !== undefined && (typeof description !== \"string\" || !description.trim())) {\n\t\tthrow new Error(`Capability plugin description must be a non-empty string: ${manifestPath}`);\n\t}\n\t// Capability cards render one system-prompt line per plugin (按需挂载与能力手册设计 §4);\n\t// an unbounded description would let a manifest bloat every request.\n\tif (description !== undefined && description.length > 200) {\n\t\tthrow new Error(`Capability plugin description must be at most 200 characters: ${manifestPath}`);\n\t}\n\tif (activation !== undefined && activation !== \"always\" && activation !== \"on-demand\") {\n\t\tthrow new Error(`Capability plugin activation must be \"always\" or \"on-demand\": ${manifestPath}`);\n\t}\n\tif (manual !== undefined && (typeof manual !== \"string\" || !manual.trim())) {\n\t\tthrow new Error(`Capability plugin manual must be a non-empty string: ${manifestPath}`);\n\t}\n\tconst parsedProvides = parsePluginCapabilityDeclarations(provides, manifestPath);\n\tconst parsedRequires = parsePluginCapabilityRequirements(requires, manifestPath);\n\tconst parsedRoles = parsePluginAgentRoles(roles, manifestPath);\n\tconst parsedAgents = parsePluginAgentDeclarations(agents, parsedRoles, manifestPath);\n\treturn {\n\t\tid,\n\t\tversion,\n\t\t...(description === undefined ? {} : { description }),\n\t\tapiVersion: apiVersion as PluginManifest[\"apiVersion\"],\n\t\tentry,\n\t\trequiredCapabilities,\n\t\toptionalCapabilities,\n\t\t...(hostVersion === undefined ? {} : { hostVersion }),\n\t\t...(parsedProvides === undefined ? {} : { provides: parsedProvides }),\n\t\t...(parsedRequires === undefined ? {} : { requires: parsedRequires }),\n\t\t...(parsedRoles === undefined ? {} : { roles: parsedRoles }),\n\t\t...(parsedAgents === undefined ? {} : { agents: parsedAgents }),\n\t\t...(activation === undefined ? {} : { activation }),\n\t\t...(manual === undefined ? {} : { manual }),\n\t\t...(config === undefined ? {} : { config: config as PluginManifest[\"config\"] }),\n\t};\n}\n"]}
@@ -100,7 +100,7 @@ export interface MetricsCancelLatencySampledV1 {
100
100
  readonly p50Ms: number | null;
101
101
  readonly p95Ms: number | null;
102
102
  }
103
- /** Token/cost totals summed over usage-bearing journal events. */
103
+ /** Token totals summed over usage-bearing journal events. */
104
104
  export interface MetricsTokensSampledV1 {
105
105
  readonly status: "sampled";
106
106
  readonly totalTokens: number;
@@ -1 +1 @@
1
- {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Public contracts for the first-party observability plugin (event journal +\n * problem reports + trace sink). The plugin package\n * (`@agent-forge/plugin-observability`) is the implementation; this module is\n * the single authoring authority for its public types and the host-facing\n * trace adapter contract. The host re-exports the surface from its historical\n * `plugins/observability-types.ts` path so existing imports keep resolving.\n *\n * Contains types only, plus the `TRACE_KINDS_V1` value mirror with its\n * compile-time drift guards (CLI `--trace-kind` validation consumes the\n * value).\n */\n\nimport type { LoggerRecord, OutputArtifactRef } from \"./public-api.ts\";\n\nexport type ObservabilityLogLevel = \"info\" | \"warn\" | \"error\";\n\n/**\n * Output projection surface carried by the problem report's whole-document\n * projection. Authority moved from the host's `host/output-serializer.ts`\n * (D-075 S4 second batch); the host re-exports it from there.\n */\nexport type HostOutputSurfaceV1 = \"tui\" | \"cli-text\" | \"inspector\";\n\n/** One bounded-journal record derived from a delivered lifecycle event. */\nexport interface ObservabilityJournalEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\t/** Session id extracted from the event data; null when the event has none. */\n\treadonly sessionId: string | null;\n\t/** Lifecycle event type, e.g. \"model.stream\" or \"tool.commit\". */\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\t/** Event correlation id, used as the trace grouping key. */\n\treadonly traceId?: string;\n\treadonly summary: string;\n\t/** Validated lifecycle event data, retained verbatim for diagnosis. */\n\treadonly data?: unknown;\n}\n\nexport interface ObservabilityQueryFilterV1 {\n\treadonly sessionId?: string;\n\treadonly stage?: string;\n\treadonly level?: ObservabilityLogLevel;\n\treadonly traceId?: string;\n\t/** When set, returns the most recent `limit` matching entries in order. */\n\treadonly limit?: number;\n}\n\nexport interface ObservabilityTraceStepV1 {\n\treadonly entry: ObservabilityJournalEntryV1;\n\t/** Milliseconds since the previous step; null on the first step. */\n\treadonly durationMs: number | null;\n}\n\nexport interface ObservabilityTraceTimelineV1 {\n\treadonly traceId: string;\n\treadonly steps: readonly ObservabilityTraceStepV1[];\n}\n\nexport interface ObservabilityJournalStatsV1 {\n\treadonly size: number;\n\treadonly dropped: number;\n}\n\nexport interface ObservabilityProblemReportSessionV1 {\n\treadonly sessionId: string;\n\treadonly entryCount: number;\n\treadonly firstSequence: number | null;\n\treadonly lastSequence: number | null;\n}\n\n/** Full error envelope of the most recent error-level entry for the session. */\nexport interface ObservabilityProblemReportErrorV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\t/** Lifecycle phase when the event carries one (model.stream). */\n\treadonly phase?: string;\n\treadonly traceId: string | null;\n\treadonly code: string;\n\treadonly message: string;\n}\n\nexport interface ObservabilityProblemReportTailEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\treadonly traceId: string | null;\n\treadonly summary: string;\n\t/**\n\t * Payload rendered through the host output serializer redaction channel;\n\t * internal field keys are replaced at any depth before export.\n\t */\n\treadonly data?: string;\n}\n\nexport interface ObservabilityProblemReportCountsV1 {\n\treadonly byStage: Readonly<Record<string, number>>;\n\treadonly byLevel: Readonly<Record<string, number>>;\n}\n\n/** A metric the journal cannot currently source; the reason names the missing event granularity. */\nexport interface MetricsNotSampledV1 {\n\treadonly status: \"not_sampled\";\n\treadonly reason: string;\n}\n\n/** Cancel-latency samples: delay from a cancel signal to the turn's terminal event. */\nexport interface MetricsCancelLatencySampledV1 {\n\treadonly status: \"sampled\";\n\treadonly count: number;\n\treadonly p50Ms: number | null;\n\treadonly p95Ms: number | null;\n}\n\n/** Token/cost totals summed over usage-bearing journal events. */\nexport interface MetricsTokensSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly totalTokens: number;\n}\n\n/** Memory recall hit rate over `memory_recall` tool executions in the window. */\nexport interface MetricsMemoryRecallSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly calls: number;\n\treadonly hits: number;\n\treadonly hitRate: number;\n}\n\nexport interface MetricsToolErrorsV1 {\n\treadonly calls: number;\n\treadonly errors: number;\n\t/** `null` when no tool executions occurred in the window. */\n\treadonly errorRate: number | null;\n\t/** Errors per tool name, descending by count then ascending by name. */\n\treadonly topErrors: readonly { readonly toolName: string; readonly errors: number }[];\n}\n\nexport interface MetricsTurnsV1 {\n\treadonly count: number;\n\treadonly successCount: number;\n\t/** `null` when no turns occurred in the window. */\n\treadonly successRate: number | null;\n\treadonly p50DurationMs: number | null;\n\treadonly p95DurationMs: number | null;\n}\n\n/** Window metadata: which turns were aggregated and their time span. */\nexport interface MetricsWindowV1 {\n\t/** Requested turn bound; `null` when the whole journal was aggregated. */\n\treadonly windowTurns: number | null;\n\treadonly sampledTurns: number;\n\treadonly firstAt: number | null;\n\treadonly lastAt: number | null;\n}\n\n/**\n * Deterministic product metrics aggregated read-only from the journal ring.\n * Metrics the journal cannot source are reported as `not_sampled` with the\n * reason instead of being fabricated.\n */\nexport interface MetricsSnapshotV1 {\n\treadonly version: 1;\n\treadonly window: MetricsWindowV1;\n\treadonly turns: MetricsTurnsV1;\n\treadonly tools: MetricsToolErrorsV1;\n\treadonly cancelLatencyMs: MetricsCancelLatencySampledV1 | MetricsNotSampledV1;\n\treadonly tokens: MetricsTokensSampledV1 | MetricsNotSampledV1;\n\treadonly memoryRecall: MetricsMemoryRecallSampledV1 | MetricsNotSampledV1;\n}\n\n/** Aggregate options for {@link ObservabilityPlugin.getMetrics}. */\nexport interface ObservabilityMetricsOptions {\n\t/** Aggregate only the most recent `windowTurns` correlationId groups; omit for the whole journal. */\n\treadonly windowTurns?: number;\n}\n\n/**\n * Trace record kinds for `agent-forge/trace.v1`\n * (design: docs/design/会话轨迹与日志设计.md §4.2).\n */\nexport type TraceKindV1 =\n\t| \"session.start\"\n\t| \"turn.span\"\n\t| \"llm.request\"\n\t| \"llm.response\"\n\t| \"tool.span\"\n\t| \"lifecycle\"\n\t| \"error\"\n\t| \"decision\"\n\t| \"plugin.log\";\n\n/**\n * Runtime mirror of `TraceKindV1` for value-level consumers (CLI\n * `--trace-kind` validation), kept beside the union so the two compile-time\n * drift guards fail the build when they diverge in either direction:\n * - the `satisfies` below rejects a list entry the union no longer has;\n * - `_TRACE_KINDS_EXHAUSTIVE_CHECK` rejects a union member missing from the\n * list (`never` is not assignable from `true`).\n */\nexport const TRACE_KINDS_V1 = [\n\t\"session.start\",\n\t\"turn.span\",\n\t\"llm.request\",\n\t\"llm.response\",\n\t\"tool.span\",\n\t\"lifecycle\",\n\t\"error\",\n\t\"decision\",\n\t\"plugin.log\",\n] as const satisfies readonly TraceKindV1[];\n\ntype TraceKindListV1 = (typeof TRACE_KINDS_V1)[number];\nconst _TRACE_KINDS_EXHAUSTIVE_CHECK: TraceKindV1 extends TraceKindListV1 ? true : never = true;\n\n/** One append-only JSONL trace record (design §4.2). */\nexport interface TraceRecordV1 {\n\treadonly v: 1;\n\treadonly schema: \"agent-forge/trace.v1\";\n\t/** Monotonic sequence within one trace file, starting at 1. */\n\treadonly seq: number;\n\t/** Host timestamp (epoch ms). */\n\treadonly t: number;\n\t/** Duration of a closed span (ms). */\n\treadonly dur?: number;\n\treadonly kind: TraceKindV1;\n\t/** Turn/request correlation id, shared with lifecycle events and logs. */\n\treadonly traceId?: string;\n\treadonly level?: ObservabilityLogLevel;\n\t/** Kind-specific bounded data. */\n\treadonly data?: unknown;\n}\n\n/** Bounds for trace payload snapshots (design §4.3); each field defaults when omitted. */\nexport interface TraceSinkOptionsV1 {\n\t/** Strings longer than this truncate with a `…[truncated N chars]` suffix. Default 16_000. */\n\treadonly maxStringChars?: number;\n\t/**\n\t * Serialized snapshot budget in bytes, enforced largest-string-first inside\n\t * `llm.request.data.payload`. Strictly below the 512 KiB per-record budget.\n\t * Default 393_216 (384 KiB).\n\t */\n\treadonly maxSnapshotBytes?: number;\n\t/**\n\t * OTLP/HTTP+JSON trace export (design §8). Omitted keeps the exporter\n\t * absent: no transport, no buffering, file-sink behavior unchanged. The\n\t * endpoint is the OTLP base URL (e.g. `http://localhost:4318`); the\n\t * `/v1/traces` signal path is appended unless already present.\n\t */\n\treadonly otlp?: OtlpExportOptionsV1;\n}\n\n/** OTLP/HTTP+JSON exporter options; all durations/bounds are deterministic inputs. */\nexport interface OtlpExportOptionsV1 {\n\t/** OTLP base URL; `/v1/traces` is appended unless already present. */\n\treadonly endpoint: string;\n\t/**\n\t * Transport injection for deterministic tests and custom hosts (proxy,\n\t * in-process collector); defaults to `globalThis.fetch`. Only the fields\n\t * below are consumed by the plugin.\n\t */\n\treadonly fetchImpl?: (\n\t\turl: string,\n\t\tinit: { method: \"POST\"; headers: { \"content-type\": string }; body: string; signal: AbortSignal },\n\t) => Promise<{ ok: boolean; status: number }>;\n\t/** Per-flush HTTP timeout in ms. Default 5_000. */\n\treadonly timeoutMs?: number;\n\t/** Buffered-span count that triggers an early flush. Default 64. */\n\treadonly flushThreshold?: number;\n}\n\n/**\n * Telemetry export seam (design §8): the single attachment point for trace\n * exporters (first OTLP/HTTP+JSON; future Prometheus-style pull surfaces hook\n * here instead of growing bespoke taps). Hosts inject sinks through\n * `ObservabilityTraceConfig.exportSinks`; the plugin never reaches into host\n * internals and never lets a sink break event delivery.\n */\nexport interface ObservabilityExportSinkV1 {\n\t/** Called synchronously for every finalized trace record. Must not throw. */\n\treadonly onTraceRecord: (record: TraceRecordV1) => void;\n\t/** Best-effort flush; resolves after the batch is delivered or dropped. */\n\treadonly flush: (reason: string) => Promise<void>;\n}\n\n/**\n * Host-injected adapter for the trace sink (design §4.4). The host closure\n * owns session-path resolution, provider payload capture, and failure\n * reporting; the plugin never reaches into host internals.\n */\nexport interface ObservabilityHostAdapterV1 {\n\t/** Trace file path for the current session, or `null` when tracing is off. */\n\tgetTraceFilePath(): string | null;\n\t/** Host push of the finalized provider payload (sdk `onPayload` tail). */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Trace write-failure channel; the host forwards to the process log. */\n\tonSinkFailure(error: unknown): void;\n\t/**\n\t * Correlation id of the turn currently in flight, or `undefined` outside a\n\t * turn. Omitted keeps the phase-1 behavior: `llm.request` and `plugin.log`\n\t * records carry no traceId.\n\t */\n\tgetTurnCorrelationId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent agent session id recorded at session\n\t * creation (`SessionHeader.parentAgentSession`, session-manager.ts).\n\t * Omitted — or returning `undefined` when the header has no such field —\n\t * keeps the previous behavior: the `session.start` record carries no\n\t * `parentSessionId`.\n\t */\n\tgetParentAgentSessionId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent-turn correlation id that created this\n\t * session (`SessionHeader.parentTraceId`, session-manager.ts; frozen at\n\t * creation). Omitted — or returning `undefined` — keeps the previous\n\t * behavior: the `session.start` record carries no `parentTraceId`.\n\t */\n\tgetParentTraceId?(): string | undefined;\n\t/**\n\t * Plugin-to-host facade registration (D-075 S4 second batch). The plugin\n\t * entry calls it once after creating the plugin instance so the host's\n\t * assembly refs (provider payload capture, plugin.log mirror, /metrics\n\t * source) keep their embedded-era semantics; calling it again with\n\t * `undefined` on teardown clears them. Omitted on hosts that consume the\n\t * facade through another channel — the entry must probe before calling.\n\t */\n\tregisterFacade?(facade: ObservabilityPluginFacadeV1 | undefined): void;\n}\n\n/**\n * The narrow plugin-facing face the host consumes through\n * {@link ObservabilityHostAdapterV1.registerFacade}. The plugin instance is\n * structurally assignable to it.\n */\nexport interface ObservabilityPluginFacadeV1 {\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Optional: present since the plugin.log mirror (design §7.2). */\n\tmirrorPluginLog?(record: LoggerRecord): void;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n}\n\n/** Deterministic problem report; `reportId` is FNV-1a-32 hex over the canonical JSON of the report body. */\nexport interface ProblemReportV1 {\n\treadonly version: 1;\n\treadonly reportId: string;\n\treadonly createdAt: number;\n\treadonly reason: string;\n\treadonly session: ObservabilityProblemReportSessionV1;\n\treadonly lastError: ObservabilityProblemReportErrorV1 | null;\n\treadonly journalTail: readonly ObservabilityProblemReportTailEntryV1[];\n\treadonly counts: ObservabilityProblemReportCountsV1;\n\treadonly dropped: number;\n\treadonly redactionApplied: boolean;\n\t/** Whole-report projection rendered by the host output serializer. */\n\treadonly projection: {\n\t\treadonly surface: HostOutputSurfaceV1;\n\t\treadonly text: string;\n\t};\n\t/** Present only when the host declared the `output-artifacts` feature. */\n\treadonly artifact?: OutputArtifactRef;\n}\n\n/**\n * The observability plugin's public behavior face (journal read side, trace\n * explanation, deterministic metrics, problem-report export). The plugin\n * package returns this shape from its factory; hosts consume it through\n * {@link ObservabilityPluginFacadeV1} or their own references.\n */\nexport interface ObservabilityPlugin {\n\treadonly id: string;\n\tquery(filter?: ObservabilityQueryFilterV1): readonly ObservabilityJournalEntryV1[];\n\texplainTrace(traceId: string): ObservabilityTraceTimelineV1;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n\tstats(): ObservabilityJournalStatsV1;\n\texportProblemReport(input: { readonly sessionId: string; readonly reason: string }): Promise<ProblemReportV1>;\n\t/**\n\t * Receives the finalized provider payload from the host adapter and emits\n\t * one `llm.request` trace record with a bounded redacted snapshot (design\n\t * §4.3). No-op when no trace adapter was injected.\n\t */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/**\n\t * Receives one plugin LoggerAPI record forwarded by the host runtime sink\n\t * (`CapabilityRuntimeOptions.onLogRecord`) and appends a `plugin.log` trace\n\t * record (design §4.2/§7.2). No-op when no trace adapter was injected.\n\t * Must never call `api.logger`: the host forwards logger output here, so\n\t * logging from inside would recurse.\n\t */\n\tmirrorPluginLog(record: LoggerRecord): void;\n\tdispose(): void;\n}\n"]}
1
+ {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Public contracts for the first-party observability plugin (event journal +\n * problem reports + trace sink). The plugin package\n * (`@agent-forge/plugin-observability`) is the implementation; this module is\n * the single authoring authority for its public types and the host-facing\n * trace adapter contract. The host re-exports the surface from its historical\n * `plugins/observability-types.ts` path so existing imports keep resolving.\n *\n * Contains types only, plus the `TRACE_KINDS_V1` value mirror with its\n * compile-time drift guards (CLI `--trace-kind` validation consumes the\n * value).\n */\n\nimport type { LoggerRecord, OutputArtifactRef } from \"./public-api.ts\";\n\nexport type ObservabilityLogLevel = \"info\" | \"warn\" | \"error\";\n\n/**\n * Output projection surface carried by the problem report's whole-document\n * projection. Authority moved from the host's `host/output-serializer.ts`\n * (D-075 S4 second batch); the host re-exports it from there.\n */\nexport type HostOutputSurfaceV1 = \"tui\" | \"cli-text\" | \"inspector\";\n\n/** One bounded-journal record derived from a delivered lifecycle event. */\nexport interface ObservabilityJournalEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\t/** Session id extracted from the event data; null when the event has none. */\n\treadonly sessionId: string | null;\n\t/** Lifecycle event type, e.g. \"model.stream\" or \"tool.commit\". */\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\t/** Event correlation id, used as the trace grouping key. */\n\treadonly traceId?: string;\n\treadonly summary: string;\n\t/** Validated lifecycle event data, retained verbatim for diagnosis. */\n\treadonly data?: unknown;\n}\n\nexport interface ObservabilityQueryFilterV1 {\n\treadonly sessionId?: string;\n\treadonly stage?: string;\n\treadonly level?: ObservabilityLogLevel;\n\treadonly traceId?: string;\n\t/** When set, returns the most recent `limit` matching entries in order. */\n\treadonly limit?: number;\n}\n\nexport interface ObservabilityTraceStepV1 {\n\treadonly entry: ObservabilityJournalEntryV1;\n\t/** Milliseconds since the previous step; null on the first step. */\n\treadonly durationMs: number | null;\n}\n\nexport interface ObservabilityTraceTimelineV1 {\n\treadonly traceId: string;\n\treadonly steps: readonly ObservabilityTraceStepV1[];\n}\n\nexport interface ObservabilityJournalStatsV1 {\n\treadonly size: number;\n\treadonly dropped: number;\n}\n\nexport interface ObservabilityProblemReportSessionV1 {\n\treadonly sessionId: string;\n\treadonly entryCount: number;\n\treadonly firstSequence: number | null;\n\treadonly lastSequence: number | null;\n}\n\n/** Full error envelope of the most recent error-level entry for the session. */\nexport interface ObservabilityProblemReportErrorV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\t/** Lifecycle phase when the event carries one (model.stream). */\n\treadonly phase?: string;\n\treadonly traceId: string | null;\n\treadonly code: string;\n\treadonly message: string;\n}\n\nexport interface ObservabilityProblemReportTailEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\treadonly traceId: string | null;\n\treadonly summary: string;\n\t/**\n\t * Payload rendered through the host output serializer redaction channel;\n\t * internal field keys are replaced at any depth before export.\n\t */\n\treadonly data?: string;\n}\n\nexport interface ObservabilityProblemReportCountsV1 {\n\treadonly byStage: Readonly<Record<string, number>>;\n\treadonly byLevel: Readonly<Record<string, number>>;\n}\n\n/** A metric the journal cannot currently source; the reason names the missing event granularity. */\nexport interface MetricsNotSampledV1 {\n\treadonly status: \"not_sampled\";\n\treadonly reason: string;\n}\n\n/** Cancel-latency samples: delay from a cancel signal to the turn's terminal event. */\nexport interface MetricsCancelLatencySampledV1 {\n\treadonly status: \"sampled\";\n\treadonly count: number;\n\treadonly p50Ms: number | null;\n\treadonly p95Ms: number | null;\n}\n\n/** Token totals summed over usage-bearing journal events. */\nexport interface MetricsTokensSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly totalTokens: number;\n}\n\n/** Memory recall hit rate over `memory_recall` tool executions in the window. */\nexport interface MetricsMemoryRecallSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly calls: number;\n\treadonly hits: number;\n\treadonly hitRate: number;\n}\n\nexport interface MetricsToolErrorsV1 {\n\treadonly calls: number;\n\treadonly errors: number;\n\t/** `null` when no tool executions occurred in the window. */\n\treadonly errorRate: number | null;\n\t/** Errors per tool name, descending by count then ascending by name. */\n\treadonly topErrors: readonly { readonly toolName: string; readonly errors: number }[];\n}\n\nexport interface MetricsTurnsV1 {\n\treadonly count: number;\n\treadonly successCount: number;\n\t/** `null` when no turns occurred in the window. */\n\treadonly successRate: number | null;\n\treadonly p50DurationMs: number | null;\n\treadonly p95DurationMs: number | null;\n}\n\n/** Window metadata: which turns were aggregated and their time span. */\nexport interface MetricsWindowV1 {\n\t/** Requested turn bound; `null` when the whole journal was aggregated. */\n\treadonly windowTurns: number | null;\n\treadonly sampledTurns: number;\n\treadonly firstAt: number | null;\n\treadonly lastAt: number | null;\n}\n\n/**\n * Deterministic product metrics aggregated read-only from the journal ring.\n * Metrics the journal cannot source are reported as `not_sampled` with the\n * reason instead of being fabricated.\n */\nexport interface MetricsSnapshotV1 {\n\treadonly version: 1;\n\treadonly window: MetricsWindowV1;\n\treadonly turns: MetricsTurnsV1;\n\treadonly tools: MetricsToolErrorsV1;\n\treadonly cancelLatencyMs: MetricsCancelLatencySampledV1 | MetricsNotSampledV1;\n\treadonly tokens: MetricsTokensSampledV1 | MetricsNotSampledV1;\n\treadonly memoryRecall: MetricsMemoryRecallSampledV1 | MetricsNotSampledV1;\n}\n\n/** Aggregate options for {@link ObservabilityPlugin.getMetrics}. */\nexport interface ObservabilityMetricsOptions {\n\t/** Aggregate only the most recent `windowTurns` correlationId groups; omit for the whole journal. */\n\treadonly windowTurns?: number;\n}\n\n/**\n * Trace record kinds for `agent-forge/trace.v1`\n * (design: docs/design/会话轨迹与日志设计.md §4.2).\n */\nexport type TraceKindV1 =\n\t| \"session.start\"\n\t| \"turn.span\"\n\t| \"llm.request\"\n\t| \"llm.response\"\n\t| \"tool.span\"\n\t| \"lifecycle\"\n\t| \"error\"\n\t| \"decision\"\n\t| \"plugin.log\";\n\n/**\n * Runtime mirror of `TraceKindV1` for value-level consumers (CLI\n * `--trace-kind` validation), kept beside the union so the two compile-time\n * drift guards fail the build when they diverge in either direction:\n * - the `satisfies` below rejects a list entry the union no longer has;\n * - `_TRACE_KINDS_EXHAUSTIVE_CHECK` rejects a union member missing from the\n * list (`never` is not assignable from `true`).\n */\nexport const TRACE_KINDS_V1 = [\n\t\"session.start\",\n\t\"turn.span\",\n\t\"llm.request\",\n\t\"llm.response\",\n\t\"tool.span\",\n\t\"lifecycle\",\n\t\"error\",\n\t\"decision\",\n\t\"plugin.log\",\n] as const satisfies readonly TraceKindV1[];\n\ntype TraceKindListV1 = (typeof TRACE_KINDS_V1)[number];\nconst _TRACE_KINDS_EXHAUSTIVE_CHECK: TraceKindV1 extends TraceKindListV1 ? true : never = true;\n\n/** One append-only JSONL trace record (design §4.2). */\nexport interface TraceRecordV1 {\n\treadonly v: 1;\n\treadonly schema: \"agent-forge/trace.v1\";\n\t/** Monotonic sequence within one trace file, starting at 1. */\n\treadonly seq: number;\n\t/** Host timestamp (epoch ms). */\n\treadonly t: number;\n\t/** Duration of a closed span (ms). */\n\treadonly dur?: number;\n\treadonly kind: TraceKindV1;\n\t/** Turn/request correlation id, shared with lifecycle events and logs. */\n\treadonly traceId?: string;\n\treadonly level?: ObservabilityLogLevel;\n\t/** Kind-specific bounded data. */\n\treadonly data?: unknown;\n}\n\n/** Bounds for trace payload snapshots (design §4.3); each field defaults when omitted. */\nexport interface TraceSinkOptionsV1 {\n\t/** Strings longer than this truncate with a `…[truncated N chars]` suffix. Default 16_000. */\n\treadonly maxStringChars?: number;\n\t/**\n\t * Serialized snapshot budget in bytes, enforced largest-string-first inside\n\t * `llm.request.data.payload`. Strictly below the 512 KiB per-record budget.\n\t * Default 393_216 (384 KiB).\n\t */\n\treadonly maxSnapshotBytes?: number;\n\t/**\n\t * OTLP/HTTP+JSON trace export (design §8). Omitted keeps the exporter\n\t * absent: no transport, no buffering, file-sink behavior unchanged. The\n\t * endpoint is the OTLP base URL (e.g. `http://localhost:4318`); the\n\t * `/v1/traces` signal path is appended unless already present.\n\t */\n\treadonly otlp?: OtlpExportOptionsV1;\n}\n\n/** OTLP/HTTP+JSON exporter options; all durations/bounds are deterministic inputs. */\nexport interface OtlpExportOptionsV1 {\n\t/** OTLP base URL; `/v1/traces` is appended unless already present. */\n\treadonly endpoint: string;\n\t/**\n\t * Transport injection for deterministic tests and custom hosts (proxy,\n\t * in-process collector); defaults to `globalThis.fetch`. Only the fields\n\t * below are consumed by the plugin.\n\t */\n\treadonly fetchImpl?: (\n\t\turl: string,\n\t\tinit: { method: \"POST\"; headers: { \"content-type\": string }; body: string; signal: AbortSignal },\n\t) => Promise<{ ok: boolean; status: number }>;\n\t/** Per-flush HTTP timeout in ms. Default 5_000. */\n\treadonly timeoutMs?: number;\n\t/** Buffered-span count that triggers an early flush. Default 64. */\n\treadonly flushThreshold?: number;\n}\n\n/**\n * Telemetry export seam (design §8): the single attachment point for trace\n * exporters (first OTLP/HTTP+JSON; future Prometheus-style pull surfaces hook\n * here instead of growing bespoke taps). Hosts inject sinks through\n * `ObservabilityTraceConfig.exportSinks`; the plugin never reaches into host\n * internals and never lets a sink break event delivery.\n */\nexport interface ObservabilityExportSinkV1 {\n\t/** Called synchronously for every finalized trace record. Must not throw. */\n\treadonly onTraceRecord: (record: TraceRecordV1) => void;\n\t/** Best-effort flush; resolves after the batch is delivered or dropped. */\n\treadonly flush: (reason: string) => Promise<void>;\n}\n\n/**\n * Host-injected adapter for the trace sink (design §4.4). The host closure\n * owns session-path resolution, provider payload capture, and failure\n * reporting; the plugin never reaches into host internals.\n */\nexport interface ObservabilityHostAdapterV1 {\n\t/** Trace file path for the current session, or `null` when tracing is off. */\n\tgetTraceFilePath(): string | null;\n\t/** Host push of the finalized provider payload (sdk `onPayload` tail). */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Trace write-failure channel; the host forwards to the process log. */\n\tonSinkFailure(error: unknown): void;\n\t/**\n\t * Correlation id of the turn currently in flight, or `undefined` outside a\n\t * turn. Omitted keeps the phase-1 behavior: `llm.request` and `plugin.log`\n\t * records carry no traceId.\n\t */\n\tgetTurnCorrelationId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent agent session id recorded at session\n\t * creation (`SessionHeader.parentAgentSession`, session-manager.ts).\n\t * Omitted — or returning `undefined` when the header has no such field —\n\t * keeps the previous behavior: the `session.start` record carries no\n\t * `parentSessionId`.\n\t */\n\tgetParentAgentSessionId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent-turn correlation id that created this\n\t * session (`SessionHeader.parentTraceId`, session-manager.ts; frozen at\n\t * creation). Omitted — or returning `undefined` — keeps the previous\n\t * behavior: the `session.start` record carries no `parentTraceId`.\n\t */\n\tgetParentTraceId?(): string | undefined;\n\t/**\n\t * Plugin-to-host facade registration (D-075 S4 second batch). The plugin\n\t * entry calls it once after creating the plugin instance so the host's\n\t * assembly refs (provider payload capture, plugin.log mirror, /metrics\n\t * source) keep their embedded-era semantics; calling it again with\n\t * `undefined` on teardown clears them. Omitted on hosts that consume the\n\t * facade through another channel — the entry must probe before calling.\n\t */\n\tregisterFacade?(facade: ObservabilityPluginFacadeV1 | undefined): void;\n}\n\n/**\n * The narrow plugin-facing face the host consumes through\n * {@link ObservabilityHostAdapterV1.registerFacade}. The plugin instance is\n * structurally assignable to it.\n */\nexport interface ObservabilityPluginFacadeV1 {\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Optional: present since the plugin.log mirror (design §7.2). */\n\tmirrorPluginLog?(record: LoggerRecord): void;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n}\n\n/** Deterministic problem report; `reportId` is FNV-1a-32 hex over the canonical JSON of the report body. */\nexport interface ProblemReportV1 {\n\treadonly version: 1;\n\treadonly reportId: string;\n\treadonly createdAt: number;\n\treadonly reason: string;\n\treadonly session: ObservabilityProblemReportSessionV1;\n\treadonly lastError: ObservabilityProblemReportErrorV1 | null;\n\treadonly journalTail: readonly ObservabilityProblemReportTailEntryV1[];\n\treadonly counts: ObservabilityProblemReportCountsV1;\n\treadonly dropped: number;\n\treadonly redactionApplied: boolean;\n\t/** Whole-report projection rendered by the host output serializer. */\n\treadonly projection: {\n\t\treadonly surface: HostOutputSurfaceV1;\n\t\treadonly text: string;\n\t};\n\t/** Present only when the host declared the `output-artifacts` feature. */\n\treadonly artifact?: OutputArtifactRef;\n}\n\n/**\n * The observability plugin's public behavior face (journal read side, trace\n * explanation, deterministic metrics, problem-report export). The plugin\n * package returns this shape from its factory; hosts consume it through\n * {@link ObservabilityPluginFacadeV1} or their own references.\n */\nexport interface ObservabilityPlugin {\n\treadonly id: string;\n\tquery(filter?: ObservabilityQueryFilterV1): readonly ObservabilityJournalEntryV1[];\n\texplainTrace(traceId: string): ObservabilityTraceTimelineV1;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n\tstats(): ObservabilityJournalStatsV1;\n\texportProblemReport(input: { readonly sessionId: string; readonly reason: string }): Promise<ProblemReportV1>;\n\t/**\n\t * Receives the finalized provider payload from the host adapter and emits\n\t * one `llm.request` trace record with a bounded redacted snapshot (design\n\t * §4.3). No-op when no trace adapter was injected.\n\t */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/**\n\t * Receives one plugin LoggerAPI record forwarded by the host runtime sink\n\t * (`CapabilityRuntimeOptions.onLogRecord`) and appends a `plugin.log` trace\n\t * record (design §4.2/§7.2). No-op when no trace adapter was injected.\n\t * Must never call `api.logger`: the host forwards logger output here, so\n\t * logging from inside would recurse.\n\t */\n\tmirrorPluginLog(record: LoggerRecord): void;\n\tdispose(): void;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"observability.js","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAuLH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,eAAe;IACf,WAAW;IACX,aAAa;IACb,cAAc;IACd,WAAW;IACX,WAAW;IACX,OAAO;IACP,UAAU;IACV,YAAY;CAC8B,CAAC;AAG5C,MAAM,6BAA6B,GAAuD,IAAI,CAAC","sourcesContent":["/**\n * Public contracts for the first-party observability plugin (event journal +\n * problem reports + trace sink). The plugin package\n * (`@agent-forge/plugin-observability`) is the implementation; this module is\n * the single authoring authority for its public types and the host-facing\n * trace adapter contract. The host re-exports the surface from its historical\n * `plugins/observability-types.ts` path so existing imports keep resolving.\n *\n * Contains types only, plus the `TRACE_KINDS_V1` value mirror with its\n * compile-time drift guards (CLI `--trace-kind` validation consumes the\n * value).\n */\n\nimport type { LoggerRecord, OutputArtifactRef } from \"./public-api.ts\";\n\nexport type ObservabilityLogLevel = \"info\" | \"warn\" | \"error\";\n\n/**\n * Output projection surface carried by the problem report's whole-document\n * projection. Authority moved from the host's `host/output-serializer.ts`\n * (D-075 S4 second batch); the host re-exports it from there.\n */\nexport type HostOutputSurfaceV1 = \"tui\" | \"cli-text\" | \"inspector\";\n\n/** One bounded-journal record derived from a delivered lifecycle event. */\nexport interface ObservabilityJournalEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\t/** Session id extracted from the event data; null when the event has none. */\n\treadonly sessionId: string | null;\n\t/** Lifecycle event type, e.g. \"model.stream\" or \"tool.commit\". */\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\t/** Event correlation id, used as the trace grouping key. */\n\treadonly traceId?: string;\n\treadonly summary: string;\n\t/** Validated lifecycle event data, retained verbatim for diagnosis. */\n\treadonly data?: unknown;\n}\n\nexport interface ObservabilityQueryFilterV1 {\n\treadonly sessionId?: string;\n\treadonly stage?: string;\n\treadonly level?: ObservabilityLogLevel;\n\treadonly traceId?: string;\n\t/** When set, returns the most recent `limit` matching entries in order. */\n\treadonly limit?: number;\n}\n\nexport interface ObservabilityTraceStepV1 {\n\treadonly entry: ObservabilityJournalEntryV1;\n\t/** Milliseconds since the previous step; null on the first step. */\n\treadonly durationMs: number | null;\n}\n\nexport interface ObservabilityTraceTimelineV1 {\n\treadonly traceId: string;\n\treadonly steps: readonly ObservabilityTraceStepV1[];\n}\n\nexport interface ObservabilityJournalStatsV1 {\n\treadonly size: number;\n\treadonly dropped: number;\n}\n\nexport interface ObservabilityProblemReportSessionV1 {\n\treadonly sessionId: string;\n\treadonly entryCount: number;\n\treadonly firstSequence: number | null;\n\treadonly lastSequence: number | null;\n}\n\n/** Full error envelope of the most recent error-level entry for the session. */\nexport interface ObservabilityProblemReportErrorV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\t/** Lifecycle phase when the event carries one (model.stream). */\n\treadonly phase?: string;\n\treadonly traceId: string | null;\n\treadonly code: string;\n\treadonly message: string;\n}\n\nexport interface ObservabilityProblemReportTailEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\treadonly traceId: string | null;\n\treadonly summary: string;\n\t/**\n\t * Payload rendered through the host output serializer redaction channel;\n\t * internal field keys are replaced at any depth before export.\n\t */\n\treadonly data?: string;\n}\n\nexport interface ObservabilityProblemReportCountsV1 {\n\treadonly byStage: Readonly<Record<string, number>>;\n\treadonly byLevel: Readonly<Record<string, number>>;\n}\n\n/** A metric the journal cannot currently source; the reason names the missing event granularity. */\nexport interface MetricsNotSampledV1 {\n\treadonly status: \"not_sampled\";\n\treadonly reason: string;\n}\n\n/** Cancel-latency samples: delay from a cancel signal to the turn's terminal event. */\nexport interface MetricsCancelLatencySampledV1 {\n\treadonly status: \"sampled\";\n\treadonly count: number;\n\treadonly p50Ms: number | null;\n\treadonly p95Ms: number | null;\n}\n\n/** Token/cost totals summed over usage-bearing journal events. */\nexport interface MetricsTokensSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly totalTokens: number;\n}\n\n/** Memory recall hit rate over `memory_recall` tool executions in the window. */\nexport interface MetricsMemoryRecallSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly calls: number;\n\treadonly hits: number;\n\treadonly hitRate: number;\n}\n\nexport interface MetricsToolErrorsV1 {\n\treadonly calls: number;\n\treadonly errors: number;\n\t/** `null` when no tool executions occurred in the window. */\n\treadonly errorRate: number | null;\n\t/** Errors per tool name, descending by count then ascending by name. */\n\treadonly topErrors: readonly { readonly toolName: string; readonly errors: number }[];\n}\n\nexport interface MetricsTurnsV1 {\n\treadonly count: number;\n\treadonly successCount: number;\n\t/** `null` when no turns occurred in the window. */\n\treadonly successRate: number | null;\n\treadonly p50DurationMs: number | null;\n\treadonly p95DurationMs: number | null;\n}\n\n/** Window metadata: which turns were aggregated and their time span. */\nexport interface MetricsWindowV1 {\n\t/** Requested turn bound; `null` when the whole journal was aggregated. */\n\treadonly windowTurns: number | null;\n\treadonly sampledTurns: number;\n\treadonly firstAt: number | null;\n\treadonly lastAt: number | null;\n}\n\n/**\n * Deterministic product metrics aggregated read-only from the journal ring.\n * Metrics the journal cannot source are reported as `not_sampled` with the\n * reason instead of being fabricated.\n */\nexport interface MetricsSnapshotV1 {\n\treadonly version: 1;\n\treadonly window: MetricsWindowV1;\n\treadonly turns: MetricsTurnsV1;\n\treadonly tools: MetricsToolErrorsV1;\n\treadonly cancelLatencyMs: MetricsCancelLatencySampledV1 | MetricsNotSampledV1;\n\treadonly tokens: MetricsTokensSampledV1 | MetricsNotSampledV1;\n\treadonly memoryRecall: MetricsMemoryRecallSampledV1 | MetricsNotSampledV1;\n}\n\n/** Aggregate options for {@link ObservabilityPlugin.getMetrics}. */\nexport interface ObservabilityMetricsOptions {\n\t/** Aggregate only the most recent `windowTurns` correlationId groups; omit for the whole journal. */\n\treadonly windowTurns?: number;\n}\n\n/**\n * Trace record kinds for `agent-forge/trace.v1`\n * (design: docs/design/会话轨迹与日志设计.md §4.2).\n */\nexport type TraceKindV1 =\n\t| \"session.start\"\n\t| \"turn.span\"\n\t| \"llm.request\"\n\t| \"llm.response\"\n\t| \"tool.span\"\n\t| \"lifecycle\"\n\t| \"error\"\n\t| \"decision\"\n\t| \"plugin.log\";\n\n/**\n * Runtime mirror of `TraceKindV1` for value-level consumers (CLI\n * `--trace-kind` validation), kept beside the union so the two compile-time\n * drift guards fail the build when they diverge in either direction:\n * - the `satisfies` below rejects a list entry the union no longer has;\n * - `_TRACE_KINDS_EXHAUSTIVE_CHECK` rejects a union member missing from the\n * list (`never` is not assignable from `true`).\n */\nexport const TRACE_KINDS_V1 = [\n\t\"session.start\",\n\t\"turn.span\",\n\t\"llm.request\",\n\t\"llm.response\",\n\t\"tool.span\",\n\t\"lifecycle\",\n\t\"error\",\n\t\"decision\",\n\t\"plugin.log\",\n] as const satisfies readonly TraceKindV1[];\n\ntype TraceKindListV1 = (typeof TRACE_KINDS_V1)[number];\nconst _TRACE_KINDS_EXHAUSTIVE_CHECK: TraceKindV1 extends TraceKindListV1 ? true : never = true;\n\n/** One append-only JSONL trace record (design §4.2). */\nexport interface TraceRecordV1 {\n\treadonly v: 1;\n\treadonly schema: \"agent-forge/trace.v1\";\n\t/** Monotonic sequence within one trace file, starting at 1. */\n\treadonly seq: number;\n\t/** Host timestamp (epoch ms). */\n\treadonly t: number;\n\t/** Duration of a closed span (ms). */\n\treadonly dur?: number;\n\treadonly kind: TraceKindV1;\n\t/** Turn/request correlation id, shared with lifecycle events and logs. */\n\treadonly traceId?: string;\n\treadonly level?: ObservabilityLogLevel;\n\t/** Kind-specific bounded data. */\n\treadonly data?: unknown;\n}\n\n/** Bounds for trace payload snapshots (design §4.3); each field defaults when omitted. */\nexport interface TraceSinkOptionsV1 {\n\t/** Strings longer than this truncate with a `…[truncated N chars]` suffix. Default 16_000. */\n\treadonly maxStringChars?: number;\n\t/**\n\t * Serialized snapshot budget in bytes, enforced largest-string-first inside\n\t * `llm.request.data.payload`. Strictly below the 512 KiB per-record budget.\n\t * Default 393_216 (384 KiB).\n\t */\n\treadonly maxSnapshotBytes?: number;\n\t/**\n\t * OTLP/HTTP+JSON trace export (design §8). Omitted keeps the exporter\n\t * absent: no transport, no buffering, file-sink behavior unchanged. The\n\t * endpoint is the OTLP base URL (e.g. `http://localhost:4318`); the\n\t * `/v1/traces` signal path is appended unless already present.\n\t */\n\treadonly otlp?: OtlpExportOptionsV1;\n}\n\n/** OTLP/HTTP+JSON exporter options; all durations/bounds are deterministic inputs. */\nexport interface OtlpExportOptionsV1 {\n\t/** OTLP base URL; `/v1/traces` is appended unless already present. */\n\treadonly endpoint: string;\n\t/**\n\t * Transport injection for deterministic tests and custom hosts (proxy,\n\t * in-process collector); defaults to `globalThis.fetch`. Only the fields\n\t * below are consumed by the plugin.\n\t */\n\treadonly fetchImpl?: (\n\t\turl: string,\n\t\tinit: { method: \"POST\"; headers: { \"content-type\": string }; body: string; signal: AbortSignal },\n\t) => Promise<{ ok: boolean; status: number }>;\n\t/** Per-flush HTTP timeout in ms. Default 5_000. */\n\treadonly timeoutMs?: number;\n\t/** Buffered-span count that triggers an early flush. Default 64. */\n\treadonly flushThreshold?: number;\n}\n\n/**\n * Telemetry export seam (design §8): the single attachment point for trace\n * exporters (first OTLP/HTTP+JSON; future Prometheus-style pull surfaces hook\n * here instead of growing bespoke taps). Hosts inject sinks through\n * `ObservabilityTraceConfig.exportSinks`; the plugin never reaches into host\n * internals and never lets a sink break event delivery.\n */\nexport interface ObservabilityExportSinkV1 {\n\t/** Called synchronously for every finalized trace record. Must not throw. */\n\treadonly onTraceRecord: (record: TraceRecordV1) => void;\n\t/** Best-effort flush; resolves after the batch is delivered or dropped. */\n\treadonly flush: (reason: string) => Promise<void>;\n}\n\n/**\n * Host-injected adapter for the trace sink (design §4.4). The host closure\n * owns session-path resolution, provider payload capture, and failure\n * reporting; the plugin never reaches into host internals.\n */\nexport interface ObservabilityHostAdapterV1 {\n\t/** Trace file path for the current session, or `null` when tracing is off. */\n\tgetTraceFilePath(): string | null;\n\t/** Host push of the finalized provider payload (sdk `onPayload` tail). */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Trace write-failure channel; the host forwards to the process log. */\n\tonSinkFailure(error: unknown): void;\n\t/**\n\t * Correlation id of the turn currently in flight, or `undefined` outside a\n\t * turn. Omitted keeps the phase-1 behavior: `llm.request` and `plugin.log`\n\t * records carry no traceId.\n\t */\n\tgetTurnCorrelationId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent agent session id recorded at session\n\t * creation (`SessionHeader.parentAgentSession`, session-manager.ts).\n\t * Omitted — or returning `undefined` when the header has no such field —\n\t * keeps the previous behavior: the `session.start` record carries no\n\t * `parentSessionId`.\n\t */\n\tgetParentAgentSessionId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent-turn correlation id that created this\n\t * session (`SessionHeader.parentTraceId`, session-manager.ts; frozen at\n\t * creation). Omitted — or returning `undefined` — keeps the previous\n\t * behavior: the `session.start` record carries no `parentTraceId`.\n\t */\n\tgetParentTraceId?(): string | undefined;\n\t/**\n\t * Plugin-to-host facade registration (D-075 S4 second batch). The plugin\n\t * entry calls it once after creating the plugin instance so the host's\n\t * assembly refs (provider payload capture, plugin.log mirror, /metrics\n\t * source) keep their embedded-era semantics; calling it again with\n\t * `undefined` on teardown clears them. Omitted on hosts that consume the\n\t * facade through another channel — the entry must probe before calling.\n\t */\n\tregisterFacade?(facade: ObservabilityPluginFacadeV1 | undefined): void;\n}\n\n/**\n * The narrow plugin-facing face the host consumes through\n * {@link ObservabilityHostAdapterV1.registerFacade}. The plugin instance is\n * structurally assignable to it.\n */\nexport interface ObservabilityPluginFacadeV1 {\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Optional: present since the plugin.log mirror (design §7.2). */\n\tmirrorPluginLog?(record: LoggerRecord): void;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n}\n\n/** Deterministic problem report; `reportId` is FNV-1a-32 hex over the canonical JSON of the report body. */\nexport interface ProblemReportV1 {\n\treadonly version: 1;\n\treadonly reportId: string;\n\treadonly createdAt: number;\n\treadonly reason: string;\n\treadonly session: ObservabilityProblemReportSessionV1;\n\treadonly lastError: ObservabilityProblemReportErrorV1 | null;\n\treadonly journalTail: readonly ObservabilityProblemReportTailEntryV1[];\n\treadonly counts: ObservabilityProblemReportCountsV1;\n\treadonly dropped: number;\n\treadonly redactionApplied: boolean;\n\t/** Whole-report projection rendered by the host output serializer. */\n\treadonly projection: {\n\t\treadonly surface: HostOutputSurfaceV1;\n\t\treadonly text: string;\n\t};\n\t/** Present only when the host declared the `output-artifacts` feature. */\n\treadonly artifact?: OutputArtifactRef;\n}\n\n/**\n * The observability plugin's public behavior face (journal read side, trace\n * explanation, deterministic metrics, problem-report export). The plugin\n * package returns this shape from its factory; hosts consume it through\n * {@link ObservabilityPluginFacadeV1} or their own references.\n */\nexport interface ObservabilityPlugin {\n\treadonly id: string;\n\tquery(filter?: ObservabilityQueryFilterV1): readonly ObservabilityJournalEntryV1[];\n\texplainTrace(traceId: string): ObservabilityTraceTimelineV1;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n\tstats(): ObservabilityJournalStatsV1;\n\texportProblemReport(input: { readonly sessionId: string; readonly reason: string }): Promise<ProblemReportV1>;\n\t/**\n\t * Receives the finalized provider payload from the host adapter and emits\n\t * one `llm.request` trace record with a bounded redacted snapshot (design\n\t * §4.3). No-op when no trace adapter was injected.\n\t */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/**\n\t * Receives one plugin LoggerAPI record forwarded by the host runtime sink\n\t * (`CapabilityRuntimeOptions.onLogRecord`) and appends a `plugin.log` trace\n\t * record (design §4.2/§7.2). No-op when no trace adapter was injected.\n\t * Must never call `api.logger`: the host forwards logger output here, so\n\t * logging from inside would recurse.\n\t */\n\tmirrorPluginLog(record: LoggerRecord): void;\n\tdispose(): void;\n}\n"]}
1
+ {"version":3,"file":"observability.js","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAuLH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,eAAe;IACf,WAAW;IACX,aAAa;IACb,cAAc;IACd,WAAW;IACX,WAAW;IACX,OAAO;IACP,UAAU;IACV,YAAY;CAC8B,CAAC;AAG5C,MAAM,6BAA6B,GAAuD,IAAI,CAAC","sourcesContent":["/**\n * Public contracts for the first-party observability plugin (event journal +\n * problem reports + trace sink). The plugin package\n * (`@agent-forge/plugin-observability`) is the implementation; this module is\n * the single authoring authority for its public types and the host-facing\n * trace adapter contract. The host re-exports the surface from its historical\n * `plugins/observability-types.ts` path so existing imports keep resolving.\n *\n * Contains types only, plus the `TRACE_KINDS_V1` value mirror with its\n * compile-time drift guards (CLI `--trace-kind` validation consumes the\n * value).\n */\n\nimport type { LoggerRecord, OutputArtifactRef } from \"./public-api.ts\";\n\nexport type ObservabilityLogLevel = \"info\" | \"warn\" | \"error\";\n\n/**\n * Output projection surface carried by the problem report's whole-document\n * projection. Authority moved from the host's `host/output-serializer.ts`\n * (D-075 S4 second batch); the host re-exports it from there.\n */\nexport type HostOutputSurfaceV1 = \"tui\" | \"cli-text\" | \"inspector\";\n\n/** One bounded-journal record derived from a delivered lifecycle event. */\nexport interface ObservabilityJournalEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\t/** Session id extracted from the event data; null when the event has none. */\n\treadonly sessionId: string | null;\n\t/** Lifecycle event type, e.g. \"model.stream\" or \"tool.commit\". */\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\t/** Event correlation id, used as the trace grouping key. */\n\treadonly traceId?: string;\n\treadonly summary: string;\n\t/** Validated lifecycle event data, retained verbatim for diagnosis. */\n\treadonly data?: unknown;\n}\n\nexport interface ObservabilityQueryFilterV1 {\n\treadonly sessionId?: string;\n\treadonly stage?: string;\n\treadonly level?: ObservabilityLogLevel;\n\treadonly traceId?: string;\n\t/** When set, returns the most recent `limit` matching entries in order. */\n\treadonly limit?: number;\n}\n\nexport interface ObservabilityTraceStepV1 {\n\treadonly entry: ObservabilityJournalEntryV1;\n\t/** Milliseconds since the previous step; null on the first step. */\n\treadonly durationMs: number | null;\n}\n\nexport interface ObservabilityTraceTimelineV1 {\n\treadonly traceId: string;\n\treadonly steps: readonly ObservabilityTraceStepV1[];\n}\n\nexport interface ObservabilityJournalStatsV1 {\n\treadonly size: number;\n\treadonly dropped: number;\n}\n\nexport interface ObservabilityProblemReportSessionV1 {\n\treadonly sessionId: string;\n\treadonly entryCount: number;\n\treadonly firstSequence: number | null;\n\treadonly lastSequence: number | null;\n}\n\n/** Full error envelope of the most recent error-level entry for the session. */\nexport interface ObservabilityProblemReportErrorV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\t/** Lifecycle phase when the event carries one (model.stream). */\n\treadonly phase?: string;\n\treadonly traceId: string | null;\n\treadonly code: string;\n\treadonly message: string;\n}\n\nexport interface ObservabilityProblemReportTailEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\treadonly traceId: string | null;\n\treadonly summary: string;\n\t/**\n\t * Payload rendered through the host output serializer redaction channel;\n\t * internal field keys are replaced at any depth before export.\n\t */\n\treadonly data?: string;\n}\n\nexport interface ObservabilityProblemReportCountsV1 {\n\treadonly byStage: Readonly<Record<string, number>>;\n\treadonly byLevel: Readonly<Record<string, number>>;\n}\n\n/** A metric the journal cannot currently source; the reason names the missing event granularity. */\nexport interface MetricsNotSampledV1 {\n\treadonly status: \"not_sampled\";\n\treadonly reason: string;\n}\n\n/** Cancel-latency samples: delay from a cancel signal to the turn's terminal event. */\nexport interface MetricsCancelLatencySampledV1 {\n\treadonly status: \"sampled\";\n\treadonly count: number;\n\treadonly p50Ms: number | null;\n\treadonly p95Ms: number | null;\n}\n\n/** Token totals summed over usage-bearing journal events. */\nexport interface MetricsTokensSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly totalTokens: number;\n}\n\n/** Memory recall hit rate over `memory_recall` tool executions in the window. */\nexport interface MetricsMemoryRecallSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly calls: number;\n\treadonly hits: number;\n\treadonly hitRate: number;\n}\n\nexport interface MetricsToolErrorsV1 {\n\treadonly calls: number;\n\treadonly errors: number;\n\t/** `null` when no tool executions occurred in the window. */\n\treadonly errorRate: number | null;\n\t/** Errors per tool name, descending by count then ascending by name. */\n\treadonly topErrors: readonly { readonly toolName: string; readonly errors: number }[];\n}\n\nexport interface MetricsTurnsV1 {\n\treadonly count: number;\n\treadonly successCount: number;\n\t/** `null` when no turns occurred in the window. */\n\treadonly successRate: number | null;\n\treadonly p50DurationMs: number | null;\n\treadonly p95DurationMs: number | null;\n}\n\n/** Window metadata: which turns were aggregated and their time span. */\nexport interface MetricsWindowV1 {\n\t/** Requested turn bound; `null` when the whole journal was aggregated. */\n\treadonly windowTurns: number | null;\n\treadonly sampledTurns: number;\n\treadonly firstAt: number | null;\n\treadonly lastAt: number | null;\n}\n\n/**\n * Deterministic product metrics aggregated read-only from the journal ring.\n * Metrics the journal cannot source are reported as `not_sampled` with the\n * reason instead of being fabricated.\n */\nexport interface MetricsSnapshotV1 {\n\treadonly version: 1;\n\treadonly window: MetricsWindowV1;\n\treadonly turns: MetricsTurnsV1;\n\treadonly tools: MetricsToolErrorsV1;\n\treadonly cancelLatencyMs: MetricsCancelLatencySampledV1 | MetricsNotSampledV1;\n\treadonly tokens: MetricsTokensSampledV1 | MetricsNotSampledV1;\n\treadonly memoryRecall: MetricsMemoryRecallSampledV1 | MetricsNotSampledV1;\n}\n\n/** Aggregate options for {@link ObservabilityPlugin.getMetrics}. */\nexport interface ObservabilityMetricsOptions {\n\t/** Aggregate only the most recent `windowTurns` correlationId groups; omit for the whole journal. */\n\treadonly windowTurns?: number;\n}\n\n/**\n * Trace record kinds for `agent-forge/trace.v1`\n * (design: docs/design/会话轨迹与日志设计.md §4.2).\n */\nexport type TraceKindV1 =\n\t| \"session.start\"\n\t| \"turn.span\"\n\t| \"llm.request\"\n\t| \"llm.response\"\n\t| \"tool.span\"\n\t| \"lifecycle\"\n\t| \"error\"\n\t| \"decision\"\n\t| \"plugin.log\";\n\n/**\n * Runtime mirror of `TraceKindV1` for value-level consumers (CLI\n * `--trace-kind` validation), kept beside the union so the two compile-time\n * drift guards fail the build when they diverge in either direction:\n * - the `satisfies` below rejects a list entry the union no longer has;\n * - `_TRACE_KINDS_EXHAUSTIVE_CHECK` rejects a union member missing from the\n * list (`never` is not assignable from `true`).\n */\nexport const TRACE_KINDS_V1 = [\n\t\"session.start\",\n\t\"turn.span\",\n\t\"llm.request\",\n\t\"llm.response\",\n\t\"tool.span\",\n\t\"lifecycle\",\n\t\"error\",\n\t\"decision\",\n\t\"plugin.log\",\n] as const satisfies readonly TraceKindV1[];\n\ntype TraceKindListV1 = (typeof TRACE_KINDS_V1)[number];\nconst _TRACE_KINDS_EXHAUSTIVE_CHECK: TraceKindV1 extends TraceKindListV1 ? true : never = true;\n\n/** One append-only JSONL trace record (design §4.2). */\nexport interface TraceRecordV1 {\n\treadonly v: 1;\n\treadonly schema: \"agent-forge/trace.v1\";\n\t/** Monotonic sequence within one trace file, starting at 1. */\n\treadonly seq: number;\n\t/** Host timestamp (epoch ms). */\n\treadonly t: number;\n\t/** Duration of a closed span (ms). */\n\treadonly dur?: number;\n\treadonly kind: TraceKindV1;\n\t/** Turn/request correlation id, shared with lifecycle events and logs. */\n\treadonly traceId?: string;\n\treadonly level?: ObservabilityLogLevel;\n\t/** Kind-specific bounded data. */\n\treadonly data?: unknown;\n}\n\n/** Bounds for trace payload snapshots (design §4.3); each field defaults when omitted. */\nexport interface TraceSinkOptionsV1 {\n\t/** Strings longer than this truncate with a `…[truncated N chars]` suffix. Default 16_000. */\n\treadonly maxStringChars?: number;\n\t/**\n\t * Serialized snapshot budget in bytes, enforced largest-string-first inside\n\t * `llm.request.data.payload`. Strictly below the 512 KiB per-record budget.\n\t * Default 393_216 (384 KiB).\n\t */\n\treadonly maxSnapshotBytes?: number;\n\t/**\n\t * OTLP/HTTP+JSON trace export (design §8). Omitted keeps the exporter\n\t * absent: no transport, no buffering, file-sink behavior unchanged. The\n\t * endpoint is the OTLP base URL (e.g. `http://localhost:4318`); the\n\t * `/v1/traces` signal path is appended unless already present.\n\t */\n\treadonly otlp?: OtlpExportOptionsV1;\n}\n\n/** OTLP/HTTP+JSON exporter options; all durations/bounds are deterministic inputs. */\nexport interface OtlpExportOptionsV1 {\n\t/** OTLP base URL; `/v1/traces` is appended unless already present. */\n\treadonly endpoint: string;\n\t/**\n\t * Transport injection for deterministic tests and custom hosts (proxy,\n\t * in-process collector); defaults to `globalThis.fetch`. Only the fields\n\t * below are consumed by the plugin.\n\t */\n\treadonly fetchImpl?: (\n\t\turl: string,\n\t\tinit: { method: \"POST\"; headers: { \"content-type\": string }; body: string; signal: AbortSignal },\n\t) => Promise<{ ok: boolean; status: number }>;\n\t/** Per-flush HTTP timeout in ms. Default 5_000. */\n\treadonly timeoutMs?: number;\n\t/** Buffered-span count that triggers an early flush. Default 64. */\n\treadonly flushThreshold?: number;\n}\n\n/**\n * Telemetry export seam (design §8): the single attachment point for trace\n * exporters (first OTLP/HTTP+JSON; future Prometheus-style pull surfaces hook\n * here instead of growing bespoke taps). Hosts inject sinks through\n * `ObservabilityTraceConfig.exportSinks`; the plugin never reaches into host\n * internals and never lets a sink break event delivery.\n */\nexport interface ObservabilityExportSinkV1 {\n\t/** Called synchronously for every finalized trace record. Must not throw. */\n\treadonly onTraceRecord: (record: TraceRecordV1) => void;\n\t/** Best-effort flush; resolves after the batch is delivered or dropped. */\n\treadonly flush: (reason: string) => Promise<void>;\n}\n\n/**\n * Host-injected adapter for the trace sink (design §4.4). The host closure\n * owns session-path resolution, provider payload capture, and failure\n * reporting; the plugin never reaches into host internals.\n */\nexport interface ObservabilityHostAdapterV1 {\n\t/** Trace file path for the current session, or `null` when tracing is off. */\n\tgetTraceFilePath(): string | null;\n\t/** Host push of the finalized provider payload (sdk `onPayload` tail). */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Trace write-failure channel; the host forwards to the process log. */\n\tonSinkFailure(error: unknown): void;\n\t/**\n\t * Correlation id of the turn currently in flight, or `undefined` outside a\n\t * turn. Omitted keeps the phase-1 behavior: `llm.request` and `plugin.log`\n\t * records carry no traceId.\n\t */\n\tgetTurnCorrelationId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent agent session id recorded at session\n\t * creation (`SessionHeader.parentAgentSession`, session-manager.ts).\n\t * Omitted — or returning `undefined` when the header has no such field —\n\t * keeps the previous behavior: the `session.start` record carries no\n\t * `parentSessionId`.\n\t */\n\tgetParentAgentSessionId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent-turn correlation id that created this\n\t * session (`SessionHeader.parentTraceId`, session-manager.ts; frozen at\n\t * creation). Omitted — or returning `undefined` — keeps the previous\n\t * behavior: the `session.start` record carries no `parentTraceId`.\n\t */\n\tgetParentTraceId?(): string | undefined;\n\t/**\n\t * Plugin-to-host facade registration (D-075 S4 second batch). The plugin\n\t * entry calls it once after creating the plugin instance so the host's\n\t * assembly refs (provider payload capture, plugin.log mirror, /metrics\n\t * source) keep their embedded-era semantics; calling it again with\n\t * `undefined` on teardown clears them. Omitted on hosts that consume the\n\t * facade through another channel — the entry must probe before calling.\n\t */\n\tregisterFacade?(facade: ObservabilityPluginFacadeV1 | undefined): void;\n}\n\n/**\n * The narrow plugin-facing face the host consumes through\n * {@link ObservabilityHostAdapterV1.registerFacade}. The plugin instance is\n * structurally assignable to it.\n */\nexport interface ObservabilityPluginFacadeV1 {\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Optional: present since the plugin.log mirror (design §7.2). */\n\tmirrorPluginLog?(record: LoggerRecord): void;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n}\n\n/** Deterministic problem report; `reportId` is FNV-1a-32 hex over the canonical JSON of the report body. */\nexport interface ProblemReportV1 {\n\treadonly version: 1;\n\treadonly reportId: string;\n\treadonly createdAt: number;\n\treadonly reason: string;\n\treadonly session: ObservabilityProblemReportSessionV1;\n\treadonly lastError: ObservabilityProblemReportErrorV1 | null;\n\treadonly journalTail: readonly ObservabilityProblemReportTailEntryV1[];\n\treadonly counts: ObservabilityProblemReportCountsV1;\n\treadonly dropped: number;\n\treadonly redactionApplied: boolean;\n\t/** Whole-report projection rendered by the host output serializer. */\n\treadonly projection: {\n\t\treadonly surface: HostOutputSurfaceV1;\n\t\treadonly text: string;\n\t};\n\t/** Present only when the host declared the `output-artifacts` feature. */\n\treadonly artifact?: OutputArtifactRef;\n}\n\n/**\n * The observability plugin's public behavior face (journal read side, trace\n * explanation, deterministic metrics, problem-report export). The plugin\n * package returns this shape from its factory; hosts consume it through\n * {@link ObservabilityPluginFacadeV1} or their own references.\n */\nexport interface ObservabilityPlugin {\n\treadonly id: string;\n\tquery(filter?: ObservabilityQueryFilterV1): readonly ObservabilityJournalEntryV1[];\n\texplainTrace(traceId: string): ObservabilityTraceTimelineV1;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n\tstats(): ObservabilityJournalStatsV1;\n\texportProblemReport(input: { readonly sessionId: string; readonly reason: string }): Promise<ProblemReportV1>;\n\t/**\n\t * Receives the finalized provider payload from the host adapter and emits\n\t * one `llm.request` trace record with a bounded redacted snapshot (design\n\t * §4.3). No-op when no trace adapter was injected.\n\t */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/**\n\t * Receives one plugin LoggerAPI record forwarded by the host runtime sink\n\t * (`CapabilityRuntimeOptions.onLogRecord`) and appends a `plugin.log` trace\n\t * record (design §4.2/§7.2). No-op when no trace adapter was injected.\n\t * Must never call `api.logger`: the host forwards logger output here, so\n\t * logging from inside would recurse.\n\t */\n\tmirrorPluginLog(record: LoggerRecord): void;\n\tdispose(): void;\n}\n"]}
@@ -8,6 +8,7 @@
8
8
  * surface (D-075 S2); the host (`@agent-forge/agent-forge`) re-exports it from
9
9
  * its historical module path so existing imports keep resolving.
10
10
  */
11
+ import type { DurableServiceV1 } from "./durable.ts";
11
12
  import type { MemoryStorageComponentsV1 } from "./memory.ts";
12
13
  import type { ObservabilityHostAdapterV1, TraceSinkOptionsV1 } from "./observability.ts";
13
14
  /** @experimental Promoted out of draft only when a real consumer drives stabilization. */
@@ -220,6 +221,21 @@ export interface HostCapabilities {
220
221
  * 凭据条目投影(`api.host.modelCatalog`)。缺席 = 宿主不提供目录读面。
221
222
  */
222
223
  modelCatalog?: ModelCatalogV1;
224
+ /**
225
+ * Durable agent-task results store (durable replay assembly, 2026-10-13
226
+ * 用户裁决"子代理任务结果必须可回放", host service contract). Runtime
227
+ * service, not a type: the host passes its durable facility (`store` plus
228
+ * its writer id) and the first-party subagent-delegate plugin binds it as
229
+ * the scheduler's `results` store, so terminal records survive in the
230
+ * host's durable log outside the sessions tree. The host owns the writer
231
+ * lease lifecycle (the CLI default store self-heals the lease per persist;
232
+ * the serve assembly claims once at startup). Absent = the plugin degrades
233
+ * to in-memory records only (no durable replay), never a load failure.
234
+ */
235
+ agentTaskResults?: {
236
+ readonly store: DurableServiceV1;
237
+ readonly writerId: string;
238
+ };
223
239
  }
224
240
  export interface DisposableRegistration {
225
241
  id: string;
@@ -1701,6 +1717,8 @@ export declare const SUMMARY_MODEL_INVOCATION_ERROR_CODES: {
1701
1717
  readonly lengthCap: "summary_length_cap";
1702
1718
  /** The model attempted a tool call in a tool-less completion. */
1703
1719
  readonly toolCall: "summary_tool_call";
1720
+ /** Completed response has no non-whitespace summary text. */
1721
+ readonly emptyText: "summary_empty_text";
1704
1722
  /** The transport threw; `message` mirrors the original error text. */
1705
1723
  readonly transportError: "summary_transport_error";
1706
1724
  };