synapse-cli-agent 0.1.17__tar.gz → 0.1.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/.gitignore +2 -1
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/AGENTS.md +52 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/CHANGELOG.md +20 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/PKG-INFO +2 -2
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/README.md +1 -1
- synapse_cli_agent-0.1.18/decode_screenshot.py +26 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/pyproject.toml +1 -1
- synapse_cli_agent-0.1.18/release_body.md +19 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/app/agent.py +19 -1
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/mcp.py +8 -9
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/mcp_client.py +59 -7
- synapse_cli_agent-0.1.18/src/synapse/observability/debug_inspector.html +19 -0
- synapse_cli_agent-0.1.18/src/synapse/observability/debug_server.py +430 -0
- synapse_cli_agent-0.1.18/src/synapse/observability/llm_debug.py +295 -0
- synapse_cli_agent-0.1.18/src/synapse/runtime/debug_capture_middleware.py +80 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tools/__init__.py +3 -0
- synapse_cli_agent-0.1.18/src/synapse/tools/describe_image.py +92 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/transcript_blocks.py +0 -28
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/tui.py +52 -7
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/turn_rail_widgets.py +7 -1
- synapse_cli_agent-0.1.18/tests/test_debug_inspector.py +115 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_describe_image.py +45 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_slash_and_mcp.py +54 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_turn_rail_minimap.py +18 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/uv.lock +5 -5
- synapse_cli_agent-0.1.17/release_body.md +0 -16
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/.github/workflows/ci.yml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/.github/workflows/docs.yml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/.github/workflows/native-compression-wheels.yml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/.github/workflows/native-search-wheels.yml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/.github/workflows/release.yml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/.python-version +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/.readthedocs.yaml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/agent_upstream.txt +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/cli_upstream.txt +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/agent-development-guide.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/architecture-flow.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/architecture.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/cli.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/codex-session-discovery-phase-1.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/codex-session-import-phase-2.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/config.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/design.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/index.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/install.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/mcp.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/models.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/native-compression-core.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/native-search-core.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/permissions.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/quickstart.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/sessions.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/skills.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/smith_output.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/tool-output-transform.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/tui-cursor-refactor.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/tutorial.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/docs/x-agent-development-guide.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/examples/mcp.example.json +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/examples/models.example.json +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/examples/settings.example.json +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/middleware_upstream.txt +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/mkdocs.yml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-search-core/Cargo.lock +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-search-core/Cargo.toml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-search-core/LICENSE +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-search-core/NOTICE +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-search-core/README.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-search-core/pyproject.toml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-search-core/python/synapse_search_core/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-search-core/src/lib.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/Cargo.lock +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/Cargo.toml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/LICENSE +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/NOTICE +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/README.md +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/pyproject.toml +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/python/synapse_tool_compress_core/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/ccr.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/ccr_in_memory.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/mod.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/relevance/base.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/relevance/bm25.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/signals/keyword_detector.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/signals/line_importance.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/adaptive_sizer.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/anchor_selector.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/code_compressor.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/diff_compressor.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/log_compressor.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/search_compressor.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/analyzer.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/anchors.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/builder.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/classifier.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/classifier.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/compactor.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/formatter.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/ir.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/mod.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/walker.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/config.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/constraints.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/crusher.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/crushers.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/error_keywords.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/field_detect.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/hashing.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/mod.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/observer.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/orchestration.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/outliers.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/planning.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/statistics.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/stats_math.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/traits.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/types.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/rust/synapse-tool-compress-core/src/lib.rs +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/scripts/install.ps1 +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/scripts/release.ps1 +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/slash_cmds_upstream.txt +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/__main__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/app/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/app/agent_md.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/cli.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/compression.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/helpers.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/model.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/result.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/sessions.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/slash_cmds.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/slash_complete.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/commands/theme.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/config.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/content/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/content/input_history.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/content/multimodal.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/content/prompts.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/content/skills_catalog.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/checkpoint_seed.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/codex_history.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/codex_import.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/codex_sessions.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/describe_image.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/http_clients.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/llm_openai_compat.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/llm_openai_websocket.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/openai_oauth.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/openai_oauth_middleware.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/vision_middleware.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/memory/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/memory/auto_recorder.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/memory/embedder.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/memory/long_term.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/models/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/models/config.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/models/helpers.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/models/profile.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/models/registry.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/models_registry.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/observability/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/observability/startup_trace.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/parallel_subagents.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/planner/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/planner/task_planner.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/rag/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/rag/knowledge_base.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/async_runtime.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/backends.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/context_compact.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/execute_capture.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/fs_permissions.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/harness.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/hitl.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/interaction_ledger.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/middleware.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/model_request_compression_middleware.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/pathing.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/safety.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/steer.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/subagents.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/tool_ignore.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/tool_output_eval.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/tool_output_middleware.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/runtime/tool_output_usage_middleware.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/sessions/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/sessions/cancel_repair.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/sessions/session_recap.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/sessions/store.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/sessions/transcript.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/settings/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/settings/config_paths.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/settings/schema.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/subagent_monitor.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tool_output/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tool_output/detection.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tool_output/metrics.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tool_output/models.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tool_output/pipeline.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tool_output/repository.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tool_output/transformers.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tools/filesystem_search.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/tools/session_tools.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/answer_divider.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/bottombar/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/bottombar/components/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/bottombar/components/key_hints.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/bottombar/components/mcp.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/bottombar/components/mode.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/bottombar/components/model.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/bottombar/components/thread.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/bottombar/context.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/bottombar/core.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/clipboard.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/base.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/codex_session_list.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/compression_diagnostics.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/git_explore.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/mcp_panel.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/model_picker.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/safety_panel.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/session_list.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/subagent_monitor.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/theme_designer.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/dialogs/theme_picker.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/formatters.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/git_explore/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/git_explore/engine.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/git_explore/provider.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/git_explore/unified.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/rendering.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/selectable_static.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/selectable_text.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/sink.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/steer_widget.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/stream.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/stream_events.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/stream_runtime.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/textual_stream_sink.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/theme.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/timeline.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/tool_blocks.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/components/__init__.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/components/branch.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/components/title.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/components/tool_output.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/components/usage.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/components/workspace.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/context.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/core.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/git_changes_popover.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/git_chrome.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/tool_output_popover.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/topbar/widget.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/turn_rail.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/user_turn.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/user_turn_block.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/ui/welcome.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/synapse.cmd +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/codex_rollouts/duplicate_response_item.jsonl +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/codex_rollouts/incomplete_and_aborted.jsonl +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/codex_rollouts/legacy_compaction.jsonl +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/codex_rollouts/normal_completed.jsonl +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/codex_rollouts/replacement_history.jsonl +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/codex_rollouts/rollback.jsonl +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/codex_rollouts/unsupported_replacement.jsonl +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/sample_repo/calculator.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/sample_repo/test_calculator.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/fixtures/tool_output_eval.json +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_agent_factory.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_async_checkpointer.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_backends.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_bottombar_registry.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_cancel_repair.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_capabilities.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_checkpoint_seed.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_cli.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_cli_codex_import.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_codex_history.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_codex_import.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_codex_sessions.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_codex_sessions_windows.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_compact_tool_descriptions.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_config.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_context_compact.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_context_window.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_dag_parallel.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_dialogs.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_filesystem_search_tools.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_git_changes_popover.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_git_chrome.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_git_explore.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_http_clients.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_input_history.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_iteration_abc.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_layered_config.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_mcp_schema.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_mermaid_render.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_middleware_async.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_model_request_compression_middleware.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_model_retry.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_multimodal.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_openai_compat_reasoning.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_openai_oauth.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_openai_oauth_middleware.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_openai_websocket.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_pathing.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_safety.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_session_recap.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_slash_complete.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_startup_defer.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_steer.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_stream_cancel.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_stream_tool_items.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_stream_ui.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_subagent_monitor_dialog.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_subagent_status.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_theme.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_timeline.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_tool_ignore.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_tool_intent.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_tool_output.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_tool_output_eval.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_tool_output_slash.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_tool_output_usage_middleware.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_topbar_context_ratio.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_topbar_registry.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_transcript.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_tui_codex_import.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_tui_selection.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_tui_sink.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_usage_restore.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_user_turn_style.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/test_welcome.py +0 -0
- {synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/tests/tool_output_plugin_fixture.py +0 -0
|
@@ -131,3 +131,55 @@ powershell -ExecutionPolicy Bypass -File scripts/release.ps1
|
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
8. 脚本会创建并推送 `v{version}` tag;`.github/workflows/release.yml` 会从 `CHANGELOG.md` 提取同名段落、执行 `uv build` 并创建 GitHub Release。
|
|
134
|
+
|
|
135
|
+
## 10. cdp_take_screenshot 工具使用说明
|
|
136
|
+
|
|
137
|
+
### 10.1 已知限制
|
|
138
|
+
- `filePath` 参数无法写入工作区目录(浏览器沙箱限制),所有指定 `filePath` 的尝试均会报 `Access denied`。
|
|
139
|
+
- 不指定 `filePath` 时,截图以文本格式(含 base64)自动存入 `/large_tool_results/call_xx_xxx`。
|
|
140
|
+
|
|
141
|
+
### 10.2 标准流程:截图 → 解码 → 识图
|
|
142
|
+
|
|
143
|
+
执行完 `cdp_take_screenshot`(不指定 `filePath`)后,按以下步骤操作:
|
|
144
|
+
|
|
145
|
+
#### Step 1 — 编写/复用解码脚本
|
|
146
|
+
如果工作区不存在 `/decode_screenshot.py`,创建它:
|
|
147
|
+
```python
|
|
148
|
+
import base64, re, sys, glob, os
|
|
149
|
+
|
|
150
|
+
files = glob.glob('large_tool_results/call_*')
|
|
151
|
+
if not files:
|
|
152
|
+
print("No screenshot files found")
|
|
153
|
+
sys.exit(1)
|
|
154
|
+
|
|
155
|
+
latest = max(files, key=os.path.getctime)
|
|
156
|
+
print(f"Processing: {latest}")
|
|
157
|
+
|
|
158
|
+
content = open(latest, 'r').read()
|
|
159
|
+
m = re.search(r"data='([^']+)'", content)
|
|
160
|
+
if not m:
|
|
161
|
+
print("No base64 data found")
|
|
162
|
+
sys.exit(1)
|
|
163
|
+
|
|
164
|
+
os.makedirs('.tmp', exist_ok=True)
|
|
165
|
+
out = f'.tmp/screenshot_{os.path.getmtime(latest)}.png'
|
|
166
|
+
open(out, 'wb').write(base64.b64decode(m.group(1)))
|
|
167
|
+
print(f"OK -> {out}")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### Step 2 — 执行解码
|
|
171
|
+
```bash
|
|
172
|
+
python decode_screenshot.py
|
|
173
|
+
# 输出示例: OK -> .tmp/screenshot_1712345678.1234567.png
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Step 3 — 调用识图
|
|
177
|
+
```bash
|
|
178
|
+
# 使用 Step 2 返回的文件名
|
|
179
|
+
describe_image(image_path="/.tmp/screenshot_1712345678.1234567.png", ...)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 10.3 注意事项
|
|
183
|
+
- 每次截图都会在 `/large_tool_results/` 生成一个新文件,解码脚本自动选最新的。
|
|
184
|
+
- 输出到 `/.tmp/` 目录,该目录需加入 `.gitignore`。
|
|
185
|
+
- 不要在对话中手动执行多步解码,应直接调用解码脚本。
|
|
@@ -7,6 +7,26 @@ The release workflow automatically extracts the matching section as release note
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## v0.1.18
|
|
11
|
+
|
|
12
|
+
### 新功能
|
|
13
|
+
|
|
14
|
+
- 新增 LLM Debug Inspector(`F11`),实时监控模型通信、工具调用和 token 消耗。
|
|
15
|
+
- Inspector 支持采集开关、跟随最新、按类型筛选(异常/工具/慢调用)、回合折叠和调用详情查看。
|
|
16
|
+
- Inspector 概览栏显示失败率(基于工具级错误检测),工具标签页展示失败工具及原因。
|
|
17
|
+
|
|
18
|
+
### 修复
|
|
19
|
+
|
|
20
|
+
- TUI:`F10` 恢复删除 session 弹框入口;修复鼠标选中与点击复制的冲突,拖选后自动复制。
|
|
21
|
+
|
|
22
|
+
### 工程改进
|
|
23
|
+
|
|
24
|
+
- `DebugCaptureRecord` 增加工具级错误检测(LangChain `ToolMessage.status` + 内容模式)。
|
|
25
|
+
- `_tool_pairs` 返回 `error` 字段,区分 "等待中"(result null)与 "真失败"(有错误内容)。
|
|
26
|
+
- Inspector 前端:失败率仅统计真正失败的工具,"待响应"不计入。
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
10
30
|
## v0.1.17
|
|
11
31
|
|
|
12
32
|
### 新功能
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synapse-cli-agent
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.18
|
|
4
4
|
Summary: Local coding agent built on LangChain Deep Agents (LocalShell, no sandbox)
|
|
5
5
|
Project-URL: Homepage, https://github.com/alex8224/synapse-agent
|
|
6
6
|
Project-URL: Repository, https://github.com/alex8224/synapse-agent
|
|
@@ -287,7 +287,7 @@ OpenAI Responses API 同时支持 HTTP/SSE 与普通 LLM WebSocket。profile 中
|
|
|
287
287
|
}
|
|
288
288
|
```
|
|
289
289
|
|
|
290
|
-
`vision_model.model`、`base_url`、`api_key_env` 均可更换;`api_key` 也支持,但建议使用环境变量。`think` 是识图模型独立的思考开关,开启后发送 `thinking: {"type": "enabled"}`;它不影响主模型。默认只处理本地/内存图片;将 `allow_remote_urls` 设为 `true` 才会把消息中的 HTTP(S) 图片 URL
|
|
290
|
+
`vision_model.model`、`base_url`、`api_key_env` 均可更换;`api_key` 也支持,但建议使用环境变量。`think` 是识图模型独立的思考开关,开启后发送 `thinking: {"type": "enabled"}`;它不影响主模型。默认只处理本地/内存图片;将 `allow_remote_urls` 设为 `true` 才会把消息中的 HTTP(S) 图片 URL 转发给识图服务。该服务只用于把图片转换成文字,配置后图片内容会发送到指定服务。主模型 profile 显式配置 `image_input=false` 时,Synapse 还会自动注入 `describe_image` 工具,让 Agent 主动读取并识别工作区内的图片;未配置 `vision_model` 时工具会明确返回不可用。多模态主模型会跳过 middleware,且不会注入该工具。
|
|
291
291
|
|
|
292
292
|
### MCP 服务器配置
|
|
293
293
|
|
|
@@ -251,7 +251,7 @@ OpenAI Responses API 同时支持 HTTP/SSE 与普通 LLM WebSocket。profile 中
|
|
|
251
251
|
}
|
|
252
252
|
```
|
|
253
253
|
|
|
254
|
-
`vision_model.model`、`base_url`、`api_key_env` 均可更换;`api_key` 也支持,但建议使用环境变量。`think` 是识图模型独立的思考开关,开启后发送 `thinking: {"type": "enabled"}`;它不影响主模型。默认只处理本地/内存图片;将 `allow_remote_urls` 设为 `true` 才会把消息中的 HTTP(S) 图片 URL
|
|
254
|
+
`vision_model.model`、`base_url`、`api_key_env` 均可更换;`api_key` 也支持,但建议使用环境变量。`think` 是识图模型独立的思考开关,开启后发送 `thinking: {"type": "enabled"}`;它不影响主模型。默认只处理本地/内存图片;将 `allow_remote_urls` 设为 `true` 才会把消息中的 HTTP(S) 图片 URL 转发给识图服务。该服务只用于把图片转换成文字,配置后图片内容会发送到指定服务。主模型 profile 显式配置 `image_input=false` 时,Synapse 还会自动注入 `describe_image` 工具,让 Agent 主动读取并识别工作区内的图片;未配置 `vision_model` 时工具会明确返回不可用。多模态主模型会跳过 middleware,且不会注入该工具。
|
|
255
255
|
|
|
256
256
|
### MCP 服务器配置
|
|
257
257
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
# 找到最新的 large_tool_results 文件
|
|
8
|
+
files = glob.glob('large_tool_results/call_*')
|
|
9
|
+
if not files:
|
|
10
|
+
print("No screenshot files found")
|
|
11
|
+
sys.exit(1)
|
|
12
|
+
|
|
13
|
+
latest = max(files, key=os.path.getctime)
|
|
14
|
+
print(f"Processing: {latest}")
|
|
15
|
+
|
|
16
|
+
content = open(latest).read()
|
|
17
|
+
m = re.search(r"data='([^']+)'", content)
|
|
18
|
+
if not m:
|
|
19
|
+
print("No base64 data found")
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
|
|
22
|
+
os.makedirs('.tmp', exist_ok=True)
|
|
23
|
+
ts = str(os.path.getmtime(latest))
|
|
24
|
+
out = f'.tmp/screenshot_{ts}.png'
|
|
25
|
+
open(out, 'wb').write(base64.b64decode(m.group(1)))
|
|
26
|
+
print(f"OK -> {out}")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## v0.1.18
|
|
2
|
+
|
|
3
|
+
### 新功能
|
|
4
|
+
|
|
5
|
+
- 新增 LLM Debug Inspector(`F11`),实时监控模型通信、工具调用和 token 消耗。
|
|
6
|
+
- Inspector 支持采集开关、跟随最新、按类型筛选(异常/工具/慢调用)、回合折叠和调用详情查看。
|
|
7
|
+
- Inspector 概览栏显示失败率(基于工具级错误检测),工具标签页展示失败工具及原因。
|
|
8
|
+
|
|
9
|
+
### 修复
|
|
10
|
+
|
|
11
|
+
- TUI:`F10` 恢复删除 session 弹框入口;修复鼠标选中与点击复制的冲突,拖选后自动复制。
|
|
12
|
+
|
|
13
|
+
### 工程改进
|
|
14
|
+
|
|
15
|
+
- `DebugCaptureRecord` 增加工具级错误检测(LangChain `ToolMessage.status` + 内容模式)。
|
|
16
|
+
- `_tool_pairs` 返回 `error` 字段,区分 "等待中"(result null)与 "真失败"(有错误内容)。
|
|
17
|
+
- Inspector 前端:失败率仅统计真正失败的工具,"待响应"不计入。
|
|
18
|
+
|
|
19
|
+
---
|
|
@@ -49,7 +49,11 @@ from synapse.settings import Settings
|
|
|
49
49
|
from synapse.tool_output.pipeline import ToolOutputTransformPipeline
|
|
50
50
|
from synapse.tool_output.repository import ToolOutputRepository
|
|
51
51
|
from synapse.tool_output.transformers import load_transformer_plugins
|
|
52
|
-
from synapse.tools import
|
|
52
|
+
from synapse.tools import (
|
|
53
|
+
build_describe_image_tools,
|
|
54
|
+
build_filesystem_search_tools,
|
|
55
|
+
build_session_tools,
|
|
56
|
+
)
|
|
53
57
|
|
|
54
58
|
|
|
55
59
|
def _build_checkpointer(settings: Settings):
|
|
@@ -390,6 +394,13 @@ def build_coding_agent(
|
|
|
390
394
|
getattr(selected_profile, "base_url", None) or getattr(settings, "openai_base_url", None),
|
|
391
395
|
)
|
|
392
396
|
vision_config = VisionModelConfig.from_registry(registry, settings)
|
|
397
|
+
tools.extend(
|
|
398
|
+
build_describe_image_tools(
|
|
399
|
+
image_input=getattr(selected_profile, "image_input", None),
|
|
400
|
+
backend=backend,
|
|
401
|
+
config=vision_config,
|
|
402
|
+
)
|
|
403
|
+
)
|
|
393
404
|
|
|
394
405
|
output_repository = ToolOutputRepository(settings.resolved_tool_output_db_path())
|
|
395
406
|
middleware: list[Any] = [
|
|
@@ -458,6 +469,13 @@ def build_coding_agent(
|
|
|
458
469
|
|
|
459
470
|
middleware.append(build_openai_oauth_compat_middleware())
|
|
460
471
|
|
|
472
|
+
# Debug capture middleware — innermost, records final provider-ready
|
|
473
|
+
# request/response pairs for the Debug Inspector (F11 / /debug).
|
|
474
|
+
from synapse.observability.llm_debug import get_debug_store
|
|
475
|
+
from synapse.runtime.debug_capture_middleware import build_debug_capture_middleware
|
|
476
|
+
|
|
477
|
+
middleware.append(build_debug_capture_middleware(get_debug_store()))
|
|
478
|
+
|
|
461
479
|
if progress is not None:
|
|
462
480
|
progress("compiling agent graph")
|
|
463
481
|
with span("create_deep_agent"):
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""MCP slash-command handler and presentation helpers."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import json
|
|
@@ -102,6 +103,7 @@ def _md_tool_list(names: list[str], warnings: list[str] | None = None) -> str:
|
|
|
102
103
|
lines.append(f"\nwarn: {w}")
|
|
103
104
|
return "\n".join(lines)
|
|
104
105
|
|
|
106
|
+
|
|
105
107
|
def handle_mcp(
|
|
106
108
|
args: list[str],
|
|
107
109
|
*,
|
|
@@ -216,8 +218,7 @@ def handle_mcp(
|
|
|
216
218
|
md_lines.append("|---|---|")
|
|
217
219
|
for transport, names in sorted(by_transport.items()):
|
|
218
220
|
md_lines.append(
|
|
219
|
-
f"| {markdown_escape(transport)} | "
|
|
220
|
-
f"{markdown_escape(', '.join(names))} |"
|
|
221
|
+
f"| {markdown_escape(transport)} | {markdown_escape(', '.join(names))} |"
|
|
221
222
|
)
|
|
222
223
|
if result.tools:
|
|
223
224
|
md_lines.append("")
|
|
@@ -291,9 +292,7 @@ def handle_mcp(
|
|
|
291
292
|
loaded = getattr(build_coding_agent, "last_mcp_servers", []) or []
|
|
292
293
|
warnings_list = getattr(build_coding_agent, "last_mcp_warnings", []) or []
|
|
293
294
|
tools = getattr(build_coding_agent, "last_mcp_tool_names", []) or []
|
|
294
|
-
notice = (
|
|
295
|
-
f"mcp '{target}' {'enabled' if changed.enabled else 'disabled'} 路 tools={len(tools)}"
|
|
296
|
-
)
|
|
295
|
+
notice = f"mcp '{target}' {'enabled' if changed.enabled else 'disabled'} tools={len(tools)}"
|
|
297
296
|
lines = [
|
|
298
297
|
f"mcp server '{target}' {'enabled' if changed.enabled else 'disabled'}; agent rebuilt",
|
|
299
298
|
f"loaded servers: {', '.join(loaded) or '(none)'}",
|
|
@@ -325,7 +324,7 @@ def handle_mcp(
|
|
|
325
324
|
return SlashResult(
|
|
326
325
|
handled=True,
|
|
327
326
|
lines=["mcp enabled (run /mcp reload to rebuild agent)"],
|
|
328
|
-
notice="mcp enabled
|
|
327
|
+
notice="mcp enabled reload pending",
|
|
329
328
|
settings_changed=True,
|
|
330
329
|
)
|
|
331
330
|
|
|
@@ -348,7 +347,7 @@ def handle_mcp(
|
|
|
348
347
|
return SlashResult(
|
|
349
348
|
handled=True,
|
|
350
349
|
lines=["mcp disabled; agent rebuilt without MCP tools"],
|
|
351
|
-
notice="mcp disabled
|
|
350
|
+
notice="mcp disabled tools=0",
|
|
352
351
|
agent=new_agent,
|
|
353
352
|
settings_changed=True,
|
|
354
353
|
)
|
|
@@ -372,9 +371,9 @@ def handle_mcp(
|
|
|
372
371
|
warnings = getattr(build_coding_agent, "last_mcp_warnings", []) or []
|
|
373
372
|
tools = getattr(build_coding_agent, "last_mcp_tool_names", []) or []
|
|
374
373
|
enabled_flag = bool(getattr(settings, "enable_mcp", True))
|
|
375
|
-
notice = f"mcp reloaded
|
|
374
|
+
notice = f"mcp reloaded servers={len(loaded)} tools={len(tools)}"
|
|
376
375
|
if not enabled_flag:
|
|
377
|
-
notice = "mcp reloaded
|
|
376
|
+
notice = "mcp reloaded disabled"
|
|
378
377
|
lines = [
|
|
379
378
|
f"agent rebuilt; mcp enabled={getattr(settings, 'enable_mcp', True)}",
|
|
380
379
|
f"loaded servers: {', '.join(loaded) or '(none)'}",
|
{synapse_cli_agent-0.1.17 → synapse_cli_agent-0.1.18}/src/synapse/integrations/mcp_client.py
RENAMED
|
@@ -13,11 +13,13 @@ import asyncio
|
|
|
13
13
|
import atexit
|
|
14
14
|
import json
|
|
15
15
|
import logging
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
16
18
|
import threading
|
|
17
19
|
from concurrent.futures import Future
|
|
18
20
|
from dataclasses import dataclass, field
|
|
19
21
|
from pathlib import Path
|
|
20
|
-
from typing import Any
|
|
22
|
+
from typing import Any, TextIO
|
|
21
23
|
|
|
22
24
|
logger = logging.getLogger(__name__)
|
|
23
25
|
|
|
@@ -71,6 +73,31 @@ def _expand_mapping(raw: dict[str, Any] | None) -> dict[str, str]:
|
|
|
71
73
|
return out
|
|
72
74
|
|
|
73
75
|
|
|
76
|
+
def _valid_subprocess_stream(stream: TextIO | None) -> TextIO | None:
|
|
77
|
+
"""Return a stream only when its OS descriptor is valid for inheritance."""
|
|
78
|
+
if stream is None or getattr(stream, "closed", False):
|
|
79
|
+
return None
|
|
80
|
+
try:
|
|
81
|
+
fd = stream.fileno()
|
|
82
|
+
os.fstat(fd)
|
|
83
|
+
except (AttributeError, OSError, ValueError):
|
|
84
|
+
return None
|
|
85
|
+
return stream
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _stdio_errlog() -> tuple[TextIO, bool]:
|
|
89
|
+
"""Choose a valid stderr target and report whether the caller owns it.
|
|
90
|
+
|
|
91
|
+
Textual, redirected Windows launchers, and GUI executables can leave
|
|
92
|
+
``sys.stderr`` present while its underlying descriptor is closed. The MCP
|
|
93
|
+
SDK passes this object to subprocess creation, which then raises EBADF.
|
|
94
|
+
"""
|
|
95
|
+
stream = _valid_subprocess_stream(sys.stderr)
|
|
96
|
+
if stream is not None:
|
|
97
|
+
return stream, False
|
|
98
|
+
return open(os.devnull, "w", encoding="utf-8"), True # noqa: SIM115
|
|
99
|
+
|
|
100
|
+
|
|
74
101
|
def _parse_server(raw: dict[str, Any]) -> McpServerConfig:
|
|
75
102
|
name = str(raw.get("name") or "").strip()
|
|
76
103
|
if not name:
|
|
@@ -337,10 +364,11 @@ class _LoopThread:
|
|
|
337
364
|
class _LiveServer:
|
|
338
365
|
config: McpServerConfig
|
|
339
366
|
session: Any
|
|
340
|
-
# Keep transport context managers open for process lifetime.
|
|
367
|
+
# Keep transport context managers and owned streams open for process lifetime.
|
|
341
368
|
transport_cm: Any
|
|
342
369
|
session_cm: Any
|
|
343
370
|
streams: Any = None
|
|
371
|
+
errlog: TextIO | None = None
|
|
344
372
|
|
|
345
373
|
|
|
346
374
|
class McpSessionPool:
|
|
@@ -385,6 +413,8 @@ class McpSessionPool:
|
|
|
385
413
|
await live.transport_cm.__aexit__(None, None, None)
|
|
386
414
|
except Exception: # noqa: BLE001
|
|
387
415
|
pass
|
|
416
|
+
if live.errlog is not None:
|
|
417
|
+
live.errlog.close()
|
|
388
418
|
self._servers.clear()
|
|
389
419
|
|
|
390
420
|
async def _open_stdio(self, server: McpServerConfig) -> _LiveServer:
|
|
@@ -398,16 +428,38 @@ class McpSessionPool:
|
|
|
398
428
|
args=server.args,
|
|
399
429
|
env=server.env or None,
|
|
400
430
|
)
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
431
|
+
errlog, owns_errlog = _stdio_errlog()
|
|
432
|
+
transport_cm = stdio_client(params, errlog=errlog)
|
|
433
|
+
transport_entered = False
|
|
434
|
+
session_cm = None
|
|
435
|
+
session_entered = False
|
|
436
|
+
try:
|
|
437
|
+
read, write = await transport_cm.__aenter__()
|
|
438
|
+
transport_entered = True
|
|
439
|
+
session_cm = ClientSession(read, write)
|
|
440
|
+
session = await session_cm.__aenter__()
|
|
441
|
+
session_entered = True
|
|
442
|
+
await session.initialize()
|
|
443
|
+
except BaseException:
|
|
444
|
+
if session_entered and session_cm is not None:
|
|
445
|
+
try:
|
|
446
|
+
await session_cm.__aexit__(None, None, None)
|
|
447
|
+
except BaseException:
|
|
448
|
+
pass
|
|
449
|
+
if transport_entered:
|
|
450
|
+
try:
|
|
451
|
+
await transport_cm.__aexit__(None, None, None)
|
|
452
|
+
except BaseException:
|
|
453
|
+
pass
|
|
454
|
+
if owns_errlog:
|
|
455
|
+
errlog.close()
|
|
456
|
+
raise
|
|
406
457
|
return _LiveServer(
|
|
407
458
|
config=server,
|
|
408
459
|
session=session,
|
|
409
460
|
transport_cm=transport_cm,
|
|
410
461
|
session_cm=session_cm,
|
|
462
|
+
errlog=errlog if owns_errlog else None,
|
|
411
463
|
)
|
|
412
464
|
|
|
413
465
|
async def _open_http(self, server: McpServerConfig) -> _LiveServer:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Synapse Inspector</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root{color-scheme:dark;--bg:#0d131b;--panel:#121b25;--raised:#182431;--hover:#1d2b3a;--line:#29394b;--text:#e7edf4;--muted:#93a4b7;--blue:#68b5f8;--red:#ff8f8f;--amber:#efc36b;--green:#74c69d}*{box-sizing:border-box}body{margin:0;height:100vh;overflow:hidden;background:var(--bg);color:var(--text);font:13px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif}button,input{font:inherit}button{border:0;color:inherit;background:transparent;cursor:pointer}button:focus-visible,input:focus-visible{outline:2px solid var(--blue);outline-offset:2px}.top{height:52px;display:flex;align-items:center;gap:16px;padding:0 16px;border-bottom:1px solid var(--line);background:#101822}.brand{font-size:15px;font-weight:700}.brand span{color:var(--blue)}.capture{display:flex;align-items:center;gap:7px;color:var(--muted)}.switch{width:31px;height:18px;padding:2px;border-radius:10px;background:#465466}.switch i{display:block;width:14px;height:14px;border-radius:50%;background:#edf3f8;transition:.15s}.switch.on{background:var(--green)}.switch.on i{transform:translateX(13px)}.actions{display:flex;align-items:center;gap:4px;margin-left:auto}.btn{min-height:28px;padding:4px 9px;border:1px solid transparent;border-radius:5px;color:var(--muted)}.btn:hover{border-color:var(--line);background:var(--raised);color:var(--text)}.danger:hover{color:var(--red)}#app{display:grid;grid-template-columns:300px minmax(0,1fr);height:calc(100vh - 52px)}#side{display:flex;min-width:0;min-height:0;flex-direction:column;border-right:1px solid var(--line);background:var(--panel)}.side-head{padding:12px;border-bottom:1px solid var(--line)}.side-title{display:flex;justify-content:space-between;color:var(--muted);font-size:11px;font-weight:700}.search{width:100%;height:32px;margin-top:9px;padding:0 9px;border:1px solid var(--line);border-radius:5px;background:#0d151e;color:var(--text)}.filters{display:flex;gap:5px;margin-top:8px}.filter{padding:3px 7px;border:1px solid var(--line);border-radius:4px;color:var(--muted);font-size:11px}.filter.active,.filter:hover{border-color:#3b6d98;background:#1b3044;color:var(--blue)}#records{flex:1;min-height:0;overflow-y:scroll;overflow-x:hidden;padding:6px 0;scrollbar-color:#40546a #111923;scrollbar-width:thin}#records::-webkit-scrollbar{width:9px}#records::-webkit-scrollbar-track{background:#111923}#records::-webkit-scrollbar-thumb{border:2px solid #111923;border-radius:8px;background:#40546a}.turn-group{margin:0}.turn{display:flex;width:100%;align-items:center;gap:7px;padding:8px 12px;color:var(--muted);font-size:10px;font-weight:700;text-align:left;text-transform:uppercase}.turn:hover{background:var(--hover);color:var(--text)}.turn-arrow{width:10px;color:var(--blue);transition:transform .15s}.turn-group.collapsed .turn-arrow{transform:rotate(-90deg)}.turn-count{margin-left:auto;font-weight:500}.turn-group.collapsed .turn-records{display:none}.record{display:block;width:100%;padding:8px 12px;border-left:3px solid transparent;text-align:left}.record:hover{background:var(--hover)}.record.selected{border-left-color:var(--blue);background:#1d3043}.r-top,.r-bottom{display:flex;align-items:center;gap:7px}.r-id{font-weight:700}.r-model{flex:1;overflow:hidden;color:var(--muted);text-overflow:ellipsis;white-space:nowrap}.r-bottom{justify-content:space-between;margin-top:3px;color:var(--muted);font:11px ui-monospace,Consolas,monospace}.bad{color:var(--red)}.warn{color:var(--amber)}.tag{padding:0 5px;border-radius:8px;background:#26384a;color:#b9c8d6}#main{min-width:0;overflow:auto;padding:20px 24px 56px}.empty{display:grid;height:100%;place-content:center;gap:5px;text-align:center;color:var(--muted)}.empty strong{color:var(--text);font-size:16px}.call-head{display:flex;align-items:flex-start;justify-content:space-between;gap:18px}.eyebrow{color:var(--blue);font-size:10px;font-weight:700;text-transform:uppercase}h1{margin:3px 0 2px;font-size:20px}.sub{color:var(--muted);font-size:12px}.metrics{display:grid;grid-template-columns:repeat(6,minmax(80px,1fr));margin:16px 0;border:1px solid var(--line);border-radius:6px;background:var(--panel)}.metric{padding:9px 11px;border-right:1px solid var(--line)}.metric:last-child{border:0}.metric label{display:block;color:var(--muted);font-size:10px}.metric strong{display:block;margin-top:1px;font:600 14px ui-monospace,Consolas,monospace}.tabs{display:flex;gap:2px;border-bottom:1px solid var(--line)}.tab{padding:9px 12px;border-bottom:2px solid transparent;color:var(--muted)}.tab.active{border-bottom-color:var(--blue);color:var(--text)}#view{padding-top:14px}.section{margin-bottom:12px}.section-head{display:flex;align-items:center;margin-bottom:7px}.section-head h2{margin:0;font-size:14px}.section-head .meta{margin-left:8px;color:var(--muted);font-size:11px}.section-head .btn{margin-left:auto}.message,.tool-row,.raw-box{border:1px solid var(--line);border-radius:6px;background:var(--panel)}.message{margin-bottom:8px}.message-head{display:flex;align-items:center;padding:7px 10px;border-bottom:1px solid var(--line)}.role{font-size:11px;font-weight:700;text-transform:uppercase}.message-head .meta{margin-left:8px;color:var(--muted);font-size:11px}.message-head .btn{margin-left:auto}.content,pre{margin:0;overflow:auto;padding:10px;white-space:pre-wrap;overflow-wrap:anywhere;color:#dce6ef;font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace}.content{max-height:340px}.tool-summary{padding:9px 11px;border:1px solid var(--line);border-radius:6px;background:var(--panel);color:var(--muted)}.tool-row{margin-top:7px;overflow:hidden}.tool-toggle{display:grid;width:100%;grid-template-columns:62px 140px minmax(0,1fr);align-items:center;gap:8px;padding:9px 11px;text-align:left}.tool-toggle:hover{background:var(--hover)}.status{font-size:11px}.status.ok{color:var(--green)}.status.missing{color:var(--amber)}.tool-name{overflow:hidden;color:var(--text);font-weight:650;text-overflow:ellipsis;white-space:nowrap}.tool-intent{overflow:hidden;color:var(--muted);text-overflow:ellipsis;white-space:nowrap}.tool-detail{display:none;grid-template-columns:1fr 1fr;gap:1px;border-top:1px solid var(--line);background:var(--line)}.tool-row.open .tool-detail{display:grid}.tool-side{min-width:0;background:#101821}.tool-label{padding:6px 10px;border-bottom:1px solid var(--line);color:var(--muted);font-size:10px;font-weight:700}.tool-side pre{max-height:300px}.raw-box pre{max-height:65vh}.notice{margin-bottom:10px;padding:9px 11px;border-left:3px solid var(--amber);background:#2a251a;color:#dbcda9}.new-records{display:none;color:var(--blue)}@media(max-width:850px){#app{grid-template-columns:250px minmax(0,1fr)}.metrics{grid-template-columns:repeat(3,1fr)}.metric:nth-child(3),.metric:nth-child(6){border-right:0}.tool-detail{grid-template-columns:1fr}}@media(max-width:650px){#app{display:block}#side{display:none}#main{padding:16px}.metrics{grid-template-columns:repeat(2,1fr)}.metric:nth-child(even){border-right:0}}
|
|
9
|
+
</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<header class="top"><div class="brand"><span>//</span> Synapse Inspector</div><button class="capture" id="capture" title="开启或暂停模型通信采集">采集 <span class="switch"><i></i></span><strong id="state">关闭</strong></button><div class="actions"><button class="btn new-records" id="newRecords"></button><label class="btn"><input id="follow" type="checkbox" checked> 跟随最新</label><button class="btn" id="refresh">刷新</button><button class="btn danger" id="clear">清空</button></div></header>
|
|
13
|
+
<div id="app"><aside id="side"><div class="side-head"><div class="side-title"><span>调用记录</span><span id="count">0 条</span></div><input class="search" id="search" type="search" placeholder="筛选模型、工具、回合或错误"><div class="filters"><button class="filter active" data-filter="all">全部</button><button class="filter" data-filter="error">异常</button><button class="filter" data-filter="tool">工具</button><button class="filter" data-filter="slow">慢调用</button></div></div><div id="records"></div></aside><main id="main"><div class="empty"><strong>等待通信记录</strong><span>开启采集并运行一个回合后,在这里检查模型调用。</span></div></main></div>
|
|
14
|
+
<script>
|
|
15
|
+
const $=id=>document.getElementById(id);let records=[],selected=-1,loaded=null,filter='all',tab='overview',timer,newCount=0;const collapsedTurns=new Set;const esc=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));const dump=v=>JSON.stringify(v,null,2);const ms=v=>v>=1000?(v/1000).toFixed(2)+'s':Math.round(v||0)+'ms';const tok=v=>Number(v||0).toLocaleString();async function api(url,options){try{const response=await fetch(url,options);return response.ok?response.json():null}catch(_){return null}}function copy(text,button){navigator.clipboard?.writeText(text).then(()=>{const old=button.textContent;button.textContent='已复制';setTimeout(()=>button.textContent=old,900)}).catch(()=>{})}function visibleRows(){const query=$('search').value.trim().toLowerCase();return records.filter(r=>(filter==='all'||filter==='error'&&r.error||filter==='tool'&&r.has_tools||filter==='slow'&&r.duration_ms>=10000)&&(!query||`${r.turn_index} ${r.model_call_index} ${r.model_name} ${r.provider} ${r.error||''} ${r.tool_names||''}`.toLowerCase().includes(query)))}function drawList(){const out=$('records'),rows=visibleRows(),groups=new Map,scrollTop=out.scrollTop;out.innerHTML='';$('count').textContent=`${rows.length} / ${records.length} 条`;for(const record of rows){if(!groups.has(record.turn_index))groups.set(record.turn_index,[]);groups.get(record.turn_index).push(record)}for(const [turn,items] of groups){const group=document.createElement('section'),collapsed=collapsedTurns.has(turn);group.className='turn-group'+(collapsed?' collapsed':'');group.innerHTML=`<button class="turn" aria-expanded="${!collapsed}"><span class="turn-arrow">▼</span><span>回合 ${turn}</span><span class="turn-count">${items.length} 次调用</span></button><div class="turn-records"></div>`;const container=group.querySelector('.turn-records');for(const r of items){const button=document.createElement('button'),tools=r.tool_count?`<span class="tag">${r.tool_count} 工具</span>`:'';button.className='record'+(r.index===selected?' selected':'');button.innerHTML=`<div class="r-top"><span class="r-id ${r.error?'bad':''}">#${r.model_call_index}</span><span class="r-model">${esc(r.model_name)}</span>${r.error?'<span class="bad">失败</span>':''}</div><div class="r-bottom"><span class="${r.duration_ms>=10000?'warn':''}">${ms(r.duration_ms)}</span><span>${tok(r.usage?.input_tokens)} → ${tok(r.usage?.output_tokens)}</span>${tools}</div>`;button.onclick=()=>select(r.index);container.appendChild(button)}group.querySelector('.turn').onclick=()=>{if(collapsedTurns.has(turn))collapsedTurns.delete(turn);else collapsedTurns.add(turn);drawList()};out.appendChild(group)}out.scrollTop=scrollTop}function messageCard(message){const text=message.content_full||message.content_preview||'',hasCalls=(message.tool_calls||[]).length;let body=text&&!looksLikeRawCalls(text)?`<div class="content">${esc(text)}</div>`:'';if(!body&&hasCalls)body=`<div class="tool-summary">模型发起 ${hasCalls} 个结构化工具调用。详细参数请在“工具”标签查看。</div>`;if(!body)body='<div class="tool-summary">此消息没有可展示的文本内容。</div>';return `<article class="message"><div class="message-head"><span class="role">${esc(message.role||'unknown')}</span><span class="meta">${message.name?' / '+esc(message.name):''} ${tok(message.estimated_tokens)} tokens${hasCalls?' · '+hasCalls+' 工具':''}</span><button class="btn copy-message" data-text="${encodeURIComponent(text)}">复制文本</button></div>${body}</article>`}function looksLikeRawCalls(text){return text.includes("'type': 'function_call'")||text.includes('"type":"function_call"')}function section(title,meta,body,raw){return `<section class="section"><div class="section-head"><h2>${title}</h2><span class="meta">${meta}</span>${raw?'<button class="btn copy-section">复制 JSON</button>':''}</div>${body}</section>`}function intentOf(pair){try{const value=typeof pair.args==='string'?JSON.parse(pair.args):pair.args;return value?.intent||value?.command||value?.file_path||value?.pattern||''}catch(_){const match=String(pair.args||'').match(/["']intent["']\s*:\s*["']([^"']+)/);return match?.[1]||''}}function toolsView(pairs){if(!pairs.length)return '<div class="empty"><strong>本次调用没有关联工具</strong><span>这里只展示本次请求新增的工具结果和本次响应发起的工具调用。</span></div>';const failed=pairs.filter(p=>p.error===true);let failHtml='';if(failed.length)failHtml=`<div class="notice bad">${failed.length} 个工具失败:${failed.map(p=>esc(p.name)+' — '+esc((p.result||'').slice(0,100))).join('<br>')}</div>`;return `${failHtml}<div class="notice">默认折叠参数和结果;缺失响应的工具会优先展开。</div>${pairs.map((pair,index)=>`<article class="tool-row ${pair.result==null?'open':''}"><button class="tool-toggle"><span class="status ${pair.error===true?'bad':pair.result==null?'missing':'ok'}">${pair.error===true?'失败':pair.result==null?'待响应':'已完成'}</span><span class="tool-name">${esc(pair.name)}</span><span class="tool-intent">${esc(intentOf(pair)||pair.id)}</span></button><div class="tool-detail"><div class="tool-side"><div class="tool-label">调用参数</div><pre>${esc(pair.args||'{}')}</pre></div><div class="tool-side"><div class="tool-label">工具响应</div><pre>${esc(pair.result??'尚未捕获对应工具响应')}</pre></div></div></article>`).join('')}`}
|
|
16
|
+
function renderView(record){const req=record.request_messages||[],delta=req.slice(record.request_delta_start||0),resp=record.response_messages||[],pairs=record.tool_pairs||[];let html;if(tab==='overview'){const directCalls=resp.reduce((n,m)=>n+(m.tool_calls||[]).length,0),results=delta.filter(m=>m.role==='tool').length;html=section('本次请求新增',`${delta.length} 条 · 约 ${tok(delta.reduce((n,m)=>n+(m.estimated_tokens||0),0))} tokens`,delta.length?delta.map(messageCard).join(''):'<div class="tool-summary">本次调用沿用同回合上下文,没有新增请求消息。</div>',dump(delta))+section('模型输出',`${resp.length} 条消息 · ${directCalls} 个工具调用`,resp.length?resp.map(messageCard).join(''):'<div class="tool-summary">模型没有返回消息。</div>',dump(resp))+section('执行摘要',`${results} 条工具结果输入 · ${directCalls} 个新工具调用`,`<div class="tool-summary">本次调用接收了 ${results} 条工具结果,并发起 ${directCalls} 个后续工具调用。切换到“工具”查看结构化参数和结果。</div>`)}else if(tab==='request'){html=section('请求增量',`${delta.length} 新增 / ${req.length} 总消息`,delta.map(messageCard).join('')||'<div class="tool-summary">没有新增消息。</div>',dump(delta))+section('完整请求',`${req.length} 条消息`,req.map(messageCard).join(''),dump(req))}else if(tab==='response'){html=section('响应消息',`${resp.length} 条 · ${tok(record.usage?.output_tokens)} output tokens`,resp.map(messageCard).join('')||'<div class="tool-summary">模型没有返回消息。</div>',dump(resp))}else if(tab==='tools'){html=toolsView(pairs)}else{html='<div class="raw-box"><div class="tool-summary">正在加载捕获快照…</div></div>';loadRaw(record)}$('view').innerHTML=html;bindView(record)}function bindView(record){$('view').querySelectorAll('.tool-toggle').forEach(button=>button.onclick=()=>button.parentElement.classList.toggle('open'));$('view').querySelectorAll('.copy-message').forEach(button=>button.onclick=()=>copy(decodeURIComponent(button.dataset.text),button));$('view').querySelectorAll('.copy-section').forEach(button=>button.onclick=()=>{const title=button.parentElement.querySelector('h2').textContent;let value=title.includes('完整')?record.request_messages:title.includes('响应')?record.response_messages:(record.request_messages||[]).slice(record.request_delta_start||0);copy(dump(value),button)})}async function loadRaw(record){const raw=await api('/api/records/'+selected+'/raw');if(tab!=='raw'||loaded!==record)return;const box=$('view');box.innerHTML=`<section class="section"><div class="section-head"><h2>捕获快照</h2><span class="meta">按需加载</span><button class="btn" id="copyRaw">复制 JSON</button></div><div class="raw-box"><pre>${esc(dump(raw))}</pre></div></section>`;$('copyRaw').onclick=()=>copy(dump(raw),$('copyRaw'))}function drawDetail(record){if(!record){$('main').innerHTML='<div class="empty"><strong>等待通信记录</strong><span>开启采集并运行一个回合后,在这里检查模型调用。</span></div>';return}const pairs=record.tool_pairs||[],delta=(record.request_messages||[]).slice(record.request_delta_start||0),newTokens=delta.reduce((n,m)=>n+(m.estimated_tokens||0),0),tools=pairs.length,failed=tools?pairs.filter(p=>p.error===true).length:0,failRate=tools?Math.round(failed/tools*100)+'%':'—',status=record.error?'失败':'已完成';$('main').innerHTML=`<div class="call-head"><div><div class="eyebrow">模型通信 / 回合 ${record.turn_index}</div><h1>调用 #${record.model_call_index} <span class="${record.error?'bad':''}">${status}</span></h1><div class="sub">${esc(record.provider)} · ${esc(record.model_name)} · ${new Date((record.started_at||0)*1000).toLocaleString()}</div></div><button class="btn" id="copyRawTop">复制原始记录</button></div><div class="metrics"><div class="metric"><label>耗时</label><strong>${ms(record.duration_ms)}</strong></div><div class="metric"><label>总输入</label><strong>${tok(record.usage?.input_tokens)}</strong></div><div class="metric"><label>本次新增</label><strong>+${tok(newTokens)}</strong></div><div class="metric"><label>输出</label><strong>${tok(record.usage?.output_tokens)}</strong></div><div class="metric"><label>关联工具</label><strong>${tools}</strong></div><div class="metric"><label>失败率</label><strong>${failRate}</strong></div></div>${record.error?`<div class="notice bad">${esc(record.error)}</div>`:''}<nav class="tabs">${[['overview','概览'],['request','请求'],['response','响应'],['tools',`工具 ${tools}`],['raw','原始']].map(([id,label])=>`<button class="tab ${tab===id?'active':''}" data-tab="${id}">${label}</button>`).join('')}</nav><div id="view"></div>`;$('main').querySelectorAll('.tab').forEach(button=>button.onclick=()=>{tab=button.dataset.tab;drawDetail(record)});$('copyRawTop').onclick=async()=>{const raw=await api('/api/records/'+selected+'/raw');if(raw)copy(dump(raw),$('copyRawTop'))};renderView(record)}async function select(index){selected=index;drawList();$('main').innerHTML='<div class="empty"><strong>正在加载调用详情</strong></div>';const record=await api('/api/records/'+index);if(selected===index&&record){loaded=record;drawDetail(record)}}async function refresh(forceFollow=false){const [status,next]=await Promise.all([api('/api/status'),api('/api/records')]);if(status){$('capture').querySelector('.switch').classList.toggle('on',!!status.enabled);$('state').textContent=status.enabled?'开启':'关闭'}if(!next)return;const previousCount=records.length,hadSelection=selected>=0;records=next;drawList();if(records.length>previousCount&&hadSelection&&!$('follow').checked){newCount+=records.length-previousCount;$('newRecords').style.display='block';$('newRecords').textContent=`${newCount} 条新记录`}if((!hadSelection||forceFollow||$('follow').checked&&records.length>previousCount)&&records.length)select(records.at(-1).index);else if(selected>=records.length){selected=-1;loaded=null;drawDetail(null)}}$('capture').onclick=async()=>{await api('/api/toggle',{method:'POST'});refresh()};$('refresh').onclick=()=>refresh(false);$('newRecords').onclick=()=>{newCount=0;$('newRecords').style.display='none';refresh(true)};$('clear').onclick=async()=>{if(records.length&&confirm('清空全部已采集的通信记录?')){await api('/api/clear',{method:'POST'});records=[];selected=-1;loaded=null;drawList();drawDetail(null)}};$('search').oninput=drawList;document.querySelectorAll('.filter').forEach(button=>button.onclick=()=>{filter=button.dataset.filter;document.querySelectorAll('.filter').forEach(item=>item.classList.toggle('active',item===button));drawList()});document.addEventListener('visibilitychange',()=>{clearInterval(timer);if(!document.hidden)timer=setInterval(refresh,1500)});refresh();timer=setInterval(refresh,1500);
|
|
17
|
+
</script>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|