@x-otto/coding 0.0.1-alpha.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["aiStream","evaluateClaudeTrust","evaluatePluginTrust","discoverPlugins","evaluatePluginTrust","parseDotMcpJson","resolveClaudeMcpServers","evaluatePluginTrust","logger","withTimeout","requireModule","evaluatePluginTrust","logger","spec","defaultMaxToolTurnExtensions","globalAgentJobRegistry","isProjectTrusted","workspaceKey","toWorkspaceKey","logger","discoverPlugins","evaluatePluginTrust","evaluatePluginTrust","evaluatePluginTrust","resolveActions","spec","logger","spec","spec","discoverFileCommands","logger","MAX_OUTPUT","logger","evaluatePluginTrust","logger","evaluatePluginTrust","logger","discoverPlugins","evaluatePluginTrust","evaluateClaudeTrust","logger","evaluatePluginTrust","logger","evaluatePluginTrust","logger","logger","logger","tmpDir","entryPath","logger","requireModule","isPluginTrusted","evaluatePluginTrust","logger","tmpDir","logger","logger","isPluginTrusted","evaluatePluginTrust","logger","evaluatePluginTrust","logger","evaluatePluginTrust","DEFAULT_WORKSPACE_KEY","logger","logger","findWorkspaceRoot","PLUGIN_MANIFEST_FILENAME","parsePluginManifest","discoverPlugins","DEFAULT_WORKSPACE_KEY","resolveSessionTools","evaluatePluginTrust","logger","evaluatePluginTrust","globalProcessRegistry","resolveProjectConfigTrusted","discoverPlugins","createBuiltinHashtags","createFileProvider","globalProcessRegistry","createSessionStableInjectionHooks","createSessionStableInjectionHooks","createSessionStableInjectionHooks","initializer","util.jsonStringifyReplacer","core.$ZodAsyncError","util.finalizeIssue","core.config","parse","errors.$ZodRealError","parseAsync","errors.$ZodError","safeParse","safeParseAsync","encode","decode","encodeAsync","decodeAsync","safeEncode","safeDecode","safeEncodeAsync","safeDecodeAsync","duration","_emoji","date","time","datetime","string","number","util.floatSafeRemainder","util.NUMBER_FORMAT_RANGES","regexes.integer","util.nullish","util.getLengthableOrigin","regexes.lowercase","regexes.uppercase","util.escapeRegex","util.aborted","core.$ZodAsyncError","safeParse","safeParseAsync","regexes.string","regexes.guid","regexes.uuid","regexes.email","regexes.emoji","regexes.nanoid","regexes.cuid","regexes.cuid2","regexes.ulid","regexes.xid","regexes.ksuid","regexes.datetime","regexes.date","regexes.time","regexes.duration","regexes.ipv4","regexes.ipv6","regexes.cidrv4","regexes.cidrv6","regexes.base64","regexes.base64url","regexes.e164","regexes.number","util.prefixIssues","util.optionalKeys","util.cached","isObject","util.isObject","util.esc","allowsEval","util.allowsEval","util.finalizeIssue","core.config","util.cleanRegex","util.isPlainObject","util.getEnumValues","util.escapeRegex","core.$ZodEncodeError","util.issue","util.normalizeParams","checks.$ZodCheckLessThan","checks.$ZodCheckGreaterThan","checks.$ZodCheckMultipleOf","checks.$ZodCheckMaxLength","checks.$ZodCheckMinLength","checks.$ZodCheckLengthEquals","checks.$ZodCheckRegex","checks.$ZodCheckLowerCase","checks.$ZodCheckUpperCase","checks.$ZodCheckIncludes","checks.$ZodCheckStartsWith","checks.$ZodCheckEndsWith","checks.$ZodCheckOverwrite","util.slugify","issue","util.issue","checks.$ZodCheck","describe","meta","process","process","core._isoDateTime","core._isoDate","core._isoTime","core._isoDuration","core.formatError","core.flattenError","util.jsonStringifyReplacer","core.$constructor","util.mergeDefs","core.clone","parse.parse","parse.safeParse","parse.parseAsync","parse.safeParseAsync","parse.encode","parse.decode","parse.encodeAsync","parse.decodeAsync","parse.safeEncode","parse.safeDecode","parse.safeEncodeAsync","parse.safeDecodeAsync","checks.overwrite","processors.stringProcessor","checks.regex","checks.includes","checks.startsWith","checks.endsWith","checks.minLength","checks.maxLength","checks.length","checks.lowercase","checks.uppercase","checks.trim","checks.normalize","checks.toLowerCase","checks.toUpperCase","checks.slugify","core._email","core._url","core._jwt","core._emoji","core._guid","core._uuid","core._uuidv4","core._uuidv6","core._uuidv7","core._nanoid","core._cuid","core._cuid2","core._ulid","core._base64","core._base64url","core._xid","core._ksuid","core._ipv4","core._ipv6","core._cidrv4","core._cidrv6","core._e164","iso.datetime","iso.date","iso.time","iso.duration","core._string","processors.numberProcessor","checks.gt","checks.gte","checks.lt","checks.lte","checks.multipleOf","core._number","core._int","processors.unknownProcessor","core._unknown","processors.neverProcessor","core._never","processors.arrayProcessor","core._array","processors.objectProcessor","util.extend","util.safeExtend","util.merge","util.pick","util.omit","util.partial","util.required","util.normalizeParams","processors.unionProcessor","processors.intersectionProcessor","processors.enumProcessor","processors.transformProcessor","core.$ZodEncodeError","issue","util.issue","processors.optionalProcessor","processors.nullableProcessor","processors.defaultProcessor","util.shallowClone","processors.prefaultProcessor","processors.nonoptionalProcessor","processors.catchProcessor","processors.pipeProcessor","processors.readonlyProcessor","processors.customProcessor","core._refine","core._superRefine","core.describe","core.meta","z.object","z.string","z.array","z.number","WORKSPACE_KEY_FALLBACK","z.object","z.string","z.enum","z.number","z.array","z.object","z.string","z.array","z.enum","z.number","createClaudeHookBridgeHooks","globalProcessRegistry","globalProcessRegistry","createPluginInputRegistry","modelMethods.getAllModels","modelMethods.resolveModel","modelMethods.listProviders","modelMethods.unregisterHook","modelMethods.hydrateModelCache","modelMethods.getSessionAvailableModels","modelMethods.refreshModels","reloadCapabilitiesImpl","ensureSettingsLoadedHelper","permissionMethods.allowToolAlways","permissionMethods.getPermissionMode","permissionMethods.setPermissionMode","permissionMethods.getSessionAlwaysAllow","bashAlwaysAllowPrefixFn","isWellFormedAllowEntryFn","permissionMethods.disallowToolAlways","completeWorkspaceDependentStartupImpl","needsWorkspaceInitImpl","confirmWorkspaceInitImpl","adoptParentWorkspaceImpl","createLocalWorkspaceImpl","setWorkspaceMemorySyncEnabledImpl","sweepOrphanTraceFilesImpl","sessionMethods.createSession","sessionMethods.getSession","sessionMethods.shareSession","sessionMethods.getActiveSession","sessionMethods.listSessions","sessionMethods.listAllSessions","sessionMethods.removeSession","sessionMethods.archiveSession","sessionMethods.forkSession","modelMethods.buildModelCatalog","globalAgentJobRegistry","evaluatePluginTrust","logger","evaluatePluginTrust"],"sources":["../src/app/storage-wiring.ts","../src/session/capability-tool-map.ts","../src/app/agent-services-setup.ts","../src/app/permission-sync.ts","../src/app/sandbox-controller.ts","../src/plugin/mcp.ts","../src/app/mcp-controller.ts","../src/app/extension-manager.ts","../src/feedback/feedback-registry.ts","../src/input/sigil-resolver-registry.ts","../src/input/sigil-expand.ts","../src/plugin/load-compiled-contribution.ts","../src/plugin/a2ui-renderer-loader.ts","../src/agent/agent-tool-names.ts","../src/session/model-resolver.ts","../src/session/tool-resolver.ts","../src/session/types.ts","../src/session/session-config-resolver.ts","../src/agent/task-output.ts","../src/agent/detached-run.ts","../src/app/agent-job-service.ts","../src/app/applied-diff-store.ts","../src/app/agent-job-wiring.ts","../src/app/app-agent-jobs.ts","../src/session/assistant-utils.ts","../src/session/task-runner.ts","../src/app/notification-setup.ts","../src/app/model-catalog-manager.ts","../src/app/app-model-methods.ts","../src/app/app-notification-methods.ts","../src/app/app-permission-methods.ts","../src/app/app-session-methods.ts","../src/session/session-tools-sync.ts","../src/plugin/plugin-panel-manager.ts","../src/plugin/status-items.ts","../src/plugin/menu-items.ts","../src/plugin/renderer-loader.ts","../src/plugin/a2ui-component-loader.ts","../src/plugin/status-widget-loader.ts","../src/plugin/file-viewer-loader.ts","../src/app/plugin-contributions-facade.ts","../src/plugin/plugin-hooks.ts","../src/plugin/plugin-lifecycle.ts","../src/plugin/path-guard.ts","../src/plugin/plugin-i18n-loader.ts","../src/agent/declarative-agents.ts","../src/agent/declarative-teams.ts","../src/app/capability-reloader.ts","../src/plugin/panel-backend-controller.ts","../src/plugin/plugin-log-writer.ts","../src/plugin/module-wiring.ts","../src/plugin/plugin-health-tracker.ts","../src/plugin/plugin-capability-quota.ts","../src/plugin/inquiry-center.ts","../src/plugin/plugin-service-entry-template.ts","../src/plugin/plugin-service-pool.ts","../src/plugin/transcript-projection.ts","../src/plugin/module-loader.ts","../src/plugin/plugin-isolation-entry-template.ts","../src/plugin/isolated-plugin-transport.ts","../src/plugin/plugin-worker-pool.ts","../src/plugin/plugin-process-pool.ts","../src/plugin/module-loader-isolated.ts","../src/plugin/activation-events.ts","../src/app/plugin-axis-handlers/hooks-axis-handler.ts","../src/app/plugin-axis-handlers/monitors-axis-handler.ts","../src/app/plugin-axis-handlers/feedback-axis-handler.ts","../src/plugin/sigil-loader.ts","../src/app/plugin-axis-handlers/sigil-axis-handler.ts","../src/app/plugin-axis-handlers/computed-commands-axis-handler.ts","../src/app/plugin-axis-handlers/computed-agents-axis-handler.ts","../src/app/plugin-axis-handlers/computed-skills-axis-handler.ts","../src/plugin/provider-loader.ts","../src/app/plugin-axis-handlers/oauth-provider-axis-handler.ts","../src/app/plugin-axis-handlers/code-provider-axis-handler.ts","../src/app/plugin-axis-handlers/wire-protocol-axis-handler.ts","../src/app/plugin-axis-handlers/tools-axis-handler.ts","../src/plugin/plugin-schedule-subscriptions.ts","../src/app/plugin-axis-handlers/services-axis-handler.ts","../src/plugin/plugin-scoped-storage.ts","../src/app/plugin-module-manager.ts","../src/plugin/panel-compiler.ts","../src/plugin/install-plugin.ts","../src/plugin/installed-plugins-registry.ts","../src/app/plugin-topology-manager.ts","../src/app/model-infra-factory.ts","../src/app/session-pool-factory.ts","../src/agent/swarm-runtime.ts","../src/vision/vision-delegate-service.ts","../src/plugin/panel-global-runtime.ts","../src/plugin/panel-runtime.ts","../src/plugin/panel-backend-runtime.ts","../src/plugin/engine-locale.ts","../src/app/plugin-provider-manager.ts","../src/app/devtools-setup.ts","../src/app/permission-controller.ts","../src/plugin/context-sources.ts","../src/app/process-lifecycle-hooks.ts","../src/input/builtin-resolvers.ts","../src/app/app-builder.ts","../src/app/tool-callbacks.ts","../src/hooks/tool-guidance.ts","../src/hooks/lesson-injection.ts","../src/hooks/skill-catalog-injection.ts","../src/hooks/notification-injection.ts","../src/hooks/background-process-notification.ts","../src/hooks/background-process-session-kill.ts","../src/hooks/session-permission-sync.ts","../src/hooks/session-learning.ts","../src/hooks/skill-loop-guidance.ts","../src/skill-loop/candidate-brief.ts","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/core.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/util.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/errors.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/parse.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/regexes.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/checks.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/doc.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/versions.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/schemas.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/registries.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/api.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/to-json-schema.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/json-schema-processors.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/iso.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/errors.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/parse.js","../../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/schemas.js","../src/skill-loop/candidate-reader.ts","../src/skill-loop/candidate-proposal.ts","../src/hooks/skill-loop.ts","../src/hooks/sandbox-guard.ts","../src/hooks/agent-job-notification.ts","../src/skill-loop/loop-status.ts","../src/skill-loop/episode-evidence.ts","../src/skill-loop/skill-draft-prompt.ts","../src/skill-loop/skill-draft.ts","../src/app/app-hooks.ts","../src/app/tool-wiring.ts","../src/app/workspace-io.ts","../src/app/app-lifecycle.ts","../src/app/app-session-export.ts","../src/app/app-capability-reload.ts","../src/app/app.ts","../src/app/frontend-port-factory.ts","../src/plugin/collect-plugin-perf-metrics.ts","../src/plugin/read-plugin-log-tail.ts","../src/plugin/contribution-host.ts","../src/plugin/plugin-cli-passthrough.ts","../src/plugin/theme-preset-loader.ts"],"sourcesContent":["import { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { stream as aiStream } from '@x-otto/ai'\nimport { createLogger } from '@x-otto/shared'\nimport { isClaudeCompatEnabled, isShadowModeEnabled, normalizeEnv } from '@x-otto/env'\nimport {\n AutoMemory,\n DEFAULT_MEMORY_SOURCES,\n FileSystemMemoryStore,\n HttpMemoryStore,\n InMemoryArchiveStorage,\n createMemoryManager,\n createOverlayMemoryStore,\n discoverAgentsSources,\n} from '@x-otto/memory'\nimport { buildEngineStores } from '@x-otto/runtime'\nimport { createFileStorageHost, createLiveTappedAppendLog } from '@x-otto/persistence'\nimport { SESSION_MAX_PER_SESSION_BYTES } from '@x-otto/session-contract'\n\nimport type { Model, ProviderRegistry } from '@x-otto/ai'\nimport type { MemorySource, MemoryStore } from '@x-otto/memory'\nimport type { MemorySubsystem } from '@x-otto/runtime'\nimport type { SqliteSessionRepository } from '@x-otto/session'\nimport type { UserIdentity } from '@x-otto/env'\nimport type { WorkspaceRef } from '@x-otto/workspace'\nimport type { StorageOptions, StorageHostBinding, AppOptions } from '../types'\n\nexport const DEFAULT_STORAGE_DIR = join(homedir(), '.otto')\n\n/**\n * remote 存储(session/memory 走 url)的请求超时默认值(ms)。\n *\n * 死配置审计修复(2026-08-09):`StorageOptions.remote.timeoutMs` 是程序化 API option,\n * 但两个生产装配点(cli.ts / engine-main.ts)从不传它,此前 `?? 30_000` 硬编码兜底\n * 成为实际生效值、无任何 env/settings 覆盖路径(典型假参数化)。补 `OTTO_REMOTE_STORAGE_TIMEOUT_MS`\n * 覆盖——慢网络/代理下 remote session 同步 30s 超时会失败。优先级:程序化 `remote.timeoutMs`\n * (显式传入时)> 本 env > 默认 30s。同时消除此前两处 `?? 30_000` 的同值不同源。\n */\nconst REMOTE_STORAGE_TIMEOUT_MS = normalizeEnv(process.env['OTTO_REMOTE_STORAGE_TIMEOUT_MS'], 30_000)\n\n/** RFC-344 方案丙:压缩请求成本量化日志(供决定是否投入 cache 复用重写)。 */\nconst compactionCostLogger = createLogger('@x-otto/coding:rfc344-compaction-cost')\n\nexport function requireRemote(\n storage: StorageOptions | undefined,\n domain: 'session' | 'memory',\n): NonNullable<StorageOptions['remote']> {\n if (!storage?.remote?.getAuth) {\n throw new Error(`storage.${domain}.url is configured, so storage.remote.getAuth is required.`)\n }\n return storage.remote\n}\n\n/**\n * 构建 MemoryStore。\n *\n * 优先级:\n * 1. `storage.memory.dir` 显式目录 → FileSystemMemoryStore\n * 2. `identity.userId` 已配置(无论是否 R 形态)**或** R 形态(`sessionUrl` 已知,即便\n * 未登录)→ HttpMemoryStore(RFC-164 M164-2 扩展:user-scope 与 workspace-scope 独立\n * 判定,见类头注释与 `isWorkspaceSourceEnabled`)。\n * 3. `workspaceRoot` 存在 → FileSystemMemoryStore\n * 4. 无本地 root → undefined\n *\n * user-scope(~/.otto/AGENTS.md 等)需要 `identity.userId` 才生效;workspace-scope\n * (./.otto/MEMORY.md 等)需要 R 形态 opt-in(`getWorkspaceRef().memorySyncEnabled===true`)\n * 才生效——两者互不依赖,惰性 getter 使得 opt-in 状态可以在 App 构造完成后(`start()`\n * 解析完 workspaceRef)动态生效,不需要重建整个 store(RFC-164 M164-2 摸底阶段发现的\n * 时序断层修正:`workspaceRef` 要到 `start()` 才异步解析,构造期读不到)。\n */\nexport function buildMemoryStore(\n storage: StorageOptions | undefined,\n workspaceRoot: string | undefined,\n identity?: UserIdentity,\n getWorkspaceRef?: () => WorkspaceRef | undefined,\n onMemoryConflict?: (path: string, conflictPath: string) => void,\n): MemoryStore | undefined {\n const base = buildBaseMemoryStore(\n storage,\n workspaceRoot,\n identity,\n getWorkspaceRef,\n onMemoryConflict,\n )\n // RFC-345 §D3:影子态用 overlay 包装——读穿透继承既有记忆、写只进内存(退出即失)。\n // 单点包装(§D1 白名单),base store 各分支不感知影子态。\n return base && isShadowModeEnabled() ? createOverlayMemoryStore(base) : base\n}\n\nfunction buildBaseMemoryStore(\n storage: StorageOptions | undefined,\n workspaceRoot: string | undefined,\n identity?: UserIdentity,\n getWorkspaceRef?: () => WorkspaceRef | undefined,\n onMemoryConflict?: (path: string, conflictPath: string) => void,\n): MemoryStore | undefined {\n const location = storage?.memory\n if (location && 'dir' in location) {\n return new FileSystemMemoryStore(location.dir)\n }\n\n const sessionUrl = storage?.session && 'url' in storage.session ? storage.session.url : undefined\n // RFC-164 M164-2:R 形态(sessionUrl 已知)即构造 HttpMemoryStore,即便未登录\n // (identity 缺失)——workspace-scope 同步不依赖用户身份,只依赖 opt-in(惰性判定)。\n // user-scope 仍需 identity.userId(HttpMemoryStore 内部据此各自降级,互不影响)。\n if (storage?.remote?.getAuth && sessionUrl) {\n const remote = storage.remote\n return new HttpMemoryStore({\n baseUrl: sessionUrl,\n getAuth: remote.getAuth,\n fetch: remote.fetch ?? globalThis.fetch,\n timeoutMs: remote.timeoutMs ?? REMOTE_STORAGE_TIMEOUT_MS,\n userId: identity?.userId,\n workspaceKey: () => getWorkspaceRef?.()?.key,\n isWorkspaceSyncEnabled: () => getWorkspaceRef?.()?.memorySyncEnabled === true,\n localDir: workspaceRoot,\n onConflict: onMemoryConflict,\n })\n }\n\n if (workspaceRoot) {\n return new FileSystemMemoryStore(workspaceRoot)\n }\n return undefined\n}\n\nexport interface DefaultMemoryDeps {\n /** 构造期阈值用 model(最佳努力;缺省时 MemoryManager 回落 200k contextWindow)。 */\n model: Model | undefined\n /** summarize 用 model 的惰性解析——启动未传 --model 时,运行期选定模型后仍可压缩。 */\n getModel?: () => Model | undefined\n storage: StorageOptions | undefined\n memoryStore: MemoryStore | undefined\n providerRegistry: ProviderRegistry\n autoMemory: AutoMemory | null\n /** 工作区根(M5:AGENTS.md 向上递归发现的起点);缺省 process.cwd()。 */\n workspaceDir?: string\n /** RFC-178:活跃会话字节估算惰性读数(见 StorageMemorySetupDeps 同名字段注释)。 */\n getActiveSessionBytes?: () => number | undefined\n /** RFC-321 M3:生效字节预算惰性读数(跟随 ResidencyGovernor 收紧档位)。 */\n getEffectiveMaxBytes?: () => number | undefined\n /**\n * RFC-324 D3:摘要覆盖率阈值(`residency.coverage_warn_threshold` /\n * `coverage_error_threshold`)。惰性读——settings 在 App 构造期尚未 load,\n * 与 `getEffectiveMaxBytes` 同构。返回 undefined 的字段回退 memory 侧常量(env 已并入)。\n */\n getCoverageThresholds?: () => { warn?: number; error?: number } | undefined\n /**\n * archive 从 session 派生——session entries 加载委托(由 App 接\n * SessionRepository/SessionPersistence)。惰性——session persistence 在 memory 之后构建,\n * 传 getter 即可首次 readArchive/listArchives 时拿到实时实现。未传时 archive 回退到\n * InMemoryArchiveStorage(V 形态/无持久化 session tree)。\n */\n getLoadSessionEntries?: () =>\n | ((sessionId: string) => Promise<import('@x-otto/memory').MinimalEntry[]>)\n | undefined\n}\n\n/**\n * 单次「prompt → 文本」模型调用闭包(惰性模型解析 + 单流收敛为纯文本)。memory 的 `summarize`\n * 与经验抽取的 `ExtractLLM` 共用此形;抽成复用件,避免两处重复模型接线漂移。\n */\nexport function createModelTextCaller(deps: {\n model?: Model\n getModel?: () => Model | undefined\n providerRegistry: ProviderRegistry\n /**\n * RFC-344 方案丙(成本量化):每次调用完成后回调本次请求的真实 usage。\n * 现状 createModelTextCaller 只取 `result.content`、丢弃 `result.usage`——压缩请求的\n * input_tokens 成本因此不可观测。本回调把它透出(不改调用行为),供压缩路径把\n * \"每次压缩的 input token 成本\"记入可观测面,据此量化 cache 复用(层 B)的 ROI。\n * 缺省不传 = 行为逐字节不变(经验抽取等其他消费方不受影响)。\n */\n onUsage?: (usage: import('@x-otto/ai').Usage) => void\n}): (prompt: string, opts?: { maxTokens?: number; signal?: AbortSignal }) => Promise<string> {\n const { model, getModel, providerRegistry, onUsage } = deps\n return async (prompt, opts) => {\n const m = getModel?.() ?? model\n if (!m) {\n throw new Error('No model available — sign in or pick a model first.')\n }\n const provider = providerRegistry.get(m.api)\n const es = aiStream(\n m,\n provider,\n {\n model: m,\n systemPrompt: '',\n messages: [{ role: 'user', content: prompt, timestamp: Date.now() }],\n maxTokens: opts?.maxTokens,\n },\n { signal: opts?.signal },\n )\n const result = await es.result()\n if (onUsage && result.usage) onUsage(result.usage)\n return result.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text)\n .join('\\n')\n }\n}\n\nexport function buildDefaultMemory(deps: DefaultMemoryDeps): MemorySubsystem | undefined {\n const {\n model,\n getModel,\n memoryStore,\n providerRegistry,\n autoMemory,\n workspaceDir,\n getLoadSessionEntries,\n getActiveSessionBytes,\n getEffectiveMaxBytes,\n getCoverageThresholds,\n } = deps\n\n const summarize = createModelTextCaller({\n model,\n getModel,\n providerRegistry,\n // RFC-344 方案丙:把每次压缩请求的真实 usage 记入可观测面(此前被丢弃)。\n // inputTokens = 现状\"无 cache 复用\"下压缩付的全额 input 成本;cacheReadTokens\n // ≈ 0(现状 systemPrompt=''、无 tools、单条合成 user 消息,前缀零命中)。\n // 长会话累计这些数据 → 量化 cache 复用(层 B)的 ROI,据此定甲/乙(见 RFC-344 §5)。\n onUsage: (usage) =>\n compactionCostLogger.info(\n {\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n cacheReadTokens: usage.cacheReadTokens,\n cacheWriteTokens: usage.cacheWriteTokens,\n },\n '[RFC-344] compaction summarize request cost',\n ),\n })\n\n const writableSources = DEFAULT_MEMORY_SOURCES.filter((s) => s.writable)\n const projectSources = discoverAgentsSources(workspaceDir ?? process.cwd())\n const claudeSources: MemorySource[] = isClaudeCompatEnabled()\n ? [\n { path: '~/.claude/CLAUDE.md', writable: false },\n ...discoverAgentsSources(workspaceDir ?? process.cwd(), { fileName: 'CLAUDE.md' }),\n ]\n : []\n const baseSources: MemorySource[] = [...writableSources, ...claudeSources, ...projectSources]\n const sources: MemorySource[] | undefined =\n memoryStore && autoMemory ? [...baseSources, autoMemory.asSource()] : undefined\n\n return createMemoryManager({\n summarize,\n model: model\n ? { contextWindow: model.contextWindow, maxOutput: model.maxOutputTokens }\n : undefined,\n getContextWindow: () => (getModel?.() ?? model)?.contextWindow,\n // RFC-321 M2:**条数触发因子的 maxHistoryMessages 注入已移除**——条数不再参与\"压不压\n // 缩\"的决策(死区空转 + 口径失配 + 维度越界,详见 memory-manager.ts needsCompaction\n // 内注释)。条数维度只保留 capStoredHistory 的内存驻留防御上限,其\"活跃区仍超限\"的\n // 兜底由驻留层逃生阀承担(persistence-sync 的 activeOversized → compactNow)。\n // RFC-178 触发 B 软段:字节预算 + 活跃会话字节估算读数。\n // C5(事后 review 修正语义失配):本值原本是 SESSION_MAX_CONTENT_BYTES=64MB——\n // M5 把它当主闸写入,但 M5 后真正主闸是 ResidencyAllocator(MAX=256MB),该 64MB\n // 已退化为\"构造期静态回退\"。Network y-score 消费点在 getMaxContentBytes 注入时\n // 从未触发(?? 回退);但 getter 缺席时(测试/未来调用点)会用到这个静态值,\n // 与主闸语义冲突。故回退值改为与主闸一致(分配器 MAX)。\n maxContentBytes: SESSION_MAX_PER_SESSION_BYTES,\n // RFC-321 M3:生效预算惰性读数——跟随 ResidencyGovernor 的 warning/critical 收紧\n // (驻留层 cap 除以 2/4)。此前 memory 侧恒按全值判定,压力档下收紧在压缩层完全\n // 不可见,使\"cap 触发时压缩边界必然已存在\"的不变式在最需要它的时刻失效。\n getMaxContentBytes: getEffectiveMaxBytes,\n // RFC-324 D3:摘要覆盖率阈值惰性透传(此前 residency.coverage_* 已进 schema\n // 却无任何消费方——用户改配置无效果且无警告,RFC-330 §4.5 记录的静默失效面)。\n getCoverageThresholds,\n getEstimatedBytes: getActiveSessionBytes,\n memoryStore,\n sources,\n archiveStorage: new InMemoryArchiveStorage(),\n messageFromEntry: (entry) => {\n const d = entry.data as Record<string, unknown> | null | undefined\n if (!d || typeof d !== 'object') return null\n const msg = (d as { message?: import('@x-otto/ai').Message }).message\n if (msg && typeof (msg as { role?: string }).role === 'string') return msg\n return null\n },\n loadSessionEntries: async (sessionId) => {\n const fn = getLoadSessionEntries?.()\n if (!fn) throw new Error('Session entries not wired')\n return fn(sessionId)\n },\n })\n}\n\nexport interface StorageMemorySetupDeps {\n options: AppOptions\n workspaceDir: string\n providerRegistry: ProviderRegistry\n defaultModel: Model | undefined\n resolveEffectiveModel: () => Model | undefined\n /** 共享可变 ref:App 构造器创建;buildDefaultMemory 的 loadSessionEntries 闭包 + 稍后 agentRuntime 共用(穿线)。 */\n repoRef: { current?: SqliteSessionRepository }\n /**\n * 惰性 workspaceRef getter(RFC-164 M164-2)——`workspaceRef` 要到 `start()` 才异步解析完成,\n * `buildStorageAndMemory` 在 App 构造期同步调用,此刻读不到真实值。传闭包而非快照值,\n * 使 `HttpMemoryStore` 内部对 `memorySyncEnabled` 的判定始终读取当下最新状态\n * (与 `getWorkspaceId`/`getWorkspaceKey` 同一模式,见 `app.ts`)。\n */\n getWorkspaceRef?: () => WorkspaceRef | undefined\n /** RFC-164 M164-2 D6:workspace 记忆远端写入冲突回调——透传给 HttpMemoryStore.onConflict。 */\n onMemoryConflict?: (path: string, conflictPath: string) => void\n /**\n * RFC-178 触发器 B 软段:活跃会话消息字节估算的惰性读数(`InMemorySession.\n * estimatedContentBytes`)。构造期 sessionFacade 尚不存在,传闭包运行时求值(与\n * getWorkspaceRef 同模式)。缺省时字节触发因子不启用(向后兼容)。\n */\n getActiveSessionBytes?: () => number | undefined\n /**\n * RFC-321 M3:生效字节预算的惰性读数(`ResidencyGovernor.effectiveMaxBytes`)。\n * 与 `getActiveSessionBytes` 同为运行时闭包求值;缺省时回退静态 `maxContentBytes`。\n */\n getEffectiveMaxBytes?: () => number | undefined\n /**\n * RFC-324 D3:摘要覆盖率阈值的惰性读数(`residency.coverage_warn_threshold` /\n * `coverage_error_threshold`)。缺省时回退 memory 侧常量(env 已并入)。\n */\n getCoverageThresholds?: () => { warn?: number; error?: number } | undefined\n}\n\n/**\n * storage + memory 装配。\n *\n * 建 storageHost / memoryStore→autoMemory / memory(buildDefaultMemory) / engineStores(trace+checkpoint),\n * 并触发 tokenizer 预热。返回 App 直接回填的 6 个字段。`repoRef` 由调用方传入并继续传给 agentRuntime\n * (memory 的 loadSessionEntries 惰性经它拿到 session repo)。返回类型让 TS 推断(与 App 字段类型对齐)。\n */\nexport function buildStorageAndMemory(deps: StorageMemorySetupDeps) {\n const {\n options,\n workspaceDir,\n providerRegistry,\n defaultModel,\n resolveEffectiveModel,\n repoRef,\n getWorkspaceRef,\n } = deps\n\n const storageHost: StorageHostBinding | null = options.storage?.host\n ? {\n host: createFileStorageHost({\n baseDir: options.storage.host.dir,\n namespaces: options.storage.host.namespaces,\n }),\n token: options.storage.host.token,\n }\n : null\n\n const memoryStore = buildMemoryStore(\n options.storage,\n workspaceDir,\n options.userIdentity,\n getWorkspaceRef,\n deps.onMemoryConflict,\n )\n const autoMemory = memoryStore ? new AutoMemory({ store: memoryStore }) : null\n\n const memory =\n options.memory ??\n buildDefaultMemory({\n model: defaultModel,\n getModel: resolveEffectiveModel,\n storage: options.storage,\n memoryStore,\n providerRegistry,\n autoMemory,\n workspaceDir,\n getLoadSessionEntries: () =>\n repoRef.current\n ? async (sessionId) => {\n const rows = await repoRef.current!.loadEntries(sessionId)\n return rows.map((r) => ({\n type: r.type as 'message' | 'compaction' | 'clear',\n seq: r.seq,\n data: r.data,\n }))\n }\n : undefined,\n })\n\n // RFC-150 D4:不再在启动主链显式预热 tokenizer——实测 js-tiktoken 的 cl100k_base\n // BPE 表构建耗时 ~150ms,此前虽写成 fire-and-forget(`.catch(()=>{})`),但单线程下\n // 同步计算段仍会挤占 app.start() 主链(见 §1.1 手工分析时间线)。`estimateTokens()`\n // 自身已有懒加载 + 启发式 fallback(token-estimator.ts:未加载时走\n // `estimateTokensHeuristic`,首次真实调用会自动触发后台加载),启动期无需主动预热\n // ——首次估算发生时 TUI 通常已可交互,不再挤占启动主链,正确性无损失(估算本就是\n // 近似值,短暂走启发式不影响功能)。\n const sessionLoc = options.storage?.session\n const sessionSnapshotDir = sessionLoc && 'dir' in sessionLoc ? sessionLoc.dir : undefined\n const isRemoteSession = sessionLoc && 'url' in sessionLoc && !!options.storage?.remote?.getAuth\n const remoteLogOptions = isRemoteSession\n ? {\n baseUrl: (sessionLoc as { url: string }).url,\n getAuth: options.storage!.remote!.getAuth,\n fetch: options.storage!.remote!.fetch,\n timeoutMs: options.storage!.remote!.timeoutMs ?? REMOTE_STORAGE_TIMEOUT_MS,\n }\n : undefined\n\n // trace/checkpoint 后端选择走单源 buildEngineStores(与 runtime 共用,消重复)。\n // RFC-345 §D8:影子态注入 ephemeral 强制 MemoryAppendLog(磁盘零写、devtools 观测仍可用)。\n const engineStores = buildEngineStores(\n { sessionDir: sessionSnapshotDir, remoteLog: remoteLogOptions, ephemeral: isShadowModeEnabled() },\n { traceStore: options.traceStore, checkpointStore: options.checkpointStore },\n )\n\n // L3 终局:给 traceStore 套内存实时旁路——recorder.append 写盘同时 publish 给内存订阅者(inspector),\n // live 观测不再绕磁盘 tail 轮询;底层 store 仍负责持久化与回放历史会话的 read/tail。\n const { store: traceStore, live: traceLive } = createLiveTappedAppendLog(engineStores.traceStore)\n\n return {\n storageHost,\n autoMemory,\n memory,\n traceStore,\n traceLive,\n checkpointStore: engineStores.checkpointStore,\n clock: options.clock,\n // RFC-164 M164-2 D5:向外暴露 memoryStore,供 buildAgentServices 装配 learningStore/\n // localLearningStore 时接入同一 store(使 lessons.json 与 auto-memory 走同一远端同步\n // 管道,不再是子agent评审 F1 指出的碎片化设计)。\n memoryStore,\n }\n}\n","/** Capability→tool map: derive tools set from granular capability flags. */\n\n/**\n * 能力键 → 代理工具名。工具存在即视为该能力可用。\n * 与 `packages/prompt/prompts/lead-guidance.md` 中的 `requires-capability` 标记一一对应:\n * - delegation:Workflow Guidance 主体、Model Slot Guidance\n * - task-observability:Background tasks 子节\n * - file-state-refresh:File State Refresh\n */\nexport const CAPABILITY_TOOL_MAP: Readonly<Record<string, string>> = {\n delegation: 'task_delegate',\n 'task-observability': 'task_inspect',\n 'file-state-refresh': 'capture_file_state',\n}\n\n/**\n * 全部已声明的能力键(`lead-guidance.md` 中出现的 `requires-capability` 值必须 ⊆ 此集合,\n * 由 T101-5 的双向漂移守卫测试断言)。\n */\nexport const KNOWN_CAPABILITIES: ReadonlySet<string> = new Set(Object.keys(CAPABILITY_TOOL_MAP))\n\n/**\n * 把已解析工具名集合(`resolveTools()` 输出)转换为能力键集合,供 `PromptManager.assemblePreset()`\n * 的 `enabledCapabilities` 参数使用。\n */\nexport function resolveEnabledCapabilities(toolNames: ReadonlySet<string>): Set<string> {\n const capabilities = new Set<string>()\n for (const [capability, toolName] of Object.entries(CAPABILITY_TOOL_MAP)) {\n if (toolNames.has(toolName)) {\n capabilities.add(capability)\n }\n }\n return capabilities\n}\n","/** Agent services setup: BackgroundAgentManager/SwarmEngine wiring. */\nimport { join } from 'node:path'\n\nimport { createSkillRegistry } from '@x-otto/skill'\nimport { FileStateManager, OperationalLearningStore, createMemoryAutoExtractor } from '@x-otto/memory'\nimport { PromptManager, createPromptProvider, BUILTIN_PROMPTS_DIR } from '@x-otto/prompt'\n\nimport { KNOWN_CAPABILITIES } from '../session/capability-tool-map'\n\nimport { isClaudeCompatEnabled, isShadowModeEnabled } from '@x-otto/env'\n\nimport { createModelTextCaller } from './storage-wiring'\nimport { evaluatePluginTrust } from '@x-otto/plugin'\nimport { evaluateClaudeTrust } from '@x-otto/claude-compat'\n\nimport type { Model, ProviderRegistry } from '@x-otto/ai'\nimport type { MemoryStore, MemoryAutoExtractConfig } from '@x-otto/memory'\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport type { AppOptions } from '../types'\n\nexport interface AgentServicesDeps {\n skillProvider: AppOptions['skillProvider']\n workspaceDir: string\n activePlugins: () => DiscoveredPlugin[]\n resolveEffectiveModel: () => Model | undefined\n providerRegistry: ProviderRegistry\n prompt: AppOptions['prompt']\n /**\n * RFC-164 M164-2 D5:与 auto-memory 共用的 MemoryStore(`buildStorageAndMemory` 产出)。\n * `learningStore`(workspace-scope lessons)接入此 store,使其与 auto-memory 走同一远端\n * 同步管道;`localLearningStore`(scope=local)**绝不**接入——继续走本地文件路径\n * (M164-1 D3 硬约束:机器特定内容永不跨设备同步)。缺省(无 memoryStore,如无\n * workspaceRoot 的极端场景)时 `learningStore` 回退现状本地文件行为。\n */\n memoryStore?: MemoryStore\n /**\n * RFC-036 死配置修复(2026-08-09):`memory_auto_extract` 的 `every_n_turns`/`max_lessons`/\n * `similarity_threshold` 三个 settings 字段此前只在 schema 定义、生产从不消费(`config` 曾\n * 硬编码 `{enabled:true}`)。经此惰性 getter 从 settings 映射进 extractor。**必须惰性**——\n * App 构造期(本函数调用时)`settings` 尚未 `load()`,构造期定档会永远读到空值(见\n * app.ts settings 时序注释);故返回的 config 对象用 getter 定义各字段,每次 `onIdle`\n * 访问属性时才读当前 settings 值。`enabled` 底座恒 true,真实开关在 hook 层的惰性 gate\n * (app-hooks.ts createMemoryAutoExtractHooks 的 isEnabled)。\n */\n memoryAutoExtractConfig?: MemoryAutoExtractConfig\n}\n\nexport function buildAgentServices(deps: AgentServicesDeps) {\n const {\n skillProvider,\n workspaceDir,\n activePlugins,\n resolveEffectiveModel,\n providerRegistry,\n prompt,\n memoryStore,\n memoryAutoExtractConfig,\n } = deps\n\n // RFC-081 O1:项目 `.claude/skills` 同属攻击者可控内容面(clone 带入),未信任则剔除——\n // 仅留 `.otto/skills`(用户自有)。`undefined` 时 registry 用默认(含 .claude/skills when compat)。\n // 注:projectDirs 在构造期定档,`/compat trust` 后需下次启动才纳入 .claude/skills(错向安全)。\n const claudeSkillsGated = isClaudeCompatEnabled() && evaluateClaudeTrust(workspaceDir).gated\n const skillRegistry = skillProvider\n ? null\n : createSkillRegistry({\n workspaceDir,\n library: {\n ...(claudeSkillsGated ? { projectDirs: ['.otto/skills'] } : {}),\n pluginDirs: activePlugins()\n .filter((p) => p.manifest.contributes?.skills !== false)\n // RFC-081 O1:项目级未信任插件的 skills 是攻击者可控内容面,门控跳过\n .filter((p) => !evaluatePluginTrust(p).gated)\n .map((p) => ({ id: p.id, dir: join(p.dir, 'skills') })),\n },\n })\n\n const fileStateManager = new FileStateManager()\n // RFC-164 M164-2 D5:提供 memoryStore 时接入同一 store(与 auto-memory 共享远端同步\n // 管道,opt-in 状态由 store 内部的惰性 getter 判定);未提供时(无 workspaceRoot 的极端\n // 场景)回退现状本地文件路径(向后兼容)。\n const shadow = isShadowModeEnabled()\n const learningStore = new OperationalLearningStore({\n path: `${workspaceDir}/.otto/lessons.json`,\n // storePath 用相对路径('./.otto/lessons.json')而非 path 的绝对路径——\n // 必须与 HttpMemoryStore.isWorkspaceSource() 的判定格式一致(对齐 AutoMemory 的\n // './.otto/MEMORY.md' 等路径约定),否则 workspace-scope 判定恒不命中,\n // lessons.json 永远走本地分支(实现阶段发现的接线缺口)。\n ...(memoryStore ? { store: memoryStore, storePath: './.otto/lessons.json' } : {}),\n // RFC-345 §D3:影子态 persist 短路(内存优先,读继承既有教训、写只进内存)。\n ephemeral: shadow,\n })\n // RFC-164 M164-1 D3:机器特定经验(scope='local')落独立文件,**绝不**接入 memoryStore——\n // 物理隔离于 workspace lessons 之外,永不参与远端同步(M164-2 硬约束)。与 workspace\n // learningStore 完全独立的实例——不共享内存态、不共享去重索引(local/workspace 各自去重)。\n const localLearningStore = new OperationalLearningStore({\n path: `${workspaceDir}/.otto/local/lessons.json`,\n ephemeral: shadow,\n })\n const memoryAutoExtractor = createMemoryAutoExtractor({\n llm: createModelTextCaller({ getModel: resolveEffectiveModel, providerRegistry }),\n store: learningStore,\n config: memoryAutoExtractConfig ?? { enabled: true },\n })\n const completeText = createModelTextCaller({ getModel: resolveEffectiveModel, providerRegistry })\n\n const promptProvider = createPromptProvider(\n prompt ?? { type: 'local', baseDir: BUILTIN_PROMPTS_DIR },\n )\n // S2(终局审查 2026-07-18):注入已知能力键全集——lead-guidance.md 的 requires-capability\n // 标记拼写错误/能力键改名时 console.warn 告警,防止段落静默消失(此前零检测)。\n const promptManager = new PromptManager({\n provider: promptProvider,\n knownCapabilities: KNOWN_CAPABILITIES,\n })\n\n return {\n skillRegistry,\n fileStateManager,\n learningStore,\n localLearningStore,\n memoryAutoExtractor,\n completeText,\n promptManager,\n }\n}\n","import {\n compilePolicyFromSetting,\n createDisabledToolsPolicy,\n createEgressAllowlistPolicy,\n createPermissionModePolicy,\n createPermissionToolSetsFromRegistry,\n} from '@x-otto/hooks'\n\nimport type { PermissionMode, PermissionPolicyRegistry, PermissionPolicySetting } from '@x-otto/hooks'\nimport type { ToolRegistry } from '@x-otto/tools'\nimport type { Logger } from '@x-otto/shared'\n\nexport interface PermissionSyncDeps {\n logger: Logger\n getToolRegistry: () => ToolRegistry\n getPermissionRegistry: () => PermissionPolicyRegistry\n}\n\nexport class PermissionPolicySync {\n // undefined = 尚未注册任何 mode 策略(首 sync 前)。不可初始化为 'auto'——否则首个默认\n // 'auto' sync 因 effective===current 早退,mode::auto 永不注册(RFC-077 M10 修:默认 app\n // 的 allow-reads 便利策略缺失)。\n private currentPermissionMode: PermissionMode | undefined = undefined\n\n constructor(private readonly deps: PermissionSyncDeps) {}\n\n sync(setting: {\n permission_mode?: PermissionMode\n permissions?: PermissionPolicySetting[]\n permission_always_allow?: string[]\n disabled_tools?: string[]\n web_fetch_allowed_hosts?: string[]\n }): void {\n const toolSets = createPermissionToolSetsFromRegistry(this.deps.getToolRegistry().getAll())\n\n this.syncModePolicy(setting.permission_mode, toolSets)\n this.syncDisabledToolsPolicy(setting.disabled_tools)\n this.syncEgressPolicy(setting.web_fetch_allowed_hosts)\n this.syncUserPolicies(setting.permissions)\n this.registry.setAlwaysAllow(setting.permission_always_allow ?? [])\n\n this.deps.logger.debug(\n 'Permission policies synced: %d policies active',\n this.registry.getAll().length,\n )\n }\n\n private get registry(): PermissionPolicyRegistry {\n return this.deps.getPermissionRegistry()\n }\n\n private syncModePolicy(\n mode: PermissionMode | undefined,\n toolSets: ReturnType<typeof createPermissionToolSetsFromRegistry>,\n ): void {\n // permission_mode 无 schema 默认 → undefined 视为系统默认 'auto'(与全局 `?? 'auto'` 一致)。\n // RFC-342:即便 mode 字面值未变也必须重建策略——MCP 工具在启动后/热重载时才动态进入\n // ToolRegistry,mode policy 持有的是 toolSets 快照,早退会让新增的非只读 mcp__ 工具永远\n // 不进入 writeTools,confirm/readonly/auto 语义失效。\n const effective = mode ?? 'auto'\n if (this.currentPermissionMode) {\n this.registry.unregister(`mode::${this.currentPermissionMode}`)\n }\n this.registry.register(createPermissionModePolicy(effective, toolSets))\n this.currentPermissionMode = effective\n }\n\n private syncDisabledToolsPolicy(disabledTools: string[] | undefined): void {\n if (disabledTools && disabledTools.length > 0) {\n this.registry.register(createDisabledToolsPolicy(disabledTools))\n } else {\n this.registry.unregister('setting::disabled-tools')\n }\n }\n\n private syncEgressPolicy(allowedHosts: string[] | undefined): void {\n if (allowedHosts && allowedHosts.length > 0) {\n this.registry.register(createEgressAllowlistPolicy(allowedHosts))\n } else {\n this.registry.unregister('setting::egress-allowlist')\n }\n }\n\n private syncUserPolicies(permissions: PermissionPolicySetting[] | undefined): void {\n for (const p of this.registry.getAll()) {\n if (p.name.startsWith('user::')) {\n this.registry.unregister(p.name)\n }\n }\n\n if (!permissions) {\n return\n }\n\n for (const pc of permissions) {\n this.registry.register(compilePolicyFromSetting({ ...pc, name: `user::${pc.name}` }))\n }\n }\n}\n","import {\n createPlatformSandbox,\n isPlatformSandboxAvailable,\n credentialDenyReadPaths,\n} from '@x-otto/tools'\n\nimport type { SandboxConfig, SandboxStrategy } from '@x-otto/tools'\nimport type { Logger } from '@x-otto/shared'\nimport type { Setting } from '@x-otto/setting'\n\nexport class SandboxController {\n private strategy: SandboxStrategy | null = null\n private pinned = false\n private settingSnapshot = 'null'\n private networkAllowed = true\n private configuredWritablePaths: string[] = []\n private autoAllowBash = false\n\n constructor(private readonly logger: Logger) {}\n\n /** 晚绑定读取面(bash 工具 provider)。 */\n get current(): SandboxStrategy | null {\n return this.strategy\n }\n\n /** 当前生效的沙箱策略名(null = 无沙箱)。可观测面:测试/调试/CLI 状态展示。 */\n get activeName(): string | null {\n return this.strategy?.name ?? null\n }\n\n /** 沙箱是否启用(null=否 → sandbox-guard 即 no-op)。 */\n get enabled(): boolean {\n return this.strategy !== null\n }\n\n /** 当前沙箱网络策略(deny 时 networkAllowed=false)。 */\n get isNetworkAllowed(): boolean {\n return this.networkAllowed\n }\n\n /** 配置的额外可写路径(writable-roots 计算输入)。 */\n get writablePaths(): readonly string[] {\n return this.configuredWritablePaths\n }\n\n /** bash 沙箱越界自动放行开关(opt-in,默认 false)。见 SandboxConfig.autoAllowBashIfSandboxed。 */\n get autoAllowBashIfSandboxed(): boolean {\n return this.autoAllowBash\n }\n\n private readonly bypassCommands = new Set<string>()\n\n /** 记录一个命令为「已批准绕过沙箱」(会话内持续,同命令不再重复问/拦)。 */\n approveBypass(command: string): void {\n if (command) {\n this.bypassCommands.add(command)\n }\n }\n\n /** 该命令是否已获批 bypass。 */\n isBypassed(command: string | undefined): boolean {\n return command !== undefined && this.bypassCommands.has(command)\n }\n\n /** options.sandbox 显式注入 → 立即构造并钉死(settings 不覆盖)。 */\n pin(config: SandboxConfig): void {\n this.pinned = true\n this.networkAllowed = config.network !== 'deny'\n this.configuredWritablePaths = Array.isArray(config.writablePaths)\n ? config.writablePaths.filter((p): p is string => typeof p === 'string')\n : []\n this.autoAllowBash = config.autoAllowBashIfSandboxed === true\n if (config.enabled && !isPlatformSandboxAvailable()) {\n this.logger.warn(\n 'Sandbox pinned but no platform sandbox available (install bwrap on Linux); running WITHOUT sandbox',\n )\n this.strategy = null\n } else {\n this.strategy = config.enabled\n ? createPlatformSandbox({\n network: config.network,\n writablePaths: config.writablePaths,\n denyReadPaths: config.protectCredentials ? credentialDenyReadPaths() : undefined,\n })\n : null\n }\n }\n\n /**\n * settings.sandbox → 沙箱策略(load 与热更新走同一条路)。\n *\n * - options.sandbox 显式注入时钉死,settings 不覆盖(程序化优先);\n * - 配置内容不变时幂等跳过(change 事件对任意配置变更都触发);\n * - 替换/关闭时异步释放旧策略(seatbelt 临时 profile 文件)。\n * wrap→child spawn 同 tick 同步完成,dispose 落在后续 tick,无竞争窗口。\n */\n applySetting(config: SandboxConfig | undefined): void {\n if (this.pinned) {\n return\n }\n\n const snapshot = JSON.stringify(config ?? null)\n if (snapshot === this.settingSnapshot) {\n return\n }\n this.settingSnapshot = snapshot\n this.networkAllowed = config?.network !== 'deny'\n this.configuredWritablePaths = Array.isArray(config?.writablePaths)\n ? config!.writablePaths!.filter((p): p is string => typeof p === 'string')\n : []\n this.autoAllowBash = config?.autoAllowBashIfSandboxed === true\n\n const previous = this.strategy\n if (config?.enabled === true && !isPlatformSandboxAvailable()) {\n this.logger.warn(\n 'Sandbox enabled but no platform sandbox available (install bwrap on Linux); running WITHOUT sandbox',\n )\n this.strategy = null\n } else {\n this.strategy =\n config?.enabled === true\n ? createPlatformSandbox({\n network: config.network === 'deny' ? 'deny' : 'allow',\n writablePaths: this.configuredWritablePaths,\n denyReadPaths: config.protectCredentials ? credentialDenyReadPaths() : undefined,\n })\n : null\n }\n void previous?.dispose().catch(() => undefined)\n\n this.logger.info(\n 'Sandbox strategy %s (settings)',\n this.strategy ? `enabled: ${this.strategy.name}` : 'disabled',\n )\n }\n\n /**\n * 从 setting 解析沙箱配置(组合根策略下沉自此,不再留在 App)。\n *\n * - 显式 `setting.sandbox` → 原样尊重;\n * - 无 sandbox + bypass 模式 → 无沙箱;\n * - 无 sandbox + 非 bypass → 默认 on(network allow,writable 由策略约束)。\n */\n resolveConfig(setting: Setting): SandboxConfig | undefined {\n if (setting.sandbox) {\n return setting.sandbox\n }\n const mode = setting.permission_mode ?? 'auto'\n if (mode === 'bypass') {\n return undefined\n }\n const network = process.env['OTTO_SANDBOX_NETWORK'] === 'deny' ? 'deny' : 'allow'\n return { enabled: true, network, writablePaths: [] }\n }\n\n async dispose(): Promise<void> {\n if (this.strategy) {\n await this.strategy.dispose().catch(() => undefined)\n this.strategy = null\n }\n }\n}\n","import { isAbsolute, resolve } from 'node:path'\nimport type { McpServerConfig } from '@x-otto/mcp'\nimport { parseDotMcpJson } from '@x-otto/claude-compat'\nimport { evaluatePluginTrust } from '@x-otto/plugin'\nimport { discoverPlugins, type DiscoveredPlugin } from '@x-otto/plugin'\n\n/**\n * 相对路径按**插件目录**解析(通用能力,非某插件专属):自包含插件把自带的 server 脚本/二进制\n * 以相对路径(`./bin/server.mjs`)写在 `.mcp.json` 里,spawn 无 cwd 会解析错——此处在插件目录下钉成\n * 绝对路径。仅动 `./`/`../` 前缀的相对路径;绝对路径 / PATH 上的裸命令(如 `node`/`uvx`)原样保留。\n */\nfunction isRelPath(s: unknown): s is string {\n return typeof s === 'string' && (s.startsWith('./') || s.startsWith('../') || s.startsWith('.\\\\'))\n}\n\nfunction resolvePluginRelativePaths(config: McpServerConfig, dir: string): McpServerConfig {\n const out: McpServerConfig = { ...config }\n if (isRelPath(out.command) && !isAbsolute(out.command)) out.command = resolve(dir, out.command)\n if (Array.isArray(out.args)) {\n out.args = out.args.map((a) => (isRelPath(a) ? resolve(dir, a) : a))\n }\n return out\n}\n\n/**\n * mcp.ts —— 插件 `.mcp.json` 解析(RFC-044 M044-02-07,并行 `resolveClaudeMcpServers`).\n *\n * 读各已发现插件目录 `<dir>/.mcp.json` 的 mcp_servers(回退兼容 mcpServers),作**最低优先级**合并进 McpController\n * (otto/claude 同名覆盖插件,R7)。`.mcp.json` = 可执行面(stdio server 启动即跑二进制),\n * 故项目级未信任插件(gated)跳过(RFC-044 R4);`contributes.mcp===false` 亦跳过。\n * 只读导入:插件 server 不参与 setServerEnabled 落盘。\n */\nexport interface ResolvePluginMcpOptions {\n workspaceDir: string\n homedir: string\n /** 被禁用插件 id(RFC-044 bundle 启停);转发给 discoverPlugins 过滤。 */\n disabled?: readonly string[]\n /** 缺省自动发现;可注入供测试。 */\n plugins?: DiscoveredPlugin[]\n}\n\nexport function resolvePluginMcpServers(\n opts: ResolvePluginMcpOptions,\n): Record<string, McpServerConfig> {\n const plugins =\n opts.plugins ??\n discoverPlugins({ cwd: opts.workspaceDir, homedir: opts.homedir, disabled: opts.disabled })\n const out: Record<string, McpServerConfig> = {}\n for (const p of plugins) {\n if (p.manifest.contributes?.mcp === false) continue\n const trust = evaluatePluginTrust(p)\n if (trust.gated) continue\n const hasMcpServerCapability = p.manifest.capabilities?.includes('mcp.server') ?? false\n if (!hasMcpServerCapability) continue\n if (trust.pendingCapabilities.includes('mcp.server')) continue\n const servers = parseDotMcpJson(p.dir)\n for (const [name, config] of Object.entries(servers)) {\n const resolved = resolvePluginRelativePaths(config, p.dir)\n out[name] = { ...resolved, _pluginId: p.id } as McpServerConfig & { _pluginId: string }\n }\n }\n return out\n}\n","import { McpManager } from '@x-otto/mcp'\nimport type { McpClient, McpServerConfig, ProcessSpawnFactory, LightManagedProcess } from '@x-otto/mcp'\nimport { globalProcessRuntime } from '@x-otto/runtime'\nimport type { SettingsManager } from '@x-otto/setting'\nimport type { ToolRegistry } from '@x-otto/tools'\nimport type { Logger } from '@x-otto/shared'\nimport { homedir } from 'node:os'\nimport { resolveClaudeMcpServers } from '@x-otto/claude-compat'\nimport { resolvePluginMcpServers } from '../plugin/mcp'\n\n/**\n * RFC-085 M2:把 MCP stdio 子进程纳入统一 ProcessRuntime。\n * env 不在此白名单——由 `ProcessRuntime.spawn` 内部统一 buildAllowedEnv(规则 7)。\n * MCP server 长驻 → lifecycle 'pinned'(免 LRU 驱逐,规则 6);退出经 app.stop →\n * mcpController.stop() → transport.close() → kill;crash 兜底经 installExitReaper(Layer 3)。\n */\nconst mcpSpawnFactory: ProcessSpawnFactory = (command, args, env): LightManagedProcess => {\n const proc = globalProcessRuntime.spawn({\n command,\n args,\n env,\n owner: { type: 'mcp-server', id: command },\n category: 'mcp',\n lifecycle: 'pinned',\n label: `mcp:${command}`,\n })\n return {\n pid: proc.pid,\n stdin: proc.stdin,\n stdout: proc.stdout,\n stderr: proc.stderr,\n kill: () => proc.kill(),\n onError: (cb) => { proc.onError(cb) },\n onExit: (cb) => { proc.onExit(() => cb()) },\n }\n}\n\n/**\n * 首方能力(browser-use 等)不再走宿主硬编码的特例表——已收敛为**自描述插件**\n * (`otto extension install browser-use`,随扩展带 `otto-plugin.json`)。MCP server 层因此\n * 三源:otto > claude > plugin(曾有的 first-party 层已退役,消除「宿主认识具体扩展」的耦合)。\n */\n\n/**\n * MCP 服务器生命周期控制器。\n *\n * 按 settings.mcp_servers 连接 MCP 服务器并把工具注册进 ToolRegistry。\n * 事件驱动同步:connected/tools.changed/disconnected → 全量重投影 mcp__ 前缀工具\n * (adapter 限定名 `mcp__<server>__<tool>` 天然防与内置工具冲突)。\n *\n * /extension 面板经 setServerEnabled 运行时单独连/断某 server——manager 惰性创建\n * (即使启动时全禁用、manager 为 null,运行时启用某 server 也能拉起),落盘 mcp_servers[].disabled。\n */\n/**\n * 宿主侧 MCP 错误渲染器。协议层(@x-otto/mcp)只报中性事实 + 错误码,\n * actionable 的 CLI 指引('otto mcp login'——otto CLI 词汇,宿主知识)在此据码渲染。\n * 返回 null 表示无额外 actionable 指引(manager 已 warn 中性 message,不重复刷屏)。\n */\nexport function renderMcpServerError(info: { name: string; error: string; code?: string }): string | null {\n if (info.code === 'MCP_UNAUTHORIZED') {\n return `MCP server \"${info.name}\" requires authentication. Run 'otto mcp login ${info.name}' to authenticate.`\n }\n return null\n}\n\n/**\n * 带来源元数据的有效 MCP server(RFC-081)。\n * source 标识来自哪一层;pluginId 仅 plugin 层有值;managed 标记是否由插件托管。\n */\nexport interface EffectiveMcpServer extends McpServerConfig {\n disabled?: boolean\n source: 'otto' | 'claude' | 'plugin'\n pluginId?: string\n}\n\nexport class McpController {\n private _manager: McpManager | null = null\n\n /** 暴露内部 McpManager 供向后兼容。 */\n get manager(): McpManager | null {\n return this._manager\n }\n\n /** 暴露单个已连接的 McpClient 供插件面板调用 tool(RFC-081)。 */\n getMcpClient(name: string): McpClient | undefined {\n return this._manager?.getClient(name)\n }\n\n constructor(\n private readonly settings: SettingsManager,\n private readonly toolRegistry: ToolRegistry,\n private readonly logger: Logger,\n private readonly workspaceDir?: string,\n /**\n * RFC-230:统一重试/超时/退避配置——惰性 getter(同 `SessionConfigResolverDeps.\n * getResilienceConfig` 模式)。`ensureManager()` 惰性创建 `McpManager`,真正调用\n * 该 getter 的时机是运行时(首次连接 MCP server 时),此时 App 必然已 `start()`。\n */\n private readonly getResilienceConfig?: () => import('@x-otto/env').ResilienceConfig,\n ) {}\n\n /** 三源 MCP server 层(otto > claude > plugin)的统一取数——effectiveServers /\n * listEffectiveServers 共用,消除两处重复的 source 解析(ARCH-083-04)。 */\n private resolveServerLayers() {\n const otto = (this.settings.get('mcp_servers') ?? {}) as Record<\n string,\n McpServerConfig & { disabled?: boolean }\n >\n const claude = this.workspaceDir ? resolveClaudeMcpServers(this.workspaceDir) : {}\n const plugin = this.workspaceDir\n ? resolvePluginMcpServers({\n workspaceDir: this.workspaceDir,\n homedir: homedir(),\n disabled: this.settings.get('disabled_plugins') ?? [],\n })\n : {}\n return { otto, claude, plugin }\n }\n\n /**\n * 有效 server 集 = settings.mcp_servers(otto,胜)合并 `.mcp.json`(claude)+ 插件\n * `.mcp.json`(最低优先级)。优先级(高→低):otto > claude > plugin。\n * 三者皆经各自信任门;`.mcp.json`/插件 server 为只读导入:不参与 setServerEnabled 落盘。\n */\n private effectiveServers(): Record<string, McpServerConfig & { disabled?: boolean }> {\n const { otto, claude, plugin } = this.resolveServerLayers()\n return { ...plugin, ...claude, ...otto }\n }\n\n /**\n * 有效 server 集 + 来源元数据(RFC-081)。\n * 保留每个 server 的 source/pluginId/managed 信息,供 extension-manager 和 panel runtime 使用。\n * 与 effectiveServers() 返回相同 server 集,但结构不同(EffectiveMcpServer vs 扁平 McpServerConfig)。\n */\n listEffectiveServers(): Record<string, EffectiveMcpServer> {\n const { otto, claude, plugin } = this.resolveServerLayers()\n\n const result: Record<string, EffectiveMcpServer> = {}\n\n // 最低优先级:plugin\n for (const [name, config] of Object.entries(plugin)) {\n const raw = config as McpServerConfig & { _pluginId?: string }\n result[name] = {\n ...config,\n source: 'plugin' as const,\n pluginId: raw._pluginId,\n }\n }\n // claude 覆盖 plugin\n for (const [name, config] of Object.entries(claude)) {\n result[name] = { ...config, source: 'claude' as const }\n }\n // otto 最高优先级(覆盖 claude/plugin 同名)\n for (const [name, config] of Object.entries(otto)) {\n result[name] = { ...config, source: 'otto' as const }\n }\n\n // 应用 disabled 状态\n for (const [name, server] of Object.entries(result)) {\n if (server.source === 'otto') {\n const entry = (otto[name] ?? {}) as { disabled?: boolean }\n server.disabled = entry.disabled ?? false\n } else if (server.source === 'plugin' && server.managed) {\n // managed 插件 server 跟随插件启用/禁用状态\n server.disabled =\n this.settings.get('disabled_plugins')?.includes(server.pluginId ?? '') ?? false\n }\n }\n\n return result\n }\n\n /** 全量重投影 mcp__ 前缀工具:清旧 → 注册活 manager 的全部工具。 */\n private syncTools(): void {\n const stale = this.toolRegistry\n .getAll()\n .map((tool) => tool.name)\n .filter((name) => name.startsWith('mcp__'))\n if (stale.length > 0) {\n this.toolRegistry.unregister(stale)\n }\n const tools = this._manager?.getAllTools() ?? []\n if (tools.length > 0) {\n this.toolRegistry.register(tools, { category: 'mcp', preset: 'full' })\n }\n }\n\n /** 惰性创建 manager 并挂同步监听(启动全禁用时 manager 为 null,运行时启用某 server 时拉起)。 */\n private ensureManager(): McpManager {\n if (this._manager) return this._manager\n const resilience = this.getResilienceConfig?.()\n const manager = new McpManager({\n skipOnError: true,\n processSpawnFactory: mcpSpawnFactory,\n maxReconnectAttempts: resilience?.mcp.maxReconnectAttempts,\n reconnectDelayMs: resilience?.mcp.reconnectDelayMs,\n requestTimeoutMs: resilience?.mcp.requestTimeoutMs,\n })\n this._manager = manager\n const sync = () => this.syncTools()\n manager.on('server.connected', sync)\n manager.on('tools.changed', sync)\n manager.on('server.disconnected', sync)\n // 宿主据协议层错误码渲染 actionable 指引(CLI 文案是宿主知识,\n // 不驻协议层)。skipOnError 下连接失败仅 warn,此处补 actionable 行(如 MCP_UNAUTHORIZED)。\n manager.on('server.error', (info) => {\n const actionable = renderMcpServerError(info)\n if (actionable) this.logger.warn(actionable)\n })\n return manager\n }\n\n /**\n * 连接全部启用的 MCP server 并重投影工具。返回本轮连接统计(RFC-150 D1:宿主层\n * `App.start()` 据调用方是否 await 本方法决定阻塞/非阻塞语义——本方法自身行为不变,\n * \"非阻塞\"是调用方 `void this.mcpController.start()` 不 await 的结果,不需要 controller\n * 内部感知交互/headless 模式)。\n */\n async start(): Promise<{ connected: number; failed: number }> {\n const servers = this.effectiveServers()\n if (Object.keys(servers).length === 0) {\n return { connected: 0, failed: 0 }\n }\n\n const enabled = Object.fromEntries(\n Object.entries(servers).filter(([, config]) => !config.disabled),\n )\n if (Object.keys(enabled).length === 0) {\n return { connected: 0, failed: 0 }\n }\n\n const manager = this.ensureManager()\n const result = await manager.connectAll(enabled as Record<string, McpServerConfig>)\n this.syncTools()\n return result\n }\n\n /**\n * 运行时启用/禁用单个 MCP server:落盘 mcp_servers[name].disabled → 实时连/断 → 重投影工具。\n * 启用:惰性拉起 manager 并 connect;禁用:disconnect(manager 留存)。未知 server 抛错。\n */\n async setServerEnabled(name: string, enabled: boolean): Promise<void> {\n const servers = { ...(this.settings.get('mcp_servers') ?? {}) }\n const config = servers[name]\n if (!config) {\n throw new Error(`Unknown MCP server \"${name}\"`)\n }\n servers[name] = { ...config, disabled: !enabled }\n await this.settings.persist({ mcp_servers: servers })\n\n if (enabled) {\n const manager = this.ensureManager()\n await manager.connect(name, servers[name] as McpServerConfig)\n } else {\n await this._manager?.disconnect(name)\n }\n this.syncTools()\n }\n\n /**\n * 热重载——断当前全部、按最新 settings.mcp_servers 重连 enabled 的、重投影工具\n * (不重启进程)。delegate 写了新 MCP server 配置 apply 后 /reload 即认到。\n * syncTools 反应式重投影 → 主 toolRegistry 即时更新 → App.tools getter 下次读即见新工具。\n */\n async reload(): Promise<void> {\n if (this._manager) {\n await this._manager.disconnectAll()\n this._manager = null\n }\n await this.start()\n this.syncTools()\n }\n\n async stop(): Promise<void> {\n if (this._manager) {\n await this._manager.disconnectAll()\n this._manager = null\n }\n }\n}\n","import type { SettingsManager } from '@x-otto/setting'\nimport type { SkillRegistry } from '@x-otto/skill'\nimport type { AgentProfile } from '@x-otto/prompt'\nimport type { McpController } from './mcp-controller'\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport { evaluatePluginTrust } from '@x-otto/plugin'\n\n/**\n * extension-manager.ts —— 统一「可插拔项」管理(/extension 命令的后端)。\n *\n * 终局视角:把用户**为扩展 agent 能力而装的东西**收敛到单一管理面:\n * - mcp MCP 服务器(实时连/断;落盘 mcp_servers[].disabled)\n * - skill Skills(registry enable/disable,下一轮生效;落盘 disabled_skills)\n * - agent 自定义声明式 agent(落盘 disabled_agents,下次 spawn 生效)\n * - plugin 插件 bundle(RFC-044;禁用即其 skill/agent/command/mcp 贡献全部消失;落盘 disabled_plugins)\n *\n * 刻意**不**纳入引擎内部接线(hooks)与低层单工具(tools,归 /permissions 的 deny 策略管辖)——\n * 它们不是「用户装的扩展」。新类别 = 新增一个 ExtensionProvider,命令/UI 零改动。\n *\n * 单一真源 = settings:每次切换都落盘对应 disabled_* / mcp_servers[].disabled。\n */\n\nexport type ExtensionCategory = 'mcp' | 'skill' | 'agent' | 'plugin'\n\n/** 切换后何时生效(UI 据此提示)。 */\nexport type ExtensionApplies = 'live' | 'next-turn'\n\nexport interface ExtensionItem {\n category: ExtensionCategory\n /** 类别内唯一 id(server 名 / skill 名 / agent 名)。 */\n id: string\n name: string\n description: string\n enabled: boolean\n /** 附加状态行(如 MCP 'ready · 3 tools'、skill 来源)。 */\n detail?: string\n applies: ExtensionApplies\n /** 是否不可单独切换(RFC-081:managed MCP server 必须通过卸载插件移除)。 */\n readonly?: boolean\n /** 插件 scope(仅 plugin 类别有值;user / project / repository / builtin)。 */\n scope?: string\n}\n\n/** 单类别的列举 + 切换契约(落盘由各 provider 负责)。 */\ninterface ExtensionProvider {\n readonly category: ExtensionCategory\n list(): ExtensionItem[]\n setEnabled(id: string, enabled: boolean): Promise<void>\n}\n\nclass McpExtensionProvider implements ExtensionProvider {\n readonly category = 'mcp' as const\n constructor(\n private readonly mcp: McpController,\n ) {}\n\n list(): ExtensionItem[] {\n const effectiveServers = this.mcp.listEffectiveServers()\n const infos = this.mcp.manager?.getServerInfos() ?? []\n const byName = new Map(infos.map((i) => [i.name, i]))\n const mcpDetail = (enabled: boolean, name: string): string | undefined => {\n const info = byName.get(name)\n const detail: string[] = []\n if (enabled && info) {\n detail.push(String(info.status))\n if (info.tools?.length) detail.push(`${info.tools.length} tools`)\n }\n return detail.length ? detail.join(' · ') : undefined\n }\n const items: ExtensionItem[] = []\n\n for (const [name, server] of Object.entries(effectiveServers)) {\n // claude 层不在 /extension 面板展示\n if (server.source === 'claude') continue\n\n const isManaged = server.source === 'plugin' && server.managed === true\n const isUser = server.source === 'otto'\n if (!isManaged && !isUser) continue\n\n const description = isManaged\n ? `由 ${server.pluginId ?? 'unknown'} 插件管理`\n : (server.url ?? server.command ?? 'MCP server')\n const detail = mcpDetail(!server.disabled, name)\n const managedNote = isManaged ? 'managed' : undefined\n const combinedDetail = [detail, managedNote].filter(Boolean).join(' · ')\n\n items.push({\n category: this.category,\n id: name,\n name,\n description,\n enabled: !server.disabled,\n detail: combinedDetail || undefined,\n applies: 'live' as const,\n readonly: isManaged,\n })\n }\n\n return items\n }\n\n async setEnabled(id: string, enabled: boolean): Promise<void> {\n // RFC-081:managed MCP server 不可单独 toggle\n const effective = this.mcp.listEffectiveServers()\n const server = effective[id]\n if (server?.source === 'plugin' && server.managed) {\n throw new Error(\n `MCP server \"${id}\" 由插件 ${server.pluginId ?? 'unknown'} 管理,无法单独禁用。请卸载插件以移除。`,\n )\n }\n await this.mcp.setServerEnabled(id, enabled)\n }\n}\n\nclass SkillExtensionProvider implements ExtensionProvider {\n readonly category = 'skill' as const\n constructor(\n private readonly settings: SettingsManager,\n private readonly skills: SkillRegistry,\n ) {}\n\n list(): ExtensionItem[] {\n const out: ExtensionItem[] = []\n for (const [name, reg] of this.skills.getAll()) {\n out.push({\n category: this.category,\n id: name,\n name,\n description: reg.definition.metadata.description ?? '',\n enabled: reg.status !== 'disabled',\n detail: reg.source?.type,\n applies: 'next-turn',\n })\n }\n return out\n }\n\n async setEnabled(id: string, enabled: boolean): Promise<void> {\n if (enabled) this.skills.enable(id)\n else this.skills.disable(id)\n const disabled: string[] = []\n for (const [name, reg] of this.skills.getAll()) {\n if (reg.status === 'disabled') disabled.push(name)\n }\n await this.settings.persist({ disabled_skills: disabled })\n }\n}\n\nclass AgentExtensionProvider implements ExtensionProvider {\n readonly category = 'agent' as const\n constructor(\n private readonly settings: SettingsManager,\n private readonly getProfiles: () => readonly AgentProfile[],\n ) {}\n\n list(): ExtensionItem[] {\n const disabled = new Set(this.settings.get('disabled_agents') ?? [])\n return this.getProfiles().map((p) => ({\n category: this.category,\n id: p.name,\n name: p.name,\n description: p.description ?? '',\n enabled: !disabled.has(p.name),\n applies: 'next-turn' as const,\n }))\n }\n\n async setEnabled(id: string, enabled: boolean): Promise<void> {\n const disabled = new Set(this.settings.get('disabled_agents') ?? [])\n if (enabled) disabled.delete(id)\n else disabled.add(id)\n await this.settings.persist({ disabled_agents: [...disabled] })\n }\n}\n\nclass PluginExtensionProvider implements ExtensionProvider {\n readonly category = 'plugin' as const\n constructor(\n private readonly settings: SettingsManager,\n /** 列全部插件(**含已禁用**,供面板重新启用)= 不带 disabled 过滤的 discoverPlugins。 */\n private readonly getPlugins: () => readonly DiscoveredPlugin[],\n /** RFC-085 M3:disable 时 kill 关联子进程(可选,渐进接入)。 */\n private readonly processRuntime?: import('@x-otto/runtime').ProcessRuntime,\n ) {}\n\n list(): ExtensionItem[] {\n const disabled = new Set(this.settings.get('disabled_plugins') ?? [])\n return this.getPlugins().map((p) => {\n // RFC-128:builtin scope 恒定信任、恒定启用——`gated`/`disabled.has` 判断对它\n // 天然不生效(discoverPlugins 已在磁盘扫描前注入、不受 disabled 参数影响;\n // evaluatePluginTrust 的 gated 只对 scope==='project'||scope==='repository' 判定,\n // RFC-130 已将仓库级插件纳入同一信任门),此处额外标 readonly:true\n // 让 UI 层不渲染可切换控件(对齐既有 managed MCP server 的 readonly 语义),而非依赖\n // \"反正切了也没用\"的隐式行为。\n const isBuiltin = p.scope === 'builtin'\n const gated = evaluatePluginTrust(p).gated\n const detail = [p.scope, gated ? 'untrusted' : undefined].filter(Boolean).join(' · ')\n return {\n category: this.category,\n id: p.id,\n name: p.manifest.name ?? p.id,\n description: p.manifest.description ?? '',\n enabled: isBuiltin ? true : !disabled.has(p.id),\n detail: detail || undefined,\n applies: 'next-turn' as const,\n readonly: isBuiltin || undefined,\n scope: p.scope,\n }\n })\n }\n\n async setEnabled(id: string, enabled: boolean): Promise<void> {\n const plugin = this.getPlugins().find((p) => p.id === id)\n if (plugin?.scope === 'builtin') {\n // RFC-128:builtin 插件恒定启用,不接受禁用请求——不静默忽略(那样用户点了\n // \"禁用\"看起来成功、下次打开面板却发现还是启用状态,体验上是个谜;同 McpExtensionProvider\n // 对 managed server 的处理方式,显式抛错说明原因)。\n throw new Error(`插件 \"${id}\" 是 otto 内置插件,恒定信任+启用,无法禁用。`)\n }\n const disabled = new Set(this.settings.get('disabled_plugins') ?? [])\n if (enabled) disabled.delete(id)\n else disabled.add(id)\n await this.settings.persist({ disabled_plugins: [...disabled] })\n\n // RFC-085 M3:disable 时 kill 所有关联子进程\n if (!enabled && this.processRuntime) {\n await this.processRuntime.killAll({ owner: { type: 'plugin-hook', id } })\n await this.processRuntime.killAll({ owner: { type: 'plugin-script', id } })\n }\n }\n}\n\nexport interface ExtensionManagerDeps {\n settings: SettingsManager\n mcp: McpController\n skills: SkillRegistry | null\n /** 全量声明式 agent profile(含已禁用的,供面板列出可切换)。 */\n getAgentProfiles: () => readonly AgentProfile[]\n /** 列全部插件(含已禁用,RFC-044 bundle 启停);省略则不暴露 plugin 类别。 */\n getPlugins?: () => readonly DiscoveredPlugin[]\n /** RFC-085 M3:插件 disable 时 kill 关联子进程。 */\n processRuntime?: import('@x-otto/runtime').ProcessRuntime\n /**\n * RFC-303 D4:插件 enable/disable 切换完成后的通知(落盘后调用,由 App 桥接为\n * monitor 事件 `plugin.enabled`/`plugin.disabled`)。可选——不传则插件切换不对外\n * 广播(仅影响依赖该事件的 monitor 插件,不影响切换本身)。\n */\n onPluginToggle?: (id: string, enabled: boolean) => void\n}\n\nexport class ExtensionManager {\n private readonly providers: ExtensionProvider[]\n private readonly deps: ExtensionManagerDeps\n\n constructor(deps: ExtensionManagerDeps) {\n this.deps = deps\n this.providers = [\n new McpExtensionProvider(deps.mcp),\n ...(deps.skills ? [new SkillExtensionProvider(deps.settings, deps.skills)] : []),\n new AgentExtensionProvider(deps.settings, deps.getAgentProfiles),\n ...(deps.getPlugins ? [new PluginExtensionProvider(deps.settings, deps.getPlugins, deps.processRuntime)] : []),\n ]\n }\n\n /** 全类别扁平列举(按 provider 注册序:mcp → skill → agent → plugin;skill/plugin 条件注册)。 */\n list(): ExtensionItem[] {\n return this.providers.flatMap((p) => p.list())\n }\n\n /** 切换某项启用态:路由到对应 provider(落盘 + 实时/下轮生效由 provider 负责)。 */\n async setEnabled(category: ExtensionCategory, id: string, enabled: boolean): Promise<void> {\n const provider = this.providers.find((p) => p.category === category)\n if (!provider) {\n throw new Error(`Unknown extension category \"${category}\"`)\n }\n await provider.setEnabled(id, enabled)\n // RFC-303 D4:插件 bundle 启停完成(落盘后)→ 通知 monitor 插件(observer-only)。\n if (category === 'plugin' && this.deps.onPluginToggle) {\n this.deps.onPluginToggle(id, enabled)\n }\n }\n}\n","/** Feedback registry: store/query user satisfaction feedback entries. */\nimport type { FeedbackProvider } from '@x-otto/plugin'\n\nexport class FeedbackRegistry {\n private provider: FeedbackProvider | undefined\n private source: string | undefined\n\n /**\n * 注册。同 source 幂等更新放行(reload 场景);跨 source 覆盖检查——已有其他插件\n * 注册时,非同一 source 的二次注册被拒绝(同 `OAuthRegistry.register` 覆盖攻击防护)。\n * 返回是否注册成功。\n */\n register(provider: FeedbackProvider, source: string): boolean {\n if (this.source !== undefined && this.source !== source) {\n return false\n }\n this.provider = provider\n this.source = source\n return true\n }\n\n /**\n * 原子替换(同 `OAuthRegistry.replace` 模式)——插件 reload 场景\"卸旧装新\"在单次同步\n * 调用内完成,不经过中间的空档态。不做跨 source 覆盖检查(调用方已确认是同一插件的\n * reload)。\n */\n replace(provider: FeedbackProvider, source: string): void {\n this.provider = provider\n this.source = source\n }\n\n /**\n * 卸载/禁用时清理(对应 `disabled_plugins` 切换、`/reload` 卸载分支)。仅当当前\n * provider 的 source 与传入 source 匹配才清空——防止跨插件误清(如插件 A 卸载时\n * 传入的 source 恰好不是当前占用者,不应清掉插件 B 的注册)。\n */\n unregister(source: string): void {\n if (this.source === source) {\n this.provider = undefined\n this.source = undefined\n }\n }\n\n get(): FeedbackProvider | undefined {\n return this.provider\n }\n\n reset(): void {\n this.provider = undefined\n this.source = undefined\n }\n}\n","/** Sigil resolver registry: plugin-registered prefix resolvers. */\nimport type { SigilResolver } from '@x-otto/interchange'\n\nexport class SigilResolverRegistry {\n private readonly resolvers = new Map<string, { resolver: SigilResolver; source: string }>()\n\n /**\n * 注册一批 resolver(last-write-wins 按 id 覆盖——重载场景先 unregisterBySource 再\n * register,正常不撞;跨 source 同 id 覆盖记 warn 由调用方装载器负责,这里保持纯粹)。\n */\n register(resolvers: readonly SigilResolver[], source: string): void {\n for (const r of resolvers) {\n this.resolvers.set(r.id, { resolver: r, source })\n }\n }\n\n /** 按 id 查 resolver(expand 管线消费)。 */\n get(id: string): SigilResolver | undefined {\n return this.resolvers.get(id)?.resolver\n }\n\n /** 注销某 source 的全部 resolver(插件 unload/reload 必调,R5)。 */\n unregisterBySource(source: string): void {\n for (const [id, entry] of this.resolvers) {\n if (entry.source === source) this.resolvers.delete(id)\n }\n }\n\n /** 全量只读快照(诊断/测试用)。 */\n getAll(): ReadonlyMap<string, SigilResolver> {\n const out = new Map<string, SigilResolver>()\n for (const [id, entry] of this.resolvers) out.set(id, entry.resolver)\n return out\n }\n\n get size(): number {\n return this.resolvers.size\n }\n}\n","/** Sigil expansion: parse #tag/@mention in user input → resolved content chips. */\nimport { createLogger } from '@x-otto/shared'\n\nimport type { SigilResolveContext, SigilResolver } from '@x-otto/interchange'\nimport type { SigilResolverRegistry } from './sigil-resolver-registry'\n\nconst logger = createLogger('@x-otto/coding:sigil-expand')\n\n/** chip 语法 `[@kind:param]`(param 内 `\\]` 转义,对齐既有 extractModelChips 正则约定)。 */\nconst CHIP_RE = /\\[@([a-z][\\w.-]*):((?:\\\\\\]|[^\\]])+)\\]/g\n/** 无参数 chip 语法 `[@kind]`(@changes/@git/@clipboard 这类零参数上下文 chip)。 */\nconst BARE_CHIP_RE = /\\[@([a-z][\\w.-]*)\\]/g\n/**\n * 正则型引用(非 chip 语法,收编自 cli expandContextChips 的两个特例):\n * `@path:12-20` 文件行范围 → 查 resolver id `file-range`(param = `path:12` / `path:12-20`);\n * `@def:Symbol` 符号定义 → 查 resolver id `def`(param = 符号名)。\n * resolve 返回空串 = \"无法展开,引用原样保留\"(如文件不存在/非 git 仓——对齐原实现\n * `return m` 兜底,不算失败不记 warn)。\n */\nconst FILE_RANGE_RE = /@([\\w./\\-]+):(\\d+)(?:-(\\d+))?/g\nconst SYMBOL_RE = /@def:([A-Za-z_]\\w*)/g\n\nconst DEFAULT_TIMEOUT_MS = 10_000\nconst DEFAULT_MAX_OUTPUT = 20_000\n\nexport interface ExpandSigilChipsOptions {\n /** action 型 chip 的 kind 前缀集合——命中即整体跳过(不查 resolver、原样保留)。 */\n skipPrefixes?: ReadonlySet<string>\n /** 是否运行在与用户同机的 UI 宿主进程(TUI/cli = true,service = false)。缺省 true。 */\n uiHost?: boolean\n /** 输出上限(字符),缺省 20_000。 */\n maxOutput?: number\n /**\n * chip 原文 → resolverId 的条目级查找(RFC-210 D2:插件声明式条目经 `entry.resolverId`\n * 绑定 resolver,chip 的 kind 段是通用 'resource' 不含 resolver 信息)。返回非空 id 时\n * 优先于 kind 直查。App 层接线为 `pluginInput` 按 value===chip 原文查条目取 resolverId。\n */\n lookupResolverId?: (rawChip: string) => string | undefined\n}\n\ninterface ChipOccurrence {\n /** 原文中的完整 chip 文本(替换 key)。 */\n raw: string\n /** kind 段(resolver 查找键)。 */\n kind: string\n /** param 段(`\\]` 已还原;无参数 chip 为 '')。 */\n param: string\n}\n\nfunction collectChips(text: string, skipPrefixes?: ReadonlySet<string>): ChipOccurrence[] {\n const seen = new Set<string>()\n const out: ChipOccurrence[] = []\n for (const re of [CHIP_RE, BARE_CHIP_RE]) {\n re.lastIndex = 0\n for (const m of text.matchAll(re)) {\n const raw = m[0]!\n if (seen.has(raw)) continue\n seen.add(raw)\n const kind = m[1]!\n if (skipPrefixes?.has(kind)) continue\n out.push({ raw, kind, param: (m[2] ?? '').replace(/\\\\\\]/g, ']') })\n }\n }\n // 正则型引用(文件行范围 / @def 符号)→ 映射到固定 resolver id\n FILE_RANGE_RE.lastIndex = 0\n for (const m of text.matchAll(FILE_RANGE_RE)) {\n const raw = m[0]!\n if (seen.has(raw)) continue\n seen.add(raw)\n if (skipPrefixes?.has('file-range')) continue\n out.push({ raw, kind: 'file-range', param: raw.slice(1) }) // 去掉前导 @,param = path:start[-end]\n }\n SYMBOL_RE.lastIndex = 0\n for (const m of text.matchAll(SYMBOL_RE)) {\n const raw = m[0]!\n if (seen.has(raw)) continue\n seen.add(raw)\n if (skipPrefixes?.has('def')) continue\n out.push({ raw, kind: 'def', param: m[1]! })\n }\n return out\n}\n\nfunction withTimeout(p: Promise<string>, ms: number): Promise<string> {\n return new Promise((resolvePromise, rejectPromise) => {\n const timer = setTimeout(() => rejectPromise(new Error(`resolver timeout after ${ms}ms`)), ms)\n p.then(\n (v) => {\n clearTimeout(timer)\n resolvePromise(v)\n },\n (e) => {\n clearTimeout(timer)\n rejectPromise(e)\n },\n )\n })\n}\n\nfunction truncateOutput(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max)}\\n[...truncated ${s.length - max} chars...]`\n}\n\n/**\n * 统一展开出口。resolve 结果直接替换 chip 原文(resolver 自行负责围栏块包裹——收编的\n * 内置 resolver 保留既有 `fenced()` 行为,插件 resolver 的输出经 truncate 后原样注入,\n * 提示注入防护由 `input.resolver` capability 高危信任门承担,RFC-210 D3)。\n */\nexport async function expandSigilChips(\n text: string,\n registry: SigilResolverRegistry,\n ctx: { workspaceDir: string },\n opts?: ExpandSigilChipsOptions,\n): Promise<string> {\n const chips = collectChips(text, opts?.skipPrefixes)\n if (chips.length === 0) return text\n\n const uiHost = opts?.uiHost ?? true\n const maxOutput = opts?.maxOutput ?? DEFAULT_MAX_OUTPUT\n const resolveCtx: SigilResolveContext = { workspaceDir: ctx.workspaceDir, maxOutput }\n\n const jobs = chips.map(async (chip): Promise<[string, string] | null> => {\n // 条目级 resolverId 优先(插件声明式条目,kind='resource' 通用段不含 resolver 信息),\n // 回退 kind 直查(内置 chip:changes/git/clipboard/file-range/def 的 id 即 kind)。\n const entryResolverId = opts?.lookupResolverId?.(chip.raw)\n const resolver: SigilResolver | undefined =\n (entryResolverId ? registry.get(entryResolverId) : undefined) ?? registry.get(chip.kind)\n if (!resolver) return null // 透传型 chip(agent/skill/mcp/file…)——原样保留\n if (resolver.uiHostOnly && !uiHost) {\n logger.debug({ chip: chip.raw }, 'uiHostOnly resolver skipped in non-uiHost context')\n return null\n }\n const timeoutMs = resolver.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const body = await withTimeout(\n Promise.resolve().then(() => resolver.resolve(chip.param, resolveCtx)),\n timeoutMs,\n )\n // 空串 = \"无法展开,引用原样保留\"(file-range/def 的文件不存在/无命中兜底,\n // 对齐原 expandContextChips 的 `return m` 语义,不算失败)。\n if (body === '') return null\n return [chip.raw, truncateOutput(body, maxOutput)]\n })\n\n const settled = await Promise.allSettled(jobs)\n let result = text\n for (let i = 0; i < settled.length; i++) {\n const s = settled[i]!\n if (s.status === 'rejected') {\n // fail-soft:chip 原样保留,不阻断提交(对齐既有 expandContextChips 失败兜底心智)\n logger.warn({ chip: chips[i]!.raw, err: String(s.reason) }, 'sigil resolver failed, chip left as-is')\n continue\n }\n if (s.value === null) continue\n const [raw, body] = s.value\n result = result.split(raw).join(body)\n }\n return result\n}\n","/** Load compiled contribution: parse plugin manifest → typed contribution objects. */\nimport { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport { evaluatePluginTrust, type DiscoveredPlugin, type PluginCapability } from '@x-otto/plugin'\n\nimport type { Logger } from '@x-otto/shared'\n\nconst requireModule = createRequire(import.meta.url)\n\n/** 单个代码式贡献轴的装载配置(差异点全参数化,共性固化在 `loadCompiledContributions`)。 */\nexport interface CompiledContributionSpec<TEntry, TOut extends { pluginId: string }> {\n /** 门控 capability(renderers/statusWidgets/fileViewers 用 `tui.renderer`(RFC-210 改名),a2ui 用 `a2ui.component`)。 */\n capability: PluginCapability\n /** manifest 轴名(信任门日志模板:`skipping ${axis}` / `contributes.${axis}`)。 */\n axis: string\n /** 条目级日志名词(`${noun} entry not found, skipped` 等模板)。 */\n noun: string\n /** pending 日志尾注(renderer 现状 `RFC-140 D3`,其余 `RFC-140 D3 pattern`——逐字保真)。 */\n pendingNote: string\n /** 各轴自己的 createLogger 实例(保持既有 logger 名,如 `@x-otto/coding:plugin-renderer-loader`)。 */\n logger: Logger\n /** 从 manifest 取该轴条目。 */\n entries: (plugin: DiscoveredPlugin) => readonly TEntry[] | undefined\n /** 条目内的编译产物相对路径。 */\n entryPath: (entry: TEntry) => string\n /** 导出名(undefined → 'default')。 */\n exportName: (entry: TEntry) => string | undefined\n /** 条目 id 的日志字段(字段名逐轴不同:rendererId/widgetId/viewerId/type,逐字保真)。 */\n idLogFields: (entry: TEntry) => Record<string, unknown>\n /** not-function 日志是否附 `type: typeof render` 字段(a2ui 现状无,且会与其 `type` id 字段撞键)。 */\n logTypeofOnNotFunction: boolean\n /**\n * 装载前逐条预检(信任门之后、循环内):a2ui 的内置保留字检查挂这里。\n * 返回 false = 跳过该条(拒绝告警由 spec 实现方自行打,文案轴特定)。缺省全放行。\n */\n preCheck?: (plugin: DiscoveredPlugin, entry: TEntry) => boolean\n /** require 成功且已确认为 function 后,组装该轴的输出对象(fn 由实现方收窄为轴渲染签名)。 */\n build: (plugin: DiscoveredPlugin, entry: TEntry, fn: unknown) => TOut\n /**\n * 成功加载 info 日志的 mergingObject(不含 pluginId,管道补)。\n * 缺省 undefined = 不打成功日志(a2ui 现状);renderer/fileViewer 含 matcher、statusWidget 不含。\n */\n successLogFields?: (entry: TEntry) => Record<string, unknown>\n}\n\n/**\n * 装载某插件在一个代码式贡献轴上的全部条目。三层信任门(gated → capabilities 声明 →\n * pendingCapabilities)+ fail-soft 逐条装载——与四轴收敛前的行为逐字等价。\n */\nexport function loadCompiledContributions<TEntry, TOut extends { pluginId: string }>(\n plugin: DiscoveredPlugin,\n spec: CompiledContributionSpec<TEntry, TOut>,\n): TOut[] {\n const entries = spec.entries(plugin)\n if (!entries?.length) return []\n\n const verdict = evaluatePluginTrust(plugin)\n if (verdict.gated) {\n spec.logger.warn(\n { pluginId: plugin.id },\n `project/repository plugin not trusted, skipping ${spec.axis} (fail-closed)`,\n )\n return []\n }\n\n if (!plugin.manifest.capabilities?.includes(spec.capability)) {\n spec.logger.warn(\n { pluginId: plugin.id },\n `plugin declares contributes.${spec.axis} but not capabilities:[\"${spec.capability}\"], skipped (fail-closed)`,\n )\n return []\n }\n\n // RFC-140 D3:已布尔信任插件升级中新增声明高危能力时,仅凭 capabilities 字段(声明)无法\n // 拦截,须检查 pendingCapabilities(授予状态)——历史上该检查曾在 renderer 轴单独缺失,\n // 收敛进本管道后单点生效(RFC-202 M1 的核心动机)。\n if (verdict.pendingCapabilities.includes(spec.capability)) {\n spec.logger.warn(\n { pluginId: plugin.id },\n `plugin capability \"${spec.capability}\" declared but not yet granted, skipping ${spec.axis} (fail-closed, ${spec.pendingNote})`,\n )\n return []\n }\n\n const out: TOut[] = []\n for (const entry of entries) {\n if (spec.preCheck && !spec.preCheck(plugin, entry)) continue\n\n const modulePath = join(plugin.dir, spec.entryPath(entry))\n if (!existsSync(modulePath)) {\n spec.logger.warn(\n { pluginId: plugin.id, ...spec.idLogFields(entry), modulePath },\n `${spec.noun} entry not found, skipped`,\n )\n continue\n }\n try {\n delete requireModule.cache[modulePath] // 防热重载读旧产物(同 module-loader 纹理)\n const mod = requireModule(modulePath) as Record<string, unknown>\n const exportName = spec.exportName(entry) ?? 'default'\n const fn = mod?.[exportName]\n if (typeof fn !== 'function') {\n spec.logger.warn(\n {\n pluginId: plugin.id,\n ...spec.idLogFields(entry),\n exportName,\n ...(spec.logTypeofOnNotFunction ? { type: typeof fn } : {}),\n },\n `${spec.noun} export is not a function, skipped`,\n )\n continue\n }\n out.push(spec.build(plugin, entry, fn))\n if (spec.successLogFields) {\n spec.logger.info(\n { pluginId: plugin.id, ...spec.successLogFields(entry) },\n `plugin ${spec.noun} loaded successfully`,\n )\n }\n } catch (err) {\n spec.logger.warn(\n { pluginId: plugin.id, ...spec.idLogFields(entry), err: String(err) },\n `${spec.noun} load failed, skipped`,\n )\n }\n }\n return out\n}\n\n/**\n * 跨插件聚合。`dedupeKey` 缺省 = flatMap 不去重(renderers/statusWidgets/fileViewers 的候选\n * 列表模型——允许多插件为同一 matcher 注册不同视图);提供 = first-registered-wins 去重\n * (a2ui 的 type 全局命名空间),冲突条目经 `onDedupeConflict` 告警(文案轴特定)后丢弃。\n * 此前四轴的聚合策略差异是隐式约定,参数化后显式可见(RFC-202 D1)。\n */\nexport function loadAllCompiledContributions<TEntry, TOut extends { pluginId: string }>(\n plugins: readonly DiscoveredPlugin[],\n spec: CompiledContributionSpec<TEntry, TOut>,\n dedupe?: {\n key: (out: TOut) => string\n onConflict: (out: TOut, existingOwner: string) => void\n },\n): TOut[] {\n if (!dedupe) return plugins.flatMap((p) => loadCompiledContributions(p, spec))\n const seen = new Map<string, string>() // key -> pluginId(已注册者)\n const out: TOut[] = []\n for (const plugin of plugins) {\n for (const item of loadCompiledContributions(plugin, spec)) {\n const key = dedupe.key(item)\n const existingOwner = seen.get(key)\n if (existingOwner) {\n dedupe.onConflict(item, existingOwner)\n continue\n }\n seen.set(key, item.pluginId)\n out.push(item)\n }\n }\n return out\n}\n","/** A2UI renderer loader: plugin-supplied React components for structured UI. */\nimport { type DiscoveredPlugin, type PluginA2uiRendererEntry } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\nimport {\n loadAllCompiledContributions,\n loadCompiledContributions,\n type CompiledContributionSpec,\n} from './load-compiled-contribution'\n\nimport type { A2uiComponent } from '@x-otto/interchange'\n\nconst logger = createLogger('@x-otto/coding:plugin-a2ui-renderer-loader')\n\n/** toPayload 函数签名:返回 a2ui 组件树;null = 放弃接管,回退默认渲染。 */\nexport type A2uiRendererToPayload = (\n message: unknown,\n) => A2uiComponent[] | null | Promise<A2uiComponent[] | null>\n\nexport interface RegisteredA2uiRenderer {\n /** 全局唯一 id(`pluginId:rendererId`)。 */\n id: string\n pluginId: string\n matcher: PluginA2uiRendererEntry['matcher']\n toPayload: A2uiRendererToPayload\n}\n\nconst spec: CompiledContributionSpec<PluginA2uiRendererEntry, RegisteredA2uiRenderer> = {\n capability: 'a2ui.renderer',\n axis: 'a2uiRenderers',\n noun: 'a2ui renderer',\n pendingNote: 'RFC-210 D5',\n logger,\n entries: (plugin) => plugin.manifest.contributes?.a2uiRenderers,\n entryPath: (entry) => entry.entry,\n exportName: (entry) => entry.export,\n idLogFields: (entry) => ({ a2uiRendererId: entry.id }),\n logTypeofOnNotFunction: true,\n build: (plugin, entry, fn) => ({\n id: `${plugin.id}:${entry.id}`,\n pluginId: plugin.id,\n matcher: entry.matcher,\n toPayload: fn as A2uiRendererToPayload,\n }),\n successLogFields: (entry) => ({ a2uiRendererId: entry.id, matcher: entry.matcher }),\n}\n\n/** 装载某插件的全部跨端渲染器(三层信任门 + fail-soft,同 renderers 轴纪律)。 */\nexport function loadPluginA2uiRenderers(plugin: DiscoveredPlugin): RegisteredA2uiRenderer[] {\n return loadCompiledContributions(plugin, spec)\n}\n\n/** 装载全部活跃插件的跨端渲染器。 */\nexport function loadAllPluginA2uiRenderers(plugins: readonly DiscoveredPlugin[]): RegisteredA2uiRenderer[] {\n return loadAllCompiledContributions(plugins, spec)\n}\n\n/** matcher 判定(与 tui plugin-renderer-registry.matches 同构——role/toolName/contentType)。 */\nexport function a2uiRendererMatches(\n matcher: RegisteredA2uiRenderer['matcher'],\n message: unknown,\n): boolean {\n const m = message as Record<string, unknown> | null | undefined\n if (!m || typeof m !== 'object') return false\n if (matcher.role != null && m.role !== matcher.role) return false\n if (matcher.toolName != null) {\n const toolName = m.name ?? m.toolName\n if (toolName !== matcher.toolName) return false\n }\n if (matcher.contentType != null) {\n const content = m.content\n const hasType =\n Array.isArray(content) &&\n content.some((b) => b && typeof b === 'object' && (b as Record<string, unknown>).type === matcher.contentType)\n if (!hasType) return false\n }\n return true\n}\n\nconst TO_PAYLOAD_TIMEOUT_MS = 2_000\n\n/**\n * 对一条消息执行首个 matcher 命中的 toPayload(fail-soft:超时/抛错/非法返回 → 尝试下一\n * 候选;全部耗尽 → null 回退默认渲染)。宿主消费出口(App.resolveA2uiRendererPayload)。\n */\nexport async function resolveA2uiRendererPayloadFrom(\n renderers: readonly RegisteredA2uiRenderer[],\n message: unknown,\n): Promise<A2uiComponent[] | null> {\n for (const r of renderers) {\n if (!a2uiRendererMatches(r.matcher, message)) continue\n try {\n const result = await Promise.race([\n Promise.resolve().then(() => r.toPayload(message)),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`toPayload timeout after ${TO_PAYLOAD_TIMEOUT_MS}ms`)), TO_PAYLOAD_TIMEOUT_MS),\n ),\n ])\n if (Array.isArray(result) && result.length > 0) return result\n // null / 空数组 = 放弃接管,尝试下一候选\n } catch (err) {\n logger.warn({ a2uiRendererId: r.id, err: String(err) }, 'toPayload failed, trying next candidate (fail-soft)')\n }\n }\n return null\n}\n","/** Agent tool names: tool name constants and categorization sets. */\nconst PROFILE_TOOL_NAME_ALIASES: Record<string, string[]> = {\n file_read: ['read'],\n file_write: ['write'],\n file_edit: ['edit'],\n shell: ['bash'],\n search: ['find', 'grep'],\n}\n\n/**\n * B11:必须区分 `undefined`(未声明 → 不限制 → 继承全部)与 `[]`(显式空 → 零工具)。\n * 旧实现把两者都塌成 `[]`,下游 resolveTools 又把空数组当\"全部工具\" → 显式 `defaultTools:[]`\n * 的锁定 agent 反而拿到 bash+edit+write 全集。保留 undefined 让下游据此判\"不限制\"。\n */\nexport function resolveAgentToolNames(defaultTools?: string[]): string[] | undefined {\n if (defaultTools === undefined) {\n return undefined\n }\n if (defaultTools.length === 0) {\n return []\n }\n\n const resolved = new Set<string>()\n for (const name of defaultTools) {\n const aliases = PROFILE_TOOL_NAME_ALIASES[name]\n if (aliases && aliases.length > 0) {\n for (const alias of aliases) {\n resolved.add(alias)\n }\n continue\n }\n resolved.add(name)\n }\n\n return [...resolved]\n}\n","import {\n createModelSlot,\n CATEGORY_POLICY,\n builtinSlotModels,\n rankModelsBySlotAffinity,\n} from '@x-otto/ai'\n\nimport type {\n IsModelUsable,\n Model,\n ModelDecisionLayer,\n ModelRegistry,\n ModelResolution,\n ModelSpec,\n Usability,\n WorkflowSlot,\n} from '@x-otto/ai'\nimport type { SettingsManager } from '@x-otto/setting'\nimport type { Logger } from '@x-otto/shared'\n\n/**\n * RFC-211 M1:从 session-config-resolver.ts 拆出的模型选型子系统——分层候选构建\n * (explicit > slot > category > default)+ usability gate + fallback 链,只依赖\n * `settings`/`getModelRegistry`/`defaultModel`/`explicitModelSpec`/`isModelUsable`/\n * `hasCredential`/`logger`(`SessionConfigResolverDeps` 的一个真子集)。\n *\n * 纯搬迁纪律:本文件与拆分前 session-config-resolver.ts 内对应方法体逐字节等价,\n * 不改变任何分层优先级、usability 判定逻辑、fallback 顺序。\n */\nfunction subagentModelPolicyOff(): boolean {\n return process.env.OTTO_SUBAGENT_MODEL_POLICY === 'off'\n}\n\nexport interface ModelResolverDeps {\n settings: SettingsManager\n getModelRegistry: () => ModelRegistry\n defaultModel?: Model\n /** createApp({ modelId }) 在插件 provider 装载前可能尚未解析——start() 后由 tryResolve 补全。 */\n explicitModelSpec?: ModelSpec\n /**\n * provider 是否已认证(有凭证)——用于「配置模型不可用」时挑**可用**回退模型,而非 getAll()[0]\n * (后者可能是未认证 provider 的模型,如 github-copilot 未登录)。缺省时退回 getAll()[0]。\n */\n hasCredential?: (provider: string) => Promise<boolean>\n /**\n * 模型可用性安全闸。逐候选过此闸,拒绝未认证 / OAuth-429 风险的模型。\n * 由 App 用 `createModelUsabilityGate(authStore)` 注入;缺省时退回 hasCredential-only 判定。\n */\n isModelUsable?: IsModelUsable\n /** 模型决策溯源日志(debug)——记录每次委派选了哪个模型、哪层决定、拒了谁。 */\n logger?: Pick<Logger, 'debug'>\n}\n\nexport class ModelResolver {\n constructor(private readonly deps: ModelResolverDeps) {}\n\n /**\n * 分层模型 resolution。构建有序候选(explicit > slot > category > default),逐个过\n * usability gate,首个可用者胜出;全不可用则跌入 pickUsableFallback。返回带溯源的 ModelResolution。\n *\n * `gated`(= subagent 委派)时才逐候选过 usability gate 并启用 category 智能默认;主会话(gated=false)\n * **尊重显式/配置模型原样返回**(非目标:不改主会话默认模型行为),仅在配置不可解析时兜底。\n */\n async resolveModel(\n explicitModel: Model | undefined,\n slot: WorkflowSlot | undefined,\n category: string | undefined,\n gated: boolean,\n ): Promise<ModelResolution> {\n const registry = this.deps.getModelRegistry()\n const candidates: Array<{ model: Model; layer: ModelDecisionLayer }> = []\n const seen = new Set<string>()\n const add = (model: Model | undefined, layer: ModelDecisionLayer) => {\n if (model && !seen.has(model.id)) {\n seen.add(model.id)\n candidates.push({ model, layer })\n }\n }\n\n add(explicitModel, 'explicit')\n\n const modelSlots = this.deps.settings.get('model_slots')\n const globalModel = this.deps.settings.get('model')\n const defaultSpec = modelSlots?.default ?? globalModel\n // 有效默认模型 = 配置 model(_slots.default) 解析,否则 createApp 注入的 defaultModel。\n // category/slot 内置策略据此判定「同 provider」,故须取有效值而非仅配置值。\n const defaultModel =\n (defaultSpec !== undefined ? registry.tryResolve(defaultSpec) : undefined) ??\n this.deps.defaultModel ??\n (this.deps.explicitModelSpec\n ? registry.tryResolve(this.deps.explicitModelSpec)\n : undefined)\n\n // slot 层:slot 声明的候选(不含 default,default 单列在后)。\n if (slot && defaultModel) {\n for (const m of this.slotCandidates(slot, defaultModel, registry)) {\n add(m, 'slot')\n }\n }\n // category 层(仅 gated):config categories.* > 内置 CATEGORY_POLICY→slot。\n if (gated && category) {\n for (const m of this.categoryCandidates(category, defaultModel, registry)) {\n add(m, 'category')\n }\n }\n // default 层。\n add(defaultModel, 'default')\n\n const rejected: ModelResolution['rejected'] = []\n for (const { model, layer } of candidates) {\n // 主会话:首个候选(显式/配置)原样胜出,不过闸——尊重用户配置。\n if (!gated) {\n return { model, decidedBy: layer, rejected }\n }\n const usability = await this.checkUsable(model)\n if (usability.usable) {\n return { model, decidedBy: layer, rejected }\n }\n if (usability.reason) {\n rejected.push({ spec: model.id, reason: usability.reason })\n }\n }\n\n if (\n candidates.length === 0 &&\n !defaultSpec &&\n !this.deps.defaultModel &&\n !this.deps.explicitModelSpec\n ) {\n throw new Error(\n 'No model configured. Set model in .otto/config.json or pass modelId to createApp().',\n )\n }\n\n // fallback 排序 hint:优先显式 slot,否则 category→CATEGORY_POLICY 的隐含 slot\n // (与 categoryCandidates 用的映射同源,保证「category=critique 委派」在 fallback 兜底时\n // 也偏向 reasoning/planning 强的模型,而非 registry 插入序随便凑合的第一个可用者)。\n const fallbackSlotHint = slot ?? (category ? CATEGORY_POLICY[category] : undefined)\n return {\n model: await this.pickUsableFallback(registry, fallbackSlotHint),\n decidedBy: 'fallback',\n rejected,\n }\n }\n\n /**\n * 模型可用性判定:优先用注入的 usability gate(认证 + 凭证质量);未注入时退回\n * hasCredential-only(旧行为,未认证→unauthenticated)。gate/凭证查询异常一律 fail-open\n * 为可用,避免基础设施抖动阻断选型(与原 pickUsableFallback 的 try/catch 吞错一致)。\n */\n private async checkUsable(model: Model): Promise<Usability> {\n if (this.deps.isModelUsable) {\n try {\n return await this.deps.isModelUsable(model)\n } catch {}\n }\n if (this.deps.hasCredential) {\n try {\n const ok = await this.deps.hasCredential(model.provider)\n return ok ? { usable: true } : { usable: false, reason: 'unauthenticated' }\n } catch {}\n }\n return { usable: true }\n }\n\n /**\n * 配置/各层候选均不可用时挑回退模型:复用 usability gate,选首个过闸(已认证、非 429 风险)的\n * 真实模型——避免回退到未登录 provider 的模型(如 github-copilot 未授权却被 getAll()[0] 选中)。\n * 一个都不过闸 → 退回 getAll()[0] → defaultModel;全无才报错(交上层 /login)。\n *\n * 终局 review 2026-07-23 修复:此前是 registry 插入序纯顺序遍历——一旦分层候选全部被\n * usability gate 拒绝(如 default 模型是不可用的 OAuth 订阅凭证),第一个\"随便凑合可用\"\n * 的模型胜出,完全无视任务类别(critique 委派可能落到能力标签很弱的模型,纯属插件加载\n * 顺序巧合)。`slotHint` 传入后用 `rankModelsBySlotAffinity` 按能力标签重排——无 hint 或\n * hint 无对应策略时行为不变(仍是原插入序)。\n */\n private async pickUsableFallback(\n registry: ModelRegistry,\n slotHint?: WorkflowSlot,\n ): Promise<Model> {\n const all = rankModelsBySlotAffinity(registry.getAll(), slotHint)\n for (const m of all) {\n if ((await this.checkUsable(m)).usable) return m\n }\n // 全员不过闸:优先用户配置的 defaultModel(其显式意图)而非注册序首个(可能是未认证/429 模型)\n // 两者皆无才报错(交上层 /login)。\n const fallback = this.deps.defaultModel ?? all[0]\n if (fallback) return fallback\n throw new Error('No model available — sign in (/login) or pick a model (/model) first.')\n }\n\n /** slot 层候选:该 slot 在 model_slots.slots 声明的真实模型(按序、去 default)。 */\n private slotCandidates(slot: WorkflowSlot, defaultModel: Model, registry: ModelRegistry): Model[] {\n const modelSlots = this.deps.settings.get('model_slots')\n const ms = createModelSlot({ slots: modelSlots?.slots ?? {}, default: defaultModel }, registry)\n return ms.candidates(slot).filter((m) => m.id !== defaultModel.id)\n }\n\n /**\n * category 层候选:优先 config `categories[cat]`(default_model/preferred_models),\n * 否则内置 CATEGORY_POLICY[cat]→slot——先取该 slot 的 config 绑定,仍无则取内置默认(compact→同\n * provider 最便宜=flash)。OTTO_SUBAGENT_MODEL_POLICY=off 仅停用内置策略,config categories.* 仍生效。\n */\n private categoryCandidates(\n category: string,\n defaultModel: Model | undefined,\n registry: ModelRegistry,\n ): Model[] {\n const out: Model[] = []\n const tryAdd = (id?: string) => {\n if (id) {\n const m = registry.tryResolve(id)\n if (m) out.push(m)\n }\n }\n\n const cfg = this.deps.settings.get('categories')?.[category]\n tryAdd(cfg?.default_model)\n for (const id of cfg?.preferred_models ?? []) tryAdd(id)\n if (out.length > 0) return out\n\n if (subagentModelPolicyOff()) return []\n const catSlot = CATEGORY_POLICY[category]\n if (!catSlot || !defaultModel) return []\n\n // 该 slot 的 config 绑定(不含 default)。\n for (const m of this.slotCandidates(catSlot, defaultModel, registry)) out.push(m)\n if (out.length > 0) return out\n\n // 内置默认:compact → 同 provider 最便宜(flash)。\n return builtinSlotModels(catSlot, defaultModel, registry)\n }\n}\n","import { ORCHESTRATION_TOOL_NAMES } from '@x-otto/tools'\n\nimport type { AgentTool } from '@x-otto/agent'\n\n/** RFC-211 M1:无跨调用状态的会话工具解析纯函数。 */\nexport function filterToolsByNames(tools: AgentTool[], names: string[]): AgentTool[] {\n const nameSet = new Set(names)\n return tools.filter((tool) => nameSet.has(tool.name))\n}\n\nexport function resolveTools(\n getTools: () => AgentTool[],\n explicitTools: AgentTool[] | undefined,\n agentToolNames: string[] | undefined,\n disallowedToolNames?: string[],\n allowSubagents?: boolean,\n): AgentTool[] {\n let tools: AgentTool[]\n if (explicitTools) {\n tools = explicitTools\n } else if (agentToolNames) {\n // B11:已声明(含显式空 `[]`)→ 按名过滤。`[]` → 零工具(正确锁定),不再塌成\"全部工具\"。\n // `undefined`(未声明 → 不限制)走 else 分支取全集。\n tools = filterToolsByNames(getTools(), agentToolNames)\n } else {\n tools = getTools()\n }\n\n if (disallowedToolNames?.length) {\n tools = tools.filter((t) => !disallowedToolNames.includes(t.name))\n }\n\n if (!allowSubagents) {\n tools = tools.filter((t) => !ORCHESTRATION_TOOL_NAMES.has(t.name))\n }\n\n return tools\n}\n\n/**\n * RFC-150 D2:活 session 工具重同步入口——供 `SessionToolsSyncCoordinator` 在 MCP 工具\n * 变化(连接完成/断开)或 `/reload` 后,对声明式解析(无 `options.tools` 显式覆盖)的\n * 活 session 重新计算工具集。复用与 `resolve()` 完全相同的 `readAgentSources` +\n * `resolveTools` 组合(单一来源,不重复实现工具解析规则),只是不重新走模型/prompt 等\n * 其余解析(那些与\"工具集变化\"无关,重跑是浪费且可能产生非预期副作用)。\n *\n * **显式排除 `explicitTools` 会话**(调用方必须在调用前自行判断,见\n * `SessionToolsSyncCoordinator.record` 的 `hasExplicitTools` 标记)——那些会话的工具集是\n * 调用方(如 task_delegate 传入固定工具子集)刻意指定的,不应被全局工具变化\n * 覆盖,这是 RFC-150 重要事项规则的直接要求。\n *\n * RFC-211 M1:签名从\"方法内部调用 this.readAgentSources\"改为显式接收 agent 来源解析结果\n * (`readOnly` 分支的合并逻辑逐字保留),调用方(`SessionConfigResolver.resolveToolsForResync`\n * 薄委托方法)负责在调用前算好 `AgentSources` 传入。\n */\nexport function resolveToolsForResync(\n getTools: () => AgentTool[],\n agent: {\n toolNames?: string[]\n disallowedToolNames?: string[]\n allowSubagents?: boolean\n },\n readOnly: boolean | undefined,\n readonlyDeniedTools: readonly string[],\n): AgentTool[] {\n const disallowedToolNames = readOnly\n ? [...(agent.disallowedToolNames ?? []), ...readonlyDeniedTools]\n : agent.disallowedToolNames\n return resolveTools(getTools, undefined, agent.toolNames, disallowedToolNames, agent.allowSubagents)\n}\n","import type { PromptPreset, SubAgentPromptContext } from '@x-otto/prompt'\nimport type { WorkflowSlot } from '@x-otto/ai'\nimport type { AgentSessionOptions } from '@x-otto/runtime'\n\n/**\n * coding 层自己的会话选项扩展,补齐 runtime 剔除的编码策略 carrier 字段\n * (promptPreset / promptContext / slot)。runtime 的 AgentSession 从未消费这些字段;\n * 它们仅在本 resolver 与 task-runner 之间传递——定义在此而非污染 runtime 类型。\n *\n * RFC-211 M1:从 session-config-resolver.ts 移出为公共契约类型(被 app/ 6 处消费,\n * 不该定义在私有实现文件里)。`session-config-resolver.ts` 通过\n * `export type { CodingSessionOptions } from './types'` 保持原有 import 路径向后兼容。\n */\nexport interface CodingSessionOptions extends Partial<AgentSessionOptions> {\n promptPreset?: PromptPreset\n slot?: WorkflowSlot\n promptContext?: SubAgentPromptContext\n /** 显式模型 id(如 subagent 按能力选型)——优先于 slot,由 registry 解析为 Model。 */\n modelId?: string\n /**\n * 任务类别(task_delegate.category,如 search/quick/deep/critique/review)——\n * 委派子会话据此走内置 category→slot 智能默认(search→flash/deep→pro),插于 slot 与 default 之间。\n */\n category?: string\n /**\n * 本会话是否为委派/子代理(由 task-runner 注入 true)。决定是否对模型选择启用 usability\n * gate + category 智能默认。主会话不设此项 → 尊重显式/配置模型原样(非目标)。\n * 与 promptPreset==='subagent' 取或:覆盖「有 category 但无 agentName」的 main-preset 委派。\n */\n delegated?: boolean\n /**\n * Phase 0(task_delegate/job 统一):只读子会话——从工具集剔除 READONLY_DENIED_TOOLS(文件改动工具)。\n * 由 task_delegate(background) 经 task-runner 注入;改动型后台活走 /job(worktree 隔离 + diff 审计)。\n * 详见 docs/design/task-delegate-job-unification.md。\n */\n readOnly?: boolean\n}\n\n/**\n * Phase 0 只读委托禁用工具集——直接改主 worktree 的文件改动工具。background 委托脱离父对话、\n * 无审计边界且无人在环,故给硬边界(非 Explore 式软只读):\n * edit/write —— 显式文件改写\n * lsp_rename —— 跨文件符号重命名(改文件)\n * bash —— 可 `>file`/`sed -i`/`git commit`,留着只读保证就是空话;只读分析靠 read/grep/find/lsp_*\n * 留 bash_output/kill_shell(无 bash 即惰性,无害)。memory_record/write_todos 不碰 worktree,不禁。\n */\nexport const READONLY_DENIED_TOOLS = ['edit', 'write', 'lsp_rename', 'bash'] as const\n","import { TIER_TO_SLOT, builtinSlotModels } from '@x-otto/ai'\nimport { BUILTIN_AGENT_PROFILES } from '@x-otto/setting'\nimport { resolveAgentProfile } from '@x-otto/prompt'\n\nimport { resolveAgentToolNames } from '../agent/agent-tool-names'\nimport { resolveEnabledCapabilities } from './capability-tool-map'\nimport { ModelResolver, type ModelResolverDeps } from './model-resolver'\nimport { resolveTools, resolveToolsForResync } from './tool-resolver'\n\nimport { defaultMaxToolTurnExtensions, resolveTodoContinuationConfig } from '@x-otto/runtime'\n\nimport type { ThinkingLevel, WorkflowSlot } from '@x-otto/ai'\nimport type { AgentProfile, PromptManager, SubAgentPromptContext } from '@x-otto/prompt'\nimport type { AgentTool, DescribeImagesPort } from '@x-otto/agent'\nimport type { Logger } from '@x-otto/shared'\nimport type { ResolvedSessionConfig } from '@x-otto/runtime'\n\n// RFC-211 M1:公共类型移到 session/types.ts,此处 re-export 保持向后兼容的 import 路径\n// (app.ts/app-builder.ts/tool-wiring.ts/app-session-methods.ts/task-runner.ts/types.ts\n// 共 6 处消费方零改动)。\nexport type { CodingSessionOptions } from './types'\nimport { READONLY_DENIED_TOOLS, type CodingSessionOptions } from './types'\nexport { READONLY_DENIED_TOOLS } from './types'\n\n/**\n * restore 保真度:据持久化 agentName 重建子代理 prompt 身份,供 restore 模板\n * (configProvider)构造 promptRefresh。否则 restore 退回 main preset——replace 子代理重启后丢整段\n * profile prompt、append 子代理基底退回 main。与 resolve() 的子代理分支同走 assemblePreset\n * ({preset:'subagent'})(单一来源);restore 无原 task context(systemTail 已由持久化恢复),故\n * context 留空(角色块/任务描述以持久化的 systemTail 为准)。\n *\n * RFC-101:`tools` 是该 restore 会话的已解析工具集(`deps.getTools()`),经 `resolveEnabledCapabilities`\n * 转换为能力键集合传给 `assemblePreset()`——append 模式 profile 同样消费 `lead-guidance.md`,需要\n * 与 resolve() 主路径同等门控,否则 restore 会话可能看到悬空引用。\n */\nexport function buildRestoredSubagentPromptRefresh(\n promptManager: PromptManager,\n declarativeProfiles: AgentProfile[],\n agentName: string,\n tools: AgentTool[],\n): () => Promise<string> {\n const profile = resolveAgentProfile(\n [...declarativeProfiles, ...BUILTIN_AGENT_PROFILES],\n agentName,\n )\n const enabledCapabilities = resolveEnabledCapabilities(new Set(tools.map((t) => t.name)))\n return () =>\n promptManager.assemblePreset(\n {\n preset: 'subagent',\n agentName,\n profile,\n promptMode: profile?.promptMode,\n },\n enabledCapabilities,\n )\n}\n\ninterface AgentSources {\n slot?: WorkflowSlot\n profileTier?: string\n toolNames?: string[]\n disallowedToolNames?: string[]\n allowSubagents?: boolean\n systemPrompt?: string\n maxToolTurns?: number\n maxToolTurnExtensions?: number\n /** RFC-104 D3:agent 级 per-prompt 预算覆盖。 */\n promptOutputTokenBudget?: number\n promptWallClockBudgetMs?: number\n /** RFC-204 D1/D3:agent 级会话成本软预算覆盖(美元)。 */\n sessionCostBudgetUSD?: number\n /** RFC-204 D4:agent 级早期停滞检测配置覆盖。 */\n stallDetection?: { windowTurns?: number; repeatThreshold?: number } | false\n promptMode?: 'append' | 'replace'\n promptContext?: SubAgentPromptContext\n profile?: AgentProfile\n}\n\nexport interface SessionConfigResolverDeps extends ModelResolverDeps {\n promptManager: PromptManager\n /** app.tools 派生自 toolRegistry + defaultToolPreset(热更新可变)。 */\n getTools: () => AgentTool[]\n /** 声明式 agent 在 start() 才发现,getter 注入运行时状态。 */\n getDeclarativeAgentProfiles: () => AgentProfile[]\n /** app 级缺省 maxToolTurns 兜底。 */\n maxToolTurns: number\n /** app 级缺省 maxToolTurnExtensions 兜底(createApp / CLI --max-tool-turn-extensions)。\n * 未设时不强制,留给 depth/transient 缺省策略(defaultMaxToolTurnExtensions)兜底。 */\n maxToolTurnExtensions?: number\n /** RFC-104 D3:app 级缺省 per-prompt output-token 预算(createApp / CLI --prompt-token-budget)。 */\n promptOutputTokenBudget?: number\n /** RFC-104 D3:app 级缺省 per-prompt 墙钟预算 ms(createApp / CLI --prompt-time-budget)。 */\n promptWallClockBudgetMs?: number\n /** RFC-204 D1/D3:app 级缺省会话成本软预算美元(createApp / CLI --session-cost-budget)。 */\n sessionCostBudgetUSD?: number\n /** RFC-204 D4:app 级缺省早期停滞检测配置(createApp / CLI --no-stall-detection 等)。 */\n stallDetection?: { windowTurns?: number; repeatThreshold?: number } | false\n workspaceDir: string\n /**\n * workspace 逻辑标识(workspaceRef.id)——惰性 getter(而非静态字段)。\n * 修复:`workspaceRef` 在 `App.start()` 才异步解析完成,而本 resolver 在 App 构造期\n * 就已经 new 出来;若用静态字段在构造期固化,常规启动(无 `--id`)下该值永远是\n * `undefined`(`workspaceRef.id` 解析完成后从未回填),导致会话 `workspaceId` 元数据\n * 恒缺失、`/resume --workspace <id>` 过滤对内存态会话形同虚设。传 getter 即可在\n * 每次 resolve() 时读到 `workspaceRef` 就绪后的真实 id(与 `getModelRegistry`/\n * `getDeclarativeAgentProfiles` 同一惰性注入模式)。\n */\n getWorkspaceId?: () => string | undefined\n /**\n * RFC-230:统一重试/超时/退避配置查询——惰性 getter(同 `getWorkspaceId` 模式),不让本\n * resolver 直接依赖 `App` 类型(保持依赖方向:resolver 是被 App 组装的下游)。`resolve()`\n * 在会话创建时被调用,此时 App 必然已 `start()`(`ensureSettingsLoaded()` 已完成),\n * getter 调用安全。未注入时 `streamRetry`/`networkDisconnectRetry` 保持 undefined,\n * runtime/agent 层各自回退到内部默认常量。\n */\n getResilienceConfig?: () => import('@x-otto/env').ResilienceConfig\n /** 模型决策溯源日志(debug)——记录每次委派选了哪个模型、哪层决定、拒了谁。 */\n logger?: Pick<Logger, 'debug'>\n /**\n * RFC-181 D3/M3:图像委托描述端口(App 用 VisionDelegateService.describeImages 注入)。\n * 缺省时会话按 M1 占位分支运行(主模型不支持视觉 → image block 降级为占位文本)。\n */\n describeImages?: DescribeImagesPort\n}\n\n/**\n * RFC-211 M1:session-config-resolver.ts 从 637 行拆分为薄编排层——模型选型下沉\n * `ModelResolver`(model-resolver.ts)、工具解析下沉自由函数(tool-resolver.ts)、\n * 公共类型下沉 `session/types.ts`。本类只保留 `readAgentSources`(模型选型与工具\n * 解析共同的输入源,两块共同消费)与 `resolve()` 主流程编排。\n *\n * 纯搬迁纪律:拆分不改变任何字段优先级链、模型/工具解析结果,端到端行为逐字节保留。\n */\nexport class SessionConfigResolver {\n private readonly modelResolver: ModelResolver\n\n constructor(private readonly deps: SessionConfigResolverDeps) {\n this.modelResolver = new ModelResolver(deps)\n }\n\n private readAgentSources(\n agentName: string | undefined,\n promptPreset: 'main' | 'subagent',\n ): AgentSources {\n const userSetting = agentName ? this.deps.settings.get('agents')?.[agentName] : undefined\n\n const profile =\n promptPreset === 'subagent' && agentName\n ? resolveAgentProfile(\n [...this.deps.getDeclarativeAgentProfiles(), ...BUILTIN_AGENT_PROFILES],\n agentName,\n )\n : undefined\n\n return {\n slot: userSetting?.default_workflow_slot,\n profileTier: profile?.preferredModelTier,\n toolNames: userSetting?.tools ?? resolveAgentToolNames(profile?.defaultTools),\n disallowedToolNames: profile?.disallowedTools,\n allowSubagents: promptPreset === 'main' ? true : profile?.allowSubagents,\n systemPrompt: userSetting?.system_prompt,\n maxToolTurns: userSetting?.max_tool_turns ?? profile?.defaultMaxToolTurns,\n maxToolTurnExtensions:\n userSetting?.max_tool_turn_extensions ?? profile?.defaultMaxToolTurnExtensions,\n promptOutputTokenBudget: userSetting?.prompt_output_token_budget,\n promptWallClockBudgetMs: userSetting?.prompt_wall_clock_budget_ms,\n sessionCostBudgetUSD: userSetting?.session_cost_budget_usd,\n stallDetection:\n userSetting?.stall_detection === false\n ? false\n : userSetting?.stall_detection\n ? { windowTurns: userSetting.stall_detection.window_turns, repeatThreshold: userSetting.stall_detection.repeat_threshold }\n : undefined,\n promptMode: profile?.promptMode,\n promptContext: undefined,\n profile,\n }\n }\n\n /**\n * RFC-150 D2:活 session 工具重同步入口——供 `SessionToolsSyncCoordinator` 在 MCP 工具\n * 变化(连接完成/断开)或 `/reload` 后,对声明式解析(无 `options.tools` 显式覆盖)的\n * 活 session 重新计算工具集。委托 tool-resolver.ts 的自由函数,`readAgentSources` 在\n * 此计算后传入(RFC-211 M1 拆分后的签名变化,见 tool-resolver.ts 注释)。\n */\n /**\n * 持久化推理投入档解析(校验 + 迁移,与交互式 interactive-session-setup.ts 同规则):\n * `minimal`(存量 Claude Code 最低档)迁移为 `low`;其余非法值忽略(返回 undefined →\n * 调用方回退 'medium')。顶层会话据此尊重 settings.thinking_level,headless 与交互式统一。\n */\n private resolvePersistedThinkingLevel(): ThinkingLevel | undefined {\n const raw = this.deps.settings.get('thinking_level')\n const migrated = raw === 'minimal' ? 'low' : raw\n const VALID: readonly ThinkingLevel[] = ['low', 'medium', 'high', 'xhigh', 'max']\n return typeof migrated === 'string' && (VALID as readonly string[]).includes(migrated)\n ? (migrated as ThinkingLevel)\n : undefined\n }\n\n resolveToolsForResync(\n agentName: string | undefined,\n promptPreset: 'main' | 'subagent' | undefined,\n readOnly: boolean | undefined,\n ): AgentTool[] {\n const preset = promptPreset ?? (agentName ? 'subagent' : 'main')\n const agent = this.readAgentSources(agentName, preset)\n return resolveToolsForResync(this.deps.getTools, agent, readOnly, READONLY_DENIED_TOOLS)\n }\n\n resolveTools(\n explicitTools: AgentTool[] | undefined,\n agentToolNames: string[] | undefined,\n disallowedToolNames?: string[],\n allowSubagents?: boolean,\n ): AgentTool[] {\n return resolveTools(this.deps.getTools, explicitTools, agentToolNames, disallowedToolNames, allowSubagents)\n }\n\n /**\n * ── Layer 3: Session config assembler ────────────────────────────────────\n *\n * 优先级(高 → 低):\n * model: options.model > slot(model_slots) > settings.model > defaultModel\n * slot: options.slot > agentConfig.slot > agentProfile.tier → slot\n * tools: options.tools > agentConfig.tools 过滤 > agentProfile.tools 过滤 > 全量\n * systemPrompt: options.systemPrompt/agent.systemPrompt append 到 base 之后(RFC-353 S1)\n * maxToolTurns: options.maxToolTurns > agentConfig > agentProfile > App.maxToolTurns\n * maxToolTurnExtensions:\n * options > agentConfig.max_tool_turn_extensions > agentProfile.defaultMaxToolTurnExtensions\n * > App.maxToolTurnExtensions > 缺省策略(depth>0||transient ? 0 : 3)\n */\n async resolve(options: CodingSessionOptions): Promise<ResolvedSessionConfig & { id?: string }> {\n const promptPreset = options.promptPreset ?? (options.agentName ? 'subagent' : 'main')\n\n const agent = this.readAgentSources(options.agentName, promptPreset)\n\n const gated = promptPreset === 'subagent' || options.delegated === true\n let slot = options.slot ?? agent.slot ?? TIER_TO_SLOT[agent.profileTier ?? '']\n // 委派子会话未显式 slot/category 时,套用 model_slots.subagent_default_slot(若配置)——\n // 让「不带 category 的委派」也默认走便宜档(如 compact→flash)。category 存在则交 category 层,不覆盖。\n if (!slot && gated && !options.category) {\n slot = this.deps.settings.get('model_slots')?.subagent_default_slot\n }\n\n const explicitModel =\n options.model ??\n (options.modelId ? this.deps.getModelRegistry().tryResolve(options.modelId) : undefined) ??\n (this.deps.explicitModelSpec\n ? this.deps.getModelRegistry().tryResolve(this.deps.explicitModelSpec)\n : undefined)\n const modelResolution = await this.modelResolver.resolveModel(explicitModel, slot, options.category, gated)\n const model = modelResolution.model\n\n // RFC-078 M134-02:标题生成用便宜模型 = 同 provider 最便宜真实模型(复用 compact-slot 逻辑:\n // deepseek-v4-pro→flash);已是最便宜则用对话模型本身(仍是便宜档)。与对话模型同 provider→\n // 共用同一已过 usability gate 的认证域,不引入 claude-OAuth 等 429 风险(R3)。\n const titleModel =\n builtinSlotModels('compact', model, this.deps.getModelRegistry())[0] ?? model\n if (gated) {\n this.deps.logger?.debug(\n {\n model: model.id,\n decidedBy: modelResolution.decidedBy,\n category: options.category,\n slot,\n rejected: modelResolution.rejected,\n },\n 'subagent model resolved',\n )\n }\n const disallowedToolNames = options.readOnly\n ? [...(agent.disallowedToolNames ?? []), ...READONLY_DENIED_TOOLS]\n : agent.disallowedToolNames\n let tools = this.resolveTools(\n options.tools,\n agent.toolNames,\n disallowedToolNames,\n agent.allowSubagents,\n )\n // RFC-130 M131-2:`Model.supportsTools === false` 声明的 provider(如 Cursor agent API\n // 只服务端自跑内置工具,M131-3-00b 已证伪客户端双向映射可行性)宿主剥离 tools,避免\n // provider 抛协议错误。声明化字段而非厂商字符串比较(终局 review 修复:此前硬编码\n // `model.api === 'cursor'` 直接违反 RFC-148 框架层零厂商知识判据,是该系列终局收口\n // 完成后立刻引入的倒退,复用 imageConstraints 同款声明化模式修正)。\n if (model.supportsTools === false) {\n tools = []\n }\n\n // RFC-101:本会话已解析工具集(tools,上方 resolveTools() 产出)转换为能力键集合,\n // 门控 lead-guidance.md 中依赖特定能力的段落——零新增计算,复用已算好的 tools。\n const enabledCapabilities = resolveEnabledCapabilities(new Set(tools.map((t) => t.name)))\n\n const promptRefresh =\n options.promptRefresh ??\n (async () => {\n // RFC-353 S1: custom systemPrompt 永远 append 到 base(lead-guidance.md)之后,\n // 不替换 base——安全指导/工具指导不丢,custom 内容作为 volatile 尾段附加。\n // 此前 custom systemPrompt 直接替换整个 base prompt,但 tools 仍启用——\n // prompt injection 面扩大。与 subagent append 模式对齐。\n const base =\n promptPreset === 'subagent' && options.agentName\n ? await this.deps.promptManager.assemblePreset(\n {\n preset: 'subagent',\n agentName: options.agentName,\n profile: agent.profile,\n context: options.promptContext,\n promptMode: agent.promptMode,\n },\n enabledCapabilities,\n )\n : await this.deps.promptManager.assemblePreset({ preset: 'main' }, enabledCapabilities)\n\n const custom = options.systemPrompt ?? agent.systemPrompt\n if (custom !== undefined && custom.length > 0) {\n return base ? `${base}\\n\\n${custom}` : custom\n }\n return base\n })\n\n const initialSystemPrompt = await promptRefresh()\n\n // subagent tail:仅在没有 custom systemPrompt 时才走 profile 模板尾段\n // (custom prompt 已 append 到 base 末尾,不需要额外角色块尾段)。\n const useSubAgentPreset =\n options.systemPrompt === undefined &&\n agent.systemPrompt === undefined &&\n promptPreset === 'subagent' &&\n Boolean(options.agentName)\n const subAgentTail = useSubAgentPreset\n ? this.deps.promptManager.assembleSubAgentTail({\n preset: 'subagent',\n agentName: options.agentName!,\n profile: agent.profile,\n context: options.promptContext,\n promptMode: agent.promptMode,\n })\n : undefined\n\n // 推理投入档解析:options 显式值 > 持久化 settings.thinking_level > 'medium'。\n // 此前 headless 路径(`otto --json/--print`)无视 settings.thinking_level 恒取 'medium'——\n // 只有交互式 TUI 在 interactive-session-setup.ts 里 session 创建后才套用持久化档,\n // 于是 `/effort max` 后跑 headless(含 RFC-316 基准适配器写的 config.thinking_level)完全失效。\n // 顶层会话统一尊重持久化档(校验+minimal→low 迁移与交互式同规则);子代理/委派(gated)\n // 不继承顶层 effort,保持 'medium' 默认(子代理档由 options.thinkingLevel 显式下发)。\n const resolvedThinkingLevel =\n options.thinkingLevel ??\n (gated ? undefined : this.resolvePersistedThinkingLevel()) ??\n 'medium'\n\n return {\n id: options.id,\n model,\n titleModel,\n modelResolution,\n agentName: options.agentName,\n systemPrompt: initialSystemPrompt ?? '',\n systemTail: subAgentTail ? [subAgentTail] : undefined,\n tools,\n thinkingLevel: resolvedThinkingLevel,\n maxToolTurns: options.maxToolTurns ?? agent.maxToolTurns ?? this.deps.maxToolTurns,\n maxToolTurnExtensions:\n options.maxToolTurnExtensions ??\n agent.maxToolTurnExtensions ??\n this.deps.maxToolTurnExtensions ??\n defaultMaxToolTurnExtensions(options.depth ?? 0, options.transient),\n // RFC-104 D3/D6:options > agent 级 > settings 顶层 > app 级;depth>0 一律不给\n // (runtime 构造层还有第二道剥除,双保险——resolver 不下发即主防线)。\n promptOutputTokenBudget:\n (options.depth ?? 0) > 0\n ? undefined\n : options.promptOutputTokenBudget ??\n agent.promptOutputTokenBudget ??\n this.deps.settings.get('prompt_output_token_budget') ??\n this.deps.promptOutputTokenBudget,\n promptWallClockBudgetMs:\n (options.depth ?? 0) > 0\n ? undefined\n : options.promptWallClockBudgetMs ??\n agent.promptWallClockBudgetMs ??\n this.deps.settings.get('prompt_wall_clock_budget_ms') ??\n this.deps.promptWallClockBudgetMs,\n // RFC-204 D6:sessionCostBudgetUSD 同 promptOutputTokenBudget 门控纹理(depth>0 不下发,\n // runtime 构造层还有第二道剥除,双保险);stallDetection 不做 depth 门控(D6 理由:\n // 本地环形窗口,子代理各自独立不存在复合泄漏)。\n sessionCostBudgetUSD:\n (options.depth ?? 0) > 0\n ? undefined\n : options.sessionCostBudgetUSD ??\n agent.sessionCostBudgetUSD ??\n this.deps.settings.get('session_cost_budget_usd') ??\n this.deps.sessionCostBudgetUSD,\n stallDetection:\n options.stallDetection ??\n agent.stallDetection ??\n (() => {\n const s = this.deps.settings.get('stall_detection')\n if (s === undefined) return undefined\n if (s === false) return false\n return { windowTurns: s.window_turns, repeatThreshold: s.repeat_threshold }\n })() ??\n this.deps.stallDetection,\n // RFC-104 D4:todo 闸门 cap settings 化——options > settings.todo_continue_max > env(缺省)。\n // enabled 仍由 env 决定(resolveTodoContinuationConfig);settings 只覆盖 max。\n todoContinuation:\n options.todoContinuation ??\n (() => {\n const settingsMax = this.deps.settings.get('todo_continue_max')\n if (settingsMax === undefined) return undefined // 交 runtime env 缺省解析\n const envBase = resolveTodoContinuationConfig(process.env)\n const max = Number.isFinite(settingsMax) && settingsMax >= 0 ? Math.trunc(settingsMax) : envBase.max\n return { enabled: envBase.enabled, max }\n })(),\n promptRefresh,\n workspaceDir: options.workspaceDir ?? this.deps.workspaceDir,\n workspaceId: options.workspaceId ?? this.deps.getWorkspaceId?.(),\n transient: options.transient,\n depth: options.depth,\n interactive: options.interactive,\n retryAccelerate: options.retryAccelerate,\n // RFC-230:从宿主 ResilienceConfig 投影 streamRetry/networkDisconnectRetry——\n // options 显式覆盖优先于 resilience 投影(与 retryAccelerate 等其余字段的\n // \"options > 其他来源\" 优先级链一致)。\n streamRetry: options.streamRetry ?? this.deps.getResilienceConfig?.().stream,\n networkDisconnectRetry:\n options.networkDisconnectRetry ??\n toNetworkDisconnectStreamRetryConfig(this.deps.getResilienceConfig?.().networkDisconnect),\n describeImages: this.deps.describeImages,\n }\n }\n}\n\n/**\n * RFC-230:`ResilienceConfig.networkDisconnect` 是用户友好的简化形状(`{maxRetries,\n * intervalMs}`——网络断连重试固定间隔,暴露指数退避内部参数如 `backoffFactor` 对用户没有\n * 意义),但 `@x-otto/agent` 的 `StreamRetryConfig`(`work-loop-stream-error.ts` 的\n * `NETWORK_DISCONNECT_RETRY`)用统一形状表达\"固定间隔\"——`backoffFactor:1` 让指数退避\n * 公式退化为常量、`initialDelayMs`/`maxDelayMs` 都等于 `intervalMs`。此处做一次形状转换,\n * 保持两边契约各自的语义清晰(用户配置层 vs 引擎工程层),不强行统一成同一形状。\n */\nfunction toNetworkDisconnectStreamRetryConfig(\n cfg: { maxRetries: number; intervalMs: number } | undefined,\n): { maxRetries: number; initialDelayMs: number; backoffFactor: number; maxDelayMs: number } | undefined {\n if (!cfg) return undefined\n return {\n maxRetries: cfg.maxRetries,\n initialDelayMs: cfg.intervalMs,\n backoffFactor: 1,\n maxDelayMs: cfg.intervalMs,\n }\n}\n","/** Task output: structured result from agent_task/subagent/delegation runs. */\n\nimport { closeSync, constants, openSync, readFileSync, writeSync } from 'node:fs'\n\nconst O_NOFOLLOW = constants.O_NOFOLLOW ?? 0\n\n/** 追加一段转录到作业输出文件(O_NOFOLLOW append;文件不存在则创建,0600)。 */\nexport function appendTaskOutput(path: string, text: string): void {\n const fd = openSync(\n path,\n constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | O_NOFOLLOW,\n 0o600,\n )\n try {\n writeSync(fd, text.endsWith('\\n') ? text : text + '\\n')\n } finally {\n closeSync(fd)\n }\n}\n\n/** 读作业输出末 N 行(O_NOFOLLOW;文件不存在/读失败 → 空串)。有界:只回末 N 行。 */\nexport function readTaskOutputTail(path: string, lines = 20): string {\n let fd: number | undefined\n try {\n fd = openSync(path, constants.O_RDONLY | O_NOFOLLOW)\n const body = readFileSync(fd, 'utf-8')\n return body.replace(/\\n+$/, '').split('\\n').slice(-lines).join('\\n')\n } catch {\n return ''\n } finally {\n if (fd !== undefined) closeSync(fd)\n }\n}\n","/** Detached run: execute agent job in isolated session, no TUI attachment. */\n\nimport { Agent } from '@x-otto/agent'\nimport {\n createToolHookExecutor,\n attachAgentObserver,\n globalAgentObservability,\n} from '@x-otto/runtime'\n\nimport type { AssistantMessage, Model, ThinkingLevel } from '@x-otto/ai'\nimport type { AgentRunContext, AgentTool, AgentMessage, StreamFunction } from '@x-otto/agent'\nimport type { HookRegistry } from '@x-otto/hooks'\nimport type { Logger } from '@x-otto/shared'\n\nimport { appendTaskOutput } from './task-output'\n\nexport interface DetachedRunOptions {\n /** 运行标识(= 作业 id),进 toolHookExecutor 的 sessionId(权限闸/审计归属)。 */\n runId: string\n /** 启动 prompt(首个 user 消息)。 */\n prompt: string\n model: Model\n /** 会话内稳定 system 前缀;缺省空串。 */\n systemPrompt?: string\n /** 注入的工具集(Phase 1 为 worktree-rooted)。 */\n tools: AgentTool[]\n thinkingLevel?: ThinkingLevel\n maxToolTurns?: number\n maxTokens?: number\n /** 转录输出文件(logPath 式;与后台进程同一输出模型,/jobs tail 复用)。每轮文本追加到此。 */\n outputPath: string\n /** LLM 流式函数(取自 runtime.sessions.stream,与主引擎同一路径)。 */\n stream: StreamFunction\n logger: Logger\n hookRegistry: HookRegistry\n /** 委托深度(防 fork 风暴;缺省 0)。 */\n depth?: number\n /**\n * run 撞到\"需确认\"的操作(权限 hook 返 'ask')时回调——调用方据此把作业置 needs_input。\n * 缺省:仍提供 approval 端口但默默拒绝(fail-closed,后台作业不越权也不挂死);提供此回调即转\"挂起等用户答\"。\n */\n onAwaitingInput?: (reason: string) => void\n}\n\nexport interface DetachedRunResult {\n text: string\n messages: AgentMessage[]\n tokenUsage: { input: number; output: number; cacheRead: number }\n durationMs: number\n}\n\nexport interface DetachedRunHandle {\n /** run 完成/失败的 promise(**不**被主回合 await;作业服务挂 then/catch)。 */\n promise: Promise<DetachedRunResult>\n /** 取消该 run(Agent.abort → 内部 AbortController)。 */\n abort(): void\n /**\n * 向**运行中**的 run 注入一条 user 消息(steer),下一个工具回合被 steeringQueue 吸入。\n * 用于\"进 delegate 补充信息\"。run 已完成则无效果(消息进队列但 loop 已结束)。\n */\n steer(text: string): void\n /** 转录输出文件路径(logPath 式;经 readTaskOutputTail 读末 N 行)。 */\n outputPath: string\n}\n\nfunction assistantText(msg: AssistantMessage): string {\n return msg.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text)\n .join('\\n')\n}\n\n/**\n * 起一个 detached agent run。立即返回句柄,run 在后台推进。\n * 调用方负责对 handle.promise 挂 then/catch(如完成后注入会话通知、产 diff)。\n */\nexport function spawnDetachedRun(opts: DetachedRunOptions): DetachedRunHandle {\n const startTime = Date.now()\n\n const context: AgentRunContext = {\n model: opts.model,\n systemPrompt: opts.systemPrompt ?? '',\n messages: [\n {\n role: 'user',\n content: [{ type: 'text', text: opts.prompt }],\n timestamp: Date.now(),\n } as AgentMessage,\n ],\n tools: opts.tools,\n thinkingLevel: opts.thinkingLevel ?? 'medium',\n maxTokens: opts.maxTokens,\n }\n\n let pendingApproval: ((approved: boolean) => void) | undefined\n\n const agent = new Agent({\n stream: opts.stream,\n logger: opts.logger,\n agentName: opts.runId,\n maxToolTurns: opts.maxToolTurns,\n toolHookExecutor: createToolHookExecutor({\n hookRegistry: opts.hookRegistry,\n agentName: opts.runId,\n sessionId: opts.runId,\n }),\n depth: opts.depth ?? 0,\n approval: (\n toolName: string,\n _args: Record<string, unknown>,\n reason: string,\n ): Promise<boolean> => {\n const notifyAwaiting = opts.onAwaitingInput\n if (!notifyAwaiting) return Promise.resolve(false)\n return new Promise<boolean>((resolve) => {\n globalAgentObservability.setAwaitingApproval(opts.runId, `${toolName}: ${reason}`)\n pendingApproval = (approved: boolean) => {\n globalAgentObservability.clearAwaitingApproval(opts.runId)\n resolve(approved)\n }\n try {\n appendTaskOutput(\n opts.outputPath,\n `⏳ 需要你确认「${toolName}」:${reason}\\n 进 Delegates 面板按 o,回复 yes 允许 / no 拒绝。`,\n )\n } catch {}\n notifyAwaiting(reason)\n })\n },\n })\n\n agent.on('turn.end', ({ message }) => {\n const text = assistantText(message)\n if (text) {\n try {\n appendTaskOutput(opts.outputPath, text)\n } catch {}\n }\n })\n\n const abortRun = (): void => {\n if (pendingApproval) {\n const resolve = pendingApproval\n pendingApproval = undefined\n resolve(false)\n }\n agent.abort()\n }\n\n const detachObs = attachAgentObserver(agent, {\n id: opts.runId,\n agentName: opts.runId,\n title: opts.runId,\n prompt: opts.prompt,\n model: opts.model.id,\n origin: 'user',\n depth: opts.depth ?? 0,\n abort: abortRun,\n messages: context.messages,\n })\n\n const promise: Promise<DetachedRunResult> = agent\n .run(context)\n .then((result) => ({\n text: assistantText(result),\n messages: context.messages,\n tokenUsage: {\n input: result.usage.inputTokens,\n output: result.usage.outputTokens,\n cacheRead: result.usage.cacheReadTokens,\n },\n durationMs: Date.now() - startTime,\n }))\n .finally(() => {\n detachObs()\n globalAgentObservability.markEnded(opts.runId)\n })\n\n return {\n promise,\n abort: abortRun,\n steer: (text: string) => {\n if (pendingApproval) {\n const resolve = pendingApproval\n pendingApproval = undefined\n resolve(/^\\s*(y|yes|是|好|ok|approve|允许|同意|确认|可以|行)/i.test(text))\n return\n }\n agent.steer({\n role: 'user',\n content: [{ type: 'text', text }],\n timestamp: Date.now(),\n } as AgentMessage)\n },\n outputPath: opts.outputPath,\n }\n}\n","/** Agent job service: schedule/fire lifecycle + cron + task store. */\n\nimport {\n createWorktree,\n worktreeDiff,\n removeWorktree,\n applyDiff,\n revertDiff,\n touchesForbiddenZone,\n touchesCapabilitySurface,\n hasRepoDrifted,\n type AgentJobRegistry,\n type AgentSessionJob,\n type Worktree,\n} from '@x-otto/runtime'\n\nimport type { AgentJobNotification } from '../hooks/agent-job-notification'\nimport type { AppliedDiffStore } from './applied-diff-store'\n\n/** Codex cloud-tasks 式应用结果(success/partial/error + 冲突/跳过路径)。 */\nexport interface ApplyOutcome {\n applied: boolean\n status: 'success' | 'partial' | 'error'\n message: string\n conflictPaths: string[]\n /** 该改动触及的治理禁区文件(空=未触)。手动 apply 仅警示;自主层须强制 L0。 */\n forbiddenZone?: string[]\n /** 该改动是否触及能力面(MCP/provider/声明式 agent)→ apply 后应热重载。 */\n capabilityTouched?: boolean\n /**\n * 主仓 HEAD 自 worktree 建立后是否已前进(review J6,提示性——真正的冲突判定仍是\n * `applyDiff` 的 `git apply --check`)。冲突/apply 失败时附带此信号,帮助用户理解成因\n * (\"因为主仓在此期间被改过\",而非 diff 本身有问题)。\n */\n repoDrifted?: boolean\n}\n\n/** 一个 detached run 句柄(spawnDetachedRun 的子集;DI 便于测试)。promise 解析值带 `text`(没改文件时作结果摘要)。 */\nexport interface JobRunHandle {\n promise: Promise<unknown>\n abort(): void\n /** 向运行中的作业注入一条 user 消息(\"进 delegate 补充信息\")。 */\n steer?(text: string): void\n outputPath: string\n}\n\nexport interface AgentJobServiceDeps {\n /** 主仓根(worktree 的源 + apply 目标)。 */\n repoRoot: string\n registry: AgentJobRegistry\n /** 完成通知注入(agent-job-notification 桥的 notify)。 */\n notify: (sessionId: string, n: AgentJobNotification) => void\n /**\n * 起一个 detached run,工具 root 在 workspaceDir(= worktree 路径,实现隔离)。\n * app 层实现 = 构造 worktree-rooted 工具 + spawnDetachedRun。\n */\n spawnRun: (input: {\n runId: string\n prompt: string\n outputPath: string\n workspaceDir: string\n /** 作业撞到\"需确认\"挂起时回调(→ 置 needs_input)。 */\n onAwaitingInput?: (reason: string) => void\n }) => JobRunHandle\n /** 作业转录文件路径(默认 tmp/job-<id>.log;注入便于测试)。 */\n buildOutputPath: (jobId: string) => string\n /** 生成作业 id。 */\n newJobId: () => string\n /** appliedDiffs 持久化存储(可选注入,未注入则纯内存)。 */\n appliedDiffStore?: AppliedDiffStore\n /**\n * worktree 清理失败上报(review J4,可选注入)。覆盖本服务直接调用的两处 removeWorktree\n * (spawnRun 同步抛出的孤儿清理 + 显式 cleanupAgentJob);AgentJobRegistry 内部的\n * removeWorktree 调用点另有独立注入(见 agent-job-registry.ts 的 onWorktreeCleanupError)。\n */\n onWorktreeCleanupError?: (op: 'worktree remove' | 'branch delete' | 'worktree prune', err: unknown) => void\n}\n\nexport interface StartAgentJobInput {\n title: string\n prompt: string\n sessionId: string\n /** 来源(缺省 user):user = 用户 /delegate;main = 主对话/模型起的(面板分组);\n * plugin = 插件经 agent.dispatch 自主发起(RFC-287 R2,apply 永远走人手闸)。 */\n origin?: 'user' | 'main' | 'plugin'\n}\n\nexport class AgentJobService {\n private readonly ownedJobIds = new Set<string>()\n private readonly handles = new Map<string, JobRunHandle>()\n private readonly appliedDiffs = new Map<string, string>()\n\n constructor(private readonly deps: AgentJobServiceDeps) {\n // 从持久化存储回填 appliedDiffs(支持进程重启后 revert)。\n if (deps.appliedDiffStore) {\n for (const { jobId, diff } of deps.appliedDiffStore.loadAll()) {\n this.appliedDiffs.set(jobId, diff)\n }\n }\n }\n\n /** 起一个 agent-session 作业(后台跑,立即返回作业记录)。 */\n start(input: StartAgentJobInput): AgentSessionJob {\n const { repoRoot, registry, spawnRun, buildOutputPath, newJobId } = this.deps\n const jobId = newJobId()\n const worktree: Worktree = createWorktree(repoRoot, jobId)\n const outputPath = buildOutputPath(jobId)\n\n let handle: JobRunHandle\n try {\n handle = spawnRun({\n runId: jobId,\n prompt: input.prompt,\n outputPath,\n workspaceDir: worktree.path,\n onAwaitingInput: () => this.deps.registry.update(jobId, { status: 'needs_input' }),\n })\n } catch (err) {\n // spawnRun 同步抛出:worktree 已建却未注册,显式清理避免孤儿。\n removeWorktree(worktree, this.deps.onWorktreeCleanupError)\n throw err\n }\n\n const job = registry.register({\n id: jobId,\n sessionId: input.sessionId,\n title: input.title,\n origin: input.origin,\n worktree,\n outputPath,\n abort: handle.abort,\n })\n this.ownedJobIds.add(jobId)\n this.handles.set(jobId, handle)\n\n handle.promise\n .then((result: unknown) =>\n this.onRunComplete(jobId, input.title, input.sessionId, worktree, outputPath, result),\n )\n .catch((e: unknown) => this.onRunFailed(jobId, input.title, input.sessionId, e))\n .finally(() => this.handles.delete(jobId))\n\n return job\n }\n\n /**\n * 向一个**运行中**的作业补充信息(进 delegate 对话)——经 Agent.steer 注入 user 消息,\n * 作业下一个工具回合采纳。返回是否送达(作业不存在/已终态/句柄无 steer → false)。\n */\n supply(jobId: string, text: string): boolean {\n const handle = this.handles.get(jobId)\n if (!handle?.steer) return false\n const cur = this.deps.registry.get(jobId)\n if (!cur || (cur.status !== 'running' && cur.status !== 'needs_input')) return false\n handle.steer(text)\n if (cur.status === 'needs_input') this.deps.registry.update(jobId, { status: 'running' })\n return true\n }\n\n /**\n * App 级收尾:取消本服务起的、仍在跑的作业(abort detached run + removeWorktree)。\n * registry.cancel 对已终态/已移除作业幂等 no-op,故无需剔除已完成 id。只动自己 owned 的,\n * 不像 globalProcessRegistry.killAll 那样越界杀兄弟 App 的作业。\n */\n dispose(): void {\n for (const id of this.ownedJobIds) {\n this.deps.registry.cancel(id)\n }\n this.ownedJobIds.clear()\n this.handles.clear()\n this.appliedDiffs.clear()\n }\n\n private onRunComplete(\n jobId: string,\n title: string,\n sessionId: string,\n worktree: Worktree,\n outputPath: string,\n result: unknown,\n ): void {\n const cur = this.deps.registry.get(jobId)\n if (!cur || cur.status === 'canceled') return\n\n let diff = ''\n try {\n diff = worktreeDiff(worktree)\n } catch (e) {\n // J2 修复:worktreeDiff 异常(git 失败/OOM/worktree 损坏)不得静默转 ready,\n // 应交由 onRunFailed 统一走 failed 通知路径。\n this.onRunFailed(jobId, title, sessionId, e)\n return\n }\n this.deps.registry.update(jobId, { status: 'ready', diff })\n\n if (diff.trim()) {\n this.deps.notify(sessionId, {\n jobId,\n title,\n status: 'ready',\n summary: `produced a diff (${diffStat(diff)})`,\n outputPath,\n worktree: { path: worktree.path, branch: worktree.branch },\n })\n return\n }\n const text = readResultText(result).trim()\n const summary = text\n ? `完成 — /jobs 按 o 看结果(约 ${text.length} 字)`\n : '完成(无文件改动、无文本输出)'\n this.deps.notify(sessionId, { jobId, title, status: 'ready', summary, outputPath })\n }\n\n private onRunFailed(jobId: string, title: string, sessionId: string, e: unknown): void {\n const cur = this.deps.registry.get(jobId)\n if (!cur || cur.status === 'canceled') return\n const error = e instanceof Error ? e.message : String(e)\n this.deps.registry.update(jobId, { status: 'failed', error })\n this.deps.notify(sessionId, { jobId, title, status: 'failed', summary: error })\n }\n\n /** 把作业产物 apply 到主仓(带冲突检测;冲突不强改)。 */\n apply(jobId: string): ApplyOutcome {\n const job = this.deps.registry.get(jobId)\n if (!job)\n return {\n applied: false,\n status: 'error',\n message: `job ${jobId} not found`,\n conflictPaths: [],\n }\n if (job.status !== 'ready') {\n return {\n applied: false,\n status: 'error',\n message: `job ${jobId} is ${job.status}, not ready`,\n conflictPaths: [],\n }\n }\n const forbiddenZone = touchesForbiddenZone(job.diff ?? '')\n const capabilityTouched = touchesCapabilitySurface(job.diff ?? '').length > 0\n const res = applyDiff(this.deps.repoRoot, job.diff ?? '')\n if (res.applied) {\n if (job.diff?.trim()) {\n this.appliedDiffs.set(jobId, job.diff)\n this.deps.appliedDiffStore?.save(jobId, job.diff)\n }\n this.deps.registry.update(jobId, { status: 'applied' })\n return {\n applied: true,\n status: 'success',\n message: 'applied cleanly',\n conflictPaths: [],\n forbiddenZone,\n capabilityTouched,\n }\n }\n // review J6: apply failed/conflicted — surface whether the main repo drifted since the\n // worktree was created (advisory only; the real conflict verdict is applyDiff's --check).\n const repoDrifted = hasRepoDrifted(this.deps.repoRoot, job.worktree.base)\n return {\n applied: false,\n status: res.conflictPaths.length ? 'partial' : 'error',\n message: res.conflictPaths.length\n ? `conflicts in ${res.conflictPaths.length} file(s); resolve or cancel${repoDrifted ? ' (main repo has moved since this job started)' : ''}`\n : 'apply failed',\n conflictPaths: res.conflictPaths,\n repoDrifted,\n }\n }\n\n /**\n * 撤销一次已 apply 的作业改动(反向 diff,check-first 不强撤)。\n * 工作树自 apply 后漂移 → 返回 partial + 冲突路径,不强撤。撤成则该 diff 不再可 revert。\n * 用 service 自存的 appliedDiffs(独立于注册表),故 applied 作业被驱逐后仍可撤。\n */\n revert(jobId: string): ApplyOutcome {\n const diff = this.appliedDiffs.get(jobId)\n if (!diff)\n return {\n applied: false,\n status: 'error',\n message: `job ${jobId} has no applied change to revert`,\n conflictPaths: [],\n }\n const res = revertDiff(this.deps.repoRoot, diff)\n if (res.applied) {\n this.appliedDiffs.delete(jobId)\n this.deps.appliedDiffStore?.delete(jobId)\n return { applied: true, status: 'success', message: 'reverted cleanly', conflictPaths: [] }\n }\n return {\n applied: false,\n status: res.conflictPaths.length ? 'partial' : 'error',\n message: res.conflictPaths.length\n ? `cannot revert: ${res.conflictPaths.length} file(s) diverged since apply`\n : 'revert failed',\n conflictPaths: res.conflictPaths,\n }\n }\n\n /** 取消作业(abort run + 清 worktree)。 */\n cancel(jobId: string): void {\n this.deps.registry.cancel(jobId)\n }\n}\n\n/** 从 detached run 解析值里安全取文字结果(connector 作业用;非字符串 → 空串)。 */\nfunction readResultText(result: unknown): string {\n const text = (result as { text?: unknown } | null | undefined)?.text\n return typeof text === 'string' ? text : ''\n}\n\n/**\n * 极简 diff 统计(+N/-M 行,外加二进制文件计数)。\n *\n * review J10:`worktreeDiff` 用 `git diff --cached --binary`,二进制文件的内容是 base85 编码的\n * `GIT binary patch` 块——这些编码行可能任意以 `+`/`-` 开头,逐行文本计数会把它们错当成文本\n * 增删行,统计虚高失真。逐段(以 `diff --git` 分隔)先判定该文件是否为 `GIT binary patch`\n * 块;是则只计入 binary 文件数,不参与逐行 +/- 统计。\n */\nfunction diffStat(diff: string): string {\n let added = 0\n let removed = 0\n let binaryFiles = 0\n\n const segments = diff.split(/^diff --git /m).filter(Boolean)\n for (const segment of segments) {\n if (segment.includes('\\nGIT binary patch')) {\n binaryFiles++\n continue\n }\n for (const line of segment.split('\\n')) {\n if (line.startsWith('+') && !line.startsWith('+++')) added++\n else if (line.startsWith('-') && !line.startsWith('---')) removed++\n }\n }\n\n const binarySuffix = binaryFiles > 0 ? `, ${binaryFiles} binary file${binaryFiles === 1 ? '' : 's'}` : ''\n return `+${added}/-${removed}${binarySuffix}`\n}\n\n/** 兜底清理(宿主退出/服务销毁时清残留 worktree)。registry reaper 也会做,此为显式入口。 */\nexport function cleanupAgentJob(worktree: Worktree): void {\n removeWorktree(worktree)\n}\n","import {\n mkdirSync,\n writeFileSync,\n readFileSync,\n unlinkSync,\n readdirSync,\n existsSync,\n} from 'node:fs'\nimport { join, extname } from 'node:path'\n\n/** appliedDiffs 持久化存储接口(同步,简单文件系统实现)。 */\nexport interface AppliedDiffStore {\n save(jobId: string, diff: string): void\n load(jobId: string): string | undefined\n delete(jobId: string): void\n loadAll(): Array<{ jobId: string; diff: string }>\n}\n\nconst ALLOWED_JOB_ID_RE = /^[a-zA-Z0-9_-]+$/\n\n/**\n * 基于文件系统的 applied diff store。\n * 每个 jobId 存储为 `<dir>/<jobId>.patch`。\n * jobId 只允许 [a-zA-Z0-9_-] 字符,其余抛错防止路径穿越。\n */\nexport function createFileAppliedDiffStore(dir: string): AppliedDiffStore {\n return {\n save(jobId, diff) {\n if (!ALLOWED_JOB_ID_RE.test(jobId)) {\n throw new Error(`Invalid jobId for applied diff store: ${jobId}`)\n }\n mkdirSync(dir, { recursive: true })\n writeFileSync(join(dir, `${jobId}.patch`), diff, 'utf-8')\n },\n\n load(jobId) {\n const path = join(dir, `${jobId}.patch`)\n if (!existsSync(path)) return undefined\n return readFileSync(path, 'utf-8')\n },\n\n delete(jobId) {\n const path = join(dir, `${jobId}.patch`)\n if (existsSync(path)) unlinkSync(path)\n },\n\n loadAll() {\n if (!existsSync(dir)) return []\n return readdirSync(dir)\n .filter(f => extname(f) === '.patch')\n .map(f => {\n const jobId = f.slice(0, -6) // remove '.patch'\n return { jobId, diff: readFileSync(join(dir, f), 'utf-8') }\n })\n },\n }\n}\n","/** Agent job wiring: connect scheduler/executor/session lifecycle. */\n\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\nimport { mkdirSync } from 'node:fs'\nimport { randomUUID } from 'node:crypto'\n\nimport type { AgentJobRegistry } from '@x-otto/runtime'\nimport type { Model, StreamFunction } from '@x-otto/ai'\nimport type { AgentTool } from '@x-otto/agent'\nimport type { HookRegistry } from '@x-otto/hooks'\nimport type { Logger } from '@x-otto/shared'\n\nimport { spawnDetachedRun } from '../agent/detached-run'\nimport { AgentJobService } from './agent-job-service'\nimport { createFileAppliedDiffStore } from './applied-diff-store'\nimport type { AgentJobNotification } from '../hooks/agent-job-notification'\n\n/**\n * fork-bomb 防护:作业 agent 不可见的工具名。detached 作业是全新 agent(depth=0),\n * 深度护栏挡不住 delegate_job 跨作业递归 → 作业内剔除 delegate_job(作业能干活、能调别的工具,\n * 但不能再起作业,递归第一层即断)。export 供回归测试钉死。\n */\nexport const JOB_EXCLUDED_TOOL_NAMES: readonly string[] = ['delegate_job']\n\n/** 从作业工具集剔除禁止在作业内出现的工具(fork-bomb 防护)。 */\nexport function excludeJobForbiddenTools(tools: AgentTool[]): AgentTool[] {\n return tools.filter((t) => !JOB_EXCLUDED_TOOL_NAMES.includes(t.name))\n}\n\n/**\n * 默认作业 id 生成(review J9):CODE-STYLE 禁止引擎核心直接调 `Math.random()`(确定性重放\n * 前提)。此前用 `Math.random().toString(36).slice(2,10)`,改用 `crypto.randomUUID()`\n * 派生——本函数是 wiring 层已有的注入豁免点(`newJobId` 可被调用方覆盖),换成 CSPRNG 只是\n * 就手把熵源换正规,无功能变化。export 供单测直接验证格式/唯一性,不必起真实 detached run。\n */\nexport function defaultNewJobId(): string {\n return `job_${randomUUID().replace(/-/g, '').slice(0, 8)}`\n}\n\nexport interface AgentJobWiringDeps {\n /** 主仓根(worktree 源 + apply 目标)。 */\n repoRoot: string\n registry: AgentJobRegistry\n /** 完成通知注入(agent-job-notification 桥的 notify)。 */\n notify: (sessionId: string, n: AgentJobNotification) => void\n /** app 提供:为给定 cwd(=worktree 路径)组装工具集 → 实现隔离。 */\n buildTools: (workspaceDir: string) => AgentTool[]\n /**\n * 主会话已注册的 MCP 工具(`mcp__*`),并入每个作业的工具集(同进程复用已认证连接)。\n * 缺省空 → 无 MCP 时作业只拿文件工具(满配里 MCP 那部分为空,无害)。\n */\n mcpTools?: () => AgentTool[]\n /** 每次起作业**当场**解析模型(而非构造时冻结一次)——用户切模型后新作业用新模型。 */\n getModel: () => Model\n /** LLM 流式函数(runtime.sessions.stream,与主引擎同一路径)。 */\n stream: StreamFunction\n logger: Logger\n hookRegistry: HookRegistry\n /** 作业转录文件目录(缺省 tmpdir/otto-jobs)。 */\n outputDir?: string\n /** 作业 id 生成(缺省 job_<rand>;注入便于测试)。 */\n newJobId?: () => string\n /** agent 作业的 system prompt(缺省空——继承默认;app 可注入聚焦 prompt 前缀)。 */\n systemPrompt?: string\n maxToolTurns?: number\n /**\n * appliedDiffs 持久化目录(review J1:重启后 revert 仍可用)。缺省不传则纯内存\n * (行为等同修复前——重启后已 apply 的作业不可 revert)。\n */\n appliedDiffDir?: string\n}\n\nexport function createAgentJobService(deps: AgentJobWiringDeps): AgentJobService {\n const outputDir = deps.outputDir ?? join(tmpdir(), 'otto-jobs')\n\n // review J4:worktree 清理失败不再静默——接到已有的 logger(缺省纯内存注入时行为不变)。\n const onWorktreeCleanupError = (op: 'worktree remove' | 'branch delete' | 'worktree prune', err: unknown): void => {\n deps.logger.warn({ op, err: err instanceof Error ? err.message : String(err) }, 'Failed to clean up agent job worktree')\n }\n deps.registry.setWorktreeCleanupErrorHandler((jobId, op, err) =>\n deps.logger.warn({ jobId, op, err: err instanceof Error ? err.message : String(err) }, 'Failed to clean up agent job worktree'),\n )\n\n return new AgentJobService({\n repoRoot: deps.repoRoot,\n registry: deps.registry,\n notify: deps.notify,\n onWorktreeCleanupError,\n appliedDiffStore: deps.appliedDiffDir\n ? createFileAppliedDiffStore(deps.appliedDiffDir)\n : undefined,\n spawnRun: ({ runId, prompt, outputPath, workspaceDir, onAwaitingInput }) =>\n spawnDetachedRun({\n runId,\n prompt,\n outputPath,\n model: deps.getModel(),\n systemPrompt: deps.systemPrompt,\n tools: [\n ...excludeJobForbiddenTools(deps.buildTools(workspaceDir)),\n ...(deps.mcpTools?.() ?? []),\n ],\n maxToolTurns: deps.maxToolTurns,\n stream: deps.stream,\n logger: deps.logger,\n hookRegistry: deps.hookRegistry,\n onAwaitingInput,\n }),\n buildOutputPath: (jobId) => {\n mkdirSync(outputDir, { recursive: true })\n return join(outputDir, `agent-job-${jobId}.log`)\n },\n newJobId: deps.newJobId ?? defaultNewJobId,\n })\n}\n","/**\n * app-agent-jobs.ts — AgentJob 服务懒构造(RFC-199 M1,从 app.ts 纯搬迁)。\n *\n * 原 app.ts 的 agentJobs getter 体逐字搬迁至此。\n * App 类保留公共 API 签名,方法体改为一行委托至本模块。\n *\n * 纯搬迁纪律:行为不变、公共 API 签名不变。\n */\nimport { join } from 'node:path'\nimport { OTTO_HOME, isShadowModeEnabled } from '@x-otto/env'\nimport { createAgentJobService } from './agent-job-wiring'\nimport { globalAgentJobRegistry } from '@x-otto/runtime'\nimport type { AgentJobService } from './agent-job-service'\nimport type { AgentJobNotification } from '../hooks/agent-job-notification'\nimport type { AgentTool } from '@x-otto/agent'\nimport type { ModelRegistry, StreamFunction } from '@x-otto/ai'\nimport type { HookRegistry } from '@x-otto/hooks'\nimport type { Logger } from '@x-otto/shared'\nimport type { SettingsManager } from '@x-otto/setting'\n\nexport interface CreateAgentJobsInput {\n workspaceDir: string\n agentJobNotify: ((sessionId: string, n: AgentJobNotification) => void) | undefined\n buildToolsForWorkspace: (workspaceDir: string) => AgentTool[]\n connectorTools: () => AgentTool[]\n settings: SettingsManager\n modelRegistry: ModelRegistry\n /** LLM 流式函数(runtime.sessions.stream,与主引擎同一路径,见 agent-job-wiring.ts)。 */\n stream: StreamFunction\n logger: Logger\n hookRegistry: HookRegistry\n}\n\nexport function createAgentJobs(input: CreateAgentJobsInput): AgentJobService {\n return createAgentJobService({\n repoRoot: input.workspaceDir,\n registry: globalAgentJobRegistry,\n notify: (sid, n) => input.agentJobNotify?.(sid, n),\n buildTools: (wd) => input.buildToolsForWorkspace(wd),\n mcpTools: () => input.connectorTools(),\n getModel: () => {\n const id = input.settings.get('model') as string | undefined\n const m = (id ? input.modelRegistry.find(id) : undefined) ?? input.modelRegistry.getAll().find(m => m.id !== '__otto_setup__')\n if (!m) throw new Error('No model available — configure a model or run /model first.')\n return m\n },\n stream: input.stream,\n logger: input.logger,\n hookRegistry: input.hookRegistry,\n // RFC-345 影子模式:applied diff 持久化目录置 undefined → agent-job-wiring 走既有\n // 纯内存回退(appliedDiffStore 未注入,save/delete 全为可选调用),零磁盘写入且不 crash。\n appliedDiffDir: isShadowModeEnabled() ? undefined : join(OTTO_HOME, 'jobs', 'applied'),\n })\n}\n","import type { AgentMessage } from '@x-otto/agent'\n\nexport function getLastAssistantText(messages: readonly AgentMessage[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i] as {\n role?: string\n content?: Array<{ type?: string; text?: string }>\n }\n if (message?.role !== 'assistant') continue\n\n const content = message.content ?? []\n const text = content\n .filter((part) => part.type === 'text' && typeof part.text === 'string')\n .map((part) => part.text)\n .join('\\n')\n .trim()\n\n if (text.length > 0) return text\n }\n return ''\n}\n\nexport function getIncompleteSubagentReason(\n messages: readonly AgentMessage[],\n): string | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i] as { role?: string; stopReason?: string }\n if (message?.role !== 'assistant') continue\n const sr = message.stopReason\n return sr === 'refusal' || sr === 'max_tokens' ? sr : null\n }\n return null\n}\n","import { getLastAssistantText, getIncompleteSubagentReason } from './assistant-utils'\n\nimport type { Logger } from '@x-otto/shared'\nimport type { RunSessionOptions } from '@x-otto/orchestrator'\nimport { SUBAGENT_GUARDRAILS } from '@x-otto/prompt'\nimport type { CodingSessionOptions } from './session-config-resolver'\n\nimport type { AgentSession } from '@x-otto/runtime'\nimport { attachAgentObserver, globalAgentObservability } from '@x-otto/runtime'\n\nfunction abortError(message: string): Error {\n const error = new Error(message)\n error.name = 'AbortError'\n return error\n}\n\nexport interface TaskRunnerDeps {\n logger: Logger\n getSession: (id: string) => AgentSession | undefined\n createSession: (options: CodingSessionOptions) => Promise<AgentSession>\n removeSession: (id: string) => Promise<boolean>\n}\n\nexport interface TaskRunner {\n /**\n * Orchestrator.runSession 注入体:创建/复用子会话 → prompt → 提取输出文本。\n * 入参用 orchestrator 的单源契约 `RunSessionOptions`。\n */\n runSession: (options: RunSessionOptions) => Promise<{\n text: string\n sessionId: string\n durationMs: number\n }>\n /** cancel → 中止任务(updateTask cancel / background cancel 同语义)。 */\n abortTask: (taskId: string) => void\n}\n\nexport function createTaskRunner(deps: TaskRunnerDeps): TaskRunner {\n const taskAbortControllers = new Map<string, AbortController>()\n\n const runSession = async (runOptions: RunSessionOptions) => {\n const startTime = Date.now()\n\n const taskAbort = new AbortController()\n if (runOptions.taskId) {\n taskAbortControllers.set(runOptions.taskId, taskAbort)\n }\n\n const parentSignal = runOptions.signal\n const onParentAbort = () => taskAbort.abort()\n if (parentSignal) {\n if (parentSignal.aborted) taskAbort.abort()\n else parentSignal.addEventListener('abort', onParentAbort, { once: true })\n }\n\n let session: AgentSession\n if (runOptions.sessionMode === 'sticky' && runOptions.sessionId) {\n const existing = deps.getSession(runOptions.sessionId)\n if (existing) {\n session = existing\n } else {\n session = await deps.createSession({\n id: runOptions.sessionId,\n agentName: runOptions.agentName,\n slot: runOptions.slot,\n modelId: runOptions.model,\n category: runOptions.category,\n delegated: true,\n promptContext:\n runOptions.promptContext ??\n (runOptions.agentName ? { taskDescription: runOptions.prompt } : undefined),\n depth: runOptions.depth,\n readOnly: runOptions.readOnly,\n })\n }\n } else {\n session = await deps.createSession({\n agentName: runOptions.agentName,\n slot: runOptions.slot,\n modelId: runOptions.model,\n category: runOptions.category,\n delegated: true,\n promptContext:\n runOptions.promptContext ??\n (runOptions.agentName ? { taskDescription: runOptions.prompt } : undefined),\n depth: runOptions.depth,\n transient: true,\n readOnly: runOptions.readOnly,\n })\n }\n\n const obsId = session.id\n const detachObs = attachAgentObserver(session.coreAgent, {\n id: obsId,\n agentName: runOptions.agentName ?? 'agent',\n title: runOptions.agentName ?? runOptions.prompt.replace(/\\s+/g, ' ').slice(0, 48),\n prompt: runOptions.prompt,\n model: runOptions.model ?? session.model.id,\n // 模型选型溯源(哪层选中)——供 inspector/SubagentsPane 显示「为何用这个模型」。\n // rejected 不进观测层(无 UI 消费方);其溯源由 resolver 的 logger.debug 承载。\n modelDecidedBy: session.modelResolution?.decidedBy,\n origin: 'main',\n depth: runOptions.depth ?? 0,\n abort: () => session.abort(),\n messages: () => session.session.messages(),\n })\n\n try {\n if (taskAbort.signal.aborted) {\n throw abortError('Task cancelled before the session started')\n }\n\n const effectivePrompt = runOptions.agentName\n ? `<agent-guardrails>\\n${SUBAGENT_GUARDRAILS}\\n</agent-guardrails>\\n\\n<task>\\n${runOptions.prompt}\\n</task>`\n : runOptions.prompt\n const promptPromise = session.prompt(effectivePrompt)\n promptPromise.catch(() => {})\n await Promise.race([\n promptPromise,\n new Promise<never>((_, reject) => {\n taskAbort.signal.addEventListener('abort', () => {\n session.abort()\n reject(abortError('Task cancelled'))\n })\n }),\n ])\n\n const messages = session.session.messages()\n let text = getLastAssistantText(messages)\n const incomplete = getIncompleteSubagentReason(messages)\n if (incomplete) {\n text = `${text}\\n\\n[⚠ subagent did not complete cleanly (stop reason: ${incomplete}) — the output above may be partial or unreliable; verify before relying on it]`\n }\n\n return {\n text,\n sessionId: session.id,\n durationMs: Date.now() - startTime,\n }\n } finally {\n globalAgentObservability.setMessages(obsId, session.session.messages())\n detachObs()\n globalAgentObservability.markEnded(obsId)\n parentSignal?.removeEventListener('abort', onParentAbort)\n if (runOptions.taskId) {\n taskAbortControllers.delete(runOptions.taskId)\n }\n if (runOptions.sessionMode === 'ephemeral') {\n await deps.removeSession(session.id).catch((error) => {\n deps.logger.warn('Failed to remove ephemeral session %s: %s', session.id, error)\n })\n }\n }\n }\n\n return {\n runSession,\n abortTask: (taskId) => {\n taskAbortControllers.get(taskId)?.abort()\n },\n }\n}\n","/** notification setup: dispatcher + channel wiring at App construction. */\nimport {\n NotificationDispatcher,\n TerminalChannel,\n TimestampPresenceGate,\n PresenceTracker,\n CallbackChannel,\n CommandChannel,\n type NotificationConfig,\n} from '@x-otto/notification'\n\nimport { isProjectTrusted } from '@x-otto/claude-compat'\n\nexport interface NotificationSetupDeps {\n /** () => settings.get('notification')(dispatcher + gate + 各 channel 共用)。 */\n getConfig: () => NotificationConfig | undefined\n getCwd: () => string\n isFollowUpPending: () => boolean\n /** hook 通道:把通知转发为 `notification` hook(通常 hookRegistry.emit)。 */\n emitHookNotification: (payload: {\n sessionId: string\n message: string\n title?: string\n notification_type: string\n }) => void\n}\n\nexport interface NotificationSetup {\n dispatcher: NotificationDispatcher\n presenceTracker: PresenceTracker\n}\n\nexport function buildNotificationDispatcher(deps: NotificationSetupDeps): NotificationSetup {\n const presenceTracker = new PresenceTracker({\n focusTracking: process.env['OTTO_TUI_FOCUS'] === '1',\n })\n const dispatcher = new NotificationDispatcher({\n getConfig: deps.getConfig,\n getCwd: deps.getCwd,\n onInteraction: () => presenceTracker.recordInteraction(),\n isFollowUpPending: deps.isFollowUpPending,\n gate: new TimestampPresenceGate({\n getCondition: () => deps.getConfig()?.condition,\n getIdleThresholdMs: () => deps.getConfig()?.idle_threshold_ms,\n state: presenceTracker,\n }),\n })\n dispatcher.registerChannel(new TerminalChannel({ getConfig: deps.getConfig }))\n dispatcher.registerChannel(\n new CallbackChannel('hook', () => true, (p) =>\n deps.emitHookNotification({\n sessionId: p.sessionId,\n message: p.message,\n title: p.title,\n notification_type: p.type,\n }),\n ),\n )\n dispatcher.registerChannel(\n new CommandChannel({\n getConfig: deps.getConfig,\n isTrusted: () => isProjectTrusted(deps.getCwd()),\n }),\n )\n return { dispatcher, presenceTracker }\n}\n","/** Model catalog: refresh providers, build model registry from catalog. */\nimport {\n resolveModelsRequest,\n fetchModels,\n mergeFetchedModels,\n ModelCatalogStore,\n type AuthStore,\n type ModelRegistry,\n} from '@x-otto/ai'\n\n/**\n * 按 glob 模式过滤模型 ID 列表(大小写不敏感,`*` 匹配任意字符序列)。\n * 例如 `[\"*Claude*\"]` 排除所有含 \"Claude\" 的模型 ID。\n * 导出供测试覆盖。\n */\nexport function filterExcludedModelIds(ids: string[], excludePatterns?: string[]): string[] {\n if (!excludePatterns || excludePatterns.length === 0) return ids\n const regexes = excludePatterns.map(\n (p) =>\n new RegExp(\n '^' + p.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*') + '$',\n 'i',\n ),\n )\n return ids.filter((id) => !regexes.some((re) => re.test(id)))\n}\n\n/** refresh() 每个 provider 的结果。`skipped`/`failed` 绝不抛——回退静态/缓存。 */\nexport interface ModelRefreshResult {\n provider: string\n status: 'ok' | 'skipped' | 'failed'\n added?: number\n kept?: number\n total?: number\n availableIds?: string[]\n reason?: string\n}\n\nexport class ModelCatalogManager {\n /** 实时拉取的模型列表落盘缓存(每次启动 hydrate + 刷新后写回)。 */\n private readonly catalog: ModelCatalogStore\n /** 每个 mergeProvider 我们派生注册过的 model id(refresh 时剪掉已下线的,不碰静态种子)。 */\n private readonly derivedModelIds = new Map<string, Set<string>>()\n\n constructor(\n private readonly modelRegistry: ModelRegistry,\n private readonly authStore: AuthStore,\n /** 可注入(测试用);默认真实落盘 store。 */\n catalog?: ModelCatalogStore,\n ) {\n this.catalog = catalog ?? new ModelCatalogStore()\n }\n\n /**\n * 启动时把上次落盘的模型缓存并回 registry(瞬时、离线可用)。返回缓存确认「账号可用」的 id 集合。\n * 内置 JSON 仅作派生模板(种子),真实列表来自此缓存 / 随后的后台 refresh。\n */\n async hydrate(): Promise<Set<string>> {\n const available = new Set<string>()\n const cached = await this.catalog.load()\n if (!cached) return available\n for (const entry of Object.values(cached.providers)) {\n const merged = mergeFetchedModels(this.modelRegistry, entry.mergeProvider, entry.modelIds, {\n templateApi: entry.modelApi,\n })\n for (const id of merged.availableIds) available.add(id)\n const set = this.derivedModelIds.get(entry.mergeProvider) ?? new Set<string>()\n for (const id of merged.derivedIds) set.add(id)\n this.derivedModelIds.set(entry.mergeProvider, set)\n }\n return available\n }\n\n /**\n * `refresh()` 并发去重(RFC-136 D1):飞书 `/model refresh` 是多用户群聊场景下可被\n * 并发触发的入口,同一时刻只允许一次真实刷新在途——后到的调用直接复用同一个 Promise,\n * 不重复发起网络请求,也避免 `derivedModelIds`/`ModelRegistry` 的并发写入竞态。\n */\n private refreshInFlight: Promise<ModelRefreshResult[]> | null = null\n\n /**\n * 从各**已认证 provider** 的 models 端点实时拉取可用模型并并入 registry + 落盘缓存。\n * 按鉴权 provider(openai-codex/github-copilot/…)解析端点+头+并入目标——凭据 id ≠ 模型 provider。\n * 未认证/无端点→skipped;拉取失败→failed(绝不抛,回退静态/缓存);成功→并入、缓存、回报 availableIds。\n *\n * 并发调用天然去重(见 `refreshInFlight`)——单发调用行为/返回值/错误语义不变。\n */\n async refresh(): Promise<ModelRefreshResult[]> {\n if (this.refreshInFlight) return this.refreshInFlight\n this.refreshInFlight = this.doRefresh().finally(() => {\n this.refreshInFlight = null\n })\n return this.refreshInFlight\n }\n\n private async doRefresh(): Promise<ModelRefreshResult[]> {\n const reg = this.modelRegistry\n const auth = this.authStore\n const known = auth.listProviders()\n const sources = await Promise.all(known.map((p) => auth.getAuthSource(p).catch(() => null)))\n const results: ModelRefreshResult[] = []\n for (let i = 0; i < known.length; i++) {\n const provider = known[i]!\n if (!sources[i]) {\n results.push({ provider, status: 'skipped', reason: 'not authenticated' })\n continue\n }\n const resolved = await auth.resolveAuth(provider).catch(() => null)\n if (!resolved?.token) {\n // 回归修复(真实用户反馈:/model refresh 报 \"credential expired\" 但 token 实际\n // 未过期):`resolveAuth` 在 cache 里有历史 OAuth 缓存条目、但对应插件的 OAuth\n // provider 尚未装载时(如插件已 symlink 但高危能力未授权,见 RFC-125/RFC-124 C2\n // 的 pendingCapabilities fail-closed 门)同样返回 null——`AuthStore.\n // resolveOAuthAccessKey` 内部 `if (!oauthProvider) return null` 这一分支与\"token\n // 刷新真的失败\"是完全不同的语义,此前统一报\"credential expired or refresh\n // failed\"会误导用户去重新登录,而真正需要做的是去 /extension 信任该插件授予\n // provider 能力。仅在 authSource==='oauth' 时用 `auth.oauth.has(provider)` 细分\n // 原因(api_key/env 模式天然不在 oauth registry 里,若也套用这条判断会把\"api_key\n // 失效\"误判成\"能力未授权\"——这两种场景语义不同,不能共用同一个判定条件)。\n const reason =\n sources[i] === 'oauth' && !auth.oauth.has(provider)\n ? 'provider capability not granted — trust the plugin in /extension'\n : 'credential expired or refresh failed — re-login'\n results.push({ provider, status: 'skipped', reason })\n continue\n }\n const req = resolveModelsRequest(provider, resolved.token, {\n mode: resolved.mode,\n beta: resolved.beta,\n })\n if (!req) {\n // RFC-130 D7 catalog-only:无 live /models 端点,但插件 manifest 已注册静态模型\n //(如 cursor/default)——已认证即视为账号可用,供 TUI /model 选择器启用。\n const catalogModels = reg.getByProvider(provider)\n if (catalogModels.length > 0) {\n const availableIds = catalogModels.map((m) => m.id)\n await this.catalog\n .put(provider, {\n fetchedAt: Date.now(),\n mergeProvider: provider,\n modelIds: availableIds,\n })\n .catch(() => {})\n results.push({\n provider,\n status: 'ok',\n added: 0,\n kept: availableIds.length,\n total: availableIds.length,\n availableIds,\n })\n } else {\n results.push({ provider, status: 'skipped', reason: 'no live models endpoint' })\n }\n continue\n }\n try {\n const ids = await fetchModels(req)\n const filteredIds = filterExcludedModelIds(ids, req.modelIdExclude)\n const merged = mergeFetchedModels(reg, req.mergeProvider, filteredIds, {\n templateApi: req.modelApi,\n })\n const nowAvail = new Set(merged.availableIds)\n const prevDerived = this.derivedModelIds.get(req.mergeProvider) ?? new Set<string>()\n for (const id of prevDerived) if (!nowAvail.has(id)) reg.unregister(id)\n this.derivedModelIds.set(\n req.mergeProvider,\n new Set<string>([\n ...[...prevDerived].filter((id) => nowAvail.has(id)),\n ...merged.derivedIds,\n ]),\n )\n await this.catalog\n .put(provider, {\n fetchedAt: Date.now(),\n mergeProvider: req.mergeProvider,\n modelApi: req.modelApi,\n modelIds: filteredIds,\n })\n .catch(() => {})\n results.push({\n provider,\n status: 'ok',\n added: merged.added,\n kept: merged.kept,\n total: merged.total,\n availableIds: merged.availableIds,\n })\n } catch (err) {\n results.push({\n provider,\n status: 'failed',\n reason: err instanceof Error ? err.message : String(err),\n })\n }\n }\n return results\n }\n}\n","/** App 暴露的 model 操作方法: refreshModels, listModels, currentModel. */\nimport type { ModelRegistry, AuthStore, ProviderRegistry, Model, Api, ModelSpec } from '@x-otto/ai'\nimport { createModelUsabilityGate } from '@x-otto/ai'\nimport type { HookRegistry, HookSpec } from '@x-otto/hooks'\nimport type { SessionPool as CodingSessionPool } from '@x-otto/runtime'\nimport type { ModelCatalogManager, ModelRefreshResult } from './model-catalog-manager'\n\nexport interface ModelCatalogEntry {\n id: string\n provider: string\n strengths: string[]\n available: boolean\n usable: boolean\n unusableReason?: string\n}\n\nexport interface ModelMethodsHost {\n readonly modelRegistry: ModelRegistry\n readonly authStore: AuthStore\n readonly providerRegistry: ProviderRegistry\n readonly hookRegistry: HookRegistry\n readonly modelCatalogManager: ModelCatalogManager\n readonly codingSessionPool: CodingSessionPool\n}\n\n/**\n * RFC-148 M2:`ServiceHostPort` 收窄——service 侧不再拿到 `modelRegistry`/\n * `providerRegistry`/`hookRegistry` 整个类实例,改用以下窄方法。纯转发,\n * 不改变底层 registry 行为。\n */\nexport function getAllModels(host: ModelMethodsHost): Model[] {\n return host.modelRegistry.getAll()\n}\n\nexport function resolveModel(host: ModelMethodsHost, spec: ModelSpec): Model {\n return host.modelRegistry.resolve(spec)\n}\n\nexport function listProviders(host: ModelMethodsHost): Api[] {\n return host.providerRegistry.list()\n}\n\nexport function registerHook(host: ModelMethodsHost, spec: HookSpec): void {\n host.hookRegistry.register(spec)\n}\n\nexport function unregisterHook(host: ModelMethodsHost, name: string): boolean {\n return host.hookRegistry.unregister(name)\n}\n\n/** 启动时把落盘模型缓存并回 registry。返回缓存确认「账号可用」的 id 集合。委托 ModelCatalogManager。 */\nexport async function hydrateModelCache(host: ModelMethodsHost): Promise<Set<string>> {\n return host.modelCatalogManager.hydrate()\n}\n\n/** 会话级「账号可用模型」只读访问(cli --continue 据此保留恢复会话的集合)。 */\nexport function getSessionAvailableModels(host: ModelMethodsHost, sessionId: string): string[] {\n return host.codingSessionPool.getSessionAvailableModels(sessionId)\n}\n\n/** 写会话级「账号可用模型」(随快照持久化,--continue 回灌)。 */\nexport function setSessionAvailableModels(\n host: ModelMethodsHost,\n sessionId: string,\n modelIds: readonly string[],\n): void {\n host.codingSessionPool.setSessionAvailableModels(sessionId, modelIds)\n}\n\n/**\n * 从各**已认证 provider** 的 models 端点实时拉取可用模型并并入 registry + 落盘缓存。\n * 按鉴权 provider(openai-codex/github-copilot/…)解析端点+头+并入目标——凭据 id ≠ 模型 provider。\n * 未认证/无端点→skipped;拉取失败→failed(绝不抛,回退静态/缓存);成功→并入、缓存、回报 availableIds。\n */\nexport async function refreshModels(host: ModelMethodsHost): Promise<ModelRefreshResult[]> {\n return host.modelCatalogManager.refresh()\n}\n\n/** list_models 工具的目录——registry 全量 + 会话级可用集合 + usability gate 标记。 */\nexport async function buildModelCatalog(host: ModelMethodsHost, sessionId?: string): Promise<ModelCatalogEntry[]> {\n const avail = new Set(\n sessionId ? host.codingSessionPool.getSessionAvailableModels(sessionId) : [],\n )\n const gate = createModelUsabilityGate(host.authStore)\n return Promise.all(\n host.modelRegistry.getAll().filter(m => m.id !== '__otto_setup__').map(async (m) => {\n const u = await gate(m)\n return {\n id: m.id,\n provider: m.provider,\n strengths: m.strengths ?? [],\n available: avail.has(m.id),\n usable: u.usable,\n ...(u.reason ? { unusableReason: u.reason } : {}),\n }\n }),\n )\n}\n","/** App 暴露的通知操作方法。 */\nimport type { NotificationDispatcher, PresenceTracker, NotificationChannel, NotificationKind } from '@x-otto/notification'\nimport { CallbackChannel } from '@x-otto/notification'\n\nexport interface NotificationMethodsHost {\n readonly notificationDispatcher: NotificationDispatcher\n readonly presenceTracker: PresenceTracker\n}\n\n/** 注册一个通知交付通道(终端/带外/应用内)。前端接线入口。 */\nexport function registerNotificationChannel(host: NotificationMethodsHost, channel: NotificationChannel): void {\n host.notificationDispatcher.registerChannel(channel)\n}\n\n/**\n * 注册应用内通知通道(TUI NotificationCenter)。CLI 在 startTui 后接线\n * push=handle.pushNotification。**透传原始 kind**——type→level 视觉映射(UI 策略)\n * 已下沉宿主呈现层(cli `notificationLevel`,RFC-077 ARCH-077-12),App 不再决定严重度呈现。\n */\nexport function registerInAppNotificationChannel(\n host: NotificationMethodsHost,\n push: (title: string, message: string, kind: NotificationKind) => void,\n): void {\n host.notificationDispatcher.registerChannel(\n new CallbackChannel('inapp', () => true, (p) => push(p.title ?? 'otto', p.message, p.type)),\n )\n}\n\n/** TUI 可选在每次按键调用,喂更细的在场信号(默认已由 prompt.start 粗粒度喂入)。 */\nexport function notifyUserInteraction(host: NotificationMethodsHost): void {\n host.presenceTracker.recordInteraction()\n}\n\n/** DEC1004 焦点事件(opt-in OTTO_TUI_FOCUS=1)。TUI 解析 \\x1b[I/\\x1b[O 后调用。 */\nexport function setTerminalFocus(host: NotificationMethodsHost, focused: boolean): void {\n host.presenceTracker.setFocus(focused)\n}\n","/** App 暴露的权限操作方法。 */\nimport type { PermissionPolicyRegistry, PermissionMode } from '@x-otto/hooks'\nimport type { SettingsManager } from '@x-otto/setting'\nimport type { SessionPool as CodingSessionPool } from '@x-otto/runtime'\nimport type { Logger } from '@x-otto/shared'\n\nexport interface PermissionMethodsHost {\n readonly permissionRegistry: PermissionPolicyRegistry\n readonly settings: SettingsManager\n readonly codingSessionPool: CodingSessionPool\n readonly logger: Logger\n}\n\n/**\n * 「始终允许」某工具(R8-B):把该工具的 ask 升级为 allow(deny 不受影响),幂等。\n *\n * 传 sessionId(弹窗运行时授权的正路)→ **会话级**:只对该会话生效、随会话快照持久化\n * (--continue 回灌),不泄漏到项目其它会话/不永久污染 config。不传 → 旧的**项目级**\n * config.json 行为(手动常驻放行基线,向后兼容)。\n */\nexport async function allowToolAlways(\n host: PermissionMethodsHost,\n toolName: string,\n sessionId?: string,\n): Promise<void> {\n if (!toolName) {\n return\n }\n if (sessionId) {\n host.permissionRegistry.addSessionAlwaysAllow(sessionId, toolName)\n host.codingSessionPool.setSessionPermissionAllow(\n sessionId,\n host.permissionRegistry.getSessionAlwaysAllow(sessionId),\n )\n host.logger.info({ tool: toolName, sessionId }, 'Tool added to session always-allow list')\n return\n }\n const current = host.settings.get('permission_always_allow') ?? []\n if (current.includes(toolName)) {\n return\n }\n host.permissionRegistry.addAlwaysAllow(toolName)\n await host.settings.persist({ permission_always_allow: [...current, toolName] })\n host.logger.info({ tool: toolName }, 'Tool added to project always-allow list')\n}\n\n/** 当前权限 mode。缺省 'auto'。 */\nexport function getPermissionMode(host: PermissionMethodsHost): PermissionMode {\n return (host.settings.get('permission_mode') ?? 'auto') as PermissionMode\n}\n\n/**\n * 设置用户面权限 mode(**机制**:persist 落盘 + reload → 'change' → permissionSync 重同步 mode 策略,\n * 即时生效 + 下次启动继承)。**循环顺序(UI 策略)已下沉宿主呈现层**(cli `nextPermissionMode`,\n * RFC-077 ARCH-077-12)——App 不再决定 Confirm→Auto→Bypass 的循环次序,只负责持久化机制。\n */\nexport async function setPermissionMode(host: PermissionMethodsHost, next: PermissionMode): Promise<void> {\n const cur = getPermissionMode(host)\n await host.settings.update({ permission_mode: next })\n void host.settings.persist({ permission_mode: next }).catch((e: unknown) =>\n host.logger.warn(\n { err: e instanceof Error ? e.message : String(e), mode: next },\n 'Failed to persist permission mode',\n ),\n )\n host.logger.info({ from: cur, to: next }, 'Permission mode set')\n}\n\n/** 某会话的「始终允许」工具名快照(弹窗运行时授权,不含项目级基线)。 */\nexport function getSessionAlwaysAllow(host: PermissionMethodsHost, sessionId: string): string[] {\n return host.permissionRegistry.getSessionAlwaysAllow(sessionId)\n}\n\n/** 移除一条项目级「始终允许」(/permissions remove):撤 registry + 抹持久化。返回是否命中。 */\nexport async function disallowToolAlways(host: PermissionMethodsHost, toolName: string): Promise<boolean> {\n if (!toolName) return false\n const current = host.settings.get('permission_always_allow') ?? []\n if (!current.includes(toolName)) return false\n const next = current.filter((t) => t !== toolName)\n host.permissionRegistry.setAlwaysAllow(next)\n await host.settings.persist({ permission_always_allow: next })\n host.logger.info({ tool: toolName }, 'Tool removed from project always-allow list')\n return true\n}\n\n/** 清空某会话的「始终允许」(/permissions clear):撤销 registry + 抹掉持久化字段。 */\nexport function clearSessionAlwaysAllow(host: PermissionMethodsHost, sessionId: string): void {\n if (!sessionId) return\n host.permissionRegistry.clearSessionAlwaysAllow(sessionId)\n host.codingSessionPool.setSessionPermissionAllow(sessionId, [])\n host.logger.info({ sessionId }, 'Session always-allow list cleared')\n}\n","/** App 暴露的 session 操作方法。 */\nimport type { AgentRuntime, AgentSession, SessionPool as CodingSessionPool } from '@x-otto/runtime'\nimport type { AgentSessionInfo } from '@x-otto/runtime'\nimport type { SessionConfigResolver, CodingSessionOptions } from '../session/session-config-resolver'\nimport type { SessionToolsSyncCoordinator } from '../session/session-tools-sync'\nimport type { NotificationDispatcher } from '@x-otto/notification'\nimport type { AppStorage } from '../types'\nimport { workspaceKey as toWorkspaceKey, type WorkspaceRef } from '@x-otto/workspace'\nimport type { Logger } from '@x-otto/shared'\n\nexport interface SessionMethodsHost {\n readonly codingSessionPool: CodingSessionPool\n readonly sessionConfigResolver: SessionConfigResolver\n /** App 持有的 runtime 门面——createSession 经此转发(同原 SessionFacade 依赖的\n * `agentRuntime.createSession` 闭包,此处直接读字段而非重新包一层 thunk)。 */\n readonly agentRuntime: AgentRuntime\n readonly notificationDispatcher: NotificationDispatcher\n readonly storage: AppStorage\n /** workspace 身份引用(App 的 public 可变字段,随 start() 解析后就位)。 */\n workspaceRef?: WorkspaceRef\n readonly logger: Logger\n readonly ensureNotDisposed: () => void\n readonly ensureSettingsLoaded: () => Promise<void>\n /** RFC-150 D2:活 session 工具重同步协调器——createSession 后记录上下文,removeSession/\n * archiveSession 后遗忘,供 App 在 MCP 工具变化时驱动 resync()。可选(测试装配可不传)。 */\n readonly toolsSyncCoordinator?: SessionToolsSyncCoordinator\n}\n\nexport async function createSession(\n host: SessionMethodsHost,\n options: CodingSessionOptions = {},\n): Promise<AgentSession> {\n host.ensureNotDisposed()\n await host.ensureSettingsLoaded()\n\n const config = await host.sessionConfigResolver.resolve(options)\n const agentSession = await host.agentRuntime.createSession(config)\n\n host.notificationDispatcher.attach(agentSession)\n\n // RFC-150 D2:记录重同步上下文(promptPreset 推导规则与 SessionConfigResolver.resolve()\n // 内部一致——纯函数,外部重算不重复实现逻辑,只是复用同一条推导规则)。\n // `hasExplicitTools` 判据:调用方在 options.tools 显式传了固定工具集时不记录。\n host.toolsSyncCoordinator?.record(agentSession.id, {\n agentName: options.agentName,\n promptPreset: options.promptPreset ?? (options.agentName ? 'subagent' : 'main'),\n readOnly: options.readOnly,\n hasExplicitTools: options.tools !== undefined,\n })\n\n host.logger.info({ sessionId: agentSession.id }, 'Session created')\n return agentSession\n}\n\nexport function getSession(host: SessionMethodsHost, id: string): AgentSession | undefined {\n return host.codingSessionPool.getSession(id)\n}\n\nexport async function shareSession(\n host: SessionMethodsHost,\n sessionId: string,\n opts: { visibility: 'private' | 'shared' },\n): Promise<void> {\n if (!host.storage.storageHost) {\n throw new Error('shareSession requires a configured storage.host')\n }\n await host.storage.storageHost.host.updateMetadata(\n 'sessions',\n host.workspaceRef?.key,\n sessionId,\n { visibility: opts.visibility },\n undefined,\n )\n host.logger.info({ sessionId, visibility: opts.visibility }, 'Session visibility changed')\n}\n\nexport function getActiveSession(host: SessionMethodsHost): AgentSession | undefined {\n return host.codingSessionPool.active\n}\n\nexport function listSessions(host: SessionMethodsHost, filter?: { workspaceId?: string }): AgentSessionInfo[] {\n return host.codingSessionPool.listSessions(filter)\n}\n\n/**\n * listSessions() 的超集——额外并入**持久化后端已知但尚未加载进内存**的会话(本 workspace\n * 分区下、未被 restoreAll/getSession 触碰过的历史会话;本地=磁盘冷会话,远端=历史会话)。\n *\n * 根因:SessionPool.listSessions() 只遍历内存 Map,持久层从不参与——多开终端/换目录场景下,\n * 本 workspace 的历史会话哪怕真实存在于持久化后端,只要没被 restore 进内存,`/resume` 的\n * 选择列表就永远看不到它们(用户也不知道 id,无法手动 resume)。\n *\n * RFC-146:泛化为消费 `SessionPool.listAllPersistedSessions()`——本地/远端统一契约,\n * 不再由 App 直接摸 `SqliteSessionRepository` 拼 SQL 查询(那部分逻辑下沉到\n * `session-pool-factory.ts` 构造期注入的 `listColdSessionsOverride` 闭包)。\n * `workspaceId → workspaceKey` 的身份转换仍留在本层(不下沉,SessionPool 不依赖\n * `@x-otto/workspace`)。冷会话(本地磁盘态/远端历史态)缺完整 messages(),标题回落到\n * 持久化 title(已是最高优先级值)→ id 前 8 位,messageCount/status 留空/占位(真实值需\n * restore 后才知道,列表展示不强依赖)。\n *\n * `filter.workspaceId`:按 `filter.workspaceId` 换算目标 workspaceKey 查询分区,\n * filter 缺省时回退当前 workspaceRef(与内存态 listSessions 缺省行为一致)。\n */\nexport async function listAllSessions(\n host: SessionMethodsHost,\n filter?: { workspaceId?: string },\n): Promise<AgentSessionInfo[]> {\n const inMemory = listSessions(host, filter)\n\n // 非法 workspace id(如 filter 传入不合法字符集)查不到任何分区,返回空——\n // fail-closed 而非 fail-open(不应误把非法输入退化为\"当前 workspace\"查询)。\n // toWorkspaceKey 校验失败会同步抛错,故用 try/catch(而非 promise .catch)兜底。\n let workspaceKey: string | undefined\n try {\n workspaceKey =\n filter?.workspaceId !== undefined\n ? toWorkspaceKey(filter.workspaceId)\n : host.workspaceRef?.key\n } catch {\n workspaceKey = undefined\n }\n if (!workspaceKey) return inMemory\n\n const persisted = await host.codingSessionPool\n .listAllPersistedSessions({ workspaceKey })\n .catch(() => [])\n const targetWorkspaceId = filter?.workspaceId ?? host.workspaceRef?.id\n const knownIds = new Set(inMemory.map((s) => s.id))\n const coldSessions: AgentSessionInfo[] = persisted\n .filter((s) => !knownIds.has(s.id))\n .map((s) => ({ ...s, workspaceId: targetWorkspaceId }))\n\n const merged = [...inMemory, ...coldSessions]\n merged.sort((a, b) => {\n const aTime = a.updatedAt?.getTime() ?? a.createdAt.getTime()\n const bTime = b.updatedAt?.getTime() ?? b.createdAt.getTime()\n return bTime - aTime\n })\n return merged\n}\n\nexport async function removeSession(host: SessionMethodsHost, id: string): Promise<boolean> {\n host.notificationDispatcher.detach(id)\n const removed = await host.codingSessionPool.removeSession(id)\n if (removed) {\n // RFC-150 D2:session 真正销毁(非 archive——archive 只标记不删除,session 仍在\n // pool 内,仍应参与工具重同步)时遗忘协调器记录,避免持有失效 id。\n host.toolsSyncCoordinator?.forget(id)\n host.logger.info({ sessionId: id }, 'Session removed')\n }\n return removed\n}\n\nexport async function archiveSession(host: SessionMethodsHost, id: string): Promise<boolean> {\n const ok = await host.codingSessionPool.archiveSession(id)\n if (ok) {\n host.logger.info({ sessionId: id }, 'Session archived')\n }\n return ok\n}\n\nexport async function forkSession(\n host: SessionMethodsHost,\n sourceId: string,\n newId?: string,\n): Promise<AgentSession | undefined> {\n const forked = await host.codingSessionPool.forkSession(sourceId, newId)\n // B7:fork 在 pool 层产新会话,绕过 createSession → 对称 attach 通知派发器(用户 /fork 的会话\n // 同主会话一样需要完成/出错/需批准通知)。ephemeral fork_call 走 orchestrator 不经此,不受影响。attach 幂等。\n if (forked) host.notificationDispatcher.attach(forked)\n // RFC-150 D2:fork 会话不记录进工具重同步协调器(源会话的 promptPreset/readOnly 上下文\n // 未随 fork 传递,无法安全重建)——降级为\"该会话工具集固定于 fork 时刻\",不随后续 MCP\n // 工具变化自动更新,用户可 /reload 或重新整个会话手动刷新。这是可接受的窄化(fork 场景\n // 边缘,聚焦核心链路),非缺陷。\n return forked\n}\n","/** Session tools sync: merge plugin/hook/permission tools into session tool set. */\nimport type { AgentSession } from '@x-otto/runtime'\nimport type { AgentTool } from '@x-otto/agent'\nimport type { Logger } from '@x-otto/shared'\nimport type { SessionConfigResolver } from './session-config-resolver'\n\n/** 单个 session 的重同步上下文——`resolveToolsForResync()` 的最小必要输入。 */\ninterface ResyncContext {\n agentName: string | undefined\n promptPreset: 'main' | 'subagent' | undefined\n readOnly: boolean | undefined\n}\n\nexport interface SessionToolsSyncCoordinatorDeps {\n sessionConfigResolver: SessionConfigResolver\n /** 按 id 取活 session 实例(`App.getSession`/`SessionFacade.getSession`)。 */\n getSession: (id: string) => AgentSession | undefined\n /** 列出当前全部活 session id(`SessionPool.listSessions()` 的投影已足够,只取 id)。 */\n listSessionIds: () => string[]\n logger: Logger\n}\n\nexport class SessionToolsSyncCoordinator {\n private readonly contexts = new Map<string, ResyncContext>()\n\n constructor(private readonly deps: SessionToolsSyncCoordinatorDeps) {}\n\n /**\n * 会话创建后调用——记录重同步所需的最小上下文。\n * `hasExplicitTools=true`(调用方在 `CodingSessionOptions.tools` 显式传了固定工具集)时\n * 不记录,该会话永不参与后续的全局工具重同步(规则 2)。\n */\n record(\n sessionId: string,\n ctx: ResyncContext & { hasExplicitTools: boolean },\n ): void {\n if (ctx.hasExplicitTools) return\n this.contexts.set(sessionId, {\n agentName: ctx.agentName,\n promptPreset: ctx.promptPreset,\n readOnly: ctx.readOnly,\n })\n }\n\n /** session 销毁时调用,避免协调器持有失效 id。 */\n forget(sessionId: string): void {\n this.contexts.delete(sessionId)\n }\n\n /**\n * 对单个已记录 session 重算工具集(RFC-130 M131-2:`/model` 切换后 session.model 已变但\n * tools 仍是创建时快照——须按新 model.supportsTools 刷新,否则 catalog-only 模型仍带 tools\n * 进 provider 抛 CURSOR_TOOLS_NOT_SUPPORTED)。\n */\n resyncOne(sessionId: string): boolean {\n const ctx = this.contexts.get(sessionId)\n if (!ctx) return false\n return this.resyncSession(sessionId, ctx)\n }\n\n /**\n * 对全部已记录且仍存活的 session 重新计算工具集并整体替换 `session.tools` 引用。\n * fail-soft:单个 session 重算失败不影响其余 session(记录 warn,不抛出——本身是\n * 后台被动触发的同步动作,不应因一个 session 的边缘错误打断整体)。\n *\n * 返回实际完成重同步的 session 数(供调用方 toast/日志展示)。\n */\n resync(): number {\n const liveIds = new Set(this.deps.listSessionIds())\n // 清理已失效 id(session 通过其它路径销毁但未显式 forget 的兜底,如直接操作 pool)。\n for (const id of this.contexts.keys()) {\n if (!liveIds.has(id)) this.contexts.delete(id)\n }\n\n let synced = 0\n for (const [id, ctx] of this.contexts) {\n if (this.resyncSession(id, ctx)) synced++\n }\n return synced\n }\n\n private resyncSession(id: string, ctx: ResyncContext): boolean {\n const session = this.deps.getSession(id)\n if (!session) return false\n try {\n let tools: AgentTool[] = this.deps.sessionConfigResolver.resolveToolsForResync(\n ctx.agentName,\n ctx.promptPreset,\n ctx.readOnly,\n )\n // 与 SessionConfigResolver.resolve() 的既有规则对齐(RFC-130 M131-2):\n // 声明 supportsTools===false 的模型(如 Cursor agent API)恒零工具。\n if (session.model.supportsTools === false) {\n tools = []\n }\n // 整体替换引用,不 mutate(RFC-150 规则 2)——进行中 prompt 持旧引用跑完本轮。\n session.tools = tools\n return true\n } catch (err) {\n this.deps.logger.warn(\n { sessionId: id, err: err instanceof Error ? err.message : String(err) },\n '[session-tools-sync] resync failed for session, skipping (fail-soft)',\n )\n return false\n }\n }\n}\n","/** Plugin panel manager: register/lifecycle/dependency of plugin UI panels. */\n\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { discoverPlugins, type DiscoveredPlugin } from '@x-otto/plugin'\nimport { evaluatePluginTrust } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\n\nconst logger = createLogger('@x-otto/coding:plugin-panel-manager')\n\nexport interface PanelRegistration {\n /** 面板唯一 id(格式 `pluginId:panelId`)。 */\n id: string\n /** 插件 id。 */\n pluginId: string\n /** 面板标题。 */\n title: string\n /** 已编译组件的绝对路径(panels-dist/xxx.js)。 */\n modulePath: string\n /** 导出名('default' 或具名)。 */\n exportName: string\n}\n\nexport interface PluginPanelManagerOptions {\n workspaceDir: string\n homedir: string\n disabledPlugins?: readonly string[]\n /** 缺省自动发现;可注入 `App.activePlugins()` 复用避免重复 discover I/O(RFC-081 review M1)。 */\n plugins?: readonly DiscoveredPlugin[]\n}\n\n/**\n * 扫描插件面板并返回待注册的面板列表。\n * 每个面板需验证编译产物存在(若不存在则跳过 + warn)。\n *\n * @returns 面板注册信息列表,供 TUI handle.registerPanel 消费。\n */\nexport function discoverPluginPanels(opts: PluginPanelManagerOptions): PanelRegistration[] {\n const plugins =\n opts.plugins ??\n discoverPlugins({\n cwd: opts.workspaceDir,\n homedir: opts.homedir,\n disabled: opts.disabledPlugins,\n })\n\n const result: PanelRegistration[] = []\n\n for (const p of plugins) {\n const panels = p.manifest.contributes?.panels\n if (!panels || panels.length === 0) continue\n\n // RFC-081 review H3:信任门——项目级未信任插件的面板不可注册。\n const verdict = evaluatePluginTrust(p)\n if (verdict.gated) continue\n\n // 终局 review(endgame-2026-07-10 F4/P1-5):panels 是 in-process 编译执行的代码\n // (同 renderers),此前只查布尔 gated、不查 capability/pendingCapabilities——门控强度\n // 弱于同性质的 renderers 轴(renderer-loader.ts 要求 capabilities 含 `tui.renderer` 且\n // 已被显式授予)。现对齐:要求声明 `tui.renderer` 且该能力已被授予(非 pending),\n // 否则 fail-closed 跳过(不阻塞该插件其余轴或其他插件)。\n // RFC-210 D1:capability 原名 `ui.renderer`,旧声明经 manifest schema transform 映射到\n // 新名后到达此处——本检查只需认新名。\n if (!p.manifest.capabilities?.includes('tui.renderer')) {\n logger.warn(\n { pluginId: p.id },\n 'plugin declares contributes.panels but not capabilities:[\"tui.renderer\"], skipped (fail-closed)',\n )\n continue\n }\n if (verdict.pendingCapabilities.includes('tui.renderer')) {\n logger.warn(\n { pluginId: p.id },\n 'plugin capability \"tui.renderer\" declared but not yet granted, skipping panels (fail-closed)',\n )\n continue\n }\n\n for (const panel of panels) {\n const modulePath = join(p.dir, panel.entry)\n if (!existsSync(modulePath)) {\n // postinstall 可能未执行或编译失败——跳过但不阻止其他面板;\n // RFC-303 M3(T303-08):显式 warn,不再静默(静默跳过曾掩盖\n // miniprogram-use 面板 entry 悬空的真实生产缺陷)。\n logger.warn({ pluginId: p.id, panelId: panel.id, modulePath }, 'panel entry not found — postinstall build may not have run')\n continue\n }\n\n result.push({\n id: `${p.id}:${panel.id}`,\n pluginId: p.id,\n title: panel.title,\n modulePath,\n exportName: panel.export ?? 'default',\n })\n }\n }\n\n return result\n}\n","/** Status items: plugin-supplied status bar entries (badges, indicators). */\nimport { evaluateContextKeys, evaluateWhen, evaluatePluginTrust, SHELL_POINTS, type DiscoveredPlugin } from '@x-otto/plugin'\n\nexport interface ResolvedStatusItem {\n id: string\n text: string\n}\n\n/** 解析全部已发现插件对 `SHELL_POINTS.statuslineItem` 贡献点的条目(纯函数,无 IO)。 */\nexport function resolveStatusItems(plugins: readonly DiscoveredPlugin[]): ResolvedStatusItem[] {\n const out: Array<{ item: ResolvedStatusItem; order: number }> = []\n for (const plugin of plugins) {\n if (evaluatePluginTrust(plugin).gated) continue\n const items = plugin.manifest.contributes?.statusItems\n if (!items?.length) continue\n const ctx = evaluateContextKeys(plugin.manifest.contributes?.contextKeys)\n for (const item of items) {\n if ((item.point ?? SHELL_POINTS.statuslineItem) !== SHELL_POINTS.statuslineItem) continue\n if (!evaluateWhen(item.when, ctx)) continue\n const text = ctx[item.dataSource.contextKey]\n if (text == null) continue\n out.push({ item: { id: `${plugin.id}:${item.id}`, text }, order: item.order ?? 0 })\n }\n }\n return out.sort((a, b) => a.order - b.order).map((x) => x.item)\n}\n","/**\n * menu-items.ts —— 全局菜单贡献点解析(RFC-105 D5/M105-4-02c,`shell/menu/item`)。\n *\n * 与 RFC-084 既有的「单插件详情动作」(extensionDetailActions,per-viewed-plugin scope)不同——\n * 本贡献点是**跨插件聚合**:任意插件都可以往全局菜单(如 `/extension` 列表页)追加一条固定入口,\n * 不局限于「查看某插件详情」场景。复用同一套前端无关基础设施(`resolveActions`/`evaluateWhen`),\n * 只是 context 与聚合范围不同。\n */\nimport { resolveActions, evaluateContextKeys, evaluatePluginTrust, SHELL_POINTS, type DiscoveredPlugin, type ResolvedAction } from '@x-otto/plugin'\n\n/**\n * 解析全部已发现插件对 `shell/menu/item` 贡献点的贡献,聚合成一份有序动作列表。\n * 项目级未信任插件(gated)跳过(同 statusItems 门控基线)。\n * 跨插件 id 冲突:`ResolvedAction.id` 未加插件前缀(action.id 由插件作者声明)——\n * 与既有 `resolveActions` 契约一致(action id 命名空间化是插件作者的责任,同 RFC-084 既有设计)。\n */\nexport function resolveGlobalMenuItems(plugins: readonly DiscoveredPlugin[]): ResolvedAction[] {\n const out: ResolvedAction[] = []\n for (const plugin of plugins) {\n if (evaluatePluginTrust(plugin).gated) continue\n const contribs = plugin.manifest.contributes\n if (!contribs?.menus?.length) continue\n const ctx = evaluateContextKeys(contribs.contextKeys)\n out.push(...resolveActions(SHELL_POINTS.menuItem, contribs.menus, contribs.actions, ctx))\n }\n return out\n}\n","/** Renderer loader: plugin-declared message renderers for custom content types. */\nimport { type DiscoveredPlugin, type PluginRendererEntry } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\nimport {\n loadAllCompiledContributions,\n loadCompiledContributions,\n type CompiledContributionSpec,\n} from './load-compiled-contribution'\n\nconst logger = createLogger('@x-otto/coding:plugin-renderer-loader')\n\n/** 渲染函数签名(与 `@x-otto/tui renderMessageLines` 同构)。 */\nexport type PluginMessageRenderer = (message: unknown, columns: number) => string[]\n\nexport interface RegisteredRenderer {\n /** 全局唯一 id(`pluginId:rendererId`)。 */\n id: string\n pluginId: string\n matcher: PluginRendererEntry['matcher']\n render: PluginMessageRenderer\n}\n\nconst spec: CompiledContributionSpec<PluginRendererEntry, RegisteredRenderer> = {\n capability: 'tui.renderer',\n axis: 'renderers',\n noun: 'renderer',\n pendingNote: 'RFC-140 D3',\n logger,\n entries: (plugin) => plugin.manifest.contributes?.renderers,\n entryPath: (entry) => entry.entry,\n exportName: (entry) => entry.export,\n idLogFields: (entry) => ({ rendererId: entry.id }),\n logTypeofOnNotFunction: true,\n build: (plugin, entry, fn) => ({\n id: `${plugin.id}:${entry.id}`,\n pluginId: plugin.id,\n matcher: entry.matcher,\n render: fn as PluginMessageRenderer,\n }),\n successLogFields: (entry) => ({ rendererId: entry.id, matcher: entry.matcher }),\n}\n\n/**\n * 装载某插件 manifest 声明的全部渲染器。要求 `capabilities` 含 `tui.renderer`(fail-closed R4,\n * 同声明式轴门控一致);单条加载失败 fail-soft 跳过,不阻塞该插件其余渲染器或其他插件。\n * 项目级未信任插件(gated)整体跳过(可执行面,同 panels 信任基线)。\n */\nexport function loadPluginRenderers(plugin: DiscoveredPlugin): RegisteredRenderer[] {\n return loadCompiledContributions(plugin, spec)\n}\n\n/** 装载全部已发现插件的渲染器(跨插件聚合,候选列表模型不去重)。 */\nexport function loadAllPluginRenderers(plugins: readonly DiscoveredPlugin[]): RegisteredRenderer[] {\n return loadAllCompiledContributions(plugins, spec)\n}\n","/** A2UI component loader: dynamically load React component from plugin bundle. */\nimport { type DiscoveredPlugin, type PluginA2uiComponentEntry } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\nimport {\n loadAllCompiledContributions,\n loadCompiledContributions,\n type CompiledContributionSpec,\n} from './load-compiled-contribution'\n\nconst logger = createLogger('@x-otto/coding:plugin-a2ui-component-loader')\n\n/** 内置 a2ui 组件类型名(保留字,见 `@x-otto/tui a2ui-renderer.ts` 的 renderComponent switch)。 */\nconst BUILTIN_A2UI_COMPONENT_TYPES = new Set(['Text', 'Card', 'List', 'Progress', 'Chip', 'Button'])\n\n/** 插件 a2ui 组件渲染函数签名(与 `@x-otto/tui a2ui-renderer.ts` renderComponent 同构)。 */\nexport type PluginA2uiRenderFn = (\n component: unknown,\n columns: number,\n resolveChild: (id: string) => string[],\n) => string[]\n\nexport interface RegisteredA2uiComponent {\n /** 组件类型名(跨插件全局命名空间,供 tui 侧注册表按 type 查表)。 */\n type: string\n pluginId: string\n render: PluginA2uiRenderFn\n}\n\nconst spec: CompiledContributionSpec<PluginA2uiComponentEntry, RegisteredA2uiComponent> = {\n capability: 'a2ui.component',\n axis: 'a2uiComponents',\n noun: 'a2ui component',\n pendingNote: 'RFC-140 D3 pattern',\n logger,\n entries: (plugin) => plugin.manifest.contributes?.a2uiComponents,\n entryPath: (entry) => entry.entry,\n exportName: (entry) => entry.export,\n idLogFields: (entry) => ({ type: entry.type }),\n // a2ui 现状的 not-function 日志不含 `type: typeof render` 字段(其 id 字段名恰为 `type`,\n // 加了会撞键覆盖)——RFC-202 对抗评审 C1/C2 保真点。\n logTypeofOnNotFunction: false,\n preCheck: (plugin, entry) => {\n if (BUILTIN_A2UI_COMPONENT_TYPES.has(entry.type)) {\n logger.warn(\n { pluginId: plugin.id, type: entry.type },\n 'plugin a2ui component type collides with builtin reserved type, rejected (R4)',\n )\n return false\n }\n return true\n },\n build: (plugin, entry, fn) => ({\n type: entry.type,\n pluginId: plugin.id,\n render: fn as PluginA2uiRenderFn,\n }),\n // a2ui 现状无成功加载 info 日志——保真不趁机补齐(RFC-202 零行为变化纪律)。\n}\n\n/**\n * 装载某插件 manifest 声明的全部 a2ui 组件。要求 `capabilities` 含 `a2ui.component`\n * (fail-closed R4,同声明式轴门控一致);单条加载失败 fail-soft 跳过,不阻塞该插件其余\n * 组件或其他插件。项目级未信任插件(gated)整体跳过(可执行面,同 renderers 信任基线)。\n * 内置类型名保留字拒绝注册(跨插件同名冲突检查见 `loadAllA2uiComponents`)。\n */\nexport function loadPluginA2uiComponents(plugin: DiscoveredPlugin): RegisteredA2uiComponent[] {\n return loadCompiledContributions(plugin, spec)\n}\n\n/**\n * 装载全部已发现插件的 a2ui 组件(跨插件聚合)。跨插件同名 type 冲突:先注册者生效,\n * 后来者拒绝并告警(RFC-155 §5 R4,对齐 RFC-105 R3/R5 的\"内置 id 保留字/不静默 first-win\n * 覆盖\"精神——这里是\"非内置但跨插件\"场景,同样不静默覆盖,只是判定顺序为声明顺序)。\n */\nexport function loadAllA2uiComponents(plugins: readonly DiscoveredPlugin[]): RegisteredA2uiComponent[] {\n return loadAllCompiledContributions(plugins, spec, {\n key: (component) => component.type,\n onConflict: (component, existingOwner) => {\n logger.warn(\n { pluginId: component.pluginId, type: component.type, existingOwner },\n 'a2ui component type already registered by another plugin, rejected (first-registered wins)',\n )\n },\n })\n}\n","/** Status widget loader: plugin-declared status bar widgets. */\nimport { type DiscoveredPlugin, type StatusWidgetContribution } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\nimport {\n loadAllCompiledContributions,\n loadCompiledContributions,\n type CompiledContributionSpec,\n} from './load-compiled-contribution'\n\nconst logger = createLogger('@x-otto/coding:plugin-status-widget-loader')\n\n/** 徽标语义色(镜像 `@x-otto/tui badge-entry.ts BadgeColorKind`——本包对渲染层零依赖,\n * 跨包各自定义同构类型,装载调用方按结构赋值,同 `RegisteredRenderer` 既有纹理)。 */\nexport type StatusWidgetColorKind = 'warning' | 'inactive'\n\n/**\n * 宿主组装的只读快照(RFC-184 D2:v1 无事件订阅,widget 只消费此快照,不持有 TuiHandle)。\n * `providerUsage` 类型故意保持 `unknown`——真实快照组装与调用只发生在 `@x-otto/tui`\n * (见 `status-widget-registry.ts StatusWidgetContext`,那里用 TUI 实际持有的\n * `ProviderUsageBadgeItem` 精确类型);本接口是**装载模块的文档性契约类型**,供插件作者\n * 参考渲染函数签名,本包不构造该快照实例,收紧类型只会制造与 TUI 侧的跨包类型耦合而无实益。\n */\nexport interface StatusWidgetContext {\n nowMs: number\n model: string\n providerUsage: unknown\n columns: number\n}\n\n/** 插件状态栏徽标渲染函数签名。 */\nexport type PluginStatusWidgetRenderFn = (\n ctx: StatusWidgetContext,\n) => { text: string; color?: StatusWidgetColorKind } | null\n\nexport interface RegisteredStatusWidget {\n /** 全局唯一 id(`pluginId:widgetId`)。 */\n id: string\n pluginId: string\n render: PluginStatusWidgetRenderFn\n /** 宿主 clamp 前的原始声明值(clamp 逻辑在 `@x-otto/tui` 侧共享定时器实现,RFC-184 D4)。 */\n tickMs?: number\n order?: number\n}\n\nconst spec: CompiledContributionSpec<StatusWidgetContribution, RegisteredStatusWidget> = {\n capability: 'tui.renderer',\n axis: 'statusWidgets',\n noun: 'status widget',\n pendingNote: 'RFC-140 D3 pattern',\n logger,\n entries: (plugin) => plugin.manifest.contributes?.statusWidgets,\n entryPath: (entry) => entry.entry,\n exportName: (entry) => entry.export,\n idLogFields: (entry) => ({ widgetId: entry.id }),\n logTypeofOnNotFunction: true,\n build: (plugin, entry, fn) => ({\n id: `${plugin.id}:${entry.id}`,\n pluginId: plugin.id,\n render: fn as PluginStatusWidgetRenderFn,\n tickMs: entry.tickMs,\n order: entry.order,\n }),\n successLogFields: (entry) => ({ widgetId: entry.id }),\n}\n\n/**\n * 装载某插件 manifest 声明的全部状态栏徽标。要求 `capabilities` 含 `tui.renderer`\n * (fail-closed R4,同 renderers/a2uiComponents 信任基线);单条加载失败 fail-soft 跳过,\n * 不阻塞该插件其余徽标或其他插件。项目级未信任插件(gated)整体跳过(可执行面)。\n */\nexport function loadPluginStatusWidgets(plugin: DiscoveredPlugin): RegisteredStatusWidget[] {\n return loadCompiledContributions(plugin, spec)\n}\n\n/** 装载全部已发现插件的状态栏徽标(跨插件聚合,候选列表模型不去重)。 */\nexport function loadAllPluginStatusWidgets(plugins: readonly DiscoveredPlugin[]): RegisteredStatusWidget[] {\n return loadAllCompiledContributions(plugins, spec)\n}\n","/** File viewer loader: plugin-supplied file viewers for code/markdown/binary. */\nimport { type DiscoveredPlugin, type PluginFileViewerEntry } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\nimport {\n loadAllCompiledContributions,\n loadCompiledContributions,\n type CompiledContributionSpec,\n} from './load-compiled-contribution'\n\nconst logger = createLogger('@x-otto/coding:plugin-file-viewer-loader')\n\n/** 渲染函数签名(插件作者侧,`@x-otto/extension defineFileViewer` 薄包装同签名)。 */\nexport type PluginFileViewerRenderFn = (\n content: string,\n path: string,\n columns: number,\n) => { lines: string[]; wrapMode?: 'pre-wrap' | 'truncate' } | null\n\nexport interface RegisteredFileViewer {\n /** 全局唯一 id(`pluginId:entryId`)。 */\n id: string\n pluginId: string\n label: string\n matcher: PluginFileViewerEntry['matcher']\n render: PluginFileViewerRenderFn\n order?: number\n}\n\nconst spec: CompiledContributionSpec<PluginFileViewerEntry, RegisteredFileViewer> = {\n capability: 'tui.renderer',\n axis: 'fileViewers',\n noun: 'file viewer',\n pendingNote: 'RFC-140 D3 pattern',\n logger,\n entries: (plugin) => plugin.manifest.contributes?.fileViewers,\n entryPath: (entry) => entry.entry,\n exportName: (entry) => entry.export,\n idLogFields: (entry) => ({ viewerId: entry.id }),\n logTypeofOnNotFunction: true,\n build: (plugin, entry, fn) => ({\n id: `${plugin.id}:${entry.id}`,\n pluginId: plugin.id,\n label: entry.label,\n matcher: entry.matcher,\n render: fn as PluginFileViewerRenderFn,\n order: entry.order,\n }),\n successLogFields: (entry) => ({ viewerId: entry.id, matcher: entry.matcher }),\n}\n\n/**\n * 装载某插件 manifest 声明的全部文件查看器。要求 `capabilities` 含 `tui.renderer`\n * (fail-closed R3,三层信任门同 renderers/statusWidgets 轴);单条加载失败 fail-soft\n * 跳过,不阻塞该插件其余视图或其他插件。项目级未信任插件(gated)整体跳过。\n */\nexport function loadPluginFileViewers(plugin: DiscoveredPlugin): RegisteredFileViewer[] {\n return loadCompiledContributions(plugin, spec)\n}\n\n/** 装载全部已发现插件的文件查看器(跨插件聚合,候选列表模型不去重)。 */\nexport function loadAllPluginFileViewers(plugins: readonly DiscoveredPlugin[]): RegisteredFileViewer[] {\n return loadAllCompiledContributions(plugins, spec)\n}\n","/** Plugin contributions facade: aggregated tool/agent/provider/mcp from all plugins. */\nimport { homedir } from 'node:os'\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport type { SettingsManager } from '@x-otto/setting'\nimport type { PluginModuleManager } from './plugin-module-manager'\nimport type { McpController } from './mcp-controller'\nimport { discoverPluginPanels, type PanelRegistration } from '../plugin/plugin-panel-manager'\nimport { resolveStatusItems, type ResolvedStatusItem } from '../plugin/status-items'\nimport { resolveGlobalMenuItems } from '../plugin/menu-items'\nimport { loadAllPluginRenderers, type RegisteredRenderer } from '../plugin/renderer-loader'\nimport { loadAllA2uiComponents, type RegisteredA2uiComponent } from '../plugin/a2ui-component-loader'\nimport { loadAllPluginStatusWidgets, type RegisteredStatusWidget } from '../plugin/status-widget-loader'\nimport { loadAllPluginFileViewers, type RegisteredFileViewer } from '../plugin/file-viewer-loader'\nimport type { ResolvedAction } from '@x-otto/plugin'\nimport { discoverFileCommands, type FileCommand } from '@x-otto/claude-compat'\n\nexport interface PluginContributionsFacadeDeps {\n getWorkspaceDir: () => string\n settings: SettingsManager\n getActivePlugins: () => DiscoveredPlugin[]\n pluginModuleManager: PluginModuleManager\n mcpController: McpController\n}\n\nexport class PluginContributionsFacade {\n constructor(private readonly deps: PluginContributionsFacadeDeps) {}\n\n /** ServiceHostPort:文件命令目录。封装 discoverFileCommands,使 service 不直接 import coding 值。 */\n listFileCommands(): FileCommand[] {\n // 文件发现的命令在前、computed 插件命令在后——保留「内置/文件优先」不夺名(R7)。\n return [\n ...discoverFileCommands(this.deps.getWorkspaceDir(), this.deps.getActivePlugins()),\n ...this.deps.pluginModuleManager.commands,\n ]\n }\n\n /** computed 插件命令(FileCommand[])。供 cli 命令系统与 listFileCommands 并入。 */\n computedPluginCommands(): FileCommand[] {\n return this.deps.pluginModuleManager.commands\n }\n\n /** ServiceHostPort:调用 MCP server 上的指定 tool(RFC-081)。供插件面板和 catalog API 使用。 */\n async callMcpTool(\n serverName: string,\n toolName: string,\n params?: Record<string, unknown>,\n ): Promise<{ content: { type: string; text?: string }[]; isError?: boolean }> {\n const client = this.deps.mcpController.getMcpClient(serverName)\n if (!client) {\n throw new Error(`MCP server \"${serverName}\" not connected or not found`)\n }\n const result = await client.callTool({ name: toolName, arguments: params })\n return { content: result.content, isError: result.isError }\n }\n\n /**\n * RFC-081:获取当前应注册到 TUI 的插件面板列表。\n * CLI 在 reloadCapabilities 后调用此方法,将面板注册到 TuiHandle。\n */\n getPluginPanelRegistrations(): PanelRegistration[] {\n return discoverPluginPanels({\n workspaceDir: this.deps.getWorkspaceDir(),\n homedir: homedir(),\n disabledPlugins: this.deps.settings.get('disabled_plugins'),\n plugins: this.deps.getActivePlugins(), // 复用,避免重复 discover I/O(RFC-081 review M1)\n })\n }\n\n /**\n * RFC-105 D5/M105-4:解析当前应显示在 TUI 状态栏的插件条目。\n * RFC-131 T8 起,`getPluginRegistrations()` 是主消费入口(聚合喂给 `TuiHandle.setPluginRegistrations`);\n * 本方法仍单独保留,供 `/extension` 详情页(`extension-plugin.ts`)按插件 id 过滤展示贡献计数。\n */\n getPluginStatusItems(): ResolvedStatusItem[] {\n return resolveStatusItems(this.deps.getActivePlugins())\n }\n\n /**\n * RFC-105 D5/M105-4:解析插件对全局菜单贡献点(`shell/menu/item`)的贡献。\n * 消费点:`/extension` 列表页(openList)在内置条目之后追加插件贡献的固定入口。\n */\n getGlobalMenuItems(): ResolvedAction[] {\n return resolveGlobalMenuItems(this.deps.getActivePlugins())\n }\n\n /**\n * RFC-105 D6/M105-5a:装载全部插件的消息渲染器(`contributes.renderers`)。\n * RFC-131 T8 起,`getPluginRegistrations()` 是主消费入口(聚合喂给 `TuiHandle.setPluginRegistrations`);\n * 本方法仍单独保留,供 `/extension` 详情页(`extension-plugin.ts`)按插件 id 过滤展示贡献计数。\n */\n getPluginRenderers(): RegisteredRenderer[] {\n return loadAllPluginRenderers(this.deps.getActivePlugins())\n }\n\n /**\n * RFC-105 D1/D6/RFC-155 M155-3:装载全部插件的 a2ui 组件扩展(`contributes.a2uiComponents`)。\n * 同 `getPluginRenderers()` 模式——聚合入 `getPluginRegistrations()` 供统一入口消费。\n */\n getPluginA2uiComponents(): RegisteredA2uiComponent[] {\n return loadAllA2uiComponents(this.deps.getActivePlugins())\n }\n\n /**\n * RFC-184 D2/D3:装载全部插件的状态栏代码式徽标(`contributes.statusWidgets`)。\n * 同 `getPluginRenderers()` 模式——聚合入 `getPluginRegistrations()` 供统一入口消费。\n */\n getPluginStatusWidgets(): RegisteredStatusWidget[] {\n return loadAllPluginStatusWidgets(this.deps.getActivePlugins())\n }\n\n /**\n * RFC-187 D3/D4:装载全部插件的文件查看器候选(`contributes.fileViewers`)。\n * 同 `getPluginRenderers()` 模式——聚合入 `getPluginRegistrations()` 供统一入口消费。\n */\n getPluginFileViewers(): RegisteredFileViewer[] {\n return loadAllPluginFileViewers(this.deps.getActivePlugins())\n }\n\n /**\n * RFC-131 T8:聚合当前活跃插件的全部 TUI 侧贡献(状态栏条目/消息渲染器/面板),\n * 按插件 id 分组为 `PluginRegistration[]`,供 `TuiHandle.setPluginRegistrations` 统一入口\n * 消费(替代 `setPluginStatusItems`/`registerPluginRenderers` 两个存量分散调用)。\n *\n * **不 import `@x-otto/tui` 类型**(保持宿主侧对渲染层零依赖的既有架构边界,RFC-131 §背景\n * \"TUI 作为纯渲染层不处理插件的发现/信任/装载\"的对称面)——返回值的字段形状(`pluginId`/\n * `renderers`/`statusItems`/`panels`)与 `@x-otto/tui` 的 `PluginRegistration` 接口结构一致,\n * 靠 TypeScript 结构类型在调用方(`packages/cli`,同时依赖 `@x-otto/coding` 与 `@x-otto/tui`)\n * 完成兼容——那一层本就同时认识两个包的类型,是唯一需要知道两者可互换的地方。\n *\n * 分组依据:`RegisteredRenderer`/`PanelRegistration` 自带 `pluginId` 字段;`ResolvedStatusItem`\n * 的 `id` 是 `${pluginId}:${itemId}` 拼接格式(`status-items.ts` 既有约定,插件 id 本身是\n * kebab-case、不含冒号,解析稳定),从中提取首段还原 pluginId。\n *\n * `commands` 字段留空——slash 命令表(`refreshSlashCommands()`)当前由 CLI 层直接构造,\n * 不经过插件贡献聚合(存量 `setCommands` 数据源与本方法覆盖的三类贡献是不同轴,见\n * RFC-131 milestone T8 设计闸门),本方法不越界处理。\n *\n * **`panels` 字段现状(对抗审核 finding E)**:数据已推入 `TuiPluginRegistry`(经\n * `TuiHandle.setPluginRegistrations`),但 `packages/tui/src/plugin-registry.ts` 的\n * `getPanels()` 当前**零生产消费方**——面板的实际渲染路径仍是 `/extension` 详情 dialog\n * 独立按需调用 `getPluginPanelRegistrations()` 现算(`extension-plugin.ts`),两条路径\n * 并存。若未来新增消费方读取 `TuiPluginRegistry.getPanels()`,需确保本方法与\n * `getPluginPanelRegistrations()` 在信任门/禁用状态判定上保持同源(当前两者都调用\n * `discoverPluginPanels()`,同源),并核对全部触发 `setPluginRegistrations` 调用的事件点\n * (install/uninstall/trust/reload)是否也覆盖了该新消费方需要的更新时机。\n */\n getPluginRegistrations(): Array<{\n pluginId: string\n renderers?: RegisteredRenderer[]\n statusItems?: { id: string; text: string }[]\n panels?: PanelRegistration[]\n a2uiComponents?: RegisteredA2uiComponent[]\n statusWidgets?: RegisteredStatusWidget[]\n fileViewers?: RegisteredFileViewer[]\n }> {\n const byPlugin = new Map<\n string,\n {\n pluginId: string\n renderers?: RegisteredRenderer[]\n statusItems?: { id: string; text: string }[]\n panels?: PanelRegistration[]\n a2uiComponents?: RegisteredA2uiComponent[]\n statusWidgets?: RegisteredStatusWidget[]\n fileViewers?: RegisteredFileViewer[]\n }\n >()\n const ensure = (pluginId: string) => {\n let entry = byPlugin.get(pluginId)\n if (!entry) {\n entry = { pluginId }\n byPlugin.set(pluginId, entry)\n }\n return entry\n }\n\n for (const renderer of this.getPluginRenderers()) {\n const entry = ensure(renderer.pluginId)\n ;(entry.renderers ??= []).push(renderer)\n }\n for (const item of this.getPluginStatusItems()) {\n const pluginId = item.id.split(':')[0] ?? item.id\n const entry = ensure(pluginId)\n ;(entry.statusItems ??= []).push(item)\n }\n for (const panel of this.getPluginPanelRegistrations()) {\n const entry = ensure(panel.pluginId)\n ;(entry.panels ??= []).push(panel)\n }\n for (const component of this.getPluginA2uiComponents()) {\n const entry = ensure(component.pluginId)\n ;(entry.a2uiComponents ??= []).push(component)\n }\n for (const widget of this.getPluginStatusWidgets()) {\n const entry = ensure(widget.pluginId)\n ;(entry.statusWidgets ??= []).push(widget)\n }\n for (const viewer of this.getPluginFileViewers()) {\n const entry = ensure(viewer.pluginId)\n ;(entry.fileViewers ??= []).push(viewer)\n }\n\n return [...byPlugin.values()]\n }\n}\n","/** Plugin hook registration: lifecycle/shutdown/permission hooks. */\n\nimport { spawn } from 'node:child_process'\nimport { buildAllowedEnv, checkShellCommand, createLogger } from '@x-otto/shared'\n\nconst logger = createLogger('@x-otto/coding:plugin-hooks')\n\n/** stdout/stderr 最大捕获字节数,超出截断并标记 truncated。 */\nconst MAX_OUTPUT = 4096\n\n/** 钩子执行上下文。 */\nexport interface HookContext {\n /** 插件安装目录的绝对路径。 */\n pluginDir: string\n /** 插件 manifest id(kebab-case)。 */\n pluginId: string\n /** 项目工作目录(项目级插件有值,全局插件为 undefined)。 */\n workspaceDir?: string\n}\n\n/** 钩子执行结果。 */\nexport interface HookResult {\n success: boolean\n hook: string\n command: string\n exitCode?: number | null\n signal?: string | null\n stdout?: string\n stderr?: string\n error?: string\n timeout?: boolean\n /** stdout 或 stderr 超过 4KB 上限被截断。 */\n truncated?: boolean\n}\n\n/** 构建钩子进程的安全环境变量。 */\nfunction buildHookEnv(ctx: HookContext, extra?: Record<string, string>): Record<string, string> {\n const base = buildAllowedEnv()\n base['OTTO_PLUGIN_DIR'] = ctx.pluginDir\n base['OTTO_PLUGIN_ID'] = ctx.pluginId\n if (ctx.workspaceDir) {\n base['OTTO_WORKSPACE_DIR'] = ctx.workspaceDir\n }\n if (extra) Object.assign(base, extra)\n return base\n}\n\n/** shell 命令安全检查(防御纵深:拦截已知危险模式)。 */\nfunction checkHookCommand(command: string): HookResult | null {\n const result = checkShellCommand(command)\n if (!result.safe) {\n return {\n success: false,\n hook: 'pre-check',\n command,\n error: `Dangerous command blocked: ${result.reason}`,\n }\n }\n return null\n}\n\nfunction extractOutput(stdout?: Buffer | string, stderr?: Buffer | string): {\n stdout?: string\n stderr?: string\n truncated: boolean\n} {\n let truncated = false\n let outStr: string | undefined\n let errStr: string | undefined\n\n if (stdout?.length) {\n const buf = typeof stdout === 'string' ? stdout : stdout.toString('utf-8')\n outStr = buf.slice(0, MAX_OUTPUT)\n truncated = buf.length > MAX_OUTPUT\n }\n if (stderr?.length) {\n const buf = typeof stderr === 'string' ? stderr : stderr.toString('utf-8')\n errStr = buf.slice(0, MAX_OUTPUT)\n truncated = truncated || buf.length > MAX_OUTPUT\n }\n return { stdout: outStr, stderr: errStr, truncated }\n}\n\n/**\n * 执行 postinstall 钩子(异步,30s 超时)。\n *\n * 仅信任门通过后由 install flow 调用。执行前先经 `checkShellCommand` 安全检查(防御纵深)。\n * 非零退出或超时 → `HookResult.success = false`,但不抛异常。\n */\nexport async function executePostInstall(ctx: HookContext, command: string): Promise<HookResult> {\n // 防御纵深:即使信任门已过,仍拦截已知危险模式\n const blocked = checkHookCommand(command)\n if (blocked) return blocked\n\n logger.info({ pluginId: ctx.pluginId, command }, 'Executing postinstall hook')\n\n return new Promise((resolve) => {\n const child = spawn(command, {\n cwd: ctx.pluginDir,\n env: buildHookEnv(ctx),\n shell: true,\n stdio: 'pipe',\n })\n let stdoutBuf: Buffer[] = []\n let stderrBuf: Buffer[] = []\n\n child.stdout?.on('data', (d: Buffer) => { stdoutBuf.push(d) })\n child.stderr?.on('data', (d: Buffer) => { stderrBuf.push(d) })\n\n let timedOut = false\n const timer = setTimeout(() => { timedOut = true; child.kill() }, 30_000)\n\n child.on('close', (code, signal) => {\n clearTimeout(timer)\n const stdout = Buffer.concat(stdoutBuf)\n const stderr = Buffer.concat(stderrBuf)\n const { stdout: outStr, stderr: errStr, truncated } = extractOutput(stdout, stderr)\n\n const hookResult: HookResult = {\n success: !timedOut && code === 0 && signal === null,\n hook: 'postinstall',\n command,\n exitCode: code,\n signal: signal ?? undefined,\n stdout: outStr,\n stderr: errStr,\n truncated: truncated || undefined,\n }\n\n if (timedOut) {\n hookResult.timeout = true\n hookResult.error = 'postinstall hook timed out (30s)'\n }\n\n if (!hookResult.success) {\n logger.warn({ pluginId: ctx.pluginId, result: hookResult }, 'Postinstall hook failed')\n }\n\n resolve(hookResult)\n })\n\n child.on('error', (err) => {\n clearTimeout(timer)\n resolve({\n success: false,\n hook: 'postinstall',\n command,\n error: err.message,\n })\n })\n })\n}\n\n/**\n * 执行 setup 钩子(异步非阻塞,60s 超时)。\n *\n * 仅信任门通过后由插件加载 flow 调用。执行前先经 `checkShellCommand` 安全检查。\n * fire-and-forget:调用立即返回,完成时通过 `onComplete` 回调通知。\n *\n * @param ctx 钩子上下文\n * @param command 要执行的 shell 命令\n * @param onComplete 完成回调(含 HookResult)。在子进程 close/error 时调用。\n * 成功失败均回调——调用方据此标记 setupDone(RFC-080 设计:失败也标记 done,避免无限重试)。\n */\nexport function executeSetup(\n ctx: HookContext,\n command: string,\n onComplete?: (result: HookResult) => void,\n /** RFC-085 M3:可选的 ProcessRuntime 实例 + AbortSignal */\n processRuntime?: import('@x-otto/runtime').ProcessRuntime,\n signal?: AbortSignal,\n): void {\n // 防御纵深:拦截已知危险模式\n const blocked = checkHookCommand(command)\n if (blocked) {\n logger.warn({ pluginId: ctx.pluginId, command }, 'Setup hook blocked by safety check')\n onComplete?.(blocked)\n return\n }\n\n logger.info({ pluginId: ctx.pluginId, command }, 'Executing setup hook (async)')\n\n // RFC-085 M3:ProcessRuntime 接入 + abort 支持\n if (processRuntime) {\n const proc = processRuntime.spawn({\n command,\n args: [],\n // shell:true 与旧路径一致——setup 命令是 shell 串(如 \"npm install && node x.js\"),\n // 否则会被当单个可执行文件名 exec → ENOENT。detached 由 spawn 强制(组 kill)。\n options: { shell: true },\n owner: { type: 'plugin-hook', id: ctx.pluginId },\n category: 'plugin',\n label: `${ctx.pluginId}:setup`,\n lifecycle: 'evictable',\n killGraceMs: 5_000,\n cwd: ctx.pluginDir,\n env: buildHookEnv(ctx),\n signal,\n })\n\n // 最大运行时 60s(对齐旧路径 timeout)——超时 kill,避免 runaway setup 无界运行(B5)。\n const runTimeout = setTimeout(() => {\n logger.warn({ pluginId: ctx.pluginId }, 'Setup hook exceeded 60s — killing')\n void proc.kill({ reason: 'setup timeout (60s)' })\n }, 60_000)\n runTimeout.unref?.()\n\n let stdoutBuf = ''\n let stderrBuf = ''\n\n proc.onStdoutLine((line: string) => {\n stdoutBuf += line + '\\n'\n })\n proc.onStderrLine((line: string) => {\n stderrBuf += line + '\\n'\n })\n\n proc.onExit((exit) => {\n clearTimeout(runTimeout)\n const { stdout, stderr, truncated } = extractOutput(stdoutBuf, stderrBuf)\n\n const result: HookResult = {\n success: exit.code === 0 && exit.signal === null,\n hook: 'setup',\n command,\n exitCode: exit.code,\n signal: exit.signal ?? undefined,\n stdout,\n stderr,\n truncated: truncated || undefined,\n }\n\n if (!result.success) {\n logger.error(\n { pluginId: ctx.pluginId, exitCode: exit.code, signal: exit.signal, stderr: stderr?.slice(0, 512) },\n 'Setup hook failed',\n )\n } else {\n logger.info({ pluginId: ctx.pluginId }, 'Setup hook succeeded')\n }\n\n onComplete?.(result)\n })\n\n proc.onError((err: Error) => {\n logger.error({ pluginId: ctx.pluginId, err }, 'Setup hook spawn error')\n onComplete?.({\n success: false,\n hook: 'setup',\n command,\n error: err.message,\n })\n })\n\n return\n }\n\n // 旧路径:直接 spawn(ProcessRuntime 未注入时向下兼容)\n const child = spawn(command, {\n cwd: ctx.pluginDir,\n env: buildHookEnv(ctx),\n shell: true,\n timeout: 60_000,\n stdio: 'pipe',\n })\n\n let stdoutBuf = ''\n let stderrBuf = ''\n\n child.stdout?.on('data', (chunk: Buffer) => {\n stdoutBuf += chunk.toString('utf-8')\n })\n child.stderr?.on('data', (chunk: Buffer) => {\n stderrBuf += chunk.toString('utf-8')\n })\n\n child.on('close', (code, signal) => {\n const { stdout, stderr, truncated } = extractOutput(stdoutBuf, stderrBuf)\n\n const result: HookResult = {\n success: code === 0 && signal === null,\n hook: 'setup',\n command,\n exitCode: code,\n signal,\n stdout,\n stderr,\n truncated: truncated || undefined,\n }\n\n if (!result.success) {\n logger.error(\n { pluginId: ctx.pluginId, exitCode: code, signal, stderr: stderr?.slice(0, 512) },\n 'Setup hook failed',\n )\n } else {\n logger.info({ pluginId: ctx.pluginId }, 'Setup hook succeeded')\n }\n\n onComplete?.(result)\n })\n\n child.on('error', (err) => {\n logger.error({ pluginId: ctx.pluginId, err }, 'Setup hook spawn error')\n onComplete?.({\n success: false,\n hook: 'setup',\n command,\n error: err.message,\n })\n })\n}\n","/** Plugin lifecycle: load/unload/reload with dependency ordering. */\n\nimport { evaluatePluginTrust } from '@x-otto/plugin'\nimport { executeSetup } from './plugin-hooks'\nimport type { HookResult } from './plugin-hooks'\nimport { globalProcessRuntime } from '@x-otto/runtime'\nimport type { SettingsManager } from '@x-otto/setting'\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\n\nconst logger = createLogger('@x-otto/coding:plugin-lifecycle')\n\nconst SETUP_LIFECYCLE_KEY = 'plugin_lifecycle' as const\n\n/**\n * 正在执行 setup 的插件 id(进程级,跨 `runPendingSetups` 调用共享)。\n * setup 是 fire-and-forget,`setupDone` 直到 onComplete 才落盘;若两次 `reloadCapabilities`\n * 紧邻触发,第二次会看到该插件仍未 done → 重复 spawn(双重副作用)。此 Set 守卫去重\n * (RFC-081 review H4)。\n */\nconst setupInFlight = new Set<string>()\n\ninterface SetupEntry {\n id: string\n /** setup 执行时的插件版本(无版本时按缺失处理,见下方逻辑)。 */\n version?: string\n}\n\ninterface PluginLifecycleState {\n setupDone?: SetupEntry[]\n}\n\n/**\n * 检查并执行所有未完成 setup 的插件。\n *\n * 需传入**全量**插件列表(含 disabled),保证 disabled 插件在其 id 首次出现时即被标记 done,\n * 后续启用时不会重复执行 setup。\n *\n * @param allPlugins 全量已发现插件(含 disabled)\n * @param settings SettingsManager 实例(用于读写 plugin_lifecycle 状态)\n * @param workspaceDir 项目工作目录(传给 setup 脚本的 OTTO_WORKSPACE_DIR)\n */\nexport async function runPendingSetups(\n allPlugins: readonly DiscoveredPlugin[],\n settings: SettingsManager,\n workspaceDir?: string,\n): Promise<void> {\n const rawState = settings.get(SETUP_LIFECYCLE_KEY) as PluginLifecycleState | undefined\n const prevDone = rawState?.setupDone ?? []\n\n // Map<id, version>\n const done = new Map<string, string | undefined>(\n prevDone.map((e) => [e.id, e.version]),\n )\n\n // 清理已卸载插件的 stale 条目\n const activeIds = new Set(allPlugins.map((p) => p.id))\n for (const id of done.keys()) {\n if (!activeIds.has(id)) done.delete(id)\n }\n\n // 收集需要持久化的变更\n let dirty = false\n\n for (const p of allPlugins) {\n const prevVersion = done.get(p.id)\n const currentVersion = p.manifest.version\n\n const script = p.manifest.scripts?.setup\n if (!script) {\n // 无 setup 脚本 → 标记 done(避免每次检查)\n if (prevVersion === undefined || prevVersion !== currentVersion) {\n done.set(p.id, currentVersion)\n dirty = true\n }\n continue\n }\n\n // 版本未变且已执行过 → 跳过\n if (prevVersion !== undefined && prevVersion === currentVersion) {\n continue\n }\n\n // 信任门:gated 插件 deferred(不标记 done,下次 reload 重试)\n const verdict = evaluatePluginTrust(p)\n if (verdict.gated) {\n logger.info({ pluginId: p.id }, 'Setup hook deferred: plugin not yet trusted')\n continue\n }\n\n // 去重守卫:已在执行中则跳过,避免紧邻 reload 重复 spawn(RFC-081 review H4)\n if (setupInFlight.has(p.id)) continue\n setupInFlight.add(p.id)\n\n // 执行 setup(fire-and-forget)——RFC-085 M3:经 globalProcessRuntime 追踪,\n // 使 setup 进程可被 disable(killAll owner=plugin-hook) 与 app.stop 统一清退,非无踪 spawn。\n executeSetup(\n { pluginDir: p.dir, pluginId: p.id, workspaceDir },\n script,\n (_result: HookResult) => {\n setupInFlight.delete(p.id)\n // 无论成败都标记 done——失败需人工介入,不无限重试\n done.set(p.id, currentVersion)\n void persistState(settings, done)\n },\n globalProcessRuntime,\n )\n }\n\n if (dirty) {\n await persistState(settings, done)\n }\n}\n\nasync function persistState(\n settings: SettingsManager,\n done: Map<string, string | undefined>,\n): Promise<void> {\n const setupDone: SetupEntry[] = [...done].map(([id, version]) => ({ id, version }))\n await settings.persist({\n [SETUP_LIFECYCLE_KEY]: { setupDone },\n } as Record<string, unknown> as Parameters<typeof settings.persist>[0])\n}\n","/** Path guard: validate file paths are within allowed directories (security). */\nimport { realpathSync } from 'node:fs'\nimport { sep } from 'node:path'\n\n/**\n * 校验 `resolvedPath` 确实落在 `pluginDir` 内(装载层二次防御,防符号链接绕过\n * schema 层的字面量 `..` 检测)。base 用 `realpathSync` 解析符号链接后的真实路径做比较基准\n * (对齐仓库既有的归档解压路径穿越防御纪律)。\n */\nexport function isWithinPluginDir(resolvedPath: string, pluginDir: string): boolean {\n let realBase: string\n try {\n realBase = realpathSync(pluginDir)\n } catch {\n return false\n }\n let realTarget: string\n try {\n realTarget = realpathSync(resolvedPath)\n } catch {\n // 目标不存在——交由调用方的 readFileSync/readdirSync 走既有 catch 分支处理,\n // 此处只做路径边界判断。\n realTarget = resolvedPath\n }\n return realTarget === realBase || realTarget.startsWith(realBase + sep)\n}\n","/** Plugin i18n loader: per-plugin locale bundles, namespace isolation. */\nimport { readFileSync, readdirSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport {\n evaluatePluginTrust,\n MAX_I18N_ENTRIES_PER_PLUGIN,\n MAX_I18N_TRANSLATION_BYTES,\n MAX_I18N_LOCALE_FILES_PER_PLUGIN,\n MAX_I18N_FILE_BYTES,\n type DiscoveredPlugin,\n type PluginI18nEntry,\n} from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\n\nimport { isWithinPluginDir } from './path-guard'\n\nconst logger = createLogger('@x-otto/coding:plugin-i18n-loader')\n\n/** locale 文件名合法性校验(去掉 `.json` 后缀即 locale 标识)。 */\nconst LOCALE_FILENAME_RE = /^([a-zA-Z0-9_-]+)\\.json$/\n\n/**\n * 单条翻译文本超限时按**字节**截断并标记(独立评审 Low finding 修复:`String.slice()`\n * 按 UTF-16 code unit 切,与 `Buffer.byteLength` 的字节计量单位不一致——中文/日文等\n * 多字节字符场景下 `text.slice(0, N)` 保留的字符数远超 N 字节,截断上限形同虚设。\n * 用 `Buffer.from(text, 'utf8').subarray(0, N)` 做真正的字节级截断,再转回字符串\n * (`toString()` 在多字节字符边界截断处可能产生替换字符 U+FFFD,属于截断本就有损的\n * 可接受代价,同类问题 `context-sources.ts:62` 现存但不在本次修复范围,本函数独立实现\n * 正确行为,不复制既有瑕疵)。\n */\nfunction truncateTranslation(text: string): string {\n if (Buffer.byteLength(text, 'utf8') <= MAX_I18N_TRANSLATION_BYTES) return text\n const truncated = Buffer.from(text, 'utf8').subarray(0, MAX_I18N_TRANSLATION_BYTES).toString('utf8')\n return `${truncated}…`\n}\n\n/**\n * 读取 `pluginDir/i18nDir` 目录下全部合法 `<locale>.json` 文件,产出\n * `Map<id, Record<locale, text>>`(尚未加 pluginId 前缀,供上层与内联条目合并)。\n * fail-soft:目录不存在/不可读、路径穿越、单文件超限/解析失败均记录日志后跳过,\n * 不致整体装载失败(RFC-141 D4)。\n */\nfunction readLocaleDirEntries(\n pluginId: string,\n pluginDir: string,\n i18nDir: string,\n): Map<string, Record<string, string>> {\n const merged = new Map<string, Record<string, string>>()\n\n const resolvedDir = resolve(pluginDir, i18nDir)\n if (!isWithinPluginDir(resolvedDir, pluginDir)) {\n logger.warn({ pluginId, i18nDir }, 'i18nDir resolves outside plugin dir, skipped')\n return merged\n }\n\n let filenames: string[]\n try {\n filenames = readdirSync(resolvedDir)\n } catch {\n // 目录不存在/不可读——fail-soft,不视为错误(插件可能声明了 i18nDir 但尚未创建目录)。\n return merged\n }\n\n let localeFiles = filenames.filter((f) => LOCALE_FILENAME_RE.test(f)).sort()\n if (localeFiles.length > MAX_I18N_LOCALE_FILES_PER_PLUGIN) {\n logger.warn(\n { pluginId, i18nDir, declared: localeFiles.length, limit: MAX_I18N_LOCALE_FILES_PER_PLUGIN },\n 'plugin i18nDir declares more locale files than the per-plugin limit, truncated',\n )\n localeFiles = localeFiles.slice(0, MAX_I18N_LOCALE_FILES_PER_PLUGIN)\n }\n\n for (const filename of localeFiles) {\n const match = LOCALE_FILENAME_RE.exec(filename)\n const locale = match?.[1]\n if (!locale) continue\n\n const resolvedFile = resolve(resolvedDir, filename)\n if (!isWithinPluginDir(resolvedFile, pluginDir)) {\n logger.warn({ pluginId, filename }, 'i18nDir locale file resolves outside plugin dir, skipped')\n continue\n }\n\n let raw: string\n try {\n raw = readFileSync(resolvedFile, 'utf8')\n } catch (err) {\n logger.warn({ pluginId, filename, err: String(err) }, 'i18nDir locale file read failed, skipped')\n continue\n }\n if (Buffer.byteLength(raw, 'utf8') > MAX_I18N_FILE_BYTES) {\n logger.warn({ pluginId, filename, limit: MAX_I18N_FILE_BYTES }, 'i18nDir locale file exceeds byte limit, skipped')\n continue\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (err) {\n logger.warn({ pluginId, filename, err: String(err) }, 'i18nDir locale file is not valid JSON, skipped')\n continue\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n logger.warn({ pluginId, filename }, 'i18nDir locale file is not a flat object, skipped')\n continue\n }\n\n for (const [id, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof value !== 'string') continue\n const existing = merged.get(id) ?? {}\n existing[locale] = value\n merged.set(id, existing)\n }\n }\n\n return merged\n}\n\n/**\n * 收集全部已发现插件的 `contributes.i18n`(内联)+ `contributes.i18nDir`(外部目录)贡献,\n * 产出可直接 `register()` 的 `PluginI18nEntry[]`。\n *\n * 合并顺序(RFC-141 D3):先装配目录来源的全部条目,再用内联 `contributes.i18n` 数组条目\n * **逐 locale 覆盖**(同 id 若目录已声明部分 locale,内联条目只覆盖它显式声明的 locale,\n * 目录声明的其余 locale 保留)。\n */\nexport function collectPluginI18nEntries(plugins: readonly DiscoveredPlugin[]): PluginI18nEntry[] {\n const out: PluginI18nEntry[] = []\n for (const plugin of plugins) {\n if (evaluatePluginTrust(plugin).gated) continue\n\n const i18nDir = plugin.manifest.contributes?.i18nDir\n const inlineContributions = plugin.manifest.contributes?.i18n\n if (!i18nDir && !inlineContributions?.length) continue\n\n // 先装目录来源(Map<id, Record<locale, text>>)。\n const merged: Map<string, Record<string, string>> = i18nDir\n ? readLocaleDirEntries(plugin.id, plugin.dir, i18nDir)\n : new Map()\n\n // 再用内联条目逐 locale 覆盖(RFC-141 D3:内联 = 补丁/覆盖,目录 = 批量来源)。\n if (inlineContributions?.length) {\n for (const contribution of inlineContributions) {\n const existing = merged.get(contribution.id) ?? {}\n for (const [locale, text] of Object.entries(contribution.translations)) {\n // RFC-141 D3:覆盖目录已声明的 locale 是插件作者的合法用法(批量文件 + 少量\n // 补丁),非错误,故用 debug 而非 warn 记录(完成审核 P2 finding 修复)。\n if (locale in existing) {\n logger.debug(\n { pluginId: plugin.id, id: contribution.id, locale },\n 'i18n inline entry overrides locale dir value',\n )\n }\n existing[locale] = text\n }\n merged.set(contribution.id, existing)\n }\n }\n\n let ids = [...merged.keys()]\n if (ids.length > MAX_I18N_ENTRIES_PER_PLUGIN) {\n logger.warn(\n { pluginId: plugin.id, declared: ids.length, limit: MAX_I18N_ENTRIES_PER_PLUGIN },\n 'plugin declares more i18n entries than the per-plugin limit, truncated',\n )\n ids = ids.slice(0, MAX_I18N_ENTRIES_PER_PLUGIN)\n }\n\n for (const id of ids) {\n const rawTranslations = merged.get(id)\n if (!rawTranslations) continue\n const translations: Record<string, string> = {}\n for (const [locale, text] of Object.entries(rawTranslations)) {\n translations[locale] = truncateTranslation(text)\n }\n out.push({ pluginId: plugin.id, id, translations })\n }\n }\n return out\n}\n","import { createLogger } from '@x-otto/shared'\nimport { isClaudeCompatEnabled } from '@x-otto/env'\n\nimport { parseFrontmatterList, splitFrontmatter } from '@x-otto/shared'\nimport { discoverPlugins, type DiscoveredPlugin } from '@x-otto/plugin'\nimport { evaluatePluginTrust } from '@x-otto/plugin'\n// RFC-161 M1:mapClaudeAgentToolNames/evaluateClaudeTrust 随 claude-tool-map.ts/\n// claude-trust.ts 一并迁出到 @x-otto/claude-compat,直连新真源(非经 coding 内部相对路径)。\nimport { mapClaudeAgentToolNames, evaluateClaudeTrust } from '@x-otto/claude-compat'\n\nimport type { AgentProfile } from '@x-otto/prompt'\n\nconst logger = createLogger('@x-otto/coding:declarative-agents')\n\n/**\n * RFC-032 §3.0:把 `.claude/agents` 解析出的 profile 的工具名归一到 otto 命名空间。\n *\n * **安全核心**:`tools` 未声明(undefined)= 继承全部(与 Claude 语义一致,保持 undefined);\n * 但**声明了 tools**就必须经 §3.0 映射且**结果保持非空**——否则 resolveTools 会把空白名单\n * 当成\"全部工具\",让本应受限的只读 agent 越权(session-config-resolver.ts:163-167)。\n * `mapClaudeAgentToolNames` 对未知名保留原样正是为了维持非空。\n */\nfunction mapClaudeAgentProfile(profile: AgentProfile): AgentProfile {\n return {\n ...profile,\n defaultTools: profile.defaultTools?.length\n ? mapClaudeAgentToolNames(profile.defaultTools)\n : profile.defaultTools,\n disallowedTools: profile.disallowedTools?.length\n ? mapClaudeAgentToolNames(profile.disallowedTools)\n : profile.disallowedTools,\n }\n}\n\nexport function parseAgentDefinition(content: string, sourcePath: string): AgentProfile | null {\n const parsed = splitFrontmatter(content)\n if (!parsed) {\n logger.warn({ sourcePath }, 'Agent definition missing frontmatter, skipped')\n return null\n }\n\n const { fields, body } = parsed\n const name = fields.get('name')\n const description = fields.get('description')\n\n if (!name || !description || !body) {\n logger.warn({ sourcePath }, 'Agent definition requires name/description/body, skipped')\n return null\n }\n\n const tools = parseFrontmatterList(fields.get('tools'))\n const maxToolTurns = Number.parseInt(fields.get('maxtoolturns') ?? '', 10)\n const maxToolTurnExtensions = Number.parseInt(fields.get('maxtoolturnextensions') ?? '', 10)\n\n const promptMode = fields.get('prompt-mode') as AgentProfile['promptMode']\n const disallowedTools = parseFrontmatterList(fields.get('disallowed-tools'))\n const allowSubagents =\n fields.get('allow-subagents') === 'true'\n ? true\n : fields.get('allow-subagents') === 'false'\n ? false\n : undefined\n\n return {\n name,\n description,\n taskTypes: parseFrontmatterList(fields.get('tasktypes')) ?? [description],\n capabilities: parseFrontmatterList(fields.get('capabilities')) ?? [],\n systemPromptTemplate: body,\n promptMode: promptMode === 'append' || promptMode === 'replace' ? promptMode : undefined,\n defaultTools: tools,\n disallowedTools: disallowedTools?.length ? disallowedTools : undefined,\n preferredModelTier: fields.get('slot') ?? fields.get('tier'),\n defaultMaxToolTurns: Number.isInteger(maxToolTurns) ? maxToolTurns : undefined,\n defaultMaxToolTurnExtensions: Number.isInteger(maxToolTurnExtensions)\n ? maxToolTurnExtensions\n : undefined,\n allowSubagents,\n }\n}\n\n/**\n * 发现并解析声明式 agent。项目目录优先于全局(同名覆盖)。\n * 目录缺失/单文件解析失败均不致命。\n */\nexport async function discoverDeclarativeAgents(\n workspaceDir: string,\n options?: { globalDir?: string; plugins?: DiscoveredPlugin[] },\n): Promise<AgentProfile[]> {\n const { homedir } = await import('node:os')\n const { join } = await import('node:path')\n\n const globalDir = options?.globalDir ?? join(homedir(), '.otto', 'agents')\n const projectDir = join(workspaceDir, '.otto', 'agents')\n\n const plugins = options?.plugins ?? discoverPlugins({ cwd: workspaceDir, homedir: homedir() })\n const pluginLayers = plugins\n .filter((p) => p.manifest.contributes?.agents !== false)\n // RFC-081 O1:项目级未信任插件 agents 是攻击者可控 profile(默认全工具),门控跳过\n .filter((p) => !evaluatePluginTrust(p).gated)\n .map((p) => ({ dir: join(p.dir, 'agents'), claude: false }))\n\n const compat = isClaudeCompatEnabled()\n // RFC-081 O1:项目 `.claude/agents` 同属攻击者可控内容面(clone 带入),未信任则跳过\n // (对齐 `.claude/commands` 的 claude-trust 门控)。用户级 `~/.claude/agents` 用户自有,不门控。\n const claudeProjectAllowed = compat && !evaluateClaudeTrust(workspaceDir).gated\n const layers: { dir: string; claude: boolean }[] = [\n ...pluginLayers,\n ...(compat ? [{ dir: join(homedir(), '.claude', 'agents'), claude: true }] : []),\n { dir: globalDir, claude: false },\n ...(claudeProjectAllowed ? [{ dir: join(workspaceDir, '.claude', 'agents'), claude: true }] : []),\n { dir: projectDir, claude: false },\n ]\n\n const byName = new Map<string, AgentProfile>()\n for (const { dir, claude } of layers) {\n for (const profile of await loadAgentsFromDir(dir)) {\n byName.set(profile.name, claude ? mapClaudeAgentProfile(profile) : profile)\n }\n }\n\n const profiles = [...byName.values()]\n if (profiles.length > 0) {\n logger.info(\n 'Loaded %d declarative agent(s): %s',\n profiles.length,\n profiles.map((p) => p.name).join(', '),\n )\n }\n return profiles\n}\n\nasync function loadAgentsFromDir(dir: string): Promise<AgentProfile[]> {\n const { readFile, readdir } = await import('node:fs/promises')\n const { join } = await import('node:path')\n\n let entries: string[]\n try {\n entries = await readdir(dir)\n } catch {\n return []\n }\n\n const profiles: AgentProfile[] = []\n for (const entry of entries.filter((name) => name.endsWith('.md')).sort()) {\n const sourcePath = join(dir, entry)\n try {\n const content = await readFile(sourcePath, 'utf-8')\n const profile = parseAgentDefinition(content, sourcePath)\n if (profile) {\n profiles.push(profile)\n }\n } catch (error) {\n logger.warn({ sourcePath, err: error }, 'Failed to read agent definition, skipped')\n }\n }\n return profiles\n}\n","import { createLogger } from '@x-otto/shared'\n\nimport { parseFrontmatterList, splitFrontmatter } from '@x-otto/shared'\n\nimport type { OrchestrationPattern } from '@x-otto/orchestrator'\n\nconst logger = createLogger('@x-otto/coding:declarative-teams')\n\nconst PATTERNS: ReadonlySet<string> = new Set([\n 'handoff',\n 'sequential',\n 'parallel',\n 'hierarchical',\n 'router',\n])\n\nexport interface TeamRouteRule {\n keywords: string[]\n agentId: string\n}\n\nexport interface TeamDeclaration {\n name: string\n pattern: OrchestrationPattern\n /**\n * 成员标识(解析为 SwarmAgentDef 由 app 负责)。\n * M16e:成员可为 agent 名,或 `team:<子团队名>` 嵌套引用(原样保留,resolveTeam 判前缀递归)。\n */\n agentNames: string[]\n pipeline?: string[]\n supervisorId?: string\n entryAgentId?: string\n routerStrategy?: string\n /** M16e:声明式路由规则(router:rule 时接入 swarm RouterConfig.rules)。 */\n rules?: TeamRouteRule[]\n maxHandoffDepth?: number\n}\n\nexport const TEAM_MEMBER_PREFIX = 'team:'\n\n/**\n * 解析声明式路由规则。语法:`kw,kw→agentId | kw→agentId`(`|` 分隔规则,`,` 分隔关键词,\n * `→` 或 `->` 分隔关键词组与目标 agentId)。非法片段跳过。空结果返回 undefined。\n */\nexport function parseTeamRules(raw: string | undefined): TeamRouteRule[] | undefined {\n if (!raw) {\n return undefined\n }\n const rules: TeamRouteRule[] = []\n for (const segment of raw.split('|')) {\n const [left, right] = segment.split(/→|->/)\n if (left === undefined || right === undefined) {\n continue\n }\n const keywords = left\n .split(',')\n .map((k) => k.trim().toLowerCase())\n .filter((k) => k !== '')\n const agentId = right.trim()\n if (keywords.length > 0 && agentId !== '') {\n rules.push({ keywords, agentId })\n }\n }\n return rules.length > 0 ? rules : undefined\n}\n\nexport function parseTeamDefinition(content: string, sourcePath: string): TeamDeclaration | null {\n const parsed = splitFrontmatter(content)\n if (!parsed) {\n logger.warn({ sourcePath }, 'Team definition missing frontmatter, skipped')\n return null\n }\n\n const { fields } = parsed\n const name = fields.get('name')\n const pattern = fields.get('pattern')\n const agentNames = parseFrontmatterList(fields.get('agents'))\n\n if (!name || !pattern || !PATTERNS.has(pattern) || !agentNames || agentNames.length === 0) {\n logger.warn(\n { sourcePath },\n 'Team definition requires name, valid pattern and non-empty agents, skipped',\n )\n return null\n }\n\n const maxHandoffDepth = Number.parseInt(fields.get('maxhandoffdepth') ?? '', 10)\n\n return {\n name,\n pattern: pattern as OrchestrationPattern,\n agentNames,\n pipeline: parseFrontmatterList(fields.get('pipeline')),\n supervisorId: fields.get('supervisor'),\n entryAgentId: fields.get('entry'),\n routerStrategy: fields.get('router'),\n rules: parseTeamRules(fields.get('rules')),\n maxHandoffDepth: Number.isInteger(maxHandoffDepth) ? maxHandoffDepth : undefined,\n }\n}\n\n/**\n * 发现并解析声明式团队。项目目录优先于全局(同名覆盖)。\n * 目录缺失/单文件解析失败均不致命。\n */\nexport async function discoverDeclarativeTeams(\n workspaceDir: string,\n options?: { globalDir?: string },\n): Promise<TeamDeclaration[]> {\n const { homedir } = await import('node:os')\n const { join } = await import('node:path')\n\n const globalDir = options?.globalDir ?? join(homedir(), '.otto', 'teams')\n const projectDir = join(workspaceDir, '.otto', 'teams')\n\n const byName = new Map<string, TeamDeclaration>()\n for (const dir of [globalDir, projectDir]) {\n for (const team of await loadTeamsFromDir(dir)) {\n byName.set(team.name, team)\n }\n }\n\n const teams = [...byName.values()]\n if (teams.length > 0) {\n logger.info(\n 'Loaded %d declarative team(s): %s',\n teams.length,\n teams.map((t) => t.name).join(', '),\n )\n }\n return teams\n}\n\nasync function loadTeamsFromDir(dir: string): Promise<TeamDeclaration[]> {\n const { readFile, readdir } = await import('node:fs/promises')\n const { join } = await import('node:path')\n\n let entries: string[]\n try {\n entries = await readdir(dir)\n } catch {\n return []\n }\n\n const teams: TeamDeclaration[] = []\n for (const entry of entries.filter((name) => name.endsWith('.md')).sort()) {\n const sourcePath = join(dir, entry)\n try {\n const team = parseTeamDefinition(await readFile(sourcePath, 'utf-8'), sourcePath)\n if (team) {\n teams.push(team)\n }\n } catch (error) {\n logger.warn({ sourcePath, err: error }, 'Failed to read team definition, skipped')\n }\n }\n return teams\n}\n","import { join } from 'node:path'\nimport { evaluatePluginTrust } from '@x-otto/plugin'\nimport type { DiscoveredPlugin, PluginI18nRegistry } from '@x-otto/plugin'\nimport { runPendingSetups } from '../plugin/plugin-lifecycle'\nimport { collectPluginI18nEntries } from '../plugin/plugin-i18n-loader'\nimport { discoverDeclarativeAgents } from '../agent/declarative-agents'\nimport { discoverDeclarativeTeams } from '../agent/declarative-teams'\nimport type { TeamDeclaration } from '../agent/declarative-teams'\nimport type { McpController } from './mcp-controller'\nimport type { PluginModuleManager } from './plugin-module-manager'\nimport type { PluginProviderManager } from './plugin-provider-manager'\nimport type { ModelCatalogManager } from './model-catalog-manager'\nimport type { ToolRegistry } from '@x-otto/tools'\nimport type { SkillRegistry } from '@x-otto/skill'\nimport type { SettingsManager } from '@x-otto/setting'\nimport type { AgentProfile } from '@x-otto/prompt'\n\nexport interface CapabilityReloadResult {\n mcpTools: number\n models: number\n agents: number\n teamCount: number\n agentProfiles: AgentProfile[]\n teamDeclarations: TeamDeclaration[]\n /**\n * RFC-125 D5:因 `pendingCapabilities.includes('provider')` 被跳过装载的插件 id 列表\n * (已通过布尔信任门,但高危能力尚未被 `grantPluginCapabilities` 显式授予)。此前该信号\n * 只留一条结构化 `logger.warn`,从未传递到任何用户可见界面——`/model` 等消费点据此呈现\n * \"N 个 provider 待授权\"提示,直达对应插件的授权入口(见 RFC-125 §0.4 附带发现)。\n */\n pendingProviderCapabilities: string[]\n /** RFC-138 D6:本轮热重载装配的插件 i18n 字符串条目总数(第六轴)。 */\n i18nEntries: number\n}\n\n/**\n * 五轴能力热重载编排器(RFC-107 D4)。\n *\n * 承载原 App.reloadCapabilities() 的编排逻辑——skill/agent/plugin/mcp/model 五轴\n * 按依赖顺序重载(settings.load → mcp → skills → models → agents → teams → plugin modules → plugin providers),\n * 保持幂等语义不变。CapabilityReloader 有真实语义(依赖顺序 + 幂等重载),不是 RFC-077 §4 规则 2 禁止的\"纯转发 facade\"。\n *\n * 边界:不持有 App 的可变状态(declarativeAgentProfiles/declarativeTeams),\n * ReloadResult 返回最新值由 App 自行赋值——只做编排,不反向吸收其他 App 职责。\n */\nexport class CapabilityReloader {\n constructor(\n private readonly settings: SettingsManager,\n private readonly mcp: McpController,\n private readonly skills: SkillRegistry | null,\n private readonly pluginModules: PluginModuleManager,\n private readonly pluginProviders: PluginProviderManager,\n private readonly tools: ToolRegistry,\n private readonly modelCatalog: ModelCatalogManager,\n private readonly workspaceDir: string,\n private readonly activePlugins: () => DiscoveredPlugin[],\n /** RFC-138 D6:第六轴——插件 i18n 字符串注册表(幂等重载)。 */\n private readonly i18n: PluginI18nRegistry,\n ) {}\n\n /**\n * 热重载五轴能力——不重启进程认到 delegate 新加的 MCP / provider / 声明式 agent。\n * MCP 走 McpController.reload(断重连+反应式重投影工具);模型走 ModelCatalogManager.refresh;\n * 声明式 agent/team 重新扫工作区。返回各面摘要 + 最新 agent 配置/team 声明(供 App 赋值)。\n */\n async reload(): Promise<CapabilityReloadResult> {\n await this.settings.load()\n // RFC-080:执行所有待处理的插件 setup 钩子(非阻塞,fire-and-forget)\n void runPendingSetups(this.activePlugins(), this.settings, this.workspaceDir)\n\n await this.mcp.reload()\n // 刷新 skill registry plugin 目录(新安装的 plugin 可能贡献 skills/ 目录)\n if (this.skills) {\n this.skills.updatePluginDirs(\n this.activePlugins()\n .filter((p) => p.manifest.contributes?.skills !== false)\n // RFC-081 O1:项目级未信任插件 skills 门控(与构造期 buildAgentServices 对称)\n .filter((p) => !evaluatePluginTrust(p).gated)\n .map((p) => ({ id: p.id, dir: join(p.dir, 'skills') })),\n )\n await this.skills.discover()\n }\n // RFC-138 D6:第六轴——插件 i18n 字符串(纯数据,无外部依赖,紧邻 skills 之后,\n // 位置选择只为保持\"纯数据类贡献点\"聚在一起的可读性,不存在真实顺序依赖约束)。\n const i18nEntries = collectPluginI18nEntries(this.activePlugins())\n this.i18n.register(i18nEntries)\n const modelResults = await this.modelCatalog.refresh().catch(() => [])\n const models = modelResults.reduce((n, r) => n + (r.added ?? 0), 0)\n const agentProfiles = await discoverDeclarativeAgents(this.workspaceDir, {\n plugins: this.activePlugins(),\n })\n const teams = await discoverDeclarativeTeams(this.workspaceDir)\n const appendedAgents = await this.pluginModules.reload(\n new Set(agentProfiles.map((p) => p.name)),\n )\n agentProfiles.push(...appendedAgents)\n // RFC-105 M105-2-03b:插件声明式 provider 幂等 reload(先卸载上一轮,再装载当前 active)。\n const providerReload = await this.pluginProviders.reload()\n const mcpTools = this.tools\n .getAvailable('full')\n .filter((t) => t.name.startsWith('mcp__')).length\n return {\n mcpTools,\n models,\n agents: agentProfiles.length,\n teamCount: teams.length,\n agentProfiles,\n teamDeclarations: teams,\n pendingProviderCapabilities: providerReload.pendingCapabilityPluginIds,\n i18nEntries: i18nEntries.length,\n }\n }\n}\n","/** Panel backend controller: spawn/healthcheck/restart panel backend processes. */\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport { evaluatePluginTrust } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\nimport type { McpManager } from '@x-otto/mcp'\nimport type { McpController, EffectiveMcpServer } from '../app/mcp-controller'\n\nconst logger = createLogger('@x-otto/coding:panel-backend-controller')\n\nexport type PanelBackendStatus = 'connecting' | 'ready' | 'crashed' | 'disabled'\n\nexport interface ResolveServerResult {\n ok: boolean\n serverName?: string\n reason?: string\n}\n\ntype StateListener = (params: Record<string, unknown>) => void\ntype StatusListener = (status: PanelBackendStatus) => void\n\n/**\n * In-process backend handler(RFC-349 D1):插件不需要 MCP server 子进程时,\n * 宿主可直接在 App 主进程注册 backend methods。面板 `usePanelBackend().call()` 仍\n * 走同一条 `PanelBackendController.call()` 路径——区别只在于 call 被路由到\n * in-process handler 还是 MCP server 的 wire call。\n *\n * 用途:schedule 插件的 SchedulerService 依赖 `AgentJobService`/`sessionRef`/\n * `notifySubscriber`——这些只能存活在 App 主进程,无法搬到 MCP server 子进程。\n * 故 schedule 的 backend methods 注册为 in-process handler,不建 MCP server。\n */\nexport type InProcessBackendHandler = (\n method: string,\n params: Record<string, unknown>,\n) => Promise<unknown>\n\nexport interface PanelBackendControllerDeps {\n mcpController: McpController\n getActivePlugins: () => readonly DiscoveredPlugin[]\n}\n\nexport class PanelBackendController {\n private stateListeners = new Map<string, Set<StateListener>>()\n private statusListeners = new Map<string, Set<StatusListener>>()\n /**\n * RFC-349 D1:in-process backend handlers——`pluginId → handler`。\n * 注册后 `call()` 优先走 in-process,不经过 MCP server。\n * `getStatus()` 对注册了 in-process handler 的插件恒返回 `'ready'`(无需子进程连接)。\n */\n private readonly inProcessHandlers = new Map<string, InProcessBackendHandler>()\n /**\n * 记录当前已订阅事件的 `McpManager` 实例引用(而非布尔位)——真实事故根因:\n * `McpController.reload()`(信任/授权插件高危能力后的热重载路径,见\n * `promptPluginTrust`→`reloadCapabilities`→`mcp.reload()`)会把 `_manager` 置 null 后\n * `new McpManager(...)` 重建一个全新实例。此前用布尔位 `subscribedNotifications` 把关,\n * 只在\"全进程首次有面板消费方\"时订阅一次,订阅的是当时那个 manager 实例——reload 换实例\n * 后,旧监听器绑在已经死掉的旧 manager 上,永远收不到新 manager 的 `server.connected`\n * 事件,导致已挂载面板在用户完成\"信任/授权\"操作、`getStatus()` 权威判定其实已经从\n * `disabled` 变为 `ready` 后,UI 因收不到 `notifyStatusChange` 通知而继续显示旧的\n * \"面板后端不可用\"(真实用户反馈:授权后重开详情页才刷新,不授权当场生效)。\n * 现改为记录已订阅的 manager 引用,每次 `ensureNotificationSubscription` 都比对当前\n * `mcpController.manager` 是否已变化,变化则对新实例重新订阅(旧实例的监听器随旧实例一起\n * 被 GC,不需要显式 `off`——`TypedEventEmitter` 无跨实例状态泄漏)。\n */\n private subscribedManager: McpManager | null = null\n\n constructor(private readonly deps: PanelBackendControllerDeps) {}\n\n /**\n * RFC-349 D1:注册 in-process backend handler。\n * 注册后该插件的 `call()` 直接走 handler,不经 MCP server。\n * 仍要求 manifest 声明 `panel.backend` capability(信任门不绕过)。\n */\n registerInProcessBackend(pluginId: string, handler: InProcessBackendHandler): void {\n this.inProcessHandlers.set(pluginId, handler)\n // 通知已挂载的面板:状态从 disabled → ready\n const listeners = this.statusListeners.get(pluginId)\n if (listeners) {\n for (const cb of listeners) cb('ready')\n }\n }\n\n /** RFC-349 D1:注销 in-process backend handler。 */\n unregisterInProcessBackend(pluginId: string): void {\n this.inProcessHandlers.delete(pluginId)\n }\n\n /**\n * 给定插件 id,解析出它可用于 backend 调用的 MCP server name。\n * fail-closed:capability 未声明/未授予 → `ok:false`;未找到匹配 server → `ok:false`。\n * RFC-349 D1:已注册 in-process handler 的插件跳过 MCP server 解析。\n */\n resolveServerForPlugin(pluginId: string): ResolveServerResult {\n // RFC-349 D1:in-process handler 优先——不需要 MCP server。\n if (this.inProcessHandlers.has(pluginId)) {\n return { ok: true }\n }\n\n const plugin = this.deps.getActivePlugins().find((p) => p.id === pluginId)\n if (!plugin) {\n return { ok: false, reason: `plugin \"${pluginId}\" not found or not active` }\n }\n\n if (!plugin.manifest.capabilities?.includes('panel.backend')) {\n return { ok: false, reason: `plugin \"${pluginId}\" does not declare capabilities:[\"panel.backend\"]` }\n }\n const verdict = evaluatePluginTrust(plugin)\n if (verdict.pendingCapabilities.includes('panel.backend')) {\n return { ok: false, reason: `plugin \"${pluginId}\" capability \"panel.backend\" declared but not yet granted` }\n }\n if (verdict.gated) {\n return { ok: false, reason: `plugin \"${pluginId}\" is untrusted` }\n }\n\n const servers = this.deps.mcpController.listEffectiveServers()\n const matches = Object.entries(servers).filter(([, s]) => s.pluginId === pluginId)\n if (matches.length === 0) {\n return { ok: false, reason: `plugin \"${pluginId}\" has no associated MCP server` }\n }\n if (matches.length > 1) {\n logger.warn(\n { pluginId, serverNames: matches.map(([n]) => n) },\n 'plugin contributes multiple MCP servers; panel backend uses the first match (multi-server routing not yet supported)',\n )\n }\n return { ok: true, serverName: matches[0]![0] }\n }\n\n /**\n * 面板发起的 `backend/*` 请求转发到对应插件的 MCP server 连接。\n *\n * `operationId`(可选):若该 method 支持取消(D6),调用方生成并随请求携带一个客户端侧\n * 操作 id(约定字段名 `__operationId`,`definePanelBackend` SDK 侧剥离后再校验业务\n * params,见 `panel-backend-sdk.ts` 文件头\"操作 id 的归属\"说明)——**必须由客户端先生成\n * 再发起请求**,而非等服务端返回后才知道 id,否则调用尚未返回时无法发起 cancel(真实\n * 时序约束,非任意设计选择)。\n */\n async call(\n pluginId: string,\n method: string,\n params: Record<string, unknown>,\n opts?: { operationId?: string; signal?: AbortSignal },\n ): Promise<unknown> {\n // RFC-349 D1:in-process handler 优先。\n const inProcess = this.inProcessHandlers.get(pluginId)\n if (inProcess) {\n return inProcess(method, params)\n }\n\n const resolved = this.resolveServerForPlugin(pluginId)\n if (!resolved.ok || !resolved.serverName) {\n throw new Error(resolved.reason ?? `cannot resolve backend server for plugin \"${pluginId}\"`)\n }\n const client = this.deps.mcpController.getMcpClient(resolved.serverName)\n if (!client) {\n throw new Error(`MCP server \"${resolved.serverName}\" for plugin \"${pluginId}\" is not connected`)\n }\n const wireParams = opts?.operationId ? { ...params, __operationId: opts.operationId } : params\n return client.callRaw(method, wireParams, opts?.signal)\n }\n\n /** 面板发起的 `backend/cancel(operationId)`——转发到对应插件的 MCP server。 */\n async cancel(pluginId: string, operationId: string): Promise<void> {\n // RFC-349 D1:in-process handler 不支持取消(schedule 操作都是即时同步的)。\n if (this.inProcessHandlers.has(pluginId)) return\n\n const resolved = this.resolveServerForPlugin(pluginId)\n if (!resolved.ok || !resolved.serverName) return\n const client = this.deps.mcpController.getMcpClient(resolved.serverName)\n if (!client) return\n await client.callRaw('backend/cancel', { id: operationId }).catch(() => undefined)\n }\n\n /**\n * 订阅该插件的 `backend/state` 推送(全量快照)。惰性订阅底层 `McpManager` 的\n * `server.notification` 事件(首次调用时才订阅,避免无消费者时空转)。\n */\n subscribeState(pluginId: string, cb: StateListener): () => void {\n this.ensureNotificationSubscription()\n if (!this.stateListeners.has(pluginId)) {\n this.stateListeners.set(pluginId, new Set())\n }\n this.stateListeners.get(pluginId)!.add(cb)\n return () => {\n this.stateListeners.get(pluginId)?.delete(cb)\n }\n }\n\n /** 订阅该插件 backend 连接状态变化(四态,见文件头 getStatus 判定说明)。 */\n onStatusChange(pluginId: string, cb: StatusListener): () => void {\n this.ensureNotificationSubscription()\n if (!this.statusListeners.has(pluginId)) {\n this.statusListeners.set(pluginId, new Set())\n }\n this.statusListeners.get(pluginId)!.add(cb)\n return () => {\n this.statusListeners.get(pluginId)?.delete(cb)\n }\n }\n\n /** 查询该插件当前的 backend 连接状态(四态,终局判定见文件头注释)。 */\n getStatus(pluginId: string): PanelBackendStatus {\n // RFC-349 D1:in-process handler 恒 ready(无子进程连接态)。\n if (this.inProcessHandlers.has(pluginId)) return 'ready'\n\n const resolved = this.resolveServerForPlugin(pluginId)\n if (!resolved.ok || !resolved.serverName) {\n return 'disabled'\n }\n const servers = this.deps.mcpController.listEffectiveServers()\n const effective: EffectiveMcpServer | undefined = servers[resolved.serverName]\n if (effective?.disabled) {\n return 'disabled'\n }\n const client = this.deps.mcpController.getMcpClient(resolved.serverName)\n if (!client) {\n return 'crashed'\n }\n const status = client.getStatus()\n if (status === 'ready') return 'ready'\n if (status === 'connecting' || status === 'reconnecting') return 'connecting'\n return 'crashed'\n }\n\n /**\n * `App.reloadCapabilities()` 完成后调用(RFC-165 D8 补充修复)——`promptPluginTrust`\n * 授权 `panel.backend` 能力走的正是这条热重载路径,但触发时机在**面板已挂载期间**\n * (用户在 disabled 态详情页里点击\"信任\",面板组件本身不会因此卸载重挂载)。仅让\n * `ensureNotificationSubscription` 在下次挂载时对新 manager 重新订阅还不够——已挂载面板\n * 绑定的监听器停留在旧 manager 上,reload 期间不会有任何契机重新调用\n * `ensureNotificationSubscription`。本方法主动完成两件事:① 重新对齐订阅到当前\n * manager 实例;② 对全部已注册 `statusListeners` 的插件重新查询 `getStatus()` 并推送,\n * 让已挂载面板的 `usePanelBackend()` 状态即时刷新(而不必等待用户手动关闭重开详情页)。\n */\n notifyReload(): void {\n this.ensureNotificationSubscription()\n for (const [pluginId, listeners] of this.statusListeners) {\n if (listeners.size === 0) continue\n const status = this.getStatus(pluginId)\n for (const cb of listeners) cb(status)\n }\n }\n\n /**\n * 惰性订阅 `McpManager` 的 `server.notification`/`server.connected`/`server.disconnected`\n * 事件——首次有面板消费方(subscribeState/onStatusChange)时才订阅。每次调用都比对当前\n * `mcpController.manager` 与已订阅实例是否相同(见 `subscribedManager` 字段头注释:\n * `McpController.reload()` 会重建全新 manager 实例),实例变化则对新实例重新订阅,\n * 保证 reload 后(如信任/授权插件高危能力触发的热重载)状态变化仍能正确通知到已挂载面板。\n */\n private ensureNotificationSubscription(): void {\n const manager = this.deps.mcpController.manager\n if (this.subscribedManager === manager) return\n this.subscribedManager = manager\n if (!manager) return\n\n manager.on('server.notification', ({ name, method, params }) => {\n if (method !== 'backend/state') return\n const pluginId = this.pluginIdForServer(name)\n if (!pluginId) return\n const listeners = this.stateListeners.get(pluginId)\n if (!listeners || listeners.size === 0) return\n for (const cb of listeners) cb(params ?? {})\n })\n\n const notifyStatusChange = (serverName: string): void => {\n const pluginId = this.pluginIdForServer(serverName)\n if (!pluginId) return\n const listeners = this.statusListeners.get(pluginId)\n if (!listeners || listeners.size === 0) return\n const status = this.getStatus(pluginId)\n for (const cb of listeners) cb(status)\n }\n manager.on('server.connected', ({ name }) => notifyStatusChange(name))\n manager.on('server.disconnected', ({ name }) => notifyStatusChange(name))\n }\n\n private pluginIdForServer(serverName: string): string | undefined {\n return this.deps.mcpController.listEffectiveServers()[serverName]?.pluginId\n }\n}\n","/** Plugin log writer: structured log output per plugin, rotation/buffer. */\nimport { existsSync, mkdirSync, appendFileSync, statSync, readFileSync, writeFileSync } from 'node:fs'\nimport { readdir, stat, unlink } from 'node:fs/promises'\nimport { resolve, join } from 'node:path'\n\nimport { OTTO_HOME, isShadowModeEnabled } from '@x-otto/env'\nimport { createLogger } from '@x-otto/shared'\nimport { ID_RE } from '@x-otto/plugin'\n\nconst logger = createLogger('@x-otto/coding:plugin-log-writer')\n\n/** 单文件大小上限(5MB);超限截断保留最后 50%(诊断价值更高的近期数据)。 */\nconst MAX_LOG_FILE_BYTES = 5 * 1024 * 1024\n\n/** 过期清理 TTL(7 天)——比照 `sweepStaleProjectOverflowLogs` 的既有判断依据。 */\nexport const PLUGIN_LOG_TTL_MS = 7 * 24 * 60 * 60_000\n\nexport type PluginLogLevel = 'info' | 'warn' | 'error'\n\nexport interface PluginLogEntry {\n ts: number\n level: PluginLogLevel\n pluginId: string\n msg: string\n}\n\n/** 导出供 `read-plugin-log-tail.ts`(D4)复用同一目录约定,避免两处各写一遍路径规则。 */\nexport function pluginLogsDir(): string {\n return resolve(OTTO_HOME, 'plugin-logs')\n}\n\n/** 规则 4:路径构造前必须先过 kebab-case 校验,防路径穿越。非法 id 返回 null(fail-soft)。\n * 导出供 `read-plugin-log-tail.ts`(D4)复用同一份校验+路径构造逻辑。 */\nexport function resolvePluginLogPath(pluginId: string): string | null {\n if (!ID_RE.test(pluginId)) {\n logger.warn({ pluginId }, 'plugin id failed kebab-case validation, skipping log path resolution')\n return null\n }\n return join(pluginLogsDir(), `${pluginId}.jsonl`)\n}\n\n/** 单文件超限时截断保留最后 50%(有损策略,诊断用途非归档需求)。 */\nfunction truncateIfOversized(path: string): void {\n let size: number\n try {\n size = statSync(path).size\n } catch {\n return // 文件不存在,无需截断\n }\n if (size <= MAX_LOG_FILE_BYTES) return\n\n try {\n const content = readFileSync(path, 'utf8')\n const keepFrom = Math.floor(content.length / 2)\n // 从字符中点找下一个换行符,避免截断到半行 JSON。\n const nl = content.indexOf('\\n', keepFrom)\n const truncated = nl >= 0 ? content.slice(nl + 1) : content.slice(keepFrom)\n writeFileSync(path, truncated)\n } catch (error) {\n // fail-soft:截断失败不影响后续写入尝试(append 会继续追加到超限文件,下次再试截断)。\n logger.warn({ path, err: String(error) }, 'plugin log truncation failed (fail-soft)')\n }\n}\n\n/**\n * 追加一条插件日志到 per-plugin `.jsonl` 文件。fail-soft:任何异常(目录创建失败/磁盘满/权限\n * 问题)均被吞掉,不向调用方传播——插件日志基础设施故障不得污染主流程或插件代码本身。\n */\nexport function appendPluginLog(pluginId: string, level: PluginLogLevel, msg: string): void {\n // RFC-345 影子模式:otto 自有状态零磁盘写入——插件 jsonl 属于自有日志,出口单点短路\n // (调用方 plugin-module-manager.ts 不改,对齐 R1 模式)。\n if (isShadowModeEnabled()) return\n try {\n const path = resolvePluginLogPath(pluginId)\n if (!path) return\n\n const dir = pluginLogsDir()\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n\n truncateIfOversized(path)\n\n const entry: PluginLogEntry = { ts: Date.now(), level, pluginId, msg }\n appendFileSync(path, `${JSON.stringify(entry)}\\n`)\n } catch (error) {\n // fail-soft(规则 3):写入失败降级为仅全局 pino(调用方 plugin-module-manager.ts 已经打了\n // 一份到全局 logger,本函数失败不需要重复报错,只需静默吞掉避免异常上冒)。\n logger.warn({ pluginId, err: String(error) }, 'plugin log append failed (fail-soft)')\n }\n}\n\n/**\n * 清理过期插件日志文件(mtime 早于 now-ttlMs 的 `plugin-logs/*.jsonl`)。\n * best-effort:单文件出错跳过、目录不可读返回 0;返回已删除数量。\n * 对齐 `@x-otto/shared` 的 `sweepStaleLogsIn` mtime 判定模式,供既有清理生命周期钩子调用。\n */\nexport async function sweepStalePluginLogs(ttlMs: number, now: number): Promise<number> {\n const dir = pluginLogsDir()\n let entries: string[]\n try {\n entries = await readdir(dir)\n } catch {\n return 0\n }\n const candidates = entries.filter((name) => name.endsWith('.jsonl'))\n let removed = 0\n await Promise.all(\n candidates.map(async (name) => {\n const full = join(dir, name)\n try {\n const info = await stat(full)\n if (now - info.mtimeMs > ttlMs) {\n await unlink(full)\n removed++\n }\n } catch {\n // best-effort:单文件失败不影响其余文件清理。\n }\n }),\n )\n return removed\n}\n","import { defineHook, type HookSpec, type HookTiming } from '@x-otto/hooks'\nimport type {\n AgentContribution,\n CommandContribution,\n SkillContribution,\n PluginProviderFactory,\n PluginTool,\n PluginToolContext,\n PluginMonitor,\n MonitorEvent,\n} from '@x-otto/plugin'\nimport type { PluginHooks } from '@x-otto/plugin'\nimport type { AgentProfile } from '@x-otto/prompt'\nimport type { SkillDefinition, SkillSource } from '@x-otto/skill'\nimport type { ProviderFactory } from '@x-otto/ai'\nimport type { ProviderStream } from '@x-otto/provider'\nimport type { AgentTool, ToolResult, StreamEvent } from '@x-otto/interchange'\nimport { createLogger, CodedError, ProviderError } from '@x-otto/shared'\nimport type { FileCommand } from '@x-otto/claude-compat'\nimport { appendPluginLog } from './plugin-log-writer'\n\nconst logger = createLogger('@x-otto/coding:plugin-tool-wiring')\n\n/**\n * module-wiring.ts —— PluginModule → 引擎接线(RFC-082 M082-04-03)。\n *\n * 把 `definePlugin` 产出的 `PluginHooks`(SDK 形态)适配为引擎 `HookSpec[]`,注册进 `HookRegistry`。\n * 事件映射(对齐 `claude-hooks.ts` + `hook-nodes.ts`):\n * preToolUse→`tool.execute.before`(拦截器) / postToolUse→`tool.execute.after` /\n * userPromptSubmit→`chat.message.before` / sessionStart→`session.created` / stop→`stream.end`。\n *\n * 安全(RFC-082 R10,safe-by-construction):\n * - `preToolUse` 只能 **deny/ask(升级限制)**;插件返回 `allow`/void → **不动 output**——\n * 故插件 hook **永不能授权**,只能限制。配合 registry 的 sticky-deny,插件的 deny 一旦设定即不可翻案。\n * - 其余 hook 是 fire-and-forget 观察器,零权限影响。\n */\n\n/** 插件 hook 名(`unregister` key,复用 source=pluginId 纹理,RFC-044 R6)。 */\nexport function pluginHookName(pluginId: string, event: string): string {\n return `plugin:${pluginId}:${event}`\n}\n\n/**\n * RFC-153 D1/D3:从 `pluginHookName` 产出的 hook 名反解析出 pluginId——`HookRegistry` 的\n * `onHookTimeout` 回调只携带领域无关的 `hookName` 字符串(规则 7:`packages/hooks` 零认知\n * 插件概念),`packages/coding` 层需要这个反解析才能把超时事件路由到 `PluginHealthTracker`。\n * 非本仓插件 hook(如 `hook-state-cleanup` 等引擎内置 hook,名称不含 `plugin:` 前缀)返回\n * null——调用方应据此跳过(不是所有超时 hook 都来自插件)。\n */\nexport function parsePluginIdFromHookName(hookName: string): string | null {\n const parts = hookName.split(':')\n if (parts.length < 3 || parts[0] !== 'plugin') return null\n return parts[1] || null\n}\n\n/** 从 AgentMessage 安全提取文本(content 可能是 string 或 block 数组)。 */\nfunction messageText(message: unknown): string {\n const content = (message as { content?: unknown })?.content\n if (typeof content === 'string') return content\n if (Array.isArray(content)) {\n return content\n .map((b) =>\n b && typeof b === 'object' && 'text' in b ? String((b as { text: unknown }).text) : '',\n )\n .join('')\n }\n return ''\n}\n\n/**\n * 把 PluginHooks 适配为引擎 HookSpec[]。纯函数(无副作用、不碰 registry)——便于测试与组合。\n * 仅为实际声明的 hook 生成 spec。\n */\n/**\n * `PluginHooks` 字段 → 引擎 `HookTiming` 的编译期穷举映射表(终局 review 2026-07-20 O-1\n * 修复)。此前 `PluginHooks`(`packages/plugin/src/sdk.ts`,16 字段)与 `HookTiming`\n * (`packages/hook-contracts/src/timings.ts`,25 值)之间的对应关系只靠下方 `defineHook`\n * 调用点里手写的字符串字面量维系,无编译期交叉校验——新增/重命名 `PluginHooks` 字段时\n * 若漏改对应的 `timing` 字面量,`tsc` 不会报错,只有运行时才会发现 hook 从未触发。\n *\n * `satisfies Record<keyof Required<PluginHooks>, HookTiming>` 强制本表与 `PluginHooks`\n * 接口字段一一对应——新增第 17 个字段时若漏写本表项,`tsc` 直接报错(同 `AXIS_MERGE_TABLE`\n * 的编译期穷举门模式,`packages/plugin/src/contributions.ts`)。下方 `pluginHooksToSpecs`\n * 的每个 `defineHook` 调用改为引用 `PLUGIN_HOOK_TIMING_MAP[field]`,不再手写字面量。\n */\nconst PLUGIN_HOOK_TIMING_MAP = {\n preToolUse: 'tool.execute.before',\n postToolUse: 'tool.execute.after',\n userPromptSubmit: 'chat.message.before',\n sessionStart: 'session.created',\n stop: 'stream.end',\n sessionRestored: 'session.restored',\n sessionDeleted: 'session.deleted',\n sessionIdle: 'session.idle',\n sessionError: 'session.error',\n streamStart: 'stream.start',\n compactionPending: 'compaction.before',\n compactionOccurred: 'compaction.after',\n taskCreated: 'task.created',\n taskStarted: 'task.started',\n taskCompleted: 'task.completed',\n taskFailed: 'task.failed',\n} satisfies Record<keyof Required<PluginHooks>, HookTiming>\n\nexport function pluginHooksToSpecs(pluginId: string, hooks: PluginHooks): HookSpec[] {\n const specs: HookSpec[] = []\n\n if (hooks.preToolUse) {\n const fn = hooks.preToolUse\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'preToolUse'),\n timing: PLUGIN_HOOK_TIMING_MAP.preToolUse,\n handle: async (input, output) => {\n const decision = await fn({ toolName: input.toolName, input: input.args })\n if (!decision) return // void = 无意见\n if (decision.decision === 'deny') {\n output.decision = 'deny'\n output.cancelled = true\n output.cancelReason = decision.reason ?? `denied by plugin ${pluginId}`\n } else if (decision.decision === 'ask') {\n output.decision = 'ask'\n output.decisionReason = decision.reason ?? `requires approval (plugin ${pluginId})`\n }\n // 'allow' → 不动 output(R10:不能授权,只能限制)\n },\n }),\n )\n }\n\n if (hooks.postToolUse) {\n const fn = hooks.postToolUse\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'postToolUse'),\n timing: PLUGIN_HOOK_TIMING_MAP.postToolUse,\n handle: async (input) => {\n await fn({ toolName: input.toolName, input: input.args, result: input.result })\n },\n }),\n )\n }\n\n if (hooks.userPromptSubmit) {\n const fn = hooks.userPromptSubmit\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'userPromptSubmit'),\n timing: PLUGIN_HOOK_TIMING_MAP.userPromptSubmit,\n handle: async (input) => {\n await fn({ prompt: messageText(input.message) })\n },\n }),\n )\n }\n\n if (hooks.sessionStart) {\n const fn = hooks.sessionStart\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'sessionStart'),\n timing: PLUGIN_HOOK_TIMING_MAP.sessionStart,\n handle: async () => {\n await fn()\n },\n }),\n )\n }\n\n if (hooks.stop) {\n const fn = hooks.stop\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'stop'),\n timing: PLUGIN_HOOK_TIMING_MAP.stop,\n handle: async () => {\n await fn()\n },\n }),\n )\n }\n\n // RFC-129 D1/D2:新增 9 个 observer 字段映射,沿用既有 if 块模式(不引入抽象层)。\n if (hooks.sessionRestored) {\n const fn = hooks.sessionRestored\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'sessionRestored'),\n timing: PLUGIN_HOOK_TIMING_MAP.sessionRestored,\n handle: async (input) => {\n await fn({ sessionId: input.sessionId })\n },\n }),\n )\n }\n\n if (hooks.sessionDeleted) {\n const fn = hooks.sessionDeleted\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'sessionDeleted'),\n timing: PLUGIN_HOOK_TIMING_MAP.sessionDeleted,\n handle: async (input) => {\n await fn({ sessionId: input.sessionId })\n },\n }),\n )\n }\n\n if (hooks.sessionIdle) {\n const fn = hooks.sessionIdle\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'sessionIdle'),\n timing: PLUGIN_HOOK_TIMING_MAP.sessionIdle,\n handle: async (input) => {\n await fn({ sessionId: input.sessionId })\n },\n }),\n )\n }\n\n if (hooks.sessionError) {\n const fn = hooks.sessionError\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'sessionError'),\n timing: PLUGIN_HOOK_TIMING_MAP.sessionError,\n handle: async (input) => {\n // RFC-143 M0:HookRegistry.emit() 已在 registry 层对 session.error 做统一 sanitize\n // (input.error 已是字符串,非原始 Error 对象)——此处不再重复转换,直接透传。\n await fn({ sessionId: input.sessionId, error: input.error as unknown as string })\n },\n }),\n )\n }\n\n if (hooks.streamStart) {\n const fn = hooks.streamStart\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'streamStart'),\n timing: PLUGIN_HOOK_TIMING_MAP.streamStart,\n handle: async (input) => {\n await fn({ sessionId: input.sessionId, model: input.model })\n },\n }),\n )\n }\n\n if (hooks.compactionPending) {\n const fn = hooks.compactionPending\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'compactionPending'),\n timing: PLUGIN_HOOK_TIMING_MAP.compactionPending,\n handle: async (input) => {\n await fn({ sessionId: input.sessionId, messageCount: input.messageCount })\n },\n }),\n )\n }\n\n if (hooks.compactionOccurred) {\n const fn = hooks.compactionOccurred\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'compactionOccurred'),\n timing: PLUGIN_HOOK_TIMING_MAP.compactionOccurred,\n handle: async (input) => {\n await fn({ sessionId: input.sessionId, retainedCount: input.retainedCount })\n },\n }),\n )\n }\n\n // RFC-143 M1:补齐 task.created/task.started 映射(真实缺口——executor.ts 已 emit,此前未接线)。\n if (hooks.taskCreated) {\n const fn = hooks.taskCreated\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'taskCreated'),\n timing: PLUGIN_HOOK_TIMING_MAP.taskCreated,\n handle: async (input) => {\n await fn({ taskId: input.task.id })\n },\n }),\n )\n }\n\n if (hooks.taskStarted) {\n const fn = hooks.taskStarted\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'taskStarted'),\n timing: PLUGIN_HOOK_TIMING_MAP.taskStarted,\n handle: async (input) => {\n await fn({ taskId: input.task.id, agent: input.agent })\n },\n }),\n )\n }\n\n if (hooks.taskCompleted) {\n const fn = hooks.taskCompleted\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'taskCompleted'),\n timing: PLUGIN_HOOK_TIMING_MAP.taskCompleted,\n handle: async (input) => {\n await fn({\n taskId: input.task.id,\n status: input.task.status,\n durationMs: input.result.durationMs,\n tokenUsage: input.result.tokenUsage,\n })\n },\n }),\n )\n }\n\n if (hooks.taskFailed) {\n const fn = hooks.taskFailed\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, 'taskFailed'),\n timing: PLUGIN_HOOK_TIMING_MAP.taskFailed,\n handle: async (input) => {\n await fn({ taskId: input.task.id, error: input.error.message })\n },\n }),\n )\n }\n\n return specs\n}\n\n/**\n * 把 computed 命令贡献(`module.contributions.commands`,definePlugin 装载时算出)转为 `FileCommand[]`\n * (RFC-082 M082-04-04)。这是「装载器注入 source/scope/pluginId/path」那一步(contributions.ts 词表为作者意图层)。\n *\n * 优先级(R7):source='plugin',命名永低于 otto/claude/内置——消费方按既有「内置/文件优先」顺序追加在后即不夺名。\n * filePath 为合成标记(无真实文件),用于诊断与 `unregisterBySource`(R6,source=pluginId)。\n */\nexport function computedCommandsToFileCommands(\n pluginId: string,\n // bundled 与 project/repository/user 一样是有代码的插件(区别于无代码的 builtin),\n // 可贡献命令。FileCommand.scope 是 claude-compat 的公开类型(project/repository/user),\n // 为不跨包扩展类型面,bundled 在此映射为 'project'(随分发第一方插件,语义最接近)。\n scope: 'project' | 'repository' | 'user' | 'bundled',\n pluginDir: string,\n commands: readonly CommandContribution[],\n): FileCommand[] {\n return commands.map((c) => ({\n name: c.name,\n description: c.description,\n argumentHint: c.argumentHint,\n allowedTools: c.allowedTools,\n model: c.model,\n body: c.body,\n source: 'plugin' as const,\n scope: scope === 'bundled' ? 'project' : scope,\n pluginId,\n filePath: `${pluginDir}#computed:${c.name}`,\n }))\n}\n\n/**\n * 把 computed agent 贡献(`module.contributions.agents`)转为 `AgentProfile[]`(RFC-082 M082-04-05)。\n * 作者声明子集 → 运行时 profile,补必填默认(taskTypes/capabilities 缺省 [])。\n * 与 computed 命令同型漏斗(追加进 `declarativeAgentProfiles`,命名永低于 otto/claude,R7)。\n */\nexport function computedAgentsToProfiles(agents: readonly AgentContribution[]): AgentProfile[] {\n return agents.map((a) => ({\n name: a.name,\n description: a.description,\n taskTypes: a.taskTypes ?? [],\n capabilities: a.capabilities ?? [],\n systemPromptTemplate: a.systemPromptTemplate,\n ...(a.promptMode ? { promptMode: a.promptMode } : {}),\n ...(a.defaultTools ? { defaultTools: a.defaultTools } : {}),\n ...(a.disallowedTools ? { disallowedTools: a.disallowedTools } : {}),\n ...(a.preferredModelTier ? { preferredModelTier: a.preferredModelTier } : {}),\n ...(a.allowSubagents !== undefined ? { allowSubagents: a.allowSubagents } : {}),\n }))\n}\n\n/** computed skill 注册项(SkillRegistry.register 的两参)。 */\nexport interface ComputedSkillRegistration {\n definition: SkillDefinition\n source: SkillSource\n}\n\n/**\n * 把 computed skill 贡献(`module.contributions.skills`)转为 SkillRegistry 注册项(RFC-082 M082-04-05)。\n * 合成 `SkillDefinition`(metadata 来自 name/description,body 直用,目录/路径为合成标记、无附属文件)+\n * `source={type:'plugin',root,id}`(最低优先级 R7;provenance 用 pluginId)。\n */\nexport function computedSkillsToDefinitions(\n pluginId: string,\n pluginDir: string,\n skills: readonly SkillContribution[],\n): ComputedSkillRegistration[] {\n return skills.map((s) => ({\n definition: {\n metadata: { name: s.name, description: s.description ?? '' },\n body: s.body,\n directory: pluginDir,\n filePath: `${pluginDir}#computed:${s.name}`,\n supportingFiles: [],\n },\n source: { type: 'plugin' as const, root: pluginDir, id: pluginId },\n }))\n}\n\n/**\n * RFC-153 D6:把 `converse()` 抛出的异常标记来源插件(`pluginSource`),不改变异常本身的\n * 传播路径——裸错误(非 `CodedError`)转换为携带 `pluginSource` 的 `ProviderError`;已是\n * `CodedError`(如插件复用了 `@x-otto/provider` 的错误类型)则浅拷贝补充该字段,不覆盖插件\n * 自己可能已设置的更精确 `code`/`errorType`(规则 8:只标记来源,不改变错误分类/重试决策)。\n */\nfunction tagProviderError(pluginId: string, error: unknown): Error {\n if (error instanceof CodedError) {\n if (error.pluginSource !== undefined) return error // 已标记,不重复包装\n return new (error.constructor as new (message: string, options: ConstructorParameters<typeof CodedError>[1]) => CodedError)(\n error.message,\n {\n code: error.code,\n cause: error.cause,\n status: error.status,\n headers: error.headers,\n errorType: error.errorType,\n pluginSource: pluginId,\n },\n )\n }\n const message = error instanceof Error ? error.message : String(error)\n return new ProviderError(message, {\n code: 'PROVIDER_PLUGIN_ERROR',\n cause: error instanceof Error ? error : undefined,\n pluginSource: pluginId,\n })\n}\n\n/**\n * 把代码式 provider 工厂适配为 `@x-otto/ai ProviderFactory`(RFC-105 D3/M105-2-04)。\n *\n * RFC-140 D6:SDK 侧签名(`PluginProviderFactory`)现在直接返回 `@x-otto/provider` 的\n * `ProviderStream`(不再是宽松的 `Record<string, unknown>`),插件作者写代码式 provider\n * 时即获得编译期类型检查,不再需要 `as unknown as` 断言。`@x-otto/ai ProviderStream` 与\n * `@x-otto/provider ProviderStream` 是同一份类型的两个 re-export 入口(`@x-otto/ai` re-export\n * 自 `@x-otto/provider`,见 `packages/ai/src/types.ts`),此处透传即可,无需类型转换。\n *\n * RFC-153 D6:`converse()` 包一层——同步/异步抛出的异常与 `{type:'error'}` 流事件均补充\n * `pluginSource` 标记(供 `classifySubmitError`/`ErrorRetryCard` 展示\"来自插件: <id>\")。\n * 既有 `stream.ts` 的异常归一化路径(`start()` 的 try/catch)不变,本包装层只在其上游\n * 补充一层标记,不改变任何重试/降级决策(规则 8)。\n */\nexport function toProviderFactory(pluginId: string, factory: PluginProviderFactory): ProviderFactory {\n return () => {\n const provider = factory()\n const wrapped: ProviderStream = {\n ...provider,\n async *converse(model, context, options, signal) {\n try {\n for await (const event of provider.converse(model, context, options, signal)) {\n if (event.type === 'error') {\n yield { type: 'error', error: tagProviderError(pluginId, event.error) } as StreamEvent\n } else {\n yield event\n }\n }\n } catch (error) {\n throw tagProviderError(pluginId, error)\n }\n },\n }\n return wrapped\n }\n}\n\n/**\n * 收窄 `PluginOAuthProviderFactory` 产出为 `OAuthProvider` 形状(RFC-122 D1 代码式轴)。\n * 结构不符 → null + warn(fail-soft,不阻塞插件其余轴)。\n */\nexport function toOAuthProvider(\n pluginId: string,\n factory: () => import('@x-otto/ai').OAuthProvider,\n): import('@x-otto/ai').OAuthProvider | null {\n // 终局 review 修复:`PluginOAuthProviderFactory` 现直接返回官方 `OAuthProvider` 类型\n // (编译期类型检查,见 `packages/plugin/src/sdk.ts`),不再需要运行时结构收窄——只保留\n // try/catch(工厂函数体执行仍可能抛错,fail-soft 不阻塞插件其余轴,同 `toProviderFactory`\n // 纪律)。\n try {\n return factory()\n } catch (err) {\n const message = String(err)\n logger.warn({ pluginId, err: message }, 'plugin oauthProviderFactory threw, skipped')\n // RFC-153 D6:既有 logger.warn 已带 pluginId——复用同一份信息写入 D2 的 per-plugin\n // 日志(非新增采集点),供 /extension 详情页日志区块展示。fail-soft:appendPluginLog\n // 内部自带异常吞掉,这里不需要额外包裹。\n appendPluginLog(pluginId, 'warn', `oauthProviderFactory threw, skipped: ${message}`)\n return null\n }\n}\n\n/**\n * 把 `text/image` 之外的返回值规整为 `ToolResult`(RFC-105 D4/M105-3-02)。\n * 插件工具签名是 `execute(args) => Promise<unknown>`(宽松,SDK 侧不下沉协议类型);\n * 这里做结构收窄——字符串/普通值包成单条 text content,已是 `ToolResult` 形状(含 content\n * 数组)的直接透传(宽容识别,不强制插件作者拼确切协议类型)。\n */\nfunction normalizeToolResult(raw: unknown): ToolResult {\n if (raw && typeof raw === 'object' && Array.isArray((raw as { content?: unknown }).content)) {\n return raw as ToolResult\n }\n const text = typeof raw === 'string' ? raw : JSON.stringify(raw ?? null)\n return { content: [{ type: 'text', text }] }\n}\n\n/**\n * 校验 `PluginTool` 结构并适配为真实 `AgentTool`(RFC-105 D4)。结构不符(缺 name/execute/\n * parameters.safeParse)→ 返回 null + warn,fail-soft 丢弃该条(同 manifest 各轴 fail-soft 纹理),\n * 不阻塞该插件其余工具的注册。\n */\n/**\n * RFC-303 M4:进程内工具 ctx 来源(workspaceDir/logger/host)——toAgentTool 在\n * 适配 AgentTool 时注入;不传则 ctx 只含 pluginId(测试/轻量装配场景)。\n */\nexport interface PluginToolCtxSource {\n workspaceDir: string\n logger: { info: (msg: string) => void; warn: (msg: string) => void; error: (msg: string) => void }\n /** 受控宿主能力面(fail-closed:未授予为 undefined)。 */\n host?: import('@x-otto/plugin').PluginHostCapabilities\n /** RFC-303 M5:scoped persistence(未声明 storage 能力时为 undefined)。 */\n storage?: import('@x-otto/plugin').PluginScopedStorageShape\n}\n\nexport function toAgentTool(\n pluginId: string,\n tool: unknown,\n source?: PluginToolCtxSource,\n): AgentTool | null {\n const t = tool as Partial<PluginTool> | null | undefined\n if (!t || typeof t !== 'object') {\n logger.warn({ pluginId }, 'plugin tool entry is not an object, skipped')\n return null\n }\n if (typeof t.name !== 'string' || !t.name) {\n logger.warn({ pluginId }, 'plugin tool missing valid name, skipped')\n return null\n }\n if (typeof t.execute !== 'function') {\n logger.warn({ pluginId, tool: t.name }, 'plugin tool missing execute function, skipped')\n return null\n }\n if (!t.parameters || typeof t.parameters.safeParse !== 'function') {\n logger.warn(\n { pluginId, tool: t.name },\n 'plugin tool missing valid parameters.safeParse, skipped',\n )\n return null\n }\n const name = t.name\n const description = t.description ?? ''\n const parameters = t.parameters\n const execute = t.execute\n return {\n name,\n description,\n parameters: parameters as AgentTool['parameters'],\n // RFC-153 D6:异常路径补 pluginSource 标记(供 ErrorRetryCard 展示\"来自插件: <id>\")。\n // 抛出的异常必须原样 rethrow(不在此吞掉/转换)——`tool-executor.ts` 的外层 catch 依赖\n // 鸭子类型读取 `error.errorKind`(`toolError()` 附带的字段,RFC-074 R-ERRKIND)来定型\n // 错误类别;此前本函数无 try/catch,异常直接传播到那一层。这里只在 throw 前用\n // `Object.assign` 补一个 `pluginSource` 字段到同一个 Error 对象上(保留 errorKind 等\n // 既有鸭子类型字段不变),不新建包装对象、不改变传播路径,符合规则 8。\n // 正常返回路径(`isError:true` 的 ToolResult,非 throw)单独补标记。\n async execute(ctx: { params: unknown; signal?: AbortSignal }) {\n try {\n // RFC-303 M4:第二参数 = PluginToolContext(pluginId/workspaceDir/logger/host/signal)。\n // signal 并入 ctx(RFC-153 D6 的 { signal } 形态向后兼容——存量工具读 ctx.signal\n // 或忽略多余字段均成立);主进程内联单参工具 (args) => ... 自然忽略第二参数,无害。\n const toolCtx: PluginToolContext = {\n pluginId,\n workspaceDir: source?.workspaceDir ?? '',\n logger: source?.logger ?? {\n info: () => {}, warn: () => {}, error: () => {},\n },\n host: source?.host,\n signal: ctx.signal,\n storage: source?.storage,\n }\n const result = await (execute as (args: unknown, ctx?: PluginToolContext) => Promise<unknown>)(\n ctx.params,\n toolCtx,\n )\n const normalized = normalizeToolResult(result)\n if (normalized.isError) {\n return { ...normalized, pluginSource: pluginId }\n }\n return normalized\n } catch (error) {\n if (error instanceof Error) {\n Object.assign(error, { pluginSource: pluginId })\n }\n throw error\n }\n },\n }\n}\n\n/**\n * 把插件 Monitor 适配为引擎 HookSpec[](RFC-129 D6)。与 `pluginHooksToSpecs` 完全独立——\n * 各自的 `defineHook` 注册,互不影响。投影自**既有**(非 D1 新增)HookTiming:`stream.end`\n * (含 D6 新增的可选 `tokenUsage` 字段)、`compaction.after`、`session.error`。\n *\n * **失败隔离(R5)**:每个 monitor 的每次 `onEvent` 调用独立 try/catch,抛错只记录日志、\n * 绝不 rethrow——不得让一个插件的 monitor 崩溃影响其他 monitor 或引擎主流程。\n *\n * `compaction.occurred` 的 `tokensSaved` 需要装载器自行换算(不是 `compaction.after` payload\n * 直接携带的字段)——但 `compaction.after` 只有 `retainedCount`(裁剪后保留的消息数),没有\n * \"裁剪前后 token 差值\"这个量纲;由于引擎侧当前未在该 timing 暴露 token 层面的前后对比,\n * 这里保守地不做臆测换算,`tokensSaved` 固定为 `0`(诚实降级:宁可给出已知为不准确的占位值\n * 并在类型层保留字段语义,也不用消息数冒充 token 数)。若未来 `compaction.after` payload\n * 补充 token 维度数据,此处应改为真实值透传。\n */\nexport function pluginMonitorsToSpecs(\n pluginId: string,\n monitors: readonly PluginMonitor[],\n): HookSpec[] {\n const specs: HookSpec[] = []\n\n const dispatch = async (monitor: PluginMonitor, event: MonitorEvent): Promise<void> => {\n try {\n await monitor.onEvent(event)\n } catch (err) {\n logger.warn(\n { pluginId, monitorId: monitor.id, err: String(err) },\n 'plugin monitor onEvent threw, isolated',\n )\n }\n }\n\n for (const monitor of monitors) {\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, `monitor:${monitor.id}:stream.end`),\n timing: 'stream.end',\n handle: async (input) => {\n if (!input.tokenUsage) return\n await dispatch(monitor, {\n type: 'turn.completed',\n sessionId: input.sessionId,\n tokens: { input: input.tokenUsage.input, output: input.tokenUsage.output },\n })\n },\n }),\n )\n\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, `monitor:${monitor.id}:compaction.after`),\n timing: 'compaction.after',\n handle: async (input) => {\n await dispatch(monitor, {\n type: 'compaction.occurred',\n sessionId: input.sessionId,\n tokensSaved: 0, // 见函数注释:compaction.after 当前不携带 token 维度数据,诚实降级\n })\n },\n }),\n )\n\n specs.push(\n defineHook({\n name: pluginHookName(pluginId, `monitor:${monitor.id}:session.error`),\n timing: 'session.error',\n handle: async (input) => {\n // RFC-143 M0:HookRegistry.emit() 已在 registry 层对 session.error 做统一 sanitize\n // (input.error 已是字符串,非原始 Error 对象)——此处不再调用 .message,直接使用。\n await dispatch(monitor, {\n type: 'error',\n sessionId: input.sessionId,\n message: input.error as unknown as string,\n })\n },\n }),\n )\n }\n\n return specs\n}\n","/** Plugin health tracker: crash/restart/hang detection + circuit breaker. */\n\nexport interface PluginHealthStats {\n pluginId: string\n /** interceptor hook 挂起超时累计次数(M1 写入点)。 */\n hookTimeouts: number\n /** 代码式工具(PluginWorkerPool)挂起超时累计次数(collect-plugin-perf-metrics.ts 写入点)。 */\n toolTimeouts: number\n /** 最近一次错误(加载/编译/工厂失败等,module-loader*.ts 写入点)。 */\n lastError?: { message: string; timestamp: number }\n /** 最近一次触发超时的 hook 名(诊断定位用)。 */\n lastTimeoutHook?: string\n}\n\ninterface MutablePluginHealthStats {\n pluginId: string\n hookTimeouts: number\n toolTimeouts: number\n lastError?: { message: string; timestamp: number }\n lastTimeoutHook?: string\n}\n\nfunction toReadonly(s: MutablePluginHealthStats): PluginHealthStats {\n return { ...s }\n}\n\n/** 插件健康度内存态单例存储:挂起/错误计数按 pluginId 聚合。不持久化跨进程(规则 5)。 */\nexport class PluginHealthTracker {\n private readonly stats = new Map<string, MutablePluginHealthStats>()\n private readonly clock: () => number\n\n constructor(clock: () => number = () => Date.now()) {\n this.clock = clock\n }\n\n private ensure(pluginId: string): MutablePluginHealthStats {\n let s = this.stats.get(pluginId)\n if (!s) {\n s = { pluginId, hookTimeouts: 0, toolTimeouts: 0 }\n this.stats.set(pluginId, s)\n }\n return s\n }\n\n /** interceptor hook 挂起超时(M1 `onHookTimeout` 回调写入点)。 */\n recordHookTimeout(pluginId: string, hookName: string): void {\n const s = this.ensure(pluginId)\n s.hookTimeouts++\n s.lastTimeoutHook = hookName\n }\n\n /** 代码式工具(PluginWorkerPool)挂起超时(collect-plugin-perf-metrics.ts 写入点)。 */\n recordToolTimeout(pluginId: string): void {\n const s = this.ensure(pluginId)\n s.toolTimeouts++\n }\n\n /** 插件加载/编译/工厂执行失败(module-loader*.ts 写入点)。 */\n recordError(pluginId: string, message: string): void {\n const s = this.ensure(pluginId)\n s.lastError = { message, timestamp: this.clock() }\n }\n\n /** 查询单个插件的健康度快照;无记录时返回全零默认值(供 D4 面板\"全零不展示\"判定)。 */\n query(pluginId: string): PluginHealthStats {\n const s = this.stats.get(pluginId)\n return s ? toReadonly(s) : { pluginId, hookTimeouts: 0, toolTimeouts: 0 }\n }\n\n /** 全量快照(诊断/测试用途)。 */\n all(): PluginHealthStats[] {\n return [...this.stats.values()].map(toReadonly)\n }\n\n clear(): void {\n this.stats.clear()\n }\n}\n\nexport function createPluginHealthTracker(clock?: () => number): PluginHealthTracker {\n return new PluginHealthTracker(clock)\n}\n","/**\n * plugin-capability-quota.ts —— RFC-318 M1:受控能力的宿主侧配额与访问白名单。\n *\n * 为什么配额是**宿主侧**状态而非插件自律:`readTranscript`/`llm.complete` 的滥用面\n * (读任意会话 / 烧 token / 批量外流)全都在插件代码里,任何\"请插件自觉\"的设计等于没有\n * 设计(RFC-318 R2:配额在宿主侧强制,插件不可影响)。\n *\n * **notify epoch 是配额的时间基准**:service 是长生命周期的,用绝对时间做\"每 N 次\"配额\n * 要么太松(一小时窗口内可连发)要么太紧。RFC-318 的语义是\"每收到一次 session.idle\n * 通知,允许读 1 次、调模型 2 次\"——即配额随事件节拍刷新,与归纳器的真实工作节奏一致,\n * 也天然阻断\"不等事件就自主刷接口\"的行为。\n */\n\n/** 每个 notify epoch 内允许的 readTranscript 次数。 */\nexport const READ_TRANSCRIPT_PER_EPOCH = 1\n/** 每个 notify epoch 内允许的 llm.complete 次数。 */\nexport const LLM_COMPLETE_PER_EPOCH = 2\n/** llm.complete 的滑动小时窗上限(防长会话内 epoch 累积成大量调用)。 */\nexport const LLM_COMPLETE_PER_HOUR = 30\n/** 单次 readTranscript 返回的最大消息条数。 */\nexport const TRANSCRIPT_MAX_MESSAGES = 500\n/** llm.complete 的 prompt 长度上限(chars)——超限拒绝,不截断。 */\nexport const LLM_PROMPT_MAX_CHARS = 32_000\n/** llm.complete 的 maxTokens 上限——超限取此值(非拒绝:这是补全长度,夹紧无歧义)。 */\nexport const LLM_MAX_TOKENS_CAP = 2_048\n\n/**\n * RFC-327 M3:user.notify 的滑动小时窗上限。\n *\n * **不用 epoch 基准**(区别于 read/llm):通知的正当触发源包括 schedule.tick、生命周期\n * 事件等**非会话**事件,它们不开 epoch;若挂 epoch 会导致定时服务永远发不出通知。\n * 故用绝对时间窗——它约束的是\"刷屏\"这一唯一滥用形态,与触发源无关。\n */\nexport const NOTIFY_PER_HOUR = 20\n/** 通知正文长度上限(chars)——超限截断(展示面,截断不改变语义)。 */\nexport const NOTIFY_MESSAGE_MAX_CHARS = 500\n/** 通知 code 长度上限(chars)——机器标识,不面向用户展示。 */\nexport const NOTIFY_CODE_MAX_CHARS = 120\n\nconst HOUR_MS = 60 * 60 * 1000\n\ninterface PluginQuotaState {\n /** 经 notify 下发给本插件的 sessionId(readTranscript 白名单,RFC-318 R2)。 */\n authorizedSessionIds: Set<string>\n readCountThisEpoch: number\n llmCountThisEpoch: number\n /** llm.complete 调用时间戳(滑动小时窗)。 */\n llmCallTimes: number[]\n /** RFC-327 M3:user.notify 调用时间戳(滑动小时窗)。 */\n notifyTimes: number[]\n}\n\nfunction emptyState(): PluginQuotaState {\n return {\n authorizedSessionIds: new Set(),\n readCountThisEpoch: 0,\n llmCountThisEpoch: 0,\n llmCallTimes: [],\n notifyTimes: [],\n }\n}\n\n/**\n * per-plugin 的能力配额与会话白名单登记处。\n *\n * 生命周期:随宿主装配点存活;插件停止时 `reset(pluginId)` 清空(不保留跨启停的授权——\n * 插件重启后必须重新经 notify 获得 sessionId 授权,防\"停用期间攒下的白名单\"被复用)。\n */\nexport class PluginCapabilityQuota {\n private readonly states = new Map<string, PluginQuotaState>()\n\n constructor(private readonly now: () => number = () => Date.now()) {}\n\n private stateOf(pluginId: string): PluginQuotaState {\n let s = this.states.get(pluginId)\n if (!s) {\n s = emptyState()\n this.states.set(pluginId, s)\n }\n return s\n }\n\n /**\n * 记录一次成功送出的 notify:授权该 sessionId 并开启新 epoch(重置 per-epoch 计数)。\n * 这是插件获得 `readTranscript` 访问权的**唯一**入口。\n */\n onNotifyDelivered(pluginId: string, sessionId: string): void {\n const s = this.stateOf(pluginId)\n s.authorizedSessionIds.add(sessionId)\n s.readCountThisEpoch = 0\n s.llmCountThisEpoch = 0\n }\n\n /** 该插件是否被授权读这个会话(未经 notify 下发 = 拒绝)。 */\n isSessionAuthorized(pluginId: string, sessionId: string): boolean {\n return this.stateOf(pluginId).authorizedSessionIds.has(sessionId)\n }\n\n /** 消费一次 readTranscript 配额;返回 false 表示超限(调用方应拒绝并说明原因)。 */\n tryConsumeRead(pluginId: string): boolean {\n const s = this.stateOf(pluginId)\n if (s.readCountThisEpoch >= READ_TRANSCRIPT_PER_EPOCH) return false\n s.readCountThisEpoch += 1\n return true\n }\n\n /** 消费一次 llm.complete 配额(epoch + 滑动小时窗双门)。 */\n tryConsumeLlm(pluginId: string): { ok: true } | { ok: false; reason: 'epoch' | 'hourly' } {\n const s = this.stateOf(pluginId)\n if (s.llmCountThisEpoch >= LLM_COMPLETE_PER_EPOCH) return { ok: false, reason: 'epoch' }\n const cutoff = this.now() - HOUR_MS\n s.llmCallTimes = s.llmCallTimes.filter((t) => t > cutoff)\n if (s.llmCallTimes.length >= LLM_COMPLETE_PER_HOUR) return { ok: false, reason: 'hourly' }\n s.llmCountThisEpoch += 1\n s.llmCallTimes.push(this.now())\n return { ok: true }\n }\n\n /**\n * RFC-327 M3:消费一次 user.notify 配额;返回 false 表示超限(调用方应**静默丢弃**)。\n *\n * 用滑动小时窗而非 epoch——通知的正当触发源(schedule.tick、生命周期事件)不开 epoch,\n * 挂 epoch 会让定时服务永远发不出通知。\n */\n tryConsumeNotify(pluginId: string): boolean {\n const s = this.stateOf(pluginId)\n const cutoff = this.now() - HOUR_MS\n s.notifyTimes = s.notifyTimes.filter((t) => t > cutoff)\n if (s.notifyTimes.length >= NOTIFY_PER_HOUR) return false\n s.notifyTimes.push(this.now())\n return true\n }\n\n /** 插件停止/卸载:清空全部授权与计数。 */\n reset(pluginId: string): void {\n this.states.delete(pluginId)\n }\n}\n","import type { GrillAnswer, GrillQuestion } from '@x-otto/interchange'\n\n/**\n * RFC-344 M2:宿主问询中心——两类人在环问询的统一队列与仲裁。\n *\n * **为什么需要它**(M1 核实的既有陷阱):`packages/tui/src/bootstrap/handle-modal.ts:324`\n * 的 `resolvers.questions` 是**单槽位**——第二个 `openModal({kind:'questions'})` 到来时\n * 直接覆盖前一个的 resolver,被覆盖的 Promise **永远不会 resolve**(调用方挂死)。\n *\n * 这在只有 agent grill 一个投递源时已经是漏洞(同会话连续 grill 会触发),只是不易暴露;\n * 新增插件问询作为第二个投递源后必然频繁命中。故本模块的核心不变式是:\n *\n * **同一时刻最多只有一个问询在飞(in-flight)。**\n *\n * 两类问询都必须入队(独立评审补充)——只拦插件侧无法建立该不变式,且会原样保留\n * `requestGrill` 的既有并发漏洞。\n *\n * **职责边界**:纯逻辑状态机,不碰 TUI、不做 IO、不读时钟以外的环境。\n * 时钟经 `now` 端口注入(确定性可测,对齐本仓 Clock 端口纪律)。\n */\n\n/** 问询来源——决定优先级与配额归属。 */\nexport type InquiryOrigin =\n | { kind: 'session'; sessionId: string }\n | { kind: 'plugin'; pluginId: string }\n\nexport interface InquiryRequest {\n /** 宿主分配的唯一 id。 */\n id: string\n origin: InquiryOrigin\n /** 复用 grill 的问题结构(M1 D4:数据结构全复用)。 */\n question: Omit<GrillQuestion, 'id'>\n}\n\n/** 已入队的问询——`enqueuedAt` 由中心用自己的时钟盖章,不接受调用方传入。 */\nexport interface QueuedInquiry extends InquiryRequest {\n /**\n * 入队时刻(epoch ms)。\n *\n * **必须由中心盖章而非调用方传入**:TTL 判定用的是中心的 `now` 端口,若时间戳来自\n * 另一个时钟(调用方的 `Date.now()`、测试里的固定值),两个时间源不一致会让问询\n * 一入队就被判超时(或永不超时)。单一时间源是 TTL 正确性的前提。\n */\n enqueuedAt: number\n}\n\n/** 拒绝原因——调用方据此给出可诊断的失败,而非静默丢弃。 */\nexport type RejectReason =\n | 'queue-full'\n | 'plugin-concurrency'\n | 'timeout'\n | 'shutdown'\n\nexport interface InquiryCenterOptions {\n /** 队列容量上限(含在飞的那一个)。超限拒绝**新**问询,不丢弃旧的——旧的可能已在用户眼前。 */\n maxPending?: number\n /** 单个插件的并发问询上限——防止插件用连续发问霸占队列。 */\n maxPerPlugin?: number\n /** 问询存活上限(ms)。超时按 R5 fail-closed 返回拒绝,**不是**返回推荐值。 */\n ttlMs?: number\n now?: () => number\n}\n\nconst DEFAULT_MAX_PENDING = 8\nconst DEFAULT_MAX_PER_PLUGIN = 1\nconst DEFAULT_TTL_MS = 5 * 60 * 1000\n\n/**\n * 会话问询优先于插件问询。\n *\n * 判据:用户正在对话中的问题比后台插件的更紧急——agent 在等着它继续干活,\n * 而插件问询通常源于定时任务,晚几分钟无妨。\n */\nfunction priorityOf(origin: InquiryOrigin): number {\n return origin.kind === 'session' ? 0 : 1\n}\n\nexport interface EnqueueResult {\n /** 入队成功——调用方 await 此 Promise 拿最终答案。 */\n accepted: true\n answer: Promise<GrillAnswer>\n}\n\nexport interface RejectResult {\n accepted: false\n reason: RejectReason\n}\n\ninterface QueueEntry {\n request: QueuedInquiry\n resolve: (answer: GrillAnswer) => void\n /** 已交给 UI 渲染(in-flight)——此时不可被 TTL 清理,只能由用户作答或 UI 侧取消。 */\n dispatched: boolean\n}\n\n/**\n * 队列 + 仲裁状态机。\n *\n * 生命周期:`enqueue` → (轮到时)`takeNext` 交给 UI → `settle` 收敛答案 → 自动派发下一个。\n * 调用方(宿主接线层)负责把 `takeNext` 的产物送进 `openModal`,并在 UI 回调里调 `settle`。\n */\nexport class InquiryCenter {\n private readonly queue: QueueEntry[] = []\n private inFlightId: string | null = null\n private readonly maxPending: number\n private readonly maxPerPlugin: number\n private readonly ttlMs: number\n private readonly now: () => number\n\n constructor(options: InquiryCenterOptions = {}) {\n this.maxPending = options.maxPending ?? DEFAULT_MAX_PENDING\n this.maxPerPlugin = options.maxPerPlugin ?? DEFAULT_MAX_PER_PLUGIN\n this.ttlMs = options.ttlMs ?? DEFAULT_TTL_MS\n this.now = options.now ?? Date.now\n }\n\n /**\n * 入队一个问询。返回的 Promise 在用户作答、超时或被拒时收敛。\n *\n * **永不 reject**:拒绝经 `{accepted:false, reason}` 表达,超时/取消经 answer 的\n * `resolvedBy` 表达。调用方不需要 try/catch——人在环的\"没答成\"不是异常。\n */\n enqueue(request: InquiryRequest): EnqueueResult | RejectResult {\n this.sweepExpired()\n\n if (this.queue.length >= this.maxPending) {\n return { accepted: false, reason: 'queue-full' }\n }\n\n if (request.origin.kind === 'plugin') {\n const pluginId = request.origin.pluginId\n const own = this.queue.filter(\n (e) => e.request.origin.kind === 'plugin' && e.request.origin.pluginId === pluginId,\n ).length\n if (own >= this.maxPerPlugin) {\n return { accepted: false, reason: 'plugin-concurrency' }\n }\n }\n\n let resolve!: (answer: GrillAnswer) => void\n const answer = new Promise<GrillAnswer>((r) => {\n resolve = r\n })\n const queued: QueuedInquiry = { ...request, enqueuedAt: this.now() }\n this.queue.push({ request: queued, resolve, dispatched: false })\n // 稳定排序:优先级升序,同优先级按入队时刻(FIFO)。\n this.queue.sort(\n (a, b) =>\n priorityOf(a.request.origin) - priorityOf(b.request.origin) ||\n a.request.enqueuedAt - b.request.enqueuedAt,\n )\n return { accepted: true, answer }\n }\n\n /**\n * 等到轮到指定问询渲染。\n *\n * 调用方模式:`enqueue` 成功后 `await waitForTurn(id)`,返回 true 即可安全渲染\n * (此时它已是 in-flight),渲染完调 `settle`。返回 false 表示该问询已被 TTL 清理\n * 或 shutdown 收敛——调用方不应再渲染(它的 Promise 已被中心 resolve)。\n *\n * 为什么把等待放在中心而不是让调用方轮询:轮询逻辑写在每个调用点会各自跑偏\n * (漏判 TTL 清理、漏判 shutdown),而\"谁该渲染\"本就是仲裁者的职责。\n */\n async waitForTurn(id: string, pollMs = 25): Promise<boolean> {\n for (;;) {\n const next = this.takeNext()\n if (next?.id === id) return true\n // 已不在队列 = 被 TTL 清理或 shutdown 收敛(其 Promise 已 resolve)。\n if (!this.queue.some((e) => e.request.id === id)) return false\n await new Promise((r) => setTimeout(r, pollMs))\n }\n }\n\n /**\n * 取出下一个应渲染的问询——**同一时刻只有一个在飞**(核心不变式)。\n *\n * 已有在飞问询时返回 undefined:调用方不得强行再开一个,否则会命中\n * `resolvers.questions` 单槽位覆盖,让前一个 Promise 永远挂死。\n */\n takeNext(): QueuedInquiry | undefined {\n this.sweepExpired()\n if (this.inFlightId !== null) return undefined\n\n const next = this.queue.find((e) => !e.dispatched)\n if (!next) return undefined\n\n next.dispatched = true\n this.inFlightId = next.request.id\n return next.request\n }\n\n /**\n * 收敛一个问询的答案(用户作答 / Esc 取消 / UI 侧异常兜底)。\n *\n * 返回是否命中——重复 settle 同一 id 时返回 false(幂等,防 UI 层重复回调导致的双重收敛)。\n */\n settle(id: string, answer: GrillAnswer): boolean {\n const idx = this.queue.findIndex((e) => e.request.id === id)\n if (idx === -1) return false\n\n const [entry] = this.queue.splice(idx, 1)\n if (this.inFlightId === id) this.inFlightId = null\n entry!.resolve(answer)\n return true\n }\n\n /**\n * 释放槽位但**不经中心交付答案**——供\"答案走自有通道\"的调用方使用。\n *\n * 会话问询就是这种形态:它的答案经 `resolveAskUser` 直接回 AgentSession 的 grill gate,\n * 中心的 `answer` Promise 无人消费。此时若用 `settle(id, someAnswer)`,那个 answer\n * 参数是无意义的噪音,还会让读代码的人以为答案从这里走。\n *\n * 插件问询则相反——它**必须**用 `settle`,因为 `enqueue` 返回的 Promise 就是插件\n * 等待的那个。\n *\n * 幂等:重复调用或 id 不存在时返回 false。\n */\n release(id: string): boolean {\n const idx = this.queue.findIndex((e) => e.request.id === id)\n if (idx === -1) return false\n this.queue.splice(idx, 1)\n if (this.inFlightId === id) this.inFlightId = null\n return true\n }\n\n /** 当前是否有问询正在渲染。 */\n get hasInFlight(): boolean {\n return this.inFlightId !== null\n }\n\n // ── 队列诊断 ──────────────────────────────────────────────────────────────\n\n /** 队列长度(含在飞的那一个)——供诊断与测试。 */\n get size(): number {\n return this.queue.length\n }\n\n /**\n * 清理超时问询。\n *\n * **在飞的不清理**:它已经在用户眼前,用户可能正在读背景信息;由 UI 侧的取消/作答收敛。\n * 只清理还在排队、从未渲染过的——它们的等待是纯粹的机会成本。\n *\n * 超时按 R5 fail-closed:返回**拒绝**(`acceptedRecommendation:false`),\n * **不是** `auto_recommendation`,也**不是** `environment_headless`:\n * - `auto_recommendation`:推荐被采纳,回答者不是环境限制(不需问的场合)。\n * - `environment_headless`:环境不能问(headless),推荐被环境沉降。\n * - 超时是\"能问但用户没答\"——对插件必须保守(相当于没拿到批准)。\n * 这条区分是安全边界(M1 D3)。\n */\n private sweepExpired(): void {\n const cutoff = this.now() - this.ttlMs\n for (let i = this.queue.length - 1; i >= 0; i--) {\n const entry = this.queue[i]!\n if (entry.dispatched) continue // 在飞的不清理\n if (entry.request.enqueuedAt > cutoff) continue\n\n this.queue.splice(i, 1)\n entry.resolve(timeoutAnswer(entry.request.question))\n }\n }\n\n /**\n * 停机:收敛全部待决问询。\n *\n * 与超时同样按 fail-closed 拒绝——宿主要关了,没人能回答了,\n * 此时返回推荐值等于替用户点头。\n */\n shutdown(): void {\n const pending = this.queue.splice(0, this.queue.length)\n this.inFlightId = null\n for (const entry of pending) {\n entry.resolve(timeoutAnswer(entry.request.question))\n }\n }\n}\n\n/**\n * 超时/停机的 fail-closed 答案。\n *\n * `resolvedBy: 'human'` 而非 `'auto_recommendation'`/`'environment_headless'`——\n * 后两者都意味着\"推荐被采纳\"。这里的语义是\"没有得到批准\",故\n * `acceptedRecommendation: false` 且 answer 为空串:调用方拿不到可执行的决定,\n * 只能走自己的保守分支。\n */\nfunction timeoutAnswer(_question: Omit<GrillQuestion, 'id'>): GrillAnswer {\n return {\n answer: '',\n acceptedRecommendation: false,\n resolvedBy: 'human',\n }\n}\n","/**\n * plugin-service-entry-template.ts —— RFC-287 M3:常驻服务 worker 的入口脚本模板。\n *\n * 为什么不复用 `plugin-isolation-entry-template.ts`:那份模板服务于**一次性 RPC**语义\n * (load → executeTool/resolveSigil → stop),其消息全是宿主→隔离端单向发起;常驻服务\n * 需要的是 ①独立的 start/stop 生命周期、②**反向 RPC**(隔离端→宿主发起 host 能力调用)。\n * 两者协议形状不同,且既有模板被两个生产池(worker/process)共用——就地扩写会把常驻\n * 服务的语义风险扩散到全部插件工具执行路径。故独立一份,爆炸半径收敛在 services 轴内。\n *\n * **R7 运行约束**:本入口跑在启用了 Node 权限模型的 worker 里(`execArgv` 注入\n * `--experimental-permission`,见 `plugin-service-pool.ts`),`child_process` 被阻断。\n * 这是宿主强制的,不依赖本模板自觉——模板里也不提供任何 spawn 包装。\n */\n\n/** 常驻服务 RPC 消息类型(与一次性 RPC 协议独立,不共用 ISOLATION_MSG)。 */\nexport const SERVICE_MSG = {\n /** 宿主 → service worker:装载插件模块并启动指定服务 */\n start: 'service.start',\n /** service worker → 宿主:启动完成(error 非空即启动失败,触发退避重启) */\n started: 'service.started',\n /** 宿主 → service worker:优雅停机 */\n stop: 'service.stop',\n /** service worker → 宿主:停机完成 */\n stopped: 'service.stopped',\n /**\n * service worker → 宿主:**反向 RPC**——调用宿主能力(当前仅 startJob)。\n *\n * 安全:消息里**不带 pluginId**,宿主侧按连接(worker 实例)反查身份。这是刻意的——\n * 若身份取自消息内容,插件可谎称自己是别的插件调用其能力(R1 零豁免在 RPC 层的对应物)。\n */\n hostCall: 'service.hostCall',\n /** 宿主 → service worker:反向 RPC 结果 */\n hostResult: 'service.hostResult',\n /** service worker → 宿主:服务运行期未捕获错误(宿主据此计入崩溃并退避重启) */\n crashed: 'service.crashed',\n /**\n * 宿主 → service worker:**事件通知**(RFC-318 D9,单向推送、无回执)。\n *\n * 为什么不复用 `start`/`stop` 那类生命周期消息:通知是**运行期反复发生**的,且\n * 语义由宿主决定(当前 `session.idle`)。为什么不让 service 自己轮询:轮询拿不到\n * sessionId,且 hook 轴(宿主进程)与 service 轴(隔离 worker)之间此前根本无桥。\n *\n * 安全:负载最小化(`{type, sessionId}`),不夹带会话内容——插件要读内容必须另经\n * `readTranscript` 能力(独立声明、独立授权、独立配额)。\n */\n notify: 'service.notify',\n} as const\n\n/**\n * 生成 service worker 入口源码。\n *\n * `hostCall` 侧的设计要点:worker 侧维护 callId → {resolve, reject} 表,宿主回\n * `hostResult` 时按 callId 派发。宿主侧不可达/超时由宿主负责 reject(worker 侧不自设\n * 超时——避免与宿主超时双重计时产生歧义状态)。\n */\nexport function buildServiceEntrySrc(): string {\n return `const { parentPort, workerData } = require('worker_threads')\nconst __send = (m) => parentPort.postMessage(m)\n\n// RFC-303 M5:worker 侧 scoped storage 实现(无第三方依赖,直接文件读写)。\n// 宿主已 sanitize + mkdir + realpath 断言,且 execArgv 把写面收窄到 storageDir——\n// 这里只做纯读写,不做路径判断(权限层由 Node 权限模型兜底)。\nconst fs = require('fs')\nconst path = require('path')\nfunction createScopedStorage(baseDir, pluginId, workspaceKey) {\n const fileFor = (id) => path.join(baseDir, encodeURIComponent(id) + '.json')\n return {\n baseDir,\n pluginId,\n workspaceKey,\n async save(id, data) {\n const f = fileFor(id)\n fs.mkdirSync(path.dirname(f), { recursive: true })\n const tmp = f + '.tmp'\n fs.writeFileSync(tmp, JSON.stringify({ version: 1, id, data, metadata: {} }))\n fs.renameSync(tmp, f)\n },\n async load(id) {\n try { return JSON.parse(fs.readFileSync(fileFor(id), 'utf8')).data ?? null }\n catch (e) { if (e && e.code === 'ENOENT') return null; throw e }\n },\n async delete(id) {\n try { fs.unlinkSync(fileFor(id)); return true } catch (e) { return false }\n },\n async list() {\n try {\n return fs.readdirSync(baseDir)\n .filter((f) => f.endsWith('.json'))\n .map((f) => decodeURIComponent(f.slice(0, -5)))\n } catch (e) { return [] }\n },\n }\n}\n\nlet serviceInstance = null\nlet hostCallSeq = 0\nconst hostCalls = new Map()\n// RFC-318 D9:service 注册的通知处理器(单 handler,后注册覆盖先注册——多播由 service\n// 自己分发,宿主不承担订阅者生命周期)。\nlet notifyHandler = null\n\n/** 反向 RPC:把 host 能力调用送回宿主执行。宿主按连接身份鉴定,不信任本侧声明的身份。 */\nfunction callHost(method, params) {\n return new Promise((resolve, reject) => {\n const callId = 'hc_' + (++hostCallSeq)\n hostCalls.set(callId, { resolve, reject })\n __send({ type: '${SERVICE_MSG.hostCall}', callId, method, params })\n })\n}\n\n/** 按宿主下发的能力清单构造 ctx.host——未授予的能力不出现(fail-closed 语义跨越 RPC 边界)。 */\nfunction buildHost(capabilities) {\n if (!capabilities || capabilities.length === 0) return undefined\n const host = {}\n if (capabilities.includes('startJob')) {\n host.startJob = (input) => callHost('startJob', { title: input && input.title, prompt: input && input.prompt })\n }\n // RFC-318 M1:逐字段提取入参,绝不透传插件对象(同 startJob 的既有纪律——透传会让\n // 插件夹带宿主侧未预期的字段)。\n if (capabilities.includes('readTranscript')) {\n host.readTranscript = (input) => callHost('readTranscript', {\n sessionId: input && input.sessionId,\n sinceIndex: input && input.sinceIndex,\n })\n }\n if (capabilities.includes('complete')) {\n host.complete = (input) => callHost('complete', {\n prompt: input && input.prompt,\n maxTokens: input && input.maxTokens,\n })\n }\n // RFC-327 M3:非阻塞通知(同上逐字段提取纪律)。\n if (capabilities.includes('notify')) {\n host.notify = (input) => callHost('notify', {\n level: input && input.level,\n code: input && input.code,\n message: input && input.message,\n })\n }\n return host\n}\n\n// 服务运行期的未捕获错误 → 上报宿主(计入崩溃,触发退避重启)。不 process.exit:\n// 让宿主决定重启策略,worker 自杀会丢失错误上下文。\nprocess.on('uncaughtException', (err) => {\n __send({ type: '${SERVICE_MSG.crashed}', error: (err && err.message) || String(err) })\n})\nprocess.on('unhandledRejection', (err) => {\n __send({ type: '${SERVICE_MSG.crashed}', error: (err && err.message) || String(err) })\n})\n\nparentPort.on('message', async (msg) => {\n if (msg.type === '${SERVICE_MSG.hostResult}') {\n const pending = hostCalls.get(msg.callId)\n if (!pending) return\n hostCalls.delete(msg.callId)\n if (msg.error) pending.reject(new Error(msg.error))\n else pending.resolve(msg.result)\n return\n }\n\n if (msg.type === '${SERVICE_MSG.start}') {\n try {\n const mod = require(msg.compiledPath)\n const factory = mod && (mod.default || mod)\n if (typeof factory !== 'function') {\n __send({ type: '${SERVICE_MSG.started}', error: 'plugin entry is not a factory function' })\n return\n }\n // 工厂期 ctx:service 场景下不注入 host(host 只进 service ctx),保持与既有\n // 隔离路径一致的纯数据 ctx。\n const instance = await factory(msg.factoryCtx)\n const services = Array.isArray(instance && instance.services) ? instance.services : []\n const svc = services.find((s) => s && s.id === msg.serviceId)\n if (!svc || typeof svc.start !== 'function') {\n __send({ type: '${SERVICE_MSG.started}', error: 'service not found: ' + msg.serviceId })\n return\n }\n serviceInstance = svc\n // RFC-303 M5:scoped persistence——storageDir 由宿主 sanitize + mkdir 后下发\n // (写面已被 execArgv 收窄到该目录,实例化即真实可写;无权限时 Node 权限模型拒绝)。\n let storage = undefined\n if (msg.serviceCtx.hasStorageCapability && msg.serviceCtx.storageDir) {\n storage = createScopedStorage(msg.serviceCtx.storageDir, msg.serviceCtx.pluginId, msg.serviceCtx.workspaceKey)\n }\n await svc.start({\n pluginId: msg.serviceCtx.pluginId,\n workspaceDir: msg.serviceCtx.workspaceDir,\n workspaceKey: msg.serviceCtx.workspaceKey,\n logger: {\n info: (m) => __send({ type: '${SERVICE_MSG.hostCall}', callId: 'log', method: 'log.info', params: { msg: String(m) } }),\n warn: (m) => __send({ type: '${SERVICE_MSG.hostCall}', callId: 'log', method: 'log.warn', params: { msg: String(m) } }),\n error: (m) => __send({ type: '${SERVICE_MSG.hostCall}', callId: 'log', method: 'log.error', params: { msg: String(m) } }),\n },\n host: buildHost(msg.serviceCtx.hostCapabilities),\n storage,\n // RFC-318 D9:通知注册面。始终提供(不需要能力声明——它只是\"接收宿主推送\"的\n // 被动面,真正的数据面是 readTranscript,那才有独立能力门)。\n onNotify: (handler) => { notifyHandler = typeof handler === 'function' ? handler : null },\n })\n __send({ type: '${SERVICE_MSG.started}' })\n } catch (err) {\n __send({ type: '${SERVICE_MSG.started}', error: (err && err.message) || String(err) })\n }\n return\n }\n\n if (msg.type === '${SERVICE_MSG.notify}') {\n // 未注册 handler → 丢弃(服务可能还没 start 完,或根本不关心通知)。\n if (!notifyHandler) return\n try {\n await notifyHandler(msg.notification)\n } catch (err) {\n // 不静默:handler 异常按运行期崩溃上报,宿主据此退避重启(对齐 uncaughtException 纹理)。\n __send({ type: '${SERVICE_MSG.crashed}', error: (err && err.message) || String(err) })\n }\n return\n }\n\n if (msg.type === '${SERVICE_MSG.stop}') {\n try {\n if (serviceInstance && typeof serviceInstance.stop === 'function') await serviceInstance.stop()\n __send({ type: '${SERVICE_MSG.stopped}' })\n } catch (err) {\n __send({ type: '${SERVICE_MSG.stopped}', error: (err && err.message) || String(err) })\n }\n return\n }\n})\n`\n}\n","/**\n * plugin-service-pool.ts —— RFC-287 M3:常驻服务池(长生命周期 worker)。\n *\n * 与既有两池的分工(三者并列,各自独立生命周期):\n * - `PluginWorkerPool`:一次性 RPC(工具执行),worker 随 load/unload 走。\n * - `PluginProcessPool`:project/repository scope 的子进程+OS 沙箱隔离,同为一次性 RPC。\n * - **本池**:常驻服务,worker 活到服务停止/插件禁用/宿主停机;有崩溃退避重启与二阶停机。\n *\n * ## R7 硬闸门(RFC-287 §4 R7,无此项不得 Done)\n *\n * service worker 创建时经 `execArgv` 注入 Node 权限模型 flag,使 worker 内\n * `child_process` 调用抛 `ERR_ACCESS_DENIED`。这挡的是一条真实提权路径:service 若能\n * spawn,即可直接跑 `otto` CLI 绕过 `startJob` 句柄的全部 schema 红线(R2)。\n *\n * 实测要点(M3 摸底,Node v22):`execArgv` 的权限模型作用域是 **per-worker**——宿主\n * 父进程 spawn 不受影响(对照实验确认)。这是本方案与 G1「worker 池」能共存的前提;\n * 若权限模型是进程级的,启用它会连带废掉宿主自己的 bash 工具。\n *\n * 不用 `--allow-worker`(官方 SecurityWarning 明示\"可能使权限模型失效\")——因此 service\n * 内不能再起嵌套 worker,这是刻意的能力收窄。\n */\n\nimport { Worker } from 'node:worker_threads'\nimport { mkdtempSync, rmSync, writeFileSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport { createLogger } from '@x-otto/shared'\nimport { SERVICE_MSG, buildServiceEntrySrc } from './plugin-service-entry-template'\n\nimport type { PluginServiceNotification, TranscriptMessage } from '@x-otto/plugin'\n\nconst logger = createLogger('@x-otto/coding:plugin-service-pool')\n\n/** 优雅停机上限——超时后强杀(二阶停机)。对齐 onDeactivate 护城河的 5s 量级。 */\nexport const SERVICE_STOP_TIMEOUT_MS = 5_000\n/** 崩溃退避基数;第 n 次重启延迟 = BASE * 2^(n-1),封顶 MAX。 */\nexport const SERVICE_RESTART_BASE_MS = 1_000\nexport const SERVICE_RESTART_MAX_MS = 30_000\n/** 连续崩溃重启上限——超过即标记 failed 永久停止(防崩溃循环烧 CPU)。 */\nexport const SERVICE_MAX_RESTARTS = 5\n\n/**\n * R7:service worker 的 Node 权限模型 flag。\n *\n * 三条禁令,每条都对应一条实测坐实的提权路径(M3 执行期本机实验,非推断):\n *\n * 1. **无 `--allow-child-process`** —— service 若能 spawn,可直接跑 `otto` CLI 绕过\n * `startJob` 句柄的全部 schema 红线(R2)。实测:移除权限 flag 后插件拿到\n * `SPAWN_ALLOWED`,加上后 `ERR_ACCESS_DENIED`(见 plugin-service-pool.test.ts 的\n * 突变验证)。\n *\n * 2. **写权限收窄到临时目录,不是 `--allow-fs-write=*`** —— 首版曾用全盘可写,实测发现\n * 两条持久化提权路径:① service 可写 `~/.otto/plugin-trust.json` **给自己授予任意\n * 高危能力**(信任门形同虚设,直接击穿 R1 零豁免);② 可覆盖 otto 自身 `dist/`\n * 实现下次启动时的任意代码执行。收窄后两者均 `ERR_ACCESS_DENIED`,而 service 真正\n * 需要的能力(读工作区/读 node_modules 完成 require/写自己的状态目录)全部保留。\n *\n * 3. **无 `--allow-worker`** —— 官方 SecurityWarning 明示它\"可能使权限模型失效\",\n * 允许嵌套 worker 等于给出一条绕过本profile 的通道。故 service 内不能再起 worker。\n *\n * 读权限保持 `*`:worker 必须能 require 编译产物与 node_modules 依赖树,收窄读权限会\n * 直接让 service 无法启动;且\"读\"本身不构成提权(凭据面另有既有信任门与\n * `credentialDenyReadPaths` 机制把关)。\n */\n/**\n * R7 补充(RFC-303 M5):`--allow-fs-write` 按插件收窄到其 plugin-state 目录——\n * service 只能写自己的 `${OTTO_HOME}/plugin-state/<pluginId>/` 与临时目录,不能写\n * 其他插件的 plugin-state 子目录(跨插件写 = 提权路径:可篡改他插件状态/信任面)。\n * 保留 tmpdir 写权:部分 npm 依赖库默认写 tmpdir 根目录(如 pino thread-stream),\n * 全部收窄会让既有 service 无法启动。\n *\n * 注意:此函数在**宿主进程**构造 execArgv,worker 内实际生效的是该字符串字面量——\n * 目录必须已存在(Node 权限模型对不存在的路径前缀会拒绝写入,mkdir 前置)。\n */\nexport function buildServiceWorkerExecArgv(storageDir: string): readonly string[] {\n // 写面**只**列本插件 plugin-state 目录:不加 tmpdir——实测发现 Node 权限模型对\n // 同路径多规则取**并集**,而 OTTO_HOME/plugin-state 在 mac 上是 /var/folders/.../T/\n // 的子路径,tmpdir 写面会整体放行 plugin-state(跨插件写 WROTE)。临时文件需求\n // 由插件在自己的 plugin-state 子目录内自建 tmp/ 承载(写面内自包含)。\n const argv = [\n '--experimental-permission',\n '--allow-fs-read=*',\n // 写面 = 本插件 plugin-state 目录(收窄后跨插件写被权限模型拒绝;storageDir 为\n // 空——未声明 storage 能力——时省略该 flag,`--allow-fs-write=` 空参会触发\n // ERR_WORKER_INVALID_EXEC_ARGV)。\n ...(storageDir ? [`--allow-fs-write=${storageDir}`] : []),\n ]\n return argv\n}\n\n/** 兼容既有引用(默认写面 = 空 storageDir + tmpdir;具体收窄见 buildServiceWorkerExecArgv)。\n * **生产路径勿用**——仅测试引用(RFC-303 应用点 C 观察项 3):含 tmpdir 写面会整体放行\n * plugin-state(mac 上 OTTO_HOME/plugin-state 是 tmpdir 子路径,Node 权限模型同路径多规则\n * 取并集 = 跨插件写)。新代码一律走 buildServiceWorkerExecArgv(storageDir)。 */\nexport const SERVICE_WORKER_EXEC_ARGV: readonly string[] = [\n '--experimental-permission',\n '--allow-fs-read=*',\n `--allow-fs-write=${tmpdir()}/`,\n]\n\nlet tmpDir: string | null = null\nlet entryPath: string | null = null\n\nfunction getServiceEntryPath(): string {\n if (entryPath) return entryPath\n if (!tmpDir) tmpDir = mkdtempSync(join(tmpdir(), 'otto-plugin-service-'))\n entryPath = join(tmpDir, 'service-entry.cjs')\n writeFileSync(entryPath, buildServiceEntrySrc())\n return entryPath\n}\n\n/** 宿主侧可被 service 经反向 RPC 调用的能力(method 白名单派发,不做动态转发)。 */\nexport interface ServiceHostBridge {\n /**\n * 派发 agent 作业。**R2 的 origin 强制不在本层**——本回调由宿主装配点提供,\n * 内部固定 `origin:'plugin'`(见 `plugin-module-manager.ts` 的 buildHostCapabilities\n * 与 `app.ts` 的 startPluginJob)。本池只负责把调用送过去,不复制安全逻辑(单一强制点)。\n */\n startJob?: (input: { title: string; prompt: string }) => Promise<{ jobId: string }>\n /**\n * RFC-318 M1:读取会话轨迹。**安全强制全在桥的实现里**(宿主装配点)——sessionId\n * 白名单、投影、secret 掩码、配额;本池只负责派发,不复制安全逻辑(单一强制点,\n * 同 startJob 的 origin 强制纪律)。\n */\n readTranscript?: (input: {\n sessionId: string\n sinceIndex?: number\n }) => Promise<{ messages: readonly TranscriptMessage[]; nextIndex: number }>\n /** RFC-318 M1:模型文本补全。配额(prompt/maxTokens/频率)同样在桥的实现里强制。 */\n complete?: (input: { prompt: string; maxTokens?: number }) => Promise<string>\n /**\n * RFC-327 M3:向用户推送非阻塞通知。**内容约束与限频全在桥的实现里**(长度上限、\n * 控制字符剥离、滑动窗口);本池只负责派发,不复制安全逻辑(单一强制点)。\n */\n notify?: (input: { level: 'info' | 'warning' | 'error'; code: string; message: string }) => Promise<void>\n}\n\nexport interface ServiceStartInput {\n pluginId: string\n serviceId: string\n compiledPath: string\n workspaceDir: string\n workspaceKey: string\n /** 工厂期 ctx(纯数据,同既有隔离路径)。 */\n factoryCtx: { pluginId: string; pluginDir: string; workspaceDir: string }\n /** 该插件已被授予的 host 能力名(fail-closed:宿主算好后下发,worker 侧照此构造 ctx.host)。 */\n hostCapabilities: string[]\n /** RFC-303 M5:该插件 scoped storage 目录(`plugin-state/<pluginId>/<wsKey>`,宿主已 mkdir)。 */\n storageDir: string\n /** RFC-303 M5:是否已声明 storage 能力(fail-closed:未声明不注入 ctx.storage)。 */\n hasStorageCapability: boolean\n /** 反向 RPC 的宿主侧实现(按 pluginId 绑定,身份不取自消息内容)。 */\n bridge: ServiceHostBridge\n}\n\ntype ServiceStatus = 'starting' | 'running' | 'stopped' | 'failed'\n\ninterface ServiceEntry {\n worker: Worker\n input: ServiceStartInput\n status: ServiceStatus\n restarts: number\n restartTimer?: ReturnType<typeof setTimeout>\n /** 正在优雅停机——收到 exit 不再触发重启。 */\n stopping: boolean\n}\n\n/** 服务全局键:同一插件可贡献多个 service。 */\nfunction keyOf(pluginId: string, serviceId: string): string {\n return `${pluginId}:${serviceId}`\n}\n\nexport class PluginServicePool {\n private readonly services = new Map<string, ServiceEntry>()\n\n /** 已启动的服务键(供 /extension 面板与测试观测)。 */\n get runningKeys(): string[] {\n return [...this.services.entries()].filter(([, e]) => e.status === 'running').map(([k]) => k)\n }\n\n statusOf(pluginId: string, serviceId: string): ServiceStatus | undefined {\n return this.services.get(keyOf(pluginId, serviceId))?.status\n }\n\n restartsOf(pluginId: string, serviceId: string): number {\n return this.services.get(keyOf(pluginId, serviceId))?.restarts ?? 0\n }\n\n /**\n * RFC-318 D9:向一个运行中的 service 推送事件通知(单向,无回执)。\n *\n * 返回是否真的送出——`false` 表示服务不存在或未处于 running(**不抛**:通知是尽力而为的\n * 旁路信号,宿主主循环不该因为某个插件没起来而出错)。调用方据此决定是否记账/降级。\n */\n notify(pluginId: string, serviceId: string, notification: PluginServiceNotification): boolean {\n const entry = this.services.get(keyOf(pluginId, serviceId))\n // 不查 `entry.stopping`:`stop()` 置位后**同步**把 entry 移出 map(见 stop() 的\n // `this.services.delete(key)`),停机中的服务在这里必然已经查不到——加该条件是不可达\n // 的死逻辑。真正可达的非 running 态是 'starting' / 'failed'(entry 仍在 map 里)。\n if (!entry || entry.status !== 'running') return false\n try {\n entry.worker.postMessage({ type: SERVICE_MSG.notify, notification })\n return true\n } catch {\n // worker 已终止(竞态:status 判定与 postMessage 之间退出)——按未送出处理。\n return false\n }\n }\n\n /** 该插件当前处于 running 的 serviceId 列表(通知广播用:调用方无需知道插件的服务命名)。 */\n runningServiceIds(pluginId: string): string[] {\n const prefix = `${pluginId}:`\n return [...this.services.entries()]\n .filter(([k, e]) => k.startsWith(prefix) && e.status === 'running')\n .map(([k]) => k.slice(prefix.length))\n }\n\n /** 启动一个常驻服务(幂等:同键已在跑则先停后启)。 */\n async start(input: ServiceStartInput): Promise<{ error?: string }> {\n const key = keyOf(input.pluginId, input.serviceId)\n await this.stop(input.pluginId, input.serviceId)\n return this.spawnAndStart(key, input, 0)\n }\n\n private spawnAndStart(\n key: string,\n input: ServiceStartInput,\n restarts: number,\n ): Promise<{ error?: string }> {\n return new Promise((resolve) => {\n let worker: Worker\n try {\n worker = new Worker(getServiceEntryPath(), {\n // R7:per-worker 权限模型——阻断 child_process 逃逸;写面按插件收窄到\n // 自己的 plugin-state 目录(RFC-303 M5)。\n execArgv: [...buildServiceWorkerExecArgv(input.storageDir)],\n })\n } catch (err) {\n logger.error({ key, err: String(err) }, 'failed to create service worker')\n resolve({ error: String(err) })\n return\n }\n // 常驻服务不应钉死事件循环——宿主 /exit 时靠显式 disposeAll 收敛,\n // 而不是靠 worker 句柄把进程吊住(对齐既有两池的 unref 纪律)。\n worker.unref()\n\n const entry: ServiceEntry = { worker, input, status: 'starting', restarts, stopping: false }\n this.services.set(key, entry)\n\n let settled = false\n const settle = (r: { error?: string }): void => {\n if (settled) return\n settled = true\n resolve(r)\n }\n\n worker.on('message', (msg: { type: string; error?: string; callId?: string; method?: string; params?: unknown }) => {\n void this.onWorkerMessage(key, entry, msg, settle)\n })\n\n worker.on('error', (err) => {\n logger.warn({ key, err: String(err) }, 'service worker error')\n entry.status = 'failed'\n settle({ error: String(err) })\n this.scheduleRestart(key, entry)\n })\n\n worker.on('exit', () => {\n if (entry.stopping) return\n // 非预期退出(含 R7 阻断导致的崩溃)——按崩溃处理。\n if (entry.status !== 'failed') entry.status = 'failed'\n settle({ error: 'service worker exited unexpectedly' })\n this.scheduleRestart(key, entry)\n })\n\n worker.postMessage({\n type: SERVICE_MSG.start,\n compiledPath: input.compiledPath,\n serviceId: input.serviceId,\n factoryCtx: input.factoryCtx,\n serviceCtx: {\n pluginId: input.pluginId,\n workspaceDir: input.workspaceDir,\n workspaceKey: input.workspaceKey,\n hostCapabilities: input.hostCapabilities,\n storageDir: input.storageDir,\n hasStorageCapability: input.hasStorageCapability,\n },\n })\n })\n }\n\n /**\n * 反向 RPC 派发 + 生命周期消息处理。\n *\n * **身份鉴定(R1 在 RPC 层的对应物)**:`entry` 由 `key` 从连接(worker 实例)侧查得,\n * 能力桥 `entry.input.bridge` 是宿主装配时按该插件绑定的——**绝不读 msg 里的任何身份\n * 字段**。即便 worker 侧伪造消息内容,也只能调到自己那份 bridge。\n */\n private async onWorkerMessage(\n key: string,\n entry: ServiceEntry,\n msg: { type: string; error?: string; callId?: string; method?: string; params?: unknown },\n settle: (r: { error?: string }) => void,\n ): Promise<void> {\n if (msg.type === SERVICE_MSG.started) {\n if (msg.error) {\n entry.status = 'failed'\n logger.warn({ key, err: msg.error }, 'plugin service failed to start')\n settle({ error: msg.error })\n this.scheduleRestart(key, entry)\n } else {\n entry.status = 'running'\n logger.info({ key }, 'plugin service started')\n settle({})\n }\n return\n }\n\n if (msg.type === SERVICE_MSG.crashed) {\n logger.warn({ key, err: msg.error }, 'plugin service crashed at runtime')\n entry.status = 'failed'\n this.scheduleRestart(key, entry)\n return\n }\n\n if (msg.type === SERVICE_MSG.hostCall) {\n await this.dispatchHostCall(key, entry, msg)\n return\n }\n }\n\n /** method 白名单派发——未知 method 一律 error,不做动态转发(防能力面被反射式扩大)。 */\n private async dispatchHostCall(\n key: string,\n entry: ServiceEntry,\n msg: { callId?: string; method?: string; params?: unknown },\n ): Promise<void> {\n const { callId, method, params } = msg\n\n // 日志通道:worker 侧 logger 复用 hostCall 管道(callId==='log',不需要回执)。\n if (method === 'log.info' || method === 'log.warn' || method === 'log.error') {\n const text = (params as { msg?: string } | undefined)?.msg ?? ''\n const level = method.slice('log.'.length) as 'info' | 'warn' | 'error'\n logger[level]({ pluginId: entry.input.pluginId, serviceId: entry.input.serviceId }, text)\n return\n }\n\n if (!callId) return\n\n const reply = (result?: unknown, error?: string): void => {\n try {\n entry.worker.postMessage({ type: SERVICE_MSG.hostResult, callId, result, error })\n } catch {\n /* worker 已终止——丢弃回执 */\n }\n }\n\n if (method === 'startJob') {\n const startJob = entry.input.bridge.startJob\n if (!startJob) {\n reply(undefined, 'capability not granted: agent.dispatch')\n return\n }\n const p = params as { title?: string; prompt?: string } | undefined\n try {\n // 逐字段提取——不透传 worker 侧对象(同 M2 buildHostCapabilities 的纪律:\n // 绝不 spread,否则插件可夹带 origin/skipPermissions 等字段)。\n const res = await startJob({ title: String(p?.title ?? ''), prompt: String(p?.prompt ?? '') })\n reply(res)\n } catch (err) {\n reply(undefined, err instanceof Error ? err.message : String(err))\n }\n return\n }\n\n // RFC-318 M1:两个新能力——同 startJob 纪律,逐字段提取入参、桥缺失即 fail-closed 拒绝。\n if (method === 'readTranscript') {\n const readTranscript = entry.input.bridge.readTranscript\n if (!readTranscript) {\n reply(undefined, 'capability not granted: session.read')\n return\n }\n const p = params as { sessionId?: unknown; sinceIndex?: unknown } | undefined\n try {\n const sinceIndex = typeof p?.sinceIndex === 'number' ? p.sinceIndex : undefined\n const res = await readTranscript({ sessionId: String(p?.sessionId ?? ''), sinceIndex })\n reply(res)\n } catch (err) {\n reply(undefined, err instanceof Error ? err.message : String(err))\n }\n return\n }\n\n // RFC-327 M3:notify 同上纪律——逐字段提取、桥缺失即 fail-closed 拒绝。\n // level 白名单化:非法值归一为 'info',不透传 worker 侧任意字符串到 UI 层。\n if (method === 'notify') {\n const notify = entry.input.bridge.notify\n if (!notify) {\n reply(undefined, 'capability not granted: user.notify')\n return\n }\n const p = params as { level?: unknown; code?: unknown; message?: unknown } | undefined\n const rawLevel = String(p?.level ?? 'info')\n const level: 'info' | 'warning' | 'error' =\n rawLevel === 'warning' || rawLevel === 'error' ? rawLevel : 'info'\n try {\n await notify({ level, code: String(p?.code ?? ''), message: String(p?.message ?? '') })\n reply(undefined)\n } catch (err) {\n reply(undefined, err instanceof Error ? err.message : String(err))\n }\n return\n }\n\n // A service worker has no host-signed, per-turn InteractionTarget. Never route an ask\n // by active/recent session or plugin-provided ids: either choice could cross chat principals.\n if (method === 'ask') {\n reply(undefined, 'capability not granted: user.ask')\n return\n }\n\n if (method === 'complete') {\n const complete = entry.input.bridge.complete\n if (!complete) {\n reply(undefined, 'capability not granted: llm.complete')\n return\n }\n const p = params as { prompt?: unknown; maxTokens?: unknown } | undefined\n try {\n const maxTokens = typeof p?.maxTokens === 'number' ? p.maxTokens : undefined\n const res = await complete({ prompt: String(p?.prompt ?? ''), maxTokens })\n reply(res)\n } catch (err) {\n reply(undefined, err instanceof Error ? err.message : String(err))\n }\n return\n }\n\n logger.warn({ key, method }, 'plugin service requested unknown host method (rejected)')\n reply(undefined, `unknown host method: ${String(method)}`)\n }\n\n /** 崩溃退避重启;超过上限标记 failed 永久停止。 */\n private scheduleRestart(key: string, entry: ServiceEntry): void {\n if (entry.stopping) return\n if (entry.restartTimer) return\n if (entry.restarts >= SERVICE_MAX_RESTARTS) {\n logger.error(\n { key, restarts: entry.restarts },\n 'plugin service exceeded restart limit, giving up (no further restarts)',\n )\n entry.status = 'failed'\n return\n }\n const nextRestarts = entry.restarts + 1\n const delay = Math.min(SERVICE_RESTART_BASE_MS * 2 ** (nextRestarts - 1), SERVICE_RESTART_MAX_MS)\n logger.warn({ key, attempt: nextRestarts, delayMs: delay }, 'restarting plugin service after crash')\n const timer = setTimeout(() => {\n entry.restartTimer = undefined\n const current = this.services.get(key)\n if (!current || current.stopping) return\n void this.spawnAndStart(key, entry.input, nextRestarts)\n }, delay)\n timer.unref?.()\n entry.restartTimer = timer\n }\n\n /**\n * 停止一个服务:二阶停机——先请 worker 优雅 `stop()`,超时则强杀。\n *\n * 服务可能持有外部连接(DB/WebSocket),强杀会丢数据,故先给优雅窗口;但单个服务\n * 挂起不得拖死其余服务的卸载(对齐 onDeactivate 护城河纹理)。\n */\n async stop(pluginId: string, serviceId: string): Promise<void> {\n const key = keyOf(pluginId, serviceId)\n const entry = this.services.get(key)\n if (!entry) return\n entry.stopping = true\n if (entry.restartTimer) {\n clearTimeout(entry.restartTimer)\n entry.restartTimer = undefined\n }\n this.services.delete(key)\n\n const graceful = new Promise<boolean>((resolve) => {\n const onMsg = (msg: { type?: string }): void => {\n if (msg?.type === SERVICE_MSG.stopped) {\n entry.worker.off('message', onMsg)\n resolve(true)\n }\n }\n entry.worker.on('message', onMsg)\n try {\n entry.worker.postMessage({ type: SERVICE_MSG.stop })\n } catch {\n resolve(false)\n }\n })\n const timeout = new Promise<boolean>((resolve) => {\n const t = setTimeout(() => resolve(false), SERVICE_STOP_TIMEOUT_MS)\n t.unref?.()\n })\n\n const clean = await Promise.race([graceful, timeout])\n if (!clean) {\n logger.warn(\n { key, timeoutMs: SERVICE_STOP_TIMEOUT_MS },\n 'plugin service did not stop gracefully, terminating (state may be lost)',\n )\n }\n entry.status = 'stopped'\n try {\n await entry.worker.terminate()\n } catch {\n /* best-effort */\n }\n }\n\n /** 停止某插件的全部服务(插件禁用/卸载路径)。 */\n async stopPlugin(pluginId: string): Promise<void> {\n const prefix = `${pluginId}:`\n const keys = [...this.services.keys()].filter((k) => k.startsWith(prefix))\n for (const k of keys) {\n const serviceId = k.slice(prefix.length)\n await this.stop(pluginId, serviceId)\n }\n }\n\n /** 宿主停机:停全部服务并清理临时目录。 */\n async dispose(): Promise<void> {\n const keys = [...this.services.keys()]\n for (const k of keys) {\n const idx = k.indexOf(':')\n await this.stop(k.slice(0, idx), k.slice(idx + 1))\n }\n if (tmpDir) {\n try {\n rmSync(tmpDir, { recursive: true, force: true })\n } catch {\n /* best-effort */\n }\n tmpDir = null\n entryPath = null\n }\n }\n}\n\n/** 全局单例(对齐既有两池的 getter 纹理)。 */\nlet sharedServicePool: PluginServicePool | null = null\nexport function getPluginServicePool(): PluginServicePool {\n if (!sharedServicePool) sharedServicePool = new PluginServicePool()\n return sharedServicePool\n}\nexport function setPluginServicePool(pool: PluginServicePool | null): void {\n sharedServicePool = pool\n}\nexport async function disposePluginServicePool(): Promise<void> {\n await sharedServicePool?.dispose()\n sharedServicePool = null\n}\n","/**\n * transcript-projection.ts —— RFC-318 M1:会话轨迹出境前的投影与脱敏。\n *\n * **为什么是投影而非透传**(RFC-318 D2,评审坐实的隐私缺口):内部 `Message` 携带\n * `tool_call.arguments`(可能含密钥、完整文件内容)、工具结果原始结构与 system 通知。\n * 插件持有 `llm.complete` 时这些内容可被拼进 prompt 送往模型 API——即真实的数据外流路径。\n * 故出境结构在**类型层就没有**可填敏感字段的位置:只留 `{role, text, toolName?}`。\n *\n * **为什么保留 toolName**:执行路径骨架(grep→read→edit)是\"最佳路径\"归纳的关键\n * procedural 信号,缺了它 episode 只剩自然语言描述、归纳质量塌陷;而工具名本身不含\n * 用户数据——真正的载体是 arguments,仍然不出境。\n */\n\nimport type { Message } from '@x-otto/ai'\nimport type { TranscriptMessage } from '@x-otto/plugin'\n\n/**\n * 常见凭据形态的掩码规则。\n *\n * 定位是**最后一道兜底**而非唯一防线:主防线是\"结构上不出境\"(arguments 根本不投影)。\n * 因此这里追求低误伤下的高频命中,不做启发式熵检测(会把正常 base64 数据/hash 全打码,\n * 反而破坏归纳信号)。\n */\nconst SECRET_PATTERNS: readonly RegExp[] = [\n /\\bsk-[A-Za-z0-9_-]{16,}/g, // OpenAI / Anthropic 风格\n /\\bgh[pousr]_[A-Za-z0-9]{20,}/g, // GitHub token\n /\\bAKIA[0-9A-Z]{16}\\b/g, // AWS access key id\n /\\bxox[abposr]-[A-Za-z0-9-]{10,}/g, // Slack\n /\\bBearer\\s+[A-Za-z0-9._~+/-]{20,}=*/g, // Authorization header\n /\\beyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g, // JWT\n]\n\n/** 把文本里的常见凭据形态替换为 `***`。 */\nexport function maskSecrets(text: string): string {\n let out = text\n for (const re of SECRET_PATTERNS) out = out.replace(re, '***')\n return out\n}\n\n/** 取内容块里的纯文本(忽略图片/thinking/a2ui——它们要么无归纳价值,要么是敏感面)。 */\nfunction textOfContent(content: unknown): string {\n if (typeof content === 'string') return content\n if (!Array.isArray(content)) return ''\n const parts: string[] = []\n for (const c of content) {\n const part = c as { type?: string; text?: string }\n if (part?.type === 'text' && typeof part.text === 'string') parts.push(part.text)\n }\n return parts.join('\\n')\n}\n\n/**\n * 单条消息投影。返回 `null` 表示该条不出境(system 通知、内部合成消息)。\n *\n * assistant 条目的 `toolName` 取其发起的第一个 tool_call 名——一条 assistant 消息可含多个\n * tool_call,但对\"执行路径骨架\"而言首个已足够标记该步意图;完整并发调用的细节属于\n * arguments 级信息,本就不出境。\n */\nfunction projectOne(message: Message): TranscriptMessage | null {\n if (message.role === 'user') {\n if (message.internal) return null\n return { role: 'user', text: maskSecrets(textOfContent(message.content)) }\n }\n if (message.role === 'assistant') {\n const calls = message.content.filter((c) => c.type === 'tool_call') as { name?: string }[]\n const toolName = calls[0]?.name\n const projected: TranscriptMessage = {\n role: 'assistant',\n text: maskSecrets(textOfContent(message.content)),\n }\n if (toolName) projected.toolName = toolName\n return projected\n }\n if (message.role === 'tool_result') {\n if (message.internal) return null\n return {\n role: 'tool_result',\n text: maskSecrets(textOfContent(message.content)),\n toolName: message.toolName,\n }\n }\n // system 通知等:不出境。\n return null\n}\n\n/**\n * 投影一段会话切片。\n *\n * @param messages 完整历史(调用方给全量,本函数负责切片)\n * @param sinceIndex 上次读到的下标(含义:从该下标开始读)\n * @param maxMessages 单次上限——超出时取**最近** maxMessages 条(新的比旧的更有归纳价值)\n * @returns `nextIndex` 是下次该传的 sinceIndex,始终等于 `messages.length`(即便本次因\n * 上限丢弃了中间段——如实告诉调用方\"已推进到末尾\",避免它反复重读同一批旧消息)\n */\nexport function projectTranscript(\n messages: readonly Message[],\n sinceIndex: number,\n maxMessages: number,\n): { messages: TranscriptMessage[]; nextIndex: number } {\n const from = Math.max(0, Math.min(sinceIndex, messages.length))\n const slice = messages.slice(from)\n const capped = slice.length > maxMessages ? slice.slice(slice.length - maxMessages) : slice\n const projected: TranscriptMessage[] = []\n for (const m of capped) {\n const p = projectOne(m)\n if (p) projected.push(p)\n }\n return { messages: projected, nextIndex: messages.length }\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync, type Dirent } from 'node:fs'\nimport { createHash } from 'node:crypto'\nimport { createRequire } from 'node:module'\nimport { dirname, join, relative, sep } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { build as EsbuildBuild } from 'esbuild'\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport type { PluginContext, PluginFactory, PluginModule } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\nimport { isPluginTrusted, evaluatePluginTrust, PLUGIN_API_VERSION } from '@x-otto/plugin'\nimport { SDK_VERSION } from '@x-otto/env'\n\n/**\n * module-loader.ts —— 插件代码模块加载器(RFC-082 M082-04-02,R10 安全敏感)。\n *\n * 把作者写的 `plugin.ts`(`export default definePlugin(ctx => ({...}))`)编译 + 主进程内加载 + 调工厂,\n * 产出 `PluginModule`(hooks/computed 贡献/生命周期)。本模块只**加载**、不接线——\n * 拆解喂 hook 注册表 / `mergeContributions` 由调用方在 reloadCapabilities 做(M082-04-03,D1/R1 无中央执行器)。\n *\n * 安全(RFC-082 R4/R10/D3):\n * - 项目级插件**未信任 → 不编译/不执行**(fail-closed)。用户级 `~/.otto/plugins` 自有可信。\n * - 一旦执行,`plugin.ts` 是主进程内全 Node 权限代码(信任门门控*是否*执行,非沙箱)。\n *\n * `@x-otto/plugin` 的 `definePlugin` 是纯 identity、类型编译期擦除 → 编译时用 inline shim 替换,\n * 免去对该包的文件系统解析(也使编译产物自包含、不依赖运行时 require '@x-otto/plugin'——生产环境\n * 用户安装的插件目录不会有该包的 node_modules,见下方 SDK_SHIM_PLUGIN 注释)。\n */\n\nconst logger = createLogger('@x-otto/coding:plugin-module-loader')\nconst requireModule = createRequire(import.meta.url)\n\nconst PLUGIN_ENTRY_CANDIDATES = ['plugin.ts', 'plugin.tsx'] as const\n\nexport interface LoadedPluginModule {\n pluginId: string\n module: PluginModule\n /**\n * RFC-287 M3:编译产物绝对路径(`plugin-dist/plugin.cjs` 或既有预编译产物)。\n *\n * 只有 services 轴需要它——常驻服务跑在独立的 service worker 里,worker 必须能\n * `require()` 到同一份产物才能取到 `module.services`(函数体无法经 postMessage 传递)。\n * 其余轴消费的是已在本进程求值好的 `module` 对象,不需要路径。\n *\n * 隔离加载路径(`module-loader-isolated.ts`)同样回填此字段——那条路径的 services\n * 也要靠它启动服务 worker。\n */\n compiledPath?: string\n}\n\nfunction findEntry(pluginDir: string): string | null {\n for (const c of PLUGIN_ENTRY_CANDIDATES) {\n const p = join(pluginDir, c)\n if (existsSync(p)) return p\n }\n return null\n}\n\n/**\n * definePlugin 是 identity → 编译时注入 inline shim,无需解析真实 `@x-otto/plugin` 包\n * (bug 修复,RFC-105 M105-6:`@x-otto/plugin-sdk` 是 RFC-082 设计草案期的包名,M-C2\n * 决策把作者面 `definePlugin`/SDK 类型并入了 `@x-otto/plugin` 本体——`packages/plugin/src/index.ts:49`\n * `export { definePlugin } from './sdk'`——但本 shim 的 `onResolve` filter 从未跟着这次\n * 并入更新,一直拦截着一个不存在的包名。`otto extension create --with-tool` 生成的 scaffold\n * 模板正确地 `import { definePlugin } from '@x-otto/plugin'`(真实包名),却因为 shim 拦截\n * 目标不对而编译失败 `Could not resolve \"@x-otto/plugin\"`——本次实现 `compileCodeModuleAndReport`\n * dev 即时反馈时端到端验证 scaffold 产物才发现,此前从未有测试真正跑通 `--with-tool` 产物的\n * 完整编译流程(既有测试只验证了生成的源码字符串内容,未验证可编译性)。生产环境影响:\n * 用户安装的插件目录没有 `@x-otto/plugin` 的 node_modules,`loadPluginModule` 编译失败会被\n * fail-soft 吞掉(catch → null + warn,`plugin.ts` 代码轴静默不加载),此 bug 长期不可见\n * 正是这个吞错误路径的直接后果。\n *\n * **设计约束(对抗审核提示,需插件作者知晓)**:shim 内联的虚拟模块只导出 `definePlugin`\n * 这一个值——`@x-otto/plugin` 的其余值级导出(`parsePluginManifest`/`discoverPlugins`/\n * `dispatch` 等宿主侧 API)在 `plugin.ts` 里 import 会解析为 `undefined`(运行时\n * `TypeError: xxx is not a function`)。这是有意为之,非缺陷:插件作者只应 import\n * `definePlugin`,需要的宿主能力经工厂函数入参 `ctx: PluginContext` 注入(`sdk.ts:86`),\n * 不应绕过信任门直接调用宿主内部函数。类型级 import(`PluginContext`/`PluginModule` 等)\n * 不受影响——esbuild 编译期擦除类型,不产生运行时依赖。\n *\n * **`@x-otto/extension` 同拦(RFC-202 M2)**:RFC-113 D1 定稿 `@x-otto/extension` 为第三方\n * extension 作者统一入口(re-export `definePlugin`),但本 shim 此前只拦 `@x-otto/plugin`——\n * 遵循官方文档从 `@x-otto/extension` import `definePlugin` 的作者会编译失败\n * `Could not resolve \"@x-otto/extension\"`(与上述 RFC-105 M105-6 \"shim 拦截目标过时\"同缺陷类,\n * 此前未爆发仅因四个第一方插件全部 import `@x-otto/plugin`)。两个包名等价映射到同一虚拟模块;\n * `@x-otto/extension` 的非 definePlugin 值级导出(`definePanelBackend`/`defineFileViewer`/\n * `createStdioServer` 等有真实运行时逻辑,非 identity)在 shim 下同样解析为 `undefined`——\n * 与上段设计约束同边界:插件 bundle 内用到它们时本就需要真实依赖或打包,shim 只覆盖 identity。\n */\nconst SDK_SHIM_PLUGIN = {\n name: 'otto-plugin-sdk-shim',\n setup(b: Parameters<NonNullable<Parameters<typeof EsbuildBuild>[0]['plugins']>[number]['setup']>[0]): void {\n b.onResolve({ filter: /^@otto\\/(plugin|extension)$/ }, () => ({ path: 'otto-plugin-sdk', namespace: 'otto-sdk-shim' }))\n b.onLoad({ filter: /.*/, namespace: 'otto-sdk-shim' }, () => ({\n contents: 'export const definePlugin = (f) => f;',\n loader: 'js' as const,\n }))\n },\n}\n\n/**\n * `pino`/`thread-stream`/`jsonc-parser` external 化的绝对路径解析基准目录(RFC-148 终局\n * 修复,2026-07-11,生产阻断 bug):以上三个包 external 化的原始假设——\"运行时从**插件\n * 目录**的 node_modules 逐级向上能 resolve 到它们\"——在 pnpm 严格 node_modules 布局下不\n * 成立:这三个包只是 `@x-otto/shared` 的直接依赖(symlink 进 `packages/shared/node_modules/`),\n * 不会被提升到消费方插件目录(该目录的 `package.json` 通常并未直接声明它们)。此前从未\n * 暴露是因为:`plugin-anthropic`/`plugin-deepseek` 不直接调用 `createLogger`(不触发 pino\n * 加载路径)、`plugin-github-copilot` 在真实环境从未被安装过。`extensions/plugin-otto-wire-protocols`\n * 是第一个同时满足\"用 `@x-otto/shared` 的 `createLogger`\"+\"被本加载器编译执行\"两个条件的\n * 插件,暴露出 `Cannot find module 'pino'`(生产阻断,非信任门问题——诊断时曾误判为插件\n * 未信任,实际 `evaluatePluginTrust` 早已放行,真正卡住的是这里)。\n *\n * 修复:不再依赖\"运行时从插件目录能 resolve 到裸模块名\"这个不成立的假设,改为在**编译期**\n * 用宿主自身(`@x-otto/coding`,经由 `@x-otto/shared` 的真实 dist 位置)的模块解析上下文把这三\n * 个包解析成**绝对路径**,esbuild `onResolve` 返回 `{ path: <绝对路径>, external: true }`\n * 后,产物里的 `require()` 调用即变成绝对路径——不再依赖插件目录的 node_modules 布局,且\n * pino 自身的 `__dirname` 相对路径(worker_threads 脚本加载)依然基于真实文件系统位置,\n * 保留了原修复的 worker 线程创建效果(同一份注释见下方 `compilePluginModule` doc,两处\n * 修复动机不冲突:external 化本身仍然必要,只是路径解析方式从\"运行时裸模块名\"改为\n * \"编译期绝对路径\")。解析失败(如未来 `@x-otto/shared` 移除某个依赖)时静默回退到原始裸\n * 模块名 external(保底不比修复前更差——退化为此前的已知行为,不引入新故障模式)。\n */\nfunction resolveExternalAbsolutePaths(): Record<string, string> {\n const resolved: Record<string, string> = {}\n try {\n // `@x-otto/shared` 是纯 ESM 包(`package.json` 只有 `\"import\"` 导出条件,无 `\"require\"`)\n // ——CJS `require.resolve('@x-otto/shared')` 会直接抛 `ERR_PACKAGE_PATH_NOT_EXPORTED`\n // (模块顶部注释已记录的已知坑)。`import.meta.resolve` 是 ESM 原生解析器,能正确处理\n // `exports` 条件拿到真实 dist 路径;`pino`/`thread-stream`/`jsonc-parser` 本身是 CJS\n // 兼容包,从该目录起用 `requireModule.resolve` 解析不受此限制。\n const sharedUrl = import.meta.resolve('@x-otto/shared')\n const sharedDir = dirname(fileURLToPath(sharedUrl))\n const pinoPath = requireModule.resolve('pino', { paths: [sharedDir] })\n resolved['pino'] = pinoPath\n resolved['thread-stream'] = requireModule.resolve('thread-stream', { paths: [dirname(pinoPath)] })\n resolved['jsonc-parser'] = requireModule.resolve('jsonc-parser', { paths: [sharedDir] })\n } catch (err) {\n logger.warn({ err: String(err) }, 'failed to pre-resolve pino/thread-stream/jsonc-parser absolute paths, falling back to bare-specifier external (pre-fix behavior)')\n }\n return resolved\n}\n\n/**\n * 把 `external` 列表中已能解析出绝对路径的包,编译期固化为绝对路径 external(见上方\n * `resolveExternalAbsolutePaths` doc);解析不到的包(如 `react`/`ink`,当前无插件真正\n * import 它们,故未触发过修复前的同类故障)维持原始裸模块名 external,行为不变。\n */\nfunction createAbsoluteExternalPlugin(resolvedPaths: Record<string, string>): {\n name: string\n setup(b: Parameters<NonNullable<Parameters<typeof EsbuildBuild>[0]['plugins']>[number]['setup']>[0]): void\n} {\n const names = Object.keys(resolvedPaths)\n return {\n name: 'otto-plugin-absolute-external',\n setup(b): void {\n if (names.length === 0) return\n const filter = new RegExp(`^(${names.map((n) => n.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')).join('|')})$`)\n b.onResolve({ filter }, (args: { path: string }) => ({\n path: resolvedPaths[args.path],\n external: true,\n }))\n },\n }\n}\n\n/**\n * 编译 `plugin.ts` → `plugin-dist/plugin.cjs`(esbuild,CJS)。无入口 → null。\n * `@x-otto/plugin` 经 shim 内联(仅 `definePlugin`);react/ink/jsonc-parser external(若被 import)。\n *\n * `@x-otto/ai`/`@x-otto/provider` **均不** external(provider 插件化收尾试验后订正,\n * 2026-07-09;RFC-140 T13 复核,2026-07-10):最初尝试把 `@x-otto/ai` external(同\n * react/ink),让首方代码式 provider 插件(如 GitHub Copilot)`import { createCopilotProvider }\n * from '@x-otto/ai'` 时复用宿主同一份实例,运行时走 Node 原生 `require('@x-otto/ai')` 解析。\n * 但 `@x-otto/ai` 是纯 ESM 包(`package.json` `\"type\": \"module\"`,只有 `\"import\"` 导出\n * 条件、无 `\"require\"`),CJS 插件产物用 `require()` 加载会直接抛\n * `ERR_PACKAGE_PATH_NOT_EXPORTED`(已用真实编译+加载复现)。react/ink 能 external 是\n * 因为它们本身双格式导出(CJS 兼容),@x-otto/ai 不是。修复:改回打包进插件产物(同其余\n * 非 react/ink 的 import),接受少量体积重复,换取零构建链改动。\n *\n * RFC-140 T13 复核(2026-07-10):`@x-otto/provider`(从 `@x-otto/ai` 拆出的 provider 插入点\n * 公开包,见 RFC-140 D4/D6)继承了同样的纯 ESM `exports` 写法,天然会踩同一个坑——\n * 原计划让 `module-loader.ts` external 掉 `@x-otto/provider`(\"provider 是独立公开包,\n * external 后走 Node 原生 require 解析、不再打包进每个插件产物\")。**已实测复现此方案\n * 目前不可行**:给 `@x-otto/provider` 单独加 CJS 双格式输出后,它自身可被 `require()`,\n * 但其传递依赖 `@x-otto/shared`(纯 ESM,被全仓 ~20+ 个包依赖)在同一条 require 链上\n * 会炸同样的 `ERR_PACKAGE_PATH_NOT_EXPORTED`——问题没被解决,只是从\"provider 不能\n * external\"变成\"provider 能 external 但 shared 不能\"。给 `@x-otto/shared` 也补双格式\n * 输出是仓库级构建体系改造(影响 20+ 个包的发布产物形态),超出 M4 里程碑范围,需要\n * 独立 RFC 评估(非本次顺带做掉)。**结论:`@x-otto/provider` 继续打包进插件产物**\n * (现状不变,接受体积重复),T13 的 external 简化目标标记为阻塞、留待后续里程碑。\n *\n * 架构下沉后续(同批次):copilot 的定制协议实现(`createCopilotProvider`)已从\n * `@x-otto/provider` 进一步下沉至 `extensions/plugin-github-copilot/src/provider.ts`(厂商\n * 专属实现,唯一消费方是该插件自身)。插件现在 `import from './src/provider'`(同目录,\n * 随 esbuild 一起编译打包,不涉及跨包 external 问题),只有其复用的协议基座\n * (`buildChatCompletionsBody`/`streamChatCompletions`)从 `@x-otto/provider` 导入——因\n * `@x-otto/provider` 仍打包进产物(本节结论未变),这条路径不受影响。\n *\n * `jsonc-parser` external(同次收尾追加发现的第二个真实坑):`@x-otto/ai` 传递依赖\n * `@x-otto/shared`(`createLogger` 等),`@x-otto/shared` barrel 导出 `parseJsonc`(依赖\n * `jsonc-parser`,一个 UMD 包,内部工厂函数用 `require2(\"./impl/format\")` 相对路径)。\n * esbuild 把该 UMD 工厂重新打包进 CJS bundle 时,工厂内部的 `require2` 调用点与其\n * \"./impl/*\" 相对路径的原始文件系统位置不再对应,运行时抛 `Cannot find module\n * './impl/format'`(已用真实编译+加载复现,与 provider 插件本身逻辑无关,是 esbuild\n * 对该特定 UMD 包的已知打包局限)。external 后运行时走 Node 原生 `require('jsonc-parser')`\n * 从插件目录的 node_modules 解析(该包体积小、无副作用,重复安装成本可忽略)。\n *\n * `pino`/`thread-stream` external(终局 review 发现的 P0 生产阻断修复,2026-07-09):\n * `@x-otto/shared` 的 `createLogger` 用 `pino.transport(...)` 做异步日志分发,pino 内部\n * 用 `worker_threads` 动态 `new Worker(path.join(__dirname, 'lib/worker.js'))` 加载\n * 自身的 worker 脚本——esbuild 把 pino 整体打包进单文件 CJS 产物后,这个相对路径不再\n * 对应任何真实文件,worker 线程创建静默失败但**不产生同步异常**(不会被 `requireModule()`\n * 的 try/catch 捕获),而是遗留悬空的 IPC handle(Socket/MessagePort)不会被垃圾回收,\n * 导致任何 `require()` 了本插件编译产物的宿主进程**无法自然退出**(已用真实子进程实测\n * 复现:仅 `require()` + 构造 provider 工厂,不调用任何网络请求,子进程即挂起直到被\n * SIGKILL——这不是\"测试环境限定\"问题,是 100% 复现的生产级 bug)。修复:把 `pino`\n * (及其内部依赖 `thread-stream`)external,运行时从插件目录的 node_modules 走 Node\n * 原生 require 解析——pino 自身的 `__dirname` 相对路径在真实文件系统位置下能正确解析,\n * worker 线程可正常创建、进程可自然退出(已实测三次复现验证:exit code 从被 kill\n * 的非正常终止变为稳定 0)。\n *\n * 产物用 `.cjs` 扩展名(RFC-130 M131-1 plugin-cursor 实测发现并修复):插件 `package.json`\n * 若声明 `\"type\":\"module\"`,`.js` 会被 Node 当 ESM 解析,`require()` 加载失败。\n */\nexport async function compilePluginModule(pluginDir: string): Promise<string | null> {\n const entry = findEntry(pluginDir)\n if (!entry) return null\n const outDir = join(pluginDir, 'plugin-dist')\n mkdirSync(outDir, { recursive: true })\n const outfile = join(outDir, 'plugin.cjs')\n // 绝对路径 external 修复(见 resolveExternalAbsolutePaths doc):pino/thread-stream/\n // jsonc-parser 编译期固化为绝对路径;解析失败的名字(如 react/ink,当前未触发过同类\n // 故障)保留在裸模块名 external 列表,行为与修复前一致。\n const absolutePaths = resolveExternalAbsolutePaths()\n const bareExternal = ['react', 'ink', 'jsonc-parser', 'pino', 'thread-stream'].filter(\n (name) => !(name in absolutePaths),\n )\n // 惰性加载 esbuild(RFC 启动开销优化):正常启动命中 `findPrebuiltOutfile` 走\"存在即用\"\n // 分支,永不进入本函数——把 esbuild 的 require 收进函数体,使编译器仅在真正需要现场编译\n // 时才被载入内存(顶层静态 import 会让它无条件常驻,即便一次编译都不跑)。esbuild 是 CJS\n // 包(无 exports 字段),用 `requireModule`(createRequire)同步取,不引入异步涟漪。\n const { build } = requireModule('esbuild') as { build: typeof EsbuildBuild }\n await build({\n entryPoints: [entry],\n outfile,\n bundle: true,\n format: 'cjs',\n platform: 'node',\n external: bareExternal,\n jsx: 'transform',\n logLevel: 'silent',\n // `import.meta.url` → CJS 等价物修复(RFC-148 终局收尾,2026-07-11 生产噪音修复):\n // `@x-otto/env` 的 `readPackageVersion()`(`packages/env/src/index.ts`)用 `import.meta.url`\n // 读自身 `package.json` 版本号——esbuild 把 ESM 编译为 CJS 时把 `import.meta` 替换为空\n // 对象存根(`var import_meta = {}`),`import_meta.url` 恒为 `undefined`,\n // `new URL('.', undefined)` 抛 `ERR_INVALID_URL`。该函数已有 try/catch 防护(fail-soft,\n // 版本号退化为 'unknown',不影响功能),但每次插件加载都会在 stderr 打印一段吓人的\n // stack trace,容易被误判为真实故障(`packages/shared/src/logger.ts` 同一根因的\n // `createRequire(import.meta.url)` 已有独立注释记录,未级联到本文件的编译配置修复)。\n // 用 esbuild `define`+`banner` 把 `import.meta.url` 全局替换为真实文件系统路径构造的\n // file: URL(`pathToFileURL(__filename).href`)——产物加载后 `__filename` 指向真实\n // `plugin-dist/plugin.cjs` 路径,虽然与源文件路径不同,但该函数只用它计算相对路径找\n // package.json,找不到时依旧走既有 try/catch 静默降级,不引入新故障模式,只是消除了\n // 原本必然触发的错误路径本身。\n define: { 'import.meta.url': '__otto_plugin_import_meta_url' },\n banner: { js: 'const __otto_plugin_import_meta_url = require(\"url\").pathToFileURL(__filename).href;' },\n plugins: [SDK_SHIM_PLUGIN, createAbsoluteExternalPlugin(absolutePaths)],\n })\n writeArtifactMeta(outDir, pluginDir)\n return outfile\n}\n\n/**\n * RFC-185 D4:编译产物元信息——`plugin-dist/meta.json` 记录编译时的宿主契约版本\n * (`PLUGIN_API_VERSION`)与编译器版本(宿主 SDK_VERSION)。「产物存在即用」策略\n * (RFC-150 D5)下源码修复对存量 `plugin.cjs` 零生效(`4195ed95` 三重失效教训之一);\n * meta 不一致 = 产物陈旧 → 强制重编译,宿主升级后修复自动触达全部已安装插件。\n * 原子写(tmp + rename),编译成功后才写——崩溃窗口最多留下无 meta 的产物(判陈旧重编,\n * 幂等无害)。\n *\n * `sourceHash`(生产事故后补强,2026-07-27):apiVersion+compilerVersion 只能检测\n * **宿主升级**导致的陈旧,无法检测**插件源码本身被修改却未重装**的陈旧——本仓开发/迭代\n * 场景(改 `extensions/plugin-otto-wire-protocols/src/*.ts` 后同步到 `~/.otto/plugins/` 却\n * 不删 `plugin-dist`)下,SDK 版本号不变、meta 判\"新鲜\"、旧 bundle 被直接复用,源码修复零\n * 生效(本次 oxygen adaptive thinking 修复失效的直接根因)。sourceHash 对插件目录下全部源文件\n * (入口 + `src/**`)内容做聚合 sha256,源码任一字节变化即 hash 变 → 判陈旧重编译。用内容\n * hash 而非 mtime:git checkout/cp/rsync 会重置 mtime 但不改内容,mtime 判据会产生大量假阴性\n * (漏判\"其实没变\")与假阳性(误判\"变了\"),内容 hash 精确且与文件系统时间戳无关。\n */\ninterface ArtifactMeta {\n apiVersion: number\n compilerVersion: string\n /** 插件源文件聚合内容 hash(缺省=旧格式产物,判陈旧强制重编译一次以回填)。 */\n sourceHash?: string\n}\n\n/** 参与 sourceHash 的源文件扩展名(编译输入面:TS/JS 源 + JSON 配置)。 */\nconst HASHED_SOURCE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json']\n\n/**\n * 递归收集插件目录下的源文件路径(排除编译产物目录 `plugin-dist`/`panels-dist` 与\n * `node_modules`),供 sourceHash 计算。返回相对 `pluginDir` 的路径列表,排序保证确定性。\n */\nfunction collectSourceFiles(pluginDir: string): string[] {\n const out: string[] = []\n const walk = (absDir: string): void => {\n let entries: Dirent[]\n try {\n entries = readdirSync(absDir, { withFileTypes: true })\n } catch {\n return\n }\n for (const entry of entries) {\n const name = entry.name\n if (name === 'plugin-dist' || name === 'panels-dist' || name === 'node_modules' || name.startsWith('.')) {\n continue\n }\n const abs = join(absDir, name)\n if (entry.isDirectory()) {\n walk(abs)\n } else if (entry.isFile()) {\n const dot = name.lastIndexOf('.')\n const ext = dot >= 0 ? name.slice(dot) : ''\n if (HASHED_SOURCE_EXTENSIONS.includes(ext)) {\n out.push(relative(pluginDir, abs))\n }\n }\n }\n }\n walk(pluginDir)\n return out.sort()\n}\n\n/**\n * 对插件目录下全部源文件做聚合内容 hash(sha256)。哈希内容 = 每个文件的「相对路径 + 字节内容」\n * 序列(路径已排序,跨平台归一化分隔符),故文件重命名/新增/删除/内容变更都会改变结果。\n * 读取失败的单个文件计入其错误标记(不静默跳过,避免\"删到只剩坏文件\"被判等价于旧 hash)。\n */\nfunction computeSourceHash(pluginDir: string): string {\n const files = collectSourceFiles(pluginDir)\n const hash = createHash('sha256')\n for (const rel of files) {\n const normalized = rel.split(sep).join('/')\n hash.update(normalized)\n hash.update('\\0')\n try {\n hash.update(readFileSync(join(pluginDir, rel)))\n } catch (err) {\n hash.update(`<unreadable:${err instanceof Error ? err.message : String(err)}>`)\n }\n hash.update('\\0')\n }\n return hash.digest('hex')\n}\n\nfunction currentArtifactMeta(pluginDir: string): ArtifactMeta {\n return { apiVersion: PLUGIN_API_VERSION, compilerVersion: SDK_VERSION, sourceHash: computeSourceHash(pluginDir) }\n}\n\nfunction writeArtifactMeta(outDir: string, pluginDir: string): void {\n try {\n const metaPath = join(outDir, 'meta.json')\n const tmpPath = join(outDir, `.meta.json.tmp-${process.pid}`)\n writeFileSync(tmpPath, JSON.stringify(currentArtifactMeta(pluginDir)))\n renameSync(tmpPath, metaPath)\n } catch (err) {\n // fail-soft:meta 写失败只导致下次启动多一次重编译,不阻断本次加载。\n logger.warn('Failed to write plugin artifact meta: %s', err instanceof Error ? err.message : String(err))\n }\n}\n\n/** meta 缺失/损坏/不一致(版本或源码 hash)均判陈旧(fail-safe 走重编译,不抛错)。 */\nfunction isArtifactMetaCurrent(pluginDir: string): boolean {\n try {\n const raw = readFileSync(join(pluginDir, 'plugin-dist', 'meta.json'), 'utf8')\n const meta = JSON.parse(raw) as Partial<ArtifactMeta>\n const want = currentArtifactMeta(pluginDir)\n // sourceHash 缺失(旧格式产物)视为不匹配 → 判陈旧,强制重编译一次回填新格式。\n return (\n meta.apiVersion === want.apiVersion &&\n meta.compilerVersion === want.compilerVersion &&\n meta.sourceHash === want.sourceHash\n )\n } catch {\n return false\n }\n}\n\n/**\n * RFC-150 D5:启动/reload 期优先读取已编译产物(\"存在即用\"),跳过 esbuild 重编译。\n *\n * 判据只看**产物是否存在**,不比对入口 mtime(RFC-150 重要事项规则 3——`plugin.ts` 常\n * `import` 同插件目录内的其他源文件,如 `plugin-anthropic` 的\n * `./src/anthropic-oauth-client-identity`,只比对入口文件的 mtime 会漏判这些被依赖文件的\n * 改动,让产物\"假新鲜\")。产物的新鲜度由\n * 生产者一侧负责:`otto extension install`(`installPluginFromPackage`/`installPluginFromDir`)\n * 安装/升级时总是重新编译一次;开发场景的 `otto extension dev` 自带 watch 重编译(\n * `compileCodeModuleAndReport`)。生产环境用户不应手改已安装插件目录内的源码却不重装/\n * 不走 dev 模式——那不是本机制要覆盖的场景。\n *\n * 缺失产物(旧安装、从未编译过)时回退到现编译(`compilePluginModule`),编译成功后\n * 产物落盘在磁盘上,下次调用即可命中\"存在即用\"分支——一次性迁移成本,无需用户感知。\n */\nfunction findPrebuiltOutfile(pluginDir: string): string | null {\n const outfile = join(pluginDir, 'plugin-dist', 'plugin.cjs')\n if (!existsSync(outfile)) return null\n // RFC-185 D4:产物存在还不够——meta.json 的 apiVersion/compilerVersion 必须与当前宿主\n // 一致,否则视为陈旧产物(如宿主升级带来的源码修复未触达 bundle),回 null 走重编译。\n // 无 meta.json 的存量产物(本机制上线前编译的)天然判陈旧,首次启动强制重编译一次。\n if (!isArtifactMetaCurrent(pluginDir)) {\n logger.info('Plugin artifact at %s is stale (meta mismatch/missing) — recompiling', outfile)\n return null\n }\n return outfile\n}\n\n/**\n * 信任门后、主进程内加载插件代码模块(R10)。\n * - 无 plugin.ts → null(无代码入口,正常)。\n * - 项目级未信任 → null + warn(不编译/不执行,fail-closed R4/D3)。\n * - 编译 / require / 工厂任一失败 → null + warn(fail-soft,不崩主进程)。\n */\nexport async function loadPluginModule(\n plugin: DiscoveredPlugin,\n ctx: PluginContext,\n /** RFC-153 D3:加载/编译/工厂失败的旁路通知(可选,宿主层注入),供\n * `PluginHealthTracker.recordError` 写入。不改变现有 fail-soft 返回 null 的控制流。 */\n onLoadError?: (pluginId: string, message: string) => void,\n): Promise<LoadedPluginModule | null> {\n if (!findEntry(plugin.dir)) return null\n\n if ((plugin.scope === 'project' || plugin.scope === 'repository') && !isPluginTrusted(plugin.dir)) {\n logger.warn({ pluginId: plugin.id }, 'project/repository plugin not trusted, skipping code module (fail-closed)')\n return null\n }\n\n // RFC-140 D3(review P0-2):per-capability 门禁此前只在 `plugin-module-manager.ts` 对\n // `providerFactories`/`tools` 的**注册**环节生效(factory 已执行完毕之后)——工厂体内的\n // 副作用(网络调用、写盘等)不受这层门禁保护,是纵深防御的缺口。这里把检查提前到工厂\n // 执行**之前**:manifest 声明了任一 `HIGH_RISK_CAPABILITIES` 成员但尚未被显式授予\n // (`pendingCapabilities` 非空),整个代码模块(含声明式合法的 hooks/monitor 等其余轴)\n // 一律不加载——插件必须先完成能力确认,才能让其代码在主进程内执行一次。\n // 声明式轴(`contributes.providers`/`contributes.oauth` 等)走独立的 `provider-loader.ts`\n // 门控,不受本检查影响(那里从不执行插件代码,无需前置门禁)。\n const verdict = evaluatePluginTrust(plugin)\n if (verdict.pendingCapabilities.length > 0) {\n logger.warn(\n { pluginId: plugin.id, pendingCapabilities: verdict.pendingCapabilities },\n 'plugin declares high-risk capabilities not yet granted, skipping code module entirely before factory execution (fail-closed, RFC-140 D3)',\n )\n return null\n }\n\n let outfile: string | null = findPrebuiltOutfile(plugin.dir)\n if (outfile) {\n logger.info({ pluginId: plugin.id, outfile }, 'using prebuilt plugin module (RFC-150 D5, skip compile)')\n } else {\n try {\n outfile = await compilePluginModule(plugin.dir)\n } catch (err) {\n const message = String(err)\n logger.warn({ pluginId: plugin.id, err: message }, 'plugin.ts compile failed')\n onLoadError?.(plugin.id, `compile failed: ${message}`)\n return null\n }\n }\n if (!outfile) return null\n\n let factory: unknown\n try {\n delete requireModule.cache[outfile] // 防热重载读旧产物(M082-04-03 dev 复编)\n const mod = requireModule(outfile) as { default?: unknown }\n factory = mod?.default ?? mod\n } catch (err) {\n const message = String(err)\n logger.warn({ pluginId: plugin.id, err: message }, 'plugin.ts load failed')\n onLoadError?.(plugin.id, `load failed: ${message}`)\n return null\n }\n if (typeof factory !== 'function') {\n logger.warn({ pluginId: plugin.id }, 'plugin.ts default export is not a factory function')\n onLoadError?.(plugin.id, 'default export is not a factory function')\n return null\n }\n\n try {\n const module = await (factory as PluginFactory)(ctx)\n // compiledPath 回填:services 轴要用它在独立 service worker 里 require 同一份产物。\n return { pluginId: plugin.id, module, compiledPath: outfile }\n } catch (err) {\n const message = String(err)\n logger.warn({ pluginId: plugin.id, err: message }, 'plugin factory threw')\n onLoadError?.(plugin.id, `factory threw: ${message}`)\n return null\n }\n}\n","/** Plugin isolation entry template: bootstrap script for Worker_threads context. */\n\n/**\n * 隔离池 RPC 协议消息类型单源(RFC-199 D4/O-6)。\n *\n * 模板生成侧(下方 protocolBody 字符串插值)与双池消费侧(plugin-worker-pool.ts /\n * plugin-process-pool.ts 的 message 路由与 send 调用)共同引用本常量——消除「模板字符串\n * 里拼写错只在池启动时暴露」的风险面。值绝不能改(是运行中 worker/子进程与宿主之间的\n * wire 协议字面量);新增消息类型时先加常量再在模板/双池同步消费。\n */\nexport const ISOLATION_MSG = {\n /** 宿主 → 隔离端:加载插件模块 */\n load: 'worker.load',\n /** 隔离端 → 宿主:模块加载完成(含 tools/hooks 元数据投影) */\n loaded: 'worker.loaded',\n /** 隔离端 → 宿主:加载失败 */\n error: 'worker.error',\n /** 宿主 → 隔离端:执行工具 */\n toolExecute: 'tool.execute',\n /** 隔离端 → 宿主:工具结果(result 或 error) */\n toolResult: 'tool.result',\n /** 宿主 → 隔离端:取消 in-flight 工具(结果丢弃) */\n toolCancel: 'tool.cancel',\n /**\n * 宿主 → 隔离端:执行 @ 资源 resolve 函数(RFC-210 D3 sigilResolvers 轴代理,\n * 修复\"声明 tools(触发隔离装载)+ sigilResolvers 组合时 resolver 从未被代理\"的\n * 生产缺口——此前 RFC-192/RFC-177 双池 RPC 协议只覆盖 tools/hooks/lifecycle 三类,\n * sigilResolvers 轴完全未接线,任何走隔离路径的插件其 @ 展开静默失效)。\n */\n sigilResolve: 'sigil.resolve',\n /** 隔离端 → 宿主:resolve 结果(result 或 error) */\n sigilResult: 'sigil.result',\n /** 宿主 → 隔离端:onActivate 生命周期 */\n onActivate: 'worker.onActivate',\n /** 隔离端 → 宿主:onActivate 完成 */\n activated: 'worker.activated',\n /** 宿主 → 隔离端:onDeactivate 生命周期 */\n onDeactivate: 'worker.onDeactivate',\n /** 隔离端 → 宿主:onDeactivate 完成 */\n deactivated: 'worker.deactivated',\n /** 宿主 → 隔离端:终止(清理并 process.exit(0)) */\n stop: 'worker.stop',\n /**\n * RFC-303 M4:隔离端 → 宿主——执行宿主能力面方法(host 反向 RPC)。\n * 工具 ctx.host 的方法调用(如 startJob)经此消息发回宿主,宿主按白名单派发后回\n * `hostResult`。R4 身份纪律:宿主侧取**连接侧绑定**的 pluginId(load 时记录),\n * 不取消息内容。\n */\n hostCall: 'host.call',\n /** 宿主 → 隔离端:host 方法执行结果(result 或 error)。 */\n hostResult: 'host.result',\n} as const\n\nexport function buildIsolationEntrySrc(transport: 'worker' | 'process'): string {\n const transportPrelude = transport === 'worker'\n ? `const { parentPort } = require('worker_threads')\\nconst __send = (m) => parentPort.postMessage(m)\\nconst __onMessage = (fn) => parentPort.on('message', fn)`\n : `const __send = (m) => process.send(m)\\nconst __onMessage = (fn) => process.on('message', fn)`\n\n const protocolBody = `// RFC-303 M4:工具执行上下文——host/logger 经反向 RPC 代理回宿主\n// (postMessage 结构化克隆不能传函数,隔离端本地构造 Proxy stub,方法调用发 host.call 消息)。\nconst hostCallPending = new Map()\nlet hostCallSeq = 0\nlet toolCtx = null\n\nfunction makeHostStub(methods) {\n return new Proxy({}, {\n get(_, method) {\n if (typeof method !== 'string' || !methods.includes(method)) return undefined\n return (...args) => new Promise((resolve, reject) => {\n const id = 'host-' + (++hostCallSeq)\n hostCallPending.set(id, { resolve, reject })\n __send({ type: '${ISOLATION_MSG.hostCall}', id, method, args })\n })\n },\n })\n}\n\nconst toolExecutors = new Map()\nconst sigilResolverExecutors = new Map()\nconst cancelledToolIds = new Set()\nlet pluginId = ''\nlet pluginInstance = null\n\n__onMessage(async (msg) => {\n if (msg.type === '${ISOLATION_MSG.load}') {\n pluginId = msg.pluginId\n try {\n const mod = require(msg.compiledPath)\n const factory = mod?.default ?? mod\n if (typeof factory !== 'function') {\n __send({ type: '${ISOLATION_MSG.loaded}', pluginId, module: { tools: [], hookEvents: [], hasOnActivate: false, hasOnDeactivate: false } })\n return\n }\n // RFC-303 M4:host stub(工具 ctx 与 factory ctx 共用;白名单方法名随 load 下发)。\n const hostMethods = Array.isArray(msg.hostMethods) ? msg.hostMethods : []\n const hostStub = makeHostStub(hostMethods)\n // RFC-303 D5(M7):隔离端 factory ctx 的 listActivePlugins——函数无法经 postMessage\n // 传递,宿主在 load 消息里下发标记(msg.ctx.listActivePlugins === true),隔离端构造\n // stub 经 hostCall 通道回宿主执行(复用 M4 反向 RPC,method='listActivePlugins')。\n const factoryCtx = { ...(msg.ctx || {}) }\n if (msg.ctx && msg.ctx.listActivePlugins === true) {\n factoryCtx.listActivePlugins = () => hostStub.listActivePlugins()\n }\n const instance = factory(factoryCtx)\n pluginInstance = instance\n const tools = (instance.tools ?? []).filter((t) => t.name && typeof t.execute === 'function')\n for (const t of tools) toolExecutors.set(t.name, t.execute)\n const hookEvents = Object.keys(instance.hooks ?? {}).filter((k) => typeof (instance.hooks ?? {})[k] === 'function')\n // sigilResolvers(RFC-210 D3):值可以是裸函数或 {resolve, timeoutMs?, uiHostOnly?}——\n // 归一化存执行体到 sigilResolverExecutors,元数据(timeoutMs/uiHostOnly)随 loaded 消息\n // 回传宿主(同 tools 的 parametersSchema 投影模式,宿主侧不需要访问函数本体)。\n const sigilResolverDefs = instance.sigilResolvers ?? {}\n const sigilResolverMeta = []\n for (const name of Object.keys(sigilResolverDefs)) {\n const def = sigilResolverDefs[name]\n const fn = typeof def === 'function' ? def : (def && typeof def.resolve === 'function' ? def.resolve : null)\n if (!fn) continue\n sigilResolverExecutors.set(name, fn)\n sigilResolverMeta.push({\n name,\n timeoutMs: typeof def === 'object' ? def.timeoutMs : undefined,\n uiHostOnly: typeof def === 'object' ? def.uiHostOnly : undefined,\n })\n }\n // RFC-303 M4:工具 ctx——host stub 已在上方构造(factory ctx 共用);\n // logger 三方法同样走反向 RPC(method 'logger.info'/'logger.warn'/'logger.error')。\n toolCtx = {\n pluginId: msg.pluginId,\n workspaceDir: msg.ctx && msg.ctx.workspaceDir ? msg.ctx.workspaceDir : '',\n logger: {\n info: (m) => { hostStub['logger.info'] ? hostStub['logger.info'](m) : undefined },\n warn: (m) => { hostStub['logger.warn'] ? hostStub['logger.warn'](m) : undefined },\n error: (m) => { hostStub['logger.error'] ? hostStub['logger.error'](m) : undefined },\n },\n // host 字段的 present 判定只看真实 host 能力(listActivePlugins 是互发现快照,\n // 非 host 能力面——强制加入 hostMethods 只是让 stub 可调,不能让它撑起 ctx.host)。\n host: hostMethods.some((m) => m !== 'listActivePlugins') ? hostStub : undefined,\n }\n __send({\n type: '${ISOLATION_MSG.loaded}', pluginId,\n module: {\n contributions: instance.contributions,\n tools: tools.map((t) => ({\n name: t.name,\n description: String(t.description ?? ''),\n hasParameters: t.parameters != null,\n parametersSchema: t.parameters != null ? t.parameters?.toJSONSchema?.() : undefined,\n })),\n sigilResolvers: sigilResolverMeta,\n hasSigilProviders: Array.isArray(instance.sigilProviders) && instance.sigilProviders.length > 0,\n hookEvents, hasOnActivate: typeof instance.onActivate === 'function',\n hasOnDeactivate: typeof instance.onDeactivate === 'function',\n },\n })\n } catch (err) {\n __send({ type: '${ISOLATION_MSG.error}', pluginId, error: err?.message ?? String(err) })\n }\n return\n }\n if (msg.type === '${ISOLATION_MSG.toolExecute}') {\n const exec = toolExecutors.get(msg.name)\n if (!exec) { __send({ type: '${ISOLATION_MSG.toolResult}', id: msg.id, error: \"tool not found\" }); return }\n try {\n const r = await exec(msg.params, toolCtx)\n if (cancelledToolIds.delete(msg.id)) return\n __send({ type: '${ISOLATION_MSG.toolResult}', id: msg.id, result: r })\n } catch (e) {\n if (cancelledToolIds.delete(msg.id)) return\n __send({ type: '${ISOLATION_MSG.toolResult}', id: msg.id, error: e?.message ?? String(e) })\n }\n return\n }\n if (msg.type === '${ISOLATION_MSG.toolCancel}') {\n cancelledToolIds.add(msg.id)\n return\n }\n if (msg.type === '${ISOLATION_MSG.sigilResolve}') {\n const fn = sigilResolverExecutors.get(msg.name)\n if (!fn) { __send({ type: '${ISOLATION_MSG.sigilResult}', id: msg.id, error: \"sigil resolver not found\" }); return }\n try {\n const r = await fn(msg.param, msg.ctx)\n __send({ type: '${ISOLATION_MSG.sigilResult}', id: msg.id, result: r })\n } catch (e) {\n __send({ type: '${ISOLATION_MSG.sigilResult}', id: msg.id, error: e?.message ?? String(e) })\n }\n return\n }\n if (msg.type === '${ISOLATION_MSG.hostResult}') {\n const p = hostCallPending.get(msg.id)\n if (!p) return\n hostCallPending.delete(msg.id)\n if (msg.error) p.reject(new Error(msg.error))\n else p.resolve(msg.result)\n return\n }\n if (msg.type === '${ISOLATION_MSG.onActivate}') {\n if (!pluginInstance || typeof pluginInstance.onActivate !== 'function') {\n __send({ type: '${ISOLATION_MSG.activated}', id: msg.id })\n return\n }\n try { await pluginInstance.onActivate(); __send({ type: '${ISOLATION_MSG.activated}', id: msg.id }) }\n catch (e) { __send({ type: '${ISOLATION_MSG.activated}', id: msg.id, error: e?.message ?? String(e) }) }\n return\n }\n if (msg.type === '${ISOLATION_MSG.onDeactivate}') {\n if (!pluginInstance || typeof pluginInstance.onDeactivate !== 'function') {\n __send({ type: '${ISOLATION_MSG.deactivated}', id: msg.id })\n return\n }\n try { await pluginInstance.onDeactivate(); __send({ type: '${ISOLATION_MSG.deactivated}', id: msg.id }) }\n catch (e) { __send({ type: '${ISOLATION_MSG.deactivated}', id: msg.id, error: e?.message ?? String(e) }) }\n return\n }\n if (msg.type === '${ISOLATION_MSG.stop}') { toolExecutors.clear(); pluginInstance = null; process.exit(0) }\n})`\n\n return `${transportPrelude}\\n${protocolBody}`\n}\n","/** Isolated plugin transport: structured IPC between main process and plugin worker. */\nimport { ISOLATION_MSG } from './plugin-isolation-entry-template'\n\n/**\n * 插件工具单次执行的默认超时(2026-07-17 终局 review 必须调整项,两池共用同一常量)。\n *\n * 此前 `executeTool` 无任何超时——worker/进程存活但工具死循环/永不 resolve 时,真实\n * 工具调用路径(`module-loader-isolated.ts` 的 execute proxy)永久挂起,用户整个 turn\n * 卡死且 Esc 中断无效。对齐内置工具纪律(bash 默认 30s)后取 60s:插件工具可能包含\n * 真实网络/编译类操作,给出比 bash 更宽的默认,可被调用方逐次覆盖。\n */\nexport const PLUGIN_TOOL_EXECUTE_TIMEOUT_MS = 60_000\n\nexport interface WorkerLoadedModule {\n pluginId: string\n contributions?: Record<string, unknown>\n /** RFC-105 M-N1 T-N1a:结构化工具元数据(替换旧 toolNames: string[],带回 description 与 hasParameters)。 */\n tools: Array<{\n name: string\n description: string\n hasParameters: boolean\n /**\n * `toJSONSchema()` 输出(纯 JSON-可序列化数据),供主线程 proxy 的 `toJsonSchema` 使用。\n * worker 侧调用 `t.parameters?.toJSONSchema?.()` 产生,经 `postMessage` 的结构化克隆跨越线程边界。\n * `undefined` = 该工具无参数 schema(hasParameters=false)或 toJSONSchema 方法缺失。\n */\n parametersSchema?: unknown\n }>\n /**\n * RFC-210 D3 代理修复:@ 资源 resolve 函数元数据(不含函数体——执行走 RPC,见\n * `IsolatedPluginPool.resolveSigil`)。`SigilAxisHandler` 消费本字段转换为\n * `SigilResolver[]` 时,`resolve` 字段包一层 RPC 代理闭包(同 tools 的 execute proxy)。\n */\n sigilResolvers: Array<{ name: string; timeoutMs?: number; uiHostOnly?: boolean }>\n /**\n * 终局 review 2026-07-21 B-1:插件模块是否声明了 `sigilProviders`(@ 动态搜索,pull 模型)。\n * 该轴**未**被 RPC 协议代理(高频逐键查询,代理需独立的 debounce/batch 设计,见文件头\n * \"已知限制\"),隔离路径下会静默不生效——本标志让宿主侧能对\"声明了但拿不到\"的组合\n * 发显式 warn,把静默失效变成可诊断信号。\n */\n hasSigilProviders?: boolean\n hookEvents: string[]\n hasOnActivate: boolean\n hasOnDeactivate: boolean\n}\n\n/**\n * RFC-177 B2:`PluginWorkerPool`(worker_threads)与 `PluginProcessPool`\n * (child_process + OS 沙箱)的共同接口——`module-loader-isolated.ts` 的\n * `buildIsolatedModule`/`loadPluginModuleIsolated` 依赖此接口而非具体池实现,\n * 使调用方按 scope 选择传输层时无需感知底层差异。\n */\nexport interface IsolatedPluginPool {\n load(\n pluginId: string,\n compiledPath: string,\n ctx: { pluginId: string; pluginDir: string; workspaceDir: string; hostMethods?: string[]; listActivePlugins?: boolean },\n ): Promise<WorkerLoadedModule | null>\n /**\n * RFC-303 M4:注入 host 方法派发器(宿主侧实现白名单;per-pool 单实例,load 时\n * 透传给新建的 transport——连接侧绑定 pluginId 由 transport 自己记录)。\n */\n setHostCallDispatcher?(\n dispatcher: (pluginId: string, method: string, args: unknown[]) => Promise<unknown>,\n ): void\n executeTool(\n pluginId: string,\n toolName: string,\n params: unknown,\n options?: { timeoutMs?: number; signal?: AbortSignal },\n ): Promise<{ result?: unknown; error?: string }>\n /** RFC-210 D3 代理修复:@ 资源 resolve 函数 RPC 代理(同 executeTool 传输纪律)。 */\n resolveSigil(\n pluginId: string,\n resolverName: string,\n param: string,\n ctx: { workspaceDir: string; maxOutput: number },\n options?: { timeoutMs?: number },\n ): Promise<{ result?: string; error?: string }>\n activate(pluginId: string): Promise<{ error?: string }>\n deactivate(pluginId: string): Promise<{ error?: string }>\n unload(pluginId: string): Promise<void>\n dispose(): Promise<void>\n}\n\ninterface PendingRequest {\n type: 'load'\n resolve: (result: WorkerLoadedModule) => void\n reject: (err: Error) => void\n}\n\ninterface ToolPendingRequest {\n type: 'tool'\n resolve: (result: { result?: unknown; error?: string }) => void\n}\n\ninterface SigilPendingRequest {\n type: 'sigil'\n resolve: (result: { result?: string; error?: string }) => void\n}\n\ninterface LifecyclePendingRequest {\n type: 'lifecycle'\n resolve: (result: { error?: string }) => void\n}\n\ntype Pending = PendingRequest | ToolPendingRequest | SigilPendingRequest | LifecyclePendingRequest\n\nexport interface IncomingMessage {\n type: string\n pluginId?: string\n module?: WorkerLoadedModule\n result?: unknown\n error?: string\n id?: string\n /** RFC-303 M4:host 反向 RPC 请求(隔离端 → 宿主)。 */\n method?: string\n args?: unknown[]\n /** RFC-303 M4:宿主下发给隔离端的 host 白名单方法名(load 消息携带)。 */\n hostMethods?: string[]\n}\n\n/**\n * 传输层适配接口——`PluginWorker`(worker_threads)与 `PluginProcess`(child_process)\n * 各自实现,向基类屏蔽 `worker.postMessage`/`child.send` 等 API 差异。\n *\n * `ref`/`unref`:pending 期 ref、空闲期 unref,防止宿主进程因残留的 worker/子进程\n * 句柄钉住事件循环导致退不净(lesson_85 worker 侧实锤修复;M3 执行期实测确认\n * child_process 侧需要 `channel.unref()`+`stderr.unref()` 两处才能达到对等语义,\n * 详见 `plugin-process-pool.ts` 的 `TransportAdapter` 实现)。\n */\nexport interface TransportAdapter {\n send(msg: Record<string, unknown>): void\n onMessage(fn: (msg: IncomingMessage) => void): void\n onError(fn: (err: Error) => void): void\n onExit(fn: (code: number | null) => void): void\n ref(): void\n unref(): void\n /**\n * 工具执行超时通知(可选)——子类在此实现各自的 `logger.warn` 文案(worker 版与\n * process 版消息文案不同:后者带\"(isolated process)\"后缀),基类只负责触发时机。\n */\n onToolTimeout?(name: string, timeoutMs: number): void\n /** 传输层 error 事件通知(可选,子类各自 logger.warn 文案)。 */\n onErrorLog?(err: Error): void\n /** 传输层 exit(带 pending 请求)事件通知(可选,子类各自 logger.warn 文案)。 */\n onExitLog?(code: number | null): void\n /**\n * exit(带 pending 请求)时构造 reject 用的 Error 消息文案——worker 版是\n * \"plugin worker exited unexpectedly (code N)\",process 版是\n * \"plugin process exited unexpectedly (code N)\"(两者被既有测试断言精确匹配,\n * 不能统一成通用文案)。缺省回退到通用文案。\n */\n exitErrorMessage?(code: number | null): string\n /** load 失败(无 msg.error 时)兜底文案——worker 版/process 版各自不同。 */\n loadFailedMessage?(): string\n /** 已在加载中时的错误文案——worker 版/process 版各自不同。 */\n alreadyLoadingMessage?(pluginId: string): string\n}\n\n/**\n * 插件双池宿主侧 RPC 管理基类。子类只需提供 `TransportAdapter`(构造函数中调用\n * `super(adapter)`)与实现 `stop()`(传输层特定的终止逻辑:worker 用 `terminate()`,\n * 子进程用 `kill()`+沙箱资源清理)。\n */\nexport abstract class IsolatedPluginTransport {\n private pending = new Map<string, Pending>()\n private idCounter = 0\n /**\n * RFC-303 M4:连接侧绑定的 pluginId(load 时记录)——host 反向 RPC 的**身份来源**,\n * 不取消息内容(R4 纪律:消息里即使有 pluginId 字段也不读,防止隔离端伪装成别的插件)。\n */\n private boundPluginId = ''\n /** RFC-303 M4:host 方法派发器(宿主侧注入;未注入时隔离端 host 调用一律 reject)。 */\n private hostCallDispatcher:\n | ((pluginId: string, method: string, args: unknown[]) => Promise<unknown>)\n | null = null\n\n /**\n * RFC-303 M4:注入 host 方法派发器(宿主侧实现白名单 + fail-closed——方法不在\n * PluginHostCapabilities 上或未授权时 reject;隔离端拿到的 host stub 只暴露\n * load 时下发的白名单方法名,宿主侧派发时二次校验,双保险)。\n */\n setHostCallDispatcher(\n dispatcher: (pluginId: string, method: string, args: unknown[]) => Promise<unknown>,\n ): void {\n this.hostCallDispatcher = dispatcher\n }\n\n constructor(private readonly adapter: TransportAdapter) {\n this.adapter.onMessage((msg) => {\n if (msg.type === ISOLATION_MSG.loaded) {\n const p = this.settlePending('__load__') as PendingRequest | undefined\n if (p) p.resolve(msg.module as WorkerLoadedModule)\n return\n }\n // RFC-140 D7/T17:worker/子进程侧 require/工厂执行真实失败——reject(同 'error'\n // 已有纹理),调用方(module-loader-isolated.ts)catch 到此错误后 fail-soft 记录 warn,\n // 不再误判为\"插件合法地零工具\"。\n if (msg.type === ISOLATION_MSG.error) {\n const p = this.settlePending('__load__') as PendingRequest | undefined\n if (p) p.reject(new Error(msg.error ?? this.adapter.loadFailedMessage?.() ?? 'plugin isolated transport load failed'))\n return\n }\n if (msg.type === ISOLATION_MSG.toolResult) {\n const p = this.settlePending(msg.id!) as ToolPendingRequest | undefined\n if (p) p.resolve({ result: msg.result, error: msg.error })\n return\n }\n // RFC-210 D3 代理修复:@ 资源 resolve 结果\n if (msg.type === ISOLATION_MSG.sigilResult) {\n const p = this.settlePending(msg.id!) as SigilPendingRequest | undefined\n if (p) p.resolve({ result: msg.result as string | undefined, error: msg.error })\n return\n }\n // RFC-105 M-N1 T-N1c:onActivate/onDeactivate RPC 响应\n if (msg.type === ISOLATION_MSG.activated || msg.type === ISOLATION_MSG.deactivated) {\n const p = this.settlePending(msg.id!) as LifecyclePendingRequest | undefined\n if (p) p.resolve({ error: msg.error })\n return\n }\n // RFC-303 M4:host 反向 RPC(隔离端请求执行宿主能力面方法)。\n if (msg.type === ISOLATION_MSG.hostCall) {\n const pluginId = this.boundPluginId // R4:身份取连接侧绑定,不取消息内容\n const dispatcher = this.hostCallDispatcher\n const reply = (payload: { result?: unknown; error?: string }): void => {\n try {\n this.adapter.send({ type: ISOLATION_MSG.hostResult, id: msg.id, ...payload })\n } catch {\n // 传输层已终止等边界——回发失败无害(隔离端 host 调用会因超时/exit reject)。\n }\n }\n if (!dispatcher) {\n reply({ error: 'host call dispatcher not installed (plugin host capabilities unavailable)' })\n return\n }\n dispatcher(pluginId, msg.method ?? '', msg.args ?? [])\n .then((result) => reply({ result }))\n .catch((err) => reply({ error: err instanceof Error ? err.message : String(err) }))\n return\n }\n })\n\n this.adapter.onError((err) => {\n this.adapter.onErrorLog?.(err)\n this.rejectAllPending(err)\n })\n\n // 元审查发现(2026-07-13,四轮既有 review 均遗漏):worker/子进程内部同步调用\n // `process.exit()` 或被 OS OOM-kill(SIGKILL)只会触发 'exit' 事件,不会触发\n // 'error' 事件。此前无 'exit' 监听时——崩溃时 `pending` Map 里等待中的 tool/lifecycle\n // RPC 永远不会被 resolve,调用方(真实工具调用路径 `module-loader-isolated.ts` 的\n // `execute()`,非仅性能采集旁路)的 `await` 永久挂起,用户侧表现为整个 turn 卡死无响应。\n this.adapter.onExit((code) => {\n if (this.pending.size === 0) return\n this.adapter.onExitLog?.(code)\n const message =\n this.adapter.exitErrorMessage?.(code) ?? `plugin isolated transport exited unexpectedly (code ${code})`\n this.rejectAllPending(new Error(message))\n })\n }\n\n /**\n * 统一清理全部 pending 请求('error' 或 'exit' 时调用)。\n * `load` 类型走 `reject`(其 Promise 天生允许 reject,见 `load()` 的 `new Promise<...>((resolve, reject) => ...)`);\n * `tool`/`lifecycle` 类型只能 `resolve`(其 Promise 构造时未接收 `reject` 回调,见\n * `executeTool()`/`activate()`/`deactivate()` 的 `new Promise((resolve) => ...)`)——\n * 统一编码为 `{ error: string }` 结果对象,调用方按既有的 `if (error) throw ...` 纹理处理。\n */\n protected rejectAllPending(err: Error): void {\n for (const [, p] of this.pending) {\n if (p.type === 'load') p.reject(err)\n else p.resolve({ error: err.message })\n }\n this.pending.clear()\n this.adapter.unref()\n }\n\n /**\n * pending 期 ref / 空闲期 unref(事件循环钉死修复):非空则 ref(保证响应必达),\n * 清空则 unref(不阻退出)。每次 pending 集合增删后调用——统一在 trackPending/\n * settlePending 内部触发,保证不被遗漏。\n *\n * 行为变化说明(RFC-211 D3,独立评审要求明确记录而非隐性归为\"纯搬迁\"):\n * `PluginProcess` 此前的 `trackPending`/`settlePending` 是零 ref/unref 调用的裸实现,\n * 提取到本基类后统一走这套 ref/unref 纪律——这是一次刻意的、正确方向的行为改进\n * (补齐与 `PluginWorker` 对等的事件循环保活语义,lesson_85 同款修复),不是纯搬迁。\n */\n private syncRefState(): void {\n if (this.pending.size > 0) this.adapter.ref()\n else this.adapter.unref()\n }\n\n /** pending 增删的统一入口(保证 syncRefState 不被遗漏)。 */\n protected trackPending(id: string, p: Pending): void {\n this.pending.set(id, p)\n this.syncRefState()\n }\n\n protected settlePending(id: string): Pending | undefined {\n const p = this.pending.get(id)\n if (p) {\n this.pending.delete(id)\n this.syncRefState()\n }\n return p\n }\n\n async load(\n pluginId: string,\n compiledPath: string,\n ctx: { pluginId: string; pluginDir: string; workspaceDir: string; hostMethods?: string[]; listActivePlugins?: boolean },\n ): Promise<WorkerLoadedModule> {\n if (this.pending.has('__load__')) {\n throw new Error(this.adapter.alreadyLoadingMessage?.(pluginId) ?? `plugin isolated transport already loading ${pluginId}`)\n }\n this.boundPluginId = pluginId // RFC-303 M4:连接侧绑定(host RPC 身份来源)\n return new Promise<WorkerLoadedModule>((resolve, reject) => {\n this.trackPending('__load__', { type: 'load', resolve, reject })\n this.adapter.send({ type: ISOLATION_MSG.load, pluginId, compiledPath, ctx, ...(ctx.hostMethods ? { hostMethods: ctx.hostMethods } : {}) })\n })\n }\n\n async executeTool(\n name: string,\n params: unknown,\n options?: { timeoutMs?: number; signal?: AbortSignal },\n ): Promise<{ result?: unknown; error?: string }> {\n const id = `tool-${++this.idCounter}`\n const timeoutMs = options?.timeoutMs ?? PLUGIN_TOOL_EXECUTE_TIMEOUT_MS\n const signal = options?.signal\n if (signal?.aborted) return { error: 'plugin tool execution aborted' }\n return new Promise<{ result?: unknown; error?: string }>((resolve) => {\n let timer: ReturnType<typeof setTimeout> | undefined\n let onAbort: (() => void) | undefined\n\n const cleanup = (): void => {\n if (timer !== undefined) clearTimeout(timer)\n if (onAbort && signal) signal.removeEventListener('abort', onAbort)\n }\n // 包一层 resolve:无论哪条路径(正常回包/超时/abort/传输层 crash 的 rejectAllPending)\n // settle,都先解除定时器与 abort 监听,防泄漏。\n const settle = (result: { result?: unknown; error?: string }): void => {\n cleanup()\n resolve(result)\n }\n this.trackPending(id, { type: 'tool', resolve: settle })\n\n /** 主线程侧提前 settle + 通知传输层丢弃迟到结果(JS 无法抢占中止同步代码)。 */\n const settleEarly = (error: string): void => {\n const p = this.settlePending(id)\n if (!p) return // 已被正常回包/rejectAllPending settle,竞态无害\n try {\n this.adapter.send({ type: ISOLATION_MSG.toolCancel, id })\n } catch {\n // 传输层已终止等边界——丢弃通知失败无害。\n }\n ;(p as ToolPendingRequest).resolve({ error })\n }\n\n if (timeoutMs > 0) {\n timer = setTimeout(() => {\n this.adapter.onToolTimeout?.(name, timeoutMs)\n settleEarly(`plugin tool \"${name}\" timed out after ${timeoutMs}ms`)\n }, timeoutMs)\n timer.unref?.()\n }\n if (signal) {\n onAbort = () => settleEarly('plugin tool execution aborted')\n signal.addEventListener('abort', onAbort, { once: true })\n }\n\n this.adapter.send({ type: ISOLATION_MSG.toolExecute, id, name, params })\n })\n }\n\n /**\n * RFC-210 D3 代理修复:通过 RPC 代理调用隔离端插件的 sigilResolver。超时纪律同\n * `executeTool`——缺省 `PLUGIN_TOOL_EXECUTE_TIMEOUT_MS`(60s,两池共用同一常量)\n * 作为 RPC 层兜底超时;真正生效的主超时是调用方 `sigil-expand.ts` 传入的\n * `resolver.timeoutMs ?? DEFAULT_TIMEOUT_MS`(10s,更紧)——`sigil-expand.ts` 自身\n * 的 `withTimeout` 包装会先于本层 60s 触发,本层 60s 只是兜底防御(对称 executeTool\n * 的既有纪律,非本方法独立的主超时来源,独立评审 finding 8 修正)。\n * 不支持 abort signal(sigil resolve 发生在提交热路径,本身已有 per-resolver\n * 超时兜底,未见有中断场景,YAGNI,与 executeTool 的 signal 参数正交按需添加)。\n */\n async resolveSigil(\n resolverName: string,\n param: string,\n ctx: { workspaceDir: string; maxOutput: number },\n options?: { timeoutMs?: number },\n ): Promise<{ result?: string; error?: string }> {\n const id = `sigil-${++this.idCounter}`\n const timeoutMs = options?.timeoutMs ?? PLUGIN_TOOL_EXECUTE_TIMEOUT_MS\n return new Promise<{ result?: string; error?: string }>((resolve) => {\n let timer: ReturnType<typeof setTimeout> | undefined\n const settle = (result: { result?: string; error?: string }): void => {\n if (timer !== undefined) clearTimeout(timer)\n resolve(result)\n }\n this.trackPending(id, { type: 'sigil', resolve: settle })\n if (timeoutMs > 0) {\n timer = setTimeout(() => {\n const p = this.settlePending(id)\n if (p) (p as SigilPendingRequest).resolve({ error: `sigil resolver \"${resolverName}\" timed out after ${timeoutMs}ms` })\n }, timeoutMs)\n timer.unref?.()\n }\n this.adapter.send({ type: ISOLATION_MSG.sigilResolve, id, name: resolverName, param, ctx })\n })\n }\n\n /** RFC-105 M-N1 T-N1c:通过 RPC 代理调用隔离端插件的 onActivate。 */\n async activate(): Promise<{ error?: string }> {\n const id = `lifecycle-${++this.idCounter}`\n return new Promise<{ error?: string }>((resolve) => {\n this.trackPending(id, { type: 'lifecycle', resolve })\n this.adapter.send({ type: ISOLATION_MSG.onActivate, id })\n })\n }\n\n /** RFC-105 M-N1 T-N1c:通过 RPC 代理调用隔离端插件的 onDeactivate。 */\n async deactivate(): Promise<{ error?: string }> {\n const id = `lifecycle-${++this.idCounter}`\n return new Promise<{ error?: string }>((resolve) => {\n this.trackPending(id, { type: 'lifecycle', resolve })\n this.adapter.send({ type: ISOLATION_MSG.onDeactivate, id })\n })\n }\n\n protected clearPending(): void {\n this.pending.clear()\n }\n\n /** 传输层特定的终止逻辑(worker: terminate();子进程: kill()+沙箱资源清理)。 */\n abstract stop(): Promise<void>\n}\n","/** Plugin worker pool: shared pool of Node Worker_threads for plugin execution. */\n\nimport { Worker } from 'node:worker_threads'\nimport { mkdtempSync, writeFileSync, rmSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport { createLogger } from '@x-otto/shared'\nimport { buildIsolationEntrySrc, ISOLATION_MSG } from './plugin-isolation-entry-template'\nimport {\n IsolatedPluginTransport,\n type TransportAdapter,\n type WorkerLoadedModule,\n type IsolatedPluginPool,\n} from './isolated-plugin-transport'\n\nexport { PLUGIN_TOOL_EXECUTE_TIMEOUT_MS } from './isolated-plugin-transport'\nexport type { WorkerLoadedModule, IsolatedPluginPool } from './isolated-plugin-transport'\n\nconst logger = createLogger('@x-otto/coding:plugin-worker-pool')\n\n/** 临时目录(全局单例,进程退出时清理)。 */\nlet tmpDir: string | null = null\nlet workerEntryPath: string | null = null\n\nfunction getWorkerEntryPath(): string {\n if (workerEntryPath) return workerEntryPath\n if (!tmpDir) tmpDir = mkdtempSync(join(tmpdir(), 'otto-plugin-worker-'))\n workerEntryPath = join(tmpDir, 'worker-entry.cjs')\n writeFileSync(workerEntryPath, buildIsolationEntrySrc('worker'))\n return workerEntryPath\n}\n\nclass PluginWorker extends IsolatedPluginTransport {\n private worker: Worker\n\n constructor() {\n const worker = new Worker(getWorkerEntryPath())\n // 事件循环钉死修复(2026-07-14,生产 5 个僵尸 otto 进程 inspector 实锤):worker_threads\n // 的 kPublicPort 在宿主侧订阅 'message' 后默认 ref 事件循环——空闲的 plugin worker 会让\n // 宿主进程在 /exit 全部收尾完成后仍然无法自然排干(陪跑父进程路径没有硬 process.exit,\n // 表现为「/exit 后还要 Ctrl+C 才能退出」+ 孤儿进程挂 launchd 下常驻)。默认 unref:空闲\n // worker 不钉事件循环;有 pending RPC 时基类 syncRefState() 临时 ref 保证响应能被事件循环收到。\n worker.unref()\n\n const adapter: TransportAdapter = {\n send: (msg) => worker.postMessage(msg),\n onMessage: (fn) =>\n worker.on('message', (msg: { type: string; pluginId?: string; module?: WorkerLoadedModule; result?: unknown; error?: string; id?: string }) => fn(msg)),\n onError: (fn) => worker.on('error', (err) => fn(err as Error)),\n onExit: (fn) => worker.on('exit', fn),\n ref: () => worker.ref(),\n unref: () => worker.unref(),\n onToolTimeout: (name, timeoutMs) => {\n logger.warn({ tool: name, timeoutMs }, 'plugin tool execution timed out')\n },\n onErrorLog: (err) => {\n logger.warn({ err: String(err) }, 'plugin worker error')\n },\n onExitLog: (code) => {\n logger.warn({ code }, 'plugin worker exited unexpectedly with pending requests')\n },\n exitErrorMessage: (code) => `plugin worker exited unexpectedly (code ${code})`,\n loadFailedMessage: () => 'plugin worker load failed',\n alreadyLoadingMessage: (pluginId) => `plugin worker already loading ${pluginId}`,\n }\n super(adapter)\n this.worker = worker\n }\n\n async stop(): Promise<void> {\n try { this.worker.postMessage({ type: ISOLATION_MSG.stop }); await this.worker.terminate() } catch {}\n this.clearPending()\n // 防御性收口(独立评审 F1):terminate() 抛异常时 worker 可能仍存活——pending 清空后\n // 必须同步回 unref 态,否则残留的 ref 会钉住事件循环(与 rejectAllPending 对称)。\n this.worker.unref()\n }\n}\n\nexport class PluginWorkerPool implements IsolatedPluginPool {\n private readonly workers = new Map<string, PluginWorker>()\n private readonly maxConcurrent: number\n /** RFC-303 M4:host 方法派发器(宿主侧注入,load 时透传给新 worker)。 */\n private hostCallDispatcher:\n | ((pluginId: string, method: string, args: unknown[]) => Promise<unknown>)\n | null = null\n\n constructor(maxConcurrent = 8) { this.maxConcurrent = maxConcurrent }\n\n setHostCallDispatcher(\n dispatcher: (pluginId: string, method: string, args: unknown[]) => Promise<unknown>,\n ): void {\n this.hostCallDispatcher = dispatcher\n }\n\n /** RFC-105 M-N1 T-N1b:池满时返回 null(而非抛异常),由调用方降级为主进程内联加载。 */\n async load(pluginId: string, compiledPath: string, ctx: { pluginId: string; pluginDir: string; workspaceDir: string; hostMethods?: string[]; listActivePlugins?: boolean }): Promise<WorkerLoadedModule | null> {\n if (this.workers.size >= this.maxConcurrent) return null\n await this.unload(pluginId)\n const w = new PluginWorker()\n if (this.hostCallDispatcher) w.setHostCallDispatcher(this.hostCallDispatcher)\n this.workers.set(pluginId, w)\n try {\n return await w.load(pluginId, compiledPath, ctx)\n } catch (err) {\n // 泄漏修复(2026-07-14):此前只从 map 删除、不 stop()——加载失败的 worker 线程\n // 及其 ref 住事件循环的 kPublicPort 永久悬空(dispose 只遍历 map,摸不到它),\n // 宿主进程 /exit 后无法自然退出。fire-and-forget stop:调用方等的是 load 结果,\n // 不应被清理耗时阻塞;terminate 失败无害(进程退出兜底回收线程)。\n // 与 unload() 的 `await w.stop()`(RFC-124 L1)语义不同且各自正确:unload 是主动\n // 卸载,调用方(如紧接的 reload→load)依赖「旧 worker 已完全终止」;此处 worker\n // 已从池中移除、无人可达,等待清理只会延缓错误传播,无一致性收益。\n this.workers.delete(pluginId)\n void w.stop().catch(() => {})\n throw err\n }\n }\n\n async executeTool(\n pluginId: string,\n toolName: string,\n params: unknown,\n options?: { timeoutMs?: number; signal?: AbortSignal },\n ): Promise<{ result?: unknown; error?: string }> {\n const w = this.workers.get(pluginId)\n if (!w) throw new Error(`no worker for plugin ${pluginId}`)\n return w.executeTool(toolName, params, options)\n }\n\n /** RFC-210 D3 代理修复:通过 RPC 代理调用 worker 侧插件的 sigilResolver。 */\n async resolveSigil(\n pluginId: string,\n resolverName: string,\n param: string,\n ctx: { workspaceDir: string; maxOutput: number },\n options?: { timeoutMs?: number },\n ): Promise<{ result?: string; error?: string }> {\n const w = this.workers.get(pluginId)\n if (!w) throw new Error(`no worker for plugin ${pluginId}`)\n return w.resolveSigil(resolverName, param, ctx, options)\n }\n\n /** RFC-105 M-N1 T-N1c:通过 RPC 代理调用 worker 侧插件的 onActivate。 */\n async activate(pluginId: string): Promise<{ error?: string }> {\n const w = this.workers.get(pluginId)\n if (!w) throw new Error(`no worker for plugin ${pluginId}`)\n return w.activate()\n }\n\n /** RFC-105 M-N1 T-N1c:通过 RPC 代理调用 worker 侧插件的 onDeactivate。 */\n async deactivate(pluginId: string): Promise<{ error?: string }> {\n const w = this.workers.get(pluginId)\n if (!w) throw new Error(`no worker for plugin ${pluginId}`)\n return w.deactivate()\n }\n\n async unload(pluginId: string): Promise<void> {\n const w = this.workers.get(pluginId)\n if (!w) return\n this.workers.delete(pluginId)\n // RFC-124 L1 修复:此前 `void w.stop()` 是 fire-and-forget——若 worker 正在执行工具,\n // `stop()` 发出 `worker.stop` 消息后立即 `terminate()`,调用方(如同一插件紧接着的\n // reload → load)拿到的 Promise 早已 resolve,但 worker 进程可能仍在被强制终止的过程中,\n // 未完成的工具回调(`pending` map 里的 resolve/reject)可能悬挂。改为 `await`,与\n // `dispose()`(已用 `Promise.all` 等待全部 worker)保持一致的\"卸载即等待终止完成\"语义。\n await w.stop()\n }\n\n async dispose(): Promise<void> {\n const ws = [...this.workers.values()]\n this.workers.clear()\n if (tmpDir) { try { rmSync(tmpDir, { recursive: true, force: true }) } catch {}; tmpDir = null; workerEntryPath = null }\n await Promise.all(ws.map((w) => w.stop().catch(() => {})))\n }\n\n get size(): number { return this.workers.size }\n}\n","/** Plugin process pool: per-plugin worker threads + lifecycle/restart/heartbeat. */\n\nimport { spawn, type ChildProcess } from 'node:child_process'\nimport { mkdtempSync, writeFileSync, rmSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport { createLogger } from '@x-otto/shared'\nimport { buildIsolationEntrySrc, ISOLATION_MSG } from './plugin-isolation-entry-template'\nimport { createPlatformSandbox, isPlatformSandboxAvailable } from '@x-otto/tools'\n\nimport {\n IsolatedPluginTransport,\n type TransportAdapter,\n type WorkerLoadedModule,\n type IsolatedPluginPool,\n} from './isolated-plugin-transport'\n\nconst logger = createLogger('@x-otto/coding:plugin-process-pool')\n\n// 子进程入口脚本——与 plugin-worker-pool.ts 共享 plugin-isolation-entry-template.ts 生成的\n// load/tool.execute/tool.cancel/worker.onActivate/worker.onDeactivate/worker.stop\n// 消息类型),差异仅在于用 process.send/process.on('message') 替代 parentPort(子进程\n// IPC 语义),使调用方(module-loader-isolated.ts 的 buildIsolatedModule)可以对两种\n// 传输层复用同一套 proxy 包装逻辑。\n\nlet tmpDir: string | null = null\nlet entryPath: string | null = null\n\nfunction getEntryPath(): string {\n if (entryPath) return entryPath\n if (!tmpDir) tmpDir = mkdtempSync(join(tmpdir(), 'otto-plugin-process-'))\n entryPath = join(tmpDir, 'process-entry.cjs')\n writeFileSync(entryPath, buildIsolationEntrySrc('process'))\n return entryPath\n}\n\nclass PluginProcess extends IsolatedPluginTransport {\n private child: ChildProcess\n /** 沙箱策略实例(如 macOS Seatbelt)持有的临时资源(如 .sb profile 文件)必须存活到\n * 子进程真正启动读取之后才能释放——见下方 constructor 内的时序说明。 */\n private sandboxToDispose: { dispose(): Promise<void> } | undefined\n\n constructor(projectRoot: string) {\n const entryPath = getEntryPath()\n const rawCommand = process.execPath\n const rawArgs = [entryPath]\n\n let spawnCommand = rawCommand\n let spawnArgs = rawArgs\n let sandboxToDispose: { dispose(): Promise<void> } | undefined\n\n if (isPlatformSandboxAvailable()) {\n const sandbox = createPlatformSandbox({ network: 'deny' })\n const wrapped = sandbox.wrap({\n command: rawCommand,\n args: rawArgs,\n options: { timeout: 0, cwd: projectRoot },\n projectRoot,\n })\n spawnCommand = wrapped.command\n spawnArgs = wrapped.args\n // 指向一个临时 .sb profile 文件的路径——真正读取这个文件发生在下方 spawn() 启动\n // sandbox-exec 子进程之后(sandbox-exec 自己在其进程内 open(2) 该文件)。若在\n // spawn() 之前就 dispose()(此前的实现是 wrap 后立即 fire-and-forget dispose),\n // profile 文件在子进程真正读取前就被删除,导致 sandbox-exec 以 65(EX_DATAERR)\n // 退出——本地实测复现:'plugin process exited unexpectedly (code 65)',\n // stderr 为空(sandbox-exec 对 -f 指向的文件已消失时不总是产生可读错误输出)。\n // 修复:持有 sandbox 实例到子进程 'exit' 事件触发后才释放(见 stop()/exit 处理)。\n sandboxToDispose = sandbox\n } else {\n logger.warn(\n 'No platform sandbox available for project/repository scope plugin isolation — ' +\n 'falling back to plain child_process (still process-isolated, but without OS-level file/network constraints)',\n )\n }\n\n const child = spawn(spawnCommand, spawnArgs, {\n cwd: projectRoot,\n stdio: ['ignore', 'ignore', 'pipe', 'ipc'],\n })\n child.unref()\n\n const adapter: TransportAdapter = {\n send: (msg) => child.send(msg),\n onMessage: (fn) =>\n child.on('message', (msg: { type: string; pluginId?: string; module?: WorkerLoadedModule; result?: unknown; error?: string; id?: string }) => fn(msg)),\n onError: (fn) => child.on('error', (err) => fn(err)),\n onExit: (fn) => child.on('exit', fn),\n // RFC-211 M3 R2 实测确认(见 RFC-211 §2 D3):仅 `child.unref()` 不足以让宿主进程\n // 在有 pending IPC 请求时正确判定事件循环空闲——需同时 unref channel 与 stderr pipe\n // 两个独立句柄,缺一都会导致宿主进程在应能自然退出时挂起。`@types/node` 的 `Readable`\n // 类型声明未包含 `ref`/`unref`(运行时确实存在,Node 的 pipe stream 底层 Handle 具备\n // 该方法,仅类型声明缺失),用最小范围的类型断言桥接。\n ref: () => {\n child.channel?.ref()\n ;(child.stderr as unknown as { ref?(): void } | null | undefined)?.ref?.()\n },\n unref: () => {\n child.channel?.unref()\n ;(child.stderr as unknown as { unref?(): void } | null | undefined)?.unref?.()\n },\n onToolTimeout: (name, timeoutMs) => {\n logger.warn({ tool: name, timeoutMs }, 'plugin tool execution timed out (isolated process)')\n },\n onErrorLog: (err) => {\n logger.warn({ err: String(err) }, 'plugin process error')\n },\n onExitLog: (code) => {\n logger.warn({ code }, 'plugin process exited unexpectedly with pending requests')\n },\n exitErrorMessage: (code) => `plugin process exited unexpectedly (code ${code})`,\n loadFailedMessage: () => 'plugin process load failed',\n alreadyLoadingMessage: (pluginId) => `plugin process already loading ${pluginId}`,\n }\n super(adapter)\n this.child = child\n this.sandboxToDispose = sandboxToDispose\n\n this.child.stderr?.on('data', (chunk: Buffer) => {\n logger.warn({ stderr: chunk.toString().slice(0, 500) }, 'plugin process stderr output')\n })\n\n // 同 plugin-worker-pool.ts 的 'exit' 监听纪律(2026-07-13 元审查修复的同一类问题):\n // 子进程被沙箱拒绝/OOM-kill/崩溃时只触发 'exit',不触发 'error',pending RPC 需要\n // 在此兜底 reject(基类已处理),此处额外释放沙箱资源。\n this.child.on('exit', () => {\n // 子进程已退出,沙箱 profile 等临时资源不再需要——此时释放安全(子进程不会再读它)。\n void this.sandboxToDispose?.dispose().catch(() => undefined)\n this.sandboxToDispose = undefined\n })\n }\n\n async stop(): Promise<void> {\n try {\n this.child.send({ type: ISOLATION_MSG.stop })\n } catch {\n // 子进程可能已终止——发送失败无害,走 kill 兜底。\n }\n try {\n this.child.kill()\n } catch {}\n this.clearPending()\n }\n}\n\n/** 与 PluginWorkerPool 同接口的子进程池(B2 project/repository scope 专用)。 */\nexport class PluginProcessPool implements IsolatedPluginPool {\n private readonly processes = new Map<string, PluginProcess>()\n private readonly maxConcurrent: number\n /** RFC-303 M4:host 方法派发器(宿主侧注入,load 时透传给新子进程)。 */\n private hostCallDispatcher:\n | ((pluginId: string, method: string, args: unknown[]) => Promise<unknown>)\n | null = null\n\n constructor(maxConcurrent = 4) {\n // 子进程+沙箱的资源开销明显高于 worker_threads(独立进程 + namespace 建立),\n // 默认并发上限比 PluginWorkerPool 的 8 更保守。\n this.maxConcurrent = maxConcurrent\n }\n\n setHostCallDispatcher(\n dispatcher: (pluginId: string, method: string, args: unknown[]) => Promise<unknown>,\n ): void {\n this.hostCallDispatcher = dispatcher\n }\n\n async load(\n pluginId: string,\n compiledPath: string,\n ctx: { pluginId: string; pluginDir: string; workspaceDir: string; hostMethods?: string[]; listActivePlugins?: boolean },\n ): Promise<WorkerLoadedModule | null> {\n if (this.processes.size >= this.maxConcurrent) return null\n await this.unload(pluginId)\n const p = new PluginProcess(ctx.workspaceDir)\n if (this.hostCallDispatcher) p.setHostCallDispatcher(this.hostCallDispatcher)\n this.processes.set(pluginId, p)\n try {\n return await p.load(pluginId, compiledPath, ctx)\n } catch (err) {\n this.processes.delete(pluginId)\n void p.stop().catch(() => {})\n throw err\n }\n }\n\n async executeTool(\n pluginId: string,\n toolName: string,\n params: unknown,\n options?: { timeoutMs?: number; signal?: AbortSignal },\n ): Promise<{ result?: unknown; error?: string }> {\n const p = this.processes.get(pluginId)\n if (!p) throw new Error(`no process for plugin ${pluginId}`)\n return p.executeTool(toolName, params, options)\n }\n\n /** RFC-210 D3 代理修复:通过 RPC 代理调用子进程侧插件的 sigilResolver。 */\n async resolveSigil(\n pluginId: string,\n resolverName: string,\n param: string,\n ctx: { workspaceDir: string; maxOutput: number },\n options?: { timeoutMs?: number },\n ): Promise<{ result?: string; error?: string }> {\n const p = this.processes.get(pluginId)\n if (!p) throw new Error(`no process for plugin ${pluginId}`)\n return p.resolveSigil(resolverName, param, ctx, options)\n }\n\n async activate(pluginId: string): Promise<{ error?: string }> {\n const p = this.processes.get(pluginId)\n if (!p) throw new Error(`no process for plugin ${pluginId}`)\n return p.activate()\n }\n\n async deactivate(pluginId: string): Promise<{ error?: string }> {\n const p = this.processes.get(pluginId)\n if (!p) throw new Error(`no process for plugin ${pluginId}`)\n return p.deactivate()\n }\n\n async unload(pluginId: string): Promise<void> {\n const p = this.processes.get(pluginId)\n if (!p) return\n this.processes.delete(pluginId)\n await p.stop()\n }\n\n async dispose(): Promise<void> {\n const ps = [...this.processes.values()]\n this.processes.clear()\n if (tmpDir) {\n try {\n rmSync(tmpDir, { recursive: true, force: true })\n } catch {}\n tmpDir = null\n entryPath = null\n }\n await Promise.all(ps.map((p) => p.stop().catch(() => {})))\n }\n\n get size(): number {\n return this.processes.size\n }\n}\n","/** Isolated module loader: compile + eval in per-plugin vm sandbox. */\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport type { PluginContext, PluginModule, PluginTool } from '@x-otto/plugin'\nimport { evaluatePluginTrust, isPluginTrusted } from '@x-otto/plugin'\nimport { compilePluginModule, loadPluginModule } from './module-loader'\nimport { PluginWorkerPool, type IsolatedPluginPool } from './plugin-worker-pool'\nimport { PluginProcessPool } from './plugin-process-pool'\nimport { createLogger } from '@x-otto/shared'\n\nconst logger = createLogger('@x-otto/coding:plugin-module-loader-isolated')\n\n/** 全局单例 worker 线程池(scope=user/builtin 插件走此池,见 poolForScope)。 */\nlet sharedWorkerPool: PluginWorkerPool | null = null\nexport function getPluginWorkerPool(): PluginWorkerPool {\n if (!sharedWorkerPool) sharedWorkerPool = new PluginWorkerPool()\n return sharedWorkerPool\n}\nexport function setPluginWorkerPool(pool: PluginWorkerPool): void {\n sharedWorkerPool = pool\n}\n\n/**\n * RFC-177 B2:全局单例子进程+沙箱池(scope=project/repository 插件走此池)。\n * 与 sharedWorkerPool 平行存在,各自独立生命周期(dispose 时两者都要清理)。\n */\nlet sharedProcessPool: PluginProcessPool | null = null\nexport function getPluginProcessPool(): PluginProcessPool {\n if (!sharedProcessPool) sharedProcessPool = new PluginProcessPool()\n return sharedProcessPool\n}\nexport function setPluginProcessPool(pool: PluginProcessPool): void {\n sharedProcessPool = pool\n}\n\nexport async function disposePluginWorkerPool(): Promise<void> {\n await Promise.all([\n sharedWorkerPool?.dispose() ?? Promise.resolve(),\n sharedProcessPool?.dispose() ?? Promise.resolve(),\n ])\n}\n\n/**\n * RFC-177 B2:按插件 scope 选择隔离强度——project/repository(不受控第三方代码,\n * 团队协作场景下来源可能是仓库贡献者而非用户本人)用子进程+OS沙箱重隔离\n * (PluginProcessPool);user/builtin(用户本地主动安装/仓库自带代码,来源相对\n * 可控)继续用同进程 worker_threads 轻隔离(PluginWorkerPool,已验证稳定)。\n * 与 evaluatePluginTrust 的 pendingCapabilities 门禁正交——门禁决定\"能不能跑\",\n * 本函数决定\"跑在哪种隔离强度下\",两者独立生效、互不覆盖。\n */\nexport function poolForScope(scope: DiscoveredPlugin['scope']): IsolatedPluginPool {\n if (scope === 'project' || scope === 'repository') {\n return getPluginProcessPool()\n }\n return getPluginWorkerPool()\n}\n\n/**\n * 判断该插件是否应路由到 worker 隔离加载:声明了 `tools` 能力即路由(无论是否已获授权——\n * 授权判定由 `loadPluginModuleIsolated` 内部的 `pendingCapabilities` 门禁独立把关,见上方\n * 模块定位说明)。未声明 `tools` 的插件走 `module-loader.ts`(最小侵入、零性能回退)。\n */\nexport function shouldUseIsolatedLoading(plugin: DiscoveredPlugin): boolean {\n // RFC-177 §1 两层隔离设计(RFC-183 O-4 考古确认,勿再判为语义不一致):本函数\n // (capabilities 路由)决定\"是否走隔离\",poolForScope(scope 映射)决定\"隔离强度\"\n // ——两者正交,独立生效。声明了 tools → 需要隔离执行(纵深防御沙箱,非\"免于信任门\"\n // 通道——信任门由 loadPluginModuleIsolated 内的 pendingCapabilities 独立把关)。\n return Boolean(plugin.manifest.capabilities?.includes('tools'))\n}\n\n/**\n * 把 worker 侧返回的声明式部分 + tool 名前列表,包装成主线程 PluginModule(工具 execute 走 RPC proxy)。\n * 纯数据部分(contributions)直接复用;没有 hooks/tools/providerFactories 在隔离模式下(后续升级才加入)。\n */\nfunction buildIsolatedModule(\n pluginId: string,\n loaded: import('./plugin-worker-pool').WorkerLoadedModule,\n pool: IsolatedPluginPool,\n): PluginModule {\n const module: PluginModule = {}\n\n // 声明式 contribs —— worker 返回的纯数据可安全复用\n if (loaded.contributions) {\n module.contributions = loaded.contributions as PluginModule['contributions']\n }\n\n // 工具 → proxy AgentTool(execute 走 RPC 到 worker)\n if (loaded.tools.length > 0) {\n module.tools = loaded.tools.map((toolMeta) => ({\n name: toolMeta.name,\n description: toolMeta.description || toolMeta.name,\n parameters: {\n safeParse(input: unknown) {\n // RFC-105 M-N1 T-N1a:若无 parameters schema(hasParameters=false),放行空对象;\n // 有 parameters 时透传原始输入(worker 端做实际校验)\n if (!toolMeta.hasParameters) return { success: true, data: {} }\n return { success: true, data: input }\n },\n // worker 侧在装载时调用原始 zod schema 的 toJSONSchema() 并序列化经 postMessage\n // 传回主线程;此处作为 proxy 方法返回缓存结果,供 provider 请求体型别\n // (chat-completions.ts → toJsonSchema → toJSONSchema?.())。\n ...(toolMeta.parametersSchema !== undefined\n ? { toJSONSchema: () => toolMeta.parametersSchema }\n : {}),\n },\n // 第二参数由 toAgentTool 透传({ signal }):超时默认值在 pool 层生效(60s),\n // abort signal 让 Esc/turn 中断能提前 settle 这次 RPC(终局 review 必须调整项——\n // 此前真实工具调用路径既无超时也无 signal 传播,插件工具挂起 = 整个 turn 卡死)。\n async execute(args: unknown, opts?: { signal?: AbortSignal }) {\n const { result, error } = await pool.executeTool(pluginId, toolMeta.name, args, {\n signal: opts?.signal,\n })\n if (error) throw new Error(error)\n // normalize 为 ToolResult 形状(对齐 toAgentTool 的 normalizeToolResult)\n if (result && typeof result === 'object' && Array.isArray((result as { content?: unknown }).content)) {\n return result\n }\n const text = typeof result === 'string' ? result : JSON.stringify(result ?? null)\n return { content: [{ type: 'text' as const, text }] }\n },\n })) as PluginTool[]\n }\n\n // 终局 review 2026-07-21 B-1:sigilProviders(@ 动态搜索)未被 RPC 协议代理(高频逐键\n // 查询,代理需独立 debounce/batch 设计),隔离路径下静默不生效。此前该组合(tools 触发\n // 隔离装载 + sigilProviders)零告警——SigilAxisHandler 见 undefined 直接跳过,用户只能\n // 黑盒排查\"@ 搜索怎么不工作\"。显式 warn 把静默失效变成可诊断信号 + 给出规避方向。\n if (loaded.hasSigilProviders) {\n logger.warn(\n { pluginId },\n 'plugin declares sigilProviders but is loaded via the isolated path (tools capability) — ' +\n 'dynamic @ search providers are not proxied over RPC and will be inactive; ' +\n 'declare input.resolver without tools to use the in-process path, or use static sigilEntries',\n )\n }\n\n // @ 资源 resolve(RFC-210 D3 代理修复):worker 侧只回传元数据(name/timeoutMs/uiHostOnly),\n // resolve 函数体经 RPC 代理执行(同 tools 的 execute proxy 心智模型)。\n if (loaded.sigilResolvers?.length > 0) {\n module.sigilResolvers = {}\n for (const meta of loaded.sigilResolvers) {\n module.sigilResolvers[meta.name] = {\n resolve: (param: string, ctx: { workspaceDir: string; maxOutput: number }) =>\n pool.resolveSigil(pluginId, meta.name, param, ctx, { timeoutMs: meta.timeoutMs }).then((r) => {\n if (r.error) throw new Error(r.error)\n return r.result ?? ''\n }),\n timeoutMs: meta.timeoutMs,\n uiHostOnly: meta.uiHostOnly,\n }\n }\n }\n\n // lifecycle hooks: onActivate/onDeactivate 经 RPC 代理到 worker 侧执行(RFC-105 M-N1 T-N1c)\n if (loaded.hasOnActivate) {\n module.onActivate = async () => {\n const { error } = await pool.activate(pluginId)\n if (error) throw new Error(error)\n }\n }\n if (loaded.hasOnDeactivate) {\n module.onDeactivate = async () => {\n const { error } = await pool.deactivate(pluginId)\n if (error) throw new Error(error)\n }\n }\n\n return module\n}\n\nexport interface IsolatedLoadResult {\n pluginId: string\n module: PluginModule\n /** RFC-287 M3:编译产物路径(services 轴用于在 service worker 中 require)。 */\n compiledPath?: string\n}\n\n/**\n * 在 worker 线程中隔离加载插件代码模块(不执行主进程 require)。\n * 编译在主进程做(esbuild 已隔离——输入=文件、输出=字节流),执行在 worker 做。\n *\n * @returns null 当编译/worker 加载/工厂调用失败时(fail-soft,同 module-loader 行为)。\n */\nexport async function loadPluginModuleIsolated(\n plugin: DiscoveredPlugin,\n ctx: PluginContext,\n pool?: IsolatedPluginPool,\n /** RFC-153 D3:加载/编译/worker 失败的旁路通知(可选,宿主层注入),供\n * `PluginHealthTracker.recordError` 写入。不改变现有 fail-soft 返回 null 的控制流。 */\n onLoadError?: (pluginId: string, message: string) => void,\n): Promise<IsolatedLoadResult | null> {\n // RFC-140 D3(review P0-2 的隔离路径对等修复):此前本函数无任何信任检查——连\n // module-loader.ts 原有的布尔 `isPluginTrusted` 门控都缺失,调用方(`plugin-module-manager.ts`)\n // 只在 worker 已经执行完插件代码之后才核对 `verdict.gated`。补齐与 `loadPluginModule`\n // 完全对称的两层检查:布尔信任(fail-closed R4/D3)+ per-capability pendingCapabilities\n // (fail-closed,工厂执行前)——未授权插件在两条加载路径下都不会被执行,worker 隔离\n // 只对已授权插件生效(纵深防御,见模块头注释的定位说明,非\"隔离未授权插件\"通道)。\n if ((plugin.scope === 'project' || plugin.scope === 'repository') && !isPluginTrusted(plugin.dir)) {\n logger.warn({ pluginId: plugin.id }, 'project/repository plugin not trusted, skipping isolated code module (fail-closed)')\n return null\n }\n const verdict = evaluatePluginTrust(plugin)\n if (verdict.pendingCapabilities.length > 0) {\n logger.warn(\n { pluginId: plugin.id, pendingCapabilities: verdict.pendingCapabilities },\n 'plugin declares high-risk capabilities not yet granted, skipping isolated code module before worker execution (fail-closed, RFC-140 D3)',\n )\n return null\n }\n\n // RFC-177 B2:显式传入 pool(测试/调用方定制)优先;否则按 plugin.scope 自动选择\n // 隔离强度(project/repository → 子进程+沙箱,user/builtin → worker_threads)。\n const actualPool = pool ?? poolForScope(plugin.scope)\n\n // Step 1: 主进程编译(esbuild 本身是隔离的构建工具,在此执行不引入插件代码)\n let compiledPath: string | null\n try {\n compiledPath = await compilePluginModule(plugin.dir)\n } catch (err) {\n const message = String(err)\n logger.warn({ pluginId: plugin.id, err: message }, 'plugin.ts compile failed (isolated path)')\n onLoadError?.(plugin.id, `compile failed: ${message}`)\n return null\n }\n if (!compiledPath) return null\n\n // RFC-303 M4:host 能力面经反向 RPC 代理到隔离端——load 时下发白名单方法名\n // (隔离端据此构造 host stub),并注入派发器(隔离端 host 调用回宿主执行,R2 的\n // origin 强制仍在宿主侧 buildHostCapabilities 单点完成,RPC 只传输不复制安全逻辑)。\n // RFC-303 D5(M7):互发现经同一 hostCall 通道——'listActivePlugins' 加入白名单方法名\n //(隔离端 stub 据此可调;宿主侧 dispatcher 白名单放行它)。\n const hostMethods = [...Object.keys(ctx.host ?? {}), 'listActivePlugins']\n // RFC-303 D5(M7):dispatcher 安装条件放宽——listActivePlugins 是 PluginContext 字段\n // (非 host 能力),插件可能只有互发现没有 host 能力;两者都无时不安装(keep 原 fail-closed)。\n if ((ctx.host && Object.keys(ctx.host).length > 0) || ctx.listActivePlugins) {\n const host = ctx.host\n actualPool.setHostCallDispatcher?.(async (_pluginId, method, args) => {\n // RFC-303 D5(M7):互发现快照(R2 纯数据)——ctx.listActivePlugins 是 PluginContext\n // 字段非 host 能力,必须在 host 检查**之前**分支(否则被\"方法不在 host 上\"误拒)。\n if (method === 'listActivePlugins') {\n return ctx.listActivePlugins ? ctx.listActivePlugins() : []\n }\n if (!host || typeof (host as Record<string, unknown>)[method] !== 'function') {\n throw new Error(`host method '${method}' not available (not declared or not granted)`)\n }\n // 白名单二次校验:只放行 PluginHostCapabilities 的既有字段(fail-closed 双保险,\n // 与隔离端 stub 的白名单过滤互补——即使隔离端被攻破伪造方法名也到不了真实宿主方法)。\n if (!(method in host)) {\n throw new Error(`host method '${method}' not in whitelist`)\n }\n const fn = (host as Record<string, (...a: unknown[]) => unknown>)[method]!\n return fn(...(args as unknown[]))\n })\n }\n\n // Step 2: worker 加载 + 执行工厂\n try {\n const loaded = await actualPool.load(plugin.id, compiledPath, {\n pluginId: ctx.pluginId,\n pluginDir: ctx.pluginDir,\n workspaceDir: ctx.workspaceDir,\n hostMethods,\n // 隔离端 factory ctx 的 listActivePlugins 标记(entry-template 读 msg.ctx.listActivePlugins\n // 据此构造 stub——经 postMessage 传到隔离端的是纯数据标记,函数在隔离端本地构造)。\n listActivePlugins: true,\n })\n if (loaded === null) {\n // RFC-105 M-N1 T-N1b:池满时降级为主进程内联加载(信任门已在调用方过,safe)。\n // RFC-177 B2 修正:project/repository scope 插件**不允许**降级到零隔离的\n // 主进程内联执行——那会彻底违背\"给不受控第三方代码更强隔离\"的设计初衷(用户\n // 特意要求的隔离强度不能因为池资源紧张被静默降级为最弱档)。这两档 scope 的\n // 池满场景改为 fail-soft 返回 null(同编译/加载失败的既有处理路径),插件本轮\n // 不可用但记录可诊断日志,不悄悄降级安全边界。只有 user/builtin scope(本就\n // 只要求 worker_threads 强度)才允许池满降级为主进程内联。\n if (plugin.scope === 'project' || plugin.scope === 'repository') {\n const message = 'plugin process pool exhausted (project/repository scope requires isolated execution, refusing to downgrade to in-process)'\n logger.warn({ pluginId: plugin.id }, message)\n onLoadError?.(plugin.id, message)\n return null\n }\n logger.info({ pluginId: plugin.id }, 'plugin worker pool exhausted, falling back to in-process load')\n return loadPluginModule(plugin, ctx, onLoadError)\n }\n return {\n pluginId: plugin.id,\n module: buildIsolatedModule(plugin.id, loaded, actualPool),\n // RFC-287 M3:services 轴要用它在独立 service worker 里 require 同一份产物。\n compiledPath,\n }\n } catch (err) {\n const message = String(err)\n logger.warn({ pluginId: plugin.id, err: message }, 'plugin.ts worker load failed')\n onLoadError?.(plugin.id, `worker load failed: ${message}`)\n return null\n }\n}\n","/** Plugin activation events: onPluginActivate/onPluginDeactivate hook dispatch. */\n\n/** 该组激活事件在给定「已触发事件集合」下是否应激活。`onStartup` 恒真。 */\nexport function shouldActivateNow(events: readonly string[], firedEvents: ReadonlySet<string>): boolean {\n if (events.includes('onStartup')) return true\n return events.some((e) => firedEvents.has(e))\n}\n","import type { HookRegistry } from '@x-otto/hooks'\nimport { pluginHooksToSpecs } from '../../plugin/module-wiring'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * hooks 轴(`PluginHooks`→`HookSpec[]`)。无 capability 门控——hooks 是插件基础能力,\n * 不受高危能力体系约束。\n */\nexport class HooksAxisHandler implements PluginAxisHandler {\n readonly axisName = 'hooks'\n /** RFC-216 D1:hook 名按 pluginId 分桶,支持 per-plugin 差量撤销。 */\n private hookNamesByPlugin = new Map<string, string[]>()\n\n constructor(private readonly hookRegistry: HookRegistry) {}\n\n register(ctx: PluginAxisContext): void {\n const hooks = ctx.loaded.module.hooks\n if (hooks) {\n const specs = pluginHooksToSpecs(ctx.plugin.id, hooks)\n this.hookRegistry.registerAll(specs)\n const bucket = this.hookNamesByPlugin.get(ctx.plugin.id) ?? []\n bucket.push(...specs.map((s) => s.name))\n this.hookNamesByPlugin.set(ctx.plugin.id, bucket)\n }\n }\n\n unregister(pluginId: string): void {\n const names = this.hookNamesByPlugin.get(pluginId)\n if (!names) return\n for (const name of names) this.hookRegistry.unregister(name)\n this.hookNamesByPlugin.delete(pluginId)\n }\n\n unregisterAll(): void {\n for (const pluginId of [...this.hookNamesByPlugin.keys()]) this.unregister(pluginId)\n }\n}\n","import type { HookRegistry } from '@x-otto/hooks'\nimport type { Logger } from '@x-otto/shared'\nimport { pluginMonitorsToSpecs } from '../../plugin/module-wiring'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\nimport type { MonitorEvent, PluginMonitor } from '@x-otto/plugin'\n\n/**\n * monitors 轴(RFC-129 D6/M129-3):需 manifest 声明 capabilities:[\"monitor\"](fail-closed R6)。\n * 底层复用 hookNames 数组做幂等清理(原实现纪律:不新建独立注册表)——本 handler 持有\n * 自己的追踪数组(不与 HooksAxisHandler 共享),效果等价(都是 hookRegistry.unregister)。\n *\n * RFC-303 D4(M7):除 HookTiming 派发的 3 个引擎事件外,宿主层事件(plugin 启停、\n * settings 变更、tool 增减)由宿主直接派发——本 handler 额外持有已激活 monitors 列表\n * (monitorsByPlugin),提供 `dispatch` 统一派发通道(R5 失败隔离:每个 monitor 每次\n * onEvent 独立 try/catch,绝不 rethrow 影响引擎主流程或其他插件)。reload 时随\n * unregister 同步清理,避免卸载后插件仍收到事件。\n */\nexport class MonitorsAxisHandler implements PluginAxisHandler {\n readonly axisName = 'monitors'\n /** RFC-216 D1:monitor hook 名按 pluginId 分桶,支持 per-plugin 差量撤销。 */\n private hookNamesByPlugin = new Map<string, string[]>()\n /** RFC-303 D4:已激活 monitors(pluginId → monitors),供宿主层事件直接派发。 */\n private monitorsByPlugin = new Map<string, readonly PluginMonitor[]>()\n\n constructor(\n private readonly hookRegistry: HookRegistry,\n private readonly logger: Logger,\n ) {}\n\n register(ctx: PluginAxisContext): void {\n const monitors = ctx.loaded.module.monitors\n if (!monitors?.length) return\n if (!ctx.plugin.manifest.capabilities?.includes('monitor')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin declares monitors but not capabilities:[\"monitor\"], skipped (fail-closed)',\n )\n return\n }\n const monitorSpecs = pluginMonitorsToSpecs(ctx.plugin.id, monitors)\n this.hookRegistry.registerAll(monitorSpecs)\n const bucket = this.hookNamesByPlugin.get(ctx.plugin.id) ?? []\n bucket.push(...monitorSpecs.map((s) => s.name))\n this.hookNamesByPlugin.set(ctx.plugin.id, bucket)\n // RFC-303 D4:存入宿主派发表(reload 差量时随 unregister 清理)。\n this.monitorsByPlugin.set(ctx.plugin.id, monitors)\n }\n\n unregister(pluginId: string): void {\n const names = this.hookNamesByPlugin.get(pluginId)\n if (names) {\n for (const name of names) this.hookRegistry.unregister(name)\n this.hookNamesByPlugin.delete(pluginId)\n }\n this.monitorsByPlugin.delete(pluginId)\n }\n\n unregisterAll(): void {\n for (const pluginId of [...this.hookNamesByPlugin.keys()]) this.unregister(pluginId)\n }\n\n /**\n * RFC-303 D4:向全部已激活 monitor 派发宿主层事件(observer-only,R5 失败隔离)。\n * 顺序遍历(插件激活顺序),单个 monitor 抛错只记录日志、继续派发其余 monitor。\n */\n async dispatch(event: MonitorEvent): Promise<void> {\n for (const [pluginId, monitors] of this.monitorsByPlugin) {\n for (const monitor of monitors) {\n try {\n await monitor.onEvent(event)\n } catch (err) {\n this.logger.warn(\n { pluginId, monitorId: monitor.id, eventType: event.type, err: String(err) },\n 'plugin monitor onEvent threw (host event), isolated',\n )\n }\n }\n }\n }\n\n /** 已激活 monitor 的插件 id 集合(供观测/测试断言)。 */\n get monitorPluginIds(): readonly string[] {\n return [...this.monitorsByPlugin.keys()]\n }\n}\n","import type { Logger } from '@x-otto/shared'\nimport type { FeedbackRegistry } from '../../feedback/feedback-registry'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * feedback 轴(RFC-209 §2.3):需 manifest 声明 capabilities:[\"feedback\"](fail-closed)。\n * 单实现注册——同 source(同插件)reload 走 replace() 原子覆盖,跨插件冲突走 register()\n * 覆盖检查(见 FeedbackRegistry 头注释)。\n */\nexport class FeedbackAxisHandler implements PluginAxisHandler {\n readonly axisName = 'feedback'\n private feedbackRegisteredSources = new Set<string>()\n\n constructor(\n private readonly feedbackRegistry: FeedbackRegistry | undefined,\n private readonly logger: Logger,\n ) {}\n\n register(ctx: PluginAxisContext): void {\n const feedbackProviders = ctx.loaded.module.feedbackProviders\n if (!feedbackProviders || !this.feedbackRegistry) return\n if (!ctx.plugin.manifest.capabilities?.includes('feedback')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin declares feedbackProviders but not capabilities:[\"feedback\"], skipped (fail-closed)',\n )\n return\n }\n const accepted = this.feedbackRegistry.register(feedbackProviders, ctx.plugin.id)\n if (accepted) {\n this.feedbackRegisteredSources.add(ctx.plugin.id)\n } else {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin feedbackProviders rejected (already registered by a different plugin)',\n )\n }\n }\n\n /** RFC-216 D1:per-plugin 差量撤销(FeedbackRegistry 本就按 source=pluginId 注册)。 */\n unregister(pluginId: string): void {\n if (!this.feedbackRegistry || !this.feedbackRegisteredSources.has(pluginId)) return\n this.feedbackRegistry.unregister(pluginId)\n this.feedbackRegisteredSources.delete(pluginId)\n }\n\n unregisterAll(): void {\n for (const source of [...this.feedbackRegisteredSources]) this.unregister(source)\n }\n}\n","/** Sigil loader: plugin-declared prefix resolvers + content providers. */\nimport { evaluatePluginTrust, type DiscoveredPlugin } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\n\nimport type { SigilEntry, SigilProvider, SigilResolver } from '@x-otto/interchange'\nimport type { PluginModule } from '@x-otto/plugin'\n\nconst logger = createLogger('@x-otto/coding:plugin-sigil-loader')\n\n/** value 保留前缀守卫(schema 层已拦,这里是装载层第二道,双保险防手改 manifest 绕过)。 */\nfunction hasReservedPrefix(value: string): boolean {\n return value.startsWith('!') || value.startsWith('#')\n}\n\n/**\n * 声明式轴:收集全部活跃插件的 `contributes.sigilEntries` → 带 source 的 SigilEntry[]。\n * 项目级未信任插件(gated)整体跳过;resolverId 拼 pluginId 命名空间前缀。\n */\nexport function collectPluginSigilEntries(plugins: readonly DiscoveredPlugin[]): SigilEntry[] {\n const out: SigilEntry[] = []\n for (const plugin of plugins) {\n const entries = plugin.manifest.contributes?.sigilEntries\n if (!entries?.length) continue\n if (evaluatePluginTrust(plugin).gated) {\n logger.warn({ pluginId: plugin.id }, 'plugin gated (untrusted), sigilEntries skipped')\n continue\n }\n // inputEntries 兼容窗口提示(RFC-210 D3:文件路径形态已被 sigilEntries 收编)\n for (const entry of entries) {\n if (hasReservedPrefix(entry.value)) {\n logger.warn(\n { pluginId: plugin.id, label: entry.label },\n \"sigilEntries value starts with '!' or '#' (mode-prefix collision), skipped\",\n )\n continue\n }\n out.push({\n label: entry.label,\n value: entry.value,\n kind: entry.kind,\n description: entry.description,\n source: plugin.id,\n resolverId: entry.resolverId ? `${plugin.id}:${entry.resolverId}` : undefined,\n })\n }\n }\n return out\n}\n\n/** 旧 `inputEntries` 字段兼容 warn(解析保留一个版本,不装载——RFC-210 D3 收编退役)。 */\nexport function warnDeprecatedInputEntries(plugins: readonly DiscoveredPlugin[]): void {\n for (const plugin of plugins) {\n if (plugin.manifest.contributes?.inputEntries) {\n logger.warn(\n { pluginId: plugin.id },\n 'contributes.inputEntries is deprecated (RFC-210); migrate to contributes.sigilEntries (inline array) — the file-path form is no longer loaded',\n )\n }\n }\n}\n\n/**\n * 代码式轴:把 `PluginModule.sigilResolvers` 转为带命名空间前缀的 SigilResolver[]。\n * capability 门(`input.resolver` 声明 + granted)由调用方(plugin-module-manager)核对——\n * 与 monitors/feedbackProviders 轴同位检查,本函数纯转换。\n */\nexport function pluginSigilResolversToRegistrations(\n pluginId: string,\n sigilResolvers: NonNullable<PluginModule['sigilResolvers']>,\n): SigilResolver[] {\n const out: SigilResolver[] = []\n for (const [name, def] of Object.entries(sigilResolvers)) {\n const id = `${pluginId}:${name}`\n if (typeof def === 'function') {\n out.push({ id, resolve: def })\n } else if (def && typeof def.resolve === 'function') {\n out.push({ id, resolve: def.resolve, timeoutMs: def.timeoutMs, uiHostOnly: def.uiHostOnly })\n } else {\n logger.warn({ pluginId, name }, 'sigilResolvers entry is neither function nor {resolve}, skipped')\n }\n }\n return out\n}\n\n/**\n * 代码式轴:包装 `PluginModule.sigilProviders`,强制产出条目的 source=pluginId(R2 打标,\n * 防插件伪造 source 逃逸 unregisterBySource 漏斗)。\n */\nexport function wrapPluginSigilProviders(\n pluginId: string,\n providers: readonly SigilProvider[],\n): SigilProvider[] {\n return providers.map((provider): SigilProvider => {\n return async (query, sigil) => {\n const entries = await provider(query, sigil)\n return entries\n .filter((e) => {\n if (hasReservedPrefix(e.value)) return false // 动态结果同样过保留前缀守卫(fail-soft filter)\n return true\n })\n .map((e) => ({ ...e, source: pluginId }))\n }\n })\n}\n\n/**\n * 引用完整性校验(装载完两轴后调用):sigilEntries.resolverId 无对应 resolver → warn。\n * fail-soft:不删条目(面板仍可见可选,提交时透传),只提醒作者接线缺口。\n */\nexport function validateResolverReferences(\n entries: readonly SigilEntry[],\n hasResolver: (id: string) => boolean,\n): void {\n for (const entry of entries) {\n if (entry.resolverId && !hasResolver(entry.resolverId)) {\n logger.warn(\n { label: entry.label, resolverId: entry.resolverId, source: entry.source },\n 'sigilEntries.resolverId has no matching resolver (declared sigilResolvers missing or capability not granted) — entry will pass through unexpanded at submit',\n )\n }\n }\n}\n","import type { Logger } from '@x-otto/shared'\nimport type { SigilResolverRegistry } from '../../input/sigil-resolver-registry'\nimport type { PluginInputRegistry } from '@x-otto/plugin'\nimport {\n collectPluginSigilEntries,\n pluginSigilResolversToRegistrations,\n validateResolverReferences,\n wrapPluginSigilProviders,\n} from '../../plugin/sigil-loader'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * @ 资源代码轴(RFC-210 D3):sigilResolvers(resolve 函数)+ sigilProviders(动态搜索)。\n * 需 manifest 声明 capabilities:[\"input.resolver\"](fail-closed)且已被显式授予\n * (非 pendingCapabilities——resolve 输出直接注入 prompt,高危,同 tools 轴待遇)。\n */\nexport class SigilAxisHandler implements PluginAxisHandler {\n readonly axisName = 'sigil'\n private sigilResolverSourcePluginIds = new Set<string>()\n /** RFC-216 D1:provider 注销闭包按 pluginId 分桶(原为无归属追踪的扁平数组)。 */\n private sigilProviderUnsubsByPlugin = new Map<string, Array<() => void>>()\n\n constructor(\n private readonly sigilResolverRegistry: SigilResolverRegistry | undefined,\n private readonly pluginInput: PluginInputRegistry | undefined,\n private readonly logger: Logger,\n ) {}\n\n register(ctx: PluginAxisContext): void {\n const sigilResolvers = ctx.loaded.module.sigilResolvers\n const sigilProviders = ctx.loaded.module.sigilProviders\n if (!((sigilResolvers && Object.keys(sigilResolvers).length > 0) || sigilProviders?.length)) return\n\n if (!ctx.plugin.manifest.capabilities?.includes('input.resolver')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin declares sigilResolvers/sigilProviders but not capabilities:[\"input.resolver\"], skipped (fail-closed)',\n )\n return\n }\n if (ctx.verdict.pendingCapabilities.includes('input.resolver')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin capability \"input.resolver\" declared but not yet granted, skipping sigil axes (fail-closed)',\n )\n return\n }\n\n if (sigilResolvers && this.sigilResolverRegistry) {\n const registrations = pluginSigilResolversToRegistrations(ctx.plugin.id, sigilResolvers)\n if (registrations.length > 0) {\n this.sigilResolverRegistry.register(registrations, ctx.plugin.id)\n this.sigilResolverSourcePluginIds.add(ctx.plugin.id)\n }\n }\n if (sigilProviders?.length && this.pluginInput) {\n const bucket = this.sigilProviderUnsubsByPlugin.get(ctx.plugin.id) ?? []\n for (const provider of wrapPluginSigilProviders(ctx.plugin.id, sigilProviders)) {\n bucket.push(this.pluginInput.registerProvider('@', provider))\n }\n this.sigilProviderUnsubsByPlugin.set(ctx.plugin.id, bucket)\n }\n // 引用完整性校验(评审回填):该插件声明式条目的 resolverId 是否都有对应 resolver。\n if (this.sigilResolverRegistry) {\n const registry = this.sigilResolverRegistry\n const declaredEntries = collectPluginSigilEntries([ctx.plugin])\n validateResolverReferences(declaredEntries, (id) => registry.get(id) !== undefined)\n }\n }\n\n /** RFC-216 D1:per-plugin 差量撤销(resolver 注册 + provider 注销闭包同桶清理)。 */\n unregister(pluginId: string): void {\n if (this.sigilResolverRegistry && this.sigilResolverSourcePluginIds.has(pluginId)) {\n this.sigilResolverRegistry.unregisterBySource(pluginId)\n this.sigilResolverSourcePluginIds.delete(pluginId)\n }\n const unsubs = this.sigilProviderUnsubsByPlugin.get(pluginId)\n if (unsubs) {\n for (const unsub of unsubs) {\n try {\n unsub()\n } catch {\n /* fail-soft:单个注销失败不阻塞其余 */\n }\n }\n this.sigilProviderUnsubsByPlugin.delete(pluginId)\n }\n }\n\n unregisterAll(): void {\n const pluginIds = new Set([...this.sigilResolverSourcePluginIds, ...this.sigilProviderUnsubsByPlugin.keys()])\n for (const pluginId of pluginIds) this.unregister(pluginId)\n }\n}\n","import type { FileCommand } from '@x-otto/claude-compat'\nimport { computedCommandsToFileCommands } from '../../plugin/module-wiring'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * computed commands 轴——产出不是\"注册进 registry\",而是回传数据供 App 并入\n * (`App.listFileCommands()`/`computedPluginCommands()` 消费)。`getComputedCommands()`\n * getter 供 `PluginModuleManager.commands` 转发。\n *\n * builtin scope 插件永远走不到 register()(`loadPluginModule`/`loadPluginModuleIsolated`\n * 要求真实磁盘 `plugin.ts` 存在,builtin 插件的合成 `dir` 不指向任何真实文件,`loaded`\n * 恒为 null,调用方在此之前已提前退出)——`plugin.scope !== 'builtin'` 窄化仅为满足\n * `FileCommand.scope` 的类型约束。\n */\nexport class ComputedCommandsAxisHandler implements PluginAxisHandler {\n readonly axisName = 'computedCommands'\n /** RFC-216 D1:命令按 pluginId 分桶(命令无判重逻辑、天然唯一归属插件,存完整列表即可——\n * 对抗评审 P2 已核实无 computedAgents 那类 pre/post-dedup 问题)。 */\n private commandsByPlugin = new Map<string, FileCommand[]>()\n\n register(ctx: PluginAxisContext): void {\n const computedCommands = ctx.loaded.module.contributions?.commands\n if (computedCommands?.length && ctx.plugin.scope !== 'builtin') {\n const bucket = this.commandsByPlugin.get(ctx.plugin.id) ?? []\n bucket.push(\n ...computedCommandsToFileCommands(ctx.plugin.id, ctx.plugin.scope, ctx.plugin.dir, computedCommands),\n )\n this.commandsByPlugin.set(ctx.plugin.id, bucket)\n }\n }\n\n /** RFC-216 D1:per-plugin 差量撤销(清桶)。 */\n unregister(pluginId: string): void {\n this.commandsByPlugin.delete(pluginId)\n }\n\n /**\n * 本轴无外部 registry 持有引用(computedCommands 只是内存态列表,供 App 只读消费),\n * unregisterAll() 因此是 no-op——与原实现的不对称行为对齐(原 cleanup() 从不清空\n * computedCommands,只有 reload() 清空,见下方 resetForReload())。差量 reload 的\n * removed/changed 分支走上方 unregister(pluginId),不受本 no-op 影响。\n */\n unregisterAll(): void {\n /* no-op:无外部 registry 需要撤销 */\n }\n\n /**\n * 注意(原实现的不对称行为,纯搬迁必须保留):reload() 每轮开头清空 computedCommands,\n * cleanup()(进程退出)不清空。只应由 PluginModuleManager.reload() 显式调用,\n * 不纳入 unregisterAll()(那样会被 cleanup() 也调用,破坏原有不对称行为)。\n */\n resetForReload(): void {\n this.commandsByPlugin.clear()\n }\n\n getComputedCommands(): FileCommand[] {\n return [...this.commandsByPlugin.values()].flat()\n }\n}\n","import type { AgentProfile } from '@x-otto/prompt'\nimport { computedAgentsToProfiles } from '../../plugin/module-wiring'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * computed agents 轴——产出不是\"注册进 registry\",而是回传数据供 App 并入\n * `declarativeAgentProfiles`(`reload()` 的返回值)。同名已存在(otto/claude/文件 +\n * 早前插件)→ 跳过,插件不夺名 R7。\n *\n * RFC-216 D1(对抗评审 P0 修订):桶里存判重**前**的全量 profiles(含会被 shadow 的),\n * 判重完全移到 `getAppendedAgents()` 惰性执行——差量 reload 下 unchanged 插件不重跑\n * register,若序前插件被 removed,其 shadow 的后继同名 agent 必须能在 getter 重算时\n * 自然浮现(桶只存 accepted 子集则无从恢复)。判重顺序 = Map 插入顺序 = register 调用\n * 顺序(先到先得语义与原实现等价)。\n *\n * `existingAgentNames`(本轮 reload() 调用时已有声明式 agent 名)在 `resetForReload()`\n * 时注入,供 getter 判重基线。\n */\nexport class ComputedAgentsAxisHandler implements PluginAxisHandler {\n readonly axisName = 'computedAgents'\n /** pre-dedup 全量 profiles 按 pluginId 分桶(RFC-216 D1)。 */\n private profilesByPlugin = new Map<string, AgentProfile[]>()\n private existingAgentNames: ReadonlySet<string> = new Set()\n\n register(ctx: PluginAxisContext): void {\n const computedAgents = ctx.loaded.module.contributions?.agents\n if (!computedAgents?.length) return\n const bucket = this.profilesByPlugin.get(ctx.plugin.id) ?? []\n bucket.push(...computedAgentsToProfiles(computedAgents))\n this.profilesByPlugin.set(ctx.plugin.id, bucket)\n }\n\n /** RFC-216 D1:per-plugin 差量撤销(清桶;getter 重算时被其 shadow 的同名 agent 自然浮现)。 */\n unregister(pluginId: string): void {\n this.profilesByPlugin.delete(pluginId)\n }\n\n /**\n * no-op——与原实现的不对称行为对齐(原 unregisterAll 即 no-op,cleanup() 从不清空\n * appendedAgents,清空历来由 reload 路径负责;差量化后由 unregister(pluginId) 承担)。\n */\n unregisterAll(): void {\n /* no-op:无外部 registry 需要撤销 */\n }\n\n /** RFC-216 D2:差量 reload 每轮开头调用——只更新判重基线,不清桶(桶由 per-plugin\n * unregister 差量清理,unchanged 插件的 pre-dedup profiles 跨轮保留)。 */\n setExistingAgentNames(existingAgentNames: ReadonlySet<string>): void {\n this.existingAgentNames = existingAgentNames\n }\n\n /** 全量重置(清桶 + 判重基线)。差量 reload 不再使用;保留给测试/显式全量场景。 */\n resetForReload(existingAgentNames: ReadonlySet<string>): void {\n this.profilesByPlugin.clear()\n this.existingAgentNames = existingAgentNames\n }\n\n /** 惰性判重:按桶插入顺序(= register 调用顺序)先到先得,与原实现的插入时判重等价。 */\n getAppendedAgents(): AgentProfile[] {\n const existing = new Set(this.existingAgentNames)\n const accepted: AgentProfile[] = []\n for (const profiles of this.profilesByPlugin.values()) {\n for (const profile of profiles) {\n if (!existing.has(profile.name)) {\n existing.add(profile.name)\n accepted.push(profile)\n }\n }\n }\n return accepted\n }\n}\n","import type { SkillRegistry } from '@x-otto/skill'\nimport { computedSkillsToDefinitions } from '../../plugin/module-wiring'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * computed skills 轴——注册进 SkillRegistry(discover 已在前跑;同名已存在→跳过不夺名 R7)。\n */\nexport class ComputedSkillsAxisHandler implements PluginAxisHandler {\n readonly axisName = 'computedSkills'\n /** RFC-216 D1:skill 名按 pluginId 分桶,支持 per-plugin 差量撤销。 */\n private skillNamesByPlugin = new Map<string, string[]>()\n\n constructor(private readonly skillRegistry: SkillRegistry | null) {}\n\n register(ctx: PluginAxisContext): void {\n const computedSkills = ctx.loaded.module.contributions?.skills\n if (!computedSkills?.length || !this.skillRegistry) return\n const bucket = this.skillNamesByPlugin.get(ctx.plugin.id) ?? []\n for (const { definition, source } of computedSkillsToDefinitions(\n ctx.plugin.id,\n ctx.plugin.dir,\n computedSkills,\n )) {\n if (!this.skillRegistry.get(definition.metadata.name)) {\n this.skillRegistry.register(definition, source)\n bucket.push(definition.metadata.name)\n }\n }\n if (bucket.length > 0) this.skillNamesByPlugin.set(ctx.plugin.id, bucket)\n }\n\n unregister(pluginId: string): void {\n const names = this.skillNamesByPlugin.get(pluginId)\n if (!names) return\n for (const name of names) this.skillRegistry?.unregister(name)\n this.skillNamesByPlugin.delete(pluginId)\n }\n\n unregisterAll(): void {\n for (const pluginId of [...this.skillNamesByPlugin.keys()]) this.unregister(pluginId)\n }\n}\n","/** Provider loader: plugin-declared model providers (OAuth/key/endpoint). */\nimport {\n registerProviderDescriptor,\n unregisterProviderDescriptor,\n isBuiltinProviderId,\n OAUTH_PROVIDER_IDS,\n} from '@x-otto/ai'\nimport type { ProviderRegistry, AuthStore, ModelRegistry, Model, ProviderStream, Api, TokenDerivedHeader, ModelsRequestOpts, Cost, WireProtocolRegistry } from '@x-otto/ai'\n// RFC-148 D3:buildFactory 不再直接 import 三个 createXxxProvider 工厂——改为惰性查\n// WireProtocolRegistry(M1 起由 wire-protocol-shim.ts 桥接,M2 迁移完成后改由\n// otto-wire-protocols 插件填充)。PKCE/DeviceFlow OAuth 模板与协议实现无关,仍直接 import。\nimport { PKCEOAuthTemplate, DeviceFlowOAuthTemplate } from '@x-otto/provider'\nimport type { PluginProviderEntry, PluginManifest } from '@x-otto/plugin'\nimport { createLogger, ProviderError } from '@x-otto/shared'\n\nconst logger = createLogger('@x-otto/coding:plugin-provider-loader')\n\n/**\n * RFC-185 D3:给 wire 协议实现构造宿主侧 logger 端口。宿主进程的 createLogger 单例\n * 受 setLogLevel / OTTO_LOG_STDERR 通道开关统一管辖——插件 bundle 内的协议代码经此\n * 端口打日志,不再自建第二单例。\n */\nfunction buildWireProtocolLogger(wireApi: string): import('@x-otto/provider').WireProtocolLogger {\n const wireLogger = createLogger(`wire-protocol:${wireApi}`)\n // pino 的 LogFn 重载((obj, msg?, ...args) | (msg, ...args))与端口的宽签名不直接匹配,\n // 经单一收窄点转发(行为等价:首参对象走 mergingObject 形态,字符串走格式化形态)。\n const forward = (fn: (this: void, ...a: never[]) => void) =>\n (objOrMsg: string | Record<string, unknown>, ...args: unknown[]) =>\n (fn as (...a: unknown[]) => void)(objOrMsg, ...args)\n return {\n debug: forward(wireLogger.debug.bind(wireLogger)),\n info: forward(wireLogger.info.bind(wireLogger)),\n warn: forward(wireLogger.warn.bind(wireLogger)),\n error: forward(wireLogger.error.bind(wireLogger)),\n }\n}\n\n/** 插件声明式 provider 的 api 命名空间前缀(RFC-105 D3:`<pluginId>:<id>` 防碰撞)。 */\nexport function pluginProviderApi(pluginId: string, providerId: string): string {\n return `${pluginId}:${providerId}`\n}\n\n/**\n * 计算某插件 manifest 里\"该走裸 id(跳过命名空间前缀)\"的 OAuth 短名集合(终局 review\n * 2026-07-20 O-3 修复)。并集来源两个:① `contributes.providers[].preserveProviderId`\n * 引用的 `oauthRef`(声明式轴首方插件信号);② 顶层 `manifest.codeProviderIds`(代码式\n * provider 轴的 `preserveProviderId` 对等物,见 `manifest.ts` 该字段注释)——两条轴共用\n * 同一份\"该走裸 id\"判定。\n *\n * 此前 `namespacedOAuthIds`/`loadPluginProviders`/`unloadPluginProviders` 三处各自计算\n * 一份逐字相同的逻辑(三轮独立 review 均发现),必须保持一致——否则 load/unload 不是\n * 同一把钥匙。抽取为单一函数消除重复,三处调用方改为直接复用本函数的结果。\n */\nexport function buildPreservedOAuthRefs(manifest: PluginManifest): Set<string> {\n return new Set([\n ...(manifest.contributes?.providers ?? [])\n .filter((p) => p.preserveProviderId === true && p.oauthRef)\n .map((p) => p.oauthRef!),\n ...(manifest.codeProviderIds ?? []),\n ])\n}\n\n/**\n * 计算某插件 manifest 声明的 OAuth 条目对应的命名空间化 id 集合(RFC-140 D7/T18)。\n * 与 `loadPluginProviders`/`unloadPluginProviders` 内部计算 `namespacedId` 用的同一套\n * `preservedOAuthRefs` 规则(必须保持一致,否则 load/unload 不是同一把钥匙)。供\n * `PluginProviderManager` 在 reload 时算出\"changed 插件新旧 manifest 都存在的 OAuth id\"\n * 交集,驱动 T18 的原子替换路径。\n */\nexport function namespacedOAuthIds(pluginId: string, manifest: PluginManifest): Set<string> {\n const preservedOAuthRefs = buildPreservedOAuthRefs(manifest)\n return new Set(\n (manifest.contributes?.oauth ?? []).map((e) =>\n preservedOAuthRefs.has(e.id) ? e.id : pluginProviderApi(pluginId, e.id),\n ),\n )\n}\n\n/**\n * 计算某插件 manifest 声明的\"会注册 wire 工厂\"的 provider api 集合(终局 review\n * 2026-07-21 A-1,与 `namespacedOAuthIds` 的 T18 角色对等):`contributes.providers`\n * 中非 catalogOnly 且具备 baseUrl+wireApi 的条目——只有这些条目会走\n * `ProviderRegistry.registerPluginProvider`(catalogOnly 条目只注册 descriptor/envKey/\n * models,不注册工厂)。api 推导规则与 `loadProviderAxis` 内部一致(preserveProviderId\n * 生效时裸 id,否则命名空间 id)——必须保持一致,否则 load/unload 不是同一把钥匙。\n * 供 `PluginProviderManager` 在 reload 时算出\"changed 插件新旧 manifest 都存在的 api\"\n * 交集,驱动 `replacePluginProvider` 原子替换路径(消除 unregister→register 之间的\n * await 间隙内并发流请求命中 PROVIDER_NOT_FOUND 的窗口)。\n */\nexport function namespacedProviderApis(pluginId: string, manifest: PluginManifest): Set<string> {\n return new Set(\n (manifest.contributes?.providers ?? [])\n .filter((e) => e.catalogOnly !== true && e.baseUrl && e.wireApi)\n .map((e) => (e.preserveProviderId === true ? e.id : pluginProviderApi(pluginId, e.id))),\n )\n}\n\n/**\n * 代码式 `providerFactories` 注册 api 的裸 id 逃生口(RFC-130 D7 扩展):兼容两条路径——\n * ① manifest `codeProviderIds` 白名单(M105-6 既有机制,纯代码式插件如 plugin-anthropic\n * 专用,无 `contributes.providers` 声明);② `contributes.providers[].preserveProviderId`\n * (RFC-130 D7 新增,供\"声明式 catalog + 代码式工厂\"共存的插件如 plugin-cursor 复用同一\n * 处声明,不强制重复声明 codeProviderIds——单一事实来源)。任一命中即用裸 id 注册。\n */\nexport function resolveCodeProviderApi(\n pluginId: string,\n providerName: string,\n manifest: PluginManifest,\n): string {\n const preserved = new Set([\n ...(manifest.codeProviderIds ?? []),\n ...(manifest.contributes?.providers ?? [])\n .filter((p) => p.preserveProviderId === true)\n .map((p) => p.id),\n ])\n return preserved.has(providerName) ? providerName : pluginProviderApi(pluginId, providerName)\n}\n\n/**\n * 代码式 provider(`PluginModule.providerFactories`)在 `ProviderRegistry`/`ModelRegistry` 中使用\n * 的 source 标签命名空间(里程碑审核发现修复):声明式轴(`PluginProviderManager`/本文件的\n * `loadPluginProviders`)用裸 `pluginId` 作 source;若同一插件同时贡献声明式 + 代码式 provider,\n * 两条独立 reload/cleanup 路径都用裸 `pluginId` 调 `unregisterBySource` 会互相清空对方注册\n * (靠调用顺序侥幸收敛,脆弱)。代码式轴统一加 `code:` 前缀隔离两个 source 命名空间。\n */\nexport function codeProviderSource(pluginId: string): string {\n return `code:${pluginId}`\n}\n\n/**\n * 插件 model entry → 通用 `Model`(缺省值补全:非推理、纯文本、零成本——声明式厂商默认保守值)。\n *\n * 回归修复(真实用户反馈:DeepSeek 400 \"supported API model names are deepseek-v4-pro or\n * deepseek-v4-flash, but you passed deepseek/deepseek-v4-pro\"):`Model.id`(otto 内部命名空间键,\n * 如 `deepseek/deepseek-v4-pro`,用于 registry/UI 展示与去重)与 `Model.name`(发给真实厂商 API\n * 的裸模型名,如 `deepseek-v4-pro`)语义上是两个不同字段——`packages/ai/src/provider/\n * chat-completions.ts` 的 `buildChatCompletionsBody()` 用 `model.name` 组装请求体的 `model` 字段\n * 直发上游。三个首方 provider 插件(`extensions/plugin-anthropic`/`plugin-deepseek`/\n * `plugin-openai-codex`)的 `otto-plugin.json` 沿用 `<vendor>/<bare-name>` 格式声明 `models[].id`\n * (对齐退役前 `packages/ai/src/data/{anthropic,deepseek,openai}-models.json` 静态数据的既有约定,\n * 那里 `id`/`name` 本就是分开两个字段),但 RFC-122 迁移时 `toModel()` 把 `name` 直接取\n * `entry.id`(未剥离 `<vendor>/` 前缀),导致真实请求把整个 `deepseek/deepseek-v4-pro` 当模型名\n * 发给 DeepSeek API 被拒。修复:`name` 剥离 `id` 中第一个 `/` 之前的部分(无 `/` 时原样使用,\n * 兼容未来可能声明无前缀 id 的插件),不改动 manifest schema(不新增字段,最小改动面)。\n */\nexport function toModel(\n entry: {\n id: string\n contextWindow: number\n maxOutput: number\n cost?: Cost\n strengths?: Model['strengths']\n reasoning?: boolean\n thinkingLevels?: Model['thinkingLevels']\n thinkingMode?: Model['thinkingMode']\n },\n provider: string,\n api: Api,\n baseUrl: string,\n /**\n * RFC-148 M5:provider 级图片约束声明(manifest `providerEntrySchema.imageConstraints`/\n * `codeProviderModelsEntrySchema.imageConstraints`),应用到该 provider 下全部模型——\n * 引擎(`packages/agent/src/image-degradation.ts`)读 `Model.imageConstraints` 决定\n * 单请求图片数量上限,不再硬编码 Anthropic 官方规则。\n */\n imageConstraints?: Model['imageConstraints'],\n /**\n * RFC-130 M131-2:provider 级工具支持声明(manifest `providerEntrySchema.supportsTools`),\n * 应用到该 provider 下全部模型——引擎(`session-config-resolver.ts`)读\n * `Model.supportsTools === false` 剥离工具集,不再硬编码厂商 api 字符串比较。\n */\n supportsTools?: boolean,\n /**\n * RFC-213:openai-responses 请求体策略声明(manifest `providerEntrySchema.responseBodyPolicy`/\n * `codeProviderModelsEntrySchema.responseBodyPolicy`),应用到该 provider 下全部模型——\n * `extensions/plugin-otto-wire-protocols/src/openai-responses.ts` 的 `buildResponsesBody` 读\n * `Model.responseBodyPolicy` 决定是否设置 `store`/发送 `max_output_tokens`,替代此前失效的\n * `model.api === 'openai-codex-responses'` 字符串判断(RFC-122 迁移后已物理不可达的死代码)。\n */\n responseBodyPolicy?: Model['responseBodyPolicy'],\n): Model {\n const slashIndex = entry.id.indexOf('/')\n const apiModelName = slashIndex >= 0 ? entry.id.slice(slashIndex + 1) : entry.id\n return {\n id: entry.id,\n name: apiModelName,\n api,\n provider,\n baseUrl,\n // RFC-235:透传 manifest 声明的推理能力(此前硬编码 reasoning:false,导致 DeepSeek 等\n // 声明式 provider 的全部推理模型 /effort 完全不生效——请求体不带 reasoning_effort/thinking)。\n // 未声明回退 false(向后兼容存量插件);thinkingLevels 未声明为 undefined(不臆造)。\n reasoning: entry.reasoning ?? false,\n input: ['text'],\n // RFC-127 补充修复 + 终局修复:此前恒定归零(不管 manifest 是否声明了真实定价),\n // 导致 RFC-122 迁移后 Anthropic/DeepSeek/OpenAI Codex 的全部模型(含静态真实型号)\n // 开销估算永远显示 $0。现在直接透传 manifest 声明的 entry.cost;未声明时\n // (如运行时从供应商 /models 接口动态发现的新型号,接口响应本就不含定价)不设置该\n // 字段(`Model.cost` 已改为可选,undefined 表示\"无已知定价来源\",不再无中生有编造\n // $0 这个具体数字——两者语义不同,UI 层应据此不显示而非显示误导性的 $0)。\n cost: entry.cost,\n contextWindow: entry.contextWindow,\n maxOutputTokens: entry.maxOutput,\n imageConstraints,\n supportsTools,\n // 终局 review 2026-07-21(厂商默认值清零):透传 manifest 声明的能力标签——\n // `ModelRegistry.register()` 经 `withModelStrengths()` 优先尊重已有非空 `strengths`\n // (见 model-capabilities.ts),未声明时才回退框架内置家族图/启发式推导。此前\n // anthropic/deepseek/openai-codex 三家的能力标签 100% 依赖框架硬编码正则表(纯声明式\n // 插件如 plugin-deepseek 无 plugin.ts 代码入口,无法调用 registerModelStrengthHint),\n // 本字段让它们能在 manifest 里自行声明,框架不再需要为已插件化的厂商内置默认规则。\n strengths: entry.strengths,\n thinkingLevels: entry.thinkingLevels,\n thinkingMode: entry.thinkingMode,\n responseBodyPolicy,\n }\n}\n\n/**\n * 按 wireApi 用既有通用 provider 实现构造工厂(不新增协议代码,RFC-105 D2)。\n * `credentialProvider` = 命名空间化 provider 名(同 `registerEnvKey`/`registerProviderDescriptor`\n * 使用的 key),保证凭据按插件 provider 隔离(R10)——不会误读同名内置 provider 的凭据。\n */\n/**\n * 把 `modelsEndpoint` 声明(url/responseFormat/authScheme/headers)转换为 `buildModelsRequest`\n * 闭包(供 `registerProviderDescriptor` 使用)。声明式轴(`contributes.providers[].\n * modelsEndpoint`)与代码式轴(`codeProviderModelsEndpoints`,provider 插件化收尾新增)\n * 共用本函数——两者字段形状一致,只是挂载位置不同(前者嵌在 providerEntry 里、`{baseUrl}`\n * 占位符指向 entry.baseUrl;后者是独立顶层字段、无 baseUrl 概念,url 必须写完整值)。\n */\nexport function buildModelsRequestFromEndpoint(\n endpoint: {\n url: string\n responseFormat: 'openai-list'\n authScheme?: 'bearer' | 'x-api-key' | 'auto'\n headers?: Record<string, string>\n /** RFC-148 终局验收 N1:`authScheme:'auto'` 下 oauth 模式追加的 beta header 名(插件声明,见 manifest.ts)。 */\n betaHeaderName?: string\n /** 从拉取结果中排除的模型 ID 模式(glob 风格,`*` 匹配任意字符序列)。 */\n modelIdExclude?: string[]\n },\n providerName: string,\n api: Api,\n baseUrl?: string,\n /**\n * 声明式轴 provider entry 顶层的静态头(如 Anthropic 的 `anthropic-version`,声明在\n * `providerEntrySchema.headers`,与 `modelsEndpoint.headers` 是两个不同字段——前者是\n * \"该 provider 全局静态头\",后者是\"modelsEndpoint 专属头\",真实 manifest 通常只填\n * 前者)。终局 review 修复:本函数抽取时遗漏了这个参数,只合并了 `endpoint.headers`,\n * 导致 `anthropic-version` 等必需头不再随 `/model refresh` 请求发出(已用真实\n * plugin-anthropic manifest 复现并修复)。代码式轴(`codeProviderModelsEndpoints`)\n * 没有对应的 provider entry 顶层 headers 概念,调用时省略即可。\n */\n entryHeaders?: Record<string, string>,\n): (apiKey: string, opts: ModelsRequestOpts) => ReturnType<NonNullable<import('@x-otto/ai').ProviderDescriptor['buildModelsRequest']>> {\n return (apiKey: string, opts: ModelsRequestOpts) => {\n const url = endpoint.url\n .replace('{baseUrl}', baseUrl ?? '')\n .replace('{clientVersion}', process.env['OTTO_CODEX_CLIENT_VERSION'] || '1.0.0')\n const authScheme = endpoint.authScheme ?? 'bearer'\n // 'auto':按运行时鉴权 mode 动态选择(Anthropic 场景,恢复迁移前已验证行为);\n // 'x-api-key':恒定用 x-api-key(不随 mode 变化);'bearer':恒定用 Authorization。\n const useApiKeyHeader = authScheme === 'x-api-key' || (authScheme === 'auto' && opts.mode !== 'oauth')\n const authHeaders: Record<string, string> = useApiKeyHeader\n ? { 'x-api-key': apiKey }\n : { authorization: `Bearer ${apiKey}` }\n // auto + oauth 模式下,opts.beta(对齐 resolveAuth 解出的 oauthBeta)存在时追加\n // endpoint.betaHeaderName 声明的头(RFC-148 终局验收 N1 修复:此前硬编码\n // 'anthropic-beta',框架层不该认识具体厂商 header 名——现由插件声明,未声明时\n // 不追加任何 beta 头,不臆造厂商默认值)。\n if (authScheme === 'auto' && opts.mode === 'oauth' && opts.beta && endpoint.betaHeaderName) {\n authHeaders[endpoint.betaHeaderName] = opts.beta\n }\n return {\n url,\n // 合并顺序:entry 顶层静态头 → modelsEndpoint 专属头 → 鉴权头(鉴权头最后合并,\n // 保证 Authorization/x-api-key 不会被静态头意外覆盖)。\n headers: { ...entryHeaders, ...endpoint.headers, ...authHeaders },\n mergeProvider: providerName,\n modelApi: api,\n modelIdExclude: endpoint.modelIdExclude,\n }\n }\n}\n\n/**\n * 按 wireApi 构造 provider 工厂(RFC-148 D3:从 switch 三分支改为惰性查\n * `WireProtocolRegistry`)。查表发生在**首次建流时**(工厂闭包内部),不在装载时——\n * 声明式 provider 插件与协议插件的装载顺序因此解耦:协议插件晚装载也不影响声明式插件\n * 装载成功,只在真正建流时若协议未注册才报错(`ProviderError`,文案含修复指引)。\n *\n * `resolveAuth` 统一走 `AuthStore.resolveAuth`(含 mode/beta 完整信息),不再区分\n * `resolveKey`(裸 key)与 `resolveAuth`(完整 `ResolvedAuth`)两种历史签名——协议实现\n * (现由 `extensions/plugin-otto-wire-protocols` 插件提供)内部各自决定是否只取 `.token`。\n *\n * RFC-148 M3:Claude Code 伪装的 `preserveProviderId → customizeRequest` 特判分支已随\n * `plugin-anthropic` 代码式化删除——`plugin-anthropic` 已从声明式 `contributes.providers`\n * 单轴化为纯代码式 `providerFactories`(其自己的 customizeRequest 定制器,见\n * `extensions/plugin-anthropic/plugin.ts`),不再经过本文件的声明式装载路径。本函数\n * 现在对任何 `wireApi` 都是零厂商知识的纯查表转发——框架层彻底不认识 \"Claude Code\"\n * 这个具体厂商概念(终局判据)。\n */\nfunction buildFactory(\n entry: PluginProviderEntry,\n authStore: AuthStore,\n credentialProvider: string,\n wireProtocolRegistry: WireProtocolRegistry,\n /**\n * RFC-130 D7:`entry.baseUrl`/`entry.wireApi` 现为可选字段(`catalogOnly` 条目不声明)——\n * 调用方在非-catalogOnly 分支已做过 `if (!baseUrl || !wireApi) continue` 校验并收窄为\n * 非空局部变量,这里显式接收收窄后的值(而非重新读取 `entry.wireApi`),让 TypeScript\n * 能追踪到该保证,避免可选类型污染 `WireProtocolRegistry.get()`/`resolveBaseUrl` 的调用点。\n */\n baseUrl: string,\n wireApi: string,\n): () => ProviderStream {\n return () => {\n const protocol = wireProtocolRegistry.get(wireApi)\n if (!protocol) {\n throw new ProviderError(\n `wire protocol '${wireApi}' is not registered — is its provider plugin installed and trusted? (RFC-148)`,\n { code: 'WIRE_PROTOCOL_NOT_REGISTERED' },\n )\n }\n return protocol.create({\n resolveAuth: () => authStore.resolveAuth(credentialProvider).then((a) => a ?? undefined),\n resolveBaseUrl: async () => baseUrl,\n headers: entry.headers,\n tokenDerivedHeaders: entry.tokenDerivedHeaders as TokenDerivedHeader[] | undefined,\n // RFC-270 D3:鉴权头覆盖逃生口(缺省 undefined = 管线默认丢弃自定义鉴权头 + warn)。\n allowAuthHeaderOverride: entry.allowAuthHeaderOverride,\n // RFC-185 D3:宿主 logger 注入——协议实现的日志走宿主单例(级别/stderr 通道\n // 统一受控),不再允许插件 bundle 内自建 createLogger 第二单例。\n logger: buildWireProtocolLogger(wireApi),\n })\n }\n}\n\nexport interface LoadPluginProviderDeps {\n providerRegistry: ProviderRegistry\n authStore: AuthStore\n modelRegistry: ModelRegistry\n /** RFC-148 D3:buildFactory 惰性查表目标。 */\n wireProtocolRegistry: WireProtocolRegistry\n}\n\nexport interface LoadPluginProvidersResult {\n registered: string[]\n rejected: Array<{ id: string; reason: string }>\n}\n\n/**\n * OAuth 轴装载(RFC-122 D2,RFC-199 D4 自 loadPluginProviders 提取,行为零变化)。\n * 先于 provider 轴执行,供 provider 条目的 `oauthRef` 引用。结果写入共享 `result`。\n */\nfunction loadOAuthAxis(\n pluginId: string,\n manifest: PluginManifest,\n oauthRegistry: LoadPluginProviderDeps['authStore']['oauth'],\n preservedOAuthRefs: ReadonlySet<string>,\n oauthRefDisplayNames: ReadonlyMap<string, string>,\n result: LoadPluginProvidersResult,\n atomicReplaceOAuthIds?: ReadonlySet<string>,\n): void {\n const oauthEntries = manifest.contributes?.oauth\n if (oauthEntries?.length && oauthRegistry) {\n if (!manifest.capabilities?.includes('provider')) {\n logger.warn(\n { pluginId },\n 'plugin declares contributes.oauth but not capabilities:[\"provider\"], skipped (fail-closed)',\n )\n for (const e of oauthEntries)\n result.rejected.push({ id: e.id, reason: 'capability \"provider\" not declared' })\n } else {\n for (const oauthEntry of oauthEntries) {\n const namespacedId = preservedOAuthRefs.has(oauthEntry.id)\n ? oauthEntry.id\n : pluginProviderApi(pluginId, oauthEntry.id)\n\n if (\n OAUTH_PROVIDER_IDS.includes(oauthEntry.id) ||\n oauthRegistry.isReservedId(oauthEntry.id)\n ) {\n result.rejected.push({\n id: oauthEntry.id,\n reason: 'oauth id collides with builtin reserved OAuth provider',\n })\n logger.warn(\n { pluginId, oauthId: oauthEntry.id },\n 'plugin oauth id collides with builtin reserved, rejected (R3)',\n )\n continue\n }\n\n const displayName =\n oauthEntry.name ?? oauthRefDisplayNames.get(oauthEntry.id) ?? oauthEntry.id\n const provider =\n oauthEntry.kind === 'pkce'\n ? PKCEOAuthTemplate({\n ...oauthEntry,\n id: namespacedId,\n name: displayName,\n tokenExchangeEncoding: oauthEntry.tokenExchangeEncoding ?? 'form',\n assumesLoopbackAlways: oauthEntry.assumesLoopbackAlways ?? false,\n subscriptionScoped: oauthEntry.subscriptionScoped ?? false,\n })\n : DeviceFlowOAuthTemplate({\n ...oauthEntry,\n id: namespacedId,\n name: displayName,\n allowCustomDomain: oauthEntry.allowCustomDomain ?? false,\n })\n\n if (atomicReplaceOAuthIds?.has(namespacedId)) {\n // RFC-140 D7/T18:changed 插件的存续 OAuth id——原子覆盖,不经过跨 source 拒绝\n // 检查(调用方已确认这是同一插件的 reload,unload 阶段也跳过了对应的 unregister)。\n oauthRegistry.replace(provider, pluginId)\n } else {\n // 终局 review 修复:传 pluginId 作为 source——防止两个不同插件都用\n // preserveProviderId:true 声明相同裸 oauthRef id 时后者静默覆盖前者的 OAuth\n // provider(覆盖攻击,已用真实二次注册脚本复现)。\n const accepted = oauthRegistry.register(provider, pluginId)\n if (!accepted) {\n result.rejected.push({ id: oauthEntry.id, reason: 'oauth registration rejected (id already claimed by another plugin or reserved)' })\n continue\n }\n }\n }\n }\n }\n\n}\n\n/**\n * provider 轴装载(RFC-105 D2 + RFC-130 catalogOnly + RFC-140 T16 OAuth 原子回滚,\n * RFC-199 D4 自 loadPluginProviders 提取,行为零变化)。结果写入共享 `result`。\n */\nfunction loadProviderAxis(\n pluginId: string,\n manifest: PluginManifest,\n deps: LoadPluginProviderDeps,\n oauthRegistry: LoadPluginProviderDeps['authStore']['oauth'],\n result: LoadPluginProvidersResult,\n atomicReplaceApis?: ReadonlySet<string>,\n): void {\n const providerEntries = manifest.contributes?.providers\n if (!providerEntries?.length) return\n\n if (!manifest.capabilities?.includes('provider')) {\n logger.warn(\n { pluginId },\n 'plugin declares contributes.providers but not capabilities:[\"provider\"], skipped (fail-closed)',\n )\n for (const e of providerEntries)\n result.rejected.push({ id: e.id, reason: 'capability \"provider\" not declared' })\n return\n }\n\n /**\n * M2 事务化回滚:跟踪本插件内每个 oauthRef(解析为 credentialProvider id)被多少\n * provider entry 成功引用。失败回滚时只在\"无成功引用\"时才 unregister OAuth——\n * 避免共享同一 oauthRef 的 provider A 成功、provider B 失败时,B 的回滚误删 A 正在\n * 使用的 OAuth 注册。\n */\n const successfulOAuthRefs = new Set<string>()\n\n for (const entry of providerEntries) {\n // RFC-122 review F5:重复 id 检测——第二个同 id 条目会导致第一个条目的注册被回滚\n //(registerProviderDescriptor 撞名 → 清理代码删第一个条目的 descriptor),提前拒绝防 corruption。\n if (result.registered.includes(entry.id)) {\n result.rejected.push({ id: entry.id, reason: 'duplicate provider id in manifest' })\n logger.warn(\n { pluginId, providerId: entry.id },\n 'duplicate provider id in manifest, second entry rejected (review F5)',\n )\n continue\n }\n\n if (isBuiltinProviderId(entry.id)) {\n result.rejected.push({ id: entry.id, reason: 'reserved builtin provider id' })\n logger.warn(\n { pluginId, providerId: entry.id },\n 'plugin provider id collides with builtin, rejected (R3)',\n )\n continue\n }\n\n const noNamespace = entry.preserveProviderId === true\n const api = (noNamespace ? entry.id : pluginProviderApi(pluginId, entry.id)) as Api\n const providerName = noNamespace ? entry.id : pluginProviderApi(pluginId, entry.id)\n\n // ── RFC-130 D7:catalog-only 轴——descriptor/envKey/models,跳过 wireApi 工厂 ──\n if (entry.catalogOnly === true) {\n let credentialProvider = providerName\n if (entry.oauthRef) {\n const oauthId = noNamespace ? entry.oauthRef : pluginProviderApi(pluginId, entry.oauthRef)\n const resolved = oauthRegistry?.get(oauthId)\n if (!resolved) {\n result.rejected.push({\n id: entry.id,\n reason: `oauthRef '${entry.oauthRef}' not found in current plugin's oauth table (R7)`,\n })\n logger.warn(\n { pluginId, providerId: entry.id, oauthRef: entry.oauthRef },\n 'catalog provider oauthRef not found, rejected (R7)',\n )\n continue\n }\n credentialProvider = oauthId\n }\n\n // RFC-130 D2:catalog + oauthRef 时仍注册 envKey 作 CURSOR_ACCESS_TOKEN 兜底(A 未登录 → C)。\n if (entry.envKey) {\n const ok = deps.authStore.registerEnvKey(providerName, entry.envKey, pluginId)\n if (!ok) {\n result.rejected.push({\n id: entry.id,\n reason: 'envKey registration rejected (reserved or claimed)',\n })\n continue\n }\n }\n\n const descriptorAccepted = registerProviderDescriptor(\n { id: providerName, name: entry.name, env: entry.oauthRef ? undefined : entry.envKey },\n pluginId,\n )\n if (!descriptorAccepted) {\n result.rejected.push({ id: entry.id, reason: 'provider descriptor id already claimed' })\n if (entry.envKey) deps.authStore.unregisterEnvKey(providerName)\n continue\n }\n\n if (entry.models?.length) {\n const models = entry.models.map((m) =>\n toModel(\n m,\n credentialProvider,\n api,\n entry.baseUrl ?? '',\n undefined,\n entry.supportsTools,\n entry.responseBodyPolicy,\n ),\n )\n const rejectedModelIds = deps.modelRegistry.registerWithSource(models, pluginId)\n for (const id of rejectedModelIds) {\n logger.warn({ pluginId, modelId: id }, 'plugin model id collides, rejected')\n }\n }\n\n result.registered.push(entry.id)\n continue\n }\n\n // RFC-122 review F5:重复 id 检测——第二个同 id 条目会导致第一个条目的注册被回滚\n let credentialProvider = providerName\n if (entry.oauthRef) {\n // 回归修复(同上 preservedOAuthRefs 注释):`preserveProviderId: true` 时上方 OAuth 轴\n // 用裸 id 注册了该 oauthRef 对应的 OAuth provider,这里查找必须用同一个 id,否则\n // `oauthRegistry.get()` 找不到(查了命名空间 id,注册的却是裸 id)导致 R7 误判为\n // \"oauthRef 不存在于当前插件表\"而被拒绝装载。\n const oauthId = noNamespace ? entry.oauthRef : pluginProviderApi(pluginId, entry.oauthRef)\n // R7:仅限当前插件的 oauth 表内解析,绝不允许跨插件/回退内置\n const resolved = oauthRegistry?.get(oauthId)\n if (!resolved) {\n result.rejected.push({\n id: entry.id,\n reason: `oauthRef '${entry.oauthRef}' not found in current plugin's oauth table (R7: cross-plugin/invalid ref rejected)`,\n })\n logger.warn(\n { pluginId, providerId: entry.id, oauthRef: entry.oauthRef },\n 'provider oauthRef not found in current plugin oauth table, rejected (R7)',\n )\n continue\n }\n credentialProvider = oauthId\n // R2:oauthRef 与 envKey 互斥,oauth 优先\n if (entry.envKey) {\n logger.warn(\n { pluginId, providerId: entry.id },\n 'provider declares both oauthRef and envKey; ignoring envKey (R2, oauth takes priority)',\n )\n }\n }\n\n if (entry.envKey && !entry.oauthRef) {\n // RFC-124 M2:source=pluginId 传入,拒绝跨插件撞名覆盖 envKey 映射。\n const ok = deps.authStore.registerEnvKey(providerName, entry.envKey, pluginId)\n if (!ok) {\n result.rejected.push({\n id: entry.id,\n reason: 'envKey registration rejected (reserved or claimed by another plugin)',\n })\n continue\n }\n }\n\n const baseUrl = entry.baseUrl\n const wireApi = entry.wireApi\n if (!baseUrl || !wireApi) {\n result.rejected.push({\n id: entry.id,\n reason: 'baseUrl and wireApi required for non-catalog providers',\n })\n continue\n }\n\n // RFC-124 M3:source=pluginId 传入,拒绝跨插件撞名覆盖(entry.id 无 kebab-case 约束,\n // 恶意插件可构造与其他插件已注册 descriptor 撞名的 id)。\n // RFC-122 D4:若 entry 声明 modelsEndpoint,生成 buildModelsRequest 闭包(实时模型拉取)。\n // RFC-127 D1:恢复 RFC-122 物理迁移前内置 Anthropic/OpenAI Codex descriptor 已验证过的\n // 鉴权头行为——entry.headers(如 Anthropic 的 anthropic-version)随鉴权头一并合并,\n // 不再只发裸鉴权头(此前遗漏会导致缺 anthropic-version 被服务端拒绝)。\n const buildModelsRequest = entry.modelsEndpoint\n ? buildModelsRequestFromEndpoint(entry.modelsEndpoint, providerName, api, entry.baseUrl, entry.headers)\n : undefined\n\n // RFC-140 D7/T16(review P1-4):OAuth 轴回滚原子化。此前 provider entry 引用了\n // `oauthRef` 时,OAuth provider 已在上方 OAuth 轴装载阶段注册成功——但若本 entry 自己的\n // descriptor/api 注册随后失败(撞名等),此处只回滚了 envKey/descriptor,从未回滚该\n // entry 拉起的 OAuth 注册,导致\"僵尸 /login 条目\":`oauthRegistry` 里存在一个可点击登录\n // 的 provider,背后却没有一个真正可用的 provider 实例(登录成功后 `/model` 找不到对应\n // provider,用户体验为\"登录了但没反应\")。`rollbackOAuthIfOwned` 只在 credentialProvider\n // 确实来自本 entry 的 oauthRef(而非 envKey 路径)时才回滚,且只 unregister 本插件本次\n // 装载才注册的那一条,不影响其他插件/其他 entry 的 OAuth 注册。\n //\n // M2 事务化:若同一插件内其他 provider entry 已成功引用同一 oauthRef(credentialProvider),\n // 则不回滚——该 OAuth 注册仍有活跃消费者。只在本 entry 是唯一引用者时才 unregister。\n const rollbackOAuthIfOwned = (): void => {\n if (entry.oauthRef && oauthRegistry && !successfulOAuthRefs.has(credentialProvider)) {\n oauthRegistry.unregister(credentialProvider)\n }\n }\n\n const descriptorAccepted = registerProviderDescriptor(\n {\n id: providerName,\n name: entry.name,\n env: entry.oauthRef ? undefined : entry.envKey,\n buildModelsRequest,\n },\n pluginId,\n )\n if (!descriptorAccepted) {\n result.rejected.push({\n id: entry.id,\n reason: 'provider descriptor id already claimed by another plugin',\n })\n if (entry.envKey && !entry.oauthRef) deps.authStore.unregisterEnvKey(providerName)\n rollbackOAuthIfOwned()\n continue\n }\n\n // RFC-270 D3:鉴权头覆盖是显式声明的高权限行为——装载期留痕一条,使其可审计\n // (运行期管线还会在真实覆盖发生时各记一条)。\n if (entry.allowAuthHeaderOverride) {\n logger.warn(\n { pluginId, providerId: entry.id },\n 'provider declares allowAuthHeaderOverride: custom headers may replace auth headers (RFC-270 D3)',\n )\n }\n\n const factory = buildFactory(entry, deps.authStore, credentialProvider, deps.wireProtocolRegistry, baseUrl, wireApi)\n // 终局 review 2026-07-21 A-1:changed 插件 reload 且该 api 在新旧 manifest 中都存在时,\n // 走 replacePluginProvider 原子覆盖(unload 侧经 keepApis 跳过了 unregister,工厂全程\n // 在位,同一 api 上从不出现 PROVIDER_NOT_FOUND 中间态)——与 OAuth 轴的\n // OAuthRegistry.replace()(RFC-140 T18)语义对等。\n const accepted = atomicReplaceApis?.has(api)\n ? deps.providerRegistry.replacePluginProvider(api, factory, pluginId)\n : deps.providerRegistry.registerPluginProvider(api, factory, pluginId)\n if (!accepted) {\n result.rejected.push({\n id: entry.id,\n reason: 'api already registered by another plugin or reserved',\n })\n unregisterProviderDescriptor(providerName)\n if (entry.envKey && !entry.oauthRef) deps.authStore.unregisterEnvKey(providerName)\n rollbackOAuthIfOwned()\n continue\n }\n\n if (entry.models?.length) {\n const models = entry.models.map((m) =>\n toModel(\n m,\n credentialProvider,\n api,\n baseUrl,\n entry.imageConstraints,\n entry.supportsTools,\n entry.responseBodyPolicy,\n ),\n )\n // RFC-124 H2:registerWithSource 现返回撞名被拒的 model id 列表——插件声明的 model id\n // 与内置/其他插件已注册的 model 撞名时不覆盖,记录告警。\n const rejectedModelIds = deps.modelRegistry.registerWithSource(models, pluginId)\n for (const id of rejectedModelIds) {\n logger.warn(\n { pluginId, modelId: id },\n 'plugin model id collides with an existing model, rejected (no silent override)',\n )\n }\n }\n\n result.registered.push(entry.id)\n\n // M2 事务化:记录本 entry 成功引用的 oauthRef(credentialProvider)——\n // 后续同插件其他 entry 失败回滚时,不会误删仍有活跃消费者的 OAuth 注册。\n if (entry.oauthRef) {\n successfulOAuthRefs.add(credentialProvider)\n }\n }\n\n}\n\n/**\n * 装载某插件 manifest 声明的全部 provider。要求 `capabilities` 含 `provider`(R4 fail-closed),\n * 否则整批跳过(不部分生效,避免「声明了能力的一半插件」的困惑状态)。\n *\n * RFC-122 D2:先装载 `contributes.oauth` 轴(OAuth provider 注册),再装载 `contributes.providers`\n * 轴——provider 条目可通过 `oauthRef` 引用同插件已注册的 OAuth id,凭据走 OAuth 登录流。\n */\nexport function loadPluginProviders(\n pluginId: string,\n manifest: PluginManifest,\n deps: LoadPluginProviderDeps,\n /**\n * RFC-140 D7/T18:与 `unloadPluginProviders` 的同名参数配对——`changed` 插件 reload 时,\n * 调用方(`PluginProviderManager`)算出\"新旧 manifest 都存在的 OAuth id\"集合,两侧\n * 都传入。`unloadPluginProviders` 跳过这些 id 的 `unregister()`;本函数对这些 id 改用\n * `OAuthRegistry.replace()`(而非 `register()`)原子覆盖——两处配合消除先删后建的\n * await 间隙窗口。省略时行为与此前一致。\n */\n atomicReplaceOAuthIds?: ReadonlySet<string>,\n /**\n * 终局 review 2026-07-21 A-1:与 `atomicReplaceOAuthIds` 的 provider 工厂轴对等物——\n * `changed` 插件 reload 时新旧 manifest 都存在的 wire 工厂 api 集合(由\n * `namespacedProviderApis` 交集算出)。`unloadPluginProviders` 经 `keepApis` 跳过这些\n * api 的卸载;本函数对这些 api 改用 `replacePluginProvider` 原子覆盖。\n */\n atomicReplaceApis?: ReadonlySet<string>,\n): LoadPluginProvidersResult {\n const result: LoadPluginProvidersResult = { registered: [], rejected: [] }\n const oauthRegistry = deps.authStore.oauth\n\n // 回归修复(真实用户反馈:`/login` 列表同时出现 \"Anthropic [anthropic]\" 与\n // \"anthropic [plugin-anthropic:anthropic]\" 两条):`preserveProviderId: true` 此前只对\n // provider 轴自身生效(`providerName`/`api`/`envKey` 跳过命名空间前缀),但引用它的 OAuth\n // 轴(`contributes.oauth`)从未感知这个标志——`namespacedId` 恒为 `<pluginId>:<oauthId>`,\n // 导致 OAuth provider 用命名空间 id 注册(如 `plugin-anthropic:anthropic`),而\n // provider descriptor/展示用的却是 preserveProviderId 生效后的裸 id(如 `anthropic`)——\n // 两者不一致,`/login` 里同一个厂商拆成两条互不相干的凭据槽(一条是历史遗留的 bare 凭据缓存,\n // 一条是真正生效但从未真正登录过的命名空间 OAuth)。\n // 修复:预扫描 `contributes.providers`,收集\"哪些 oauth 短名被声明了 preserveProviderId:true\n // 的 provider 引用\",OAuth 轴据此对这些条目使用裸 id 注册,保持与 provider 轴的命名空间语义\n // 一致(首方插件 = 单一裸 id 贯穿 OAuth 注册表 + provider descriptor + envKey + 凭据槽)。\n // 计算逻辑见 `buildPreservedOAuthRefs`(终局 review O-3 修复,与 `unloadPluginProviders`\n // 共用同一函数,消除此前两处各写一遍的重复)。\n const preservedOAuthRefs = buildPreservedOAuthRefs(manifest)\n // 回归修复(真实用户反馈 \"/login 需要根据插件来显示,不是固定的\"):`oauthEntry.name` 在\n // 三个首方插件 manifest 里均未声明——友好展示名实际写在引用它的 provider entry 的 `name`\n // 字段上(如 \"Anthropic (Claude Code)\")。此前 `displayName = oauthEntry.name ?? oauthEntry.id`\n // 落到裸 id(如 \"anthropic\"),OAuth provider 注册的 `.name` 字段因此也是裸 id,CLI 侧\n // `authStore.oauth.get(p)?.name` 读到的只是裸 id,退化到 `PROVIDER_DISPLAY_NAMES` 硬编码表\n // 兜底——不查表就没有友好名。修复:预建 oauthRef 短名 → 引用它的 provider.name 映射,\n // OAuth 轴装载时优先用这个名字。\n const oauthRefDisplayNames = new Map(\n (manifest.contributes?.providers ?? [])\n .filter((p) => p.oauthRef && p.name)\n .map((p) => [p.oauthRef!, p.name!] as const),\n )\n\n loadOAuthAxis(\n pluginId,\n manifest,\n oauthRegistry,\n preservedOAuthRefs,\n oauthRefDisplayNames,\n result,\n atomicReplaceOAuthIds,\n )\n\n loadProviderAxis(pluginId, manifest, deps, oauthRegistry, result, atomicReplaceApis)\n\n return result\n}\n\n/**\n * 卸载某插件注册的全部声明式 provider + OAuth + descriptor + envKey + models(RFC-105 R2 /\n * RFC-122 R8 五件套)。\n *\n * 回归修复:`providerName`/OAuth `namespacedId` 此前恒使用命名空间前缀,未respect\n * `preserveProviderId`——首方插件(`preserveProviderId: true`)注册时用裸 id,卸载时却按\n * 命名空间 id 去删,实际删不掉真正注册的那条(残留僵尸 descriptor/envKey/OAuth 注册 +\n * 凭据),是与 `loadPluginProviders` 对称的必要修复(同一 id 推导逻辑必须一致,否则\n * load/unload 不是同一把钥匙)。\n *\n * 终局 review 修复(2026-07-09):`preservedOAuthRefs` 此前只并入\n * `contributes.providers[].preserveProviderId`,遗漏了 `manifest.codeProviderIds`\n * (代码式 provider 轴的裸 id 逃生口,`loadPluginProviders` 的装载路径两者都并入,见\n * `buildPreservedOAuthRefs`)。GitHub Copilot 类插件(走 `codeProviderIds` 而非声明式\n * `preserveProviderId`)装载时 OAuth 用裸 id 注册,卸载时因此处遗漏该来源,\n * `preservedOAuthRefs.has(oauthEntry.id)` 判假 → 回退到命名空间 id 去卸载 → 卸载\n * 的是从未注册过的 id,真正注册的裸 id OAuth 条目永久残留(僵尸注册,同一插件\n * reload 会因 R7/source 追踪判定\"跨 source 撞名\"被拒绝重新装载)。\n *\n * 终局 review 2026-07-20 O-3 修复:`preservedOAuthRefs` 的计算逻辑此前与\n * `loadPluginProviders` 各写一遍完全相同的代码,现共用 `buildPreservedOAuthRefs`。\n */\nexport async function unloadPluginProviders(\n pluginId: string,\n manifest: PluginManifest,\n deps: LoadPluginProviderDeps,\n /**\n * RFC-140 D7/T18(review P1-8):`changed` 插件 reload(先卸旧再装新)场景下,若某个\n * OAuth id 在新旧 manifest 中都存在(同一插件、同一 oauthRef,只是其他字段变化),\n * 此参数传入该 id 集合——本函数跳过对应条目的 `unregister()`,交由随后的\n * `loadPluginProviders` 用 `OAuthRegistry.replace()` 原子覆盖,消除\"先删后建\"之间的\n * await 间隙窗口(同一 id 上从不出现 `undefined`)。调用方(`PluginProviderManager`)\n * 负责算出这个集合;省略时行为与此前一致(真卸载/进程退出场景,无\"即将重装\"的概念)。\n */\n skipOAuthIds?: ReadonlySet<string>,\n /**\n * 终局 review 2026-07-21 A-1:与 `skipOAuthIds` 的 provider 工厂轴对等物——changed\n * 插件 reload 时新旧 manifest 都存在的 wire 工厂 api 集合。本函数对这些 api 跳过\n * `unregisterBySource`(工厂全程在位),交由随后的 `loadPluginProviders` 用\n * `replacePluginProvider` 原子覆盖。descriptor/envKey/model 仍照常卸载重建(它们的\n * 消费方都是\"查表返回 undefined 即跳过\"的容错读取,无 PROVIDER_NOT_FOUND 类硬失败)。\n */\n keepApis?: ReadonlySet<string>,\n): Promise<void> {\n const oauthRegistry = deps.authStore.oauth\n const preservedOAuthRefs = buildPreservedOAuthRefs(manifest)\n\n // ── 卸载 OAuth provider(RFC-139 D1:只清进程态注册表,不碰用户凭据)──\n //\n // 历史事故(RFC-139 §0,2026-07-10 根因分析):此处曾按 RFC-122 R8/F7 调\n // `authStore.remove(namespacedId)` + `save()` 把 auth.json 里的 OAuth 凭据物理删除。\n // 但本函数同时服务三种调用语义——①真卸载(<1%)②进程退出 cleanup(~90%)\n // ③reloadCapabilities 的 unload-then-load(~10%)——②③每次都在删用户登录态,\n // 再经其他并发 otto 进程的 fs.watch 传播 cache.delete,造成\"Anthropic 登录态\n // 快速失效\"的全会话 AUTH_MISSING 爆发(trace 实证 65/65 条)。\n //\n // R8 的威胁模型不成立:OAuth 凭据在 provider 未注册时是惰性数据——\n // `resolveOAuthAccessKey` 查 `oauth.get(provider)` 为 undefined 直接返回 null,\n // 无任何路径可消费。凭据是用户级持久态,删除权只属于用户显式 logout\n // (`AuthStore.logout`,唯一删除路径)。插件卸载后凭据保留,重装即恢复登录\n // (对照系:VS Code / Claude Code 卸载扩展均不删用户 token)。\n const oauthEntries = manifest.contributes?.oauth\n if (oauthEntries?.length && oauthRegistry) {\n for (const oauthEntry of oauthEntries) {\n const namespacedId = preservedOAuthRefs.has(oauthEntry.id)\n ? oauthEntry.id\n : pluginProviderApi(pluginId, oauthEntry.id)\n if (skipOAuthIds?.has(namespacedId)) continue\n oauthRegistry.unregister(namespacedId)\n }\n }\n\n const entries = manifest.contributes?.providers\n if (!entries?.length) return\n for (const entry of entries) {\n const providerName =\n entry.preserveProviderId === true ? entry.id : pluginProviderApi(pluginId, entry.id)\n unregisterProviderDescriptor(providerName)\n if (entry.envKey && !entry.oauthRef) deps.authStore.unregisterEnvKey(providerName)\n }\n await deps.providerRegistry.unregisterBySource(pluginId, keepApis)\n deps.modelRegistry.unregisterBySource(pluginId)\n}\n","import type { AuthStore } from '@x-otto/ai'\nimport type { Logger } from '@x-otto/shared'\nimport { pluginProviderApi } from '../../plugin/provider-loader'\nimport { toOAuthProvider } from '../../plugin/module-wiring'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * 代码式 OAuth 工厂轴(RFC-122 D1,RFC-130 M131-1):须在 providerFactories 轴之前注册\n * (供 catalog manifest 的 oauthRef 解析,capability-reloader 顺序保证——本 handler 装配\n * 顺序需排在 CodeProviderAxisHandler 之前,见 plugin-module-manager.ts 装配处)。\n */\nexport class OAuthProviderAxisHandler implements PluginAxisHandler {\n readonly axisName = 'oauthProviderFactories'\n /** RFC-216 D1:OAuth 注册 id 按 pluginId 分桶,支持 per-plugin 差量撤销。 */\n private oauthIdsByPlugin = new Map<string, Set<string>>()\n\n constructor(\n private readonly authStore: AuthStore | undefined,\n private readonly logger: Logger,\n ) {}\n\n register(ctx: PluginAxisContext): void {\n const oauthProviderFactories = ctx.loaded.module.oauthProviderFactories\n if (!oauthProviderFactories || !this.authStore) return\n if (!ctx.plugin.manifest.capabilities?.includes('provider')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin declares oauthProviderFactories but not capabilities:[\"provider\"], skipped (fail-closed)',\n )\n return\n }\n if (ctx.verdict.pendingCapabilities.includes('provider')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin capability \"provider\" not yet granted, skipping oauthProviderFactories (fail-closed)',\n )\n return\n }\n const preservedOAuthRefs = new Set(\n (ctx.plugin.manifest.contributes?.providers ?? [])\n .filter((p) => p.preserveProviderId === true && p.oauthRef)\n .map((p) => p.oauthRef!),\n )\n for (const [name, factory] of Object.entries(oauthProviderFactories)) {\n const provider = toOAuthProvider(ctx.plugin.id, factory)\n if (!provider) continue\n const expectedId = preservedOAuthRefs.has(name) ? name : pluginProviderApi(ctx.plugin.id, name)\n if (provider.id !== expectedId) {\n this.logger.warn(\n { pluginId: ctx.plugin.id, name, expectedId, actualId: provider.id },\n 'plugin oauth provider id mismatch with manifest oauthRef/preserveProviderId, skipped',\n )\n continue\n }\n const accepted = this.authStore.oauth.register(provider, ctx.plugin.id)\n if (accepted) {\n const bucket = this.oauthIdsByPlugin.get(ctx.plugin.id) ?? new Set<string>()\n bucket.add(provider.id)\n this.oauthIdsByPlugin.set(ctx.plugin.id, bucket)\n } else {\n this.logger.warn(\n { pluginId: ctx.plugin.id, oauthId: provider.id },\n 'plugin oauthProviderFactory rejected (reserved or claimed)',\n )\n }\n }\n }\n\n /**\n * RFC-122 D1 / RFC-130 M131-1:只撤销运行期 OAuth provider 注册,不删除用户持久化\n * 凭据(同 lesson_65/RFC-122 R8 撤销的同一反模式)——reload()/cleanup() 都是进程级\n * 生命周期钩子,不应清用户已登录凭据。\n */\n /** RFC-216 D1:per-plugin 差量撤销。仍只清运行期注册,不碰用户凭据(RFC-139 D1)。 */\n unregister(pluginId: string): void {\n if (!this.authStore) return\n const ids = this.oauthIdsByPlugin.get(pluginId)\n if (!ids) return\n for (const oauthId of ids) this.authStore.oauth.unregister(oauthId)\n this.oauthIdsByPlugin.delete(pluginId)\n }\n\n unregisterAll(): void {\n for (const pluginId of [...this.oauthIdsByPlugin.keys()]) this.unregister(pluginId)\n }\n}\n","import type { Api, ModelRegistry, ProviderRegistry } from '@x-otto/ai'\nimport { registerProviderDescriptor, unregisterProviderDescriptor } from '@x-otto/ai'\nimport type { Logger } from '@x-otto/shared'\nimport {\n buildModelsRequestFromEndpoint,\n codeProviderSource,\n resolveCodeProviderApi,\n toModel,\n} from '../../plugin/provider-loader'\nimport { toProviderFactory } from '../../plugin/module-wiring'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * 代码式 provider 工厂轴(RFC-105 D3/M105-2-04):需 manifest 声明 capabilities:[\"provider\"]\n * (fail-closed R4);api 命名空间默认强制前缀防碰撞(D3)。RFC-124 C2:还需该高危能力已被\n * 显式授予(非 pendingCapabilities)。\n *\n * 含两个子注册(provider 插件化收尾,2026-07-09):`codeProviderModelsEndpoints`\n * (实时拉取端点 descriptor)+ `codeProviderModels`(静态模型种子)——两者与主 provider\n * 工厂注册共用同一插件遍历,但落地到三张独立数据表,各自独立追踪供幂等 reload/cleanup。\n */\nexport class CodeProviderAxisHandler implements PluginAxisHandler {\n readonly axisName = 'providerFactories'\n private providerSourcePluginIds = new Set<string>()\n private modelSourcePluginIds = new Set<string>()\n /** RFC-216 D1:descriptor api 按 pluginId 分桶,支持 per-plugin 差量撤销。 */\n private descriptorIdsByPlugin = new Map<string, Set<string>>()\n\n constructor(\n private readonly providerRegistry: ProviderRegistry | undefined,\n private readonly modelRegistry: ModelRegistry | undefined,\n private readonly logger: Logger,\n ) {}\n\n register(ctx: PluginAxisContext): void {\n const providerFactories = ctx.loaded.module.providerFactories\n if (!providerFactories || !this.providerRegistry) return\n if (!ctx.plugin.manifest.capabilities?.includes('provider')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin declares providerFactories but not capabilities:[\"provider\"], skipped (fail-closed)',\n )\n return\n }\n if (ctx.verdict.pendingCapabilities.includes('provider')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin capability \"provider\" declared but not yet granted, skipping providerFactories (fail-closed, RFC-124 C2)',\n )\n return\n }\n const modelsEndpoints = ctx.plugin.manifest.codeProviderModelsEndpoints ?? {}\n const codeModelsField = ctx.plugin.manifest.codeProviderModels ?? {}\n for (const [name, factory] of Object.entries(providerFactories)) {\n const api = resolveCodeProviderApi(ctx.plugin.id, name, ctx.plugin.manifest)\n const accepted = this.providerRegistry.registerPluginProvider(\n api,\n toProviderFactory(ctx.plugin.id, factory),\n codeProviderSource(ctx.plugin.id),\n )\n if (!accepted) {\n this.logger.warn(\n { pluginId: ctx.plugin.id, api },\n 'plugin providerFactory rejected (reserved builtin or claimed by another plugin)',\n )\n continue\n }\n this.providerSourcePluginIds.add(ctx.plugin.id)\n // provider 插件化收尾:代码式 provider 的 `/model refresh` 实时拉取能力\n // (`codeProviderModelsEndpoints`,纯数据元数据,不含协议工厂——工厂已由\n // `providerFactories` 提供,二者职责正交、不会对同一 api 产生双重注册)。\n const endpoint = modelsEndpoints[name]\n if (endpoint) {\n const descriptorAccepted = registerProviderDescriptor(\n { id: api, buildModelsRequest: buildModelsRequestFromEndpoint(endpoint, api, api as Api) },\n codeProviderSource(ctx.plugin.id),\n )\n if (descriptorAccepted) {\n const bucket = this.descriptorIdsByPlugin.get(ctx.plugin.id) ?? new Set<string>()\n bucket.add(api)\n this.descriptorIdsByPlugin.set(ctx.plugin.id, bucket)\n } else {\n this.logger.warn(\n { pluginId: ctx.plugin.id, api },\n 'plugin codeProviderModelsEndpoints descriptor rejected (reserved builtin or claimed by another plugin)',\n )\n }\n }\n // RFC-148 M3:代码式 provider 静态模型(codeProviderModels,纯数据)——与\n // codeProviderModelsEndpoints 职责正交(后者是实时拉取端点,本段是静态种子)。\n const codeModelsEntry = codeModelsField[name]\n if (codeModelsEntry?.models?.length && this.modelRegistry) {\n const models = codeModelsEntry.models.map((m) =>\n toModel(\n m,\n api,\n api as Api,\n codeModelsEntry.baseUrl,\n codeModelsEntry.imageConstraints,\n codeModelsEntry.supportsTools,\n codeModelsEntry.responseBodyPolicy,\n ),\n )\n const rejectedModelIds = this.modelRegistry.registerWithSource(models, ctx.plugin.id)\n this.modelSourcePluginIds.add(ctx.plugin.id)\n if (rejectedModelIds.length > 0) {\n this.logger.warn(\n { pluginId: ctx.plugin.id, rejectedModelIds },\n 'plugin codeProviderModels contains model ids already claimed by another source, rejected',\n )\n }\n }\n }\n }\n\n /** RFC-216 D1:per-plugin 差量撤销(工厂 unregisterBySource 含 dispose + model 表 + descriptor 表三清)。 */\n async unregister(pluginId: string): Promise<void> {\n if (this.providerRegistry && this.providerSourcePluginIds.has(pluginId)) {\n // unregisterBySource(非裸 unregister):含 dispose 调用,防代码式 provider 持久资源泄漏。\n await this.providerRegistry.unregisterBySource(codeProviderSource(pluginId))\n this.providerSourcePluginIds.delete(pluginId)\n }\n // RFC-148 M3:codeProviderModels 幂等 reload——ModelRegistry 是独立数据表。\n if (this.modelRegistry && this.modelSourcePluginIds.has(pluginId)) {\n this.modelRegistry.unregisterBySource(pluginId)\n this.modelSourcePluginIds.delete(pluginId)\n }\n // descriptor 表与 ProviderRegistry 是两个独立数据表,必须单独清理。\n const descriptorIds = this.descriptorIdsByPlugin.get(pluginId)\n if (descriptorIds) {\n for (const api of descriptorIds) unregisterProviderDescriptor(api)\n this.descriptorIdsByPlugin.delete(pluginId)\n }\n }\n\n async unregisterAll(): Promise<void> {\n const pluginIds = new Set([\n ...this.providerSourcePluginIds,\n ...this.modelSourcePluginIds,\n ...this.descriptorIdsByPlugin.keys(),\n ])\n for (const pluginId of pluginIds) await this.unregister(pluginId)\n }\n}\n","import type { Api, WireProtocolRegistry } from '@x-otto/ai'\nimport { registerCacheReportingApi, unregisterCacheReportingApi } from '@x-otto/ai'\nimport type { Logger } from '@x-otto/shared'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * Wire 协议实现轴(RFC-148 D2):需 manifest 声明 capabilities:[\"wire-protocol\"]\n * (fail-closed,高危能力,门控三连同 providerFactories 轴一致)。key = wireApi 字符串,\n * 不加 pluginId 前缀(协议名跨插件共享,first-win 由 WireProtocolRegistry.register 内部判定)。\n */\nexport class WireProtocolAxisHandler implements PluginAxisHandler {\n readonly axisName = 'wireProtocols'\n private wireProtocolSourcePluginIds = new Set<string>()\n /** RFC-216 D1:cache-hit-rate 联动 api 按 pluginId 分桶(原为无 source 追踪的扁平集合)。 */\n private cacheReportingApisByPlugin = new Map<string, Set<Api>>()\n\n constructor(\n private readonly wireProtocolRegistry: WireProtocolRegistry | undefined,\n private readonly logger: Logger,\n ) {}\n\n register(ctx: PluginAxisContext): void {\n const wireProtocols = ctx.loaded.module.wireProtocols\n if (!wireProtocols || !this.wireProtocolRegistry) return\n if (!ctx.plugin.manifest.capabilities?.includes('wire-protocol')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin declares wireProtocols but not capabilities:[\"wire-protocol\"], skipped (fail-closed)',\n )\n return\n }\n if (ctx.verdict.pendingCapabilities.includes('wire-protocol')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin capability \"wire-protocol\" declared but not yet granted, skipping wireProtocols (fail-closed, RFC-124 C2 pattern)',\n )\n return\n }\n for (const [wireApi, registration] of Object.entries(wireProtocols)) {\n const accepted = this.wireProtocolRegistry.register(wireApi, registration, ctx.plugin.id)\n if (accepted) {\n this.wireProtocolSourcePluginIds.add(ctx.plugin.id)\n // RFC-148 M4:meta.reportsCacheTokens 声明联动 cache-hit-rate 注册表(RFC-147 B-1 收口)。\n if (registration.meta?.reportsCacheTokens) {\n registerCacheReportingApi(wireApi)\n const bucket = this.cacheReportingApisByPlugin.get(ctx.plugin.id) ?? new Set<Api>()\n bucket.add(wireApi)\n this.cacheReportingApisByPlugin.set(ctx.plugin.id, bucket)\n }\n } else {\n this.logger.warn(\n { pluginId: ctx.plugin.id, wireApi },\n 'plugin wireProtocol rejected (already registered by another plugin, first-win)',\n )\n }\n }\n }\n\n /** RFC-216 D1:per-plugin 差量撤销(协议注册 + cache-hit-rate 联动同桶清理)。 */\n unregister(pluginId: string): void {\n if (this.wireProtocolRegistry && this.wireProtocolSourcePluginIds.has(pluginId)) {\n this.wireProtocolRegistry.unregisterBySource(pluginId)\n this.wireProtocolSourcePluginIds.delete(pluginId)\n }\n const apis = this.cacheReportingApisByPlugin.get(pluginId)\n if (apis) {\n for (const api of apis) unregisterCacheReportingApi(api)\n this.cacheReportingApisByPlugin.delete(pluginId)\n }\n }\n\n unregisterAll(): void {\n const pluginIds = new Set([...this.wireProtocolSourcePluginIds, ...this.cacheReportingApisByPlugin.keys()])\n for (const pluginId of pluginIds) this.unregister(pluginId)\n }\n}\n","import type { ToolRegistry } from '@x-otto/tools'\nimport type { Logger } from '@x-otto/shared'\nimport { toAgentTool } from '../../plugin/module-wiring'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/**\n * 代码式工具轴(RFC-105 D4/M105-3-02):需 manifest 声明 capabilities:[\"tools\"]\n * (fail-closed R4);结构不符的条目 fail-soft 丢弃(toAgentTool 返回 null)。\n *\n * **R3 同名拒注册**:`ToolRegistry.register()` 底层是 `Map.set()`,对已存在的工具名是\n * **覆盖写**、非拒绝——若不在此处显式拦截,插件工具可静默劫持同名内置工具,用户已对\n * 该名授予的权限会被挪用到插件代码上,是权限旁路级风险。逐条核对 `toolRegistry.has(name)`,\n * 命中则 warn + 丢弃该条,不覆盖、不给注册机会。\n */\nexport class ToolsAxisHandler implements PluginAxisHandler {\n readonly axisName = 'tools'\n private toolSourcePluginIds = new Set<string>()\n\n constructor(\n private readonly toolRegistry: ToolRegistry | undefined,\n private readonly logger: Logger,\n ) {}\n\n register(ctx: PluginAxisContext): void {\n const pluginTools = ctx.loaded.module.tools\n if (!pluginTools?.length || !this.toolRegistry) return\n if (!ctx.plugin.manifest.capabilities?.includes('tools')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin declares tools but not capabilities:[\"tools\"], skipped (fail-closed)',\n )\n return\n }\n if (ctx.verdict.pendingCapabilities.includes('tools')) {\n this.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin capability \"tools\" declared but not yet granted, skipping tools (fail-closed, RFC-124 C2)',\n )\n return\n }\n const registry = this.toolRegistry\n // RFC-303 M4:进程内工具 ctx 注入(workspaceDir/logger/host——host 经 PluginAxisContext\n // 透传,fail-closed:未授权为 undefined,工具侧 ctx.host 即 undefined)。\n const toolCtxSource = {\n workspaceDir: ctx.workspaceDir,\n logger: {\n info: (m: string) => this.logger.info({ pluginId: ctx.plugin.id }, m),\n warn: (m: string) => this.logger.warn({ pluginId: ctx.plugin.id }, m),\n error: (m: string) => this.logger.error({ pluginId: ctx.plugin.id }, m),\n },\n host: ctx.host,\n storage: ctx.storage,\n }\n const agentTools = pluginTools\n .map((t) => toAgentTool(ctx.plugin.id, t, toolCtxSource))\n .filter((t): t is NonNullable<typeof t> => t !== null)\n .filter((t) => {\n if (registry.has(t.name)) {\n this.logger.warn(\n { pluginId: ctx.plugin.id, tool: t.name },\n 'plugin tool name collides with an existing tool, rejected (R3, no silent override)',\n )\n return false\n }\n return true\n })\n if (agentTools.length > 0) {\n registry.register(agentTools, { preset: 'full', source: ctx.plugin.id })\n this.toolSourcePluginIds.add(ctx.plugin.id)\n }\n }\n\n /** RFC-216 D1:per-plugin 差量撤销(ToolRegistry 本就按 source=pluginId 注册)。 */\n unregister(pluginId: string): void {\n if (!this.toolRegistry || !this.toolSourcePluginIds.has(pluginId)) return\n this.toolRegistry.unregisterBySource(pluginId)\n this.toolSourcePluginIds.delete(pluginId)\n }\n\n unregisterAll(): void {\n for (const pluginId of [...this.toolSourcePluginIds]) this.unregister(pluginId)\n }\n}\n","import type { PluginScheduleSubscription } from '@x-otto/plugin'\nimport type { ScheduledTask, ScheduleSubscriber } from '@x-otto/schedule'\nimport type { Logger } from '@x-otto/shared'\n\n/**\n * RFC-327 D2(M2):插件服务 schedule 订阅的宿主侧差量对账。\n *\n * 插件**声明**它要订阅什么(`PluginService.schedules`),宿主负责把声明与 core schedule\n * 的实际状态对账——新增缺失的、更新变化的、删除多余的。这样:\n * - 插件重载不会产生重复订阅(同 key 视为同一订阅);\n * - 插件 disable/uninstall 时由 services 轴的 `unregister` 触发 `revokeAll`,不依赖\n * 插件自己清理(它已经不在运行了);\n * - owner 由宿主从注册上下文推导,插件无法自报身份。\n *\n * **不拥有调度**:本模块只调用 core schedule 的 add/update/remove,cron 解析、持久化、\n * fire ownership、崩溃恢复全部仍归 core(R4 timer 单一真源)。\n */\n\n/** core schedule 侧的最小操作面——只取本模块真正需要的四个方法,不耦合完整 SchedulerService。 */\nexport interface ScheduleSubscriptionPort {\n list(): ScheduledTask[]\n add(input: {\n name: string\n prompt: string\n cronExpression: string\n recurring: boolean\n origin: 'user' | 'model'\n subscriber?: ScheduleSubscriber\n }): ScheduledTask\n update(\n id: string,\n patch: { name?: string; prompt?: string; cronExpression?: string; enabled?: boolean },\n ): boolean\n remove(id: string): boolean\n}\n\n/**\n * 订阅任务的 name 前缀——宿主据此在 `list()` 里识别\"哪些任务属于插件订阅\"。\n *\n * 为什么不靠 `subscriber` 字段筛:`subscriber` 是可选字段,旧版持久化的任务没有它;\n * 用 name 前缀 + subscriber 双重判定,避免把用户手建的同名任务误删。\n */\nconst SUBSCRIPTION_NAME_PREFIX = '[plugin]'\n\n/**\n * 订阅在 core schedule 里的展示名。\n *\n * **格式必须让 `keyOfTask` 能反解出 `key`(稳定标识)而非展示名**——两者可以不同\n * (`name` 是给人看的、可随时改;`key` 是对账身份、不可变)。若 name 里只编码展示名,\n * 有 `name` 的订阅每轮对账都会因\"反解出的展示名 ≠ 声明的 key\"而被判为\"已不存在\"→\n * 删除 → 重建,产生无限的无效 remove+add 循环(独立评审发现,已由回归钉死)。\n *\n * 故格式固定为:`[plugin] <pluginId>:<serviceId>:<key> — <展示名>`,\n * key 段在 ` — ` 之前,展示名在其后(展示名含 ` — ` 也不影响反解)。\n */\nexport function subscriptionTaskName(\n pluginId: string,\n serviceId: string,\n sub: PluginScheduleSubscription,\n): string {\n const label = sub.name?.trim() || sub.key\n return `${SUBSCRIPTION_NAME_PREFIX} ${pluginId}:${serviceId}:${sub.key} — ${label}`\n}\n\n/** 判定一个已存在任务是否属于指定服务的订阅(双重判定:subscriber 归属 + name 前缀)。 */\nfunction belongsToService(task: ScheduledTask, pluginId: string, serviceId: string): boolean {\n const sub = task.subscriber\n if (!sub || sub.kind !== 'plugin-service') return false\n if (sub.pluginId !== pluginId || sub.serviceId !== serviceId) return false\n return task.name.startsWith(SUBSCRIPTION_NAME_PREFIX)\n}\n\n/**\n * 从任务名反解订阅 key(对账用)——与 `subscriptionTaskName` 严格互逆。\n *\n * 取 ` — ` **之前**的 key 段,故展示名里含 ` — ` 不影响反解。\n * 名字不合本模块格式(如用户手改过)→ 返回 undefined,该任务不被接管(第二道防线)。\n */\nfunction keyOfTask(task: ScheduledTask, pluginId: string, serviceId: string): string | undefined {\n const prefix = `${SUBSCRIPTION_NAME_PREFIX} ${pluginId}:${serviceId}:`\n if (!task.name.startsWith(prefix)) return undefined\n const rest = task.name.slice(prefix.length)\n const sep = rest.indexOf(' — ')\n if (sep <= 0) return undefined // key 段为空或无分隔符 → 非本模块生成的名字\n return rest.slice(0, sep)\n}\n\nexport interface ReconcileResult {\n added: number\n updated: number\n removed: number\n}\n\n/**\n * 把某个服务的订阅声明对账到 core schedule。\n *\n * 幂等:同一份声明重复调用只在首次产生变更。声明为空(或未声明)等价于\"撤销该服务的\n * 全部订阅\"——故插件删掉一条 schedule 声明后重载,对应任务会被清理。\n */\nexport function reconcileServiceSubscriptions(input: {\n port: ScheduleSubscriptionPort\n logger: Logger\n pluginId: string\n serviceId: string\n declared: readonly PluginScheduleSubscription[]\n}): ReconcileResult {\n const { port, logger, pluginId, serviceId } = input\n const result: ReconcileResult = { added: 0, updated: 0, removed: 0 }\n\n // 去重 + 过滤非法声明:key/cron 缺失的条目直接丢弃并告警(fail-soft,不阻断其余订阅)。\n const declaredByKey = new Map<string, PluginScheduleSubscription>()\n for (const sub of input.declared) {\n const key = sub?.key?.trim()\n const cron = sub?.cron?.trim()\n if (!key || !cron) {\n logger.warn(\n { pluginId, serviceId, key: sub?.key },\n 'plugin schedule subscription missing key or cron, skipped (RFC-327 D2)',\n )\n continue\n }\n if (declaredByKey.has(key)) {\n logger.warn(\n { pluginId, serviceId, key },\n 'duplicate plugin schedule subscription key, later declaration ignored',\n )\n continue\n }\n declaredByKey.set(key, { ...sub, key, cron })\n }\n\n const existing = new Map<string, ScheduledTask>()\n for (const task of port.list()) {\n if (!belongsToService(task, pluginId, serviceId)) continue\n const key = keyOfTask(task, pluginId, serviceId)\n if (key !== undefined) existing.set(key, task)\n }\n\n // 新增 / 更新\n for (const [key, sub] of declaredByKey) {\n const name = subscriptionTaskName(pluginId, serviceId, sub)\n const found = existing.get(key)\n if (!found) {\n try {\n port.add({\n name,\n // 订阅任务不派 AgentJob,prompt 不会被执行;填说明性文本便于用户在 UI 里理解。\n prompt: `plugin service subscription (${pluginId}:${serviceId}:${key})`,\n cronExpression: sub.cron,\n recurring: true,\n origin: 'user',\n subscriber: { kind: 'plugin-service', pluginId, serviceId },\n })\n result.added += 1\n } catch (err) {\n // cron 非法/超出任务上限等——不阻断其余订阅(fail-soft,同既有轴纪律)。\n logger.warn(\n { pluginId, serviceId, key, err: err instanceof Error ? err.message : String(err) },\n 'failed to register plugin schedule subscription',\n )\n }\n continue\n }\n if (found.cronExpression !== sub.cron || found.name !== name) {\n if (port.update(found.id, { cronExpression: sub.cron, name })) result.updated += 1\n }\n }\n\n // 删除:声明里已不存在的旧订阅\n for (const [key, task] of existing) {\n if (declaredByKey.has(key)) continue\n if (port.remove(task.id)) result.removed += 1\n }\n\n return result\n}\n\n/**\n * 撤销某插件全部服务的订阅——services 轴 `unregister`(disable/uninstall/重载前)调用。\n *\n * 这是 D2「disable/uninstall 自动撤销」的执行点:插件已不在运行,不可能自己清理,\n * 故必须由宿主在生命周期钩子里做。\n */\nexport function revokePluginSubscriptions(input: {\n port: ScheduleSubscriptionPort\n pluginId: string\n}): number {\n const { port, pluginId } = input\n let removed = 0\n for (const task of port.list()) {\n const sub = task.subscriber\n if (!sub || sub.kind !== 'plugin-service' || sub.pluginId !== pluginId) continue\n if (!task.name.startsWith(SUBSCRIPTION_NAME_PREFIX)) continue\n if (port.remove(task.id)) removed += 1\n }\n return removed\n}\n","import {\n reconcileServiceSubscriptions,\n revokePluginSubscriptions,\n type ScheduleSubscriptionPort,\n} from '../../plugin/plugin-schedule-subscriptions'\nimport type { PluginScheduleSubscription } from '@x-otto/plugin'\n\nimport type { Logger } from '@x-otto/shared'\nimport type { PluginServicePool, ServiceHostBridge } from '../../plugin/plugin-service-pool'\nimport type { PluginAxisContext, PluginAxisHandler } from './types'\n\n/** 宿主注入面:解析 service 运行所需的外部依赖(保持 handler 不持 App 引用)。 */\nexport interface ServicesAxisDeps {\n pool: PluginServicePool\n logger: Logger\n workspaceDir: string\n /** 工作区稳定标识——service 是 per-workspace 一实例(RFC-287 D1)。 */\n getWorkspaceKey: () => string\n /**\n * 该插件的宿主能力桥(按 pluginId 绑定)。R2 的 origin 强制在桥的实现内部完成\n * (`app.ts` 硬写 `origin:'plugin'`),本轴只负责把桥交给池,不复制安全逻辑。\n */\n bridgeFor: (pluginId: string) => ServiceHostBridge\n /** 该插件已被授予的 host 能力名(fail-closed 由调用方算好;空数组 = 无能力)。 */\n hostCapabilitiesFor: (pluginId: string) => string[]\n /**\n * RFC-303 M5:该插件 scoped storage 目录构造器(`plugin-state/<pluginId>/<wsKey>`)。\n * 调用方(app 装配)负责 mkdir + realpath 断言;返回目录绝对路径。\n */\n storageDirFor: (pluginId: string, workspaceKey: string) => string\n /** RFC-303 M5:该插件是否声明了 storage 能力(fail-closed:未声明不注入 ctx.storage)。 */\n hasStorageCapability: (pluginId: string) => boolean\n /**\n * RFC-327 D2(M2):core schedule 的订阅对账端口(惰性 getter——scheduler 在 TUI\n * 启动流程里才构造,晚于插件装载)。返回 undefined 时跳过订阅对账并 debug 记录:\n * headless/CLI standalone 路径没有 scheduler,插件声明的 schedules 自然不生效,\n * 这是既定边界(同 monitor 事件的跨进程边界)而非错误。\n */\n getSchedulePort?: () => ScheduleSubscriptionPort | undefined\n}\n\n/**\n * services 轴(RFC-287 D-M3-1):常驻服务贡献。\n *\n * 与其余 11 个轴的关键差异——**本轴的产物是长生命周期的**:其余轴 register 完即结束\n * (注册进某张表),本轴 register 会真正启动一个常驻 worker,unregister 要停掉它。故\n * register 是 async(等待服务 start 结果),unregister 走池的二阶停机。\n *\n * fail-closed(对齐 ToolsAxisHandler 等既有高危轴纹理):未声明 `capabilities:[\"service\"]`\n * 或已声明但未授予(`pendingCapabilities`)→ 不启动,只 warn。\n *\n * **隔离路径要求**:service 必须有编译产物路径才能在 worker 里 require。`compiledPath`\n * 由 `LoadedPluginModule` 提供;缺失时跳过并 warn(不静默)。\n */\nexport class ServicesAxisHandler implements PluginAxisHandler {\n readonly axisName = 'services'\n /** 已启动的 serviceId 按 pluginId 分桶,支持 per-plugin 差量撤销(RFC-216 D1 纹理)。 */\n private startedByPlugin = new Map<string, string[]>()\n /**\n * M3:per-plugin/service 的 schedules 声明缓存——首次 register 时若 scheduler 尚未\n * 就绪,对账被跳过;setScheduleService 后调 `reconcileAllSchedules()` 补跑。\n * key = `${pluginId}:${serviceId}`,value = schedules 声明(可能为空数组=显式撤销)。\n */\n private readonly scheduleDeclarations = new Map<string, readonly PluginScheduleSubscription[]>()\n\n constructor(private readonly deps: ServicesAxisDeps) {}\n\n async register(ctx: PluginAxisContext): Promise<void> {\n const services = ctx.loaded.module.services\n if (!services?.length) return\n\n if (!ctx.plugin.manifest.capabilities?.includes('service')) {\n this.deps.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin declares services but not capabilities:[\"service\"], skipped (fail-closed, RFC-287 D-M3-1)',\n )\n return\n }\n if (ctx.verdict.pendingCapabilities.includes('service')) {\n this.deps.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin capability \"service\" declared but not yet granted, skipping services (fail-closed)',\n )\n return\n }\n\n const compiledPath = ctx.loaded.compiledPath\n if (!compiledPath) {\n this.deps.logger.warn(\n { pluginId: ctx.plugin.id },\n 'plugin declares services but has no compiled entry path; services need a compiled module to run in the service worker (skipped)',\n )\n return\n }\n\n const bucket = this.startedByPlugin.get(ctx.plugin.id) ?? []\n for (const svc of services) {\n if (!svc?.id) continue\n const workspaceKey = this.deps.getWorkspaceKey()\n const hasStorage = this.deps.hasStorageCapability(ctx.plugin.id)\n const { error } = await this.deps.pool.start({\n pluginId: ctx.plugin.id,\n serviceId: svc.id,\n compiledPath,\n workspaceDir: this.deps.workspaceDir,\n workspaceKey,\n factoryCtx: {\n pluginId: ctx.plugin.id,\n pluginDir: ctx.plugin.dir,\n workspaceDir: this.deps.workspaceDir,\n },\n hostCapabilities: this.deps.hostCapabilitiesFor(ctx.plugin.id),\n // RFC-303 M5:storage 目录(宿主侧 mkdir + sanitize;worker 写面按此收窄)。\n storageDir: hasStorage ? this.deps.storageDirFor(ctx.plugin.id, workspaceKey) : '',\n hasStorageCapability: hasStorage,\n bridge: this.deps.bridgeFor(ctx.plugin.id),\n })\n if (error) {\n // 启动失败不阻断其余服务/插件(fail-soft,同既有轴纪律);池内部已按退避重启。\n this.deps.logger.warn(\n { pluginId: ctx.plugin.id, serviceId: svc.id, err: error },\n 'plugin service failed to start',\n )\n }\n bucket.push(svc.id)\n\n // M3:缓存本 service 的 schedules 声明——scheduler 晚绑定时用 `reconcileAllSchedules` 补跑。\n this.scheduleDeclarations.set(`${ctx.plugin.id}:${svc.id}`, svc.schedules ?? [])\n\n // RFC-327 D2:订阅声明 → core schedule 差量对账。放在 start 之后:服务起不来时\n // 订阅也不该生效(避免\"服务不在跑却有定时任务在触发\"的悬空态)。\n const port = this.deps.getSchedulePort?.()\n if (svc.schedules?.length) {\n if (!port) {\n this.deps.logger.debug(\n { pluginId: ctx.plugin.id, serviceId: svc.id, count: svc.schedules.length },\n 'plugin declares schedule subscriptions but no scheduler in this process, skipped (RFC-327 D2 boundary)',\n )\n } else if (!error) {\n const r = reconcileServiceSubscriptions({\n port,\n logger: this.deps.logger,\n pluginId: ctx.plugin.id,\n serviceId: svc.id,\n declared: svc.schedules,\n })\n if (r.added || r.updated || r.removed) {\n this.deps.logger.info(\n { pluginId: ctx.plugin.id, serviceId: svc.id, ...r },\n 'plugin schedule subscriptions reconciled',\n )\n }\n }\n } else if (port) {\n // 声明为空 = 撤销该服务此前的全部订阅(插件删掉声明后重载即清理)。\n reconcileServiceSubscriptions({\n port,\n logger: this.deps.logger,\n pluginId: ctx.plugin.id,\n serviceId: svc.id,\n declared: [],\n })\n }\n }\n if (bucket.length > 0) this.startedByPlugin.set(ctx.plugin.id, bucket)\n }\n\n async unregister(pluginId: string): Promise<void> {\n if (!this.startedByPlugin.has(pluginId)) return\n this.startedByPlugin.delete(pluginId)\n\n // M3:清理该插件全部 service 的 schedules 声明缓存。\n for (const key of [...this.scheduleDeclarations.keys()]) {\n if (key.startsWith(`${pluginId}:`)) this.scheduleDeclarations.delete(key)\n }\n\n // RFC-327 D2:先撤销订阅再停服务——顺序不能反。反了会有一个窗口期:服务已停但\n // 订阅还在,到点触发时投递失败(服务不在跑),白白产生一次无效 tick 与错误日志。\n const port = this.deps.getSchedulePort?.()\n if (port) {\n const removed = revokePluginSubscriptions({ port, pluginId })\n if (removed > 0) {\n this.deps.logger.info({ pluginId, removed }, 'plugin schedule subscriptions revoked')\n }\n }\n\n await this.deps.pool.stopPlugin(pluginId)\n }\n\n async unregisterAll(): Promise<void> {\n for (const pluginId of [...this.startedByPlugin.keys()]) await this.unregister(pluginId)\n }\n\n /**\n * M3:scheduler 就绪后对已启动 services 补跑 schedule 声明对账。\n *\n * 首次启动时序:App.start() 内 services axis register(scheduler 尚不存在)→\n * interactive setup 构造 scheduler → setScheduleService() → 调本方法补跑。\n *\n * 幂等:`reconcileServiceSubscriptions` 自身幂等(同声明重复调用不产生变更)。\n * 只对已成功启动的 service 跑(`startedByPlugin` 里的),跳过启动失败的。\n */\n reconcileAllSchedules(): void {\n const port = this.deps.getSchedulePort?.()\n if (!port) return\n\n for (const [pluginId, serviceIds] of this.startedByPlugin) {\n for (const serviceId of serviceIds) {\n const declared = this.scheduleDeclarations.get(`${pluginId}:${serviceId}`)\n if (declared === undefined) continue\n const r = reconcileServiceSubscriptions({\n port,\n logger: this.deps.logger,\n pluginId,\n serviceId,\n declared,\n })\n if (r.added || r.updated || r.removed) {\n this.deps.logger.info(\n { pluginId, serviceId, ...r, source: 'scheduler-ready reconcile' },\n 'plugin schedule subscriptions reconciled (M3 scheduler-ready)',\n )\n }\n }\n }\n }\n}\n","/**\n * plugin-scoped-storage.ts —— RFC-303 M5:插件 scoped persistence。\n *\n * 每个插件在自己的隔离目录 `${OTTO_HOME}/plugin-state/<pluginId>/<workspaceKey>/` 内\n * 用 `Persistence<T>`(复用 `packages/persistence`,R6 不新造存储抽象)读写状态。\n *\n * 安全设计(与 R7 写面收窄配套):\n * 1. **pluginId sanitize**:只允许 kebab-case(`^[a-z0-9]+(?:-[a-z0-9]+)*$`),拒绝\n * `..`/`/`/`\\` 等路径穿越字符——构造期即抛错,不静默接受(伪造 pluginId 不可能\n * 逃逸出 plugin-state 根目录)。\n * 2. **realpath 逃逸断言**:目标目录 resolve 后取 realpath,断言以\n * `${OTTO_HOME}/plugin-state/` 为前缀(防符号链接逃逸——插件目录里若有人预先放\n * 一个指向外部的 symlink,realpath 会暴露真实路径,前缀断言拒绝之)。\n * 3. **物理写面隔离**:service worker 的 execArgv `--allow-fs-write` 已按插件收窄到\n * 自己的目录(plugin-service-pool.ts),写他人 plugin-state 子目录会被 Node 权限\n * 模型拒绝(ERR_ACCESS_DENIED)——本类做路径层防御,权限层在 OS/Node 模型兜底。\n *\n * 能力声明:`capabilities: [\"storage\"]`,非高危(不进 HIGH_RISK_CAPABILITIES 弹窗——\n * 写面物理隔离无提权路径),但仍走 fail-closed 门控(未声明不注入 ctx.storage)。\n */\nimport { existsSync, mkdirSync, realpathSync, rmSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { createPersistence, type Persistence } from '@x-otto/persistence'\nimport { OTTO_HOME, isShadowModeEnabled } from '@x-otto/env'\n\n/** pluginId 白名单:kebab-case(对齐插件 manifest id 校验,拒绝路径穿越字符)。 */\nconst PLUGIN_ID_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/\n\n/** plugin-state 根目录(`OTTO_HOME/plugin-state`)。 */\nexport const PLUGIN_STATE_ROOT = join(OTTO_HOME, 'plugin-state')\n\n/**\n * 清理某插件的全部 plugin-state(`otto extension purge <id>` 出口,RFC-303 D3/M6 接线)。\n * 只删除该插件的子目录,不影响其他插件;插件不存在/目录不存在时幂等成功。\n */\nexport function purgePluginState(pluginId: string): { removed: boolean; dir?: string } {\n const dir = pluginStateDirFor(pluginId)\n try {\n rmSync(dir, { recursive: true, force: true })\n return { removed: !existsSync(dir), dir }\n } catch {\n return { removed: false, dir }\n }\n}\n\n/** 校验 pluginId 并返回其 scoped 根目录(`plugin-state/<pluginId>`)。 */\nexport function pluginStateDirFor(pluginId: string): string {\n if (!PLUGIN_ID_RE.test(pluginId)) {\n throw new Error(`invalid pluginId for scoped storage: ${pluginId} (must be kebab-case)`)\n }\n return join(PLUGIN_STATE_ROOT, pluginId)\n}\n\n/**\n * 插件 scoped persistence 门面——包裹 `Persistence<T>`,构造时固化\n * `pluginId`/`workspaceKey` 并做 sanitize + realpath 逃逸断言。\n *\n * 用法(插件侧):\n * ```ts\n * // service ctx:\n * await ctx.storage.save({ id: 'state', count: 1 })\n * const state = await ctx.storage.load('state')\n * ```\n *\n * 对外只暴露 `save/load/delete/list`(与 `Persistence<T>` 同语义),实现细节\n * (sanitize/逃逸断言)不泄漏给调用方。\n */\nexport class PluginScopedStorage {\n private readonly persistence: Persistence<Record<string, unknown>>\n /** 实际解析后的目标目录(realpath 已断言在 plugin-state 根下)。 */\n readonly baseDir: string\n readonly pluginId: string\n readonly workspaceKey: string\n\n constructor(pluginId: string, workspaceKey: string) {\n this.pluginId = pluginId\n this.workspaceKey = workspaceKey\n if (!PLUGIN_ID_RE.test(pluginId)) {\n throw new Error(`invalid pluginId for scoped storage: ${pluginId} (must be kebab-case)`)\n }\n if (typeof workspaceKey !== 'string' || workspaceKey.length === 0 || workspaceKey.includes('/') || workspaceKey.includes('\\\\') || workspaceKey.includes('..')) {\n throw new Error(`invalid workspaceKey for scoped storage: ${workspaceKey}`)\n }\n\n // RFC-345 §D3:影子态用内存后端,完全不碰 plugin-state 磁盘目录(不 mkdir、不 realpath)。\n // 插件状态(skill-inductor episode/candidate 等)只活在进程内存,退出即失——影子会话\n // 不污染长期积累。sanitize 校验仍在上方执行(防御深度不因影子态放松)。\n if (isShadowModeEnabled()) {\n this.baseDir = `:memory:/plugin-state/${pluginId}/${workspaceKey}`\n this.persistence = createPersistence<Record<string, unknown>>({ type: 'memory' })\n return\n }\n\n const scopedRoot = join(pluginStateDirFor(pluginId), workspaceKey)\n const resolved = resolve(scopedRoot)\n\n // realpath 逃逸断言:目标目录真实路径必须以 plugin-state 根为前缀。\n // (目录可能尚不存在——mkdir 后再 realpath;已存在则直接断言。)\n mkdirSync(resolved, { recursive: true })\n const real = realpathSync(resolved)\n const realRoot = realpathSync(PLUGIN_STATE_ROOT)\n if (real !== realRoot && !real.startsWith(realRoot + '/')) {\n throw new Error(`scoped storage path escapes plugin-state root: ${real}`)\n }\n\n this.baseDir = resolved\n this.persistence = createPersistence<Record<string, unknown>>({\n type: 'file',\n baseDir: resolved,\n })\n }\n\n /**\n * 持久化快照。`Persistence<T>` 的契约是 `Snapshot<T>`(version/id/data/metadata 信封)——\n * 插件侧心智模型是\"按 id 存我的状态对象\",故这里把插件传入的任意对象包进 data 字段:\n * `save(id, data)` → `{ id, data }`(version/metadata 由 FilePersistence 缺省填充)。\n */\n async save(id: string, data: Record<string, unknown>): Promise<void> {\n await this.persistence.save({\n id,\n data,\n version: 1,\n metadata: {},\n } as Parameters<Persistence<Record<string, unknown>>['save']>[0])\n }\n\n /** 读回快照数据;不存在返回 null。 */\n async load(id: string): Promise<Record<string, unknown> | null> {\n const snap = await this.persistence.load(id)\n return snap ? (snap.data as Record<string, unknown>) : null\n }\n\n /** 删除快照。 */\n delete(id: string): Promise<boolean> {\n return this.persistence.delete(id)\n }\n\n /** 列出全部快照 id。 */\n list(): Promise<string[]> {\n return this.persistence.list()\n }\n}\n","import type { HookRegistry } from '@x-otto/hooks'\nimport type { Logger } from '@x-otto/shared'\nimport type { SkillRegistry } from '@x-otto/skill'\nimport type { AgentProfile } from '@x-otto/prompt'\nimport type { DiscoveredPlugin, PluginContext, MonitorEvent } from '@x-otto/plugin'\nimport { evaluatePluginTrust } from '@x-otto/plugin'\nimport type { Api, ProviderRegistry, WireProtocolRegistry, ModelRegistry, AuthStore } from '@x-otto/ai'\nimport type { ToolRegistry } from '@x-otto/tools'\nimport type { FeedbackRegistry } from '../feedback/feedback-registry'\nimport type { SigilResolverRegistry } from '../input/sigil-resolver-registry'\nimport type { PluginInputRegistry } from '@x-otto/plugin'\n\nimport type { Message } from '@x-otto/ai'\nimport type { GrillAnswer, GrillQuestion } from '@x-otto/interchange'\n\nimport { statSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { projectTranscript } from '../plugin/transcript-projection'\nimport {\n PluginCapabilityQuota,\n LLM_MAX_TOKENS_CAP,\n LLM_PROMPT_MAX_CHARS,\n TRANSCRIPT_MAX_MESSAGES,\n NOTIFY_MESSAGE_MAX_CHARS,\n NOTIFY_CODE_MAX_CHARS,\n} from '../plugin/plugin-capability-quota'\n\nimport { loadPluginModule } from '../plugin/module-loader'\nimport type { LoadedPluginModule } from '../plugin/module-loader'\nimport {\n loadPluginModuleIsolated,\n shouldUseIsolatedLoading,\n} from '../plugin/module-loader-isolated'\nimport { resolveActivationEvents } from '@x-otto/plugin'\nimport { shouldActivateNow } from '../plugin/activation-events'\nimport { appendPluginLog } from '../plugin/plugin-log-writer'\nimport type { FileCommand } from '@x-otto/claude-compat'\nimport {\n type PluginAxisContext,\n type PluginAxisHandler,\n HooksAxisHandler,\n MonitorsAxisHandler,\n FeedbackAxisHandler,\n SigilAxisHandler,\n ComputedCommandsAxisHandler,\n ComputedAgentsAxisHandler,\n ComputedSkillsAxisHandler,\n OAuthProviderAxisHandler,\n CodeProviderAxisHandler,\n WireProtocolAxisHandler,\n ToolsAxisHandler,\n ServicesAxisHandler,\n} from './plugin-axis-handlers'\nimport {\n getPluginServicePool,\n type PluginServicePool,\n type ServiceHostBridge,\n} from '../plugin/plugin-service-pool'\nimport { DEFAULT_WORKSPACE_KEY } from '@x-otto/workspace'\nimport { PluginScopedStorage } from '../plugin/plugin-scoped-storage'\n\n/** PluginModuleManager 依赖面(M100 DI 范式:interface 声明依赖、不持 App 引用、可独立测)。 */\nexport interface PluginModuleManagerDeps {\n hookRegistry: HookRegistry\n skillRegistry: SkillRegistry | null\n /** RFC-105 D3:代码式 provider 工厂注册目标(可选——不传则 `providerFactories` 轴静默跳过)。 */\n providerRegistry?: ProviderRegistry\n /** RFC-148 M3:代码式 provider 静态模型注册目标(可选——不传则 `codeProviderModels` 静默跳过)。 */\n modelRegistry?: ModelRegistry\n /** RFC-148 D2:Wire 协议实现注册目标(可选——不传则 `wireProtocols` 轴静默跳过)。 */\n wireProtocolRegistry?: WireProtocolRegistry\n /** RFC-122 D1 / RFC-130 M131-1:代码式 OAuth 工厂注册目标。 */\n authStore?: AuthStore\n /** RFC-105 D4:代码式工具注册目标(可选——不传则 `tools` 轴静默跳过)。 */\n toolRegistry?: ToolRegistry\n /** RFC-209 §2.3:Feedback 业务契约注册目标(可选——不传则 `feedbackProviders` 轴静默跳过)。 */\n feedbackRegistry?: FeedbackRegistry\n /** RFC-210 D3:@ 资源 resolve 注册目标(可选——不传则 `sigilResolvers` 轴静默跳过)。 */\n sigilResolverRegistry?: SigilResolverRegistry\n /** RFC-210 D3:@ 资源动态 provider 注册目标(可选——不传则 `sigilProviders` 轴静默跳过)。\n * registerProvider 返回注销函数,由本管理器持有供 reload/cleanup 撤销。 */\n pluginInput?: PluginInputRegistry\n /** RFC-105 D9:是否对声明了 `tools` 能力的插件启用 worker 隔离加载(默认 true——隔离)。 */\n useIsolatedLoading?: boolean\n /** RFC-153 D3:插件加载/编译/工厂失败的旁路通知(可选,不传则不记录健康度)。 */\n onPluginLoadError?: (pluginId: string, message: string) => void\n workspaceDir: string\n activePlugins: () => DiscoveredPlugin[]\n /**\n * RFC-287 D2:派发 agent 作业的宿主能力源(可选——不传则 `agent.dispatch` 能力面\n * 静默不可用,同其余可选 registry 的既有纹理)。惰性 getter 而非直接传服务实例:\n * `AgentJobService` 在 App 里是 lazy 构造(`app.ts` 的 `get agentJobs()`),构造期\n * 直接取值会提前实例化整条作业链。\n *\n * **安全契约(R2,本处是唯一强制点)**:本回调的入参不含 `origin`——`origin: 'plugin'`\n * 由 `buildHostCapabilities` 内部固定写入,插件无法influence;入参也不含\n * `skipPermissions`/`useWorktree`,插件无法借作业绕权限或直改主树。\n */\n startPluginJob?: (input: { pluginId: string; title: string; prompt: string; sessionId: string }) => { id: string }\n /** RFC-287 D2:插件作业归属的会话 id(活引用——`/resume` 换 session 后闭包读最新值)。 */\n getSessionId?: () => string | undefined\n /**\n * RFC-318 M1:会话消息读取源(可选——不传则 `session.read` 能力静默不可用)。\n * 返回该会话的完整历史;投影/掩码/切片由 `buildHostCapabilities` 统一处理,本回调\n * 只负责\"取原始数据\",不承担安全职责(单一强制点原则)。\n */\n getSessionMessages?: (sessionId: string) => readonly Message[] | undefined\n /**\n * RFC-318 M1:模型文本补全源(可选——不传则 `llm.complete` 能力静默不可用)。\n * 与 RFC-036 lesson 抽取共用同一 `createModelTextCaller` 形状(D10:两条抽取管线\n * 并存、各自独立调用,不合并)。\n */\n completeText?: (prompt: string, opts?: { maxTokens?: number }) => Promise<string>\n /**\n * RFC-327 M3:宿主 UI 通知出口(app 层接线到 TUI 活动流/toast)。\n * 缺省 → `user.notify` 能力不注入(fail-closed,同其他能力的\"宿主未提供源\"分支)。\n */\n notifyUser?: (input: {\n pluginId: string\n level: 'info' | 'warning' | 'error'\n code: string\n message: string\n }) => void\n /**\n * RFC-327 D2(M2):core schedule 订阅对账端口(惰性 getter)。\n * 缺省或返回 undefined → 跳过订阅对账(headless 无 scheduler 的既定边界)。\n */\n getSchedulePort?: () => import('../plugin/plugin-schedule-subscriptions').ScheduleSubscriptionPort | undefined\n /**\n * RFC-318 M1:受控能力的配额与会话白名单(可选——不传则内部自建实例)。\n * 由宿主装配点持有,使 `pool.notify` 的授权登记与能力调用的配额校验共用同一份状态。\n */\n capabilityQuota?: PluginCapabilityQuota\n /** RFC-344 M3:阻塞式 user.ask 出口。缺省 → 能力不注入(fail-closed)。 */\n askUser?: (pluginId: string, input: Omit<GrillQuestion, 'id' | 'askedBy'>) => Promise<GrillAnswer>\n /**\n * RFC-287 M3:常驻服务池(可选——不传则用全局单例;测试注入隔离实例)。\n * 缺省惰性取全局单例,避免不使用 services 的装配路径也被迫构造池。\n */\n servicePool?: PluginServicePool\n /** RFC-287 M3:工作区稳定标识(service 是 per-workspace 一实例)。缺省 'default'。 */\n /** RFC-287 M3:工作区稳定标识(service 是 per-workspace 一实例)。缺省 'default'(storageDirFor 兜底);\n * RFC-303 M5 复用同一字段做 scoped persistence 的 workspaceKey 来源,惰性未就位时用 DEFAULT_WORKSPACE_KEY 兜底。 */\n getWorkspaceKey?: () => string | undefined\n callMcpTool: (\n server: string,\n tool: string,\n params?: Record<string, unknown>,\n ) => Promise<{ content: { type: string; text?: string }[]; isError?: boolean }>\n logger: Logger\n}\n\n/**\n * 跨插件凭据隔离判定(终局 review P0 修复,RFC-148 M3 抽为共享 helper):只放行该插件自身\n * 声明的裸 id 白名单——`codeProviderIds`(纯代码式插件,如 plugin-anthropic)或\n * `contributes.providers[].preserveProviderId`(RFC-130 D7,\"声明式 catalog + 代码式工厂\"\n * 共存的插件如 plugin-cursor 复用同一处声明,同 `resolveCodeProviderApi` 判定一致——两处\n * 必须保持同源,否则声明式轴已注册的裸 id provider 无法通过代码式轴的凭据解析)——或以\n * 自身 `<pluginId>:` 为前缀的命名空间 api。`resolveProviderCredential`/`resolveProviderAuth`\n * 两个凭据解析方法共用同一份判定,防止任意插件在其代码式 provider 工厂里查任意 provider id\n * 窃取其他插件/内置 provider 的已存储凭据。\n */\nfunction ownedApiChecker(plugin: DiscoveredPlugin): (api: string) => boolean {\n const ownedBareIds = new Set([\n ...(plugin.manifest.codeProviderIds ?? []),\n ...(plugin.manifest.contributes?.providers ?? [])\n .filter((p) => p.preserveProviderId === true)\n .map((p) => p.id),\n ])\n const namespacePrefix = `${plugin.id}:`\n return (api: string) => ownedBareIds.has(api) || api.startsWith(namespacePrefix)\n}\n\n/**\n * RFC-287 D2:按 per-capability 授权状态构建插件可见的宿主能力面。\n *\n * fail-closed 三重判定(与 ToolsAxisHandler 等既有高危轴的门控纹理一致):\n * ① manifest 未声明该能力 → 不注入;② 声明了但用户尚未授予(`pendingCapabilities`)→\n * 不注入;③ 宿主未提供能力源(可选依赖缺省)→ 不注入。任一不满足即该字段为 `undefined`,\n * 插件侧只能拿到 undefined 而非一个\"会抛错的假句柄\"——让\"没有能力\"在类型层就可判定。\n *\n * **R2 强制点(唯一)**:`origin: 'plugin'` 在此写死,`startJob` 对外暴露的入参里没有\n * origin 字段,插件无法自选或伪装;同样没有 `skipPermissions`/`useWorktree`。\n */\nfunction buildHostCapabilities(\n plugin: DiscoveredPlugin,\n verdict: ReturnType<typeof evaluatePluginTrust>,\n deps: PluginModuleManagerDeps,\n): import('@x-otto/plugin').PluginHostCapabilities | undefined {\n const declared = plugin.manifest.capabilities ?? []\n const host: import('@x-otto/plugin').PluginHostCapabilities = {}\n let any = false\n\n const startPluginJob = deps.startPluginJob\n if (\n declared.includes('agent.dispatch') &&\n !verdict.pendingCapabilities.includes('agent.dispatch') &&\n startPluginJob\n ) {\n host.startJob = async (input: { title: string; prompt: string }) => {\n const sessionId = deps.getSessionId?.()\n if (!sessionId) {\n throw new Error('agent.dispatch unavailable: no active session to attach the job to')\n }\n // origin 不在入参里——由本处固定为 'plugin'(R2)。pluginId 由宿主从闭包捕获的\n // plugin 对象取得(不读插件传入的任何字段),供作业标题标注发起方,满足 R2 的\n // \"可审计到具体插件\"意图;**不把 pluginId 编进 origin 值本身**——`JobOrigin` 是\n // 闭合联合,`decideApply` 按 `origin === 'plugin'` 精确匹配走最保守分支,改成\n // `plugin:<id>` 会使该判定失配、坠入 main 分支,在 autonomy L2+ 反而可能 auto 落地\n // (独立评审曾建议此改法,经 apply-policy.ts:79 核实会引入提权,故不采纳)。\n const job = startPluginJob({ pluginId: plugin.id, title: input.title, prompt: input.prompt, sessionId })\n return { jobId: job.id }\n }\n any = true\n } else if (declared.includes('agent.dispatch')) {\n deps.logger.warn(\n { pluginId: plugin.id, pending: verdict.pendingCapabilities.includes('agent.dispatch') },\n 'plugin declares agent.dispatch but it is not available (not yet granted, or host provides no job dispatcher) — skipping (fail-closed, RFC-287 D2)',\n )\n }\n\n // RFC-318 M1:session.read —— 同一三重门(声明 / 已授予 / 宿主提供源)。\n const getSessionMessages = deps.getSessionMessages\n const quota = capabilityQuotaOf(deps)\n if (\n declared.includes('session.read') &&\n !verdict.pendingCapabilities.includes('session.read') &&\n getSessionMessages\n ) {\n host.readTranscript = async (input: { sessionId: string; sinceIndex?: number }) => {\n // ① 白名单:只有经 pool.notify 下发给本插件的 sessionId 可读(R2)。插件传任意\n // sessionId 探读其他会话 → 拒绝。\n if (!quota.isSessionAuthorized(plugin.id, input.sessionId)) {\n throw new Error('session not authorized for this plugin')\n }\n // ② 先确认会话确实存在再消费配额——顺序不能反:notify 之后会话被清理是真实竞态,\n // 先扣配额会让插件白白损失本 epoch 的唯一一次读取机会(审核 #2)。\n const messages = getSessionMessages(input.sessionId)\n if (!messages) throw new Error('session not found')\n // ③ 配额:每个 notify epoch 一次。\n if (!quota.tryConsumeRead(plugin.id)) {\n throw new Error('readTranscript quota exceeded for this notification')\n }\n // ④ 投影 + 掩码 + 条数上限(敏感字段在类型层就不存在,见 transcript-projection)。\n return projectTranscript(messages, input.sinceIndex ?? 0, TRANSCRIPT_MAX_MESSAGES)\n }\n any = true\n } else if (declared.includes('session.read')) {\n deps.logger.warn(\n { pluginId: plugin.id, pending: verdict.pendingCapabilities.includes('session.read') },\n 'plugin declares session.read but it is not available (not yet granted, or host provides no transcript source) — skipping (fail-closed, RFC-318 D2)',\n )\n }\n\n // RFC-318 M1:llm.complete —— 同一三重门 + 长度/频率配额。\n const completeText = deps.completeText\n if (\n declared.includes('llm.complete') &&\n !verdict.pendingCapabilities.includes('llm.complete') &&\n completeText\n ) {\n host.complete = async (input: { prompt: string; maxTokens?: number }) => {\n // prompt 超限**拒绝而非截断**:截断会让插件误以为调用成功(拿到基于残缺输入的结果);\n // 该上限同时限制 readTranscript + complete 组合的单次外流规模。\n if (input.prompt.length > LLM_PROMPT_MAX_CHARS) {\n throw new Error(`prompt exceeds ${LLM_PROMPT_MAX_CHARS} chars limit`)\n }\n const verdictQuota = quota.tryConsumeLlm(plugin.id)\n if (!verdictQuota.ok) {\n throw new Error(`llm.complete quota exceeded (${verdictQuota.reason})`)\n }\n // maxTokens 夹紧(非拒绝:补全长度无歧义,夹紧不会让插件误判语义)。\n const maxTokens = Math.min(input.maxTokens ?? LLM_MAX_TOKENS_CAP, LLM_MAX_TOKENS_CAP)\n return completeText(input.prompt, { maxTokens })\n }\n any = true\n } else if (declared.includes('llm.complete')) {\n deps.logger.warn(\n { pluginId: plugin.id, pending: verdict.pendingCapabilities.includes('llm.complete') },\n 'plugin declares llm.complete but it is not available (not yet granted, or host provides no model caller) — skipping (fail-closed, RFC-318 D2)',\n )\n }\n\n // RFC-344 M3:user.ask —— 高危阻塞式问询,同一三重门(声明 / 已授予 / 宿主提供出口)。\n const askUser = deps.askUser\n if (\n declared.includes('user.ask') &&\n !verdict.pendingCapabilities.includes('user.ask') &&\n askUser\n ) {\n host.ask = async (input: import('@x-otto/plugin').HostAskInput) => {\n // 逐字段提取,绝不透传插件对象:插件即使夹带 id/askedBy/sessionId 也会在这里被丢弃。\n // askedBy 由 askUser 内部按闭包捕获的 plugin.id 强制填充(R7)。\n return askUser(plugin.id, {\n header: String(input.header ?? ''),\n question: String(input.question ?? ''),\n background: String(input.background ?? ''),\n recommendation: String(input.recommendation ?? ''),\n ...(Array.isArray(input.options) ? { options: input.options } : {}),\n ...(input.allowFreeform !== undefined ? { allowFreeform: Boolean(input.allowFreeform) } : {}),\n ...(input.multiSelect !== undefined ? { multiSelect: Boolean(input.multiSelect) } : {}),\n ...(Array.isArray(input.evidence) ? { evidence: input.evidence.map(String) } : {}),\n })\n }\n any = true\n } else if (declared.includes('user.ask')) {\n deps.logger.warn(\n { pluginId: plugin.id, pending: verdict.pendingCapabilities.includes('user.ask') },\n 'plugin declares user.ask but it is not available (not yet granted, or host provides no UI sink) — skipping (fail-closed, RFC-344 M3)',\n )\n }\n\n // RFC-327 M3:user.notify —— 同一三重门(声明 / 已授予 / 宿主提供出口)。\n // 非高危故 verdict 通常不会 pending,但仍走同一判定,不因\"非高危\"就跳过授权检查。\n const notifyUser = deps.notifyUser\n if (\n declared.includes('user.notify') &&\n !verdict.pendingCapabilities.includes('user.notify') &&\n notifyUser\n ) {\n host.notify = async (input: { level: 'info' | 'warning' | 'error'; code: string; message: string }) => {\n // 限频:超限**静默丢弃**并记宿主诊断,不向插件反馈——反馈会让插件据此探测\n // 并调整节奏绕过限频(同 R6 的\"由宿主强制\"意图)。\n if (!quota.tryConsumeNotify(plugin.id)) {\n deps.logger.debug(\n { pluginId: plugin.id, code: input.code },\n 'plugin user.notify dropped: rate limit exceeded (silent by design, RFC-327 D3)',\n )\n return\n }\n // 内容强制:纯文本化 + 长度上限。截断而非拒绝——通知是展示面,截断不改变语义。\n //\n // 剥离范围(独立评审补齐,缺一即可伪造 UI):\n // - `\\u0000-\\u001f` + `\\u007f`:C0 控制字符与 DEL(含 ESC,挡 `\\u001b[` 双字节 ANSI);\n // - `\\u0080-\\u009f`:**C1 控制块**——其中 `\\u009b` 是单字节 CSI 起始符,在 8-bit 模式\n // 终端可直接发起 ANSI 序列(清屏/移光标/改色)而绕过双字节 ESC 检测;\n // - bidi 控制符(`\\u200e/\\u200f`、`\\u202a-\\u202e`、`\\u2066-\\u2069`):可视觉重排文本,\n // 把恶意内容显示成正常文案(如 RTL override 让字符串反向渲染)。\n // eslint-disable-next-line no-control-regex\n const sanitized = input.message\n // eslint-disable-next-line no-control-regex\n .replace(/[\\u0000-\\u001f\\u007f-\\u009f\\u200e\\u200f\\u202a-\\u202e\\u2066-\\u2069]/g, ' ')\n .slice(0, NOTIFY_MESSAGE_MAX_CHARS)\n notifyUser({\n pluginId: plugin.id, // 由宿主从闭包捕获,不读插件传入的任何身份字段\n level: input.level,\n code: input.code.slice(0, NOTIFY_CODE_MAX_CHARS),\n message: sanitized,\n })\n }\n any = true\n } else if (declared.includes('user.notify')) {\n deps.logger.warn(\n { pluginId: plugin.id, pending: verdict.pendingCapabilities.includes('user.notify') },\n 'plugin declares user.notify but it is not available (not yet granted, or host provides no UI sink) — skipping (fail-closed, RFC-327 D3)',\n )\n }\n\n return any ? host : undefined\n}\n\n/**\n * RFC-318 M1:配额实例解析——宿主未注入时按 deps 对象缓存一个。\n *\n * 必须与 `pool.notify` 的授权登记共用同一实例,否则白名单永远为空(读能力恒拒)。\n * 生产装配点显式传入;测试与降级路径用此兜底,保证行为一致而非静默失效。\n */\nconst fallbackQuotas = new WeakMap<PluginModuleManagerDeps, PluginCapabilityQuota>()\nfunction capabilityQuotaOf(deps: PluginModuleManagerDeps): PluginCapabilityQuota {\n if (deps.capabilityQuota) return deps.capabilityQuota\n let q = fallbackQuotas.get(deps)\n if (!q) {\n q = new PluginCapabilityQuota()\n fallbackQuotas.set(deps, q)\n }\n return q\n}\n\n/**\n * 插件代码模块(definePlugin `plugin.ts`)的加载 / 接线 / 卸载单源(RFC-083 M1 · ARCH-083-01;\n * RFC-211 M2:`reload()`/`cleanup()` 内联的 11 条贡献轴拆分为独立 `PluginAxisHandler`,\n * 本类只保留跨轴共用的编排逻辑:removed/changed 判定 + onDeactivate 调用、信任门检查、\n * 编译加载、激活事件门控、handler 遍历循环)。\n *\n * 从 App 抽出:持三段 computed 状态(hook 名 / 命令 / skill 名)+ `reload`(注销上轮 → 信任门内编译加载 →\n * 注册 hooks/skills、收集 computed 命令、回传待并入的 computed agents)+ `cleanup`(stop 时注销,防泄漏进\n * shared registry)。行为逐字搬自 `App.reloadPluginModules`,唯一差异:把「就地 push 进 App.declarativeAgentProfiles」\n * 改为「回传待追加的 profiles,由 App 并入」——消除对 App 状态的就地写,满足「不持 App 引用」。\n *\n * 纯搬迁纪律(RFC-211 M2):11 个 axis handler 的装配顺序与原 reload()/cleanup() 中轴出现\n * 顺序逐字节保持一致(hooks → monitors → feedback → sigil → computedCommands →\n * computedAgents → computedSkills → oauthProviderFactories → providerFactories →\n * wireProtocols → tools),不做任何重排——oauthProviderFactories 必须先于 providerFactories\n * (catalog manifest oauthRef 解析依赖顺序)。\n */\n/** 贡献轴名提取(manifest contributes 的顶层键名,不展开条目明细——G1 定稿边界)。 */\nfunction axisNamesOf(plugin: DiscoveredPlugin): string[] {\n const c = plugin.manifest.contributes\n if (!c || typeof c !== 'object') return []\n return Object.keys(c)\n}\n\nexport class PluginModuleManager {\n /**\n * RFC-303 D5(M7):插件 → 贡献轴名快照缓存。\n * 只在插件 enabled 完成时刷新(reload 差量完成点),两次 enabled 间保持不变——\n * listActivePlugins 的 contributes 字段取此缓存,保证时序稳定(M7 文档\"contributes\n * 时序稳定性\"落地约束)。\n */\n private readonly contributesSnapshot = new Map<string, string[]>()\n /** onDeactivate 超时护城河:对齐 hook emit 的既有模式(hook-registry.ts:207-227),\n * 防止单个插件的 onDeactivate 挂起拖死所有其他插件的卸载流程(RFC-151 D1)。 */\n private static readonly DEACTIVATE_TIMEOUT_MS = 5000\n\n private readonly hooksAxis: HooksAxisHandler\n private readonly monitorsAxis: MonitorsAxisHandler\n private readonly feedbackAxis: FeedbackAxisHandler\n private readonly sigilAxis: SigilAxisHandler\n private readonly computedCommandsAxis: ComputedCommandsAxisHandler\n private readonly computedAgentsAxis: ComputedAgentsAxisHandler\n private readonly computedSkillsAxis: ComputedSkillsAxisHandler\n private readonly oauthProviderAxis: OAuthProviderAxisHandler\n private readonly codeProviderAxis: CodeProviderAxisHandler\n private readonly wireProtocolAxis: WireProtocolAxisHandler\n private readonly toolsAxis: ToolsAxisHandler\n /** RFC-287 M3:services 常驻轴(唯一产物为长生命周期 worker 的轴)。 */\n private readonly servicesAxis: ServicesAxisHandler\n /** 装配顺序即注册/卸载顺序(纯搬迁纪律:与原内联代码轴出现顺序逐字节一致)。 */\n private readonly axisHandlers: readonly PluginAxisHandler[]\n\n /** 本进程内已触发的激活事件(RFC-105 D7)。`onStartup` 恒真,不需放入此集合。 */\n private readonly firedEvents = new Set<string>()\n /** 上一轮因未匹配激活事件而被跳过代码模块加载的插件 id(供 `deferredPluginIds` 观测)。 */\n private deferredIds = new Set<string>()\n /**\n * RFC-151 D1:已加载插件模块追踪表(key=pluginId)。`LoadedPluginModule` 本身不含 manifest,\n * 因此值类型同步保存 manifest 快照供 changed 判定(JSON.stringify 签名比对)。\n * reload() 中 removed/changed 插件在 unregister 循环之前调用 onDeactivate;\n * cleanup() 中对全部已加载插件调用 onDeactivate;处理后清理不再存活的条目。\n */\n private loadedModules = new Map<string, {\n module: LoadedPluginModule\n manifest: DiscoveredPlugin['manifest']\n /** RFC-216 D2:上一轮装载时的三维签名(manifest + 信任裁决指纹 + plugin.ts 文件指纹)。 */\n signature: string\n }>()\n\n constructor(private readonly deps: PluginModuleManagerDeps) {\n this.hooksAxis = new HooksAxisHandler(deps.hookRegistry)\n this.monitorsAxis = new MonitorsAxisHandler(deps.hookRegistry, deps.logger)\n this.feedbackAxis = new FeedbackAxisHandler(deps.feedbackRegistry, deps.logger)\n this.sigilAxis = new SigilAxisHandler(deps.sigilResolverRegistry, deps.pluginInput, deps.logger)\n this.computedCommandsAxis = new ComputedCommandsAxisHandler()\n this.computedAgentsAxis = new ComputedAgentsAxisHandler()\n this.computedSkillsAxis = new ComputedSkillsAxisHandler(deps.skillRegistry)\n // oauthProviderFactories 必须先于 providerFactories 装配(catalog manifest oauthRef 解析依赖)。\n this.oauthProviderAxis = new OAuthProviderAxisHandler(deps.authStore, deps.logger)\n this.codeProviderAxis = new CodeProviderAxisHandler(deps.providerRegistry, deps.modelRegistry, deps.logger)\n this.wireProtocolAxis = new WireProtocolAxisHandler(deps.wireProtocolRegistry, deps.logger)\n this.toolsAxis = new ToolsAxisHandler(deps.toolRegistry, deps.logger)\n // RFC-287 M3:services 轴——bridge/hostCapabilities 复用与 ctx.host 相同的算法\n // (buildHostCapabilities),保证\"进程内 ctx.host\"与\"service ctx.host\"授权判定同源,\n // 不会出现某条路径宽松、另一条严格的分叉。\n this.servicesAxis = new ServicesAxisHandler({\n pool: deps.servicePool ?? getPluginServicePool(),\n logger: deps.logger,\n workspaceDir: deps.workspaceDir,\n getWorkspaceKey: () => deps.getWorkspaceKey?.() ?? 'default',\n // RFC-318 M1:三个能力同源装配——逐字段挑选而非 spread(spread 会把未来新增的\n // 宿主内部字段意外送进 worker 侧能力面)。\n bridgeFor: (pluginId) => {\n const plugin = deps.activePlugins().find((p) => p.id === pluginId)\n if (!plugin) return {}\n const host = buildHostCapabilities(plugin, evaluatePluginTrust(plugin), deps)\n if (!host) return {}\n const bridge: ServiceHostBridge = {}\n if (host.startJob) bridge.startJob = host.startJob\n if (host.readTranscript) bridge.readTranscript = host.readTranscript\n if (host.complete) bridge.complete = host.complete\n if (host.notify) bridge.notify = host.notify\n // 常驻 service 不携带宿主签发的单次会话 InvocationContext;若把 ask 注入此处,\n // 只能猜 active/recent chat 或信任插件自报的 sessionId,都会造成跨会话投递。\n // 因而 service user.ask 永远不注入(RFC-350 fail-closed);后台服务应 notify +\n // 持久化候选,只有将来 agent-bound 调用链才可消费 InteractionTarget。\n return bridge\n },\n hostCapabilitiesFor: (pluginId) => {\n const plugin = deps.activePlugins().find((p) => p.id === pluginId)\n if (!plugin) return []\n const host = buildHostCapabilities(plugin, evaluatePluginTrust(plugin), deps)\n if (!host) return []\n const names: string[] = []\n if (host.startJob) names.push('startJob')\n if (host.readTranscript) names.push('readTranscript')\n if (host.complete) names.push('complete')\n if (host.notify) names.push('notify')\n // See bridgeFor: service workers deliberately never receive user.ask without an\n // agent-bound InvocationContext / trusted InteractionTarget.\n return names\n },\n // RFC-303 M5:scoped persistence——目录构造 + 能力声明判定(fail-closed)。\n storageDirFor: (pluginId, workspaceKey) => {\n const dir = new PluginScopedStorage(pluginId, workspaceKey).baseDir\n // 构造即完成 sanitize + mkdir + realpath 断言;目录给 worker 写面收窄用。\n return dir\n },\n hasStorageCapability: (pluginId) => {\n const plugin = deps.activePlugins().find((p) => p.id === pluginId)\n return Boolean(plugin?.manifest.capabilities?.includes('storage'))\n },\n // RFC-327 D2:schedule 订阅对账端口(惰性——scheduler 在 TUI 启动流程才构造)。\n ...(deps.getSchedulePort ? { getSchedulePort: deps.getSchedulePort } : {}),\n })\n\n this.axisHandlers = [\n this.hooksAxis,\n this.monitorsAxis,\n this.feedbackAxis,\n this.sigilAxis,\n this.computedCommandsAxis,\n this.computedAgentsAxis,\n this.computedSkillsAxis,\n this.oauthProviderAxis,\n this.codeProviderAxis,\n this.wireProtocolAxis,\n this.toolsAxis,\n // RFC-287 M3:追加在末尾——纯搬迁纪律要求既有 11 轴顺序不得重排(oauthProviderFactories\n // 必须先于 providerFactories 等既有依赖),新轴只能末位追加。\n this.servicesAxis,\n ]\n }\n\n /** 上一轮 reload 中因懒激活被跳过代码模块加载的插件 id(骨架观测用,M105-6 前无真实触发源)。 */\n get deferredPluginIds(): ReadonlySet<string> {\n return this.deferredIds\n }\n\n /**\n * RFC-303 D4:向全部已激活 monitor 派发宿主层事件(plugin 启停、settings 变更、tool 增减)。\n * 由宿主装配点(App)在对应事件发射时机调用;observer-only,R5 失败隔离由\n * MonitorsAxisHandler.dispatch 保证。引擎生命周期事件(turn.completed 等)仍走\n * HookTiming 派发(pluginMonitorsToSpecs),不经本通道。\n */\n emitMonitorEvent(event: MonitorEvent): Promise<void> {\n return this.monitorsAxis.dispatch(event)\n }\n\n /**\n * M3:scheduler 就绪后对已启动 services 补跑 schedule 声明对账。\n * 委托 ServicesAxisHandler.reconcileAllSchedules()——详见该方法注释。\n */\n reconcileServiceSchedules(): void {\n this.servicesAxis.reconcileAllSchedules()\n }\n\n /** 已激活 monitor 的插件 id 集合(观测/测试用)。 */\n get monitorPluginIds(): readonly string[] {\n return this.monitorsAxis.monitorPluginIds\n }\n\n /**\n * 触发一个激活事件(如 `onCommand:foo`);若因此有插件从 deferred 转为可激活,\n * 对其重新执行一次完整 `reload()`(简单正确性优先于增量加载,插件数量级小)。\n * 真实触发点(RFC-124 P0-C):`onCommand:<name>`(interactive.ts 命令分发后)、\n * `onView:<panelId>`(extension-plugin.ts 打开插件面板时)、`onProvider:<api>`\n * (interactive-model.ts 模型切换后),均经 `App.firePluginActivationEvent()` 调用本方法。\n */\n async activateOn(\n event: string,\n existingAgentNames: ReadonlySet<string>,\n ): Promise<AgentProfile[]> {\n this.firedEvents.add(event)\n return this.reload(existingAgentNames)\n }\n\n /** computed 插件命令(FileCommand[])。供 cli 命令系统与 App.listFileCommands 并入。 */\n get commands(): FileCommand[] {\n return this.computedCommandsAxis.getComputedCommands()\n }\n\n /**\n * RFC-151 D1:超时护城河 + try/catch 隔离调用单个插件的 onDeactivate。\n * 对齐 hook emit 的既有模式(hook-registry.ts:207-227):Promise.race + 5s 超时 +\n * unref() 防止定时器阻塞进程退出。挂起或抛错只 logger.warn,不阻塞其余插件。\n */\n private async callOnDeactivate(\n pluginId: string,\n onDeactivate: () => void | Promise<void>,\n ): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n onDeactivate(),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(\n new Error(\n `plugin \"${pluginId}\" onDeactivate timed out after ${PluginModuleManager.DEACTIVATE_TIMEOUT_MS}ms`,\n ),\n )\n }, PluginModuleManager.DEACTIVATE_TIMEOUT_MS)\n timer.unref?.()\n }),\n ])\n } catch (err) {\n this.deps.logger.warn(\n { pluginId, err: String(err) },\n 'plugin onDeactivate failed or timed out',\n )\n } finally {\n if (timer !== undefined) clearTimeout(timer)\n }\n }\n\n /** 撤销全部轴上一轮的注册(cleanup() 进程退出路径用)。 */\n private async unregisterAllAxes(): Promise<void> {\n for (const handler of this.axisHandlers) {\n await handler.unregisterAll()\n }\n }\n\n /** RFC-216 D2:撤销**指定插件**在全部轴上一轮的注册(差量 reload 的 removed/changed 分支)。 */\n private async unregisterPluginAxes(pluginId: string): Promise<void> {\n for (const handler of this.axisHandlers) {\n await handler.unregister(pluginId)\n }\n // RFC-318 D-M1-4:插件的 service 已停 → 清空其会话授权白名单与配额计数。\n // **不保留跨启停的授权**:否则插件被禁用期间攒下的 sessionId 会在重新启用后复活,\n // 使\"授权只能由宿主 notify 下发\"的语义出现时间维度的漏洞。放在轴撤销之后(而非\n // ServicesAxisHandler 内)是因为配额是 per-plugin 而非 per-service 的状态,且此处是\n // 「该插件全部贡献已撤销」的唯一汇合点。\n capabilityQuotaOf(this.deps).reset(pluginId)\n }\n\n /**\n * RFC-216 D2:插件三维签名——unchanged 判定的唯一依据,任一维变化即 changed。\n * ① manifest 内容(JSON.stringify,复用 RFC-139 D2 已验证模式);\n * ② 信任裁决指纹(gated + pendingCapabilities 排序串)——能力刚被授予时 manifest 不变,\n * 但 register 的 fail-closed 分支结果不同,必须重装才能生效;\n * ③ plugin.ts / plugin.tsx 文件指纹(mtimeMs:size)——插件开发场景改代码不改 manifest。\n *\n * 指纹边界(RFC-216 对抗评审 P1):不追踪 plugin.ts 的传递依赖文件(import 的 src/*.ts)\n * 变更——对齐 RFC-150 D5「产物存在即用」的既有取舍(findPrebuiltOutfile 同样只比入口),\n * 生产环境代码变更应走 install/dev 路径,本判定不比现状差。\n */\n private pluginSignature(plugin: DiscoveredPlugin): string {\n const verdict = evaluatePluginTrust(plugin)\n const verdictPart = `${verdict.gated ? 'gated' : 'ok'}:${[...verdict.pendingCapabilities].sort().join(',')}`\n let filePart = 'nofile'\n for (const candidate of ['plugin.ts', 'plugin.tsx']) {\n try {\n const st = statSync(join(plugin.dir, candidate))\n filePart = `${candidate}:${st.mtimeMs}:${st.size}`\n break\n } catch {\n /* 下一个候选 */\n }\n }\n return `${JSON.stringify(plugin.manifest)}|${verdictPart}|${filePart}`\n }\n\n /**\n * 重载所有 active 插件的代码模块。先注销上轮 hook/skill(幂等 reload);对每个含 `plugin.ts` 的插件\n * 信任门后编译 + 进程内加载 → `module.hooks` 注册进引擎、`onActivate` 跑、computed 贡献并轴。失败 fail-soft\n * (loadPluginModule 内部 try/catch 返 null),不阻塞 reload。\n *\n * `existingAgentNames`=本轮已有声明式 agent 名(文件/otto/claude),用于 computed agents 去重(插件不夺名 R7,\n * 与 App.reloadPluginModules 等价:每插件按「原有名 + 早前插件已追加名」判重)。返回待追加进\n * App.declarativeAgentProfiles 的 computed agent profiles。\n */\n async reload(existingAgentNames: ReadonlySet<string>): Promise<AgentProfile[]> {\n // RFC-216 D2:三段 diff(removed/changed/added/unchanged)取代全量卸载→重装。\n // 事故背景与声明式轴(PluginProviderManager,RFC-139 D2)同源:全量路径让 unchanged\n // 插件也经历 11 张注册表的 unregister→register 窗口 + esbuild 重编译 + 隔离池重装。\n // unchanged 判定 = 三维签名(见 pluginSignature),任一维变化即 changed。\n const activeList = this.deps.activePlugins()\n const activeById = new Map(activeList.map((p) => [p.id, p]))\n const signatureById = new Map(activeList.map((p) => [p.id, this.pluginSignature(p)]))\n\n // 第一段:removed/changed 判定。onDeactivate 时序(RFC-216 对抗评审 P1 文档化):\n // 先批量跑全部 removed/changed 的 onDeactivate(保持既有\"deactivate 时注册表仍在\"\n // 语义),再逐插件差量撤销——unchanged 插件的注册条目全程不动。\n const toDeactivate: Array<{ pluginId: string; onDeactivate: () => void | Promise<void> }> = []\n const removedIds = new Set<string>()\n const changedIds = new Set<string>()\n for (const [pluginId, entry] of this.loadedModules) {\n const active = activeById.get(pluginId)\n if (!active) {\n // removed:上一轮存在、本轮 activePlugins() 中已无此插件。\n if (entry.module.module.onDeactivate) {\n toDeactivate.push({ pluginId, onDeactivate: entry.module.module.onDeactivate })\n }\n removedIds.add(pluginId)\n } else if (signatureById.get(pluginId) !== entry.signature) {\n // changed:同 id 但三维签名已变(manifest / 信任裁决 / plugin.ts 指纹任一)。\n if (entry.module.module.onDeactivate) {\n toDeactivate.push({ pluginId, onDeactivate: entry.module.module.onDeactivate })\n }\n changedIds.add(pluginId)\n // 不从 map 中删除——新一轮加载会覆盖该条目。\n }\n // unchanged:零动作——不 deactivate、不撤销、不重编译、不 register(RFC-216 R1)。\n }\n for (const { pluginId, onDeactivate } of toDeactivate) {\n await this.callOnDeactivate(pluginId, onDeactivate)\n }\n // 第二段:差量撤销(仅 removed/changed 的桶被清,unchanged 注册条目全程不动)。\n for (const id of removedIds) {\n await this.unregisterPluginAxes(id)\n this.loadedModules.delete(id)\n }\n for (const id of changedIds) {\n await this.unregisterPluginAxes(id)\n }\n\n // computed agents 判重基线每轮更新(getter 惰性判重,见 ComputedAgentsAxisHandler\n // RFC-216 D1 注释);computedCommands 桶由 unregister(pluginId) 差量清理,不再全量 reset。\n this.computedAgentsAxis.setExistingAgentNames(existingAgentNames)\n\n const deferred = new Set<string>()\n\n // 第三段:added/changed 装载(unchanged 跳过,loadedModules 条目原样保留)。\n for (const plugin of this.deps.activePlugins()) {\n const events = resolveActivationEvents(plugin.manifest)\n if (!shouldActivateNow(events, this.firedEvents)) {\n deferred.add(plugin.id)\n continue\n }\n const previous = this.loadedModules.get(plugin.id)\n if (previous && !changedIds.has(plugin.id)) {\n // unchanged:模块与全部轴注册均保留,直接跳过(RFC-216 R1 零抖动)。\n continue\n }\n const ctx = {\n pluginId: plugin.id,\n pluginDir: plugin.dir,\n workspaceDir: this.deps.workspaceDir,\n callMcpTool: (server: string, tool: string, params?: Record<string, unknown>) =>\n this.deps.callMcpTool(server, tool, params),\n // RFC-153 D2:并行写入 per-plugin `.jsonl` 文件(全局 pino 行为保留不变,供集中式\n // debug/OTTO_LOG_FILE 场景),供 `/extension` 详情面板(D4)tail 展示。`appendPluginLog`\n // 内部 fail-soft,不需要额外 try/catch 包裹。\n logger: {\n // RFC-185 D3:debug 档(协议实现的流式诊断)——进全局 pino(受级别/stderr 通道\n // 管控),不进 per-plugin jsonl(面板 tail 只关心 info+,debug 噪音大)。\n debug: (msg: string) => {\n this.deps.logger.debug({ pluginId: plugin.id }, msg)\n },\n info: (msg: string) => {\n this.deps.logger.info({ pluginId: plugin.id }, msg)\n appendPluginLog(plugin.id, 'info', msg)\n },\n warn: (msg: string) => {\n this.deps.logger.warn({ pluginId: plugin.id }, msg)\n appendPluginLog(plugin.id, 'warn', msg)\n },\n error: (msg: string) => {\n this.deps.logger.error({ pluginId: plugin.id }, msg)\n appendPluginLog(plugin.id, 'error', msg)\n },\n },\n // RFC-148 M3:providerFactories 工厂复用其他插件(如 otto-wire-protocols)注册的\n // wire 协议实现的公开通道——插件间无 symbol import,只能经宿主注册表查表。\n getWireProtocol: (wireApi: string) => this.deps.wireProtocolRegistry?.get(wireApi),\n // provider 插件化收尾:复用既有 providerRegistry.resolveAccessKey(内部转发\n // authStore.getCredentialKey),不新增 authStore 依赖字段——ProviderRegistry\n // 本就是 PluginModuleManagerDeps 的既有可选依赖(供 providerFactories 轴注册)。\n // 未注入 providerRegistry 时(如轻量测试装配)resolveProviderCredential 恒返回\n // null,代码式 provider 工厂应据此判空并抛出\"缺凭据\"错误(同声明式轴无凭据时的\n // 既有错误纹理,不静默用空字符串糊弄)。\n //\n // 安全边界修复(终局 review 发现的 P0):本函数此前对任意 api 字符串无差别解析\n // AuthStore 全局凭据表——任何插件(甚至只声明 hooks/monitor 能力、未声明\n // capabilities:[\"provider\"] 的插件)在其工厂函数体内都能调用\n // `ctx.resolveProviderCredential('github-copilot')` 等任意 provider id 窃取\n // 其他插件/内置 provider 的已存储凭据(跨插件凭据隔离被绕过)。修复:只放行该\n // 插件自身 manifest 声明的 `codeProviderIds`(裸 id 白名单)或以自身 `<pluginId>:`\n // 为前缀的命名空间 api(其代码式 provider 工厂唯一可能注册到的 api 空间)——\n // 其余一律返回 null,不触达 AuthStore。\n resolveProviderCredential: ((): (api: string) => Promise<string | null> => {\n const isOwnedApi = ownedApiChecker(plugin)\n return (api: string) => {\n if (!isOwnedApi(api)) return Promise.resolve(null)\n return this.deps.providerRegistry?.resolveAccessKey(api as Api) ?? Promise.resolve(null)\n }\n })(),\n // RFC-148 M3:resolveProviderCredential 的姊妹方法——返回完整 ResolvedAuth(含\n // mode/beta),供需要区分 api_key/oauth 模式的代码式 provider 工厂使用(如\n // plugin-anthropic 的 anthropic-messages customizeRequest 定制器)。跨插件隔离\n // 规则与 resolveProviderCredential 共用同一份 ownedApiChecker 判定。\n resolveProviderAuth: ((): (api: string) => ReturnType<PluginContext['resolveProviderAuth']> => {\n const isOwnedApi = ownedApiChecker(plugin)\n return (api: string) => {\n if (!isOwnedApi(api)) return Promise.resolve(null)\n return this.deps.providerRegistry?.resolveAuth(api) ?? Promise.resolve(null)\n }\n })(),\n // RFC-287 D2:受控宿主能力面(per-capability fail-closed,见 buildHostCapabilities)。\n host: buildHostCapabilities(plugin, evaluatePluginTrust(plugin), this.deps),\n // RFC-303 D5(M7):插件间互发现只读快照(含自己)。\n // contributes 取该插件\"上一次 enabled 完成时刻\"的贡献轴快照缓存(this.contributesSnapshot\n // ——两次 enabled 间 reload 差量变化不反映到返回值,避免高频轮询构成时序侦察面)。\n listActivePlugins: () =>\n this.deps.activePlugins().map((p) => ({\n id: p.id,\n version: p.manifest.version,\n capabilities: p.manifest.capabilities ?? [],\n contributes: this.contributesSnapshot.get(p.id) ?? axisNamesOf(p),\n })),\n }\n const useIsolated = this.deps.useIsolatedLoading !== false && shouldUseIsolatedLoading(plugin)\n // RFC-287 D2:隔离路径的 ctx 经 postMessage 传递,只能携带纯数据——`host` 的函数\n // 字段传不过去(同 sigilProviders 在隔离路径的既有失效先例,module-loader-isolated.ts\n // 已为其加 warn)。此处显式告警,把\"声明了 agent.dispatch 却拿不到句柄\"从静默失效\n // 变成可诊断信号;反向 RPC 与 M3 的 services 常驻轴一并设计(契约不变,只加传输)。\n // RFC-303 M4:隔离路径 host 反向 RPC 已打通(module-loader-isolated.ts 经\n // setHostCallDispatcher 注入派发器)——此前的\"隔离路径 host 不代理\"warn 已不适用,\n // 移除(RFC-287 D2 的 warn 前提已随 M4 消除)。\n const loaded = useIsolated\n ? await loadPluginModuleIsolated(plugin, ctx, undefined, this.deps.onPluginLoadError)\n : await loadPluginModule(plugin, ctx, this.deps.onPluginLoadError)\n if (!loaded) continue\n\n // RFC-124 C1/C2 修复:module-loader.ts 的 isPluginTrusted 只门控「plugin.ts 是否执行」\n // (布尔信任),不检查 per-capability 授权状态——providerFactories/tools 两个高危代码轴\n // 此前完全没有 pendingCapabilities 检查,导致「已信任插件后续版本新增 provider/tools\n // 声明」时静默获得新特权(RFC-105 D8/R9 F2 设计意图未被此路径执行)。gated 兜底同样\n // 显式核对(防御纵深,与 renderer/menu/statusItems 等轴的既有风格一致)。\n const verdict = evaluatePluginTrust(plugin)\n if (verdict.gated) {\n this.deps.logger.warn(\n { pluginId: plugin.id },\n 'project plugin not trusted, skipping code axes (fail-closed, RFC-124 C1)',\n )\n continue\n }\n\n // RFC-303 M5:scoped persistence——只对声明了 storage 能力的插件实例化(fail-closed,\n // 未声明能力时 axisCtx.storage 恒为 undefined,工具/服务侧 ctx.storage 随之为\n // undefined)。构造失败(如 pluginId 非法/realpath 逃逸断言触发)降级为 undefined,\n // 不阻断该插件其余轴的注册。\n let storage: PluginScopedStorage | undefined\n if (plugin.manifest.capabilities?.includes('storage')) {\n try {\n const workspaceKey = this.deps.getWorkspaceKey?.() ?? DEFAULT_WORKSPACE_KEY\n storage = new PluginScopedStorage(plugin.id, workspaceKey)\n } catch (err) {\n this.deps.logger.warn(\n { pluginId: plugin.id, err: String(err) },\n 'failed to construct scoped storage for plugin, storage capability unavailable',\n )\n }\n }\n const axisCtx: PluginAxisContext = { plugin, verdict, loaded, host: buildHostCapabilities(plugin, verdict, this.deps), workspaceDir: this.deps.workspaceDir, storage }\n for (const handler of this.axisHandlers) {\n await handler.register(axisCtx)\n }\n\n if (loaded.module.onActivate) {\n try {\n await loaded.module.onActivate()\n } catch (err) {\n this.deps.logger.warn(\n { pluginId: plugin.id, err: String(err) },\n 'plugin onActivate threw',\n )\n }\n }\n // RFC-151 D1:加载成功后追踪插件模块与 manifest 快照,供下一轮 reload 做 removed/changed 判定。\n // RFC-216 D2:同步保存装载时刻的三维签名(供下一轮 unchanged 判定)。\n // RFC-303 D5(M7):enabled 完成 → 刷新该插件 contributes 轴名快照(时序稳定约束:\n // 两次 enabled 之间 listActivePlugins 的 contributes 保持不变)。\n this.contributesSnapshot.set(plugin.id, axisNamesOf(plugin))\n this.loadedModules.set(plugin.id, {\n module: loaded,\n manifest: plugin.manifest,\n signature: signatureById.get(plugin.id) ?? this.pluginSignature(plugin),\n })\n }\n this.deferredIds = deferred\n return this.computedAgentsAxis.getAppendedAgents()\n }\n\n /** stop 清理:注销插件 hook + computed skill + provider + tools + firedEvents(防泄漏进 shared registry;provider 含 dispose)。 */\n async cleanup(): Promise<void> {\n // RFC-151 D1:进程退出前对全部已加载插件调用 onDeactivate(超时护城河 + try/catch 隔离)。\n for (const [pluginId, entry] of this.loadedModules) {\n if (entry.module.module.onDeactivate) {\n await this.callOnDeactivate(pluginId, entry.module.module.onDeactivate)\n }\n }\n this.loadedModules.clear()\n\n // 注意:cleanup() 不调用 computedCommandsAxis.resetForReload()——原实现的不对称行为\n // (只有 reload() 每轮开头清空 computedCommands,cleanup() 从不清空),见该 handler 注释。\n await this.unregisterAllAxes()\n // RFC-318 D-M1-4:全部 service 已停 → 清空全部插件的会话授权与配额(同 unregisterPluginAxes\n // 的理由;此处走全量路径,逐插件 reset)。\n const quota = capabilityQuotaOf(this.deps)\n for (const plugin of this.deps.activePlugins()) quota.reset(plugin.id)\n // RFC-105 M-N1 T-N1e:清空已触发激活事件,防止 reload 周期间历史事件残留误判(独立评审 2026-07-04 发现)\n this.firedEvents.clear()\n }\n}\n","import { existsSync, mkdirSync, mkdtempSync, readdirSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport type { buildSync as EsbuildBuildSync } from 'esbuild'\nimport { createLogger } from '@x-otto/shared'\n\n/**\n * panel-compiler.ts —— 插件面板编译器(RFC-082 M082-03-03,编译收归 otto)。\n *\n * 把插件 `panels/*.tsx`(Ink/React TSX)用 esbuild 编译为 `panels-dist/*.js`(CJS),\n * 供 TUI `React.lazy(() => import())` 加载(RFC-081)。编译规格对齐 RFC-081 §3.3:\n * `format: cjs` / `platform: node` / `external: react+ink` / `jsx: transform` /\n * banner 注入 `globalThis.__ottoPanelRuntime`(usePluginData hook 由 App 启动时 installPanelRuntime 注入)。\n *\n * 历史背景(RFC-082 G1):RFC-081 §3.3 原设想插件 postinstall 自己 `require('esbuild')` 编译,\n * 但 esbuild 在仓里未 hoist、不可解析 → 那条路实际不通。本模块把编译收归 otto:esbuild 作 `@x-otto/coding`\n * 直接依赖(§3.3 推荐),由 `otto extension build`/`otto extension dev` 与安装钩子统一调用。\n */\n\nconst logger = createLogger('@x-otto/coding:panel-compiler')\n\nconst requireModule = createRequire(import.meta.url)\n\n/** banner:面板编译产物经此引用 App 注入的全局运行时(RFC-081 §3.3,含防御性回退避免未注入时模块加载即崩)。 */\nconst PANEL_RUNTIME_BANNER = 'const { usePluginData } = globalThis.__ottoPanelRuntime || {};'\n\n/**\n * banner:面板关闭请求 + footer 提示通道(RFC-166 D9 → RFC-167 精简 → 真实用户反馈修复)——\n * 面板现在独立成全屏 dialog(`PluginPanelDialog`),挂载期间天然拥有 100% 键盘,\n * `claimPanelFocus()`/`releasePanelFocus()` 保留为 no-op 桩(兼容仍调用它们的存量面板代码,\n * 零副作用);`usePanelFocusClaimed()` 恒返回 `true`;`requestPanelClose()` 在\"当前层级\n * 无处可退\"时把关闭请求发给宿主 `PluginPanelDialog`;`setPanelHint(text | undefined)` 把\n * 当前层级的操作提示文案上报给宿主,替换 `PluginPanelDialog` 固定的 \"Esc 关闭\" footer——\n * 面板不应再自行渲染提示行(会与宿主 footer 重复堆叠,见 `panel-focus-bridge.ts` 头注释)。\n * 防御性回退(未注入时给 no-op/false)避免面板在非独立 dialog 上下文(如未来其他宿主)\n * 加载时崩溃。见 `packages/tui/src/panel-focus-bridge.ts` + `panel-globals.ts`。\n */\nconst PANEL_FOCUS_BRIDGE_BANNER =\n 'const { claim: claimPanelFocus, release: releasePanelFocus, usePanelFocusClaimed, requestClose: requestPanelClose, setHint: setPanelHint } = ' +\n 'globalThis.__ottoPanelFocusBridge || { claim(){}, release(){}, usePanelFocusClaimed: () => true, requestClose(){}, setHint(){} };'\n\n/**\n * banner:设计 token 访问器(RFC-166 D10)——面板经此调用 `getTheme()`/`getFigures()`\n * 取宿主 otto TUI 的固定语义色(`THEME`,RFC-205 起单一固定配色,不再随主题切换变化)与\n * 统一符号(`FIGURES`),解决面板内联硬编码颜色字符串('green'/'red')与裸 Unicode 符号与\n * 宿主设计语言脱节的问题。防御性回退值与 `theme.ts` 的 `THEME` 常量逐角色一致(未注入时\n * 给同值兜底,避免面板在非 TUI 宿主上下文加载时崩溃)。见 `panel-globals.ts`。\n */\nconst PANEL_THEME_BANNER =\n 'const { getTheme, getFigures } = globalThis.__ottoPanelTheme || { ' +\n \"getTheme: () => ({ text: undefined, inactive: '#9498B8', secondary: '#C8CBDB', success: '#7EE095', error: '#FF9592', warning: '#FFC966', special: '#DD78C9', selection: '#4D7CFF', focusBorder: '#4D7CFF', panelBorder: '#9498B8' }), \" +\n \"getFigures: () => ({ check: '✓', cross: '✗', pointer: '▶', bullet: '●' }) };\"\n\n/**\n * banner:间距/宽度裕量常量(终局 review endgame-2026-07-23 补齐)——面板经此引用\n * `SPACING`/`LAYOUT`,取代此前只能靠 `getTheme()`/`getFigures()` 却唯独漏了间距 token\n * 的缺口(面板全部只能写裸间距数字 `marginTop={1}` 等)。两者均为 `packages/tui/src/theme.ts`\n * 的编译期静态常量(非 live-binding,随主题切换不变),防御性回退值与其逐字段一致。\n */\nconst PANEL_SPACING_BANNER =\n 'const { SPACING, LAYOUT } = globalThis.__ottoPanelSpacing || { ' +\n 'SPACING: { none: 0, tight: 1, section: 2, loose: 4 }, ' +\n 'LAYOUT: { contentInset: 4, borderInset: 2 } };'\n\n/**\n * banner:i18n 翻译函数(面板国际化)——面板经 `t(key)` 取宿主 i18next 实例的翻译,\n * 与 TUI 内部组件同一份文案真源(`resources/en.ts`/`zh.ts`)。防御性回退:未注入时\n * `t` 直接返回 key(英文兜底),面板不崩。\n */\nconst PANEL_I18N_BANNER =\n \"const { t: t, i18next: panelI18next } = globalThis.__ottoPanelI18n || { t: (k) => k, i18next: { language: 'en' } };\"\n\n/**\n * banner:面板后端调用桥(RFC-165 M2 D5/D6,M3 修正注入源)——面板经此调用\n * `usePanelBackend()` 发起 `backend/call`/`backend/cancel`、订阅 `backend/state` 全量\n * 快照推送、读取连接四态(`connecting`/`ready`/`crashed`/`disabled`)。防御性回退(未\n * 注入时给一个恒返回 `status:'disabled'`、`call` reject 的桩实现)避免面板在非 TUI 宿主\n * 上下文加载时崩溃。注入源是 `globalThis.__ottoPanelBackendRuntime`(由\n * `packages/coding/src/plugin/panel-backend-runtime.ts` 的 `installPanelBackendRuntime`\n * 在 App 启动时注入,对齐 `__ottoPanelRuntime`/`usePluginData` 既有模式——`usePanelBackend`\n * 定义在 `packages/coding`,不在 `packages/tui`,因为 `app.ts` 不能反向依赖 `@x-otto/tui`)。\n */\nconst PANEL_BACKEND_BANNER =\n 'const { usePanelBackend } = globalThis.__ottoPanelBackendRuntime || { ' +\n \"usePanelBackend: () => ({ call: () => Promise.reject(new Error('otto panel: backend runtime not installed')), cancel(){}, state: undefined, status: 'disabled' }) };\"\n\n/**\n * react/ink shim 源(RFC-082):面板的 `import … from 'react'|'ink'` 经 esbuild alias 到这两个\n * 文件,运行时从 globalThis 取 **TUI 进程的同一个 React/Ink 实例**(见 tui/panel-globals.ts)。\n * 这样面板产物完全自包含(无 external、无磁盘 resolution),且 hook 跨边界单实例生效。\n * 具名导出须枚举(ESM 不能 `export *` 一个运行时值);覆盖面板常用的 React hook + Ink 组件。\n */\nconst REACT_SHIM_SRC = `const R = globalThis.React\nif (!R) throw new Error('otto panel: globalThis.React 未注入(TUI 须在加载面板前 import panel-globals)')\nexport default R\nexport const useState = R.useState, useEffect = R.useEffect, useCallback = R.useCallback,\n useMemo = R.useMemo, useRef = R.useRef, useContext = R.useContext, useReducer = R.useReducer,\n useLayoutEffect = R.useLayoutEffect, createElement = R.createElement, Fragment = R.Fragment,\n memo = R.memo, forwardRef = R.forwardRef, createContext = R.createContext, Children = R.Children,\n cloneElement = R.cloneElement, isValidElement = R.isValidElement\n`\nconst INK_SHIM_SRC = `const I = globalThis.__ottoPanelInk\nif (!I) throw new Error('otto panel: globalThis.__ottoPanelInk 未注入(TUI 须在加载面板前 import panel-globals)')\nexport const Box = I.Box, Text = I.Text, Newline = I.Newline, Spacer = I.Spacer, Static = I.Static,\n Transform = I.Transform, useApp = I.useApp, useInput = I.useInput, useStdin = I.useStdin,\n useStdout = I.useStdout, useStderr = I.useStderr, useFocus = I.useFocus,\n useFocusManager = I.useFocusManager, measureElement = I.measureElement, render = I.render\n`\n\n/**\n * @x-otto/tui shim:面板\n * `import { Tabs, DataList, Button, GroupDivider, ScrollIndicatorAbove, ScrollIndicatorBelow }\n * from '@x-otto/tui'` → 从 globalThis 取 TUI 进程的单实例组件(非重新实现,与 host 共用同一份\n * `THEME`/`SPACING` live-binding 渲染逻辑)。RFC-166 D10 后续(extension 默认详情面板 +\n * browser-use host 面板迁移)新增 `DataList`——插件面板展示「分组键值信息」(如 MCP 启动\n * 配置/检查清单)不必再手写 `<Box><Text bold>` 分组标题 + 手动 label/value 双色拼接,直接\n * 复用宿主 `/about` 同款组件。真实用户反馈修复新增 `Button`——插件面板的快捷入口/操作按钮\n * (如 miniprogram-use StatusView 的\"前往登录 →\")改用宿主统一的 variant/color 正交设计,\n * 不再手写 `<Text color={...}>` 拼焦点态;同批新增 `GroupDivider`——插件面板的分组标题\n * (如 StatusView 的\"基本配置\"/\"登录状态\")改用宿主统一的「大写 + bold + inactive」视觉,\n * 不再各自手写 `<Text bold color={theme.secondary}>`。真实用户反馈修复(\"使用 scroll view\")\n * 新增 `ScrollIndicatorAbove`/`ScrollIndicatorBelow`——mobile-use 设备列表的\"▲/▼ 更多\"提示行\n * 改用宿主统一组件(与 FileContentViewer 等 6 处宿主消费方同款渲染),不再手写裸中文字面量。\n */\nconst TUI_SHIM_SRC = `const T = globalThis.__ottoPanelTui\nif (!T) throw new Error('otto panel: globalThis.__ottoPanelTui 未注入(TUI 须在加载面板前 import panel-globals)')\nexport const Tabs = T.Tabs\nexport const DataList = T.DataList\nexport const Button = T.Button\nexport const GroupDivider = T.GroupDivider\nexport const ScrollIndicatorAbove = T.ScrollIndicatorAbove\nexport const ScrollIndicatorBelow = T.ScrollIndicatorBelow\nexport const Tabable = T.Tabable\nexport const useTabable = T.useTabable\nexport const TabScope = T.TabScope\n`\n\nexport interface CompilePanelsResult {\n /** 已产出的相对路径(如 `panels-dist/status.js`)。 */\n compiled: string[]\n /** 单个面板的编译错误(entry → message);不阻塞其余面板(fail-soft)。 */\n errors: { entry: string; message: string }[]\n /** 无 `panels/` 目录或无 `*.tsx` → true。 */\n skipped: boolean\n}\n\n/**\n * 编译插件目录下所有 `panels/*.tsx` → `panels-dist/*.js`。\n * fail-soft:单个面板编译失败记入 errors、继续其余;无面板目录/文件 → skipped。\n */\nexport function compilePluginPanels(pluginDir: string): CompilePanelsResult {\n const panelsDir = join(pluginDir, 'panels')\n if (!existsSync(panelsDir)) return { compiled: [], errors: [], skipped: true }\n\n let entries: string[]\n try {\n entries = readdirSync(panelsDir).filter((f) => f.endsWith('.tsx'))\n } catch {\n return { compiled: [], errors: [], skipped: true }\n }\n if (entries.length === 0) return { compiled: [], errors: [], skipped: true }\n\n const outDir = join(pluginDir, 'panels-dist')\n mkdirSync(outDir, { recursive: true })\n\n // 写一次 react/ink/tui shim 到临时目录,供 esbuild alias 指向(见 *_SHIM_SRC 注释)。\n const shimDir = mkdtempSync(join(tmpdir(), 'otto-panel-shims-'))\n const reactShim = join(shimDir, 'react-shim.js')\n const inkShim = join(shimDir, 'ink-shim.js')\n const tuiShim = join(shimDir, 'tui-shim.js')\n writeFileSync(reactShim, REACT_SHIM_SRC)\n writeFileSync(inkShim, INK_SHIM_SRC)\n writeFileSync(tuiShim, TUI_SHIM_SRC)\n\n const compiled: string[] = []\n const errors: { entry: string; message: string }[] = []\n // 惰性加载 esbuild(RFC 启动开销优化):正常启动读取 `panels-dist/` 预编译产物,不进本函数\n // ——把 esbuild 的 require 收进函数体,使编译器仅在真正现场编译面板时才载入内存。esbuild 是\n // CJS 包(无 exports 字段),用 `requireModule`(createRequire)同步取,与 buildSync 同步语义一致。\n const { buildSync } = requireModule('esbuild') as { buildSync: typeof EsbuildBuildSync }\n for (const entry of entries) {\n // 产物用 .mjs:必须 ESM 格式——ink v7 是 ESM-with-top-level-await(yoga TLA),\n // CJS `require(\"ink\")` 在运行时抛「require() cannot be used on an ESM graph with TLA」,\n // 即任何真实 ink 面板都加载即崩。改 ESM 后 TUI 的 `import()` 走 ESM 图、正确 await ink 的 TLA。\n const out = entry.replace(/\\.tsx$/, '.mjs')\n try {\n buildSync({\n entryPoints: [join(panelsDir, entry)],\n outfile: join(outDir, out),\n // bundle:true 是用 external 的前提(RFC-081 §3.3 的 bundle:false+external 是非法配置,已修正);\n // 同时把面板的本地 helper import 打进单文件,react/ink 留作 import 由 TUI 提供(external + ESM\n // → 产物用 `import … from \"ink\"`,运行时复用 TUI 进程的 react/ink 实例,避免多实例 hook 失效)。\n bundle: true,\n format: 'esm',\n platform: 'node',\n // react/ink/@x-otto/tui 不 external 而是 alias 到 shim:产物自包含(无运行时磁盘解析),\n // shim 从 globalThis 取 TUI 的单实例(见 *_SHIM_SRC + tui/panel-globals.ts)。\n alias: { react: reactShim, ink: inkShim, '@x-otto/tui': tuiShim },\n jsx: 'transform',\n banner: {\n js:\n PANEL_RUNTIME_BANNER +\n PANEL_FOCUS_BRIDGE_BANNER +\n PANEL_THEME_BANNER +\n PANEL_SPACING_BANNER +\n PANEL_I18N_BANNER +\n PANEL_BACKEND_BANNER,\n },\n logLevel: 'silent',\n })\n compiled.push(join('panels-dist', out))\n } catch (err) {\n errors.push({ entry, message: (err as Error).message })\n logger.warn({ pluginDir, entry, err: String(err) }, 'panel compile failed')\n }\n }\n logger.info({ pluginDir, compiled: compiled.length, errors: errors.length }, 'compiled plugin panels')\n return { compiled, errors, skipped: false }\n}\n","import {\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n readlinkSync,\n rmSync,\n symlinkSync,\n unlinkSync,\n} from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport { OTTO_HOME, findWorkspaceRoot } from '@x-otto/env'\nimport { parsePluginManifest, PLUGIN_MANIFEST_FILENAME } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\nimport { compilePluginModule } from './module-loader'\nimport { compilePluginPanels } from './panel-compiler'\n\nconst logger = createLogger('@x-otto/coding:plugin-install')\n\n/**\n * install-plugin.ts —— **通用**插件安装:把任何自带 `otto-plugin.json` 的 npm 包物化成 otto 插件。\n *\n * 宿主对具体插件零认知——只认 `otto-plugin.json`(RFC-044 自描述)。解析包目录 → 校验 manifest →\n * 软链进 `<ottoDir>/plugins/<id>`(默认用户级,永远受信任)。软链保留包内**全部贡献面**\n * (commands/agents/skills/panels/.mcp.json),且 `.mcp.json` 的相对 server 路径由\n * `resolvePluginMcpServers` 按插件目录解析成绝对路径——故无需装机拷贝/改写。\n *\n * browser-use 只是「第一个自带插件定义的扩展」,经此通用路径安装(CLI 提供友好别名)。\n *\n * RFC-150 D5:安装时编译(终局——生产环境不应在启动热路径跑编译器)。软链就位后立即编译\n * `plugin.ts`(代码模块)与 `panels/*.tsx`(面板),产物落 `plugin-dist/`/`panels-dist/`——\n * `loadPluginModule()`(module-loader.ts)启动时优先读取这些产物(\"存在即用\"),不再默认\n * 每次启动都重新 esbuild。编译失败**不阻断安装**(fail-soft,插件目录已软链就位是主要\n * 副作用,符合 RFC-044 自描述哲学——宿主不应因为编译失败就认为整个安装失败)但必须\n * **显式可见**(RFC-150 D5 决策:module-loader.ts 现有的\"编译失败 fail-soft 吞掉\"模式\n * 曾导致真实生产 bug 长期不可见,安装期是暴露编译错误的最佳时机——用户此刻正盯着终端,\n * 而不是数天后启动 otto 时才在 debug 日志里发现)。返回值新增 `compileWarnings` 字段,\n * 调用方(CLI/TUI)据此向用户呈现警告,不静默。\n */\nfunction compilePluginAfterInstall(pluginDir: string): Promise<string[]> {\n const warnings: string[] = []\n const compileAll = async (): Promise<void> => {\n try {\n const outfile = await compilePluginModule(pluginDir)\n if (outfile) logger.info({ pluginDir, outfile }, 'plugin code module compiled at install time')\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n logger.warn({ pluginDir, err: msg }, 'plugin.ts compile failed at install time (fail-soft, will retry at load time)')\n warnings.push(`plugin.ts 编译失败:${msg}(不影响安装,otto 启动时会自动重试编译)`)\n }\n try {\n const { errors, skipped } = compilePluginPanels(pluginDir)\n if (!skipped && errors.length > 0) {\n for (const e of errors) {\n logger.warn({ pluginDir, entry: e.entry, err: e.message }, 'plugin panel compile failed at install time')\n warnings.push(`面板 ${e.entry} 编译失败:${e.message}`)\n }\n }\n } catch (err) {\n // compilePluginPanels 本身是同步且已 fail-soft(单面板失败计入 errors),此 catch\n // 只兜途中意外抛出(如 panels/ 目录权限问题),防止安装流程本身被编译异常打断。\n const msg = err instanceof Error ? err.message : String(err)\n logger.warn({ pluginDir, err: msg }, 'plugin panel compilation crashed unexpectedly at install time')\n warnings.push(`面板编译异常:${msg}`)\n }\n }\n return compileAll().then(() => warnings)\n}\n\nexport interface InstallPluginResult {\n /** 插件 id(来自包内 otto-plugin.json)。 */\n id: string\n /** 解析到的包目录(软链目标)。 */\n packageDir: string\n /** 安装位置 `<ottoDir>/plugins/<id>`(软链)。 */\n dir: string\n /** 首次创建(true)还是刷新既有安装(false)。 */\n created: boolean\n /**\n * RFC-150 D5:安装时编译产生的非致命警告(plugin.ts/面板编译失败)。空数组 = 编译\n * 全部成功或该插件无需编译内容(纯声明式插件)。调用方应向用户展示这些警告,不静默。\n */\n compileWarnings: string[]\n /**\n * 插件声明的版本号(来自 `otto-plugin.json` manifest,可选字段)(RFC-219 M1)。\n * 供调用方写入 `installed_plugins` 清单的 version 字段,供审计展示用途。\n */\n version?: string\n}\n\nexport interface InstallPluginFromPackageOptions {\n /** 包名或包内路径(`createRequire` 可解析),如 `@x-otto/plugin-browser-use-mcp`。 */\n pkg: string\n /** 插件根所在 otto 目录。默认 `OTTO_HOME`(用户级,自动受信任)。 */\n ottoDir?: string\n /** 解析包的基准目录(默认 `process.cwd()`);缺失时回退宿主自身位置(workspace 依赖)。 */\n cwd?: string\n}\n\nfunction isSymlink(p: string): boolean {\n try {\n return lstatSync(p).isSymbolicLink()\n } catch {\n return false\n }\n}\n\n/**\n * `findWorkspaceRoot` re-export(RFC-130 终局审核后收敛到 `@x-otto/env` 单一实现,\n * 消除此前与 `@x-otto/plugin/discovery.ts` 的 copy-paste 重复)。此处保留 re-export\n * 是为了不破坏既有消费方的 import 路径(`@x-otto/coding` 导出面)。\n */\nexport { findWorkspaceRoot } from '@x-otto/env'\n\n/**\n * 解析 `pnpm-workspace.yaml` 里 `packages:` 数组的 glob 模式列表(本仓格式简单,\n * 只有单层 `dir/*` 与不含通配符的裸路径两种形态,无需完整 YAML 解析器/新增依赖,\n * 手写行级解析足够、避免为这一处引入 `yaml`/`js-yaml` 包)。\n *\n * 对抗审核 Finding 1 修复(CRITICAL):原始实现对 `packages:` 行使用精确 `===` 匹配,\n * 且列表项正则用 `$` 锚点——这两处都会在合法 YAML 注释面前静默失败(如 `packages:\n * # monorepo packages` 或 `- 'dir/*' # comment`),产生空 glob 列表且无任何错误提示。\n * 修复:① `packages:` 改成前缀匹配(`startsWith`)容忍行尾注释;② 列表项正则去掉\n * `$` 并加上可选行尾注释剥离(`(?:\\s+#.*)?$`)——只处理 block-style `- xxx` 格式,\n * 不尝试支持 flow-style `packages: ['a','b']`(本仓从未使用,且真遇到此格式时解析器\n * 会产出空数组,导致后续 `installPluginFromPackage` 报\"无法解析包\",属于明确的错误\n * 信号而非静默缺失数据——F1 关键修复是消除\"静默失败\",而非支持所有 YAML 方言)。\n */\nfunction parseWorkspaceGlobs(workspaceYamlPath: string): string[] {\n const content = readFileSync(workspaceYamlPath, 'utf8')\n const globs: string[] = []\n let inPackagesList = false\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n // 只处理 block-style `packages:`(本仓真实格式);前缀匹配容忍 `packages: # ...`\n if (trimmed.startsWith('packages:')) {\n inPackagesList = true\n continue\n }\n if (inPackagesList) {\n const match = trimmed.match(/^-\\s*['\"]?([^'\"]+)['\"]?(?:\\s+#.*)?$/)\n if (match?.[1]) {\n globs.push(match[1])\n continue\n }\n // 非 `- xxx` 格式的行(新的顶层 key 或空行)视为列表结束。\n if (trimmed.length > 0 && !trimmed.startsWith('-')) {\n inPackagesList = false\n }\n }\n }\n return globs\n}\n\n/**\n * 展开单层 glob(`dir/*` 或裸路径,本仓 `pnpm-workspace.yaml` 现有格式无需支持\n * `**` 深层递归)为真实存在的目录列表。\n *\n * 对抗审核 Finding 2 修复:对 `glob` 做路径穿越防御——虽然当前 glob 值来源于仓库\n * 自身的 `pnpm-workspace.yaml`(受信任),但如果此函数未来被复用到其他上下文或\n * yaml 被意外篡改,`join()` 的规范化仍可能产生脱离 `root` 的结果。用 `resolve()`\n * 规范路径后强制检查 `startsWith(root)`,拒绝穿越结果。\n */\nfunction expandWorkspaceGlob(root: string, glob: string): string[] {\n const sep = '/'\n const candidate = glob.endsWith('/*') ? glob.slice(0, -2) : glob\n const resolved = resolve(root, candidate)\n // 拒绝路径穿越(`../../etc` 被 resolve 到 `/etc`,不满足 startsWith(root))\n if (!resolved.startsWith(root.endsWith(sep) ? root : root + sep) && resolved !== root) {\n return []\n }\n if (!glob.endsWith('/*')) {\n return existsSync(resolved) ? [resolved] : []\n }\n if (!existsSync(resolved)) {\n return []\n }\n try {\n return readdirSync(resolved, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => join(resolved, entry.name))\n } catch {\n return []\n }\n}\n\n/**\n * 对抗审核发现的真实缺陷修复:`workspace 位置` fallback(见下方 `resolvePackageDir`)\n * 曾经依赖 Node `createRequire().resolve()` 隐式假设\"包能被从 @x-otto/coding 位置解析\n * 到\"——这要求 `node_modules` 里存在软链,而软链只在 `package.json` 声明依赖时由\n * pnpm 可靠创建。首方扩展(browser-use/mobile-use 的 mcp 包)为保持\"宿主对具体\n * 插件零认知\"的架构原则,故意不被 `packages/coding` 声明依赖——导致该 fallback\n * 完全依赖不受 lockfile 保护的历史残留软链,真删除验证后证实:全新 clone+\n * `pnpm install` 环境下 `otto extension install browser-use/mobile-use` 会失败。\n *\n * 修复:不依赖 node_modules 解析,改为直接按 `pnpm-workspace.yaml` 声明的 glob\n * 扫描真实目录、读 `package.json` 的 `name` 字段精确匹配——不需要任何依赖声明,\n * 新增首方扩展只要落在已声明的 workspace glob 范围内就自动可被发现,不会重蹈\n * \"记得给每个新扩展手动加路径解析用途依赖\"这种容易被遗忘的模式。\n *\n * RFC-163 M3 补充修复:`pkg` 参数按 `package.json.name` 匹配是原始设计(npm 包名\n * 命名空间),但 `manifest.dependsOn`(otto-plugin.json 命名空间,如 `otto-wire-protocols`)\n * 与 npm 包名(如 `@x-otto/plugin-otto-wire-protocols`)不是同一字符串——两者按约定只是\"加/去\n * `@x-otto/` 前缀\"的关系,但这是一条经验规律不是保证,不应在调用方硬编码转换。改为在本\n * 函数内按 `package.json.name` 匹配失败后,兜底尝试读候选目录的 `otto-plugin.json`\n * 按 `manifest.id` 匹配——两种匹配都是通用的、厂商中立的字符串比对(不认识任何具体\n * 插件身份,只是把\"能查找的命名空间\"从单一的 npm 包名扩展到\"npm 包名或插件 id 二选一\"),\n * 使 `resolveViaWorkspaceGlob`/`resolvePackageDir` 对 `missingDeps` 产出的插件 id\n * 字符串也能直接生效,不需要调用方做任何前缀猜测。\n */\nfunction resolveViaWorkspaceGlob(pkg: string, searchStartDir: string): string | undefined {\n const root = findWorkspaceRoot(searchStartDir)\n if (!root) {\n return undefined\n }\n const globs = parseWorkspaceGlobs(join(root, 'pnpm-workspace.yaml'))\n for (const glob of globs) {\n for (const dir of expandWorkspaceGlob(root, glob)) {\n const manifestPath = join(dir, 'package.json')\n if (!existsSync(manifestPath)) {\n continue\n }\n try {\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as { name?: string }\n if (manifest.name === pkg) {\n return dir\n }\n } catch {\n /* 忽略非法 package.json,继续扫描其余候选目录 */\n }\n // 兜底:按 otto-plugin.json 的 id 匹配(RFC-163 M3,见上方函数头注释)。\n const ottoManifestPath = join(dir, PLUGIN_MANIFEST_FILENAME)\n if (!existsSync(ottoManifestPath)) {\n continue\n }\n try {\n const ottoManifest = parsePluginManifest(readFileSync(ottoManifestPath, 'utf8'), ottoManifestPath)\n if (ottoManifest?.id === pkg) {\n return dir\n }\n } catch {\n /* 忽略非法 otto-plugin.json,继续扫描其余候选目录 */\n }\n }\n }\n return undefined\n}\n\n/**\n * 解析包目录(含 package.json 的目录)。优先级:① 从 `cwd` 解析(用户项目里\n * `npm install` 了某个 otto 插件包的真实依赖场景);② 从宿主自身位置解析(宿主\n * 对该包有真实 `package.json` 依赖声明的场景,如未来首方扩展改为显式依赖);\n * ③ workspace glob 扫描 fallback(本仓首方扩展的真实生效路径,不依赖 node_modules\n * 软链,见 `resolveViaWorkspaceGlob` 文档)。\n */\nexport function resolvePackageDir(pkg: string, cwd: string = process.cwd()): string | undefined {\n const bases = [pathToFileURL(join(cwd, '__resolve__.js')).href, import.meta.url]\n for (const base of bases) {\n try {\n return dirname(createRequire(base).resolve(`${pkg}/package.json`))\n } catch {\n /* try next base */\n }\n }\n const hostDir = dirname(fileURLToPath(import.meta.url))\n return resolveViaWorkspaceGlob(pkg, hostDir) ?? resolveViaWorkspaceGlob(pkg, cwd)\n}\n\n/**\n * 通用安装:软链包目录进 `<ottoDir>/plugins/<id>`。幂等——已指向同包的软链原样返回;\n * 既有(软链或我们之前物化的同 id 目录)则替换;目标是他物则拒绝覆盖。\n *\n * RFC-150 D5:软链就位后总是编译一次(`created:true`/`false` 均编译,非仅首装)——\n * 保证幂等调用后产物一定新鲜(如用户从旧版本升级、`plugin-dist/` 产物此前从未生成过的\n * 场景,仅在 `created:true` 时编译会遗漏这种情况)。单插件编译成本极低(实测 13-50ms),\n * install 本就是用户主动触发的非热路径动作,可接受。\n */\nexport async function installPluginFromPackage(\n opts: InstallPluginFromPackageOptions,\n): Promise<InstallPluginResult> {\n const pkgDir = resolvePackageDir(opts.pkg, opts.cwd)\n if (!pkgDir) {\n throw new Error(`无法解析包 ${opts.pkg}(未安装或不可从当前位置解析)。`)\n }\n const manifestPath = join(pkgDir, PLUGIN_MANIFEST_FILENAME)\n if (!existsSync(manifestPath)) {\n throw new Error(\n `${opts.pkg} 不是 otto 插件:缺 ${PLUGIN_MANIFEST_FILENAME}(RFC-044 自描述)。`,\n )\n }\n const manifest = parsePluginManifest(readFileSync(manifestPath, 'utf8'), manifestPath)\n if (!manifest) {\n throw new Error(`${opts.pkg} 的 ${PLUGIN_MANIFEST_FILENAME} 非法(需合法 kebab-case id)。`)\n }\n\n const ottoDir = opts.ottoDir ?? OTTO_HOME\n const dir = join(ottoDir, 'plugins', manifest.id)\n mkdirSync(join(ottoDir, 'plugins'), { recursive: true })\n const target = resolve(pkgDir)\n\n if (isSymlink(dir)) {\n try {\n if (resolve(readlinkSync(dir)) === target) {\n const compileWarnings = await compilePluginAfterInstall(pkgDir)\n return { id: manifest.id, packageDir: pkgDir, dir, created: false, compileWarnings, version: manifest.version }\n }\n } catch {\n /* fall through to replace */\n }\n unlinkSync(dir)\n } else if (existsSync(dir)) {\n // 既有真实目录:仅当是同 id 的 otto 插件(我们之前物化的)才替换,避免误删他物。\n const existingManifest = existsSync(join(dir, PLUGIN_MANIFEST_FILENAME))\n ? parsePluginManifest(readFileSync(join(dir, PLUGIN_MANIFEST_FILENAME), 'utf8'), dir)\n : null\n if (existingManifest?.id === manifest.id) {\n rmSync(dir, { recursive: true, force: true })\n } else {\n throw new Error(`目标已存在且非本插件,拒绝覆盖:${dir}`)\n }\n }\n\n symlinkSync(target, dir)\n const compileWarnings = await compilePluginAfterInstall(pkgDir)\n return { id: manifest.id, packageDir: pkgDir, dir, created: true, compileWarnings, version: manifest.version }\n}\n\n/**\n * 从本地目录安装插件(RFC-122 物理迁移:首方 provider 插件安装)。\n *\n * 把 `sourceDir`(包含 `otto-plugin.json` 的目录)symlink 到 `~/.otto/plugins/<id>`。\n * 已存在同 id 的旧安装会被替换;非插件目录会整体 throw。\n *\n * 用户级安装默认受信任(`~/.otto/plugins/` 不受 trust 门限制,同 `installPluginFromPackage`)。\n * RFC-150 D5:同 `installPluginFromPackage`,软链就位后总是编译一次。\n */\nexport async function installPluginFromDir(sourceDir: string): Promise<InstallPluginResult> {\n const manifestPath = join(sourceDir, PLUGIN_MANIFEST_FILENAME)\n if (!existsSync(manifestPath)) {\n throw new Error(`${sourceDir} 不是 otto 插件:缺 ${PLUGIN_MANIFEST_FILENAME}`)\n }\n const manifest = parsePluginManifest(readFileSync(manifestPath, 'utf8'), manifestPath)\n if (!manifest) {\n throw new Error(`${sourceDir} 的 ${PLUGIN_MANIFEST_FILENAME} 非法。`)\n }\n\n const dir = join(OTTO_HOME, 'plugins', manifest.id)\n mkdirSync(join(OTTO_HOME, 'plugins'), { recursive: true })\n const target = resolve(sourceDir)\n\n if (isSymlink(dir)) {\n try {\n if (resolve(readlinkSync(dir)) === target) {\n const compileWarnings = await compilePluginAfterInstall(sourceDir)\n return { id: manifest.id, packageDir: sourceDir, dir, created: false, compileWarnings, version: manifest.version }\n }\n } catch {\n /* fall through to replace */\n }\n unlinkSync(dir)\n } else if (existsSync(dir)) {\n const existingManifest = existsSync(join(dir, PLUGIN_MANIFEST_FILENAME))\n ? parsePluginManifest(readFileSync(join(dir, PLUGIN_MANIFEST_FILENAME), 'utf8'), dir)\n : null\n if (existingManifest?.id === manifest.id) {\n rmSync(dir, { recursive: true, force: true })\n } else {\n throw new Error(`目标已存在且非本插件,拒绝覆盖:${dir}`)\n }\n }\n\n symlinkSync(target, dir)\n const compileWarnings = await compilePluginAfterInstall(sourceDir)\n return { id: manifest.id, packageDir: sourceDir, dir, created: true, compileWarnings, version: manifest.version }\n}\n","/** Installed plugins registry: scan install dir, detect manifests, validate. */\nimport type { InstalledPluginEntry, SettingsManager } from '@x-otto/setting'\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport type { InstallScope } from '@x-otto/install'\n\n/**\n * 新增或更新一个已安装插件条目(幂等:同 id 覆盖,不追加重复项)。\n * 返回新数组(不修改入参),符合 settings.persist 的不可变更新模式。\n */\nexport function upsertInstalledPluginEntry(\n entries: readonly InstalledPluginEntry[],\n entry: InstalledPluginEntry,\n): InstalledPluginEntry[] {\n const filtered = entries.filter((e) => e.id !== entry.id)\n return [...filtered, entry]\n}\n\n/**\n * 移除一个已安装插件条目(卸载时调用)。id 不存在时返回原数组等价副本(不报错,幂等)。\n */\nexport function removeInstalledPluginEntry(\n entries: readonly InstalledPluginEntry[],\n id: string,\n): InstalledPluginEntry[] {\n return entries.filter((e) => e.id !== id)\n}\n\n/**\n * 记录一次插件安装到 `installed_plugins` 清单(RFC-219 M1 接线)。供 CLI `otto extension\n * install`(`pluginInstall`)与 TUI 安装路径(`runPluginInstall`)在\n * `installPluginFromPackage`/`installPluginFromDir` 成功返回后调用。\n *\n * 不在 `install-plugin.ts` 内部调用本函数——保持该模块\"零 settings 依赖、宿主对具体插件\n * 零认知\"的既有边界(RFC-148 框架层零厂商知识纪律),配置维护是调用方职责。\n */\nexport async function recordPluginInstalled(\n settings: SettingsManager,\n entry: {\n id: string\n source: InstalledPluginEntry['source']\n sourceSpec: string\n version?: string\n scope: InstallScope\n /**\n * 安装时声明的高危能力快照(RFC-219 D1c)。调用方在安装前已用\n * `readHighRiskCapabilities()` 计算过该值(用于信任确认弹窗),此处复用同一结果,\n * 不重新读盘。\n */\n capabilities?: string[]\n },\n now: () => number = () => Date.now(),\n): Promise<void> {\n const current = settings.get('installed_plugins') ?? []\n const next = upsertInstalledPluginEntry(current, {\n id: entry.id,\n source: entry.source,\n sourceSpec: entry.sourceSpec,\n installedAt: now(),\n version: entry.version,\n scope: entry.scope,\n capabilities: entry.capabilities,\n })\n await settings.persist({ installed_plugins: next })\n}\n\n/**\n * 从 `installed_plugins` 清单移除一个条目,并清理该插件在 `disabled_plugins`/\n * `plugin_lifecycle.setupDone` 中的残留(卸载时调用)。供 `@x-otto/install` `uninstall()`\n * 的 plugin 分支在删除 symlink 成功后调用(见 packages/install/src/uninstaller.ts)。\n *\n * 这是 RFC-219 D1 规则1\"引导修复的正向对应\"——**用户主动卸载**是唯一允许清理这些\n * 配置残留的路径(区别于对不一致状态的\"自动双向纠正\",后者禁止)。三处残留一次性\n * 清理是本函数存在的意义:不清理会导致重装同 id 插件时误继承旧的禁用/setup 状态。\n */\nexport async function recordPluginUninstalled(\n settings: SettingsManager,\n id: string,\n): Promise<void> {\n const patch: Record<string, unknown> = {}\n let dirty = false\n\n const installed = settings.get('installed_plugins') ?? []\n if (installed.some((e) => e.id === id)) {\n patch['installed_plugins'] = removeInstalledPluginEntry(installed, id)\n dirty = true\n }\n\n const disabled = settings.get('disabled_plugins') ?? []\n if (disabled.includes(id)) {\n patch['disabled_plugins'] = disabled.filter((d) => d !== id)\n dirty = true\n }\n\n const lifecycle = settings.get('plugin_lifecycle') as { setupDone?: Array<{ id: string; version?: string }> } | undefined\n const setupDone = lifecycle?.setupDone ?? []\n if (setupDone.some((e) => e.id === id)) {\n patch['plugin_lifecycle'] = { setupDone: setupDone.filter((e) => e.id !== id) }\n dirty = true\n }\n\n if (!dirty) return\n await settings.persist(patch as Parameters<typeof settings.persist>[0])\n}\n\n/** 一致性告警的两种形态(RFC-219 D1:只告警,不自动修复)。 */\nexport type PluginConsistencyWarning =\n | { kind: 'missing-on-disk'; id: string; message: string }\n | { kind: 'missing-in-registry'; id: string; message: string }\n\n/**\n * 比对\"清单\"(`installed_plugins`,逻辑记录)与\"磁盘发现\"(`discoverPlugins()` 结果,\n * 物化形态)——不一致时产出告警,不做任何修复动作。\n *\n * 只比对清单里 `scope: 'global'`(`@x-otto/install` `InstallScope` 词表,对应\n * `DiscoveredPlugin.scope: 'user'`——两套词表对同一概念命名不同,是仓库既有历史差异,\n * 非本模块引入,见 RFC-219 重要事项规则 1a)的条目,因为 `installed_plugins` 清单本身\n * 恒定路由 user 级配置存储(重要事项规则 5),不与 project/repository/builtin scope 的\n * 磁盘插件比对。\n */\nexport function checkPluginConsistency(\n discovered: readonly DiscoveredPlugin[],\n installed: readonly InstalledPluginEntry[],\n): PluginConsistencyWarning[] {\n const warnings: PluginConsistencyWarning[] = []\n const discoveredUserIds = new Set(\n discovered.filter((p) => p.scope === 'user').map((p) => p.id),\n )\n const globalInstalled = installed.filter((e) => e.scope === 'global')\n const installedIds = new Set(globalInstalled.map((e) => e.id))\n\n for (const entry of globalInstalled) {\n if (!discoveredUserIds.has(entry.id)) {\n warnings.push({\n kind: 'missing-on-disk',\n id: entry.id,\n message: `${entry.id}: 清单记录已安装,但磁盘 symlink 缺失,建议重装(otto extension install ${entry.id})`,\n })\n }\n }\n for (const p of discovered) {\n if (p.scope === 'user' && !installedIds.has(p.id)) {\n warnings.push({\n kind: 'missing-in-registry',\n id: p.id,\n message: `${p.id}: 磁盘已安装,但清单未记录(可能是旧版本 otto 安装的),建议重新安装以补登记`,\n })\n }\n }\n return warnings\n}\n","/** Plugin topology: dependency graph + activation ordering. */\nimport { homedir } from 'node:os'\nimport {\n discoverPlugins,\n topoSortPlugins,\n filterEngineCompatible,\n PLUGIN_API_VERSION,\n type DiscoveredPlugin,\n type TopoSortResult,\n} from '@x-otto/plugin'\nimport type { Logger } from '@x-otto/shared'\nimport type { SettingsManager } from '@x-otto/setting'\nimport { installPluginFromPackage } from '../plugin/install-plugin'\nimport { recordPluginInstalled } from '../plugin/installed-plugins-registry'\n\nexport class PluginTopologyManager {\n /** 最近一次 `resolve()` 的拓扑排序原始结果(供 `diagnose()` 低频诊断读取)。 */\n private lastTopoSort: TopoSortResult | undefined\n\n /** 最近一次 `resolve()` 被版本门剔除的插件 id → 声明 range(RFC-148 M0 诊断用)。 */\n private lastEngineIncompatible: Map<string, string> = new Map()\n\n /** 上一轮已告警过的 cyclic/missingDeps 插件 id 集合(去重用,防同一问题每次 reload 都重复 warn)。 */\n private warnedTopoIssues = new Set<string>()\n\n /** 上一轮已告警过的版本门不兼容插件 id 集合(去重用,同 `warnedTopoIssues` 纹理)。 */\n private warnedEngineIncompatible = new Set<string>()\n\n /**\n * 当前活跃插件(bundle 启停的统一发现点)。\n * 应用 `disabled_plugins` 过滤——禁用插件不返回,其 skill/agent/command/mcp 贡献全部消失。\n * 各轴发现(skill/agent 经此注入;command 经 cli 调用此方法;mcp 控制器自带 settings 等价过滤)共用。\n *\n * RFC-132 D3/D5:`discoverPlugins()` 的字母序结果经 `topoSortPlugins()` 转为拓扑序\n * (被依赖者排在依赖者之前),全部装载消费方(module/provider/renderer/status/menu\n * loader)经此统一供数点自动获得拓扑序,无需逐个改装载循环。**供数与诊断分离**\n * (高频调用点不打日志,防止同一个环/缺失依赖每次调用都刷屏)——只把最新\n * `TopoSortResult` 缓存到 `lastTopoSort`,诊断日志在低频的 `reloadCapabilities()`\n * 完成点读取该缓存并去重告警(见 `diagnose()`)。\n */\n /**\n * `homedir` 参数(可选,缺省真实 `os.homedir()`):插件 user-scope 发现的 homedir 覆盖,\n * 供 App 层透传测试注入的隔离路径(见 `AppOptions.pluginHomedir` 注释)。生产调用方\n * 从不传此参数,行为不变。\n */\n resolve(params: { workspaceDir: string; disabledPlugins: string[]; homedir?: string }): DiscoveredPlugin[] {\n const discovered = discoverPlugins({\n cwd: params.workspaceDir,\n homedir: params.homedir ?? homedir(),\n disabled: params.disabledPlugins,\n })\n // RFC-148 M0:版本门在拓扑排序之前过滤——不兼容插件被当作\"不存在\",其依赖方经既有\n // missingDeps 机制自然 fail-closed 剔除(复用 RFC-132 诊断链路,不新增诊断通道)。\n const { compatible, incompatible } = filterEngineCompatible(discovered)\n this.lastEngineIncompatible = incompatible\n const result = topoSortPlugins(compatible)\n this.lastTopoSort = result\n return result.ordered\n }\n\n /**\n * RFC-132 D5:拓扑排序 fail-closed 诊断——低频事件点(`reloadCapabilities()` 完成时)调用,\n * 对 `lastTopoSort` 的 cyclic/missingDeps 做一次去重告警。**只在依赖问题新出现或变化时**\n * `logger.warn`(对比上一轮已告警集合),避免高频重算的 `resolve()` 内打日志导致\n * 同一个环/缺失依赖每次调用都刷屏。被剔除的插件完全不参与本轮激活,其贡献不进任何 loader。\n *\n * RFC-163 D5:`missingDeps` 非空时,除告警外追加尝试自动补装——`installPluginFromPackage()`\n * 是通用函数(走 workspace glob 扫描,不认识任何具体插件身份),失败静默降级为告警\n * (不影响本方法其余诊断逻辑,不向上抛异常,见 RFC-163 R4)。补装成功后不需要额外触发\n * \"重新发现\"——`installPluginFromPackage()` 软链就位后,下一次任意 `resolve()` 调用\n * (磁盘扫描)会自然发现它(RFC-163 M1b 摸底已确认,软链+扫描天然最终一致)。\n *\n * 签名从同步改为 `async`(RFC-163 M1b 摸底确认两处调用点均已在 `await` 密集的异步函数体\n * 内,补 `await` 安全,不破坏现有调用结构)。\n *\n * RFC-219 M1 完成审核修复(F1):自动补装成功后同步记录到 `installed_plugins` 清单——\n * 此前只落 symlink,导致下次 `checkPluginConsistency` 误判\"磁盘已安装但清单未记录\"。\n * `settings` 参数可选(缺省跳过记录,兼容未传 settings 的调用方/测试),传入时才记录。\n */\n async diagnose(logger: Logger, settings?: SettingsManager): Promise<void> {\n // RFC-148 M0:版本门不兼容诊断(与拓扑诊断共用去重节奏,独立 warned 集合防止两类问题\n // 互相掩盖去重状态)。\n for (const [pluginId, range] of this.lastEngineIncompatible) {\n if (!this.warnedEngineIncompatible.has(pluginId)) {\n logger.warn(\n { pluginId, requiredRange: range, hostApiVersion: PLUGIN_API_VERSION },\n 'plugin engines.otto not satisfied by host, skipping plugin (fail-closed, RFC-148 M0)',\n )\n }\n }\n this.warnedEngineIncompatible = new Set(this.lastEngineIncompatible.keys())\n\n const result = this.lastTopoSort\n if (!result) return\n\n const currentIssues = new Set<string>([...result.cyclic.keys(), ...result.missingDeps.keys()])\n\n for (const [pluginId, cyclePath] of result.cyclic) {\n if (!this.warnedTopoIssues.has(pluginId)) {\n logger.warn(\n { pluginId, cyclePath },\n 'plugin dependency cycle detected, skipping cyclic plugin (fail-closed, RFC-132 D5)',\n )\n }\n }\n for (const [pluginId, missing] of result.missingDeps) {\n if (!this.warnedTopoIssues.has(pluginId)) {\n logger.warn(\n { pluginId, missingDeps: missing },\n 'plugin dependencies missing, skipping plugin (fail-closed, RFC-132 D5)',\n )\n // RFC-163 D5:仅对\"新出现\"的缺失依赖尝试自动补装(同现有去重节奏,防止每次\n // diagnose() 调用都重复尝试已知会失败的安装)。逐个缺失依赖 id 尝试——一个插件\n // 可能同时缺多个依赖,某个补装失败不应阻断其余依赖的补装尝试。\n for (const missingId of missing) {\n try {\n const installResult = await installPluginFromPackage({ pkg: missingId })\n // RFC-219 M1(对抗审核 F1 修复):补装成功后记录到 installed_plugins 清单,\n // 恒定 'global' scope(installPluginFromPackage 未传 ottoDir 时默认 OTTO_HOME,\n // 即用户级安装)。settings 未传入时静默跳过记录(不阻断诊断流程本身)。\n if (settings) {\n await recordPluginInstalled(settings, {\n id: installResult.id,\n source: 'npm',\n sourceSpec: missingId,\n version: installResult.version,\n scope: 'global',\n })\n }\n logger.info(\n { pluginId, missingId },\n 'missing plugin dependency auto-installed (RFC-163 D5), will be picked up on next discovery',\n )\n } catch (err) {\n // 失败静默降级(R4):workspace glob 找不到目标是预期场景(如未来 npm 发布\n // 环境),不应中断诊断流程或向上抛异常——用户已经从上面的 warn 看到\"依赖缺失\"\n // 提示,这里只是锦上添花的自动化尝试,不是新增的失败点。\n logger.warn(\n { pluginId, missingId, err: err instanceof Error ? err.message : String(err) },\n 'automatic install of missing plugin dependency failed, manual \"otto extension install\" may be required (RFC-163 D5)',\n )\n }\n }\n }\n }\n\n this.warnedTopoIssues = currentIssues\n }\n}\n","/** ModelInfra factory: model registry + provider lookup + default model. */\nimport {\n createDefaultProviderRegistry,\n createDefaultAuthStore,\n createDefaultModelRegistry,\n createWireProtocolRegistry,\n type AuthStore,\n type Model,\n type ModelRegistry,\n type ProviderRegistry,\n type WireProtocolRegistry,\n} from '@x-otto/ai'\nimport type { SettingsManager } from '@x-otto/setting'\nimport { ModelCatalogManager } from './model-catalog-manager'\nimport type { AppOptions } from '../types'\n\nexport interface ModelInfra {\n authStore: AuthStore\n modelRegistry: ModelRegistry\n providerRegistry: ProviderRegistry\n wireProtocolRegistry: WireProtocolRegistry\n modelCatalogManager: ModelCatalogManager\n defaultModel: Model | undefined\n resolveEffectiveModel: () => Model | undefined\n}\n\n/**\n * 原构造函数 P1-P3 段落逐字迁移:auth/model/provider/wire-protocol registry 构造 +\n * `modelCatalogManager` 构造 + `explicitModelId`/`defaultModel`/`resolveEffectiveModel` 闭包。\n *\n * 注:`hookRegistry` 的默认值构造(`createHookRegistry({ preset: 'default' })`)仍在\n * `App` 构造函数原位置执行——它在原代码中先于本段(用于后续多处依赖),为保持\"逐字迁移\"\n * 的最小改动面,本工厂函数不涉及 hookRegistry。\n */\nexport function buildModelInfrastructure(\n options: Pick<AppOptions, 'model' | 'authStore' | 'modelRegistry' | 'providerRegistry' | 'modelId'>,\n getSettings: () => SettingsManager,\n): ModelInfra {\n const { model, authStore, modelRegistry, providerRegistry } = options\n\n const resolvedAuthStore = authStore ?? createDefaultAuthStore()\n const resolvedModelRegistry = modelRegistry ?? createDefaultModelRegistry()\n const resolvedProviderRegistry =\n providerRegistry ??\n createDefaultProviderRegistry({\n authStore: resolvedAuthStore,\n modelRegistry: resolvedModelRegistry,\n })\n // RFC-148 M2:三个标准协议实现已物理迁出至 extensions/plugin-otto-wire-protocols 插件(M1 的\n // wire-protocol-shim.ts 缝合层已删除)——registry 此处只构造空表,由插件装载时经\n // wireProtocols 轴(plugin-module-manager.ts)填充。声明式 provider 插件(如\n // plugin-deepseek)经 buildFactory 惰性查表(RFC-148 D3),若 otto-wire-protocols 插件\n // 尚未装载完成会在建流时报错(文案含 install 指引)——装载顺序由 discoverPlugins 的\n // 拓扑排序 + 插件目录内既有的 dependsOn 声明保证(otto-wire-protocols 是首方内置插件,\n // 首次运行即随分发物一同安装)。\n const wireProtocolRegistry = createWireProtocolRegistry()\n const modelCatalogManager = new ModelCatalogManager(resolvedModelRegistry, resolvedAuthStore)\n\n // RFC-130 M131:插件声明式 model(如 cursor/default)在构造期尚未注册——\n // resolveModel 会抛 Model not found。tryResolve 延迟到 start() 装载 provider 后再解析。\n const explicitModelId = options.modelId\n const defaultModel =\n model ?? (explicitModelId ? resolvedModelRegistry.tryResolve(explicitModelId) : undefined)\n\n const resolveEffectiveModel = (): Model | undefined => {\n if (defaultModel) return defaultModel\n if (explicitModelId) {\n const resolved = resolvedModelRegistry.tryResolve(explicitModelId)\n if (resolved) return resolved\n }\n const id = getSettings().get('model')\n return (\n (id ? resolvedModelRegistry.tryResolve(id) ?? resolvedModelRegistry.find(id) : undefined) ??\n resolvedModelRegistry.getAll().find(m => m.id !== '__otto_setup__')\n )\n }\n\n return {\n authStore: resolvedAuthStore,\n modelRegistry: resolvedModelRegistry,\n providerRegistry: resolvedProviderRegistry,\n wireProtocolRegistry,\n modelCatalogManager,\n defaultModel,\n resolveEffectiveModel,\n }\n}\n","import { join } from 'node:path'\nimport { createAgentRuntime } from '@x-otto/runtime'\nimport { createLogger } from '@x-otto/shared'\nimport { isShadowModeEnabled } from '@x-otto/env'\nimport {\n createInMemoryPanelStatePersistence,\n createInMemoryPasteStatePersistence,\n} from '@x-otto/persistence'\nimport type { AgentRuntime, AgentSessionInfo, ResidencyGovernor } from '@x-otto/runtime'\nimport type { SessionStorageConfig } from '@x-otto/runtime'\nimport {\n RelationalSessionPersistence,\n SqliteSessionRepository,\n createSessionWriteLeaseManager,\n createNoopSessionWriteLeaseManager,\n} from '@x-otto/session'\nimport { SESSION_RESUME_WINDOW_MESSAGES } from '@x-otto/session-contract'\nimport { DEFAULT_WORKSPACE_KEY } from '@x-otto/workspace'\nimport type { SessionPersistence } from '@x-otto/session'\nimport type { AgentMessage, ClockPort, TraceEvent } from '@x-otto/agent'\nimport type { AppendLog } from '@x-otto/persistence'\nimport type { CheckpointData, MemorySubsystem, ResolvedSessionConfig } from '@x-otto/runtime'\nimport type { HookRegistry } from '@x-otto/hooks'\nimport type { ProviderRegistry, ModelRegistry, Model } from '@x-otto/ai'\nimport type { Logger } from '@x-otto/shared'\nimport type { Devtools } from '@x-otto/devtools'\nimport { PromptManager } from '@x-otto/prompt'\nimport type { AgentProfile } from '@x-otto/prompt'\nimport type { AgentTool } from '@x-otto/agent'\n\nimport { requireRemote } from './storage-wiring'\nimport { buildRestoredSubagentPromptRefresh } from '../session/session-config-resolver'\nimport { resolveEnabledCapabilities } from '../session/capability-tool-map'\nimport type { AppOptions } from '../types'\n\n/**\n * AgentRuntime 工厂(App 经此持有 runtime 门面)。\n *\n * 按 storage.session 选址创建 runtime:\n * dir → 磁盘 / url → remote / 缺省内存。\n * 标准后端委托 createAgentRuntime(单一装配权威)直接返回其 AgentRuntime;\n * offline/PersistenceClient 路径构造 SessionPool 后包成同形 AgentRuntime\n * (contextSources 经 createDefaultContextSourceRegistry 复用 → 与标准后端一致,\n * 宿主统一经 rt.contextSources.sealAndBuildHooks 构建注入 hooks,消灭二次造表;\n * RFC-129 D4:方法名从 buildHooks 改为 sealAndBuildHooks,纯改名不影响此处逻辑)。\n */\nexport function createAppRuntime(\n options: AppOptions,\n deps: {\n hookRegistry: HookRegistry\n providerRegistry: ProviderRegistry\n modelRegistry: ModelRegistry\n logger: Logger\n workspaceDir: string\n /** lazy getter:工厂调用时 toolRegistry 尚未初始化,延迟到 restore 路径实际调用 configProvider 时才取。 */\n getTools: () => AgentTool[]\n /** restore 时据持久化 agentName 重解析子代理 profile(缺省=空,子代理身份退回 main)。 */\n getDeclarativeAgentProfiles: () => AgentProfile[]\n /** 关系化会话持久化的 workspaceKey 来源(惰性——`workspaceRef` 在 start() 才解析)。 */\n getWorkspaceKey?: () => string | undefined\n /**\n * SqliteSessionRepository 的回填引用——关系化分支创建 repo 后设置,\n * 供外部(MemoryManager.readArchive/listArchives)读取 session entries 以重建 archive。\n */\n repoRef?: { current?: SqliteSessionRepository }\n maxToolTurns: number\n promptManager: PromptManager\n memory?: MemorySubsystem\n traceStore: AppendLog<TraceEvent>\n checkpointStore: AppendLog<CheckpointData>\n clock?: ClockPort\n devtools?: Devtools\n /** RFC-178 触发器 C:驻留收紧系数源(app 构造持有,app-lifecycle 接分级信号)。 */\n residencyGovernor?: ResidencyGovernor\n /** RFC-323 M4:驻留预算运行时配置 getter(settings 注入)。 */\n getResidencyConfig?: () => import('@x-otto/session-contract').ResidencyBudgetConfig | undefined\n },\n getDefaultModel: () => Model | undefined,\n): AgentRuntime {\n const configProvider = (agentName?: string, modelId?: string): ResolvedSessionConfig => {\n const model =\n (modelId ? deps.modelRegistry.find(modelId) : undefined) ??\n getDefaultModel()\n if (!model) {\n throw new Error(\n `configProvider: 无法解析恢复模型(modelId=\"${modelId ?? ''}\" 未命中、且无默认/settings/注册表模型)`,\n )\n }\n const tools = deps.getTools()\n // RFC-101:restore 路径的 main preset 同样门控——与 resolve() 主路径同等对待。\n const enabledCapabilities = resolveEnabledCapabilities(new Set(tools.map((t) => t.name)))\n return {\n model,\n systemPrompt: '',\n tools,\n thinkingLevel: 'medium' as const,\n maxToolTurns: deps.maxToolTurns,\n promptRefresh: agentName\n ? buildRestoredSubagentPromptRefresh(\n deps.promptManager,\n deps.getDeclarativeAgentProfiles(),\n agentName,\n tools,\n )\n : () => deps.promptManager.assemblePreset({ preset: 'main' }, enabledCapabilities),\n workspaceDir: deps.workspaceDir,\n }\n }\n\n const loc = options.storage?.session\n\n if (loc && 'dir' in loc && process.env['OTTO_SESSION_BLOB'] !== '1') {\n // 关系化 SQLite(M60 起默认)经 createAgentRuntime 的 `kind:'custom'` 分支托管——\n // 不再手搓平行 AgentRuntime 字面量(消除装配漂移:trace 所有权/contextSources/stream/dispose\n // 全由单一装配权威 createAgentRuntime 提供,与标准后端一致)。\n // RFC-345 §D8 影子模式:会话库走 :memory:(单 owner,§D11 分裂风险不 materialize,\n // 复用 SqliteSessionRepository 保住 RFC-159/160/321/339 全部不变式);写租约退化为\n // no-op(§R5:无跨进程并发写同一磁盘会话,红线保护对象不存在);面板态/粘贴态走内存实现。\n const shadow = isShadowModeEnabled()\n const repo = new SqliteSessionRepository({\n dbPath: shadow ? ':memory:' : join(loc.dir, 'sessions.db'),\n })\n if (deps.repoRef) deps.repoRef.current = repo\n // 2026-07-14 修复:`/exit -r` 陪跑重启下父进程 spawn 子进程后不退出(持续陪跑到\n // 子进程退出,见 cli.ts),process.on('exit') 释放钩子因此永不触发——stopApp() 的\n // saveAll() 又会在 spawn 之前 lazy-acquire 租约,导致新会话进程对同一 session\n // 永久只读。持有独立引用以便下方 onDispose 中显式 close()(agentRuntime.dispose()\n // 位于 stopApp 的 saveAll() 之后、app.stop() 返回之前,时序恰好正确)。\n const writeLease = shadow\n ? createNoopSessionWriteLeaseManager()\n : createSessionWriteLeaseManager({ lockDir: join(loc.dir, 'locks') })\n const persistence = new RelationalSessionPersistence<AgentMessage>({\n repo,\n workspaceKey: deps.getWorkspaceKey ?? (() => DEFAULT_WORKSPACE_KEY),\n // RFC-159 D4:超限会话 resume 只加载尾窗口(DB 全量保留,append-forever)。\n // RFC-321 D7:改用独立的 SESSION_RESUME_WINDOW_MESSAGES(5000)——此前与条数 cap\n // 同源,cap 上调到 50000 后会让 resume 一次性装载 108MB/113ms(实测),启动不可接受。\n // 切点谓词同语义(窗口首条非孤儿 tool_result,防 provider 400——事故 7e79407f)。\n maxLoadEntries: SESSION_RESUME_WINDOW_MESSAGES,\n canStartHistory: (m) => m.role !== 'tool_result',\n // RFC-159 D3:per-session 单写者租约——防多进程写竞态(/exit -r 陪跑、多开\n // --continue 同一会话;2026-07-13 数据丢失事故 R2 根因)。拿不到租约的进程\n // 对该会话只读(save 抛 SessionWriteLeaseDeniedError,persistence-sync 告警)。\n // 正常退出经 process exit 钩子同步释放;崩溃残留锁由 stale takeover(60s)接管;\n // 陪跑重启场景由 onDispose 中的 writeLease.close() 显式释放(见上方注释)。\n writeLease,\n // RFC-160 D2/D5:装载时剥离非活跃 compaction 的 replacement(内存投影,DB 全量\n // 保留)。生产实测:最坏会话 resume 堆占用 181MB→9.8MB。三出口(checkpoint/fork/\n // restore)hydrate 已接线(M2),branch() fail-fast 兜底。\n stripInactiveReplacements: true,\n })\n // RFC-146:本地高效冷会话枚举——单次 SQL 查询即拿到完整元数据,注入 SessionPool\n // 供 listAllPersistedSessions 消费,无需回退到远端场景的 listPaginated+load 慢路径。\n const listColdSessionsOverride = async (workspaceKey: string): Promise<AgentSessionInfo[]> => {\n const rows = await repo.listSessions(workspaceKey).catch(() => [])\n return rows.map((row) => ({\n id: row.id,\n title: row.title?.trim() || row.id.slice(0, 8),\n messageCount: 0,\n createdAt: new Date(row.createdAt),\n updatedAt: new Date(row.lastActiveAt),\n // /archive /pin 持久化镜像透传(未加载进内存的冷会话同样需要正确展示归档/置顶态/分组)。\n status: row.archived ? 'archived' : 'idle',\n pinned: row.pinned,\n pinGroup: row.pinGroup,\n writeLocked: writeLease.peekLockedByOther(row.id),\n // 与 messageCount(恒 0 占位)不同,entryCount 是零额外查询代价的真实条目数\n // (row.entryCount 源自 sessions.next_seq),供 /resume 列表区分\"真空会话\"。\n entryCount: row.entryCount,\n }))\n }\n // 只读探测(/resume 列表用):薄封装 writeLease.peekLockedByOther(无副作用,不 acquire)。\n const isSessionWriteLocked = (sessionId: string): boolean =>\n writeLease.peekLockedByOther(sessionId)\n // RFC-171 D4:写租约诊断 + 强制释放(供 `/session unlock` 消费)。\n const inspectSessionWriteLease = (sessionId: string) => writeLease.inspect(sessionId)\n const forceReleaseSessionWriteLease = (sessionId: string): void =>\n writeLease.forceRelease(sessionId)\n return createAgentRuntime({\n providerRegistry: deps.providerRegistry,\n hookRegistry: deps.hookRegistry,\n logger: deps.logger,\n workspaceDir: deps.workspaceDir,\n clock: deps.clock,\n traceStore: deps.traceStore,\n checkpointStore: deps.checkpointStore,\n memory: deps.memory,\n maxSessions: options.maxSessions,\n devtools: deps.devtools,\n configProvider,\n residencyGovernor: deps.residencyGovernor,\n getResidencyConfig: deps.getResidencyConfig,\n storage: {\n kind: 'custom',\n persistence: persistence as SessionPersistence<AgentMessage>,\n autoPersist: true,\n // RFC-108 D3 面板态落盘(2026-07-21 修复):关系化 SQLite 分支此前未启用\n // PanelStateStore,drafts/todoList 等面板态重启即丢——与 disk 后端同款目录惯例。\n // RFC-345 §D8:影子态注入内存持久化(磁盘零写),否则用 panel-state 目录。\n ...(shadow\n ? {\n panelStatePersistence: createInMemoryPanelStatePersistence(),\n pasteStatePersistence: createInMemoryPasteStatePersistence(),\n }\n : { panelStateDir: join(loc.dir, 'panel-state') }),\n listColdSessionsOverride,\n isSessionWriteLocked,\n inspectSessionWriteLease,\n forceReleaseSessionWriteLease,\n // writeLease.close() 必须先于 repo.close()(虽然两者独立无数据依赖,保持\"锁资源\n // 先于数据库句柄\"释放的直觉顺序)。close() 是终态:此后任何迟到的 fire-and-forget\n // save 都会被拒(fail-safe),不会复活已删除的锁文件/心跳。\n onDispose: () => {\n writeLease.close()\n repo.close()\n },\n },\n })\n }\n\n if (loc && 'dir' in loc && process.env['OTTO_SESSION_BLOB'] === '1') {\n createLogger('@x-otto/coding:session-pool-factory').warn(\n 'OTTO_SESSION_BLOB=1 — blob session persistence is deprecated (M63). ' +\n 'Relational append (sessions.db) is the default since M60. ' +\n 'The blob escape hatch will be removed when the relational path has proven stable in production.',\n )\n }\n\n const storageConfig: SessionStorageConfig | undefined = !loc\n ? undefined\n : 'dir' in loc\n ? { kind: 'disk', sessionDir: loc.dir }\n : 'url' in loc\n ? (() => {\n const remote = requireRemote(options.storage!, 'session')\n return {\n kind: 'remote',\n sessionUrl: loc.url,\n fetch: remote.fetch ?? globalThis.fetch,\n getAuth: remote.getAuth,\n timeoutMs: remote.timeoutMs,\n userId: options.userIdentity?.userId,\n // 修复(N3):remote 后端此前从不透传 workspaceRef.key,恒落 'default' 分区——\n // 与本地 SQLite 分支(下方 workspaceKey: deps.getWorkspaceKey)行为不一致。\n // 传函数本身(非立即调用结果)——createAppRuntime 在 App 构造期同步调用,\n // 此时 workspaceRef 尚未经 start() 异步解析完成,立即调用会永久固化为\n // undefined(与 Bug1 同因,见修复记录);惰性传递到 RemotePersistence.wsKey\n // getter,真正用到时(每次 HTTP 请求)才求值,此时 workspaceRef 已就绪。\n wsKey: deps.getWorkspaceKey,\n }\n })()\n : undefined\n\n const rt = createAgentRuntime({\n providerRegistry: deps.providerRegistry,\n hookRegistry: deps.hookRegistry,\n logger: deps.logger,\n workspaceDir: deps.workspaceDir,\n clock: deps.clock,\n traceStore: deps.traceStore,\n checkpointStore: deps.checkpointStore,\n memory: deps.memory,\n maxSessions: options.maxSessions,\n storage: storageConfig,\n devtools: deps.devtools,\n configProvider,\n residencyGovernor: deps.residencyGovernor,\n })\n return rt\n}\n","import { Agent } from '@x-otto/agent'\nimport { createSwarm, createHandoffTool } from '@x-otto/orchestrator'\nimport { resolveAgentProfile, assembleGenericSubAgentPrompt } from '@x-otto/prompt'\n\nimport { resolveAgentToolNames } from './agent-tool-names'\nimport { resolveTools as resolveSessionTools } from '../session/tool-resolver'\n\nimport {\n createToolHookExecutor,\n attachAgentObserver,\n globalAgentObservability,\n} from '@x-otto/runtime'\n\nimport type { AssistantMessage, Model, ProviderRegistry, UserMessage } from '@x-otto/ai'\nimport type { AgentRunContext, AgentTool, StreamFunction } from '@x-otto/agent'\nimport type { AgentProfile } from '@x-otto/prompt'\nimport type { HookRegistry } from '@x-otto/hooks'\nimport type { Logger } from '@x-otto/shared'\nimport type {\n SwarmAgentDef,\n SwarmConfig,\n OrchestrationPattern,\n RoutingStrategy,\n RouteRule,\n AgentTurnInput,\n AgentTurnResult,\n AgentToolDef,\n CoreToolDescriptor,\n HandoffRequest,\n} from '@x-otto/orchestrator'\nimport { TEAM_MEMBER_PREFIX, type TeamDeclaration } from './declarative-teams'\n\nconst MAX_NESTING_DEPTH = 5\n\nexport interface SwarmAgentRunnerDeps {\n hookRegistry: HookRegistry\n /** 引擎 stream 取自 runtime.sessions.stream(单一路径)。 */\n stream: StreamFunction\n logger: Logger\n defaultModel?: Model\n /**\n * RFC-332 M1:leaf 重解析真实工具的窄依赖。\n * - `getTools`:当前真实工具目录(与会话解析同源)。\n * - `getDeclarativeAgentProfiles`:member profile allowlist 来源。\n * 两者缺省时 member 工具解析返回空集(fail closed——模型看不到任何工具,\n * 不生成 stub),保持旧调用点(测试/独立使用)类型兼容。\n */\n getTools?: () => AgentTool[]\n getDeclarativeAgentProfiles?: () => AgentProfile[]\n}\n\n/** 每个 swarm 执行实例的 deps(含执行期标识 + M-B2 orchestrator 注入)。 */\nexport interface SwarmRuntimeDeps extends SwarmAgentRunnerDeps {\n providerRegistry: ProviderRegistry\n /** 运行标识,进 SwarmConfig.runId → AgentTurnInput.sessionId。 */\n runId: string\n /** 本 swarm 运行的委托深度,进 SwarmConfig.depth → AgentTurnInput.depth。 */\n depth?: number\n /** M-B2: orchestrator.runAgentTurn — 经 TaskExecutor 治理的 member turn 执行器。 */\n runAgentTurn?: (input: AgentTurnInput) => Promise<AgentTurnResult>\n}\n\n/**\n * RFC-332 M3:宿主解析层的**边界投影**——真实 `AgentTool[]` → 纯数据 `AgentToolDef[]`。\n *\n * execute 剥离从此发生在跨入 orchestrator **之前**(而非 orchestrator 内部的 `mapTools`),\n * 使 orchestrator 全包不再持有可执行工具类型。`description` 若为惰性函数(`ToolDescriptionContext`\n * → string)需先求值——orchestrator 只接受字符串,且 member 描述不依赖 projectRoot。\n */\nfunction toToolDefs(tools: AgentTool[]): AgentToolDef[] {\n return tools.map((t) => ({\n name: t.name,\n description:\n typeof t.description === 'function' ? t.description({ projectRoot: '' }) : t.description,\n parameters: t.parameters as unknown as Record<string, unknown>,\n }))\n}\n\n/**\n * RFC-332 M1:leaf 侧真实工具重解析(替代旧 throw stub 投影)。\n *\n * orchestrator 边界(`AgentTurnInput.tools`)是有意瘦化的纯数据元数据——它是「请求 leaf\n * 暴露何种能力」的声明,不是能力本身。本函数在 coding leaf 按名称从真实工具目录重新解析\n * `AgentTool`,并强制双重最小权限:\n *\n * 1. **profile allowlist**:member 有 agent profile 时,只有 `defaultTools` 解析出的名称\n * 可被暴露;无 profile 沿用既有「不限制」策略,但仍只从真实目录取工具。\n * 2. **真实目录**:名称必须存在于 `getTools()` 当前目录;元数据声明不能提升权限。\n *\n * fail-closed 规则(RFC-332 不变量 2/5):\n * - deps 缺失(旧调用点/测试未注入)→ 解析结果为空集,模型看不到任何 member 工具;\n * - 名称在目录/allowlist 缺失 → 该名称不进入模型 schema,记录诊断;\n * - 绝不构造 `execute() => throw` 的假工具。\n */\nfunction resolveMemberTools(\n requested: { name: string }[],\n agentId: string,\n deps: SwarmAgentRunnerDeps,\n): AgentTool[] {\n if (requested.length === 0) return []\n if (!deps.getTools) {\n deps.logger.warn(\n 'Swarm member \"%s\" requested %d tools but leaf has no tool directory (getTools not injected); all hidden (fail closed)',\n agentId,\n requested.length,\n )\n return []\n }\n\n const profiles = deps.getDeclarativeAgentProfiles?.() ?? []\n const profile = resolveAgentProfile(profiles, agentId)\n const allowlistNames = profile ? resolveAgentToolNames(profile.defaultTools) : undefined\n // 与会话/团队解析同源的规则(tool-resolver.resolveTools):undefined = 不限制,[] = 零工具。\n const available = resolveSessionTools(deps.getTools, undefined, allowlistNames)\n const availableByName = new Map(available.map((t) => [t.name, t]))\n\n const resolved: AgentTool[] = []\n const seen = new Set<string>()\n for (const req of requested) {\n if (seen.has(req.name)) continue // 去重,保留首个(稳定顺序)\n seen.add(req.name)\n const tool = availableByName.get(req.name)\n if (!tool) {\n deps.logger.warn(\n 'Swarm member \"%s\" tool \"%s\" not resolvable at leaf (missing from directory or denied by profile allowlist); hidden from model',\n agentId,\n req.name,\n )\n continue\n }\n resolved.push(tool)\n }\n return resolved\n}\n\n/**\n * RFC-332 M1:受限 core resolver——把 `CoreToolDescriptor` 纯数据还原为真实可执行 core 工具。\n * 只认注册名 `handoff_to_agent`;未知名称、origin 不匹配、无可用 target 均 fail closed\n * (隐藏 + 诊断,不退回 stub)。handoff intent 写入 per-turn 局部状态(first-write-wins),\n * 回合结束经 `AgentTurnResult.handoff` 纯数据回传,由 orchestrator 策略二次验证后切换。\n */\nfunction resolveCoreTools(\n descriptors: CoreToolDescriptor[],\n agentId: string,\n logger: Logger,\n captureHandoff: (request: HandoffRequest) => void,\n): AgentTool[] {\n const resolved: AgentTool[] = []\n const seenNames = new Set<string>()\n for (const desc of descriptors) {\n if (seenNames.has(desc.name)) {\n logger.warn('Swarm member \"%s\" duplicate core descriptor \"%s\"; ignored', agentId, desc.name)\n continue\n }\n if (desc.name !== 'handoff_to_agent') {\n logger.warn(\n 'Swarm member \"%s\" received unknown core tool descriptor \"%s\"; hidden (fail closed)',\n agentId,\n (desc as { name: string }).name,\n )\n continue\n }\n if (desc.context.from !== agentId) {\n logger.warn(\n 'Swarm member \"%s\" handoff descriptor origin mismatch (from=\"%s\"); hidden (fail closed)',\n agentId,\n desc.context.from,\n )\n continue\n }\n if (desc.context.allowedTargets.length === 0) {\n logger.warn('Swarm member \"%s\" handoff descriptor has no allowed targets; hidden', agentId)\n continue\n }\n seenNames.add(desc.name)\n resolved.push(\n createHandoffTool(desc.context.from, desc.context.allowedTargets, captureHandoff) as AgentTool,\n )\n }\n return resolved\n}\n\nexport interface ResolvedTeam {\n name: string\n pattern: OrchestrationPattern\n agents: SwarmAgentDef[]\n pipeline?: string[]\n supervisorId?: string\n entryAgentId?: string\n maxHandoffDepth?: number\n router?: SwarmConfig['router']\n}\n\n/**\n * 构造 swarm member turn 执行器(B+ 门禁①适配——M-B2 终局形态)。\n * 接受 AgentTurnInput → AgentTurnResult,由 orchestrator.runAgentTurn 经 TaskExecutor 调用。\n * per-execution 上下文(sessionId/depth)从 AgentTurnInput 提取,不再由闭包捕获。\n */\nexport function createSwarmAgentRunner(deps: SwarmAgentRunnerDeps) {\n return async (input: AgentTurnInput): Promise<AgentTurnResult> => {\n const startTime = Date.now()\n\n // RFC-303 D8:显式错误路径替代非空断言——deps.defaultModel 在应用未配置模型时\n // 合法为 undefined(对齐 \"No model configured\" 场景,见 lesson_1)。构造期\n // (createSwarmAgentRunner 调用时)不知道会不会真的被执行,故校验推迟到这里\n // (执行时),失败时给出可诊断的错误而非静默 undefined 传入 Agent。\n if (!deps.defaultModel) {\n throw new Error(\n `Swarm member turn for agent \"${input.agentId}\" failed: no default model configured. ` +\n `Configure a model before running swarm/team sessions.`,\n )\n }\n const defaultModel = deps.defaultModel\n\n // RFC-332 M1:leaf 重解析真实工具(双重最小权限:profile allowlist ∩ 真实目录)。\n // 解析失败的名称不进入模型 schema(fail closed),绝不生成 throw stub。\n const memberTools = resolveMemberTools(input.tools ?? [], input.agentId, deps)\n\n // 旧 extraTools 元数据通道 fail closed:它无法承载执行闭包(RFC-332 D3),\n // 忽略并诊断;core 工具唯一合法来源是 coreTools 描述符。\n if (input.extraTools?.length) {\n deps.logger.warn(\n 'Swarm member \"%s\" received %d extraTools via deprecated metadata channel; ignored (use coreTools descriptors)',\n input.agentId,\n input.extraTools.length,\n )\n }\n\n // handoff intent 捕获:first-write-wins(与旧 strategy 闭包语义一致——单回合单 handoff)。\n let capturedHandoff: HandoffRequest | undefined\n const coreTools = resolveCoreTools(\n input.coreTools ?? [],\n input.agentId,\n deps.logger,\n (request) => {\n if (!capturedHandoff) capturedHandoff = request\n },\n )\n\n // member 工具与 core 工具名称冲突时 core 优先且去重(handoff 是策略控制工具,\n // 不允许被目录里的同名工具遮蔽);其余保持稳定顺序。\n const coreNames = new Set(coreTools.map((t) => t.name))\n const tools = [...coreTools, ...memberTools.filter((t) => !coreNames.has(t.name))]\n\n const context: AgentRunContext = {\n model: defaultModel,\n systemPrompt: input.systemPrompt ?? '',\n messages: [],\n tools,\n thinkingLevel: (input.thinkingLevel ?? 'medium') as AgentRunContext['thinkingLevel'],\n maxTokens: input.maxTokens,\n temperature: input.temperature,\n }\n\n context.messages.push({\n role: 'user',\n content: [{ type: 'text', text: input.prompt }],\n timestamp: Date.now(),\n } as UserMessage)\n\n const sessionId = input.sessionId ?? `swarm:${input.agentId}`\n const depth = input.depth ?? 0\n\n const agent = new Agent({\n stream: deps.stream,\n logger: deps.logger,\n agentName: input.agentId,\n maxToolTurns: input.maxToolTurns,\n toolHookExecutor: createToolHookExecutor({\n hookRegistry: deps.hookRegistry,\n agentName: input.agentId,\n sessionId,\n }),\n depth,\n })\n\n const onAbort = () => agent.abort()\n if (input.signal?.aborted) agent.abort()\n else input.signal?.addEventListener('abort', onAbort, { once: true })\n\n const obsId = `${sessionId}:${input.agentId}`\n const detachObs = attachAgentObserver(agent, {\n id: obsId,\n agentName: input.agentId,\n title: input.agentId,\n prompt: input.prompt,\n model: input.model ?? defaultModel.id,\n origin: 'main',\n depth,\n abort: () => agent.abort(),\n messages: context.messages,\n })\n\n let result: AssistantMessage\n try {\n result = await agent.run(context)\n } finally {\n input.signal?.removeEventListener('abort', onAbort)\n detachObs()\n globalAgentObservability.markEnded(obsId)\n }\n\n return {\n agentId: input.agentId,\n messages: context.messages,\n // RFC-332 M1:handoff intent 纯数据回传。abort 优先——被中断的回合不提交 handoff\n // (abort 时 agent.run 抛出,走不到这里;此处再防御 signal 已中止的边界)。\n handoff: input.signal?.aborted ? undefined : capturedHandoff,\n text: result.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c: { type: 'text'; text: string }) => c.text)\n .join('\\n'),\n tokenUsage: {\n input: result.usage.inputTokens,\n output: result.usage.outputTokens,\n cacheRead: result.usage.cacheReadTokens,\n },\n durationMs: Date.now() - startTime,\n }\n }\n}\n\nexport function buildSwarmConfig(team: ResolvedTeam, deps: SwarmRuntimeDeps): SwarmConfig {\n // RFC-303 D8:显式错误路径替代非空断言——两个调用点(resolveTeam 递归嵌套 / createTeamRunner)\n // 均已在调用前校验 model 非空(createTeamRunner 的 `if (!model) return error`),故这里的\n // undefined 理论不可达;但保留运行时校验而非断言,防止未来新增第三个调用点时静默跳过校验。\n if (!deps.defaultModel) {\n throw new Error(`buildSwarmConfig for team \"${team.name}\" failed: no default model configured.`)\n }\n return {\n name: team.name,\n agents: team.agents,\n defaultModel: deps.defaultModel,\n pattern: team.pattern,\n router: team.router,\n pipeline: team.pipeline,\n supervisorId: team.supervisorId,\n entryAgentId: team.entryAgentId,\n maxHandoffDepth: team.maxHandoffDepth,\n runId: deps.runId,\n depth: deps.depth ?? 0,\n // M-B2: orchestrator.runAgentTurn 优先(经 TaskExecutor),回退到裸 Agent 创建(测试/独立使用)\n runAgentTurn: deps.runAgentTurn ?? createSwarmAgentRunner(deps),\n }\n}\n\nexport interface TeamRunnerDeps {\n logger: Logger\n providerRegistry: ProviderRegistry\n hookRegistry: HookRegistry\n /** 引擎 stream 取自 runtime.sessions.stream(单一路径)。 */\n stream: StreamFunction\n /** 未配置时 runTeam 返回面向用户的明确错误。 */\n defaultModel?: Model\n /** 声明式 team 在 start() 才发现,getter 注入运行时状态。 */\n getDeclarativeTeams: () => TeamDeclaration[]\n /** 声明式 agent 同上。 */\n getDeclarativeAgentProfiles: () => AgentProfile[]\n /** 复用会话配置解析器的工具白名单过滤(Layer 2b)。 */\n resolveTools: (\n explicitTools: AgentTool[] | undefined,\n agentToolNames: string[] | undefined,\n disallowedToolNames?: string[],\n allowSubagents?: boolean,\n ) => AgentTool[]\n /** M-B2: orchestrator.runAgentTurn — 经 TaskExecutor 治理的 member turn 执行器。 */\n runAgentTurn?: (input: AgentTurnInput) => Promise<AgentTurnResult>\n}\n\nexport type RunTeam = (\n team: string,\n input: string,\n signal?: AbortSignal,\n depth?: number,\n) => Promise<{ success: boolean; output?: string; pattern?: string; error?: string }>\n\n/**\n * 团队成员 → SwarmAgentDef(经 resolveAgentProfile + 工具白名单解析)。缺失成员跳过(warn 不抛)。\n *\n * 嵌套:成员 `team:<sub>` → 递归解析子团队 → buildSwarmConfig → 注入 runNested(执行该成员\n * = 递归运行整个子 swarm,结果投影为本回合)。循环(visited)/深度(MAX_NESTING_DEPTH)守卫,\n * 越界跳过 + warn(对齐\"成员不可解析即跳过\"语义)。\n * 路由规则:router:rule 时把声明式 rules 接入 RouterConfig.rules(补齐\"恒走 fallback\"空转)。\n */\nfunction resolveTeam(\n declaration: TeamDeclaration,\n deps: TeamRunnerDeps,\n model: Model,\n visited: Set<string>,\n depth: number,\n globalDepth = 0,\n): ResolvedTeam {\n const profiles = deps.getDeclarativeAgentProfiles()\n const agents: SwarmAgentDef[] = []\n\n for (const member of declaration.agentNames) {\n if (member.startsWith(TEAM_MEMBER_PREFIX)) {\n const subName = member.slice(TEAM_MEMBER_PREFIX.length).trim()\n if (depth + 1 > MAX_NESTING_DEPTH) {\n deps.logger.warn(\n 'Team \"%s\" nesting exceeds max depth %d, member skipped: %s',\n declaration.name,\n MAX_NESTING_DEPTH,\n member,\n )\n continue\n }\n if (visited.has(subName)) {\n deps.logger.warn(\n 'Team \"%s\" nesting cycle detected, member skipped: %s',\n declaration.name,\n member,\n )\n continue\n }\n const subDecl = deps.getDeclarativeTeams().find((t) => t.name === subName)\n if (!subDecl) {\n deps.logger.warn(\n 'Team \"%s\" sub-team not found, member skipped: %s',\n declaration.name,\n member,\n )\n continue\n }\n const subResolved = resolveTeam(\n subDecl,\n deps,\n model,\n new Set([...visited, subName]),\n depth + 1,\n globalDepth + 1,\n )\n if (subResolved.agents.length === 0) {\n deps.logger.warn(\n 'Team \"%s\" sub-team \"%s\" has no resolvable members, skipped',\n declaration.name,\n subName,\n )\n continue\n }\n const subConfig = buildSwarmConfig(subResolved, {\n providerRegistry: deps.providerRegistry,\n hookRegistry: deps.hookRegistry,\n stream: deps.stream,\n logger: deps.logger,\n defaultModel: model,\n runId: `team:${subName}`,\n depth: globalDepth + 1,\n runAgentTurn: deps.runAgentTurn,\n })\n agents.push({\n kind: 'team',\n id: member,\n name: subName,\n description: `Nested team: ${subName}`,\n runNested: async (input, signal) => {\n const r = await createSwarm(subConfig).run(input, { signal })\n return {\n agentId: member,\n messages: [],\n text: r.output,\n tokenUsage: { input: 0, output: 0, cacheRead: 0 },\n durationMs: r.durationMs,\n }\n },\n })\n continue\n }\n\n const profile = resolveAgentProfile(profiles, member)\n if (!profile) {\n agents.push({\n kind: 'agent',\n id: member,\n name: member,\n description: member,\n systemPrompt: assembleGenericSubAgentPrompt(member),\n // B11:无 profile 的通用成员 = 不限制工具(继承全集)。传 undefined(而非 [],后者现表示零工具)。\n tools: toToolDefs(deps.resolveTools(undefined, undefined)),\n maxToolTurns: undefined,\n })\n continue\n }\n agents.push({\n kind: 'agent',\n id: member,\n name: profile.name,\n description: profile.taskTypes[0] ?? profile.name,\n systemPrompt: profile.systemPromptTemplate,\n tools: toToolDefs(deps.resolveTools(undefined, resolveAgentToolNames(profile.defaultTools))),\n maxToolTurns: profile.defaultMaxToolTurns,\n })\n }\n\n return {\n name: declaration.name,\n pattern: declaration.pattern,\n agents,\n pipeline: declaration.pipeline,\n supervisorId: declaration.supervisorId,\n entryAgentId: declaration.entryAgentId,\n maxHandoffDepth: declaration.maxHandoffDepth,\n router: declaration.routerStrategy\n ? {\n strategy: declaration.routerStrategy as RoutingStrategy,\n rules: declaration.rules?.map(\n (r): RouteRule => ({ match: r.keywords, agentId: r.agentId }),\n ),\n fallbackAgentId: declaration.entryAgentId ?? agents[0]?.id,\n }\n : undefined,\n }\n}\n\nexport function createTeamRunner(deps: TeamRunnerDeps): RunTeam {\n return async (team, input, signal, depth) => {\n const declaration = deps.getDeclarativeTeams().find((t) => t.name === team)\n if (!declaration) {\n return { success: false, error: `Team not found: ${team}` }\n }\n\n const model = deps.defaultModel\n if (!model) {\n return { success: false, error: 'No model configured for team execution.' }\n }\n\n if (declaration.routerStrategy === 'llm') {\n return {\n success: false,\n error: `Team \"${team}\": router strategy 'llm' is not supported in declarative teams (it needs a customRouter function). Use 'rule', 'round-robin', or 'random'.`,\n }\n }\n\n const resolved = resolveTeam(declaration, deps, model, new Set([team]), 0, depth ?? 0)\n if (resolved.agents.length === 0) {\n return { success: false, error: `Team \"${team}\" has no resolvable member agents.` }\n }\n\n try {\n const swarm = createSwarm(\n buildSwarmConfig(resolved, {\n providerRegistry: deps.providerRegistry,\n hookRegistry: deps.hookRegistry,\n stream: deps.stream,\n logger: deps.logger,\n defaultModel: model,\n runId: `team:${team}`,\n depth,\n runAgentTurn: deps.runAgentTurn,\n }),\n )\n const result = await swarm.run(input, { signal })\n return { success: true, output: result.output, pattern: result.pattern }\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) }\n }\n }\n}\n","/** Vision delegate: route vision requests to provider with capability checks. */\nimport { complete } from '@x-otto/ai'\nimport { createHash } from 'node:crypto'\n\nimport type { DescribeImagesPort } from '@x-otto/agent'\nimport type {\n IsModelUsable,\n Model,\n ModelRegistry,\n ModelSpec,\n ProviderRegistry,\n WorkflowSlot,\n} from '@x-otto/ai'\nimport type { UserMessage } from '@x-otto/interchange'\nimport type { Logger } from '@x-otto/shared'\n\n/** RFC-181 D3/D4:委托超时(宿主侧独立于主 loop 的 stream retry)。 */\nexport const DESCRIBE_IMAGES_TIMEOUT_MS = 30_000\n\n/** RFC-181 D4:单图描述 token 上限(防代述文本反超被替换图像块的 token 占用)。 */\nconst DESCRIBE_MAX_TOKENS = 800\n\n/** RFC-181 D4:进程内 LRU 缓存上限(sha256(base64) → 描述文本,跨会话共享、不持久化)。 */\nconst CACHE_MAX_ENTRIES = 100\n\nconst DESCRIBE_PROMPT = `You are describing an image for a text-only AI assistant that cannot see images itself. Produce a concise structured description (aim for well under 500 tokens) covering, when applicable:\n- Overall layout (columns/sections, relative position and proportions)\n- Color palette (primary/secondary colors as hex codes)\n- Text content and font-size hierarchy (headings vs body)\n- Interactive component inventory (buttons, inputs, nav items, etc.)\n- Key size ratios/dimensions\n\nIf the image is not a UI/design screenshot, give a normal concise description instead. Do not speculate beyond what's visible.`\n\nexport interface VisionDelegateServiceDeps {\n getModelRegistry: () => ModelRegistry\n providerRegistry: ProviderRegistry\n /** 会话配置里的 vision slot 候选(model_slots.slots.vision),可能未配置。 */\n getVisionSlotCandidates: () => ModelSpec[] | undefined\n isModelUsable: IsModelUsable\n logger: Logger\n}\n\ninterface CacheEntry {\n description: string\n}\n\n/**\n * 视觉委托服务:把图像块交给 vision 模型生成结构化描述。构造函数返回可直接注入\n * `AgentContext.contextTransform?.describeImages` / `WorkLoopContext.contextTransform?.describeImages` 的端口函数。\n */\nexport class VisionDelegateService {\n private readonly cache = new Map<string, CacheEntry>()\n\n constructor(private readonly deps: VisionDelegateServiceDeps) {}\n\n /** 符合 `DescribeImagesPort` 签名,可直接注入引擎端口。 */\n describeImages: DescribeImagesPort = async (images, signal) => {\n const model = await this.resolveVisionModel()\n if (!model) {\n // 全部不可用:返回全 null,引擎侧走占位分支(RFC-181 D2)。\n return images.map(() => null)\n }\n\n return Promise.all(\n images.map((image) => this.describeOne(model, image, signal)),\n )\n }\n\n /**\n * 模型解析顺序(RFC-181 D4):`model_slots.slots.vision` 配置候选(经 usability gate)\n * → 无配置/全不可用时 fallback 扫描 registry 中 `strengths` 含 `vision` 的 usable 模型。\n * 不 fallback 到 default(评审 F5)。\n */\n private async resolveVisionModel(): Promise<Model | undefined> {\n const registry = this.deps.getModelRegistry()\n const configured = this.deps.getVisionSlotCandidates() ?? []\n\n for (const spec of configured) {\n const model = registry.tryResolve(spec)\n if (model && (await this.isUsable(model))) return model\n }\n\n for (const model of registry.getAll()) {\n if (!model.strengths?.includes('vision')) continue\n if (await this.isUsable(model)) return model\n }\n\n return undefined\n }\n\n private async isUsable(model: Model): Promise<boolean> {\n try {\n return (await this.deps.isModelUsable(model)).usable\n } catch {\n return false\n }\n }\n\n private async describeOne(\n model: Model,\n image: { mime: string; source: string; contextHint?: string },\n signal?: AbortSignal,\n ): Promise<string | null> {\n const cacheKey = this.hashImage(image.source)\n const cached = this.cache.get(cacheKey)\n if (cached) return this.formatDescription(model, cached.description)\n\n try {\n const description = await this.callVisionModel(model, image, signal)\n if (!description) return null\n this.rememberInCache(cacheKey, description)\n return this.formatDescription(model, description)\n } catch (error) {\n this.deps.logger.debug({ error, modelId: model.id }, 'vision delegate describe failed')\n return null\n }\n }\n\n private async callVisionModel(\n model: Model,\n image: { mime: string; source: string; contextHint?: string },\n signal?: AbortSignal,\n ): Promise<string | undefined> {\n const provider = this.deps.providerRegistry.get(model.api)\n const userMessage: UserMessage = {\n role: 'user',\n timestamp: Date.now(),\n content: [\n { type: 'text', text: image.contextHint ? `${DESCRIBE_PROMPT}\\n\\nContext: ${image.contextHint}` : DESCRIBE_PROMPT },\n { type: 'image', mime: image.mime, source: image.source },\n ],\n }\n\n const timeoutController = new AbortController()\n const timer = setTimeout(() => timeoutController.abort(), DESCRIBE_IMAGES_TIMEOUT_MS)\n const combinedSignal = signal\n ? AbortSignal.any([signal, timeoutController.signal])\n : timeoutController.signal\n\n try {\n const result = await complete(\n model,\n provider,\n {\n model,\n systemPrompt: '',\n messages: [userMessage],\n maxTokens: DESCRIBE_MAX_TOKENS,\n },\n { signal: combinedSignal },\n )\n return result.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text)\n .join('')\n .trim() || undefined\n } finally {\n clearTimeout(timer)\n }\n }\n\n private formatDescription(model: Model, description: string): string {\n return `[image seen by vision model ${model.id}]\\n${description}`\n }\n\n private hashImage(source: string): string {\n return createHash('sha256').update(source).digest('hex')\n }\n\n private rememberInCache(key: string, description: string): void {\n if (this.cache.size >= CACHE_MAX_ENTRIES) {\n const oldest = this.cache.keys().next().value\n if (oldest !== undefined) this.cache.delete(oldest)\n }\n this.cache.set(key, { description })\n }\n}\n\n/**\n * 从 `model_slots.slots.vision` 配置提取候选数组(可能是单个 spec 或数组,也可能未配置)。\n * 与 `SessionConfigResolver.slotCandidates` 的读取方式一致(modelSlots?.slots?.[slot])。\n */\nexport function extractVisionSlotCandidates(\n modelSlots: { slots?: Partial<Record<WorkflowSlot, ModelSpec | ModelSpec[]>> } | undefined,\n): ModelSpec[] | undefined {\n const spec = modelSlots?.slots?.vision\n if (!spec) return undefined\n return Array.isArray(spec) ? spec : [spec]\n}\n","/** Panel global runtime: shared state/events for all plugin panels. */\nexport function installGlobalPanelRuntime<T extends object>(\n globalKey: string,\n buildRuntime: () => T,\n): void {\n // 幂等:全局已注入则只重绑、不重定义——目标全局键是 non-configurable,\n // 同进程再次 defineProperty 会抛 `Cannot redefine property`(多 App / 测试套件场景)。\n if ((globalThis as Record<string, unknown>)[globalKey]) {\n return\n }\n Object.defineProperty(globalThis, globalKey, {\n value: Object.freeze(buildRuntime()),\n writable: false,\n configurable: false,\n })\n}\n","/** Panel runtime: render lifecycle for plugin UI panels within TUI. */\n\nimport { installGlobalPanelRuntime } from './panel-global-runtime'\n\ninterface McpToolCallFn {\n (\n serverName: string,\n toolName: string,\n params?: Record<string, unknown>,\n ): Promise<{ content: { type: string; text?: string }[]; isError?: boolean }>\n}\n\ninterface PanelDataState<T = unknown> {\n data: T | undefined\n loading: boolean\n error: string | undefined\n callTool: (action: string, params?: Record<string, unknown>) => Promise<{ success: boolean; error?: string }>\n}\n\n/**\n * PanelRuntime —— 单例运行时,存储 callMcpTool 引用。\n * usePluginData hook 通过全局对象获取此引用。\n */\nclass PanelRuntime {\n private _callMcpTool: McpToolCallFn | null = null\n\n get callMcpTool(): McpToolCallFn {\n if (!this._callMcpTool) {\n throw new Error('PanelRuntime not installed — call installPanelRuntime() during App startup')\n }\n return this._callMcpTool\n }\n\n /**\n * 安装运行时——在 App 构造时调用一次。\n * 安装后将 callMcpTool 注入全局对象,供插件面板的编译产物使用。\n */\n install(callMcpTool: McpToolCallFn): void {\n // 始终重绑(同进程第二个 App 指向最新 MCP controller)。\n this._callMcpTool = callMcpTool\n // 全局注入:插件面板通过 `globalThis.__ottoPanelRuntime.usePluginData` 调用。\n // 委托到实例方法(而非捕获 callMcpTool 值),使后续重绑生效。\n // 冻结防止面板代码替换运行时对象本身(注:面板仍是 in-process 受信代码,非沙箱)。\n installGlobalPanelRuntime('__ottoPanelRuntime', () => ({\n callMcpTool: (server: string, tool: string, params?: Record<string, unknown>) =>\n this.callMcpTool(server, tool, params),\n usePluginData: usePluginDataFactory(this),\n }))\n }\n\n}\n\n/**\n * usePluginData 工厂——返回一个绑定到指定 PanelRuntime 实例的 React hook。\n *\n * 该 hook 在插件面板组件中被调用(经 esbuild banner 注入为全局引用)。\n * 内部使用 React.useState/useEffect/useCallback 实现轮询刷新。\n *\n * @param rt PanelRuntime 实例\n */\nfunction usePluginDataFactory(rt: PanelRuntime) {\n return function usePluginData<T = unknown>(\n mcpServer: string,\n mcpTool: string,\n refreshIntervalMs = 5000,\n ): PanelDataState<T> {\n const React = globalThis.React\n\n if (!React || typeof React.useState !== 'function') {\n throw new Error(\n 'usePluginData requires React runtime. Panel components must be rendered in a React (Ink) context.',\n )\n }\n\n const [data, setData] = React.useState(undefined) as [T | undefined, (v: T | undefined) => void]\n const [loading, setLoading] = React.useState(true) as [boolean, (v: boolean) => void]\n const [error, setError] = React.useState(undefined) as [string | undefined, (v: string | undefined) => void]\n\n const refresh = React.useCallback(async () => {\n setLoading(true)\n try {\n const result = await rt.callMcpTool(mcpServer, mcpTool, {})\n if (result.isError) {\n setError('MCP tool returned error')\n return\n }\n const textContent = result.content?.find(\n (c: { type: string; text?: string }) => c.type === 'text',\n )\n if (textContent?.text) {\n try {\n setData(JSON.parse(textContent.text) as T)\n } catch {\n setData(textContent.text as unknown as T)\n }\n setError(undefined)\n } else {\n setData(undefined)\n setError('MCP tool returned no text content')\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setLoading(false)\n }\n }, [mcpServer, mcpTool]) as () => Promise<void>\n\n React.useEffect(() => {\n void refresh()\n const timer = setInterval(() => void refresh(), refreshIntervalMs)\n return () => clearInterval(timer)\n }, [refresh, refreshIntervalMs])\n\n const callTool = React.useCallback(\n async (action: string, params?: Record<string, unknown>) => {\n try {\n const result = await rt.callMcpTool(mcpServer, action, params ?? {})\n return { success: !result.isError }\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : String(err) }\n }\n },\n [mcpServer],\n ) as (action: string, params?: Record<string, unknown>) => Promise<{ success: boolean; error?: string }>\n\n return { data, loading, error, callTool }\n }\n}\n\n/** 全局单例。 */\nconst panelRuntime = new PanelRuntime()\n\n/**\n * 安装插件面板运行时到全局对象。\n * 在 App 启动时调用一次(`packages/coding/src/app/app.ts` 构造时)。\n */\nexport function installPanelRuntime(callMcpTool: McpToolCallFn): void {\n panelRuntime.install(callMcpTool)\n}\n","/** Panel backend runtime: Node.js process for serving plugin panel APIs. */\nimport { installGlobalPanelRuntime } from './panel-global-runtime'\n\nexport type PanelBackendStatus = 'connecting' | 'ready' | 'crashed' | 'disabled'\n\n/**\n * `backend/` wire 前缀(RFC-165 D1:backend facet 的 method 名统一带此前缀,区别于 `mcp`\n * facet 的模型可见工具)。面板作者经 `usePanelBackend().call()` 传入的是**不带前缀**的\n * 简名(如 `'config.save'`,milestone 文档全部示例与 `definePanelBackend` SDK 的\n * `backend/${name}` 注册命名对齐),hook 在转发给 `PanelBackendController.call()`\n * (其契约是转发到 wire 层、不做前缀猜测,见 `panel-backend-controller.test.ts`/\n * `rfc165-panel-backend-e2e.test.ts` 均以带前缀方法名驱动)之前统一补齐前缀——\n * 之前遗漏此步导致面板调用报 `unknown method: config.save`(真实用户回归,2026-07-19 修复)。\n * 幂等:调用方若已带前缀(防御性场景)不重复添加。\n */\nconst BACKEND_METHOD_PREFIX = 'backend/'\n\nfunction toBackendWireMethod(method: string): string {\n return method.startsWith(BACKEND_METHOD_PREFIX) ? method : `${BACKEND_METHOD_PREFIX}${method}`\n}\n\nexport interface PanelBackendRuntimeFns {\n call: (\n pluginId: string,\n method: string,\n params: Record<string, unknown>,\n opts?: { operationId?: string; signal?: AbortSignal },\n ) => Promise<unknown>\n cancel: (pluginId: string, operationId: string) => void\n subscribeState: (pluginId: string, cb: (params: Record<string, unknown>) => void) => () => void\n getStatus: (pluginId: string) => PanelBackendStatus\n onStatusChange: (pluginId: string, cb: (status: PanelBackendStatus) => void) => () => void\n}\n\n/**\n * PanelBackendRuntime —— 单例运行时,存储 `PanelBackendController` 的方法引用。\n * usePanelBackend hook 通过全局对象获取此引用。\n */\nclass PanelBackendRuntime {\n private fns: PanelBackendRuntimeFns | null = null\n\n get(): PanelBackendRuntimeFns | null {\n return this.fns\n }\n\n /**\n * 安装运行时——在 App 构造时调用一次。\n * 安装后将 usePanelBackend 注入全局对象,供插件面板的编译产物使用。\n */\n install(fns: PanelBackendRuntimeFns): void {\n // 始终重绑(同进程第二个 App 指向最新 PanelBackendController)。\n this.fns = fns\n installGlobalPanelRuntime('__ottoPanelBackendRuntime', () => ({\n usePanelBackend: usePanelBackendFactory(this),\n }))\n }\n}\n\nfunction currentPanelPluginId(): string | undefined {\n return (globalThis as { __ottoCurrentPanelPluginId?: string }).__ottoCurrentPanelPluginId\n}\n\nexport interface UsePanelBackendResult<TState> {\n /**\n * `opts.operationId`(可选):若该调用可能需要中途取消(D6),面板作者自行生成并持有\n * 一个稳定 id(如存进 `useRef`),随 `call()` 一起传入;随后可用同一 id 调用 `cancel()`。\n * 必须由调用方在发起调用**之前**就准备好该 id——等 `call()` 的 Promise resolve/reject\n * 后才能拿到 id 的设计在时序上不可能支持\"调用进行中取消\"(真实约束,非任意选择)。\n */\n call: <TParams extends Record<string, unknown>, TResult = unknown>(\n method: string,\n params: TParams,\n opts?: { operationId?: string },\n ) => Promise<TResult>\n cancel: (operationId: string) => void\n state: TState | undefined\n status: PanelBackendStatus\n}\n\n/**\n * usePanelBackend 工厂——返回一个绑定到指定 PanelBackendRuntime 实例的 React hook。\n *\n * 该 hook 在插件面板组件中被调用(经 esbuild banner 注入为全局引用)。内部使用\n * `globalThis.React`(类型声明见 `global.d.ts`)的 useState/useEffect/useCallback\n * (对齐 usePluginData 模式,不 import React——`packages/coding` 不依赖 react 包)。\n *\n * 无有效 `pluginId`(面板上下文未注入,防御性场景)或 runtime 未安装(非 TUI 宿主场景)\n * 时,`status` 恒为 `'disabled'`、`call` reject 明确错误,不崩溃面板(fail-soft)。\n */\nfunction usePanelBackendFactory(rt: PanelBackendRuntime) {\n return function usePanelBackend<TState = unknown>(): UsePanelBackendResult<TState> {\n const React = globalThis.React\n if (!React || typeof React.useState !== 'function') {\n throw new Error(\n 'usePanelBackend requires React runtime. Panel components must be rendered in a React (Ink) context.',\n )\n }\n\n const pluginId = currentPanelPluginId()\n const fns = rt.get()\n\n const [state, setState] = React.useState(undefined) as [TState | undefined, (v: TState | undefined) => void]\n const [status, setStatus] = React.useState(\n pluginId && fns ? fns.getStatus(pluginId) : 'disabled',\n ) as [PanelBackendStatus, (v: PanelBackendStatus) => void]\n\n React.useEffect(() => {\n if (!pluginId || !fns) return\n setStatus(fns.getStatus(pluginId))\n const unsubscribeState = fns.subscribeState(pluginId, (params) => {\n setState(params as TState)\n })\n const unsubscribeStatus = fns.onStatusChange(pluginId, setStatus)\n return () => {\n unsubscribeState()\n unsubscribeStatus()\n }\n }, [pluginId])\n\n const call = React.useCallback(\n async (method: string, params: Record<string, unknown>, opts?: { operationId?: string }) => {\n if (!pluginId || !fns) {\n throw new Error(\n 'usePanelBackend: no plugin context available (panel must be rendered inside a plugin detail view with backend runtime installed)',\n )\n }\n return fns.call(pluginId, toBackendWireMethod(method), params, opts)\n },\n [pluginId],\n ) as UsePanelBackendResult<TState>['call']\n\n const cancel = React.useCallback(\n (operationId: string) => {\n if (!pluginId || !fns) return\n fns.cancel(pluginId, operationId)\n },\n [pluginId],\n ) as (operationId: string) => void\n\n return { call, cancel, state, status }\n }\n}\n\n/** 全局单例。 */\nconst panelBackendRuntime = new PanelBackendRuntime()\n\n/**\n * 安装插件面板后端运行时到全局对象。\n * 在 App 启动时调用一次(`packages/coding/src/app/app.ts` 构造时)。\n */\nexport function installPanelBackendRuntime(fns: PanelBackendRuntimeFns): void {\n panelBackendRuntime.install(fns)\n}\n","/**\n * engine-locale.ts —— 引擎侧当前生效语言解析(RFC-141 T141-05,接线 RFC-138-A 尾债)。\n *\n * `Setting.language` 允许 `'auto'`(跟随宿主机环境检测)或具体语言标识(如 `'zh'`/`'en'`)。\n * 与 `@x-otto/tui` 的 `detectLanguage()` 做同等的环境变量检测(`LC_ALL`/`LC_MESSAGES`/`LANG`\n * 含 `zh` 则判定中文,否则英文)——不能直接 import `@x-otto/tui`(`packages/coding` 与\n * `packages/tui` 无依赖关系,见 RFC-138 D1 已确立的架构边界),故内联最小实现。\n */\n\n/** 自动检测语言:读环境 LC_ALL/LC_MESSAGES/LANG,含 zh 即中文,否则英文。 */\nfunction detectEngineLocale(): string {\n const l = process.env.LC_ALL || process.env.LC_MESSAGES || process.env.LANG || ''\n return /zh/i.test(l) ? 'zh' : 'en'\n}\n\n/** 解析 `Setting.language` 为传给 `PluginI18nRegistry.setLocale()` 的具体语言标识。 */\nexport function resolveEngineLocale(language: string | undefined): string {\n if (!language || language === 'auto') return detectEngineLocale()\n return language\n}\n","import type { AuthStore, ModelRegistry, ProviderRegistry, WireProtocolRegistry } from '@x-otto/ai'\nimport type { Logger } from '@x-otto/shared'\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport { evaluatePluginTrust } from '@x-otto/plugin'\nimport {\n loadPluginProviders,\n unloadPluginProviders,\n namespacedOAuthIds,\n namespacedProviderApis,\n} from '../plugin/provider-loader'\n\n/** PluginProviderManager 依赖面(同 PluginModuleManager 的 DI 范式,M100)。 */\nexport interface PluginProviderManagerDeps {\n providerRegistry: ProviderRegistry\n authStore: AuthStore\n modelRegistry: ModelRegistry\n /** RFC-148 D3:声明式 provider 装载器(provider-loader.ts)惰性查表目标。 */\n wireProtocolRegistry: WireProtocolRegistry\n activePlugins: () => DiscoveredPlugin[]\n /** RFC-124 C1/C2:信任门拒绝时的结构化告警(同 PluginModuleManagerDeps.logger 范式)。 */\n logger: Logger\n}\n\n/**\n * 插件声明式 provider(`contributes.providers`)的加载/卸载单源(RFC-105 M105-2-03b)。\n *\n * 幂等 reload:每次调用先卸载上一轮已加载插件的 provider/model/envKey/descriptor,\n * 再对当前 `activePlugins()` 逐个装载——与 `PluginModuleManager.reload()` 同构(先注销后重注册,\n * 避免插件被禁用/卸载后残留僵尸 provider)。\n */\nexport class PluginProviderManager {\n private loadedPluginIds: string[] = []\n private previousSnapshot: Array<{ id: string; manifest: DiscoveredPlugin['manifest'] }> = []\n\n constructor(private readonly deps: PluginProviderManagerDeps) {}\n\n /**\n * 幂等 reload:先卸载上一轮快照记录的全部插件 provider(保证禁用/卸载插件不残留僵尸),\n * 再对当前 `activePlugins()` 逐个装载并记录新快照。\n *\n * `pendingCapabilityPluginIds`(RFC-125 D5):因 `pendingCapabilities.includes('provider')`\n * 被跳过装载的插件 id 列表——不同于 `gated`(项目级未信任,需 `/trust plugin` 布尔信任),\n * 这批插件已通过布尔信任门(或天然 scope==='user'),只是高危能力尚未被显式授予\n * (`grantPluginCapabilities`)。此前该信号只留一条 `logger.warn`,从未传递到任何用户可见\n * 界面(见 RFC-125 §0.2/§0.4)——调用方据此列表在 `/model` 等界面呈现\"待授权\"提示。\n */\n async reload(): Promise<{ registered: number; rejected: number; pendingCapabilityPluginIds: string[] }> {\n // RFC-139 D2:差量 reload(原为 unload-all → load-all)。\n //\n // 事故背景(RFC-139 §0/D2):全量先卸后装会让\"未变化\"的插件也经历\n // `oauthRegistry.unregister → register` 的窗口——reload 是 async 逐插件 await,\n // 事件循环在窗口内可调度并发的流式请求,`resolveAuth` 查 `oauth.get()` 为\n // undefined 直接返回 null → 瞬时 PROVIDER_AUTH_MISSING(trace 中零星单发失效)。\n // 改为三段 diff 后 unchanged 插件的注册表条目全程不动,窗口归零。\n //\n // 判等用 manifest 全量 JSON 签名:manifest 是纯 JSON、体量小、reload 低频;\n // 同 id 内容变化(changed)按 removed+added 处理(先卸旧再装新)。\n let registered = 0\n let rejected = 0\n const pendingCapabilityPluginIds: string[] = []\n // 第一遍:过信任门,算出目标插件集(desired)\n const desired = new Map<string, DiscoveredPlugin['manifest']>()\n for (const plugin of this.deps.activePlugins()) {\n // RFC-122 M122-2 review fix:此前只检查 `providers` 数组,oauth-only 插件被静默跳过。\n // 插件可能只贡献 OAuth(`contributes.oauth`)而不贡献任何 provider(如纯 OAuth 代理插件)。\n // 此时 OAuth 条目在 `loadPluginProviders` 内独立装载、provider 条目为空则跳过循环。\n const hasOauth = !!plugin.manifest.contributes?.oauth?.length\n const hasProviders = !!plugin.manifest.contributes?.providers?.length\n if (!hasOauth && !hasProviders) continue\n // RFC-124 C1/C2 修复:此前本装载路径完全没有信任门检查(只有 renderer/menu/statusItems/\n // mcp/skills/panels 等轴才检查 evaluatePluginTrust),provider 轴可被未信任的项目级插件\n // 绕过信任确认直接注册——包括纯 manifest 声明(无 plugin.ts、无任何目录内容)的场景。\n // gated:项目级未信任插件整批跳过(同 renderer-loader.ts 信任基线)。\n // pendingCapabilities:即使已布尔信任,manifest 声明的 'provider' 高危能力若未被显式\n // 授予(trust-store 的 grantedCapabilities),同样跳过——防止「已信任插件后续版本新增\n // provider 声明」这类静默特权升级(RFC-105 D8/R9 对抗评审 F2 的设计意图,此前从未被\n // 装载路径强制执行)。\n const verdict = evaluatePluginTrust(plugin)\n if (verdict.gated) {\n this.deps.logger.warn(\n { pluginId: plugin.id },\n 'project plugin not trusted, skipping declarative providers (fail-closed, RFC-124 C1)',\n )\n rejected += (plugin.manifest.contributes?.providers?.length ?? 0) + (plugin.manifest.contributes?.oauth?.length ?? 0)\n continue\n }\n if (verdict.pendingCapabilities.includes('provider')) {\n this.deps.logger.warn(\n { pluginId: plugin.id },\n 'plugin capability \"provider\" declared but not yet granted, skipping declarative providers (fail-closed, RFC-124 C2)',\n )\n rejected += (plugin.manifest.contributes?.providers?.length ?? 0) + (plugin.manifest.contributes?.oauth?.length ?? 0)\n pendingCapabilityPluginIds.push(plugin.id)\n continue\n }\n desired.set(plugin.id, plugin.manifest)\n }\n\n // 第二遍:三段 diff——removed 卸载、changed 先卸后装、added 装载、unchanged 不动。\n const sig = (m: DiscoveredPlugin['manifest']): string => JSON.stringify(m)\n const snapshot: Array<{ id: string; manifest: DiscoveredPlugin['manifest'] }> = []\n\n // RFC-140 D7/T18(review P1-8):changed 插件(非 removed)新旧 manifest 都声明的\n // OAuth id 交集——这些 id 走 T18 的原子替换路径(unload 跳过 unregister,随后\n // load 用 replace() 覆盖),消除先删后建之间的 await 间隙窗口。removed 插件(新\n // manifest 里已不存在,next===undefined)没有\"即将重装\"的概念,不适用本优化,\n // 其 OAuth 条目走原有的直接 unregister。\n const atomicReplaceIdsByPlugin = new Map<string, Set<string>>()\n // 终局 review 2026-07-21 A-1:与 OAuth id 交集同构的 provider 工厂 api 交集——changed\n // 插件新旧 manifest 都会注册 wire 工厂的 api 走 replacePluginProvider 原子覆盖路径\n // (unload 跳过 unregisterBySource,load 用 replace() 覆盖),消除先删后建之间的\n // await 间隙内并发流请求命中 PROVIDER_NOT_FOUND 的窗口。\n const atomicReplaceApisByPlugin = new Map<string, Set<string>>()\n for (const prev of this.previousSnapshot) {\n const next = desired.get(prev.id)\n if (next !== undefined && sig(next) !== sig(prev.manifest)) {\n const prevIds = namespacedOAuthIds(prev.id, prev.manifest)\n const nextIds = namespacedOAuthIds(prev.id, next)\n const surviving = new Set([...prevIds].filter((id) => nextIds.has(id)))\n if (surviving.size > 0) atomicReplaceIdsByPlugin.set(prev.id, surviving)\n const prevApis = namespacedProviderApis(prev.id, prev.manifest)\n const nextApis = namespacedProviderApis(prev.id, next)\n const survivingApis = new Set([...prevApis].filter((api) => nextApis.has(api)))\n if (survivingApis.size > 0) atomicReplaceApisByPlugin.set(prev.id, survivingApis)\n }\n }\n\n for (const prev of this.previousSnapshot) {\n const next = desired.get(prev.id)\n if (next === undefined || sig(next) !== sig(prev.manifest)) {\n // removed 或 changed:按上一轮 manifest 卸载(load/unload 必须同钥匙,见 provider-loader 注释)\n await unloadPluginProviders(\n prev.id,\n prev.manifest,\n this.deps,\n atomicReplaceIdsByPlugin.get(prev.id),\n atomicReplaceApisByPlugin.get(prev.id),\n )\n }\n }\n\n const prevSigById = new Map(this.previousSnapshot.map((s) => [s.id, sig(s.manifest)]))\n for (const [id, manifest] of desired) {\n if (prevSigById.get(id) === sig(manifest)) {\n // unchanged:注册表条目全程未动,直接进入新快照。registered 计数仍计入\n // (该插件的 provider 当前确实处于已注册状态——返回值语义是\"本轮生效的注册数\",\n // 消费方 toast 据此显示,不能因跳过重装而少报)。\n snapshot.push({ id, manifest })\n registered += (manifest.contributes?.providers?.length ?? 0)\n continue\n }\n // added 或 changed(旧注册已在上一段卸载)。changed 插件传入本轮算出的\n // atomicReplaceIdsByPlugin/atomicReplaceApisByPlugin——分别驱动 T18 的\n // OAuthRegistry.replace() 与 A-1 的 replacePluginProvider() 原子覆盖路径。\n const result = loadPluginProviders(\n id,\n manifest,\n this.deps,\n atomicReplaceIdsByPlugin.get(id),\n atomicReplaceApisByPlugin.get(id),\n )\n registered += result.registered.length\n rejected += result.rejected.length\n snapshot.push({ id, manifest })\n }\n this.previousSnapshot = snapshot\n this.loadedPluginIds = snapshot.map((s) => s.id)\n return { registered, rejected, pendingCapabilityPluginIds }\n }\n\n /** 已加载声明式 provider 的插件 id 列表(观测用)。 */\n get activePluginIds(): readonly string[] {\n return this.loadedPluginIds\n }\n\n /** stop 清理:卸载全部(防泄漏进 shared registry,同 PluginModuleManager.cleanup)。 */\n async cleanup(): Promise<void> {\n for (const { id, manifest } of this.previousSnapshot) {\n await unloadPluginProviders(id, manifest, this.deps)\n }\n this.previousSnapshot = []\n this.loadedPluginIds = []\n }\n}\n","/**\n * buildInspectDevtools — inspect 模式 devtools + 日志转发。\n *\n * inspect 时建 Devtools,并把名字匹配 loggerName 的结构化日志转发给它;否则全 null。返回 devtools\n * 实例 + 日志监听退订(App.stop 解绑 globalLogSubscription)。\n */\nimport { attachLogListener } from '@x-otto/shared'\nimport { Devtools } from '@x-otto/devtools'\n\nexport interface InspectDevtools {\n devtools: Devtools | null\n logSubscription: ReturnType<typeof attachLogListener> | null\n}\n\nexport function buildInspectDevtools(\n inspect: boolean | undefined,\n loggerName: string,\n): InspectDevtools {\n if (!inspect) return { devtools: null, logSubscription: null }\n const devtools = new Devtools()\n const logSubscription = attachLogListener((data) => {\n if (\n typeof data === 'object' &&\n data !== null &&\n 'name' in data &&\n 'level' in data &&\n 'msg' in data\n ) {\n const log = data as { name: string; level: string; msg: string }\n if (log.name === loggerName) {\n devtools.sendLog(log)\n }\n }\n })\n return { devtools, logSubscription }\n}\n","import {\n PermissionPolicyRegistry,\n PermissionAuditLog,\n createPermissionGuardHook,\n createPermissionToolSetsFromRegistry,\n createPermissionRegistry,\n} from '@x-otto/hooks'\nimport type { HookRegistry } from '@x-otto/hooks'\nimport type { ToolRegistry } from '@x-otto/tools'\n\n/**\n * 权限系统控制器(P4 从 App 提取)。\n *\n * 根据 ToolRegistry 的工具集创建权限策略注册表 + 审计日志,\n * 并注册 permission-guard hook。\n */\nexport class PermissionController {\n readonly registry: PermissionPolicyRegistry\n readonly auditLog: PermissionAuditLog\n\n constructor(\n toolRegistry: ToolRegistry,\n hookRegistry: HookRegistry,\n ) {\n const permissionToolSets = createPermissionToolSetsFromRegistry(toolRegistry.getAll())\n this.auditLog = new PermissionAuditLog()\n this.registry = createPermissionRegistry({\n toolSets: permissionToolSets,\n })\n hookRegistry.register(createPermissionGuardHook(this.registry, this.auditLog))\n }\n}\n","/** Context sources: plugin-supplied system prompt sections and context injectors. */\nimport { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { evaluatePluginTrust, type DiscoveredPlugin } from '@x-otto/plugin'\nimport type { ContextSource } from '@x-otto/runtime'\nimport { createLogger } from '@x-otto/shared'\n\nimport { isWithinPluginDir } from './path-guard'\n\nconst logger = createLogger('@x-otto/coding:plugin-context-sources')\n\n/** 注入内容大小上限(对齐既有 memory 注入截断量级),超过则截断并标记。 */\nconst MAX_CONTENT_BYTES = 64 * 1024\n\n/** 读取 `content.file` 引用的文件内容,越界/读取失败返回 undefined(fail-soft,记录日志)。 */\nfunction readContentFile(pluginId: string, pluginDir: string, file: string): string | undefined {\n const resolved = resolve(pluginDir, file)\n if (!isWithinPluginDir(resolved, pluginDir)) {\n logger.warn({ pluginId, file }, 'contextSources.content.file resolves outside plugin dir, skipped')\n return undefined\n }\n try {\n const raw = readFileSync(resolved, 'utf8')\n if (Buffer.byteLength(raw, 'utf8') > MAX_CONTENT_BYTES) {\n return `${raw.slice(0, MAX_CONTENT_BYTES)}\\n<!-- truncated -->`\n }\n return raw\n } catch (err) {\n logger.warn({ pluginId, file, err: String(err) }, 'contextSources.content.file read failed, skipped')\n return undefined\n }\n}\n\n/**\n * 收集全部已发现插件的 `contextSources` 贡献,产出可直接 `register()` 的 `ContextSource[]`。\n *\n * 门控(对齐 RFC-129 D5 安全 + 既有 status-items.ts 信任基线纪律):\n * - 项目级未信任插件(`evaluatePluginTrust(...).gated`)跳过。\n * - manifest 未声明 `capabilities: [\"context\"]` 的插件跳过其 `contextSources` 轴(fail-closed,R6)。\n *\n * `content` 为静态字符串时直接使用;为 `{file}` 时**每次 contribute() 调用都重新读取**\n * (不在本函数/装载时缓存快照,见 RFC-129 D5\"随磁盘内容变化\"设计意图)——因此本函数只做\n * 门控与装配,实际文件 I/O 延迟到 `ContextSource.contribute()` 被调用的那一刻。\n */\nexport function collectPluginContextSources(plugins: readonly DiscoveredPlugin[]): ContextSource[] {\n const out: ContextSource[] = []\n for (const plugin of plugins) {\n if (evaluatePluginTrust(plugin).gated) continue\n const contributions = plugin.manifest.contributes?.contextSources\n if (!contributions?.length) continue\n if (!plugin.manifest.capabilities?.includes('context')) {\n logger.warn(\n { pluginId: plugin.id },\n 'plugin declares contextSources but not capabilities:[\"context\"], skipped (fail-closed)',\n )\n continue\n }\n for (const contribution of contributions) {\n out.push({\n id: `${plugin.id}:${contribution.id}`,\n priority: contribution.priority,\n contribute: () => {\n const body =\n typeof contribution.content === 'string'\n ? contribution.content\n : readContentFile(plugin.id, plugin.dir, contribution.content.file)\n if (body == null) return undefined\n return `<!-- plugin:${plugin.id} -->\\n${body}`\n },\n })\n }\n }\n return out\n}\n","/** Process lifecycle hook registration for subshell/server integrations. */\nimport { globalProcessRegistry, installProcessReaper, installAgentJobReaper } from '@x-otto/runtime'\n\nimport type { Logger } from '@x-otto/shared'\n\nexport interface ProcessLifecycleDeps {\n /** 该 sessionId 是否仍有活跃会话(无则丢弃事件,不发 hook)。 */\n hasSession: (sessionId: string) => boolean\n /** 转发 hook(通常 hookRegistry.emit)。 */\n emitHook: (\n timing: 'process.spawned' | 'process.exited',\n input: Record<string, unknown>,\n ) => Promise<unknown>\n logger: Logger\n /**\n * 主仓根(agent job worktree 的源)。传入后 installAgentJobReaper 会在启动时先扫描一遍\n * SIGKILL/OOM 残留的孤儿 worktree(review J3);缺省跳过扫描(行为等同修复前)。\n */\n repoRoot?: string\n}\n\n/** 装 reaper + 订阅进程生命周期 → hook。返回退订函数数组(供 App.stop 解绑)。 */\nexport function wireProcessLifecycleHooks(deps: ProcessLifecycleDeps): Array<() => void> {\n installProcessReaper()\n installAgentJobReaper(undefined, deps.repoRoot)\n\n const emitProcessHook = (\n timing: 'process.spawned' | 'process.exited',\n input: Record<string, unknown>,\n ): void => {\n if (!deps.hasSession(input.sessionId as string)) {\n return\n }\n deps.emitHook(timing, input).catch((err) => {\n deps.logger.warn(\n { err: err instanceof Error ? err.message : String(err), timing },\n 'Failed to emit process lifecycle hook',\n )\n })\n }\n\n return [\n globalProcessRegistry.onSpawn((p) => {\n deps.logger.info(\n { id: p.id, pid: p.pid, command: p.command, port: p.port },\n 'Background process started',\n )\n emitProcessHook('process.spawned', {\n sessionId: p.sessionId,\n processId: p.id,\n pid: p.pid,\n command: p.command,\n port: p.port,\n })\n }),\n globalProcessRegistry.onExit((p) => {\n deps.logger.info(\n { id: p.id, pid: p.pid, status: p.status, exitCode: p.exitCode },\n 'Background process exited',\n )\n emitProcessHook('process.exited', {\n sessionId: p.sessionId,\n processId: p.id,\n pid: p.pid,\n command: p.command,\n status: p.status,\n exitCode: p.exitCode,\n })\n }),\n ]\n}\n","/** Builtin sigil resolvers: file-range, def. */\nimport { spawnSync } from 'node:child_process'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { isAbsolute, resolve } from 'node:path'\n\nimport type { SigilResolver } from '@x-otto/interchange'\n\nconst TIMEOUT_MS = 10_000\nconst MAX_BUFFER = 4 * 1024 * 1024\nconst MAX_OUTPUT = 20_000\n\n/** 只读 git 执行器(测试可注入)。返回 null 表示失败/非 git 仓。 */\nexport type GitRunner = (args: readonly string[], cwd: string) => string | null\n\nconst defaultGitRunner: GitRunner = (args, cwd) => {\n const r = spawnSync('git', [...args], { cwd, timeout: TIMEOUT_MS, encoding: 'utf-8', maxBuffer: MAX_BUFFER })\n if (r.error || r.status !== 0) return null\n return r.stdout ?? ''\n}\n\nfunction truncate(s: string): string {\n return s.length <= MAX_OUTPUT ? s : `${s.slice(0, MAX_OUTPUT)}\\n[...truncated ${s.length - MAX_OUTPUT} chars...]`\n}\n\n\n/** 读 cwd 内文件的 [start,end] 行(1-based,含端点);越界/不存在/越出 cwd/二进制 → null。 */\nexport function readFileRange(cwd: string, relPath: string, start: number, end: number): string | null {\n const root = resolve(cwd)\n const abs = isAbsolute(relPath) ? relPath : resolve(root, relPath)\n if (abs !== root && !abs.startsWith(`${root}/`)) return null\n if (!existsSync(abs) || !statSync(abs).isFile()) return null\n let content: string\n try {\n content = readFileSync(abs, 'utf-8')\n } catch {\n return null\n }\n if (content.includes(String.fromCharCode(0))) return null\n const lines = content.split('\\n')\n const s = Math.max(1, start)\n if (s > lines.length) return null\n const e = Math.min(lines.length, end > 0 ? end : start)\n return truncate(lines.slice(s - 1, e).join('\\n'))\n}\n\nconst DEF_KEYWORDS = 'function|class|interface|type|enum|const|let|var|def|fn|struct|func'\n\n/** git grep 找符号定义首处,读其往下 around+1 行;非 git/无命中 → null。 */\nexport function findSymbolDef(cwd: string, name: string, run: GitRunner, around = 14): string | null {\n const safe = name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const out = run(['grep', '-nE', `(${DEF_KEYWORDS})[[:space:]]+${safe}`], cwd)\n if (out === null || !out.trim()) return null\n const m = /^(.+?):(\\d+):/.exec(out.split('\\n')[0] ?? '')\n if (!m) return null\n const line = parseInt(m[2]!, 10)\n return readFileRange(cwd, m[1]!, line, line + around)\n}\n\nexport interface BuiltinResolverDeps {\n /** 只读 git 执行器(测试注入)。 */\n gitRunner?: GitRunner\n}\n\n/**\n * 构造两个内置 resolver:file-range / def(正则型引用,id 由 sigil-expand.ts\n * collectChips 固定映射)。\n */\nexport function createBuiltinResolvers(deps?: BuiltinResolverDeps): SigilResolver[] {\n const run = deps?.gitRunner ?? defaultGitRunner\n\n return [\n {\n id: 'file-range',\n resolve: (param, ctx) => {\n // param 形如 `path:12` / `path:12-20`(sigil-expand collectChips 已去前导 @)\n const m = /^([\\w./\\-]+):(\\d+)(?:-(\\d+))?$/.exec(param)\n if (!m) return ''\n const body = readFileRange(ctx.workspaceDir, m[1]!, parseInt(m[2]!, 10), m[3] ? parseInt(m[3], 10) : 0)\n if (body === null) return '' // 空串 = 原样保留引用(对齐原实现 return m)\n const path = m[1]!\n const ext = path.includes('.') ? (path.split('.').pop() ?? '') : ''\n const range = m[3] ? `${m[2]}-${m[3]}` : m[2]\n return `\\n\\n${path}:${range}:\\n\\`\\`\\`${ext}\\n${body}\\n\\`\\`\\`\\n`\n },\n },\n {\n id: 'def',\n resolve: (param, ctx) => {\n const body = findSymbolDef(ctx.workspaceDir, param, run)\n if (body === null) return '' // 空串 = 原样保留引用\n return `\\n\\n@def:${param}:\\n\\`\\`\\`\\n${body}\\n\\`\\`\\`\\n`\n },\n },\n ]\n}\n","/** AppBuilder: wiring layer for App construction (registries, sessions, lifecycle). */\nimport { Devtools } from '@x-otto/devtools'\nimport type { GrillAnswer, GrillQuestion } from '@x-otto/interchange'\nimport {\n createModelUsabilityGate,\n type Model,\n type ModelRegistry,\n type ProviderRegistry,\n type WireProtocolRegistry,\n type AuthStore,\n} from '@x-otto/ai'\nimport {\n createHookRegistry,\n type HookRegistry,\n} from '@x-otto/hooks'\nimport { PermissionPolicySync } from './permission-sync'\nimport type { ToolRegistry } from '@x-otto/tools'\nimport type { FeedbackRegistry } from '../feedback/feedback-registry'\nimport { SandboxController } from './sandbox-controller'\nimport {\n FleetMonitor,\n MemoryGovernor,\n ResidencyGovernor,\n type AgentRuntime,\n type AgentSession,\n} from '@x-otto/runtime'\nimport { McpController } from './mcp-controller'\nimport type { PluginCapabilityQuota } from '../plugin/plugin-capability-quota'\nimport { resolveProjectConfigTrusted } from '@x-otto/claude-compat'\nimport {\n PluginI18nRegistry,\n type DiscoveredPlugin,\n} from '@x-otto/plugin'\nimport { createAppRuntime } from './session-pool-factory'\nimport type { SqliteSessionRepository } from '@x-otto/session'\nimport { Orchestrator } from '@x-otto/orchestrator'\nimport type { WorkflowOptions } from '@x-otto/orchestrator'\nimport { createTodoStore } from '@x-otto/orchestration-contracts'\nimport type { WriteTodos } from '@x-otto/orchestration-contracts'\n\nimport type { WorkspaceRef } from '@x-otto/workspace'\n\nimport { setDefaultProxy, installGlobalProxyDispatcher, type Logger } from '@x-otto/shared'\nimport {\n SessionConfigResolver,\n type CodingSessionOptions,\n} from '../session/session-config-resolver'\nimport { createTaskRunner } from '../session/task-runner'\nimport { createTeamRunner, createSwarmAgentRunner, type RunTeam } from '../agent/swarm-runtime'\nimport type { SkillRegistry } from '@x-otto/skill'\nimport { PromptManager } from '@x-otto/prompt'\nimport type { AgentProfile } from '@x-otto/prompt'\nimport type { TeamDeclaration } from '../agent/declarative-teams'\nimport { SettingsManager } from '@x-otto/setting'\n\nimport type { AgentTool, ClockPort } from '@x-otto/agent'\nimport { ModelCatalogManager } from './model-catalog-manager'\nimport { VisionDelegateService, extractVisionSlotCandidates } from '../vision/vision-delegate-service'\nimport { SessionToolsSyncCoordinator } from '../session/session-tools-sync'\nimport { PluginContributionsFacade } from './plugin-contributions-facade'\n\nimport type { AppStorage, AppOptions } from '../types'\nimport { CapabilityReloader } from './capability-reloader'\nimport { installPanelRuntime } from '../plugin/panel-runtime'\nimport { PanelBackendController } from '../plugin/panel-backend-controller'\nimport { installPanelBackendRuntime } from '../plugin/panel-backend-runtime'\nimport { collectPluginI18nEntries } from '../plugin/plugin-i18n-loader'\nimport { resolveEngineLocale } from '../plugin/engine-locale'\nimport { PluginModuleManager } from './plugin-module-manager'\nimport { PluginProviderManager } from './plugin-provider-manager'\nimport { buildInspectDevtools } from './devtools-setup'\nimport type { PermissionPolicyRegistry, PermissionAuditLog } from '@x-otto/hooks'\nimport { PermissionController } from './permission-controller'\nimport { ExtensionManager } from './extension-manager'\nimport {\n createBuiltinHashtags,\n createFileProvider,\n discoverPlugins,\n} from '@x-otto/plugin'\nimport { collectPluginContextSources } from '../plugin/context-sources'\nimport { wireProcessLifecycleHooks } from './process-lifecycle-hooks'\nimport { globalProcessRuntime } from '@x-otto/runtime'\nimport type { PluginInputRegistry } from '@x-otto/plugin'\nimport { createBuiltinResolvers } from '../input/builtin-resolvers'\nimport type { SigilResolverRegistry } from '../input/sigil-resolver-registry'\nimport { collectPluginSigilEntries, warnDeprecatedInputEntries } from '../plugin/sigil-loader'\n\n// ── Phase 1: Core Infrastructure ─────────────────────────────────────────────\n\nexport interface BuildCoreInfraInput {\n logger: Logger\n hookRegistry?: HookRegistry\n /** Thunk: lazy reference to ToolRegistry (created later in construction). */\n getToolRegistry: () => ToolRegistry\n /** Thunk: lazy reference to PermissionRegistry (created later in construction). */\n getPermissionRegistry: () => PermissionPolicyRegistry\n /** RFC-153 hook timeout → PluginHealthTracker callback(插件 hook 名反解析)。 */\n onHookTimeout: (event: { hookName: string; timeoutMs: number }) => void\n}\n\nexport interface BuildCoreInfraResult {\n sandbox: SandboxController\n memoryGovernor: MemoryGovernor\n residencyGovernor: ResidencyGovernor\n fleetMonitor: FleetMonitor\n permissionSync: PermissionPolicySync\n hookRegistry: HookRegistry\n}\n\nexport function buildCoreInfrastructure(input: BuildCoreInfraInput): BuildCoreInfraResult {\n const sandbox = new SandboxController(input.logger)\n // RFC-115 M1 采样+分级;RFC-118 M2:构造期只 new 不 start——start 挪到 App.start() 末尾,\n // 消除「回调(onWarning/onCritical 由 cli 晚绑定 setCallbacks)未接线期的空转采样」。\n const memoryGovernor = new MemoryGovernor({ logger: input.logger })\n // RFC-178 触发器 C:驻留收紧系数源——app-lifecycle 在 MemoryGovernor 分级回调里写\n // setLevel,PersistenceSync 的 cap 执行读取收紧系数(离散阶梯 1/2/4,D1 确定性论证)。\n const residencyGovernor = new ResidencyGovernor()\n // RFC-196 M2:进程群预算感知——同 MemoryGovernor 构造期只 new 不 start(app-lifecycle\n // 接回调后 start),采样/通知回调在 app-lifecycle 接线(headless/serve 同享)。\n const fleetMonitor = new FleetMonitor({ logger: input.logger })\n const permissionSync = new PermissionPolicySync({\n logger: input.logger,\n getToolRegistry: input.getToolRegistry,\n getPermissionRegistry: input.getPermissionRegistry,\n })\n\n // RFC-153 D1/D3:`onHookTimeout` 把超时事件(领域无关字段)路由到 `PluginHealthTracker`——\n // `hookName` 反解析(`parsePluginIdFromHookName`)失败(非插件 hook,如引擎内置\n // `hook-state-cleanup`)时静默跳过,不是所有超时都来自插件。\n const hookRegistry =\n input.hookRegistry ??\n createHookRegistry({\n preset: 'default',\n onHookTimeout: input.onHookTimeout,\n })\n\n return {\n sandbox,\n memoryGovernor,\n residencyGovernor,\n fleetMonitor,\n permissionSync,\n hookRegistry,\n }\n}\n\n// ── Phase 2: Settings + Devtools ─────────────────────────────────────────────\n\nexport interface BuildSettingsInput {\n inspect?: boolean\n loggerName: string\n workspaceDir: string\n projectConfigPath?: string\n interactive: boolean\n permissionSync: PermissionPolicySync\n sandbox: SandboxController\n /** Called from settings.on('change') before the phase-level side effects. */\n onSettingsApplied: (setting: Record<string, unknown>) => void\n /** Thunk: lazy reference to i18nRegistry (created later in construction). */\n getI18nRegistry: () => PluginI18nRegistry\n}\n\nexport interface BuildSettingsResult {\n devtools: Devtools | null\n logSubscription: ReturnType<typeof import('@x-otto/shared').attachLogListener> | null\n settings: SettingsManager\n}\n\nexport function buildSettingsAndDevtools(input: BuildSettingsInput): BuildSettingsResult {\n const inspectDevtools = buildInspectDevtools(input.inspect, input.loggerName)\n const devtools = inspectDevtools.devtools\n const logSubscription = inspectDevtools.logSubscription\n\n const settings = new SettingsManager({\n workspaceDir: input.workspaceDir,\n projectConfigPath: input.projectConfigPath,\n // 严格模式(OTTO_STRICT_PROJECT_CONFIG=1)下未信任仓库剔除高危键;缺省全信任(零破坏)。\n // 仅交互态门控(headless fail-open)。thunk:每次 settings.load() 重新求值 →\n // `/compat trust` 后 reloadCapabilities 即时解除剔除。\n projectConfigTrusted: () =>\n resolveProjectConfigTrusted(input.workspaceDir, { interactive: input.interactive }),\n })\n\n // NOTE: settings.on('change') 回调引用了晚构造的 i18nRegistry——回调异步触发所以安全。\n // 注册时机不变(不要挪到所有阶段完成后统一注册,那会改变时序语义——纯搬迁红线)。\n settings.on('change', (setting) => {\n input.onSettingsApplied(setting)\n\n input.permissionSync.sync(setting)\n input.sandbox.applySetting(input.sandbox.resolveConfig(setting))\n\n // 代理配置变更 → 注入到进程级默认代理(HTTP 层 createDispatcher 读此值)+ 安装全局\n // dispatcher(RFC-288 D0b)。两者同判据、同步更新:前者服务显式消费 dispatcher 的调用方\n // (shared 的 request/stream、Anthropic SDK),后者兜住进程内所有裸 `fetch()`——此前\n // 只有前者,导致 Claude 请求等主要路径完全绕过代理(RFC-288 §背景·第零类)。\n const proxy = setting.proxy\n const proxyUrl = proxy?.enabled !== false && proxy?.url ? proxy.url : undefined\n setDefaultProxy(proxyUrl)\n installGlobalProxyDispatcher(proxyUrl)\n\n // RFC-141 T141-05:顺手接线 RFC-138-A 尾债——`i18nRegistry` 此前只在构造时读取\n // `DEFAULT_LOCALE`('zh'),不会随 `/lang` 切换。此处下推当前生效语言,\n // 消费方(如飞书桥接)省略 `t()` 的 locale 参数时即可跟随切换。`'auto'`/缺省时\n // 走与 `@x-otto/tui` 的 `detectLanguage()` 等价的环境变量检测(不可 import @x-otto/tui,\n // `packages/coding` 与 `packages/tui` 无依赖关系,故内联最小实现)。\n input.getI18nRegistry().setLocale(resolveEngineLocale(setting.language))\n })\n\n return { devtools, logSubscription, settings }\n}\n\n// ── Phase 3: Vision + Session Config ─────────────────────────────────────────\n\nexport interface BuildVisionAndConfigInput {\n getModelRegistry: () => ModelRegistry\n providerRegistry: ProviderRegistry\n authStore: AuthStore\n getSettings: () => SettingsManager\n promptManager: PromptManager\n getTools: () => AgentTool[]\n getDeclarativeAgentProfiles: () => AgentProfile[]\n defaultModel: Model | undefined\n explicitModelId?: string\n isModelUsable: ReturnType<typeof createModelUsabilityGate>\n maxToolTurns: number\n maxToolTurnExtensions?: number\n promptOutputTokenBudget?: number\n promptWallClockBudgetMs?: number\n /** RFC-204 D1/D3:app 级缺省会话成本软预算美元。 */\n sessionCostBudgetUSD?: number\n /** RFC-204 D4:app 级缺省早期停滞检测配置。 */\n stallDetection?: { windowTurns?: number; repeatThreshold?: number } | false\n workspaceDir: string\n getWorkspaceRef: () => WorkspaceRef | undefined\n workspaceId?: string\n logger: Logger\n /** RFC-230:惰性 getter,见 `SessionConfigResolverDeps.getResilienceConfig` 注释。 */\n getResilienceConfig?: () => import('@x-otto/env').ResilienceConfig\n}\n\nexport interface BuildVisionAndConfigResult {\n visionDelegateService: VisionDelegateService\n sessionConfigResolver: SessionConfigResolver\n}\n\nexport function buildVisionAndSessionConfig(\n input: BuildVisionAndConfigInput,\n): BuildVisionAndConfigResult {\n // RFC-181 M3:视觉委托服务(模型不支持图像输入时,把图像块交给 vision 模型代述)。\n // 与 usability gate 共用同一 authStore 判据;vision slot 候选从 model_slots.slots.vision\n // 惰性读取(配置可能在会话生命周期中变化,故用 getter 而非构造期固化)。\n const visionDelegateService = new VisionDelegateService({\n getModelRegistry: input.getModelRegistry,\n providerRegistry: input.providerRegistry,\n getVisionSlotCandidates: () => extractVisionSlotCandidates(input.getSettings().get('model_slots')),\n isModelUsable: input.isModelUsable,\n logger: input.logger,\n })\n\n const sessionConfigResolver = new SessionConfigResolver({\n settings: input.getSettings(),\n promptManager: input.promptManager,\n getModelRegistry: input.getModelRegistry,\n getTools: input.getTools,\n getDeclarativeAgentProfiles: input.getDeclarativeAgentProfiles,\n defaultModel: input.defaultModel,\n explicitModelSpec: input.explicitModelId,\n hasCredential: (provider) => input.providerRegistry.hasCredential(provider),\n isModelUsable: input.isModelUsable,\n maxToolTurns: input.maxToolTurns,\n maxToolTurnExtensions: input.maxToolTurnExtensions,\n promptOutputTokenBudget: input.promptOutputTokenBudget,\n promptWallClockBudgetMs: input.promptWallClockBudgetMs,\n sessionCostBudgetUSD: input.sessionCostBudgetUSD,\n stallDetection: input.stallDetection,\n workspaceDir: input.workspaceDir,\n // 修复(Bug1):惰性 getter——`workspaceRef` 在 start() 中异步解析,早于此处的构造期取值。\n // 优先读已就绪的 workspaceRef.id(真实解析值);未就绪时(start() 尚未跑完 / --id 显式场景\n // 提前已知)回退显式 `_workspaceId`,两者在 --id 场景下最终一致。\n getWorkspaceId: () => input.getWorkspaceRef()?.id ?? input.workspaceId,\n logger: input.logger,\n describeImages: visionDelegateService.describeImages,\n getResilienceConfig: input.getResilienceConfig,\n })\n\n return { visionDelegateService, sessionConfigResolver }\n}\n\n// ── Phase 4: Agent Runtime ───────────────────────────────────────────────────\n\nexport interface BuildAgentRuntimeInput {\n options: AppOptions\n hookRegistry: HookRegistry\n providerRegistry: ProviderRegistry\n getModelRegistry: () => ModelRegistry\n logger: Logger\n workspaceDir: string\n getTools: () => AgentTool[]\n getDeclarativeAgentProfiles: () => AgentProfile[]\n getWorkspaceRef: () => WorkspaceRef | undefined\n repoRef: { current?: SqliteSessionRepository }\n maxToolTurns: number\n promptManager: PromptManager\n memory: AppStorage['memory']\n traceStore: AppStorage['traceStore']\n checkpointStore: AppStorage['checkpointStore']\n clock: ClockPort | undefined\n devtools: Devtools | undefined\n residencyGovernor: ResidencyGovernor\n /** RFC-323 M4:驻留预算运行时配置 getter(settings 注入透传)。 */\n getResidencyConfig?: () => import('@x-otto/session-contract').ResidencyBudgetConfig | undefined\n visionDelegateService: VisionDelegateService\n resolveEffectiveModel: () => Model | undefined\n /** modelInfra.defaultModel 透传(`options.model ?? tryResolve(modelId)`,无 getAll()[0] 回退——语义与提取前一致,不得重算)。 */\n defaultModel: Model | undefined\n}\n\nexport interface BuildAgentRuntimeResult {\n agentRuntime: AgentRuntime\n codingSessionPool: import('@x-otto/runtime').SessionPool\n swarmAgentRunner: ReturnType<typeof createSwarmAgentRunner>\n defaultModel: Model | undefined\n}\n\nexport function buildAppAgentRuntime(input: BuildAgentRuntimeInput): BuildAgentRuntimeResult {\n const agentRuntime = createAppRuntime(\n input.options,\n {\n hookRegistry: input.hookRegistry,\n providerRegistry: input.providerRegistry,\n modelRegistry: input.getModelRegistry(),\n logger: input.logger,\n workspaceDir: input.workspaceDir,\n getTools: input.getTools,\n getDeclarativeAgentProfiles: input.getDeclarativeAgentProfiles,\n getWorkspaceKey: () => input.getWorkspaceRef()?.key,\n repoRef: input.repoRef,\n maxToolTurns: input.maxToolTurns,\n promptManager: input.promptManager,\n memory: input.memory,\n traceStore: input.traceStore,\n checkpointStore: input.checkpointStore,\n clock: input.clock,\n devtools: input.devtools ?? undefined,\n residencyGovernor: input.residencyGovernor,\n getResidencyConfig: input.getResidencyConfig,\n },\n input.resolveEffectiveModel,\n )\n const codingSessionPool = agentRuntime.sessions\n\n // M-B2: 构造 member turn 的 leaf 执行器(app 级 deps,per-execution 上下文从 AgentTurnInput 提取)\n // RFC-332 M1: 注入真实工具目录 + profile 来源——leaf 按名重解析真实 AgentTool(双重最小权限),\n // 未注入时 member 工具解析 fail closed(空集),绝不生成 throw stub。\n const swarmAgentRunner = createSwarmAgentRunner({\n hookRegistry: input.hookRegistry,\n stream: codingSessionPool.stream,\n logger: input.logger,\n defaultModel: input.defaultModel,\n getTools: input.getTools,\n getDeclarativeAgentProfiles: input.getDeclarativeAgentProfiles,\n })\n\n return { agentRuntime, codingSessionPool, swarmAgentRunner, defaultModel: input.defaultModel }\n}\n\n// ── Phase 5: Orchestrators + Teams ───────────────────────────────────────────\n\nexport interface BuildOrchAndTeamsInput {\n hookRegistry: HookRegistry\n stream: import('@x-otto/runtime').SessionPool['stream']\n logger: Logger\n defaultModel: Model | undefined\n providerRegistry: ProviderRegistry\n getDeclarativeTeams: () => TeamDeclaration[]\n getDeclarativeAgentProfiles: () => AgentProfile[]\n getSession: (id: string) => AgentSession | undefined\n createSession: (options?: CodingSessionOptions) => Promise<AgentSession>\n removeSession: (id: string) => Promise<boolean>\n sessionConfigResolver: SessionConfigResolver\n swarmAgentRunner: ReturnType<typeof createSwarmAgentRunner>\n /**\n * 用户 `.otto/config.json` 的 `workflow.retry`/`workflow.circuitBreaker` 投影\n * (RFC-067 M115-09:字段本身非 dormant,但此前从未接线到 Orchestrator——配了不生效)。\n * `workflow.review`/`workflow.routing` 是无机器实现的预留键,不投影。\n */\n workflowConfig?: WorkflowOptions\n}\n\nexport interface BuildOrchAndTeamsResult {\n orchestrator: Orchestrator\n runSession: ReturnType<typeof createTaskRunner>['runSession']\n runTeam: RunTeam\n /**\n * 会话级 Todo 存储(终局架构 review O2)——不再挂在 Orchestrator 上,\n * 独立构造并随 App 单例生命周期持有。\n */\n writeTodos: WriteTodos\n}\n\nexport function buildOrchestratorsAndTeams(\n input: BuildOrchAndTeamsInput,\n): BuildOrchAndTeamsResult {\n const { runSession, abortTask } = createTaskRunner({\n logger: input.logger,\n getSession: input.getSession,\n createSession: input.createSession,\n removeSession: input.removeSession,\n })\n\n const main = new Orchestrator({\n hookRegistry: input.hookRegistry,\n runSession,\n abortTask,\n runAgentTurn: input.swarmAgentRunner,\n workflowConfig: input.workflowConfig,\n })\n\n const runTeam = createTeamRunner({\n logger: input.logger,\n providerRegistry: input.providerRegistry,\n hookRegistry: input.hookRegistry,\n stream: input.stream,\n defaultModel: input.defaultModel,\n getDeclarativeTeams: input.getDeclarativeTeams,\n getDeclarativeAgentProfiles: input.getDeclarativeAgentProfiles,\n resolveTools: (explicitTools, agentToolNames) =>\n input.sessionConfigResolver.resolveTools(explicitTools, agentToolNames),\n runAgentTurn: (turnInput) => main.runAgentTurn(turnInput),\n })\n\n return { orchestrator: main, runSession, runTeam, writeTodos: createTodoStore() }\n}\n\n// ── Phase 6: Panel Infrastructure ────────────────────────────────────────────\n\nexport interface BuildPanelInfraInput {\n mcpController: McpController\n callMcpTool: (serverName: string, toolName: string, params?: Record<string, unknown>) => Promise<{ content: { type: string; text?: string }[]; isError?: boolean }>\n getActivePlugins: () => DiscoveredPlugin[]\n}\n\nexport interface BuildPanelInfraResult {\n panelBackendController: PanelBackendController\n}\n\nexport function buildPanelInfrastructure(\n input: BuildPanelInfraInput,\n): BuildPanelInfraResult {\n // RFC-081:安装插件面板运行时(usePluginData hook 全局注入)。\n // callMcpTool 绑定到 App 实例方法,经 McpController 调用原始 MCP client。\n installPanelRuntime(input.callMcpTool)\n\n // RFC-165 M3:装配 PanelBackendController + 安装面板后端运行时(usePanelBackend hook\n // 全局注入,见 panel-backend-runtime.ts 头注释\"架构修正\")。紧邻上方 installPanelRuntime,\n // 同一装配时机、同一依赖注入模式。\n const panelBackendController = new PanelBackendController({\n mcpController: input.mcpController,\n getActivePlugins: input.getActivePlugins,\n })\n installPanelBackendRuntime({\n call: (pluginId, method, params, opts) =>\n panelBackendController.call(pluginId, method, params, opts),\n cancel: (pluginId, operationId) => {\n void panelBackendController.cancel(pluginId, operationId)\n },\n subscribeState: (pluginId, cb) => panelBackendController.subscribeState(pluginId, cb),\n getStatus: (pluginId) => panelBackendController.getStatus(pluginId),\n onStatusChange: (pluginId, cb) => panelBackendController.onStatusChange(pluginId, cb),\n })\n\n return { panelBackendController }\n}\n\n// ── Phase 7: Plugin Modules (module mgr + provider mgr + i18n + capability reloader) ──\n\nexport interface BuildPluginModulesInput {\n hookRegistry: HookRegistry\n skillRegistry: SkillRegistry | null\n providerRegistry: ProviderRegistry\n wireProtocolRegistry: WireProtocolRegistry\n modelRegistry: ModelRegistry\n authStore: AuthStore\n toolRegistry: ToolRegistry\n /** RFC-209 §2.3:Feedback 业务契约注册目标。 */\n feedbackRegistry: FeedbackRegistry\n /** RFC-210 D3:@ 资源 resolve 注册目标(代码轴 sigilResolvers)。 */\n sigilResolverRegistry: SigilResolverRegistry\n /** RFC-210 D3:@ 资源动态 provider 注册目标(代码轴 sigilProviders)。 */\n pluginInput: PluginInputRegistry\n workspaceDir: string\n activePlugins: () => DiscoveredPlugin[]\n /** RFC-287 D2:agent.dispatch 能力源(可选——不传则该能力面不可用,fail-closed)。 */\n startPluginJob?: (input: { pluginId: string; title: string; prompt: string; sessionId: string }) => { id: string }\n /** RFC-287 D2:插件作业归属会话 id(活引用)。 */\n getPluginJobSessionId?: () => string | undefined\n /** RFC-318 M1:session.read 能力源(可选——不传则该能力不可用,fail-closed)。 */\n getSessionMessages?: (sessionId: string) => readonly import('@x-otto/ai').Message[] | undefined\n /** RFC-318 M1:llm.complete 能力源(可选——不传则该能力不可用,fail-closed)。 */\n completeText?: (prompt: string, opts?: { maxTokens?: number }) => Promise<string>\n /** RFC-318 M1:受控能力配额(与 pool.notify 的授权登记共用同一实例)。 */\n capabilityQuota?: PluginCapabilityQuota\n /** RFC-344 M3:user.ask 能力源(可选——不传则该能力不可用,fail-closed)。 */\n askUser?: (pluginId: string, input: Omit<GrillQuestion, 'id' | 'askedBy'>) => Promise<GrillAnswer>\n /** RFC-303 M5:scoped persistence 的 workspaceKey 来源(惰性,workspaceRef 未就位时为 undefined)。 */\n getWorkspaceKey?: () => string | undefined\n callMcpTool: (server: string, tool: string, params?: Record<string, unknown>) => Promise<{ content: { type: string; text?: string }[]; isError?: boolean }>\n logger: Logger\n onPluginLoadError: (pluginId: string, message: string) => void\n /**\n * RFC-327 D2(M2):core schedule 订阅对账端口(惰性 getter)。\n * 缺省 → 插件的 schedule 声明不生效(headless 无 scheduler 的既定边界)。\n */\n getSchedulePort?: () => import('../plugin/plugin-schedule-subscriptions').ScheduleSubscriptionPort | undefined\n /** RFC-327 D3(M3):user.notify 的宿主 UI 出口(缺省 → 该能力不注入)。 */\n notifyUser?: (input: {\n pluginId: string\n level: 'info' | 'warning' | 'error'\n code: string\n message: string\n }) => void\n settings: SettingsManager\n mcpController: McpController\n skillRegistryForReloader: SkillRegistry | null\n modelCatalogManager: ModelCatalogManager\n getActivePluginsForReloader: () => DiscoveredPlugin[]\n i18nRegistry: PluginI18nRegistry\n}\n\nexport interface BuildPluginModulesResult {\n pluginModuleManager: PluginModuleManager\n pluginProviderManager: PluginProviderManager\n capabilityReloader: CapabilityReloader\n}\n\nexport function buildPluginModules(\n input: BuildPluginModulesInput,\n): BuildPluginModulesResult {\n const pluginModuleManager = new PluginModuleManager({\n hookRegistry: input.hookRegistry,\n skillRegistry: input.skillRegistry,\n providerRegistry: input.providerRegistry,\n wireProtocolRegistry: input.wireProtocolRegistry,\n modelRegistry: input.modelRegistry,\n authStore: input.authStore,\n toolRegistry: input.toolRegistry,\n feedbackRegistry: input.feedbackRegistry,\n sigilResolverRegistry: input.sigilResolverRegistry,\n pluginInput: input.pluginInput,\n workspaceDir: input.workspaceDir,\n activePlugins: input.activePlugins,\n callMcpTool: input.callMcpTool,\n logger: input.logger,\n // RFC-287 D2:agent.dispatch 能力源。惰性 getter——AgentJobService 在 App 里是 lazy\n // 构造,构造期直接取值会提前实例化整条作业链。origin 固定 'plugin' 在\n // buildHostCapabilities 内强制(R2),此处只提供派发通道。\n startPluginJob: input.startPluginJob,\n getSessionId: input.getPluginJobSessionId,\n getWorkspaceKey: input.getWorkspaceKey,\n // RFC-318 M1:session.read / llm.complete 能力源 + 共享配额实例(配额必须与\n // pool.notify 的授权登记同源,否则 sessionId 白名单恒空、读能力恒拒)。\n getSessionMessages: input.getSessionMessages,\n completeText: input.completeText,\n capabilityQuota: input.capabilityQuota,\n askUser: input.askUser,\n // RFC-153 D3:插件加载/编译/工厂失败 → PluginHealthTracker(供 /extension 面板健康度摘要)。\n onPluginLoadError: input.onPluginLoadError,\n // RFC-327 D2:schedule 订阅对账端口(惰性——scheduler 在 TUI 启动流程才 setScheduleService)。\n ...(input.getSchedulePort ? { getSchedulePort: input.getSchedulePort } : {}),\n // RFC-327 D3:user.notify 的 UI 出口(惰性——TUI 层接线)。\n ...(input.notifyUser ? { notifyUser: input.notifyUser } : {}),\n })\n\n const pluginProviderManager = new PluginProviderManager({\n providerRegistry: input.providerRegistry,\n authStore: input.authStore,\n modelRegistry: input.modelRegistry,\n wireProtocolRegistry: input.wireProtocolRegistry,\n activePlugins: input.activePlugins,\n logger: input.logger,\n })\n\n // i18nRegistry is already created by the caller and passed in — register initial entries\n input.i18nRegistry.register(collectPluginI18nEntries(input.activePlugins()))\n\n const capabilityReloader = new CapabilityReloader(\n input.settings,\n input.mcpController,\n input.skillRegistryForReloader,\n pluginModuleManager,\n pluginProviderManager,\n input.toolRegistry,\n input.modelCatalogManager,\n input.workspaceDir,\n input.getActivePluginsForReloader,\n input.i18nRegistry,\n )\n\n return { pluginModuleManager, pluginProviderManager, capabilityReloader }\n}\n\n// ── Phase 8: Facades ─────────────────────────────────────────────────────────\n\nexport interface BuildFacadesInput {\n sessionConfigResolver: SessionConfigResolver\n getSession: (id: string) => AgentSession | undefined\n listSessionIds: () => string[]\n logger: Logger\n workspaceDir: string\n settings: SettingsManager\n pluginModuleManager: PluginModuleManager\n mcpController: McpController\n getActivePlugins: () => DiscoveredPlugin[]\n}\n\n/**\n * RFC-157 M2:model/notification/permission/session 4 个纯转发 facade 类已删除\n * (回收进 app-model-methods.ts / app-notification-methods.ts / app-permission-methods.ts /\n * app-session-methods.ts 自由函数,App 类同名方法直接以 `this` 为 host 调用)。\n * 本函数仅保留两个有真实语义的构件:`toolsSyncCoordinator`(RFC-150 D2 工具重同步协调)\n * 与 `pluginContributionsFacade`(RFC-148 M4,按插件聚合分组的真实领域逻辑)。\n */\nexport interface BuildFacadesResult {\n toolsSyncCoordinator: SessionToolsSyncCoordinator\n pluginContributionsFacade: PluginContributionsFacade\n}\n\nexport function buildFacades(input: BuildFacadesInput): BuildFacadesResult {\n const toolsSyncCoordinator = new SessionToolsSyncCoordinator({\n sessionConfigResolver: input.sessionConfigResolver,\n getSession: input.getSession,\n listSessionIds: input.listSessionIds,\n logger: input.logger,\n })\n const pluginContributionsFacade = new PluginContributionsFacade({\n getWorkspaceDir: () => input.workspaceDir,\n settings: input.settings,\n getActivePlugins: input.getActivePlugins,\n pluginModuleManager: input.pluginModuleManager,\n mcpController: input.mcpController,\n })\n\n return {\n toolsSyncCoordinator,\n pluginContributionsFacade,\n }\n}\n\n// ── Phase 9b: Tools + Permission + MCP ───────────────────────────────────────\n\nexport interface BuildToolsMCPInput {\n toolRegistry: ToolRegistry\n hookRegistry: HookRegistry\n settings: SettingsManager\n workspaceDir: string\n logger: Logger\n /** RFC-230:惰性 getter,见 `McpController` 构造参数注释。 */\n getResilienceConfig?: () => import('@x-otto/env').ResilienceConfig\n}\n\nexport interface BuildToolsMCPResult {\n permissionRegistry: PermissionPolicyRegistry\n auditLog: PermissionAuditLog\n mcpController: McpController\n preExistingHooks: Set<string>\n}\n\nexport function buildToolsAndMCP(input: BuildToolsMCPInput): BuildToolsMCPResult {\n const preExistingHooks = new Set(\n input.hookRegistry.getRegistered().map((h) => h.name),\n )\n\n const permController = new PermissionController(input.toolRegistry, input.hookRegistry)\n\n const mcpController = new McpController(\n input.settings,\n input.toolRegistry,\n input.logger,\n input.workspaceDir,\n input.getResilienceConfig,\n )\n\n return {\n permissionRegistry: permController.registry,\n auditLog: permController.auditLog,\n mcpController,\n preExistingHooks,\n }\n}\n\n// ── Phase 9c: Extensions ─────────────────────────────────────────────────────\n\nexport interface BuildExtensionsInput {\n settings: SettingsManager\n mcpController: McpController\n skillRegistry: SkillRegistry | null\n declarativeAgentProfiles: import('@x-otto/prompt').AgentProfile[]\n workspaceDir: string\n pluginHomedir: string\n /** RFC-303 D4:插件 enable/disable 切换完成通知(App 桥接 monitor 事件)。 */\n onPluginToggle?: (id: string, enabled: boolean) => void\n}\n\nexport function buildExtensions(input: BuildExtensionsInput): ExtensionManager {\n return new ExtensionManager({\n settings: input.settings,\n mcp: input.mcpController,\n skills: input.skillRegistry,\n getAgentProfiles: () => input.declarativeAgentProfiles,\n getPlugins: () =>\n discoverPlugins({ cwd: input.workspaceDir, homedir: input.pluginHomedir }),\n processRuntime: globalProcessRuntime,\n onPluginToggle: input.onPluginToggle,\n })\n}\n\n// ── Phase 11b: Plugin Input + Context Sources + Hooks ────────────────────────\n\nexport interface BuildPluginRegistrationsInput {\n pluginInput: PluginInputRegistry\n /** RFC-210 D4:@ 资源 resolve 注册表——内置 resolver 在此注册(与 builtin hashtags 同位)。 */\n sigilResolvers: SigilResolverRegistry\n /** RFC-233 D4:shortcut resolver 需动态读取当前 shortcuts 列表(管理面板改动即时生效)。 */\n settings: SettingsManager\n workspaceDir: string\n activePlugins: () => DiscoveredPlugin[]\n agentRuntime: AgentRuntime\n hookRegistry: HookRegistry\n preExistingHooks: Set<string>\n codingSessionPoolGet: (id: string) => AgentSession | undefined\n logger: Logger\n registerHooks: () => void\n}\n\nexport interface BuildPluginRegistrationsResult {\n ownedHookNames: string[]\n processRegistryUnsubs: Array<() => void>\n}\n\nexport function buildPluginRegistrations(\n input: BuildPluginRegistrationsInput,\n): BuildPluginRegistrationsResult {\n input.pluginInput.register(createBuiltinHashtags())\n input.pluginInput.registerProvider('@', createFileProvider({ cwd: input.workspaceDir }))\n // RFC-210 D4:内置 @ 资源 resolver(file-range/@def;@changes/@git/@clipboard\n // 已随 RFC-233 退役)——resolve 从 cli CONTEXT_CHIPS 收编至此,cli 不再持有任何\n // @ 资源展开定义。\n input.sigilResolvers.register(createBuiltinResolvers(), 'builtin')\n // RFC-233 D4:用户自定义 @shortcut resolver——动态读取当前 settings.shortcuts\n // 列表(管理面板改动即时生效,无需重启/重新注册 resolver)。查不到名称 = 空串\n // = 引用原样保留(对齐 file-range/def 兜底语义)。\n input.sigilResolvers.register(\n [\n {\n id: 'shortcut',\n resolve: (param: string) => {\n const list = input.settings.get('shortcuts') ?? []\n const found = list.find((s) => s.name === param)\n return found ? found.text : ''\n },\n },\n ],\n 'builtin',\n )\n\n // RFC-210 D3:插件声明式 sigil 条目(contributes.sigilEntries,纯数据、启动即注册;\n // 与 contextSources 同型只读 manifest 漏斗,不依赖 plugin.ts 加载)。代码式\n // resolver/provider 轴在 plugin-module-manager(模块加载后、capability 门内)注册。\n const pluginSigilEntries = collectPluginSigilEntries(input.activePlugins())\n if (pluginSigilEntries.length > 0) input.pluginInput.register(pluginSigilEntries)\n warnDeprecatedInputEntries(input.activePlugins())\n\n for (const source of collectPluginContextSources(input.activePlugins())) {\n input.agentRuntime.contextSources.register(source)\n }\n\n input.registerHooks()\n\n const ownedHookNames = input.hookRegistry\n .getRegistered()\n .map((h) => h.name)\n .filter((name) => !input.preExistingHooks.has(name))\n\n const processRegistryUnsubs = wireProcessLifecycleHooks({\n hasSession: (id) => !!input.codingSessionPoolGet(id),\n emitHook: (timing, hookInput) => input.hookRegistry.emit(timing, hookInput as never),\n logger: input.logger,\n repoRoot: input.workspaceDir,\n })\n\n return { ownedHookNames, processRegistryUnsubs }\n}\n\n// ── Settings helper ──────────────────────────────────────────────────────────\n\nexport async function ensureSettingsLoaded(\n settings: SettingsManager,\n getFlag: () => boolean,\n setFlag: (v: boolean) => void,\n): Promise<void> {\n if (getFlag()) return\n await settings.load()\n setFlag(true)\n}\n","import type {\n RegisterBuiltinOptions,\n SandboxProvider,\n BackgroundProcessPort,\n BackgroundProcessRegisterInput,\n} from '@x-otto/tools'\nimport type { Orchestrator, RunSessionOptions } from '@x-otto/orchestrator'\nimport type { WriteTodos } from '@x-otto/orchestration-contracts'\nimport type { SkillProvider } from '@x-otto/skill'\nimport type { AutoMemory, FileStateManager, OperationalLearningStore } from '@x-otto/memory'\nimport type { RunTeam } from '../agent/swarm-runtime'\nimport type { AgentSession } from '@x-otto/runtime'\nimport type { AgentSessionInfo } from '@x-otto/runtime'\nimport { globalProcessRegistry } from '@x-otto/runtime'\nimport { createScheduleCapability, type SchedulerService } from '@x-otto/schedule'\n\nexport interface ToolCallbacksDeps {\n workspaceDir: string\n orchestrator: Orchestrator\n /** 会话级 Todo 存储(终局架构 review O2)——独立于 Orchestrator 构造/持有。 */\n writeTodos: WriteTodos\n /** 注入优先,缺省内建 SkillRegistry 适配(解析在组合根 wiring)。 */\n skillProvider?: SkillProvider\n /** LSP 工具 opt-in(原注册死链已激活)。 */\n enableLsp: boolean\n /** bash OS 级沙箱(晚绑定 provider:settings 在 start() 才 load 且可热更新)。\n * provider 接受可选 ctx({command}) — 被批准 bypass 的命令返回 undefined。 */\n sandbox: SandboxProvider\n /** auto-memory(storage.memory 配置时生效;缺省工具回未配置提示)。 */\n autoMemory: AutoMemory | null\n /**\n * RFC-228 D2:`memory_record(scope:'project-shared')` 写入 `.otto/AGENTS.md` 成功后\n * 触发的 reload 回调(复用 `ContextInjectionPort.loadMemory()`——语义等同 reload,\n * 见 `MemoryManager.reloadMemory()` 实现,两者都是 `persistent.load()`),使当前会话\n * 下一轮 system prompt(经 memory-delta volatile 注入)即时感知变化,不需要等新会话。\n * 缺省(`storage.memory` 未配置)时 project-shared 写入仍生效,只是不触发即时 reload。\n */\n reloadMemory?: () => Promise<void>\n /**\n * RFC-340 M2:会话归档召回能力(压缩折叠掉的历史全文)。\n *\n * 来源是 `storage.memory`(`MemorySubsystem` 的 `ArchiveRecallPort` facet);\n * 未配置时 undefined → 归档工具整组不注册(`tool-nodes` 的 `when` 门),模型看不到\n * 该能力,而非看到一个恒失败的工具。\n */\n archiveRecall?: import('@x-otto/runtime').ArchiveRecallPort\n /** agent team 工具回调(始终注入;无声明 team 时按名返回 not-found)。 */\n runTeam: RunTeam\n /** fork_call 的 runSession 旁路(用 orchestrator 单源契约 RunSessionOptions,去 as-unknown-as) */\n runSession: (options: RunSessionOptions) => Promise<{ text: string }>\n fileStateManager: FileStateManager\n learningStore: OperationalLearningStore\n /** 机器特定经验(scope='local',RFC-164 M164-1 D3);缺省 → learn 工具的 scope='local' 降级写入 learningStore(向后兼容单实例场景)。 */\n localLearningStore?: OperationalLearningStore\n sessions: {\n list: () => AgentSessionInfo[]\n create: () => Promise<AgentSession>\n remove: (id: string) => Promise<boolean>\n get: (id: string) => AgentSession | undefined\n }\n /** list_models 工具的目录查询(app 注入:registry + 会话级可用集合 + usability gate)。 */\n listModels?: (\n sessionId?: string,\n ) => Promise<\n Array<{\n id: string\n provider: string\n strengths: string[]\n available: boolean\n usable?: boolean\n unusableReason?: string\n }>\n >\n /** App-scoped 进程归属标识:注入后 bash 后台进程 register 时打此 owner,App.stop 用 killAllForOwner 只杀自己的。 */\n processOwner?: string\n /** 起隔离可变作业(delegate_job → agentJobs.start origin:'main')。惰性,缺省 → 工具回 missingCapability。 */\n startJob?: (input: { prompt: string; title: string; sessionId: string }) => { jobId: string }\n /** Schedule callbacks (schedule_create/list/delete/run/cancel tools). */\n schedule?: {\n create: (input: { cron: string; prompt: string; recurring: boolean; name?: string }) => { taskId: string; nextFireAt: number; name: string }\n list: () => Array<{ id: string; name: string; cronExpression: string; enabled: boolean; recurring: boolean; nextFireAt: number; lastFiredAt?: number }>\n delete: (taskId: string) => boolean\n run: (taskId: string) => void\n cancel: (taskId: string) => { ok: boolean; reason?: string }\n }\n /** RFC-287 M4:生态检索(capability_gap 工具)。缺省 → 该工具不注册。 */\n searchEcosystem?: import('@x-otto/tools').SearchEcosystem\n}\n\n/**\n * 工具回调 deps 的共享构造(原 App.buildSharedToolCallbackDeps,RFC-107 D4 下沉)。\n * 除 workspaceDir / skillProvider / enableLsp 三个随调用点变化的入参外,\n * 其余 deps 全为 App 构造后稳定的单例引用。\n */\nexport interface SharedToolCallbackAppDeps {\n processOwnerId: string\n orchestrator: Orchestrator\n writeTodos: WriteTodos\n sandbox: SandboxProvider\n autoMemory: AutoMemory | null\n /** RFC-228 D2:project-shared 写入后即时 reload(见 `ToolCallbacksDeps.reloadMemory`)。 */\n reloadMemory?: () => Promise<void>\n /** RFC-340 M2:会话归档召回(见 `ToolCallbacksDeps.archiveRecall`)。 */\n archiveRecall?: ToolCallbacksDeps['archiveRecall']\n runTeam: RunTeam\n runSession: (options: RunSessionOptions) => Promise<{ text: string }>\n fileStateManager: FileStateManager\n learningStore: OperationalLearningStore\n localLearningStore?: OperationalLearningStore\n sessions: {\n list: () => AgentSessionInfo[]\n create: () => Promise<AgentSession>\n remove: (id: string) => Promise<boolean>\n get: (id: string) => AgentSession | undefined\n }\n listModels: (sessionId?: string) => Promise<\n Array<{\n id: string\n provider: string\n strengths: string[]\n available: boolean\n usable?: boolean\n unusableReason?: string\n }>\n >\n startJob?: (input: { prompt: string; title: string; sessionId: string }) => { jobId: string }\n getScheduleService: () => SchedulerService | undefined\n /**\n * RFC-287 M4:生态检索(`capability_gap` 工具)。缺省 → 该工具不注册(`when` 门)。\n *\n * 注入的是**只读检索**函数,不是安装能力——R1 零豁免要求安装始终经用户显式确认的\n * 既有信任门,模型侧不存在任何自助安装通路(见 capability-gap.ts 头注)。\n */\n searchEcosystem?: import('@x-otto/tools').SearchEcosystem\n}\n\nexport function buildSharedToolCallbackDeps(\n deps: SharedToolCallbackAppDeps,\n workspaceDir: string,\n skillProvider: ToolCallbacksDeps['skillProvider'],\n enableLsp: boolean,\n): ToolCallbacksDeps {\n return {\n workspaceDir,\n processOwner: deps.processOwnerId,\n orchestrator: deps.orchestrator,\n writeTodos: deps.writeTodos,\n skillProvider,\n enableLsp,\n sandbox: deps.sandbox,\n autoMemory: deps.autoMemory,\n reloadMemory: deps.reloadMemory,\n archiveRecall: deps.archiveRecall,\n runTeam: deps.runTeam,\n runSession: deps.runSession,\n fileStateManager: deps.fileStateManager,\n learningStore: deps.learningStore,\n localLearningStore: deps.localLearningStore,\n sessions: deps.sessions,\n listModels: deps.listModels,\n startJob: deps.startJob,\n schedule: createScheduleCapability(deps.getScheduleService),\n searchEcosystem: deps.searchEcosystem,\n }\n}\n\nexport function buildToolCallbacks(deps: ToolCallbacksDeps): RegisterBuiltinOptions {\n const {\n orchestrator,\n writeTodos,\n skillProvider,\n autoMemory,\n reloadMemory,\n archiveRecall,\n } = deps\n\n const owner = deps.processOwner\n const processRegistry: BackgroundProcessPort = owner\n ? {\n register: (input: BackgroundProcessRegisterInput) =>\n globalProcessRegistry.register({ ...input, owner }),\n get: (id) => globalProcessRegistry.get(id),\n getForSession: (sid, id) => globalProcessRegistry.getForSession(sid, id),\n listForSession: (sid) => globalProcessRegistry.listForSession(sid),\n kill: (id) => globalProcessRegistry.kill(id),\n // RFC-223:owner 标记与识别结果无关(识别结果按 bgId 落地,不区分宿主),\n // 直接转发给底层单例即可。\n updateDetectedService: (id, service) =>\n globalProcessRegistry.updateDetectedService(id, service),\n }\n : globalProcessRegistry\n\n return {\n orchestration: {\n dispatchTask: orchestrator.dispatchTask,\n getTask: orchestrator.getTask,\n listTasks: orchestrator.listTasks,\n updateTask: orchestrator.updateTask,\n getBackgroundOutput: orchestrator.getBackgroundOutput,\n cancelBackground: orchestrator.cancelBackground,\n writeTodos,\n startJob: deps.startJob,\n },\n agents: {\n callAgent: orchestrator.callAgent,\n },\n fork: {\n fork: async (prompt, depth, signal) => {\n const result = await deps.runSession({\n prompt,\n sessionMode: 'ephemeral',\n depth,\n signal,\n })\n return { ok: true, value: result.text }\n },\n },\n skills: skillProvider\n ? {\n loadSkill: (path) => skillProvider.load(path),\n executeSkill: (name) => skillProvider.execute(name),\n }\n : undefined,\n memory: autoMemory\n ? {\n recordMemory: async (entry) => {\n await autoMemory.record(entry, entry.scope)\n // RFC-228 D2:project-shared 写入 .otto/AGENTS.md 成功后触发 reload,使当前\n // 会话下一轮 system prompt(经 memory-delta volatile 注入)即时可见新内容,\n // 不需要等到下一次全新会话才生效。reloadMemory 未注入(storage.memory 未配置)\n // 时静默跳过——写入仍生效,只是不即时刷新注入缓存。\n if (entry.scope === 'project-shared' && reloadMemory) {\n await reloadMemory()\n }\n },\n readMemory: (name) => autoMemory.read(name),\n // RFC-340 M2:会话归档召回。`memory`(MemorySubsystem)未配置时整组工具不注册\n // (tool-nodes 的 when 门),模型看不到而非看到恒失败的工具。\n // 端口(readArchive/listArchives)→ 工具能力面(read/list)的投影。\n // tools 包不依赖 @x-otto/runtime,故在此做名称适配,而非让端口迁就工具命名。\n archive: archiveRecall\n ? {\n read: (archivePath) => archiveRecall.readArchive(archivePath),\n list: (sessionId) => archiveRecall.listArchives(sessionId),\n }\n : undefined,\n }\n : undefined,\n team: { runTeam: deps.runTeam },\n session: {\n manager: {\n list: async () =>\n deps.sessions.list().map((s) => ({\n id: s.id,\n title: s.id,\n messageCount: s.messageCount,\n })),\n create: async () => {\n const s = await deps.sessions.create()\n return { id: s.id }\n },\n remove: async (id: string) => deps.sessions.remove(id),\n compact: async (id: string) => {\n const s = deps.sessions.get(id)\n if (!s) {\n return false\n }\n // B3:走真实摘要路径(compactNow → memory.process → recordCompaction,保留近期消息),\n // 不用 compact('占位串',1)——那是 fold-all 位置式压缩,会把 LLM 上下文整体替换成占位字符串。\n await s.compactNow()\n return true\n },\n },\n },\n features: {\n listModels: deps.listModels,\n grill: (sessionId, q) => {\n const s = deps.sessions.get(sessionId)\n return s\n ? s.requestGrill(q)\n : Promise.resolve({\n answer: q.recommendation,\n acceptedRecommendation: true,\n resolvedBy: 'auto_recommendation' as const,\n })\n },\n enableLsp: deps.enableLsp,\n sandbox: deps.sandbox,\n processRegistry,\n captureFileState: async (input) => {\n const snapshot = await deps.fileStateManager.capture({\n workspaceDir: deps.workspaceDir,\n recentFiles: input.recentFiles,\n planStatus: input.planStatus,\n })\n return {\n success: true,\n summary: deps.fileStateManager.formatSnapshot(snapshot),\n timestamp: snapshot.timestamp,\n }\n },\n learn: async (lesson) => {\n // RFC-164 M164-1 D3:scope='local' 路由到独立 store(永不参与未来远端同步);\n // 缺省/'workspace' 走现状 learningStore(向后兼容,不传 scope 的旧调用行为零变化)。\n // localLearningStore 未注入时(deps 层未接线,向后兼容防御)降级复用 learningStore。\n const store =\n lesson.scope === 'local' ? (deps.localLearningStore ?? deps.learningStore) : deps.learningStore\n const { lesson: saved, deduped } = await store.saveDeduped({\n tags: lesson.tags,\n trigger: lesson.trigger,\n insight: lesson.insight,\n sourceSessionId: lesson.sessionId,\n })\n return { success: true, lessonId: saved.id, deduped }\n },\n },\n schedule: deps.schedule ? {\n create: deps.schedule.create,\n list: deps.schedule.list,\n delete: deps.schedule.delete,\n run: deps.schedule.run,\n cancel: deps.schedule.cancel,\n } : undefined,\n // RFC-287 M4:只在宿主真的注入了检索实现时才暴露 capability_gap(fail-closed,\n // 同 schedule 的 `when` 门纹理)。注入的是只读检索,非安装能力(R1)。\n capabilityGap: deps.searchEcosystem ? { search: deps.searchEcosystem } : undefined,\n }\n}\n","/**\n * tool-guidance.ts\n *\n * 将 ToolRegistry 的工具说明(guidance snippet)注入到 system prompt。\n * 注入顺序在 environment-injection 之前(priority=20)。\n */\n\nimport type { HookSpec } from '@x-otto/hooks'\nimport { defineHook } from '@x-otto/hooks'\nimport { SYSTEM_PROMPT_SECTIONS } from '@x-otto/runtime'\nimport type { ToolRegistry } from '@x-otto/tools'\n\nexport function createToolGuidanceHook(\n toolRegistry: ToolRegistry,\n getPreset: () => 'minimal' | 'standard' | 'full',\n): HookSpec {\n return defineHook({\n name: 'tool-guidance-injection',\n timing: 'system.prompt.transform',\n priority: SYSTEM_PROMPT_SECTIONS.toolGuidance.priority,\n handle: async (_input, output) => {\n const guidance = toolRegistry.buildToolGuidance(getPreset())\n if (guidance) {\n output.systemPrompt = `${output.systemPrompt}\\n\\n${guidance}`\n }\n },\n })\n}\n","/** Lesson injection: auto-memory retrieval + injection into system prompt. */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { buildLessonInjection } from '@x-otto/prompt'\nimport { SYSTEM_PROMPT_SECTIONS } from '@x-otto/runtime'\nimport { createSessionStableInjectionHooks } from '@x-otto/runtime'\nimport { defineHook } from '@x-otto/hooks'\n\nimport type { HookSpec, SystemPromptTransformInput, SystemPromptTransformOutput } from '@x-otto/hooks'\nimport type { OperationalLearningStore } from '@x-otto/memory'\nimport type { PromptManager } from '@x-otto/prompt'\n\nconst RFC284_CORE_PACK_CONTENT_PATH = join(\n process.cwd(),\n 'docs/rfc/RFC-284-core-pack/content.txt',\n)\nconst RFC284_GOVERNANCE_PLAN_PATH = join(\n process.cwd(),\n 'docs/rfc/RFC-284-governance-plan.json',\n)\nconst RELEVANT_HINTS_HARD_TOKENS = 1024\n\ninterface GovernanceDecision {\n key: string\n id: string\n scope: 'workspace' | 'local'\n class: 'norm' | 'fact' | 'case' | 'ephemeral'\n subclass?: string\n state: string\n supersededBy?: string\n}\n\nfunction loadRfc284CorePackContent(): string | undefined {\n if (!existsSync(RFC284_CORE_PACK_CONTENT_PATH)) {\n return undefined\n }\n const content = readFileSync(RFC284_CORE_PACK_CONTENT_PATH, 'utf8').trim()\n return content.length > 0 ? content : undefined\n}\n\nfunction loadGovernanceDecisions(): Map<string, GovernanceDecision> {\n if (!existsSync(RFC284_GOVERNANCE_PLAN_PATH)) {\n return new Map()\n }\n const plan = JSON.parse(readFileSync(RFC284_GOVERNANCE_PLAN_PATH, 'utf8')) as {\n decisions?: GovernanceDecision[]\n }\n return new Map((plan.decisions ?? []).map((decision) => [decision.key, decision]))\n}\n\nfunction textTokens(text: string): Set<string> {\n return new Set(\n text\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}._/-]+/u)\n .filter((token) => token.length >= 3),\n )\n}\n\nfunction estimatedTokens(text: string): number {\n let cjk = 0\n for (const ch of text) {\n if (/[\\u3000-\\u9fff\\uff00-\\uffef]/.test(ch)) cjk++\n }\n return Math.ceil(cjk + (text.length - cjk) / 4)\n}\n\nfunction scoreRelevantLesson(lesson: Awaited<ReturnType<OperationalLearningStore['list']>>[number], query: string): number {\n const queryTokens = textTokens(query)\n if (queryTokens.size === 0) return 0\n\n let score = 0\n const tags = lesson.tags ?? []\n for (const tag of tags) {\n if (queryTokens.has(tag.toLowerCase())) score += 3\n }\n\n const bodyTokens = textTokens(`${lesson.trigger} ${lesson.insight}`)\n for (const token of queryTokens) {\n if (bodyTokens.has(token)) score += 1\n }\n\n return score\n}\n\nfunction buildRelevantHints(\n lessons: Awaited<ReturnType<OperationalLearningStore['list']>>,\n decisions: Map<string, GovernanceDecision>,\n userText: string | undefined,\n): { text: string; hitIds: string[] } | undefined {\n if (!userText) return undefined\n\n const ranked = lessons\n .map((lesson) => {\n const key = `workspace:${lesson.id}`\n const decision = decisions.get(key)\n if (!decision || decision.class !== 'fact' || decision.state !== 'active' || decision.supersededBy) {\n return undefined\n }\n const score = scoreRelevantLesson(lesson, userText)\n if (score <= 0) return undefined\n const text = `[${lesson.tags.join(', ')}] ${lesson.trigger} → ${lesson.insight}`\n return { lesson, score, text, tokens: estimatedTokens(text) }\n })\n .filter((item): item is { lesson: Awaited<ReturnType<OperationalLearningStore['list']>>[number]; score: number; text: string; tokens: number } => Boolean(item))\n .sort((a, b) => b.score - a.score || a.tokens - b.tokens || a.lesson.id.localeCompare(b.lesson.id))\n\n const selected: typeof ranked = []\n let tokens = 0\n for (const item of ranked) {\n if (tokens + item.tokens > RELEVANT_HINTS_HARD_TOKENS) continue\n selected.push(item)\n tokens += item.tokens\n if (selected.length >= 3) break\n }\n\n if (selected.length === 0) return undefined\n return {\n text: [\n '### Relevant Lesson Hints',\n 'Query-specific volatile hints selected from RFC-284 fact lessons:',\n ...selected.map((item, index) =>\n `${index + 1}. workspace:${item.lesson.id} score=${item.score} ${item.text}`,\n ),\n ].join('\\n'),\n hitIds: selected.map((item) => item.lesson.id),\n }\n}\n\n/**\n * `localLearningStore`(RFC-164 M164-1 D3):机器特定经验(scope='local')的独立 store。\n *\n * RFC-284 T2c 起,workspace lessons 优先由 T2b Core Pack artifact 注入,避免全量 150+\n * 条原始 lesson 进入 stable prefix;若 artifact 尚未生成,则回退 legacy 全量注入以保持兼容。\n * local pack 仍待后续里程碑落地,因此 local lessons 暂保留为独立 legacy block,不与可提交\n * workspace pack 混合,避免破坏 RFC-164 local-only 语义。\n */\nexport function createLessonInjectionHooks(\n learningStore: OperationalLearningStore,\n promptManager: PromptManager,\n localLearningStore?: OperationalLearningStore,\n): HookSpec[] {\n const stableHooks = createSessionStableInjectionHooks({\n name: 'lesson-injection',\n priority: SYSTEM_PROMPT_SECTIONS.lesson.priority,\n compute: async () => {\n const corePackContent = loadRfc284CorePackContent()\n const localLessons = localLearningStore ? await localLearningStore.list() : []\n\n if (corePackContent) {\n const template = localLessons.length > 0\n ? ((await promptManager.loadRuntimeLessonsTemplate()) ?? undefined)\n : undefined\n const localBlock = localLessons.length > 0\n ? buildLessonInjection(localLessons, template)\n : ''\n return [corePackContent, localBlock].filter((block) => block.length > 0).join('\\n\\n') || undefined\n }\n\n const lessons = await learningStore.list()\n const combined = [...lessons, ...localLessons]\n if (combined.length === 0) {\n return undefined\n }\n const template = (await promptManager.loadRuntimeLessonsTemplate()) ?? undefined\n return buildLessonInjection(combined, template) || undefined\n },\n })\n\n const relevantHintsHook = defineHook({\n name: 'lesson-relevant-hints',\n timing: 'system.prompt.transform',\n priority: SYSTEM_PROMPT_SECTIONS.lesson.priority + 1,\n handle: async (\n input: SystemPromptTransformInput,\n output: SystemPromptTransformOutput,\n ) => {\n if (!input.userText) return\n const decisions = loadGovernanceDecisions()\n if (decisions.size === 0) return\n const lessons = await learningStore.list()\n const hints = buildRelevantHints(lessons, decisions, input.userText)\n if (!hints) return\n await learningStore.recordRelevantHits(hints.hitIds)\n output.systemTail = [...(output.systemTail ?? []), hints.text]\n },\n })\n\n return [...stableHooks, relevantHintsHook]\n}\n","/** Skill catalog injection: append skill descriptions to system prompt. */\n\nimport { SYSTEM_PROMPT_SECTIONS } from '@x-otto/runtime'\nimport { createSessionStableInjectionHooks } from '@x-otto/runtime'\n\nimport type { HookSpec } from '@x-otto/hooks'\nimport type { SkillRegistry } from '@x-otto/skill'\n\nexport function createSkillCatalogInjectionHooks(registry: SkillRegistry): HookSpec[] {\n return createSessionStableInjectionHooks({\n name: 'skill-catalog-injection',\n priority: SYSTEM_PROMPT_SECTIONS.skillCatalog.priority,\n compute: () => {\n const catalog = registry.buildCatalog()\n if (!catalog) {\n return undefined\n }\n return `${catalog}\\n\\nUse the skill tool with a skill name to load its full instructions.`\n },\n })\n}\n","/**\n * notification-injection.ts —— 通知桥的注入合并逻辑单一真源。\n *\n * background-process-notification 与 agent-job-notification 两座 messages.transform\n * 注入桥的合并逻辑逐字相同(差异仅在各自的 tag/格式化,发生在写入 pending 之前):\n * 把缓存在 pending[sessionId] 的待注入文本拼成一条 user 消息落到 output.messages,\n * 末条若已是 user/数组 content 则追加,否则新 push 一条,注入后清空该 session 缓存。\n */\n\nimport type { AgentMessage } from '@x-otto/agent'\n\nexport function injectPendingAsUserMessage(\n pending: Map<string, string[]>,\n input: { sessionId: string },\n output: { messages: AgentMessage[] },\n): void {\n const list = pending.get(input.sessionId)\n if (!list || list.length === 0) {\n return\n }\n pending.delete(input.sessionId)\n const text = list.join('\\n')\n\n const lastIndex = output.messages.length - 1\n const last = output.messages[lastIndex] as { role?: string; content?: unknown } | undefined\n if (last && last.role === 'user' && Array.isArray(last.content)) {\n output.messages[lastIndex] = {\n ...(last as Record<string, unknown>),\n content: [...(last.content as unknown[]), { type: 'text', text }],\n } as AgentMessage\n return\n }\n output.messages.push({\n role: 'user',\n timestamp: Date.now(),\n content: [{ type: 'text', text }],\n } as AgentMessage)\n}\n","/** Background process notification: notify user on process start/end/error. */\n\nimport type { HookSpec } from '@x-otto/hooks'\nimport { defineHook } from '@x-otto/hooks'\nimport { injectPendingAsUserMessage } from './notification-injection'\n\nfunction formatExit(input: {\n processId: string\n pid: number\n command: string\n status: string\n exitCode?: number\n}): string {\n const exitLine = input.exitCode !== undefined ? `\\nexit code: ${input.exitCode}` : ''\n return (\n `<background-process-notification>\\n` +\n `id: ${input.processId}\\n` +\n `pid: ${input.pid}\\n` +\n `command: ${input.command}\\n` +\n `status: ${input.status}${exitLine}\\n` +\n `</background-process-notification>`\n )\n}\n\nexport function createBackgroundProcessNotificationHooks(): HookSpec[] {\n const pending = new Map<string, string[]>()\n\n const collectorHook = defineHook({\n name: 'background-process-exit-collector',\n timing: 'process.exited',\n handle: (input) => {\n const list = pending.get(input.sessionId) ?? []\n list.push(formatExit(input))\n pending.set(input.sessionId, list)\n },\n })\n\n const injectorHook = defineHook({\n name: 'background-process-exit-injector',\n timing: 'messages.transform',\n priority: 25,\n handle: (input, output) => {\n injectPendingAsUserMessage(pending, input, output)\n },\n })\n\n const cleanupHook = defineHook({\n name: 'background-process-notification-cleanup',\n timing: 'session.deleted',\n handle: (input) => {\n pending.delete(input.sessionId)\n },\n })\n\n return [collectorHook, injectorHook, cleanupHook]\n}\n","/** Background process session kill: kill running child processes on session end. */\nimport type { HookSpec } from '@x-otto/hooks'\nimport { defineHook } from '@x-otto/hooks'\n\nexport function createBackgroundProcessSessionKillHook(\n killForSession: (sessionId: string) => void,\n): HookSpec {\n return defineHook({\n name: 'background-process-session-kill',\n timing: 'session.deleted',\n handle: (input) => {\n const reason = input.metadata.reason\n if (reason === 'idle' || reason === 'evicted') {\n return\n }\n killForSession(input.sessionId)\n },\n })\n}\n","/** Session permission sync: sync project-level permissions to session scope. */\nimport type { HookSpec, PermissionPolicyRegistry } from '@x-otto/hooks'\nimport { defineHook } from '@x-otto/hooks'\n\nexport function createSessionPermissionSyncHooks(deps: {\n registry: PermissionPolicyRegistry\n /** 取某会话持久化的「始终允许」工具(= SessionPool.getSessionPermissionAllow)。 */\n getPersistedAllow: (sessionId: string) => string[]\n}): HookSpec[] {\n return [\n defineHook({\n name: 'session-permission-restore-hydrate',\n timing: 'session.restored',\n handle: (input) => {\n const tools = deps.getPersistedAllow(input.sessionId)\n if (tools.length > 0) deps.registry.setSessionAlwaysAllow(input.sessionId, tools)\n },\n }),\n defineHook({\n name: 'session-permission-cleanup',\n timing: 'session.deleted',\n handle: (input) => {\n deps.registry.clearSessionAlwaysAllow(input.sessionId)\n },\n }),\n ]\n}\n","/** Session learning: extract patterns from session for future prompting. */\n\nimport type { HookSpec } from '@x-otto/hooks'\nimport { defineHook } from '@x-otto/hooks'\nimport type { AgentSession } from '@x-otto/runtime'\nimport type { AgentMessage } from '@x-otto/agent'\nimport type { Message } from '@x-otto/ai'\n\n/** AgentMessage 是 `Message | SystemAgentMessage | …` 的联合;抽取只要真 LLM 消息,过滤掉 system 等。 */\nfunction isLLMMessage(m: AgentMessage): m is Message {\n return m.role === 'user' || m.role === 'assistant' || m.role === 'tool_result'\n}\n\n/** RFC-036 编排器的最小结构契约(`createMemoryAutoExtractor` 的返回值即满足)。 */\nexport interface MemoryAutoExtractor {\n onIdle: (sessionId: string, messages: readonly Message[]) => Promise<unknown>\n reset: (sessionId: string) => void\n}\n\n/**\n * RFC-036 M62-03b:把经验自动抽取编排器接到 `session.idle`——这是对**已删除**的\n * 旧路径 `createSessionLearningHooks`(prompt 驱动、烧主 agent 一回合,2026-07-18 随\n * session-end-learning.md 一并移除,见文件头注释)的**升级替代**(RFC-036 §8 方案 A):\n * 后台抽取、不烧回合、结构化 + 去重 + fail-closed。gate/节流/不抢由编排器 `onIdle` 内部负责;\n * 此处仅复用「≥minMessages、每会话触发(由 onIdle 的 index 增量天然保证)」的省调用门 +\n * `session.deleted` 时 `reset`。`onIdle` 永不抛,故无需额外 try/catch。\n */\nexport function createMemoryAutoExtractHooks(\n getSession: (id: string) => AgentSession | undefined,\n extractor: MemoryAutoExtractor,\n options: { minMessages?: number; isEnabled?: () => boolean } = {},\n): HookSpec[] {\n const minMessages = options.minMessages ?? 6\n\n const extractHook = defineHook({\n name: 'memory-auto-extract',\n timing: 'session.idle',\n handle: async (input) => {\n if (options.isEnabled && !options.isEnabled()) {\n return\n }\n const session = getSession(input.sessionId)\n if (!session) {\n return\n }\n const messages = session.session.messages()\n if (messages.length < minMessages) {\n return\n }\n await extractor.onIdle(input.sessionId, messages.filter(isLLMMessage))\n },\n })\n\n const cleanupHook = defineHook({\n name: 'memory-auto-extract-cleanup',\n timing: 'session.deleted',\n handle: (input) => {\n extractor.reset(input.sessionId)\n },\n })\n\n return [extractHook, cleanupHook]\n}\n","/** Skill loop guidance injection: 三分流判别段(RFC-318 D7)。 */\n\nimport { SKILL_LOOP_GUIDANCE } from '@x-otto/prompt'\nimport { SYSTEM_PROMPT_SECTIONS, createSessionStableInjectionHooks } from '@x-otto/runtime'\n\nimport type { HookSpec } from '@x-otto/hooks'\n\n/**\n * 判别段注入。\n *\n * **只在技能回路开启时注入**:关着的时候注入一段\"otto 会在后台观测重复流程\"的承诺却不\n * 兑现,是对模型的误导——它可能据此判断\"这事不用我管\",而实际上根本没人在观测。\n *\n * 段内容恒定(不随会话变),故走 stable lane 参与 prompt cache;开关切换会改变 stable\n * 前缀、使缓存失效一次,但开关不是高频操作,可接受。\n */\nexport function createSkillLoopGuidanceHooks(options: { isEnabled: () => boolean }): HookSpec[] {\n return createSessionStableInjectionHooks({\n name: SYSTEM_PROMPT_SECTIONS.skillLoopGuidance.name,\n priority: SYSTEM_PROMPT_SECTIONS.skillLoopGuidance.priority,\n compute: () => (options.isEnabled() ? SKILL_LOOP_GUIDANCE : undefined),\n })\n}\n","/**\n * candidate-brief.ts —— RFC-318 M4a:把候选渲染成 grill 简报。\n *\n * **固定模板 + 值填充**,不做自由拼接:插件可控的只有 `taskSignature`/`tags`/`sessionIds`\n * 三处**值**(且都已过 schema 长度上限),模板结构本身不可被插件影响。\n *\n * 简报显式声明来源——让模型知道这段内容来自插件观测,不是用户指令也不是宿主指令。\n * 这是\"受控注入通道\"里除长度约束外的第二层软防御:即便某个值里塞了指令式文本,它出现在\n * 一个被明确标注为\"插件观测数据\"的位置,而不是伪装成系统或用户的话。\n */\n\nimport type { CandidateBrief } from './candidate-reader'\n\n/**\n * 冷却期缺省值(14 天)。用户可在 `/preference` 里调(`skill_loop.dismiss_cooldown_days`)。\n *\n * **这里是真正的冷却 gate**——插件侧虽也有一份同名常量与 `pendingCandidates`,但那个函数\n * 零生产消费方;候选能否浮出完全由本函数决定。\n */\nexport const DISMISS_COOLDOWN_MS = 14 * 24 * 60 * 60 * 1000\n\n/** 冷却期下限——拒绝后至少隔一天再问,防止用户配成 0 后变成每次会话空闲都问。 */\nexport const DISMISS_COOLDOWN_FLOOR_MS = 24 * 60 * 60 * 1000\n\n/** 解析生效冷却期:非法值(非数字/过小)回退。 */\nexport function resolveCooldownMs(days?: number): number {\n if (typeof days !== 'number' || !Number.isFinite(days)) return DISMISS_COOLDOWN_MS\n return Math.max(DISMISS_COOLDOWN_FLOOR_MS, Math.round(days * 24 * 60 * 60 * 1000))\n}\n\n/**\n * 待提案的候选——过滤掉冷却期内被拒绝过的(R9)。\n *\n * @param cooldownMs 生效冷却期;缺省用 14 天。\n */\nexport function pendingBriefs(\n briefs: readonly CandidateBrief[],\n now: number,\n cooldownMs: number = DISMISS_COOLDOWN_MS,\n): CandidateBrief[] {\n return briefs.filter((b) => b.dismissedAt === undefined || now - b.dismissedAt >= cooldownMs)\n}\n\nfunction formatDate(ms: number): string {\n return new Date(ms).toISOString().slice(0, 10)\n}\n\n/** grill 的 question 段。 */\nexport function renderQuestion(brief: CandidateBrief): string {\n return `你重复做过同一类任务 ${brief.size} 次,要把它的执行流程固化成一个可复用的 skill 吗?`\n}\n\n/**\n * grill 的 background 段——**结构固定**,只填结构化元信息。\n *\n * 刻意**不展示**:插件生成的自由文本摘要、skill 骨架、episode 的具体步骤。用户想看细节\n * 时可以用 read 工具直接看候选文件原文(走正常工具审视路径,是用户主动发起的,不是被\n * 静默塞进上下文)。\n */\nexport function renderBackground(brief: CandidateBrief): string {\n const sessions = brief.sessionIds.slice(0, 5).join(', ')\n const more = brief.sessionIds.length > 5 ? ` 等 ${brief.sessionIds.length} 个会话` : ''\n return [\n '以下内容由插件 plugin-skill-inductor 观测生成(非用户输入、非宿主指令),仅供参考:',\n '',\n `任务类型:${brief.taskSignature}`,\n `出现次数:${brief.size}`,\n `标签:${brief.tags.length > 0 ? brief.tags.join(' / ') : '(无)'}`,\n `涉及会话:${sessions}${more}`,\n `时间跨度:${formatDate(brief.firstAt)} ~ ${formatDate(brief.lastAt)}`,\n '',\n '若同意,otto 会据这些会话的执行路径起草一份 SKILL.md 交你审阅——生成物仍需你显式 apply 才会生效。',\n `完整候选数据可用 read 工具查看插件状态目录下的 candidates.json。`,\n ].join('\\n')\n}\n\n/**\n * grill 的 recommendation 段。\n *\n * **必须是拒绝方向**(D-M4a-4,安全关键):`requestGrill` 在非交互模式下会**自动采纳\n * recommendation**。若推荐\"生成\",headless/CI 等无人值守场景会默默同意,违背\"用户显式\n * 授权\"的前提。让\"没人回答\"等价于\"这次先不做\"。\n */\nexport function renderRecommendation(): string {\n return '暂不生成。技能固化应由你在看到具体流程后主动决定;无人应答时默认跳过,稍后同类任务再出现会重新征询。'\n}\n\n/** grill 的选项(recommended 标在拒绝项上,与 recommendation 段一致)。 */\nexport function renderOptions(): {\n label: string\n rationale: string\n recommended?: boolean\n}[] {\n return [\n {\n label: '暂不生成',\n rationale: '保持现状。同类任务再积累一段时间后会重新征询(两周冷却期内不再打扰)。',\n recommended: true,\n },\n {\n label: '起草 SKILL.md 给我看',\n rationale: '据这些会话的执行路径生成一份技能草稿,仍需你审阅 diff 并显式 apply 才会生效。',\n },\n ]\n}\n\nexport const ACCEPT_OPTION_LABEL = '起草 SKILL.md 给我看'\n","/** A special constant with type `never` */\nexport const NEVER = Object.freeze({\n status: \"aborted\",\n});\nexport /*@__NO_SIDE_EFFECTS__*/ function $constructor(name, initializer, params) {\n function init(inst, def) {\n if (!inst._zod) {\n Object.defineProperty(inst, \"_zod\", {\n value: {\n def,\n constr: _,\n traits: new Set(),\n },\n enumerable: false,\n });\n }\n if (inst._zod.traits.has(name)) {\n return;\n }\n inst._zod.traits.add(name);\n initializer(inst, def);\n // support prototype modifications\n const proto = _.prototype;\n const keys = Object.keys(proto);\n for (let i = 0; i < keys.length; i++) {\n const k = keys[i];\n if (!(k in inst)) {\n inst[k] = proto[k].bind(inst);\n }\n }\n }\n // doesn't work if Parent has a constructor with arguments\n const Parent = params?.Parent ?? Object;\n class Definition extends Parent {\n }\n Object.defineProperty(Definition, \"name\", { value: name });\n function _(def) {\n var _a;\n const inst = params?.Parent ? new Definition() : this;\n init(inst, def);\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n for (const fn of inst._zod.deferred) {\n fn();\n }\n return inst;\n }\n Object.defineProperty(_, \"init\", { value: init });\n Object.defineProperty(_, Symbol.hasInstance, {\n value: (inst) => {\n if (params?.Parent && inst instanceof params.Parent)\n return true;\n return inst?._zod?.traits?.has(name);\n },\n });\n Object.defineProperty(_, \"name\", { value: name });\n return _;\n}\n////////////////////////////// UTILITIES ///////////////////////////////////////\nexport const $brand = Symbol(\"zod_brand\");\nexport class $ZodAsyncError extends Error {\n constructor() {\n super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);\n }\n}\nexport class $ZodEncodeError extends Error {\n constructor(name) {\n super(`Encountered unidirectional transform during encode: ${name}`);\n this.name = \"ZodEncodeError\";\n }\n}\nexport const globalConfig = {};\nexport function config(newConfig) {\n if (newConfig)\n Object.assign(globalConfig, newConfig);\n return globalConfig;\n}\n","// functions\nexport function assertEqual(val) {\n return val;\n}\nexport function assertNotEqual(val) {\n return val;\n}\nexport function assertIs(_arg) { }\nexport function assertNever(_x) {\n throw new Error(\"Unexpected value in exhaustive check\");\n}\nexport function assert(_) { }\nexport function getEnumValues(entries) {\n const numericValues = Object.values(entries).filter((v) => typeof v === \"number\");\n const values = Object.entries(entries)\n .filter(([k, _]) => numericValues.indexOf(+k) === -1)\n .map(([_, v]) => v);\n return values;\n}\nexport function joinValues(array, separator = \"|\") {\n return array.map((val) => stringifyPrimitive(val)).join(separator);\n}\nexport function jsonStringifyReplacer(_, value) {\n if (typeof value === \"bigint\")\n return value.toString();\n return value;\n}\nexport function cached(getter) {\n const set = false;\n return {\n get value() {\n if (!set) {\n const value = getter();\n Object.defineProperty(this, \"value\", { value });\n return value;\n }\n throw new Error(\"cached value already set\");\n },\n };\n}\nexport function nullish(input) {\n return input === null || input === undefined;\n}\nexport function cleanRegex(source) {\n const start = source.startsWith(\"^\") ? 1 : 0;\n const end = source.endsWith(\"$\") ? source.length - 1 : source.length;\n return source.slice(start, end);\n}\nexport function floatSafeRemainder(val, step) {\n const valDecCount = (val.toString().split(\".\")[1] || \"\").length;\n const stepString = step.toString();\n let stepDecCount = (stepString.split(\".\")[1] || \"\").length;\n if (stepDecCount === 0 && /\\d?e-\\d?/.test(stepString)) {\n const match = stepString.match(/\\d?e-(\\d?)/);\n if (match?.[1]) {\n stepDecCount = Number.parseInt(match[1]);\n }\n }\n const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;\n const valInt = Number.parseInt(val.toFixed(decCount).replace(\".\", \"\"));\n const stepInt = Number.parseInt(step.toFixed(decCount).replace(\".\", \"\"));\n return (valInt % stepInt) / 10 ** decCount;\n}\nconst EVALUATING = Symbol(\"evaluating\");\nexport function defineLazy(object, key, getter) {\n let value = undefined;\n Object.defineProperty(object, key, {\n get() {\n if (value === EVALUATING) {\n // Circular reference detected, return undefined to break the cycle\n return undefined;\n }\n if (value === undefined) {\n value = EVALUATING;\n value = getter();\n }\n return value;\n },\n set(v) {\n Object.defineProperty(object, key, {\n value: v,\n // configurable: true,\n });\n // object[key] = v;\n },\n configurable: true,\n });\n}\nexport function objectClone(obj) {\n return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));\n}\nexport function assignProp(target, prop, value) {\n Object.defineProperty(target, prop, {\n value,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n}\nexport function mergeDefs(...defs) {\n const mergedDescriptors = {};\n for (const def of defs) {\n const descriptors = Object.getOwnPropertyDescriptors(def);\n Object.assign(mergedDescriptors, descriptors);\n }\n return Object.defineProperties({}, mergedDescriptors);\n}\nexport function cloneDef(schema) {\n return mergeDefs(schema._zod.def);\n}\nexport function getElementAtPath(obj, path) {\n if (!path)\n return obj;\n return path.reduce((acc, key) => acc?.[key], obj);\n}\nexport function promiseAllObject(promisesObj) {\n const keys = Object.keys(promisesObj);\n const promises = keys.map((key) => promisesObj[key]);\n return Promise.all(promises).then((results) => {\n const resolvedObj = {};\n for (let i = 0; i < keys.length; i++) {\n resolvedObj[keys[i]] = results[i];\n }\n return resolvedObj;\n });\n}\nexport function randomString(length = 10) {\n const chars = \"abcdefghijklmnopqrstuvwxyz\";\n let str = \"\";\n for (let i = 0; i < length; i++) {\n str += chars[Math.floor(Math.random() * chars.length)];\n }\n return str;\n}\nexport function esc(str) {\n return JSON.stringify(str);\n}\nexport function slugify(input) {\n return input\n .toLowerCase()\n .trim()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/[\\s_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\nexport const captureStackTrace = (\"captureStackTrace\" in Error ? Error.captureStackTrace : (..._args) => { });\nexport function isObject(data) {\n return typeof data === \"object\" && data !== null && !Array.isArray(data);\n}\nexport const allowsEval = cached(() => {\n // @ts-ignore\n if (typeof navigator !== \"undefined\" && navigator?.userAgent?.includes(\"Cloudflare\")) {\n return false;\n }\n try {\n const F = Function;\n new F(\"\");\n return true;\n }\n catch (_) {\n return false;\n }\n});\nexport function isPlainObject(o) {\n if (isObject(o) === false)\n return false;\n // modified constructor\n const ctor = o.constructor;\n if (ctor === undefined)\n return true;\n if (typeof ctor !== \"function\")\n return true;\n // modified prototype\n const prot = ctor.prototype;\n if (isObject(prot) === false)\n return false;\n // ctor doesn't have static `isPrototypeOf`\n if (Object.prototype.hasOwnProperty.call(prot, \"isPrototypeOf\") === false) {\n return false;\n }\n return true;\n}\nexport function shallowClone(o) {\n if (isPlainObject(o))\n return { ...o };\n if (Array.isArray(o))\n return [...o];\n return o;\n}\nexport function numKeys(data) {\n let keyCount = 0;\n for (const key in data) {\n if (Object.prototype.hasOwnProperty.call(data, key)) {\n keyCount++;\n }\n }\n return keyCount;\n}\nexport const getParsedType = (data) => {\n const t = typeof data;\n switch (t) {\n case \"undefined\":\n return \"undefined\";\n case \"string\":\n return \"string\";\n case \"number\":\n return Number.isNaN(data) ? \"nan\" : \"number\";\n case \"boolean\":\n return \"boolean\";\n case \"function\":\n return \"function\";\n case \"bigint\":\n return \"bigint\";\n case \"symbol\":\n return \"symbol\";\n case \"object\":\n if (Array.isArray(data)) {\n return \"array\";\n }\n if (data === null) {\n return \"null\";\n }\n if (data.then && typeof data.then === \"function\" && data.catch && typeof data.catch === \"function\") {\n return \"promise\";\n }\n if (typeof Map !== \"undefined\" && data instanceof Map) {\n return \"map\";\n }\n if (typeof Set !== \"undefined\" && data instanceof Set) {\n return \"set\";\n }\n if (typeof Date !== \"undefined\" && data instanceof Date) {\n return \"date\";\n }\n // @ts-ignore\n if (typeof File !== \"undefined\" && data instanceof File) {\n return \"file\";\n }\n return \"object\";\n default:\n throw new Error(`Unknown data type: ${t}`);\n }\n};\nexport const propertyKeyTypes = new Set([\"string\", \"number\", \"symbol\"]);\nexport const primitiveTypes = new Set([\"string\", \"number\", \"bigint\", \"boolean\", \"symbol\", \"undefined\"]);\nexport function escapeRegex(str) {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n// zod-specific utils\nexport function clone(inst, def, params) {\n const cl = new inst._zod.constr(def ?? inst._zod.def);\n if (!def || params?.parent)\n cl._zod.parent = inst;\n return cl;\n}\nexport function normalizeParams(_params) {\n const params = _params;\n if (!params)\n return {};\n if (typeof params === \"string\")\n return { error: () => params };\n if (params?.message !== undefined) {\n if (params?.error !== undefined)\n throw new Error(\"Cannot specify both `message` and `error` params\");\n params.error = params.message;\n }\n delete params.message;\n if (typeof params.error === \"string\")\n return { ...params, error: () => params.error };\n return params;\n}\nexport function createTransparentProxy(getter) {\n let target;\n return new Proxy({}, {\n get(_, prop, receiver) {\n target ?? (target = getter());\n return Reflect.get(target, prop, receiver);\n },\n set(_, prop, value, receiver) {\n target ?? (target = getter());\n return Reflect.set(target, prop, value, receiver);\n },\n has(_, prop) {\n target ?? (target = getter());\n return Reflect.has(target, prop);\n },\n deleteProperty(_, prop) {\n target ?? (target = getter());\n return Reflect.deleteProperty(target, prop);\n },\n ownKeys(_) {\n target ?? (target = getter());\n return Reflect.ownKeys(target);\n },\n getOwnPropertyDescriptor(_, prop) {\n target ?? (target = getter());\n return Reflect.getOwnPropertyDescriptor(target, prop);\n },\n defineProperty(_, prop, descriptor) {\n target ?? (target = getter());\n return Reflect.defineProperty(target, prop, descriptor);\n },\n });\n}\nexport function stringifyPrimitive(value) {\n if (typeof value === \"bigint\")\n return value.toString() + \"n\";\n if (typeof value === \"string\")\n return `\"${value}\"`;\n return `${value}`;\n}\nexport function optionalKeys(shape) {\n return Object.keys(shape).filter((k) => {\n return shape[k]._zod.optin === \"optional\" && shape[k]._zod.optout === \"optional\";\n });\n}\nexport const NUMBER_FORMAT_RANGES = {\n safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],\n int32: [-2147483648, 2147483647],\n uint32: [0, 4294967295],\n float32: [-3.4028234663852886e38, 3.4028234663852886e38],\n float64: [-Number.MAX_VALUE, Number.MAX_VALUE],\n};\nexport const BIGINT_FORMAT_RANGES = {\n int64: [/* @__PURE__*/ BigInt(\"-9223372036854775808\"), /* @__PURE__*/ BigInt(\"9223372036854775807\")],\n uint64: [/* @__PURE__*/ BigInt(0), /* @__PURE__*/ BigInt(\"18446744073709551615\")],\n};\nexport function pick(schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n const hasChecks = checks && checks.length > 0;\n if (hasChecks) {\n throw new Error(\".pick() cannot be used on object schemas containing refinements\");\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const newShape = {};\n for (const key in mask) {\n if (!(key in currDef.shape)) {\n throw new Error(`Unrecognized key: \"${key}\"`);\n }\n if (!mask[key])\n continue;\n newShape[key] = currDef.shape[key];\n }\n assignProp(this, \"shape\", newShape); // self-caching\n return newShape;\n },\n checks: [],\n });\n return clone(schema, def);\n}\nexport function omit(schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n const hasChecks = checks && checks.length > 0;\n if (hasChecks) {\n throw new Error(\".omit() cannot be used on object schemas containing refinements\");\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const newShape = { ...schema._zod.def.shape };\n for (const key in mask) {\n if (!(key in currDef.shape)) {\n throw new Error(`Unrecognized key: \"${key}\"`);\n }\n if (!mask[key])\n continue;\n delete newShape[key];\n }\n assignProp(this, \"shape\", newShape); // self-caching\n return newShape;\n },\n checks: [],\n });\n return clone(schema, def);\n}\nexport function extend(schema, shape) {\n if (!isPlainObject(shape)) {\n throw new Error(\"Invalid input to extend: expected a plain object\");\n }\n const checks = schema._zod.def.checks;\n const hasChecks = checks && checks.length > 0;\n if (hasChecks) {\n // Only throw if new shape overlaps with existing shape\n // Use getOwnPropertyDescriptor to check key existence without accessing values\n const existingShape = schema._zod.def.shape;\n for (const key in shape) {\n if (Object.getOwnPropertyDescriptor(existingShape, key) !== undefined) {\n throw new Error(\"Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.\");\n }\n }\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const _shape = { ...schema._zod.def.shape, ...shape };\n assignProp(this, \"shape\", _shape); // self-caching\n return _shape;\n },\n });\n return clone(schema, def);\n}\nexport function safeExtend(schema, shape) {\n if (!isPlainObject(shape)) {\n throw new Error(\"Invalid input to safeExtend: expected a plain object\");\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const _shape = { ...schema._zod.def.shape, ...shape };\n assignProp(this, \"shape\", _shape); // self-caching\n return _shape;\n },\n });\n return clone(schema, def);\n}\nexport function merge(a, b) {\n const def = mergeDefs(a._zod.def, {\n get shape() {\n const _shape = { ...a._zod.def.shape, ...b._zod.def.shape };\n assignProp(this, \"shape\", _shape); // self-caching\n return _shape;\n },\n get catchall() {\n return b._zod.def.catchall;\n },\n checks: [], // delete existing checks\n });\n return clone(a, def);\n}\nexport function partial(Class, schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n const hasChecks = checks && checks.length > 0;\n if (hasChecks) {\n throw new Error(\".partial() cannot be used on object schemas containing refinements\");\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const oldShape = schema._zod.def.shape;\n const shape = { ...oldShape };\n if (mask) {\n for (const key in mask) {\n if (!(key in oldShape)) {\n throw new Error(`Unrecognized key: \"${key}\"`);\n }\n if (!mask[key])\n continue;\n // if (oldShape[key]!._zod.optin === \"optional\") continue;\n shape[key] = Class\n ? new Class({\n type: \"optional\",\n innerType: oldShape[key],\n })\n : oldShape[key];\n }\n }\n else {\n for (const key in oldShape) {\n // if (oldShape[key]!._zod.optin === \"optional\") continue;\n shape[key] = Class\n ? new Class({\n type: \"optional\",\n innerType: oldShape[key],\n })\n : oldShape[key];\n }\n }\n assignProp(this, \"shape\", shape); // self-caching\n return shape;\n },\n checks: [],\n });\n return clone(schema, def);\n}\nexport function required(Class, schema, mask) {\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const oldShape = schema._zod.def.shape;\n const shape = { ...oldShape };\n if (mask) {\n for (const key in mask) {\n if (!(key in shape)) {\n throw new Error(`Unrecognized key: \"${key}\"`);\n }\n if (!mask[key])\n continue;\n // overwrite with non-optional\n shape[key] = new Class({\n type: \"nonoptional\",\n innerType: oldShape[key],\n });\n }\n }\n else {\n for (const key in oldShape) {\n // overwrite with non-optional\n shape[key] = new Class({\n type: \"nonoptional\",\n innerType: oldShape[key],\n });\n }\n }\n assignProp(this, \"shape\", shape); // self-caching\n return shape;\n },\n });\n return clone(schema, def);\n}\n// invalid_type | too_big | too_small | invalid_format | not_multiple_of | unrecognized_keys | invalid_union | invalid_key | invalid_element | invalid_value | custom\nexport function aborted(x, startIndex = 0) {\n if (x.aborted === true)\n return true;\n for (let i = startIndex; i < x.issues.length; i++) {\n if (x.issues[i]?.continue !== true) {\n return true;\n }\n }\n return false;\n}\nexport function prefixIssues(path, issues) {\n return issues.map((iss) => {\n var _a;\n (_a = iss).path ?? (_a.path = []);\n iss.path.unshift(path);\n return iss;\n });\n}\nexport function unwrapMessage(message) {\n return typeof message === \"string\" ? message : message?.message;\n}\nexport function finalizeIssue(iss, ctx, config) {\n const full = { ...iss, path: iss.path ?? [] };\n // for backwards compatibility\n if (!iss.message) {\n const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ??\n unwrapMessage(ctx?.error?.(iss)) ??\n unwrapMessage(config.customError?.(iss)) ??\n unwrapMessage(config.localeError?.(iss)) ??\n \"Invalid input\";\n full.message = message;\n }\n // delete (full as any).def;\n delete full.inst;\n delete full.continue;\n if (!ctx?.reportInput) {\n delete full.input;\n }\n return full;\n}\nexport function getSizableOrigin(input) {\n if (input instanceof Set)\n return \"set\";\n if (input instanceof Map)\n return \"map\";\n // @ts-ignore\n if (input instanceof File)\n return \"file\";\n return \"unknown\";\n}\nexport function getLengthableOrigin(input) {\n if (Array.isArray(input))\n return \"array\";\n if (typeof input === \"string\")\n return \"string\";\n return \"unknown\";\n}\nexport function parsedType(data) {\n const t = typeof data;\n switch (t) {\n case \"number\": {\n return Number.isNaN(data) ? \"nan\" : \"number\";\n }\n case \"object\": {\n if (data === null) {\n return \"null\";\n }\n if (Array.isArray(data)) {\n return \"array\";\n }\n const obj = data;\n if (obj && Object.getPrototypeOf(obj) !== Object.prototype && \"constructor\" in obj && obj.constructor) {\n return obj.constructor.name;\n }\n }\n }\n return t;\n}\nexport function issue(...args) {\n const [iss, input, inst] = args;\n if (typeof iss === \"string\") {\n return {\n message: iss,\n code: \"custom\",\n input,\n inst,\n };\n }\n return { ...iss };\n}\nexport function cleanEnum(obj) {\n return Object.entries(obj)\n .filter(([k, _]) => {\n // return true if NaN, meaning it's not a number, thus a string key\n return Number.isNaN(Number.parseInt(k, 10));\n })\n .map((el) => el[1]);\n}\n// Codec utility functions\nexport function base64ToUint8Array(base64) {\n const binaryString = atob(base64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\nexport function uint8ArrayToBase64(bytes) {\n let binaryString = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binaryString += String.fromCharCode(bytes[i]);\n }\n return btoa(binaryString);\n}\nexport function base64urlToUint8Array(base64url) {\n const base64 = base64url.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padding = \"=\".repeat((4 - (base64.length % 4)) % 4);\n return base64ToUint8Array(base64 + padding);\n}\nexport function uint8ArrayToBase64url(bytes) {\n return uint8ArrayToBase64(bytes).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\nexport function hexToUint8Array(hex) {\n const cleanHex = hex.replace(/^0x/, \"\");\n if (cleanHex.length % 2 !== 0) {\n throw new Error(\"Invalid hex string length\");\n }\n const bytes = new Uint8Array(cleanHex.length / 2);\n for (let i = 0; i < cleanHex.length; i += 2) {\n bytes[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);\n }\n return bytes;\n}\nexport function uint8ArrayToHex(bytes) {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n// instanceof\nexport class Class {\n constructor(..._args) { }\n}\n","import { $constructor } from \"./core.js\";\nimport * as util from \"./util.js\";\nconst initializer = (inst, def) => {\n inst.name = \"$ZodError\";\n Object.defineProperty(inst, \"_zod\", {\n value: inst._zod,\n enumerable: false,\n });\n Object.defineProperty(inst, \"issues\", {\n value: def,\n enumerable: false,\n });\n inst.message = JSON.stringify(def, util.jsonStringifyReplacer, 2);\n Object.defineProperty(inst, \"toString\", {\n value: () => inst.message,\n enumerable: false,\n });\n};\nexport const $ZodError = $constructor(\"$ZodError\", initializer);\nexport const $ZodRealError = $constructor(\"$ZodError\", initializer, { Parent: Error });\nexport function flattenError(error, mapper = (issue) => issue.message) {\n const fieldErrors = {};\n const formErrors = [];\n for (const sub of error.issues) {\n if (sub.path.length > 0) {\n fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];\n fieldErrors[sub.path[0]].push(mapper(sub));\n }\n else {\n formErrors.push(mapper(sub));\n }\n }\n return { formErrors, fieldErrors };\n}\nexport function formatError(error, mapper = (issue) => issue.message) {\n const fieldErrors = { _errors: [] };\n const processError = (error) => {\n for (const issue of error.issues) {\n if (issue.code === \"invalid_union\" && issue.errors.length) {\n issue.errors.map((issues) => processError({ issues }));\n }\n else if (issue.code === \"invalid_key\") {\n processError({ issues: issue.issues });\n }\n else if (issue.code === \"invalid_element\") {\n processError({ issues: issue.issues });\n }\n else if (issue.path.length === 0) {\n fieldErrors._errors.push(mapper(issue));\n }\n else {\n let curr = fieldErrors;\n let i = 0;\n while (i < issue.path.length) {\n const el = issue.path[i];\n const terminal = i === issue.path.length - 1;\n if (!terminal) {\n curr[el] = curr[el] || { _errors: [] };\n }\n else {\n curr[el] = curr[el] || { _errors: [] };\n curr[el]._errors.push(mapper(issue));\n }\n curr = curr[el];\n i++;\n }\n }\n }\n };\n processError(error);\n return fieldErrors;\n}\nexport function treeifyError(error, mapper = (issue) => issue.message) {\n const result = { errors: [] };\n const processError = (error, path = []) => {\n var _a, _b;\n for (const issue of error.issues) {\n if (issue.code === \"invalid_union\" && issue.errors.length) {\n // regular union error\n issue.errors.map((issues) => processError({ issues }, issue.path));\n }\n else if (issue.code === \"invalid_key\") {\n processError({ issues: issue.issues }, issue.path);\n }\n else if (issue.code === \"invalid_element\") {\n processError({ issues: issue.issues }, issue.path);\n }\n else {\n const fullpath = [...path, ...issue.path];\n if (fullpath.length === 0) {\n result.errors.push(mapper(issue));\n continue;\n }\n let curr = result;\n let i = 0;\n while (i < fullpath.length) {\n const el = fullpath[i];\n const terminal = i === fullpath.length - 1;\n if (typeof el === \"string\") {\n curr.properties ?? (curr.properties = {});\n (_a = curr.properties)[el] ?? (_a[el] = { errors: [] });\n curr = curr.properties[el];\n }\n else {\n curr.items ?? (curr.items = []);\n (_b = curr.items)[el] ?? (_b[el] = { errors: [] });\n curr = curr.items[el];\n }\n if (terminal) {\n curr.errors.push(mapper(issue));\n }\n i++;\n }\n }\n }\n };\n processError(error);\n return result;\n}\n/** Format a ZodError as a human-readable string in the following form.\n *\n * From\n *\n * ```ts\n * ZodError {\n * issues: [\n * {\n * expected: 'string',\n * code: 'invalid_type',\n * path: [ 'username' ],\n * message: 'Invalid input: expected string'\n * },\n * {\n * expected: 'number',\n * code: 'invalid_type',\n * path: [ 'favoriteNumbers', 1 ],\n * message: 'Invalid input: expected number'\n * }\n * ];\n * }\n * ```\n *\n * to\n *\n * ```\n * username\n * ✖ Expected number, received string at \"username\n * favoriteNumbers[0]\n * ✖ Invalid input: expected number\n * ```\n */\nexport function toDotPath(_path) {\n const segs = [];\n const path = _path.map((seg) => (typeof seg === \"object\" ? seg.key : seg));\n for (const seg of path) {\n if (typeof seg === \"number\")\n segs.push(`[${seg}]`);\n else if (typeof seg === \"symbol\")\n segs.push(`[${JSON.stringify(String(seg))}]`);\n else if (/[^\\w$]/.test(seg))\n segs.push(`[${JSON.stringify(seg)}]`);\n else {\n if (segs.length)\n segs.push(\".\");\n segs.push(seg);\n }\n }\n return segs.join(\"\");\n}\nexport function prettifyError(error) {\n const lines = [];\n // sort by path length\n const issues = [...error.issues].sort((a, b) => (a.path ?? []).length - (b.path ?? []).length);\n // Process each issue\n for (const issue of issues) {\n lines.push(`✖ ${issue.message}`);\n if (issue.path?.length)\n lines.push(` → at ${toDotPath(issue.path)}`);\n }\n // Convert Map to formatted string\n return lines.join(\"\\n\");\n}\n","import * as core from \"./core.js\";\nimport * as errors from \"./errors.js\";\nimport * as util from \"./util.js\";\nexport const _parse = (_Err) => (schema, value, _ctx, _params) => {\n const ctx = _ctx ? Object.assign(_ctx, { async: false }) : { async: false };\n const result = schema._zod.run({ value, issues: [] }, ctx);\n if (result instanceof Promise) {\n throw new core.$ZodAsyncError();\n }\n if (result.issues.length) {\n const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())));\n util.captureStackTrace(e, _params?.callee);\n throw e;\n }\n return result.value;\n};\nexport const parse = /* @__PURE__*/ _parse(errors.$ZodRealError);\nexport const _parseAsync = (_Err) => async (schema, value, _ctx, params) => {\n const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };\n let result = schema._zod.run({ value, issues: [] }, ctx);\n if (result instanceof Promise)\n result = await result;\n if (result.issues.length) {\n const e = new (params?.Err ?? _Err)(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())));\n util.captureStackTrace(e, params?.callee);\n throw e;\n }\n return result.value;\n};\nexport const parseAsync = /* @__PURE__*/ _parseAsync(errors.$ZodRealError);\nexport const _safeParse = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? { ..._ctx, async: false } : { async: false };\n const result = schema._zod.run({ value, issues: [] }, ctx);\n if (result instanceof Promise) {\n throw new core.$ZodAsyncError();\n }\n return result.issues.length\n ? {\n success: false,\n error: new (_Err ?? errors.$ZodError)(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),\n }\n : { success: true, data: result.value };\n};\nexport const safeParse = /* @__PURE__*/ _safeParse(errors.$ZodRealError);\nexport const _safeParseAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };\n let result = schema._zod.run({ value, issues: [] }, ctx);\n if (result instanceof Promise)\n result = await result;\n return result.issues.length\n ? {\n success: false,\n error: new _Err(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),\n }\n : { success: true, data: result.value };\n};\nexport const safeParseAsync = /* @__PURE__*/ _safeParseAsync(errors.$ZodRealError);\nexport const _encode = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { direction: \"backward\" }) : { direction: \"backward\" };\n return _parse(_Err)(schema, value, ctx);\n};\nexport const encode = /* @__PURE__*/ _encode(errors.$ZodRealError);\nexport const _decode = (_Err) => (schema, value, _ctx) => {\n return _parse(_Err)(schema, value, _ctx);\n};\nexport const decode = /* @__PURE__*/ _decode(errors.$ZodRealError);\nexport const _encodeAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { direction: \"backward\" }) : { direction: \"backward\" };\n return _parseAsync(_Err)(schema, value, ctx);\n};\nexport const encodeAsync = /* @__PURE__*/ _encodeAsync(errors.$ZodRealError);\nexport const _decodeAsync = (_Err) => async (schema, value, _ctx) => {\n return _parseAsync(_Err)(schema, value, _ctx);\n};\nexport const decodeAsync = /* @__PURE__*/ _decodeAsync(errors.$ZodRealError);\nexport const _safeEncode = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { direction: \"backward\" }) : { direction: \"backward\" };\n return _safeParse(_Err)(schema, value, ctx);\n};\nexport const safeEncode = /* @__PURE__*/ _safeEncode(errors.$ZodRealError);\nexport const _safeDecode = (_Err) => (schema, value, _ctx) => {\n return _safeParse(_Err)(schema, value, _ctx);\n};\nexport const safeDecode = /* @__PURE__*/ _safeDecode(errors.$ZodRealError);\nexport const _safeEncodeAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { direction: \"backward\" }) : { direction: \"backward\" };\n return _safeParseAsync(_Err)(schema, value, ctx);\n};\nexport const safeEncodeAsync = /* @__PURE__*/ _safeEncodeAsync(errors.$ZodRealError);\nexport const _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {\n return _safeParseAsync(_Err)(schema, value, _ctx);\n};\nexport const safeDecodeAsync = /* @__PURE__*/ _safeDecodeAsync(errors.$ZodRealError);\n","import * as util from \"./util.js\";\nexport const cuid = /^[cC][^\\s-]{8,}$/;\nexport const cuid2 = /^[0-9a-z]+$/;\nexport const ulid = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/;\nexport const xid = /^[0-9a-vA-V]{20}$/;\nexport const ksuid = /^[A-Za-z0-9]{27}$/;\nexport const nanoid = /^[a-zA-Z0-9_-]{21}$/;\n/** ISO 8601-1 duration regex. Does not support the 8601-2 extensions like negative durations or fractional/negative components. */\nexport const duration = /^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$/;\n/** Implements ISO 8601-2 extensions like explicit +- prefixes, mixing weeks with other units, and fractional/negative components. */\nexport const extendedDuration = /^[-+]?P(?!$)(?:(?:[-+]?\\d+Y)|(?:[-+]?\\d+[.,]\\d+Y$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:(?:[-+]?\\d+W)|(?:[-+]?\\d+[.,]\\d+W$))?(?:(?:[-+]?\\d+D)|(?:[-+]?\\d+[.,]\\d+D$))?(?:T(?=[\\d+-])(?:(?:[-+]?\\d+H)|(?:[-+]?\\d+[.,]\\d+H$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:[-+]?\\d+(?:[.,]\\d+)?S)?)??$/;\n/** A regex for any UUID-like identifier: 8-4-4-4-12 hex pattern */\nexport const guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;\n/** Returns a regex for validating an RFC 9562/4122 UUID.\n *\n * @param version Optionally specify a version 1-8. If no version is specified, all versions are supported. */\nexport const uuid = (version) => {\n if (!version)\n return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;\n return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);\n};\nexport const uuid4 = /*@__PURE__*/ uuid(4);\nexport const uuid6 = /*@__PURE__*/ uuid(6);\nexport const uuid7 = /*@__PURE__*/ uuid(7);\n/** Practical email validation */\nexport const email = /^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$/;\n/** Equivalent to the HTML5 input[type=email] validation implemented by browsers. Source: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email */\nexport const html5Email = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n/** The classic emailregex.com regex for RFC 5322-compliant emails */\nexport const rfc5322Email = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n/** A loose regex that allows Unicode characters, enforces length limits, and that's about it. */\nexport const unicodeEmail = /^[^\\s@\"]{1,64}@[^\\s@]{1,255}$/u;\nexport const idnEmail = unicodeEmail;\nexport const browserEmail = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression\nconst _emoji = `^(\\\\p{Extended_Pictographic}|\\\\p{Emoji_Component})+$`;\nexport function emoji() {\n return new RegExp(_emoji, \"u\");\n}\nexport const ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;\nexport const ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;\nexport const mac = (delimiter) => {\n const escapedDelim = util.escapeRegex(delimiter ?? \":\");\n return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);\n};\nexport const cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\/([0-9]|[1-2][0-9]|3[0-2])$/;\nexport const cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;\n// https://stackoverflow.com/questions/7860392/determine-if-string-is-in-base64-using-javascript\nexport const base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;\nexport const base64url = /^[A-Za-z0-9_-]*$/;\n// based on https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address\n// export const hostname: RegExp = /^([a-zA-Z0-9-]+\\.)*[a-zA-Z0-9-]+$/;\nexport const hostname = /^(?=.{1,253}\\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\\.?$/;\nexport const domain = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$/;\n// https://blog.stevenlevithan.com/archives/validate-phone-number#r4-3 (regex sans spaces)\n// E.164: leading digit must be 1-9; total digits (excluding '+') between 7-15\nexport const e164 = /^\\+[1-9]\\d{6,14}$/;\n// const dateSource = `((\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\\\d|30)|(02)-(0[1-9]|1\\\\d|2[0-8])))`;\nconst dateSource = `(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))`;\nexport const date = /*@__PURE__*/ new RegExp(`^${dateSource}$`);\nfunction timeSource(args) {\n const hhmm = `(?:[01]\\\\d|2[0-3]):[0-5]\\\\d`;\n const regex = typeof args.precision === \"number\"\n ? args.precision === -1\n ? `${hhmm}`\n : args.precision === 0\n ? `${hhmm}:[0-5]\\\\d`\n : `${hhmm}:[0-5]\\\\d\\\\.\\\\d{${args.precision}}`\n : `${hhmm}(?::[0-5]\\\\d(?:\\\\.\\\\d+)?)?`;\n return regex;\n}\nexport function time(args) {\n return new RegExp(`^${timeSource(args)}$`);\n}\n// Adapted from https://stackoverflow.com/a/3143231\nexport function datetime(args) {\n const time = timeSource({ precision: args.precision });\n const opts = [\"Z\"];\n if (args.local)\n opts.push(\"\");\n // if (args.offset) opts.push(`([+-]\\\\d{2}:\\\\d{2})`);\n if (args.offset)\n opts.push(`([+-](?:[01]\\\\d|2[0-3]):[0-5]\\\\d)`);\n const timeRegex = `${time}(?:${opts.join(\"|\")})`;\n return new RegExp(`^${dateSource}T(?:${timeRegex})$`);\n}\nexport const string = (params) => {\n const regex = params ? `[\\\\s\\\\S]{${params?.minimum ?? 0},${params?.maximum ?? \"\"}}` : `[\\\\s\\\\S]*`;\n return new RegExp(`^${regex}$`);\n};\nexport const bigint = /^-?\\d+n?$/;\nexport const integer = /^-?\\d+$/;\nexport const number = /^-?\\d+(?:\\.\\d+)?$/;\nexport const boolean = /^(?:true|false)$/i;\nconst _null = /^null$/i;\nexport { _null as null };\nconst _undefined = /^undefined$/i;\nexport { _undefined as undefined };\n// regex for string with no uppercase letters\nexport const lowercase = /^[^A-Z]*$/;\n// regex for string with no lowercase letters\nexport const uppercase = /^[^a-z]*$/;\n// regex for hexadecimal strings (any length)\nexport const hex = /^[0-9a-fA-F]*$/;\n// Hash regexes for different algorithms and encodings\n// Helper function to create base64 regex with exact length and padding\nfunction fixedBase64(bodyLength, padding) {\n return new RegExp(`^[A-Za-z0-9+/]{${bodyLength}}${padding}$`);\n}\n// Helper function to create base64url regex with exact length (no padding)\nfunction fixedBase64url(length) {\n return new RegExp(`^[A-Za-z0-9_-]{${length}}$`);\n}\n// MD5 (16 bytes): base64 = 24 chars total (22 + \"==\")\nexport const md5_hex = /^[0-9a-fA-F]{32}$/;\nexport const md5_base64 = /*@__PURE__*/ fixedBase64(22, \"==\");\nexport const md5_base64url = /*@__PURE__*/ fixedBase64url(22);\n// SHA1 (20 bytes): base64 = 28 chars total (27 + \"=\")\nexport const sha1_hex = /^[0-9a-fA-F]{40}$/;\nexport const sha1_base64 = /*@__PURE__*/ fixedBase64(27, \"=\");\nexport const sha1_base64url = /*@__PURE__*/ fixedBase64url(27);\n// SHA256 (32 bytes): base64 = 44 chars total (43 + \"=\")\nexport const sha256_hex = /^[0-9a-fA-F]{64}$/;\nexport const sha256_base64 = /*@__PURE__*/ fixedBase64(43, \"=\");\nexport const sha256_base64url = /*@__PURE__*/ fixedBase64url(43);\n// SHA384 (48 bytes): base64 = 64 chars total (no padding)\nexport const sha384_hex = /^[0-9a-fA-F]{96}$/;\nexport const sha384_base64 = /*@__PURE__*/ fixedBase64(64, \"\");\nexport const sha384_base64url = /*@__PURE__*/ fixedBase64url(64);\n// SHA512 (64 bytes): base64 = 88 chars total (86 + \"==\")\nexport const sha512_hex = /^[0-9a-fA-F]{128}$/;\nexport const sha512_base64 = /*@__PURE__*/ fixedBase64(86, \"==\");\nexport const sha512_base64url = /*@__PURE__*/ fixedBase64url(86);\n","// import { $ZodType } from \"./schemas.js\";\nimport * as core from \"./core.js\";\nimport * as regexes from \"./regexes.js\";\nimport * as util from \"./util.js\";\nexport const $ZodCheck = /*@__PURE__*/ core.$constructor(\"$ZodCheck\", (inst, def) => {\n var _a;\n inst._zod ?? (inst._zod = {});\n inst._zod.def = def;\n (_a = inst._zod).onattach ?? (_a.onattach = []);\n});\nconst numericOriginMap = {\n number: \"number\",\n bigint: \"bigint\",\n object: \"date\",\n};\nexport const $ZodCheckLessThan = /*@__PURE__*/ core.$constructor(\"$ZodCheckLessThan\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const origin = numericOriginMap[typeof def.value];\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n const curr = (def.inclusive ? bag.maximum : bag.exclusiveMaximum) ?? Number.POSITIVE_INFINITY;\n if (def.value < curr) {\n if (def.inclusive)\n bag.maximum = def.value;\n else\n bag.exclusiveMaximum = def.value;\n }\n });\n inst._zod.check = (payload) => {\n if (def.inclusive ? payload.value <= def.value : payload.value < def.value) {\n return;\n }\n payload.issues.push({\n origin,\n code: \"too_big\",\n maximum: typeof def.value === \"object\" ? def.value.getTime() : def.value,\n input: payload.value,\n inclusive: def.inclusive,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckGreaterThan = /*@__PURE__*/ core.$constructor(\"$ZodCheckGreaterThan\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const origin = numericOriginMap[typeof def.value];\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n const curr = (def.inclusive ? bag.minimum : bag.exclusiveMinimum) ?? Number.NEGATIVE_INFINITY;\n if (def.value > curr) {\n if (def.inclusive)\n bag.minimum = def.value;\n else\n bag.exclusiveMinimum = def.value;\n }\n });\n inst._zod.check = (payload) => {\n if (def.inclusive ? payload.value >= def.value : payload.value > def.value) {\n return;\n }\n payload.issues.push({\n origin,\n code: \"too_small\",\n minimum: typeof def.value === \"object\" ? def.value.getTime() : def.value,\n input: payload.value,\n inclusive: def.inclusive,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckMultipleOf = \n/*@__PURE__*/ core.$constructor(\"$ZodCheckMultipleOf\", (inst, def) => {\n $ZodCheck.init(inst, def);\n inst._zod.onattach.push((inst) => {\n var _a;\n (_a = inst._zod.bag).multipleOf ?? (_a.multipleOf = def.value);\n });\n inst._zod.check = (payload) => {\n if (typeof payload.value !== typeof def.value)\n throw new Error(\"Cannot mix number and bigint in multiple_of check.\");\n const isMultiple = typeof payload.value === \"bigint\"\n ? payload.value % def.value === BigInt(0)\n : util.floatSafeRemainder(payload.value, def.value) === 0;\n if (isMultiple)\n return;\n payload.issues.push({\n origin: typeof payload.value,\n code: \"not_multiple_of\",\n divisor: def.value,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckNumberFormat = /*@__PURE__*/ core.$constructor(\"$ZodCheckNumberFormat\", (inst, def) => {\n $ZodCheck.init(inst, def); // no format checks\n def.format = def.format || \"float64\";\n const isInt = def.format?.includes(\"int\");\n const origin = isInt ? \"int\" : \"number\";\n const [minimum, maximum] = util.NUMBER_FORMAT_RANGES[def.format];\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.format = def.format;\n bag.minimum = minimum;\n bag.maximum = maximum;\n if (isInt)\n bag.pattern = regexes.integer;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n if (isInt) {\n if (!Number.isInteger(input)) {\n // invalid_format issue\n // payload.issues.push({\n // expected: def.format,\n // format: def.format,\n // code: \"invalid_format\",\n // input,\n // inst,\n // });\n // invalid_type issue\n payload.issues.push({\n expected: origin,\n format: def.format,\n code: \"invalid_type\",\n continue: false,\n input,\n inst,\n });\n return;\n // not_multiple_of issue\n // payload.issues.push({\n // code: \"not_multiple_of\",\n // origin: \"number\",\n // input,\n // inst,\n // divisor: 1,\n // });\n }\n if (!Number.isSafeInteger(input)) {\n if (input > 0) {\n // too_big\n payload.issues.push({\n input,\n code: \"too_big\",\n maximum: Number.MAX_SAFE_INTEGER,\n note: \"Integers must be within the safe integer range.\",\n inst,\n origin,\n inclusive: true,\n continue: !def.abort,\n });\n }\n else {\n // too_small\n payload.issues.push({\n input,\n code: \"too_small\",\n minimum: Number.MIN_SAFE_INTEGER,\n note: \"Integers must be within the safe integer range.\",\n inst,\n origin,\n inclusive: true,\n continue: !def.abort,\n });\n }\n return;\n }\n }\n if (input < minimum) {\n payload.issues.push({\n origin: \"number\",\n input,\n code: \"too_small\",\n minimum,\n inclusive: true,\n inst,\n continue: !def.abort,\n });\n }\n if (input > maximum) {\n payload.issues.push({\n origin: \"number\",\n input,\n code: \"too_big\",\n maximum,\n inclusive: true,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\nexport const $ZodCheckBigIntFormat = /*@__PURE__*/ core.$constructor(\"$ZodCheckBigIntFormat\", (inst, def) => {\n $ZodCheck.init(inst, def); // no format checks\n const [minimum, maximum] = util.BIGINT_FORMAT_RANGES[def.format];\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.format = def.format;\n bag.minimum = minimum;\n bag.maximum = maximum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n if (input < minimum) {\n payload.issues.push({\n origin: \"bigint\",\n input,\n code: \"too_small\",\n minimum: minimum,\n inclusive: true,\n inst,\n continue: !def.abort,\n });\n }\n if (input > maximum) {\n payload.issues.push({\n origin: \"bigint\",\n input,\n code: \"too_big\",\n maximum,\n inclusive: true,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\nexport const $ZodCheckMaxSize = /*@__PURE__*/ core.$constructor(\"$ZodCheckMaxSize\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.size !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const curr = (inst._zod.bag.maximum ?? Number.POSITIVE_INFINITY);\n if (def.maximum < curr)\n inst._zod.bag.maximum = def.maximum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const size = input.size;\n if (size <= def.maximum)\n return;\n payload.issues.push({\n origin: util.getSizableOrigin(input),\n code: \"too_big\",\n maximum: def.maximum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckMinSize = /*@__PURE__*/ core.$constructor(\"$ZodCheckMinSize\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.size !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const curr = (inst._zod.bag.minimum ?? Number.NEGATIVE_INFINITY);\n if (def.minimum > curr)\n inst._zod.bag.minimum = def.minimum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const size = input.size;\n if (size >= def.minimum)\n return;\n payload.issues.push({\n origin: util.getSizableOrigin(input),\n code: \"too_small\",\n minimum: def.minimum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckSizeEquals = /*@__PURE__*/ core.$constructor(\"$ZodCheckSizeEquals\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.size !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.minimum = def.size;\n bag.maximum = def.size;\n bag.size = def.size;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const size = input.size;\n if (size === def.size)\n return;\n const tooBig = size > def.size;\n payload.issues.push({\n origin: util.getSizableOrigin(input),\n ...(tooBig ? { code: \"too_big\", maximum: def.size } : { code: \"too_small\", minimum: def.size }),\n inclusive: true,\n exact: true,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckMaxLength = /*@__PURE__*/ core.$constructor(\"$ZodCheckMaxLength\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.length !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const curr = (inst._zod.bag.maximum ?? Number.POSITIVE_INFINITY);\n if (def.maximum < curr)\n inst._zod.bag.maximum = def.maximum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const length = input.length;\n if (length <= def.maximum)\n return;\n const origin = util.getLengthableOrigin(input);\n payload.issues.push({\n origin,\n code: \"too_big\",\n maximum: def.maximum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckMinLength = /*@__PURE__*/ core.$constructor(\"$ZodCheckMinLength\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.length !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const curr = (inst._zod.bag.minimum ?? Number.NEGATIVE_INFINITY);\n if (def.minimum > curr)\n inst._zod.bag.minimum = def.minimum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const length = input.length;\n if (length >= def.minimum)\n return;\n const origin = util.getLengthableOrigin(input);\n payload.issues.push({\n origin,\n code: \"too_small\",\n minimum: def.minimum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckLengthEquals = /*@__PURE__*/ core.$constructor(\"$ZodCheckLengthEquals\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.length !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.minimum = def.length;\n bag.maximum = def.length;\n bag.length = def.length;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const length = input.length;\n if (length === def.length)\n return;\n const origin = util.getLengthableOrigin(input);\n const tooBig = length > def.length;\n payload.issues.push({\n origin,\n ...(tooBig ? { code: \"too_big\", maximum: def.length } : { code: \"too_small\", minimum: def.length }),\n inclusive: true,\n exact: true,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckStringFormat = /*@__PURE__*/ core.$constructor(\"$ZodCheckStringFormat\", (inst, def) => {\n var _a, _b;\n $ZodCheck.init(inst, def);\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.format = def.format;\n if (def.pattern) {\n bag.patterns ?? (bag.patterns = new Set());\n bag.patterns.add(def.pattern);\n }\n });\n if (def.pattern)\n (_a = inst._zod).check ?? (_a.check = (payload) => {\n def.pattern.lastIndex = 0;\n if (def.pattern.test(payload.value))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: def.format,\n input: payload.value,\n ...(def.pattern ? { pattern: def.pattern.toString() } : {}),\n inst,\n continue: !def.abort,\n });\n });\n else\n (_b = inst._zod).check ?? (_b.check = () => { });\n});\nexport const $ZodCheckRegex = /*@__PURE__*/ core.$constructor(\"$ZodCheckRegex\", (inst, def) => {\n $ZodCheckStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n def.pattern.lastIndex = 0;\n if (def.pattern.test(payload.value))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"regex\",\n input: payload.value,\n pattern: def.pattern.toString(),\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckLowerCase = /*@__PURE__*/ core.$constructor(\"$ZodCheckLowerCase\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.lowercase);\n $ZodCheckStringFormat.init(inst, def);\n});\nexport const $ZodCheckUpperCase = /*@__PURE__*/ core.$constructor(\"$ZodCheckUpperCase\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.uppercase);\n $ZodCheckStringFormat.init(inst, def);\n});\nexport const $ZodCheckIncludes = /*@__PURE__*/ core.$constructor(\"$ZodCheckIncludes\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const escapedRegex = util.escapeRegex(def.includes);\n const pattern = new RegExp(typeof def.position === \"number\" ? `^.{${def.position}}${escapedRegex}` : escapedRegex);\n def.pattern = pattern;\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.patterns ?? (bag.patterns = new Set());\n bag.patterns.add(pattern);\n });\n inst._zod.check = (payload) => {\n if (payload.value.includes(def.includes, def.position))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"includes\",\n includes: def.includes,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckStartsWith = /*@__PURE__*/ core.$constructor(\"$ZodCheckStartsWith\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const pattern = new RegExp(`^${util.escapeRegex(def.prefix)}.*`);\n def.pattern ?? (def.pattern = pattern);\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.patterns ?? (bag.patterns = new Set());\n bag.patterns.add(pattern);\n });\n inst._zod.check = (payload) => {\n if (payload.value.startsWith(def.prefix))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"starts_with\",\n prefix: def.prefix,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckEndsWith = /*@__PURE__*/ core.$constructor(\"$ZodCheckEndsWith\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const pattern = new RegExp(`.*${util.escapeRegex(def.suffix)}$`);\n def.pattern ?? (def.pattern = pattern);\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.patterns ?? (bag.patterns = new Set());\n bag.patterns.add(pattern);\n });\n inst._zod.check = (payload) => {\n if (payload.value.endsWith(def.suffix))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"ends_with\",\n suffix: def.suffix,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\n///////////////////////////////////\n///// $ZodCheckProperty /////\n///////////////////////////////////\nfunction handleCheckPropertyResult(result, payload, property) {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(property, result.issues));\n }\n}\nexport const $ZodCheckProperty = /*@__PURE__*/ core.$constructor(\"$ZodCheckProperty\", (inst, def) => {\n $ZodCheck.init(inst, def);\n inst._zod.check = (payload) => {\n const result = def.schema._zod.run({\n value: payload.value[def.property],\n issues: [],\n }, {});\n if (result instanceof Promise) {\n return result.then((result) => handleCheckPropertyResult(result, payload, def.property));\n }\n handleCheckPropertyResult(result, payload, def.property);\n return;\n };\n});\nexport const $ZodCheckMimeType = /*@__PURE__*/ core.$constructor(\"$ZodCheckMimeType\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const mimeSet = new Set(def.mime);\n inst._zod.onattach.push((inst) => {\n inst._zod.bag.mime = def.mime;\n });\n inst._zod.check = (payload) => {\n if (mimeSet.has(payload.value.type))\n return;\n payload.issues.push({\n code: \"invalid_value\",\n values: def.mime,\n input: payload.value.type,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckOverwrite = /*@__PURE__*/ core.$constructor(\"$ZodCheckOverwrite\", (inst, def) => {\n $ZodCheck.init(inst, def);\n inst._zod.check = (payload) => {\n payload.value = def.tx(payload.value);\n };\n});\n","export class Doc {\n constructor(args = []) {\n this.content = [];\n this.indent = 0;\n if (this)\n this.args = args;\n }\n indented(fn) {\n this.indent += 1;\n fn(this);\n this.indent -= 1;\n }\n write(arg) {\n if (typeof arg === \"function\") {\n arg(this, { execution: \"sync\" });\n arg(this, { execution: \"async\" });\n return;\n }\n const content = arg;\n const lines = content.split(\"\\n\").filter((x) => x);\n const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));\n const dedented = lines.map((x) => x.slice(minIndent)).map((x) => \" \".repeat(this.indent * 2) + x);\n for (const line of dedented) {\n this.content.push(line);\n }\n }\n compile() {\n const F = Function;\n const args = this?.args;\n const content = this?.content ?? [``];\n const lines = [...content.map((x) => ` ${x}`)];\n // console.log(lines.join(\"\\n\"));\n return new F(...args, lines.join(\"\\n\"));\n }\n}\n","export const version = {\n major: 4,\n minor: 3,\n patch: 6,\n};\n","import * as checks from \"./checks.js\";\nimport * as core from \"./core.js\";\nimport { Doc } from \"./doc.js\";\nimport { parse, parseAsync, safeParse, safeParseAsync } from \"./parse.js\";\nimport * as regexes from \"./regexes.js\";\nimport * as util from \"./util.js\";\nimport { version } from \"./versions.js\";\nexport const $ZodType = /*@__PURE__*/ core.$constructor(\"$ZodType\", (inst, def) => {\n var _a;\n inst ?? (inst = {});\n inst._zod.def = def; // set _def property\n inst._zod.bag = inst._zod.bag || {}; // initialize _bag object\n inst._zod.version = version;\n const checks = [...(inst._zod.def.checks ?? [])];\n // if inst is itself a checks.$ZodCheck, run it as a check\n if (inst._zod.traits.has(\"$ZodCheck\")) {\n checks.unshift(inst);\n }\n for (const ch of checks) {\n for (const fn of ch._zod.onattach) {\n fn(inst);\n }\n }\n if (checks.length === 0) {\n // deferred initializer\n // inst._zod.parse is not yet defined\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n inst._zod.deferred?.push(() => {\n inst._zod.run = inst._zod.parse;\n });\n }\n else {\n const runChecks = (payload, checks, ctx) => {\n let isAborted = util.aborted(payload);\n let asyncResult;\n for (const ch of checks) {\n if (ch._zod.def.when) {\n const shouldRun = ch._zod.def.when(payload);\n if (!shouldRun)\n continue;\n }\n else if (isAborted) {\n continue;\n }\n const currLen = payload.issues.length;\n const _ = ch._zod.check(payload);\n if (_ instanceof Promise && ctx?.async === false) {\n throw new core.$ZodAsyncError();\n }\n if (asyncResult || _ instanceof Promise) {\n asyncResult = (asyncResult ?? Promise.resolve()).then(async () => {\n await _;\n const nextLen = payload.issues.length;\n if (nextLen === currLen)\n return;\n if (!isAborted)\n isAborted = util.aborted(payload, currLen);\n });\n }\n else {\n const nextLen = payload.issues.length;\n if (nextLen === currLen)\n continue;\n if (!isAborted)\n isAborted = util.aborted(payload, currLen);\n }\n }\n if (asyncResult) {\n return asyncResult.then(() => {\n return payload;\n });\n }\n return payload;\n };\n const handleCanaryResult = (canary, payload, ctx) => {\n // abort if the canary is aborted\n if (util.aborted(canary)) {\n canary.aborted = true;\n return canary;\n }\n // run checks first, then\n const checkResult = runChecks(payload, checks, ctx);\n if (checkResult instanceof Promise) {\n if (ctx.async === false)\n throw new core.$ZodAsyncError();\n return checkResult.then((checkResult) => inst._zod.parse(checkResult, ctx));\n }\n return inst._zod.parse(checkResult, ctx);\n };\n inst._zod.run = (payload, ctx) => {\n if (ctx.skipChecks) {\n return inst._zod.parse(payload, ctx);\n }\n if (ctx.direction === \"backward\") {\n // run canary\n // initial pass (no checks)\n const canary = inst._zod.parse({ value: payload.value, issues: [] }, { ...ctx, skipChecks: true });\n if (canary instanceof Promise) {\n return canary.then((canary) => {\n return handleCanaryResult(canary, payload, ctx);\n });\n }\n return handleCanaryResult(canary, payload, ctx);\n }\n // forward\n const result = inst._zod.parse(payload, ctx);\n if (result instanceof Promise) {\n if (ctx.async === false)\n throw new core.$ZodAsyncError();\n return result.then((result) => runChecks(result, checks, ctx));\n }\n return runChecks(result, checks, ctx);\n };\n }\n // Lazy initialize ~standard to avoid creating objects for every schema\n util.defineLazy(inst, \"~standard\", () => ({\n validate: (value) => {\n try {\n const r = safeParse(inst, value);\n return r.success ? { value: r.data } : { issues: r.error?.issues };\n }\n catch (_) {\n return safeParseAsync(inst, value).then((r) => (r.success ? { value: r.data } : { issues: r.error?.issues }));\n }\n },\n vendor: \"zod\",\n version: 1,\n }));\n});\nexport { clone } from \"./util.js\";\nexport const $ZodString = /*@__PURE__*/ core.$constructor(\"$ZodString\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = [...(inst?._zod.bag?.patterns ?? [])].pop() ?? regexes.string(inst._zod.bag);\n inst._zod.parse = (payload, _) => {\n if (def.coerce)\n try {\n payload.value = String(payload.value);\n }\n catch (_) { }\n if (typeof payload.value === \"string\")\n return payload;\n payload.issues.push({\n expected: \"string\",\n code: \"invalid_type\",\n input: payload.value,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodStringFormat = /*@__PURE__*/ core.$constructor(\"$ZodStringFormat\", (inst, def) => {\n // check initialization must come first\n checks.$ZodCheckStringFormat.init(inst, def);\n $ZodString.init(inst, def);\n});\nexport const $ZodGUID = /*@__PURE__*/ core.$constructor(\"$ZodGUID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.guid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodUUID = /*@__PURE__*/ core.$constructor(\"$ZodUUID\", (inst, def) => {\n if (def.version) {\n const versionMap = {\n v1: 1,\n v2: 2,\n v3: 3,\n v4: 4,\n v5: 5,\n v6: 6,\n v7: 7,\n v8: 8,\n };\n const v = versionMap[def.version];\n if (v === undefined)\n throw new Error(`Invalid UUID version: \"${def.version}\"`);\n def.pattern ?? (def.pattern = regexes.uuid(v));\n }\n else\n def.pattern ?? (def.pattern = regexes.uuid());\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodEmail = /*@__PURE__*/ core.$constructor(\"$ZodEmail\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.email);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodURL = /*@__PURE__*/ core.$constructor(\"$ZodURL\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n try {\n // Trim whitespace from input\n const trimmed = payload.value.trim();\n // @ts-ignore\n const url = new URL(trimmed);\n if (def.hostname) {\n def.hostname.lastIndex = 0;\n if (!def.hostname.test(url.hostname)) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid hostname\",\n pattern: def.hostname.source,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n }\n if (def.protocol) {\n def.protocol.lastIndex = 0;\n if (!def.protocol.test(url.protocol.endsWith(\":\") ? url.protocol.slice(0, -1) : url.protocol)) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid protocol\",\n pattern: def.protocol.source,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n }\n // Set the output value based on normalize flag\n if (def.normalize) {\n // Use normalized URL\n payload.value = url.href;\n }\n else {\n // Preserve the original input (trimmed)\n payload.value = trimmed;\n }\n return;\n }\n catch (_) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\nexport const $ZodEmoji = /*@__PURE__*/ core.$constructor(\"$ZodEmoji\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.emoji());\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodNanoID = /*@__PURE__*/ core.$constructor(\"$ZodNanoID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.nanoid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodCUID = /*@__PURE__*/ core.$constructor(\"$ZodCUID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.cuid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodCUID2 = /*@__PURE__*/ core.$constructor(\"$ZodCUID2\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.cuid2);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodULID = /*@__PURE__*/ core.$constructor(\"$ZodULID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.ulid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodXID = /*@__PURE__*/ core.$constructor(\"$ZodXID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.xid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodKSUID = /*@__PURE__*/ core.$constructor(\"$ZodKSUID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.ksuid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodISODateTime = /*@__PURE__*/ core.$constructor(\"$ZodISODateTime\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.datetime(def));\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodISODate = /*@__PURE__*/ core.$constructor(\"$ZodISODate\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.date);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodISOTime = /*@__PURE__*/ core.$constructor(\"$ZodISOTime\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.time(def));\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodISODuration = /*@__PURE__*/ core.$constructor(\"$ZodISODuration\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.duration);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodIPv4 = /*@__PURE__*/ core.$constructor(\"$ZodIPv4\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.ipv4);\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.format = `ipv4`;\n});\nexport const $ZodIPv6 = /*@__PURE__*/ core.$constructor(\"$ZodIPv6\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.ipv6);\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.format = `ipv6`;\n inst._zod.check = (payload) => {\n try {\n // @ts-ignore\n new URL(`http://[${payload.value}]`);\n // return;\n }\n catch {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"ipv6\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\nexport const $ZodMAC = /*@__PURE__*/ core.$constructor(\"$ZodMAC\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.mac(def.delimiter));\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.format = `mac`;\n});\nexport const $ZodCIDRv4 = /*@__PURE__*/ core.$constructor(\"$ZodCIDRv4\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.cidrv4);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodCIDRv6 = /*@__PURE__*/ core.$constructor(\"$ZodCIDRv6\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.cidrv6); // not used for validation\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n const parts = payload.value.split(\"/\");\n try {\n if (parts.length !== 2)\n throw new Error();\n const [address, prefix] = parts;\n if (!prefix)\n throw new Error();\n const prefixNum = Number(prefix);\n if (`${prefixNum}` !== prefix)\n throw new Error();\n if (prefixNum < 0 || prefixNum > 128)\n throw new Error();\n // @ts-ignore\n new URL(`http://[${address}]`);\n }\n catch {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"cidrv6\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\n////////////////////////////// ZodBase64 //////////////////////////////\nexport function isValidBase64(data) {\n if (data === \"\")\n return true;\n if (data.length % 4 !== 0)\n return false;\n try {\n // @ts-ignore\n atob(data);\n return true;\n }\n catch {\n return false;\n }\n}\nexport const $ZodBase64 = /*@__PURE__*/ core.$constructor(\"$ZodBase64\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.base64);\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.contentEncoding = \"base64\";\n inst._zod.check = (payload) => {\n if (isValidBase64(payload.value))\n return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"base64\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\n////////////////////////////// ZodBase64 //////////////////////////////\nexport function isValidBase64URL(data) {\n if (!regexes.base64url.test(data))\n return false;\n const base64 = data.replace(/[-_]/g, (c) => (c === \"-\" ? \"+\" : \"/\"));\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, \"=\");\n return isValidBase64(padded);\n}\nexport const $ZodBase64URL = /*@__PURE__*/ core.$constructor(\"$ZodBase64URL\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.base64url);\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.contentEncoding = \"base64url\";\n inst._zod.check = (payload) => {\n if (isValidBase64URL(payload.value))\n return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"base64url\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodE164 = /*@__PURE__*/ core.$constructor(\"$ZodE164\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.e164);\n $ZodStringFormat.init(inst, def);\n});\n////////////////////////////// ZodJWT //////////////////////////////\nexport function isValidJWT(token, algorithm = null) {\n try {\n const tokensParts = token.split(\".\");\n if (tokensParts.length !== 3)\n return false;\n const [header] = tokensParts;\n if (!header)\n return false;\n // @ts-ignore\n const parsedHeader = JSON.parse(atob(header));\n if (\"typ\" in parsedHeader && parsedHeader?.typ !== \"JWT\")\n return false;\n if (!parsedHeader.alg)\n return false;\n if (algorithm && (!(\"alg\" in parsedHeader) || parsedHeader.alg !== algorithm))\n return false;\n return true;\n }\n catch {\n return false;\n }\n}\nexport const $ZodJWT = /*@__PURE__*/ core.$constructor(\"$ZodJWT\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (isValidJWT(payload.value, def.alg))\n return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"jwt\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCustomStringFormat = /*@__PURE__*/ core.$constructor(\"$ZodCustomStringFormat\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (def.fn(payload.value))\n return;\n payload.issues.push({\n code: \"invalid_format\",\n format: def.format,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodNumber = /*@__PURE__*/ core.$constructor(\"$ZodNumber\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = inst._zod.bag.pattern ?? regexes.number;\n inst._zod.parse = (payload, _ctx) => {\n if (def.coerce)\n try {\n payload.value = Number(payload.value);\n }\n catch (_) { }\n const input = payload.value;\n if (typeof input === \"number\" && !Number.isNaN(input) && Number.isFinite(input)) {\n return payload;\n }\n const received = typeof input === \"number\"\n ? Number.isNaN(input)\n ? \"NaN\"\n : !Number.isFinite(input)\n ? \"Infinity\"\n : undefined\n : undefined;\n payload.issues.push({\n expected: \"number\",\n code: \"invalid_type\",\n input,\n inst,\n ...(received ? { received } : {}),\n });\n return payload;\n };\n});\nexport const $ZodNumberFormat = /*@__PURE__*/ core.$constructor(\"$ZodNumberFormat\", (inst, def) => {\n checks.$ZodCheckNumberFormat.init(inst, def);\n $ZodNumber.init(inst, def); // no format checks\n});\nexport const $ZodBoolean = /*@__PURE__*/ core.$constructor(\"$ZodBoolean\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = regexes.boolean;\n inst._zod.parse = (payload, _ctx) => {\n if (def.coerce)\n try {\n payload.value = Boolean(payload.value);\n }\n catch (_) { }\n const input = payload.value;\n if (typeof input === \"boolean\")\n return payload;\n payload.issues.push({\n expected: \"boolean\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodBigInt = /*@__PURE__*/ core.$constructor(\"$ZodBigInt\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = regexes.bigint;\n inst._zod.parse = (payload, _ctx) => {\n if (def.coerce)\n try {\n payload.value = BigInt(payload.value);\n }\n catch (_) { }\n if (typeof payload.value === \"bigint\")\n return payload;\n payload.issues.push({\n expected: \"bigint\",\n code: \"invalid_type\",\n input: payload.value,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodBigIntFormat = /*@__PURE__*/ core.$constructor(\"$ZodBigIntFormat\", (inst, def) => {\n checks.$ZodCheckBigIntFormat.init(inst, def);\n $ZodBigInt.init(inst, def); // no format checks\n});\nexport const $ZodSymbol = /*@__PURE__*/ core.$constructor(\"$ZodSymbol\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (typeof input === \"symbol\")\n return payload;\n payload.issues.push({\n expected: \"symbol\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodUndefined = /*@__PURE__*/ core.$constructor(\"$ZodUndefined\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = regexes.undefined;\n inst._zod.values = new Set([undefined]);\n inst._zod.optin = \"optional\";\n inst._zod.optout = \"optional\";\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (typeof input === \"undefined\")\n return payload;\n payload.issues.push({\n expected: \"undefined\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodNull = /*@__PURE__*/ core.$constructor(\"$ZodNull\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = regexes.null;\n inst._zod.values = new Set([null]);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (input === null)\n return payload;\n payload.issues.push({\n expected: \"null\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodAny = /*@__PURE__*/ core.$constructor(\"$ZodAny\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload) => payload;\n});\nexport const $ZodUnknown = /*@__PURE__*/ core.$constructor(\"$ZodUnknown\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload) => payload;\n});\nexport const $ZodNever = /*@__PURE__*/ core.$constructor(\"$ZodNever\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n payload.issues.push({\n expected: \"never\",\n code: \"invalid_type\",\n input: payload.value,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodVoid = /*@__PURE__*/ core.$constructor(\"$ZodVoid\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (typeof input === \"undefined\")\n return payload;\n payload.issues.push({\n expected: \"void\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodDate = /*@__PURE__*/ core.$constructor(\"$ZodDate\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n if (def.coerce) {\n try {\n payload.value = new Date(payload.value);\n }\n catch (_err) { }\n }\n const input = payload.value;\n const isDate = input instanceof Date;\n const isValidDate = isDate && !Number.isNaN(input.getTime());\n if (isValidDate)\n return payload;\n payload.issues.push({\n expected: \"date\",\n code: \"invalid_type\",\n input,\n ...(isDate ? { received: \"Invalid Date\" } : {}),\n inst,\n });\n return payload;\n };\n});\nfunction handleArrayResult(result, final, index) {\n if (result.issues.length) {\n final.issues.push(...util.prefixIssues(index, result.issues));\n }\n final.value[index] = result.value;\n}\nexport const $ZodArray = /*@__PURE__*/ core.$constructor(\"$ZodArray\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!Array.isArray(input)) {\n payload.issues.push({\n expected: \"array\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n payload.value = Array(input.length);\n const proms = [];\n for (let i = 0; i < input.length; i++) {\n const item = input[i];\n const result = def.element._zod.run({\n value: item,\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => handleArrayResult(result, payload, i)));\n }\n else {\n handleArrayResult(result, payload, i);\n }\n }\n if (proms.length) {\n return Promise.all(proms).then(() => payload);\n }\n return payload; //handleArrayResultsAsync(parseResults, final);\n };\n});\nfunction handlePropertyResult(result, final, key, input, isOptionalOut) {\n if (result.issues.length) {\n // For optional-out schemas, ignore errors on absent keys\n if (isOptionalOut && !(key in input)) {\n return;\n }\n final.issues.push(...util.prefixIssues(key, result.issues));\n }\n if (result.value === undefined) {\n if (key in input) {\n final.value[key] = undefined;\n }\n }\n else {\n final.value[key] = result.value;\n }\n}\nfunction normalizeDef(def) {\n const keys = Object.keys(def.shape);\n for (const k of keys) {\n if (!def.shape?.[k]?._zod?.traits?.has(\"$ZodType\")) {\n throw new Error(`Invalid element at key \"${k}\": expected a Zod schema`);\n }\n }\n const okeys = util.optionalKeys(def.shape);\n return {\n ...def,\n keys,\n keySet: new Set(keys),\n numKeys: keys.length,\n optionalKeys: new Set(okeys),\n };\n}\nfunction handleCatchall(proms, input, payload, ctx, def, inst) {\n const unrecognized = [];\n // iterate over input keys\n const keySet = def.keySet;\n const _catchall = def.catchall._zod;\n const t = _catchall.def.type;\n const isOptionalOut = _catchall.optout === \"optional\";\n for (const key in input) {\n if (keySet.has(key))\n continue;\n if (t === \"never\") {\n unrecognized.push(key);\n continue;\n }\n const r = _catchall.run({ value: input[key], issues: [] }, ctx);\n if (r instanceof Promise) {\n proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, isOptionalOut)));\n }\n else {\n handlePropertyResult(r, payload, key, input, isOptionalOut);\n }\n }\n if (unrecognized.length) {\n payload.issues.push({\n code: \"unrecognized_keys\",\n keys: unrecognized,\n input,\n inst,\n });\n }\n if (!proms.length)\n return payload;\n return Promise.all(proms).then(() => {\n return payload;\n });\n}\nexport const $ZodObject = /*@__PURE__*/ core.$constructor(\"$ZodObject\", (inst, def) => {\n // requires cast because technically $ZodObject doesn't extend\n $ZodType.init(inst, def);\n // const sh = def.shape;\n const desc = Object.getOwnPropertyDescriptor(def, \"shape\");\n if (!desc?.get) {\n const sh = def.shape;\n Object.defineProperty(def, \"shape\", {\n get: () => {\n const newSh = { ...sh };\n Object.defineProperty(def, \"shape\", {\n value: newSh,\n });\n return newSh;\n },\n });\n }\n const _normalized = util.cached(() => normalizeDef(def));\n util.defineLazy(inst._zod, \"propValues\", () => {\n const shape = def.shape;\n const propValues = {};\n for (const key in shape) {\n const field = shape[key]._zod;\n if (field.values) {\n propValues[key] ?? (propValues[key] = new Set());\n for (const v of field.values)\n propValues[key].add(v);\n }\n }\n return propValues;\n });\n const isObject = util.isObject;\n const catchall = def.catchall;\n let value;\n inst._zod.parse = (payload, ctx) => {\n value ?? (value = _normalized.value);\n const input = payload.value;\n if (!isObject(input)) {\n payload.issues.push({\n expected: \"object\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n payload.value = {};\n const proms = [];\n const shape = value.shape;\n for (const key of value.keys) {\n const el = shape[key];\n const isOptionalOut = el._zod.optout === \"optional\";\n const r = el._zod.run({ value: input[key], issues: [] }, ctx);\n if (r instanceof Promise) {\n proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, isOptionalOut)));\n }\n else {\n handlePropertyResult(r, payload, key, input, isOptionalOut);\n }\n }\n if (!catchall) {\n return proms.length ? Promise.all(proms).then(() => payload) : payload;\n }\n return handleCatchall(proms, input, payload, ctx, _normalized.value, inst);\n };\n});\nexport const $ZodObjectJIT = /*@__PURE__*/ core.$constructor(\"$ZodObjectJIT\", (inst, def) => {\n // requires cast because technically $ZodObject doesn't extend\n $ZodObject.init(inst, def);\n const superParse = inst._zod.parse;\n const _normalized = util.cached(() => normalizeDef(def));\n const generateFastpass = (shape) => {\n const doc = new Doc([\"shape\", \"payload\", \"ctx\"]);\n const normalized = _normalized.value;\n const parseStr = (key) => {\n const k = util.esc(key);\n return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;\n };\n doc.write(`const input = payload.value;`);\n const ids = Object.create(null);\n let counter = 0;\n for (const key of normalized.keys) {\n ids[key] = `key_${counter++}`;\n }\n // A: preserve key order {\n doc.write(`const newResult = {};`);\n for (const key of normalized.keys) {\n const id = ids[key];\n const k = util.esc(key);\n const schema = shape[key];\n const isOptionalOut = schema?._zod?.optout === \"optional\";\n doc.write(`const ${id} = ${parseStr(key)};`);\n if (isOptionalOut) {\n // For optional-out schemas, ignore errors on absent keys\n doc.write(`\n if (${id}.issues.length) {\n if (${k} in input) {\n payload.issues = payload.issues.concat(${id}.issues.map(iss => ({\n ...iss,\n path: iss.path ? [${k}, ...iss.path] : [${k}]\n })));\n }\n }\n \n if (${id}.value === undefined) {\n if (${k} in input) {\n newResult[${k}] = undefined;\n }\n } else {\n newResult[${k}] = ${id}.value;\n }\n \n `);\n }\n else {\n doc.write(`\n if (${id}.issues.length) {\n payload.issues = payload.issues.concat(${id}.issues.map(iss => ({\n ...iss,\n path: iss.path ? [${k}, ...iss.path] : [${k}]\n })));\n }\n \n if (${id}.value === undefined) {\n if (${k} in input) {\n newResult[${k}] = undefined;\n }\n } else {\n newResult[${k}] = ${id}.value;\n }\n \n `);\n }\n }\n doc.write(`payload.value = newResult;`);\n doc.write(`return payload;`);\n const fn = doc.compile();\n return (payload, ctx) => fn(shape, payload, ctx);\n };\n let fastpass;\n const isObject = util.isObject;\n const jit = !core.globalConfig.jitless;\n const allowsEval = util.allowsEval;\n const fastEnabled = jit && allowsEval.value; // && !def.catchall;\n const catchall = def.catchall;\n let value;\n inst._zod.parse = (payload, ctx) => {\n value ?? (value = _normalized.value);\n const input = payload.value;\n if (!isObject(input)) {\n payload.issues.push({\n expected: \"object\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n if (jit && fastEnabled && ctx?.async === false && ctx.jitless !== true) {\n // always synchronous\n if (!fastpass)\n fastpass = generateFastpass(def.shape);\n payload = fastpass(payload, ctx);\n if (!catchall)\n return payload;\n return handleCatchall([], input, payload, ctx, value, inst);\n }\n return superParse(payload, ctx);\n };\n});\nfunction handleUnionResults(results, final, inst, ctx) {\n for (const result of results) {\n if (result.issues.length === 0) {\n final.value = result.value;\n return final;\n }\n }\n const nonaborted = results.filter((r) => !util.aborted(r));\n if (nonaborted.length === 1) {\n final.value = nonaborted[0].value;\n return nonaborted[0];\n }\n final.issues.push({\n code: \"invalid_union\",\n input: final.value,\n inst,\n errors: results.map((result) => result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),\n });\n return final;\n}\nexport const $ZodUnion = /*@__PURE__*/ core.$constructor(\"$ZodUnion\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"optin\", () => def.options.some((o) => o._zod.optin === \"optional\") ? \"optional\" : undefined);\n util.defineLazy(inst._zod, \"optout\", () => def.options.some((o) => o._zod.optout === \"optional\") ? \"optional\" : undefined);\n util.defineLazy(inst._zod, \"values\", () => {\n if (def.options.every((o) => o._zod.values)) {\n return new Set(def.options.flatMap((option) => Array.from(option._zod.values)));\n }\n return undefined;\n });\n util.defineLazy(inst._zod, \"pattern\", () => {\n if (def.options.every((o) => o._zod.pattern)) {\n const patterns = def.options.map((o) => o._zod.pattern);\n return new RegExp(`^(${patterns.map((p) => util.cleanRegex(p.source)).join(\"|\")})$`);\n }\n return undefined;\n });\n const single = def.options.length === 1;\n const first = def.options[0]._zod.run;\n inst._zod.parse = (payload, ctx) => {\n if (single) {\n return first(payload, ctx);\n }\n let async = false;\n const results = [];\n for (const option of def.options) {\n const result = option._zod.run({\n value: payload.value,\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n results.push(result);\n async = true;\n }\n else {\n if (result.issues.length === 0)\n return result;\n results.push(result);\n }\n }\n if (!async)\n return handleUnionResults(results, payload, inst, ctx);\n return Promise.all(results).then((results) => {\n return handleUnionResults(results, payload, inst, ctx);\n });\n };\n});\nfunction handleExclusiveUnionResults(results, final, inst, ctx) {\n const successes = results.filter((r) => r.issues.length === 0);\n if (successes.length === 1) {\n final.value = successes[0].value;\n return final;\n }\n if (successes.length === 0) {\n // No matches - same as regular union\n final.issues.push({\n code: \"invalid_union\",\n input: final.value,\n inst,\n errors: results.map((result) => result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),\n });\n }\n else {\n // Multiple matches - exclusive union failure\n final.issues.push({\n code: \"invalid_union\",\n input: final.value,\n inst,\n errors: [],\n inclusive: false,\n });\n }\n return final;\n}\nexport const $ZodXor = /*@__PURE__*/ core.$constructor(\"$ZodXor\", (inst, def) => {\n $ZodUnion.init(inst, def);\n def.inclusive = false;\n const single = def.options.length === 1;\n const first = def.options[0]._zod.run;\n inst._zod.parse = (payload, ctx) => {\n if (single) {\n return first(payload, ctx);\n }\n let async = false;\n const results = [];\n for (const option of def.options) {\n const result = option._zod.run({\n value: payload.value,\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n results.push(result);\n async = true;\n }\n else {\n results.push(result);\n }\n }\n if (!async)\n return handleExclusiveUnionResults(results, payload, inst, ctx);\n return Promise.all(results).then((results) => {\n return handleExclusiveUnionResults(results, payload, inst, ctx);\n });\n };\n});\nexport const $ZodDiscriminatedUnion = \n/*@__PURE__*/\ncore.$constructor(\"$ZodDiscriminatedUnion\", (inst, def) => {\n def.inclusive = false;\n $ZodUnion.init(inst, def);\n const _super = inst._zod.parse;\n util.defineLazy(inst._zod, \"propValues\", () => {\n const propValues = {};\n for (const option of def.options) {\n const pv = option._zod.propValues;\n if (!pv || Object.keys(pv).length === 0)\n throw new Error(`Invalid discriminated union option at index \"${def.options.indexOf(option)}\"`);\n for (const [k, v] of Object.entries(pv)) {\n if (!propValues[k])\n propValues[k] = new Set();\n for (const val of v) {\n propValues[k].add(val);\n }\n }\n }\n return propValues;\n });\n const disc = util.cached(() => {\n const opts = def.options;\n const map = new Map();\n for (const o of opts) {\n const values = o._zod.propValues?.[def.discriminator];\n if (!values || values.size === 0)\n throw new Error(`Invalid discriminated union option at index \"${def.options.indexOf(o)}\"`);\n for (const v of values) {\n if (map.has(v)) {\n throw new Error(`Duplicate discriminator value \"${String(v)}\"`);\n }\n map.set(v, o);\n }\n }\n return map;\n });\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!util.isObject(input)) {\n payload.issues.push({\n code: \"invalid_type\",\n expected: \"object\",\n input,\n inst,\n });\n return payload;\n }\n const opt = disc.value.get(input?.[def.discriminator]);\n if (opt) {\n return opt._zod.run(payload, ctx);\n }\n if (def.unionFallback) {\n return _super(payload, ctx);\n }\n // no matching discriminator\n payload.issues.push({\n code: \"invalid_union\",\n errors: [],\n note: \"No matching discriminator\",\n discriminator: def.discriminator,\n input,\n path: [def.discriminator],\n inst,\n });\n return payload;\n };\n});\nexport const $ZodIntersection = /*@__PURE__*/ core.$constructor(\"$ZodIntersection\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n const left = def.left._zod.run({ value: input, issues: [] }, ctx);\n const right = def.right._zod.run({ value: input, issues: [] }, ctx);\n const async = left instanceof Promise || right instanceof Promise;\n if (async) {\n return Promise.all([left, right]).then(([left, right]) => {\n return handleIntersectionResults(payload, left, right);\n });\n }\n return handleIntersectionResults(payload, left, right);\n };\n});\nfunction mergeValues(a, b) {\n // const aType = parse.t(a);\n // const bType = parse.t(b);\n if (a === b) {\n return { valid: true, data: a };\n }\n if (a instanceof Date && b instanceof Date && +a === +b) {\n return { valid: true, data: a };\n }\n if (util.isPlainObject(a) && util.isPlainObject(b)) {\n const bKeys = Object.keys(b);\n const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);\n const newObj = { ...a, ...b };\n for (const key of sharedKeys) {\n const sharedValue = mergeValues(a[key], b[key]);\n if (!sharedValue.valid) {\n return {\n valid: false,\n mergeErrorPath: [key, ...sharedValue.mergeErrorPath],\n };\n }\n newObj[key] = sharedValue.data;\n }\n return { valid: true, data: newObj };\n }\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) {\n return { valid: false, mergeErrorPath: [] };\n }\n const newArray = [];\n for (let index = 0; index < a.length; index++) {\n const itemA = a[index];\n const itemB = b[index];\n const sharedValue = mergeValues(itemA, itemB);\n if (!sharedValue.valid) {\n return {\n valid: false,\n mergeErrorPath: [index, ...sharedValue.mergeErrorPath],\n };\n }\n newArray.push(sharedValue.data);\n }\n return { valid: true, data: newArray };\n }\n return { valid: false, mergeErrorPath: [] };\n}\nfunction handleIntersectionResults(result, left, right) {\n // Track which side(s) report each key as unrecognized\n const unrecKeys = new Map();\n let unrecIssue;\n for (const iss of left.issues) {\n if (iss.code === \"unrecognized_keys\") {\n unrecIssue ?? (unrecIssue = iss);\n for (const k of iss.keys) {\n if (!unrecKeys.has(k))\n unrecKeys.set(k, {});\n unrecKeys.get(k).l = true;\n }\n }\n else {\n result.issues.push(iss);\n }\n }\n for (const iss of right.issues) {\n if (iss.code === \"unrecognized_keys\") {\n for (const k of iss.keys) {\n if (!unrecKeys.has(k))\n unrecKeys.set(k, {});\n unrecKeys.get(k).r = true;\n }\n }\n else {\n result.issues.push(iss);\n }\n }\n // Report only keys unrecognized by BOTH sides\n const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k]) => k);\n if (bothKeys.length && unrecIssue) {\n result.issues.push({ ...unrecIssue, keys: bothKeys });\n }\n if (util.aborted(result))\n return result;\n const merged = mergeValues(left.value, right.value);\n if (!merged.valid) {\n throw new Error(`Unmergable intersection. Error path: ` + `${JSON.stringify(merged.mergeErrorPath)}`);\n }\n result.value = merged.data;\n return result;\n}\nexport const $ZodTuple = /*@__PURE__*/ core.$constructor(\"$ZodTuple\", (inst, def) => {\n $ZodType.init(inst, def);\n const items = def.items;\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!Array.isArray(input)) {\n payload.issues.push({\n input,\n inst,\n expected: \"tuple\",\n code: \"invalid_type\",\n });\n return payload;\n }\n payload.value = [];\n const proms = [];\n const reversedIndex = [...items].reverse().findIndex((item) => item._zod.optin !== \"optional\");\n const optStart = reversedIndex === -1 ? 0 : items.length - reversedIndex;\n if (!def.rest) {\n const tooBig = input.length > items.length;\n const tooSmall = input.length < optStart - 1;\n if (tooBig || tooSmall) {\n payload.issues.push({\n ...(tooBig\n ? { code: \"too_big\", maximum: items.length, inclusive: true }\n : { code: \"too_small\", minimum: items.length }),\n input,\n inst,\n origin: \"array\",\n });\n return payload;\n }\n }\n let i = -1;\n for (const item of items) {\n i++;\n if (i >= input.length)\n if (i >= optStart)\n continue;\n const result = item._zod.run({\n value: input[i],\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => handleTupleResult(result, payload, i)));\n }\n else {\n handleTupleResult(result, payload, i);\n }\n }\n if (def.rest) {\n const rest = input.slice(items.length);\n for (const el of rest) {\n i++;\n const result = def.rest._zod.run({\n value: el,\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => handleTupleResult(result, payload, i)));\n }\n else {\n handleTupleResult(result, payload, i);\n }\n }\n }\n if (proms.length)\n return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nfunction handleTupleResult(result, final, index) {\n if (result.issues.length) {\n final.issues.push(...util.prefixIssues(index, result.issues));\n }\n final.value[index] = result.value;\n}\nexport const $ZodRecord = /*@__PURE__*/ core.$constructor(\"$ZodRecord\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!util.isPlainObject(input)) {\n payload.issues.push({\n expected: \"record\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n const proms = [];\n const values = def.keyType._zod.values;\n if (values) {\n payload.value = {};\n const recordKeys = new Set();\n for (const key of values) {\n if (typeof key === \"string\" || typeof key === \"number\" || typeof key === \"symbol\") {\n recordKeys.add(typeof key === \"number\" ? key.toString() : key);\n const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(key, result.issues));\n }\n payload.value[key] = result.value;\n }));\n }\n else {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(key, result.issues));\n }\n payload.value[key] = result.value;\n }\n }\n }\n let unrecognized;\n for (const key in input) {\n if (!recordKeys.has(key)) {\n unrecognized = unrecognized ?? [];\n unrecognized.push(key);\n }\n }\n if (unrecognized && unrecognized.length > 0) {\n payload.issues.push({\n code: \"unrecognized_keys\",\n input,\n inst,\n keys: unrecognized,\n });\n }\n }\n else {\n payload.value = {};\n for (const key of Reflect.ownKeys(input)) {\n if (key === \"__proto__\")\n continue;\n let keyResult = def.keyType._zod.run({ value: key, issues: [] }, ctx);\n if (keyResult instanceof Promise) {\n throw new Error(\"Async schemas not supported in object keys currently\");\n }\n // Numeric string fallback: if key is a numeric string and failed, retry with Number(key)\n // This handles z.number(), z.literal([1, 2, 3]), and unions containing numeric literals\n const checkNumericKey = typeof key === \"string\" && regexes.number.test(key) && keyResult.issues.length;\n if (checkNumericKey) {\n const retryResult = def.keyType._zod.run({ value: Number(key), issues: [] }, ctx);\n if (retryResult instanceof Promise) {\n throw new Error(\"Async schemas not supported in object keys currently\");\n }\n if (retryResult.issues.length === 0) {\n keyResult = retryResult;\n }\n }\n if (keyResult.issues.length) {\n if (def.mode === \"loose\") {\n // Pass through unchanged\n payload.value[key] = input[key];\n }\n else {\n // Default \"strict\" behavior: error on invalid key\n payload.issues.push({\n code: \"invalid_key\",\n origin: \"record\",\n issues: keyResult.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n input: key,\n path: [key],\n inst,\n });\n }\n continue;\n }\n const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(key, result.issues));\n }\n payload.value[keyResult.value] = result.value;\n }));\n }\n else {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(key, result.issues));\n }\n payload.value[keyResult.value] = result.value;\n }\n }\n }\n if (proms.length) {\n return Promise.all(proms).then(() => payload);\n }\n return payload;\n };\n});\nexport const $ZodMap = /*@__PURE__*/ core.$constructor(\"$ZodMap\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!(input instanceof Map)) {\n payload.issues.push({\n expected: \"map\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n const proms = [];\n payload.value = new Map();\n for (const [key, value] of input) {\n const keyResult = def.keyType._zod.run({ value: key, issues: [] }, ctx);\n const valueResult = def.valueType._zod.run({ value: value, issues: [] }, ctx);\n if (keyResult instanceof Promise || valueResult instanceof Promise) {\n proms.push(Promise.all([keyResult, valueResult]).then(([keyResult, valueResult]) => {\n handleMapResult(keyResult, valueResult, payload, key, input, inst, ctx);\n }));\n }\n else {\n handleMapResult(keyResult, valueResult, payload, key, input, inst, ctx);\n }\n }\n if (proms.length)\n return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nfunction handleMapResult(keyResult, valueResult, final, key, input, inst, ctx) {\n if (keyResult.issues.length) {\n if (util.propertyKeyTypes.has(typeof key)) {\n final.issues.push(...util.prefixIssues(key, keyResult.issues));\n }\n else {\n final.issues.push({\n code: \"invalid_key\",\n origin: \"map\",\n input,\n inst,\n issues: keyResult.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n });\n }\n }\n if (valueResult.issues.length) {\n if (util.propertyKeyTypes.has(typeof key)) {\n final.issues.push(...util.prefixIssues(key, valueResult.issues));\n }\n else {\n final.issues.push({\n origin: \"map\",\n code: \"invalid_element\",\n input,\n inst,\n key: key,\n issues: valueResult.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n });\n }\n }\n final.value.set(keyResult.value, valueResult.value);\n}\nexport const $ZodSet = /*@__PURE__*/ core.$constructor(\"$ZodSet\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!(input instanceof Set)) {\n payload.issues.push({\n input,\n inst,\n expected: \"set\",\n code: \"invalid_type\",\n });\n return payload;\n }\n const proms = [];\n payload.value = new Set();\n for (const item of input) {\n const result = def.valueType._zod.run({ value: item, issues: [] }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => handleSetResult(result, payload)));\n }\n else\n handleSetResult(result, payload);\n }\n if (proms.length)\n return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nfunction handleSetResult(result, final) {\n if (result.issues.length) {\n final.issues.push(...result.issues);\n }\n final.value.add(result.value);\n}\nexport const $ZodEnum = /*@__PURE__*/ core.$constructor(\"$ZodEnum\", (inst, def) => {\n $ZodType.init(inst, def);\n const values = util.getEnumValues(def.entries);\n const valuesSet = new Set(values);\n inst._zod.values = valuesSet;\n inst._zod.pattern = new RegExp(`^(${values\n .filter((k) => util.propertyKeyTypes.has(typeof k))\n .map((o) => (typeof o === \"string\" ? util.escapeRegex(o) : o.toString()))\n .join(\"|\")})$`);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (valuesSet.has(input)) {\n return payload;\n }\n payload.issues.push({\n code: \"invalid_value\",\n values,\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodLiteral = /*@__PURE__*/ core.$constructor(\"$ZodLiteral\", (inst, def) => {\n $ZodType.init(inst, def);\n if (def.values.length === 0) {\n throw new Error(\"Cannot create literal schema with no valid values\");\n }\n const values = new Set(def.values);\n inst._zod.values = values;\n inst._zod.pattern = new RegExp(`^(${def.values\n .map((o) => (typeof o === \"string\" ? util.escapeRegex(o) : o ? util.escapeRegex(o.toString()) : String(o)))\n .join(\"|\")})$`);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (values.has(input)) {\n return payload;\n }\n payload.issues.push({\n code: \"invalid_value\",\n values: def.values,\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodFile = /*@__PURE__*/ core.$constructor(\"$ZodFile\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n // @ts-ignore\n if (input instanceof File)\n return payload;\n payload.issues.push({\n expected: \"file\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodTransform = /*@__PURE__*/ core.$constructor(\"$ZodTransform\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n throw new core.$ZodEncodeError(inst.constructor.name);\n }\n const _out = def.transform(payload.value, payload);\n if (ctx.async) {\n const output = _out instanceof Promise ? _out : Promise.resolve(_out);\n return output.then((output) => {\n payload.value = output;\n return payload;\n });\n }\n if (_out instanceof Promise) {\n throw new core.$ZodAsyncError();\n }\n payload.value = _out;\n return payload;\n };\n});\nfunction handleOptionalResult(result, input) {\n if (result.issues.length && input === undefined) {\n return { issues: [], value: undefined };\n }\n return result;\n}\nexport const $ZodOptional = /*@__PURE__*/ core.$constructor(\"$ZodOptional\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"optional\";\n inst._zod.optout = \"optional\";\n util.defineLazy(inst._zod, \"values\", () => {\n return def.innerType._zod.values ? new Set([...def.innerType._zod.values, undefined]) : undefined;\n });\n util.defineLazy(inst._zod, \"pattern\", () => {\n const pattern = def.innerType._zod.pattern;\n return pattern ? new RegExp(`^(${util.cleanRegex(pattern.source)})?$`) : undefined;\n });\n inst._zod.parse = (payload, ctx) => {\n if (def.innerType._zod.optin === \"optional\") {\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise)\n return result.then((r) => handleOptionalResult(r, payload.value));\n return handleOptionalResult(result, payload.value);\n }\n if (payload.value === undefined) {\n return payload;\n }\n return def.innerType._zod.run(payload, ctx);\n };\n});\nexport const $ZodExactOptional = /*@__PURE__*/ core.$constructor(\"$ZodExactOptional\", (inst, def) => {\n // Call parent init - inherits optin/optout = \"optional\"\n $ZodOptional.init(inst, def);\n // Override values/pattern to NOT add undefined\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n util.defineLazy(inst._zod, \"pattern\", () => def.innerType._zod.pattern);\n // Override parse to just delegate (no undefined handling)\n inst._zod.parse = (payload, ctx) => {\n return def.innerType._zod.run(payload, ctx);\n };\n});\nexport const $ZodNullable = /*@__PURE__*/ core.$constructor(\"$ZodNullable\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"optin\", () => def.innerType._zod.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.innerType._zod.optout);\n util.defineLazy(inst._zod, \"pattern\", () => {\n const pattern = def.innerType._zod.pattern;\n return pattern ? new RegExp(`^(${util.cleanRegex(pattern.source)}|null)$`) : undefined;\n });\n util.defineLazy(inst._zod, \"values\", () => {\n return def.innerType._zod.values ? new Set([...def.innerType._zod.values, null]) : undefined;\n });\n inst._zod.parse = (payload, ctx) => {\n // Forward direction (decode): allow null to pass through\n if (payload.value === null)\n return payload;\n return def.innerType._zod.run(payload, ctx);\n };\n});\nexport const $ZodDefault = /*@__PURE__*/ core.$constructor(\"$ZodDefault\", (inst, def) => {\n $ZodType.init(inst, def);\n // inst._zod.qin = \"true\";\n inst._zod.optin = \"optional\";\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n return def.innerType._zod.run(payload, ctx);\n }\n // Forward direction (decode): apply defaults for undefined input\n if (payload.value === undefined) {\n payload.value = def.defaultValue;\n /**\n * $ZodDefault returns the default value immediately in forward direction.\n * It doesn't pass the default value into the validator (\"prefault\"). There's no reason to pass the default value through validation. The validity of the default is enforced by TypeScript statically. Otherwise, it's the responsibility of the user to ensure the default is valid. In the case of pipes with divergent in/out types, you can specify the default on the `in` schema of your ZodPipe to set a \"prefault\" for the pipe. */\n return payload;\n }\n // Forward direction: continue with default handling\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then((result) => handleDefaultResult(result, def));\n }\n return handleDefaultResult(result, def);\n };\n});\nfunction handleDefaultResult(payload, def) {\n if (payload.value === undefined) {\n payload.value = def.defaultValue;\n }\n return payload;\n}\nexport const $ZodPrefault = /*@__PURE__*/ core.$constructor(\"$ZodPrefault\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"optional\";\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n return def.innerType._zod.run(payload, ctx);\n }\n // Forward direction (decode): apply prefault for undefined input\n if (payload.value === undefined) {\n payload.value = def.defaultValue;\n }\n return def.innerType._zod.run(payload, ctx);\n };\n});\nexport const $ZodNonOptional = /*@__PURE__*/ core.$constructor(\"$ZodNonOptional\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"values\", () => {\n const v = def.innerType._zod.values;\n return v ? new Set([...v].filter((x) => x !== undefined)) : undefined;\n });\n inst._zod.parse = (payload, ctx) => {\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then((result) => handleNonOptionalResult(result, inst));\n }\n return handleNonOptionalResult(result, inst);\n };\n});\nfunction handleNonOptionalResult(payload, inst) {\n if (!payload.issues.length && payload.value === undefined) {\n payload.issues.push({\n code: \"invalid_type\",\n expected: \"nonoptional\",\n input: payload.value,\n inst,\n });\n }\n return payload;\n}\nexport const $ZodSuccess = /*@__PURE__*/ core.$constructor(\"$ZodSuccess\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n throw new core.$ZodEncodeError(\"ZodSuccess\");\n }\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then((result) => {\n payload.value = result.issues.length === 0;\n return payload;\n });\n }\n payload.value = result.issues.length === 0;\n return payload;\n };\n});\nexport const $ZodCatch = /*@__PURE__*/ core.$constructor(\"$ZodCatch\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"optin\", () => def.innerType._zod.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.innerType._zod.optout);\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n return def.innerType._zod.run(payload, ctx);\n }\n // Forward direction (decode): apply catch logic\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then((result) => {\n payload.value = result.value;\n if (result.issues.length) {\n payload.value = def.catchValue({\n ...payload,\n error: {\n issues: result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n },\n input: payload.value,\n });\n payload.issues = [];\n }\n return payload;\n });\n }\n payload.value = result.value;\n if (result.issues.length) {\n payload.value = def.catchValue({\n ...payload,\n error: {\n issues: result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n },\n input: payload.value,\n });\n payload.issues = [];\n }\n return payload;\n };\n});\nexport const $ZodNaN = /*@__PURE__*/ core.$constructor(\"$ZodNaN\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n if (typeof payload.value !== \"number\" || !Number.isNaN(payload.value)) {\n payload.issues.push({\n input: payload.value,\n inst,\n expected: \"nan\",\n code: \"invalid_type\",\n });\n return payload;\n }\n return payload;\n };\n});\nexport const $ZodPipe = /*@__PURE__*/ core.$constructor(\"$ZodPipe\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"values\", () => def.in._zod.values);\n util.defineLazy(inst._zod, \"optin\", () => def.in._zod.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.out._zod.optout);\n util.defineLazy(inst._zod, \"propValues\", () => def.in._zod.propValues);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n const right = def.out._zod.run(payload, ctx);\n if (right instanceof Promise) {\n return right.then((right) => handlePipeResult(right, def.in, ctx));\n }\n return handlePipeResult(right, def.in, ctx);\n }\n const left = def.in._zod.run(payload, ctx);\n if (left instanceof Promise) {\n return left.then((left) => handlePipeResult(left, def.out, ctx));\n }\n return handlePipeResult(left, def.out, ctx);\n };\n});\nfunction handlePipeResult(left, next, ctx) {\n if (left.issues.length) {\n // prevent further checks\n left.aborted = true;\n return left;\n }\n return next._zod.run({ value: left.value, issues: left.issues }, ctx);\n}\nexport const $ZodCodec = /*@__PURE__*/ core.$constructor(\"$ZodCodec\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"values\", () => def.in._zod.values);\n util.defineLazy(inst._zod, \"optin\", () => def.in._zod.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.out._zod.optout);\n util.defineLazy(inst._zod, \"propValues\", () => def.in._zod.propValues);\n inst._zod.parse = (payload, ctx) => {\n const direction = ctx.direction || \"forward\";\n if (direction === \"forward\") {\n const left = def.in._zod.run(payload, ctx);\n if (left instanceof Promise) {\n return left.then((left) => handleCodecAResult(left, def, ctx));\n }\n return handleCodecAResult(left, def, ctx);\n }\n else {\n const right = def.out._zod.run(payload, ctx);\n if (right instanceof Promise) {\n return right.then((right) => handleCodecAResult(right, def, ctx));\n }\n return handleCodecAResult(right, def, ctx);\n }\n };\n});\nfunction handleCodecAResult(result, def, ctx) {\n if (result.issues.length) {\n // prevent further checks\n result.aborted = true;\n return result;\n }\n const direction = ctx.direction || \"forward\";\n if (direction === \"forward\") {\n const transformed = def.transform(result.value, result);\n if (transformed instanceof Promise) {\n return transformed.then((value) => handleCodecTxResult(result, value, def.out, ctx));\n }\n return handleCodecTxResult(result, transformed, def.out, ctx);\n }\n else {\n const transformed = def.reverseTransform(result.value, result);\n if (transformed instanceof Promise) {\n return transformed.then((value) => handleCodecTxResult(result, value, def.in, ctx));\n }\n return handleCodecTxResult(result, transformed, def.in, ctx);\n }\n}\nfunction handleCodecTxResult(left, value, nextSchema, ctx) {\n // Check if transform added any issues\n if (left.issues.length) {\n left.aborted = true;\n return left;\n }\n return nextSchema._zod.run({ value, issues: left.issues }, ctx);\n}\nexport const $ZodReadonly = /*@__PURE__*/ core.$constructor(\"$ZodReadonly\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"propValues\", () => def.innerType._zod.propValues);\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n util.defineLazy(inst._zod, \"optin\", () => def.innerType?._zod?.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.innerType?._zod?.optout);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n return def.innerType._zod.run(payload, ctx);\n }\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then(handleReadonlyResult);\n }\n return handleReadonlyResult(result);\n };\n});\nfunction handleReadonlyResult(payload) {\n payload.value = Object.freeze(payload.value);\n return payload;\n}\nexport const $ZodTemplateLiteral = /*@__PURE__*/ core.$constructor(\"$ZodTemplateLiteral\", (inst, def) => {\n $ZodType.init(inst, def);\n const regexParts = [];\n for (const part of def.parts) {\n if (typeof part === \"object\" && part !== null) {\n // is Zod schema\n if (!part._zod.pattern) {\n // if (!source)\n throw new Error(`Invalid template literal part, no pattern found: ${[...part._zod.traits].shift()}`);\n }\n const source = part._zod.pattern instanceof RegExp ? part._zod.pattern.source : part._zod.pattern;\n if (!source)\n throw new Error(`Invalid template literal part: ${part._zod.traits}`);\n const start = source.startsWith(\"^\") ? 1 : 0;\n const end = source.endsWith(\"$\") ? source.length - 1 : source.length;\n regexParts.push(source.slice(start, end));\n }\n else if (part === null || util.primitiveTypes.has(typeof part)) {\n regexParts.push(util.escapeRegex(`${part}`));\n }\n else {\n throw new Error(`Invalid template literal part: ${part}`);\n }\n }\n inst._zod.pattern = new RegExp(`^${regexParts.join(\"\")}$`);\n inst._zod.parse = (payload, _ctx) => {\n if (typeof payload.value !== \"string\") {\n payload.issues.push({\n input: payload.value,\n inst,\n expected: \"string\",\n code: \"invalid_type\",\n });\n return payload;\n }\n inst._zod.pattern.lastIndex = 0;\n if (!inst._zod.pattern.test(payload.value)) {\n payload.issues.push({\n input: payload.value,\n inst,\n code: \"invalid_format\",\n format: def.format ?? \"template_literal\",\n pattern: inst._zod.pattern.source,\n });\n return payload;\n }\n return payload;\n };\n});\nexport const $ZodFunction = /*@__PURE__*/ core.$constructor(\"$ZodFunction\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._def = def;\n inst._zod.def = def;\n inst.implement = (func) => {\n if (typeof func !== \"function\") {\n throw new Error(\"implement() must be called with a function\");\n }\n return function (...args) {\n const parsedArgs = inst._def.input ? parse(inst._def.input, args) : args;\n const result = Reflect.apply(func, this, parsedArgs);\n if (inst._def.output) {\n return parse(inst._def.output, result);\n }\n return result;\n };\n };\n inst.implementAsync = (func) => {\n if (typeof func !== \"function\") {\n throw new Error(\"implementAsync() must be called with a function\");\n }\n return async function (...args) {\n const parsedArgs = inst._def.input ? await parseAsync(inst._def.input, args) : args;\n const result = await Reflect.apply(func, this, parsedArgs);\n if (inst._def.output) {\n return await parseAsync(inst._def.output, result);\n }\n return result;\n };\n };\n inst._zod.parse = (payload, _ctx) => {\n if (typeof payload.value !== \"function\") {\n payload.issues.push({\n code: \"invalid_type\",\n expected: \"function\",\n input: payload.value,\n inst,\n });\n return payload;\n }\n // Check if output is a promise type to determine if we should use async implementation\n const hasPromiseOutput = inst._def.output && inst._def.output._zod.def.type === \"promise\";\n if (hasPromiseOutput) {\n payload.value = inst.implementAsync(payload.value);\n }\n else {\n payload.value = inst.implement(payload.value);\n }\n return payload;\n };\n inst.input = (...args) => {\n const F = inst.constructor;\n if (Array.isArray(args[0])) {\n return new F({\n type: \"function\",\n input: new $ZodTuple({\n type: \"tuple\",\n items: args[0],\n rest: args[1],\n }),\n output: inst._def.output,\n });\n }\n return new F({\n type: \"function\",\n input: args[0],\n output: inst._def.output,\n });\n };\n inst.output = (output) => {\n const F = inst.constructor;\n return new F({\n type: \"function\",\n input: inst._def.input,\n output,\n });\n };\n return inst;\n});\nexport const $ZodPromise = /*@__PURE__*/ core.$constructor(\"$ZodPromise\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n return Promise.resolve(payload.value).then((inner) => def.innerType._zod.run({ value: inner, issues: [] }, ctx));\n };\n});\nexport const $ZodLazy = /*@__PURE__*/ core.$constructor(\"$ZodLazy\", (inst, def) => {\n $ZodType.init(inst, def);\n // let _innerType!: any;\n // util.defineLazy(def, \"getter\", () => {\n // if (!_innerType) {\n // _innerType = def.getter();\n // }\n // return () => _innerType;\n // });\n util.defineLazy(inst._zod, \"innerType\", () => def.getter());\n util.defineLazy(inst._zod, \"pattern\", () => inst._zod.innerType?._zod?.pattern);\n util.defineLazy(inst._zod, \"propValues\", () => inst._zod.innerType?._zod?.propValues);\n util.defineLazy(inst._zod, \"optin\", () => inst._zod.innerType?._zod?.optin ?? undefined);\n util.defineLazy(inst._zod, \"optout\", () => inst._zod.innerType?._zod?.optout ?? undefined);\n inst._zod.parse = (payload, ctx) => {\n const inner = inst._zod.innerType;\n return inner._zod.run(payload, ctx);\n };\n});\nexport const $ZodCustom = /*@__PURE__*/ core.$constructor(\"$ZodCustom\", (inst, def) => {\n checks.$ZodCheck.init(inst, def);\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _) => {\n return payload;\n };\n inst._zod.check = (payload) => {\n const input = payload.value;\n const r = def.fn(input);\n if (r instanceof Promise) {\n return r.then((r) => handleRefineResult(r, payload, input, inst));\n }\n handleRefineResult(r, payload, input, inst);\n return;\n };\n});\nfunction handleRefineResult(result, payload, input, inst) {\n if (!result) {\n const _iss = {\n code: \"custom\",\n input,\n inst, // incorporates params.error into issue reporting\n path: [...(inst._zod.def.path ?? [])], // incorporates params.error into issue reporting\n continue: !inst._zod.def.abort,\n // params: inst._zod.def.params,\n };\n if (inst._zod.def.params)\n _iss.params = inst._zod.def.params;\n payload.issues.push(util.issue(_iss));\n }\n}\n","var _a;\nexport const $output = Symbol(\"ZodOutput\");\nexport const $input = Symbol(\"ZodInput\");\nexport class $ZodRegistry {\n constructor() {\n this._map = new WeakMap();\n this._idmap = new Map();\n }\n add(schema, ..._meta) {\n const meta = _meta[0];\n this._map.set(schema, meta);\n if (meta && typeof meta === \"object\" && \"id\" in meta) {\n this._idmap.set(meta.id, schema);\n }\n return this;\n }\n clear() {\n this._map = new WeakMap();\n this._idmap = new Map();\n return this;\n }\n remove(schema) {\n const meta = this._map.get(schema);\n if (meta && typeof meta === \"object\" && \"id\" in meta) {\n this._idmap.delete(meta.id);\n }\n this._map.delete(schema);\n return this;\n }\n get(schema) {\n // return this._map.get(schema) as any;\n // inherit metadata\n const p = schema._zod.parent;\n if (p) {\n const pm = { ...(this.get(p) ?? {}) };\n delete pm.id; // do not inherit id\n const f = { ...pm, ...this._map.get(schema) };\n return Object.keys(f).length ? f : undefined;\n }\n return this._map.get(schema);\n }\n has(schema) {\n return this._map.has(schema);\n }\n}\n// registries\nexport function registry() {\n return new $ZodRegistry();\n}\n(_a = globalThis).__zod_globalRegistry ?? (_a.__zod_globalRegistry = registry());\nexport const globalRegistry = globalThis.__zod_globalRegistry;\n","import * as checks from \"./checks.js\";\nimport * as registries from \"./registries.js\";\nimport * as schemas from \"./schemas.js\";\nimport * as util from \"./util.js\";\n// @__NO_SIDE_EFFECTS__\nexport function _string(Class, params) {\n return new Class({\n type: \"string\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedString(Class, params) {\n return new Class({\n type: \"string\",\n coerce: true,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _email(Class, params) {\n return new Class({\n type: \"string\",\n format: \"email\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _guid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"guid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uuidv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v4\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uuidv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v6\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uuidv7(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v7\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _url(Class, params) {\n return new Class({\n type: \"string\",\n format: \"url\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _emoji(Class, params) {\n return new Class({\n type: \"string\",\n format: \"emoji\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _nanoid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"nanoid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _cuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cuid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _cuid2(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cuid2\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _ulid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ulid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _xid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"xid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _ksuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ksuid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _ipv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ipv4\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _ipv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ipv6\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _mac(Class, params) {\n return new Class({\n type: \"string\",\n format: \"mac\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _cidrv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cidrv4\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _cidrv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cidrv6\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _base64(Class, params) {\n return new Class({\n type: \"string\",\n format: \"base64\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _base64url(Class, params) {\n return new Class({\n type: \"string\",\n format: \"base64url\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _e164(Class, params) {\n return new Class({\n type: \"string\",\n format: \"e164\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _jwt(Class, params) {\n return new Class({\n type: \"string\",\n format: \"jwt\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\nexport const TimePrecision = {\n Any: null,\n Minute: -1,\n Second: 0,\n Millisecond: 3,\n Microsecond: 6,\n};\n// @__NO_SIDE_EFFECTS__\nexport function _isoDateTime(Class, params) {\n return new Class({\n type: \"string\",\n format: \"datetime\",\n check: \"string_format\",\n offset: false,\n local: false,\n precision: null,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _isoDate(Class, params) {\n return new Class({\n type: \"string\",\n format: \"date\",\n check: \"string_format\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _isoTime(Class, params) {\n return new Class({\n type: \"string\",\n format: \"time\",\n check: \"string_format\",\n precision: null,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _isoDuration(Class, params) {\n return new Class({\n type: \"string\",\n format: \"duration\",\n check: \"string_format\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _number(Class, params) {\n return new Class({\n type: \"number\",\n checks: [],\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedNumber(Class, params) {\n return new Class({\n type: \"number\",\n coerce: true,\n checks: [],\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _int(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"safeint\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _float32(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"float32\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _float64(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"float64\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _int32(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"int32\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uint32(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"uint32\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _boolean(Class, params) {\n return new Class({\n type: \"boolean\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedBoolean(Class, params) {\n return new Class({\n type: \"boolean\",\n coerce: true,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _bigint(Class, params) {\n return new Class({\n type: \"bigint\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedBigint(Class, params) {\n return new Class({\n type: \"bigint\",\n coerce: true,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _int64(Class, params) {\n return new Class({\n type: \"bigint\",\n check: \"bigint_format\",\n abort: false,\n format: \"int64\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uint64(Class, params) {\n return new Class({\n type: \"bigint\",\n check: \"bigint_format\",\n abort: false,\n format: \"uint64\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _symbol(Class, params) {\n return new Class({\n type: \"symbol\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _undefined(Class, params) {\n return new Class({\n type: \"undefined\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _null(Class, params) {\n return new Class({\n type: \"null\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _any(Class) {\n return new Class({\n type: \"any\",\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _unknown(Class) {\n return new Class({\n type: \"unknown\",\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _never(Class, params) {\n return new Class({\n type: \"never\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _void(Class, params) {\n return new Class({\n type: \"void\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _date(Class, params) {\n return new Class({\n type: \"date\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedDate(Class, params) {\n return new Class({\n type: \"date\",\n coerce: true,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _nan(Class, params) {\n return new Class({\n type: \"nan\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _lt(value, params) {\n return new checks.$ZodCheckLessThan({\n check: \"less_than\",\n ...util.normalizeParams(params),\n value,\n inclusive: false,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _lte(value, params) {\n return new checks.$ZodCheckLessThan({\n check: \"less_than\",\n ...util.normalizeParams(params),\n value,\n inclusive: true,\n });\n}\nexport { \n/** @deprecated Use `z.lte()` instead. */\n_lte as _max, };\n// @__NO_SIDE_EFFECTS__\nexport function _gt(value, params) {\n return new checks.$ZodCheckGreaterThan({\n check: \"greater_than\",\n ...util.normalizeParams(params),\n value,\n inclusive: false,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _gte(value, params) {\n return new checks.$ZodCheckGreaterThan({\n check: \"greater_than\",\n ...util.normalizeParams(params),\n value,\n inclusive: true,\n });\n}\nexport { \n/** @deprecated Use `z.gte()` instead. */\n_gte as _min, };\n// @__NO_SIDE_EFFECTS__\nexport function _positive(params) {\n return _gt(0, params);\n}\n// negative\n// @__NO_SIDE_EFFECTS__\nexport function _negative(params) {\n return _lt(0, params);\n}\n// nonpositive\n// @__NO_SIDE_EFFECTS__\nexport function _nonpositive(params) {\n return _lte(0, params);\n}\n// nonnegative\n// @__NO_SIDE_EFFECTS__\nexport function _nonnegative(params) {\n return _gte(0, params);\n}\n// @__NO_SIDE_EFFECTS__\nexport function _multipleOf(value, params) {\n return new checks.$ZodCheckMultipleOf({\n check: \"multiple_of\",\n ...util.normalizeParams(params),\n value,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _maxSize(maximum, params) {\n return new checks.$ZodCheckMaxSize({\n check: \"max_size\",\n ...util.normalizeParams(params),\n maximum,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _minSize(minimum, params) {\n return new checks.$ZodCheckMinSize({\n check: \"min_size\",\n ...util.normalizeParams(params),\n minimum,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _size(size, params) {\n return new checks.$ZodCheckSizeEquals({\n check: \"size_equals\",\n ...util.normalizeParams(params),\n size,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _maxLength(maximum, params) {\n const ch = new checks.$ZodCheckMaxLength({\n check: \"max_length\",\n ...util.normalizeParams(params),\n maximum,\n });\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _minLength(minimum, params) {\n return new checks.$ZodCheckMinLength({\n check: \"min_length\",\n ...util.normalizeParams(params),\n minimum,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _length(length, params) {\n return new checks.$ZodCheckLengthEquals({\n check: \"length_equals\",\n ...util.normalizeParams(params),\n length,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _regex(pattern, params) {\n return new checks.$ZodCheckRegex({\n check: \"string_format\",\n format: \"regex\",\n ...util.normalizeParams(params),\n pattern,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _lowercase(params) {\n return new checks.$ZodCheckLowerCase({\n check: \"string_format\",\n format: \"lowercase\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uppercase(params) {\n return new checks.$ZodCheckUpperCase({\n check: \"string_format\",\n format: \"uppercase\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _includes(includes, params) {\n return new checks.$ZodCheckIncludes({\n check: \"string_format\",\n format: \"includes\",\n ...util.normalizeParams(params),\n includes,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _startsWith(prefix, params) {\n return new checks.$ZodCheckStartsWith({\n check: \"string_format\",\n format: \"starts_with\",\n ...util.normalizeParams(params),\n prefix,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _endsWith(suffix, params) {\n return new checks.$ZodCheckEndsWith({\n check: \"string_format\",\n format: \"ends_with\",\n ...util.normalizeParams(params),\n suffix,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _property(property, schema, params) {\n return new checks.$ZodCheckProperty({\n check: \"property\",\n property,\n schema,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _mime(types, params) {\n return new checks.$ZodCheckMimeType({\n check: \"mime_type\",\n mime: types,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _overwrite(tx) {\n return new checks.$ZodCheckOverwrite({\n check: \"overwrite\",\n tx,\n });\n}\n// normalize\n// @__NO_SIDE_EFFECTS__\nexport function _normalize(form) {\n return _overwrite((input) => input.normalize(form));\n}\n// trim\n// @__NO_SIDE_EFFECTS__\nexport function _trim() {\n return _overwrite((input) => input.trim());\n}\n// toLowerCase\n// @__NO_SIDE_EFFECTS__\nexport function _toLowerCase() {\n return _overwrite((input) => input.toLowerCase());\n}\n// toUpperCase\n// @__NO_SIDE_EFFECTS__\nexport function _toUpperCase() {\n return _overwrite((input) => input.toUpperCase());\n}\n// slugify\n// @__NO_SIDE_EFFECTS__\nexport function _slugify() {\n return _overwrite((input) => util.slugify(input));\n}\n// @__NO_SIDE_EFFECTS__\nexport function _array(Class, element, params) {\n return new Class({\n type: \"array\",\n element,\n // get element() {\n // return element;\n // },\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _union(Class, options, params) {\n return new Class({\n type: \"union\",\n options,\n ...util.normalizeParams(params),\n });\n}\nexport function _xor(Class, options, params) {\n return new Class({\n type: \"union\",\n options,\n inclusive: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _discriminatedUnion(Class, discriminator, options, params) {\n return new Class({\n type: \"union\",\n options,\n discriminator,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _intersection(Class, left, right) {\n return new Class({\n type: \"intersection\",\n left,\n right,\n });\n}\n// export function _tuple(\n// Class: util.SchemaClass<schemas.$ZodTuple>,\n// items: [],\n// params?: string | $ZodTupleParams\n// ): schemas.$ZodTuple<[], null>;\n// @__NO_SIDE_EFFECTS__\nexport function _tuple(Class, items, _paramsOrRest, _params) {\n const hasRest = _paramsOrRest instanceof schemas.$ZodType;\n const params = hasRest ? _params : _paramsOrRest;\n const rest = hasRest ? _paramsOrRest : null;\n return new Class({\n type: \"tuple\",\n items,\n rest,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _record(Class, keyType, valueType, params) {\n return new Class({\n type: \"record\",\n keyType,\n valueType,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _map(Class, keyType, valueType, params) {\n return new Class({\n type: \"map\",\n keyType,\n valueType,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _set(Class, valueType, params) {\n return new Class({\n type: \"set\",\n valueType,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _enum(Class, values, params) {\n const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;\n // if (Array.isArray(values)) {\n // for (const value of values) {\n // entries[value] = value;\n // }\n // } else {\n // Object.assign(entries, values);\n // }\n // const entries: util.EnumLike = {};\n // for (const val of values) {\n // entries[val] = val;\n // }\n return new Class({\n type: \"enum\",\n entries,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\n/** @deprecated This API has been merged into `z.enum()`. Use `z.enum()` instead.\n *\n * ```ts\n * enum Colors { red, green, blue }\n * z.enum(Colors);\n * ```\n */\nexport function _nativeEnum(Class, entries, params) {\n return new Class({\n type: \"enum\",\n entries,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _literal(Class, value, params) {\n return new Class({\n type: \"literal\",\n values: Array.isArray(value) ? value : [value],\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _file(Class, params) {\n return new Class({\n type: \"file\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _transform(Class, fn) {\n return new Class({\n type: \"transform\",\n transform: fn,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _optional(Class, innerType) {\n return new Class({\n type: \"optional\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _nullable(Class, innerType) {\n return new Class({\n type: \"nullable\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _default(Class, innerType, defaultValue) {\n return new Class({\n type: \"default\",\n innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : util.shallowClone(defaultValue);\n },\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _nonoptional(Class, innerType, params) {\n return new Class({\n type: \"nonoptional\",\n innerType,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _success(Class, innerType) {\n return new Class({\n type: \"success\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _catch(Class, innerType, catchValue) {\n return new Class({\n type: \"catch\",\n innerType,\n catchValue: (typeof catchValue === \"function\" ? catchValue : () => catchValue),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _pipe(Class, in_, out) {\n return new Class({\n type: \"pipe\",\n in: in_,\n out,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _readonly(Class, innerType) {\n return new Class({\n type: \"readonly\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _templateLiteral(Class, parts, params) {\n return new Class({\n type: \"template_literal\",\n parts,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _lazy(Class, getter) {\n return new Class({\n type: \"lazy\",\n getter,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _promise(Class, innerType) {\n return new Class({\n type: \"promise\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _custom(Class, fn, _params) {\n const norm = util.normalizeParams(_params);\n norm.abort ?? (norm.abort = true); // default to abort:false\n const schema = new Class({\n type: \"custom\",\n check: \"custom\",\n fn: fn,\n ...norm,\n });\n return schema;\n}\n// same as _custom but defaults to abort:false\n// @__NO_SIDE_EFFECTS__\nexport function _refine(Class, fn, _params) {\n const schema = new Class({\n type: \"custom\",\n check: \"custom\",\n fn: fn,\n ...util.normalizeParams(_params),\n });\n return schema;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _superRefine(fn) {\n const ch = _check((payload) => {\n payload.addIssue = (issue) => {\n if (typeof issue === \"string\") {\n payload.issues.push(util.issue(issue, payload.value, ch._zod.def));\n }\n else {\n // for Zod 3 backwards compatibility\n const _issue = issue;\n if (_issue.fatal)\n _issue.continue = false;\n _issue.code ?? (_issue.code = \"custom\");\n _issue.input ?? (_issue.input = payload.value);\n _issue.inst ?? (_issue.inst = ch);\n _issue.continue ?? (_issue.continue = !ch._zod.def.abort); // abort is always undefined, so this is always true...\n payload.issues.push(util.issue(_issue));\n }\n };\n return fn(payload.value, payload);\n });\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _check(fn, params) {\n const ch = new checks.$ZodCheck({\n check: \"custom\",\n ...util.normalizeParams(params),\n });\n ch._zod.check = fn;\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function describe(description) {\n const ch = new checks.$ZodCheck({ check: \"describe\" });\n ch._zod.onattach = [\n (inst) => {\n const existing = registries.globalRegistry.get(inst) ?? {};\n registries.globalRegistry.add(inst, { ...existing, description });\n },\n ];\n ch._zod.check = () => { }; // no-op check\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function meta(metadata) {\n const ch = new checks.$ZodCheck({ check: \"meta\" });\n ch._zod.onattach = [\n (inst) => {\n const existing = registries.globalRegistry.get(inst) ?? {};\n registries.globalRegistry.add(inst, { ...existing, ...metadata });\n },\n ];\n ch._zod.check = () => { }; // no-op check\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _stringbool(Classes, _params) {\n const params = util.normalizeParams(_params);\n let truthyArray = params.truthy ?? [\"true\", \"1\", \"yes\", \"on\", \"y\", \"enabled\"];\n let falsyArray = params.falsy ?? [\"false\", \"0\", \"no\", \"off\", \"n\", \"disabled\"];\n if (params.case !== \"sensitive\") {\n truthyArray = truthyArray.map((v) => (typeof v === \"string\" ? v.toLowerCase() : v));\n falsyArray = falsyArray.map((v) => (typeof v === \"string\" ? v.toLowerCase() : v));\n }\n const truthySet = new Set(truthyArray);\n const falsySet = new Set(falsyArray);\n const _Codec = Classes.Codec ?? schemas.$ZodCodec;\n const _Boolean = Classes.Boolean ?? schemas.$ZodBoolean;\n const _String = Classes.String ?? schemas.$ZodString;\n const stringSchema = new _String({ type: \"string\", error: params.error });\n const booleanSchema = new _Boolean({ type: \"boolean\", error: params.error });\n const codec = new _Codec({\n type: \"pipe\",\n in: stringSchema,\n out: booleanSchema,\n transform: ((input, payload) => {\n let data = input;\n if (params.case !== \"sensitive\")\n data = data.toLowerCase();\n if (truthySet.has(data)) {\n return true;\n }\n else if (falsySet.has(data)) {\n return false;\n }\n else {\n payload.issues.push({\n code: \"invalid_value\",\n expected: \"stringbool\",\n values: [...truthySet, ...falsySet],\n input: payload.value,\n inst: codec,\n continue: false,\n });\n return {};\n }\n }),\n reverseTransform: ((input, _payload) => {\n if (input === true) {\n return truthyArray[0] || \"true\";\n }\n else {\n return falsyArray[0] || \"false\";\n }\n }),\n error: params.error,\n });\n return codec;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _stringFormat(Class, format, fnOrRegex, _params = {}) {\n const params = util.normalizeParams(_params);\n const def = {\n ...util.normalizeParams(_params),\n check: \"string_format\",\n type: \"string\",\n format,\n fn: typeof fnOrRegex === \"function\" ? fnOrRegex : (val) => fnOrRegex.test(val),\n ...params,\n };\n if (fnOrRegex instanceof RegExp) {\n def.pattern = fnOrRegex;\n }\n const inst = new Class(def);\n return inst;\n}\n","import { globalRegistry } from \"./registries.js\";\n// function initializeContext<T extends schemas.$ZodType>(inputs: JSONSchemaGeneratorParams<T>): ToJSONSchemaContext<T> {\n// return {\n// processor: inputs.processor,\n// metadataRegistry: inputs.metadata ?? globalRegistry,\n// target: inputs.target ?? \"draft-2020-12\",\n// unrepresentable: inputs.unrepresentable ?? \"throw\",\n// };\n// }\nexport function initializeContext(params) {\n // Normalize target: convert old non-hyphenated versions to hyphenated versions\n let target = params?.target ?? \"draft-2020-12\";\n if (target === \"draft-4\")\n target = \"draft-04\";\n if (target === \"draft-7\")\n target = \"draft-07\";\n return {\n processors: params.processors ?? {},\n metadataRegistry: params?.metadata ?? globalRegistry,\n target,\n unrepresentable: params?.unrepresentable ?? \"throw\",\n override: params?.override ?? (() => { }),\n io: params?.io ?? \"output\",\n counter: 0,\n seen: new Map(),\n cycles: params?.cycles ?? \"ref\",\n reused: params?.reused ?? \"inline\",\n external: params?.external ?? undefined,\n };\n}\nexport function process(schema, ctx, _params = { path: [], schemaPath: [] }) {\n var _a;\n const def = schema._zod.def;\n // check for schema in seens\n const seen = ctx.seen.get(schema);\n if (seen) {\n seen.count++;\n // check if cycle\n const isCycle = _params.schemaPath.includes(schema);\n if (isCycle) {\n seen.cycle = _params.path;\n }\n return seen.schema;\n }\n // initialize\n const result = { schema: {}, count: 1, cycle: undefined, path: _params.path };\n ctx.seen.set(schema, result);\n // custom method overrides default behavior\n const overrideSchema = schema._zod.toJSONSchema?.();\n if (overrideSchema) {\n result.schema = overrideSchema;\n }\n else {\n const params = {\n ..._params,\n schemaPath: [..._params.schemaPath, schema],\n path: _params.path,\n };\n if (schema._zod.processJSONSchema) {\n schema._zod.processJSONSchema(ctx, result.schema, params);\n }\n else {\n const _json = result.schema;\n const processor = ctx.processors[def.type];\n if (!processor) {\n throw new Error(`[toJSONSchema]: Non-representable type encountered: ${def.type}`);\n }\n processor(schema, ctx, _json, params);\n }\n const parent = schema._zod.parent;\n if (parent) {\n // Also set ref if processor didn't (for inheritance)\n if (!result.ref)\n result.ref = parent;\n process(parent, ctx, params);\n ctx.seen.get(parent).isParent = true;\n }\n }\n // metadata\n const meta = ctx.metadataRegistry.get(schema);\n if (meta)\n Object.assign(result.schema, meta);\n if (ctx.io === \"input\" && isTransforming(schema)) {\n // examples/defaults only apply to output type of pipe\n delete result.schema.examples;\n delete result.schema.default;\n }\n // set prefault as default\n if (ctx.io === \"input\" && result.schema._prefault)\n (_a = result.schema).default ?? (_a.default = result.schema._prefault);\n delete result.schema._prefault;\n // pulling fresh from ctx.seen in case it was overwritten\n const _result = ctx.seen.get(schema);\n return _result.schema;\n}\nexport function extractDefs(ctx, schema\n// params: EmitParams\n) {\n // iterate over seen map;\n const root = ctx.seen.get(schema);\n if (!root)\n throw new Error(\"Unprocessed schema. This is a bug in Zod.\");\n // Track ids to detect duplicates across different schemas\n const idToSchema = new Map();\n for (const entry of ctx.seen.entries()) {\n const id = ctx.metadataRegistry.get(entry[0])?.id;\n if (id) {\n const existing = idToSchema.get(id);\n if (existing && existing !== entry[0]) {\n throw new Error(`Duplicate schema id \"${id}\" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);\n }\n idToSchema.set(id, entry[0]);\n }\n }\n // returns a ref to the schema\n // defId will be empty if the ref points to an external schema (or #)\n const makeURI = (entry) => {\n // comparing the seen objects because sometimes\n // multiple schemas map to the same seen object.\n // e.g. lazy\n // external is configured\n const defsSegment = ctx.target === \"draft-2020-12\" ? \"$defs\" : \"definitions\";\n if (ctx.external) {\n const externalId = ctx.external.registry.get(entry[0])?.id; // ?? \"__shared\";// `__schema${ctx.counter++}`;\n // check if schema is in the external registry\n const uriGenerator = ctx.external.uri ?? ((id) => id);\n if (externalId) {\n return { ref: uriGenerator(externalId) };\n }\n // otherwise, add to __shared\n const id = entry[1].defId ?? entry[1].schema.id ?? `schema${ctx.counter++}`;\n entry[1].defId = id; // set defId so it will be reused if needed\n return { defId: id, ref: `${uriGenerator(\"__shared\")}#/${defsSegment}/${id}` };\n }\n if (entry[1] === root) {\n return { ref: \"#\" };\n }\n // self-contained schema\n const uriPrefix = `#`;\n const defUriPrefix = `${uriPrefix}/${defsSegment}/`;\n const defId = entry[1].schema.id ?? `__schema${ctx.counter++}`;\n return { defId, ref: defUriPrefix + defId };\n };\n // stored cached version in `def` property\n // remove all properties, set $ref\n const extractToDef = (entry) => {\n // if the schema is already a reference, do not extract it\n if (entry[1].schema.$ref) {\n return;\n }\n const seen = entry[1];\n const { ref, defId } = makeURI(entry);\n seen.def = { ...seen.schema };\n // defId won't be set if the schema is a reference to an external schema\n // or if the schema is the root schema\n if (defId)\n seen.defId = defId;\n // wipe away all properties except $ref\n const schema = seen.schema;\n for (const key in schema) {\n delete schema[key];\n }\n schema.$ref = ref;\n };\n // throw on cycles\n // break cycles\n if (ctx.cycles === \"throw\") {\n for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (seen.cycle) {\n throw new Error(\"Cycle detected: \" +\n `#/${seen.cycle?.join(\"/\")}/<root>` +\n '\\n\\nSet the `cycles` parameter to `\"ref\"` to resolve cyclical schemas with defs.');\n }\n }\n }\n // extract schemas into $defs\n for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n // convert root schema to # $ref\n if (schema === entry[0]) {\n extractToDef(entry); // this has special handling for the root schema\n continue;\n }\n // extract schemas that are in the external registry\n if (ctx.external) {\n const ext = ctx.external.registry.get(entry[0])?.id;\n if (schema !== entry[0] && ext) {\n extractToDef(entry);\n continue;\n }\n }\n // extract schemas with `id` meta\n const id = ctx.metadataRegistry.get(entry[0])?.id;\n if (id) {\n extractToDef(entry);\n continue;\n }\n // break cycles\n if (seen.cycle) {\n // any\n extractToDef(entry);\n continue;\n }\n // extract reused schemas\n if (seen.count > 1) {\n if (ctx.reused === \"ref\") {\n extractToDef(entry);\n // biome-ignore lint:\n continue;\n }\n }\n }\n}\nexport function finalize(ctx, schema) {\n const root = ctx.seen.get(schema);\n if (!root)\n throw new Error(\"Unprocessed schema. This is a bug in Zod.\");\n // flatten refs - inherit properties from parent schemas\n const flattenRef = (zodSchema) => {\n const seen = ctx.seen.get(zodSchema);\n // already processed\n if (seen.ref === null)\n return;\n const schema = seen.def ?? seen.schema;\n const _cached = { ...schema };\n const ref = seen.ref;\n seen.ref = null; // prevent infinite recursion\n if (ref) {\n flattenRef(ref);\n const refSeen = ctx.seen.get(ref);\n const refSchema = refSeen.schema;\n // merge referenced schema into current\n if (refSchema.$ref && (ctx.target === \"draft-07\" || ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\")) {\n // older drafts can't combine $ref with other properties\n schema.allOf = schema.allOf ?? [];\n schema.allOf.push(refSchema);\n }\n else {\n Object.assign(schema, refSchema);\n }\n // restore child's own properties (child wins)\n Object.assign(schema, _cached);\n const isParentRef = zodSchema._zod.parent === ref;\n // For parent chain, child is a refinement - remove parent-only properties\n if (isParentRef) {\n for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\")\n continue;\n if (!(key in _cached)) {\n delete schema[key];\n }\n }\n }\n // When ref was extracted to $defs, remove properties that match the definition\n if (refSchema.$ref && refSeen.def) {\n for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\")\n continue;\n if (key in refSeen.def && JSON.stringify(schema[key]) === JSON.stringify(refSeen.def[key])) {\n delete schema[key];\n }\n }\n }\n }\n // If parent was extracted (has $ref), propagate $ref to this schema\n // This handles cases like: readonly().meta({id}).describe()\n // where processor sets ref to innerType but parent should be referenced\n const parent = zodSchema._zod.parent;\n if (parent && parent !== ref) {\n // Ensure parent is processed first so its def has inherited properties\n flattenRef(parent);\n const parentSeen = ctx.seen.get(parent);\n if (parentSeen?.schema.$ref) {\n schema.$ref = parentSeen.schema.$ref;\n // De-duplicate with parent's definition\n if (parentSeen.def) {\n for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\")\n continue;\n if (key in parentSeen.def && JSON.stringify(schema[key]) === JSON.stringify(parentSeen.def[key])) {\n delete schema[key];\n }\n }\n }\n }\n }\n // execute overrides\n ctx.override({\n zodSchema: zodSchema,\n jsonSchema: schema,\n path: seen.path ?? [],\n });\n };\n for (const entry of [...ctx.seen.entries()].reverse()) {\n flattenRef(entry[0]);\n }\n const result = {};\n if (ctx.target === \"draft-2020-12\") {\n result.$schema = \"https://json-schema.org/draft/2020-12/schema\";\n }\n else if (ctx.target === \"draft-07\") {\n result.$schema = \"http://json-schema.org/draft-07/schema#\";\n }\n else if (ctx.target === \"draft-04\") {\n result.$schema = \"http://json-schema.org/draft-04/schema#\";\n }\n else if (ctx.target === \"openapi-3.0\") {\n // OpenAPI 3.0 schema objects should not include a $schema property\n }\n else {\n // Arbitrary string values are allowed but won't have a $schema property set\n }\n if (ctx.external?.uri) {\n const id = ctx.external.registry.get(schema)?.id;\n if (!id)\n throw new Error(\"Schema is missing an `id` property\");\n result.$id = ctx.external.uri(id);\n }\n Object.assign(result, root.def ?? root.schema);\n // build defs object\n const defs = ctx.external?.defs ?? {};\n for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (seen.def && seen.defId) {\n defs[seen.defId] = seen.def;\n }\n }\n // set definitions in result\n if (ctx.external) {\n }\n else {\n if (Object.keys(defs).length > 0) {\n if (ctx.target === \"draft-2020-12\") {\n result.$defs = defs;\n }\n else {\n result.definitions = defs;\n }\n }\n }\n try {\n // this \"finalizes\" this schema and ensures all cycles are removed\n // each call to finalize() is functionally independent\n // though the seen map is shared\n const finalized = JSON.parse(JSON.stringify(result));\n Object.defineProperty(finalized, \"~standard\", {\n value: {\n ...schema[\"~standard\"],\n jsonSchema: {\n input: createStandardJSONSchemaMethod(schema, \"input\", ctx.processors),\n output: createStandardJSONSchemaMethod(schema, \"output\", ctx.processors),\n },\n },\n enumerable: false,\n writable: false,\n });\n return finalized;\n }\n catch (_err) {\n throw new Error(\"Error converting schema to JSON.\");\n }\n}\nfunction isTransforming(_schema, _ctx) {\n const ctx = _ctx ?? { seen: new Set() };\n if (ctx.seen.has(_schema))\n return false;\n ctx.seen.add(_schema);\n const def = _schema._zod.def;\n if (def.type === \"transform\")\n return true;\n if (def.type === \"array\")\n return isTransforming(def.element, ctx);\n if (def.type === \"set\")\n return isTransforming(def.valueType, ctx);\n if (def.type === \"lazy\")\n return isTransforming(def.getter(), ctx);\n if (def.type === \"promise\" ||\n def.type === \"optional\" ||\n def.type === \"nonoptional\" ||\n def.type === \"nullable\" ||\n def.type === \"readonly\" ||\n def.type === \"default\" ||\n def.type === \"prefault\") {\n return isTransforming(def.innerType, ctx);\n }\n if (def.type === \"intersection\") {\n return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);\n }\n if (def.type === \"record\" || def.type === \"map\") {\n return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);\n }\n if (def.type === \"pipe\") {\n return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);\n }\n if (def.type === \"object\") {\n for (const key in def.shape) {\n if (isTransforming(def.shape[key], ctx))\n return true;\n }\n return false;\n }\n if (def.type === \"union\") {\n for (const option of def.options) {\n if (isTransforming(option, ctx))\n return true;\n }\n return false;\n }\n if (def.type === \"tuple\") {\n for (const item of def.items) {\n if (isTransforming(item, ctx))\n return true;\n }\n if (def.rest && isTransforming(def.rest, ctx))\n return true;\n return false;\n }\n return false;\n}\n/**\n * Creates a toJSONSchema method for a schema instance.\n * This encapsulates the logic of initializing context, processing, extracting defs, and finalizing.\n */\nexport const createToJSONSchemaMethod = (schema, processors = {}) => (params) => {\n const ctx = initializeContext({ ...params, processors });\n process(schema, ctx);\n extractDefs(ctx, schema);\n return finalize(ctx, schema);\n};\nexport const createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {\n const { libraryOptions, target } = params ?? {};\n const ctx = initializeContext({ ...(libraryOptions ?? {}), target, io, processors });\n process(schema, ctx);\n extractDefs(ctx, schema);\n return finalize(ctx, schema);\n};\n","import { extractDefs, finalize, initializeContext, process, } from \"./to-json-schema.js\";\nimport { getEnumValues } from \"./util.js\";\nconst formatMap = {\n guid: \"uuid\",\n url: \"uri\",\n datetime: \"date-time\",\n json_string: \"json-string\",\n regex: \"\", // do not set\n};\n// ==================== SIMPLE TYPE PROCESSORS ====================\nexport const stringProcessor = (schema, ctx, _json, _params) => {\n const json = _json;\n json.type = \"string\";\n const { minimum, maximum, format, patterns, contentEncoding } = schema._zod\n .bag;\n if (typeof minimum === \"number\")\n json.minLength = minimum;\n if (typeof maximum === \"number\")\n json.maxLength = maximum;\n // custom pattern overrides format\n if (format) {\n json.format = formatMap[format] ?? format;\n if (json.format === \"\")\n delete json.format; // empty format is not valid\n // JSON Schema format: \"time\" requires a full time with offset or Z\n // z.iso.time() does not include timezone information, so format: \"time\" should never be used\n if (format === \"time\") {\n delete json.format;\n }\n }\n if (contentEncoding)\n json.contentEncoding = contentEncoding;\n if (patterns && patterns.size > 0) {\n const regexes = [...patterns];\n if (regexes.length === 1)\n json.pattern = regexes[0].source;\n else if (regexes.length > 1) {\n json.allOf = [\n ...regexes.map((regex) => ({\n ...(ctx.target === \"draft-07\" || ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\"\n ? { type: \"string\" }\n : {}),\n pattern: regex.source,\n })),\n ];\n }\n }\n};\nexport const numberProcessor = (schema, ctx, _json, _params) => {\n const json = _json;\n const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;\n if (typeof format === \"string\" && format.includes(\"int\"))\n json.type = \"integer\";\n else\n json.type = \"number\";\n if (typeof exclusiveMinimum === \"number\") {\n if (ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\") {\n json.minimum = exclusiveMinimum;\n json.exclusiveMinimum = true;\n }\n else {\n json.exclusiveMinimum = exclusiveMinimum;\n }\n }\n if (typeof minimum === \"number\") {\n json.minimum = minimum;\n if (typeof exclusiveMinimum === \"number\" && ctx.target !== \"draft-04\") {\n if (exclusiveMinimum >= minimum)\n delete json.minimum;\n else\n delete json.exclusiveMinimum;\n }\n }\n if (typeof exclusiveMaximum === \"number\") {\n if (ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\") {\n json.maximum = exclusiveMaximum;\n json.exclusiveMaximum = true;\n }\n else {\n json.exclusiveMaximum = exclusiveMaximum;\n }\n }\n if (typeof maximum === \"number\") {\n json.maximum = maximum;\n if (typeof exclusiveMaximum === \"number\" && ctx.target !== \"draft-04\") {\n if (exclusiveMaximum <= maximum)\n delete json.maximum;\n else\n delete json.exclusiveMaximum;\n }\n }\n if (typeof multipleOf === \"number\")\n json.multipleOf = multipleOf;\n};\nexport const booleanProcessor = (_schema, _ctx, json, _params) => {\n json.type = \"boolean\";\n};\nexport const bigintProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"BigInt cannot be represented in JSON Schema\");\n }\n};\nexport const symbolProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Symbols cannot be represented in JSON Schema\");\n }\n};\nexport const nullProcessor = (_schema, ctx, json, _params) => {\n if (ctx.target === \"openapi-3.0\") {\n json.type = \"string\";\n json.nullable = true;\n json.enum = [null];\n }\n else {\n json.type = \"null\";\n }\n};\nexport const undefinedProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Undefined cannot be represented in JSON Schema\");\n }\n};\nexport const voidProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Void cannot be represented in JSON Schema\");\n }\n};\nexport const neverProcessor = (_schema, _ctx, json, _params) => {\n json.not = {};\n};\nexport const anyProcessor = (_schema, _ctx, _json, _params) => {\n // empty schema accepts anything\n};\nexport const unknownProcessor = (_schema, _ctx, _json, _params) => {\n // empty schema accepts anything\n};\nexport const dateProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Date cannot be represented in JSON Schema\");\n }\n};\nexport const enumProcessor = (schema, _ctx, json, _params) => {\n const def = schema._zod.def;\n const values = getEnumValues(def.entries);\n // Number enums can have both string and number values\n if (values.every((v) => typeof v === \"number\"))\n json.type = \"number\";\n if (values.every((v) => typeof v === \"string\"))\n json.type = \"string\";\n json.enum = values;\n};\nexport const literalProcessor = (schema, ctx, json, _params) => {\n const def = schema._zod.def;\n const vals = [];\n for (const val of def.values) {\n if (val === undefined) {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Literal `undefined` cannot be represented in JSON Schema\");\n }\n else {\n // do not add to vals\n }\n }\n else if (typeof val === \"bigint\") {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"BigInt literals cannot be represented in JSON Schema\");\n }\n else {\n vals.push(Number(val));\n }\n }\n else {\n vals.push(val);\n }\n }\n if (vals.length === 0) {\n // do nothing (an undefined literal was stripped)\n }\n else if (vals.length === 1) {\n const val = vals[0];\n json.type = val === null ? \"null\" : typeof val;\n if (ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\") {\n json.enum = [val];\n }\n else {\n json.const = val;\n }\n }\n else {\n if (vals.every((v) => typeof v === \"number\"))\n json.type = \"number\";\n if (vals.every((v) => typeof v === \"string\"))\n json.type = \"string\";\n if (vals.every((v) => typeof v === \"boolean\"))\n json.type = \"boolean\";\n if (vals.every((v) => v === null))\n json.type = \"null\";\n json.enum = vals;\n }\n};\nexport const nanProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"NaN cannot be represented in JSON Schema\");\n }\n};\nexport const templateLiteralProcessor = (schema, _ctx, json, _params) => {\n const _json = json;\n const pattern = schema._zod.pattern;\n if (!pattern)\n throw new Error(\"Pattern not found in template literal\");\n _json.type = \"string\";\n _json.pattern = pattern.source;\n};\nexport const fileProcessor = (schema, _ctx, json, _params) => {\n const _json = json;\n const file = {\n type: \"string\",\n format: \"binary\",\n contentEncoding: \"binary\",\n };\n const { minimum, maximum, mime } = schema._zod.bag;\n if (minimum !== undefined)\n file.minLength = minimum;\n if (maximum !== undefined)\n file.maxLength = maximum;\n if (mime) {\n if (mime.length === 1) {\n file.contentMediaType = mime[0];\n Object.assign(_json, file);\n }\n else {\n Object.assign(_json, file); // shared props at root\n _json.anyOf = mime.map((m) => ({ contentMediaType: m })); // only contentMediaType differs\n }\n }\n else {\n Object.assign(_json, file);\n }\n};\nexport const successProcessor = (_schema, _ctx, json, _params) => {\n json.type = \"boolean\";\n};\nexport const customProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Custom types cannot be represented in JSON Schema\");\n }\n};\nexport const functionProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Function types cannot be represented in JSON Schema\");\n }\n};\nexport const transformProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Transforms cannot be represented in JSON Schema\");\n }\n};\nexport const mapProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Map cannot be represented in JSON Schema\");\n }\n};\nexport const setProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Set cannot be represented in JSON Schema\");\n }\n};\n// ==================== COMPOSITE TYPE PROCESSORS ====================\nexport const arrayProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n const { minimum, maximum } = schema._zod.bag;\n if (typeof minimum === \"number\")\n json.minItems = minimum;\n if (typeof maximum === \"number\")\n json.maxItems = maximum;\n json.type = \"array\";\n json.items = process(def.element, ctx, { ...params, path: [...params.path, \"items\"] });\n};\nexport const objectProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n json.type = \"object\";\n json.properties = {};\n const shape = def.shape;\n for (const key in shape) {\n json.properties[key] = process(shape[key], ctx, {\n ...params,\n path: [...params.path, \"properties\", key],\n });\n }\n // required keys\n const allKeys = new Set(Object.keys(shape));\n const requiredKeys = new Set([...allKeys].filter((key) => {\n const v = def.shape[key]._zod;\n if (ctx.io === \"input\") {\n return v.optin === undefined;\n }\n else {\n return v.optout === undefined;\n }\n }));\n if (requiredKeys.size > 0) {\n json.required = Array.from(requiredKeys);\n }\n // catchall\n if (def.catchall?._zod.def.type === \"never\") {\n // strict\n json.additionalProperties = false;\n }\n else if (!def.catchall) {\n // regular\n if (ctx.io === \"output\")\n json.additionalProperties = false;\n }\n else if (def.catchall) {\n json.additionalProperties = process(def.catchall, ctx, {\n ...params,\n path: [...params.path, \"additionalProperties\"],\n });\n }\n};\nexport const unionProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n // Exclusive unions (inclusive === false) use oneOf (exactly one match) instead of anyOf (one or more matches)\n // This includes both z.xor() and discriminated unions\n const isExclusive = def.inclusive === false;\n const options = def.options.map((x, i) => process(x, ctx, {\n ...params,\n path: [...params.path, isExclusive ? \"oneOf\" : \"anyOf\", i],\n }));\n if (isExclusive) {\n json.oneOf = options;\n }\n else {\n json.anyOf = options;\n }\n};\nexport const intersectionProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const a = process(def.left, ctx, {\n ...params,\n path: [...params.path, \"allOf\", 0],\n });\n const b = process(def.right, ctx, {\n ...params,\n path: [...params.path, \"allOf\", 1],\n });\n const isSimpleIntersection = (val) => \"allOf\" in val && Object.keys(val).length === 1;\n const allOf = [\n ...(isSimpleIntersection(a) ? a.allOf : [a]),\n ...(isSimpleIntersection(b) ? b.allOf : [b]),\n ];\n json.allOf = allOf;\n};\nexport const tupleProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n json.type = \"array\";\n const prefixPath = ctx.target === \"draft-2020-12\" ? \"prefixItems\" : \"items\";\n const restPath = ctx.target === \"draft-2020-12\" ? \"items\" : ctx.target === \"openapi-3.0\" ? \"items\" : \"additionalItems\";\n const prefixItems = def.items.map((x, i) => process(x, ctx, {\n ...params,\n path: [...params.path, prefixPath, i],\n }));\n const rest = def.rest\n ? process(def.rest, ctx, {\n ...params,\n path: [...params.path, restPath, ...(ctx.target === \"openapi-3.0\" ? [def.items.length] : [])],\n })\n : null;\n if (ctx.target === \"draft-2020-12\") {\n json.prefixItems = prefixItems;\n if (rest) {\n json.items = rest;\n }\n }\n else if (ctx.target === \"openapi-3.0\") {\n json.items = {\n anyOf: prefixItems,\n };\n if (rest) {\n json.items.anyOf.push(rest);\n }\n json.minItems = prefixItems.length;\n if (!rest) {\n json.maxItems = prefixItems.length;\n }\n }\n else {\n json.items = prefixItems;\n if (rest) {\n json.additionalItems = rest;\n }\n }\n // length\n const { minimum, maximum } = schema._zod.bag;\n if (typeof minimum === \"number\")\n json.minItems = minimum;\n if (typeof maximum === \"number\")\n json.maxItems = maximum;\n};\nexport const recordProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n json.type = \"object\";\n // For looseRecord with regex patterns, use patternProperties\n // This correctly represents \"only validate keys matching the pattern\" semantics\n // and composes well with allOf (intersections)\n const keyType = def.keyType;\n const keyBag = keyType._zod.bag;\n const patterns = keyBag?.patterns;\n if (def.mode === \"loose\" && patterns && patterns.size > 0) {\n // Use patternProperties for looseRecord with regex patterns\n const valueSchema = process(def.valueType, ctx, {\n ...params,\n path: [...params.path, \"patternProperties\", \"*\"],\n });\n json.patternProperties = {};\n for (const pattern of patterns) {\n json.patternProperties[pattern.source] = valueSchema;\n }\n }\n else {\n // Default behavior: use propertyNames + additionalProperties\n if (ctx.target === \"draft-07\" || ctx.target === \"draft-2020-12\") {\n json.propertyNames = process(def.keyType, ctx, {\n ...params,\n path: [...params.path, \"propertyNames\"],\n });\n }\n json.additionalProperties = process(def.valueType, ctx, {\n ...params,\n path: [...params.path, \"additionalProperties\"],\n });\n }\n // Add required for keys with discrete values (enum, literal, etc.)\n const keyValues = keyType._zod.values;\n if (keyValues) {\n const validKeyValues = [...keyValues].filter((v) => typeof v === \"string\" || typeof v === \"number\");\n if (validKeyValues.length > 0) {\n json.required = validKeyValues;\n }\n }\n};\nexport const nullableProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const inner = process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n if (ctx.target === \"openapi-3.0\") {\n seen.ref = def.innerType;\n json.nullable = true;\n }\n else {\n json.anyOf = [inner, { type: \"null\" }];\n }\n};\nexport const nonoptionalProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\nexport const defaultProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n json.default = JSON.parse(JSON.stringify(def.defaultValue));\n};\nexport const prefaultProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n if (ctx.io === \"input\")\n json._prefault = JSON.parse(JSON.stringify(def.defaultValue));\n};\nexport const catchProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n let catchValue;\n try {\n catchValue = def.catchValue(undefined);\n }\n catch {\n throw new Error(\"Dynamic catch values are not supported in JSON Schema\");\n }\n json.default = catchValue;\n};\nexport const pipeProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n const innerType = ctx.io === \"input\" ? (def.in._zod.def.type === \"transform\" ? def.out : def.in) : def.out;\n process(innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = innerType;\n};\nexport const readonlyProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n json.readOnly = true;\n};\nexport const promiseProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\nexport const optionalProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\nexport const lazyProcessor = (schema, ctx, _json, params) => {\n const innerType = schema._zod.innerType;\n process(innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = innerType;\n};\n// ==================== ALL PROCESSORS ====================\nexport const allProcessors = {\n string: stringProcessor,\n number: numberProcessor,\n boolean: booleanProcessor,\n bigint: bigintProcessor,\n symbol: symbolProcessor,\n null: nullProcessor,\n undefined: undefinedProcessor,\n void: voidProcessor,\n never: neverProcessor,\n any: anyProcessor,\n unknown: unknownProcessor,\n date: dateProcessor,\n enum: enumProcessor,\n literal: literalProcessor,\n nan: nanProcessor,\n template_literal: templateLiteralProcessor,\n file: fileProcessor,\n success: successProcessor,\n custom: customProcessor,\n function: functionProcessor,\n transform: transformProcessor,\n map: mapProcessor,\n set: setProcessor,\n array: arrayProcessor,\n object: objectProcessor,\n union: unionProcessor,\n intersection: intersectionProcessor,\n tuple: tupleProcessor,\n record: recordProcessor,\n nullable: nullableProcessor,\n nonoptional: nonoptionalProcessor,\n default: defaultProcessor,\n prefault: prefaultProcessor,\n catch: catchProcessor,\n pipe: pipeProcessor,\n readonly: readonlyProcessor,\n promise: promiseProcessor,\n optional: optionalProcessor,\n lazy: lazyProcessor,\n};\nexport function toJSONSchema(input, params) {\n if (\"_idmap\" in input) {\n // Registry case\n const registry = input;\n const ctx = initializeContext({ ...params, processors: allProcessors });\n const defs = {};\n // First pass: process all schemas to build the seen map\n for (const entry of registry._idmap.entries()) {\n const [_, schema] = entry;\n process(schema, ctx);\n }\n const schemas = {};\n const external = {\n registry,\n uri: params?.uri,\n defs,\n };\n // Update the context with external configuration\n ctx.external = external;\n // Second pass: emit each schema\n for (const entry of registry._idmap.entries()) {\n const [key, schema] = entry;\n extractDefs(ctx, schema);\n schemas[key] = finalize(ctx, schema);\n }\n if (Object.keys(defs).length > 0) {\n const defsSegment = ctx.target === \"draft-2020-12\" ? \"$defs\" : \"definitions\";\n schemas.__shared = {\n [defsSegment]: defs,\n };\n }\n return { schemas };\n }\n // Single schema case\n const ctx = initializeContext({ ...params, processors: allProcessors });\n process(input, ctx);\n extractDefs(ctx, input);\n return finalize(ctx, input);\n}\n","import * as core from \"../core/index.js\";\nimport * as schemas from \"./schemas.js\";\nexport const ZodISODateTime = /*@__PURE__*/ core.$constructor(\"ZodISODateTime\", (inst, def) => {\n core.$ZodISODateTime.init(inst, def);\n schemas.ZodStringFormat.init(inst, def);\n});\nexport function datetime(params) {\n return core._isoDateTime(ZodISODateTime, params);\n}\nexport const ZodISODate = /*@__PURE__*/ core.$constructor(\"ZodISODate\", (inst, def) => {\n core.$ZodISODate.init(inst, def);\n schemas.ZodStringFormat.init(inst, def);\n});\nexport function date(params) {\n return core._isoDate(ZodISODate, params);\n}\nexport const ZodISOTime = /*@__PURE__*/ core.$constructor(\"ZodISOTime\", (inst, def) => {\n core.$ZodISOTime.init(inst, def);\n schemas.ZodStringFormat.init(inst, def);\n});\nexport function time(params) {\n return core._isoTime(ZodISOTime, params);\n}\nexport const ZodISODuration = /*@__PURE__*/ core.$constructor(\"ZodISODuration\", (inst, def) => {\n core.$ZodISODuration.init(inst, def);\n schemas.ZodStringFormat.init(inst, def);\n});\nexport function duration(params) {\n return core._isoDuration(ZodISODuration, params);\n}\n","import * as core from \"../core/index.js\";\nimport { $ZodError } from \"../core/index.js\";\nimport * as util from \"../core/util.js\";\nconst initializer = (inst, issues) => {\n $ZodError.init(inst, issues);\n inst.name = \"ZodError\";\n Object.defineProperties(inst, {\n format: {\n value: (mapper) => core.formatError(inst, mapper),\n // enumerable: false,\n },\n flatten: {\n value: (mapper) => core.flattenError(inst, mapper),\n // enumerable: false,\n },\n addIssue: {\n value: (issue) => {\n inst.issues.push(issue);\n inst.message = JSON.stringify(inst.issues, util.jsonStringifyReplacer, 2);\n },\n // enumerable: false,\n },\n addIssues: {\n value: (issues) => {\n inst.issues.push(...issues);\n inst.message = JSON.stringify(inst.issues, util.jsonStringifyReplacer, 2);\n },\n // enumerable: false,\n },\n isEmpty: {\n get() {\n return inst.issues.length === 0;\n },\n // enumerable: false,\n },\n });\n // Object.defineProperty(inst, \"isEmpty\", {\n // get() {\n // return inst.issues.length === 0;\n // },\n // });\n};\nexport const ZodError = core.$constructor(\"ZodError\", initializer);\nexport const ZodRealError = core.$constructor(\"ZodError\", initializer, {\n Parent: Error,\n});\n// /** @deprecated Use `z.core.$ZodErrorMapCtx` instead. */\n// export type ErrorMapCtx = core.$ZodErrorMapCtx;\n","import * as core from \"../core/index.js\";\nimport { ZodRealError } from \"./errors.js\";\nexport const parse = /* @__PURE__ */ core._parse(ZodRealError);\nexport const parseAsync = /* @__PURE__ */ core._parseAsync(ZodRealError);\nexport const safeParse = /* @__PURE__ */ core._safeParse(ZodRealError);\nexport const safeParseAsync = /* @__PURE__ */ core._safeParseAsync(ZodRealError);\n// Codec functions\nexport const encode = /* @__PURE__ */ core._encode(ZodRealError);\nexport const decode = /* @__PURE__ */ core._decode(ZodRealError);\nexport const encodeAsync = /* @__PURE__ */ core._encodeAsync(ZodRealError);\nexport const decodeAsync = /* @__PURE__ */ core._decodeAsync(ZodRealError);\nexport const safeEncode = /* @__PURE__ */ core._safeEncode(ZodRealError);\nexport const safeDecode = /* @__PURE__ */ core._safeDecode(ZodRealError);\nexport const safeEncodeAsync = /* @__PURE__ */ core._safeEncodeAsync(ZodRealError);\nexport const safeDecodeAsync = /* @__PURE__ */ core._safeDecodeAsync(ZodRealError);\n","import * as core from \"../core/index.js\";\nimport { util } from \"../core/index.js\";\nimport * as processors from \"../core/json-schema-processors.js\";\nimport { createStandardJSONSchemaMethod, createToJSONSchemaMethod } from \"../core/to-json-schema.js\";\nimport * as checks from \"./checks.js\";\nimport * as iso from \"./iso.js\";\nimport * as parse from \"./parse.js\";\nexport const ZodType = /*@__PURE__*/ core.$constructor(\"ZodType\", (inst, def) => {\n core.$ZodType.init(inst, def);\n Object.assign(inst[\"~standard\"], {\n jsonSchema: {\n input: createStandardJSONSchemaMethod(inst, \"input\"),\n output: createStandardJSONSchemaMethod(inst, \"output\"),\n },\n });\n inst.toJSONSchema = createToJSONSchemaMethod(inst, {});\n inst.def = def;\n inst.type = def.type;\n Object.defineProperty(inst, \"_def\", { value: def });\n // base methods\n inst.check = (...checks) => {\n return inst.clone(util.mergeDefs(def, {\n checks: [\n ...(def.checks ?? []),\n ...checks.map((ch) => typeof ch === \"function\" ? { _zod: { check: ch, def: { check: \"custom\" }, onattach: [] } } : ch),\n ],\n }), {\n parent: true,\n });\n };\n inst.with = inst.check;\n inst.clone = (def, params) => core.clone(inst, def, params);\n inst.brand = () => inst;\n inst.register = ((reg, meta) => {\n reg.add(inst, meta);\n return inst;\n });\n // parsing\n inst.parse = (data, params) => parse.parse(inst, data, params, { callee: inst.parse });\n inst.safeParse = (data, params) => parse.safeParse(inst, data, params);\n inst.parseAsync = async (data, params) => parse.parseAsync(inst, data, params, { callee: inst.parseAsync });\n inst.safeParseAsync = async (data, params) => parse.safeParseAsync(inst, data, params);\n inst.spa = inst.safeParseAsync;\n // encoding/decoding\n inst.encode = (data, params) => parse.encode(inst, data, params);\n inst.decode = (data, params) => parse.decode(inst, data, params);\n inst.encodeAsync = async (data, params) => parse.encodeAsync(inst, data, params);\n inst.decodeAsync = async (data, params) => parse.decodeAsync(inst, data, params);\n inst.safeEncode = (data, params) => parse.safeEncode(inst, data, params);\n inst.safeDecode = (data, params) => parse.safeDecode(inst, data, params);\n inst.safeEncodeAsync = async (data, params) => parse.safeEncodeAsync(inst, data, params);\n inst.safeDecodeAsync = async (data, params) => parse.safeDecodeAsync(inst, data, params);\n // refinements\n inst.refine = (check, params) => inst.check(refine(check, params));\n inst.superRefine = (refinement) => inst.check(superRefine(refinement));\n inst.overwrite = (fn) => inst.check(checks.overwrite(fn));\n // wrappers\n inst.optional = () => optional(inst);\n inst.exactOptional = () => exactOptional(inst);\n inst.nullable = () => nullable(inst);\n inst.nullish = () => optional(nullable(inst));\n inst.nonoptional = (params) => nonoptional(inst, params);\n inst.array = () => array(inst);\n inst.or = (arg) => union([inst, arg]);\n inst.and = (arg) => intersection(inst, arg);\n inst.transform = (tx) => pipe(inst, transform(tx));\n inst.default = (def) => _default(inst, def);\n inst.prefault = (def) => prefault(inst, def);\n // inst.coalesce = (def, params) => coalesce(inst, def, params);\n inst.catch = (params) => _catch(inst, params);\n inst.pipe = (target) => pipe(inst, target);\n inst.readonly = () => readonly(inst);\n // meta\n inst.describe = (description) => {\n const cl = inst.clone();\n core.globalRegistry.add(cl, { description });\n return cl;\n };\n Object.defineProperty(inst, \"description\", {\n get() {\n return core.globalRegistry.get(inst)?.description;\n },\n configurable: true,\n });\n inst.meta = (...args) => {\n if (args.length === 0) {\n return core.globalRegistry.get(inst);\n }\n const cl = inst.clone();\n core.globalRegistry.add(cl, args[0]);\n return cl;\n };\n // helpers\n inst.isOptional = () => inst.safeParse(undefined).success;\n inst.isNullable = () => inst.safeParse(null).success;\n inst.apply = (fn) => fn(inst);\n return inst;\n});\n/** @internal */\nexport const _ZodString = /*@__PURE__*/ core.$constructor(\"_ZodString\", (inst, def) => {\n core.$ZodString.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.stringProcessor(inst, ctx, json, params);\n const bag = inst._zod.bag;\n inst.format = bag.format ?? null;\n inst.minLength = bag.minimum ?? null;\n inst.maxLength = bag.maximum ?? null;\n // validations\n inst.regex = (...args) => inst.check(checks.regex(...args));\n inst.includes = (...args) => inst.check(checks.includes(...args));\n inst.startsWith = (...args) => inst.check(checks.startsWith(...args));\n inst.endsWith = (...args) => inst.check(checks.endsWith(...args));\n inst.min = (...args) => inst.check(checks.minLength(...args));\n inst.max = (...args) => inst.check(checks.maxLength(...args));\n inst.length = (...args) => inst.check(checks.length(...args));\n inst.nonempty = (...args) => inst.check(checks.minLength(1, ...args));\n inst.lowercase = (params) => inst.check(checks.lowercase(params));\n inst.uppercase = (params) => inst.check(checks.uppercase(params));\n // transforms\n inst.trim = () => inst.check(checks.trim());\n inst.normalize = (...args) => inst.check(checks.normalize(...args));\n inst.toLowerCase = () => inst.check(checks.toLowerCase());\n inst.toUpperCase = () => inst.check(checks.toUpperCase());\n inst.slugify = () => inst.check(checks.slugify());\n});\nexport const ZodString = /*@__PURE__*/ core.$constructor(\"ZodString\", (inst, def) => {\n core.$ZodString.init(inst, def);\n _ZodString.init(inst, def);\n inst.email = (params) => inst.check(core._email(ZodEmail, params));\n inst.url = (params) => inst.check(core._url(ZodURL, params));\n inst.jwt = (params) => inst.check(core._jwt(ZodJWT, params));\n inst.emoji = (params) => inst.check(core._emoji(ZodEmoji, params));\n inst.guid = (params) => inst.check(core._guid(ZodGUID, params));\n inst.uuid = (params) => inst.check(core._uuid(ZodUUID, params));\n inst.uuidv4 = (params) => inst.check(core._uuidv4(ZodUUID, params));\n inst.uuidv6 = (params) => inst.check(core._uuidv6(ZodUUID, params));\n inst.uuidv7 = (params) => inst.check(core._uuidv7(ZodUUID, params));\n inst.nanoid = (params) => inst.check(core._nanoid(ZodNanoID, params));\n inst.guid = (params) => inst.check(core._guid(ZodGUID, params));\n inst.cuid = (params) => inst.check(core._cuid(ZodCUID, params));\n inst.cuid2 = (params) => inst.check(core._cuid2(ZodCUID2, params));\n inst.ulid = (params) => inst.check(core._ulid(ZodULID, params));\n inst.base64 = (params) => inst.check(core._base64(ZodBase64, params));\n inst.base64url = (params) => inst.check(core._base64url(ZodBase64URL, params));\n inst.xid = (params) => inst.check(core._xid(ZodXID, params));\n inst.ksuid = (params) => inst.check(core._ksuid(ZodKSUID, params));\n inst.ipv4 = (params) => inst.check(core._ipv4(ZodIPv4, params));\n inst.ipv6 = (params) => inst.check(core._ipv6(ZodIPv6, params));\n inst.cidrv4 = (params) => inst.check(core._cidrv4(ZodCIDRv4, params));\n inst.cidrv6 = (params) => inst.check(core._cidrv6(ZodCIDRv6, params));\n inst.e164 = (params) => inst.check(core._e164(ZodE164, params));\n // iso\n inst.datetime = (params) => inst.check(iso.datetime(params));\n inst.date = (params) => inst.check(iso.date(params));\n inst.time = (params) => inst.check(iso.time(params));\n inst.duration = (params) => inst.check(iso.duration(params));\n});\nexport function string(params) {\n return core._string(ZodString, params);\n}\nexport const ZodStringFormat = /*@__PURE__*/ core.$constructor(\"ZodStringFormat\", (inst, def) => {\n core.$ZodStringFormat.init(inst, def);\n _ZodString.init(inst, def);\n});\nexport const ZodEmail = /*@__PURE__*/ core.$constructor(\"ZodEmail\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodEmail.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function email(params) {\n return core._email(ZodEmail, params);\n}\nexport const ZodGUID = /*@__PURE__*/ core.$constructor(\"ZodGUID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodGUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function guid(params) {\n return core._guid(ZodGUID, params);\n}\nexport const ZodUUID = /*@__PURE__*/ core.$constructor(\"ZodUUID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodUUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function uuid(params) {\n return core._uuid(ZodUUID, params);\n}\nexport function uuidv4(params) {\n return core._uuidv4(ZodUUID, params);\n}\n// ZodUUIDv6\nexport function uuidv6(params) {\n return core._uuidv6(ZodUUID, params);\n}\n// ZodUUIDv7\nexport function uuidv7(params) {\n return core._uuidv7(ZodUUID, params);\n}\nexport const ZodURL = /*@__PURE__*/ core.$constructor(\"ZodURL\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodURL.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function url(params) {\n return core._url(ZodURL, params);\n}\nexport function httpUrl(params) {\n return core._url(ZodURL, {\n protocol: /^https?$/,\n hostname: core.regexes.domain,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodEmoji = /*@__PURE__*/ core.$constructor(\"ZodEmoji\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodEmoji.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function emoji(params) {\n return core._emoji(ZodEmoji, params);\n}\nexport const ZodNanoID = /*@__PURE__*/ core.$constructor(\"ZodNanoID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodNanoID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function nanoid(params) {\n return core._nanoid(ZodNanoID, params);\n}\nexport const ZodCUID = /*@__PURE__*/ core.$constructor(\"ZodCUID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodCUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function cuid(params) {\n return core._cuid(ZodCUID, params);\n}\nexport const ZodCUID2 = /*@__PURE__*/ core.$constructor(\"ZodCUID2\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodCUID2.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function cuid2(params) {\n return core._cuid2(ZodCUID2, params);\n}\nexport const ZodULID = /*@__PURE__*/ core.$constructor(\"ZodULID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodULID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function ulid(params) {\n return core._ulid(ZodULID, params);\n}\nexport const ZodXID = /*@__PURE__*/ core.$constructor(\"ZodXID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodXID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function xid(params) {\n return core._xid(ZodXID, params);\n}\nexport const ZodKSUID = /*@__PURE__*/ core.$constructor(\"ZodKSUID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodKSUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function ksuid(params) {\n return core._ksuid(ZodKSUID, params);\n}\nexport const ZodIPv4 = /*@__PURE__*/ core.$constructor(\"ZodIPv4\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodIPv4.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function ipv4(params) {\n return core._ipv4(ZodIPv4, params);\n}\nexport const ZodMAC = /*@__PURE__*/ core.$constructor(\"ZodMAC\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodMAC.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function mac(params) {\n return core._mac(ZodMAC, params);\n}\nexport const ZodIPv6 = /*@__PURE__*/ core.$constructor(\"ZodIPv6\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodIPv6.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function ipv6(params) {\n return core._ipv6(ZodIPv6, params);\n}\nexport const ZodCIDRv4 = /*@__PURE__*/ core.$constructor(\"ZodCIDRv4\", (inst, def) => {\n core.$ZodCIDRv4.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function cidrv4(params) {\n return core._cidrv4(ZodCIDRv4, params);\n}\nexport const ZodCIDRv6 = /*@__PURE__*/ core.$constructor(\"ZodCIDRv6\", (inst, def) => {\n core.$ZodCIDRv6.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function cidrv6(params) {\n return core._cidrv6(ZodCIDRv6, params);\n}\nexport const ZodBase64 = /*@__PURE__*/ core.$constructor(\"ZodBase64\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodBase64.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function base64(params) {\n return core._base64(ZodBase64, params);\n}\nexport const ZodBase64URL = /*@__PURE__*/ core.$constructor(\"ZodBase64URL\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodBase64URL.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function base64url(params) {\n return core._base64url(ZodBase64URL, params);\n}\nexport const ZodE164 = /*@__PURE__*/ core.$constructor(\"ZodE164\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodE164.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function e164(params) {\n return core._e164(ZodE164, params);\n}\nexport const ZodJWT = /*@__PURE__*/ core.$constructor(\"ZodJWT\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodJWT.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function jwt(params) {\n return core._jwt(ZodJWT, params);\n}\nexport const ZodCustomStringFormat = /*@__PURE__*/ core.$constructor(\"ZodCustomStringFormat\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodCustomStringFormat.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function stringFormat(format, fnOrRegex, _params = {}) {\n return core._stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);\n}\nexport function hostname(_params) {\n return core._stringFormat(ZodCustomStringFormat, \"hostname\", core.regexes.hostname, _params);\n}\nexport function hex(_params) {\n return core._stringFormat(ZodCustomStringFormat, \"hex\", core.regexes.hex, _params);\n}\nexport function hash(alg, params) {\n const enc = params?.enc ?? \"hex\";\n const format = `${alg}_${enc}`;\n const regex = core.regexes[format];\n if (!regex)\n throw new Error(`Unrecognized hash format: ${format}`);\n return core._stringFormat(ZodCustomStringFormat, format, regex, params);\n}\nexport const ZodNumber = /*@__PURE__*/ core.$constructor(\"ZodNumber\", (inst, def) => {\n core.$ZodNumber.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.numberProcessor(inst, ctx, json, params);\n inst.gt = (value, params) => inst.check(checks.gt(value, params));\n inst.gte = (value, params) => inst.check(checks.gte(value, params));\n inst.min = (value, params) => inst.check(checks.gte(value, params));\n inst.lt = (value, params) => inst.check(checks.lt(value, params));\n inst.lte = (value, params) => inst.check(checks.lte(value, params));\n inst.max = (value, params) => inst.check(checks.lte(value, params));\n inst.int = (params) => inst.check(int(params));\n inst.safe = (params) => inst.check(int(params));\n inst.positive = (params) => inst.check(checks.gt(0, params));\n inst.nonnegative = (params) => inst.check(checks.gte(0, params));\n inst.negative = (params) => inst.check(checks.lt(0, params));\n inst.nonpositive = (params) => inst.check(checks.lte(0, params));\n inst.multipleOf = (value, params) => inst.check(checks.multipleOf(value, params));\n inst.step = (value, params) => inst.check(checks.multipleOf(value, params));\n // inst.finite = (params) => inst.check(core.finite(params));\n inst.finite = () => inst;\n const bag = inst._zod.bag;\n inst.minValue =\n Math.max(bag.minimum ?? Number.NEGATIVE_INFINITY, bag.exclusiveMinimum ?? Number.NEGATIVE_INFINITY) ?? null;\n inst.maxValue =\n Math.min(bag.maximum ?? Number.POSITIVE_INFINITY, bag.exclusiveMaximum ?? Number.POSITIVE_INFINITY) ?? null;\n inst.isInt = (bag.format ?? \"\").includes(\"int\") || Number.isSafeInteger(bag.multipleOf ?? 0.5);\n inst.isFinite = true;\n inst.format = bag.format ?? null;\n});\nexport function number(params) {\n return core._number(ZodNumber, params);\n}\nexport const ZodNumberFormat = /*@__PURE__*/ core.$constructor(\"ZodNumberFormat\", (inst, def) => {\n core.$ZodNumberFormat.init(inst, def);\n ZodNumber.init(inst, def);\n});\nexport function int(params) {\n return core._int(ZodNumberFormat, params);\n}\nexport function float32(params) {\n return core._float32(ZodNumberFormat, params);\n}\nexport function float64(params) {\n return core._float64(ZodNumberFormat, params);\n}\nexport function int32(params) {\n return core._int32(ZodNumberFormat, params);\n}\nexport function uint32(params) {\n return core._uint32(ZodNumberFormat, params);\n}\nexport const ZodBoolean = /*@__PURE__*/ core.$constructor(\"ZodBoolean\", (inst, def) => {\n core.$ZodBoolean.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.booleanProcessor(inst, ctx, json, params);\n});\nexport function boolean(params) {\n return core._boolean(ZodBoolean, params);\n}\nexport const ZodBigInt = /*@__PURE__*/ core.$constructor(\"ZodBigInt\", (inst, def) => {\n core.$ZodBigInt.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.bigintProcessor(inst, ctx, json, params);\n inst.gte = (value, params) => inst.check(checks.gte(value, params));\n inst.min = (value, params) => inst.check(checks.gte(value, params));\n inst.gt = (value, params) => inst.check(checks.gt(value, params));\n inst.gte = (value, params) => inst.check(checks.gte(value, params));\n inst.min = (value, params) => inst.check(checks.gte(value, params));\n inst.lt = (value, params) => inst.check(checks.lt(value, params));\n inst.lte = (value, params) => inst.check(checks.lte(value, params));\n inst.max = (value, params) => inst.check(checks.lte(value, params));\n inst.positive = (params) => inst.check(checks.gt(BigInt(0), params));\n inst.negative = (params) => inst.check(checks.lt(BigInt(0), params));\n inst.nonpositive = (params) => inst.check(checks.lte(BigInt(0), params));\n inst.nonnegative = (params) => inst.check(checks.gte(BigInt(0), params));\n inst.multipleOf = (value, params) => inst.check(checks.multipleOf(value, params));\n const bag = inst._zod.bag;\n inst.minValue = bag.minimum ?? null;\n inst.maxValue = bag.maximum ?? null;\n inst.format = bag.format ?? null;\n});\nexport function bigint(params) {\n return core._bigint(ZodBigInt, params);\n}\nexport const ZodBigIntFormat = /*@__PURE__*/ core.$constructor(\"ZodBigIntFormat\", (inst, def) => {\n core.$ZodBigIntFormat.init(inst, def);\n ZodBigInt.init(inst, def);\n});\n// int64\nexport function int64(params) {\n return core._int64(ZodBigIntFormat, params);\n}\n// uint64\nexport function uint64(params) {\n return core._uint64(ZodBigIntFormat, params);\n}\nexport const ZodSymbol = /*@__PURE__*/ core.$constructor(\"ZodSymbol\", (inst, def) => {\n core.$ZodSymbol.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.symbolProcessor(inst, ctx, json, params);\n});\nexport function symbol(params) {\n return core._symbol(ZodSymbol, params);\n}\nexport const ZodUndefined = /*@__PURE__*/ core.$constructor(\"ZodUndefined\", (inst, def) => {\n core.$ZodUndefined.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.undefinedProcessor(inst, ctx, json, params);\n});\nfunction _undefined(params) {\n return core._undefined(ZodUndefined, params);\n}\nexport { _undefined as undefined };\nexport const ZodNull = /*@__PURE__*/ core.$constructor(\"ZodNull\", (inst, def) => {\n core.$ZodNull.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.nullProcessor(inst, ctx, json, params);\n});\nfunction _null(params) {\n return core._null(ZodNull, params);\n}\nexport { _null as null };\nexport const ZodAny = /*@__PURE__*/ core.$constructor(\"ZodAny\", (inst, def) => {\n core.$ZodAny.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.anyProcessor(inst, ctx, json, params);\n});\nexport function any() {\n return core._any(ZodAny);\n}\nexport const ZodUnknown = /*@__PURE__*/ core.$constructor(\"ZodUnknown\", (inst, def) => {\n core.$ZodUnknown.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.unknownProcessor(inst, ctx, json, params);\n});\nexport function unknown() {\n return core._unknown(ZodUnknown);\n}\nexport const ZodNever = /*@__PURE__*/ core.$constructor(\"ZodNever\", (inst, def) => {\n core.$ZodNever.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.neverProcessor(inst, ctx, json, params);\n});\nexport function never(params) {\n return core._never(ZodNever, params);\n}\nexport const ZodVoid = /*@__PURE__*/ core.$constructor(\"ZodVoid\", (inst, def) => {\n core.$ZodVoid.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.voidProcessor(inst, ctx, json, params);\n});\nfunction _void(params) {\n return core._void(ZodVoid, params);\n}\nexport { _void as void };\nexport const ZodDate = /*@__PURE__*/ core.$constructor(\"ZodDate\", (inst, def) => {\n core.$ZodDate.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.dateProcessor(inst, ctx, json, params);\n inst.min = (value, params) => inst.check(checks.gte(value, params));\n inst.max = (value, params) => inst.check(checks.lte(value, params));\n const c = inst._zod.bag;\n inst.minDate = c.minimum ? new Date(c.minimum) : null;\n inst.maxDate = c.maximum ? new Date(c.maximum) : null;\n});\nexport function date(params) {\n return core._date(ZodDate, params);\n}\nexport const ZodArray = /*@__PURE__*/ core.$constructor(\"ZodArray\", (inst, def) => {\n core.$ZodArray.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.arrayProcessor(inst, ctx, json, params);\n inst.element = def.element;\n inst.min = (minLength, params) => inst.check(checks.minLength(minLength, params));\n inst.nonempty = (params) => inst.check(checks.minLength(1, params));\n inst.max = (maxLength, params) => inst.check(checks.maxLength(maxLength, params));\n inst.length = (len, params) => inst.check(checks.length(len, params));\n inst.unwrap = () => inst.element;\n});\nexport function array(element, params) {\n return core._array(ZodArray, element, params);\n}\n// .keyof\nexport function keyof(schema) {\n const shape = schema._zod.def.shape;\n return _enum(Object.keys(shape));\n}\nexport const ZodObject = /*@__PURE__*/ core.$constructor(\"ZodObject\", (inst, def) => {\n core.$ZodObjectJIT.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.objectProcessor(inst, ctx, json, params);\n util.defineLazy(inst, \"shape\", () => {\n return def.shape;\n });\n inst.keyof = () => _enum(Object.keys(inst._zod.def.shape));\n inst.catchall = (catchall) => inst.clone({ ...inst._zod.def, catchall: catchall });\n inst.passthrough = () => inst.clone({ ...inst._zod.def, catchall: unknown() });\n inst.loose = () => inst.clone({ ...inst._zod.def, catchall: unknown() });\n inst.strict = () => inst.clone({ ...inst._zod.def, catchall: never() });\n inst.strip = () => inst.clone({ ...inst._zod.def, catchall: undefined });\n inst.extend = (incoming) => {\n return util.extend(inst, incoming);\n };\n inst.safeExtend = (incoming) => {\n return util.safeExtend(inst, incoming);\n };\n inst.merge = (other) => util.merge(inst, other);\n inst.pick = (mask) => util.pick(inst, mask);\n inst.omit = (mask) => util.omit(inst, mask);\n inst.partial = (...args) => util.partial(ZodOptional, inst, args[0]);\n inst.required = (...args) => util.required(ZodNonOptional, inst, args[0]);\n});\nexport function object(shape, params) {\n const def = {\n type: \"object\",\n shape: shape ?? {},\n ...util.normalizeParams(params),\n };\n return new ZodObject(def);\n}\n// strictObject\nexport function strictObject(shape, params) {\n return new ZodObject({\n type: \"object\",\n shape,\n catchall: never(),\n ...util.normalizeParams(params),\n });\n}\n// looseObject\nexport function looseObject(shape, params) {\n return new ZodObject({\n type: \"object\",\n shape,\n catchall: unknown(),\n ...util.normalizeParams(params),\n });\n}\nexport const ZodUnion = /*@__PURE__*/ core.$constructor(\"ZodUnion\", (inst, def) => {\n core.$ZodUnion.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.unionProcessor(inst, ctx, json, params);\n inst.options = def.options;\n});\nexport function union(options, params) {\n return new ZodUnion({\n type: \"union\",\n options: options,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodXor = /*@__PURE__*/ core.$constructor(\"ZodXor\", (inst, def) => {\n ZodUnion.init(inst, def);\n core.$ZodXor.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.unionProcessor(inst, ctx, json, params);\n inst.options = def.options;\n});\n/** Creates an exclusive union (XOR) where exactly one option must match.\n * Unlike regular unions that succeed when any option matches, xor fails if\n * zero or more than one option matches the input. */\nexport function xor(options, params) {\n return new ZodXor({\n type: \"union\",\n options: options,\n inclusive: false,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodDiscriminatedUnion = /*@__PURE__*/ core.$constructor(\"ZodDiscriminatedUnion\", (inst, def) => {\n ZodUnion.init(inst, def);\n core.$ZodDiscriminatedUnion.init(inst, def);\n});\nexport function discriminatedUnion(discriminator, options, params) {\n // const [options, params] = args;\n return new ZodDiscriminatedUnion({\n type: \"union\",\n options,\n discriminator,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodIntersection = /*@__PURE__*/ core.$constructor(\"ZodIntersection\", (inst, def) => {\n core.$ZodIntersection.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.intersectionProcessor(inst, ctx, json, params);\n});\nexport function intersection(left, right) {\n return new ZodIntersection({\n type: \"intersection\",\n left: left,\n right: right,\n });\n}\nexport const ZodTuple = /*@__PURE__*/ core.$constructor(\"ZodTuple\", (inst, def) => {\n core.$ZodTuple.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.tupleProcessor(inst, ctx, json, params);\n inst.rest = (rest) => inst.clone({\n ...inst._zod.def,\n rest: rest,\n });\n});\nexport function tuple(items, _paramsOrRest, _params) {\n const hasRest = _paramsOrRest instanceof core.$ZodType;\n const params = hasRest ? _params : _paramsOrRest;\n const rest = hasRest ? _paramsOrRest : null;\n return new ZodTuple({\n type: \"tuple\",\n items: items,\n rest,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodRecord = /*@__PURE__*/ core.$constructor(\"ZodRecord\", (inst, def) => {\n core.$ZodRecord.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.recordProcessor(inst, ctx, json, params);\n inst.keyType = def.keyType;\n inst.valueType = def.valueType;\n});\nexport function record(keyType, valueType, params) {\n return new ZodRecord({\n type: \"record\",\n keyType,\n valueType: valueType,\n ...util.normalizeParams(params),\n });\n}\n// type alksjf = core.output<core.$ZodRecordKey>;\nexport function partialRecord(keyType, valueType, params) {\n const k = core.clone(keyType);\n k._zod.values = undefined;\n return new ZodRecord({\n type: \"record\",\n keyType: k,\n valueType: valueType,\n ...util.normalizeParams(params),\n });\n}\nexport function looseRecord(keyType, valueType, params) {\n return new ZodRecord({\n type: \"record\",\n keyType,\n valueType: valueType,\n mode: \"loose\",\n ...util.normalizeParams(params),\n });\n}\nexport const ZodMap = /*@__PURE__*/ core.$constructor(\"ZodMap\", (inst, def) => {\n core.$ZodMap.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.mapProcessor(inst, ctx, json, params);\n inst.keyType = def.keyType;\n inst.valueType = def.valueType;\n inst.min = (...args) => inst.check(core._minSize(...args));\n inst.nonempty = (params) => inst.check(core._minSize(1, params));\n inst.max = (...args) => inst.check(core._maxSize(...args));\n inst.size = (...args) => inst.check(core._size(...args));\n});\nexport function map(keyType, valueType, params) {\n return new ZodMap({\n type: \"map\",\n keyType: keyType,\n valueType: valueType,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodSet = /*@__PURE__*/ core.$constructor(\"ZodSet\", (inst, def) => {\n core.$ZodSet.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.setProcessor(inst, ctx, json, params);\n inst.min = (...args) => inst.check(core._minSize(...args));\n inst.nonempty = (params) => inst.check(core._minSize(1, params));\n inst.max = (...args) => inst.check(core._maxSize(...args));\n inst.size = (...args) => inst.check(core._size(...args));\n});\nexport function set(valueType, params) {\n return new ZodSet({\n type: \"set\",\n valueType: valueType,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodEnum = /*@__PURE__*/ core.$constructor(\"ZodEnum\", (inst, def) => {\n core.$ZodEnum.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.enumProcessor(inst, ctx, json, params);\n inst.enum = def.entries;\n inst.options = Object.values(def.entries);\n const keys = new Set(Object.keys(def.entries));\n inst.extract = (values, params) => {\n const newEntries = {};\n for (const value of values) {\n if (keys.has(value)) {\n newEntries[value] = def.entries[value];\n }\n else\n throw new Error(`Key ${value} not found in enum`);\n }\n return new ZodEnum({\n ...def,\n checks: [],\n ...util.normalizeParams(params),\n entries: newEntries,\n });\n };\n inst.exclude = (values, params) => {\n const newEntries = { ...def.entries };\n for (const value of values) {\n if (keys.has(value)) {\n delete newEntries[value];\n }\n else\n throw new Error(`Key ${value} not found in enum`);\n }\n return new ZodEnum({\n ...def,\n checks: [],\n ...util.normalizeParams(params),\n entries: newEntries,\n });\n };\n});\nfunction _enum(values, params) {\n const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;\n return new ZodEnum({\n type: \"enum\",\n entries,\n ...util.normalizeParams(params),\n });\n}\nexport { _enum as enum };\n/** @deprecated This API has been merged into `z.enum()`. Use `z.enum()` instead.\n *\n * ```ts\n * enum Colors { red, green, blue }\n * z.enum(Colors);\n * ```\n */\nexport function nativeEnum(entries, params) {\n return new ZodEnum({\n type: \"enum\",\n entries,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodLiteral = /*@__PURE__*/ core.$constructor(\"ZodLiteral\", (inst, def) => {\n core.$ZodLiteral.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.literalProcessor(inst, ctx, json, params);\n inst.values = new Set(def.values);\n Object.defineProperty(inst, \"value\", {\n get() {\n if (def.values.length > 1) {\n throw new Error(\"This schema contains multiple valid literal values. Use `.values` instead.\");\n }\n return def.values[0];\n },\n });\n});\nexport function literal(value, params) {\n return new ZodLiteral({\n type: \"literal\",\n values: Array.isArray(value) ? value : [value],\n ...util.normalizeParams(params),\n });\n}\nexport const ZodFile = /*@__PURE__*/ core.$constructor(\"ZodFile\", (inst, def) => {\n core.$ZodFile.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.fileProcessor(inst, ctx, json, params);\n inst.min = (size, params) => inst.check(core._minSize(size, params));\n inst.max = (size, params) => inst.check(core._maxSize(size, params));\n inst.mime = (types, params) => inst.check(core._mime(Array.isArray(types) ? types : [types], params));\n});\nexport function file(params) {\n return core._file(ZodFile, params);\n}\nexport const ZodTransform = /*@__PURE__*/ core.$constructor(\"ZodTransform\", (inst, def) => {\n core.$ZodTransform.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.transformProcessor(inst, ctx, json, params);\n inst._zod.parse = (payload, _ctx) => {\n if (_ctx.direction === \"backward\") {\n throw new core.$ZodEncodeError(inst.constructor.name);\n }\n payload.addIssue = (issue) => {\n if (typeof issue === \"string\") {\n payload.issues.push(util.issue(issue, payload.value, def));\n }\n else {\n // for Zod 3 backwards compatibility\n const _issue = issue;\n if (_issue.fatal)\n _issue.continue = false;\n _issue.code ?? (_issue.code = \"custom\");\n _issue.input ?? (_issue.input = payload.value);\n _issue.inst ?? (_issue.inst = inst);\n // _issue.continue ??= true;\n payload.issues.push(util.issue(_issue));\n }\n };\n const output = def.transform(payload.value, payload);\n if (output instanceof Promise) {\n return output.then((output) => {\n payload.value = output;\n return payload;\n });\n }\n payload.value = output;\n return payload;\n };\n});\nexport function transform(fn) {\n return new ZodTransform({\n type: \"transform\",\n transform: fn,\n });\n}\nexport const ZodOptional = /*@__PURE__*/ core.$constructor(\"ZodOptional\", (inst, def) => {\n core.$ZodOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.optionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function optional(innerType) {\n return new ZodOptional({\n type: \"optional\",\n innerType: innerType,\n });\n}\nexport const ZodExactOptional = /*@__PURE__*/ core.$constructor(\"ZodExactOptional\", (inst, def) => {\n core.$ZodExactOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.optionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function exactOptional(innerType) {\n return new ZodExactOptional({\n type: \"optional\",\n innerType: innerType,\n });\n}\nexport const ZodNullable = /*@__PURE__*/ core.$constructor(\"ZodNullable\", (inst, def) => {\n core.$ZodNullable.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.nullableProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function nullable(innerType) {\n return new ZodNullable({\n type: \"nullable\",\n innerType: innerType,\n });\n}\n// nullish\nexport function nullish(innerType) {\n return optional(nullable(innerType));\n}\nexport const ZodDefault = /*@__PURE__*/ core.$constructor(\"ZodDefault\", (inst, def) => {\n core.$ZodDefault.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.defaultProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n inst.removeDefault = inst.unwrap;\n});\nexport function _default(innerType, defaultValue) {\n return new ZodDefault({\n type: \"default\",\n innerType: innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : util.shallowClone(defaultValue);\n },\n });\n}\nexport const ZodPrefault = /*@__PURE__*/ core.$constructor(\"ZodPrefault\", (inst, def) => {\n core.$ZodPrefault.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.prefaultProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function prefault(innerType, defaultValue) {\n return new ZodPrefault({\n type: \"prefault\",\n innerType: innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : util.shallowClone(defaultValue);\n },\n });\n}\nexport const ZodNonOptional = /*@__PURE__*/ core.$constructor(\"ZodNonOptional\", (inst, def) => {\n core.$ZodNonOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.nonoptionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function nonoptional(innerType, params) {\n return new ZodNonOptional({\n type: \"nonoptional\",\n innerType: innerType,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodSuccess = /*@__PURE__*/ core.$constructor(\"ZodSuccess\", (inst, def) => {\n core.$ZodSuccess.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.successProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function success(innerType) {\n return new ZodSuccess({\n type: \"success\",\n innerType: innerType,\n });\n}\nexport const ZodCatch = /*@__PURE__*/ core.$constructor(\"ZodCatch\", (inst, def) => {\n core.$ZodCatch.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.catchProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n inst.removeCatch = inst.unwrap;\n});\nfunction _catch(innerType, catchValue) {\n return new ZodCatch({\n type: \"catch\",\n innerType: innerType,\n catchValue: (typeof catchValue === \"function\" ? catchValue : () => catchValue),\n });\n}\nexport { _catch as catch };\nexport const ZodNaN = /*@__PURE__*/ core.$constructor(\"ZodNaN\", (inst, def) => {\n core.$ZodNaN.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.nanProcessor(inst, ctx, json, params);\n});\nexport function nan(params) {\n return core._nan(ZodNaN, params);\n}\nexport const ZodPipe = /*@__PURE__*/ core.$constructor(\"ZodPipe\", (inst, def) => {\n core.$ZodPipe.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.pipeProcessor(inst, ctx, json, params);\n inst.in = def.in;\n inst.out = def.out;\n});\nexport function pipe(in_, out) {\n return new ZodPipe({\n type: \"pipe\",\n in: in_,\n out: out,\n // ...util.normalizeParams(params),\n });\n}\nexport const ZodCodec = /*@__PURE__*/ core.$constructor(\"ZodCodec\", (inst, def) => {\n ZodPipe.init(inst, def);\n core.$ZodCodec.init(inst, def);\n});\nexport function codec(in_, out, params) {\n return new ZodCodec({\n type: \"pipe\",\n in: in_,\n out: out,\n transform: params.decode,\n reverseTransform: params.encode,\n });\n}\nexport const ZodReadonly = /*@__PURE__*/ core.$constructor(\"ZodReadonly\", (inst, def) => {\n core.$ZodReadonly.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.readonlyProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function readonly(innerType) {\n return new ZodReadonly({\n type: \"readonly\",\n innerType: innerType,\n });\n}\nexport const ZodTemplateLiteral = /*@__PURE__*/ core.$constructor(\"ZodTemplateLiteral\", (inst, def) => {\n core.$ZodTemplateLiteral.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.templateLiteralProcessor(inst, ctx, json, params);\n});\nexport function templateLiteral(parts, params) {\n return new ZodTemplateLiteral({\n type: \"template_literal\",\n parts,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodLazy = /*@__PURE__*/ core.$constructor(\"ZodLazy\", (inst, def) => {\n core.$ZodLazy.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.lazyProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.getter();\n});\nexport function lazy(getter) {\n return new ZodLazy({\n type: \"lazy\",\n getter: getter,\n });\n}\nexport const ZodPromise = /*@__PURE__*/ core.$constructor(\"ZodPromise\", (inst, def) => {\n core.$ZodPromise.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.promiseProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function promise(innerType) {\n return new ZodPromise({\n type: \"promise\",\n innerType: innerType,\n });\n}\nexport const ZodFunction = /*@__PURE__*/ core.$constructor(\"ZodFunction\", (inst, def) => {\n core.$ZodFunction.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.functionProcessor(inst, ctx, json, params);\n});\nexport function _function(params) {\n return new ZodFunction({\n type: \"function\",\n input: Array.isArray(params?.input) ? tuple(params?.input) : (params?.input ?? array(unknown())),\n output: params?.output ?? unknown(),\n });\n}\nexport { _function as function };\nexport const ZodCustom = /*@__PURE__*/ core.$constructor(\"ZodCustom\", (inst, def) => {\n core.$ZodCustom.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.customProcessor(inst, ctx, json, params);\n});\n// custom checks\nexport function check(fn) {\n const ch = new core.$ZodCheck({\n check: \"custom\",\n // ...util.normalizeParams(params),\n });\n ch._zod.check = fn;\n return ch;\n}\nexport function custom(fn, _params) {\n return core._custom(ZodCustom, fn ?? (() => true), _params);\n}\nexport function refine(fn, _params = {}) {\n return core._refine(ZodCustom, fn, _params);\n}\n// superRefine\nexport function superRefine(fn) {\n return core._superRefine(fn);\n}\n// Re-export describe and meta from core\nexport const describe = core.describe;\nexport const meta = core.meta;\nfunction _instanceof(cls, params = {}) {\n const inst = new ZodCustom({\n type: \"custom\",\n check: \"custom\",\n fn: (data) => data instanceof cls,\n abort: true,\n ...util.normalizeParams(params),\n });\n inst._zod.bag.Class = cls;\n // Override check to emit invalid_type instead of custom\n inst._zod.check = (payload) => {\n if (!(payload.value instanceof cls)) {\n payload.issues.push({\n code: \"invalid_type\",\n expected: cls.name,\n input: payload.value,\n inst,\n path: [...(inst._zod.def.path ?? [])],\n });\n }\n };\n return inst;\n}\nexport { _instanceof as instanceof };\n// stringbool\nexport const stringbool = (...args) => core._stringbool({\n Codec: ZodCodec,\n Boolean: ZodBoolean,\n String: ZodString,\n}, ...args);\nexport function json(params) {\n const jsonSchema = lazy(() => {\n return union([string(params), number(), boolean(), _null(), array(jsonSchema), record(string(), jsonSchema)]);\n });\n return jsonSchema;\n}\n// preprocess\n// /** @deprecated Use `z.pipe()` and `z.transform()` instead. */\nexport function preprocess(fn, schema) {\n return pipe(transform(fn), schema);\n}\n","/**\n * candidate-reader.ts —— RFC-318 M4a:宿主侧读取插件产出的技能候选。\n *\n * **这是 R3 的唯一执行点**。插件(跑在隔离 worker 里)把候选写进自己的 plugin-state;\n * 本模块是它们进入主 agent 上下文的唯一通道,因此这里的三道闸决定了整条链的安全性:\n *\n * ① 显式来源白名单 —— 只读列名插件的目录;\n * ② zod 逐条校验 —— 不合 schema 的**单条**丢弃(不是整批,一条脏数据不该让全部候选消失);\n * ③ 元信息投影 —— 对外只产结构化字段,插件的任何额外字段物理上出不来。\n *\n * **诚实定性**(RFC-318 D5 已记录):这仍是一条**受控注入通道**,不是\"零注入\"——\n * taskSignature 等值最终会进入 grill 简报、被模型读到。三道闸把它约束成\"受长度与\n * schema 限制的结构化值\",而不是自由指令段;简报侧还会声明来源(见 candidate-brief.ts)。\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { z } from 'zod'\n\nimport { pluginStateDirFor } from '../plugin/plugin-scoped-storage'\n\n/**\n * 允许被薄读取器消费的插件 id —— **显式白名单,不按 scope 推断**。\n *\n * 为什么不用 `DiscoveredPlugin.scope === 'builtin'`:那个 scope 目前是硬编码的两条内置\n * 身份牌(otto-plugin-manager / otto-tui),skill-inductor 并不在其中,它是 extensions/\n * 下的普通插件。而\"任何第三方插件写个同名 candidates.json 就能把内容送进主 agent 上下文\"\n * 正是 R3 要堵的攻击面。显式常量是这里唯一诚实的判据——新增来源必须改代码、过 review。\n */\nexport const CANDIDATE_SOURCE_ALLOWLIST: readonly string[] = ['plugin-skill-inductor']\n\n/** 候选记录的文件名(对齐插件侧 `CANDIDATE_RECORD_ID = 'candidates'`)。 */\nconst CANDIDATE_FILE = 'candidates.json'\n\n/**\n * 单条候选的 schema。长度上限即注入面的物理约束——插件塞进来的任何文本都被截断在这些\n * 界限内,不可能成为一大段自由指令。\n */\nconst SkillCandidateSchema = z.object({\n id: z.string().min(1).max(64),\n taskSignature: z.string().min(1).max(200),\n tags: z.array(z.string().max(32)).max(5),\n sessionIds: z.array(z.string().max(64)).max(50),\n size: z.number().int().min(1).max(1000),\n firstAt: z.number().int().nonnegative(),\n lastAt: z.number().int().nonnegative(),\n dismissedAt: z.number().int().nonnegative().optional(),\n})\n\n/**\n * 对外投影:**只有这些字段能离开本模块**。\n * 即便插件在文件里塞了额外字段,zod 的 strip 行为加上本类型都保证它出不来。\n */\nexport interface CandidateBrief {\n id: string\n taskSignature: string\n tags: string[]\n sessionIds: string[]\n size: number\n firstAt: number\n lastAt: number\n /** 用户拒绝时刻(冷却判定用;不进简报文本)。 */\n dismissedAt?: number\n}\n\n/**\n * 候选文件的绝对路径。\n *\n * `pluginId` 的 sanitize 复用 `pluginStateDirFor`(kebab-case 白名单);`workspaceKey`\n * 在此**独立校验**——`PluginScopedStorage` 的构造器对它有同样的检查(`plugin-scoped-storage.ts:81`),\n * 读侧不做就成了写严读松的不对称。虽然当前调用方是宿主自己(非用户/插件输入),但路径\n * 拼接函数不该依赖\"调用方一定传干净的值\"这种约定。\n */\nexport function candidateFilePath(pluginId: string, workspaceKey: string): string {\n if (\n typeof workspaceKey !== 'string' ||\n workspaceKey.length === 0 ||\n workspaceKey.includes('/') ||\n workspaceKey.includes('\\\\') ||\n workspaceKey.includes('..')\n ) {\n throw new Error(`invalid workspaceKey for candidate file: ${workspaceKey}`)\n }\n return join(pluginStateDirFor(pluginId), workspaceKey, CANDIDATE_FILE)\n}\n\n/**\n * 读取某插件产出的候选。\n *\n * **fail-closed(R4)**:来源不在白名单 / 文件不存在 / JSON 损坏 / 信封结构不符 → 返回\n * 空数组,**绝不抛**。读候选是旁路能力,任何异常都不该影响会话主流程。\n */\nexport function readCandidates(input: { pluginId: string; workspaceKey: string }): CandidateBrief[] {\n if (!CANDIDATE_SOURCE_ALLOWLIST.includes(input.pluginId)) return []\n\n let raw: string\n try {\n const file = candidateFilePath(input.pluginId, input.workspaceKey)\n if (!existsSync(file)) return []\n raw = readFileSync(file, 'utf8')\n } catch {\n return []\n }\n\n let items: unknown\n try {\n // 插件 scoped storage 的信封是 `{version, id, data, metadata}`,真实内容在 data。\n const envelope = JSON.parse(raw) as { data?: { items?: unknown } }\n items = envelope?.data?.items\n } catch {\n return []\n }\n if (!Array.isArray(items)) return []\n\n // 逐条校验:脏的丢那一条,好的照常返回。整批丢弃会让一条坏数据淹掉全部候选。\n const briefs: CandidateBrief[] = []\n for (const item of items) {\n const parsed = SkillCandidateSchema.safeParse(item)\n if (!parsed.success) continue\n briefs.push(parsed.data)\n }\n return briefs\n}\n","/**\n * candidate-proposal.ts —— RFC-318 M4a:向用户提案,并把决定写回。\n *\n * 回路里第一次真正与用户对话的环节。两个安全要点:\n * ① 推荐方向固定为拒绝(非交互模式自动采纳推荐,见 candidate-brief.ts 的说明);\n * ② 用户拒绝必须**持久化**,否则下一轮抽取又会把同一个候选提出来(R9)。\n */\n\nimport { readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs'\n\nimport {\n ACCEPT_OPTION_LABEL,\n renderBackground,\n renderOptions,\n renderQuestion,\n renderRecommendation,\n} from './candidate-brief'\nimport { candidateFilePath } from './candidate-reader'\nimport { isShadowModeEnabled } from '@x-otto/env'\n\nimport type { CandidateBrief } from './candidate-reader'\n\n/** grill 应答的最小结构(只取判定需要的字段,不绑定完整 GrillAnswer 类型)。 */\nexport interface ProposalAnswer {\n answer: string\n acceptedRecommendation?: boolean\n selectedOptionLabels?: string[]\n resolvedBy?: string\n}\n\nexport interface ProposeDeps {\n requestGrill: (q: {\n header: string\n question: string\n background: string\n recommendation: string\n options: { label: string; rationale: string; recommended?: boolean }[]\n askedBy: string\n }) => Promise<ProposalAnswer>\n brief: CandidateBrief\n pluginId: string\n workspaceKey: string\n now: number\n logger?: { warn: (m: string) => void }\n}\n\nexport type ProposalOutcome = 'accepted' | 'dismissed' | 'error'\n\n/**\n * 判定用户是否同意生成。\n *\n * **默认拒绝**:只有明确选中了同意项才算同意。自由文本、超时自动采纳推荐、异常应答——\n * 一律按拒绝处理。这条判定的方向不能反过来(\"没明确拒绝就当同意\"会让无人值守场景默默\n * 生成技能)。\n */\nfunction isAccepted(answer: ProposalAnswer): boolean {\n return (answer.selectedOptionLabels ?? []).includes(ACCEPT_OPTION_LABEL)\n}\n\n/**\n * 向用户提案一个候选。\n *\n * 返回 `accepted` 时,M4b 负责实际生成 SKILL.md——本里程碑到\"拿到用户意愿\"为止。\n */\nexport async function proposeCandidate(deps: ProposeDeps): Promise<ProposalOutcome> {\n let answer: ProposalAnswer\n try {\n answer = await deps.requestGrill({\n header: '技能沉淀提议',\n question: renderQuestion(deps.brief),\n background: renderBackground(deps.brief),\n recommendation: renderRecommendation(),\n options: renderOptions(),\n askedBy: 'skill-loop',\n })\n } catch (err) {\n deps.logger?.warn(\n `skill-loop proposal failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n return 'error'\n }\n\n if (isAccepted(answer)) return 'accepted'\n\n // 拒绝必须落库——否则下一轮抽取重新检出同一个簇时会再问一次(R9 冷却失效)。\n const written = writeDismissal(deps)\n if (!written) {\n // **返回 error 而非 dismissed**:调用方据此知道\"用户确实拒绝了,但冷却没写成\",\n // 可以选择重试或记账。若谎报 dismissed,调用方会以为冷却已生效,而实际下次空闲\n // 还会再问一次——语义误导比失败本身更糟。\n deps.logger?.warn('skill-loop: failed to persist dismissal, candidate may resurface')\n return 'error'\n }\n return 'dismissed'\n}\n\n/**\n * 把 `dismissedAt` 写回候选文件。\n *\n * **只补这一个字段**,不改候选的任何其他内容——宿主在这里是\"回写用户决定\",不是候选数据\n * 的编辑者。读改写整份文件是因为 scoped storage 的信封格式如此,但字段级只动 dismissedAt。\n *\n * ## 并发(审核发现的 lost update,已修)\n *\n * 同一个 candidates.json 有两个写者:插件(重算候选后整份覆盖,走 tmp+rename 原子写,见\n * `plugin-service-entry-template.ts:74-76`)和宿主(本函数回写 dismissedAt)。grill 等待\n * 用户回答的窗口是**分钟级**的——期间另一个会话的插件完全可能重算并写入新候选。若本函数\n * 沿用 grill 之前读到的内容覆盖全文件,插件刚写的新候选会被静默抹掉。\n *\n * 两处修正:\n * ① **写前重读**:不复用 `deps.brief`(那是 grill 之前的快照),而是重新读当前文件内容,\n * 只在最新内容上打 dismissedAt——插件在窗口内新增的候选因此得以保留。\n * ② **原子写**:tmp + rename,与插件侧同纹理。裸 `writeFileSync` 在被另一方 rename 覆盖\n * 时可能留下半截文件;rename 在同一文件系统上是原子替换。\n *\n * 剩余的理论竞态(重读与 rename 之间插件恰好完成一次写)会丢掉那一次的新候选,但候选是\n * 从 episode 重算出来的**派生数据**——下次 session.idle 会重新检出,不构成永久损失。真正\n * 不能丢的是 dismissedAt(用户意图),而它由本函数写、由插件侧 `mergeCandidates` 保留。\n *\n * RFC-228 边界:该规则保护的是项目根下的 `.otto/` 状态域;plugin-state 在\n * `${OTTO_HOME}/plugin-state/` 下,宿主是它的 authority(插件的写权限本就是宿主经\n * execArgv 授予的),此处不构成越界。\n */\nfunction writeDismissal(deps: ProposeDeps): boolean {\n // RFC-345:影子态零磁盘写入——冷却本即「写后读」模块,内存丢失不破坏「拒绝」主语义;\n // 对照 §D3「读、静默吞写」,影子下跳过 persistence 层。\n if (isShadowModeEnabled()) return true\n\n const file = candidateFilePath(deps.pluginId, deps.workspaceKey)\n const tmp = `${file}.host.tmp`\n try {\n // ① 重读最新内容(不是 grill 之前的快照)。\n const envelope = JSON.parse(readFileSync(file, 'utf8')) as {\n data?: { items?: { id?: string; dismissedAt?: number }[] }\n }\n const items = envelope?.data?.items\n if (!Array.isArray(items)) return false\n\n const target = items.find((i) => i?.id === deps.brief.id)\n // 候选在窗口内被插件淘汰掉了(FIFO/窗口滑动)——无处可写,但用户的拒绝对一个已不存在\n // 的候选也无意义,按写成功处理会谎报,故返回 false 让调用方知道。\n if (!target) return false\n target.dismissedAt = deps.now\n\n // ② 原子写:与插件侧 createScopedStorage 同纹理。\n writeFileSync(tmp, JSON.stringify(envelope))\n renameSync(tmp, file)\n return true\n } catch {\n try {\n unlinkSync(tmp)\n } catch {\n /* tmp 可能压根没创建成功,忽略 */\n }\n return false\n }\n}\n","/**\n * skill-loop.ts —— RFC-318 M5:技能回路的 session.idle 生产接线。\n *\n * 这是整条回路唯一的驱动点,负责两件事:\n * ① 通知插件去抽取(这**也是** sessionId 授权的唯一入口——插件能读哪个会话完全由这里\n * 的通知行为决定,见 RFC-318 D9/R2);\n * ② 读上一轮攒下的候选,挑一个提案给用户。\n *\n * **先通知再读**:通知触发的抽取在 worker 里异步跑(含一次模型调用,秒级),等它完成会让\n * idle handler 阻塞住会话生命周期。候选本就不需要实时——本轮读到的是上一轮的产物,这是\n * 刻意的取舍。\n *\n * **每次 idle 最多提案一个**:多个簇可能同时成熟,连弹三个 grill 是骚扰。\n *\n * **fail-closed(R4)**:全链任何异常只 warn。`session.idle` hook 抛错会污染会话生命周期,\n * 而技能回路是纯旁路能力——它出问题不该让用户的会话出问题。\n */\n\nimport { defineHook } from '@x-otto/hooks'\nimport { DEFAULT_WORKSPACE_KEY as WORKSPACE_KEY_FALLBACK } from '@x-otto/workspace'\n\nimport { pendingBriefs, resolveCooldownMs } from '../skill-loop/candidate-brief'\nimport { proposeCandidate } from '../skill-loop/candidate-proposal'\nimport { CANDIDATE_SOURCE_ALLOWLIST, readCandidates } from '../skill-loop/candidate-reader'\n\nimport type { HookSpec } from '@x-otto/hooks'\nimport type { Logger } from '@x-otto/shared'\nimport type { ProposalOutcome } from '../skill-loop/candidate-proposal'\nimport type { CandidateBrief } from '../skill-loop/candidate-reader'\nimport type { PluginServiceNotification } from '@x-otto/plugin'\n\n/**\n * 归纳器插件 id —— **取自薄读取器的白名单**,不自己写字面量。\n * 两处各写一份的话,改了白名单没改这里 = 通知发给 A、读候选读 B,静默失效。\n */\nexport const INDUCTOR_PLUGIN_ID = CANDIDATE_SOURCE_ALLOWLIST[0] as string\n\n/**\n * workspaceRef 未就位时的兜底 —— **单源自 `@x-otto/workspace`**(同插件侧 storageDir 的兜底,\n * 见 `plugin-module-manager.ts:57`)。读侧与写侧兜底必须同值,否则 workspaceRef 未就位时\n * 读取器会去翻一个插件从没写过的目录。\n */\nexport const DEFAULT_WORKSPACE_KEY = WORKSPACE_KEY_FALLBACK\n\nexport interface SkillLoopHookDeps {\n isEnabled: () => boolean\n /**\n * RFC-318:用户在 /preference 里调的抽取阈值(`skill_loop.min_messages`)。\n * 插件跑在隔离 worker、读不到宿主 settings,故由宿主随通知下发。\n * 返回 undefined = 未配置,插件用自己的缺省值。\n */\n getMinMessages?: () => number | undefined\n /**\n * RFC-318:拒绝后的冷却天数(`skill_loop.dismiss_cooldown_days`)。\n * 与阈值不同,冷却完全在宿主侧生效(插件的 pendingCandidates 无生产消费方),\n * 故直接读、不必下发。\n */\n getCooldownDays?: () => number | undefined\n /** 广播 session.idle 给运行中的 service(`App.notifyPluginServices`),返回送达数。 */\n notifyServices: (n: PluginServiceNotification) => number\n getWorkspaceKey: () => string | undefined\n /** 会话的 grill 通道(`AgentSession.requestGrill`)。会话不存在时返回 undefined。 */\n getRequestGrill: (\n sessionId: string,\n ) => ((q: Parameters<typeof proposeCandidate>[0]['requestGrill'] extends (q: infer Q) => unknown ? Q : never) => Promise<{\n answer: string\n selectedOptionLabels?: string[]\n acceptedRecommendation?: boolean\n resolvedBy?: string\n }>) | undefined\n logger: Logger\n now?: () => number\n /**\n * 用户同意生成时的回调(M4b 的接入点)。\n * 传 `brief` 本体而非只给 id:起草蓝图要用到任务签名、会话列表与簇大小。\n */\n onAccepted?: (input: { sessionId: string; brief: CandidateBrief }) => void\n}\n\n/** 单次 idle 的处理结果(供测试断言与日志)。 */\nexport type SkillLoopOutcome =\n | 'disabled'\n | 'no-candidates'\n | 'all-cooling'\n | 'no-session'\n | ProposalOutcome\n\n/**\n * 跑一轮技能回路。抽出来独立于 hook,便于测试直接驱动(不必伪造 hook registry)。\n */\nexport async function runSkillLoopOnce(\n deps: SkillLoopHookDeps,\n sessionId: string,\n): Promise<SkillLoopOutcome> {\n if (!deps.isEnabled()) return 'disabled'\n const now = deps.now ? deps.now() : Date.now()\n\n // ① 通知插件抽取(sessionId 授权的唯一入口)。插件没装/没启用时送达数为 0,\n // 不影响后续读取——可能还有历史候选待提案。\n try {\n const minMessages = deps.getMinMessages?.()\n deps.notifyServices({\n type: 'session.idle',\n sessionId,\n // 只在用户真配过时才下发——不配就让插件用自己的缺省,避免宿主与插件两处各写一份默认值。\n ...(typeof minMessages === 'number' ? { options: { minMessages } } : {}),\n })\n } catch (err) {\n deps.logger.warn(\n { err: String(err) },\n 'skill-loop: notifying inductor service failed (extraction skipped this round)',\n )\n }\n\n // ② 读上一轮攒下的候选。\n let pending: ReturnType<typeof pendingBriefs>\n try {\n const briefs = readCandidates({\n pluginId: INDUCTOR_PLUGIN_ID,\n workspaceKey: deps.getWorkspaceKey() ?? WORKSPACE_KEY_FALLBACK,\n })\n pending = pendingBriefs(briefs, now, resolveCooldownMs(deps.getCooldownDays?.()))\n } catch (err) {\n deps.logger.warn({ err: String(err) }, 'skill-loop: reading candidates failed')\n return 'no-candidates'\n }\n if (pending.length === 0) return 'no-candidates'\n\n const requestGrill = deps.getRequestGrill(sessionId)\n if (!requestGrill) return 'no-session'\n\n // ③ 每次 idle 只提案一个——多个簇同时成熟时连弹 grill 是骚扰。\n const brief = pending[0]\n if (!brief) return 'no-candidates'\n\n const outcome = await proposeCandidate({\n requestGrill,\n brief,\n pluginId: INDUCTOR_PLUGIN_ID,\n workspaceKey: deps.getWorkspaceKey() ?? WORKSPACE_KEY_FALLBACK,\n now,\n logger: { warn: (m) => deps.logger.warn(m) },\n })\n\n if (outcome === 'accepted') {\n deps.logger.info({ sessionId, candidateId: brief.id }, 'skill-loop: user accepted candidate')\n deps.onAccepted?.({ sessionId, brief })\n }\n return outcome\n}\n\n/** 注册到 `session.idle`。 */\nexport function createSkillLoopHooks(deps: SkillLoopHookDeps): HookSpec[] {\n return [\n defineHook({\n name: 'skill-loop',\n timing: 'session.idle',\n handle: async (input) => {\n try {\n await runSkillLoopOnce(deps, input.sessionId)\n } catch (err) {\n // 兜底:runSkillLoopOnce 内部已全程 fail-closed,此处防未预期路径冒泡到\n // session 生命周期(R4)。\n deps.logger.warn({ err: String(err) }, 'skill-loop: unexpected failure')\n }\n },\n }),\n ]\n}\n","/** Sandbox guard: validate tool execution environment constraints. */\nimport { resolve } from 'node:path'\n\nimport { defineHook, extractWriteTargets } from '@x-otto/hooks'\nimport type { HookSpec, ToolExecuteBeforeInput, ToolExecuteBeforeOutput } from '@x-otto/hooks'\n\nimport { assessSandboxSafety, type SandboxAssessContext } from '@x-otto/hooks'\n\n/** ask 决策原因的哨兵前缀:cli 据此识别沙箱升级批准 → 记命令 bypass(M4 1b-2)。 */\nexport const SANDBOX_ESCALATION_MARKER = '[sandbox-escalation]'\n\nexport interface SandboxGuardDeps {\n /** 返回当前沙箱评估上下文;沙箱未启用时返回 null(hook 即 no-op)。 */\n getContext: () => SandboxAssessContext | null\n}\n\nexport function createSandboxGuardHook(deps: SandboxGuardDeps): HookSpec {\n const handle = (input: ToolExecuteBeforeInput, output: ToolExecuteBeforeOutput): void => {\n if (output.cancelled) {\n return\n }\n const ctx = deps.getContext()\n if (!ctx) {\n return\n }\n\n if (input.readonly) {\n return\n }\n\n const args = input.args as Record<string, unknown> | undefined\n const rawTargets = input.paths ? [...input.paths] : []\n const command = args?.command\n if (typeof command === 'string') {\n rawTargets.push(...extractWriteTargets(command))\n }\n if (rawTargets.length === 0) {\n return\n }\n\n const targetDir = typeof args?.targetDir === 'string' ? args.targetDir : '.'\n const cwd = resolve(ctx.projectRoot, targetDir)\n const writePaths = rawTargets.map((p) => resolve(cwd, p))\n\n const assessment = assessSandboxSafety({ writePaths }, ctx)\n if (assessment.verdict === 'deny') {\n output.cancelled = true\n output.decision = 'deny'\n output.decisionReason = assessment.reason ?? 'sandbox policy'\n output.cancelReason = `Sandbox denied: ${output.decisionReason}`\n return\n }\n if (assessment.verdict === 'ask') {\n // autoAllowBashIfSandboxed:仅越界写(非 denied-root)+ 权限层已判 allow(未命中\n // destructive-guard 等安全护栏)+ 仅 bash 工具 → 降级为放行,不弹审批。\n if (\n ctx.autoAllowBashIfSandboxed === true &&\n input.toolName === 'bash' &&\n output.decision !== 'deny' &&\n output.decision !== 'ask'\n ) {\n return\n }\n if (output.decision !== 'deny') {\n output.decision = 'ask'\n output.decisionReason = `${SANDBOX_ESCALATION_MARKER} ${assessment.reason ?? 'requires approval'}`\n }\n }\n }\n\n return defineHook({\n name: 'sandbox-guard',\n timing: 'tool.execute.before',\n priority: 6,\n enabled: true,\n handle,\n })\n}\n","/** Agent job notification: push job lifecycle events to TUI. */\n\nimport type { HookSpec } from '@x-otto/hooks'\nimport { defineHook } from '@x-otto/hooks'\nimport { injectPendingAsUserMessage } from './notification-injection'\n\nexport interface AgentJobNotification {\n jobId: string\n title: string\n status: 'ready' | 'failed' | 'canceled'\n /** 一句话摘要(diff 概况 / 失败原因)。 */\n summary?: string\n /** 转录输出文件路径——供模型读全量输出(R2,对照 CC OUTPUT_FILE_TAG)。 */\n outputPath?: string\n /** worktree 路径/分支——供模型定位作业产物去 review/apply(R2,对照 CC WORKTREE_TAG)。 */\n worktree?: { path: string; branch?: string }\n}\n\nfunction formatNotification(n: AgentJobNotification): string {\n const lines = [`id: ${n.jobId}`, `title: ${n.title}`, `status: ${n.status}`]\n if (n.summary) lines.push(`summary: ${n.summary}`)\n if (n.outputPath) lines.push(`output_file: ${n.outputPath}`)\n if (n.worktree) {\n lines.push(`worktree_path: ${n.worktree.path}`)\n if (n.worktree.branch) lines.push(`worktree_branch: ${n.worktree.branch}`)\n }\n return `<delegate-notification>\\n${lines.join('\\n')}\\n</delegate-notification>`\n}\n\nexport interface AgentJobNotificationBridge {\n /** 一起注册的 hooks(注入器 + 清理)。 */\n hooks: HookSpec[]\n /** 作业服务在 run 完成/失败时调用:缓存一条待注入通知。 */\n notify(sessionId: string, n: AgentJobNotification): void\n}\n\n/** Bound on how many recently-deleted session ids we remember (review J8, see below). */\nconst MAX_TRACKED_DELETED_SESSIONS = 200\n\nexport function createAgentJobNotificationHooks(): AgentJobNotificationBridge {\n const pending = new Map<string, string[]>()\n // review J8: unlike background-process-notification's collector (a hook fired in-order\n // relative to session.deleted), notify() here is called imperatively by AgentJobService\n // from detached-run completion — no ordering guarantee against session.deleted. If notify()\n // lands after cleanup already ran, `pending.set(sessionId, list)` would silently recreate\n // the entry for a session that no longer exists, leaking it forever (no further\n // session.deleted event will fire to clean it up).\n //\n // Track a bounded window of recently-deleted session ids (insertion-order Set, oldest\n // evicted first) so a late notify() for one is dropped instead of resurrecting a leaked\n // entry — without itself growing unbounded across a long-lived process's lifetime.\n const deletedSessions = new Set<string>()\n\n const notify = (sessionId: string, n: AgentJobNotification): void => {\n if (deletedSessions.has(sessionId)) return\n const list = pending.get(sessionId) ?? []\n list.push(formatNotification(n))\n pending.set(sessionId, list)\n }\n\n const injectorHook = defineHook({\n name: 'agent-job-notification-injector',\n timing: 'messages.transform',\n priority: 25,\n handle: (input, output) => {\n injectPendingAsUserMessage(pending, input, output)\n },\n })\n\n const cleanupHook = defineHook({\n name: 'agent-job-notification-cleanup',\n timing: 'session.deleted',\n handle: (input) => {\n pending.delete(input.sessionId)\n deletedSessions.add(input.sessionId)\n if (deletedSessions.size > MAX_TRACKED_DELETED_SESSIONS) {\n const oldest = deletedSessions.values().next().value\n if (oldest !== undefined) deletedSessions.delete(oldest)\n }\n },\n })\n\n return { hooks: [injectorHook, cleanupHook], notify }\n}\n","/**\n * loop-status.ts —— RFC-318:技能回路的**可观测状态**。\n *\n * 此前用户想知道\"它到底攒了什么\",只能去 `cat ~/.otto/plugin-state/.../episodes.json`。\n * 一个默认关、且开了之后长时间没动静的功能,如果还看不见内部状态,用户无从判断\n * \"是没触发条件,还是根本坏了\"——这是让人放弃一个功能最快的方式。\n *\n * 本模块把状态读出来。**复用 M4a 的安全闸**(白名单 + zod + 投影):状态展示同样是\n * \"插件产出进入宿主视野\",没有理由比提案通道更宽松。\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { z } from 'zod'\n\nimport { pluginStateDirFor } from '../plugin/plugin-scoped-storage'\nimport { CANDIDATE_SOURCE_ALLOWLIST, readCandidates } from './candidate-reader'\nimport { pendingBriefs, resolveCooldownMs } from './candidate-brief'\n\nimport type { CandidateBrief } from './candidate-reader'\n\n/** 摘要里最多列几条——面板是给人扫一眼的,不是数据转储。 */\nexport const STATUS_SAMPLE_LIMIT = 5\n\nconst EpisodeSummarySchema = z.object({\n taskSignature: z.string().min(1).max(200),\n outcome: z.enum(['success', 'partial', 'failure']),\n at: z.number().int().nonnegative(),\n steps: z.array(z.object({ tool: z.string().max(64) })).max(20),\n})\n\nexport interface SkillLoopStatus {\n /** 插件是否已装(没装则下面全是 0,且该提示用户去装)。 */\n pluginInstalled: boolean\n /** 已观测的 episode 总数。 */\n episodeCount: number\n /** 其中成功轨迹数——只有成功的会参与聚簇。 */\n successCount: number\n /** 候选簇总数。 */\n candidateCount: number\n /** 当前可浮出的候选(未被拒绝或冷却已满)。 */\n pendingCount: number\n /** 被拒绝且仍在冷却期内的候选数。 */\n coolingCount: number\n /** 最近观测到的任务签名(最多 STATUS_SAMPLE_LIMIT 条,新→旧)。 */\n recentSignatures: string[]\n /** 待提案的候选摘要——用户最关心\"接下来它会问我什么\"。 */\n pending: CandidateBrief[]\n /** 距下一个候选成簇还差几条同类(null = 已有待提案,或无从估算)。 */\n nextProposalHint: string | null\n}\n\nfunction episodesFilePath(pluginId: string, workspaceKey: string): string {\n return join(pluginStateDirFor(pluginId), workspaceKey, 'episodes.json')\n}\n\n/** 读 episode 概览。fail-closed:任何异常返回空(R4——状态展示不该让命令崩)。 */\nfunction readEpisodeSummary(\n pluginId: string,\n workspaceKey: string,\n): { total: number; success: number; recent: string[] } {\n let raw: string\n try {\n const file = episodesFilePath(pluginId, workspaceKey)\n if (!existsSync(file)) return { total: 0, success: 0, recent: [] }\n raw = readFileSync(file, 'utf8')\n } catch {\n return { total: 0, success: 0, recent: [] }\n }\n\n let items: unknown\n try {\n items = (JSON.parse(raw) as { data?: { items?: unknown } })?.data?.items\n } catch {\n return { total: 0, success: 0, recent: [] }\n }\n if (!Array.isArray(items)) return { total: 0, success: 0, recent: [] }\n\n let success = 0\n const parsed: { signature: string; at: number }[] = []\n for (const item of items) {\n const r = EpisodeSummarySchema.safeParse(item)\n if (!r.success) continue\n if (r.data.outcome === 'success') success += 1\n parsed.push({ signature: r.data.taskSignature, at: r.data.at })\n }\n const recent = parsed\n .sort((a, b) => b.at - a.at)\n .slice(0, STATUS_SAMPLE_LIMIT)\n .map((p) => p.signature)\n\n return { total: parsed.length, success, recent }\n}\n\nexport interface StatusInput {\n workspaceKey: string\n now: number\n /** 用户配的冷却天数(未配则用缺省)。 */\n cooldownDays?: number\n /** 成簇门槛——用于估算\"还差几条\"。与插件侧 MIN_CLUSTER_SIZE 同值。 */\n minClusterSize?: number\n /** 插件是否在活跃列表里。 */\n pluginInstalled: boolean\n}\n\n/**\n * 汇总技能回路当前状态。\n *\n * 只读,不改任何东西——包括**不推进冷却、不消费候选**。用户查看状态不应产生副作用。\n */\nexport function readSkillLoopStatus(input: StatusInput): SkillLoopStatus {\n const pluginId = CANDIDATE_SOURCE_ALLOWLIST[0] as string\n const empty: SkillLoopStatus = {\n pluginInstalled: input.pluginInstalled,\n episodeCount: 0,\n successCount: 0,\n candidateCount: 0,\n pendingCount: 0,\n coolingCount: 0,\n recentSignatures: [],\n pending: [],\n nextProposalHint: null,\n }\n if (!input.pluginInstalled) return empty\n\n const episodes = readEpisodeSummary(pluginId, input.workspaceKey)\n const candidates = readCandidates({ pluginId, workspaceKey: input.workspaceKey })\n const cooldownMs = resolveCooldownMs(input.cooldownDays)\n const pending = pendingBriefs(candidates, input.now, cooldownMs)\n\n return {\n pluginInstalled: true,\n episodeCount: episodes.total,\n successCount: episodes.success,\n candidateCount: candidates.length,\n pendingCount: pending.length,\n coolingCount: candidates.length - pending.length,\n recentSignatures: episodes.recent,\n pending: pending.slice(0, STATUS_SAMPLE_LIMIT),\n nextProposalHint: buildNextHint({\n pending: pending.length,\n success: episodes.success,\n minClusterSize: input.minClusterSize ?? 3,\n }),\n }\n}\n\n/**\n * \"接下来会发生什么\"的提示。\n *\n * 刻意**不**声称精确预测——聚簇取决于签名相似度,宿主侧算不出来(那是插件的算法)。\n * 只给用户能据以判断的事实:有没有待提案、成功轨迹够不够门槛。说不了的就不说,\n * 假装能预测比不给提示更糟。\n */\nfunction buildNextHint(input: {\n pending: number\n success: number\n minClusterSize: number\n}): string | null {\n if (input.pending > 0) return null // 已有待提案,下次空闲就会问\n if (input.success === 0) return 'no-episodes'\n if (input.success < input.minClusterSize) return 'not-enough-successes'\n return 'no-cluster-yet'\n}\n","/**\n * episode-evidence.ts —— RFC-318 M4b:给起草作业读取执行路径证据。\n *\n * ## 为什么需要它(审核发现的结构性缺口)\n *\n * 蓝图最初写的是\"去读那些会话的真实轨迹\"。实证后发现这是**空头支票**:起草作业由\n * `AgentJobService` 起在隔离 worktree 里,工具集是标准代码链(bash/read/edit/grep,见\n * `agent-job-wiring.ts:99-102`),而会话历史存在 SQLite(`data/otto.db`)——作业 agent\n * 既没有跨会话读取的工具,也不该为此被授予(那是比 `session.read` 更宽的能力面)。\n * 照原样交付的话,每次同意起草都会走到蓝图的\"证据不足即放弃\"分支静默失败。\n *\n * 改为**由宿主把证据读出来注入 prompt**。证据本来就在插件的 `episodes.json` 里——每条\n * episode 都带 `steps: {tool, intent}[]`,那正是\"这类任务是怎么做的\"。\n *\n * ## 与 M4a 投影(CandidateBrief)的关系\n *\n * M4a 刻意**不**把 steps 放进 `CandidateBrief`:那是给 grill 简报用的,受 R3 约束——\n * 简报里插件可控的自由文本越少越好。起草作业是**另一个上下文**:用户已当面同意、产物是\n * 待审阅的 diff、跑在隔离 worktree 里。在这个上下文里给出执行路径是必要且相称的。\n *\n * 但注入面仍然收紧:只取本簇会话的 episode、只取 `{tool, intent}` 两个字段、逐条过长度\n * 上限、条数封顶——与 M4a 同样的\"结构化而非自由文本\"原则。\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { z } from 'zod'\n\nimport { pluginStateDirFor } from '../plugin/plugin-scoped-storage'\n\n/** episode 记录的文件名(对齐插件侧 `EPISODE_RECORD_ID = 'episodes'`)。 */\nconst EPISODE_FILE = 'episodes.json'\n\n/** 单次起草最多注入多少条 episode——够看出稳定路径即可,不必把全簇塞进 prompt。 */\nexport const MAX_EVIDENCE_EPISODES = 8\n\nconst EpisodeStepSchema = z.object({\n tool: z.string().min(1).max(64),\n intent: z.string().max(160),\n})\n\nconst EpisodeSchema = z.object({\n taskSignature: z.string().min(1).max(200),\n steps: z.array(EpisodeStepSchema).max(20),\n outcome: z.enum(['success', 'partial', 'failure']),\n sessionId: z.string().max(64),\n at: z.number().int().nonnegative(),\n})\n\n/** 出境给起草 prompt 的执行路径证据。 */\nexport interface EpisodeEvidence {\n sessionId: string\n outcome: 'success' | 'partial' | 'failure'\n steps: { tool: string; intent: string }[]\n}\n\n/**\n * 读取某候选簇成员会话的 episode 证据。\n *\n * **fail-closed(R4)**:文件缺失/损坏/schema 不符 → 返回 `[]`。起草作业拿不到证据时会\n * 走蓝图的\"证据不足即放弃\"分支——那是正确的降级,不是错误。\n */\nexport function readEpisodeEvidence(input: {\n pluginId: string\n workspaceKey: string\n sessionIds: readonly string[]\n limit?: number\n}): EpisodeEvidence[] {\n const limit = input.limit ?? MAX_EVIDENCE_EPISODES\n let raw: string\n try {\n const file = join(pluginStateDirFor(input.pluginId), input.workspaceKey, EPISODE_FILE)\n if (!existsSync(file)) return []\n raw = readFileSync(file, 'utf8')\n } catch {\n return []\n }\n\n let items: unknown\n try {\n const envelope = JSON.parse(raw) as { data?: { items?: unknown } }\n items = envelope?.data?.items\n } catch {\n return []\n }\n if (!Array.isArray(items)) return []\n\n const wanted = new Set(input.sessionIds)\n const evidence: EpisodeEvidence[] = []\n for (const item of items) {\n const parsed = EpisodeSchema.safeParse(item)\n if (!parsed.success) continue\n if (!wanted.has(parsed.data.sessionId)) continue\n // 只归纳成功路径(同 M3 聚簇口径)——失败轨迹不该被写成\"标准做法\"。\n if (parsed.data.outcome !== 'success') continue\n evidence.push({\n sessionId: parsed.data.sessionId,\n outcome: parsed.data.outcome,\n steps: parsed.data.steps,\n })\n if (evidence.length >= limit) break\n }\n return evidence\n}\n\n/** 把证据渲染成 prompt 片段(固定结构,值受 schema 长度约束)。 */\nexport function renderEvidence(evidence: readonly EpisodeEvidence[]): string {\n if (evidence.length === 0) {\n return '(No recorded execution paths are available for these sessions.)'\n }\n return evidence\n .map((e, i) => {\n const steps = e.steps.map((s, n) => ` ${n + 1}. ${s.tool} — ${s.intent}`).join('\\n')\n return `Run ${i + 1} (session ${e.sessionId}):\\n${steps || ' (no steps recorded)'}`\n })\n .join('\\n\\n')\n}\n","/**\n * skill-draft-prompt.ts —— RFC-318 M4b:SKILL.md 起草作业的任务书。\n *\n * **这不是 SKILL.md 的内容模板**,而是给一个后台 agent 的指令——让它去读真实会话轨迹、\n * 归纳出可复用流程、落成文件。为什么不直接用候选数据拼一份 SKILL.md:候选只有任务签名\n * 和会话 id 列表(M4a 的元信息投影刻意不带执行细节),拿它拼出来的只能是一句空话。真正\n * 的流程知识在那些会话的轨迹里,必须去读。\n *\n * 与 `extraction-prompt.ts`(插件侧的 episode 抽取)的分工:那个从**单次会话**里抽\"这次\n * 是怎么做的\",本文件让 agent 跨**多次同类会话**归纳\"这类任务的标准做法\"。\n *\n * **R1 零豁免**:本蓝图绝不包含任何\"自动落地/自动 apply\"的指示。作业跑在 worktree 里,\n * 产出 diff,用户审阅后 `/job apply` 才生效——这是 AgentJobService 的既有保证,蓝图不得\n * 试图绕过(也没有可绕的入参,见 RFC-287 R2)。\n */\n\nimport { renderEvidence } from './episode-evidence'\n\nimport type { CandidateBrief } from './candidate-reader'\nimport type { EpisodeEvidence } from './episode-evidence'\n\n/** 生成物的落点(相对工作区)——`.otto/skills` 是 skill-discovery 的既有扫描目录。 */\nexport const SKILL_DRAFT_DIR = '.otto/skills'\n\n/**\n * 构造起草作业的 prompt。\n *\n * 四条硬约束(每条都对应一种真实的失败模式):\n * ① **先读证据再动笔** —— 不读轨迹直接照签名编流程,产出的 skill 是幻觉;\n * ② **格式必须能被既有 parser 吃下** —— name/description 缺一即解析抛错(skill-parser.ts:52,61);\n * ③ **只写一个文件** —— 审阅 diff 时应当只看到一个新文件,夹带其他修改会让审阅失效;\n * ④ **证据不足就放弃** —— 硬凑出来的 skill 会永久污染技能库,宁可这次不产出。\n */\nexport function buildSkillDraftPrompt(\n brief: CandidateBrief,\n evidence: readonly EpisodeEvidence[] = [],\n): string {\n const sessions = brief.sessionIds.join(', ')\n return `You are drafting a reusable skill for this project, based on a recurring task pattern that otto observed.\n\n## The pattern\n\n- Task type: ${brief.taskSignature}\n- Observed ${brief.size} times\n- Tags: ${brief.tags.length > 0 ? brief.tags.join(', ') : '(none)'}\n- In sessions: ${sessions}\n\n## The recorded execution paths\n\nThese are the actual tool sequences otto observed for this task type. They are your evidence —\nyou do not need to (and cannot) go looking for the original sessions.\n\n${renderEvidence(evidence)}\n\n## What to do\n\n1. **Work from the recorded paths above.** They are the only evidence available. If they are\n too few, too varied, or too thin to support a reusable procedure, stop (see below) — do NOT\n invent steps from the task type alone.\n2. **Find what is genuinely reusable.** Across those runs, identify the stable path: the order\n of steps, the checks that mattered, the traps that were hit. Drop one-off details (specific\n file names, ids, values) — a skill describes a class of tasks, not one instance.\n3. **You may inspect the repository** to ground the procedure in real paths and commands\n (read/grep are available) — but the *sequence* must come from the recorded runs, not from\n your own idea of how the task should be done.\n4. **Write exactly one file**: \\`${SKILL_DRAFT_DIR}/<kebab-case-name>/SKILL.md\\`\n\n \\`\\`\\`\n ---\n name: <kebab-case-name>\n description: Use when <the trigger condition — when should otto reach for this skill>\n ---\n\n <the reusable procedure, in plain prose and ordered steps>\n \\`\\`\\`\n\n Both \\`name\\` and \\`description\\` are required — the skill loader rejects the file without them.\n \\`description\\` must describe **when to use this skill**, not what it does: it is what otto\n matches against future requests. Keep name + description under 1024 characters combined.\n\n5. **Change nothing else.** No edits to source, config, or docs. The reviewer should see exactly\n one new file in the diff.\n\n## When to stop instead\n\nIf the recorded paths turn out to be too varied, too thin, or not actually the same kind of task,\n**do not write the file**. Report what you found and stop. A fabricated skill pollutes the\nlibrary permanently; skipping costs nothing — otto will offer again if the pattern recurs.\n\nThe draft you produce is a proposal: it will be reviewed as a diff and only takes effect if the\nuser explicitly applies it.`\n}\n\n/** 作业标题(进 `/job` 面板,需一眼可辨)。 */\nexport function buildSkillDraftTitle(brief: CandidateBrief): string {\n const signature =\n brief.taskSignature.length > 60 ? `${brief.taskSignature.slice(0, 60)}…` : brief.taskSignature\n return `Draft skill: ${signature}`\n}\n","/**\n * skill-draft.ts —— RFC-318 M4b:用户同意后发起起草作业。\n *\n * **为什么由宿主发起而不是插件**:`plugin-skill-inductor` 的 manifest 没有声明\n * `agent.dispatch`,拿不到 `ctx.host.startJob`——这是刻意的,不给插件扩权。而到这一步\n * 用户已经在 grill 里当面点头,宿主侧用既有 `AgentJobService` 发起既自然又不扩大能力面。\n *\n * **R1 零豁免不因此放松**:作业跑在 worktree 隔离里、产出 diff、用户 `/job apply` 才生效。\n * 本模块不传任何绕过参数(入参 schema 里也没有可绕的字段,RFC-287 R2)。\n */\n\nimport { readEpisodeEvidence } from './episode-evidence'\nimport { buildSkillDraftPrompt, buildSkillDraftTitle } from './skill-draft-prompt'\n\nimport type { CandidateBrief } from './candidate-reader'\n\nexport interface DraftSkillDeps {\n /**\n * 发起后台作业。形状对齐 `AgentJobService.start` 的必要子集——**刻意不含 origin**:\n * 由宿主装配点固定(见 app-hooks 接线),本模块无从影响。\n */\n startJob: (input: { title: string; prompt: string; sessionId: string }) => { id: string }\n brief: CandidateBrief\n sessionId: string\n /** 证据来源——归纳器插件 id 与 workspaceKey(读它的 episodes 记录)。 */\n pluginId: string\n workspaceKey: string\n logger?: { warn: (m: string) => void }\n}\n\n/**\n * 据候选起草一份 SKILL.md(异步作业)。\n *\n * 返回作业 id 供调用方记账;**失败返回 undefined 而不抛**(R4)——用户同意了但作业没起来\n * 是个遗憾,不该让会话的 idle 处理链崩掉。\n */\nexport function draftSkillFromCandidate(deps: DraftSkillDeps): { jobId: string } | undefined {\n try {\n // 证据由宿主读出来注入 prompt——作业 agent 跑在隔离 worktree 里,没有(也不该有)\n // 跨会话读轨迹的能力。读不到时注入空证据,蓝图会走\"证据不足即放弃\"分支。\n const evidence = readEpisodeEvidence({\n pluginId: deps.pluginId,\n workspaceKey: deps.workspaceKey,\n sessionIds: deps.brief.sessionIds,\n })\n const job = deps.startJob({\n title: buildSkillDraftTitle(deps.brief),\n prompt: buildSkillDraftPrompt(deps.brief, evidence),\n sessionId: deps.sessionId,\n })\n return { jobId: job.id }\n } catch (err) {\n deps.logger?.warn(\n `skill-loop: failed to start skill draft job: ${err instanceof Error ? err.message : String(err)}`,\n )\n return undefined\n }\n}\n","import { homedir, tmpdir } from 'node:os'\n\nimport { createEditFilesPanelHook } from '@x-otto/hooks'\nimport type { HookRegistry, PermissionMode, EditFileCallback, PermissionPolicyRegistry } from '@x-otto/hooks'\nimport type { ToolRegistry } from '@x-otto/tools'\nimport type { NotificationDispatcher } from '@x-otto/notification'\nimport type { SessionPool, AgentRuntime, MemorySubsystem, AgentSession } from '@x-otto/runtime'\nimport type { SandboxController } from './sandbox-controller'\nimport type { SettingsManager } from '@x-otto/setting'\nimport type { OperationalLearningStore } from '@x-otto/memory'\nimport type { PromptManager } from '@x-otto/prompt'\nimport type { SkillRegistry } from '@x-otto/skill'\nimport type { AgentJobNotification } from '../hooks/agent-job-notification'\n\nimport { FailureSignalStore, createFailureSignalHook, createPermissionDenyHook } from '@x-otto/hooks'\nimport {\n createMemoryDeltaHooks,\n createTodoReminderHooks,\n createDocSyncReminderHooks,\n globalProcessRegistry,\n} from '@x-otto/runtime'\nimport {\n createToolGuidanceHook,\n createLessonInjectionHooks,\n createBackgroundProcessNotificationHooks,\n createBackgroundProcessSessionKillHook,\n createSessionPermissionSyncHooks,\n createMemoryAutoExtractHooks,\n createSkillCatalogInjectionHooks,\n createSandboxGuardHook,\n createSkillLoopGuidanceHooks,\n createSkillLoopHooks,\n INDUCTOR_PLUGIN_ID,\n DEFAULT_WORKSPACE_KEY,\n} from '../hooks'\nimport type { MemoryAutoExtractor } from '../hooks'\nimport { createAgentJobNotificationHooks } from '../hooks/agent-job-notification'\nimport { draftSkillFromCandidate } from '../skill-loop'\nimport { createClaudeHookBridgeHooks } from '@x-otto/claude-compat'\nimport { computeSandboxRoots, defaultDeniedRoots } from '@x-otto/hooks'\nimport type { Logger } from '@x-otto/shared'\nimport type { PluginServiceNotification } from '@x-otto/plugin'\n\nexport interface RegisterHooksDeps {\n hookRegistry: HookRegistry\n toolRegistry: ToolRegistry\n getDefaultToolPreset: () => 'minimal' | 'standard' | 'full'\n failureSignals: FailureSignalStore\n /**\n * agentRuntime.contextSources.sealAndBuildHooks — 透传避免暴露私有 agentRuntime。\n * RFC-129 D4:调用本函数即触发 registry seal——调用前必须保证插件贡献的 context source\n * (若有)已完成 `register()`(见 App 构造函数内 `collectPluginContextSources()` 调用点)。\n */\n contextSourcesBuildHooks: AgentRuntime['contextSources']['sealAndBuildHooks']\n workspaceDir: string\n memory?: MemorySubsystem\n learningStore: OperationalLearningStore\n /** 机器特定经验(scope='local',RFC-164 M164-1 D3),与 workspace lessons 合并常驻注入。 */\n localLearningStore?: OperationalLearningStore\n promptManager: PromptManager\n skillRegistry: SkillRegistry | null\n notificationDispatcher: NotificationDispatcher\n permissionRegistry: PermissionPolicyRegistry\n codingSessionPool: SessionPool\n sandbox: SandboxController\n settings: SettingsManager\n /** 延迟求值取回调——CLI 层在 startTui() 之后才回填 app.editFileCallback,注册时机早于该赋值,\n * 必须传 getter(活引用)而非值快照,否则闭包永远捕获构造期的 undefined(见 M72 修复记录)。 */\n getEditFileCallback: () => EditFileCallback | undefined\n editFilesDedup: Set<string>\n memoryAutoExtractor: MemoryAutoExtractor\n getSession: (id: string) => AgentSession | undefined\n /** RFC-318 M5:广播 session.idle 给运行中的 service(`App.notifyPluginServices`)。 */\n notifyPluginServices: (n: PluginServiceNotification) => number\n /** RFC-318 M5:workspaceKey(惰性——workspaceRef 在 App.start() 才解析)。 */\n getWorkspaceKey: () => string | undefined\n /** RFC-318 M4b:起草 SKILL.md 的作业入口(`AgentJobService.start`)。 */\n startSkillDraftJob: (input: {\n title: string\n prompt: string\n sessionId: string\n origin: 'user'\n }) => { id: string }\n logger: Logger\n}\n\nexport interface RegisterHooksResult {\n agentJobNotify: (sessionId: string, n: AgentJobNotification) => void\n}\n\nexport function registerAppHooks(d: RegisterHooksDeps): RegisterHooksResult {\n d.hookRegistry.register(createToolGuidanceHook(d.toolRegistry, d.getDefaultToolPreset))\n d.hookRegistry.register(createFailureSignalHook(d.failureSignals))\n d.hookRegistry.register(createPermissionDenyHook(d.failureSignals))\n d.hookRegistry.registerAll(d.contextSourcesBuildHooks(d.workspaceDir, d.memory))\n // memory 子系统恒含 delta facet(getMemories)——有 memory 即注册 delta hooks,\n // 无需 typeof 鸭子探测 / as never。\n if (d.memory) {\n d.hookRegistry.registerAll(createMemoryDeltaHooks(d.memory))\n }\n\n d.hookRegistry.registerAll(\n createTodoReminderHooks({\n getTodoList: (sessionId) => d.getSession(sessionId)?.session.getTodoList(),\n }),\n )\n // RFC-100:本仓库专属判定——edit/write 写入路径匹配 `*-milestone.md` 且新内容含\n // `Status: Done`(里程碑翻转为完成态)即视为\"需同步 write_todos 的文档变更\"。判定逻辑\n // 完全落在本仓库业务层(app-hooks.ts),@x-otto/runtime 侧的 doc-sync-reminder 框架机制\n // 对此判定语义零耦合——不使用 RFC/milestone 文档约定的项目不受影响。\n const MILESTONE_DOC_PATH_RE = /-milestone\\.md$/\n const STATUS_DONE_RE = /^Status:\\s*Done\\b/m\n d.hookRegistry.registerAll(\n createDocSyncReminderHooks({\n isDocSyncTrigger: (input) => {\n const path = typeof input.args.path === 'string' ? input.args.path : ''\n if (!MILESTONE_DOC_PATH_RE.test(path)) return false\n const newContent =\n typeof input.args.newContent === 'string'\n ? input.args.newContent\n : typeof input.args.content === 'string'\n ? input.args.content\n : ''\n return STATUS_DONE_RE.test(newContent)\n },\n }),\n )\n\n d.hookRegistry.registerAll(\n createLessonInjectionHooks(d.learningStore, d.promptManager, d.localLearningStore),\n )\n if (d.skillRegistry) {\n d.hookRegistry.registerAll(createSkillCatalogInjectionHooks(d.skillRegistry))\n }\n d.hookRegistry.registerAll(createClaudeHookBridgeHooks({ workspaceDir: d.workspaceDir }))\n d.hookRegistry.registerAll(createBackgroundProcessNotificationHooks())\n // 会话删除时解绑通知派发器。必须在 registerHooks(preExistingHooks 快照之后)注册,\n // 才计入 ownedHookNames → stop() 才能清理;若在构造器早注册会被误判 pre-existing 而泄漏进 shared registry。\n d.hookRegistry.on('session.deleted', 'rfc047-notification-detach', (input) =>\n d.notificationDispatcher.detach(input.sessionId),\n )\n const agentJobBridge = createAgentJobNotificationHooks()\n d.hookRegistry.registerAll(agentJobBridge.hooks)\n d.hookRegistry.register(\n createBackgroundProcessSessionKillHook((id) => {\n void globalProcessRegistry.killAllForSession(id)\n }),\n )\n d.hookRegistry.registerAll(\n createSessionPermissionSyncHooks({\n registry: d.permissionRegistry,\n getPersistedAllow: (id) => d.codingSessionPool.getSessionPermissionAllow(id),\n }),\n )\n\n d.hookRegistry.register(\n createSandboxGuardHook({\n getContext: () => {\n const deniedRoots = defaultDeniedRoots(d.workspaceDir, homedir())\n if (!d.sandbox.enabled) {\n // 即使沙箱未启用,denied-roots(.otto/)检查也必须生效——\n // workspace.json 等基础设施文件是 agent 工具永远不应修改的。\n // mode='bypass' + 空 writableRoots → assessSandboxSafety 在\n // denied-roots 检查之后对所有路径放行,与当前完全禁用沙箱行为一致。\n return {\n mode: 'bypass',\n projectRoot: d.workspaceDir,\n writableRoots: [],\n deniedRoots,\n networkAllowed: true,\n }\n }\n const mode = (d.settings.get('permission_mode') ?? 'auto') as PermissionMode\n return {\n mode,\n projectRoot: d.workspaceDir,\n writableRoots: computeSandboxRoots(\n mode,\n d.workspaceDir,\n tmpdir(),\n d.sandbox.writablePaths,\n ),\n deniedRoots,\n networkAllowed: d.sandbox.isNetworkAllowed,\n autoAllowBashIfSandboxed: d.sandbox.autoAllowBashIfSandboxed,\n }\n },\n }),\n )\n d.hookRegistry.registerAll(\n createMemoryAutoExtractHooks((id) => d.getSession(id), d.memoryAutoExtractor, {\n isEnabled: () =>\n d.settings.get('memory_auto_extract')?.enabled === true ||\n process.env.OTTO_MEMORY_AUTO_EXTRACT === '1',\n }),\n )\n // RFC-318 M5:技能回路——判别段(stable prompt 注入)+ session.idle 驱动。\n // 两者共用同一个开关:关着的时候既不注入\"otto 会观测重复流程\"的承诺,也不真的观测。\n const skillLoopEnabled = (): boolean =>\n d.settings.get('skill_loop')?.enabled === true || process.env.OTTO_SKILL_LOOP === '1'\n d.hookRegistry.registerAll(createSkillLoopGuidanceHooks({ isEnabled: skillLoopEnabled }))\n d.hookRegistry.registerAll(\n createSkillLoopHooks({\n isEnabled: skillLoopEnabled,\n // 用户在 /preference 里调的阈值——随通知下发给插件(它读不到宿主 settings)。\n getMinMessages: () => d.settings.get('skill_loop')?.min_messages,\n getCooldownDays: () => d.settings.get('skill_loop')?.dismiss_cooldown_days,\n notifyServices: d.notifyPluginServices,\n getWorkspaceKey: d.getWorkspaceKey,\n getRequestGrill: (sessionId) => {\n const session = d.getSession(sessionId)\n return session ? (q) => session.requestGrill({ ...q, askedBy: 'skill-loop' }) : undefined\n },\n // RFC-318 M4b:用户当面同意后起草 SKILL.md。origin 固定 'user'——这个作业确实是\n // 用户在 grill 里点头发起的(区别于 RFC-287 R2 针对的\"插件自主发起、用户不知情\")。\n // R1 不因此放松:作业跑在 worktree 隔离里、产 diff、仍需 /job apply 才生效。\n onAccepted: ({ sessionId, brief }) => {\n draftSkillFromCandidate({\n startJob: (input) => d.startSkillDraftJob({ ...input, origin: 'user' }),\n brief,\n sessionId,\n pluginId: INDUCTOR_PLUGIN_ID,\n workspaceKey: d.getWorkspaceKey() ?? DEFAULT_WORKSPACE_KEY,\n logger: { warn: (m) => d.logger.warn(m) },\n })\n },\n logger: d.logger,\n }),\n )\n d.hookRegistry.register(\n createEditFilesPanelHook({\n getOnFileEdited: d.getEditFileCallback,\n dedupCache: d.editFilesDedup,\n }),\n )\n return { agentJobNotify: agentJobBridge.notify }\n}\n","/**\n * tool-wiring.ts — 工具/hook 装配组(RFC-152 M4,从 app.ts 纯搬迁,补齐净减量缺口的追加拆分组)。\n *\n * `buildSharedToolCallbackDeps`/`buildToolsForWorkspace`/`connectorTools`/`initToolRegistry`/\n * `registerHooks` 五个方法逐字迁移。共同特征:都是\"把一批 this.* 字段组装成 deps 对象,\n * 传给既有的独立工厂函数\"的低依赖胶水代码(`buildSharedToolCallbackDeps`/`buildToolCallbacks`/\n * `createToolRegistry`/`registerAppHooks` 均已是既有导出函数),天然适合整体搬到专职文件。\n */\nimport { createToolRegistry, type ToolRegistry } from '@x-otto/tools'\nimport { createRegistrySkillProvider, type SkillRegistry } from '@x-otto/skill'\nimport { globalProcessRuntime, type AgentRuntime, type SessionPool as CodingSessionPool } from '@x-otto/runtime'\nimport type { HookRegistry, PermissionPolicyRegistry, EditFileCallback, FailureSignalStore } from '@x-otto/hooks'\nimport type { SettingsManager } from '@x-otto/setting'\nimport type { FileStateManager, OperationalLearningStore } from '@x-otto/memory'\nimport type { NotificationDispatcher } from '@x-otto/notification'\nimport type { PromptManager } from '@x-otto/prompt'\nimport type { AgentTool } from '@x-otto/agent'\nimport type { AgentSession, AgentSessionInfo } from '@x-otto/runtime'\nimport type { Orchestrator } from '@x-otto/orchestrator'\nimport type { WriteTodos } from '@x-otto/orchestration-contracts'\nimport type { RunTeam } from '../agent/swarm-runtime'\nimport type { AgentJobService } from './agent-job-service'\nimport type { SchedulerService } from '@x-otto/schedule'\nimport type { SandboxController } from './sandbox-controller'\nimport type { AppStorage, AppOptions } from '../types'\nimport type { CodingSessionOptions } from '../session/session-config-resolver'\nimport type { createTaskRunner } from '../session/task-runner'\nimport type { Logger } from '@x-otto/shared'\nimport type { PluginServiceNotification } from '@x-otto/plugin'\nimport type { MemoryAutoExtractor } from '../hooks'\nimport type { AgentJobNotification } from '../hooks/agent-job-notification'\nimport { buildToolCallbacks, buildSharedToolCallbackDeps, type ToolCallbacksDeps } from './tool-callbacks'\nimport { registerAppHooks } from './app-hooks'\n\n/**\n * `ToolWiringHost` — 五个工具/hook装配方法真实读写的窄依赖面。精确枚举自逐行读取\n * app.ts 原方法体(RFC-152 §4 规则3:不依赖 RFC 草案示例清单)。\n */\nexport interface ToolWiringHost {\n readonly processOwnerId: string\n readonly orchestrator: Orchestrator\n /** 会话级 Todo 存储(终局架构 review O2)——不再挂在 Orchestrator 上。 */\n readonly writeTodos: WriteTodos\n readonly sandbox: SandboxController\n readonly storage: AppStorage\n readonly runTeam: RunTeam\n runSession: ReturnType<typeof createTaskRunner>['runSession']\n readonly fileStateManager: FileStateManager\n readonly learningStore: OperationalLearningStore\n /** 机器特定经验(scope='local',RFC-164 M164-1 D3)。 */\n readonly localLearningStore: OperationalLearningStore\n /** RFC-318 M5:技能回路 —— 广播 session.idle 给运行中的 service。 */\n notifyPluginServices(n: PluginServiceNotification): number\n /** RFC-318 M5:workspaceKey(惰性——workspaceRef 在 App.start() 才解析)。 */\n readonly workspaceRef?: { key: string }\n readonly logger: Logger\n listSessions(filter?: { workspaceId?: string }): AgentSessionInfo[]\n createSession(options?: CodingSessionOptions): Promise<AgentSession>\n removeSession(id: string): Promise<boolean>\n getSession(id: string): AgentSession | undefined\n buildModelCatalog(sessionId?: string): Promise<\n Array<{\n id: string\n provider: string\n strengths: string[]\n available: boolean\n usable: boolean\n unusableReason?: string\n }>\n >\n readonly agentJobs: AgentJobService\n readonly scheduleServiceInstance?: SchedulerService\n /**\n * RFC-287 M4:生态检索实现(由 cli 层注入——registry 客户端住在 cli 侧)。\n * 缺省 → `capability_gap` 工具不注册,模型看不到该能力。\n */\n readonly searchEcosystem?: import('@x-otto/tools').SearchEcosystem\n readonly skillRegistry: SkillRegistry | null\n readonly workspaceDir: string\n defaultToolPreset: 'minimal' | 'standard' | 'full'\n readonly toolRegistry: ToolRegistry\n readonly hookRegistry: HookRegistry\n readonly failureSignals: FailureSignalStore\n readonly agentRuntime: AgentRuntime\n readonly promptManager: PromptManager\n readonly notificationDispatcher: NotificationDispatcher\n readonly permissionRegistry: PermissionPolicyRegistry\n readonly codingSessionPool: CodingSessionPool\n readonly settings: SettingsManager\n editFileCallback: EditFileCallback | undefined\n readonly _editFilesDedup: Set<string>\n readonly memoryAutoExtractor: MemoryAutoExtractor\n agentJobNotify?: (sessionId: string, n: AgentJobNotification) => void\n}\n\n/**\n * 工具回调 deps 的共享构造(已下沉到 tool-callbacks.ts 工厂函数,RFC-107 D4)。\n * 保留此薄调用——封装 App 的 host 绑定,避免两处调用点各写一遍 deps 字面量。\n */\nexport function buildSharedToolCallbackDepsForHost(\n host: ToolWiringHost,\n workspaceDir: string,\n skillProvider: ToolCallbacksDeps['skillProvider'],\n enableLsp: boolean,\n): ToolCallbacksDeps {\n return buildSharedToolCallbackDeps(\n {\n processOwnerId: host.processOwnerId,\n orchestrator: host.orchestrator,\n writeTodos: host.writeTodos,\n sandbox: (ctx) =>\n host.sandbox.isBypassed(ctx?.command) ? undefined : (host.sandbox.current ?? undefined),\n autoMemory: host.storage.autoMemory,\n // RFC-228 D2:project-shared 写入后即时 reload——`MemorySubsystem`(=ContextInjectionPort\n // 交集)的 `loadMemory()` 与 `MemoryManager.reloadMemory()` 语义等同(均为\n // `persistent.load()`),无需扩展契约新增方法。`storage.memory` 未配置时 undefined,\n // project-shared 写入仍生效,只是不即时刷新注入缓存(回调侧已处理该缺省)。\n reloadMemory: host.storage.memory ? () => host.storage.memory!.loadMemory() : undefined,\n // RFC-340 M2:归档召回 facet。直接把 `MemorySubsystem` 作为 `ArchiveRecallPort`\n // 传入(结构子类型,无需手工重包一层匿名对象)——这样端口契约在**注入点本身**\n // 就被编译器强制,而不只是在 `MemorySubsystem` 的 extends 处(独立评审 2026-08-10\n // 第 3 项指出的弱点:手包 facet 会让端口退化为纯注释级引用)。\n // `storage.memory` 未配置 → undefined → 归档工具整组不注册(when 门)。\n archiveRecall: host.storage.memory,\n runTeam: host.runTeam,\n runSession: host.runSession,\n fileStateManager: host.fileStateManager,\n learningStore: host.learningStore,\n localLearningStore: host.localLearningStore,\n sessions: {\n list: () => host.listSessions(),\n create: () => host.createSession(),\n remove: (id) => host.removeSession(id),\n get: (id) => host.getSession(id),\n },\n listModels: (sessionId) => host.buildModelCatalog(sessionId),\n startJob: (input) => ({ jobId: host.agentJobs.start({ ...input, origin: 'main' }).id }),\n getScheduleService: () => host.scheduleServiceInstance,\n // RFC-287 M4:生态检索(只读)。host 未提供则 capability_gap 工具不注册。\n searchEcosystem: host.searchEcosystem,\n },\n workspaceDir,\n skillProvider,\n enableLsp,\n )\n}\n\n/**\n * 为给定 workspaceDir(= worktree 路径)组装工具集——agent-session 作业的隔离关键。\n * 复用主路径同款 buildToolCallbacks(deps 全为 host.* 单例,构造后稳定),仅 workspaceDir 不同。\n * 附加方法,不改主工具注册路径。\n */\nexport function buildToolsForWorkspace(host: ToolWiringHost, workspaceDir: string): AgentTool[] {\n const skillProvider = host.skillRegistry\n ? createRegistrySkillProvider(host.skillRegistry, workspaceDir)\n : undefined\n const reg = createToolRegistry(\n workspaceDir,\n buildToolCallbacks(buildSharedToolCallbackDepsForHost(host, workspaceDir, skillProvider, false)),\n )\n // 直接返回 RegisteredTool[](保 isConcurrencySafe/resolvePath,见 get tools 注)。\n return reg.getAvailable(host.defaultToolPreset)\n}\n\n/** 主会话已注册的 MCP 工具(`mcp__*`)——满配后台作业复用同进程已认证连接。 */\nexport function connectorTools(host: ToolWiringHost): AgentTool[] {\n // 直接返回 RegisteredTool[](保 isConcurrencySafe/resolvePath,见 get tools 注)。\n return host.toolRegistry\n .getAvailable(host.defaultToolPreset)\n .filter((tool) => tool.name.startsWith('mcp__'))\n}\n\nexport function initToolRegistry(host: ToolWiringHost, options: AppOptions): ToolRegistry {\n const skillProvider =\n options.skillProvider ??\n (host.skillRegistry\n ? createRegistrySkillProvider(host.skillRegistry, host.workspaceDir)\n : undefined)\n\n if (options.sandbox) {\n host.sandbox.pin(options.sandbox)\n }\n\n // RFC-095 P1:ProcessTracker 适配器——工具层通过接口注入获取进程追踪能力,\n // 不再直接 import globalProcessRuntime。适配器在此桥接全局单例(P3 清理)。\n const processTracker = {\n registerChild(config: { command?: string; args?: string[]; owner?: unknown; category?: string; lifecycle?: string; cwd?: string }, child: { pid?: number }) {\n const managed = globalProcessRuntime.registerChild(config as import('@x-otto/runtime').ProcessSpawnConfig, child as import('node:child_process').ChildProcess)\n return { pid: managed.pid ?? 0 }\n }\n }\n\n return createToolRegistry(\n host.workspaceDir,\n {\n ...buildToolCallbacks(\n buildSharedToolCallbackDepsForHost(host, host.workspaceDir, skillProvider, options.lsp === true),\n ),\n processTracker,\n },\n )\n}\n\nexport function registerHooks(host: ToolWiringHost): void {\n const result = registerAppHooks({\n hookRegistry: host.hookRegistry,\n toolRegistry: host.toolRegistry,\n getDefaultToolPreset: () => host.defaultToolPreset,\n failureSignals: host.failureSignals,\n contextSourcesBuildHooks: (wd, mem) =>\n host.agentRuntime.contextSources.sealAndBuildHooks(wd, mem),\n workspaceDir: host.workspaceDir,\n memory: host.storage.memory,\n learningStore: host.learningStore,\n localLearningStore: host.localLearningStore,\n promptManager: host.promptManager,\n skillRegistry: host.skillRegistry,\n notificationDispatcher: host.notificationDispatcher,\n permissionRegistry: host.permissionRegistry,\n codingSessionPool: host.codingSessionPool,\n sandbox: host.sandbox,\n settings: host.settings,\n getEditFileCallback: () => host.editFileCallback,\n editFilesDedup: host._editFilesDedup,\n memoryAutoExtractor: host.memoryAutoExtractor,\n getSession: (id) => host.getSession(id),\n // RFC-318 M5:技能回路的驱动依赖。workspaceKey 惰性取——workspaceRef 在 App.start()\n // 才解析,注册时机早于该赋值。\n notifyPluginServices: (n) => host.notifyPluginServices(n),\n getWorkspaceKey: () => host.workspaceRef?.key,\n // RFC-318 M4b:起草作业走既有 AgentJobService(worktree 隔离 + 产 diff)。\n startSkillDraftJob: (input) => host.agentJobs.start(input),\n logger: host.logger,\n })\n host.agentJobNotify = result.agentJobNotify\n}\n","import { mkdir, readFile, open } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\nimport { genWorkspaceId } from '@x-otto/workspace'\nimport { atomicWriteFile } from '@x-otto/persistence'\n\nimport type { RemoteRef, WorkspaceIO } from '@x-otto/workspace'\n\n/**\n * node-fs `WorkspaceIO` 适配器(M58 前置)。\n *\n * 把 `@x-otto/workspace` 的注入式 resolver 接到真实文件系统 + 时钟 + id 生成。\n * `confirmTrust` 由调用方注入(TUI/CLI 提供交互提示);缺省 = 不传 → resolver fail-closed\n * (带 remote 的 marker 不自动信任,降级本地,RFC-034 规则3)。\n *\n * RFC-303 D7(M2 T303-05):`writeFile` 此前用裸 `writeFile`(truncate+写),非原子——\n * `writeMarker` 用它写 `.otto/workspace.json` 时,跨进程读者可能读到半写 JSON(窗口\n * 极小,marker 文件很小,但存在)。改用 `atomicWriteFile`(temp+fsync+rename+dir-fsync)\n * 消除 torn read。`writeFileExclusive` 用 `open(path, 'wx')`(O_CREAT|O_EXCL,文件系统级\n * 原子创建)实现并发首启创建互斥——两进程同时对空目录 createLocal 时只有一个能真正创建,\n * 另一个 EEXIST 后由 resolver.ts 读取复用其 id(见 resolver.ts create() 注释)。\n */\nexport function createNodeWorkspaceIO(opts?: {\n confirmTrust?: (remote: RemoteRef, markerDir: string) => Promise<boolean>\n /** 注入时钟(测试用);缺省取系统时间。 */\n now?: () => string\n /** 注入 id 生成(测试用);缺省 `genWorkspaceId`。 */\n genId?: () => string\n}): WorkspaceIO {\n return {\n async readFile(path) {\n try {\n return await readFile(path, 'utf8')\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw err\n }\n },\n async writeFile(path, content) {\n await mkdir(dirname(path), { recursive: true })\n await atomicWriteFile(path, content)\n },\n async writeFileExclusive(path, content) {\n await mkdir(dirname(path), { recursive: true })\n try {\n const handle = await open(path, 'wx')\n try {\n await handle.writeFile(content, 'utf8')\n } finally {\n await handle.close()\n }\n return true\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') return false\n throw err\n }\n },\n genId: opts?.genId ?? genWorkspaceId,\n now: opts?.now ?? (() => new Date().toISOString()),\n ...(opts?.confirmTrust ? { confirmTrust: opts.confirmTrust } : {}),\n }\n}\n","/** App lifecycle hooks: init/start/stop/shutdown ordering. */\nimport { join } from 'node:path'\nimport type { Devtools } from '@x-otto/devtools'\nimport type { WorkspaceRef, WorkspaceMarker } from '@x-otto/workspace'\nimport { createWorkspaceResolver, readMarker, writeMarker, workspaceKey } from '@x-otto/workspace'\nimport { createNodeWorkspaceIO } from './workspace-io'\nimport { markStartupPhase, traceStartupPhase, type Logger } from '@x-otto/shared'\nimport { globalProcessRegistry, globalProcessRuntime } from '@x-otto/runtime'\nimport type { AgentSession, AgentSessionInfo, AgentRuntime, FleetMonitor, MemoryGovernor, ResidencyGovernor, SessionPool as CodingSessionPool } from '@x-otto/runtime'\nimport type { AutoMemory, FileStateManager, OperationalLearningStore } from '@x-otto/memory'\nimport type { NotificationDispatcher } from '@x-otto/notification'\nimport type { HookRegistry } from '@x-otto/hooks'\nimport type { AuthStore, ProviderRegistry } from '@x-otto/ai'\nimport type { ToolRegistry } from '@x-otto/tools'\nimport type { SkillRegistry } from '@x-otto/skill'\nimport type { SettingsManager } from '@x-otto/setting'\nimport type { SqliteSessionRepository } from '@x-otto/session'\nimport type { AgentProfile } from '@x-otto/prompt'\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\nimport type { Orchestrator } from '@x-otto/orchestrator'\nimport type { AppStorage } from '../types'\nimport type { PluginModuleManager } from './plugin-module-manager'\nimport type { PluginProviderManager } from './plugin-provider-manager'\nimport type { SandboxController } from './sandbox-controller'\nimport type { SchedulerService } from '@x-otto/schedule'\nimport type { AgentJobService } from './agent-job-service'\nimport { discoverDeclarativeAgents } from '../agent/declarative-agents'\nimport { discoverDeclarativeTeams } from '../agent/declarative-teams'\nimport type { TeamDeclaration } from '../agent/declarative-teams'\nimport { disposePluginWorkerPool } from '../plugin/module-loader-isolated'\nimport { disposePluginServicePool } from '../plugin/plugin-service-pool'\n\n/**\n * `AppLifecycleHost` — `start()`/`stop()` 及其 4 个附属方法真实读写的窄依赖面。\n * 精确枚举自逐行读取 app.ts 原方法体(RFC-152 §4 规则3:不依赖 RFC 草案示例清单)。\n * 字段/方法可读写性(r/w)与原 App 类的实际访问模式一致。\n */\nexport interface AppLifecycleHost {\n // ── 前置守卫 ──\n ensureNotDisposed(): void\n ensureSettingsLoaded(): Promise<void>\n\n // ── workspace 身份 ──\n readonly workspaceDir: string\n workspaceRef?: WorkspaceRef\n pendingWorkspaceInit?: { root: string }\n /** 父级目录已存在 workspace marker(`findMarker` 在更上层命中),等待用户选择。 */\n parentWorkspaceExisted?: { parentRoot: string; currentRoot: string; marker: WorkspaceMarker }\n readonly _workspaceId?: string\n readonly _interactive: boolean\n readonly logger: Logger\n\n // ── 启动阶段:skills/agents/teams/plugin ──\n readonly storage: AppStorage\n readonly skillRegistry: SkillRegistry | null\n readonly settings: SettingsManager\n declarativeAgentProfiles: AgentProfile[]\n declarativeTeams: TeamDeclaration[]\n activePlugins(): DiscoveredPlugin[]\n readonly pluginModuleManager: PluginModuleManager\n readonly pluginProviderManager: PluginProviderManager\n _pendingProviderCapabilities: string[]\n /**\n * RFC-152 M2 补上:diagnoseTopoSortIssues 迁至 PluginTopologyManager 前,暂由 App 自身实现代理。\n * RFC-163 D5:签名改为 async(诊断内部追加缺失依赖自动补装尝试)。\n */\n diagnoseTopoSortIssues(): Promise<void>\n\n // ── MCP 非阻塞启动 ──\n readonly mcpController: { start(): Promise<{ connected: number; failed: number }>; stop(): Promise<void> }\n syncSessionTools(): number\n onMcpStartupSettled?: (result: { connected: number; failed: number }) => void\n pendingMcpStartupResult?: { connected: number; failed: number }\n\n // ── session restore ──\n readonly codingSessionPool: CodingSessionPool\n /** RFC-157 M2 T2.4:原 `sessionFacade.getActiveSession()` 回收后,host 直接暴露该读方法\n * (App 类自身已实现同名公共方法,天然满足)。 */\n getActiveSession(): AgentSession | undefined\n listSessions(filter?: { workspaceId?: string }): AgentSessionInfo[]\n getSession(id: string): AgentSession | undefined\n readonly notificationDispatcher: NotificationDispatcher\n\n // ── devtools / memory governor ──\n readonly devtools: Devtools | null\n readonly _memoryGovernor: MemoryGovernor\n /** RFC-178 触发器 C:驻留收紧系数源(分级信号在下方 setCallbacks 接线)。 */\n readonly _residencyGovernor: ResidencyGovernor\n /** RFC-196 M2:进程群预算感知(采样落 trace + 超限通知在下方接线)。 */\n readonly _fleetMonitor: FleetMonitor\n\n // ── sweepOrphanTraceFiles ──\n readonly sessionStorageDir?: string\n readonly sessionRepoRef?: { current?: SqliteSessionRepository }\n\n // ── stop() 专属 ──\n disposed: boolean\n readonly processRegistryUnsubs: Array<() => void>\n readonly processOwnerId: string\n readonly scheduleServiceInstance?: SchedulerService\n readonly agentJobServiceInstance?: AgentJobService\n readonly toolRegistry: ToolRegistry\n readonly orchestrator: Orchestrator\n readonly agentRuntime: AgentRuntime\n readonly ownedHookNames: string[]\n readonly hookRegistry: HookRegistry\n readonly sandbox: SandboxController\n globalLogSubscription: (() => void) | null\n readonly _authStore: AuthStore\n readonly userMemory: AutoMemory\n readonly fileStateManager: FileStateManager\n /** 亦被 RFC-164 M164-2 D4b `setWorkspaceMemorySyncEnabled` 的最终同步逻辑复用。 */\n readonly learningStore: OperationalLearningStore\n readonly providerRegistry: ProviderRegistry\n}\n\n/**\n * `start()` 原方法体逐字迁移。见 app.ts 原实现的完整设计注释(RFC-150 D1/D7、RFC-115、\n * RFC-118 M2、RFC-102 D3 等历史决策均在此保留)。\n */\nexport async function startApp(host: AppLifecycleHost): Promise<void> {\n host.ensureNotDisposed()\n await host.ensureSettingsLoaded()\n\n if (!host.workspaceRef && !host.pendingWorkspaceInit && !host.parentWorkspaceExisted) {\n try {\n const resolver = createWorkspaceResolver(createNodeWorkspaceIO())\n // 交互态 + 未显式 --id:marker 缺失时不自动创建(autoCreate:false)——防止本目录首次\n // 打开/`.otto/workspace.json` 被清理后静默生成新随机 id,导致 sessions.db 里旧会话\n // 因 workspace_key 对不上而\"消失\"(用户看不到、无从 /resume)。改为等信任门确认后\n // 由 cli 层调 confirmWorkspaceInit() 显式建立身份。headless(脚本/CI/service)与\n // `--id` 场景零感知:autoCreate 缺省 true,直接走原自动创建路径。\n const idOpt = host._workspaceId ? { id: host._workspaceId } : undefined\n const result = await resolver.resolveLocal(host.workspaceDir, {\n ...idOpt,\n autoCreate: host._interactive && !idOpt ? false : true,\n })\n if ('kind' in result && result.kind === 'uninitialized') {\n host.pendingWorkspaceInit = { root: result.root }\n } else if ('kind' in result && result.kind === 'parentWorkspaceExisted') {\n // 父级目录已存在 marker(非当前目录),不静默采用——等 cli 层提示用户选择\n // \"使用上级 workspace\"还是\"在当前目录创建新 workspace\"。\n host.parentWorkspaceExisted = {\n parentRoot: result.parentRoot,\n currentRoot: result.currentRoot,\n marker: result.marker,\n }\n } else {\n host.workspaceRef = result as WorkspaceRef\n }\n } catch (err) {\n host.logger.warn(\n { err, workspaceDir: host.workspaceDir },\n 'Workspace resolution failed; continuing without workspace identity. ' +\n 'Sessions previously saved under a workspace key will not be restored.',\n )\n }\n }\n\n if (host.storage.autoMemory) {\n await host.storage.autoMemory.load()\n }\n\n // RFC-150 D7/M0:skills/agents/teams 发现 + 插件代码模块加载合并为一个打点段\n // (\"plugin+skill discovery\")——这是 RFC-150 §1.1 时间线里\"插件编译 ~67ms\"一项的\n // 度量口径,与 MCP start 分开计时以验证 D1/D5 各自的优化收益。\n await traceStartupPhase('skills/agents/teams discovery + plugin module load', async () => {\n if (host.skillRegistry) {\n await host.skillRegistry.discover()\n for (const name of host.settings.get('disabled_skills') ?? []) {\n host.skillRegistry.disable(name)\n }\n const disabledPlugins = new Set(host.settings.get('disabled_plugins') ?? [])\n if (disabledPlugins.size > 0) {\n for (const [name, reg] of host.skillRegistry.getAll()) {\n if (\n reg.source?.type === 'plugin' &&\n reg.source.id &&\n disabledPlugins.has(reg.source.id)\n ) {\n host.skillRegistry.disable(name)\n }\n }\n }\n }\n\n host.declarativeAgentProfiles = await discoverDeclarativeAgents(host.workspaceDir, {\n plugins: host.activePlugins(),\n })\n\n host.declarativeTeams = await discoverDeclarativeTeams(host.workspaceDir)\n\n // 启动时加载插件代码模块(RFC-082 M082-04-06):plugin.ts 的 hooks + computed 命令/agents/skills。\n // 此前 reloadPluginModules 仅在 reloadCapabilities(事件驱动:install/uninstall//reload)跑,\n // 故新装插件的 definePlugin hooks/computed 贡献开机 dormant、要等 /reload——本调用补齐 boot 激活。\n // 顺序对齐 reloadCapabilities(skills.discover → agents → teams → reloadPluginModules);幂等可与后续 reload 共存。\n const appendedPluginAgents = await host.pluginModuleManager.reload(\n new Set(host.declarativeAgentProfiles.map((p) => p.name)),\n )\n host.declarativeAgentProfiles.push(...appendedPluginAgents)\n // RFC-105 M105-2-03b:同 M082-04-06 教训——启动时也要加载,不能只在 reloadCapabilities(事件驱动)跑,\n // 否则新装插件的声明式 provider 开机 dormant、要等 /reload。\n // RFC-125 D5:boot 时也要捕获 pendingCapabilityPluginIds——否则\"待授权\"提示只有 /reload 之后才出现,\n // 用户首次启动 otto(尚未手动 /reload 过)时看不到提示,恰好是本 RFC 要修的原始症状最常触发的时机。\n const bootProviderReload = await host.pluginProviderManager.reload()\n host._pendingProviderCapabilities = bootProviderReload.pendingCapabilityPluginIds\n // RFC-132 D5(同上 RFC-125 教训):boot 时也要诊断拓扑问题——否则用户首次启动\n // (尚未手动 /reload 过)时看不到 cyclic/missingDeps 告警,插件被静默剔除却无感知。\n // RFC-163 D5:诊断内部现在会尝试自动补装缺失依赖(如 otto-wire-protocols),故此处\n // 也需要 await(原同步调用改为 async,见 PluginTopologyManager.diagnose)。\n await host.diagnoseTopoSortIssues()\n })\n\n // RFC-150 D1:MCP 连接非阻塞化——交互模式下最慢的 MCP server 子进程冷启动不再拖住\n // \"可交互\"这一时刻(实测曾占 app.start() 总耗时的 ~67%,是启动瓶颈第一因,见 RFC-150\n // §1.1)。headless(print/json,`host._interactive===false`)保留 await——单轮模式的\n // 首个 prompt 可能立即需要 MCP 工具,语义不能变(RFC-150 重要事项规则 1,硬红线)。\n //\n // 非阻塞路径完成后:① syncSessionTools() 把新工具推给已创建的活 session(D2,session\n // 创建早于 MCP 连接完成时,其冻结的 tools 快照原本不含 mcp__ 工具,靠此步补齐);\n // ② onMcpStartupSettled 回调通知宿主(interactive.ts 晚绑定 toast,用户可感知连接结果,\n // 不静默——RFC-150 规则 6)。失败时 mcpController.start() 自身已 fail-soft(Promise.\n // allSettled),此处 catch 只兜途中抛出的意外错误(理论上不会发生,防御纵深)。\n const runMcpStart = () =>\n traceStartupPhase('mcpController.start()', () => host.mcpController.start())\n .then((result) => {\n host.syncSessionTools()\n if (host.onMcpStartupSettled) {\n host.onMcpStartupSettled(result)\n } else {\n // 回调尚未晚绑定(TUI 还没就绪)——锁存,setMcpStartupCallback 会补发。\n host.pendingMcpStartupResult = result\n }\n return result\n })\n .catch((err) => {\n host.logger.warn(\n { err: err instanceof Error ? err.message : String(err) },\n 'mcpController.start() failed unexpectedly (non-blocking path)',\n )\n return { connected: 0, failed: 0 }\n })\n\n if (host._interactive) {\n void runMcpStart()\n } else {\n await runMcpStart()\n }\n\n if (host.pendingWorkspaceInit) {\n // workspace 身份待信任确认(见上方 autoCreate:false 分支)——restoreAll 依赖 workspaceRef.key\n // 做 SQLite 分区,此刻 ref 未定,跳过(不误用 'default' 分区,也不误判\"本目录无历史会话\")。\n // cli 层需在信任确认后调 confirmWorkspaceInit(),其内部会补跑这段(含通知 attach)。\n host.logger.info(\n { workspaceDir: host.workspaceDir },\n 'Workspace identity pending trust confirmation; session restore deferred',\n )\n } else if (host.parentWorkspaceExisted) {\n // 父级目录已存在 marker(非当前目录),等待用户选择\"使用上级\"还是\"在当前创建\"——\n // 选择前 workspaceRef 未定,暂不 restore session。\n host.logger.info(\n { workspaceDir: host.workspaceDir, parentRoot: host.parentWorkspaceExisted.parentRoot },\n 'Parent workspace detected; waiting for user choice (adopt parent or create new)',\n )\n } else if (host._interactive) {\n await traceStartupPhase('completeWorkspaceDependentStartup (session restoreAll)', () =>\n completeWorkspaceDependentStartup(host),\n )\n } else {\n // RFC-154 D2:headless(otto --print/--json,非 --continue)跳过 restoreAll——会话来源是\n // 本轮 createSession()(全新)或 --continue 走的独立 sessionManager.restore(id) 单条按需路径,\n // restoreAll 批量恢复的池内会话产出零消费(headless 单轮跑完即退出)。孤儿 trace 扫除\n // (completeWorkspaceDependentStartup 内 sweepOrphanTraceFiles)与 restoreAll 无数据依赖,\n // 仍需单独调用并保留 trace 打点(可观测性——否则 headless 下看不到该阶段是否执行)。\n void traceStartupPhase('sweepOrphanTraceFiles (headless)', () => sweepOrphanTraceFiles(host))\n }\n\n if (host.devtools) {\n await host.devtools.start()\n }\n\n // RFC-115 追补:进程级内存采样落 trace(诊断专用,见 engine-nodes.ts 的\n // 'process.memory.sample' 节点)——写入当前活跃会话的 trace 流,供事后从 trace\n // 文件直接重建内存曲线,不需要像此前那样临时外挂采样脚本诊断。MemoryGovernor\n // 本身不知道 trace/session 的存在(onSample 只回传采样结果),落哪个 streamId\n // 由本层决定。无活跃会话时静默跳过(fail-soft,不影响采样本身)——多会话池场景\n // 下\"进程级事件该挂哪个会话\"没有唯一正确答案,选择挂当前活跃会话是最贴近用户\n // 实际查看路径的近似(其查看的 trace 文件就是当前会话的)。\n host._memoryGovernor.setCallbacks({\n // 2026-07-19(OOM 终局 review C1):onWarning 软降级接线从 CLI 下沉到 app 层——\n // 此前唯一注册点是 interactive.ts 的 registerMemoryGovernorCallbacks,headless/serve/\n // agent-job 长跑进程 70% 阈值命中时无任何响应(只有采样落 trace + 驻留收紧),直到\n // 撞 4GB 硬顶 SIGABRT。compactNow() 不依赖 TUI(真实 LLM 摘要压缩 + recordCompaction\n // 写回),app 层可安全执行。interactive 模式下 CLI 层稍后 setCallbacks({onWarning})\n // 按逐字段合并语义覆盖本注册(其版本额外带 toast 反馈),两层不叠加、不冲突。\n // headless critical 级的自重启/有界退出:RFC-222 D1 补齐(此前只记录见 OOM review\n // 跟踪项,跑到 SIGABRT 硬崩溃,可能丢失未落盘会话数据)。\n onWarning: () => {\n const active = host.getActiveSession()\n if (!active) return\n void active.compactNow().catch((err) => {\n host.logger.warn(\n { err: err instanceof Error ? err.message : String(err) },\n '[memory-governor] headless compactNow (soft degrade) failed',\n )\n })\n },\n // RFC-222 D1:headless/service critical 级有界响应——缺省 'log-only'(零行为变化,\n // 安全默认,避免对现有部署引入意外的进程终止行为),opt-in 'graceful-exit' 时先\n // 走既有 stopApp() 收尾(复用现有 stop 逻辑)再主动退出(退出码非 0,便于外部\n // supervisor/PM2/systemd 识别并按需重启)。interactive 模式下 CLI 层\n // registerMemoryGovernorCallbacks(interactive-memory-governor.ts)稍后\n // setCallbacks({onCritical}) 按逐字段合并语义覆盖本注册(其版本走 TUI Dialog 确认\n // 流程),两层不叠加、不冲突——与上方 onWarning 同一接线模式。\n //\n // 必须 fire-and-forget(`memory-governor.ts` tick() 内同步调用 onCritical,不可\n // await 阻塞采样循环);stopApp 失败也必须退出(.finally 确保),不能因收尾失败\n // 卡在 critical 态不退出。\n onCritical: (sample) => {\n const policy = process.env['OTTO_HEADLESS_OOM_POLICY'] ?? 'log-only'\n host.logger.error(\n { policy, level: sample.level, usedPct: sample.usedPct, heapUsed: sample.heapUsed },\n '[memory-governor] critical memory threshold reached (headless/service)',\n )\n if (policy !== 'graceful-exit') return\n void stopApp(host).finally(() => process.exit(1))\n },\n onSample: (sample) => {\n // RFC-178 触发器 C:内存分级信号 → 驻留收紧系数(app 层接线,headless/serve 同享;\n // 复用 onSample 而非 onLevelChange——后者已被 TUI 层注册(footer 指示器),\n // setCallbacks 逐字段合并语义下 app 层注册会静默覆盖它(RFC-178 风险 3 约束)。\n // setLevel 幂等,每 tick 调用无害。\n host._residencyGovernor.setLevel(sample.level)\n // OOM review G3(2026-07-19):user-timing 缓冲二道防线。根治是 bin/otto.js 的\n // NODE_ENV=production 兜底(a7eaa826,活体验证 38.5h 长跑 measures=0)——但该\n // 修复是单点防御:显式 NODE_ENV=development 调试、绕过 bin 入口直跑 dist、或\n // 任何未来引入无消费方 performance.measure/mark 的第三方库,缓冲会重新无界累积\n //(事故实测 17h 246 万条 / 2GB+ 驻留)。此处借 30s 采样节奏做护栏:超 5 万条\n //(约几十 MB,远低于事故量级)即清空 + warn。production 下 getEntriesByType\n // 返回空数组,开销为零;本仓自身无 user-timing 消费方(全仓零 getEntriesByType\n // 业务调用),清空无副作用。\n const measureCount = performance.getEntriesByType('measure').length\n if (measureCount > 50_000) {\n performance.clearMeasures()\n performance.clearMarks()\n host.logger.warn(\n { measureCount },\n '[memory-governor] user-timing buffer exceeded guard threshold — cleared (likely a dev-mode library leaking performance.measure; see a7eaa826)',\n )\n }\n const activeSessionId = host.getActiveSession()?.id\n if (!activeSessionId) return\n void host.storage.traceStore\n .append(activeSessionId, {\n // 独立评审 P2-2:复用 sample.ts(MemoryGovernor 内部注入的 now() 时间源)而非\n // 本层自行 Date.now(),保证测试用 fake timer 时 trace 时间戳与采样时间同源。\n ts: sample.ts,\n sessionId: activeSessionId,\n kind: 'lifecycle',\n node: 'process.memory.sample',\n payload: {\n // RFC-170 D3:多个 otto 进程可 active 同一会话(/resume 同会话、崩溃后新进程\n // resume)时都会写本 trace 流,此前无字段可区分样本归属,诊断内存曲线时\n // 曾两次将\"多进程交错写入\"误判为\"重启振荡\"/\"空闲期泄漏\"(RFC-170 §2 C2)。\n // 加 pid 使事后分析可按进程分组,不改变既有消费方读取的字段。\n pid: process.pid,\n level: sample.level,\n heapUsed: sample.heapUsed,\n heapSizeLimit: sample.heapSizeLimit,\n usedPct: Number(sample.usedPct.toFixed(2)),\n // RFC-178 P2:观察维度(不参与分级)——rss 覆盖容器 OOM killer 视角,\n // external 覆盖图片 Buffer/ArrayBuffer 等 V8 堆外驻留。诊断内存曲线时可\n // 区分\"V8 堆压力\"与\"堆外/进程整体压力\"两种形态(codex #25015 型 MCP 子\n // 进程树泄漏在 rss 有感、heapUsed 无感)。\n rss: sample.rss,\n external: sample.external,\n },\n })\n .catch((err) => {\n host.logger.warn({ err: err instanceof Error ? err.message : String(err) }, '[memory-governor] trace sample append failed')\n })\n },\n })\n\n // RFC-118 M2:内存治理采样在启动流程末尾开启(session restore 之后)——\n // 此前构造期即 start,初期分级变化时消费回调尚未绑定,纯空转。\n host._memoryGovernor.start()\n\n // RFC-196 M2:进程群采样落 trace(同 memory.sample 模式挂当前活跃会话,无会话静默跳过)。\n // 超限通知:FleetMonitor 内部已 logger.warn(headless 可感知基线);interactive 下 CLI 层\n // setCallbacks({onBudgetExceeded}) 追加 TUI 通知(逐字段合并,不覆盖本处 onSample)。\n host._fleetMonitor.setCallbacks({\n onSample: (sample) => {\n const activeSessionId = host.getActiveSession()?.id\n if (!activeSessionId) return\n void host.storage.traceStore\n .append(activeSessionId, {\n ts: sample.ts,\n sessionId: activeSessionId,\n kind: 'lifecycle',\n node: 'process.fleet.sample',\n payload: {\n pid: process.pid,\n processCount: sample.processCount,\n totalRssMb: sample.totalRssMb,\n suspectedOrphanCount: sample.suspectedOrphanCount,\n maxGeneration: sample.maxGeneration,\n },\n })\n .catch((err) => {\n host.logger.warn({ err: err instanceof Error ? err.message : String(err) }, '[fleet-monitor] trace sample append failed')\n })\n },\n })\n host._fleetMonitor.start()\n\n // RFC-150 D7/M0:app.start() 全流程结束标记——与 cli.ts 的 traceStartupPhase('app.start()')\n // 外层耗时对照,验证内部各子步打点之和与外层总耗时一致(无遗漏相位)。\n markStartupPhase('app.start() internal phases complete')\n\n host.logger.info('App started')\n}\n\n/**\n * workspace 身份就绪后才能跑的启动收尾——会话 restoreAll(按 workspaceRef.key 分区)+\n * 对称 attach 通知派发器 + 孤儿 trace 扫除。从 start() 抽出,供 confirmWorkspaceInit()\n * 在信任确认、marker 真正创建后补跑(见 pendingWorkspaceInit 分支)。\n */\nexport async function completeWorkspaceDependentStartup(host: AppLifecycleHost): Promise<void> {\n if (host.codingSessionPool.autoPersist) {\n const restored = await host.codingSessionPool.restoreAll()\n if (restored > 0) {\n host.logger.info({ count: restored }, 'Sessions restored from storage')\n // B7:restoreAll 在 pool 层复活会话,绕过 createSession → 必须在此对称 attach 通知派发器,\n // 否则 `otto --continue`(主多轮流)恢复的会话全程无完成/出错/需批准通知。attach 幂等(先 detach)。\n for (const info of host.listSessions()) {\n const s = host.getSession(info.id)\n if (s) host.notificationDispatcher.attach(s)\n }\n }\n }\n\n // RFC-102 D3:孤儿 trace/checkpoint 启动扫除(fire-and-forget,不阻塞启动)。\n // 放 restoreAll 之后——活跃会话(含 transient fork)已在内存池,误删保护并集完整。\n void sweepOrphanTraceFiles(host)\n}\n\n/** 是否有待信任确认的 workspace 身份(marker 缺失,交互态触发,见 start() autoCreate:false 分支)。 */\nexport function needsWorkspaceInit(host: AppLifecycleHost): boolean {\n return !!host.pendingWorkspaceInit\n}\n\n/** 是否有父级 workspace 存在(非当前目录),等待用户选择\"使用上级\"还是\"在当前创建\"。 */\nexport function needsParentWorkspaceDecision(host: AppLifecycleHost): boolean {\n return !!host.parentWorkspaceExisted\n}\n\n/**\n * 用户选择\"使用上级 workspace\"后调用——采用父级 marker 的 workspace 身份,\n * 补跑被 start() 推迟的 workspace 依赖启动收尾(session restoreAll 等)。\n *\n * fail-soft(与 confirmWorkspaceInit 对齐):抛错时恢复 parentWorkspaceExisted,\n * 本次会话降级为无 workspace 身份继续运行。\n */\nexport async function adoptParentWorkspace(host: AppLifecycleHost): Promise<WorkspaceRef | undefined> {\n const pending = host.parentWorkspaceExisted\n if (!pending) return host.workspaceRef\n host.parentWorkspaceExisted = undefined\n try {\n // resolveLocal 在父级目录上调用,复用现有 marker(同 headless 静默采用路径)。\n // 父级目录已有 marker,resolveLocal 必然返回 WorkspaceRef(不可能是 uninitialized 或 parentWorkspaceExisted)。\n const resolver = createWorkspaceResolver(createNodeWorkspaceIO())\n host.workspaceRef = (await resolver.resolveLocal(pending.parentRoot)) as WorkspaceRef\n await completeWorkspaceDependentStartup(host)\n return host.workspaceRef\n } catch (err) {\n host.parentWorkspaceExisted = pending\n host.logger.warn(\n { err, parentRoot: pending.parentRoot },\n 'Failed to adopt parent workspace; continuing without workspace identity. ' +\n 'Will retry on next startup.',\n )\n return undefined\n }\n}\n\n/**\n * 用户选择\"在当前目录创建新 workspace\"后调用——在当前目录写入 `.otto/workspace.json`,\n * 补跑被 start() 推迟的 workspace 依赖启动收尾。\n *\n * fail-soft:抛错时恢复 parentWorkspaceExisted,本次会话降级继续运行。\n */\nexport async function createLocalWorkspace(host: AppLifecycleHost): Promise<WorkspaceRef | undefined> {\n const pending = host.parentWorkspaceExisted\n if (!pending) return host.workspaceRef\n host.parentWorkspaceExisted = undefined\n try {\n const resolver = createWorkspaceResolver(createNodeWorkspaceIO())\n host.workspaceRef = await resolver.createLocal(pending.currentRoot)\n await completeWorkspaceDependentStartup(host)\n return host.workspaceRef\n } catch (err) {\n host.parentWorkspaceExisted = pending\n host.logger.warn(\n { err, currentRoot: pending.currentRoot },\n 'Failed to create new workspace; continuing without workspace identity. ' +\n 'Will retry on next startup.',\n )\n return undefined\n }\n}\n\n/**\n * 信任确认后调用——真正创建 `.otto/workspace.json`(写 marker),并补跑被 start() 推迟的\n * workspace 身份相关启动收尾(session restoreAll 等)。非 pendingWorkspaceInit 态调用是 no-op\n * (幂等,防重复调用误触发二次 restoreAll)。\n *\n * 竞态加固(终局 review):snapshot + 提前清空 pendingWorkspaceInit 放在 `await` 之前——\n * 若未来新增调用方在本次 await 期间又触发一次调用(当前 TUI 单线程无此路径,但方法本身\n * 不应依赖调用方纪律),第二次调用会看到 pendingWorkspaceInit 已为 undefined,直接走\n * no-op 分支返回现有 workspaceRef,不会重复执行 createLocal/completeWorkspaceDependentStartup\n * (后者含 notificationDispatcher.attach,重复调用会产生重复通知)。\n *\n * fail-soft(与 start() 内 resolveLocal 的 catch 分支对齐):`createLocal`/\n * `completeWorkspaceDependentStartup` 抛错时(磁盘只读/权限问题等)不再让异常穿透崩溃整个\n * CLI 进程——捕获后把 `pendingWorkspaceInit` 恢复为待处理态(而非维持\"提前清空\"造成的\n * `needsWorkspaceInit()===false` 假阴性),本次会话降级为无 workspace 身份继续运行(同\n * `start()` 里 `pendingWorkspaceInit` 分支:跳过 session restoreAll),下次启动\n * `resolveLocal` 会重新探测到 marker 缺失、重新走信任门重试创建,不会卡死在\"当次失败即\n * 永久放弃\"。\n */\nexport async function confirmWorkspaceInit(host: AppLifecycleHost): Promise<WorkspaceRef | undefined> {\n const pending = host.pendingWorkspaceInit\n if (!pending) return host.workspaceRef\n host.pendingWorkspaceInit = undefined\n try {\n host.workspaceRef = await createWorkspaceResolver(createNodeWorkspaceIO()).createLocal(\n pending.root,\n )\n await completeWorkspaceDependentStartup(host)\n return host.workspaceRef\n } catch (err) {\n host.pendingWorkspaceInit = pending\n host.logger.warn(\n { err, root: pending.root },\n 'Workspace identity creation failed; continuing without workspace identity. ' +\n 'Will retry on next startup.',\n )\n return undefined\n }\n}\n\n/**\n * RFC-164 M164-2 D2:`otto workspace memory-sync enable/disable` 命令的落点——切换\n * workspace 记忆远端同步 opt-in。读取现有 marker(保留全部既有字段:id/name/createdAt/\n * remote),只改 `memorySyncEnabled`,写回并刷新 `host.workspaceRef`(惰性 getter 消费方\n * 如 `HttpMemoryStore.isWorkspaceSyncEnabled()` 下次 I/O 时即读到新值,无需重启/重建\n * store,见 `storage-wiring.ts` 惰性 getter 设计)。\n *\n * 无 `workspaceRef.root`(V 形态/未初始化)时返回 `false`(无 marker 可写,调用方应提示\n * 用户\"当前 workspace 无本地身份\")。\n */\nexport async function setWorkspaceMemorySyncEnabled(\n host: AppLifecycleHost,\n enabled: boolean,\n): Promise<boolean> {\n const root = host.workspaceRef?.root\n if (!root) return false\n\n const io = createNodeWorkspaceIO()\n const existing = await readMarker(root, io)\n if (!existing) return false\n\n // RFC-164 M164-2 D4b:\"最终同步\"——disable 前(marker 仍是 enabled 状态,惰性 getter\n // 仍判定走远端)强制刷新一次 autoMemory/learningStore,把远端最新内容拉取覆盖本地缓存,\n // 避免用户关闭同步后看到的是 disable 前那一刻的 stale 本地版本。仅在\"从 enabled 切到\n // disabled\"时执行(enable 场景无需拉取——远端此刻还没有本地未同步的历史数据需要\"追平\")。\n if (!enabled && existing.memorySyncEnabled) {\n try {\n await host.storage.autoMemory?.load()\n } catch {}\n try {\n await host.learningStore.refresh()\n } catch {}\n }\n\n const updated: WorkspaceMarker = enabled\n ? { ...existing, memorySyncEnabled: true }\n : { id: existing.id, name: existing.name, createdAt: existing.createdAt, ...(existing.remote ? { remote: existing.remote } : {}) }\n\n await writeMarker(root, updated, io)\n\n host.workspaceRef = {\n key: workspaceKey(updated.id),\n id: updated.id,\n backend: host.workspaceRef!.backend,\n root,\n ...(updated.remote ? { remote: updated.remote } : {}),\n ...(updated.memorySyncEnabled ? { memorySyncEnabled: true as const } : {}),\n }\n\n return true\n}\n\n/**\n * 孤儿判定 = 文件 streamId ∉(关系库全量会话 id ∪ 内存池活跃会话 id)。\n * 仅本地 disk 后端执行(remote 后端 trace 归远端服务治理)。\n * OTTO_TRACE_SWEEP: off=关闭 / dry-run=只记日志 / 缺省=删除。\n */\nexport async function sweepOrphanTraceFiles(host: AppLifecycleHost): Promise<void> {\n const dir = host.sessionStorageDir\n const repo = host.sessionRepoRef?.current\n if (!dir || !repo) return\n const rawMode = process.env['OTTO_TRACE_SWEEP']\n if (rawMode === 'off') return\n try {\n const { sweepOrphanTraces } = await import('./trace-orphan-sweeper')\n const live = new Set<string>(await repo.listAllSessionIds())\n for (const info of host.codingSessionPool.listSessions()) live.add(info.id)\n await sweepOrphanTraces({\n dirs: [join(dir, 'trace'), join(dir, 'checkpoint')],\n liveSessionIds: live,\n logger: host.logger,\n mode: rawMode === 'dry-run' ? 'dry-run' : 'delete',\n })\n } catch (error) {\n host.logger.warn(\n { err: error instanceof Error ? error.message : String(error) },\n 'Orphan trace sweep failed',\n )\n }\n}\n\n/** `stop()` 原方法体逐字迁移。 */\nexport async function stopApp(host: AppLifecycleHost): Promise<void> {\n if (host.disposed) {\n return\n }\n host.disposed = true\n\n const errors: Error[] = []\n\n // safeStop: 每个子系统独立 try/catch,一个 fail 不影响后续\n const safeStop = async (label: string, fn: () => Promise<void> | void): Promise<void> => {\n try {\n await fn()\n } catch (e) {\n errors.push(new Error(`stop:${label}: ${(e as Error).message}`))\n }\n }\n\n // Phase: 持久化(现有顺序居首,规则 10 护栏:若变更须记录理由)\n if (host.codingSessionPool.autoPersist) {\n await safeStop('saveSessions', () => host.codingSessionPool.saveAll())\n }\n\n // Phase: 子进程清退\n // processRegistry 注销监听(safeStop 包裹——任一 unsub throw 不得中断后续清退,规则 10)\n await safeStop('processRegistryUnsubs', () => {\n for (const unsub of host.processRegistryUnsubs) { unsub() }\n ;(host.processRegistryUnsubs as unknown[]).length = 0\n })\n\n await safeStop('bgProcesses', () => globalProcessRegistry.killAllForOwner(host.processOwnerId))\n\n // MCP stop 从原 step 13 前移到此(子进程清退段)\n await safeStop('mcpController', () => host.mcpController.stop())\n\n // RFC-085 M4:清退接入 ProcessRuntime 的所有子系统(LSP/CLI shell/hook/binary/ripgrep 等)。\n // killAll 杀非 keepAlive(含 pinned LSP——显式清退非 LRU 驱逐),keepAlive(OAuth 浏览器)豁免。\n await safeStop('processRuntime', () => globalProcessRuntime.killAll())\n\n // Phase: sync dispose 链(原 steps 5-12,逐一 safeStop 包裹)\n // M87-10:scheduler.stop() 必须在 App 级收尾(覆盖 SIGTERM/SIGHUP/看门狗全路径),\n // 不能只靠 interactive.ts 的 finishInteractive()(仅正常 TUI 退出触发)——否则信号退出下\n // fire-ownership 的 O_EXCL 锁不释放,下个进程要等 5min STALE_MS 才能接管调度。\n await safeStop('scheduleService', () => host.scheduleServiceInstance?.stop())\n await safeStop('memoryGovernor', () => host._memoryGovernor.dispose())\n await safeStop('fleetMonitor', () => host._fleetMonitor.dispose())\n await safeStop('agentJobs', () => host.agentJobServiceInstance?.dispose())\n await safeStop('settings', () => host.settings.dispose())\n await safeStop('toolRegistry', () => host.toolRegistry.dispose())\n await safeStop('orchestrator', () => host.orchestrator.dispose())\n await safeStop('notification', () => host.notificationDispatcher.dispose())\n await safeStop('agentRuntime', () => host.agentRuntime.dispose())\n\n await safeStop('hooks', () => {\n for (const name of host.ownedHookNames) {\n host.hookRegistry.unregister(name)\n }\n host.ownedHookNames.length = 0\n })\n await safeStop('pluginModules', () => host.pluginModuleManager.cleanup())\n await safeStop('pluginProviders', () => host.pluginProviderManager.cleanup())\n await safeStop('pluginWorkers', () => disposePluginWorkerPool())\n // RFC-287 M3:常驻服务池独立于上面两池,必须单独收敛——service worker 是长生命周期的,\n // 不随插件 unload 自然消失,宿主停机时若不显式 dispose 会留下悬空 worker。\n await safeStop('pluginServices', () => disposePluginServicePool())\n\n // Phase: 运行时清退\n await safeStop('sandbox', () => host.sandbox.dispose())\n if (host.devtools) {\n await safeStop('devtools', () => host.devtools!.stop())\n }\n await safeStop('logSub', () => {\n if (host.globalLogSubscription) {\n host.globalLogSubscription()\n host.globalLogSubscription = null\n }\n })\n\n // Phase: stores close\n await safeStop('traceStore', () => host.storage.traceStore.close?.())\n await safeStop('checkpointStore', () => host.storage.checkpointStore.close?.())\n // authStore 的跨进程 auth.json watcher(见 AuthStore.dispose)——不清理会在进程退出前\n // 持有一个悬空 fs.watch 句柄,阻碍进程干净退出。\n await safeStop('authStore', () => host._authStore.dispose())\n\n if (errors.length > 0) {\n host.logger.warn(\n { count: errors.length, errors: errors.map(e => e.message) },\n 'stop: %d subsystems reported errors',\n )\n }\n host.logger.info('App stopped')\n}\n","/**\n * app-session-export.ts — 会话归档导出(RFC-199 M1,从 app.ts 纯搬迁)。\n *\n * 原 app.ts 的 exportSessionArchive 方法逐字搬迁至此。\n * App 类保留公共 API 签名,方法体改为一行委托至本模块。\n *\n * 纯搬迁纪律:行为不变、公共 API 签名不变。\n */\nimport { createHash } from 'node:crypto'\nimport { writeFile, readFile, mkdir } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport { reassembleSnapshot } from '@x-otto/session'\nimport type { SqliteSessionRepository } from '@x-otto/session'\nimport type { SessionArchiveExport } from '../types'\n\nexport async function exportSessionArchive(\n sessionId: string,\n outPath: string,\n sessionRepoRef: { current?: SqliteSessionRepository },\n): Promise<SessionArchiveExport> {\n const repo = sessionRepoRef.current\n if (!repo) {\n throw new Error('导出归档仅支持关系化 SQLite 后端(sessions.db)。当前后端不支持。')\n }\n const snap = await reassembleSnapshot(sessionId, repo)\n if (!snap) {\n throw new Error(`会话 ${sessionId.slice(0, 8)} 不存在或无法加载`)\n }\n const jsonStr = JSON.stringify(snap, null, 2)\n const preHash = createHash('sha256').update(jsonStr, 'utf-8').digest('hex')\n\n await mkdir(dirname(outPath), { recursive: true })\n await writeFile(outPath, jsonStr, 'utf-8')\n\n const written = await readFile(outPath, 'utf-8')\n const postHash = createHash('sha256').update(written, 'utf-8').digest('hex')\n if (preHash !== postHash) {\n throw new Error(\n `Checksum 校验失败:写入前 sha256=${preHash.slice(0, 16)},读回 sha256=${postHash.slice(0, 16)}。` +\n `导出文件 ${outPath} 可能已损坏,操作整体失败。`,\n )\n }\n\n return {\n entryCount: snap.entries.length,\n checksum: preHash,\n path: outPath,\n }\n}\n","/**\n * app-capability-reload.ts — 能力热重载编排(RFC-199 M1,从 app.ts 纯搬迁)。\n *\n * 原 app.ts 的 reloadCapabilities 方法体逐字搬迁至此。\n * App 类保留公共 API 签名,方法体改为一行委托至本模块。\n *\n * 纯搬迁纪律:行为不变、公共 API 签名不变。\n */\nimport type { CapabilityReloader } from './capability-reloader'\nimport type { AgentProfile } from '@x-otto/prompt'\nimport type { TeamDeclaration } from '../agent/declarative-teams'\nimport type { PanelBackendController } from '../plugin/panel-backend-controller'\n\nexport interface ReloadCapabilitiesInput {\n capabilityReloader: CapabilityReloader\n /**\n * reload() 返回后、诊断/工具重同步/面板重订阅三个副作用**之前**执行的实例字段回写\n * (declarativeAgentProfiles/declarativeTeams/_pendingProviderCapabilities)——\n * 保持与提取前完全一致的时序:syncSessionTools 经 getEnabledAgentProfiles 间接读\n * declarativeAgentProfiles,赋值必须先行(里程碑完成审核 LOW finding 修复)。\n */\n applyResults: (result: {\n agentProfiles: AgentProfile[]\n teamDeclarations: TeamDeclaration[]\n pendingProviderCapabilities: string[]\n }) => void\n diagnoseTopoSortIssues: () => Promise<void>\n syncSessionTools: () => number\n panelBackendController: PanelBackendController\n}\n\nexport interface ReloadCapabilitiesResult {\n mcpTools: number\n models: number\n agents: number\n teams: number\n /** RFC-125 D5:因待授权高危能力被跳过的 provider 插件 id 列表。 */\n pendingProviderCapabilities: string[]\n /** RFC-138 D6:本轮热重载装配的插件 i18n 字符串条目总数(第六轴)。 */\n i18nEntries: number\n agentProfiles: AgentProfile[]\n teamDeclarations: TeamDeclaration[]\n}\n\nexport async function reloadCapabilities(\n input: ReloadCapabilitiesInput,\n): Promise<ReloadCapabilitiesResult> {\n const result = await input.capabilityReloader.reload()\n input.applyResults({\n agentProfiles: result.agentProfiles,\n teamDeclarations: result.teamDeclarations,\n pendingProviderCapabilities: result.pendingProviderCapabilities,\n })\n // RFC-132 D5:低频诊断点——reload() 内部经 activePlugins() 已刷新 _lastTopoSort,\n // 在此对成环/缺失依赖做一次去重告警(供数侧 activePlugins() 本身不打日志)。\n // RFC-163 D5:诊断内部现在会尝试自动补装缺失依赖,故 await(原同步调用改为 async)。\n await input.diagnoseTopoSortIssues()\n // RFC-150 D2:/reload 完成后(含其内部的 mcp.reload())推送最新工具集到全部活 session——\n // 根治既有缺口(此前 /reload 只刷新 toolRegistry,不刷新已存在 session 的冻结工具快照)。\n input.syncSessionTools()\n // RFC-165 D8 补充修复:reload() 内部 mcp.reload() 会重建全新 McpManager 实例,\n // PanelBackendController 若已订阅旧实例的事件,reload 后收不到新实例的状态变化——\n // 已挂载面板(如用户在 panel.backend 未授权态打开详情页后点击\"信任\")不会自动感知\n // 授权成功,仍显示旧的\"面板后端不可用\"。此处主动触发重新订阅 + 推送最新状态。\n input.panelBackendController.notifyReload()\n return {\n mcpTools: result.mcpTools,\n models: result.models,\n agents: result.agents,\n teams: result.teamCount,\n pendingProviderCapabilities: result.pendingProviderCapabilities,\n i18nEntries: result.i18nEntries,\n agentProfiles: result.agentProfiles,\n teamDeclarations: result.teamDeclarations,\n }\n}\n","import { Devtools } from '@x-otto/devtools'\nimport { resolveResilienceConfig, isShadowModeEnabled, type ResilienceConfig, type ResilienceConfigOverride } from '@x-otto/env'\nimport { createModelUsabilityGate, ModelRegistry, type WireProtocolRegistry } from '@x-otto/ai'\nimport {\n bashAlwaysAllowPrefix as bashAlwaysAllowPrefixFn,\n isWellFormedAllowEntry as isWellFormedAllowEntryFn,\n type PermissionPolicyRegistry,\n type PermissionAuditLog,\n} from '@x-otto/hooks'\nimport { ToolRegistry } from '@x-otto/tools'\nimport type { SchedulerService } from '@x-otto/schedule'\nimport { buildStorageAndMemory } from './storage-wiring'\nimport { buildAgentServices } from './agent-services-setup'\nimport { PermissionPolicySync } from './permission-sync'\nimport { SandboxController } from './sandbox-controller'\nimport { FleetMonitor, MemoryGovernor, ResidencyGovernor } from '@x-otto/runtime'\nimport { McpController } from './mcp-controller'\nimport { ExtensionManager } from './extension-manager'\nimport {\n createPluginInputRegistry,\n} from '@x-otto/plugin'\nimport type { PluginInputRegistry } from '@x-otto/plugin'\nimport { FeedbackRegistry } from '../feedback/feedback-registry'\nimport { SigilResolverRegistry } from '../input/sigil-resolver-registry'\nimport { expandSigilChips, type ExpandSigilChipsOptions } from '../input/sigil-expand'\nimport { loadAllPluginA2uiRenderers, resolveA2uiRendererPayloadFrom } from '../plugin/a2ui-renderer-loader'\nimport { InMemorySession, SESSION_MAX } from '@x-otto/session'\nimport type { ResidencyBudgetConfig } from '@x-otto/session-contract'\nimport {\n RUNTIME_DEFAULTS,\n} from '@x-otto/runtime'\nimport type { AgentRuntime } from '@x-otto/runtime'\nimport { Orchestrator } from '@x-otto/orchestrator'\nimport type { WriteTodos } from '@x-otto/orchestration-contracts'\nimport {\n AutoMemory,\n FileStateManager,\n FileSystemMemoryStore,\n OperationalLearningStore,\n createOverlayMemoryStore,\n} from '@x-otto/memory'\n\nimport type { WorkspaceRef } from '@x-otto/workspace'\n\n\nimport { attachLogListener, createLogger, type Logger } from '@x-otto/shared'\nimport { AgentSession } from '@x-otto/runtime'\nimport {\n SessionConfigResolver,\n type CodingSessionOptions,\n} from '../session/session-config-resolver'\nimport { SessionPool as CodingSessionPool } from '@x-otto/runtime'\nimport type { AgentJobNotification } from '../hooks/agent-job-notification'\nimport type { AgentJobService } from './agent-job-service'\nimport { createAgentJobs } from './app-agent-jobs'\nimport type { MemoryAutoExtractor } from '../hooks'\nimport { FailureSignalStore, Diagnoser } from '@x-otto/hooks'\nimport { createTaskRunner } from '../session/task-runner'\nimport type { RunTeam } from '../agent/swarm-runtime'\nimport type { McpManager } from '@x-otto/mcp'\nimport type { SkillRegistry } from '@x-otto/skill'\nimport { PromptManager } from '@x-otto/prompt'\nimport type { AgentProfile } from '@x-otto/prompt'\nimport type { TeamDeclaration } from '../agent/declarative-teams'\nimport { SettingsManager } from '@x-otto/setting'\nimport { NotificationDispatcher, PresenceTracker } from '@x-otto/notification'\nimport { buildNotificationDispatcher } from './notification-setup'\nimport type { NotificationChannel, NotificationKind } from '@x-otto/notification'\n\nimport type { AgentTool, ClockPort } from '@x-otto/agent'\nimport type { SqliteSessionRepository } from '@x-otto/session'\nimport type { AuthStore, ProviderRegistry, Model, Api, ModelSpec, Message } from '@x-otto/ai'\nimport type { GrillAnswer, GrillQuestion } from '@x-otto/interchange'\nimport { ModelCatalogManager } from './model-catalog-manager'\nimport * as modelMethods from './app-model-methods'\nimport * as notificationMethods from './app-notification-methods'\nimport * as permissionMethods from './app-permission-methods'\nimport * as sessionMethods from './app-session-methods'\nimport { SessionToolsSyncCoordinator } from '../session/session-tools-sync'\nimport { PluginContributionsFacade } from './plugin-contributions-facade'\nimport type { HookRegistry, HookSpec, PermissionMode } from '@x-otto/hooks'\nimport type { EditFileCallback } from '@x-otto/hooks'\nimport { homedir } from 'node:os'\nimport type { AgentSessionInfo } from '@x-otto/runtime'\n\nimport type { AppStorage, AppOptions, AppContext, SessionArchiveExport } from '../types'\nimport { PluginI18nRegistry, type DiscoveredPlugin } from '@x-otto/plugin'\nimport { CapabilityReloader } from './capability-reloader'\nimport { PanelBackendController } from '../plugin/panel-backend-controller'\nimport type { PanelRegistration } from '../plugin/plugin-panel-manager'\nimport type { ResolvedStatusItem } from '../plugin/status-items'\nimport type { RegisteredRenderer } from '../plugin/renderer-loader'\nimport type { RegisteredStatusWidget } from '../plugin/status-widget-loader'\nimport type { RegisteredFileViewer } from '../plugin/file-viewer-loader'\nimport type { RegisteredA2uiComponent } from '../plugin/a2ui-component-loader'\nimport { parsePluginIdFromHookName } from '../plugin/module-wiring'\nimport { createPluginHealthTracker } from '../plugin/plugin-health-tracker'\nimport { PluginCapabilityQuota } from '../plugin/plugin-capability-quota'\nimport { InquiryCenter } from '../plugin/inquiry-center'\nimport { getPluginServicePool } from '../plugin/plugin-service-pool'\nimport type { SessionIdleNotification } from '@x-otto/plugin'\nimport type { ResolvedAction } from '@x-otto/plugin'\nimport { PluginModuleManager } from './plugin-module-manager'\nimport { PluginTopologyManager } from './plugin-topology-manager'\nimport { buildModelInfrastructure } from './model-infra-factory'\nimport {\n buildCoreInfrastructure,\n buildSettingsAndDevtools,\n buildVisionAndSessionConfig,\n buildAppAgentRuntime,\n buildOrchestratorsAndTeams,\n buildPanelInfrastructure,\n buildPluginModules,\n buildFacades,\n buildToolsAndMCP,\n buildExtensions,\n buildPluginRegistrations,\n ensureSettingsLoaded as ensureSettingsLoadedHelper,\n} from './app-builder'\nimport {\n buildToolsForWorkspace,\n connectorTools,\n initToolRegistry,\n registerHooks as registerHooksForHost,\n} from './tool-wiring'\nimport {\n startApp,\n stopApp,\n completeWorkspaceDependentStartup as completeWorkspaceDependentStartupImpl,\n needsWorkspaceInit as needsWorkspaceInitImpl,\n confirmWorkspaceInit as confirmWorkspaceInitImpl,\n needsParentWorkspaceDecision,\n adoptParentWorkspace as adoptParentWorkspaceImpl,\n createLocalWorkspace as createLocalWorkspaceImpl,\n setWorkspaceMemorySyncEnabled as setWorkspaceMemorySyncEnabledImpl,\n sweepOrphanTraceFiles as sweepOrphanTraceFilesImpl,\n} from './app-lifecycle'\nimport { exportSessionArchive } from './app-session-export'\nimport { reloadCapabilities as reloadCapabilitiesImpl } from './app-capability-reload'\nimport { PluginProviderManager } from './plugin-provider-manager'\nimport type { FileCommand } from '@x-otto/claude-compat'\nexport { type AppOptions, type AppContext } from '../types'\n\nlet appOwnerCounter = 0\n\n/**\n * RFC-325 M5:`otto doctor attach <pid>` 的只读动态驻留桥。\n *\n * 仅在当前 Node 进程内暴露聚合数字,既不持久化、也不含会话内容/ID;属性不可重写,\n * 诊断端只能调用 `getResidency()`。同一进程仅允许一个 App(CLI 正常模型),重复安装时\n * 保留最先启动的实例,避免跨 App 的诊断真源漂移。\n */\nfunction installResidencyDiagnosticsBridge(app: App): void {\n const key = '__ottoResidencyDiagnostics'\n const target = globalThis as typeof globalThis & {\n [key]?: { getResidency: () => import('@x-otto/runtime').ResidencyDiagnostics }\n }\n if (target[key]) return\n Object.defineProperty(target, key, {\n configurable: false,\n enumerable: false,\n writable: false,\n value: Object.freeze({ getResidency: () => app.getResidencyDiagnostics() }),\n })\n}\n\nexport class App implements AppContext {\n readonly processOwnerId = `app-${++appOwnerCounter}`\n\n /** 本 App 的进程归属 id(后台进程 register 时打的 owner 标;introspection/测试用)。 */\n get processOwner(): string {\n return this.processOwnerId\n }\n\n public readonly settings: SettingsManager\n public readonly toolRegistry: ToolRegistry\n public readonly hookRegistry: HookRegistry\n public readonly providerRegistry: ProviderRegistry\n /** RFC-148 D2b:Wire 协议实现注册表(key space = wireApi,与 providerRegistry 职责正交)。 */\n public readonly wireProtocolRegistry: WireProtocolRegistry\n /** App 持有 runtime 门面(contextSources/createSession/dispose 经此)。 */\n readonly agentRuntime: AgentRuntime\n public readonly codingSessionPool: CodingSessionPool\n public readonly permissionRegistry: PermissionPolicyRegistry\n public readonly auditLog: PermissionAuditLog\n public readonly logger: Logger\n\n /** 孤儿 trace 扫除(RFC-102 D3)依赖:关系化 repo 引用 + session 落盘根目录。 */\n sessionRepoRef?: { current?: SqliteSessionRepository }\n sessionStorageDir?: string\n\n /**\n * workspace 根目录——`workspaceRef` 就位后由 `ref.root` 派生,否则退回构造期\n * 传入值。L/R 形态下二者等价(start() 中 resolveLocal 把 workspaceDir 记入 ref.root);V 形态(无 root)\n * 当前零接线(设计保留在 RFC-034),此时退回到构造期传入值。\n */\n public get workspaceDir(): string {\n return this.workspaceRef?.root ?? this._workspaceDir\n }\n\n private readonly _workspaceDir: string\n /** 显式 workspace id(`--id` CLI flag),供 resolveLocal opts.id 覆盖。 */\n readonly _workspaceId?: string\n /** 交互(TUI)态。仅交互态启用项目配置信任门(headless fail-open)。 */\n readonly _interactive: boolean\n /**\n * RFC-345:影子模式(零落盘会话)。与 `@x-otto/env` 的 `isShadowModeEnabled()` 同源。\n * 供 App 层显式感知(D5 可见性标识 / D9 不参与 daemon 竞选);存储层内存化选择不读此\n * 字段,而在 §D1 白名单装配点直接查 env 函数——避免开关有第二真源。\n */\n readonly _shadow: boolean\n /** 插件发现 user-scope homedir 覆盖(缺省用真实 `os.homedir()`,见 AppOptions.pluginHomedir 注释)。 */\n private readonly _pluginHomedir: string\n\n /**\n * workspace 身份引用(resolve-or-create `.otto/workspace.json`,start() 中解析)。\n * 持 `key = ws_<id>`(持久化分区第一维)+ backend/root。供会话持久化分区 + memory 文件 scoping 消费。\n */\n public workspaceRef?: WorkspaceRef\n /**\n * start() 探测到本目录无 `.otto/workspace.json` 且处于交互态时置位——不自动创建 marker,\n * 等待 cli 层信任确认后调 `confirmWorkspaceInit()`(本次修复:防止 marker 缺失时静默生成新\n * 随机 id 导致 sessions.db 里旧会话因 workspace_key 对不上而\"消失\")。headless 态/`--id`\n * 显式给定/marker 已存在均不会置位(那些路径 start() 内直接完成解析,行为不变)。\n */\n pendingWorkspaceInit?: { root: string }\n /**\n * start() 向上递归在**父级目录**(非当前目录)命中 `.otto/workspace.json` 时置位——\n * 不静默采用父级身份。等待 cli 层提示用户选择\"使用上级 workspace\"还是\"在当前目录创建\",\n * 防止 monorepo 子目录被意外吞入父 workspace。headless 态不触发(该路径 autoCreate 缺省\n * true,直接采用父级 marker,行为不变)。\n */\n parentWorkspaceExisted?: { parentRoot: string; currentRoot: string; marker: import('@x-otto/workspace').WorkspaceMarker }\n public readonly maxSessions: number\n public readonly maxToolTurns: number\n public readonly maxToolTurnExtensions?: number\n /** RFC-104 D3:app 级缺省 per-prompt 预算(CLI flags 透传)。 */\n public readonly promptOutputTokenBudget?: number\n public readonly promptWallClockBudgetMs?: number\n /** RFC-204 D1/D3:app 级缺省会话成本软预算美元(CLI flags 透传)。 */\n public readonly sessionCostBudgetUSD?: number\n /** RFC-204 D4:app 级缺省早期停滞检测配置(CLI flags 透传)。 */\n public readonly stallDetection?: { windowTurns?: number; repeatThreshold?: number } | false\n\n public readonly devtools: Devtools | null = null\n\n /** 编辑文件面板回调。CLI 层在 startTui() 后将此属性接线到 tui.addEditedFile()。\n * hook 采用 getOnFileEdited 延迟求值——注册时 TUI 未就绪,emit 时通过此属性拿到回调。 */\n public editFileCallback: EditFileCallback | undefined = undefined\n readonly _editFilesDedup = new Set<string>()\n\n /** RFC-118 S1:存储/记忆集群分组(memory/traceStore/checkpointStore 等 6 字段 + 远端配置标志)。 */\n public readonly storage: AppStorage\n /** 用户级记忆(~/.otto/memory,跨项目)。`#`=项目级,`/memory user`=用户级。\n * RFC-345 §D3:影子态用 overlay 包装——读穿透继承、写只进内存。 */\n public readonly userMemory: AutoMemory = new AutoMemory({\n store: (() => {\n const base = new FileSystemMemoryStore(homedir())\n return isShadowModeEnabled() ? createOverlayMemoryStore(base) : base\n })(),\n })\n /** 一次性轻量模型补全(供 `#` 记忆 opt-in 智能命名等)。无模型时抛错由调用方兜底。 */\n public readonly completeText!: (\n prompt: string,\n opts?: { maxTokens?: number; signal?: AbortSignal },\n ) => Promise<string>\n private readonly clock?: ClockPort\n\n readonly fileStateManager: FileStateManager\n readonly learningStore: OperationalLearningStore\n /** 机器特定经验(scope='local',RFC-164 M164-1 D3),落 `.otto/local/lessons.json`,永不参与远端同步。 */\n readonly localLearningStore: OperationalLearningStore\n /** 经验自动抽取编排器(接 session.idle;env OTTO_MEMORY_AUTO_EXTRACT 灰度开关,默认关)。 */\n readonly memoryAutoExtractor: MemoryAutoExtractor\n readonly promptManager: PromptManager\n\n readonly orchestrator: Orchestrator\n runSession!: ReturnType<typeof createTaskRunner>['runSession']\n /** 会话级 Todo 存储(终局架构 review O2)——独立于 Orchestrator 持有。 */\n readonly writeTodos: WriteTodos\n\n readonly sessionConfigResolver: SessionConfigResolver\n readonly runTeam: RunTeam\n\n public readonly skillRegistry: SkillRegistry | null\n\n /** RFC-138:插件 i18n 注册表(构造装配一次;/reload 经 CapabilityReloader 六轴重装)。 */\n public readonly i18nRegistry: PluginI18nRegistry\n\n /** RFC-125 D5:因 pendingCapabilities 含 provider 被跳过装载的插件 id(/extension 提示用)。 */\n _pendingProviderCapabilities: string[] = []\n\n /** 只读访问当前因待授权高危能力而被跳过的 provider 插件 id 列表(RFC-125 D5)。 */\n public get pendingProviderCapabilities(): readonly string[] {\n return this._pendingProviderCapabilities\n }\n\n declarativeAgentProfiles: AgentProfile[] = []\n\n /** 统一可插拔项管理(/extension 命令后端:mcp / skill / agent 启停 + 落盘)。 */\n public readonly extensions: ExtensionManager\n\n /**\n * 插件输入注册表(#/@ sigil 条目,L1 数据权威单例)。\n * UI 表面通过 props 接收此引用。与 ExtensionManager 解耦——自有 register/unregisterBySource 生命周期。\n */\n public readonly pluginInput: PluginInputRegistry = createPluginInputRegistry()\n\n /**\n * @ 资源 resolve 注册表(RFC-210 D2):与 pluginInput(纯数据)平行的代码契约注册表。\n * 内置 resolver(file-range/@def;@changes/@git/@clipboard 已随 RFC-233 退役,@shortcut\n * 随 RFC-233 新增)在 buildPluginRegistrations 注册;插件 resolver 经 M3 sigil-loader\n * 装载(capability `input.resolver` 门控)。\n * 展开消费方:`expandSigilChips()`(cli 提交管线 / service expand 端点)。\n */\n public readonly sigilResolvers = new SigilResolverRegistry()\n\n /**\n * @ 资源统一展开出口(RFC-210 D2):扫描文本中全部 `[@kind:param]` chip 与\n * `@path:行范围`/`@def:符号` 引用,执行对应 resolver 替换为实际内容。\n * fail-soft:resolver 失败/超时 → chip 原样保留,不阻断提交。\n * opts.skipPrefixes:action 型 chip(如 @model)调用方声明式跳过;\n * opts.uiHost=false(service 场景):uiHostOnly resolver(@clipboard)跳过执行。\n */\n public expandSigilChips(text: string, opts?: ExpandSigilChipsOptions): Promise<string> {\n return expandSigilChips(text, this.sigilResolvers, { workspaceDir: this.workspaceDir }, {\n ...opts,\n // 条目级 resolverId 查找(RFC-210 D2):插件声明式条目的 chip(value)绑定了\n // resolverId(装载时已拼 <pluginId>: 前缀),kind 段是通用 'resource' 查不到——\n // 按 chip 原文匹配条目取其 resolverId。\n lookupResolverId: (rawChip) => {\n for (const entry of this.pluginInput.getAll()) {\n if (entry.value === rawChip && entry.resolverId) return entry.resolverId\n }\n return undefined\n },\n })\n }\n\n /**\n * RFC-209 §2.3/§3.1:Feedback 业务契约宿主注册表(满意度评分记录/issue 提交)。\n * 核心层(interactive-events.ts 等)只通过本注册表接口调用具体插件实现(如\n * extensions/plugin-feedback),从不直接 import 插件代码(重要事项规则1)。\n */\n public readonly feedbackRegistry: FeedbackRegistry = new FeedbackRegistry()\n\n /**\n * RFC-233:用户自定义 `[@shortcut:name]` 文本快捷方式——单一权威门面,TUI(进程内\n * 直调)与 web-ui(经 service REST 路由调用同一方法)共用同一套增删改逻辑,避免\n * 两端各自实现一遍持久化/校验/sigil 条目刷新(重要事项规则同 draft/queue 数据模式,\n * 但走 App 门面而非各前端直接摸 settings)。\n */\n public getShortcuts(): Array<{ name: string; text: string }> {\n return this.settings.get('shortcuts') ?? []\n }\n\n /**\n * 新建或编辑(`previousName` 存在且与 `name` 不同时视为重命名——先删旧名再存新名,\n * 保持数组内相对顺序:重命名条目移到末尾,符合\"最近编辑的排后面\"直觉,同 draft 模式)。\n * 校验:`name` 非空、仅 `[A-Za-z0-9_-]`(chip 语法安全边界)、不与除自身外的既有名称重复;\n * `text` 非空。校验失败抛 `Error`(调用方——CLI handler / service 路由——各自决定\n * 如何呈现给用户,App 层不感知前端形态)。\n */\n public async saveShortcut(name: string, text: string, previousName?: string): Promise<void> {\n const trimmedName = name.trim()\n if (!trimmedName || !/^[A-Za-z0-9_-]+$/.test(trimmedName)) {\n throw new Error(`invalid shortcut name: ${JSON.stringify(name)}`)\n }\n if (!text.trim()) {\n throw new Error('shortcut text cannot be empty')\n }\n const current = this.getShortcuts()\n const isDuplicate = current.some((s) => s.name === trimmedName && s.name !== previousName)\n if (isDuplicate) {\n throw new Error(`shortcut name already exists: ${trimmedName}`)\n }\n const withoutOld = previousName ? current.filter((s) => s.name !== previousName) : current\n const next = [...withoutOld, { name: trimmedName, text }]\n await this.settings.persist({ shortcuts: next }, 'project')\n }\n\n /** 删除指定名称的快捷方式(不存在则静默 no-op,对齐 draft handleDelete 的宽松语义)。 */\n public async deleteShortcut(name: string): Promise<void> {\n const current = this.getShortcuts()\n const next = current.filter((s) => s.name !== name)\n if (next.length === current.length) return\n await this.settings.persist({ shortcuts: next }, 'project')\n }\n\n declarativeTeams: TeamDeclaration[] = []\n\n readonly mcpController: McpController\n\n /** RFC-165 M3:插件面板后端控制器(backend facet 路由/门控/状态订阅)。 */\n readonly panelBackendController: PanelBackendController\n\n /** @deprecated 内部重构为 McpController,保留 getter 兼容外部测试/示例。 */\n public get mcpManager(): McpManager | null {\n return this.mcpController.manager\n }\n\n disposed = false\n ownedHookNames: string[] = []\n /** 跨会话失败信号(诊断原料)。tool isError 经 hook 记入;诊断据此提案。 */\n public readonly failureSignals = new FailureSignalStore()\n /** 诊断器——从复发硬信号生成改进提案(L1 只提不执行,去重防风暴)。/diagnose 消费。 */\n public readonly diagnoser = new Diagnoser(this.failureSignals)\n /** RFC-153 D3:插件健康度追踪(挂起/错误计数,内存态诊断原料,不跨进程持久化,规则 5)。\n * 三处写入点:M1 interceptor 超时(`onHookTimeout` 回调)、代码式工具超时\n * (`collect-plugin-perf-metrics.ts`)、插件加载/编译/工厂失败(`module-loader*.ts`)。\n * 读取点:`/extension` 详情面板(D4)。 */\n public readonly pluginHealthTracker = createPluginHealthTracker()\n /**\n * RFC-318 M1:受控能力(session.read / llm.complete)的配额与会话白名单。\n *\n * 必须是**单一实例**:`notifyPluginServices()` 在这里登记\"某 sessionId 已下发给某插件\",\n * `buildHostCapabilities` 的 readTranscript 闭包读同一份白名单——两处分家会让白名单\n * 恒空、读能力恒拒(fail-closed 方向安全,但功能全废)。\n */\n public readonly pluginCapabilityQuota = new PluginCapabilityQuota()\n /**\n * RFC-344 M2:人在环问询的统一队列与仲裁。\n *\n * 必须是**单一实例**:两类问询(agent grill 与插件 ask)共用 TUI 的\n * `resolvers.questions` 单槽位——分家会让\"同一时刻只有一个问询在飞\"的不变式失效,\n * 被覆盖的 Promise 永久挂死(见 `inquiry-center.ts` 头注释)。\n */\n public readonly inquiryCenter = new InquiryCenter()\n private userAskRenderer?: (question: GrillQuestion) => Promise<GrillAnswer>\n private userAskSeq = 0\n processRegistryUnsubs: Array<() => void> = []\n private settingsLoaded = false\n /** RFC-303 D4:settings.changed 事件桥接的上一次配置快照(首次 change 只快照不触发;\n * 逐键深比较只发变更键名,值不进入事件 payload——R8 脱敏)。 */\n private lastSettingsConfig: Record<string, unknown> | null = null\n /**\n * RFC-230:统一重试/超时/退避配置。构造期只存程序化注入(`_resilienceOverride`),\n * 不在此计算最终值——`this.settings` 此时尚未 `load()`(settings 层配置永远读不到,\n * 见 RFC-230 §1.4 记录的 `workflowConfig` 同源时序 bug)。真正的合并求值延迟到\n * `ensureSettingsLoaded()` 完成后由 `freezeResilienceConfig()` 执行一次并冻结。\n */\n private readonly _resilienceOverride: ResilienceConfigOverride | undefined\n private _resilienceConfig: ResilienceConfig | undefined\n defaultToolPreset: 'minimal' | 'standard' | 'full' = 'full'\n\n private readonly permissionSync: PermissionPolicySync\n readonly sandbox: SandboxController\n /** RFC-115 M1:长驻进程内存自我感知(采样+分级)。M2/M3 后续接入降级/重启回调。 */\n readonly _memoryGovernor: MemoryGovernor\n /** RFC-178 触发器 C:驻留收紧系数源(app-lifecycle 接分级信号;PersistenceSync 读系数)。 */\n readonly _residencyGovernor: ResidencyGovernor\n /** RFC-196 M2:机器级进程群预算感知(10min 级 ps 枚举采样,独立于堆采样)。 */\n readonly _fleetMonitor: FleetMonitor\n\n public get tools(): AgentTool[] {\n // 直接返回 RegisteredTool[](extends AgentTool 超集,仅多 metadata)。\n // 手抄 7 字段投影会**静默丢** isConcurrencySafe/resolvePath → task_delegate 等编排工具\n // 失去并行(被迫串行)、read/write/edit 路径守卫看到未解析的相对路径(锚定缺口)。\n return this.toolRegistry.getAvailable(this.defaultToolPreset)\n }\n\n agentJobNotify?: (sessionId: string, n: AgentJobNotification) => void\n agentJobServiceInstance?: AgentJobService\n scheduleServiceInstance?: SchedulerService\n /**\n * RFC-287 M4:生态检索实现(`capability_gap` 工具的数据源)。由 cli 层注入——\n * registry 客户端住在 cli 侧,coding 不反向依赖 cli。缺省 → 该工具不注册。\n */\n searchEcosystem?: import('@x-otto/tools').SearchEcosystem\n readonly notificationDispatcher: NotificationDispatcher\n readonly presenceTracker: PresenceTracker\n private followUpPendingProbe?: () => boolean\n /** RFC-150 D2:活 session 工具重同步协调器(MCP 工具变化/`/reload` 后推送新工具集)。 */\n readonly toolsSyncCoordinator: SessionToolsSyncCoordinator\n /** RFC-150 D1:MCP 非阻塞启动完成回调(交互模式晚绑定;已 settle 则闭包引用下一 tick 仍触发)。 */\n onMcpStartupSettled?: (result: { connected: number; failed: number }) => void\n /**\n * 竞态兜底:若 MCP 连接在宿主晚绑定回调**之前**就已完成(如 browser-use 单 server\n * 场景实测仅 ~114ms,可能快于 TUI 首帧渲染),先锁存结果;`setMcpStartupCallback` 内\n * 发现已有锁存值时立即补发一次,保证通知不会因为绑定时机竞态而静默丢失(RFC-150 规则 6)。\n */\n pendingMcpStartupResult?: { connected: number; failed: number }\n\n /** 供宿主(cli 层)晚绑定 MCP 非阻塞启动完成回调(D1)。 */\n public setMcpStartupCallback(cb: (result: { connected: number; failed: number }) => void): void {\n this.onMcpStartupSettled = cb\n if (this.pendingMcpStartupResult) {\n cb(this.pendingMcpStartupResult)\n this.pendingMcpStartupResult = undefined\n }\n }\n /** RFC-148 M4:插件贡献查询域公共方法门面。 */\n private readonly pluginContributionsFacade: PluginContributionsFacade\n\n /**\n * 为给定 workspaceDir(= worktree 路径)组装工具集。委托 tool-wiring.ts(RFC-152 M4)。\n */\n public buildToolsForWorkspace(workspaceDir: string): AgentTool[] {\n return buildToolsForWorkspace(this, workspaceDir)\n }\n\n /** 主会话已注册的 MCP 工具(`mcp__*`)。委托 tool-wiring.ts(RFC-152 M4)。 */\n private connectorTools(): AgentTool[] {\n return connectorTools(this)\n }\n\n /** agent-session 作业服务(懒构造,确保 deps 就绪)。委托 app-agent-jobs.ts。 */\n public get agentJobs(): AgentJobService {\n if (!this.agentJobServiceInstance) {\n this.agentJobServiceInstance = createAgentJobs({\n workspaceDir: this.workspaceDir,\n agentJobNotify: this.agentJobNotify,\n buildToolsForWorkspace: (wd) => this.buildToolsForWorkspace(wd),\n connectorTools: () => this.connectorTools(),\n settings: this.settings,\n modelRegistry: this.modelRegistry,\n stream: this.codingSessionPool.stream,\n logger: this.logger,\n hookRegistry: this.hookRegistry,\n })\n }\n return this.agentJobServiceInstance\n }\n\n /** Wire schedule service(interactive setup 期构建)供 cron_* 工具消费。 */\n public setScheduleService(scheduler: SchedulerService): void {\n this.scheduleServiceInstance = scheduler\n // M3:scheduler 就绪后对已启动 services 补跑 schedule 声明对账——\n // App.start() 内 services axis 先于 scheduler 构造,首次 register 时 getSchedulePort()\n // 返回 undefined 导致声明被跳过。此处补跑,使首次启动即可生效(而非等下次 /reload)。\n this.pluginModuleManager?.reconcileServiceSchedules()\n }\n\n /**\n * RFC-287 M4:晚绑定生态检索实现(供 `capability_gap` 工具)。对齐\n * `setScheduleService` 的晚绑定纹理——registry 客户端在 cli 层构造,coding 侧\n * 只持函数引用。注入的是**只读检索**,不含安装能力(R1:安装恒经用户信任门)。\n */\n public setEcosystemSearch(search: import('@x-otto/tools').SearchEcosystem): void {\n if (this.searchEcosystem === search) return\n this.searchEcosystem = search\n // The tool registry snapshots conditional callbacks at construction. Late binding\n // search must therefore use the established reload+session-sync path, otherwise\n // the initial interactive session permanently misses capability_gap. This is\n // intentionally fire-and-forget for the public synchronous injection API.\n void this.reloadCapabilities().catch((err) => {\n this.logger.warn(\n { err: err instanceof Error ? err.message : String(err) },\n 'Failed to refresh capability_gap after ecosystem search injection',\n )\n })\n }\n\n /**\n * RFC-115 M2:晚绑定内存压力回调——App 构造时活跃 session 尚不存在,interactive.ts 在\n * session 就绪后调用注入。onWarning 走 compactNow()(软降级),onCritical 交给调用方\n * (M3 弹 Dialog)。\n */\n public get memoryGovernor(): MemoryGovernor {\n return this._memoryGovernor\n }\n\n /** RFC-196 M2:进程群预算感知——CLI 层经此接 onBudgetExceeded TUI 通知。 */\n public get fleetMonitor(): FleetMonitor {\n return this._fleetMonitor\n }\n\n public get modelRegistry(): ModelRegistry {\n return this._modelRegistry\n }\n\n public get authStore(): AuthStore {\n return this._authStore\n }\n\n /** 模型目录管理(实时拉取/落盘缓存/hydrate)。 */\n readonly modelCatalogManager: ModelCatalogManager\n private readonly _modelRegistry: ModelRegistry\n readonly _authStore: AuthStore\n\n /** RFC-148 M2:ServiceHostPort 收窄——service 侧经窄方法消费,不再直捅 registry。\n * 委托 app-model-methods.ts(RFC-157 M2 T2.3,回收自已删除的 ModelFacade)。 */\n public getAllModels(): Model[] {\n return modelMethods.getAllModels(this)\n }\n\n public resolveModel(spec: ModelSpec): Model {\n return modelMethods.resolveModel(this, spec)\n }\n\n public listProviders(): Api[] {\n return modelMethods.listProviders(this)\n }\n\n public registerHook(spec: HookSpec): void {\n modelMethods.registerHook(this, spec)\n }\n\n public unregisterHook(name: string): boolean {\n return modelMethods.unregisterHook(this, name)\n }\n\n /** 启动时把落盘模型缓存并回 registry。返回缓存确认「账号可用」的 id 集合。委托 app-model-methods.ts。 */\n public async hydrateModelCache(): Promise<Set<string>> {\n return modelMethods.hydrateModelCache(this)\n }\n\n /** 会话级「账号可用模型」只读访问(cli --continue 据此保留恢复会话的集合)。 */\n public getSessionAvailableModels(sessionId: string): string[] {\n return modelMethods.getSessionAvailableModels(this, sessionId)\n }\n\n /** 实时拉取已认证 provider 的模型目录并入 registry + 落盘缓存。委托 ModelCatalogManager。 */\n public async refreshModels(): Promise<\n Array<{\n provider: string\n status: 'ok' | 'skipped' | 'failed'\n added?: number\n kept?: number\n total?: number\n availableIds?: string[]\n reason?: string\n }>\n > {\n return modelMethods.refreshModels(this)\n }\n\n /**\n * 热重载能力——不重启进程认到 delegate 新加的 MCP / provider / 声明式 agent。\n * 编排逻辑已下沉到 CapabilityReloader(RFC-107 D4),此方法为薄调用兼容层。\n * 供 /reload 命令与 apply 后自动触发用。签名与返回值不变,消费方零感知。\n */\n public async reloadCapabilities(): Promise<{\n mcpTools: number\n models: number\n agents: number\n teams: number\n /** RFC-125 D5:因待授权高危能力被跳过的 provider 插件 id 列表(新增可选消费字段)。 */\n pendingProviderCapabilities: string[]\n /** RFC-138 D6:本轮热重载装配的插件 i18n 字符串条目总数(第六轴)。 */\n i18nEntries: number\n }> {\n const result = await reloadCapabilitiesImpl({\n capabilityReloader: this.capabilityReloader,\n // 副作用(诊断/工具重同步/面板重订阅)之前回写实例字段——与提取前时序逐字一致。\n applyResults: (r) => {\n this.declarativeAgentProfiles = r.agentProfiles\n this.declarativeTeams = r.teamDeclarations\n this._pendingProviderCapabilities = r.pendingProviderCapabilities\n },\n diagnoseTopoSortIssues: () => this.diagnoseTopoSortIssues(),\n syncSessionTools: () => this.syncSessionTools(),\n panelBackendController: this.panelBackendController,\n })\n return {\n mcpTools: result.mcpTools,\n models: result.models,\n agents: result.agents,\n teams: result.teams,\n pendingProviderCapabilities: result.pendingProviderCapabilities,\n i18nEntries: result.i18nEntries,\n }\n }\n\n /** RFC-150 D2:活 session 工具重同步(/reload 后调用)。委托 SessionToolsSyncCoordinator。 */\n public syncSessionTools(): number {\n return this.toolsSyncCoordinator.resync()\n }\n\n /** 单 session 工具重同步(`/model` 切换后按新 model.supportsTools 刷新 tools 快照)。 */\n public syncSessionToolsFor(sessionId: string): boolean {\n return this.toolsSyncCoordinator.resyncOne(sessionId)\n }\n\n /** 插件拓扑状态管理(RFC-152 M2):activePlugins()/diagnoseTopoSortIssues() 委托目标。 */\n private readonly pluginTopology = new PluginTopologyManager()\n\n /** 插件代码模块(plugin.ts)的加载/接线/卸载——三段 computed 状态 + reload + cleanup(ARCH-083-01)。 */\n readonly pluginModuleManager: PluginModuleManager\n\n /** 插件声明式 provider(`contributes.providers`)的加载/卸载单源(RFC-105 M105-2-03b)。 */\n readonly pluginProviderManager: PluginProviderManager\n\n /** 五轴能力热重载编排器(RFC-107 D4)。 */\n private readonly capabilityReloader: CapabilityReloader\n\n /** computed 插件命令(FileCommand[])。供 cli 命令系统与 listFileCommands 并入。委托 PluginContributionsFacade。 */\n computedPluginCommands(): FileCommand[] {\n return this.pluginContributionsFacade.computedPluginCommands()\n }\n\n public get sessionManager(): CodingSessionPool {\n return this.codingSessionPool\n }\n\n /** RFC-325 M5:供 `otto doctor attach <pid>` 受控读取的只读聚合诊断入口。 */\n getResidencyDiagnostics(): import('@x-otto/runtime').ResidencyDiagnostics {\n return this.codingSessionPool.getResidencyDiagnostics()\n }\n\n globalLogSubscription: ReturnType<typeof attachLogListener> | null = null\n\n constructor(options: AppOptions) {\n const {\n inspect,\n logger,\n projectConfigPath,\n maxSessions,\n maxToolTurns,\n maxToolTurnExtensions,\n promptOutputTokenBudget,\n promptWallClockBudgetMs,\n sessionCostBudgetUSD,\n stallDetection,\n workspaceDir,\n workspaceId,\n pluginHomedir,\n } = options\n\n this.maxSessions = maxSessions ?? SESSION_MAX\n this.maxToolTurns = maxToolTurns ?? RUNTIME_DEFAULTS.maxToolTurns\n this.maxToolTurnExtensions = maxToolTurnExtensions\n this.promptOutputTokenBudget = promptOutputTokenBudget\n this.promptWallClockBudgetMs = promptWallClockBudgetMs\n this.sessionCostBudgetUSD = sessionCostBudgetUSD\n this.stallDetection = stallDetection\n this._workspaceDir = workspaceDir ?? process.cwd()\n this._workspaceId = workspaceId\n this._interactive = options.interactive ?? false\n // RFC-345 M9(轴 2 同源复制收紧):options.shadow 携带的是 flag 传入的形态,\n // 但真源是 env——daemon/embed/SDK 宿主直接注入 OTTO_SHADOW 时,\n // options.shadow 为 false 但 env 为 true,必须取并集才立于不败之地。\n // isShadowModeEnabled() 求值时代码在此是 D1 白名单装配点(App 构造),与原\n // 约束一致(仍不调 isShadowModeEnabled() 到存储逻辑,仅用于同步 _shadow 镜像)。\n this._shadow = options.shadow ?? isShadowModeEnabled()\n this._pluginHomedir = pluginHomedir ?? homedir()\n // RFC-230:只存程序化注入,不在此求值最终 ResilienceConfig(settings 尚未 load)。\n this._resilienceOverride = options.resilience\n\n this.logger = createLogger(logger.name, {\n level: logger.level,\n destination: logger.destination,\n })\n\n const repoRef: { current?: SqliteSessionRepository } = {}\n this.sessionRepoRef = repoRef\n this.sessionStorageDir =\n options.storage?.session && 'dir' in options.storage.session\n ? options.storage.session.dir\n : undefined\n\n // ── Phase 1: Core Infrastructure ──\n const core = buildCoreInfrastructure({\n logger: this.logger,\n hookRegistry: options.hookRegistry,\n getToolRegistry: () => this.toolRegistry,\n getPermissionRegistry: () => this.permissionRegistry,\n onHookTimeout: ({ hookName, timeoutMs: _timeoutMs }) => {\n const pluginId = parsePluginIdFromHookName(hookName)\n if (pluginId) this.pluginHealthTracker.recordHookTimeout(pluginId, hookName)\n },\n })\n this.sandbox = core.sandbox\n this._memoryGovernor = core.memoryGovernor\n this._residencyGovernor = core.residencyGovernor\n this._fleetMonitor = core.fleetMonitor\n this.permissionSync = core.permissionSync\n this.hookRegistry = core.hookRegistry\n\n // ── Phase 2: Model Infra (thin: already extracted to model-infra-factory.ts) ──\n const modelInfra = buildModelInfrastructure(options, () => this.settings)\n this._authStore = modelInfra.authStore\n this._modelRegistry = modelInfra.modelRegistry\n this.providerRegistry = modelInfra.providerRegistry\n this.wireProtocolRegistry = modelInfra.wireProtocolRegistry\n this.modelCatalogManager = modelInfra.modelCatalogManager\n const defaultModel = modelInfra.defaultModel\n const resolveEffectiveModel = modelInfra.resolveEffectiveModel\n const explicitModelId = options.modelId\n\n // ── Phase 3: Storage + Memory (thin: already extracted to storage-wiring.ts) ──\n const storageMemory = buildStorageAndMemory({\n options,\n workspaceDir: this.workspaceDir,\n providerRegistry: this.providerRegistry,\n defaultModel,\n resolveEffectiveModel,\n repoRef,\n getWorkspaceRef: () => this.workspaceRef,\n onMemoryConflict: options.onMemoryConflict,\n getActiveSessionBytes: () => {\n const raw = this.codingSessionPool?.active?.session\n return raw instanceof InMemorySession ? raw.estimatedContentBytes : undefined\n },\n // RFC-323 D3:memory 侧 maxContentBytes 改读「分配层 × 压力档」两层。\n // 与 persistence-sync 的 applyResidencyCaps 同源(两个路径读同一有效值)。\n // 口径差异:persistence-sync 侧排除 transient(精确),此处用 listSessions().length\n // (含 transient 近似)——transient 子会话通常很少,近似误差可忽略且更保守(N 偏大\n // → 预算偏小 → 更早压缩,安全方向)。\n getEffectiveMaxBytes: () => {\n // RFC-325 M3 后续修复(RFC-330 M2 审查发现):压缩触发阈值必须与**裁剪**读同一\n // 有效预算,否则 RFC-321 M3 确立的不变式(\"cap 触发时压缩边界必然已存在\")失效。\n //\n // 缺陷经过:M3 把裁剪路径的预算换成 GlobalResidencyEngine(带负反馈),但此处\n // 仍是旧的 effectiveResidencyBudget(无负反馈)。高驻留时裁剪阈值远低于压缩阈值\n // (实测 N=3/驻留900MB:压缩 256MB vs 裁剪 41MB),形成\"压缩认为无需压、裁剪已在裁\"\n // 的区间——压缩前置在最需要它的高压场景下被架空。\n //\n // 复用 getResidencyDiagnostics() 而非重算:它内部就是同一个 engine 决策,\n // 避免再造第三份预算公式(口径同样排除 transient)。\n const pool = this.codingSessionPool\n if (!pool) return undefined\n return pool.getResidencyDiagnostics().maxBytesPerSession\n },\n // RFC-324 D3:摘要覆盖率阈值——此前已进 schema 但零消费方(用户改配置无效果\n // 且无警告,RFC-330 §4.5 记录的静默失效面)。惰性读:settings 在 App 构造之后\n // 才 load,静态传值会永远读到 undefined。\n //\n // 优先级 env > config > 默认,与 residency 预算同一约定(RFC-323 D6b):\n // memory 侧常量本身已是 normalizeEnvFraction(env, 默认) 的结果,故 env 显式设置时\n // 此处留空该字段即可让常量(= env 值)胜出。\n getCoverageThresholds: () => {\n const r = this.settings.get('residency')\n const pick = (envKey: string, val: number | undefined): number | undefined =>\n val != null && process.env[envKey] === undefined ? val : undefined\n return {\n warn: pick('OTTO_MEMORY_COVERAGE_WARN_THRESHOLD', r?.coverage_warn_threshold),\n error: pick('OTTO_MEMORY_COVERAGE_ERROR_THRESHOLD', r?.coverage_error_threshold),\n // RFC-340 D1:覆盖率硬下限(低于它给摘要加低保真标注)——同一优先级约定。\n floor: pick('OTTO_MEMORY_COVERAGE_FLOOR_THRESHOLD', r?.coverage_floor_threshold),\n }\n },\n })\n this.storage = {\n storageHost: storageMemory.storageHost,\n autoMemory: storageMemory.autoMemory,\n memory: storageMemory.memory,\n traceStore: storageMemory.traceStore,\n traceLive: storageMemory.traceLive,\n checkpointStore: storageMemory.checkpointStore,\n remoteMemoryConfigured: !!(options.storage?.remote?.getAuth && options.storage?.session && 'url' in options.storage.session),\n remoteSessionConfigured: !!(options.storage?.session && 'url' in options.storage.session),\n }\n this.clock = storageMemory.clock\n\n // ── Phase 4: Settings + Devtools ──\n const settingsPhase = buildSettingsAndDevtools({\n inspect,\n loggerName: logger.name,\n workspaceDir: this.workspaceDir,\n projectConfigPath,\n interactive: this._interactive,\n permissionSync: this.permissionSync,\n sandbox: this.sandbox,\n onSettingsApplied: (setting) => {\n this.settingsLoaded = true\n if (setting.tool_preset) {\n this.defaultToolPreset = setting.tool_preset as 'minimal' | 'standard' | 'full'\n }\n },\n getI18nRegistry: () => this.i18nRegistry,\n })\n this.devtools = settingsPhase.devtools\n this.globalLogSubscription = settingsPhase.logSubscription\n this.settings = settingsPhase.settings\n\n // ── Phase 5: Notifications (thin: already extracted to notification-setup.ts) ──\n const notifications = buildNotificationDispatcher({\n getConfig: () => this.settings.get('notification'),\n getCwd: () => this.workspaceDir,\n isFollowUpPending: () => this.followUpPendingProbe?.() ?? false,\n emitHookNotification: (payload) => void this.hookRegistry.emit('notification', payload),\n })\n this.notificationDispatcher = notifications.dispatcher\n this.presenceTracker = notifications.presenceTracker\n\n // ── Phase 6: Agent Services (thin: already extracted to agent-services-setup.ts) ──\n const settings$ = this.settings\n const agentServices = buildAgentServices({\n skillProvider: options.skillProvider,\n workspaceDir: this.workspaceDir,\n activePlugins: () => this.activePlugins(),\n resolveEffectiveModel,\n providerRegistry: this.providerRegistry,\n prompt: options.prompt,\n memoryStore: storageMemory.memoryStore,\n // RFC-036 死配置修复:memory_auto_extract 的 every_n_turns/max_lessons/similarity_threshold\n // 经 getter 惰性映射(settings 此刻未 load,构造期定档会读空——见 app.ts settings 时序)。\n // enabled 底座恒 true,真实开关在 app-hooks.ts 的 isEnabled 惰性 gate。\n memoryAutoExtractConfig: {\n enabled: true,\n get everyNTurns() {\n return settings$.get('memory_auto_extract')?.every_n_turns\n },\n get maxLessons() {\n return settings$.get('memory_auto_extract')?.max_lessons\n },\n get similarityThreshold() {\n return settings$.get('memory_auto_extract')?.similarity_threshold\n },\n },\n })\n this.skillRegistry = agentServices.skillRegistry\n this.fileStateManager = agentServices.fileStateManager\n this.learningStore = agentServices.learningStore\n this.localLearningStore = agentServices.localLearningStore\n this.memoryAutoExtractor = agentServices.memoryAutoExtractor\n this.completeText = agentServices.completeText\n this.promptManager = agentServices.promptManager\n\n // ── Phase 7: Vision + Session Config ──\n const isModelUsable = createModelUsabilityGate(this._authStore)\n const visionAndConfig = buildVisionAndSessionConfig({\n getModelRegistry: () => this.modelRegistry,\n providerRegistry: this.providerRegistry,\n authStore: this._authStore,\n getSettings: () => this.settings,\n promptManager: this.promptManager,\n getTools: () => this.tools,\n getDeclarativeAgentProfiles: () => this.getEnabledAgentProfiles(),\n defaultModel,\n explicitModelId,\n isModelUsable,\n maxToolTurns: this.maxToolTurns,\n maxToolTurnExtensions: this.maxToolTurnExtensions,\n promptOutputTokenBudget: this.promptOutputTokenBudget,\n promptWallClockBudgetMs: this.promptWallClockBudgetMs,\n sessionCostBudgetUSD: this.sessionCostBudgetUSD,\n stallDetection: this.stallDetection,\n workspaceDir: this.workspaceDir,\n getWorkspaceRef: () => this.workspaceRef,\n workspaceId: this._workspaceId,\n logger: this.logger,\n // RFC-230:惰性 getter——resolve() 在会话创建时才调用,此时 App 必然已 start()\n // (ensureSettingsLoaded() 已完成),getResilienceConfig() 调用安全。\n getResilienceConfig: () => this.getResilienceConfig(),\n })\n const visionDelegateService = visionAndConfig.visionDelegateService\n this.sessionConfigResolver = visionAndConfig.sessionConfigResolver\n\n // ── Phase 8: Agent Runtime ──\n const runtime = buildAppAgentRuntime({\n options,\n hookRegistry: this.hookRegistry,\n providerRegistry: this.providerRegistry,\n getModelRegistry: () => this._modelRegistry,\n logger: this.logger,\n workspaceDir: this.workspaceDir,\n getTools: () => this.tools,\n getDeclarativeAgentProfiles: () => this.getEnabledAgentProfiles(),\n getWorkspaceRef: () => this.workspaceRef,\n repoRef,\n maxToolTurns: this.maxToolTurns,\n promptManager: this.promptManager,\n memory: this.storage.memory,\n traceStore: this.storage.traceStore,\n checkpointStore: this.storage.checkpointStore,\n clock: this.clock,\n devtools: this.devtools ?? undefined,\n residencyGovernor: this._residencyGovernor,\n // RFC-323 M4:注入驻留配置 getter(惰性——ensureSettingsLoaded 后才有值)。\n getResidencyConfig: () => this.getResidencyConfig(),\n visionDelegateService,\n resolveEffectiveModel,\n defaultModel,\n })\n this.agentRuntime = runtime.agentRuntime\n this.codingSessionPool = runtime.codingSessionPool\n installResidencyDiagnosticsBridge(this)\n const swarmAgentRunner = runtime.swarmAgentRunner\n\n // ── Phase 9: Orchestrators + Teams ──\n const orch = buildOrchestratorsAndTeams({\n hookRegistry: this.hookRegistry,\n stream: this.codingSessionPool.stream,\n logger: this.logger,\n defaultModel: runtime.defaultModel,\n providerRegistry: this.providerRegistry,\n getDeclarativeTeams: () => this.declarativeTeams,\n getDeclarativeAgentProfiles: () => this.getEnabledAgentProfiles(),\n getSession: (id) => this.getSession(id),\n createSession: (opts) => this.createSession(opts),\n removeSession: (id) => this.removeSession(id),\n sessionConfigResolver: this.sessionConfigResolver,\n swarmAgentRunner,\n // RFC-067 M115-09:workflow 配置字段本身非 dormant(10 readers),但此前从未接线到\n // Orchestrator——用户配了 retry/circuitBreaker 也不生效。仅投影 orchestrator 认识的\n // 两个字段(review/routing 是无机器实现的预留键,不投影)。\n workflowConfig: {\n retry: this.settings.get('workflow')?.retry,\n circuitBreaker: this.settings.get('workflow')?.circuitBreaker,\n },\n })\n this.orchestrator = orch.orchestrator\n this.runSession = orch.runSession\n this.runTeam = orch.runTeam\n this.writeTodos = orch.writeTodos\n\n // ── Phase 10: Tools + Permission + MCP ──\n this.toolRegistry = this.initToolRegistry(options)\n const tpm = buildToolsAndMCP({\n toolRegistry: this.toolRegistry,\n hookRegistry: this.hookRegistry,\n settings: this.settings,\n workspaceDir: this.workspaceDir,\n logger: this.logger,\n // RFC-230:惰性 getter——ensureManager() 首次连接 MCP server 时才调用,此时 App 必然已 start()。\n getResilienceConfig: () => this.getResilienceConfig(),\n })\n this.permissionRegistry = tpm.permissionRegistry\n this.auditLog = tpm.auditLog\n this.mcpController = tpm.mcpController\n const preExistingHooks = tpm.preExistingHooks\n\n // ── Phase 11: Extensions ──\n this.extensions = buildExtensions({\n settings: this.settings,\n mcpController: this.mcpController,\n skillRegistry: this.skillRegistry,\n declarativeAgentProfiles: this.declarativeAgentProfiles,\n workspaceDir: this.workspaceDir,\n pluginHomedir: this._pluginHomedir,\n // RFC-303 D4:插件启停完成 → monitor 事件(observer-only)。pluginModuleManager\n // 在 Phase 14 才构造,此处用可选链惰性引用——插件 reload 实际发生在 start() 之后。\n onPluginToggle: (id, enabled) => {\n void this.pluginModuleManager?.emitMonitorEvent({\n type: enabled ? 'plugin.enabled' : 'plugin.disabled',\n pluginId: id,\n })\n },\n })\n\n // ── Phase 12: Panel Infrastructure ──\n const panelPhase = buildPanelInfrastructure({\n mcpController: this.mcpController,\n callMcpTool: (server, tool, params) => this.callMcpTool(server, tool, params),\n getActivePlugins: () => this.activePlugins(),\n })\n this.panelBackendController = panelPhase.panelBackendController\n\n // ── Phase 13: Plugin Input + Context Sources + Hooks ──\n const reg = buildPluginRegistrations({\n pluginInput: this.pluginInput,\n sigilResolvers: this.sigilResolvers,\n settings: this.settings,\n workspaceDir: this.workspaceDir,\n activePlugins: () => this.activePlugins(),\n agentRuntime: this.agentRuntime,\n hookRegistry: this.hookRegistry,\n preExistingHooks,\n codingSessionPoolGet: (id) => this.codingSessionPool.getSession(id),\n logger: this.logger,\n registerHooks: () => this.registerHooks(),\n })\n this.ownedHookNames = reg.ownedHookNames\n this.processRegistryUnsubs = reg.processRegistryUnsubs\n\n // ── Phase 14: Plugin Modules + Provider Manager + i18n + Capability Reloader ──\n this.i18nRegistry = new PluginI18nRegistry()\n\n const pluginPhase = buildPluginModules({\n hookRegistry: this.hookRegistry,\n skillRegistry: this.skillRegistry,\n providerRegistry: this.providerRegistry,\n wireProtocolRegistry: this.wireProtocolRegistry,\n modelRegistry: this._modelRegistry,\n authStore: this._authStore,\n toolRegistry: this.toolRegistry,\n feedbackRegistry: this.feedbackRegistry,\n sigilResolverRegistry: this.sigilResolvers,\n pluginInput: this.pluginInput,\n workspaceDir: this.workspaceDir,\n activePlugins: () => this.activePlugins(),\n callMcpTool: (server, tool, params) => this.callMcpTool(server, tool, params),\n // RFC-287 D2:agent.dispatch 能力源。惰性——`this.agentJobs` 是 lazy getter,\n // 只在插件真正调用 startJob 时才实例化作业链(构造期不提前拉起)。\n // origin 固定 'plugin'(R2)——插件不可自选、不可伪装。发起方插件 id 进**标题**\n // 而非 origin 值:`JobOrigin` 是闭合联合,`decideApply` 按 `origin === 'plugin'`\n // 精确匹配走最保守分支,把 id 编进 origin(如 `plugin:foo`)会使该判定失配、\n // 坠入 main 分支并在 autonomy L2+ 可能自动落地。标题标注同样满足\"可审计到具体\n // 插件\"的意图,且不动安全判定面。\n startPluginJob: ({ pluginId, title, prompt, sessionId }) =>\n this.agentJobs.start({ title: `[${pluginId}] ${title}`, prompt, sessionId, origin: 'plugin' }),\n getPluginJobSessionId: () => this.getActiveSession()?.id,\n // RFC-318 M1:session.read 的数据源——只取原始历史,投影/掩码/白名单/配额全部在\n // buildHostCapabilities 内强制(单一强制点,同 startJob 的 origin 纪律)。\n // `session.messages()` 返回 AgentMessage(`Message | SystemAgentMessage | …` 联合)——\n // 过滤到真 LLM 消息(同 hooks/session-learning.ts:10 的既有纹理),system 类本就不出境。\n getSessionMessages: (sessionId) =>\n this.getSession(sessionId)\n ?.session.messages()\n .filter(\n (m): m is Message =>\n m.role === 'user' || m.role === 'assistant' || m.role === 'tool_result',\n ),\n // RFC-318 M1:llm.complete 的模型源——与 RFC-036 lesson 抽取同一个 createModelTextCaller\n // 形状(D10:两条抽取管线并存、各自独立调用,不合并)。\n completeText: (prompt, opts) => this.completeText(prompt, opts),\n capabilityQuota: this.pluginCapabilityQuota,\n askUser: (pluginId, input) => this.askPluginUser(pluginId, input),\n // RFC-303 M5:scoped persistence 的 workspaceKey 来源——workspaceRef 在 App.start()\n // 才解析(同 storage-wiring.ts:79 getWorkspaceKey 既有惰性 getter 模式),插件装载\n // 阶段可能早于该时点,故惰性取值而非构造期直接传 key 字面量。未就位时退回\n // DEFAULT_WORKSPACE_KEY(同 session-pool-factory.ts 的既有回退常量语义)。\n getWorkspaceKey: () => this.workspaceRef?.key,\n logger: this.logger,\n onPluginLoadError: (pluginId, message) => this.pluginHealthTracker.recordError(pluginId, message),\n // RFC-327 D2:插件 schedule 订阅的对账端口。惰性——scheduler 经 setScheduleService\n // 在 TUI 启动流程晚绑定;headless 路径恒 undefined(既定边界,非错误)。\n getSchedulePort: () => this.scheduleServiceInstance,\n settings: this.settings,\n mcpController: this.mcpController,\n skillRegistryForReloader: this.skillRegistry,\n modelCatalogManager: this.modelCatalogManager,\n getActivePluginsForReloader: () => this.activePlugins(),\n i18nRegistry: this.i18nRegistry,\n })\n this.pluginModuleManager = pluginPhase.pluginModuleManager\n this.pluginProviderManager = pluginPhase.pluginProviderManager\n this.capabilityReloader = pluginPhase.capabilityReloader\n\n // ── RFC-303 D4(M7):宿主层 monitor 事件桥接 ──\n // 工具面增减(ToolRegistry.onMutation)→ tool.registered/unregistered。\n // (插件启停桥接在 Phase 11 buildExtensions 的 onPluginToggle 传入——彼时\n // pluginModuleManager 尚未构造,用可选链惰性引用;settings 桥接在 start() 订阅。)\n this.toolRegistry.onMutation = (delta) => {\n this.permissionSync.sync(this.settings.config)\n for (const name of delta.added ?? []) {\n void this.pluginModuleManager.emitMonitorEvent({\n type: 'tool.registered',\n toolName: name,\n source: delta.source,\n })\n }\n for (const name of delta.removed ?? []) {\n void this.pluginModuleManager.emitMonitorEvent({\n type: 'tool.unregistered',\n toolName: name,\n source: delta.source,\n })\n }\n }\n // 配置变更(settings change 事件)→ settings.changed(只带变更键名,R8 脱敏——\n // settings 含 mcp_servers headers 等敏感面,插件需要值时读自己的配置域)。\n // 首次 change 只做快照不触发(避免启动时全员广播噪音)。\n // 审核增强(RFC-303 应用点 C,2026-07-31):顶层键集合 diff 只能捕获\"键新增/删除\",\n // 值变更(model/theme 切换等最常见形态)静默。改为逐键比对(深比较值),事件仍只发\n // 变更键名(R8 脱敏不变)。\n this.settings.on('change', (config) => {\n const current = config as Record<string, unknown>\n const prev = this.lastSettingsConfig\n this.lastSettingsConfig = current\n if (!prev) return\n const allKeys = new Set([...Object.keys(prev), ...Object.keys(current)])\n const diffKeys: string[] = []\n for (const k of allKeys) {\n if (prev[k] !== current[k]) diffKeys.push(k)\n }\n if (diffKeys.length > 0) {\n void this.pluginModuleManager.emitMonitorEvent({\n type: 'settings.changed',\n keys: diffKeys,\n })\n }\n })\n\n // ── Phase 15: Facades(RFC-157 M2:仅保留 toolsSyncCoordinator + PluginContributionsFacade,\n // 4 个纯转发 facade [model/notification/permission/session] 已回收进 app-*-methods.ts 自由函数) ──\n const facades = buildFacades({\n sessionConfigResolver: this.sessionConfigResolver,\n getSession: (id) => this.codingSessionPool.getSession(id),\n listSessionIds: () => this.codingSessionPool.listSessions().map((s) => s.id),\n logger: this.logger,\n workspaceDir: this.workspaceDir,\n settings: this.settings,\n pluginModuleManager: this.pluginModuleManager,\n mcpController: this.mcpController,\n getActivePlugins: () => this.activePlugins(),\n })\n this.toolsSyncCoordinator = facades.toolsSyncCoordinator\n this.pluginContributionsFacade = facades.pluginContributionsFacade\n }\n\n /** 委托 app-lifecycle.ts ensureReady helper。 */\n async ensureSettingsLoaded(): Promise<void> {\n await ensureSettingsLoadedHelper(this.settings, () => this.settingsLoaded, (v) => { this.settingsLoaded = v })\n // RFC-230:settings 真正加载完成后才能安全读取 settings.get('resilience'),\n // 此处是唯一正确的求值时机(延迟冻结,见 §3 D5)。\n this.freezeResilienceConfig()\n // RFC-323 M4:residency 预算配置延迟到此刻冻结(settings 此前未 load)。\n // 走**注入**而非桥接 env——session-contract 常量在模块加载时已固化,写 env 无效(D6b)。\n this.freezeResidencyConfig()\n // RFC-324 D2(M1):MemoryGovernor 阈值同样延迟到此刻注入——构造期走 env/默认值,\n // settings load 后若 env 未设则用 settings 值覆盖。优先级 env > config > 默认。\n const residency = this.settings.get('residency')\n if (residency) {\n this._memoryGovernor.updateThresholds({\n warnThresholdPct: residency.warn_threshold_pct,\n hardThresholdPct: residency.hard_threshold_pct,\n })\n }\n // RFC-230:OAuth 刷新锁超时值同样延迟到此刻回填(AuthStore 构造期只能用硬编码默认值兜底)。\n this._authStore.setRefreshLockTimeoutMs(this.getResilienceConfig().oauth.refreshLockTimeoutMs)\n }\n\n /**\n * RFC-230:合并四层配置源(程序化注入 > settings > env > 默认值)并冻结——只在\n * `ensureSettingsLoaded()` 完成后首次调用时真正计算,之后的调用是 no-op(幂等,\n * 应对 `ensureSettingsLoaded()` 可能被多处调用点重复触发,如 `createSession()`)。\n */\n private freezeResilienceConfig(): void {\n if (this._resilienceConfig !== undefined) return\n this._resilienceConfig = resolveResilienceConfig(this._resilienceOverride, this.settings.get('resilience'))\n }\n\n /** RFC-323 M4:注入式驻留配置(settings > 默认;env 由常量层已处理,见下)。 */\n private _residencyConfig: ResidencyBudgetConfig | undefined\n\n /**\n * RFC-323 M4:把 .otto/config.json(otto 原生配置,非 Claude 的 settings.json)的\n * residency 配置冻结为注入对象。\n *\n * 优先级 env > settings > 默认的实现约定(D6b):session-contract 常量本身已是\n * `normalizeEnv(env, 默认)` 的结果——env 显式设置时常量即为 env 值。故此处**仅当对应\n * env 未设置时**才取 settings 值,交给 resolveResidencyBudget 与常量合并。env 已设则\n * 留空该字段 → resolveResidencyBudget 回退到常量(= env 值),保证 env 最高优先级。\n */\n private freezeResidencyConfig(): void {\n if (this._residencyConfig !== undefined) return\n const r = this.settings.get('residency')\n const pick = (envKey: string, val: number | undefined): number | undefined =>\n val != null && process.env[envKey] === undefined ? val : undefined\n this._residencyConfig = {\n totalBytes: pick('OTTO_SESSION_TOTAL_RESIDENCY_BUDGET', r?.total_bytes),\n minPerSessionBytes: pick('OTTO_SESSION_MIN_PER_SESSION_BYTES', r?.min_per_session_bytes),\n maxPerSessionBytes: pick('OTTO_SESSION_MAX_PER_SESSION_BYTES', r?.max_per_session_bytes),\n maxContentBytes: pick('OTTO_SESSION_MAX_CONTENT_BYTES', r?.max_content_bytes),\n maxHistoryMessages: pick('OTTO_SESSION_MAX_HISTORY_MESSAGES', r?.max_history_messages),\n resumeWindowMessages: pick('OTTO_SESSION_RESUME_WINDOW_MESSAGES', r?.resume_window_messages),\n // RFC-324 D1/R5:turn-gate 压缩超时(此前已进 schema 但零消费方,RFC-330 §4.5)。\n compactionTimeoutMs: pick('OTTO_SESSION_COMPACTION_TIMEOUT_MS', r?.compaction_timeout_ms),\n }\n }\n\n /** RFC-323 M4:注入给 persistence-sync / memory 侧的驻留配置只读查询。 */\n public getResidencyConfig(): ResidencyBudgetConfig | undefined {\n return this._residencyConfig\n }\n\n /**\n * RFC-230:统一重试/超时/退避配置只读查询 API——诊断命令(如未来的 `otto doctor`/\n * `/config show`)据此展示当前生效值。冻结前调用(即 `ensureSettingsLoaded()`/`start()`\n * 尚未完成时)显式抛错,而非静默返回只含 env+默认值的半成品配置——防止调用方误以为\n * 拿到的已经是最终生效值(这是 v1 草案 P0 缺陷的直接防呆,见 RFC-230 §3 D5)。\n */\n public getResilienceConfig(): ResilienceConfig {\n if (this._resilienceConfig === undefined) {\n throw new Error(\n 'getResilienceConfig() called before settings are loaded — call after ensureSettingsLoaded()/start()',\n )\n }\n return this._resilienceConfig\n }\n\n /** 「始终允许」某工具(R8-B):ask→allow 幂等升级(deny 不动)。委托 app-permission-methods.ts\n * (RFC-157 M2 T2.3,回收自已删除的 PermissionFacade)。 */\n async allowToolAlways(toolName: string, sessionId?: string): Promise<void> {\n return permissionMethods.allowToolAlways(this, toolName, sessionId)\n }\n\n /** 当前权限 mode。缺省 'auto'。委托 app-permission-methods.ts。 */\n getPermissionMode(): PermissionMode {\n return permissionMethods.getPermissionMode(this)\n }\n\n /** 设置权限 mode:persist 落盘 → reload → change → permissionSync 重同步。委托 app-permission-methods.ts。 */\n async setPermissionMode(next: PermissionMode): Promise<void> {\n return permissionMethods.setPermissionMode(this, next)\n }\n\n /** 某会话的「始终允许」工具名快照(弹窗运行时授权,不含项目级基线)。委托 app-permission-methods.ts。 */\n getSessionAlwaysAllow(sessionId: string): string[] {\n return permissionMethods.getSessionAlwaysAllow(this, sessionId)\n }\n\n /**\n * bash 命令的「始终允许」两段前缀提取(如 `git status`,而非整个 bash 工具)。\n * 终局架构 review 2026-07-10 P1-5:转发 @x-otto/hooks 纯函数,前端不再直连引擎权限模块。\n */\n bashAlwaysAllowPrefix(command: string): string | null {\n return bashAlwaysAllowPrefixFn(command)\n }\n\n /**\n * 校验一条 always-allow 字符串是否格式良好(`/permissions add` 落盘前的守门)。\n * 终局架构 review 2026-07-10 P1-5:转发 @x-otto/hooks 纯函数,前端不再直连引擎权限模块。\n */\n isWellFormedAllowEntry(entry: string): boolean {\n return isWellFormedAllowEntryFn(entry)\n }\n\n /** 移除一条项目级「始终允许」(/permissions remove):撤 registry + 抹持久化。返回是否命中。委托 app-permission-methods.ts。 */\n async disallowToolAlways(toolName: string): Promise<boolean> {\n return permissionMethods.disallowToolAlways(this, toolName)\n }\n\n /** 清空某会话的「始终允许」(/permissions clear):撤销 registry + 抹掉持久化字段。委托 app-permission-methods.ts。 */\n clearSessionAlwaysAllow(sessionId: string): void {\n permissionMethods.clearSessionAlwaysAllow(this, sessionId)\n }\n\n /**\n * 当前活跃插件(bundle 启停的统一发现点)。委托 PluginTopologyManager(RFC-152 M2)。\n * 应用 `disabled_plugins` 过滤——禁用插件不返回,其 skill/agent/command/mcp 贡献全部消失。\n * 各轴发现(skill/agent 经此注入;command 经 cli 调用此方法;mcp 控制器自带 settings 等价过滤)共用。\n */\n activePlugins(): DiscoveredPlugin[] {\n return this.pluginTopology.resolve({\n workspaceDir: this.workspaceDir,\n disabledPlugins: this.settings.get('disabled_plugins') ?? [],\n homedir: this._pluginHomedir,\n })\n }\n\n /**\n * 插件拓扑排序 fail-closed 诊断。委托 PluginTopologyManager(RFC-152 M2)。\n * RFC-163 D5:签名改为 async(诊断内部追加缺失依赖自动补装尝试,见 PluginTopologyManager.diagnose)。\n * RFC-219 M1(对抗审核 F1 修复):传入 `this.settings`,使自动补装成功后能记录到\n * installed_plugins 清单(此前只落 symlink,导致清单与磁盘状态永久性不一致)。\n */\n async diagnoseTopoSortIssues(): Promise<void> {\n await this.pluginTopology.diagnose(this.logger, this.settings)\n }\n\n /** ServiceHostPort:文件命令目录。委托 PluginContributionsFacade。 */\n listFileCommands(): FileCommand[] {\n return this.pluginContributionsFacade.listFileCommands()\n }\n\n /** ServiceHostPort:调用 MCP server 上的指定 tool(RFC-081)。委托 PluginContributionsFacade。 */\n async callMcpTool(\n serverName: string,\n toolName: string,\n params?: Record<string, unknown>,\n ): Promise<{ content: { type: string; text?: string }[]; isError?: boolean }> {\n return this.pluginContributionsFacade.callMcpTool(serverName, toolName, params)\n }\n\n /** RFC-081:TUI 插件面板列表。完整注释见 `PluginContributionsFacade.getPluginPanelRegistrations`。 */\n getPluginPanelRegistrations(): PanelRegistration[] {\n return this.pluginContributionsFacade.getPluginPanelRegistrations()\n }\n\n /** RFC-105 D5:状态栏插件条目(/extension 详情页按 id 过滤用)。完整注释见 facade 同名方法。 */\n getPluginStatusItems(): ResolvedStatusItem[] {\n return this.pluginContributionsFacade.getPluginStatusItems()\n }\n\n /** RFC-105 D5:全局菜单插件贡献(/extension 列表页消费)。完整注释见 facade 同名方法。 */\n getGlobalMenuItems(): ResolvedAction[] {\n return this.pluginContributionsFacade.getGlobalMenuItems()\n }\n\n /** RFC-105 D6:插件消息渲染器(/extension 详情页按 id 过滤用)。完整注释见 facade 同名方法。 */\n getPluginRenderers(): RegisteredRenderer[] {\n return this.pluginContributionsFacade.getPluginRenderers()\n }\n\n /** RFC-184 D2/D3:插件状态栏代码式徽标(RFC-206 M9 前置子任务②新增公开代理方法——\n * facade 侧原已实现但此前未在 App 上公开,web-ui catalog 端点需要它)。完整注释见\n * facade 同名方法。 */\n getPluginStatusWidgets(): RegisteredStatusWidget[] {\n return this.pluginContributionsFacade.getPluginStatusWidgets()\n }\n\n /** RFC-187 D3/D4:插件文件查看器候选。完整注释见 facade 同名方法。 */\n getPluginFileViewers(): RegisteredFileViewer[] {\n return this.pluginContributionsFacade.getPluginFileViewers()\n }\n\n /** RFC-209 §2.5/§3.1:Feedback 插件是否已装载(ServiceHostPort 消费,见该接口同名方法注释)。 */\n isFeedbackAvailable(): boolean {\n return this.feedbackRegistry.get() !== undefined\n }\n\n /** RFC-209 §2.5:满意度脉冲调查触发配置读取(ServiceHostPort 消费,见该接口同名方法注释)。 */\n getPulseSurveyConfig(): { enabled?: boolean; probability?: number; min_turn_gap?: number } {\n return this.settings.get('pulse_survey') ?? {}\n }\n\n /** RFC-209 §2.5:满意度评分记录(fail-soft,插件未装载时静默丢弃)。 */\n recordPulseSurvey(entry: import('@x-otto/plugin').PulseSurveyEntry): void {\n this.feedbackRegistry.get()?.recordPulseSurvey?.(entry)\n }\n\n /**\n * RFC-209 §2.5/重要事项规则7:两端共用的 issue URL 构造——**只调用 `buildIssueUrl`**,\n * 绝不引用 `submitIssueViaGh`(本方法体内物理上没有到达 gh CLI 调用路径的代码)。\n */\n buildFeedbackIssueUrl(input: import('@x-otto/plugin').FeedbackIssueInput): string | undefined {\n return this.feedbackRegistry.get()?.buildIssueUrl?.(input)\n }\n\n /**\n * RFC-348 M1:ServiceHostPort settings 写面——委托 `SettingsManager.update()`。\n * service 的 settings route 四个写端点(PUT /、POST /mode、POST /autonomy、\n * POST /thinking)经本方法调 `settings.update()`,persist + change 事件语义不变。\n */\n updateSettings(overrides: Partial<import('@x-otto/setting').Setting>): Promise<import('@x-otto/setting').Setting> {\n return this.settings.update(overrides)\n }\n\n /**\n * RFC-210 D5(M4):跨端渲染 toPayload 执行出口(ServiceHostPort 契约实现)。\n * 装载走 loadAllPluginA2uiRenderers(capability `a2ui.renderer` 三层信任门);\n * 执行 fail-soft(超时 2s/抛错 → 下一候选 → null 回退默认渲染)。\n * 每次调用重新装载列表的成本由 loadAllCompiledContributions 的 require cache 抵消\n * (模块首装后 require 命中缓存,只余 matcher 循环——与 renderers 轴 getPluginRenderers\n * 同款每调用重扫模式,插件启停后无需失效通知)。\n */\n resolveA2uiRendererPayload(message: unknown): Promise<import('@x-otto/interchange').A2uiComponent[] | null> {\n return resolveA2uiRendererPayloadFrom(loadAllPluginA2uiRenderers(this.activePlugins()), message)\n }\n\n /** RFC-131 T8:聚合插件 TUI 贡献(`TuiHandle.setPluginRegistrations` 统一入口)。完整注释见 `PluginContributionsFacade.getPluginRegistrations`。 */\n getPluginRegistrations(): Array<{\n pluginId: string\n renderers?: RegisteredRenderer[]\n statusItems?: { id: string; text: string }[]\n panels?: PanelRegistration[]\n a2uiComponents?: RegisteredA2uiComponent[]\n statusWidgets?: RegisteredStatusWidget[]\n fileViewers?: RegisteredFileViewer[]\n }> {\n return this.pluginContributionsFacade.getPluginRegistrations()\n }\n\n /** RFC-105 D7:触发插件激活事件(匹配 activationEvents 的 deferred 插件执行完整 reload)。 */\n async firePluginActivationEvent(event: string): Promise<void> {\n await this.pluginModuleManager.activateOn(\n event,\n new Set(this.declarativeAgentProfiles.map((p) => p.name)),\n )\n }\n\n async start(): Promise<void> {\n return startApp(this)\n }\n\n async completeWorkspaceDependentStartup(): Promise<void> {\n return completeWorkspaceDependentStartupImpl(this)\n }\n\n needsWorkspaceInit(): boolean {\n return needsWorkspaceInitImpl(this)\n }\n\n async confirmWorkspaceInit(): Promise<WorkspaceRef | undefined> {\n return confirmWorkspaceInitImpl(this)\n }\n\n hasParentWorkspace(): boolean {\n return needsParentWorkspaceDecision(this)\n }\n\n async adoptParentWorkspace(): Promise<WorkspaceRef | undefined> {\n return adoptParentWorkspaceImpl(this)\n }\n\n async createLocalWorkspace(): Promise<WorkspaceRef | undefined> {\n return createLocalWorkspaceImpl(this)\n }\n\n async setWorkspaceMemorySyncEnabled(enabled: boolean): Promise<boolean> {\n return setWorkspaceMemorySyncEnabledImpl(this, enabled)\n }\n\n async sweepOrphanTraceFiles(): Promise<void> {\n return sweepOrphanTraceFilesImpl(this)\n }\n\n async stop(): Promise<void> {\n return stopApp(this)\n }\n\n /** 委托 app-session-methods.ts(RFC-157 M2 T2.4,回收自已删除的 SessionFacade)。 */\n async createSession(options: CodingSessionOptions = {}): Promise<AgentSession> {\n return sessionMethods.createSession(this, options)\n }\n\n /** 注册一个通知交付通道(终端/带外/应用内)。前端接线入口。委托 app-notification-methods.ts。 */\n registerNotificationChannel(channel: NotificationChannel): void {\n notificationMethods.registerNotificationChannel(this, channel)\n }\n\n /** 注册应用内通知通道(TUI NotificationCenter;透传原始 kind,视觉映射归 UI)。委托 app-notification-methods.ts。 */\n registerInAppNotificationChannel(\n push: (title: string, message: string, kind: NotificationKind) => void,\n ): void {\n notificationMethods.registerInAppNotificationChannel(this, push)\n }\n\n /** RFC-344 M3:TUI 层设置/清除阻塞式问询渲染器。headless 不设置时 user.ask fail-closed。 */\n setUserAskRenderer(renderer: ((question: GrillQuestion) => Promise<GrillAnswer>) | undefined): void {\n this.userAskRenderer = renderer\n }\n\n private async askPluginUser(\n pluginId: string,\n input: Omit<GrillQuestion, 'id' | 'askedBy'>,\n ): Promise<GrillAnswer> {\n const question: GrillQuestion = {\n ...sanitizeAskInput(input),\n id: `plugin_ask_${++this.userAskSeq}`,\n // R7:askedBy 由宿主从闭包捕获的插件身份强制填充,插件 payload 里的同名字段(若有)\n // 不在类型/模板透传面,宿主也不读取。插件不能伪装成 agent/user/其他插件。\n askedBy: `plugin:${pluginId}`,\n }\n\n const renderer = this.userAskRenderer\n // `interactive` 是本地 TUI 的 gate,绝不能在没有可信外部 InteractionTarget 时被\n // 解释为“可自动替用户同意 recommendation”。没有 renderer 的 ask 必须拒绝。\n if (!this._interactive || !renderer) return denyAnswer()\n\n const admitted = this.inquiryCenter.enqueue({\n id: question.id,\n origin: { kind: 'plugin', pluginId },\n question,\n })\n if (!admitted.accepted) return denyAnswer()\n\n if (!(await this.inquiryCenter.waitForTurn(question.id))) return await admitted.answer\n\n try {\n const answer = await renderer(question)\n this.inquiryCenter.settle(question.id, answer)\n return answer\n } catch (err) {\n this.inquiryCenter.settle(question.id, denyAnswer())\n throw err\n }\n }\n\n /** TUI 可选在每次按键调用,喂更细的在场信号(默认已由 prompt.start 粗粒度喂入)。委托 app-notification-methods.ts。 */\n notifyUserInteraction(): void {\n notificationMethods.notifyUserInteraction(this)\n }\n\n /** DEC1004 焦点事件(opt-in OTTO_TUI_FOCUS=1)。TUI 解析 \\x1b[I/\\x1b[O 后调用。委托 app-notification-methods.ts。 */\n setTerminalFocus(focused: boolean): void {\n notificationMethods.setTerminalFocus(this, focused)\n }\n\n /** TUI 注入\"有待跑 follow-up\"探针(读 state.queued);有则抑制 turn_complete 通知。 */\n setFollowUpPendingProbe(probe: () => boolean): void {\n this.followUpPendingProbe = probe\n }\n\n /**\n * RFC-318 D9:向全部运行中的 service 广播一条宿主事件通知。\n *\n * **这是 `session.read` 能力的授权入口**:每成功送达一次通知,就把该 sessionId 登记进\n * 目标插件的白名单并开启新配额 epoch。因此\"插件能读哪个会话\"完全由宿主的通知行为决定,\n * 插件无法自行扩大可读范围(RFC-318 R2)。\n *\n * 尽力而为:未运行/已停机的 service 直接跳过,不抛错——通知是旁路信号,不该让某个插件\n * 的状态影响宿主主循环。返回实际送达的 `<pluginId>:<serviceId>` 数量,供调用方记账。\n */\n notifyPluginServices(notification: SessionIdleNotification): number {\n const pool = getPluginServicePool()\n let delivered = 0\n for (const plugin of this.activePlugins()) {\n for (const serviceId of pool.runningServiceIds(plugin.id)) {\n if (!pool.notify(plugin.id, serviceId, notification)) continue\n this.pluginCapabilityQuota.onNotifyDelivered(plugin.id, notification.sessionId)\n delivered += 1\n }\n }\n return delivered\n }\n\n /**\n * RFC-327 D2:向**指定**插件服务投递一次 schedule 到点通知。\n *\n * **与 `notifyPluginServices` 的关键区别(安全边界,不可合并)**:\n * 1. **定向而非广播**——只投给订阅该 schedule 的那一个 service,其他插件收不到;\n * 2. **不开会话授权 epoch**——`schedule.tick` 没有 sessionId,绝不调用\n * `onNotifyDelivered`。后者是插件获得 `readTranscript` 的唯一入口,若在此误调,\n * 定时任务将成为绕过\"宿主决定插件能读哪个会话\"(RFC-318 R2)的提权旁路。\n *\n * 服务要产生工作仍须自己走 capability-gated `startJob`——timer 触发不携带、也不授予\n * 任何额外能力。返回是否送达(服务未运行 = false,调度器据此记日志,不重试)。\n */\n notifyPluginServiceSchedule(\n subscriber: { pluginId: string; serviceId: string },\n tick: { scheduleId: string; occurrenceId: string; occurredAt: number },\n ): boolean {\n // 只允许投给当前激活插件的运行中服务——已禁用/已卸载插件的残留订阅不得再被唤醒\n // (RFC-327 D2 的 capability revoke 语义)。\n const active = this.activePlugins().some((p) => p.id === subscriber.pluginId)\n if (!active) return false\n\n return getPluginServicePool().notify(subscriber.pluginId, subscriber.serviceId, {\n type: 'schedule.tick',\n scheduleId: tick.scheduleId,\n occurrenceId: tick.occurrenceId,\n occurredAt: tick.occurredAt,\n })\n }\n\n /** 委托 app-session-methods.ts。 */\n getSession(id: string): AgentSession | undefined {\n return sessionMethods.getSession(this, id)\n }\n\n /** 委托 app-session-methods.ts。 */\n async shareSession(sessionId: string, opts: { visibility: 'private' | 'shared' }): Promise<void> {\n return sessionMethods.shareSession(this, sessionId, opts)\n }\n\n /** 委托 app-session-methods.ts。 */\n getActiveSession(): AgentSession | undefined {\n return sessionMethods.getActiveSession(this)\n }\n\n /** 委托 app-session-methods.ts。 */\n listSessions(filter?: { workspaceId?: string }): AgentSessionInfo[] {\n return sessionMethods.listSessions(this, filter)\n }\n\n /**\n * listSessions() 的超集——额外并入持久化后端已知但尚未加载进内存的会话。\n * 详见 app-session-methods.ts listAllSessions 的完整设计注释(RFC-146)。委托 app-session-methods.ts。\n */\n async listAllSessions(filter?: { workspaceId?: string }): Promise<AgentSessionInfo[]> {\n return sessionMethods.listAllSessions(this, filter)\n }\n\n /** 委托 app-session-methods.ts。 */\n async removeSession(id: string): Promise<boolean> {\n return sessionMethods.removeSession(this, id)\n }\n\n /** 委托 app-session-export.ts(RFC-199 M1 提取)。 */\n async exportSessionArchive(\n sessionId: string,\n outPath: string,\n ): Promise<SessionArchiveExport> {\n return exportSessionArchive(sessionId, outPath, this.sessionRepoRef ?? {})\n }\n\n /** 委托 app-session-methods.ts。 */\n archiveSession(id: string): Promise<boolean> {\n return sessionMethods.archiveSession(this, id)\n }\n\n /** 委托 app-session-methods.ts。 */\n async forkSession(sourceId: string, newId?: string): Promise<AgentSession | undefined> {\n return sessionMethods.forkSession(this, sourceId, newId)\n }\n\n /**\n * 引擎 spawn 路径可见的声明式 agent——按 settings.disabled_agents 过滤(/extension 切换的真源)。\n * 每次调用重读 settings → 运行时切换下次 spawn 生效,无需重建。空禁用集时返回原数组(零开销)。\n */\n private getEnabledAgentProfiles(): AgentProfile[] {\n const disabled = new Set(this.settings.get('disabled_agents') ?? [])\n return disabled.size === 0\n ? this.declarativeAgentProfiles\n : this.declarativeAgentProfiles.filter((p) => !disabled.has(p.name))\n }\n\n /** 工具注册表初始化。委托 tool-wiring.ts(RFC-152 M4)。 */\n private initToolRegistry(options: AppOptions): ToolRegistry {\n return initToolRegistry(this, options)\n }\n\n /** list_models 工具的目录——registry 全量 + 会话级可用集合 + usability gate 标记。委托 app-model-methods.ts。 */\n public async buildModelCatalog(sessionId?: string): Promise<\n Array<{\n id: string\n provider: string\n strengths: string[]\n available: boolean\n usable: boolean\n unusableReason?: string\n }>\n > {\n return modelMethods.buildModelCatalog(this, sessionId)\n }\n\n /** 写会话级「账号可用模型」(随快照持久化,--continue 回灌)。委托 app-model-methods.ts。 */\n public setSessionAvailableModels(sessionId: string, modelIds: readonly string[]): void {\n modelMethods.setSessionAvailableModels(this, sessionId, modelIds)\n }\n\n /** hook 注册装配。委托 tool-wiring.ts(RFC-152 M4)。 */\n private registerHooks(): void {\n registerHooksForHost(this)\n }\n\n /** 当前生效的沙箱策略名(null = 无沙箱)。可观测面:测试/调试/CLI 状态展示。 */\n public get activeSandbox(): string | null {\n return this.sandbox.activeName\n }\n\n /**\n * M4 1b-2:标记一个命令为「已批准绕过沙箱」(会话级)。\n * cli 在沙箱升级 ask 被用户批准时调用——该命令后续执行不再套沙箱(用户已审,\n * = Codex approval=escalated)。仅沙箱启用时有意义;未启用时无副作用。\n */\n public approveSandboxBypass(command: string): void {\n this.sandbox.approveBypass(command)\n }\n\n ensureNotDisposed(): void {\n if (this.disposed) {\n throw new Error('App has been stopped and cannot be reused.')\n }\n }\n}\n\nfunction denyAnswer(): GrillAnswer {\n return {\n answer: '',\n acceptedRecommendation: false,\n resolvedBy: 'human',\n }\n}\n\nfunction sanitizeAskInput(input: Omit<GrillQuestion, 'id' | 'askedBy'>): Omit<GrillQuestion, 'id' | 'askedBy'> {\n return {\n header: sanitizeAskText(input.header, 160),\n question: sanitizeAskText(input.question, 2000),\n background: sanitizeAskText(input.background, 4000),\n recommendation: sanitizeAskText(input.recommendation, 1000),\n ...(input.options\n ? {\n options: input.options.slice(0, 8).map((o) => ({\n label: sanitizeAskText(o.label, 120),\n rationale: sanitizeAskText(o.rationale, 1000),\n ...(o.recommended ? { recommended: true } : {}),\n })),\n }\n : {}),\n ...(input.allowFreeform !== undefined ? { allowFreeform: input.allowFreeform } : {}),\n ...(input.multiSelect !== undefined ? { multiSelect: input.multiSelect } : {}),\n ...(input.evidence ? { evidence: input.evidence.slice(0, 8).map((e) => sanitizeAskText(e, 1000)) } : {}),\n }\n}\n\nfunction sanitizeAskText(value: unknown, maxChars: number): string {\n return String(value ?? '')\n // eslint-disable-next-line no-control-regex\n .replace(/[\u0000-\u001f-Ÿ‎‏‪-‮⁦-⁩]/g, ' ')\n .slice(0, maxChars)\n}\n\nexport function createApp(options: AppOptions): App {\n return new App(options)\n}\n","/** FrontendPort factory: create FrontendSessionPort for remote clients. */\nimport { globalAgentJobRegistry } from '@x-otto/runtime'\nimport type { AgentSession } from '@x-otto/runtime'\nimport type { AgentSessionEvent, AgentSessionEventMap, AgentSessionEventType } from '@x-otto/interchange'\nimport type {\n FrontendPort,\n FrontendSessionPort,\n FrontendPromptImage,\n} from '@x-otto/session-contract'\nimport { getLastAssistantText } from '../session/assistant-utils'\nimport type { App } from './app'\n\n/** `AgentSession` → `FrontendSessionPort` 适配器(纯投影,不新增行为)。 */\nexport function adaptAgentSession(session: AgentSession): FrontendSessionPort {\n return {\n get id() {\n return session.id\n },\n get title() {\n return session.title\n },\n get agentName() {\n return session.agentName\n },\n get model() {\n return { id: session.model.id, name: session.model.name, provider: session.model.provider }\n },\n get isBusy() {\n return session.isBusy\n },\n messageCount: () => session.session.messages().length,\n lastAssistantText: () => getLastAssistantText(session.session.messages()),\n prompt: (text: string, options?: { images?: FrontendPromptImage[] }) =>\n session.prompt(text, options),\n // `AgentSession.subscribe` 是继承自 EventBus 的重载方法(按类型分派 handler 签名),\n // 委托转发时 TS 无法跨方法重载验证 type/handler 的配对关系类型安全(虽运行时确实安全,\n // 调用方总是同时传入一致的 type 与对应 handler)——用 unknown 双跳收口这一处转发点,\n // 不扩散到 FrontendSessionPort 接口本身(接口签名保持强类型判别联合)。\n subscribe: ((type: AgentSessionEventType, handler: (event: AgentSessionEvent) => void) =>\n session.subscribe(\n type,\n handler as unknown as AgentSessionEventMap[typeof type],\n )) as FrontendSessionPort['subscribe'],\n resolveApproval: (approvalId: string, approved: boolean) =>\n session.resolveApproval(approvalId, approved),\n resolveAskUser: (requestId, answer) => session.resolveAskUser(requestId, answer),\n clearContext: () => session.clearContext(),\n compactNow: () => session.compactNow(),\n renameSession: (title: string) => session.renameSession(title),\n abort: () => session.abort(),\n dispose: () => session.dispose(),\n }\n}\n\n/**\n * 构造 `FrontendPort` 实现(引擎侧唯一装配点)。前端宿主(进程内嵌入时 = 前端自己的\n * 装配入口,如 feishu-use bridge)调用一次,得到的端口对象即前端业务代码的全部消费面——\n * 不再需要 import 任何 `@x-otto/coding`/`@x-otto/runtime` 具体类型。\n *\n * 入参类型刻意用具体类 `App`(而非收窄接口 `AppContext`)——本工厂本身是引擎侧代码,\n * 职责正是把完整实现(`App` 的全部 public 成员,含未列入 `AppContext` 接口的\n * `i18nRegistry` 等)收窄成 `FrontendPort` 契约给前端消费。前端业务代码永远只接触\n * `createFrontendPort` 的返回值,不持有 `App` 本身。\n */\nexport function createFrontendPort(app: App): FrontendPort {\n return {\n createSession: async (options) => {\n const session = await app.createSession(options)\n return adaptAgentSession(session)\n },\n jobs: globalAgentJobRegistry,\n models: {\n getAll: () => app.modelRegistry.getAll().map((m) => ({ id: m.id, name: m.name, provider: m.provider })),\n switchModel: async (sessionId, modelId) => {\n // FrontendPort 契约层面 sessionId 是不透明标识——引擎侧按前端自身的会话路由约定\n // 解析(本仓约定:sessionId 即 AgentSession.id,与 feishu 的 chatId 路由无关,\n // chatId→session 的映射由前端自己的路由层维护,不下沉进本工厂)。\n const session = app.getSession(sessionId)\n if (!session) return '会话未找到'\n if (session.isBusy) return '当前有正在执行的对话,请等待完成后重试。'\n const target = app.modelRegistry.find(modelId)\n if (!target) return `未知模型 \\`${modelId}\\`。`\n session.model = target\n return null\n },\n refresh: async () => {\n const results = await app.refreshModels()\n return results.map((r) => ({\n provider: r.provider,\n status: r.status,\n added: r.added,\n kept: r.kept,\n total: r.total,\n reason: r.reason,\n }))\n },\n },\n permission: {\n getMode: () => app.getPermissionMode(),\n setMode: (mode) => app.setPermissionMode(mode as Parameters<typeof app.setPermissionMode>[0]),\n },\n auth: {\n getAuthSource: (providerId) => app.authStore.getAuthSource(providerId),\n },\n i18n: {\n t: (fullKey, locale) => app.i18nRegistry.t(fullKey, locale),\n },\n reloadCapabilities: () => app.reloadCapabilities(),\n workspaceDir: app.workspaceDir,\n }\n}\n","/** Collect plugin perf metrics: startup time, memory, tool call latency. */\nimport { evaluatePluginTrust } from '@x-otto/plugin'\nimport type { DiscoveredPlugin, PluginTrustVerdict } from '@x-otto/plugin'\n\nimport { poolForScope } from './module-loader-isolated'\n\n// RFC-118 小修(S10):类型本体已上移 @x-otto/interchange(tui 侧同源消费,镜像漂移消除),\n// 此处重导出保持消费方兼容。\nexport type { PluginPerfMetricResult } from '@x-otto/interchange'\nimport type { PluginPerfMetricResult } from '@x-otto/interchange'\n\nconst COLLECT_TIMEOUT_MS = 5000\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error('timeout')), ms)\n // unref:超时保护定时器不应阻止进程退出(对齐仓库既有的 unref 定时器约定)。\n timer.unref?.()\n promise.then(\n (v) => { clearTimeout(timer); resolve(v) },\n (e) => { clearTimeout(timer); reject(e) },\n )\n })\n}\n\n/**\n * 判定某插件的 `perfMetrics` 采集器是否可调用——`gated`(项目级未信任)整插件不可见;\n * `tools` 能力在 `pendingCapabilities` 中(声明但未显式授予)该插件采集器不可见。\n * 经独立评审 P1-2 修正:不能只查 `gated`(与\"能力是否已授权\"是正交维度)。\n *\n * `evaluateTrust` 可注入(测试用,避免依赖真实磁盘 trust store);默认用真实\n * `evaluatePluginTrust`(生产路径)。\n */\nfunction isPerfMetricsAllowed(\n plugin: DiscoveredPlugin,\n evaluateTrust: (p: DiscoveredPlugin) => PluginTrustVerdict = evaluatePluginTrust,\n): boolean {\n const verdict = evaluateTrust(plugin)\n if (verdict.gated) return false\n const declaresTools = plugin.manifest.capabilities?.includes('tools') ?? false\n if (!declaresTools) return false\n return !verdict.pendingCapabilities.includes('tools')\n}\n\n/**\n * 采集所有已授权插件的性能指标——并发调用(`Promise.allSettled`),单个插件超时/\n * 报错不阻塞其他插件。手动触发(`/perf` 面板打开/刷新时调用),不做自动轮询\n * (见 RFC-116 D7,规避\"监控自身造成性能问题\"的自反风险)。\n */\nexport async function collectPluginPerfMetrics(\n plugins: readonly DiscoveredPlugin[],\n /** 测试用注入点——生产路径不传,使用真实 `evaluatePluginTrust`。 */\n evaluateTrust?: (p: DiscoveredPlugin) => PluginTrustVerdict,\n /** RFC-153 D3:代码式工具挂起超时的旁路通知(可选,宿主层注入)。只在真实超时(非\n * \"no worker\" 场景的 throw)时触发,供 `PluginHealthTracker.recordToolTimeout` 写入。 */\n onToolTimeout?: (pluginId: string) => void,\n): Promise<PluginPerfMetricResult[]> {\n const tasks: Array<Promise<PluginPerfMetricResult>> = []\n\n for (const plugin of plugins) {\n if (!isPerfMetricsAllowed(plugin, evaluateTrust)) continue\n const contributions = plugin.manifest.contributes?.perfMetrics\n if (!contributions?.length) continue\n // RFC-177 B2:按插件 scope 选对应的池——project/repository 插件的工具执行在\n // PluginProcessPool 里,若仍固定查 PluginWorkerPool 会因\"no worker for plugin\"\n // 误判为失败(池里确实没有该插件的记录,但不是真的失败,是问错了池)。\n const pool = poolForScope(plugin.scope)\n\n for (const contribution of contributions) {\n const task = (async (): Promise<PluginPerfMetricResult> => {\n try {\n const { result, error } = await withTimeout(\n pool.executeTool(plugin.id, contribution.toolRef, {}),\n COLLECT_TIMEOUT_MS,\n )\n if (error) return { id: contribution.id, label: contribution.label, error }\n return { id: contribution.id, label: contribution.label, value: result }\n } catch (err) {\n // 归一化两类失败:① 超时(withTimeout 的 reject);② 池层\"no worker for\n // plugin\"(编译失败/worker 未启动时的 throw,见 P1-1 已知退化场景)。\n const message = err instanceof Error ? err.message : String(err)\n if (message === 'timeout') onToolTimeout?.(plugin.id)\n return { id: contribution.id, label: contribution.label, error: message }\n }\n })()\n tasks.push(task)\n }\n }\n\n const settled = await Promise.allSettled(tasks)\n // 经独立评审确认:每个 task 的 IIFE 全部代码都在 try/catch 内部(含 pool.executeTool\n // 的参数求值),catch 分支必定 return 而非 rethrow——`s.status === 'rejected'` 分支\n // 在当前实现下不可达(死代码)。保留只是防御性冗余,防止未来重构不慎在 IIFE 外\n // 引入可能 throw 的代码时静默丢失结果,而非真实存在的触发路径。\n return settled.map((s, i) =>\n s.status === 'fulfilled'\n ? s.value\n : { id: `unknown-${i}`, label: 'unknown', error: String(s.reason) },\n )\n}\n","/** Read plugin log tail helper (last N lines, byte-cap). */\nimport { readFileSync, existsSync } from 'node:fs'\n\nimport { resolvePluginLogPath } from './plugin-log-writer'\nimport type { PluginLogEntry } from './plugin-log-writer'\n\n/** 面板默认展示的最近日志条数(规则 6:固定上限,非用户可调)。 */\nexport const PLUGIN_LOG_TAIL_LINES = 20\n\n/**\n * 读取指定插件日志文件的最后 N 行,解析为结构化条目。fail-soft:\n * - 非法 pluginId(未过 kebab-case 校验)→ 空数组。\n * - 文件不存在(插件从未打过日志)→ 空数组(调用方据此展示\"暂无日志\"占位)。\n * - 单行 JSON 解析失败(如截断产生的半行残留,理论上不应发生但防御性处理)→ 跳过该行。\n */\nexport function readPluginLogTail(\n pluginId: string,\n maxLines: number = PLUGIN_LOG_TAIL_LINES,\n): PluginLogEntry[] {\n const path = resolvePluginLogPath(pluginId)\n if (!path || !existsSync(path)) return []\n\n let content: string\n try {\n content = readFileSync(path, 'utf8')\n } catch {\n return [] // fail-soft:读取失败(权限/竞态)不影响面板渲染。\n }\n\n const lines = content.split('\\n').filter((l) => l.trim().length > 0)\n const tail = lines.slice(-maxLines)\n\n const entries: PluginLogEntry[] = []\n for (const line of tail) {\n try {\n entries.push(JSON.parse(line) as PluginLogEntry)\n } catch {\n // 跳过解析失败的行(防御性——正常写入路径不应产生非法 JSON 行)。\n }\n }\n return entries\n}\n","/**\n * contribution-host.ts —— RFC-084 宿主侧贡献接线(M084-02/03)。\n *\n * 契约层(@x-otto/plugin)是纯函数;宿主在此提供**副作用 capability** + 构建 context:\n * - `buildExtensionContext`:内置 `extension.*` 键 + 求值「当前查看插件」声明的 context-key providers(§4.4b per-viewed-plugin)。\n * - `openTarget`:`open` 型 opener(chrome:// 特判拉起浏览器)。\n * 其余 caps(runCommand=dispatchCliCommand、callMcp)在 cli handler 就近装配(那里有 CliContext)。\n */\nimport { spawn } from 'node:child_process'\nimport { buildAllowedEnv, checkShellCommand } from '@x-otto/shared'\nimport { evaluateContextKeys, type ContextMap, type DiscoveredPlugin } from '@x-otto/plugin'\n\n/**\n * 构建「查看某扩展」的 context:内置键(谁在被看)+ 该插件声明的 context-key providers 求值(文件探针)。\n * `exists` 可注入以便单测。\n */\nexport function buildExtensionContext(\n plugin: DiscoveredPlugin | undefined,\n builtin: Record<string, string | boolean>,\n exists?: (p: string) => boolean,\n): ContextMap {\n const probes = plugin?.manifest.contributes?.contextKeys\n return { ...builtin, ...evaluateContextKeys(probes, exists) }\n}\n\n/**\n * `script` 型 capability:运行**该插件**声明的命名脚本(`manifest.scripts[name]`,RFC-084 D8)。\n * 在插件目录内跑 shell 命令,等退出码。**信任门由调用方在装配 cap 时把关**(可执行面,RFC-082)。\n * 无该脚本 → 抛。\n */\nexport function runPluginScript(plugin: DiscoveredPlugin, name: string): Promise<void> {\n const cmd = (plugin.manifest.scripts as Record<string, string | undefined> | undefined)?.[name]\n if (!cmd) return Promise.reject(new Error(`plugin ${plugin.id} 无脚本「${name}」`))\n // 防御纵深(endgame review B2):信任门之外再拦已知危险模式 + env 白名单过滤\n // (不泄漏 API key/token 给插件脚本)——与 plugin-hooks executePostInstall 同基线。\n const safety = checkShellCommand(cmd)\n if (!safety.safe) {\n return Promise.reject(new Error(`脚本「${name}」被安全检查拦截:${safety.reason}`))\n }\n return new Promise<void>((resolve, reject) => {\n const child = spawn(cmd, { shell: true, cwd: plugin.dir, stdio: 'ignore', env: buildAllowedEnv() })\n child.on('error', reject)\n child.on('exit', (code) => (code === 0 ? resolve() : reject(new Error(`脚本「${name}」退出码 ${code}`))))\n })\n}\n\n/**\n * `open` 型 capability:拉起外部目标。`chrome://` 无系统默认 handler → 特判用浏览器打开。\n * best-effort、detached、不阻塞;失败由调用方 toast(OQ3)。\n */\nexport function openTarget(target: string): void {\n const isChrome = target.startsWith('chrome://')\n if (process.platform === 'darwin') {\n const args = isChrome ? ['-a', 'Google Chrome', target] : [target]\n spawn('open', args, { detached: true, stdio: 'ignore' }).unref()\n } else if (process.platform === 'win32') {\n spawn('cmd', ['/c', 'start', '', target], { detached: true, stdio: 'ignore' }).unref()\n } else {\n const cmd = isChrome ? 'google-chrome' : 'xdg-open'\n spawn(cmd, [target], { detached: true, stdio: 'ignore' }).unref()\n }\n}\n","/** Plugin CLI passthrough: plugin commands exposed as top-level /commands. */\nimport { existsSync, readFileSync, realpathSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { spawn } from 'node:child_process'\nimport { buildAllowedEnv } from '@x-otto/shared'\nimport { createLogger } from '@x-otto/shared'\nimport type { DiscoveredPlugin } from '@x-otto/plugin'\n\nconst logger = createLogger('@x-otto/coding:plugin-cli-passthrough')\n\n/** 透传子进程等待超时(毫秒)——对齐 `runPluginScript` 现有安全基建的超时量级。 */\nconst SPAWN_TIMEOUT_MS = 60_000\n\n/**\n * 解析插件声明的 CLI 可执行文件绝对路径。优先级:manifest 显式 `contributes.cli.bin`\n * (相对插件目录)→ 插件目录 `package.json` 的 `bin` 字段(字符串或对象取第一个 value)。\n *\n * **symlink 逃逸防御(D4)**:解析后的路径必须 `realpathSync` 归一化后校验落在\n * `plugin.dir` 的 realpath 内部——单纯 `resolve()+startsWith` 无法防御插件目录内一个\n * 指向目录外的符号链接。任一校验失败(不存在/不是文件/逃逸出插件目录)返回 `undefined`,\n * 调用方负责给出清晰错误文案(不得静默失败)。\n */\nexport function resolvePluginCliBin(plugin: DiscoveredPlugin): string | undefined {\n const declaredBin = plugin.manifest.contributes?.cli?.bin\n const candidate = declaredBin\n ? join(plugin.dir, declaredBin)\n : resolveFromPackageJsonBin(plugin.dir)\n\n if (!candidate) return undefined\n return validateBinPath(candidate, plugin.dir)\n}\n\nfunction resolveFromPackageJsonBin(pluginDir: string): string | undefined {\n const packageJsonPath = join(pluginDir, 'package.json')\n if (!existsSync(packageJsonPath)) return undefined\n try {\n const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {\n bin?: string | Record<string, string>\n }\n if (typeof pkg.bin === 'string') {\n return join(pluginDir, pkg.bin)\n }\n if (pkg.bin && typeof pkg.bin === 'object') {\n const first = Object.values(pkg.bin)[0]\n return typeof first === 'string' ? join(pluginDir, first) : undefined\n }\n return undefined\n } catch {\n return undefined\n }\n}\n\n/** symlink-safe 路径穿越校验 + 存在性/可执行性(是文件)校验。 */\nfunction validateBinPath(candidate: string, pluginDir: string): string | undefined {\n if (!existsSync(candidate)) return undefined\n let realCandidate: string\n let realPluginDir: string\n try {\n realCandidate = realpathSync(candidate)\n realPluginDir = realpathSync(pluginDir)\n } catch {\n return undefined\n }\n const normalizedDir = realPluginDir.endsWith('/') ? realPluginDir : `${realPluginDir}/`\n if (!realCandidate.startsWith(normalizedDir) && realCandidate !== realPluginDir) {\n logger.warn(\n { candidate, pluginDir },\n 'Plugin CLI bin resolves outside plugin directory (symlink escape rejected)',\n )\n return undefined\n }\n try {\n if (!statSync(realCandidate).isFile()) return undefined\n } catch {\n return undefined\n }\n return realCandidate\n}\n\n/**\n * 从已发现插件构建\"命令名 → 插件\"表(RFC-112 D2 落地位置)。\n * - 跳过 `name` 命中 `reservedNames`(内建 `SUB_COMMAND_NAMES`)的贡献,记 warn 日志。\n * - **同名冲突 fail-closed**(RFC-124 §10.2.2 修复,原\"先到先得\"已废弃):≥2 个插件声明\n * 相同命令名时,**全部**拒绝注册(而非放行第一个、静默吞掉其余),并给出一条列出全部\n * 冲突方 id 的明确诊断。理由:`discoverPlugins()` 的排序已从纯字母序演进为拓扑序\n * (RFC-132 M2),\"先到先得\"在拓扑序下变成不确定性行为——同一插件在不同依赖图布局下\n * 可能被不同对手抢注命令,插件作者无法预测、也无从感知(此前只有 warn 日志,用户命令\n * 静默不可达)。fail-closed 后命令名不可用(`otto <name>` 落回既有的\"未命中任何插件\n * 命令\"回退路径,即被当作 prompt 处理),但错误是**确定性且可诊断**的——不会随排序\n * 变化而改变哪个插件\"赢\",且冲突方在诊断日志里清晰可见,可据此重命名解决。\n */\n/**\n * 按命令名分组候选插件(跳过保留字命中项,单独 warn)。是\"谁声明了这个命令名\"的唯一权威\n * 分组逻辑——`buildPluginCliRegistry`(构建可路由表)与 `findPluginCliNameConflict`(查询\n * 单个名字是否冲突)都复用本函数,避免两处各自实现分组/冲突判定而在未来产生逻辑漂移\n * (子 agent 对抗审核 RFC-124 §10.2.2 finding B,建议优化项)。\n */\nfunction groupCliCandidatesByName(\n plugins: readonly DiscoveredPlugin[],\n reservedNames: ReadonlySet<string>,\n): Map<string, DiscoveredPlugin[]> {\n const candidatesByName = new Map<string, DiscoveredPlugin[]>()\n for (const plugin of plugins) {\n const name = plugin.manifest.contributes?.cli?.name\n if (!name) continue\n if (reservedNames.has(name)) {\n logger.warn(\n { pluginId: plugin.id, name },\n 'Plugin CLI contribution name collides with a built-in command; ignored',\n )\n continue\n }\n const list = candidatesByName.get(name)\n if (list) list.push(plugin)\n else candidatesByName.set(name, [plugin])\n }\n return candidatesByName\n}\n\nexport function buildPluginCliRegistry(\n plugins: readonly DiscoveredPlugin[],\n reservedNames: ReadonlySet<string>,\n): Map<string, DiscoveredPlugin> {\n const candidatesByName = groupCliCandidatesByName(plugins, reservedNames)\n\n // 唯一候选才注册;≥2 个候选 = 冲突,fail-closed(双方均不注册)+ 明确诊断。\n const registry = new Map<string, DiscoveredPlugin>()\n for (const [name, candidates] of candidatesByName) {\n if (candidates.length === 1) {\n registry.set(name, candidates[0] as DiscoveredPlugin)\n continue\n }\n logger.warn(\n { name, conflictingPluginIds: candidates.map((p) => p.id) },\n 'Plugin CLI contribution name claimed by multiple plugins; command disabled ' +\n '(fail-closed, RFC-124 §10.2.2 — rename one of the conflicting plugins to resolve)',\n )\n }\n return registry\n}\n\n/**\n * 查询某个命令名是否因冲突被 `buildPluginCliRegistry` fail-closed 剔除(RFC-124 §10.2.2)。\n * 供调用方(`packages/cli/src/cli.ts tryDispatchPluginCli`)区分两种\"命令名未在 registry\n * 中找到\"的情形:① 压根没有插件声明该名字(应静默回退到既有的 prompt 处理路径);\n * ② 曾有 ≥2 个插件声明该名字但被 fail-closed(应向用户展示明确冲突诊断,而非静默把\n * 命令行参数当 prompt 发给 AI——那会让用户误以为打错了字,而非\"命令名被占用\")。\n * 返回冲突方插件 id 列表(≥2 项);无冲突(含\"压根没人声明\")返回 `undefined`。\n *\n * 复用 `groupCliCandidatesByName`——与 `buildPluginCliRegistry` 共用同一份分组逻辑,\n * 保证两个函数对\"是否冲突\"永远给出一致结论(注意:本函数调用会重复触发保留字命中的\n * warn 日志,因为 `groupCliCandidatesByName` 内部对每个插件都会 warn 一次;调用方\n * `tryDispatchPluginCli` 只在 `buildPluginCliRegistry` 未命中时才调用本函数,同一轮\n * CLI 调用内至多各触发一次,不构成刷屏)。\n */\nexport function findPluginCliNameConflict(\n plugins: readonly DiscoveredPlugin[],\n reservedNames: ReadonlySet<string>,\n name: string,\n): string[] | undefined {\n const candidates = groupCliCandidatesByName(plugins, reservedNames).get(name)\n return candidates && candidates.length >= 2 ? candidates.map((p) => p.id) : undefined\n}\n\n/**\n * 透传执行:`spawn(binPath, args, {stdio:'inherit', cwd, env: buildAllowedEnv()})`,\n * 等待子进程退出并转发退出码。超时(默认 60s,可传参覆盖供测试用极短值验证)触发\n * `SIGTERM` 并 reject——防止插件 CLI 挂死(如等待不可达网络)导致父进程(otto CLI)\n * 无限期阻塞。\n */\nexport function spawnPluginCli(\n binPath: string,\n args: string[],\n cwd: string,\n timeoutMs: number = SPAWN_TIMEOUT_MS,\n): Promise<number> {\n return new Promise<number>((resolvePromise, reject) => {\n const child = spawn(binPath, args, {\n stdio: 'inherit',\n cwd,\n env: buildAllowedEnv(),\n })\n\n const timeout = setTimeout(() => {\n child.kill('SIGTERM')\n reject(new Error(`插件 CLI 执行超时(${timeoutMs}ms),已终止子进程`))\n }, timeoutMs)\n timeout.unref()\n\n child.on('error', (err) => {\n clearTimeout(timeout)\n reject(err)\n })\n child.on('exit', (code, signal) => {\n clearTimeout(timeout)\n resolvePromise(code ?? (signal ? 1 : 0))\n })\n })\n}\n","/** Theme preset loader: plugin-declared theme presets (RFC-277 M5 spike). */\nimport { evaluatePluginTrust, MAX_THEME_PRESETS_PER_PLUGIN, type DiscoveredPlugin } from '@x-otto/plugin'\nimport { createLogger } from '@x-otto/shared'\n\nconst logger = createLogger('@x-otto/coding:plugin-theme-preset-loader')\n\n/**\n * 装载层产出的主题预设条目(纯数据形状,不依赖 `@x-otto/tui` 的 `ThemePreset` 类型——\n * `@x-otto/coding` 不引入 UI 包依赖,同 `getPluginRegistrations()` 的既有跨包边界模式)。\n * CLI 层(同时依赖 coding + tui)负责把这份数据适配进 `ThemeRegistry.register()`。\n */\nexport interface PluginThemePresetEntry {\n /** 贡献该 preset 的插件 id。 */\n readonly pluginId: string\n /** 插件内声明的短标识(未加前缀)。 */\n readonly localId: string\n readonly label: string\n readonly appearance: 'dark' | 'light'\n readonly colors: Record<string, string>\n readonly markdown?: Record<string, unknown>\n readonly meta?: {\n readonly author?: string\n readonly description?: string\n readonly version?: string\n }\n}\n\n/**\n * 保留前缀——插件不得贡献落在这些命名空间下的 localId(防止伪造/劫持内置或其他插件的\n * effective id)。effective id 由本函数固定生成为 `${pluginId}/${localId}`,插件贡献层\n * 完全不接触完整 id 的拼接权,故这里防的是 localId 本身撞了敏感字符串这一更弱的场景\n * (如插件声明 localId='dark' 时 effective id 会是 `${pluginId}/dark`,不会与内置 `dark`\n * 冲突——真正的保留前缀防护点在 registry 层的 `${pluginId}/` 强制拼接,这里额外拒绝\n * localId 自身等于全局保留字,避免用户困惑于\"这明明是我的插件 id 却显示成 dark\")。\n */\nconst RESERVED_LOCAL_IDS = new Set(['dark', 'light', 'system', 'builtin'])\n\n/**\n * 声明式轴:收集全部活跃插件的 `contributes.themePresets` → 带 pluginId 命名空间的\n * `PluginThemePresetEntry[]`(RFC-277 M5 spike)。\n *\n * fail-soft 边界(对齐 `collectPluginI18nEntries`/`collectPluginSigilEntries` 既有纪律):\n * - 项目级未信任插件(gated)整体跳过。\n * - 单插件声明超过 `MAX_THEME_PRESETS_PER_PLUGIN` 条 → 截断并 warn。\n * - localId 撞保留字 → 单条跳过并 warn(不影响该插件其余 preset)。\n *\n * 不做的事(RFC-277 D6/M5 边界):不做 marketplace/distribution;不做 registry 注册/卸载\n * (由 CLI 层调用方持有 `ThemeRegistry` 实例并调用 `register`/`unregisterBySource`);\n * 不做颜色安全格式的二次校验(schema 层 `themePresetColorsSchema`/`hexColor` 已 fail-closed,\n * 装载层信任 manifest 解析结果)。\n */\nexport function collectPluginThemePresets(plugins: readonly DiscoveredPlugin[]): PluginThemePresetEntry[] {\n const out: PluginThemePresetEntry[] = []\n for (const plugin of plugins) {\n const contributions = plugin.manifest.contributes?.themePresets\n if (!contributions?.length) continue\n if (evaluatePluginTrust(plugin).gated) {\n logger.warn({ pluginId: plugin.id }, 'plugin gated (untrusted), themePresets skipped')\n continue\n }\n\n let entries = contributions\n if (entries.length > MAX_THEME_PRESETS_PER_PLUGIN) {\n logger.warn(\n { pluginId: plugin.id, declared: entries.length, limit: MAX_THEME_PRESETS_PER_PLUGIN },\n 'plugin declares more theme presets than the per-plugin limit, truncated',\n )\n entries = entries.slice(0, MAX_THEME_PRESETS_PER_PLUGIN)\n }\n\n for (const contribution of entries) {\n if (RESERVED_LOCAL_IDS.has(contribution.localId)) {\n logger.warn(\n { pluginId: plugin.id, localId: contribution.localId },\n 'themePresets localId collides with a reserved name, skipped',\n )\n continue\n }\n out.push({\n pluginId: plugin.id,\n localId: contribution.localId,\n label: contribution.label,\n appearance: contribution.appearance,\n colors: contribution.colors,\n markdown: contribution.markdown,\n meta: contribution.meta,\n })\n }\n }\n return out\n}\n"],"x_google_ignoreList":[111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127],"mappings":"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"}