Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/en/architecture/deepseek-harness-apps.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ flowchart LR
APPS --> MINIAPP[Mini Programs]
APPS --> MARKET[Smart apps]
ROUTES --> MARKET
MARKET --> BUILDER[Smart App Builder plugin]
MARKET --> DEFAULTS[Register bundled marketplace and idempotently install defaults]
DEFAULTS --> BUILDER[Smart App Builder plugin]
BUILDER --> DISCOVER[DSH setup / discovery / composition]
DISCOVER --> BROWSER[Wework built-in browser verification]
BROWSER --> ZIP
Expand Down Expand Up @@ -63,6 +64,7 @@ sequenceDiagram
A->>M: Select Smart apps beside Sites and Mini Programs
alt Create a Smart app
U->>M: Create Smart app
M->>M: Register wework-personal and confirm smart-app-builder is installed
M->>B: Start a new chat with smart-app-builder
B->>B: Prepare DSH, discover plugins, and compose the app
B->>WB: Launch the local profile and verify its primary flow
Expand Down Expand Up @@ -109,10 +111,13 @@ sequenceDiagram
| Top tab bar entry | `wework/src/features/workspace-tabs/WorkspaceTabStrip.tsx` |
| Applications workspace and Sites / Mini Programs / Smart apps navigation | `wework/src/pages/SitesPage.tsx`, `wework/src/components/sites/SitesWorkspace.tsx` |
| Smart app marketplace and Marketplace / Installed navigation | `wework/src/pages/SmartAppsMarketplacePage.tsx`, `wework/src/components/smart-apps/SmartAppsSectionNav.tsx` |
| Bundled marketplace registration and default plugin installation | `wework/src/tauri/localExecutor.ts`, `wework/src-tauri/src/local_executor.rs`, `executor/src/runtime_work/handler/runtime_rpc.rs` |
| Smart app creation workflow plugin | `wework/src-tauri/bundled-plugins/wework-personal/plugins/smart-app-builder/` |
| Installation, management, and lifecycle UI | `wework/src/pages/HarnessAppsPage.tsx` |
| Resident app startup restoration | `wework/src/features/harness-apps/ResidentSmartAppsManager.tsx` |
| App-to-tab launch motion | `wework/src/features/harness-apps/smartAppLaunchAnimation.ts`, `wework/src/styles/globals.css` |
| Workspace tabs and native WebViews | `wework/src/App.tsx`, `wework/src/features/workspace-tabs/workspaceTabs.ts` |

Invariants: all user-facing names use “Smart apps,” while DeepSeek Harness appears only as a runtime implementation detail; Smart apps belong to the Applications workspace and must appear as a peer application type beside Sites and Mini Programs, while the plugin marketplace and plugin management page do not host Smart app management UI; `smart-app-builder` may exist as a development-tool plugin, but its product entry belongs to the experimental Smart app marketplace, its workflow keeps DSH source read-only, composes external plugin packages, verifies them in the Wework built-in browser, and ends installation through native preview, version validation, and model confirmation instead of editing the local installation registry; the entire Smart apps feature is experimental, so when the toggle is off the top “+” and Applications workspace expose no Smart apps entry, direct visits to `/sites?app_type=smart_app` and stale app tabs leave the feature, and resident apps are not restored; when enabled, `/sites?app_type=smart_app` owns the Smart app marketplace and installed management views, while running apps use independent tabs, with no mixing of the Applications workspace, management view, and runtime tab responsibilities; top tab bar “+ → Smart apps” opens the Smart apps type inside Applications, while the workspace tab title remains “Applications”; DeepSeek Harness source remains read-only; build output packages the DSH runtime as one managed archive, Tauri must not recursively watch or register its `node_modules`, and runtime extraction is content-addressed under app data and validates the executable entry before use; production macOS builds sign every Mach-O file in the staging directory with a Developer ID, secure timestamp, and hardened runtime before creating the archive, and the signing mode and identity are part of the content fingerprint so an unsigned archive or one signed by another identity is never reused; the archived Node must then receive the V8 JIT and executable-memory entitlements after generic Mach-O pre-signing and pass a real V8 Isolate startup check instead of only `node --version`; packages are validated before being written to immutable `name/version` directories; the package DSH version range must contain the actual runtime version; every Smart app instance owns an isolated `DSH_HOME`, port, and process group; the bound model is persisted and can only be changed while the app is stopped, while credentials exist only in the runtime proxy and child environment; Resident means that the main window automatically starts the app and opens its tab once per Wework launch; a normal Open action animates from the app card into the top tab strip after HTTP readiness and before tab creation, while reduced-motion preferences skip the animation; starting, stopping, or failing one instance cannot affect another; stop, uninstall, disabling experimental features, and Wework exit reclaim the complete process group.

