['runtime does NOT mount inside retired host drawer',!runtime.includes('attachPanelToDrawer')&&!runtime.includes('wolai-page-ai-drawer')],
['runtime does NOT toggle retired host iframe visibility',!runtime.includes('setOpenHubVisible')&&!runtime.includes('data-page-ai-openhub-frame-wrap')],
['runtime has no retired host provider tab inside Pi Lab',!runtime.includes('data-page-ai-pi-lab-openhub-tab')],
['runtime has context strip chips',runtime.includes('data-page-ai-pi-lab-context-strip')&&runtime.includes('data-page-ai-pi-lab-current-page')&&runtime.includes('data-page-ai-pi-lab-allowed-roots')&&runtime.includes('data-page-ai-pi-lab-lightrag')],
['runtime settings gear opens AI management page',runtime.includes('data-page-ai-pi-lab-open-settings title="AI 管理"')&&runtime.includes("window.location.assign('/user/ai#ai-admin-access')")],
['runtime keeps only meaningful top commandbar buttons',runtime.includes('data-page-ai-pi-lab-new')&&runtime.includes('data-page-ai-pi-lab-history')&&runtime.includes('data-page-ai-pi-lab-btn-clear')&&runtime.includes('data-page-ai-pi-lab-open-settings')],
['runtime removes no-op top commandbar buttons',!runtime.includes('data-page-ai-pi-lab-open title=')&&!runtime.includes('data-page-ai-pi-lab-toggle-artifacts')&&!runtime.includes('data-page-ai-pi-lab-clock')&&!runtime.includes('data-page-ai-pi-lab-notify')],
['runtime has left history drawer markers',runtime.includes('data-page-ai-pi-lab-history-layer')&&runtime.includes('wolai-page-ai-pi-lab-history-scrim')&&runtime.includes('历史对话')],
['runtime history drawer supports refresh/delete/export/clear',runtime.includes('data-page-ai-pi-lab-history-refresh')&&runtime.includes('data-page-ai-pi-lab-history-delete')&&runtime.includes('data-page-ai-pi-lab-history-export')&&runtime.includes('data-page-ai-pi-lab-history-clear')],
['runtime does not expose manual Pi runtime start button',!runtime.includes('data-page-ai-pi-lab-btn-start')&&!runtime.includes('启动 Pi runtime')&&!runtime.includes('预启动 Pi 会话')],
['runtime auto starts current page Pi session when drawer opens',showPiLabBody.includes('checkStatus().then(function ()')&&showPiLabBody.includes('startRuntime().then')],
['runtime has native model controls',runtime.includes('data-page-ai-pi-lab-model-provider')&&runtime.includes('data-page-ai-pi-lab-model-id')&&runtime.includes('data-page-ai-pi-lab-model-custom')],
['runtime applies model controls through Pi RPC configure endpoint',runtime.includes("CONFIGURE: '/api/page-ai/pi/configure'")&&runtime.includes('applyModelConfigToRuntime')&&runtime.includes('pendingModelConfigApply')],
['runtime collapses secondary right rail sections',runtime.includes('<details class="wolai-page-ai-pi-lab-rail-section"')&&runtime.includes('data-page-ai-pi-lab-receipts-section')],
['runtime has diagnostics collapsed by default',runtime.includes('<details class="wolai-page-ai-pi-lab-diagnostics"')&&!runtime.includes('<details class="wolai-page-ai-pi-lab-diagnostics" open')],
['runtime has Pi diagnostics endpoint constant',runtime.includes("DIAGNOSTICS: '/api/page-ai/pi/diagnostics'")],
['runtime exposes official Pi diagnostics actions in collapsed panel',runtime.includes('data-page-ai-pi-lab-diagnostic-command="doctor"')&&runtime.includes('data-page-ai-pi-lab-diagnostic-command="context-preview"')&&runtime.includes('data-page-ai-pi-lab-diagnostic-command="list"')&&runtime.includes('data-page-ai-pi-lab-diagnostic-command="update-index"')&&runtime.includes('data-page-ai-pi-lab-diagnostic-command="search"')&&runtime.includes('data-page-ai-pi-lab-diagnostic-command="info"')],
['runtime renders Pi diagnostics output separately from status text',runtime.includes('data-page-ai-pi-lab-diagnostic-output')&&runtime.includes('setDiagnosticOutput')],
['runtime shows default model in header label',runtime.includes('updateModelLabel')&&runtime.includes('defaultModelProvider')&&runtime.includes('defaultModelId')],
['runtime consumes defaultModelProvider from backend status',runtime.includes('data.defaultModelProvider')],
['runtime consumes defaultModelId from backend status',runtime.includes('data.defaultModelId')],
['runtime reuses the existing floating AI slot for Pi Lab launcher',runtime.includes('[data-mnote-action="open-page-ai-pi-lab"], [data-testid="wolai-floating-ai"]')],
['layout maps old floating AI slot to Pi Lab',layout.includes('data-testid="wolai-floating-ai"')&&layout.includes('data-mnote-action="open-page-ai-pi-lab"')&&layout.includes('{"\\u{03c0}"}')],
['layout no longer exposes legacy Page AI/OpenHub action from floating button',!layout.includes('data-mnote-action="open-page-ai"><span')],
['route has configure endpoint for Pi RPC model/thinking changes',route.includes('pub async fn configure')&&route.includes('"type": "set_model"')&&route.includes('"type": "set_thinking_level"')],
['route has send endpoint',route.includes('pub async fn send')],
['route has abort endpoint',route.includes('pub async fn abort')],
['route has events SSE endpoint',route.includes('pub async fn events')],
['route has bootstrap endpoint (legacy)',route.includes('pub async fn bootstrap')],
['route has tool_call endpoint',route.includes('pub async fn tool_call')],
['route has internal tool_call_bridge endpoint',route.includes('pub async fn tool_call_bridge')],
['route uses AppConfig enable_page_ai_pi_lab instead of direct env gate',route.includes('state.config().enable_page_ai_pi_lab')&&!route.includes('std::env::var("MNOTE_PAGE_AI_PI_LAB")')],
['route returns enabled=false when not enabled',route.includes('"enabled": false')],
['route marks independent native drawer ui mode',route.includes('independent_mnote_native_drawer')],
['route does not return openHubDefaultPreserved marker',!route.includes('openHubDefaultPreserved')],
['route has default model provider constant PI_LAB_DEFAULT_MODEL_PROVIDER=omniroute',route.includes('PI_LAB_DEFAULT_MODEL_PROVIDER')&&route.includes('omniroute')],
['route has default model id constant PI_LAB_DEFAULT_MODEL_ID=gpt-5.4-mini',route.includes('PI_LAB_DEFAULT_MODEL_ID')&&route.includes('gpt-5.4-mini')],
['route checks OmniRoute tool_calling capability before real runtime start',route.includes('ensure_session_model_supports_tools')&&route.includes('page_ai_pi_model_tools_unsupported')&&route.includes('tool_calling')],
['runtime replays deferred model config after streaming',runtime.includes('maybeApplyPendingModelConfig')&&runtime.includes('pendingModelConfigApply')],
['route writes Pi Rust models apiKey as bare env var name, not shell literal',route.includes('"apiKey": "OPENAI_API_KEY"')&&!route.includes('"apiKey": "$OPENAI_API_KEY"')],
['route derives OmniRoute tool support from verified model capability',route.includes('"supportsTools": supports_tools')&&route.includes('omniroute_model_tool_calling_capability')],
['route sends Pi Rust directly to configured OmniRoute base URL',route.includes('"baseUrl": omniroute_base_url()')&&!route.includes('omniroute_proxy_chat_completions')&&!routesMod.includes('/api/page-ai/pi/omniroute-proxy/')],
['route defaults to Pi Rust runtime implementation',route.includes('PI_LAB_RUNTIME_IMPL_RUST')&&route.includes('"pi-rust"')&&route.includes('MNOTE_PAGE_AI_PI_RUST_BIN')],
['route removes TS Pi fallback',!route.includes('PI_LAB_RUNTIME_IMPL_TS')&&!route.includes('MNOTE_PAGE_AI_PI_TS_BIN')&&!route.includes('legacy-ts')&&!route.includes('pi-ts')],
['route tool_receipt.write is not a silent no-op',route.includes('"requestedReceipt"')&&route.includes('execute_tool 统一写入')],
['route has bridge token header and non-serialized session token',route.includes('HEADER_PI_LAB_BRIDGE_TOKEN')&&route.includes('x-mnote-pi-lab-bridge-token')&&route.includes('skip_serializing')],
['route generates bridge token from OS randomness',route.includes('generate_bridge_token')&&route.includes('/dev/urandom')&&!route.includes('bridge_token: generate_id("pi_bridge")')],
['route does not write obsolete private MCP bridge config',!route.includes('write_session_mcp_bridge_config')&&!route.includes('mnote.pi.mcp-bridge.v1')&&!route.includes('mnote-bridge.json')],
['route never writes bridge token into generated extension source',!route.includes('const BRIDGE_TOKEN = {bridge_token}')],
['route loads official MNote Pi package extension',route.includes('mnote_pi_extension_path')&&route.includes('packages/pi-mnote/extensions/mnote-bridge.ts')],
['MNote Pi bridge reads session and base URL from private context fallback',mnotePiExtension.includes('bridgeSessionId')&&mnotePiExtension.includes('bridgeBaseUrl')&&mnotePiExtension.includes('readContextFileSnapshot')],
['MNote Pi bridge does not trust generic runtime session id env',mnotePiExtension.includes('PI_MNOTE_BRIDGE_SESSION_ID')&&mnotePiExtension.includes('MNOTE_PI_BRIDGE_SESSION_ID')&&!mnotePiExtension.includes('MNOTE_PI_LAB_SESSION_ID')],
['MNote Pi bridge allows native tools when context file proves Pi Rust',mnotePiExtension.includes('isPiRustNativeRuntime')&&mnotePiExtension.includes('Boolean(CONTEXT_FILE)')],
['route starts Pi with MNote tool allowlist',route.includes('--tools')&&route.includes('mnoteToolNames')&&route.includes('pi_lab_extension_tool_names')],
['route keeps Pi Rust official prompt templates and @file context enabled',!route.includes('.arg("--no-prompt-templates")')&&!route.includes('.arg("--no-context-files")')],
['route exposes controlled Pi Rust RPC command wrapper',route.includes('PI_LAB_SCHEMA_RPC_COMMAND')&&route.includes('build_pi_rpc_command')&&route.includes('abort_bash')&&route.includes('get_available_models')],
['route exposes controlled Pi Rust diagnostics wrapper',route.includes('PI_LAB_SCHEMA_DIAGNOSTICS')&&route.includes('pub async fn diagnostics')&&route.includes('build_pi_diagnostics_args')&&routesMod.includes('/api/page-ai/pi/diagnostics')],
['route only allows read-only or cache-only Pi diagnostics commands',route.includes('doctor/context-preview/list/info/search/update-index')&&route.includes('mutatesCache')&&route.includes('page_ai_pi_lab_diagnostics_command_not_allowed')],
['route rejects raw Pi diagnostics args passthrough',route.includes('page_ai_pi_lab_diagnostics_raw_args_not_allowed')&&route.includes('不接受裸 args 透传')],
['route passes official Pi Rust advanced runtime flags',route.includes('--extension-policy')&&route.includes('--repair-policy')&&route.includes('--session-durability')&&route.includes('--request-timeout')&&route.includes('--max-tool-iterations')&&route.includes('--hide-cwd-in-prompt')],
['route exposes effective Pi advanced runtime config',route.includes('advancedRuntime')&&route.includes('pi_lab_effective_advanced_runtime_config')&&route.includes('MNOTE_PAGE_AI_PI_EXTENSION_POLICY')&&route.includes('MNOTE_PAGE_AI_PI_REPAIR_POLICY')],
['route binds staged MNote bridge to absolute context path and private embedded snapshot',route.includes('bind_mnote_bridge_context_path')&&route.includes('DEFAULT_CONTEXT_FILE')&&route.includes('EMBEDDED_CONTEXT')],
['route sends per-prompt Pi Rust context through extension input hook',route.includes('mnote.pi.context.v1')&&route.includes('write_pi_mnote_context_snapshot')&&route.includes('fn pi_mnote_input_context_prefix')&&route.includes('format!(\"{input_context_prefix}{message}\")')],
['route resolves local file tools against session rootUri when params omit rootUri',route.includes('session: Option<&PiLabSession>')&&route.includes('session.and_then(|session| session.root_uri.clone())')],
['route exposes Pi JSONL replay messages from session tree',route.includes('"messages": replay_messages')&&route.includes('build_pi_replay_messages(&entries)')&&route.includes('pi_lab_active_path_entries')],
['route exposes Pi Rust builtins in full_access instead of replacing them with MNote file tools',route.includes('fn pi_lab_enabled_builtin_tools')&&route.includes('Some("full_access")')&&route.includes('pi_lab_managed_builtin_tools()')&&route.includes('Some("auto_edit")')&&route.includes('Some("plan")')&&route.includes('PI_LAB_MANAGED_BUILTIN_TOOLS')],
['route reports warmup runtime without exposing it as current page session',route.includes('"warmupRunning"')&&route.includes('"warmupSessionId"')&&route.includes('"warmupProcessCount"')&&route.includes('is_pi_lab_warmup_session_id(&session.session_id)')],
['runtime send path distinguishes warm runtime binding from cold start',runtime.includes('正在绑定当前页 Pi 会话,完成后自动发送')&&runtime.includes('Pi Rust 正在启动,启动后自动发送')&&runtime.includes('piLabState.warmupRunning')],
['dev:hot warmup defaults to no real model prompt',devHot.includes('MNOTE_PAGE_AI_PI_WARMUP_SEND')&&devHot.includes('?? "0"')],
['runtime opens Pi drawer by prestarting current page session',showPiLabBody.includes('startRuntime().then')&&showPiLabBody.includes('syncRuntimeState()')],
['runtime keeps selected model before start/configure',runtime.includes('ensurePiToolCapableModel')&&!runtime.includes('isPiToolUnsupportedModel')&&runtime.includes('gpt-5.4-mini')&&runtime.includes('freefirst')],
['@mnote/pi MCP extension uses official synchronous child_process bridge',mnotePiMcpExtension.includes('execFileSync')&&mnotePiMcpExtension.includes('client.mjs')&&mnotePiMcpExtension.includes('transport: "pi-rust-sync-client"')&&!mnotePiMcpExtension.includes('/api/page-ai/pi/mcp-call-bridge')&&!mnotePiMcpExtension.includes('fetch(')],
['@mnote/pi MCP extension does not depend on private session token config',!mnotePiMcpExtension.includes('mnote-bridge.json')&&!mnotePiMcpExtension.includes('BRIDGE_TOKEN')&&!mnotePiMcpExtension.includes('SESSION_ID')],
['@mnote/pi MCP client implements protocol handshake and tool calls',mnotePiMcpClient.includes('"initialize"')&&mnotePiMcpClient.includes('"tools/list"')&&mnotePiMcpClient.includes('"tools/call"')],
['@mnote/pi MCP client supports stdio and streamable HTTP',mnotePiMcpClient.includes('spawn(')&&mnotePiMcpClient.includes('text/event-stream')],
['route loads built-in Pi Rust MCP extension',route.includes('mnote_pi_mcp_extension_path')&&route.includes('packages/pi-mnote/extensions/mnote-mcp/index.ts')],
['route stages per-session MCP config next to built-in extension',route.includes('stage_project_mcp_config_for_extension')&&route.includes('extension_dir.join(".pi")')&&route.includes('mcp.json')],
['route does not pass unsupported Pi Rust --mcp-config flag',!route.includes('.arg("--mcp-config")')],
['route does not globally disable Pi Rust extension capability policy',!route.includes('command.env("PI_EXTENSION_ALLOW_DANGEROUS"')&&!route.includes('pi_tool_names.push("bash".into())')],
['route narrowly enables Pi Rust sync exec only for configured MCP sessions',route.includes('if pi_lab_mcp_enabled(&session)')&&route.includes('command.env("PIJS_ALLOW_UNSAFE_SYNC_EXEC", "1")')],
['mod.rs mounts pi session history delete and clear routes',routesMod.includes('get(page_ai_pi::list_sessions).delete(page_ai_pi::clear_sessions)')&&routesMod.includes('.delete(page_ai_pi::delete_session_history)')],
['syncRuntimeState syncs model/thinking from API.STATE',runtime.includes('data.modelProvider')&&runtime.includes('data.thinkingLevel')],
['syncRuntimeState called after showPiLab drawer open',runtime.indexOf('showPiLab')<runtime.indexOf('syncRuntimeState()')||runtime.includes('showPiLab')&&runtime.includes('syncRuntimeState')],
['syncRuntimeState called after startRuntime success (in success .then)',runtime.includes('syncRuntimeState();')&&runtime.includes('Pi runtime ready')&&runtime.includes('return data;')],
['syncRuntimeState called after sendPrompt accepted (in sendPrompt .then)',runtime.includes('syncRuntimeState();')&&runtime.includes('data.accepted')&&runtime.includes('Pi Lab rejected prompt')],
['syncRuntimeState called after abortPrompt (in abortPrompt function)',runtime.includes('syncRuntimeState();')&&runtime.includes('abortPrompt')&&runtime.includes('setState(STATE_ABORTED)')],
['syncRuntimeState called in SSE connected event',runtime.includes("'connected'")&&runtime.includes('syncRuntimeState()')],
['syncRuntimeState called in runtime_started event',runtime.includes("runtime_started")&&runtime.includes('syncRuntimeState()')],
['syncRuntimeState called in runtime_aborted event',runtime.includes("runtime_aborted")&&runtime.includes('syncRuntimeState')],
['syncRuntimeState called from checkStatus when session running',runtime.includes("connectEventSource(data.session.sessionId)")&&runtime.includes("syncRuntimeState()")],
['syncRuntimeState does NOT use setInterval',!runtime.includes('setInterval(syncRuntimeState')&&!runtime.includes("setInterval(syncRuntimeState")],
// === Runtime JS: compact function ===
['runtime has triggerCompact function',runtime.includes('function triggerCompact')],