feat: purge legacy agent hosts and land vault Chrome extension path

Retire ACP/Hermes/OpenCode surfaces and rename hermes_tools to
mnote_agent_tools so Page AI stays on Pi Lab only. Add Chrome vault
extension + extension token route, pre-release purge design, and soft-retire
legacy smokes for the small-group production cut.
This commit is contained in:
Agent Board
2026-07-25 14:25:37 +08:00
parent bc6f8488ee
commit 262e66b02e
137 changed files with 9018 additions and 46049 deletions
+83 -205
View File
@@ -241,25 +241,12 @@ mod tests {
include_str!("../../../browser/sidebar-workspace-runtime.js");
const SIDEBAR_PAGE_TREE_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-tree-runtime.js");
// Browser Page AI runtime is served as a module; keep this include explicit so
// resume/journal contract checks inspect the actual shipped JS.
// Page AI 产品面:facadePi 入口桥接)+ Pi Lab runtime。Hermes/OpenCode 子模块已物理删除。
const SIDEBAR_PAGE_AI_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-runtime.js");
const SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-pi-lab-runtime.js");
const MNOTE_UI_RUNTIME_JS: &str = include_str!("../../../browser/mnote-ui-runtime.js");
const SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-markdown-runtime.js");
const SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-render-runtime.js");
const SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-permission-runtime.js");
const SIDEBAR_PAGE_AI_PROFILE_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-profile-runtime.js");
const SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-session-runtime.js");
const SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-skill-runtime.js");
const SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-target-runtime.js");
const SIDEBAR_PAGE_SETTINGS_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-settings-runtime.js");
const FILETREE_CONTEXT_MENU_RUNTIME_JS: &str =
@@ -341,7 +328,12 @@ mod tests {
assert!(SIDEBAR_PAGE_TREE_RUNTIME_JS.contains("application/x-mnote-page-tree-node"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("dragstart"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("drop"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-drop-feedback"));
// drop feedback 已外置到 filetree-dnd / page-tree runtimetree 仍委托 drop 入口
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("data-drop-feedback")
|| FILETREE_DND_RUNTIME_JS.contains("data-drop-feedback")
|| SIDEBAR_PAGE_TREE_RUNTIME_JS.contains("data-drop-feedback")
);
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("action: 'move'"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("sidebar-file-tree-root"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree.filetree.open"));
@@ -623,7 +615,7 @@ mod tests {
#[test]
fn page_layout_adds_dev_hot_cache_buster_to_browser_runtime_scripts() {
let _guard = crate::test_support::hermes_env_lock()
let _guard = crate::test_support::agent_env_lock()
.lock()
.expect("env lock");
std::env::set_var("MNOTE_WEB_DEV_HOT_RELOAD", "1");
@@ -745,6 +737,8 @@ mod tests {
< html.find(r#"data-testid="wolai-floating-ai""#)
);
assert!(html.contains(r#"data-mnote-action="open-page-ai-pi-lab""#));
assert!(html.contains(r#"aria-label="Pi Lab""#));
assert!(html.contains(r#"title="打开 Pi Lab""#));
assert!(!html.contains(r#"data-mnote-action="open-page-ai""#));
assert!(!html.contains(r#"data-mnote-action="open-knowledge-rag-settings""#));
assert!(html.contains(r#"data-icon="travel_explore""#));
@@ -755,6 +749,33 @@ mod tests {
assert!(!html.contains(r#"data-mnote-action="toggle-ocr-tasks""#));
}
#[test]
fn page_ai_product_entry_is_pi_lab_only() {
// 产品入口:浮钮 + tree click + openPageAiDrawer 均只打开 Pi Lab。
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("产品唯一入口:Pi Lab"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("mnote:pi-lab-show"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function openPageAiDrawer"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function installPageAiDelegates"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function updatePageAiTriggerState"));
assert!(SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("createSidebarPageAiPiLabRuntime"));
assert!(SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("mnote:pi-lab-show"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("产品面唯一 Page AIPi Lab"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnote:pi-lab-show"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains(r#"data-mnote-action="open-page-ai-pi-lab""#));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("sidebarPageAi.installPageAiDelegates()"));
// Hermes/OpenCode drawer 与子模块已物理删除
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiOpencodeHostEnabled"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiSessionRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiRenderRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("/api/hermes/"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("切换到 Hermes"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("切换到 OpenCode"));
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("切换到 Hermes"));
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("切换到 OpenCode"));
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("pageAiOpenHermesSettings"));
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("pageAiSetHideHermesBuiltinSkills"));
}
#[test]
fn sidebar_settings_runtime_routes_index_and_ocr_to_knowledge_rag_settings() {
assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("mnote-knowledge-rag-settings-popover"));
@@ -879,196 +900,43 @@ mod tests {
}
#[test]
fn page_ai_fast_path_is_not_local_first_main_path() {
assert!(
SIDEBAR_PAGE_AI_RUNTIME_JS
.contains("if (currentSourceKind() === 'local_folder') return false;"),
"local-first 页面 AI 不应继续加厚 page-ai fast-path;本地编辑应走受控文件工具"
);
fn page_ai_facade_is_pi_lab_bridge_only() {
// facade 只桥接 Pi Lab;不承载 Hermes session / ACP / OpenCode host。
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("export function createSidebarPageAiRuntime"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("window.createSidebarPageAiPiLabRuntime"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("mnote:pi-lab-show"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("mnote:pi-lab-hide"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("window.__mnoteSidebarPageAiRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiOpencode"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiAcpRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("reasonix"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("/api/page-ai/sessions"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("/api/hermes/"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiMarkdownRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiPermissionRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiProfileRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiRenderRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiSessionRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiSkillRuntime"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiTargetRuntime"));
}
#[test]
fn page_ai_local_source_passes_file_reference_fields_to_agent_run() {
fn page_ai_pi_lab_runtime_is_product_host() {
assert!(SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("function createSidebarPageAiPiLabRuntime"));
assert!(SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("mnote:pi-lab-show"));
assert!(SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("mnote:pi-lab-hide"));
assert!(SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("/api/page-ai/pi/"));
assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("function currentRootUri()"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("sourceKind: currentSourceKind()"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("rootUri: currentRootUri()"));
assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
.contains("if (sourceKind && !payload.sourceKind) payload.sourceKind = sourceKind"));
assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
.contains("if (rootUri && !payload.rootUri) payload.rootUri = rootUri"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageContext: scopedContext.pageContext"));
assert!(
SIDEBAR_PAGE_AI_RUNTIME_JS
.contains("if (currentSourceKind() === 'local_folder') return false;"),
"local source 不应进入 page-ai fast-path,后端会把 run 收敛为文件引用 scope"
);
assert!(
!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiEnrichOcrContextRefs"),
"Page AI 目标包不应保留已退役 OCR sidecar context enrichment"
);
assert!(
!SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("ocrRootRelativePath"),
"Page AI target runtime 不应再注入旧 OCR sidecar 路径"
);
}
#[test]
fn page_ai_agent_target_picker_contract_is_visible_and_serialized() {
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiRenderRuntime"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS
.contains("export function createSidebarPageAiRenderRuntime"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-target-button"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-target-popover"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-target-option"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-target-chip"));
assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("primaryTargetId"));
assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("targets: ["));
assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("policy: {"));
}
#[test]
fn page_ai_uses_backend_acp_session_runtime_store() {
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiSessionRuntime"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiSkillRuntime"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiTargetRuntime"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function renderPageAiControls"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function ensurePageAiDrawer"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function renderPageAiConversation"));
assert!(SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS
.contains("export function createSidebarPageAiSkillRuntime"));
assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS
.contains("export function createSidebarPageAiTargetRuntime"));
assert!(
SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("function currentPageAiOpenEditorsSnapshot")
);
assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("mnote.agent_target_package.v1"));
assert!(SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS.contains("function pageAiSkillSourceOptions"));
assert!(SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS.contains("function pageAiSkillPreferenceTable"));
assert!(SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS.contains("ai.agent.reasonix.memory_enabled"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiLoadBackendSessions"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("/api/page-ai/sessions?"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("params.set('source', 'acp')"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains(
"var draftSessions = pageAiNormalizeArray(pageUiState.pageAiSessions).filter"
));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS
.contains("pageAiDedupeSessions(backendSessions.concat(draftSessions))"));
assert!(
SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiLoadBackendSessionDetail")
);
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiSearchBackendSessions"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS
.contains("function pageAiDeleteSelectedBackendSessions"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiCheckActiveRun"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("/api/page-ai/sessions/"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("/active-run?"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-rename"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-delete"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-select"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-delete-selected"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-resume"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-search"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function pageAiRenderThoughtGroup"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-thought-card"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-collapse-card"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiUsageSummary"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("usage.updated"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("thought.delta"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("permission.requested"));
assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS
.contains("data-page-ai-permission-action=\"allow\""));
assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS
.contains("data-page-ai-permission-action=\"deny\""));
assert!(
SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS.contains("function pageAiHidePermissionDialog")
);
assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS.contains("if (!message.resolved)"));
assert!(
!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("(item.resolved ? ' disabled' : '')"),
"已决 ACP permission 事件不能继续展示假审批按钮"
);
assert!(
SIDEBAR_PAGE_AI_RUNTIME_JS.contains("session.info.updated"),
"ACP SessionInfoUpdate 事件应通过 session.info.updated SSE 转发到前端"
);
assert!(
SIDEBAR_PAGE_AI_RUNTIME_JS.contains("plan.updated"),
"ACP PlanUpdate 事件应通过 plan.updated SSE 转发到前端"
);
assert!(
SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-plan"),
"plan 消息应渲染为 data-page-ai-plan 标记的轻量系统状态面板"
);
assert!(
SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("执行计划 · "),
"plan 面板标题应显示执行计划和步数"
);
}
#[test]
fn page_ai_session_ui_labels_local_shared_and_cloud_storage() {
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiSessionStorageLabel"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("local_private"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("local_shared"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("sqlite_control_plane"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("convex_acp_runtime_store"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("本地私有"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("共享会话"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("账号会话"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("云端会话"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("sessionStorage:"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("permissionLevel:"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("shareId:"));
}
#[test]
fn page_ai_acp_runtime_legacy_selector_contract_is_explicit() {
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("var PAGE_AI_SESSION_STORAGE_VERSION = 4"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function ensurePageAiStateFacade"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiAcpRuntime: 'reasonix'"));
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("pageAiAcpRuntime: 'reasonix'"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiPermissionRuntime"));
assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS.contains("function pageAiResolvePermission"));
assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS.contains("resolve-permission"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiProfileRuntime"));
assert!(SIDEBAR_PAGE_AI_PROFILE_RUNTIME_JS.contains("function pageAiNormalizeAcpRuntimes"));
assert!(SIDEBAR_PAGE_AI_PROFILE_RUNTIME_JS.contains("return ['reasonix', 'hermes']"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiNormalizeAcpRuntimes(acpRuntimes)"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiAcpRuntimeSelect.value || 'reasonix'"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function handlePageAiClick"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function handlePageAiPointerDown"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("mnote.page_ai.drawer_width"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-resize-handle"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiOpenCitationUrl"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("a[data-page-ai-citation-link=\"true\"]"));
assert!(SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS.contains("data-page-ai-citation-link=\"true\""));
assert!(SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS.contains("renderPageAiMarkdownTable"));
assert!(SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS.contains("wolai-page-ai-markdown-table-wrap"));
assert!(SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS.contains("target=\"_blank\""));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function handlePageAiChange"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function installPageAiDelegates"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("document.addEventListener('visibilitychange'"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiCheckActiveRun(sessionId || undefined)"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiCheckAndResumeActiveRun"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiResumeActiveRunJournal"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiTrackStreamingRunEvent"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("void pageAiCheckAndResumeActiveRun().catch"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("mnote.page_ai_active_run_snapshot.v1"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-mnote-page-ai-active-run-last-seq"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("/api/page-ai/runs/"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("afterSeq="));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiCheckAndResumeActiveRun()"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("/api/page-ai/agents/descriptors"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiLoadAgentDescriptors"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-page-ai-descriptor-field"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function pageAiRenderDescriptorField"));
assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-agent-descriptor-card"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("sidebarPageAi.installPageAiDelegates()"));
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("sidebarPageAi.handlePageAi"));
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("closestAction(e.target, '[data-page-ai-action"));
assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS
.contains("activeAcpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix'"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("默认 (Hermes HTTP)"));
// 已退役:OCR sidecar / Hermes agent 切换 / ACP session store 前端
assert!(!SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("pageAiEnrichOcrContextRefs"));
assert!(!SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("ocrRootRelativePath"));
assert!(!SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("切换到 Hermes"));
assert!(!SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("切换到 OpenCode"));
}
#[test]
@@ -1158,8 +1026,10 @@ mod tests {
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("tree:local-folder-watch-batch"));
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("viaEventBus: true"));
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("emitSyntheticWatchBatch"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("__mnoteLocalFolderEventBus"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("synthetic_page_ai_receipt"));
// Page AI 写回本地文件夹:由 Pi Lab runtime 调 event bus,不再经 legacy drawer。
assert!(SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("__mnoteLocalFolderEventBus"));
assert!(SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("emitChangedFiles"));
assert!(SIDEBAR_PAGE_AI_PI_LAB_RUNTIME_JS.contains("pi_lab_tool_call"));
assert!(TREE_LIVE_CONTROLLER_JS.contains("__mnoteLocalFolderEventBus"));
assert!(TREE_LIVE_CONTROLLER_JS.contains("startLocalFolderWatcher"));
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
@@ -2032,9 +1902,17 @@ mod tests {
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("recordFileTreeActionStatus('blocked'")
);
assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS.contains("/api/tree/filetree/drop-preflight"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("moveSidebarFileTreeRows(runtimeState.sidebarFileTreeClipboard.rowIds"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("ensureFileTreeWritableTarget('drop'"));
assert!(
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("moveSidebarFileTreeRows(runtimeState.sidebarFileTreeClipboard.rowIds")
|| SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("moveSidebarFileTreeRows(clipboard.rowIds")
);
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("ensureFileTreeWritableTarget('drop'")
|| FILETREE_DND_RUNTIME_JS.contains("ensureFileTreeWritableTarget('drop'")
|| SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS.contains("ensureFileTreeWritableTarget")
);
}
#[test]
+2 -1
View File
@@ -220,7 +220,8 @@ mod tests {
assert!(MNOTE_CSS.len() > 2000);
// CSS 已拆分为模块化文件,通过 concat!(include_str!()) 组装;
// 当前包含主壳、Page AI、搜索、toast、debug 与 vault 样式,继续用上限防止意外重复打包。
assert!(MNOTE_CSS.len() < 220000);
// vault workbench 样式增长后合计约 234KB;上限放宽到 280KB。
assert!(MNOTE_CSS.len() < 280000);
}
#[test]
@@ -2002,37 +2002,6 @@ button.wolai-page-ai-history-main span {
border-color: rgba(27, 28, 28, 0.32);
}
.wolai-page-ai-reasonix-controls {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 6px;
padding: 0 8px 6px;
}
.wolai-page-ai-reasonix-controls[hidden] {
display: none !important;
}
.wolai-page-ai-reasonix-control {
display: grid;
gap: 2px;
min-width: 0;
color: #8B8782;
font-size: 11px;
}
.wolai-page-ai-reasonix-control select {
width: 100%;
min-width: 0;
height: 28px;
padding: 0 8px;
border: 1px solid rgba(27, 28, 28, 0.1);
border-radius: 7px;
background: #FFF;
color: #1B1C1C;
font-size: 12px;
}
.wolai-page-ai-composer-bar {
display: flex;
min-height: 36px;
@@ -2214,298 +2183,3 @@ button.wolai-page-ai-history-main span {
}
}
.wolai-page-ai-drawer[data-page-ai-opencode-host="true"] .wolai-page-ai-panel {
display: flex;
flex-direction: column;
gap: 0;
overflow: hidden;
}
.wolai-page-ai-opencode-header {
flex: 0 0 auto;
}
.wolai-page-ai-opencode-chrome {
display: flex;
flex: 0 0 auto;
flex-direction: column;
gap: 6px;
padding: 8px 12px 10px;
border-bottom: 1px solid rgba(27, 28, 28, 0.08);
background: rgba(247, 247, 245, 0.92);
}
.wolai-page-ai-opencode-row {
display: grid;
grid-template-columns: 44px minmax(0, 1fr);
gap: 6px;
align-items: baseline;
font-size: 12px;
color: rgba(27, 28, 28, 0.58);
}
.wolai-page-ai-opencode-row strong,
.wolai-page-ai-opencode-row code {
min-width: 0;
overflow: hidden;
color: rgba(27, 28, 28, 0.86);
text-overflow: ellipsis;
white-space: nowrap;
}
.wolai-page-ai-opencode-badges,
.wolai-page-ai-opencode-files {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.wolai-page-ai-opencode-badges span,
.wolai-page-ai-opencode-empty,
.wolai-page-ai-opencode-chip {
display: inline-flex;
align-items: center;
max-width: 100%;
min-height: 24px;
padding: 3px 8px;
border: 1px solid rgba(27, 28, 28, 0.08);
border-radius: 999px;
background: #fff;
color: rgba(27, 28, 28, 0.68);
font: inherit;
font-size: 12px;
}
.wolai-page-ai-opencode-chip {
cursor: pointer;
}
.wolai-page-ai-opencode-chip:hover {
border-color: rgba(35, 131, 226, 0.32);
color: var(--wolai-accent, #2383e2);
}
.wolai-page-ai-opencode-chip span {
margin-left: 6px;
color: rgba(27, 28, 28, 0.45);
}
.wolai-page-ai-opencode-frame-wrap {
position: relative;
display: flex;
flex: 1 1 auto;
min-height: 0;
overflow: hidden;
background: #fff;
}
.wolai-page-ai-opencode-iframe {
flex: 1 1 auto;
width: 100%;
min-height: 0;
border: 0;
background: #fff;
}
.wolai-page-ai-opencode-iframe[hidden] {
display: none;
}
.wolai-page-ai-opencode-iframe-fallback {
position: absolute;
inset: 0;
display: grid;
place-items: center;
padding: 24px;
color: rgba(27, 28, 28, 0.62);
text-align: center;
background: #fff;
}
.wolai-page-ai-opencode-iframe-fallback[hidden] {
display: none;
}
.wolai-page-ai-opencode-chat {
display: flex;
flex: 1 1 auto;
min-height: 0;
flex-direction: column;
background: #fff;
}
.wolai-page-ai-opencode-messages {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
padding: 12px;
}
.wolai-page-ai-opencode-message {
margin: 0 0 10px;
padding: 10px 12px;
border: 1px solid rgba(27, 28, 28, 0.08);
border-radius: 12px;
background: rgba(247, 247, 245, 0.9);
}
.wolai-page-ai-opencode-message[data-role="assistant"] {
background: #fff;
}
.wolai-page-ai-opencode-message-role {
margin-bottom: 4px;
color: rgba(27, 28, 28, 0.52);
font-size: 11px;
font-weight: 600;
}
.wolai-page-ai-opencode-message-body {
color: rgba(27, 28, 28, 0.88);
font-size: 13px;
line-height: 1.55;
}
.wolai-page-ai-opencode-composer {
display: flex;
gap: 8px;
padding: 10px 12px 12px;
border-top: 1px solid rgba(27, 28, 28, 0.08);
}
.wolai-page-ai-opencode-composer textarea {
flex: 1 1 auto;
min-height: 42px;
resize: vertical;
border: 1px solid rgba(27, 28, 28, 0.14);
border-radius: 10px;
padding: 8px 10px;
font: inherit;
}
.wolai-page-ai-opencode-send,
.wolai-page-ai-opencode-permission button {
border: 0;
border-radius: 10px;
padding: 0 12px;
background: #1f6feb;
color: #fff;
font-weight: 600;
}
.wolai-page-ai-opencode-permissions {
display: flex;
flex-direction: column;
gap: 6px;
padding: 0 12px;
}
.wolai-page-ai-opencode-permission {
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
gap: 6px;
align-items: center;
padding: 8px;
border: 1px solid rgba(227, 115, 14, 0.24);
border-radius: 10px;
background: rgba(255, 247, 237, 0.92);
font-size: 12px;
}
.wolai-page-ai-opencode-permission span {
overflow: hidden;
color: rgba(27, 28, 28, 0.62);
text-overflow: ellipsis;
white-space: nowrap;
}
.wolai-page-ai-opencode-sessions-wrap {
font-size: 12px;
}
.wolai-page-ai-opencode-sessions {
display: grid;
gap: 4px;
margin-top: 6px;
}
.wolai-page-ai-opencode-session-row {
display: grid;
min-width: 0;
gap: 2px;
border: 1px solid rgba(27, 28, 28, 0.08);
border-radius: 8px;
padding: 6px 8px;
background: rgba(255, 255, 255, 0.72);
color: inherit;
text-align: left;
}
.wolai-page-ai-opencode-session-row[data-active="true"] {
border-color: rgba(31, 111, 235, 0.38);
background: rgba(31, 111, 235, 0.08);
}
.wolai-page-ai-opencode-session-row span,
.wolai-page-ai-opencode-message-role span {
overflow: hidden;
color: rgba(27, 28, 28, 0.52);
text-overflow: ellipsis;
white-space: nowrap;
}
.wolai-page-ai-opencode-message-role {
display: flex;
justify-content: space-between;
gap: 8px;
}
.wolai-page-ai-opencode-part {
margin-top: 8px;
border: 1px solid rgba(27, 28, 28, 0.08);
border-radius: 10px;
padding: 8px;
background: rgba(250, 250, 249, 0.88);
font-size: 12px;
}
.wolai-page-ai-opencode-part summary {
cursor: pointer;
font-weight: 650;
}
.wolai-page-ai-opencode-part pre,
.wolai-page-ai-opencode-error {
margin: 8px 0 0;
overflow: auto;
white-space: pre-wrap;
word-break: break-word;
}
.wolai-page-ai-opencode-tool summary {
display: flex;
justify-content: space-between;
gap: 8px;
}
.wolai-page-ai-opencode-patch {
display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
.wolai-page-ai-opencode-patch button,
.wolai-page-ai-opencode-part button {
border: 1px solid rgba(27, 28, 28, 0.12);
border-radius: 999px;
padding: 3px 8px;
background: #fff;
}
.wolai-page-ai-opencode-todo {
margin: 0;
padding: 0 12px;
list-style-position: inside;
font-size: 12px;
}
@@ -456,6 +456,44 @@
border-top: 1px solid rgba(27, 28, 28, 0.06);
}
/* 账号下登录态(扩展 Cookie session)状态徽章 */
.mnote-vault-session-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 1px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
line-height: 18px;
letter-spacing: 0.01em;
}
.mnote-vault-session-badge.is-saved {
background: rgba(34, 160, 107, 0.12);
color: #1a7a4c;
border: 1px solid rgba(34, 160, 107, 0.28);
}
.mnote-vault-session-badge.is-absent {
background: rgba(109, 106, 101, 0.08);
color: #6d6a65;
border: 1px solid rgba(109, 106, 101, 0.18);
}
.mnote-vault-session-badge.is-compact {
font-size: 10px;
padding: 0 6px;
line-height: 16px;
margin-left: 6px;
}
.mnote-vault-session-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
}
.mnote-vault-session-row > label {
min-width: 0;
}
.mnote-vault-field-row > label {
padding-top: 4px;
color: #6d6a65;
@@ -556,6 +594,114 @@
line-height: 18px;
}
/* Expand-style folder picker: top-level groups stay visible (no long select). */
.mnote-vault-folder-picker {
width: 100%;
box-sizing: border-box;
max-height: 220px;
overflow: auto;
padding: 6px;
border: 1px solid rgba(27, 28, 28, 0.12);
border-radius: 6px;
background: #fafaf9;
}
.mnote-vault-folder-picker-none {
display: block;
width: 100%;
margin: 0 0 4px;
padding: 5px 8px;
border: none;
border-radius: 4px;
background: transparent;
color: #6d6a65;
font: inherit;
font-size: 12px;
text-align: left;
cursor: pointer;
}
.mnote-vault-folder-picker-none:hover,
.mnote-vault-folder-picker-label:hover {
background: rgba(35, 131, 226, 0.08);
color: #37352f;
}
.mnote-vault-folder-picker-none.is-selected,
.mnote-vault-folder-picker-row.is-selected .mnote-vault-folder-picker-label {
background: rgba(35, 131, 226, 0.14);
color: #1b64c2;
font-weight: 600;
}
.mnote-vault-folder-picker-node {
min-width: 0;
}
.mnote-vault-folder-picker-row {
display: flex;
align-items: center;
gap: 2px;
min-height: 26px;
padding-left: calc(var(--vault-picker-depth, 0) * 12px);
border-radius: 4px;
}
.mnote-vault-folder-picker-chevron {
flex: 0 0 18px;
width: 18px;
height: 22px;
margin: 0;
padding: 0;
border: none;
background: transparent;
color: #9b9a97;
font: inherit;
font-size: 9px;
line-height: 22px;
text-align: center;
cursor: pointer;
}
.mnote-vault-folder-picker-chevron.is-leaf {
cursor: default;
visibility: hidden;
}
.mnote-vault-folder-picker-label {
flex: 1 1 auto;
min-width: 0;
margin: 0;
padding: 4px 6px;
border: none;
border-radius: 4px;
background: transparent;
color: #37352f;
font: inherit;
font-size: 13px;
line-height: 18px;
text-align: left;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mnote-vault-folder-picker-children {
min-width: 0;
}
.mnote-vault-folder-picker-empty {
padding: 6px 8px;
font-size: 12px;
}
.mnote-vault-folder-hint {
margin: 0;
font-size: 11px;
color: #9b9a97;
}
.mnote-vault-folder-controls input[type="text"] {
width: 100%;
box-sizing: border-box;