Plugins marked `INSTALLED_BY_DEFAULT` in the `wework-personal` catalog are installed idempotently after bundled marketplace registration. The Smart app creation entry confirms `smart-app-builder` is installed before writing the plugin-referenced fresh-chat draft and navigating; installation failure stays on the marketplace page instead of opening an empty chat. Default-plugin reconciliation reads existing plugin configuration through the Executor-explicitly-allowed Codex `config/read` method and treats a present but user-disabled plugin as configured, so default synchronization must never re-enable it.
7 changes: 6 additions & 1 deletion docs/zh/architecture/deepseek-harness-apps.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ flowchart LR
APPS --> MINIAPP[小程序]
APPS --> MARKET[智能应用]
ROUTES --> MARKET
MARKET --> BUILDER[智能应用开发助手插件]
MARKET --> DEFAULTS[注册内置市场并幂等安装默认插件]
DEFAULTS --> BUILDER[智能应用开发助手插件]
BUILDER --> DISCOVER[DSH 环境 / 插件检索 / 拼装]
DISCOVER --> BROWSER[Wework 内置浏览器验证]
BROWSER --> ZIP
Expand Down Expand Up @@ -63,6 +64,7 @@ sequenceDiagram
A->>M: 选择与站点、小程序并列的智能应用
alt 创建智能应用
U->>M: 创建智能应用
M->>M: 注册 wework-personal 并确认 smart-app-builder 已安装
M->>B: 新对话引用 smart-app-builder
B->>B: 准备 DSH 环境、检索并拼装插件
B->>WB: 启动本地 profile 并验证主流程
Expand Down Expand Up @@ -109,10 +111,13 @@ sequenceDiagram
| 顶部标签栏入口 | `wework/src/features/workspace-tabs/WorkspaceTabStrip.tsx` |
| 应用工作区与站点 / 小程序 / 智能应用导航 | `wework/src/pages/SitesPage.tsx`、`wework/src/components/sites/SitesWorkspace.tsx` |
| 智能应用市场与市场 / 已安装导航 | `wework/src/pages/SmartAppsMarketplacePage.tsx`、`wework/src/components/smart-apps/SmartAppsSectionNav.tsx` |
| 内置市场注册与默认插件安装 | `wework/src/tauri/localExecutor.ts`、`wework/src-tauri/src/local_executor.rs`、`executor/src/runtime_work/handler/runtime_rpc.rs` |
| 智能应用创建工作流插件 | `wework/src-tauri/bundled-plugins/wework-personal/plugins/smart-app-builder/` |
| 安装、管理和生命周期 UI | `wework/src/pages/HarnessAppsPage.tsx` |
| 常驻应用的启动恢复 | `wework/src/features/harness-apps/ResidentSmartAppsManager.tsx` |
| 应用进入标签栏动效 | `wework/src/features/harness-apps/smartAppLaunchAnimation.ts`、`wework/src/styles/globals.css` |
| 工作区标签与原生 WebView | `wework/src/App.tsx`、`wework/src/features/workspace-tabs/workspaceTabs.ts` |

