{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAA2B,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAoB7G,OAAO,EAIN,sBAAsB,EACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,iBAAiB,EAA6B,MAAM,0BAA0B,CAAC;AAW7F,OAAO,EAA6B,KAAK,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAkB9F,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,YAAY,CAAC;AAExD,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAiBlF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAsB,yBAAyB,EAAY,MAAM,YAAY,CAAC;AA+G1F,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC;AAExG,MAAM,WAAW,0BAA0B;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,OAAO,CAAC,EAAE,0BAA0B,CAAC;CACrC;AAED,MAAM,WAAW,0BAA0B;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,SAAS,CAAC,EAAE,0BAA0B,CAAC;CACvC;AAED,MAAM,WAAW,QAAQ;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAiB,SAAQ,0BAA0B;IACnE,EAAE,CAAC,EAAE,QAAQ,CAAC;CACd;AAsbD,wBAAsB,yBAAyB,CAC9C,YAAY,EAAE;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,EACtD,GAAG,EAAE,gBAAgB,EACrB,IAAI,GAAE,MAAM,IAAuB,EACnC,uBAAuB,UAAO,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAsMD,wBAAsB,gBAAgB,CACrC,YAAY,EAAE;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,EACtD,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,yBAAwE,GAChF,OAAO,CAAC,IAAI,CAAC,CAoFf;AAED,wBAAsB,mBAAmB,CACxC,YAAY,EAAE;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,EACtD,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAC1C,YAAY,EAAE;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACpE,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAef;AA0fD,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,0BAA+B,GAAG,gBAAgB,CAmHpG;AAmJD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwU7F","sourcesContent":["import { rm } from \"node:fs/promises\";\nimport { createInterface } from \"node:readline/promises\";\nimport type { ThinkingLevel } from \"@ch1nyzzz/pi-agent-core\";\nimport type { ExtensionCommandContext, ExtensionContext, ExtensionFactory } from \"@ch1nyzzz/pi-coding-agent\";\nimport { DEFAULT_SPAWN_AGENT_TOOL_NAMES } from \"./bundle/runtime.ts\";\nimport {\n\ttype EvoCommandPresenter,\n\tformatInstallableTemplates,\n\tformatProposalCard,\n\tlistInstallableWorkflowTemplates,\n\tmodelRunOptions,\n\trequireActiveBundleDigest,\n\trequireValue,\n\trunSharedEvoCommand,\n\tsplitFirst,\n\twriteWorkflowTemplateToStaging,\n} from \"./cli-commands.ts\";\nimport { parseTrialDurationDays } from \"./comparison.ts\";\nimport type { EvoBudgetedCapabilityGrant, EvoCapabilityGrant } from \"./components/capabilities/broker.ts\";\nimport { parseEvoCapabilityGrants } from \"./components/capabilities/broker.ts\";\nimport { canUseEvoComponentSandbox } from \"./components/process-runtime.ts\";\nimport type { EvoDiscoveredPack } from \"./discovery/client.ts\";\nimport { getEvoPackDiscoveryConfigPath, readEvoPackDiscoveryConfig } from \"./discovery/config.ts\";\nimport {\n\ttype EvoPackRegistryInspection,\n\ttype EvoPackRegistryInstallResult,\n\ttype EvoPackRegistrySearchResult,\n\tEvoPackRegistryService,\n} from \"./discovery/service.ts\";\nimport { type EvoDiscoveryFetch, EvoPackDiscoveryTransport } from \"./discovery/transport.ts\";\nimport { type EvoActivityItem, listEvoActivityItems } from \"./evolve/activity.ts\";\nimport {\n\tformatEvolutionRuns,\n\tinspectBackgroundEvolutions,\n\tstartBackgroundBuildingResume,\n\tstartBackgroundEvidenceResumption,\n\tstartBackgroundEvolution,\n} from \"./evolve/background.ts\";\nimport { readEvoControlConfig, updateEvoControlConfigValue } from \"./evolve/config.ts\";\nimport { runUnknownAbiBuilderCycle, type UnknownAbiBuilderCycleResult } from \"./evolve/cycle.ts\";\nimport { EvolutionProcessInspector, type VerificationDecision } from \"./evolve/inspect-ui.ts\";\nimport { changesComponentSelection } from \"./evolve/release.ts\";\nimport { type EvolutionRetryFrom, resolveRetryFrom, retryEvolutionFromValidation } from \"./evolve/retry.ts\";\nimport {\n\tlistEvolutionRuns,\n\treadEvolutionRun,\n\treadFrozenExperimentForProposal,\n\tupdateEvolutionRun,\n} from \"./evolve/run.ts\";\nimport type { UnknownAbiBuilderRequest } from \"./evolve/unknown-abi.ts\";\nimport {\n\treadPendingVerification,\n\trejectPendingVerification,\n\tskipPendingVerification,\n} from \"./evolve/verification-decision.ts\";\nimport { exportEvoPack } from \"./pack/export.ts\";\nimport { type EvoPackImportPreflight, type EvoPackImportResult, importEvoPack } from \"./pack/import.ts\";\nimport { type EvoPackManifest, loadEvoPack } from \"./pack/pack.ts\";\nimport { type EvoPaths, getEvoPaths } from \"./paths.ts\";\nimport { proposalApproval } from \"./proposal.ts\";\nimport { createPiModelRunner, type ModelRunner } from \"./reflect/model-runner.ts\";\nimport {\n\taskProposalQuestion,\n\trecordPermitDefer,\n\trecordPermitReopen,\n\treviseProposalFromInstruction,\n} from \"./reflect/permit.ts\";\nimport { runReport } from \"./reflect/reflector.ts\";\nimport { runRetrospective } from \"./reflect/retrospective.ts\";\nimport {\n\ttype EvoScheduleConfig,\n\ttype EvoScheduleStatus,\n\tgetScheduleStatus,\n\tparseScheduleCadence,\n\treadScheduleConfig,\n\twriteScheduleConfig,\n} from \"./scheduler.ts\";\nimport { EvoService } from \"./service.ts\";\nimport type { CanaryTrialControl, ComponentApprovalDecision, Proposal } from \"./types.ts\";\n\nconst SUBCOMMANDS = [\n\t\"help\",\n\t\"init\",\n\t\"search\",\n\t\"install\",\n\t\"import\",\n\t\"export\",\n\t\"status\",\n\t\"report\",\n\t\"go\",\n\t\"inspect\",\n\t\"pause\",\n\t\"resume\",\n\t\"delete\",\n\t\"retry\",\n\t\"schedule\",\n\t\"playbook\",\n\t\"workflows\",\n\t\"packs\",\n\t\"config\",\n\t\"grants\",\n\t\"history\",\n\t\"triage\",\n\t\"inbox\",\n\t\"usage\",\n\t\"list\",\n\t\"model\",\n\t\"show\",\n\t\"note\",\n\t\"request\",\n\t\"preference\",\n\t\"preferences\",\n\t\"forget\",\n\t\"resolve\",\n\t\"gc\",\n\t\"permit\",\n\t\"reject\",\n\t\"rollback\",\n\t\"keep\",\n\t\"retrospect\",\n] as const;\n\nconst QUICK_APPROVE_SHORTCUT = \"ctrl+alt+e\" as const;\nconst DISCOVERY_SEARCH_LIMIT = 50;\nconst SCHEDULE_USAGE = \"/evo schedule [daily | 3d | weekly | every <n>d | manual]\";\n\nconst EVO_HELP = `Usage: /evo <command>\n\n  help                         Show this help\n  init                         Initialize and migrate Pi data into a bundle\n  search [query]               Search configured optimization-pack registries\n  install <name> [version]     Verify a trusted registry pack and stage proposals\n  import [directory]           No argument: pick a bundled workflow and install it end to end; with a directory: verify the pack and stage proposals\n  export <directory>           Export the active bundle as an optimization pack\n  status                       Show registry and trial status\n  report                       Generate a read-only evidence report\n  go [request|--scheduled]     Start a background evolution task (--scheduled: one guarded cadence-gated attempt)\n  inspect [run-id]             Inspect background and completed tasks\n  pause <run-id>               Pause a background task\n  resume <run-id>              Resume a paused task\n  delete <run-id>              Stop and permanently delete a task\n  retry <run-id> [--from research|building|validating]  Resume a terminal task: fresh research, the frozen plan from the builder, or the built component from validation\n  schedule [cadence]           Show or set the evolution cadence (daily, 3d, weekly, manual)\n  playbook [reset]             Show or reset the evolution playbook (guides research)\n  workflows                    List active workflow components and their triggers\n  packs [init <name> [dir]]    List bundled workflow pack templates or write one\n  config [set <key> <value>]   Show or update the evo control config\n  model [role]                 Interactively pick the model and thinking level for an evolution phase\n  grants                       Show component capability grants, usage, and budgets\n  history [<count>]            Show the bundle transition audit history (keep/rollback)\n  triage [now]                 Show streaming-triage status, or scan new sessions now\n  inbox                        List inbox entries with lifecycle status\n  usage [<n>d]                 Show model usage and cost by phase (default 7d)\n  list                         List proposals\n  show <proposal-id>           Show a proposal card\n  note <text>                  Record an explicit note\n  request <text>               Prioritize a research/evolution direction\n  preference <text>            Record an explicit durable preference\n  preferences                  Show active stable preferences\n  forget <preference-id>        Request removal of an active preference\n  resolve <inbox-file> [why]    Mark an externally completed input fulfilled\n  gc [--dry-run]               Collect terminal inbox payloads\n  permit <proposal-id>         Review and approve a proposal\n  reject <proposal-id> <why>   Reject a pending proposal\n  rollback [digest] [why]      Roll back the active trial or stable bundle\n  keep [why]                   Run retrospective, then keep the active trial\n  verify [run-id] <execute|skip|reject>  Decide a pending recommended verification\n  retrospect                   Run and show the active-trial retrospective`;\n\nconst EVO_CLI_HELP = EVO_HELP.replaceAll(\"/evo\", \"evo-pi\")\n\t.replace(\"export <directory>\", \"export <directory> [name] [version]\")\n\t.replace(\"note <text>\", \"note <session-id> <text>\")\n\t.replace(\"request <text>\", \"request <session-id> <text>\")\n\t.replace(\"preference <text>\", \"preference <session-id> <text>\")\n\t.replace(\"forget <preference-id>\", \"forget <session-id> <preference-id>\");\n\ninterface EvoCommandDependencies {\n\tpaths: EvoPaths;\n\tservice: EvoService;\n\trunner: ModelRunner;\n\tcwd?: string;\n\tagentDir?: string;\n\tmodel?: string;\n\tspawnAgentToolNames?: readonly string[];\n\t/** Sandbox override for imported-pack executable validation (tests/automation). */\n\tsandbox?: boolean;\n\tgetDiscoveryService(): Promise<EvoCommandDiscoveryService>;\n}\n\nexport type EvoCommandDiscoveryService = Pick<EvoPackRegistryService, \"search\" | \"inspect\" | \"install\">;\n\nexport interface EvoCommandDiscoveryOptions {\n\tconfigPath?: string;\n\tfetch?: EvoDiscoveryFetch;\n\tservice?: EvoCommandDiscoveryService;\n}\n\nexport interface EvoCommandExtensionOptions {\n\troot?: string;\n\tpaths?: EvoPaths;\n\tservice?: EvoService;\n\trunner?: ModelRunner;\n\tcwd?: string;\n\tagentDir?: string;\n\tmodel?: string;\n\t/** Exact trusted tool names available through the runtime spawn-agent host. */\n\tspawnAgentToolNames?: readonly string[];\n\t/** Sandbox override for imported-pack executable validation (tests/automation). */\n\tsandbox?: boolean;\n\t/** Local registry/trust config and injectable discovery dependencies. */\n\tdiscovery?: EvoCommandDiscoveryOptions;\n}\n\nexport interface EvoCliIO {\n\tinteractive: boolean;\n\twrite(message: string): void;\n\twriteError(message: string): void;\n\tquestion(prompt: string): Promise<string>;\n}\n\nexport interface RunEvoCliOptions extends EvoCommandExtensionOptions {\n\tio?: EvoCliIO;\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nfunction parseSubcommand(args: string): { command: string; rest: string } {\n\tconst trimmed = args.trim();\n\tif (!trimmed) return { command: \"help\", rest: \"\" };\n\tconst separator = trimmed.search(/\\s/);\n\tif (separator === -1) return { command: trimmed.toLowerCase(), rest: \"\" };\n\treturn {\n\t\tcommand: trimmed.slice(0, separator).toLowerCase(),\n\t\trest: trimmed.slice(separator).trim(),\n\t};\n}\n\nfunction requireArgumentCount(args: string[], minimum: number, maximum: number, usage: string): void {\n\tif (args.length < minimum || args.length > maximum) throw new Error(`Usage: ${usage}`);\n}\n\nfunction parseRetryArgs(value: string, usage: string): { id: string; from?: EvolutionRetryFrom } {\n\tconst { first: id, rest } = splitFirst(value);\n\trequireValue(id, usage);\n\tif (!rest.trim()) return { id };\n\tconst match = /^--from\\s+(research|building|validating)$/.exec(rest.trim());\n\tif (!match) throw new Error(`Usage: ${usage}`);\n\treturn { id, from: match[1] as EvolutionRetryFrom };\n}\n\nfunction parsePackSelection(value: string, usage: string): { name: string; version?: string } {\n\tconst { first: name, rest } = splitFirst(value);\n\trequireValue(name, usage);\n\tif (!rest) return { name };\n\tconst { first: version, rest: extra } = splitFirst(rest);\n\tif (!version || extra) throw new Error(`Usage: ${usage}`);\n\treturn { name, version };\n}\n\nfunction formatPackImportResult(\n\tresult: EvoPackImportResult,\n\tunknownAbiResults: readonly UnknownAbiBuilderCycleResult[] = [],\n): string {\n\tconst proposalCount = (result.proposal ? 1 : 0) + result.importedComponents.length + unknownAbiResults.length;\n\tconst lines = [\n\t\t`Pack: ${result.manifest.name}@${result.manifest.version}`,\n\t\t`Integrity: ${result.manifest.integrity ?? \"not declared\"}`,\n\t\t`Prompt/skill imports: ${result.addedPromptPaths.length} prompt(s), ${result.addedSkillPaths.length} skill(s)`,\n\t];\n\tif (result.proposal) {\n\t\tlines.push(\n\t\t\t`Data proposal: ${result.proposal.id} -> ${result.proposal.candidateDigest ?? \"candidate unavailable\"}`,\n\t\t);\n\t}\n\tfor (const component of result.importedComponents) {\n\t\tlines.push(\n\t\t\t`Component ${component.id} (${component.abi}): artifact ${component.artifactDigest}; proposal ${component.proposal.id} -> ${component.proposal.candidateDigest ?? \"candidate unavailable\"}`,\n\t\t);\n\t}\n\tif (result.unregisteredAbis.length > 0) {\n\t\tlines.push(`Unregistered ABIs: ${result.unregisteredAbis.join(\", \")}`);\n\t}\n\tfor (const built of unknownAbiResults) {\n\t\tlines.push(\n\t\t\t`ABI Builder ${built.request.targetAbi}: run ${built.run.id}; pending T2 proposal ${built.proposal.id}`,\n\t\t\t`Next: ${built.nextAction}`,\n\t\t);\n\t}\n\tif (result.pendingWorkflows > 0) lines.push(`Pending workflows: ${result.pendingWorkflows}`);\n\tlines.push(`Activation: none; ${proposalCount} proposal(s) staged for review.`);\n\treturn lines.join(\"\\n\");\n}\n\ninterface ActivePackExport {\n\tbundleDigest: string;\n\ttargetDirectory: string;\n\tmanifest: EvoPackManifest;\n}\n\ninterface PackCapabilityGrantPreview {\n\tmanifest: EvoPackManifest;\n\tintegrity: string;\n\tgrantsByComponent: Readonly<Record<string, readonly EvoCapabilityGrant[]>>;\n}\n\nconst DEFAULT_CALL_CAPABILITY_MAX_CALLS = 1_000;\n\nfunction createBudgetedCapabilityGrant(\n\tcapability: EvoBudgetedCapabilityGrant[\"capability\"],\n\tmodel: string,\n\ttools: readonly string[],\n): EvoBudgetedCapabilityGrant {\n\tif (capability === \"spawn-agent\") {\n\t\t// The broker holds a worst-case reservation of contextWindow x maxTurns\n\t\t// input tokens per in-flight spawn (~16.8M tokens for a 256k-context\n\t\t// model at the 64-turn host default), so these caps are sized to keep\n\t\t// several large-context child agents runnable in parallel while still\n\t\t// bounding runaway loops. Actual charged usage is real consumption.\n\t\treturn {\n\t\t\tcapability,\n\t\t\tmaxCalls: 100,\n\t\t\tmodels: [model],\n\t\t\tmaxInputTokens: 268_435_456,\n\t\t\tmaxOutputTokens: 16_777_216,\n\t\t\tmaxTotalTokens: 285_212_672,\n\t\t\tmaxCostUsd: 500,\n\t\t\tmaxOutputTokensPerCall: 65_536,\n\t\t\ttools: [...new Set(tools)].sort(),\n\t\t};\n\t}\n\treturn {\n\t\tcapability,\n\t\tmaxCalls: 200,\n\t\tmodels: [model],\n\t\tmaxInputTokens: 1_048_576,\n\t\tmaxOutputTokens: 262_144,\n\t\tmaxTotalTokens: 1_310_720,\n\t\tmaxCostUsd: 50,\n\t\tmaxOutputTokensPerCall: 16_384,\n\t};\n}\n\nasync function previewPackCapabilityGrants(\n\tpackDirectory: string,\n\tmodel: string | undefined,\n\ttools: readonly string[],\n): Promise<PackCapabilityGrantPreview> {\n\tconst loaded = await loadEvoPack(packDirectory);\n\tif (!loaded.integrity.ok) {\n\t\tthrow new Error(\n\t\t\t`pack integrity check failed: expected ${loaded.integrity.expected ?? \"(none declared)\"}, got ${loaded.integrity.actual}`,\n\t\t);\n\t}\n\treturn createPackCapabilityGrantPreview(loaded.manifest, loaded.integrity.actual, model, tools);\n}\n\nfunction createPackCapabilityGrantPreview(\n\tmanifest: EvoPackManifest,\n\tintegrity: string,\n\tmodel: string | undefined,\n\ttools: readonly string[],\n): PackCapabilityGrantPreview {\n\tconst grantsByComponent: Record<string, readonly EvoCapabilityGrant[]> = {};\n\tconst codeParts = [...manifest.contents.components, ...manifest.contents.workflows];\n\tfor (const part of codeParts) {\n\t\tif (part.capabilities.length === 0) continue;\n\t\tconst grants: unknown[] = [];\n\t\tfor (const capability of part.capabilities) {\n\t\t\tif (capability === \"infer\" || capability === \"spawn-agent\") {\n\t\t\t\tif (!model) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Pack component ${part.id} requests ${capability}; select or configure an allowed provider/model before import`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tgrants.push(createBudgetedCapabilityGrant(capability, model, tools));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tgrants.push({ capability, maxCalls: DEFAULT_CALL_CAPABILITY_MAX_CALLS });\n\t\t}\n\t\tgrantsByComponent[part.id] = parseEvoCapabilityGrants(grants, `grants for ${part.id}`);\n\t}\n\treturn {\n\t\tmanifest,\n\t\tintegrity,\n\t\tgrantsByComponent,\n\t};\n}\n\nfunction formatPackCapabilityGrantPreview(preview: PackCapabilityGrantPreview): string {\n\treturn [\n\t\t`Pack: ${preview.manifest.name}@${preview.manifest.version}`,\n\t\t`Integrity: ${preview.integrity}`,\n\t\t\"Exact persisted grants:\",\n\t\tJSON.stringify(preview.grantsByComponent, undefined, 2),\n\t\t\"These grants remain inactive until the corresponding code proposal is approved and its trial starts.\",\n\t].join(\"\\n\");\n}\n\nfunction formatPackSource(source: EvoDiscoveredPack[\"entry\"][\"source\"]): string {\n\tif (source.kind === \"https\") return `https ${source.rawUrl}`;\n\tif (source.kind === \"git\") {\n\t\treturn `git ${source.repository}@${source.revision}:${source.path} (${source.rawUrl})`;\n\t}\n\treturn `gist ${source.gistId}@${source.revision}:${source.file} (${source.rawUrl})`;\n}\n\nfunction formatPackTrust(trust: EvoDiscoveredPack[\"trust\"]): string {\n\tif (trust.status === \"trusted\") return `trusted (${trust.signer})`;\n\tif (trust.status === \"untrusted-signer\") return `untrusted signer (${trust.signer})`;\n\treturn \"unsigned\";\n}\n\nfunction formatPackRequirements(manifest: EvoPackManifest | undefined): string[] {\n\tif (!manifest) return [\"Required ABIs: unavailable (entry is not trusted)\", \"Required capabilities: unavailable\"];\n\treturn [\n\t\t`Required ABIs: ${manifest.requiresAbis.join(\", \") || \"none\"}`,\n\t\t`Required capabilities: ${manifest.requiresCapabilities.join(\", \") || \"none\"}`,\n\t];\n}\n\nfunction formatPackInspection(inspection: EvoPackRegistryInspection): string {\n\treturn [\n\t\t`Pack: ${inspection.entry.name}@${inspection.entry.version}`,\n\t\t`Trust: ${formatPackTrust(inspection.trust)}`,\n\t\t`Registry: ${formatPackSource(inspection.registrySource)}`,\n\t\t`Source: ${formatPackSource(inspection.packSource)}`,\n\t\t`Integrity: ${inspection.entry.integrity}`,\n\t\t...formatPackRequirements(inspection.manifest),\n\t].join(\"\\n\");\n}\n\nfunction formatPackSearchResults(results: readonly EvoPackRegistrySearchResult[]): string {\n\tif (results.length === 0) return \"No optimization packs found\";\n\tconst cards: string[] = [];\n\tfor (const result of results) {\n\t\tcards.push(\n\t\t\t[\n\t\t\t\t`Pack: ${result.entry.name}@${result.entry.version}`,\n\t\t\t\t`Trust: ${formatPackTrust(result.trust)}`,\n\t\t\t\t`Registry: ${formatPackSource(result.registrySource)}`,\n\t\t\t\t`Source: ${formatPackSource(result.entry.source)}`,\n\t\t\t\t`Integrity: ${result.entry.integrity}`,\n\t\t\t\t...formatPackRequirements(result.manifest),\n\t\t\t\t...(result.entry.description ? [`Description: ${result.entry.description}`] : []),\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\treturn cards.join(\"\\n\\n\");\n}\n\nfunction formatRegistryPackInstallResult(\n\tresult: EvoPackRegistryInstallResult,\n\tunknownAbiResults: readonly UnknownAbiBuilderCycleResult[],\n): string {\n\treturn [\n\t\t`Trust: ${formatPackTrust(result.trust)}`,\n\t\t`Registry: ${formatPackSource(result.registrySource)}`,\n\t\t`Source: ${formatPackSource(result.packSource)}`,\n\t\t`Downloaded: ${result.fileCount} file(s), ${result.totalBytes} byte(s)`,\n\t\tformatPackImportResult(result.imported, unknownAbiResults),\n\t].join(\"\\n\");\n}\n\nasync function stagePackImport(\n\tdependencies: EvoCommandDependencies,\n\tpackDirectory: string,\n\texpectedIntegrity: string,\n\tgrantsByComponent?: Readonly<Record<string, readonly EvoCapabilityGrant[]>>,\n\tparentDigest?: string,\n\tbeforeStage?: (preflight: EvoPackImportPreflight) => Promise<void>,\n\tsandbox?: boolean,\n): Promise<EvoPackImportResult> {\n\treturn importEvoPack({\n\t\tpaths: dependencies.paths,\n\t\tparentDigest: parentDigest ?? (await requireActiveBundleDigest(dependencies)),\n\t\tpackDir: packDirectory,\n\t\texpectedIntegrity,\n\t\t...(sandbox === undefined ? {} : { sandbox }),\n\t\t...(grantsByComponent ? { grantsByComponent } : {}),\n\t\t...(beforeStage ? { beforeStage } : {}),\n\t});\n}\n\n/**\n * The active trial's proposal when it is a validated component selection.\n * Component trials need no model retrospective to conclude: the executable\n * validation is the gate, so `keep` concludes them directly.\n */\nasync function findDirectKeepableTrial(dependencies: EvoCommandDependencies): Promise<Proposal | undefined> {\n\tconst trial = await dependencies.service.registry.readTrial();\n\tif (!trial) return undefined;\n\tconst proposal = await dependencies.service.getProposal(trial.proposalId);\n\tif (proposal.status !== \"trialing\" || !proposal.targetAbi || !proposal.artifacts.validation) return undefined;\n\treturn proposal;\n}\n\nfunction describePermitOutcome(proposal: Proposal, trigger?: string): string {\n\tif (proposal.status === \"trialing\") {\n\t\treturn trigger\n\t\t\t? `已批准并进入 Canary；重开会话后 ${trigger} 生效`\n\t\t\t: `Proposal ${proposal.id} approved; reversible trial started`;\n\t}\n\tif (proposal.status === \"kept\") {\n\t\treturn trigger\n\t\t\t? `已直接上线；重开会话后 ${trigger} 生效（/evo rollback 可回滚）`\n\t\t\t: `Proposal ${proposal.id} approved and kept`;\n\t}\n\treturn `Proposal ${proposal.id} is ${proposal.status}`;\n}\n\nconst PACK_DIRECT_VALIDATION_PROMPT =\n\t\"The OS sandbox is unavailable. Allow the imported pack components to run directly once for executable validation with your user permissions? This grant is not saved and does not activate anything.\";\n\n/**\n * Decide the sandbox mode for a pack's post-stage executable validation.\n * Returns \"cancelled\" when the sandbox is unavailable and the user declines\n * the one-time direct-execution permission.\n */\nasync function resolvePackValidationSandbox(\n\tdependencies: EvoCommandDependencies,\n\tmanifest: EvoPackManifest,\n\tconfirmDirect: () => Promise<boolean>,\n): Promise<{ sandbox?: boolean } | \"cancelled\"> {\n\tif (dependencies.sandbox !== undefined) return { sandbox: dependencies.sandbox };\n\tconst executableParts = manifest.contents.components.length + manifest.contents.workflows.length;\n\tif (executableParts === 0 || (await canUseEvoComponentSandbox())) return {};\n\tif (!(await confirmDirect())) return \"cancelled\";\n\treturn { sandbox: false };\n}\n\nasync function stageUnknownPackAbis(\n\tdependencies: EvoCommandDependencies,\n\tparentDigest: string,\n\trequests: readonly UnknownAbiBuilderRequest[],\n): Promise<UnknownAbiBuilderCycleResult[]> {\n\tconst built: UnknownAbiBuilderCycleResult[] = [];\n\tfor (const request of requests) {\n\t\tbuilt.push(\n\t\t\tawait runUnknownAbiBuilderCycle({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\trequest,\n\t\t\t\trunner: dependencies.runner,\n\t\t\t\tservice: dependencies.service,\n\t\t\t\tparentDigest,\n\t\t\t\t...(dependencies.cwd ? { cwd: dependencies.cwd } : {}),\n\t\t\t\t...(dependencies.agentDir ? { agentDir: dependencies.agentDir } : {}),\n\t\t\t}),\n\t\t);\n\t}\n\treturn built;\n}\n\nasync function exportActiveBundle(\n\tdependencies: EvoCommandDependencies,\n\ttargetDirectory: string,\n\tname?: string,\n\tversion?: string,\n): Promise<ActivePackExport> {\n\tconst bundleDigest = await requireActiveBundleDigest(dependencies);\n\tconst manifest = await exportEvoPack({\n\t\tpaths: dependencies.paths,\n\t\tbundleDigest,\n\t\ttargetDirectory,\n\t\tname: name ?? `bundle-${bundleDigest.slice(0, 12)}`,\n\t\tversion: version ?? \"0.0.0\",\n\t});\n\treturn { bundleDigest, targetDirectory, manifest };\n}\n\nfunction formatPackExportResult(result: ActivePackExport): string {\n\treturn [\n\t\t`Exported active bundle ${result.bundleDigest} to ${result.targetDirectory}`,\n\t\t`Pack: ${result.manifest.name}@${result.manifest.version}`,\n\t\t`Integrity: ${result.manifest.integrity ?? \"not declared\"}`,\n\t].join(\"\\n\");\n}\n\nasync function findPendingT0Proposal(dependencies: { service: EvoService }): Promise<Proposal | undefined> {\n\treturn (await dependencies.service.listProposals()).find(\n\t\t(proposal) => proposal.status === \"pending\" && proposal.kind === \"data\" && proposal.tier === \"T0\",\n\t);\n}\n\nfunction describeScheduleCadence(config: EvoScheduleConfig): string {\n\tif (config.mode === \"manual\") return \"manual (evolution only runs through /evo go)\";\n\tif (config.everyDays === 1) return \"auto (reflect once a day)\";\n\treturn `auto (reflect once every ${config.everyDays} days)`;\n}\n\nfunction formatScheduleStatus(status: EvoScheduleStatus): string {\n\treturn [\n\t\t`cadence: ${describeScheduleCadence(status.config)}`,\n\t\t`quiet hours: ${status.config.quietHours ? `${status.config.quietHours.start}-${status.config.quietHours.end}` : \"any time\"}`,\n\t\t`last background run: ${status.lastCompletedAt ?? \"never\"}`,\n\t\t`next eligible day: ${status.config.mode === \"manual\" ? \"n/a\" : (status.nextEligibleDay ?? \"today\")}`,\n\t\t`runs today: ${status.runsToday}/${status.config.dailyRunLimit}`,\n\t\t`trial comparison after: ${status.config.trialDueAfterDays} days or ${status.config.trialDueAfterSessions} sessions`,\n\t].join(\"\\n\");\n}\n\nconst SCHEDULE_CADENCE_CHOICES: ReadonlyArray<{\n\tlabel: string;\n\tinput: { mode: \"auto\" | \"manual\"; everyDays?: number };\n}> = [\n\t{ label: \"Every day\", input: { mode: \"auto\", everyDays: 1 } },\n\t{ label: \"Every 3 days\", input: { mode: \"auto\", everyDays: 3 } },\n\t{ label: \"Every week\", input: { mode: \"auto\", everyDays: 7 } },\n\t{ label: \"Manual only\", input: { mode: \"manual\" } },\n];\nfunction createDependencies(options: EvoCommandExtensionOptions): EvoCommandDependencies {\n\tconst paths = options.service?.paths ?? options.paths ?? getEvoPaths(options.root);\n\tlet discoveryService = options.discovery?.service;\n\tconst discoveryFetch: EvoDiscoveryFetch = options.discovery?.fetch ?? ((url, init) => globalThis.fetch(url, init));\n\treturn {\n\t\tpaths,\n\t\tservice: options.service ?? new EvoService(paths),\n\t\trunner: options.runner ?? createPiModelRunner(),\n\t\tcwd: options.cwd,\n\t\tagentDir: options.agentDir,\n\t\tmodel: options.model,\n\t\tspawnAgentToolNames: options.spawnAgentToolNames,\n\t\tsandbox: options.sandbox,\n\t\tgetDiscoveryService: async () => {\n\t\t\tif (discoveryService) return discoveryService;\n\t\t\tconst config = await readEvoPackDiscoveryConfig(\n\t\t\t\toptions.discovery?.configPath ?? getEvoPackDiscoveryConfigPath(paths),\n\t\t\t);\n\t\t\tdiscoveryService = new EvoPackRegistryService({\n\t\t\t\tregistrySources: config.registrySources,\n\t\t\t\ttrustedSigners: config.trustedSigners,\n\t\t\t\ttransport: new EvoPackDiscoveryTransport({ fetch: discoveryFetch }),\n\t\t\t});\n\t\t\treturn discoveryService;\n\t\t},\n\t};\n}\n\n/**\n * Footer status-line glyph per activity urgency. The footer appends its own\n * navigation hint, so the item text itself carries no embedded key hints.\n */\nfunction statusGlyph(item: EvoActivityItem): { icon: string; color: \"warning\" | \"accent\" | \"error\" | \"muted\" } {\n\tconst status = item.kind === \"run\" ? item.run.status : item.proposal.status;\n\tif (status === \"failed\" || status === \"cancelled\") return { icon: \"✗\", color: \"error\" };\n\tif (status === \"trialing\") {\n\t\tconst comparisonReady =\n\t\t\titem.kind === \"run\" ? item.proposal?.artifacts.retrospective : item.proposal.artifacts.retrospective;\n\t\treturn comparisonReady ? { icon: \"●\", color: \"warning\" } : { icon: \"◆\", color: \"accent\" };\n\t}\n\tif (\n\t\titem.kind === \"proposal\"\n\t\t\t? status === \"pending\"\n\t\t\t: status === \"awaiting-canary-approval\" || status === \"awaiting-decision\"\n\t)\n\t\treturn { icon: \"●\", color: \"warning\" };\n\treturn { icon: status === \"paused\" || status === \"deferred\" ? \"○\" : \"▶\", color: \"muted\" };\n}\n\nexport async function refreshEvoStatusIndicator(\n\tdependencies: { service: EvoService; paths: EvoPaths },\n\tctx: ExtensionContext,\n\t_now: () => Date = () => new Date(),\n\t_includeTrialComparison = true,\n): Promise<void> {\n\tconst items = await listEvoActivityItems(dependencies, {\n\t\tincludeTrialComparison: _includeTrialComparison,\n\t\tnow: _now,\n\t});\n\tctx.ui.setStatus(\"evo\", undefined);\n\tctx.ui.setStatusItems(\n\t\t\"evo\",\n\t\titems.length > 0\n\t\t\t? items.map((item) => {\n\t\t\t\t\tconst glyph = statusGlyph(item);\n\t\t\t\t\t// item.text is \"Evo: {headline} · {detail}\": recolor each segment\n\t\t\t\t\t// so the state pops and the subject stays quiet.\n\t\t\t\t\tconst splitAt = item.text.indexOf(\" · \");\n\t\t\t\t\tconst headline = splitAt === -1 ? item.text.slice(5) : item.text.slice(5, splitAt);\n\t\t\t\t\tconst detail = splitAt === -1 ? \"\" : item.text.slice(splitAt);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: item.key,\n\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\tctx.ui.theme.fg(\"dim\", \"Evo: \") +\n\t\t\t\t\t\t\tctx.ui.theme.fg(glyph.color, `${glyph.icon} ${headline}`) +\n\t\t\t\t\t\t\tctx.ui.theme.fg(\"dim\", detail),\n\t\t\t\t\t\tonSelect: () => openEvolutionInspector(dependencies, ctx, item.key),\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t: undefined,\n\t);\n}\n\nfunction sendCustomCard(\n\tpi: Parameters<ExtensionFactory>[0],\n\tcustomType: string,\n\tcontent: string,\n\tdetails: Record<string, unknown>,\n): void {\n\tpi.sendMessage(\n\t\t{\n\t\t\tcustomType,\n\t\t\tcontent,\n\t\t\tdisplay: true,\n\t\t\tdetails,\n\t\t},\n\t\t{ triggerTurn: false },\n\t);\n}\n\nasync function showProposal(\n\tpi: Parameters<ExtensionFactory>[0],\n\tdependencies: EvoCommandDependencies,\n\tproposal: Proposal,\n): Promise<void> {\n\tsendCustomCard(pi, \"evo.proposal\", await formatProposalCard(dependencies.paths, proposal), {\n\t\tproposalId: proposal.id,\n\t\tstatus: proposal.status,\n\t\ttier: proposal.tier,\n\t\tapprovalDigest: proposal.approvalDigest,\n\t});\n}\n\nasync function confirmExtensionMutation(\n\tctx: ExtensionCommandContext,\n\ttitle: string,\n\tmessage: string,\n): Promise<boolean> {\n\tif (!ctx.hasUI) throw new Error(\"Changing Evo-Pi state requires an interactive UI\");\n\treturn ctx.ui.confirm(title, message);\n}\n\nasync function packGrantsNeedConfirmation(paths: EvoPaths): Promise<boolean> {\n\treturn (await readEvoControlConfig(paths)).grants.approval === \"prompt\";\n}\n\nasync function confirmProposal(proposal: Proposal, ctx: ExtensionCommandContext): Promise<boolean> {\n\tif (!ctx.hasUI) throw new Error(\"Proposal approval requires an interactive UI\");\n\tif (proposal.kind === \"code\" || proposal.tier === \"T2\") {\n\t\tconst digest = proposal.kind === \"code\" ? proposal.approvalDigest : proposal.diffDigest;\n\t\tconst label = proposal.kind === \"code\" ? \"code approval context digest\" : \"final diff digest\";\n\t\tconst confirmed = await ctx.ui.input(`Approve ${proposal.id}`, `Type the ${label}: ${digest}`);\n\t\tif (confirmed?.trim() !== digest) {\n\t\t\tctx.ui.notify(\"Approval digest did not match; proposal remains pending\", \"error\");\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\treturn ctx.ui.confirm(\n\t\t`Approve ${proposal.id}`,\n\t\t`${proposal.tier}/${proposal.kind}: ${proposal.motivation}\\n\\nApply final diff ${proposal.diffDigest}?`,\n\t);\n}\n\nasync function runExtensionPermit(\n\tpi: Parameters<ExtensionFactory>[0],\n\tdependencies: EvoCommandDependencies,\n\tid: string,\n\tctx: ExtensionCommandContext,\n): Promise<Proposal | undefined> {\n\tif (!ctx.hasUI) throw new Error(\"Proposal approval requires an interactive UI\");\n\tlet proposal = await dependencies.service.getProposal(id);\n\twhile (true) {\n\t\tawait showProposal(pi, dependencies, proposal);\n\t\tconst strictPermit = proposal.kind === \"code\" || proposal.tier === \"T2\";\n\t\tif (!strictPermit && proposal.tier === \"T0\" && proposal.status === \"pending\") {\n\t\t\tif (!(await confirmProposal(proposal, ctx))) return undefined;\n\t\t\treturn dependencies.service.approve(id, proposalApproval(proposal));\n\t\t}\n\n\t\tconst actions =\n\t\t\tproposal.status === \"deferred\"\n\t\t\t\t? [\"Reopen\", ...(!strictPermit && proposal.tier === \"T0\" ? [] : [\"Ask why\"]), \"Reject\", \"Close\"]\n\t\t\t\t: [\"Ask why\", ...(strictPermit ? [\"Request revision\"] : []), \"Approve\", \"Reject\", \"Defer\", \"Close\"];\n\t\tconst action = await ctx.ui.select(`Permit ${proposal.id} r${proposal.revision}`, actions);\n\t\tif (!action || action === \"Close\") return undefined;\n\t\tconst expected = proposalApproval(proposal);\n\t\tif (action === \"Ask why\") {\n\t\t\tconst question = await ctx.ui.input(`Question ${proposal.id}`, \"Ask why, or request evidence\");\n\t\t\tif (!question?.trim()) continue;\n\t\t\tconst answer = await askProposalQuestion({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\trunner: dependencies.runner,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\texpected,\n\t\t\t\tquestion,\n\t\t\t\t...(dependencies.cwd ? { cwd: dependencies.cwd } : {}),\n\t\t\t\t...(dependencies.agentDir ? { agentDir: dependencies.agentDir } : {}),\n\t\t\t\t...(dependencies.model ? { model: dependencies.model } : {}),\n\t\t\t});\n\t\t\tsendCustomCard(pi, \"evo.permit-answer\", answer.answerMarkdown, {\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\trevision: proposal.revision,\n\t\t\t});\n\t\t\tproposal = await dependencies.service.getProposal(id);\n\t\t\tcontinue;\n\t\t}\n\t\tif (action === \"Request revision\") {\n\t\t\tconst instruction = await ctx.ui.input(\n\t\t\t\t`Revise ${proposal.id}`,\n\t\t\t\t\"Describe the exact constraints for the revised diff\",\n\t\t\t);\n\t\t\tif (!instruction?.trim()) continue;\n\t\t\tproposal = await reviseProposalFromInstruction({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\trunner: dependencies.runner,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\texpected,\n\t\t\t\tinstruction,\n\t\t\t\t...(dependencies.cwd ? { cwd: dependencies.cwd } : {}),\n\t\t\t\t...(dependencies.agentDir ? { agentDir: dependencies.agentDir } : {}),\n\t\t\t\t...(dependencies.model ? { model: dependencies.model } : {}),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (action === \"Approve\") {\n\t\t\tif (!(await confirmProposal(proposal, ctx))) return undefined;\n\t\t\treturn dependencies.service.approve(id, expected);\n\t\t}\n\t\tif (action === \"Reject\") {\n\t\t\tconst reason = await ctx.ui.input(`Reject ${proposal.id}`, \"Reason for rejection\");\n\t\t\tif (!reason?.trim()) continue;\n\t\t\treturn dependencies.service.reject(id, reason);\n\t\t}\n\t\tif (action === \"Defer\") {\n\t\t\tconst reason = await ctx.ui.input(`Defer ${proposal.id}`, \"Reason for deferral\");\n\t\t\tif (!reason?.trim()) continue;\n\t\t\tproposal = await dependencies.service.defer(id, reason);\n\t\t\tawait recordPermitDefer({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\trevision: proposal.revision,\n\t\t\t\tdiffDigest: proposal.diffDigest,\n\t\t\t\treason,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (action === \"Reopen\") {\n\t\t\tproposal = await dependencies.service.reopen(id, \"User reopened deferred proposal\");\n\t\t\tawait recordPermitReopen({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\trevision: proposal.revision,\n\t\t\t\tdiffDigest: proposal.diffDigest,\n\t\t\t\treason: \"User reopened deferred proposal\",\n\t\t\t});\n\t\t}\n\t}\n}\n\nasync function resolveCanaryControl(\n\tdependencies: { paths: EvoPaths },\n\tproposalId: string,\n\tdecision: Extract<ComponentApprovalDecision, { mode: \"canary\" }>,\n): Promise<CanaryTrialControl> {\n\tif (decision.customization === \"custom\") {\n\t\tif (\n\t\t\t!Number.isSafeInteger(decision.minimumSamples) ||\n\t\t\tdecision.minimumSamples <= 0 ||\n\t\t\tdecision.minimumSamples > 10_000 ||\n\t\t\t!Number.isSafeInteger(decision.maximumDurationDays) ||\n\t\t\tdecision.maximumDurationDays <= 0 ||\n\t\t\tdecision.maximumDurationDays > 365\n\t\t) {\n\t\t\tthrow new Error(\"Custom Canary controls must use 1-10000 sessions and 1-365 days\");\n\t\t}\n\t\treturn {\n\t\t\tcustomization: \"custom\",\n\t\t\tminimumSamples: decision.minimumSamples,\n\t\t\tmaximumDurationDays: decision.maximumDurationDays,\n\t\t};\n\t}\n\tconst [schedule, experiment] = await Promise.all([\n\t\treadScheduleConfig(dependencies.paths),\n\t\treadFrozenExperimentForProposal(dependencies.paths, proposalId),\n\t]);\n\tconst online = experiment?.evidenceStrategy.online;\n\tconst contractDays = online && online.mode !== \"none\" ? parseTrialDurationDays(online.maximumDuration) : undefined;\n\treturn {\n\t\tcustomization: \"default\",\n\t\tminimumSamples:\n\t\t\tonline && online.mode !== \"none\"\n\t\t\t\t? Math.max(schedule.trialDueAfterSessions, online.minimumSamples)\n\t\t\t\t: schedule.trialDueAfterSessions,\n\t\tmaximumDurationDays:\n\t\t\tcontractDays === undefined ? schedule.trialDueAfterDays : Math.min(schedule.trialDueAfterDays, contractDays),\n\t};\n}\n\nexport async function approveCanaryRun(\n\tdependencies: { service: EvoService; paths: EvoPaths },\n\trunId: string,\n\tdecision: ComponentApprovalDecision = { mode: \"canary\", customization: \"default\" },\n): Promise<void> {\n\tconst run = await readEvolutionRun(dependencies.paths, runId);\n\tif (run.status !== \"awaiting-canary-approval\" || !run.proposalId) {\n\t\tthrow new Error(`Evolution task ${runId} is not awaiting Canary approval`);\n\t}\n\tconst proposal = await dependencies.service.getProposal(run.proposalId);\n\tif (!changesComponentSelection(proposal) || !proposal.candidateDigest || !proposal.targetAbi) {\n\t\tthrow new Error(\"The proposal does not replace a component\");\n\t}\n\tif (\n\t\trun.canaryParentDigest !== proposal.parentBundleDigest ||\n\t\trun.canaryCandidateDigest !== proposal.candidateDigest ||\n\t\trun.canaryTargetAbi !== proposal.targetAbi\n\t) {\n\t\tthrow new Error(\"Canary review metadata no longer matches the exact proposal\");\n\t}\n\tconst stable = await dependencies.service.registry.readStableDigest();\n\tif (proposal.status === \"trialing\") {\n\t\tif (decision.mode === \"direct\") throw new Error(\"The component is already running as a Canary\");\n\t\tconst trial = await dependencies.service.registry.readTrial();\n\t\tif (\n\t\t\t!stable ||\n\t\t\tstable !== proposal.candidateDigest ||\n\t\t\t!trial ||\n\t\t\ttrial.proposalId !== proposal.id ||\n\t\t\ttrial.digest !== proposal.candidateDigest\n\t\t) {\n\t\t\tthrow new Error(\"Component proposal and active Canary state disagree\");\n\t\t}\n\t\tawait updateEvolutionRun(dependencies.paths, run.id, {\n\t\t\tstatus: \"trialing\",\n\t\t\tcanaryApprovalDigest: proposal.approvalDigest,\n\t\t\tcanaryStableDigest: proposal.parentBundleDigest,\n\t\t\tcomponentApprovalMode: trial.canary?.customization === \"custom\" ? \"custom-canary\" : \"default-canary\",\n\t\t\t...(trial.canary\n\t\t\t\t? {\n\t\t\t\t\t\tcanaryMinimumSamples: trial.canary.minimumSamples,\n\t\t\t\t\t\tcanaryMaximumDurationDays: trial.canary.maximumDurationDays,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t\treturn;\n\t}\n\tif (proposal.status === \"kept\") {\n\t\tif (!stable || stable !== proposal.candidateDigest || (await dependencies.service.registry.readTrial())) {\n\t\t\tthrow new Error(\"Directly activated component state disagrees with the reviewed proposal\");\n\t\t}\n\t\tawait updateEvolutionRun(dependencies.paths, run.id, {\n\t\t\tstatus: \"completed\",\n\t\t\tcanaryApprovalDigest: proposal.approvalDigest,\n\t\t\tcanaryStableDigest: proposal.parentBundleDigest,\n\t\t\tcomponentApprovalMode: \"direct\",\n\t\t});\n\t\treturn;\n\t}\n\tif (proposal.status !== \"pending\") throw new Error(`Component proposal is ${proposal.status}`);\n\tif (!stable || stable !== proposal.parentBundleDigest) {\n\t\tthrow new Error(\"Stable bundle changed; rebuild and review the Canary against the new baseline\");\n\t}\n\tconst canary =\n\t\tdecision.mode === \"canary\" ? await resolveCanaryControl(dependencies, proposal.id, decision) : undefined;\n\tconst approved = await dependencies.service.approveComponent(\n\t\tproposal.id,\n\t\tproposalApproval(proposal),\n\t\tdecision.mode === \"direct\" ? { mode: \"direct\" } : { mode: \"trial\", canary },\n\t);\n\tawait updateEvolutionRun(dependencies.paths, run.id, {\n\t\tstatus: decision.mode === \"direct\" ? \"completed\" : \"trialing\",\n\t\tcanaryApprovedAt: new Date().toISOString(),\n\t\tcanaryApprovalDigest: approved.approvalDigest,\n\t\tcanaryStableDigest: stable,\n\t\tcomponentApprovalMode:\n\t\t\tdecision.mode === \"direct\"\n\t\t\t\t? \"direct\"\n\t\t\t\t: canary?.customization === \"custom\"\n\t\t\t\t\t? \"custom-canary\"\n\t\t\t\t\t: \"default-canary\",\n\t\t...(canary\n\t\t\t? {\n\t\t\t\t\tcanaryMinimumSamples: canary.minimumSamples,\n\t\t\t\t\tcanaryMaximumDurationDays: canary.maximumDurationDays,\n\t\t\t\t}\n\t\t\t: {}),\n\t});\n}\n\nexport async function directKeepCanaryRun(\n\tdependencies: { service: EvoService; paths: EvoPaths },\n\trunId: string,\n): Promise<void> {\n\tconst run = await readEvolutionRun(dependencies.paths, runId);\n\tif (run.status !== \"trialing\" || !run.proposalId) {\n\t\tthrow new Error(`Evolution task ${runId} is not running a component Canary`);\n\t}\n\tconst proposal = await dependencies.service.getProposal(run.proposalId);\n\tif (!changesComponentSelection(proposal) || !proposal.candidateDigest || !proposal.targetAbi) {\n\t\tthrow new Error(\"The active trial does not replace a component\");\n\t}\n\tif (\n\t\trun.canaryParentDigest !== proposal.parentBundleDigest ||\n\t\trun.canaryCandidateDigest !== proposal.candidateDigest ||\n\t\trun.canaryTargetAbi !== proposal.targetAbi\n\t) {\n\t\tthrow new Error(\"Canary run metadata no longer matches the exact proposal\");\n\t}\n\tconst [stable, trial] = await Promise.all([\n\t\tdependencies.service.registry.readStableDigest(),\n\t\tdependencies.service.registry.readTrial(),\n\t]);\n\tif (proposal.status === \"kept\") {\n\t\tif (stable !== proposal.candidateDigest || trial) {\n\t\t\tthrow new Error(\"Directly kept component state disagrees with the reviewed proposal\");\n\t\t}\n\t} else {\n\t\tif (\n\t\t\tproposal.status !== \"trialing\" ||\n\t\t\tstable !== proposal.candidateDigest ||\n\t\t\t!trial ||\n\t\t\ttrial.proposalId !== proposal.id ||\n\t\t\ttrial.digest !== proposal.candidateDigest\n\t\t) {\n\t\t\tthrow new Error(\"Component proposal and active Canary state disagree\");\n\t\t}\n\t\tawait dependencies.service.directKeepComponentTrial(\n\t\t\tproposal.id,\n\t\t\t\"Human directly kept the active validated component and ended Canary evidence collection\",\n\t\t);\n\t}\n\tawait updateEvolutionRun(dependencies.paths, run.id, {\n\t\tstatus: \"completed\",\n\t\tcomponentApprovalMode: \"direct\",\n\t});\n}\n\n/**\n * Apply a user's execute/skip/reject decision for a run parked on a\n * recommended verification. Execute spawns a detached worker (replay plus\n * re-evaluation take minutes); skip and reject resolve synchronously.\n */\nexport async function decideVerificationRun(\n\tdependencies: { service: EvoService; paths: EvoPaths; cwd?: string },\n\trunId: string,\n\tdecision: VerificationDecision,\n): Promise<void> {\n\tif (decision === \"execute\") {\n\t\tawait startBackgroundEvidenceResumption({\n\t\t\tpaths: dependencies.paths,\n\t\t\tcwd: dependencies.cwd ?? process.cwd(),\n\t\t\trunId,\n\t\t});\n\t\treturn;\n\t}\n\tif (decision === \"skip\") {\n\t\tconst config = await readEvoControlConfig(dependencies.paths);\n\t\tawait skipPendingVerification({ paths: dependencies.paths, service: dependencies.service, config, runId });\n\t\treturn;\n\t}\n\tawait rejectPendingVerification({ paths: dependencies.paths, service: dependencies.service, runId });\n}\n\nasync function soleRunAwaitingVerification(dependencies: { paths: EvoPaths }): Promise<string> {\n\tconst pending: string[] = [];\n\tfor (const run of await listEvolutionRuns(dependencies.paths)) {\n\t\tif (run.status !== \"awaiting-evidence\") continue;\n\t\tif (await readPendingVerification(dependencies.paths, run.id)) pending.push(run.id);\n\t}\n\tif (pending.length === 1 && pending[0]) return pending[0];\n\tif (pending.length === 0) throw new Error(\"No run is awaiting a verification decision\");\n\tthrow new Error(`Multiple runs await a verification decision; pass one of: ${pending.join(\", \")}`);\n}\n\nfunction parseVerificationDecisionWord(word: string | undefined): VerificationDecision {\n\tif (word === \"execute\" || word === \"run\") return \"execute\";\n\tif (word === \"skip\") return \"skip\";\n\tif (word === \"reject\") return \"reject\";\n\tthrow new Error(\"Usage: verify [run-id] execute|skip|reject\");\n}\n\nfunction describeVerificationDecision(runId: string, decision: VerificationDecision): string {\n\tif (decision === \"execute\") return `已启动后台验证 ${runId}；完成后按发布策略自动处理`;\n\tif (decision === \"skip\") return `已跳过推荐验证，${runId} 按当前评估结果走标准发布策略`;\n\treturn `已拒绝 ${runId} 的候选`;\n}\n\nasync function openEvolutionInspector(\n\tdependencies: { service: EvoService; paths: EvoPaths; cwd?: string },\n\tctx: ExtensionContext,\n\tinitialItem?: string,\n): Promise<void> {\n\tconst itemKey = initialItem && !initialItem.includes(\":\") ? `run:${initialItem}` : initialItem;\n\tawait ctx.ui.custom<void>(\n\t\t(tui, theme, _keybindings, done) =>\n\t\t\tnew EvolutionProcessInspector(\n\t\t\t\ttui,\n\t\t\t\ttheme,\n\t\t\t\tdependencies.paths,\n\t\t\t\tdependencies.service,\n\t\t\t\titemKey,\n\t\t\t\t() => done(undefined),\n\t\t\t\t(runId, decision) => approveCanaryRun(dependencies, runId, decision),\n\t\t\t\t(runId) => directKeepCanaryRun(dependencies, runId),\n\t\t\t\t(runId, decision) => decideVerificationRun(dependencies, runId, decision),\n\t\t\t),\n\t);\n}\n\nasync function dispatchExtensionCommand(\n\tpi: Parameters<ExtensionFactory>[0],\n\tdependencies: EvoCommandDependencies,\n\targs: string,\n\tctx: ExtensionCommandContext,\n): Promise<void> {\n\tconst { command, rest } = parseSubcommand(args);\n\tawait ctx.waitForIdle();\n\n\tconst presenter: EvoCommandPresenter = {\n\t\tcommandPrefix: \"/evo\",\n\t\tcwd: dependencies.cwd ?? ctx.cwd,\n\t\tprint: (customType, content, details = {}) => sendCustomCard(pi, customType, content, details),\n\t\tnotify: (message) => ctx.ui.notify(message, \"info\"),\n\t\tconfirm: (title, message) => confirmExtensionMutation(ctx, title, message),\n\t};\n\tif (await runSharedEvoCommand(dependencies, command, rest, presenter)) return;\n\n\tswitch (command) {\n\t\tcase \"help\":\n\t\t\tsendCustomCard(pi, \"evo.help\", EVO_HELP, { command: \"help\" });\n\t\t\treturn;\n\t\tcase \"model\": {\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"/evo model requires an interactive UI; use /evo config set models.<role>.model <provider/model>\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst config = await readEvoControlConfig(dependencies.paths);\n\t\t\tconst roles = [\n\t\t\t\t{ key: \"researchPlanner\", label: \"研究 (researchPlanner)\" },\n\t\t\t\t{ key: \"builder\", label: \"构建 (builder)\" },\n\t\t\t\t{ key: \"evaluator\", label: \"评估 (evaluator)\" },\n\t\t\t\t{ key: \"triage\", label: \"分流 (triage)\" },\n\t\t\t] as const;\n\t\t\tlet role: (typeof roles)[number] | undefined;\n\t\t\tif (rest.trim()) {\n\t\t\t\trole = roles.find((candidate) => candidate.key === rest.trim());\n\t\t\t\tif (!role) {\n\t\t\t\t\tthrow new Error(`Usage: /evo model [${roles.map((candidate) => candidate.key).join(\"|\")}]`);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst roleOptions = roles.map(\n\t\t\t\t\t(candidate) => `${candidate.label} — 当前 ${config.models[candidate.key].model}`,\n\t\t\t\t);\n\t\t\t\tconst roleChoice = await ctx.ui.select(\"选择要配置模型的进化阶段\", roleOptions);\n\t\t\t\trole = roles[roleOptions.indexOf(roleChoice ?? \"\")];\n\t\t\t\tif (!role) return;\n\t\t\t}\n\n\t\t\tconst current = config.models[role.key];\n\t\t\tconst available = ctx.modelRegistry.getAvailable();\n\t\t\tif (available.length === 0) {\n\t\t\t\tctx.ui.notify(\"没有已配置鉴权的可用模型，请先登录对应 provider\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst routes = [...new Set(available.map((model) => `${model.provider}/${model.id}`))].sort((a, b) =>\n\t\t\t\ta === current.model ? -1 : b === current.model ? 1 : a.localeCompare(b),\n\t\t\t);\n\t\t\tconst modelOptions = routes.map((route) => (route === current.model ? `${route} (当前)` : route));\n\t\t\tconst modelChoice = await ctx.ui.select(`选择 ${role.label} 的模型`, modelOptions);\n\t\t\tconst route = routes[modelOptions.indexOf(modelChoice ?? \"\")];\n\t\t\tif (!route) return;\n\n\t\t\tconst levels: ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n\t\t\tconst levelOptions = levels.map((level) => (level === current.thinkingLevel ? `${level} (当前)` : level));\n\t\t\tconst levelChoice = await ctx.ui.select(`选择 ${role.label} 的 thinking level`, levelOptions);\n\t\t\tconst level = levels[levelOptions.indexOf(levelChoice ?? \"\")];\n\t\t\tif (!level) return;\n\n\t\t\tawait updateEvoControlConfigValue(dependencies.paths, `models.${role.key}.model`, route);\n\t\t\tawait updateEvoControlConfigValue(dependencies.paths, `models.${role.key}.thinkingLevel`, level);\n\t\t\tctx.ui.notify(`Evo ${role.key} 模型已更新: ${route} (thinking ${level})`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"init\": {\n\t\t\tconst bundle = await dependencies.service.init(pi.getActiveTools());\n\t\t\tctx.ui.notify(`Evo-Pi initialized at ${bundle.digest}`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"search\": {\n\t\t\tconst discovery = await dependencies.getDiscoveryService();\n\t\t\tconst results = await discovery.search({\n\t\t\t\t...(rest ? { query: rest } : {}),\n\t\t\t\tlimit: DISCOVERY_SEARCH_LIMIT,\n\t\t\t\tincludeTrustedManifests: true,\n\t\t\t});\n\t\t\tsendCustomCard(pi, \"evo.pack-search\", formatPackSearchResults(results), {\n\t\t\t\tquery: rest,\n\t\t\t\tcount: results.length,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tcase \"install\": {\n\t\t\tconst selection = parsePackSelection(rest, \"/evo install <name> [version]\");\n\t\t\tconst discovery = await dependencies.getDiscoveryService();\n\t\t\tconst inspection = await discovery.inspect(selection.name, selection.version);\n\t\t\tconst preview = createPackCapabilityGrantPreview(\n\t\t\t\tinspection.manifest,\n\t\t\t\tinspection.entry.integrity,\n\t\t\t\tdependencies.model ?? (ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined),\n\t\t\t\tdependencies.spawnAgentToolNames ?? DEFAULT_SPAWN_AGENT_TOOL_NAMES,\n\t\t\t);\n\t\t\tsendCustomCard(pi, \"evo.pack-inspect\", formatPackInspection(inspection), {\n\t\t\t\tpack: inspection.entry.name,\n\t\t\t\tversion: inspection.entry.version,\n\t\t\t\tintegrity: inspection.entry.integrity,\n\t\t\t});\n\t\t\tconst parentDigest = await requireActiveBundleDigest(dependencies);\n\t\t\tif (Object.keys(preview.grantsByComponent).length > 0) {\n\t\t\t\tif (await packGrantsNeedConfirmation(dependencies.paths)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!(await confirmExtensionMutation(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\"Approve executable pack capability budgets\",\n\t\t\t\t\t\t\tformatPackCapabilityGrantPreview(preview),\n\t\t\t\t\t\t))\n\t\t\t\t\t) {\n\t\t\t\t\t\tctx.ui.notify(\"Pack install cancelled before staging\", \"info\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsendCustomCard(pi, \"evo.pack-grants\", formatPackCapabilityGrantPreview(preview), {\n\t\t\t\t\t\tpack: inspection.entry.name,\n\t\t\t\t\t\tapproval: \"auto\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst sandboxDecision = await resolvePackValidationSandbox(dependencies, inspection.manifest, () => {\n\t\t\t\tif (!ctx.hasUI) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Component sandbox is unavailable and one-time direct execution requires an interactive UI\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn ctx.ui.confirm(\"One-time component permission\", PACK_DIRECT_VALIDATION_PROMPT);\n\t\t\t});\n\t\t\tif (sandboxDecision === \"cancelled\") {\n\t\t\t\tctx.ui.notify(\"Pack install cancelled; no component permission was granted\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet unknownAbiResults: UnknownAbiBuilderCycleResult[] = [];\n\t\t\tconst result = await discovery.install({\n\t\t\t\tname: inspection.entry.name,\n\t\t\t\tversion: inspection.entry.version,\n\t\t\t\texpectedIntegrity: inspection.entry.integrity,\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\tparentDigest,\n\t\t\t\tgrantsByComponent: preview.grantsByComponent,\n\t\t\t\t...(sandboxDecision.sandbox === undefined ? {} : { sandbox: sandboxDecision.sandbox }),\n\t\t\t\tbeforeStage: async ({ preflight }) => {\n\t\t\t\t\tunknownAbiResults = await stageUnknownPackAbis(dependencies, parentDigest, preflight.unknownAbiRequests);\n\t\t\t\t},\n\t\t\t});\n\t\t\tsendCustomCard(pi, \"evo.pack-install\", formatRegistryPackInstallResult(result, unknownAbiResults), {\n\t\t\t\tpack: result.imported.manifest.name,\n\t\t\t\tversion: result.imported.manifest.version,\n\t\t\t\tintegrity: result.imported.manifest.integrity,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tcase \"import\": {\n\t\t\tlet packDirectory = rest.trim();\n\t\t\tlet stagingDirectory: string | undefined;\n\t\t\tif (!packDirectory) {\n\t\t\t\tconst templates = await listInstallableWorkflowTemplates(dependencies);\n\t\t\t\tif (!ctx.hasUI) {\n\t\t\t\t\tsendCustomCard(pi, \"evo.import-menu\", formatInstallableTemplates(templates), {});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst labels = templates.map(\n\t\t\t\t\t(template) =>\n\t\t\t\t\t\t`${template.name} (${template.trigger}) — ${template.state === \"active\" ? \"已激活\" : template.state === \"pending\" ? \"待批准\" : template.description}`,\n\t\t\t\t);\n\t\t\t\tconst choice = await ctx.ui.select(\"安装 workflow 模板（外部包用 /evo import <目录>）\", labels);\n\t\t\t\tconst selected = templates[labels.indexOf(choice ?? \"\")];\n\t\t\t\tif (!selected) return;\n\t\t\t\tif (selected.state === \"active\") {\n\t\t\t\t\tctx.ui.notify(`${selected.trigger} 已在当前 bundle 中激活`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (selected.state === \"pending\" && selected.pendingProposalId) {\n\t\t\t\t\tconst outcome = await runExtensionPermit(pi, dependencies, selected.pendingProposalId, ctx);\n\t\t\t\t\tif (outcome) ctx.ui.notify(describePermitOutcome(outcome, selected.trigger), \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tstagingDirectory = await writeWorkflowTemplateToStaging(selected.name);\n\t\t\t\tpackDirectory = stagingDirectory;\n\t\t\t}\n\t\t\tconst parentDigest = await requireActiveBundleDigest(dependencies);\n\t\t\tconst preview = await previewPackCapabilityGrants(\n\t\t\t\tpackDirectory,\n\t\t\t\tdependencies.model ?? (ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined),\n\t\t\t\tdependencies.spawnAgentToolNames ?? DEFAULT_SPAWN_AGENT_TOOL_NAMES,\n\t\t\t);\n\t\t\tif (Object.keys(preview.grantsByComponent).length > 0) {\n\t\t\t\tif (await packGrantsNeedConfirmation(dependencies.paths)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!(await confirmExtensionMutation(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\"Approve executable pack capability budgets\",\n\t\t\t\t\t\t\tformatPackCapabilityGrantPreview(preview),\n\t\t\t\t\t\t))\n\t\t\t\t\t) {\n\t\t\t\t\t\tctx.ui.notify(\"Pack import cancelled before staging\", \"info\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsendCustomCard(pi, \"evo.pack-grants\", formatPackCapabilityGrantPreview(preview), {\n\t\t\t\t\t\tpack: preview.manifest.name,\n\t\t\t\t\t\tapproval: \"auto\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst sandboxDecision = await resolvePackValidationSandbox(dependencies, preview.manifest, () => {\n\t\t\t\tif (!ctx.hasUI) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Component sandbox is unavailable and one-time direct execution requires an interactive UI\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn ctx.ui.confirm(\"One-time component permission\", PACK_DIRECT_VALIDATION_PROMPT);\n\t\t\t});\n\t\t\tif (sandboxDecision === \"cancelled\") {\n\t\t\t\tctx.ui.notify(\"Pack import cancelled; no component permission was granted\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet unknownAbiResults: UnknownAbiBuilderCycleResult[] = [];\n\t\t\tconst result = await stagePackImport(\n\t\t\t\tdependencies,\n\t\t\t\tpackDirectory,\n\t\t\t\tpreview.integrity,\n\t\t\t\tpreview.grantsByComponent,\n\t\t\t\tparentDigest,\n\t\t\t\tasync (preflight) => {\n\t\t\t\t\tunknownAbiResults = await stageUnknownPackAbis(dependencies, parentDigest, preflight.unknownAbiRequests);\n\t\t\t\t},\n\t\t\t\tsandboxDecision.sandbox,\n\t\t\t);\n\t\t\tsendCustomCard(pi, \"evo.pack-import\", formatPackImportResult(result, unknownAbiResults), {\n\t\t\t\tpack: result.manifest.name,\n\t\t\t\tintegrity: result.manifest.integrity,\n\t\t\t});\n\t\t\tif (stagingDirectory) await rm(stagingDirectory, { recursive: true, force: true }).catch(() => undefined);\n\t\t\t// Continue straight into approval so installing a pack is one flow\n\t\t\t// instead of a scavenger hunt across import/list/permit.\n\t\t\tif (ctx.hasUI) {\n\t\t\t\tfor (const component of result.importedComponents) {\n\t\t\t\t\tconst outcome = await runExtensionPermit(pi, dependencies, component.proposal.id, ctx);\n\t\t\t\t\tif (outcome) ctx.ui.notify(describePermitOutcome(outcome, component.trigger), \"info\");\n\t\t\t\t}\n\t\t\t\tif (result.proposal) {\n\t\t\t\t\tconst outcome = await runExtensionPermit(pi, dependencies, result.proposal.id, ctx);\n\t\t\t\t\tif (outcome) ctx.ui.notify(describePermitOutcome(outcome), \"info\");\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tcase \"export\": {\n\t\t\tconst result = await exportActiveBundle(dependencies, requireValue(rest, \"/evo export <directory>\"));\n\t\t\tsendCustomCard(pi, \"evo.pack-export\", formatPackExportResult(result), {\n\t\t\t\tbundleDigest: result.bundleDigest,\n\t\t\t\tintegrity: result.manifest.integrity,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tcase \"report\": {\n\t\t\tconst report = await runReport(modelRunOptions(dependencies));\n\t\t\tsendCustomCard(pi, \"evo.report\", report.observationsMarkdown, { file: report.file });\n\t\t\treturn;\n\t\t}\n\t\tcase \"inspect\": {\n\t\t\tconst runs = await inspectBackgroundEvolutions(dependencies.paths);\n\t\t\tif (ctx.mode !== \"tui\") {\n\t\t\t\tctx.ui.notify(formatEvolutionRuns(runs, rest || undefined), \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait openEvolutionInspector(dependencies, ctx, rest || undefined);\n\t\t\treturn;\n\t\t}\n\t\tcase \"retry\": {\n\t\t\tconst { id, from } = parseRetryArgs(rest, \"/evo retry <run-id> [--from research|building|validating]\");\n\t\t\tconst resolved = from ?? (await resolveRetryFrom(dependencies.paths, id));\n\t\t\tif (resolved === \"research\") {\n\t\t\t\tconst source = await readEvolutionRun(dependencies.paths, id);\n\t\t\t\tconst run = await startBackgroundEvolution({\n\t\t\t\t\tpaths: dependencies.paths,\n\t\t\t\t\tcwd: dependencies.cwd ?? ctx.cwd,\n\t\t\t\t\t...(source.request ? { request: source.request } : {}),\n\t\t\t\t\tretryOfRunId: id,\n\t\t\t\t});\n\t\t\t\tctx.ui.notify(`Evolution task ${run.id} restarted from research in the background`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (resolved === \"building\") {\n\t\t\t\tconst run = await startBackgroundBuildingResume({\n\t\t\t\t\tpaths: dependencies.paths,\n\t\t\t\t\tsourceRunId: id,\n\t\t\t\t\tcwd: dependencies.cwd ?? ctx.cwd,\n\t\t\t\t});\n\t\t\t\tctx.ui.notify(`Evolution task ${run.id} resumed at the builder in the background`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst sandboxAvailable = await canUseEvoComponentSandbox();\n\t\t\tif (!sandboxAvailable && !ctx.hasUI) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Component sandbox is unavailable and one-time direct execution requires an interactive UI\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst allowDirect =\n\t\t\t\t!sandboxAvailable &&\n\t\t\t\t(await ctx.ui.confirm(\n\t\t\t\t\t\"One-time component permission\",\n\t\t\t\t\t`The OS sandbox is unavailable. Allow the built component from ${id} to run directly once for validation with your user permissions? This grant is not saved and does not activate the Canary.`,\n\t\t\t\t));\n\t\t\tif (!sandboxAvailable && !allowDirect) {\n\t\t\t\tctx.ui.notify(\"Retry cancelled; no component permission was granted\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst retried = await retryEvolutionFromValidation({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\tsourceRunId: id,\n\t\t\t\tcwd: dependencies.cwd ?? ctx.cwd,\n\t\t\t\t...(allowDirect ? { sandbox: false } : {}),\n\t\t\t});\n\t\t\tctx.ui.notify(\n\t\t\t\tretried.status === \"awaiting-canary-approval\"\n\t\t\t\t\t? `Evolution task ${retried.runId} is ready for Canary review [/evo inspect]`\n\t\t\t\t\t: `Evolution task ${retried.runId} is waiting for its frozen evidence strategy`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tcase \"schedule\": {\n\t\t\tif (rest) {\n\t\t\t\tconst cadence = parseScheduleCadence(rest);\n\t\t\t\tif (!cadence) throw new Error(`Usage: ${SCHEDULE_USAGE}`);\n\t\t\t\tconst updated = await writeScheduleConfig(dependencies.paths, cadence);\n\t\t\t\tctx.ui.notify(`Evo-Pi schedule updated — ${describeScheduleCadence(updated)}`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst status = await getScheduleStatus(dependencies.paths);\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\tctx.ui.notify(formatScheduleStatus(status), \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst choice = await ctx.ui.select(\n\t\t\t\t`Evo-Pi schedule — ${describeScheduleCadence(status.config)}`,\n\t\t\t\tSCHEDULE_CADENCE_CHOICES.map((entry) => entry.label).concat(\"Keep current\"),\n\t\t\t);\n\t\t\tconst selected = SCHEDULE_CADENCE_CHOICES.find((entry) => entry.label === choice);\n\t\t\tif (!selected) {\n\t\t\t\tctx.ui.notify(formatScheduleStatus(status), \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst updated = await writeScheduleConfig(dependencies.paths, selected.input);\n\t\t\tctx.ui.notify(`Evo-Pi schedule updated — ${describeScheduleCadence(updated)}`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"note\": {\n\t\t\tconst note = await dependencies.service.note(\n\t\t\t\tctx.sessionManager.getSessionId(),\n\t\t\t\trequireValue(rest, \"/evo note <text>\"),\n\t\t\t);\n\t\t\tctx.ui.notify(`Saved ${note.fileName}`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"request\": {\n\t\t\tconst request = await dependencies.service.request(\n\t\t\t\tctx.sessionManager.getSessionId(),\n\t\t\t\trequireValue(rest, \"/evo request <text>\"),\n\t\t\t);\n\t\t\tctx.ui.notify(`Saved ${request.fileName}`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"preference\": {\n\t\t\tconst preference = await dependencies.service.preference(\n\t\t\t\tctx.sessionManager.getSessionId(),\n\t\t\t\trequireValue(rest, \"/evo preference <text>\"),\n\t\t\t);\n\t\t\tctx.ui.notify(`Activated durable preference from ${preference.fileName}`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"forget\": {\n\t\t\tconst preferenceId = requireValue(rest, \"/evo forget <preference-id>\");\n\t\t\tconst request = await dependencies.service.forgetPreference(ctx.sessionManager.getSessionId(), preferenceId);\n\t\t\tctx.ui.notify(`Saved removal request ${request.fileName}`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"permit\": {\n\t\t\tconst id = requireValue(rest, \"/evo permit <proposal-id>\");\n\t\t\tconst result = await runExtensionPermit(pi, dependencies, id, ctx);\n\t\t\tif (!result) return;\n\t\t\tawait showProposal(pi, dependencies, result);\n\t\t\tctx.ui.notify(`Proposal ${id} is ${result.status}`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"rollback\": {\n\t\t\tconst { first, rest: trailing } = splitFirst(rest);\n\t\t\tconst hasDigest = /^[a-f0-9]{64}$/.test(first);\n\t\t\tconst reason = (hasDigest ? trailing : rest).trim() || \"User requested rollback\";\n\t\t\tconst target = hasDigest ? `bundle ${first}` : \"the active trial or stable bundle parent\";\n\t\t\tif (!(await confirmExtensionMutation(ctx, \"Roll back Evo-Pi\", `Roll back ${target}?`))) {\n\t\t\t\tctx.ui.notify(\"Rollback cancelled\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = await dependencies.service.rollback(hasDigest ? first : undefined, reason);\n\t\t\tctx.ui.notify(`Rolled back ${result.from} → ${result.to}`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"keep\": {\n\t\t\tif (!ctx.hasUI) throw new Error(\"Keeping a trial requires an interactive UI\");\n\t\t\tconst componentTrial = await findDirectKeepableTrial(dependencies);\n\t\t\tif (componentTrial) {\n\t\t\t\tif (\n\t\t\t\t\t!(await ctx.ui.confirm(\n\t\t\t\t\t\t`Keep ${componentTrial.id}`,\n\t\t\t\t\t\t\"立即正式上线这个已通过可执行验证的组件并结束 Canary？（/evo rollback 可回滚）\",\n\t\t\t\t\t))\n\t\t\t\t) {\n\t\t\t\t\tctx.ui.notify(\"Trial remains active\", \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst kept = await dependencies.service.directKeepComponentTrial(\n\t\t\t\t\tcomponentTrial.id,\n\t\t\t\t\trest || \"Human directly kept the validated component trial via /evo keep\",\n\t\t\t\t);\n\t\t\t\tctx.ui.notify(`Kept ${kept.id}`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst retrospective = await runRetrospective(modelRunOptions(dependencies));\n\t\t\tsendCustomCard(pi, \"evo.retrospective\", retrospective.retrospectiveMarkdown, {\n\t\t\t\tproposalId: retrospective.proposal.id,\n\t\t\t});\n\t\t\tconst confirmed = await ctx.ui.confirm(\n\t\t\t\t`Keep ${retrospective.proposal.id}`,\n\t\t\t\t\"Keep this trial after reviewing the retrospective?\",\n\t\t\t);\n\t\t\tif (!confirmed) {\n\t\t\t\tctx.ui.notify(\"Trial remains active\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst kept = await dependencies.service.keep(rest || \"User kept trial after retrospective\");\n\t\t\tctx.ui.notify(`Kept ${kept.id}`, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"verify\": {\n\t\t\tconst { first, rest: trailing } = splitFirst(rest);\n\t\t\tconst explicitRunId = first.startsWith(\"r-\") ? first : undefined;\n\t\t\tconst decision = parseVerificationDecisionWord((explicitRunId ? trailing : first).trim().toLowerCase());\n\t\t\tconst runId = explicitRunId ?? (await soleRunAwaitingVerification(dependencies));\n\t\t\tawait decideVerificationRun(dependencies, runId, decision);\n\t\t\tctx.ui.notify(describeVerificationDecision(runId, decision), \"info\");\n\t\t\treturn;\n\t\t}\n\t\tdefault:\n\t\t\tthrow new Error(`Unknown /evo command: ${command}`);\n\t}\n}\n\nexport function createEvoCommandExtension(options: EvoCommandExtensionOptions = {}): ExtensionFactory {\n\tconst dependencies = createDependencies(options);\n\treturn (pi) => {\n\t\tlet statusTimer: NodeJS.Timeout | undefined;\n\t\tlet refreshingStatus = false;\n\t\tlet canPromptForCanary = false;\n\t\tlet canaryInspectorOpen = false;\n\t\tconst promptedCanaryRuns = new Set<string>();\n\t\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\t\tconst refresh = async (): Promise<void> => {\n\t\t\t\tif (refreshingStatus) return;\n\t\t\t\trefreshingStatus = true;\n\t\t\t\ttry {\n\t\t\t\t\tawait refreshEvoStatusIndicator(dependencies, ctx, () => new Date(), false);\n\t\t\t\t\tif (canPromptForCanary && ctx.mode === \"tui\" && !canaryInspectorOpen) {\n\t\t\t\t\t\tconst runs = await listEvolutionRuns(dependencies.paths);\n\t\t\t\t\t\tlet awaiting = runs.find(\n\t\t\t\t\t\t\t(run) => run.status === \"awaiting-canary-approval\" && !promptedCanaryRuns.has(run.id),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (!awaiting) {\n\t\t\t\t\t\t\tfor (const run of runs) {\n\t\t\t\t\t\t\t\tif (run.status !== \"awaiting-evidence\" || promptedCanaryRuns.has(run.id)) continue;\n\t\t\t\t\t\t\t\tif (await readPendingVerification(dependencies.paths, run.id)) {\n\t\t\t\t\t\t\t\t\tawaiting = run;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (awaiting) {\n\t\t\t\t\t\t\tpromptedCanaryRuns.add(awaiting.id);\n\t\t\t\t\t\t\tcanaryInspectorOpen = true;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait openEvolutionInspector(dependencies, ctx, awaiting.id);\n\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\tcanaryInspectorOpen = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\tctx.ui.setStatus(\"evo\", undefined);\n\t\t\t\t\tctx.ui.setStatusItems(\"evo\", undefined);\n\t\t\t\t} finally {\n\t\t\t\t\trefreshingStatus = false;\n\t\t\t\t}\n\t\t\t};\n\t\t\tif (statusTimer) clearInterval(statusTimer);\n\t\t\tawait refresh();\n\t\t\tcanPromptForCanary = true;\n\t\t\tstatusTimer = setInterval(() => void refresh(), 1_000);\n\t\t\tstatusTimer.unref?.();\n\t\t});\n\t\tpi.on(\"session_shutdown\", (_event, ctx) => {\n\t\t\tif (statusTimer) clearInterval(statusTimer);\n\t\t\tstatusTimer = undefined;\n\t\t\tcanPromptForCanary = false;\n\t\t\tcanaryInspectorOpen = false;\n\t\t\tpromptedCanaryRuns.clear();\n\t\t\tctx.ui.setStatus(\"evo\", undefined);\n\t\t\tctx.ui.setStatusItems(\"evo\", undefined);\n\t\t});\n\t\tpi.registerShortcut(QUICK_APPROVE_SHORTCUT, {\n\t\t\tdescription: \"Apply the first pending T0 Evo-Pi proposal\",\n\t\t\thandler: async (ctx) => {\n\t\t\t\tif (!ctx.hasUI || ctx.mode !== \"tui\") {\n\t\t\t\t\tctx.ui.notify(\"T0 quick approval requires the interactive TUI\", \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!ctx.isIdle()) {\n\t\t\t\t\tctx.ui.notify(\"Wait for the active turn before applying a T0 proposal\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst proposal = await findPendingT0Proposal(dependencies);\n\t\t\t\t\tif (!proposal) {\n\t\t\t\t\t\tctx.ui.notify(\"No pending T0 proposal\", \"info\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst approved = await dependencies.service.approve(proposal.id, proposalApproval(proposal));\n\t\t\t\t\tctx.ui.notify(`Applied T0 proposal ${approved.id}`, \"info\");\n\t\t\t\t} catch (error) {\n\t\t\t\t\tctx.ui.notify(`Evo-Pi: ${errorMessage(error)}`, \"error\");\n\t\t\t\t} finally {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait refreshEvoStatusIndicator(dependencies, ctx);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tctx.ui.setStatus(\"evo\", undefined);\n\t\t\t\t\t\tctx.ui.setStatusItems(\"evo\", undefined);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t\tpi.registerCommand(\"evo\", {\n\t\t\tdescription: \"Inspect and control Evo-Pi\",\n\t\t\tgetArgumentCompletions: (prefix) => {\n\t\t\t\tconst commandPrefix = prefix.trimStart().toLowerCase();\n\t\t\t\tif (/\\s/.test(commandPrefix)) return null;\n\t\t\t\tconst matches = SUBCOMMANDS.filter((command) => command.startsWith(commandPrefix));\n\t\t\t\treturn matches.map((command) => ({ value: command, label: command }));\n\t\t\t},\n\t\t\thandler: async (args, ctx) => {\n\t\t\t\ttry {\n\t\t\t\t\tawait dispatchExtensionCommand(pi, dependencies, args, ctx);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tctx.ui.notify(`Evo-Pi: ${errorMessage(error)}`, \"error\");\n\t\t\t\t} finally {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait refreshEvoStatusIndicator(dependencies, ctx);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tctx.ui.setStatus(\"evo\", undefined);\n\t\t\t\t\t\tctx.ui.setStatusItems(\"evo\", undefined);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t};\n}\n\nfunction createNodeCliIO(): EvoCliIO {\n\treturn {\n\t\tinteractive: Boolean(process.stdin.isTTY && process.stdout.isTTY),\n\t\twrite: (message) => console.log(message),\n\t\twriteError: (message) => console.error(message),\n\t\tquestion: async (prompt) => {\n\t\t\tconst readline = createInterface({ input: process.stdin, output: process.stdout });\n\t\t\ttry {\n\t\t\t\treturn await readline.question(prompt);\n\t\t\t} finally {\n\t\t\t\treadline.close();\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction requireInteractive(io: EvoCliIO): void {\n\tif (!io.interactive) throw new Error(\"Changing Evo-Pi state requires an interactive terminal\");\n}\n\nasync function confirmLocalMutation(io: EvoCliIO, prompt: string): Promise<boolean> {\n\trequireInteractive(io);\n\tconst answer = await io.question(`${prompt} [y/N] `);\n\treturn /^(?:y|yes)$/i.test(answer.trim());\n}\n\nasync function confirmLocalProposal(io: EvoCliIO, proposal: Proposal): Promise<boolean> {\n\trequireInteractive(io);\n\tif (proposal.kind === \"code\" || proposal.tier === \"T2\") {\n\t\tconst expected = proposal.kind === \"code\" ? proposal.approvalDigest : proposal.diffDigest;\n\t\tconst label = proposal.kind === \"code\" ? \"code approval context digest\" : \"final diff digest\";\n\t\tconst digest = await io.question(`Type the ${label} ${expected}: `);\n\t\tif (digest.trim() !== expected) throw new Error(\"Approval digest did not match\");\n\t\treturn true;\n\t}\n\tconst answer = await io.question(`Approve ${proposal.id} (${proposal.tier}/${proposal.kind})? [y/N] `);\n\treturn /^(?:y|yes)$/i.test(answer.trim());\n}\n\nasync function runLocalPermit(\n\tio: EvoCliIO,\n\tdependencies: EvoCommandDependencies,\n\tid: string,\n): Promise<Proposal | undefined> {\n\trequireInteractive(io);\n\tlet proposal = await dependencies.service.getProposal(id);\n\tlet unrecognizedActions = 0;\n\twhile (true) {\n\t\tio.write(await formatProposalCard(dependencies.paths, proposal));\n\t\tconst strictPermit = proposal.kind === \"code\" || proposal.tier === \"T2\";\n\t\tif (!strictPermit && proposal.tier === \"T0\" && proposal.status === \"pending\") {\n\t\t\tif (!(await confirmLocalProposal(io, proposal))) return undefined;\n\t\t\treturn dependencies.service.approve(id, proposalApproval(proposal));\n\t\t}\n\t\tif (proposal.status !== \"pending\" && proposal.status !== \"deferred\") return proposal;\n\t\tconst prompt =\n\t\t\tproposal.status === \"deferred\"\n\t\t\t\t? !strictPermit && proposal.tier === \"T0\"\n\t\t\t\t\t? \"Action: [o] reopen, [x] reject, [c] close: \"\n\t\t\t\t\t: \"Action: [o] reopen, [q] ask why, [x] reject, [c] close: \"\n\t\t\t\t: `Action: [q] ask why, ${strictPermit ? \"[v] revise, \" : \"\"}[a] approve, [x] reject, [d] defer, [c] close: `;\n\t\tconst action = (await io.question(prompt)).trim().toLowerCase();\n\t\tif (!action || action === \"c\" || action === \"close\") return undefined;\n\t\tconst expected = proposalApproval(proposal);\n\t\tif (action === \"q\" || action === \"ask\") {\n\t\t\tconst question = await io.question(\"Question: \");\n\t\t\tif (!question.trim()) continue;\n\t\t\tconst answer = await askProposalQuestion({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\trunner: dependencies.runner,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\texpected,\n\t\t\t\tquestion,\n\t\t\t\t...(dependencies.cwd ? { cwd: dependencies.cwd } : {}),\n\t\t\t\t...(dependencies.agentDir ? { agentDir: dependencies.agentDir } : {}),\n\t\t\t\t...(dependencies.model ? { model: dependencies.model } : {}),\n\t\t\t});\n\t\t\tio.write(answer.answerMarkdown);\n\t\t\tproposal = await dependencies.service.getProposal(id);\n\t\t\tcontinue;\n\t\t}\n\t\tif ((action === \"v\" || action === \"revise\") && strictPermit && proposal.status === \"pending\") {\n\t\t\tconst instruction = await io.question(\"Revision instruction: \");\n\t\t\tif (!instruction.trim()) continue;\n\t\t\tproposal = await reviseProposalFromInstruction({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\trunner: dependencies.runner,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\texpected,\n\t\t\t\tinstruction,\n\t\t\t\t...(dependencies.cwd ? { cwd: dependencies.cwd } : {}),\n\t\t\t\t...(dependencies.agentDir ? { agentDir: dependencies.agentDir } : {}),\n\t\t\t\t...(dependencies.model ? { model: dependencies.model } : {}),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif ((action === \"a\" || action === \"approve\") && proposal.status === \"pending\") {\n\t\t\tif (!(await confirmLocalProposal(io, proposal))) continue;\n\t\t\treturn dependencies.service.approve(id, expected);\n\t\t}\n\t\tif (action === \"x\" || action === \"reject\") {\n\t\t\tconst reason = await io.question(\"Rejection reason: \");\n\t\t\tif (!reason.trim()) continue;\n\t\t\treturn dependencies.service.reject(id, reason);\n\t\t}\n\t\tif ((action === \"d\" || action === \"defer\") && proposal.status === \"pending\") {\n\t\t\tconst reason = await io.question(\"Deferral reason: \");\n\t\t\tif (!reason.trim()) continue;\n\t\t\tproposal = await dependencies.service.defer(id, reason);\n\t\t\tawait recordPermitDefer({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\trevision: proposal.revision,\n\t\t\t\tdiffDigest: proposal.diffDigest,\n\t\t\t\treason,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif ((action === \"o\" || action === \"reopen\") && proposal.status === \"deferred\") {\n\t\t\tconst reason = \"User reopened deferred proposal\";\n\t\t\tproposal = await dependencies.service.reopen(id, reason);\n\t\t\tawait recordPermitReopen({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\trevision: proposal.revision,\n\t\t\t\tdiffDigest: proposal.diffDigest,\n\t\t\t\treason,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\t// Guard against scripted input that never matches an action key: close\n\t\t// instead of re-prompting forever.\n\t\tunrecognizedActions += 1;\n\t\tif (unrecognizedActions >= 5) return undefined;\n\t\tio.write(`Unrecognized action: ${action}`);\n\t}\n}\n\nfunction parseSessionText(value: string, usage: string): { sessionId: string; text: string } {\n\tconst { first: sessionId, rest: text } = splitFirst(value);\n\trequireValue(sessionId, usage);\n\trequireValue(text, usage);\n\treturn { sessionId, text };\n}\n\nexport async function runEvoCli(args: string[], options: RunEvoCliOptions = {}): Promise<void> {\n\tconst dependencies = createDependencies(options);\n\tconst io = options.io ?? createNodeCliIO();\n\tconst command = args[0]?.toLowerCase() ?? \"help\";\n\tconst rest = args.slice(1).join(\" \").trim();\n\n\tconst presenter: EvoCommandPresenter = {\n\t\tcommandPrefix: \"evo-pi\",\n\t\tcwd: dependencies.cwd ?? process.cwd(),\n\t\tprint: (_customType, content) => io.write(content),\n\t\tnotify: (message) => io.write(message),\n\t\tconfirm: (_title, message) => confirmLocalMutation(io, message),\n\t};\n\tif (await runSharedEvoCommand(dependencies, command, rest, presenter)) return;\n\n\tswitch (command) {\n\t\tcase \"help\":\n\t\tcase \"--help\":\n\t\tcase \"-h\":\n\t\t\tio.write(EVO_CLI_HELP);\n\t\t\treturn;\n\t\tcase \"init\": {\n\t\t\tconst bundle = await dependencies.service.init();\n\t\t\tio.write(`Initialized ${bundle.digest}`);\n\t\t\treturn;\n\t\t}\n\t\tcase \"search\": {\n\t\t\tconst discovery = await dependencies.getDiscoveryService();\n\t\t\tconst results = await discovery.search({\n\t\t\t\t...(rest ? { query: rest } : {}),\n\t\t\t\tlimit: DISCOVERY_SEARCH_LIMIT,\n\t\t\t\tincludeTrustedManifests: true,\n\t\t\t});\n\t\t\tio.write(formatPackSearchResults(results));\n\t\t\treturn;\n\t\t}\n\t\tcase \"install\": {\n\t\t\trequireArgumentCount(args, 2, 3, \"evo-pi install <name> [version]\");\n\t\t\tconst selection = parsePackSelection(args.slice(1).join(\" \"), \"evo-pi install <name> [version]\");\n\t\t\tconst discovery = await dependencies.getDiscoveryService();\n\t\t\tconst inspection = await discovery.inspect(selection.name, selection.version);\n\t\t\tconst preview = createPackCapabilityGrantPreview(\n\t\t\t\tinspection.manifest,\n\t\t\t\tinspection.entry.integrity,\n\t\t\t\tdependencies.model,\n\t\t\t\tdependencies.spawnAgentToolNames ?? DEFAULT_SPAWN_AGENT_TOOL_NAMES,\n\t\t\t);\n\t\t\tio.write(formatPackInspection(inspection));\n\t\t\tconst parentDigest = await requireActiveBundleDigest(dependencies);\n\t\t\tif (Object.keys(preview.grantsByComponent).length > 0) {\n\t\t\t\tio.write(formatPackCapabilityGrantPreview(preview));\n\t\t\t\tif (await packGrantsNeedConfirmation(dependencies.paths)) {\n\t\t\t\t\tif (!(await confirmLocalMutation(io, \"Stage this pack with the exact capability grants shown above?\"))) {\n\t\t\t\t\t\tio.write(\"Pack install cancelled before staging\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tio.write(\"Capability grants auto-approved (grants.approval is 'auto')\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst sandboxDecision = await resolvePackValidationSandbox(dependencies, inspection.manifest, () =>\n\t\t\t\tconfirmLocalMutation(io, PACK_DIRECT_VALIDATION_PROMPT),\n\t\t\t);\n\t\t\tif (sandboxDecision === \"cancelled\") {\n\t\t\t\tio.write(\"Pack install cancelled; no component permission was granted\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet unknownAbiResults: UnknownAbiBuilderCycleResult[] = [];\n\t\t\tconst result = await discovery.install({\n\t\t\t\tname: inspection.entry.name,\n\t\t\t\tversion: inspection.entry.version,\n\t\t\t\texpectedIntegrity: inspection.entry.integrity,\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\tparentDigest,\n\t\t\t\tgrantsByComponent: preview.grantsByComponent,\n\t\t\t\t...(sandboxDecision.sandbox === undefined ? {} : { sandbox: sandboxDecision.sandbox }),\n\t\t\t\tbeforeStage: async ({ preflight }) => {\n\t\t\t\t\tunknownAbiResults = await stageUnknownPackAbis(dependencies, parentDigest, preflight.unknownAbiRequests);\n\t\t\t\t},\n\t\t\t});\n\t\t\tio.write(formatRegistryPackInstallResult(result, unknownAbiResults));\n\t\t\treturn;\n\t\t}\n\t\tcase \"import\": {\n\t\t\trequireArgumentCount(args, 1, 2, \"evo-pi import [directory]\");\n\t\t\tlet packDirectory = (args[1] ?? \"\").trim();\n\t\t\tlet stagingDirectory: string | undefined;\n\t\t\tif (!packDirectory) {\n\t\t\t\tconst templates = await listInstallableWorkflowTemplates(dependencies);\n\t\t\t\tio.write(formatInstallableTemplates(templates));\n\t\t\t\tif (!io.interactive) return;\n\t\t\t\tconst answer = (await io.question(\"选择要安装的模板编号（回车取消）: \")).trim();\n\t\t\t\tif (!answer) return;\n\t\t\t\tconst selected = templates[Number(answer) - 1];\n\t\t\t\tif (!selected) throw new Error(`无效选择: ${answer}`);\n\t\t\t\tif (selected.state === \"active\") {\n\t\t\t\t\tio.write(`${selected.trigger} 已在当前 bundle 中激活`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (selected.state === \"pending\" && selected.pendingProposalId) {\n\t\t\t\t\tconst outcome = await runLocalPermit(io, dependencies, selected.pendingProposalId);\n\t\t\t\t\tio.write(\n\t\t\t\t\t\toutcome ? describePermitOutcome(outcome, selected.trigger) : \"Approval closed without a decision\",\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tstagingDirectory = await writeWorkflowTemplateToStaging(selected.name);\n\t\t\t\tpackDirectory = stagingDirectory;\n\t\t\t}\n\t\t\tconst parentDigest = await requireActiveBundleDigest(dependencies);\n\t\t\tconst preview = await previewPackCapabilityGrants(\n\t\t\t\tpackDirectory,\n\t\t\t\tdependencies.model,\n\t\t\t\tdependencies.spawnAgentToolNames ?? DEFAULT_SPAWN_AGENT_TOOL_NAMES,\n\t\t\t);\n\t\t\tif (Object.keys(preview.grantsByComponent).length > 0) {\n\t\t\t\tio.write(formatPackCapabilityGrantPreview(preview));\n\t\t\t\tif (await packGrantsNeedConfirmation(dependencies.paths)) {\n\t\t\t\t\tif (!(await confirmLocalMutation(io, \"Stage this pack with the exact capability grants shown above?\"))) {\n\t\t\t\t\t\tio.write(\"Pack import cancelled before staging\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tio.write(\"Capability grants auto-approved (grants.approval is 'auto')\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst sandboxDecision = await resolvePackValidationSandbox(dependencies, preview.manifest, () =>\n\t\t\t\tconfirmLocalMutation(io, PACK_DIRECT_VALIDATION_PROMPT),\n\t\t\t);\n\t\t\tif (sandboxDecision === \"cancelled\") {\n\t\t\t\tio.write(\"Pack import cancelled; no component permission was granted\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet unknownAbiResults: UnknownAbiBuilderCycleResult[] = [];\n\t\t\tconst result = await stagePackImport(\n\t\t\t\tdependencies,\n\t\t\t\tpackDirectory,\n\t\t\t\tpreview.integrity,\n\t\t\t\tpreview.grantsByComponent,\n\t\t\t\tparentDigest,\n\t\t\t\tasync (preflight) => {\n\t\t\t\t\tunknownAbiResults = await stageUnknownPackAbis(dependencies, parentDigest, preflight.unknownAbiRequests);\n\t\t\t\t},\n\t\t\t\tsandboxDecision.sandbox,\n\t\t\t);\n\t\t\tio.write(formatPackImportResult(result, unknownAbiResults));\n\t\t\tif (stagingDirectory) await rm(stagingDirectory, { recursive: true, force: true }).catch(() => undefined);\n\t\t\t// Continue straight into approval so installing a pack is one flow.\n\t\t\tif (io.interactive) {\n\t\t\t\tfor (const component of result.importedComponents) {\n\t\t\t\t\tconst outcome = await runLocalPermit(io, dependencies, component.proposal.id);\n\t\t\t\t\tio.write(\n\t\t\t\t\t\toutcome ? describePermitOutcome(outcome, component.trigger) : \"Approval closed without a decision\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (result.proposal) {\n\t\t\t\t\tconst outcome = await runLocalPermit(io, dependencies, result.proposal.id);\n\t\t\t\t\tio.write(outcome ? describePermitOutcome(outcome) : \"Approval closed without a decision\");\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tcase \"export\": {\n\t\t\trequireArgumentCount(args, 2, 4, \"evo-pi export <directory> [name] [version]\");\n\t\t\tio.write(\n\t\t\t\tformatPackExportResult(\n\t\t\t\t\tawait exportActiveBundle(\n\t\t\t\t\t\tdependencies,\n\t\t\t\t\t\trequireValue(args[1] ?? \"\", \"evo-pi export <directory> [name] [version]\"),\n\t\t\t\t\t\targs[2],\n\t\t\t\t\t\targs[3],\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tcase \"report\": {\n\t\t\tconst report = await runReport(modelRunOptions(dependencies));\n\t\t\tio.write(`${report.observationsMarkdown}\\n\\nSaved: ${report.file}`);\n\t\t\treturn;\n\t\t}\n\t\tcase \"inspect\":\n\t\t\tio.write(formatEvolutionRuns(await inspectBackgroundEvolutions(dependencies.paths), rest || undefined));\n\t\t\treturn;\n\t\tcase \"retry\": {\n\t\t\tconst { id, from } = parseRetryArgs(rest, \"evo-pi-admin retry <run-id> [--from research|building|validating]\");\n\t\t\tconst resolved = from ?? (await resolveRetryFrom(dependencies.paths, id));\n\t\t\tif (resolved === \"research\") {\n\t\t\t\tconst source = await readEvolutionRun(dependencies.paths, id);\n\t\t\t\tconst run = await startBackgroundEvolution({\n\t\t\t\t\tpaths: dependencies.paths,\n\t\t\t\t\tcwd: dependencies.cwd ?? process.cwd(),\n\t\t\t\t\t...(source.request ? { request: source.request } : {}),\n\t\t\t\t\tretryOfRunId: id,\n\t\t\t\t});\n\t\t\t\tio.write(`Evolution task ${run.id} restarted from research in the background`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (resolved === \"building\") {\n\t\t\t\tconst run = await startBackgroundBuildingResume({\n\t\t\t\t\tpaths: dependencies.paths,\n\t\t\t\t\tsourceRunId: id,\n\t\t\t\t\tcwd: dependencies.cwd ?? process.cwd(),\n\t\t\t\t});\n\t\t\t\tio.write(`Evolution task ${run.id} resumed at the builder in the background`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst sandboxAvailable = await canUseEvoComponentSandbox();\n\t\t\tconst allowDirect =\n\t\t\t\t!sandboxAvailable &&\n\t\t\t\t(await confirmLocalMutation(\n\t\t\t\t\tio,\n\t\t\t\t\t`OS sandbox unavailable. Run the built component from ${id} directly once for validation with your user permissions?`,\n\t\t\t\t));\n\t\t\tif (!sandboxAvailable && !allowDirect) {\n\t\t\t\tio.write(\"Retry cancelled; no component permission was granted\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst retried = await retryEvolutionFromValidation({\n\t\t\t\tpaths: dependencies.paths,\n\t\t\t\tsourceRunId: id,\n\t\t\t\tcwd: dependencies.cwd ?? process.cwd(),\n\t\t\t\t...(allowDirect ? { sandbox: false } : {}),\n\t\t\t});\n\t\t\tio.write(\n\t\t\t\tretried.status === \"awaiting-canary-approval\"\n\t\t\t\t\t? `Evolution task ${retried.runId} is ready for Canary review`\n\t\t\t\t\t: `Evolution task ${retried.runId} is waiting for its frozen evidence strategy`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tcase \"schedule\": {\n\t\t\tif (!rest) {\n\t\t\t\tio.write(formatScheduleStatus(await getScheduleStatus(dependencies.paths)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst cadence = parseScheduleCadence(rest);\n\t\t\tif (!cadence) throw new Error(`Usage: ${SCHEDULE_USAGE.replace(\"/evo\", \"evo-pi\")}`);\n\t\t\tconst updated = await writeScheduleConfig(dependencies.paths, cadence);\n\t\t\tio.write(`Evo-Pi schedule updated — ${describeScheduleCadence(updated)}`);\n\t\t\treturn;\n\t\t}\n\t\tcase \"note\": {\n\t\t\tconst value = parseSessionText(rest, \"evo-pi note <session-id> <text>\");\n\t\t\tio.write((await dependencies.service.note(value.sessionId, value.text)).path);\n\t\t\treturn;\n\t\t}\n\t\tcase \"request\": {\n\t\t\tconst value = parseSessionText(rest, \"evo-pi request <session-id> <text>\");\n\t\t\tio.write((await dependencies.service.request(value.sessionId, value.text)).path);\n\t\t\treturn;\n\t\t}\n\t\tcase \"preference\": {\n\t\t\tconst value = parseSessionText(rest, \"evo-pi preference <session-id> <text>\");\n\t\t\tio.write(\n\t\t\t\t`Activated durable preference from ${(await dependencies.service.preference(value.sessionId, value.text)).fileName}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tcase \"forget\": {\n\t\t\tconst value = parseSessionText(rest, \"evo-pi forget <session-id> <preference-id>\");\n\t\t\tio.write((await dependencies.service.forgetPreference(value.sessionId, value.text)).path);\n\t\t\treturn;\n\t\t}\n\t\tcase \"permit\": {\n\t\t\tconst id = requireValue(args[1] ?? \"\", \"evo-pi permit <proposal-id>\");\n\t\t\tconst result = await runLocalPermit(io, dependencies, id);\n\t\t\tio.write(result ? `Proposal ${id} is ${result.status}` : \"Approval closed without a decision\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"rollback\": {\n\t\t\tconst hasDigest = /^[a-f0-9]{64}$/.test(args[1] ?? \"\");\n\t\t\tconst reason =\n\t\t\t\targs\n\t\t\t\t\t.slice(hasDigest ? 2 : 1)\n\t\t\t\t\t.join(\" \")\n\t\t\t\t\t.trim() || \"User requested rollback\";\n\t\t\tconst target = hasDigest ? `to ${args[1]}` : \"the active trial or stable bundle\";\n\t\t\tif (!(await confirmLocalMutation(io, `Roll back ${target}?`))) {\n\t\t\t\tio.write(\"Rollback cancelled\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = await dependencies.service.rollback(hasDigest ? args[1] : undefined, reason);\n\t\t\tio.write(`Rolled back ${result.from} → ${result.to}`);\n\t\t\treturn;\n\t\t}\n\t\tcase \"keep\": {\n\t\t\trequireInteractive(io);\n\t\t\tconst componentTrial = await findDirectKeepableTrial(dependencies);\n\t\t\tif (componentTrial) {\n\t\t\t\tif (\n\t\t\t\t\t!(await confirmLocalMutation(\n\t\t\t\t\t\tio,\n\t\t\t\t\t\t\"立即正式上线这个已通过可执行验证的组件并结束 Canary？（rollback 可回滚）\",\n\t\t\t\t\t))\n\t\t\t\t) {\n\t\t\t\t\tio.write(\"Trial remains active\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst kept = await dependencies.service.directKeepComponentTrial(\n\t\t\t\t\tcomponentTrial.id,\n\t\t\t\t\trest || \"Human directly kept the validated component trial via keep\",\n\t\t\t\t);\n\t\t\t\tio.write(`Kept ${kept.id}`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst retrospective = await runRetrospective(modelRunOptions(dependencies));\n\t\t\tio.write(retrospective.retrospectiveMarkdown);\n\t\t\tconst answer = await io.question(`Keep trial ${retrospective.proposal.id}? [y/N] `);\n\t\t\tif (!/^(?:y|yes)$/i.test(answer.trim())) {\n\t\t\t\tio.write(\"Trial remains active\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tio.write(`Kept ${(await dependencies.service.keep(rest || \"User kept trial after retrospective\")).id}`);\n\t\t\treturn;\n\t\t}\n\t\tcase \"verify\": {\n\t\t\tconst explicitRunId = args[1]?.startsWith(\"r-\") ? args[1] : undefined;\n\t\t\tconst decision = parseVerificationDecisionWord((explicitRunId ? args[2] : args[1])?.trim().toLowerCase());\n\t\t\tconst runId = explicitRunId ?? (await soleRunAwaitingVerification(dependencies));\n\t\t\tawait decideVerificationRun(dependencies, runId, decision);\n\t\t\tio.write(describeVerificationDecision(runId, decision));\n\t\t\treturn;\n\t\t}\n\t\tdefault:\n\t\t\tio.writeError(`Unknown command: ${command}`);\n\t\t\tthrow new Error(`Unknown command: ${command}`);\n\t}\n}\n"]}