synapse-cli-agent 0.1.18__tar.gz → 0.1.19__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.18 → synapse_cli_agent-0.1.19}/CHANGELOG.md +16 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/PKG-INFO +1 -1
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/config.md +6 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/pyproject.toml +1 -1
- synapse_cli_agent-0.1.19/release_body.md +15 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/cli.py +22 -9
- synapse_cli_agent-0.1.19/src/synapse/integrations/openai_usage.py +406 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/models/registry.py +23 -7
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/context_compact.py +55 -4
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/bottombar/components/__init__.py +5 -1
- synapse_cli_agent-0.1.19/src/synapse/ui/bottombar/components/codex_usage.py +33 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/bottombar/context.py +2 -0
- synapse_cli_agent-0.1.19/src/synapse/ui/dialogs/codex_reset.py +129 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/tui.py +217 -2
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_bottombar_registry.py +5 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_cli.py +29 -2
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_context_compact.py +16 -1
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_dialogs.py +19 -0
- synapse_cli_agent-0.1.19/tests/test_openai_usage.py +151 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/uv.lock +1 -1
- synapse_cli_agent-0.1.18/release_body.md +0 -19
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/.github/workflows/ci.yml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/.github/workflows/docs.yml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/.github/workflows/native-compression-wheels.yml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/.github/workflows/native-search-wheels.yml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/.github/workflows/release.yml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/.gitignore +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/.python-version +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/.readthedocs.yaml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/AGENTS.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/README.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/agent_upstream.txt +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/cli_upstream.txt +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/decode_screenshot.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/agent-development-guide.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/architecture-flow.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/architecture.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/cli.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/codex-session-discovery-phase-1.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/codex-session-import-phase-2.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/design.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/index.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/install.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/mcp.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/models.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/native-compression-core.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/native-search-core.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/permissions.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/quickstart.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/sessions.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/skills.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/smith_output.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/tool-output-transform.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/tui-cursor-refactor.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/tutorial.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/docs/x-agent-development-guide.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/examples/mcp.example.json +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/examples/models.example.json +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/examples/settings.example.json +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/middleware_upstream.txt +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/mkdocs.yml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-search-core/Cargo.lock +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-search-core/Cargo.toml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-search-core/LICENSE +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-search-core/NOTICE +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-search-core/README.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-search-core/pyproject.toml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-search-core/python/synapse_search_core/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-search-core/src/lib.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/Cargo.lock +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/Cargo.toml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/LICENSE +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/NOTICE +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/README.md +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/pyproject.toml +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/python/synapse_tool_compress_core/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/ccr.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/ccr_in_memory.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/mod.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/relevance/base.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/relevance/bm25.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/signals/keyword_detector.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/signals/line_importance.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/adaptive_sizer.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/anchor_selector.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/code_compressor.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/diff_compressor.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/log_compressor.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/search_compressor.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/analyzer.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/anchors.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/builder.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/classifier.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/classifier.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/compactor.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/formatter.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/ir.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/mod.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/compaction/walker.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/config.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/constraints.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/crusher.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/crushers.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/error_keywords.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/field_detect.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/hashing.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/mod.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/observer.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/orchestration.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/outliers.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/planning.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/statistics.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/stats_math.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/traits.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/headroom_port/transforms/smart_crusher/types.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/rust/synapse-tool-compress-core/src/lib.rs +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/scripts/install.ps1 +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/scripts/release.ps1 +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/slash_cmds_upstream.txt +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/__main__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/app/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/app/agent.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/app/agent_md.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/compression.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/helpers.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/mcp.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/model.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/result.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/sessions.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/slash_cmds.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/slash_complete.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/commands/theme.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/config.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/content/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/content/input_history.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/content/multimodal.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/content/prompts.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/content/skills_catalog.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/checkpoint_seed.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/codex_history.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/codex_import.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/codex_sessions.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/describe_image.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/http_clients.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/llm_openai_compat.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/llm_openai_websocket.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/mcp_client.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/openai_oauth.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/openai_oauth_middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/integrations/vision_middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/memory/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/memory/auto_recorder.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/memory/embedder.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/memory/long_term.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/models/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/models/config.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/models/helpers.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/models/profile.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/models_registry.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/observability/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/observability/debug_inspector.html +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/observability/debug_server.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/observability/llm_debug.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/observability/startup_trace.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/parallel_subagents.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/planner/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/planner/task_planner.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/rag/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/rag/knowledge_base.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/async_runtime.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/backends.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/debug_capture_middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/execute_capture.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/fs_permissions.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/harness.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/hitl.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/interaction_ledger.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/model_request_compression_middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/pathing.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/safety.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/steer.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/subagents.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/tool_ignore.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/tool_output_eval.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/tool_output_middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/tool_output_usage_middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/sessions/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/sessions/cancel_repair.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/sessions/session_recap.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/sessions/store.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/sessions/transcript.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/settings/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/settings/config_paths.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/settings/schema.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/subagent_monitor.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tool_output/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tool_output/detection.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tool_output/metrics.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tool_output/models.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tool_output/pipeline.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tool_output/repository.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tool_output/transformers.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tools/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tools/describe_image.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tools/filesystem_search.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/tools/session_tools.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/answer_divider.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/bottombar/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/bottombar/components/key_hints.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/bottombar/components/mcp.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/bottombar/components/mode.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/bottombar/components/model.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/bottombar/components/thread.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/bottombar/core.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/clipboard.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/base.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/codex_session_list.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/compression_diagnostics.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/git_explore.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/mcp_panel.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/model_picker.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/safety_panel.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/session_list.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/subagent_monitor.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/theme_designer.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/dialogs/theme_picker.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/formatters.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/git_explore/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/git_explore/engine.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/git_explore/provider.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/git_explore/unified.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/rendering.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/selectable_static.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/selectable_text.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/sink.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/steer_widget.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/stream.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/stream_events.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/stream_runtime.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/textual_stream_sink.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/theme.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/timeline.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/tool_blocks.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/components/__init__.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/components/branch.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/components/title.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/components/tool_output.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/components/usage.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/components/workspace.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/context.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/core.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/git_changes_popover.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/git_chrome.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/tool_output_popover.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/topbar/widget.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/transcript_blocks.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/turn_rail.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/turn_rail_widgets.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/user_turn.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/user_turn_block.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/ui/welcome.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/synapse.cmd +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/codex_rollouts/duplicate_response_item.jsonl +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/codex_rollouts/incomplete_and_aborted.jsonl +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/codex_rollouts/legacy_compaction.jsonl +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/codex_rollouts/normal_completed.jsonl +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/codex_rollouts/replacement_history.jsonl +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/codex_rollouts/rollback.jsonl +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/codex_rollouts/unsupported_replacement.jsonl +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/sample_repo/calculator.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/sample_repo/test_calculator.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/fixtures/tool_output_eval.json +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_agent_factory.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_async_checkpointer.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_backends.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_cancel_repair.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_capabilities.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_checkpoint_seed.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_cli_codex_import.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_codex_history.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_codex_import.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_codex_sessions.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_codex_sessions_windows.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_compact_tool_descriptions.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_config.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_context_window.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_dag_parallel.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_debug_inspector.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_describe_image.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_filesystem_search_tools.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_git_changes_popover.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_git_chrome.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_git_explore.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_http_clients.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_input_history.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_iteration_abc.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_layered_config.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_mcp_schema.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_mermaid_render.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_middleware_async.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_model_request_compression_middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_model_retry.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_multimodal.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_openai_compat_reasoning.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_openai_oauth.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_openai_oauth_middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_openai_websocket.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_pathing.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_safety.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_session_recap.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_slash_and_mcp.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_slash_complete.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_startup_defer.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_steer.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_stream_cancel.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_stream_tool_items.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_stream_ui.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_subagent_monitor_dialog.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_subagent_status.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_theme.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_timeline.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_tool_ignore.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_tool_intent.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_tool_output.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_tool_output_eval.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_tool_output_slash.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_tool_output_usage_middleware.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_topbar_context_ratio.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_topbar_registry.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_transcript.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_tui_codex_import.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_tui_selection.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_tui_sink.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_turn_rail_minimap.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_usage_restore.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_user_turn_style.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/test_welcome.py +0 -0
- {synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/tests/tool_output_plugin_fixture.py +0 -0
|
@@ -7,6 +7,22 @@ The release workflow automatically extracts the matching section as release note
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## v0.1.19
|
|
11
|
+
|
|
12
|
+
### 新增功能
|
|
13
|
+
|
|
14
|
+
- Codex OAuth 用量底部栏组件:展示 5h/1d 用量窗口、重置剩余时间和账号到期时间;低于 50% 时红色显示。
|
|
15
|
+
- Codex 速率限制重置能力:直接通过 HTTP 请求 wham/rate-limit-reset-credits 读取可用重置次数与到期详情,支持在弹窗中一键消费重置。
|
|
16
|
+
- /codex reset、/codex credits 命令打开重置详情弹窗;底部栏 Codex 区域支持 hover 高亮与点击。
|
|
17
|
+
- 启动时配置错误友好提示:models.json、settings.json 或内联 JSON 环境变量格式错误时输出简洁错误与修复提示,不再抛出完整 traceback。
|
|
18
|
+
|
|
19
|
+
### 修复
|
|
20
|
+
|
|
21
|
+
- /compact 改为后台 worker 执行,避免模型摘要阻塞 TUI;执行期间禁止取消,防止压缩状态损坏。
|
|
22
|
+
- 兼容 LangChain 1.3 编译图闭包中的 SummarizationMiddleware 定位。
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
10
26
|
## v0.1.18
|
|
11
27
|
|
|
12
28
|
### 新功能
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synapse-cli-agent
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.19
|
|
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
|
|
@@ -113,6 +113,12 @@ Synapse 使用 **Pydantic Settings** 实现分层配置系统。
|
|
|
113
113
|
|
|
114
114
|
参见 [模型配置](models.md) 页面。
|
|
115
115
|
|
|
116
|
+
### 配置错误提示
|
|
117
|
+
|
|
118
|
+
启动时如果 `models.json`、`settings.json` 或内联 JSON 环境变量格式错误,Synapse 会输出
|
|
119
|
+
简短的错误说明和修复提示后退出,不会显示完整 Python traceback。对于 `models.json`,错误信息会
|
|
120
|
+
包含出错文件路径和 JSON 的行列位置;修复配置后重新启动即可。
|
|
121
|
+
|
|
116
122
|
## `.coding-agent/mcp_servers.json` 格式
|
|
117
123
|
|
|
118
124
|
参见 [MCP Server](mcp.md) 页面。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## v0.1.19
|
|
2
|
+
|
|
3
|
+
### 新增功能
|
|
4
|
+
|
|
5
|
+
- Codex OAuth 用量底部栏组件:展示 5h/1d 用量窗口、重置剩余时间和账号到期时间;低于 50% 时红色显示。
|
|
6
|
+
- Codex 速率限制重置能力:直接通过 HTTP 请求 wham/rate-limit-reset-credits 读取可用重置次数与到期详情,支持在弹窗中一键消费重置。
|
|
7
|
+
- /codex reset、/codex credits 命令打开重置详情弹窗;底部栏 Codex 区域支持 hover 高亮与点击。
|
|
8
|
+
- 启动时配置错误友好提示:models.json、settings.json 或内联 JSON 环境变量格式错误时输出简洁错误与修复提示,不再抛出完整 traceback。
|
|
9
|
+
|
|
10
|
+
### 修复
|
|
11
|
+
|
|
12
|
+
- /compact 改为后台 worker 执行,避免模型摘要阻塞 TUI;执行期间禁止取消,防止压缩状态损坏。
|
|
13
|
+
- 兼容 LangChain 1.3 编译图闭包中的 SummarizationMiddleware 定位。
|
|
14
|
+
|
|
15
|
+
---
|
|
@@ -93,6 +93,15 @@ def _print_auth_error(settings, exc: Exception) -> None:
|
|
|
93
93
|
)
|
|
94
94
|
|
|
95
95
|
|
|
96
|
+
def _print_settings_error(exc: Exception) -> None:
|
|
97
|
+
"""Print a concise, actionable configuration error without a traceback."""
|
|
98
|
+
print_error(f"Configuration error: {exc}")
|
|
99
|
+
print_info(
|
|
100
|
+
"Check models.json, settings.json, and inline JSON environment variables "
|
|
101
|
+
"(MODELS_JSON / MCP_SERVERS_JSON)."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
96
105
|
# ---------------------------------------------------------------------------
|
|
97
106
|
# Authentication commands
|
|
98
107
|
# ---------------------------------------------------------------------------
|
|
@@ -252,14 +261,18 @@ def _launch_tui(
|
|
|
252
261
|
debug: bool,
|
|
253
262
|
) -> None:
|
|
254
263
|
"""Bootstrap settings and open the full-screen Textual TUI."""
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
264
|
+
try:
|
|
265
|
+
env_path = _bootstrap_env()
|
|
266
|
+
settings = _resolve_settings(
|
|
267
|
+
workspace=workspace,
|
|
268
|
+
model=model,
|
|
269
|
+
require_approval=require_approval,
|
|
270
|
+
debug=debug,
|
|
271
|
+
readonly=readonly,
|
|
272
|
+
)
|
|
273
|
+
except (OSError, ValueError) as exc:
|
|
274
|
+
_print_settings_error(exc)
|
|
275
|
+
raise typer.Exit(code=1) from exc
|
|
263
276
|
try:
|
|
264
277
|
from synapse.ui.tui import run_tui
|
|
265
278
|
except ImportError as exc: # pragma: no cover - dependency missing
|
|
@@ -965,4 +978,4 @@ def main() -> None:
|
|
|
965
978
|
|
|
966
979
|
|
|
967
980
|
if __name__ == "__main__":
|
|
968
|
-
main()
|
|
981
|
+
main()
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"""Read-only Codex OAuth usage snapshots for the TUI bottombar."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
import uuid
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
from synapse.integrations.openai_oauth import OpenAIOAuthStore, OpenAIOAuthTokenProvider
|
|
14
|
+
|
|
15
|
+
OPENAI_USAGE_ENDPOINT = "https://chatgpt.com/backend-api/wham/usage"
|
|
16
|
+
OPENAI_RESET_CREDITS_ENDPOINT = (
|
|
17
|
+
"https://chatgpt.com/backend-api/wham/rate-limit-reset-credits"
|
|
18
|
+
)
|
|
19
|
+
OPENAI_CONSUME_RESET_ENDPOINT = (
|
|
20
|
+
"https://chatgpt.com/backend-api/wham/rate-limit-reset-credits/consume"
|
|
21
|
+
)
|
|
22
|
+
USAGE_CACHE_TTL_SECONDS = 300.0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True, slots=True)
|
|
26
|
+
class UsageWindow:
|
|
27
|
+
"""One Codex rate-limit window."""
|
|
28
|
+
used_percent: float | None
|
|
29
|
+
window_minutes: int | None
|
|
30
|
+
reset_at: float | None
|
|
31
|
+
@property
|
|
32
|
+
def remaining_percent(self) -> int | None:
|
|
33
|
+
if self.used_percent is None:
|
|
34
|
+
return None
|
|
35
|
+
return round(max(0.0, min(100.0, 100.0 - self.used_percent)))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True, slots=True)
|
|
39
|
+
class ResetCreditDetail:
|
|
40
|
+
"""One available / consumed rate-limit reset credit."""
|
|
41
|
+
id: str
|
|
42
|
+
reset_type: str
|
|
43
|
+
status: str
|
|
44
|
+
granted_at: float | None
|
|
45
|
+
expires_at: float | None
|
|
46
|
+
title: str | None
|
|
47
|
+
description: str | None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True, slots=True)
|
|
51
|
+
class ResetCredits:
|
|
52
|
+
"""Aggregated reset-credit snapshot from the backend."""
|
|
53
|
+
available_count: int
|
|
54
|
+
credits: list[ResetCreditDetail] = field(default_factory=list)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True, slots=True)
|
|
58
|
+
class ConsumeResetResult:
|
|
59
|
+
"""Outcome of a single reset-credit redemption."""
|
|
60
|
+
outcome: str
|
|
61
|
+
idempotency_key: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True, slots=True)
|
|
65
|
+
class CodexUsageSnapshot:
|
|
66
|
+
"""Parsed Codex primary/secondary usage windows + optional reset credits."""
|
|
67
|
+
primary: UsageWindow | None = None
|
|
68
|
+
secondary: UsageWindow | None = None
|
|
69
|
+
captured_at: float = 0.0
|
|
70
|
+
expires_at: float | None = None
|
|
71
|
+
reset_credits: ResetCredits | None = None
|
|
72
|
+
@property
|
|
73
|
+
def lowest_remaining_percent(self) -> int | None:
|
|
74
|
+
values = [
|
|
75
|
+
value for value in (
|
|
76
|
+
self.primary.remaining_percent if self.primary else None,
|
|
77
|
+
self.secondary.remaining_percent if self.secondary else None,
|
|
78
|
+
) if value is not None
|
|
79
|
+
]
|
|
80
|
+
return min(values) if values else None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CodexUsageClient:
|
|
84
|
+
"""Fetch and cache the Codex OAuth usage / reset-credit endpoints."""
|
|
85
|
+
def __init__(self, *, store=None, timeout=10.0, cache_ttl=USAGE_CACHE_TTL_SECONDS):
|
|
86
|
+
self._store = store or OpenAIOAuthStore()
|
|
87
|
+
self._token_provider = OpenAIOAuthTokenProvider(self._store)
|
|
88
|
+
self._timeout = max(1.0, float(timeout))
|
|
89
|
+
self._cache_ttl = max(0.0, float(cache_ttl))
|
|
90
|
+
self._lock = threading.RLock()
|
|
91
|
+
self._cached: CodexUsageSnapshot | None = None
|
|
92
|
+
self._cached_at = 0.0
|
|
93
|
+
self._cached_details: ResetCredits | None = None
|
|
94
|
+
self._cached_details_at = 0.0
|
|
95
|
+
|
|
96
|
+
def _auth_headers(self) -> dict[str, str]:
|
|
97
|
+
tokens = self._store.load()
|
|
98
|
+
if tokens is None:
|
|
99
|
+
raise RuntimeError("OpenAI Codex OAuth is not logged in")
|
|
100
|
+
access_token = self._token_provider.access_token()
|
|
101
|
+
tokens = self._store.load() or tokens
|
|
102
|
+
headers: dict[str, str] = {
|
|
103
|
+
"Authorization": f"Bearer {access_token}",
|
|
104
|
+
"originator": "synapse",
|
|
105
|
+
}
|
|
106
|
+
if tokens.account_id:
|
|
107
|
+
headers["ChatGPT-Account-Id"] = tokens.account_id
|
|
108
|
+
return headers
|
|
109
|
+
|
|
110
|
+
def get_cached(self) -> CodexUsageSnapshot | None:
|
|
111
|
+
with self._lock:
|
|
112
|
+
if self._cached is None:
|
|
113
|
+
return None
|
|
114
|
+
if time.monotonic() - self._cached_at > self._cache_ttl:
|
|
115
|
+
return None
|
|
116
|
+
return self._cached
|
|
117
|
+
|
|
118
|
+
def fetch(self, *, force=False) -> CodexUsageSnapshot:
|
|
119
|
+
with self._lock:
|
|
120
|
+
if not force:
|
|
121
|
+
cached = self.get_cached()
|
|
122
|
+
if cached is not None:
|
|
123
|
+
return cached
|
|
124
|
+
headers = self._auth_headers()
|
|
125
|
+
response = httpx.get(OPENAI_USAGE_ENDPOINT, headers=headers, timeout=self._timeout)
|
|
126
|
+
response.raise_for_status()
|
|
127
|
+
payload = response.json()
|
|
128
|
+
tokens = self._store.load()
|
|
129
|
+
snapshot = parse_usage_payload(payload, expires_at=tokens.expires_at if tokens else None)
|
|
130
|
+
with self._lock:
|
|
131
|
+
self._cached = snapshot
|
|
132
|
+
self._cached_at = time.monotonic()
|
|
133
|
+
return snapshot
|
|
134
|
+
|
|
135
|
+
def get_cached_details(self) -> ResetCredits | None:
|
|
136
|
+
with self._lock:
|
|
137
|
+
if self._cached_details is None:
|
|
138
|
+
return None
|
|
139
|
+
if time.monotonic() - self._cached_details_at > self._cache_ttl:
|
|
140
|
+
return None
|
|
141
|
+
return self._cached_details
|
|
142
|
+
|
|
143
|
+
def fetch_reset_credits(self, *, force=False) -> ResetCredits:
|
|
144
|
+
with self._lock:
|
|
145
|
+
if not force:
|
|
146
|
+
cached = self.get_cached_details()
|
|
147
|
+
if cached is not None:
|
|
148
|
+
return cached
|
|
149
|
+
headers = self._auth_headers()
|
|
150
|
+
response = httpx.get(OPENAI_RESET_CREDITS_ENDPOINT, headers=headers, timeout=self._timeout)
|
|
151
|
+
response.raise_for_status()
|
|
152
|
+
details = parse_reset_credits_details(response.json())
|
|
153
|
+
with self._lock:
|
|
154
|
+
self._cached_details = details
|
|
155
|
+
self._cached_details_at = time.monotonic()
|
|
156
|
+
return details
|
|
157
|
+
|
|
158
|
+
def consume_reset_credit(self, *, credit_id=None, idempotency_key=None) -> ConsumeResetResult:
|
|
159
|
+
key = idempotency_key or uuid.uuid4().hex
|
|
160
|
+
headers = self._auth_headers()
|
|
161
|
+
body: dict[str, str] = {"redeem_request_id": key}
|
|
162
|
+
if credit_id:
|
|
163
|
+
body["credit_id"] = credit_id
|
|
164
|
+
response = httpx.post(
|
|
165
|
+
OPENAI_CONSUME_RESET_ENDPOINT, headers=headers, json=body, timeout=self._timeout,
|
|
166
|
+
)
|
|
167
|
+
response.raise_for_status()
|
|
168
|
+
payload = response.json()
|
|
169
|
+
outcome = str(payload.get("outcome") or payload.get("code") or "unknown")
|
|
170
|
+
return ConsumeResetResult(outcome=outcome, idempotency_key=key)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def parse_usage_payload(payload, *, expires_at=None, captured_at=None) -> CodexUsageSnapshot:
|
|
174
|
+
if not isinstance(payload, dict):
|
|
175
|
+
raise ValueError("Codex usage response must be a JSON object")
|
|
176
|
+
rate_limit = payload.get("rate_limit")
|
|
177
|
+
if not isinstance(rate_limit, dict):
|
|
178
|
+
raise ValueError("Codex usage response has no rate_limit object")
|
|
179
|
+
return CodexUsageSnapshot(
|
|
180
|
+
primary=_parse_window(rate_limit.get("primary_window")),
|
|
181
|
+
secondary=_parse_window(rate_limit.get("secondary_window")),
|
|
182
|
+
captured_at=time.time() if captured_at is None else float(captured_at),
|
|
183
|
+
expires_at=expires_at,
|
|
184
|
+
reset_credits=_parse_reset_credits_summary(payload.get("rate_limit_reset_credits")),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def parse_reset_credits_details(payload) -> ResetCredits:
|
|
189
|
+
if not isinstance(payload, dict):
|
|
190
|
+
raise ValueError("Reset-credits response must be a JSON object")
|
|
191
|
+
count = _as_int(payload.get("available_count")) or 0
|
|
192
|
+
raw = payload.get("credits")
|
|
193
|
+
credits: list[ResetCreditDetail] = []
|
|
194
|
+
if isinstance(raw, list):
|
|
195
|
+
for item in raw:
|
|
196
|
+
if isinstance(item, dict):
|
|
197
|
+
credits.append(_parse_reset_credit_detail(item))
|
|
198
|
+
return ResetCredits(available_count=count, credits=credits)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _parse_reset_credits_summary(raw) -> ResetCredits | None:
|
|
202
|
+
if not isinstance(raw, dict):
|
|
203
|
+
return None
|
|
204
|
+
count = _as_int(raw.get("available_count"))
|
|
205
|
+
if count is None:
|
|
206
|
+
return None
|
|
207
|
+
raw_list = raw.get("credits")
|
|
208
|
+
credits: list[ResetCreditDetail] = []
|
|
209
|
+
if isinstance(raw_list, list):
|
|
210
|
+
for item in raw_list:
|
|
211
|
+
if isinstance(item, dict):
|
|
212
|
+
credits.append(_parse_reset_credit_detail(item))
|
|
213
|
+
return ResetCredits(available_count=count, credits=credits)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _parse_reset_credit_detail(raw: dict[str, Any]) -> ResetCreditDetail:
|
|
217
|
+
return ResetCreditDetail(
|
|
218
|
+
id=str(raw.get("id") or ""),
|
|
219
|
+
reset_type=str(raw.get("resetType") or raw.get("reset_type") or "unknown"),
|
|
220
|
+
status=str(raw.get("status") or "unknown"),
|
|
221
|
+
granted_at=_as_timestamp(raw.get("grantedAt") or raw.get("granted_at")),
|
|
222
|
+
expires_at=_as_timestamp(raw.get("expiresAt") or raw.get("expires_at")),
|
|
223
|
+
title=raw.get("title") or None,
|
|
224
|
+
description=raw.get("description") or None,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _as_timestamp(value: Any) -> float | None:
|
|
229
|
+
"""Parse Unix seconds or an ISO 8601 timestamp from backend responses."""
|
|
230
|
+
number = _as_float(value)
|
|
231
|
+
if number is not None:
|
|
232
|
+
return number
|
|
233
|
+
if not isinstance(value, str) or not value.strip():
|
|
234
|
+
return None
|
|
235
|
+
from datetime import datetime
|
|
236
|
+
|
|
237
|
+
normalized = value.strip().replace("Z", "+00:00")
|
|
238
|
+
try:
|
|
239
|
+
return datetime.fromisoformat(normalized).timestamp()
|
|
240
|
+
except ValueError:
|
|
241
|
+
return None
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _parse_window(raw) -> UsageWindow | None:
|
|
245
|
+
if not isinstance(raw, dict):
|
|
246
|
+
return None
|
|
247
|
+
used = _as_float(raw.get("used_percent"))
|
|
248
|
+
if used is None:
|
|
249
|
+
return None
|
|
250
|
+
seconds = _as_int(raw.get("limit_window_seconds"))
|
|
251
|
+
return UsageWindow(
|
|
252
|
+
used_percent=max(0.0, min(100.0, used)),
|
|
253
|
+
window_minutes=(seconds + 59) // 60 if seconds and seconds > 0 else None,
|
|
254
|
+
reset_at=_as_float(raw.get("reset_at")),
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _as_float(value) -> float | None:
|
|
259
|
+
if isinstance(value, bool) or value is None:
|
|
260
|
+
return None
|
|
261
|
+
try:
|
|
262
|
+
return float(value)
|
|
263
|
+
except (TypeError, ValueError):
|
|
264
|
+
return None
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _as_int(value) -> int | None:
|
|
268
|
+
number = _as_float(value)
|
|
269
|
+
return int(number) if number is not None else None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def format_reset_remaining(reset_at, *, now=None) -> str:
|
|
273
|
+
if reset_at is None:
|
|
274
|
+
return "--"
|
|
275
|
+
seconds = max(0, int(reset_at - (time.time() if now is None else now)))
|
|
276
|
+
if seconds < 60:
|
|
277
|
+
return f"{seconds}s"
|
|
278
|
+
minutes = (seconds + 59) // 60
|
|
279
|
+
if minutes < 60:
|
|
280
|
+
return f"{minutes}m"
|
|
281
|
+
hours = (minutes + 59) // 60
|
|
282
|
+
if hours < 24:
|
|
283
|
+
return f"{hours}h"
|
|
284
|
+
return f"{(hours + 23) // 24}d"
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def format_usage_label(snapshot) -> str:
|
|
288
|
+
if snapshot is None:
|
|
289
|
+
return "codex n/a"
|
|
290
|
+
parts: list[str] = []
|
|
291
|
+
for label, window in (("5h", snapshot.primary), ("1d", snapshot.secondary)):
|
|
292
|
+
if window is None or window.remaining_percent is None:
|
|
293
|
+
continue
|
|
294
|
+
reset = format_reset_remaining(window.reset_at)
|
|
295
|
+
parts.append(f"{label} {window.remaining_percent}%/{reset}")
|
|
296
|
+
if snapshot.reset_credits is not None and snapshot.reset_credits.available_count > 0:
|
|
297
|
+
parts.append(f"resets {snapshot.reset_credits.available_count}")
|
|
298
|
+
if snapshot.expires_at is not None:
|
|
299
|
+
parts.append(f"exp {format_reset_remaining(snapshot.expires_at)}")
|
|
300
|
+
return " · ".join(parts) if parts else "codex n/a"
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def usage_style(snapshot) -> str:
|
|
304
|
+
if snapshot is not None and snapshot.lowest_remaining_percent is not None:
|
|
305
|
+
if snapshot.lowest_remaining_percent < 50:
|
|
306
|
+
return "#f28b82"
|
|
307
|
+
return "#8ab4f8"
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
# ---------------------------------------------------------------------------
|
|
311
|
+
# TUI-facing service — owns cache / refresh / consume state
|
|
312
|
+
# ---------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class CodexUsageService:
|
|
316
|
+
"""State holder that the TUI binds to a bottombar component.
|
|
317
|
+
|
|
318
|
+
The TUI calls :meth:`refresh_usage` and :meth:`open_reset_dialog` on a
|
|
319
|
+
Textual worker thread. This object stores the latest snapshot / details
|
|
320
|
+
and exposes a ``label`` property for the render path so the bottombar
|
|
321
|
+
never blocks on I/O.
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
def __init__(self, settings=None) -> None:
|
|
325
|
+
self._client = CodexUsageClient()
|
|
326
|
+
self.snapshot: CodexUsageSnapshot | None = None
|
|
327
|
+
self.loading = False
|
|
328
|
+
self.error = False
|
|
329
|
+
self.reset_credits: ResetCredits | None = None
|
|
330
|
+
self.consuming = False
|
|
331
|
+
self._last_refresh = 0.0
|
|
332
|
+
self._settings = settings
|
|
333
|
+
|
|
334
|
+
# -- helpers called from the TUI UI thread --------------------------------
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def label(self) -> object:
|
|
338
|
+
"""Return rich Text or a plain str for the bottombar render path."""
|
|
339
|
+
from rich.text import Text
|
|
340
|
+
|
|
341
|
+
if self.loading and self.snapshot is None:
|
|
342
|
+
return Text("codex ...", style="#5f6368")
|
|
343
|
+
if self.snapshot is None:
|
|
344
|
+
return Text("codex n/a", style="#5f6368")
|
|
345
|
+
return Text(
|
|
346
|
+
format_usage_label(self.snapshot),
|
|
347
|
+
style=usage_style(self.snapshot),
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
def has_oauth_profile(self) -> bool:
|
|
351
|
+
"""Return True when the active model profile uses Codex OAuth."""
|
|
352
|
+
settings = self._settings
|
|
353
|
+
if settings is None:
|
|
354
|
+
return False
|
|
355
|
+
try:
|
|
356
|
+
from synapse.models.registry import registry_from_settings
|
|
357
|
+
|
|
358
|
+
registry = registry_from_settings(settings)
|
|
359
|
+
selected = getattr(settings, "active_model", None) or registry.default
|
|
360
|
+
return registry.get(selected).auth == "openai_oauth"
|
|
361
|
+
except Exception: # noqa: BLE001
|
|
362
|
+
return False
|
|
363
|
+
|
|
364
|
+
def invalidate(self) -> None:
|
|
365
|
+
"""Clear all state (non-OAuth model selected)."""
|
|
366
|
+
self.snapshot = None
|
|
367
|
+
self.loading = False
|
|
368
|
+
self.error = False
|
|
369
|
+
self.reset_credits = None
|
|
370
|
+
self._last_refresh = 0.0
|
|
371
|
+
|
|
372
|
+
# -- refresh (called from TUI worker thread) ------------------------------
|
|
373
|
+
|
|
374
|
+
def should_refresh(self, *, force: bool = False) -> bool:
|
|
375
|
+
if not self.has_oauth_profile():
|
|
376
|
+
return False
|
|
377
|
+
if force:
|
|
378
|
+
return True
|
|
379
|
+
if time.monotonic() - self._last_refresh < 300:
|
|
380
|
+
return False
|
|
381
|
+
if self.loading:
|
|
382
|
+
return False
|
|
383
|
+
return True
|
|
384
|
+
|
|
385
|
+
def refresh_usage(self, *, force: bool = False) -> None:
|
|
386
|
+
"""Blocking call — run on a worker thread."""
|
|
387
|
+
self.loading = True
|
|
388
|
+
self.error = False
|
|
389
|
+
self._last_refresh = time.monotonic()
|
|
390
|
+
try:
|
|
391
|
+
self.snapshot = self._client.fetch(force=force)
|
|
392
|
+
self.loading = False
|
|
393
|
+
except Exception: # noqa: BLE001
|
|
394
|
+
self.loading = False
|
|
395
|
+
self.error = True
|
|
396
|
+
|
|
397
|
+
def fetch_reset_credits(self) -> None:
|
|
398
|
+
"""Blocking call — fetch detailed reset-credit rows."""
|
|
399
|
+
try:
|
|
400
|
+
self.reset_credits = self._client.fetch_reset_credits()
|
|
401
|
+
except Exception: # noqa: BLE001
|
|
402
|
+
pass
|
|
403
|
+
|
|
404
|
+
def consume_reset(self, credit_id: str) -> ConsumeResetResult:
|
|
405
|
+
"""Blocking call — redeem one reset credit."""
|
|
406
|
+
return self._client.consume_reset_credit(credit_id=credit_id)
|
|
@@ -518,25 +518,41 @@ def _profiles_from_mapping(data: dict[str, Any]) -> ModelRegistry:
|
|
|
518
518
|
|
|
519
519
|
|
|
520
520
|
def load_models_config(path: Path | str | None) -> ModelRegistry | None:
|
|
521
|
-
"""Load models JSON if path exists; return None when unset/missing.
|
|
521
|
+
"""Load models JSON if path exists; return None when unset/missing.
|
|
522
|
+
|
|
523
|
+
Raises:
|
|
524
|
+
ValueError: If the file does not contain valid JSON or a valid model registry.
|
|
525
|
+
"""
|
|
522
526
|
if path is None:
|
|
523
527
|
return None
|
|
524
528
|
p = Path(path).expanduser()
|
|
525
529
|
if not p.is_file():
|
|
526
530
|
return None
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
+
try:
|
|
532
|
+
data = json.loads(p.read_text(encoding="utf-8"))
|
|
533
|
+
if not isinstance(data, dict):
|
|
534
|
+
raise ValueError("root must be an object")
|
|
535
|
+
return _profiles_from_mapping(data)
|
|
536
|
+
except json.JSONDecodeError as exc:
|
|
537
|
+
raise ValueError(f"invalid models config JSON in {p}: {exc}") from exc
|
|
538
|
+
except (OSError, ValueError) as exc:
|
|
539
|
+
raise ValueError(f"invalid models config in {p}: {exc}") from exc
|
|
531
540
|
|
|
532
541
|
|
|
533
542
|
def load_models_json_blob(blob: str | None) -> ModelRegistry | None:
|
|
543
|
+
"""Load an inline MODELS_JSON registry, if configured."""
|
|
534
544
|
if not blob or not blob.strip():
|
|
535
545
|
return None
|
|
536
|
-
|
|
546
|
+
try:
|
|
547
|
+
data = json.loads(blob)
|
|
548
|
+
except json.JSONDecodeError as exc:
|
|
549
|
+
raise ValueError(f"invalid MODELS_JSON: {exc}") from exc
|
|
537
550
|
if not isinstance(data, dict):
|
|
538
551
|
raise ValueError("MODELS_JSON root must be an object")
|
|
539
|
-
|
|
552
|
+
try:
|
|
553
|
+
return _profiles_from_mapping(data)
|
|
554
|
+
except ValueError as exc:
|
|
555
|
+
raise ValueError(f"invalid MODELS_JSON: {exc}") from exc
|
|
540
556
|
|
|
541
557
|
|
|
542
558
|
def merge_model_profiles(base: ModelProfile, override: ModelProfile) -> ModelProfile:
|
{synapse_cli_agent-0.1.18 → synapse_cli_agent-0.1.19}/src/synapse/runtime/context_compact.py
RENAMED
|
@@ -7,6 +7,9 @@ helpers without registering a model-callable compaction tool.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import functools
|
|
11
|
+
import inspect
|
|
12
|
+
from collections.abc import Iterator, Mapping
|
|
10
13
|
from typing import Any
|
|
11
14
|
|
|
12
15
|
|
|
@@ -98,16 +101,65 @@ def force_compact_via_agent(
|
|
|
98
101
|
|
|
99
102
|
|
|
100
103
|
def _find_summarization_middleware(agent: Any) -> Any | None:
|
|
101
|
-
"""Return deepagents' built-in automatic summarization middleware.
|
|
104
|
+
"""Return deepagents' built-in automatic summarization middleware.
|
|
105
|
+
|
|
106
|
+
LangChain 1.3 compiles model-call middleware into the ``model`` node's
|
|
107
|
+
function closure, whereas older versions exposed it through
|
|
108
|
+
``node.bound.middleware``. Support both representations so ``/compact``
|
|
109
|
+
continues to work across the supported dependency range.
|
|
110
|
+
"""
|
|
102
111
|
nodes = getattr(agent, "nodes", None)
|
|
103
112
|
node = nodes.get("model") if isinstance(nodes, dict) else None
|
|
104
|
-
|
|
105
|
-
for candidate in getattr(
|
|
113
|
+
bound = getattr(node, "bound", None)
|
|
114
|
+
for candidate in getattr(bound, "middleware", ()):
|
|
115
|
+
if getattr(candidate, "name", None) == "SummarizationMiddleware":
|
|
116
|
+
return candidate
|
|
117
|
+
|
|
118
|
+
for candidate in _iter_closure_values(getattr(bound, "func", None)):
|
|
119
|
+
if getattr(candidate, "name", None) == "SummarizationMiddleware":
|
|
120
|
+
return candidate
|
|
121
|
+
for candidate in _iter_closure_values(getattr(bound, "afunc", None)):
|
|
106
122
|
if getattr(candidate, "name", None) == "SummarizationMiddleware":
|
|
107
123
|
return candidate
|
|
108
124
|
return None
|
|
109
125
|
|
|
110
126
|
|
|
127
|
+
def _iter_closure_values(root: Any) -> Iterator[Any]:
|
|
128
|
+
"""Yield nonlocal objects captured by a compiled model-node callable.
|
|
129
|
+
|
|
130
|
+
This deliberately follows only closure cells and their lightweight
|
|
131
|
+
containers. It avoids arbitrary ``__dict__`` traversal while still finding
|
|
132
|
+
middleware instances nested in LangChain's composed call handlers.
|
|
133
|
+
"""
|
|
134
|
+
pending: list[Any] = [root]
|
|
135
|
+
seen: set[int] = set()
|
|
136
|
+
max_items = 256
|
|
137
|
+
|
|
138
|
+
while pending and len(seen) < max_items:
|
|
139
|
+
value = pending.pop()
|
|
140
|
+
if value is None or id(value) in seen:
|
|
141
|
+
continue
|
|
142
|
+
seen.add(id(value))
|
|
143
|
+
yield value
|
|
144
|
+
|
|
145
|
+
if inspect.ismethod(value):
|
|
146
|
+
pending.extend((value.__self__, value.__func__))
|
|
147
|
+
elif inspect.isfunction(value):
|
|
148
|
+
try:
|
|
149
|
+
closure = inspect.getclosurevars(value)
|
|
150
|
+
except (TypeError, ValueError):
|
|
151
|
+
continue
|
|
152
|
+
pending.extend(closure.nonlocals.values())
|
|
153
|
+
elif isinstance(value, functools.partial):
|
|
154
|
+
pending.append(value.func)
|
|
155
|
+
pending.extend(value.args)
|
|
156
|
+
pending.extend((value.keywords or {}).values())
|
|
157
|
+
elif isinstance(value, Mapping):
|
|
158
|
+
pending.extend(value.values())
|
|
159
|
+
elif isinstance(value, (list, tuple, set, frozenset)):
|
|
160
|
+
pending.extend(value)
|
|
161
|
+
|
|
162
|
+
|
|
111
163
|
def context_status_lines(agent: Any, thread_id: str) -> list[str]:
|
|
112
164
|
"""Report message count + last summarization event for current thread."""
|
|
113
165
|
lines: list[str] = [f"thread_id={thread_id}"]
|
|
@@ -216,4 +268,3 @@ def is_context_compact_text(text: str | None) -> bool:
|
|
|
216
268
|
if has_intent and ("ARTIFACTS" in body or "NEXT STEPS" in body):
|
|
217
269
|
return True
|
|
218
270
|
return False
|
|
219
|
-
|