不变量:所有用户可见名称统一为“智能应用”,DeepSeek Harness 只作为运行时技术说明出现;智能应用属于“应用”工作区,必须与“站点”“小程序”作为同级应用类型展示,插件市场和插件管理页不得承载智能应用管理界面;`smart-app-builder` 可以作为开发工具插件存在,但其产品入口必须位于实验性智能应用市场,创建流程必须保持 DSH 源码只读,复用外部插件包,通过 Wework 内置浏览器验证,并以原生预览、版本检查和模型确认结束安装,不得直接改写本机安装注册表;智能应用整体属于实验性功能,关闭开关时顶部“+”和应用工作区不得显示智能应用入口,直接访问 `/sites?app_type=smart_app` 或遗留应用标签必须退出该功能,常驻应用不得自动恢复;开启开关后,`/sites?app_type=smart_app` 必须承载智能应用市场和已安装管理,运行中的应用必须使用独立标签,应用工作区、管理视图和运行标签三层职责不得混合;顶部标签栏“+ → 智能应用”必须直接打开应用工作区的智能应用类型,工作区标签标题仍为“应用”;DeepSeek Harness 源码保持只读;构建产物必须把 DSH Runtime 压缩成单个受管归档,Tauri 不得递归监听或逐文件登记其 `node_modules`,运行时必须按内容指纹解包到应用数据目录并验证可执行入口后再使用;macOS 正式构建必须在创建归档前使用 Developer ID、secure timestamp 和 hardened runtime 签署暂存目录中的每个 Mach-O 文件,签名模式和身份必须进入内容指纹,禁止复用未签名或由其他身份签名的归档;归档中的 Node 必须在通用 Mach-O 预签名之后最后签入 V8 所需的 JIT 与可执行内存权限,并通过实际创建 V8 Isolate 校验,不能只检查 `node --version`;安装包必须先校验再写入不可变的 `name/version` 目录;插件声明的 DSH 版本范围必须包含实际 Runtime 版本;每个智能应用实例拥有独立 `DSH_HOME`、端口和进程组;绑定模型必须持久化且只能在应用停止时修改,模型凭据只存在于运行期代理和子进程环境中;“常驻”必须表示主窗口每次启动时自动启动并打开应用标签,且每个启动周期只执行一次;普通打开必须在 HTTP 就绪后播放从应用卡片进入顶部标签栏的动效,再创建标签页,减少动态效果偏好下必须跳过动效;一个实例的启动、停止或失败不得影响其他实例;停止、卸载、关闭实验性功能和 Wework 退出都必须回收完整进程组。

