2026-08-09 11:02:16 +08:00
|
|
|
<!-- 英文源文件由 scripts/gen-module-graph.ts 生成;本中文文件是通过双语配对维护的经评审对侧。
|
|
|
|
|
更新时先运行 `pnpm run gen-module-graph` 更新英文,再更新本文件并运行 `pnpm run verify-translation-pairing --write docs/module-graph.md` 重新记录配对。 -->
|
|
|
|
|
|
|
|
|
|
# 模块依赖关系图
|
|
|
|
|
|
|
|
|
|
[English](module-graph.md) | 中文
|
|
|
|
|
|
|
|
|
|
`@deepseek-ai/dsh-*` harness 包之间的依赖关系。该关系图根据各包的 `peerDependencies`(规范的运行时依赖信号)生成,并按 `packages/<group>/<pkg>` 层级分组。边 `a --> b` 表示包 `a` 依赖包 `b`。名称中的 `@deepseek-ai/dsh-` 前缀已移除。
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
flowchart TD
|
|
|
|
|
subgraph group_util["packages/util"]
|
|
|
|
|
pkg_atomic_write["atomic-write"]
|
|
|
|
|
pkg_brand["brand"]
|
|
|
|
|
pkg_environment["environment"]
|
|
|
|
|
pkg_native_command["native-command"]
|
|
|
|
|
pkg_paths["paths"]
|
|
|
|
|
pkg_retention["retention"]
|
|
|
|
|
pkg_timeout["timeout"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_llm["packages/llm"]
|
|
|
|
|
pkg_llm["llm"]
|
|
|
|
|
pkg_llm_deepseek["llm-deepseek"]
|
|
|
|
|
pkg_llm_pi_ai["llm-pi-ai"]
|
|
|
|
|
pkg_llm_retry["llm-retry"]
|
|
|
|
|
pkg_token_meter["token-meter"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_core["packages/core"]
|
|
|
|
|
pkg_agent["agent"]
|
2026-08-09 15:17:35 +08:00
|
|
|
pkg_agent_default_model["agent-default-model"]
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_agent_loop["agent-loop"]
|
2026-08-09 20:41:55 +08:00
|
|
|
pkg_agent_tool_mode["agent-tool-mode"]
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_scope["scope"]
|
|
|
|
|
pkg_session["session"]
|
|
|
|
|
pkg_system_prompt["system-prompt"]
|
|
|
|
|
pkg_tools["tools"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_goal["packages/goal"]
|
|
|
|
|
pkg_command_goal["command-goal"]
|
|
|
|
|
pkg_goal["goal"]
|
|
|
|
|
pkg_goal_session["goal-session"]
|
|
|
|
|
pkg_tool_goal["tool-goal"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_bash["packages/bash"]
|
|
|
|
|
pkg_bash["bash"]
|
|
|
|
|
pkg_bash_env["bash-env"]
|
|
|
|
|
pkg_bash_local["bash-local"]
|
|
|
|
|
pkg_bash_sandbox["bash-sandbox"]
|
|
|
|
|
pkg_pwsh_local["pwsh-local"]
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_pwsh_sandbox["pwsh-sandbox"]
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tool_bash["tool-bash"]
|
|
|
|
|
pkg_tool_pwsh["tool-pwsh"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_fs["packages/fs"]
|
|
|
|
|
pkg_fs["fs"]
|
|
|
|
|
pkg_fs_local["fs-local"]
|
|
|
|
|
pkg_fs_policy["fs-policy"]
|
|
|
|
|
pkg_fs_sandbox["fs-sandbox"]
|
|
|
|
|
pkg_tool_fs["tool-fs"]
|
|
|
|
|
pkg_tool_fs_search["tool-fs-search"]
|
|
|
|
|
pkg_tool_str_replace_editor["tool-str-replace-editor"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_skill["packages/skill"]
|
|
|
|
|
pkg_skill["skill"]
|
|
|
|
|
pkg_skill_badge["skill-badge"]
|
|
|
|
|
pkg_skill_local["skill-local"]
|
|
|
|
|
pkg_tool_skill["tool-skill"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_compact["packages/compact"]
|
|
|
|
|
pkg_command_compact["command-compact"]
|
|
|
|
|
pkg_compact["compact"]
|
|
|
|
|
pkg_compact_basic["compact-basic"]
|
|
|
|
|
pkg_compact_tool_result_prune["compact-tool-result-prune"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_subagent["packages/subagent"]
|
|
|
|
|
pkg_subagent["subagent"]
|
|
|
|
|
pkg_subagent_acp["subagent-acp"]
|
|
|
|
|
pkg_subagent_claude_code["subagent-claude-code"]
|
|
|
|
|
pkg_subagent_codex["subagent-codex"]
|
|
|
|
|
pkg_subagent_dsh_sdk["subagent-dsh-sdk"]
|
|
|
|
|
pkg_subagent_fork["subagent-fork"]
|
|
|
|
|
pkg_subagent_inprocess["subagent-inprocess"]
|
|
|
|
|
pkg_subagent_spawn["subagent-spawn"]
|
|
|
|
|
pkg_tool_subagent["tool-subagent"]
|
|
|
|
|
pkg_tool_subagent_control["tool-subagent-control"]
|
|
|
|
|
pkg_tool_subagent_report["tool-subagent-report"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_web["packages/web"]
|
|
|
|
|
pkg_tool_web["tool-web"]
|
|
|
|
|
pkg_web["web"]
|
|
|
|
|
pkg_web_fetch_local["web-fetch-local"]
|
|
|
|
|
pkg_web_search_deepseek["web-search-deepseek"]
|
|
|
|
|
pkg_web_search_exa["web-search-exa"]
|
|
|
|
|
pkg_web_search_perplexity["web-search-perplexity"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_spill["packages/spill"]
|
|
|
|
|
pkg_spill["spill"]
|
|
|
|
|
pkg_spill_local["spill-local"]
|
|
|
|
|
pkg_spill_policy["spill-policy"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_todo["packages/todo"]
|
|
|
|
|
pkg_tool_todo["tool-todo"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_plan["packages/plan"]
|
|
|
|
|
pkg_plan_mode["plan-mode"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_hooks["packages/hooks"]
|
|
|
|
|
pkg_hook_protocol["hook-protocol"]
|
|
|
|
|
pkg_hooks_claude["hooks-claude"]
|
|
|
|
|
pkg_hooks_codex["hooks-codex"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_session_query["packages/session-query"]
|
|
|
|
|
pkg_session_query["session-query"]
|
|
|
|
|
pkg_session_query_sqlite["session-query-sqlite"]
|
|
|
|
|
pkg_tool_session_query["tool-session-query"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_support["packages/support"]
|
|
|
|
|
pkg_acp_snapshot["acp-snapshot"]
|
|
|
|
|
pkg_agent_loop_testkit["agent-loop-testkit"]
|
|
|
|
|
pkg_invariants["invariants"]
|
|
|
|
|
pkg_llm_mock_server["llm-mock-server"]
|
|
|
|
|
pkg_llm_replay["llm-replay"]
|
|
|
|
|
pkg_loader_smoke["loader-smoke"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_acp["packages/acp"]
|
|
|
|
|
pkg_acp["acp"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_api["packages/api"]
|
|
|
|
|
pkg_api_gateway["api-gateway"]
|
|
|
|
|
pkg_api_remotes["api-remotes"]
|
|
|
|
|
end
|
2026-08-10 11:08:35 +08:00
|
|
|
subgraph group_attachment["packages/attachment"]
|
|
|
|
|
pkg_attachment["attachment"]
|
|
|
|
|
pkg_attachment_local["attachment-local"]
|
|
|
|
|
end
|
2026-08-09 11:02:16 +08:00
|
|
|
subgraph group_boot["packages/boot"]
|
|
|
|
|
pkg_app_boot["app-boot"]
|
2026-08-06 20:52:26 +08:00
|
|
|
pkg_cmdline["cmdline"]
|
2026-08-09 11:02:16 +08:00
|
|
|
end
|
|
|
|
|
subgraph group_bundle["packages/bundle"]
|
|
|
|
|
pkg_base["base"]
|
|
|
|
|
pkg_headless["headless"]
|
|
|
|
|
pkg_web_app["web-app"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_client["packages/client"]
|
|
|
|
|
pkg_client_connection["client-connection"]
|
|
|
|
|
pkg_client_hmr["client-hmr"]
|
|
|
|
|
pkg_client_locale["client-locale"]
|
|
|
|
|
pkg_client_modules["client-modules"]
|
|
|
|
|
pkg_client_runtime["client-runtime"]
|
|
|
|
|
pkg_client_schema_form["client-schema-form"]
|
|
|
|
|
pkg_client_test_runtime["client-test-runtime"]
|
2026-08-09 20:38:21 +08:00
|
|
|
pkg_client_ui_agent_preset["client-ui-agent-preset"]
|
2026-08-11 18:23:36 +08:00
|
|
|
pkg_client_ui_attachment["client-ui-attachment"]
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_client_ui_command["client-ui-command"]
|
|
|
|
|
pkg_client_ui_conversation["client-ui-conversation"]
|
|
|
|
|
pkg_client_ui_deliverables["client-ui-deliverables"]
|
|
|
|
|
pkg_client_ui_goal["client-ui-goal"]
|
|
|
|
|
pkg_client_ui_layout["client-ui-layout"]
|
|
|
|
|
pkg_client_ui_model["client-ui-model"]
|
|
|
|
|
pkg_client_ui_models["client-ui-models"]
|
|
|
|
|
pkg_client_ui_permission["client-ui-permission"]
|
|
|
|
|
pkg_client_ui_plan["client-ui-plan"]
|
2026-08-10 19:14:59 +08:00
|
|
|
pkg_client_ui_plugin_config["client-ui-plugin-config"]
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_client_ui_primitives["client-ui-primitives"]
|
|
|
|
|
pkg_client_ui_question["client-ui-question"]
|
|
|
|
|
pkg_client_ui_settings["client-ui-settings"]
|
|
|
|
|
pkg_client_ui_settings_general["client-ui-settings-general"]
|
|
|
|
|
pkg_client_ui_sidebar["client-ui-sidebar"]
|
|
|
|
|
pkg_client_ui_skill["client-ui-skill"]
|
|
|
|
|
pkg_client_ui_slash["client-ui-slash"]
|
|
|
|
|
pkg_client_ui_slots["client-ui-slots"]
|
|
|
|
|
pkg_client_ui_subagent["client-ui-subagent"]
|
2026-08-10 13:22:51 +08:00
|
|
|
pkg_client_ui_task["client-ui-task"]
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_client_ui_theme["client-ui-theme"]
|
|
|
|
|
pkg_client_ui_tool["client-ui-tool"]
|
|
|
|
|
pkg_client_ui_trajectory["client-ui-trajectory"]
|
2026-08-10 18:37:30 +08:00
|
|
|
pkg_client_ui_workflow_run["client-ui-workflow-run"]
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_client_ui_workspace["client-ui-workspace"]
|
|
|
|
|
pkg_client_web["client-web"]
|
|
|
|
|
pkg_client_web_react["client-web-react"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_code_runtime["packages/code-runtime"]
|
|
|
|
|
pkg_code_runtime["code-runtime"]
|
|
|
|
|
pkg_code_runtime_worker["code-runtime-worker"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_context["packages/context"]
|
|
|
|
|
pkg_session_reference["session-reference"]
|
|
|
|
|
pkg_time_context["time-context"]
|
|
|
|
|
pkg_tmux_context["tmux-context"]
|
|
|
|
|
pkg_workspace_context["workspace-context"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_credentials["packages/credentials"]
|
|
|
|
|
pkg_credentials["credentials"]
|
|
|
|
|
pkg_credentials_local["credentials-local"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_e2b["packages/e2b"]
|
|
|
|
|
pkg_e2b["e2b"]
|
|
|
|
|
pkg_fs_e2b["fs-e2b"]
|
|
|
|
|
pkg_subprocess_e2b["subprocess-e2b"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_examples["packages/examples"]
|
|
|
|
|
pkg_acp_demo["acp-demo"]
|
|
|
|
|
pkg_agent_spine_demo["agent-spine-demo"]
|
|
|
|
|
pkg_jsonrpc_demo["jsonrpc-demo"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_feedback["packages/feedback"]
|
|
|
|
|
pkg_command_feedback["command-feedback"]
|
2026-08-10 11:28:38 -07:00
|
|
|
pkg_message_feedback["message-feedback"]
|
2026-08-09 11:02:16 +08:00
|
|
|
end
|
|
|
|
|
subgraph group_guard["packages/guard"]
|
|
|
|
|
pkg_repeat_tool_guard["repeat-tool-guard"]
|
|
|
|
|
pkg_timeout_policy["timeout-policy"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_host["packages/host"]
|
|
|
|
|
pkg_frontend_static["frontend-static"]
|
|
|
|
|
pkg_host_apiproxy["host-apiproxy"]
|
|
|
|
|
pkg_host_directory_picker["host-directory-picker"]
|
|
|
|
|
pkg_host_directory_picker_auto["host-directory-picker-auto"]
|
|
|
|
|
pkg_host_directory_picker_browse["host-directory-picker-browse"]
|
|
|
|
|
pkg_host_directory_picker_native["host-directory-picker-native"]
|
|
|
|
|
pkg_host_webserver["host-webserver"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_interaction["packages/interaction"]
|
|
|
|
|
pkg_commands["commands"]
|
|
|
|
|
pkg_permission["permission"]
|
|
|
|
|
pkg_tool_ask_user["tool-ask-user"]
|
|
|
|
|
pkg_user_approval["user-approval"]
|
|
|
|
|
pkg_user_interaction["user-interaction"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_lsp["packages/lsp"]
|
|
|
|
|
pkg_lsp["lsp"]
|
|
|
|
|
pkg_lsp_local["lsp-local"]
|
|
|
|
|
pkg_tool_lsp["tool-lsp"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_mcp["packages/mcp"]
|
|
|
|
|
pkg_mcp_client["mcp-client"]
|
|
|
|
|
end
|
2026-08-09 20:28:03 +08:00
|
|
|
subgraph group_preset["packages/preset"]
|
|
|
|
|
pkg_agent_presets["agent-presets"]
|
2026-08-09 20:33:55 +08:00
|
|
|
pkg_persona["persona"]
|
2026-08-09 20:28:03 +08:00
|
|
|
end
|
2026-08-09 11:02:16 +08:00
|
|
|
subgraph group_pty["packages/pty"]
|
|
|
|
|
pkg_pty["pty"]
|
|
|
|
|
pkg_pty_local["pty-local"]
|
|
|
|
|
pkg_tool_bash_persistent["tool-bash-persistent"]
|
|
|
|
|
pkg_tool_pty["tool-pty"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_sandbox["packages/sandbox"]
|
|
|
|
|
pkg_sandbox["sandbox"]
|
|
|
|
|
pkg_sandbox_local["sandbox-local"]
|
|
|
|
|
pkg_sandbox_policy["sandbox-policy"]
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_sandbox_windows_acl["sandbox-windows-acl"]
|
2026-08-09 11:02:16 +08:00
|
|
|
end
|
2026-08-11 19:05:13 +08:00
|
|
|
subgraph group_schedule["packages/schedule"]
|
|
|
|
|
pkg_tool_schedule["tool-schedule"]
|
2026-08-09 11:02:16 +08:00
|
|
|
end
|
2026-08-11 14:20:53 +08:00
|
|
|
subgraph group_sdk["packages/sdk"]
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_jsonrpc["jsonrpc"]
|
|
|
|
|
pkg_sdk_client["sdk-client"]
|
|
|
|
|
pkg_sdk_protocol["sdk-protocol"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_self_modification["packages/self-modification"]
|
|
|
|
|
pkg_tool_cordis["tool-cordis"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_session["packages/session"]
|
|
|
|
|
pkg_session_checkpoint_policy["session-checkpoint-policy"]
|
|
|
|
|
pkg_session_persistence["session-persistence"]
|
|
|
|
|
pkg_session_persistence_jsonl["session-persistence-jsonl"]
|
|
|
|
|
pkg_session_persistence_sqlite["session-persistence-sqlite"]
|
|
|
|
|
pkg_session_projection["session-projection"]
|
|
|
|
|
pkg_session_projection_cache["session-projection-cache"]
|
|
|
|
|
pkg_session_telemetry["session-telemetry"]
|
|
|
|
|
pkg_session_telemetry_otel["session-telemetry-otel"]
|
|
|
|
|
pkg_session_title["session-title"]
|
|
|
|
|
pkg_session_title_all_messages_llm["session-title-all-messages-llm"]
|
|
|
|
|
pkg_session_title_first_message_llm["session-title-first-message-llm"]
|
|
|
|
|
pkg_session_title_llm["session-title-llm"]
|
2026-08-07 16:38:54 +08:00
|
|
|
pkg_user_id["user-id"]
|
2026-08-09 11:02:16 +08:00
|
|
|
end
|
|
|
|
|
subgraph group_settings["packages/settings"]
|
|
|
|
|
pkg_settings["settings"]
|
|
|
|
|
pkg_settings_local["settings-local"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_storage["packages/storage"]
|
|
|
|
|
pkg_storage["storage"]
|
|
|
|
|
pkg_storage_domain["storage-domain"]
|
|
|
|
|
pkg_storage_json["storage-json"]
|
|
|
|
|
pkg_storage_sqlite["storage-sqlite"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_subprocess["packages/subprocess"]
|
|
|
|
|
pkg_subprocess["subprocess"]
|
|
|
|
|
pkg_subprocess_local["subprocess-local"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_tasks["packages/tasks"]
|
|
|
|
|
pkg_tasks["tasks"]
|
|
|
|
|
pkg_tasks_local["tasks-local"]
|
|
|
|
|
pkg_tool_tasks["tool-tasks"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_typert["packages/typert"]
|
|
|
|
|
pkg_type_meta["type-meta"]
|
|
|
|
|
pkg_typert_generator["typert-generator"]
|
|
|
|
|
pkg_typert_loader["typert-loader"]
|
|
|
|
|
pkg_typert_registry["typert-registry"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_workflow["packages/workflow"]
|
|
|
|
|
pkg_tool_ralph["tool-ralph"]
|
|
|
|
|
pkg_tool_workflow["tool-workflow"]
|
|
|
|
|
pkg_workflow["workflow"]
|
|
|
|
|
pkg_workflow_workerthread["workflow-workerthread"]
|
|
|
|
|
end
|
|
|
|
|
subgraph group_workspace["packages/workspace"]
|
|
|
|
|
pkg_workspace["workspace"]
|
|
|
|
|
end
|
|
|
|
|
pkg_atomic_write --> pkg_invariants
|
|
|
|
|
pkg_brand --> pkg_invariants
|
|
|
|
|
pkg_environment --> pkg_invariants
|
|
|
|
|
pkg_native_command --> pkg_invariants
|
|
|
|
|
pkg_paths --> pkg_invariants
|
|
|
|
|
pkg_retention --> pkg_invariants
|
|
|
|
|
pkg_timeout --> pkg_invariants
|
|
|
|
|
pkg_scope --> pkg_invariants
|
|
|
|
|
pkg_llm_mock_server --> pkg_invariants
|
2026-08-06 20:52:26 +08:00
|
|
|
pkg_cmdline --> pkg_invariants
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_base --> pkg_invariants
|
|
|
|
|
pkg_client_modules --> pkg_invariants
|
|
|
|
|
pkg_client_schema_form --> pkg_invariants
|
2026-08-11 18:23:36 +08:00
|
|
|
pkg_client_ui_attachment --> pkg_invariants
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_client_ui_primitives --> pkg_invariants
|
|
|
|
|
pkg_client_ui_slots --> pkg_invariants
|
|
|
|
|
pkg_client_web --> pkg_invariants
|
|
|
|
|
pkg_client_web_react --> pkg_invariants
|
|
|
|
|
pkg_code_runtime --> pkg_invariants
|
|
|
|
|
pkg_e2b --> pkg_invariants
|
|
|
|
|
pkg_jsonrpc_demo --> pkg_invariants
|
|
|
|
|
pkg_host_directory_picker --> pkg_invariants
|
|
|
|
|
pkg_host_webserver --> pkg_invariants
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_sandbox_windows_acl --> pkg_invariants
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_storage --> pkg_invariants
|
|
|
|
|
pkg_subprocess --> pkg_invariants
|
|
|
|
|
pkg_type_meta --> pkg_invariants
|
|
|
|
|
pkg_typert_generator --> pkg_invariants
|
|
|
|
|
pkg_typert_registry --> pkg_invariants
|
2026-08-10 11:08:35 +08:00
|
|
|
pkg_attachment --> pkg_brand
|
|
|
|
|
pkg_attachment --> pkg_invariants
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_client_connection --> pkg_host_webserver
|
|
|
|
|
pkg_client_connection --> pkg_invariants
|
|
|
|
|
pkg_client_hmr --> pkg_client_modules
|
|
|
|
|
pkg_client_hmr --> pkg_host_webserver
|
|
|
|
|
pkg_client_hmr --> pkg_invariants
|
|
|
|
|
pkg_credentials --> pkg_brand
|
|
|
|
|
pkg_credentials --> pkg_invariants
|
|
|
|
|
pkg_subprocess_e2b --> pkg_e2b
|
|
|
|
|
pkg_subprocess_e2b --> pkg_invariants
|
|
|
|
|
pkg_subprocess_e2b --> pkg_subprocess
|
|
|
|
|
pkg_subprocess_e2b --> pkg_timeout
|
|
|
|
|
pkg_frontend_static --> pkg_host_webserver
|
|
|
|
|
pkg_frontend_static --> pkg_invariants
|
2026-08-07 16:38:54 +08:00
|
|
|
pkg_user_id --> pkg_brand
|
|
|
|
|
pkg_user_id --> pkg_invariants
|
|
|
|
|
pkg_user_id --> pkg_paths
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_settings --> pkg_brand
|
|
|
|
|
pkg_settings --> pkg_invariants
|
|
|
|
|
pkg_storage_domain --> pkg_invariants
|
|
|
|
|
pkg_storage_domain --> pkg_storage
|
|
|
|
|
pkg_storage_json --> pkg_invariants
|
|
|
|
|
pkg_storage_json --> pkg_storage
|
|
|
|
|
pkg_storage_sqlite --> pkg_invariants
|
|
|
|
|
pkg_storage_sqlite --> pkg_storage
|
|
|
|
|
pkg_subprocess_local --> pkg_invariants
|
|
|
|
|
pkg_subprocess_local --> pkg_subprocess
|
|
|
|
|
pkg_subprocess_local --> pkg_timeout
|
|
|
|
|
pkg_typert_loader --> pkg_invariants
|
|
|
|
|
pkg_typert_loader --> pkg_typert_registry
|
2026-08-10 11:08:35 +08:00
|
|
|
pkg_llm --> pkg_attachment
|
|
|
|
|
pkg_llm --> pkg_brand
|
|
|
|
|
pkg_llm --> pkg_invariants
|
|
|
|
|
pkg_llm --> pkg_timeout
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_api_gateway --> pkg_client_connection
|
|
|
|
|
pkg_api_gateway --> pkg_invariants
|
|
|
|
|
pkg_api_gateway --> pkg_typert_registry
|
2026-08-10 11:08:35 +08:00
|
|
|
pkg_attachment_local --> pkg_attachment
|
|
|
|
|
pkg_attachment_local --> pkg_invariants
|
|
|
|
|
pkg_attachment_local --> pkg_paths
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_credentials_local --> pkg_atomic_write
|
|
|
|
|
pkg_credentials_local --> pkg_credentials
|
|
|
|
|
pkg_credentials_local --> pkg_environment
|
|
|
|
|
pkg_credentials_local --> pkg_invariants
|
|
|
|
|
pkg_credentials_local --> pkg_paths
|
|
|
|
|
pkg_settings_local --> pkg_atomic_write
|
|
|
|
|
pkg_settings_local --> pkg_invariants
|
|
|
|
|
pkg_settings_local --> pkg_paths
|
|
|
|
|
pkg_settings_local --> pkg_settings
|
2026-08-10 11:08:35 +08:00
|
|
|
pkg_llm_deepseek --> pkg_credentials
|
|
|
|
|
pkg_llm_deepseek --> pkg_environment
|
|
|
|
|
pkg_llm_deepseek --> pkg_invariants
|
|
|
|
|
pkg_llm_deepseek --> pkg_llm
|
|
|
|
|
pkg_llm_deepseek --> pkg_settings
|
|
|
|
|
pkg_llm_deepseek --> pkg_timeout
|
2026-08-11 14:55:39 +08:00
|
|
|
pkg_llm_deepseek --> pkg_user_id
|
2026-08-10 11:08:35 +08:00
|
|
|
pkg_llm_pi_ai --> pkg_attachment
|
|
|
|
|
pkg_llm_pi_ai --> pkg_credentials
|
|
|
|
|
pkg_llm_pi_ai --> pkg_environment
|
|
|
|
|
pkg_llm_pi_ai --> pkg_invariants
|
|
|
|
|
pkg_llm_pi_ai --> pkg_llm
|
|
|
|
|
pkg_llm_pi_ai --> pkg_settings
|
|
|
|
|
pkg_llm_pi_ai --> pkg_timeout
|
|
|
|
|
pkg_session --> pkg_brand
|
|
|
|
|
pkg_session --> pkg_invariants
|
|
|
|
|
pkg_session --> pkg_llm
|
|
|
|
|
pkg_session --> pkg_scope
|
|
|
|
|
pkg_session --> pkg_type_meta
|
|
|
|
|
pkg_system_prompt --> pkg_invariants
|
|
|
|
|
pkg_system_prompt --> pkg_llm
|
|
|
|
|
pkg_system_prompt --> pkg_scope
|
|
|
|
|
pkg_skill --> pkg_invariants
|
|
|
|
|
pkg_skill --> pkg_llm
|
2026-08-10 12:33:36 +08:00
|
|
|
pkg_skill --> pkg_scope
|
2026-08-10 11:08:35 +08:00
|
|
|
pkg_web --> pkg_invariants
|
|
|
|
|
pkg_web --> pkg_llm
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_client_runtime --> pkg_api_gateway
|
|
|
|
|
pkg_client_runtime --> pkg_invariants
|
|
|
|
|
pkg_client_runtime --> pkg_type_meta
|
|
|
|
|
pkg_client_runtime --> pkg_typert_registry
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_lsp --> pkg_brand
|
|
|
|
|
pkg_lsp --> pkg_invariants
|
|
|
|
|
pkg_lsp --> pkg_llm
|
|
|
|
|
pkg_agent --> pkg_invariants
|
|
|
|
|
pkg_agent --> pkg_llm
|
|
|
|
|
pkg_agent --> pkg_scope
|
|
|
|
|
pkg_agent --> pkg_session
|
|
|
|
|
pkg_agent --> pkg_system_prompt
|
|
|
|
|
pkg_agent --> pkg_type_meta
|
|
|
|
|
pkg_skill_badge --> pkg_invariants
|
|
|
|
|
pkg_skill_badge --> pkg_skill
|
|
|
|
|
pkg_web_fetch_local --> pkg_invariants
|
|
|
|
|
pkg_web_fetch_local --> pkg_timeout
|
|
|
|
|
pkg_web_fetch_local --> pkg_web
|
|
|
|
|
pkg_web_search_exa --> pkg_environment
|
|
|
|
|
pkg_web_search_exa --> pkg_invariants
|
|
|
|
|
pkg_web_search_exa --> pkg_web
|
|
|
|
|
pkg_web_search_perplexity --> pkg_environment
|
|
|
|
|
pkg_web_search_perplexity --> pkg_invariants
|
|
|
|
|
pkg_web_search_perplexity --> pkg_web
|
|
|
|
|
pkg_spill --> pkg_brand
|
|
|
|
|
pkg_spill --> pkg_invariants
|
|
|
|
|
pkg_spill --> pkg_llm
|
|
|
|
|
pkg_spill --> pkg_session
|
|
|
|
|
pkg_acp_snapshot --> pkg_invariants
|
|
|
|
|
pkg_acp_snapshot --> pkg_session
|
|
|
|
|
pkg_app_boot --> pkg_environment
|
|
|
|
|
pkg_app_boot --> pkg_invariants
|
|
|
|
|
pkg_app_boot --> pkg_paths
|
|
|
|
|
pkg_app_boot --> pkg_system_prompt
|
|
|
|
|
pkg_code_runtime_worker --> pkg_code_runtime
|
|
|
|
|
pkg_code_runtime_worker --> pkg_invariants
|
|
|
|
|
pkg_code_runtime_worker --> pkg_session
|
|
|
|
|
pkg_code_runtime_worker --> pkg_timeout
|
2026-08-09 20:33:55 +08:00
|
|
|
pkg_persona --> pkg_invariants
|
|
|
|
|
pkg_persona --> pkg_system_prompt
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_sandbox --> pkg_invariants
|
|
|
|
|
pkg_sandbox --> pkg_llm
|
|
|
|
|
pkg_sandbox --> pkg_session
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_session_persistence --> pkg_brand
|
|
|
|
|
pkg_session_persistence --> pkg_invariants
|
|
|
|
|
pkg_session_persistence --> pkg_session
|
|
|
|
|
pkg_session_persistence --> pkg_timeout
|
|
|
|
|
pkg_session_projection --> pkg_invariants
|
|
|
|
|
pkg_session_projection --> pkg_session
|
|
|
|
|
pkg_llm_retry --> pkg_agent
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_llm_retry --> pkg_brand
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_llm_retry --> pkg_invariants
|
|
|
|
|
pkg_llm_retry --> pkg_llm
|
|
|
|
|
pkg_llm_retry --> pkg_session
|
|
|
|
|
pkg_llm_retry --> pkg_timeout
|
2026-08-09 15:17:35 +08:00
|
|
|
pkg_agent_default_model --> pkg_agent
|
|
|
|
|
pkg_agent_default_model --> pkg_invariants
|
|
|
|
|
pkg_agent_default_model --> pkg_llm
|
|
|
|
|
pkg_agent_default_model --> pkg_settings
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_goal --> pkg_agent
|
|
|
|
|
pkg_goal --> pkg_brand
|
|
|
|
|
pkg_goal --> pkg_invariants
|
|
|
|
|
pkg_goal --> pkg_llm
|
|
|
|
|
pkg_goal --> pkg_scope
|
|
|
|
|
pkg_goal --> pkg_session
|
|
|
|
|
pkg_goal --> pkg_session_projection
|
|
|
|
|
pkg_goal --> pkg_type_meta
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_bash --> pkg_invariants
|
|
|
|
|
pkg_bash --> pkg_sandbox
|
2026-08-10 16:10:33 +08:00
|
|
|
pkg_bash --> pkg_settings
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_bash --> pkg_subprocess
|
|
|
|
|
pkg_fs --> pkg_brand
|
|
|
|
|
pkg_fs --> pkg_invariants
|
|
|
|
|
pkg_fs --> pkg_llm
|
|
|
|
|
pkg_fs --> pkg_sandbox
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_web_search_deepseek --> pkg_agent
|
|
|
|
|
pkg_web_search_deepseek --> pkg_credentials
|
|
|
|
|
pkg_web_search_deepseek --> pkg_environment
|
|
|
|
|
pkg_web_search_deepseek --> pkg_invariants
|
|
|
|
|
pkg_web_search_deepseek --> pkg_session
|
2026-08-10 18:06:27 +08:00
|
|
|
pkg_web_search_deepseek --> pkg_settings
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_web_search_deepseek --> pkg_web
|
|
|
|
|
pkg_spill_local --> pkg_invariants
|
|
|
|
|
pkg_spill_local --> pkg_spill
|
|
|
|
|
pkg_loader_smoke --> pkg_agent
|
|
|
|
|
pkg_loader_smoke --> pkg_invariants
|
|
|
|
|
pkg_loader_smoke --> pkg_llm
|
|
|
|
|
pkg_loader_smoke --> pkg_session
|
|
|
|
|
pkg_time_context --> pkg_agent
|
|
|
|
|
pkg_time_context --> pkg_invariants
|
|
|
|
|
pkg_time_context --> pkg_session
|
2026-08-10 11:28:38 -07:00
|
|
|
pkg_message_feedback --> pkg_brand
|
|
|
|
|
pkg_message_feedback --> pkg_invariants
|
|
|
|
|
pkg_message_feedback --> pkg_llm
|
|
|
|
|
pkg_message_feedback --> pkg_session
|
|
|
|
|
pkg_message_feedback --> pkg_session_persistence
|
|
|
|
|
pkg_message_feedback --> pkg_storage_domain
|
|
|
|
|
pkg_message_feedback --> pkg_type_meta
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_commands --> pkg_agent
|
|
|
|
|
pkg_commands --> pkg_brand
|
|
|
|
|
pkg_commands --> pkg_invariants
|
|
|
|
|
pkg_commands --> pkg_scope
|
|
|
|
|
pkg_commands --> pkg_session
|
|
|
|
|
pkg_user_approval --> pkg_agent
|
|
|
|
|
pkg_user_approval --> pkg_brand
|
|
|
|
|
pkg_user_approval --> pkg_invariants
|
|
|
|
|
pkg_user_approval --> pkg_llm
|
|
|
|
|
pkg_user_approval --> pkg_scope
|
|
|
|
|
pkg_user_approval --> pkg_session
|
|
|
|
|
pkg_user_approval --> pkg_system_prompt
|
|
|
|
|
pkg_user_interaction --> pkg_agent
|
|
|
|
|
pkg_user_interaction --> pkg_invariants
|
|
|
|
|
pkg_user_interaction --> pkg_llm
|
fix(preset): keep the token meter host-plane and name unjoined agents
Moving the agent plane behind presets left two readers on the wrong side of
the host/agent line.
`dsh-token-meter` was disabled on the host and mounted inside each preset's
`compaction` realm, but its three projection units register into the
process-wide `sessionProjections` table. A unit registered from one preset
answers for every session, so whether a `minimal` session showed a context
meter depended on whether some other session had mounted `standard` since
boot, and a process that only ever ran `minimal` showed none. The meter takes
no configuration, keys every fold by Session, and registers no tool or prompt
section, so it returns to the host composition and leaves the presets'
`isolate` map; the realm and `compact-basic` stay, because what a preset
chooses is whether its agent compacts, not whether its tokens are counted.
Nothing named an agent that joined no preset. The join is a scope-parent link,
and without it the tools, prompt-section, and skill views resolve the empty
global layer: the agent publishes, the turn runs, and the model receives
nothing. `AgentPresets` now logs one warning per such agent while a roster is
configured, and the invariant companion fails outright — at
`system-prompt/assemble` rather than at publication, because an unjoined agent
is legal until it addresses a model and `recompose` binds exactly such an
agent. The warning stays advisory: a synchronous `agent/created` throw vetoes
publication, and the ACP bridge, SDK server, and headless bundle all create an
unjoined agent today.
Three limits are recorded rather than fixed: projection key presence is not a
per-session capability signal, a superseded standing generation is never
reclaimed, and a `cordis_mount` temporary plugin belongs to the composition
rather than the session that mounted it.
Fixes #2203
2026-08-10 22:36:06 +08:00
|
|
|
pkg_agent_presets --> pkg_agent
|
|
|
|
|
pkg_agent_presets --> pkg_atomic_write
|
|
|
|
|
pkg_agent_presets --> pkg_invariants
|
|
|
|
|
pkg_agent_presets --> pkg_paths
|
|
|
|
|
pkg_agent_presets --> pkg_scope
|
|
|
|
|
pkg_agent_presets --> pkg_session
|
|
|
|
|
pkg_agent_presets --> pkg_settings
|
|
|
|
|
pkg_agent_presets --> pkg_system_prompt
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_pty --> pkg_agent
|
|
|
|
|
pkg_pty --> pkg_brand
|
|
|
|
|
pkg_pty --> pkg_invariants
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_sandbox_local --> pkg_invariants
|
|
|
|
|
pkg_sandbox_local --> pkg_llm
|
|
|
|
|
pkg_sandbox_local --> pkg_sandbox
|
|
|
|
|
pkg_sandbox_local --> pkg_session
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_sandbox_policy --> pkg_agent
|
|
|
|
|
pkg_sandbox_policy --> pkg_invariants
|
|
|
|
|
pkg_sandbox_policy --> pkg_sandbox
|
|
|
|
|
pkg_sandbox_policy --> pkg_session
|
|
|
|
|
pkg_sandbox_policy --> pkg_system_prompt
|
|
|
|
|
pkg_session_persistence_jsonl --> pkg_invariants
|
|
|
|
|
pkg_session_persistence_jsonl --> pkg_session
|
|
|
|
|
pkg_session_persistence_jsonl --> pkg_session_persistence
|
|
|
|
|
pkg_session_persistence_sqlite --> pkg_invariants
|
|
|
|
|
pkg_session_persistence_sqlite --> pkg_session
|
|
|
|
|
pkg_session_persistence_sqlite --> pkg_session_persistence
|
|
|
|
|
pkg_session_projection_cache --> pkg_invariants
|
|
|
|
|
pkg_session_projection_cache --> pkg_session
|
|
|
|
|
pkg_session_projection_cache --> pkg_session_persistence
|
|
|
|
|
pkg_session_projection_cache --> pkg_session_projection
|
|
|
|
|
pkg_session_projection_cache --> pkg_storage_domain
|
|
|
|
|
pkg_session_telemetry --> pkg_agent
|
|
|
|
|
pkg_session_telemetry --> pkg_invariants
|
|
|
|
|
pkg_session_telemetry --> pkg_session
|
|
|
|
|
pkg_session_title --> pkg_brand
|
|
|
|
|
pkg_session_title --> pkg_invariants
|
|
|
|
|
pkg_session_title --> pkg_llm
|
|
|
|
|
pkg_session_title --> pkg_session
|
|
|
|
|
pkg_session_title --> pkg_session_projection
|
|
|
|
|
pkg_tasks --> pkg_agent
|
|
|
|
|
pkg_tasks --> pkg_brand
|
|
|
|
|
pkg_tasks --> pkg_invariants
|
|
|
|
|
pkg_tasks --> pkg_session
|
|
|
|
|
pkg_workflow --> pkg_agent
|
|
|
|
|
pkg_workflow --> pkg_brand
|
|
|
|
|
pkg_workflow --> pkg_invariants
|
|
|
|
|
pkg_workflow --> pkg_llm
|
|
|
|
|
pkg_workflow --> pkg_session
|
|
|
|
|
pkg_workspace --> pkg_brand
|
|
|
|
|
pkg_workspace --> pkg_invariants
|
|
|
|
|
pkg_workspace --> pkg_session
|
|
|
|
|
pkg_workspace --> pkg_session_persistence
|
|
|
|
|
pkg_workspace --> pkg_storage
|
|
|
|
|
pkg_workspace --> pkg_storage_domain
|
|
|
|
|
pkg_tools --> pkg_agent
|
|
|
|
|
pkg_tools --> pkg_code_runtime
|
|
|
|
|
pkg_tools --> pkg_invariants
|
|
|
|
|
pkg_tools --> pkg_llm
|
|
|
|
|
pkg_tools --> pkg_scope
|
|
|
|
|
pkg_tools --> pkg_session
|
|
|
|
|
pkg_tools --> pkg_system_prompt
|
|
|
|
|
pkg_tools --> pkg_user_approval
|
|
|
|
|
pkg_command_goal --> pkg_commands
|
|
|
|
|
pkg_command_goal --> pkg_goal
|
|
|
|
|
pkg_command_goal --> pkg_invariants
|
|
|
|
|
pkg_goal_session --> pkg_agent
|
|
|
|
|
pkg_goal_session --> pkg_goal
|
|
|
|
|
pkg_goal_session --> pkg_invariants
|
|
|
|
|
pkg_goal_session --> pkg_llm
|
|
|
|
|
pkg_goal_session --> pkg_session
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_bash_local --> pkg_bash
|
|
|
|
|
pkg_bash_local --> pkg_invariants
|
2026-08-10 16:10:33 +08:00
|
|
|
pkg_bash_local --> pkg_settings
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_bash_local --> pkg_subprocess
|
|
|
|
|
pkg_bash_local --> pkg_timeout
|
|
|
|
|
pkg_pwsh_local --> pkg_bash
|
|
|
|
|
pkg_pwsh_local --> pkg_invariants
|
2026-08-10 16:10:33 +08:00
|
|
|
pkg_pwsh_local --> pkg_settings
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_pwsh_local --> pkg_subprocess
|
|
|
|
|
pkg_pwsh_local --> pkg_timeout
|
|
|
|
|
pkg_fs_local --> pkg_fs
|
|
|
|
|
pkg_fs_local --> pkg_invariants
|
|
|
|
|
pkg_fs_policy --> pkg_fs
|
|
|
|
|
pkg_fs_policy --> pkg_invariants
|
|
|
|
|
pkg_skill_local --> pkg_fs
|
|
|
|
|
pkg_skill_local --> pkg_invariants
|
|
|
|
|
pkg_skill_local --> pkg_paths
|
|
|
|
|
pkg_skill_local --> pkg_skill
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_compact --> pkg_brand
|
|
|
|
|
pkg_compact --> pkg_commands
|
|
|
|
|
pkg_compact --> pkg_invariants
|
|
|
|
|
pkg_compact --> pkg_llm
|
|
|
|
|
pkg_compact --> pkg_session
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_hook_protocol --> pkg_bash
|
|
|
|
|
pkg_hook_protocol --> pkg_invariants
|
|
|
|
|
pkg_hook_protocol --> pkg_session
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_session_query --> pkg_brand
|
|
|
|
|
pkg_session_query --> pkg_invariants
|
|
|
|
|
pkg_session_query --> pkg_llm
|
|
|
|
|
pkg_session_query --> pkg_session
|
|
|
|
|
pkg_session_query --> pkg_session_persistence
|
|
|
|
|
pkg_session_query --> pkg_session_title
|
|
|
|
|
pkg_acp --> pkg_agent
|
|
|
|
|
pkg_acp --> pkg_invariants
|
|
|
|
|
pkg_acp --> pkg_session
|
|
|
|
|
pkg_acp --> pkg_user_approval
|
|
|
|
|
pkg_api_remotes --> pkg_agent
|
2026-08-11 17:12:41 +08:00
|
|
|
pkg_api_remotes --> pkg_agent_presets
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_api_remotes --> pkg_api_gateway
|
|
|
|
|
pkg_api_remotes --> pkg_commands
|
|
|
|
|
pkg_api_remotes --> pkg_credentials
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_api_remotes --> pkg_goal
|
|
|
|
|
pkg_api_remotes --> pkg_invariants
|
2026-08-11 17:12:41 +08:00
|
|
|
pkg_api_remotes --> pkg_llm
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_api_remotes --> pkg_session
|
|
|
|
|
pkg_api_remotes --> pkg_session_persistence
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_api_remotes --> pkg_settings
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_api_remotes --> pkg_typert_registry
|
2026-08-09 15:17:35 +08:00
|
|
|
pkg_headless --> pkg_agent
|
|
|
|
|
pkg_headless --> pkg_agent_default_model
|
|
|
|
|
pkg_headless --> pkg_invariants
|
|
|
|
|
pkg_headless --> pkg_llm
|
|
|
|
|
pkg_headless --> pkg_session
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_tmux_context --> pkg_agent
|
|
|
|
|
pkg_tmux_context --> pkg_bash
|
|
|
|
|
pkg_tmux_context --> pkg_invariants
|
|
|
|
|
pkg_tmux_context --> pkg_session
|
|
|
|
|
pkg_fs_e2b --> pkg_e2b
|
|
|
|
|
pkg_fs_e2b --> pkg_fs
|
|
|
|
|
pkg_fs_e2b --> pkg_invariants
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_command_feedback --> pkg_commands
|
|
|
|
|
pkg_command_feedback --> pkg_invariants
|
|
|
|
|
pkg_command_feedback --> pkg_session
|
2026-08-10 18:33:49 +08:00
|
|
|
pkg_command_feedback --> pkg_session_telemetry
|
2026-08-07 16:38:54 +08:00
|
|
|
pkg_command_feedback --> pkg_user_id
|
fix(preset): keep the token meter host-plane and name unjoined agents
Moving the agent plane behind presets left two readers on the wrong side of
the host/agent line.
`dsh-token-meter` was disabled on the host and mounted inside each preset's
`compaction` realm, but its three projection units register into the
process-wide `sessionProjections` table. A unit registered from one preset
answers for every session, so whether a `minimal` session showed a context
meter depended on whether some other session had mounted `standard` since
boot, and a process that only ever ran `minimal` showed none. The meter takes
no configuration, keys every fold by Session, and registers no tool or prompt
section, so it returns to the host composition and leaves the presets'
`isolate` map; the realm and `compact-basic` stay, because what a preset
chooses is whether its agent compacts, not whether its tokens are counted.
Nothing named an agent that joined no preset. The join is a scope-parent link,
and without it the tools, prompt-section, and skill views resolve the empty
global layer: the agent publishes, the turn runs, and the model receives
nothing. `AgentPresets` now logs one warning per such agent while a roster is
configured, and the invariant companion fails outright — at
`system-prompt/assemble` rather than at publication, because an unjoined agent
is legal until it addresses a model and `recompose` binds exactly such an
agent. The warning stays advisory: a synchronous `agent/created` throw vetoes
publication, and the ACP bridge, SDK server, and headless bundle all create an
unjoined agent today.
Three limits are recorded rather than fixed: projection key presence is not a
per-session capability signal, a superseded standing generation is never
reclaimed, and a `cordis_mount` temporary plugin belongs to the composition
rather than the session that mounted it.
Fixes #2203
2026-08-10 22:36:06 +08:00
|
|
|
pkg_host_apiproxy --> pkg_agent_presets
|
|
|
|
|
pkg_host_apiproxy --> pkg_invariants
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_permission --> pkg_bash
|
|
|
|
|
pkg_permission --> pkg_commands
|
|
|
|
|
pkg_permission --> pkg_invariants
|
|
|
|
|
pkg_permission --> pkg_sandbox
|
|
|
|
|
pkg_permission --> pkg_sandbox_policy
|
|
|
|
|
pkg_permission --> pkg_session
|
|
|
|
|
pkg_permission --> pkg_session_projection
|
|
|
|
|
pkg_permission --> pkg_settings
|
|
|
|
|
pkg_permission --> pkg_user_approval
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_lsp_local --> pkg_brand
|
|
|
|
|
pkg_lsp_local --> pkg_fs
|
|
|
|
|
pkg_lsp_local --> pkg_invariants
|
|
|
|
|
pkg_lsp_local --> pkg_llm
|
|
|
|
|
pkg_lsp_local --> pkg_lsp
|
|
|
|
|
pkg_lsp_local --> pkg_subprocess
|
|
|
|
|
pkg_lsp_local --> pkg_timeout
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_pty_local --> pkg_agent
|
|
|
|
|
pkg_pty_local --> pkg_invariants
|
|
|
|
|
pkg_pty_local --> pkg_pty
|
|
|
|
|
pkg_pty_local --> pkg_sandbox
|
|
|
|
|
pkg_pty_local --> pkg_sandbox_policy
|
|
|
|
|
pkg_pty_local --> pkg_session
|
|
|
|
|
pkg_pty_local --> pkg_subprocess
|
|
|
|
|
pkg_session_title_llm --> pkg_invariants
|
|
|
|
|
pkg_session_title_llm --> pkg_llm
|
|
|
|
|
pkg_session_title_llm --> pkg_session
|
|
|
|
|
pkg_session_title_llm --> pkg_session_title
|
|
|
|
|
pkg_session_title_llm --> pkg_timeout
|
|
|
|
|
pkg_tasks_local --> pkg_agent
|
|
|
|
|
pkg_tasks_local --> pkg_invariants
|
fix(tasks-local): layer control surfaces and listeners by registering scope
One host registry serves every composition in the process, so its two
service-wide collections answered per-owner questions process-wide. `start()`
asked only whether SOME surface was attached, so an agent whose own composition
loads no `tool-tasks` could start work it has no tool to collect or stop as soon
as any other preset attached one — and the answer changed depending on which
sessions happened to be open. `settle()` walked every registered listener, so a
task settling without a waiter injected one completion notice per mounted
preset into the same owner.
Both collections now sit in `ScopedLayers`, the layered-registry primitive
`tools` and `skills` already use: a registration files into its registering
context's scope, and a read unions the global layer with the owner's scope
chain. A surface or listener registered from an unscoped context lands in the
global layer and serves every owner, which is exactly the host-plane
composition's own controls, so the TUI path is unchanged without a special
case.
This supersedes the consumer-side filter in the previous commit. That filter
produced the right notices but sat in the wrong layer: it left the `start()`
gate process-wide, it could not be enforced against a producer that resolves
the registry directly, and it made a Consumer carry scope knowledge that the
other layered registries keep in the registry. `tool-tasks` is scope-agnostic
again and the `dsh-scope` edge moves to `tasks-local`.
`start()`'s refusal is now owner-relative, so its model-visible text names the
agent rather than the process. The shipped `minimal` preset keeps
`enableRunInBackground: false`, no longer as the safety boundary — the registry
owns that now — but so an agent that could never collect a task is not offered
the parameter at all.
Refs #2141
2026-08-10 16:12:41 +08:00
|
|
|
pkg_tasks_local --> pkg_scope
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tasks_local --> pkg_tasks
|
|
|
|
|
pkg_tasks_local --> pkg_timeout
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_token_meter --> pkg_compact
|
|
|
|
|
pkg_token_meter --> pkg_invariants
|
|
|
|
|
pkg_token_meter --> pkg_llm
|
|
|
|
|
pkg_token_meter --> pkg_session
|
|
|
|
|
pkg_token_meter --> pkg_session_projection
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_agent_loop --> pkg_agent
|
|
|
|
|
pkg_agent_loop --> pkg_invariants
|
|
|
|
|
pkg_agent_loop --> pkg_llm
|
|
|
|
|
pkg_agent_loop --> pkg_scope
|
|
|
|
|
pkg_agent_loop --> pkg_session
|
|
|
|
|
pkg_agent_loop --> pkg_session_persistence
|
2026-08-10 18:00:03 +08:00
|
|
|
pkg_agent_loop --> pkg_settings
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_agent_loop --> pkg_system_prompt
|
|
|
|
|
pkg_agent_loop --> pkg_tools
|
2026-08-09 20:41:55 +08:00
|
|
|
pkg_agent_tool_mode --> pkg_invariants
|
|
|
|
|
pkg_agent_tool_mode --> pkg_tools
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tool_goal --> pkg_agent
|
|
|
|
|
pkg_tool_goal --> pkg_goal
|
|
|
|
|
pkg_tool_goal --> pkg_invariants
|
|
|
|
|
pkg_tool_goal --> pkg_llm
|
|
|
|
|
pkg_tool_goal --> pkg_session
|
|
|
|
|
pkg_tool_goal --> pkg_system_prompt
|
|
|
|
|
pkg_tool_goal --> pkg_tools
|
|
|
|
|
pkg_bash_env --> pkg_bash
|
|
|
|
|
pkg_bash_env --> pkg_invariants
|
|
|
|
|
pkg_bash_env --> pkg_paths
|
|
|
|
|
pkg_bash_env --> pkg_session_persistence
|
|
|
|
|
pkg_bash_env --> pkg_tools
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_bash_sandbox --> pkg_bash
|
|
|
|
|
pkg_bash_sandbox --> pkg_bash_local
|
|
|
|
|
pkg_bash_sandbox --> pkg_invariants
|
|
|
|
|
pkg_bash_sandbox --> pkg_sandbox
|
|
|
|
|
pkg_bash_sandbox --> pkg_sandbox_policy
|
|
|
|
|
pkg_pwsh_sandbox --> pkg_bash
|
|
|
|
|
pkg_pwsh_sandbox --> pkg_invariants
|
|
|
|
|
pkg_pwsh_sandbox --> pkg_pwsh_local
|
|
|
|
|
pkg_pwsh_sandbox --> pkg_sandbox
|
|
|
|
|
pkg_pwsh_sandbox --> pkg_sandbox_policy
|
|
|
|
|
pkg_fs_sandbox --> pkg_fs
|
|
|
|
|
pkg_fs_sandbox --> pkg_fs_local
|
|
|
|
|
pkg_fs_sandbox --> pkg_invariants
|
|
|
|
|
pkg_fs_sandbox --> pkg_sandbox
|
|
|
|
|
pkg_fs_sandbox --> pkg_sandbox_policy
|
2026-08-10 15:09:07 +08:00
|
|
|
pkg_tool_fs --> pkg_attachment
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tool_fs --> pkg_fs
|
|
|
|
|
pkg_tool_fs --> pkg_invariants
|
|
|
|
|
pkg_tool_fs --> pkg_llm
|
|
|
|
|
pkg_tool_fs --> pkg_sandbox
|
|
|
|
|
pkg_tool_fs --> pkg_sandbox_policy
|
|
|
|
|
pkg_tool_fs --> pkg_session
|
|
|
|
|
pkg_tool_fs --> pkg_system_prompt
|
|
|
|
|
pkg_tool_fs --> pkg_tools
|
|
|
|
|
pkg_tool_fs --> pkg_user_approval
|
|
|
|
|
pkg_tool_fs_search --> pkg_invariants
|
|
|
|
|
pkg_tool_fs_search --> pkg_llm
|
|
|
|
|
pkg_tool_fs_search --> pkg_retention
|
|
|
|
|
pkg_tool_fs_search --> pkg_session
|
|
|
|
|
pkg_tool_fs_search --> pkg_spill
|
|
|
|
|
pkg_tool_fs_search --> pkg_subprocess
|
|
|
|
|
pkg_tool_fs_search --> pkg_system_prompt
|
|
|
|
|
pkg_tool_fs_search --> pkg_timeout
|
|
|
|
|
pkg_tool_fs_search --> pkg_tools
|
|
|
|
|
pkg_tool_str_replace_editor --> pkg_fs
|
|
|
|
|
pkg_tool_str_replace_editor --> pkg_invariants
|
|
|
|
|
pkg_tool_str_replace_editor --> pkg_sandbox
|
|
|
|
|
pkg_tool_str_replace_editor --> pkg_sandbox_policy
|
|
|
|
|
pkg_tool_str_replace_editor --> pkg_tools
|
|
|
|
|
pkg_tool_skill --> pkg_agent
|
|
|
|
|
pkg_tool_skill --> pkg_invariants
|
|
|
|
|
pkg_tool_skill --> pkg_llm
|
|
|
|
|
pkg_tool_skill --> pkg_skill
|
|
|
|
|
pkg_tool_skill --> pkg_tools
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_command_compact --> pkg_commands
|
|
|
|
|
pkg_command_compact --> pkg_compact
|
|
|
|
|
pkg_command_compact --> pkg_invariants
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_subagent --> pkg_agent
|
fix(subagent): compose children from their parent's preset
Tool and prompt-section visibility is inherited along dsh-scope's parent
chain, and an agent's scope key is minted with no parent. Per-session agent
presets moved every model-facing row onto the agent plane and made
AgentPresets.mount() the one thing that binds that link, from the api-proxy's
session create, resume, and fork paths. The two in-process subagent drivers
installed only the per-child persona and tool filter, so a child's scope chain
had length one and its registry view resolved the global layer alone — which
is empty wherever a preset roster is composed. One-shot children reached the
model with no tools, continuable ones with only the host-plane `report`, and
neither carried its parent's persona, workspace context, or skill catalog.
AgentPresets.composeFrom() joins one agent to the standing composition another
already runs on. It is a bind, not a mount: the child gets its parent's exact
generation, so a composition edited since the parent started cannot fork it
onto another one, and it is synchronous, which is what lets a child creation
window use it. applyChildComposition() now takes the parent and performs the
join first, making a child composed without it unrepresentable at the call
sites. childSessionMeta() records the joined id so a cold read rebuilds the
composition the child actually ran under.
The audit that followed found two api-proxy readers on the wrong authority:
presenterScopeFor() and the live-agent branch of assertPresetUnchanged() both
read header.agentPreset, which goes stale the moment a blank session switches
preset. A switched session's cold transcript resolved presenters in the older
composition's layer and silently degraded to generic cards, and the gateway
refused to adopt a live session under the preset it actually runs while
accepting the one it left. Both now resolve through resolveSessionPreset(),
matching the resume branch fifteen lines above. The owning architecture Agent
Note carried the stale claim that the header records what a session runs; it
is corrected to name the header/log pair and its three readers.
Fixes #2165
2026-08-10 17:46:34 +08:00
|
|
|
pkg_subagent --> pkg_agent_presets
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_subagent --> pkg_brand
|
|
|
|
|
pkg_subagent --> pkg_invariants
|
|
|
|
|
pkg_subagent --> pkg_llm
|
2026-08-10 14:52:16 +08:00
|
|
|
pkg_subagent --> pkg_sandbox
|
|
|
|
|
pkg_subagent --> pkg_sandbox_policy
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_subagent --> pkg_scope
|
|
|
|
|
pkg_subagent --> pkg_session
|
|
|
|
|
pkg_subagent --> pkg_session_persistence
|
|
|
|
|
pkg_subagent --> pkg_session_projection
|
|
|
|
|
pkg_subagent --> pkg_session_projection_cache
|
|
|
|
|
pkg_subagent --> pkg_tasks
|
|
|
|
|
pkg_subagent --> pkg_tools
|
2026-08-10 14:52:16 +08:00
|
|
|
pkg_subagent --> pkg_user_approval
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tool_web --> pkg_invariants
|
|
|
|
|
pkg_tool_web --> pkg_llm
|
|
|
|
|
pkg_tool_web --> pkg_system_prompt
|
|
|
|
|
pkg_tool_web --> pkg_tools
|
|
|
|
|
pkg_tool_web --> pkg_web
|
|
|
|
|
pkg_spill_policy --> pkg_invariants
|
|
|
|
|
pkg_spill_policy --> pkg_llm
|
|
|
|
|
pkg_spill_policy --> pkg_retention
|
|
|
|
|
pkg_spill_policy --> pkg_session
|
|
|
|
|
pkg_spill_policy --> pkg_spill
|
|
|
|
|
pkg_spill_policy --> pkg_tools
|
|
|
|
|
pkg_tool_todo --> pkg_agent
|
|
|
|
|
pkg_tool_todo --> pkg_invariants
|
|
|
|
|
pkg_tool_todo --> pkg_session
|
|
|
|
|
pkg_tool_todo --> pkg_session_projection
|
|
|
|
|
pkg_tool_todo --> pkg_tools
|
|
|
|
|
pkg_plan_mode --> pkg_agent
|
|
|
|
|
pkg_plan_mode --> pkg_commands
|
|
|
|
|
pkg_plan_mode --> pkg_invariants
|
|
|
|
|
pkg_plan_mode --> pkg_llm
|
|
|
|
|
pkg_plan_mode --> pkg_session
|
|
|
|
|
pkg_plan_mode --> pkg_session_projection
|
|
|
|
|
pkg_plan_mode --> pkg_system_prompt
|
|
|
|
|
pkg_plan_mode --> pkg_tools
|
|
|
|
|
pkg_plan_mode --> pkg_user_interaction
|
|
|
|
|
pkg_hooks_codex --> pkg_agent
|
|
|
|
|
pkg_hooks_codex --> pkg_hook_protocol
|
|
|
|
|
pkg_hooks_codex --> pkg_invariants
|
|
|
|
|
pkg_hooks_codex --> pkg_llm
|
|
|
|
|
pkg_hooks_codex --> pkg_session
|
|
|
|
|
pkg_hooks_codex --> pkg_session_persistence
|
|
|
|
|
pkg_hooks_codex --> pkg_tools
|
|
|
|
|
pkg_session_query_sqlite --> pkg_invariants
|
|
|
|
|
pkg_session_query_sqlite --> pkg_session
|
|
|
|
|
pkg_session_query_sqlite --> pkg_session_persistence
|
|
|
|
|
pkg_session_query_sqlite --> pkg_session_query
|
|
|
|
|
pkg_tool_session_query --> pkg_invariants
|
|
|
|
|
pkg_tool_session_query --> pkg_llm
|
|
|
|
|
pkg_tool_session_query --> pkg_session
|
|
|
|
|
pkg_tool_session_query --> pkg_session_query
|
|
|
|
|
pkg_tool_session_query --> pkg_system_prompt
|
|
|
|
|
pkg_tool_session_query --> pkg_timeout
|
|
|
|
|
pkg_tool_session_query --> pkg_tools
|
|
|
|
|
pkg_agent_loop_testkit --> pkg_agent
|
|
|
|
|
pkg_agent_loop_testkit --> pkg_invariants
|
|
|
|
|
pkg_agent_loop_testkit --> pkg_llm
|
|
|
|
|
pkg_agent_loop_testkit --> pkg_session
|
|
|
|
|
pkg_agent_loop_testkit --> pkg_system_prompt
|
|
|
|
|
pkg_agent_loop_testkit --> pkg_tools
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_llm_replay --> pkg_compact
|
|
|
|
|
pkg_llm_replay --> pkg_invariants
|
|
|
|
|
pkg_llm_replay --> pkg_llm
|
|
|
|
|
pkg_llm_replay --> pkg_session
|
fix(preset): keep the token meter host-plane and name unjoined agents
Moving the agent plane behind presets left two readers on the wrong side of
the host/agent line.
`dsh-token-meter` was disabled on the host and mounted inside each preset's
`compaction` realm, but its three projection units register into the
process-wide `sessionProjections` table. A unit registered from one preset
answers for every session, so whether a `minimal` session showed a context
meter depended on whether some other session had mounted `standard` since
boot, and a process that only ever ran `minimal` showed none. The meter takes
no configuration, keys every fold by Session, and registers no tool or prompt
section, so it returns to the host composition and leaves the presets'
`isolate` map; the realm and `compact-basic` stay, because what a preset
chooses is whether its agent compacts, not whether its tokens are counted.
Nothing named an agent that joined no preset. The join is a scope-parent link,
and without it the tools, prompt-section, and skill views resolve the empty
global layer: the agent publishes, the turn runs, and the model receives
nothing. `AgentPresets` now logs one warning per such agent while a roster is
configured, and the invariant companion fails outright — at
`system-prompt/assemble` rather than at publication, because an unjoined agent
is legal until it addresses a model and `recompose` binds exactly such an
agent. The warning stays advisory: a synchronous `agent/created` throw vetoes
publication, and the ACP bridge, SDK server, and headless bundle all create an
unjoined agent today.
Three limits are recorded rather than fixed: projection key presence is not a
per-session capability signal, a superseded standing generation is never
reclaimed, and a `cordis_mount` temporary plugin belongs to the composition
rather than the session that mounted it.
Fixes #2203
2026-08-10 22:36:06 +08:00
|
|
|
pkg_client_test_runtime --> pkg_client_runtime
|
|
|
|
|
pkg_client_test_runtime --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_test_runtime --> pkg_client_web_react
|
|
|
|
|
pkg_client_test_runtime --> pkg_host_apiproxy
|
|
|
|
|
pkg_client_test_runtime --> pkg_invariants
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_client_ui_models --> pkg_api_remotes
|
|
|
|
|
pkg_client_ui_models --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_models --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_models --> pkg_client_schema_form
|
|
|
|
|
pkg_client_ui_models --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_models --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_models --> pkg_client_web_react
|
|
|
|
|
pkg_client_ui_models --> pkg_invariants
|
|
|
|
|
pkg_client_ui_settings --> pkg_api_gateway
|
|
|
|
|
pkg_client_ui_settings --> pkg_api_remotes
|
|
|
|
|
pkg_client_ui_settings --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_settings --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_settings --> pkg_client_schema_form
|
|
|
|
|
pkg_client_ui_settings --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_settings --> pkg_invariants
|
|
|
|
|
pkg_client_ui_settings --> pkg_settings
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_session_reference --> pkg_agent
|
|
|
|
|
pkg_session_reference --> pkg_compact
|
|
|
|
|
pkg_session_reference --> pkg_invariants
|
|
|
|
|
pkg_session_reference --> pkg_llm
|
|
|
|
|
pkg_session_reference --> pkg_retention
|
|
|
|
|
pkg_session_reference --> pkg_session
|
|
|
|
|
pkg_session_reference --> pkg_session_query
|
|
|
|
|
pkg_workspace_context --> pkg_agent
|
|
|
|
|
pkg_workspace_context --> pkg_fs
|
|
|
|
|
pkg_workspace_context --> pkg_invariants
|
|
|
|
|
pkg_workspace_context --> pkg_llm
|
|
|
|
|
pkg_workspace_context --> pkg_paths
|
|
|
|
|
pkg_workspace_context --> pkg_session
|
|
|
|
|
pkg_workspace_context --> pkg_tools
|
|
|
|
|
pkg_repeat_tool_guard --> pkg_agent
|
|
|
|
|
pkg_repeat_tool_guard --> pkg_invariants
|
|
|
|
|
pkg_repeat_tool_guard --> pkg_tools
|
|
|
|
|
pkg_timeout_policy --> pkg_invariants
|
|
|
|
|
pkg_timeout_policy --> pkg_llm
|
|
|
|
|
pkg_timeout_policy --> pkg_timeout
|
|
|
|
|
pkg_timeout_policy --> pkg_tools
|
|
|
|
|
pkg_tool_ask_user --> pkg_agent
|
|
|
|
|
pkg_tool_ask_user --> pkg_invariants
|
|
|
|
|
pkg_tool_ask_user --> pkg_tools
|
|
|
|
|
pkg_tool_ask_user --> pkg_user_interaction
|
|
|
|
|
pkg_tool_lsp --> pkg_invariants
|
|
|
|
|
pkg_tool_lsp --> pkg_llm
|
|
|
|
|
pkg_tool_lsp --> pkg_lsp
|
|
|
|
|
pkg_tool_lsp --> pkg_system_prompt
|
|
|
|
|
pkg_tool_lsp --> pkg_timeout
|
|
|
|
|
pkg_tool_lsp --> pkg_tools
|
|
|
|
|
pkg_mcp_client --> pkg_invariants
|
|
|
|
|
pkg_mcp_client --> pkg_llm
|
|
|
|
|
pkg_mcp_client --> pkg_subprocess
|
2026-08-10 18:50:03 +08:00
|
|
|
pkg_mcp_client --> pkg_timeout
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_mcp_client --> pkg_tools
|
|
|
|
|
pkg_tool_bash_persistent --> pkg_agent
|
|
|
|
|
pkg_tool_bash_persistent --> pkg_invariants
|
|
|
|
|
pkg_tool_bash_persistent --> pkg_pty
|
|
|
|
|
pkg_tool_bash_persistent --> pkg_timeout
|
|
|
|
|
pkg_tool_bash_persistent --> pkg_tools
|
|
|
|
|
pkg_tool_pty --> pkg_agent
|
|
|
|
|
pkg_tool_pty --> pkg_invariants
|
|
|
|
|
pkg_tool_pty --> pkg_llm
|
|
|
|
|
pkg_tool_pty --> pkg_pty
|
|
|
|
|
pkg_tool_pty --> pkg_retention
|
|
|
|
|
pkg_tool_pty --> pkg_system_prompt
|
|
|
|
|
pkg_tool_pty --> pkg_tasks
|
|
|
|
|
pkg_tool_pty --> pkg_tools
|
2026-08-09 15:22:53 +08:00
|
|
|
pkg_tool_schedule --> pkg_agent
|
|
|
|
|
pkg_tool_schedule --> pkg_brand
|
|
|
|
|
pkg_tool_schedule --> pkg_invariants
|
|
|
|
|
pkg_tool_schedule --> pkg_llm
|
|
|
|
|
pkg_tool_schedule --> pkg_session
|
|
|
|
|
pkg_tool_schedule --> pkg_session_persistence
|
|
|
|
|
pkg_tool_schedule --> pkg_tools
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tool_cordis --> pkg_invariants
|
|
|
|
|
pkg_tool_cordis --> pkg_scope
|
|
|
|
|
pkg_tool_cordis --> pkg_tools
|
|
|
|
|
pkg_session_checkpoint_policy --> pkg_agent
|
|
|
|
|
pkg_session_checkpoint_policy --> pkg_invariants
|
|
|
|
|
pkg_session_checkpoint_policy --> pkg_llm
|
|
|
|
|
pkg_session_checkpoint_policy --> pkg_session
|
|
|
|
|
pkg_session_checkpoint_policy --> pkg_session_persistence
|
|
|
|
|
pkg_session_checkpoint_policy --> pkg_tools
|
|
|
|
|
pkg_session_telemetry_otel --> pkg_command_feedback
|
|
|
|
|
pkg_session_telemetry_otel --> pkg_invariants
|
|
|
|
|
pkg_session_telemetry_otel --> pkg_llm
|
|
|
|
|
pkg_session_telemetry_otel --> pkg_session
|
|
|
|
|
pkg_session_telemetry_otel --> pkg_session_telemetry
|
2026-08-07 16:38:54 +08:00
|
|
|
pkg_session_telemetry_otel --> pkg_user_id
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_session_title_all_messages_llm --> pkg_invariants
|
|
|
|
|
pkg_session_title_all_messages_llm --> pkg_llm
|
|
|
|
|
pkg_session_title_all_messages_llm --> pkg_session
|
|
|
|
|
pkg_session_title_all_messages_llm --> pkg_session_title
|
|
|
|
|
pkg_session_title_all_messages_llm --> pkg_session_title_llm
|
|
|
|
|
pkg_session_title_first_message_llm --> pkg_invariants
|
|
|
|
|
pkg_session_title_first_message_llm --> pkg_llm
|
|
|
|
|
pkg_session_title_first_message_llm --> pkg_session
|
|
|
|
|
pkg_session_title_first_message_llm --> pkg_session_title
|
|
|
|
|
pkg_session_title_first_message_llm --> pkg_session_title_llm
|
|
|
|
|
pkg_tool_tasks --> pkg_agent
|
|
|
|
|
pkg_tool_tasks --> pkg_invariants
|
|
|
|
|
pkg_tool_tasks --> pkg_llm
|
|
|
|
|
pkg_tool_tasks --> pkg_retention
|
|
|
|
|
pkg_tool_tasks --> pkg_system_prompt
|
|
|
|
|
pkg_tool_tasks --> pkg_tasks
|
|
|
|
|
pkg_tool_tasks --> pkg_tools
|
|
|
|
|
pkg_tool_workflow --> pkg_agent
|
|
|
|
|
pkg_tool_workflow --> pkg_invariants
|
|
|
|
|
pkg_tool_workflow --> pkg_llm
|
2026-08-10 18:37:30 +08:00
|
|
|
pkg_tool_workflow --> pkg_session
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tool_workflow --> pkg_system_prompt
|
|
|
|
|
pkg_tool_workflow --> pkg_tools
|
|
|
|
|
pkg_tool_workflow --> pkg_workflow
|
|
|
|
|
pkg_tool_bash --> pkg_agent
|
|
|
|
|
pkg_tool_bash --> pkg_bash
|
|
|
|
|
pkg_tool_bash --> pkg_bash_env
|
|
|
|
|
pkg_tool_bash --> pkg_invariants
|
|
|
|
|
pkg_tool_bash --> pkg_llm
|
|
|
|
|
pkg_tool_bash --> pkg_sandbox
|
|
|
|
|
pkg_tool_bash --> pkg_sandbox_policy
|
|
|
|
|
pkg_tool_bash --> pkg_system_prompt
|
|
|
|
|
pkg_tool_bash --> pkg_tasks
|
|
|
|
|
pkg_tool_bash --> pkg_tools
|
|
|
|
|
pkg_tool_bash --> pkg_user_approval
|
|
|
|
|
pkg_tool_pwsh --> pkg_agent
|
|
|
|
|
pkg_tool_pwsh --> pkg_bash
|
|
|
|
|
pkg_tool_pwsh --> pkg_bash_env
|
|
|
|
|
pkg_tool_pwsh --> pkg_invariants
|
|
|
|
|
pkg_tool_pwsh --> pkg_llm
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_tool_pwsh --> pkg_sandbox
|
|
|
|
|
pkg_tool_pwsh --> pkg_sandbox_policy
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tool_pwsh --> pkg_system_prompt
|
|
|
|
|
pkg_tool_pwsh --> pkg_tasks
|
|
|
|
|
pkg_tool_pwsh --> pkg_tools
|
2026-08-09 16:59:52 +08:00
|
|
|
pkg_tool_pwsh --> pkg_user_approval
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_compact_tool_result_prune --> pkg_compact
|
|
|
|
|
pkg_compact_tool_result_prune --> pkg_invariants
|
|
|
|
|
pkg_compact_tool_result_prune --> pkg_llm
|
|
|
|
|
pkg_compact_tool_result_prune --> pkg_session
|
|
|
|
|
pkg_compact_tool_result_prune --> pkg_token_meter
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_subagent_acp --> pkg_agent
|
|
|
|
|
pkg_subagent_acp --> pkg_invariants
|
|
|
|
|
pkg_subagent_acp --> pkg_llm
|
|
|
|
|
pkg_subagent_acp --> pkg_session
|
|
|
|
|
pkg_subagent_acp --> pkg_subagent
|
|
|
|
|
pkg_subagent_acp --> pkg_subprocess
|
|
|
|
|
pkg_subagent_acp --> pkg_timeout
|
|
|
|
|
pkg_subagent_claude_code --> pkg_invariants
|
|
|
|
|
pkg_subagent_claude_code --> pkg_llm
|
|
|
|
|
pkg_subagent_claude_code --> pkg_session
|
|
|
|
|
pkg_subagent_claude_code --> pkg_subagent
|
|
|
|
|
pkg_subagent_claude_code --> pkg_subprocess
|
|
|
|
|
pkg_subagent_claude_code --> pkg_timeout
|
|
|
|
|
pkg_subagent_inprocess --> pkg_agent
|
|
|
|
|
pkg_subagent_inprocess --> pkg_invariants
|
|
|
|
|
pkg_subagent_inprocess --> pkg_llm
|
|
|
|
|
pkg_subagent_inprocess --> pkg_session
|
|
|
|
|
pkg_subagent_inprocess --> pkg_subagent
|
|
|
|
|
pkg_subagent_inprocess --> pkg_system_prompt
|
|
|
|
|
pkg_subagent_inprocess --> pkg_tools
|
|
|
|
|
pkg_tool_subagent --> pkg_agent
|
|
|
|
|
pkg_tool_subagent --> pkg_invariants
|
|
|
|
|
pkg_tool_subagent --> pkg_llm
|
|
|
|
|
pkg_tool_subagent --> pkg_subagent
|
2026-08-11 20:00:17 +08:00
|
|
|
pkg_tool_subagent --> pkg_system_prompt
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tool_subagent --> pkg_tasks
|
|
|
|
|
pkg_tool_subagent --> pkg_tools
|
|
|
|
|
pkg_tool_subagent_control --> pkg_invariants
|
|
|
|
|
pkg_tool_subagent_control --> pkg_llm
|
|
|
|
|
pkg_tool_subagent_control --> pkg_session
|
|
|
|
|
pkg_tool_subagent_control --> pkg_subagent
|
|
|
|
|
pkg_tool_subagent_control --> pkg_tools
|
|
|
|
|
pkg_tool_subagent_report --> pkg_invariants
|
|
|
|
|
pkg_tool_subagent_report --> pkg_llm
|
|
|
|
|
pkg_tool_subagent_report --> pkg_subagent
|
2026-08-11 11:50:39 +08:00
|
|
|
pkg_tool_subagent_report --> pkg_system_prompt
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_tool_subagent_report --> pkg_tools
|
|
|
|
|
pkg_hooks_claude --> pkg_agent
|
|
|
|
|
pkg_hooks_claude --> pkg_hook_protocol
|
|
|
|
|
pkg_hooks_claude --> pkg_invariants
|
|
|
|
|
pkg_hooks_claude --> pkg_llm
|
|
|
|
|
pkg_hooks_claude --> pkg_session
|
|
|
|
|
pkg_hooks_claude --> pkg_session_persistence
|
|
|
|
|
pkg_hooks_claude --> pkg_subagent
|
|
|
|
|
pkg_hooks_claude --> pkg_tools
|
|
|
|
|
pkg_web_app --> pkg_bash_env
|
|
|
|
|
pkg_web_app --> pkg_invariants
|
|
|
|
|
pkg_web_app --> pkg_system_prompt
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_client_locale --> pkg_api_remotes
|
|
|
|
|
pkg_client_locale --> pkg_client_connection
|
|
|
|
|
pkg_client_locale --> pkg_client_runtime
|
|
|
|
|
pkg_client_locale --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_locale --> pkg_client_ui_settings
|
|
|
|
|
pkg_client_locale --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_locale --> pkg_invariants
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_sdk_protocol --> pkg_invariants
|
|
|
|
|
pkg_sdk_protocol --> pkg_llm
|
|
|
|
|
pkg_sdk_protocol --> pkg_session
|
|
|
|
|
pkg_sdk_protocol --> pkg_subagent
|
|
|
|
|
pkg_tool_ralph --> pkg_agent
|
|
|
|
|
pkg_tool_ralph --> pkg_invariants
|
|
|
|
|
pkg_tool_ralph --> pkg_llm
|
|
|
|
|
pkg_tool_ralph --> pkg_subagent
|
|
|
|
|
pkg_tool_ralph --> pkg_system_prompt
|
|
|
|
|
pkg_tool_ralph --> pkg_tools
|
|
|
|
|
pkg_tool_ralph --> pkg_workflow
|
|
|
|
|
pkg_workflow_workerthread --> pkg_agent
|
|
|
|
|
pkg_workflow_workerthread --> pkg_brand
|
|
|
|
|
pkg_workflow_workerthread --> pkg_invariants
|
|
|
|
|
pkg_workflow_workerthread --> pkg_llm
|
|
|
|
|
pkg_workflow_workerthread --> pkg_session
|
|
|
|
|
pkg_workflow_workerthread --> pkg_subagent
|
|
|
|
|
pkg_workflow_workerthread --> pkg_tools
|
|
|
|
|
pkg_workflow_workerthread --> pkg_workflow
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_compact_basic --> pkg_agent
|
|
|
|
|
pkg_compact_basic --> pkg_commands
|
|
|
|
|
pkg_compact_basic --> pkg_compact
|
|
|
|
|
pkg_compact_basic --> pkg_compact_tool_result_prune
|
|
|
|
|
pkg_compact_basic --> pkg_invariants
|
|
|
|
|
pkg_compact_basic --> pkg_llm
|
|
|
|
|
pkg_compact_basic --> pkg_session
|
|
|
|
|
pkg_compact_basic --> pkg_token_meter
|
2026-08-09 11:02:16 +08:00
|
|
|
pkg_subagent_codex --> pkg_invariants
|
|
|
|
|
pkg_subagent_codex --> pkg_llm
|
|
|
|
|
pkg_subagent_codex --> pkg_sdk_protocol
|
|
|
|
|
pkg_subagent_codex --> pkg_session
|
|
|
|
|
pkg_subagent_codex --> pkg_subagent
|
|
|
|
|
pkg_subagent_codex --> pkg_subprocess
|
|
|
|
|
pkg_subagent_codex --> pkg_timeout
|
|
|
|
|
pkg_subagent_fork --> pkg_agent
|
|
|
|
|
pkg_subagent_fork --> pkg_invariants
|
|
|
|
|
pkg_subagent_fork --> pkg_session
|
|
|
|
|
pkg_subagent_fork --> pkg_subagent
|
|
|
|
|
pkg_subagent_fork --> pkg_subagent_inprocess
|
|
|
|
|
pkg_subagent_spawn --> pkg_invariants
|
|
|
|
|
pkg_subagent_spawn --> pkg_subagent
|
|
|
|
|
pkg_subagent_spawn --> pkg_subagent_inprocess
|
2026-08-11 19:52:34 +08:00
|
|
|
pkg_client_ui_plugin_config --> pkg_api_remotes
|
|
|
|
|
pkg_client_ui_plugin_config --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_plugin_config --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_plugin_config --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_plugin_config --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_plugin_config --> pkg_client_ui_settings
|
|
|
|
|
pkg_client_ui_plugin_config --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_plugin_config --> pkg_client_web_react
|
|
|
|
|
pkg_client_ui_plugin_config --> pkg_invariants
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_client_ui_question --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_question --> pkg_invariants
|
|
|
|
|
pkg_client_ui_sidebar --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_sidebar --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_sidebar --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_sidebar --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_sidebar --> pkg_invariants
|
|
|
|
|
pkg_client_ui_slash --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_slash --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_slash --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_slash --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_slash --> pkg_invariants
|
|
|
|
|
pkg_client_ui_theme --> pkg_api_remotes
|
|
|
|
|
pkg_client_ui_theme --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_theme --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_theme --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_theme --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_theme --> pkg_client_ui_settings
|
|
|
|
|
pkg_client_ui_theme --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_theme --> pkg_host_webserver
|
|
|
|
|
pkg_client_ui_theme --> pkg_invariants
|
|
|
|
|
pkg_client_ui_trajectory --> pkg_agent
|
|
|
|
|
pkg_client_ui_trajectory --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_trajectory --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_trajectory --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_trajectory --> pkg_compact
|
|
|
|
|
pkg_client_ui_trajectory --> pkg_invariants
|
|
|
|
|
pkg_client_ui_trajectory --> pkg_tools
|
|
|
|
|
pkg_client_ui_workspace --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_workspace --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_workspace --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_workspace --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_workspace --> pkg_invariants
|
|
|
|
|
pkg_agent_spine_demo --> pkg_agent
|
|
|
|
|
pkg_agent_spine_demo --> pkg_agent_loop
|
|
|
|
|
pkg_agent_spine_demo --> pkg_bash_env
|
|
|
|
|
pkg_agent_spine_demo --> pkg_goal
|
|
|
|
|
pkg_agent_spine_demo --> pkg_goal_session
|
|
|
|
|
pkg_agent_spine_demo --> pkg_invariants
|
|
|
|
|
pkg_agent_spine_demo --> pkg_llm
|
|
|
|
|
pkg_agent_spine_demo --> pkg_llm_retry
|
|
|
|
|
pkg_agent_spine_demo --> pkg_paths
|
|
|
|
|
pkg_agent_spine_demo --> pkg_scope
|
|
|
|
|
pkg_agent_spine_demo --> pkg_session
|
|
|
|
|
pkg_agent_spine_demo --> pkg_session_title
|
|
|
|
|
pkg_agent_spine_demo --> pkg_skill
|
|
|
|
|
pkg_agent_spine_demo --> pkg_skill_local
|
|
|
|
|
pkg_agent_spine_demo --> pkg_system_prompt
|
|
|
|
|
pkg_agent_spine_demo --> pkg_tasks_local
|
|
|
|
|
pkg_agent_spine_demo --> pkg_tool_bash
|
|
|
|
|
pkg_agent_spine_demo --> pkg_tool_goal
|
|
|
|
|
pkg_agent_spine_demo --> pkg_tool_skill
|
|
|
|
|
pkg_agent_spine_demo --> pkg_tool_tasks
|
|
|
|
|
pkg_agent_spine_demo --> pkg_tools
|
|
|
|
|
pkg_agent_spine_demo --> pkg_workspace_context
|
|
|
|
|
pkg_jsonrpc --> pkg_agent
|
|
|
|
|
pkg_jsonrpc --> pkg_invariants
|
|
|
|
|
pkg_jsonrpc --> pkg_llm
|
|
|
|
|
pkg_jsonrpc --> pkg_llm_deepseek
|
|
|
|
|
pkg_jsonrpc --> pkg_scope
|
|
|
|
|
pkg_jsonrpc --> pkg_sdk_protocol
|
|
|
|
|
pkg_jsonrpc --> pkg_session
|
|
|
|
|
pkg_jsonrpc --> pkg_subagent
|
|
|
|
|
pkg_sdk_client --> pkg_invariants
|
|
|
|
|
pkg_sdk_client --> pkg_llm
|
|
|
|
|
pkg_sdk_client --> pkg_sdk_protocol
|
|
|
|
|
pkg_sdk_client --> pkg_session
|
|
|
|
|
pkg_subagent_dsh_sdk --> pkg_agent
|
|
|
|
|
pkg_subagent_dsh_sdk --> pkg_invariants
|
|
|
|
|
pkg_subagent_dsh_sdk --> pkg_llm
|
|
|
|
|
pkg_subagent_dsh_sdk --> pkg_sdk_client
|
|
|
|
|
pkg_subagent_dsh_sdk --> pkg_session
|
|
|
|
|
pkg_subagent_dsh_sdk --> pkg_subagent
|
|
|
|
|
pkg_subagent_dsh_sdk --> pkg_subprocess
|
|
|
|
|
pkg_client_ui_conversation --> pkg_agent
|
|
|
|
|
pkg_client_ui_conversation --> pkg_api_remotes
|
|
|
|
|
pkg_client_ui_conversation --> pkg_attachment
|
|
|
|
|
pkg_client_ui_conversation --> pkg_brand
|
|
|
|
|
pkg_client_ui_conversation --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_conversation --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_conversation --> pkg_client_runtime
|
2026-08-11 19:44:01 +08:00
|
|
|
pkg_client_ui_conversation --> pkg_client_ui_attachment
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_client_ui_conversation --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_conversation --> pkg_client_ui_settings
|
|
|
|
|
pkg_client_ui_conversation --> pkg_client_ui_slash
|
|
|
|
|
pkg_client_ui_conversation --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_conversation --> pkg_commands
|
|
|
|
|
pkg_client_ui_conversation --> pkg_compact
|
|
|
|
|
pkg_client_ui_conversation --> pkg_invariants
|
|
|
|
|
pkg_client_ui_conversation --> pkg_llm_retry
|
|
|
|
|
pkg_client_ui_conversation --> pkg_token_meter
|
|
|
|
|
pkg_client_ui_conversation --> pkg_tools
|
|
|
|
|
pkg_client_ui_layout --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_layout --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_layout --> pkg_client_ui_theme
|
|
|
|
|
pkg_client_ui_layout --> pkg_invariants
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_api_remotes
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_client_ui_settings
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_client_ui_sidebar
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_client_web_react
|
|
|
|
|
pkg_client_ui_settings_general --> pkg_invariants
|
|
|
|
|
pkg_acp_demo --> pkg_acp
|
|
|
|
|
pkg_acp_demo --> pkg_agent_spine_demo
|
|
|
|
|
pkg_acp_demo --> pkg_app_boot
|
|
|
|
|
pkg_acp_demo --> pkg_invariants
|
|
|
|
|
pkg_acp_demo --> pkg_session_checkpoint_policy
|
|
|
|
|
pkg_acp_demo --> pkg_session_persistence_jsonl
|
|
|
|
|
pkg_acp_demo --> pkg_session_query
|
|
|
|
|
pkg_acp_demo --> pkg_session_query_sqlite
|
|
|
|
|
pkg_acp_demo --> pkg_tools
|
|
|
|
|
pkg_acp_demo --> pkg_workspace_context
|
|
|
|
|
pkg_host_directory_picker_browse --> pkg_client_locale
|
|
|
|
|
pkg_host_directory_picker_browse --> pkg_client_runtime
|
|
|
|
|
pkg_host_directory_picker_browse --> pkg_client_ui_primitives
|
|
|
|
|
pkg_host_directory_picker_browse --> pkg_client_ui_slots
|
|
|
|
|
pkg_host_directory_picker_browse --> pkg_client_ui_workspace
|
|
|
|
|
pkg_host_directory_picker_browse --> pkg_invariants
|
|
|
|
|
pkg_host_directory_picker_native --> pkg_client_runtime
|
|
|
|
|
pkg_host_directory_picker_native --> pkg_client_ui_slots
|
|
|
|
|
pkg_host_directory_picker_native --> pkg_client_ui_workspace
|
|
|
|
|
pkg_host_directory_picker_native --> pkg_invariants
|
|
|
|
|
pkg_client_ui_agent_preset --> pkg_api_remotes
|
2026-08-09 21:36:55 +08:00
|
|
|
pkg_client_ui_agent_preset --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_agent_preset --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_agent_preset --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_agent_preset --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_agent_preset --> pkg_client_ui_primitives
|
2026-08-09 21:38:26 +08:00
|
|
|
pkg_client_ui_agent_preset --> pkg_client_ui_settings
|
2026-08-09 21:36:55 +08:00
|
|
|
pkg_client_ui_agent_preset --> pkg_client_ui_slots
|
2026-08-09 21:38:26 +08:00
|
|
|
pkg_client_ui_agent_preset --> pkg_client_web_react
|
2026-08-09 21:36:55 +08:00
|
|
|
pkg_client_ui_agent_preset --> pkg_invariants
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_client_ui_command --> pkg_api_remotes
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_client_ui_command --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_command --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_command --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_command --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_command --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_command --> pkg_client_ui_slash
|
|
|
|
|
pkg_client_ui_command --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_command --> pkg_invariants
|
2026-08-10 08:08:24 -07:00
|
|
|
pkg_client_ui_deliverables --> pkg_client_connection
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_client_ui_deliverables --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_deliverables --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_deliverables --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_deliverables --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_deliverables --> pkg_invariants
|
|
|
|
|
pkg_client_ui_goal --> pkg_api_remotes
|
|
|
|
|
pkg_client_ui_goal --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_goal --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_goal --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_goal --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_goal --> pkg_client_ui_slots
|
2026-08-10 17:13:01 +08:00
|
|
|
pkg_client_ui_goal --> pkg_commands
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_client_ui_goal --> pkg_goal
|
|
|
|
|
pkg_client_ui_goal --> pkg_invariants
|
|
|
|
|
pkg_client_ui_plan --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_plan --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_plan --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_plan --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_plan --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_plan --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_plan --> pkg_invariants
|
|
|
|
|
pkg_client_ui_plan --> pkg_plan_mode
|
|
|
|
|
pkg_client_ui_subagent --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_subagent --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_subagent --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_subagent --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_subagent --> pkg_client_ui_slash
|
|
|
|
|
pkg_client_ui_subagent --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_subagent --> pkg_invariants
|
|
|
|
|
pkg_client_ui_subagent --> pkg_subagent
|
|
|
|
|
pkg_client_ui_subagent --> pkg_token_meter
|
2026-08-10 13:22:51 +08:00
|
|
|
pkg_client_ui_task --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_task --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_task --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_task --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_task --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_task --> pkg_invariants
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_client_ui_tool --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_tool --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_tool --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_tool --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_tool --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_tool --> pkg_invariants
|
2026-08-10 18:37:30 +08:00
|
|
|
pkg_client_ui_workflow_run --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_workflow_run --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_workflow_run --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_workflow_run --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_workflow_run --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_workflow_run --> pkg_invariants
|
|
|
|
|
pkg_client_ui_workflow_run --> pkg_session
|
|
|
|
|
pkg_client_ui_workflow_run --> pkg_tool_workflow
|
|
|
|
|
pkg_client_ui_workflow_run --> pkg_workflow
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_host_directory_picker_auto --> pkg_host_directory_picker_browse
|
|
|
|
|
pkg_host_directory_picker_auto --> pkg_host_directory_picker_native
|
|
|
|
|
pkg_host_directory_picker_auto --> pkg_host_webserver
|
|
|
|
|
pkg_host_directory_picker_auto --> pkg_invariants
|
2026-08-11 17:12:41 +08:00
|
|
|
pkg_client_ui_model --> pkg_api_remotes
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_client_ui_model --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_model --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_model --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_model --> pkg_client_ui_command
|
|
|
|
|
pkg_client_ui_model --> pkg_client_ui_conversation
|
|
|
|
|
pkg_client_ui_model --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_model --> pkg_client_ui_slash
|
|
|
|
|
pkg_client_ui_model --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_model --> pkg_invariants
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_client_ui_permission --> pkg_api_remotes
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_client_ui_permission --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_permission --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_permission --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_permission --> pkg_client_schema_form
|
|
|
|
|
pkg_client_ui_permission --> pkg_client_ui_command
|
|
|
|
|
pkg_client_ui_permission --> pkg_client_ui_primitives
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
pkg_client_ui_permission --> pkg_client_ui_settings
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_client_ui_permission --> pkg_client_ui_slash
|
|
|
|
|
pkg_client_ui_permission --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_permission --> pkg_invariants
|
|
|
|
|
pkg_client_ui_permission --> pkg_permission
|
2026-08-11 17:12:41 +08:00
|
|
|
pkg_client_ui_skill --> pkg_api_remotes
|
2026-08-09 19:36:15 +08:00
|
|
|
pkg_client_ui_skill --> pkg_client_connection
|
|
|
|
|
pkg_client_ui_skill --> pkg_client_locale
|
|
|
|
|
pkg_client_ui_skill --> pkg_client_runtime
|
|
|
|
|
pkg_client_ui_skill --> pkg_client_ui_primitives
|
|
|
|
|
pkg_client_ui_skill --> pkg_client_ui_slash
|
|
|
|
|
pkg_client_ui_skill --> pkg_client_ui_slots
|
|
|
|
|
pkg_client_ui_skill --> pkg_client_ui_tool
|
|
|
|
|
pkg_client_ui_skill --> pkg_invariants
|
2026-08-09 11:02:16 +08:00
|
|
|
```
|
|
|
|
|
|
2026-08-09 21:36:55 +08:00
|
|
|
| Package | Group | Depends on |
|
2026-08-09 11:02:16 +08:00
|
|
|
| --- | --- | --- |
|
|
|
|
|
| [`invariants`](../packages/support/invariants) | `support` | — |
|
|
|
|
|
| [`atomic-write`](../packages/util/atomic-write) | `util` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`brand`](../packages/util/brand) | `util` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`environment`](../packages/util/environment) | `util` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`native-command`](../packages/util/native-command) | `util` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`paths`](../packages/util/paths) | `util` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`retention`](../packages/util/retention) | `util` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`timeout`](../packages/util/timeout) | `util` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`scope`](../packages/core/scope) | `core` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`llm-mock-server`](../packages/support/llm-mock-server) | `support` | [`invariants`](../packages/support/invariants) |
|
2026-08-06 20:52:26 +08:00
|
|
|
| [`cmdline`](../packages/boot/cmdline) | `boot` | [`invariants`](../packages/support/invariants) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`base`](../packages/bundle/base) | `bundle` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-modules`](../packages/client/modules) | `client` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-schema-form`](../packages/client/schema-form) | `client` | [`invariants`](../packages/support/invariants) |
|
2026-08-11 18:23:36 +08:00
|
|
|
| [`client-ui-attachment`](../packages/client/ui-attachment) | `client` | [`invariants`](../packages/support/invariants) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`client-ui-primitives`](../packages/client/ui-primitives) | `client` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-ui-slots`](../packages/client/ui-slots) | `client` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-web`](../packages/client/web) | `client` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-web-react`](../packages/client/web-react) | `client` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`e2b`](../packages/e2b/e2b) | `e2b` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`jsonrpc-demo`](../packages/examples/jsonrpc-demo) | `examples` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`host-directory-picker`](../packages/host/directory-picker) | `host` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`host-webserver`](../packages/host/webserver) | `host` | [`invariants`](../packages/support/invariants) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`sandbox-windows-acl`](../packages/sandbox/sandbox-windows-acl) | `sandbox` | [`invariants`](../packages/support/invariants) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`storage`](../packages/storage/storage) | `storage` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`subprocess`](../packages/subprocess/subprocess) | `subprocess` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`type-meta`](../packages/typert/type-meta) | `typert` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`typert-generator`](../packages/typert/generator) | `typert` | [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`typert-registry`](../packages/typert/registry) | `typert` | [`invariants`](../packages/support/invariants) |
|
2026-08-10 11:08:35 +08:00
|
|
|
| [`attachment`](../packages/attachment/attachment) | `attachment` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`client-connection`](../packages/client/connection) | `client` | [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-hmr`](../packages/client/hmr) | `client` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`credentials`](../packages/credentials/credentials) | `credentials` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`subprocess-e2b`](../packages/e2b/subprocess-e2b) | `e2b` | [`e2b`](../packages/e2b/e2b), [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
|
|
|
|
| [`frontend-static`](../packages/host/frontend-static) | `host` | [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
|
2026-08-07 16:38:54 +08:00
|
|
|
| [`user-id`](../packages/session/user-id) | `session` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`settings`](../packages/settings/settings) | `settings` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`storage-domain`](../packages/storage/storage-domain) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
|
|
|
|
|
| [`storage-json`](../packages/storage/storage-json) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
|
|
|
|
|
| [`storage-sqlite`](../packages/storage/storage-sqlite) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
|
|
|
|
|
| [`subprocess-local`](../packages/subprocess/subprocess-local) | `subprocess` | [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
|
|
|
|
| [`typert-loader`](../packages/typert/loader) | `typert` | [`invariants`](../packages/support/invariants), [`typert-registry`](../packages/typert/registry) |
|
2026-08-10 11:08:35 +08:00
|
|
|
| [`llm`](../packages/llm/llm) | `llm` | [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`timeout`](../packages/util/timeout) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`api-gateway`](../packages/api/gateway) | `api` | [`client-connection`](../packages/client/connection), [`invariants`](../packages/support/invariants), [`typert-registry`](../packages/typert/registry) |
|
2026-08-10 11:08:35 +08:00
|
|
|
| [`attachment-local`](../packages/attachment/attachment-local) | `attachment` | [`attachment`](../packages/attachment/attachment), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`credentials-local`](../packages/credentials/credentials-local) | `credentials` | [`atomic-write`](../packages/util/atomic-write), [`credentials`](../packages/credentials/credentials), [`environment`](../packages/util/environment), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths) |
|
|
|
|
|
| [`settings-local`](../packages/settings/settings-local) | `settings` | [`atomic-write`](../packages/util/atomic-write), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`settings`](../packages/settings/settings) |
|
2026-08-11 14:55:39 +08:00
|
|
|
| [`llm-deepseek`](../packages/llm/llm-deepseek) | `llm` | [`credentials`](../packages/credentials/credentials), [`environment`](../packages/util/environment), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings), [`timeout`](../packages/util/timeout), [`user-id`](../packages/session/user-id) |
|
2026-08-10 11:08:35 +08:00
|
|
|
| [`llm-pi-ai`](../packages/llm/llm-pi-ai) | `llm` | [`attachment`](../packages/attachment/attachment), [`credentials`](../packages/credentials/credentials), [`environment`](../packages/util/environment), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings), [`timeout`](../packages/util/timeout) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`session`](../packages/core/session) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`type-meta`](../packages/typert/type-meta) |
|
|
|
|
|
| [`system-prompt`](../packages/core/system-prompt) | `core` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
|
2026-08-09 22:10:38 +08:00
|
|
|
| [`skill`](../packages/skill/skill) | `skill` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`web`](../packages/web/web) | `web` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
| [`client-runtime`](../packages/client/runtime) | `client` | [`api-gateway`](../packages/api/gateway), [`invariants`](../packages/support/invariants), [`type-meta`](../packages/typert/type-meta), [`typert-registry`](../packages/typert/registry) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
|
|
|
|
|
| [`agent`](../packages/core/agent) | `core` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`type-meta`](../packages/typert/type-meta) |
|
|
|
|
|
| [`skill-badge`](../packages/skill/skill-badge) | `skill` | [`invariants`](../packages/support/invariants), [`skill`](../packages/skill/skill) |
|
|
|
|
|
| [`web-fetch-local`](../packages/web/web-fetch-local) | `web` | [`invariants`](../packages/support/invariants), [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
|
|
|
|
|
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`environment`](../packages/util/environment), [`invariants`](../packages/support/invariants), [`web`](../packages/web/web) |
|
|
|
|
|
| [`web-search-perplexity`](../packages/web/web-search-perplexity) | `web` | [`environment`](../packages/util/environment), [`invariants`](../packages/support/invariants), [`web`](../packages/web/web) |
|
|
|
|
|
| [`spill`](../packages/spill/spill) | `spill` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`acp-snapshot`](../packages/support/acp-snapshot) | `support` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
|
|
|
|
|
| [`app-boot`](../packages/boot/app-boot) | `boot` | [`environment`](../packages/util/environment), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`system-prompt`](../packages/core/system-prompt) |
|
|
|
|
|
| [`code-runtime-worker`](../packages/code-runtime/code-runtime-worker) | `code-runtime` | [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
|
2026-08-09 20:33:55 +08:00
|
|
|
| [`persona`](../packages/preset/persona) | `preset` | [`invariants`](../packages/support/invariants), [`system-prompt`](../packages/core/system-prompt) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`sandbox`](../packages/sandbox/sandbox) | `sandbox` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`session-persistence`](../packages/session/session-persistence) | `session` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
|
|
|
|
|
| [`session-projection`](../packages/session/session-projection) | `session` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
|
2026-08-09 19:36:15 +08:00
|
|
|
| [`llm-retry`](../packages/llm/llm-retry) | `llm` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
|
2026-08-09 15:17:35 +08:00
|
|
|
| [`agent-default-model`](../packages/core/agent-default-model) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`type-meta`](../packages/typert/type-meta) |
|
2026-08-10 16:10:33 +08:00
|
|
|
| [`bash`](../packages/bash/bash) | `bash` | [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`settings`](../packages/settings/settings), [`subprocess`](../packages/subprocess/subprocess) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
|
2026-08-10 18:06:27 +08:00
|
|
|
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`agent`](../packages/core/agent), [`credentials`](../packages/credentials/credentials), [`environment`](../packages/util/environment), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`web`](../packages/web/web) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`invariants`](../packages/support/invariants), [`spill`](../packages/spill/spill) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`loader-smoke`](../packages/support/loader-smoke) | `support` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
|
|
|
|
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
|
2026-08-10 11:28:38 -07:00
|
|
|
| [`message-feedback`](../packages/feedback/message-feedback) | `feedback` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`storage-domain`](../packages/storage/storage-domain), [`type-meta`](../packages/typert/type-meta) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`commands`](../packages/interaction/commands) | `interaction` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`scope`](../packages/core/scope), [`session`](../packages/core/session) |
|
|
|
|
|
| [`user-approval`](../packages/interaction/user-approval) | `interaction` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
|
|
|
|
|
| [`user-interaction`](../packages/interaction/user-interaction) | `interaction` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
|
fix(preset): keep the token meter host-plane and name unjoined agents
Moving the agent plane behind presets left two readers on the wrong side of
the host/agent line.
`dsh-token-meter` was disabled on the host and mounted inside each preset's
`compaction` realm, but its three projection units register into the
process-wide `sessionProjections` table. A unit registered from one preset
answers for every session, so whether a `minimal` session showed a context
meter depended on whether some other session had mounted `standard` since
boot, and a process that only ever ran `minimal` showed none. The meter takes
no configuration, keys every fold by Session, and registers no tool or prompt
section, so it returns to the host composition and leaves the presets'
`isolate` map; the realm and `compact-basic` stay, because what a preset
chooses is whether its agent compacts, not whether its tokens are counted.
Nothing named an agent that joined no preset. The join is a scope-parent link,
and without it the tools, prompt-section, and skill views resolve the empty
global layer: the agent publishes, the turn runs, and the model receives
nothing. `AgentPresets` now logs one warning per such agent while a roster is
configured, and the invariant companion fails outright — at
`system-prompt/assemble` rather than at publication, because an unjoined agent
is legal until it addresses a model and `recompose` binds exactly such an
agent. The warning stays advisory: a synchronous `agent/created` throw vetoes
publication, and the ACP bridge, SDK server, and headless bundle all create an
unjoined agent today.
Three limits are recorded rather than fixed: projection key presence is not a
per-session capability signal, a superseded standing generation is never
reclaimed, and a `cordis_mount` temporary plugin belongs to the composition
rather than the session that mounted it.
Fixes #2203
2026-08-10 22:36:06 +08:00
|
|
|
| [`agent-presets`](../packages/preset/agent-presets) | `preset` | [`agent`](../packages/core/agent), [`atomic-write`](../packages/util/atomic-write), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`pty`](../packages/pty/pty) | `pty` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`sandbox-policy`](../packages/sandbox/sandbox-policy) | `sandbox` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
|
|
|
|
|
| [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl) | `session` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
|
|
|
|
|
| [`session-persistence-sqlite`](../packages/session/session-persistence-sqlite) | `session` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
|
|
|
|
|
| [`session-projection-cache`](../packages/session/session-projection-cache) | `session` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`storage-domain`](../packages/storage/storage-domain) |
|
|
|
|
|
| [`session-telemetry`](../packages/session/session-telemetry) | `session` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
|
|
|
|
|
| [`session-title`](../packages/session/session-title) | `session` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
|
|
|
|
|
| [`tasks`](../packages/tasks/tasks) | `tasks` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
|
|
|
|
| [`workspace`](../packages/workspace/workspace) | `workspace` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`storage`](../packages/storage/storage), [`storage-domain`](../packages/storage/storage-domain) |
|
|
|
|
|
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/interaction/user-approval) |
|
|
|
|
|
| [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`goal-session`](../packages/goal/goal-session) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
2026-08-10 16:10:33 +08:00
|
|
|
| [`bash-local`](../packages/bash/bash-local) | `bash` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`settings`](../packages/settings/settings), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
|
|
|
|
| [`pwsh-local`](../packages/bash/pwsh-local) | `bash` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`settings`](../packages/settings/settings), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`fs-policy`](../packages/fs/fs-policy) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`skill-local`](../packages/skill/skill-local) | `skill` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`skill`](../packages/skill/skill) |
|
2026-08-09 19:36:15 +08:00
|
|
|
| [`compact`](../packages/compact/compact) | `compact` | [`brand`](../packages/util/brand), [`commands`](../packages/interaction/commands), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`session-query`](../packages/session-query/session-query) | `session-query` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-title`](../packages/session/session-title) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`user-approval`](../packages/interaction/user-approval) |
|
2026-08-11 17:12:41 +08:00
|
|
|
| [`api-remotes`](../packages/api/remotes) | `api` | [`agent`](../packages/core/agent), [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`commands`](../packages/interaction/commands), [`credentials`](../packages/credentials/credentials), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`settings`](../packages/settings/settings), [`typert-registry`](../packages/typert/registry) |
|
2026-08-09 15:17:35 +08:00
|
|
|
| [`headless`](../packages/bundle/headless) | `bundle` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`tmux-context`](../packages/context/tmux-context) | `context` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
|
|
|
|
|
| [`fs-e2b`](../packages/e2b/fs-e2b) | `e2b` | [`e2b`](../packages/e2b/e2b), [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants) |
|
2026-08-10 18:33:49 +08:00
|
|
|
| [`command-feedback`](../packages/feedback/command-feedback) | `feedback` | [`commands`](../packages/interaction/commands), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry), [`user-id`](../packages/session/user-id) |
|
fix(preset): keep the token meter host-plane and name unjoined agents
Moving the agent plane behind presets left two readers on the wrong side of
the host/agent line.
`dsh-token-meter` was disabled on the host and mounted inside each preset's
`compaction` realm, but its three projection units register into the
process-wide `sessionProjections` table. A unit registered from one preset
answers for every session, so whether a `minimal` session showed a context
meter depended on whether some other session had mounted `standard` since
boot, and a process that only ever ran `minimal` showed none. The meter takes
no configuration, keys every fold by Session, and registers no tool or prompt
section, so it returns to the host composition and leaves the presets'
`isolate` map; the realm and `compact-basic` stay, because what a preset
chooses is whether its agent compacts, not whether its tokens are counted.
Nothing named an agent that joined no preset. The join is a scope-parent link,
and without it the tools, prompt-section, and skill views resolve the empty
global layer: the agent publishes, the turn runs, and the model receives
nothing. `AgentPresets` now logs one warning per such agent while a roster is
configured, and the invariant companion fails outright — at
`system-prompt/assemble` rather than at publication, because an unjoined agent
is legal until it addresses a model and `recompose` binds exactly such an
agent. The warning stays advisory: a synchronous `agent/created` throw vetoes
publication, and the ACP bridge, SDK server, and headless bundle all create an
unjoined agent today.
Three limits are recorded rather than fixed: projection key presence is not a
per-session capability signal, a superseded standing generation is never
reclaimed, and a `cordis_mount` temporary plugin belongs to the composition
rather than the session that mounted it.
Fixes #2203
2026-08-10 22:36:06 +08:00
|
|
|
| [`host-apiproxy`](../packages/host/apiproxy) | `host` | [`agent-presets`](../packages/preset/agent-presets), [`invariants`](../packages/support/invariants) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`permission`](../packages/interaction/permission) | `interaction` | [`bash`](../packages/bash/bash), [`commands`](../packages/interaction/commands), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`settings`](../packages/settings/settings), [`user-approval`](../packages/interaction/user-approval) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`lsp-local`](../packages/lsp/lsp-local) | `lsp` | [`brand`](../packages/util/brand), [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`pty-local`](../packages/pty/pty-local) | `pty` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`pty`](../packages/pty/pty), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess) |
|
|
|
|
|
| [`session-title-llm`](../packages/session/session-title-llm) | `session` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`timeout`](../packages/util/timeout) |
|
fix(tasks-local): layer control surfaces and listeners by registering scope
One host registry serves every composition in the process, so its two
service-wide collections answered per-owner questions process-wide. `start()`
asked only whether SOME surface was attached, so an agent whose own composition
loads no `tool-tasks` could start work it has no tool to collect or stop as soon
as any other preset attached one — and the answer changed depending on which
sessions happened to be open. `settle()` walked every registered listener, so a
task settling without a waiter injected one completion notice per mounted
preset into the same owner.
Both collections now sit in `ScopedLayers`, the layered-registry primitive
`tools` and `skills` already use: a registration files into its registering
context's scope, and a read unions the global layer with the owner's scope
chain. A surface or listener registered from an unscoped context lands in the
global layer and serves every owner, which is exactly the host-plane
composition's own controls, so the TUI path is unchanged without a special
case.
This supersedes the consumer-side filter in the previous commit. That filter
produced the right notices but sat in the wrong layer: it left the `start()`
gate process-wide, it could not be enforced against a producer that resolves
the registry directly, and it made a Consumer carry scope knowledge that the
other layered registries keep in the registry. `tool-tasks` is scope-agnostic
again and the `dsh-scope` edge moves to `tasks-local`.
`start()`'s refusal is now owner-relative, so its model-visible text names the
agent rather than the process. The shipped `minimal` preset keeps
`enableRunInBackground: false`, no longer as the safety boundary — the registry
owns that now — but so an agent that could never collect a task is not offered
the parameter at all.
Refs #2141
2026-08-10 16:12:41 +08:00
|
|
|
| [`tasks-local`](../packages/tasks/tasks-local) | `tasks` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`scope`](../packages/core/scope), [`tasks`](../packages/tasks/tasks), [`timeout`](../packages/util/timeout) |
|
2026-08-09 19:36:15 +08:00
|
|
|
| [`token-meter`](../packages/llm/token-meter) | `llm` | [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
|
2026-08-10 18:00:03 +08:00
|
|
|
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
2026-08-09 20:41:55 +08:00
|
|
|
| [`agent-tool-mode`](../packages/core/agent-tool-mode) | `core` | [`invariants`](../packages/support/invariants), [`tools`](../packages/core/tools) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`bash-env`](../packages/bash/bash-env) | `bash` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`session-persistence`](../packages/session/session-persistence), [`tools`](../packages/core/tools) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`bash-sandbox`](../packages/bash/bash-sandbox) | `bash` | [`bash`](../packages/bash/bash), [`bash-local`](../packages/bash/bash-local), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
|
|
|
|
|
| [`pwsh-sandbox`](../packages/bash/pwsh-sandbox) | `bash` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`pwsh-local`](../packages/bash/pwsh-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
|
|
|
|
|
| [`fs-sandbox`](../packages/fs/fs-sandbox) | `fs` | [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
|
2026-08-10 15:09:07 +08:00
|
|
|
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`attachment`](../packages/attachment/attachment), [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`tool-skill`](../packages/skill/tool-skill) | `skill` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
|
2026-08-09 19:36:15 +08:00
|
|
|
| [`command-compact`](../packages/compact/command-compact) | `compact` | [`commands`](../packages/interaction/commands), [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants) |
|
2026-08-10 21:42:45 +08:00
|
|
|
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`agent-presets`](../packages/preset/agent-presets), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`tool-web`](../packages/web/tool-web) | `web` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`web`](../packages/web/web) |
|
|
|
|
|
| [`spill-policy`](../packages/spill/spill-policy) | `spill` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`tools`](../packages/core/tools) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`tool-todo`](../packages/todo/tool-todo) | `todo` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`plan-mode`](../packages/plan/plan-mode) | `plan` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-interaction`](../packages/interaction/user-interaction) |
|
|
|
|
|
| [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | `session-query` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query) |
|
|
|
|
|
| [`tool-session-query`](../packages/session-query/tool-session-query) | `session-query` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`agent-loop-testkit`](../packages/support/agent-loop-testkit) | `support` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
2026-08-09 19:36:15 +08:00
|
|
|
| [`llm-replay`](../packages/support/llm-replay) | `support` | [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
fix(preset): keep the token meter host-plane and name unjoined agents
Moving the agent plane behind presets left two readers on the wrong side of
the host/agent line.
`dsh-token-meter` was disabled on the host and mounted inside each preset's
`compaction` realm, but its three projection units register into the
process-wide `sessionProjections` table. A unit registered from one preset
answers for every session, so whether a `minimal` session showed a context
meter depended on whether some other session had mounted `standard` since
boot, and a process that only ever ran `minimal` showed none. The meter takes
no configuration, keys every fold by Session, and registers no tool or prompt
section, so it returns to the host composition and leaves the presets'
`isolate` map; the realm and `compact-basic` stay, because what a preset
chooses is whether its agent compacts, not whether its tokens are counted.
Nothing named an agent that joined no preset. The join is a scope-parent link,
and without it the tools, prompt-section, and skill views resolve the empty
global layer: the agent publishes, the turn runs, and the model receives
nothing. `AgentPresets` now logs one warning per such agent while a roster is
configured, and the invariant companion fails outright — at
`system-prompt/assemble` rather than at publication, because an unjoined agent
is legal until it addresses a model and `recompose` binds exactly such an
agent. The warning stays advisory: a synchronous `agent/created` throw vetoes
publication, and the ACP bridge, SDK server, and headless bundle all create an
unjoined agent today.
Three limits are recorded rather than fixed: projection key presence is not a
per-session capability signal, a superseded standing generation is never
reclaimed, and a `cordis_mount` temporary plugin belongs to the composition
rather than the session that mounted it.
Fixes #2203
2026-08-10 22:36:06 +08:00
|
|
|
| [`client-test-runtime`](../packages/client/test-runtime) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`host-apiproxy`](../packages/host/apiproxy), [`invariants`](../packages/support/invariants) |
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
| [`client-ui-models`](../packages/client/ui-models) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`client-schema-form`](../packages/client/schema-form), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`api-gateway`](../packages/api/gateway), [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`client-schema-form`](../packages/client/schema-form), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`settings`](../packages/settings/settings) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`session-reference`](../packages/context/session-reference) | `context` | [`agent`](../packages/core/agent), [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query) |
|
|
|
|
|
| [`workspace-context`](../packages/context/workspace-context) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`paths`](../packages/util/paths), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) | `guard` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`timeout-policy`](../packages/guard/timeout-policy) | `guard` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`tool-ask-user`](../packages/interaction/tool-ask-user) | `interaction` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`tools`](../packages/core/tools), [`user-interaction`](../packages/interaction/user-interaction) |
|
|
|
|
|
| [`tool-lsp`](../packages/lsp/tool-lsp) | `lsp` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
2026-08-10 18:50:03 +08:00
|
|
|
| [`mcp-client`](../packages/mcp/mcp-client) | `mcp` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`tool-bash-persistent`](../packages/pty/tool-bash-persistent) | `pty` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`pty`](../packages/pty/pty), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`tool-pty`](../packages/pty/tool-pty) | `pty` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`pty`](../packages/pty/pty), [`retention`](../packages/util/retention), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
|
2026-08-09 15:22:53 +08:00
|
|
|
| [`tool-schedule`](../packages/schedule/tool-schedule) | `schedule` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`tools`](../packages/core/tools) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`tool-cordis`](../packages/self-modification/tool-cordis) | `self-modification` | [`invariants`](../packages/support/invariants), [`scope`](../packages/core/scope), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy) | `session` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`tools`](../packages/core/tools) |
|
2026-08-07 16:38:54 +08:00
|
|
|
| [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | `session` | [`command-feedback`](../packages/feedback/command-feedback), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry), [`user-id`](../packages/session/user-id) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`session-title-all-messages-llm`](../packages/session/session-title-all-messages-llm) | `session` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
|
|
|
|
|
| [`session-title-first-message-llm`](../packages/session/session-title-first-message-llm) | `session` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
|
|
|
|
|
| [`tool-tasks`](../packages/tasks/tool-tasks) | `tasks` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
|
2026-08-10 18:37:30 +08:00
|
|
|
| [`tool-workflow`](../packages/workflow/tool-workflow) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`tool-bash`](../packages/bash/tool-bash) | `bash` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`bash-env`](../packages/bash/bash-env), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
|
2026-08-09 16:59:52 +08:00
|
|
|
| [`tool-pwsh`](../packages/bash/tool-pwsh) | `bash` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`bash-env`](../packages/bash/bash-env), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
|
2026-08-09 19:36:15 +08:00
|
|
|
| [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune) | `compact` | [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
|
|
|
|
| [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | `subagent` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
2026-08-10 14:52:16 +08:00
|
|
|
| [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
2026-08-11 20:00:17 +08:00
|
|
|
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`tool-subagent-control`](../packages/subagent/tool-subagent-control) | `subagent` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
|
2026-08-11 11:50:39 +08:00
|
|
|
| [`tool-subagent-report`](../packages/subagent/tool-subagent-report) | `subagent` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
2026-08-09 12:21:57 +08:00
|
|
|
| [`hooks-claude`](../packages/hooks/hooks-claude) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`web-app`](../packages/bundle/web-app) | `bundle` | [`bash-env`](../packages/bash/bash-env), [`invariants`](../packages/support/invariants), [`system-prompt`](../packages/core/system-prompt) |
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
| [`client-locale`](../packages/client/locale) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
|
2026-08-11 14:20:53 +08:00
|
|
|
| [`sdk-protocol`](../packages/sdk/protocol) | `sdk` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`tool-ralph`](../packages/workflow/tool-ralph) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
|
|
|
|
|
| [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
|
2026-08-09 19:36:15 +08:00
|
|
|
| [`compact-basic`](../packages/compact/compact-basic) | `compact` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`compact`](../packages/compact/compact), [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
|
2026-08-11 14:20:53 +08:00
|
|
|
| [`subagent-codex`](../packages/subagent/subagent-codex) | `subagent` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
2026-08-09 11:02:16 +08:00
|
|
|
| [`subagent-fork`](../packages/subagent/subagent-fork) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
|
|
|
|
|
| [`subagent-spawn`](../packages/subagent/subagent-spawn) | `subagent` | [`invariants`](../packages/support/invariants), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
|
2026-08-11 19:52:34 +08:00
|
|
|
| [`client-ui-plugin-config`](../packages/client/ui-plugin-config) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`invariants`](../packages/support/invariants) |
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
| [`client-ui-question`](../packages/client/ui-question) | `client` | [`client-locale`](../packages/client/locale), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-ui-slash`](../packages/client/ui-slash) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-ui-theme`](../packages/client/ui-theme) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`agent`](../packages/core/agent), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants), [`tools`](../packages/core/tools) |
|
|
|
|
|
| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`bash-env`](../packages/bash/bash-env), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`paths`](../packages/util/paths), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks-local`](../packages/tasks/tasks-local), [`tool-bash`](../packages/bash/tool-bash), [`tool-goal`](../packages/goal/tool-goal), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
|
|
|
|
|
| [`jsonrpc`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
|
|
|
|
|
| [`sdk-client`](../packages/sdk/client) | `sdk` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session) |
|
|
|
|
|
| [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
|
2026-08-11 19:44:01 +08:00
|
|
|
| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | [`agent`](../packages/core/agent), [`api-remotes`](../packages/api/remotes), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-attachment`](../packages/client/ui-attachment), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`commands`](../packages/interaction/commands), [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants), [`llm-retry`](../packages/llm/llm-retry), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools) |
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
| [`client-ui-layout`](../packages/client/ui-layout) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`acp-demo`](../packages/examples/acp-demo) | `examples` | [`acp`](../packages/acp/acp), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/boot/app-boot), [`invariants`](../packages/support/invariants), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
|
|
|
|
|
| [`host-directory-picker-browse`](../packages/host/directory-picker-browse) | `host` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`host-directory-picker-native`](../packages/host/directory-picker-native) | `host` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`invariants`](../packages/support/invariants) |
|
|
|
|
|
| [`client-ui-command`](../packages/client/ui-command) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
|
2026-08-10 08:08:24 -07:00
|
|
|
| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
|
2026-08-10 17:13:01 +08:00
|
|
|
| [`client-ui-goal`](../packages/client/ui-goal) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants) |
|
2026-08-09 19:36:15 +08:00
|
|
|
| [`client-ui-plan`](../packages/client/ui-plan) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`plan-mode`](../packages/plan/plan-mode) |
|
|
|
|
|
| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`subagent`](../packages/subagent/subagent), [`token-meter`](../packages/llm/token-meter) |
|
2026-08-10 13:22:51 +08:00
|
|
|
| [`client-ui-task`](../packages/client/ui-task) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
|
2026-08-09 19:36:15 +08:00
|
|
|
| [`client-ui-tool`](../packages/client/ui-tool) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
|
2026-08-10 18:37:30 +08:00
|
|
|
| [`client-ui-workflow-run`](../packages/client/ui-workflow-run) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) |
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
| [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
|
2026-08-11 17:12:41 +08:00
|
|
|
| [`client-ui-model`](../packages/client/ui-model) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-command`](../packages/client/ui-command), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
|
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
|
|
|
| [`client-ui-permission`](../packages/client/ui-permission) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-schema-form`](../packages/client/schema-form), [`client-ui-command`](../packages/client/ui-command), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`permission`](../packages/interaction/permission) |
|
2026-08-11 17:12:41 +08:00
|
|
|
| [`client-ui-skill`](../packages/client/ui-skill) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`client-ui-tool`](../packages/client/ui-tool), [`invariants`](../packages/support/invariants) |
|