feat(workbench): add browser workspace, kanban feedback, and executor CLI foundations#1
feat(workbench): add browser workspace, kanban feedback, and executor CLI foundations#1gu87 wants to merge 105 commits into
Conversation
- 新增 agent/token_juice.py(137行),对标 OpenHuman TokenJuice Gate 1: 短输出直通 (<240 chars) Gate 2: 文件查看命令不压缩 (cat/tail/head/bat) Gate 3: 无效摘要丢弃 (压缩比 >= 95% 则拒绝) - hermes_constants.py: 添加 TokenJuice 常量 - context_compressor.py: 在工具输出修剪和对话摘要两处集成安全阀
- model_metadata.py: 新增 MODEL_COST_PER_1K 价格表(10个主流模型) + get_model_cost_tier() 三级分档 (light/medium/heavy) + resolve_model_by_cost_tier() 按档位自动选模型 - auxiliary_client.py: 新增 get_cheapest_available_model(tier) - delegate_tool.py: 子 agent 创建时支持 cost_tier 自动路由 当 profile 设了 cost_tier 但未指定 model 时,自动选最便宜模型
- 新增 plugins/memory/fts5_builtin/ 插件(~250行,零依赖) - store.py: SQLite + FTS5 虚拟表 + 自动同步触发器 - __init__.py: FTS5BuiltinProvider 实现 MemoryProvider 接口 3 个工具: fts5_add / fts5_search / fts5_list prefetch() 走 user message 注入路径(保护 prefix cache) - 配置: memory.provider: fts5_builtin
…tests - token_juice: add absolute/relative floor checks in compression_safe_to_apply - fts5_builtin: add on_memory_write mirror, UNIQUE constraint, WAL mode, limit clamping - tests: add Gate3 regression, model cost-tier routing, fts5 provider tests - assets: add screenshots, search configs, and worldcup snapshot Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- DiscordEntryAdapter: normalizes Discord message payloads into EntryEvent - Maps: guild->source, category->workspace, channel->session, thread->sub-session - Bot/self-message rejection, intent detection (mention/command) - Health reporting: configured/unconfigured - Full isolation: adapter does not call agents/routes/policy directly - 39 tests covering registration, normalization, mapping, rejection, unsupported entrypoints, adapter isolation, health, intent, pipeline - Adds docs/known-issues/mcp-tooling-infra-test-failures.md for pre-existing infrastructure test debt classification
- FeishuEntryAdapter: normalizes Feishu inbound payloads into EntryEvent - Maps: tenant_id -> workspace, chat_id -> session, thread_id -> sub-session - Preserves existing Feishu transport (gateway/platforms/feishu.py untouched) - Session_key support for backward compatibility with existing session model - Intent detection: command (/) and mention (@) - Health reporting: configured/unconfigured based on app_id - 41 tests covering registration, normalization, mapping, rejection, unsupported entrypoints, adapter isolation, health, intent, pipeline - Audit doc: docs/audits/feishu-source-of-truth-audit.md
- FeishuSessionBindingBridge: sidecar module that records Feishu session
bindings without affecting existing message flow
- Hooked into FeishuAdapter._process_inbound_message() after session_key
is built but before _dispatch_inbound_event()
- Uses FeishuEntryAdapter (Phase 5A) to normalize SessionSource into EntryEvent
- Writes to SessionBinding store (Phase 1) with key=feishu:{chat_id}:{thread_id}
- Fully isolated: failures are caught and logged, main flow continues
- Preserves all existing behavior: SessionStore, dispatch, transport untouched
- 9 tests covering happy path, thread/no-thread, idempotency, graceful
failure, workspace derivation, lookup round-trip, fallback, no side effects
…teractive session cards Phase 5B implementation: - feishu_session_resolver.py: Full resolution chain (card > alias > p2p > thread > group > ambiguity > default) with FEISHU_AMBIGUITY_CARD_ENABLED feature flag (default: false) - feishu_session_cards.py: Interactive card JSON builder for session selection, acknowledgement, and rejection cards - FeishuEntryAdapter.resolve_session_with_ambiguity(): Delegates to resolver for full priority chain resolution - session_binding.py: SessionBindingValue dataclass with source tracking (card/thread/alias/default), backward-compatible read of old 2-tuple format, BindingSource literal type - feishu_session_binding_bridge.py: Extended with resolve_feishu_session_from_source(), check_feishu_ambiguity(), build_session_selection_card(), handle_select_session_card_action() Tests: 177 v2.10 scoped tests pass (28 resolver + 17 binding + 21 bridge + 41 feishu adapter + 36 discord + 14 entry + 20 workspace/session)
Phase 5C implementation: - feishu_permission_gate.py: Two-layer access control model (global allowlist + per-group policy: open/allowlist/blacklist/admin_only/disabled) Advisory only — does not modify routing or policy execution. Includes from_env() builder, check() method, health() snapshot. - feishu.py: Add select_session card action handler (~8 lines) When a user taps a session selection card button, the action is dispatched to handle_select_session_card_action() from the bridge. - Tests: 23 permission gate tests, 200 v2.10 scoped total
…lthCard, V210WorkspaceContext - Add 5 backend API endpoints: workspaces list/get, sessions list/get, adapters/health - Add v2.10 TypeScript types and API methods in api.ts - Add AdapterHealthCard component to OperationsPage - Add V210WorkspaceContext component to SessionsPage - Add 12 backend API tests - Add Phase 6 plan doc Backend: 212 v2.10 scoped tests pass Frontend: zero errors build
Phase 7 design doc for Mac App as lightweight desktop shell: - WKWebView wrapping Web Console at localhost:9119 - Process lifecycle management - macOS notification bridge - mac_app EntryAdapter registration - No code — design documentation only
Gate checks: - Working tree clean, branch codex/merge-8787-capabilities-into-9119 - 212 v2.10 scoped tests passing, 0 failures - Frontend build clean (TypeScript + Vite) - Feishu transport: 9-line sidecar, feature-flagged - No silent fallbacks, no ambiguities in disabled state - All non-capabilities explicitly documented Recommended tag: v2.10-multi-entry-session-binding
- Tag: v2.10-multi-entry-session-binding (858af63) - 6 phases implemented, 212 tests passing - Deferred: Lark CLI tooling, streaming cards, approvals, Discord prod, Mac App - Recommended next: v2.11 Feishu Operation Layer
Six sections covering: - A: Web Console (AdapterHealthCard, V210WorkspaceContext, API endpoints) - B: Feishu understandability (entry adapter, resolver, ambiguity, permission gate) - C: Discord dormant status verification - D: Release boundaries (Lark CLI, streaming, approvals — none present) - E: Observations log - F: Go/No-Go for v2.11 All checks manual, no code changes required.
…d, V210WorkspaceContext - Add operations + v210 i18n keys to types.ts, en.ts, zh.ts - Translate key OperationsPage labels: title, agent health, queue, advisory - Translate AdapterHealthCard: status labels, mode descriptions - Translate V210WorkspaceContext: mode, workspaces, sessions, adapters - Entrypoint labels in AdapterHealthCard now show in Chinese (飞书, CLI, etc.) - All v2.10 components use i18n with fallback to English - operations + v210 fields are optional in type (non-en/non-zh unchanged) Frontend build: zero errors. Backend tests: 12 passed.
Bug fixes:
- setTitle('Operations') → setTitle(t.operations?.title ?? ...) with t dependency
- H1 header uses i18n (Agent 运营中心)
- Section labels use i18n (Agent 健康, 队列与调度)
- Badge labels use i18n (仅作参考, 仅作建议)
Fixed: original variable rename corrupted UI text strings (Some operations data).
This version only changes display labels, never touches variable names.
Build: successful. Tests: 12/12 passed.
…tract
browser-host/: new Electron child-process (status/start/stop + snapshot/screenshot/context proxies) — Phase 2B + 2C
plugins/browser-workspace/dashboard/: Codex-like sidebar tab (manifest + bundled dist/index.js) — Phase 4 entry point
hermes_cli/web_server.py: expose /api/browser-host/{status,start,stop,snapshot,screenshot,context} and read-only HTTP proxy to the host on localhost — Phase 2B/2C
web/src/pages/ChatPage.tsx: window.__HERMES_INSERT_CHAT_TEXT__ (term.paste path) + window.__HERMES_SET_BROWSER_CONTEXT_REF__ (metadata only — no DOM/selection/clipboard) — Phase 4A/4B
Out of scope: Kanban review/qa CLI + diff events; Codex-like embedded-chat nav reshuffle; ToolCall selected ring; ChatSidebar structured preview parser; executors/ module; dashboard screenshots.
Add review/qa CLI subcommands, emit diff events after dispatch, and publish diff/review/qa task events to the dashboard WebSocket stream. Includes focused CLI tests for dispatch diff recording and review/QA result events.
Parse diff, review, QA, and artifact payloads from tool events and render them in a dedicated ChatSidebar detail panel. Add ToolCall onClick/selected props for activity-list selection and highlight state, with graceful fallback for unparseable results.
Redirect root and unknown routes to /chat when embedded chat mode is enabled, slim the sidebar nav for chat-first usage, and add Config diagnostics links for hidden dashboard pages.
Add the core executor types, registry, router, and health helpers behind a core-safe package API that does not import adapter implementations. Add router and registry tests covering keyword recommendations, fallback behavior, default manifests, and missing-binary health checks without invoking live models.
Add Hermes Local, Claude Code, Codex, OpenCode, and DeepSeek TUI executor adapters implementing the AgentExecutorAdapter protocol. CLI adapters use asyncio subprocess array calls without shell=True; Hermes Local restores cwd with try/finally; DeepSeek TUI remains an explicit unavailable stub. Add adapter registry tests covering imports, missing-binary health checks, inert imports, and no live CLI/model/worktree side effects.
Add project-scoped workspace context, inbox persistence, and per-executor prompt building utilities. These tools write only to caller-supplied project .hermes files and do not invoke subprocesses, models, git, or external services. Add tests covering JSON round trips, default loading, prompt generation, inbox lifecycle, and side-effect boundaries.
Add worktree lifecycle utilities and CLI helpers with dirty-tree rejection and confirmation gates for destructive merge/discard operations. Fix the clean-working-tree preflight check so user dirty files reject worktree creation while .hermes infrastructure files are ignored as intended. Add worktree tests covering dirty rejection, infra filtering, merge/discard confirmation, and tmp-repo-only git operations.
Add IPC dataclasses, review/QA prompt parsing primitives, and a fixture-driven bridge CLI simulation layer without importing the real review handler. Keep D1a read-only and subprocess-free; bridge CLI uses local stubs for review/QA simulation while D1b decides the real review backend boundary. Fix diff deletion parsing so file header lines are not counted as deletions.
Adds the executors/IPC rail for review and QA, parallel to the existing hermes_cli.kanban_feedback trigger_* path. The two rails target different consumers (Kanban SQLite vs. Electron IPC) and are NOT interchangeable. - executors/review_handler.py: trigger_review_ipc / trigger_qa_ipc async wrappers around _launch_opencode, with shutil.which gate, OpencodeUnavailable exception, emit_diff_event side-channel, and stub_review_report / stub_qa_report fallback when opencode is absent. Module docstring documents the dual-rail architecture to prevent confusion with kanban_feedback.trigger_*. - executors/review_cli.py: 6 subcommand handlers (build-prompt / parse / executor for both review and QA) + handle_review_command / handle_qa_command dispatchers. SEVERITY_ICONS map for human-readable output. - tests/executors/test_review_handler.py: 582 lines, 9 test classes covering rename verification, IPC happy paths, fallback paths, subprocess invocation, side-channel events, stub reports, exception handling, and boundary guards (no kanban_feedback, no executors.cli, no executors.bridge, no sqlite3 imports, no real opencode invocation). - tests/executors/test_review_cli.py: 443 lines, 9 test classes covering all 6 subcommand handlers, dispatchers, SEVERITY_ICONS lookup, and parallel boundary guards. The rename trigger_* -> trigger_*_ipc was required to disambiguate from hermes_cli.kanban_feedback.trigger_* which writes to the Kanban SQLite task_events table. Both are live in parallel; callers pick the rail appropriate to their consumer (CLI vs. Electron IPC). 70 new tests; full executors suite: 415 passing (was 345).
Add the argparse-based executor CLI entry point wiring registry, health, routing, worktree, context, review, QA, inbox, and bridge subcommands. Preserve destructive operation gates by delegating worktree actions to the guarded C2 handlers, and keep imports free of hermes_cli or live external CLI side effects. Add CLI tests covering registry commands, delegation, help output, missing binaries, import side effects, and boundary guards.
🚨 CRITICAL Supply Chain Risk DetectedThis PR contains a pattern that has been used in real supply chain attacks. A maintainer must review the flagged code carefully before merging. 🚨 CRITICAL: Install-hook file added or modifiedThese files can execute code during package installation or interpreter startup. Files: Scanner only fires on high-signal indicators: .pth files, base64+exec/eval combos, subprocess with encoded commands, or install-hook files. Low-signal warnings were removed intentionally — if you're seeing this comment, the finding is worth inspecting. |
🔎 Lint report:
|
| Rule | Count |
|---|---|
PLW1514 |
2 |
First entries
executors/review_cli.py:159: [PLW1514] `pathlib.Path(...).read_text` without explicit `encoding` argument
executors/worktree.py:573: [PLW1514] `open` in text mode without explicit `encoding` argument
✅ Fixed issues: none
Unchanged: 0 pre-existing issues carried over.
ty (type checker)
Total: 9033 on HEAD, 8915 on base (🆕 +118)
🆕 New issues (166):
| Rule | Count |
|---|---|
unresolved-attribute |
45 |
unresolved-import |
40 |
invalid-argument-type |
33 |
deprecated |
8 |
unsupported-operator |
7 |
invalid-assignment |
6 |
invalid-parameter-default |
5 |
unresolved-reference |
4 |
not-iterable |
4 |
invalid-return-type |
4 |
not-subscriptable |
4 |
no-matching-overload |
2 |
empty-body |
2 |
invalid-type-form |
1 |
call-non-callable |
1 |
First entries
tests/agent/test_entry_adapter.py:242: [unresolved-attribute] unresolved-attribute: Attribute `resolve_session` is not defined on `None` in union `EntryAdapter | None`
tools/delegate_tool.py:4071: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_subagent_effective_blocked` on type `ExternalCliChild`
tests/hermes_cli/test_web_server.py:336: [unresolved-attribute] unresolved-attribute: Attribute `append` is not defined on `int` in union `int | list[Unknown]`
executors/codex_adapter.py:280: [deprecated] deprecated: The function `utcnow` is deprecated: Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .now(datetime.timezone.utc)
tests/managed_agents/test_kanban_bridge.py:201: [unresolved-attribute] unresolved-attribute: Unresolved attribute `execution_plan` on type `Task`
agent/conversation_loop.py:4121: [unresolved-attribute] unresolved-attribute: Attribute `fail` is not defined on `None` in union `Any | None`
tests/agent/test_entry_adapter.py:231: [unresolved-attribute] unresolved-attribute: Attribute `resolve_workspace` is not defined on `None` in union `EntryAdapter | None`
tests/plugins/memory/test_fts5_builtin_provider.py:9: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tests/agent/test_feishu_entry_adapter.py:6: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tests/agent/test_entry_adapter.py:3: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
agent/managed_agents/session_binding.py:58: [invalid-argument-type] invalid-argument-type: Argument is incorrect: Expected `Literal["card", "thread", "alias", "default"]`, found `str`
tests/executors/test_cli.py:135: [invalid-parameter-default] invalid-parameter-default: Default value of type `None` is not assignable to annotated parameter type `list[dict[str, Any]]`
tools/delegate_tool.py:2544: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_current_model_ref` on type `AIAgent`
tests/managed_agents/test_registry.py:4: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tests/executors/test_registry.py:252: [unresolved-attribute] unresolved-attribute: Attribute `lower` is not defined on `None` in union `str | None`
hermes_cli/kanban_db.py:1067: [unresolved-reference] unresolved-reference: Name `shutil` used when not defined
tools/delegate_tool.py:4321: [invalid-argument-type] invalid-argument-type: Argument to function `_build_external_cli_child` is incorrect: Expected `str`, found `Any | str | None | list[str]`
tests/executors/test_context.py:26: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tests/managed_agents/test_gateway.py:6: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
agent/conversation_loop.py:287: [invalid-assignment] invalid-assignment: Object of type `Any | None` is not assignable to attribute `fallback_used` on type `TaskCard | None`
tests/executors/test_review_cli.py:36: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tests/executors/test_ipc.py:20: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
gateway/platforms/feishu.py:2574: [unresolved-reference] unresolved-reference: Name `P2CardActionTrigger` used when not defined
tests/agent/test_feishu_permission_gate.py:4: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tests/executors/test_review_handler.py:546: [not-iterable] not-iterable: Object of type `None` is not iterable
... and 141 more
✅ Fixed issues (76):
| Rule | Count |
|---|---|
invalid-argument-type |
55 |
unresolved-attribute |
11 |
unsupported-operator |
4 |
invalid-assignment |
3 |
unresolved-import |
1 |
not-subscriptable |
1 |
unresolved-reference |
1 |
First entries
tests/skills/test_openclaw_migration.py:33: [invalid-argument-type] invalid-argument-type: Argument to function `module_from_spec` is incorrect: Expected `ModuleSpec`, found `ModuleSpec | None`
tests/hermes_cli/test_setup_openclaw_migration.py:427: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["Discord"]` and `str | None`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2467: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["config.yaml session_reset"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2477: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["openclaw.json session (advanced)"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2410: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["archive/gateway-config.json"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2640: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["archive/channels-deep-config.json"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2757: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["openclaw.json approvals (rules)"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2772: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["archive/memory-backend-config.json"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2478: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["archive/session-config.json"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2680: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["openclaw.json browser (advanced)"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2715: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["config.yaml terminal"]`
tools/delegate_tool.py:3557: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> tuple[int, dict[str, Any] | dict[str, str | None | list[str]], Unknown], (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[tuple[int, dict[str, Any] | dict[str, str | None | list[str]], Unknown]]]` cannot be called with key of type `str` on object of type `list[tuple[int, dict[str, Any] | dict[str, str | None | list[str]], Unknown]]`
tests/hermes_cli/test_setup_openclaw_migration.py:426: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["Telegram"]` and `str | None`
agent/agent_router.py:374: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `str & ~AlwaysFalsy` and `dict[Unknown, Unknown] | None`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2788: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["archive/skills-registry-config.json"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2375: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["config.yaml agent/compression/terminal"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:25: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to `<module 'yaml'>`
tests/hermes_cli/test_setup_openclaw_migration.py:561: [invalid-argument-type] invalid-argument-type: Argument to function `_gateway_platform_short_label` is incorrect: Expected `str`, found `str | list[str] | list[dict[str, str | bool]]`
agent/conversation_loop.py:216: [invalid-assignment] invalid-assignment: Object of type `Unknown` is not assignable to attribute `routing_basis` on type `TaskCard | None`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2264: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["openclaw.json hooks.*"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2552: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["agents.defaults.models"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2724: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["openclaw.json tools (full)"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2514: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal["config.yaml custom_providers"]`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:2585: [invalid-argument-type] invalid-argument-type: Argument to function `Migrator._get_channel_field` is incorrect: Expected `str`, found `str | dict[str, str] | Unknown`
optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py:1148: [invalid-argument-type] invalid-argument-type: Argument to bound method `Migrator.record` is incorrect: Expected `Path | None`, found `Literal[""]`
... and 51 more
Unchanged: 4644 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
|
Wrong base repository/branch. This PR was opened against stale gu87:main, causing unrelated upstream files to appear in the diff (+82,204/−9,213, 347 files, 105 commits instead of the expected +18,058/−27, 60 files, 11 commits). Reopening against NousResearch/hermes-agent:main. |
Summary
This branch delivers three foundation layers for the Hermes Agent desktop workbench. First, a read-only Browser Workspace — an Electron host that renders external URLs and exposes a chat message insertion contract, without any browser automation capability. Second, kanban review/QA feedback events — structured defect tracking wired into the CLI kanban surface. Third, a complete executors framework spanning a typed registry, capability-based router, five CLI adapters, guarded worktree tools, context/inbox/prompt tooling, an IPC bridge, review chain primitives, and a top-level argparse CLI entry point. Two UI upgrades ship alongside: structured tool-result preview panels and embedded chat session routing. All executor modules are backed by 463 passing tests, destructive operations are gated behind confirmation prompts, and import side-effect guards prevent the CLI from leaking into live subsystems.
What Changed
Browser Workspace
main.ts(lifecycle, window management, IPC handlers),preload.ts(context bridge),renderer.html(shell),schema.ts(typed contracts)browser-workspace:insert-chat-message) validated against a goldencontract-snapshot.jsonviavalidate-contract.mjsplugins/browser-workspace/dashboard/Kanban Review/QA Feedback
hermes_cli/kanban_feedback.py— structured review and QA defect event types with typed routinghermes_cli/kanban.py— kanban surface integration gluehermes_cli/web_server.py— lightweight HTTP server for dashboard plugin deliverytests/hermes_cli/test_kanban_cli.pyStructured Preview UI
web/src/components/ToolCall.tsx— structured preview panel that renders tool results with formatted sections instead of raw JSON blobsweb/src/components/ChatSidebar.tsx— expanded sidebar with session list, routing, and status indicatorsweb/src/pages/ChatPage.tsx/web/src/pages/ConfigPage.tsx— page-level wiring for preview and sidebarweb/src/App.tsx— top-level routing integrationEmbedded Chat Routing Mode
Executors Framework
A — Core Registry and Router
executors/registry.py— typed executor registry with binary discovery and structured health probesexecutors/router.py— capability-based router that produces recommendations and accepts user overridesexecutors/types.py— shared dataclass type system (capabilities, routing decisions, health status, executor info)executors/health.py— per-executor health checks with table and JSON output formattersB — Adapters
executors/claude_code_adapter.py— Claude Code CLI adapterexecutors/codex_adapter.py— Codex adapterexecutors/deepseek_tui_adapter.py— DeepSeek TUI adapterexecutors/hermes_local_adapter.py— Hermes local adapterexecutors/opencode_adapter.py— OpenCode adapterExecutorCapabilitiescontract defined intypes.pyC1 — Context / Inbox / Prompt Tools
executors/context.py+executors/context_cli.py— workspace context injection CLIexecutors/inbox.py+executors/inbox_cli.py— external inbox management CLIexecutors/prompt_builder.py— structured prompt assembly from context + task specC2 — Guarded Worktree Tools
executors/worktree.py+executors/worktree_cli.py— worktree create, list, merge, and discard--force; the gate is verified in testsD1a — Bridge / IPC / Review Parsing Primitives
executors/ipc.py— typed IPC message protocol with structured envelopesexecutors/bridge.py+executors/bridge_cli.py— run-event-log ↔ review bridgeexecutors/review_agent.py— review chain agent logicD1b — Review Handler + Review CLI
executors/review_handler.py— review orchestration handlerexecutors/review_cli.py— review subcommand CLID2 — Top-Level CLI
executors/cli.py— argparse-based entry point wiring all subcommands: registry, health, routing, worktree, context, review, QA, inbox, and bridgeexecutors/__init__.py— package public API surfaceshutil.which, loadhermes_cli, or spawn subprocessesSafety Boundaries
--force. Tests verify the gate blocks unconfirmed destructive calls.executors/cli.pydoes not triggershutil.which, does not loadhermes_cli, does not spawn subprocesses, and does not importworktree,kanban_feedback, or other uncommitted modules at the top level.$TMPDIR. No repository-level destructive operations are performed by any test.Validation
validate-contract.mjs)web/)py_compile— all executor modulestests/executors/full suitetests/executors/test_cli.py--forcebypassesWhat Did Not Ship
chrome.tabs/chrome.scripting/chrome.debuggerbridgeChatSessionSidebarwiring — kept as a local excluded draft, not connected to the app shell in this PRbrowser_adapter.py)Risks / Follow-ups
kanban_feedbackevent path by design. Future work may bridge them at the routing or bridge layer, but for now they are independent subsystems.ChatSessionSidebaris kept as a local excluded draft. It is not wired into the app shell and is not part of this PR. Downstream work will need to integrate it.hermes_cli/kanban_feedback.pyintroduces a new feedback event path. Its interaction with the existing kanban CLI surface should be documented before downstream consumers adopt it.Commit Breakdown
ce62fbc5cfeat(browser-workspace): Electron host lifecycle + chat insertion contract312050bfefeat(kanban): add review and QA feedback events313b5d896feat(ui): add structured preview panel for tool results86d2b64e7feat(ui): add embedded chat routing mode51671f813feat(executors): add core registry and router APIb9b28ddf4feat(executors): add CLI adapter implementationsdb8129cf0feat(executors): add context inbox and prompt toolsa95211d9efeat(executors): add guarded worktree tools50bb17679feat(executors): add bridge IPC and review parsing primitives5b150a6befeat(executors): add review handler and CLI subcommands7bbb0a3edfeat(executors): add top-level CLI integration🤖 Generated with Claude Code