`wework-personal` 清单中标记为 `INSTALLED_BY_DEFAULT` 的插件必须在内置市场注册后幂等安装。智能应用创建入口必须确认 `smart-app-builder` 已安装后才写入带插件引用的新会话草稿并跳转,安装失败必须留在市场页,不得打开空白会话。默认插件协调必须通过 Executor 明确允许的 Codex `config/read` 读取已有插件配置,并将已存在但被用户禁用的插件视为已配置,不得借默认同步重新启用它。
1 change: 1 addition & 0 deletions executor/src/runtime_work/handler/runtime_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub(super) fn is_allowed_plugin_app_server_method(method: &str) -> bool {
| "plugin/skill/read"
| "plugin/install"
| "plugin/uninstall"
| "config/read"
| "config/value/write"
| "skills/list"
| "skills/config/write"
Expand Down
1 change: 1 addition & 0 deletions executor/src/runtime_work/handler/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1842,6 +1842,7 @@ fn plugin_app_server_method_allowlist_covers_wework_plugin_runtime_surface() {
"plugin/skill/read",
"plugin/install",
"plugin/uninstall",
"config/read",
"config/value/write",
"skills/list",
"skills/config/write",
Expand Down
73 changes: 73 additions & 0 deletions wework/src-tauri/src/local_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ pub struct BundledPluginMarketplace {
id: String,
path: String,
plugin_count: usize,
default_plugin_names: Vec<String>,
}

#[derive(Clone, Debug, Serialize)]
Expand Down Expand Up @@ -1571,6 +1572,35 @@ fn marketplace_plugin_names(manifest_path: &Path) -> Result<HashSet<String>, Str
Ok(names)
}

fn marketplace_default_plugin_names(manifest_path: &Path) -> Result<Vec<String>, String> {
let content = fs::read_to_string(manifest_path)
.map_err(|error| format!("failed to read {}: {error}", manifest_path.display()))?;
let manifest: Value = serde_json::from_str(&content)
.map_err(|error| format!("invalid marketplace {}: {error}", manifest_path.display()))?;
let plugins = manifest
.get("plugins")
.and_then(Value::as_array)
.ok_or_else(|| {
format!(
"marketplace {} must contain a plugins array",
manifest_path.display()
)
})?;
let mut names = plugins
.iter()
.filter(|plugin| {
plugin
.pointer("/policy/installation")
.and_then(Value::as_str)
== Some("INSTALLED_BY_DEFAULT")
})
.filter_map(|plugin| plugin.get("name").and_then(Value::as_str))
.map(str::to_string)
.collect::<Vec<_>>();
names.sort();
Ok(names)
}

fn remove_bundled_marketplace_path(path: &Path) -> Result<(), String> {
if path.is_dir() {
fs::remove_dir_all(path)
Expand All @@ -1591,6 +1621,7 @@ fn initialize_bundled_plugin_marketplace_from_paths(
let claude_manifest = source.join(".claude-plugin/marketplace.json");
let codex_plugins = marketplace_plugin_names(&codex_manifest)?;
let claude_plugins = marketplace_plugin_names(&claude_manifest)?;
let default_plugin_names = marketplace_default_plugin_names(&codex_manifest)?;
if codex_plugins != claude_plugins {
return Err(
"Bundled Codex and Claude Code marketplace plugin names must match".to_string(),
Expand Down Expand Up @@ -1626,6 +1657,7 @@ fn initialize_bundled_plugin_marketplace_from_paths(
id: WEWORK_PERSONAL_MARKETPLACE_ID.to_string(),
path: destination.display().to_string(),
plugin_count: codex_plugins.len(),
default_plugin_names,
})
}

Expand Down Expand Up @@ -6081,6 +6113,7 @@ mod tests {

assert_eq!(first.id, WEWORK_PERSONAL_MARKETPLACE_ID);
assert_eq!(first.plugin_count, 0);
assert!(first.default_plugin_names.is_empty());
assert_eq!(first.path, second.path);
assert!(destination
.join(".agents/plugins/marketplace.json")
Expand All @@ -6092,6 +6125,46 @@ mod tests {
fs::remove_dir_all(root).unwrap();
}

#[test]
fn exposes_bundled_plugins_marked_for_default_installation() {
let root = import_test_root("bundled-plugin-marketplace-defaults");
let source = root.join("source");
let destination = root.join("destination/wework-personal");
fs::create_dir_all(source.join(".agents/plugins")).unwrap();
fs::create_dir_all(source.join(".claude-plugin")).unwrap();
fs::write(
source.join(".agents/plugins/marketplace.json"),
r#"{
"name": "wework-personal",
"plugins": [
{"name":"available","policy":{"installation":"AVAILABLE"}},
{"name":"smart-app-builder","policy":{"installation":"INSTALLED_BY_DEFAULT"}}
]
}"#,
)
.unwrap();
fs::write(
source.join(".claude-plugin/marketplace.json"),
r#"{
"name": "wework-personal",
"plugins": [
{"name":"available"},
{"name":"smart-app-builder"}
]
}"#,
)
.unwrap();

let initialized =
initialize_bundled_plugin_marketplace_from_paths(&source, &destination).unwrap();

assert_eq!(
initialized.default_plugin_names,
vec!["smart-app-builder".to_string()]
);
fs::remove_dir_all(root).unwrap();
}

#[test]
fn rejects_mismatched_bundled_plugin_marketplaces_before_replacing_destination() {
let root = import_test_root("bundled-plugin-marketplace-mismatch");
Expand Down
2 changes: 2 additions & 0 deletions wework/src/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,8 @@
"smart_apps_marketplace_coming": "The Smart app marketplace is coming soon",
"smart_apps_marketplace_coming_hint": "Until marketplace content is connected, use Smart App Builder or import a local Smart app package; installed apps remain manageable.",
"smart_apps_builder_name": "Smart App Builder",
"smart_apps_builder_installing": "Preparing Smart App Builder...",
"smart_apps_builder_install_failed": "Smart App Builder could not be installed. Try again.",
"smart_apps_builder_prompt": "Help me create a Smart app, including DSH setup, plugin discovery and composition, built-in-browser testing, packaging, and local installation.",
"smart_apps_create": "Create Smart app",
"smart_apps_import_local": "Import local package",
Expand Down
2 changes: 2 additions & 0 deletions wework/src/i18n/locales/zh-CN/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,8 @@
"smart_apps_marketplace_coming": "智能应用市场即将上线",
"smart_apps_marketplace_coming_hint": "市场内容接入前,可以使用智能应用开发助手创建,或导入本地智能应用安装包;已安装应用仍可正常管理和运行。",
"smart_apps_builder_name": "智能应用开发助手",
"smart_apps_builder_installing": "正在准备创作助手...",
"smart_apps_builder_install_failed": "智能应用开发助手安装失败,请重试。",
"smart_apps_builder_prompt": "帮我创建一个智能应用,完成 DSH 环境准备、插件检索与拼装、内置浏览器测试、打包和本机安装。",
"smart_apps_create": "创建智能应用",
"smart_apps_import_local": "导入本地安装包",
Expand Down
Loading
Loading