thinkstack-core 4.0.0__tar.gz → 4.1.0__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.
- {thinkstack_core-4.0.0/thinkstack_core.egg-info → thinkstack_core-4.1.0}/PKG-INFO +4 -3
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/README.md +2 -2
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/pyproject.toml +2 -2
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s1_branch_merge.py +4 -4
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s24_reasoning_plus_learning.py +4 -3
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s3_sensitivity.py +1 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s7_mcp_hooks.py +12 -7
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s7_parser.py +334 -18
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_sync_bundle.py +2 -2
- thinkstack_core-4.1.0/tests/test_toon_integration.py +184 -0
- thinkstack_core-4.1.0/tests/test_toon_io.py +196 -0
- thinkstack_core-4.1.0/tests/test_toon_serialize.py +169 -0
- thinkstack_core-4.1.0/tests/test_toon_token.py +197 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/sync_bundle.py +35 -24
- thinkstack_core-4.1.0/thinkstack_core/compat.py +106 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/gcc.py +17 -15
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/invariants.py +3 -1
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/mcp/auth.py +10 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/mcp/server.py +66 -20
- thinkstack_core-4.1.0/thinkstack_core/parser/thinking.py +219 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/config.py +6 -16
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/api.py +2 -1
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/chain.py +14 -7
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/deny_list.py +20 -26
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/provenance.py +13 -23
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/recorder.py +13 -26
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/store.py +15 -36
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/sensitivity.py +5 -6
- thinkstack_core-4.1.0/thinkstack_core/serialization/__init__.py +27 -0
- thinkstack_core-4.1.0/thinkstack_core/serialization/io.py +76 -0
- thinkstack_core-4.1.0/thinkstack_core/serialization/reporting.py +85 -0
- thinkstack_core-4.1.0/thinkstack_core/serialization/token_counter.py +57 -0
- thinkstack_core-4.1.0/thinkstack_core/serialization/toon.py +507 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/theta.py +4 -7
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/topics.py +17 -10
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0/thinkstack_core.egg-info}/PKG-INFO +4 -3
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core.egg-info/SOURCES.txt +9 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core.egg-info/requires.txt +1 -0
- thinkstack_core-4.0.0/thinkstack_core/compat.py +0 -103
- thinkstack_core-4.0.0/thinkstack_core/parser/thinking.py +0 -112
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/MANIFEST.in +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/setup.cfg +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_backend_factory.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_broadcast.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_claude_config_locations.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_cli_sync_all.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_cli_topics_concepts.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_cloud_auth.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_cloud_bridge.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_cloud_integration.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_cloud_sse.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_cloud_storage.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_cloud_sync.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_concept_catalog.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_doctor_ci.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_getting_started.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_github_app.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_github_pat.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_hidden_profile_benchmark.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_hook_entrypoint.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_mcp_client.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_rep_merge.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s0_gcc.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s10_jetbrains.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s10_vscode.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s11_codex.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s11_hooks_installer.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s12_github.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s12_gitlab.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s13_dashboard_api.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s13_forensic.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s14_audit_export.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s14_ciso_api.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s15_rep_network.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s16_alerts.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s17a_metrics.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s17b_observability.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s17b_sso.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s18_context_extraction.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s18_credibility.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s18_identity.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s19_learning_evolution.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s19_projects.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s19_reasoning_query.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s20_divergence.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s20_metrics.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s20_pii_guardrails.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s20_token_budget.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s20_wrapper_capture.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s21_cross_project.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s21_hitl.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s22_swe_bench.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s22_swe_bench_dry_run.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s22_sync.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s23_reasoning_plus.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s23_sync_remaining.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s25_delivery_time.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s25_serve.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s25_setup_detection.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s25_templates.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s26_daemon.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s26_git_hooks.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s26_proxy.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s26_watcher.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s27_cross_project.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s27_drift.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s27_query.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s27_templates.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s28_cli.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s28_disagreement.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s28_planner.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s28_roles.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s28_session.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s28_simulator.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s29_ci.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s29_cli.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s29_dashboard.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s29_github.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s2_context.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s30_audit.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s31_cloud.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s4_invariants.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s4_variance_lock.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s5_privacy_disclosure.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s5_rep_sis.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s6_debug.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s7_ollama_wrapper.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s7_proxy.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s7_wrappers.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s7ent_gateway.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s7ent_helm.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s8_aggphi.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s8_theta_synthesis.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_s9_gaps.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_setup_command.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_setup_targets.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_signing.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_storage_azure.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_sync_conflicts.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_sync_state.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/tests/test_topics.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/aggphi_textual.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/alerts/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/alerts/base.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/alerts/config.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/alerts/dispatcher.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/alerts/jira.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/alerts/linear.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/alerts/pagerduty.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/alerts/slack.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/alerts/teams.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/audit/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/audit/exporter.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/audit/privacy.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/audit/scrubber.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/audit/service.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/audit/signing.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/broadcast/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/broadcast/broadcaster.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/broadcast/watcher.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/capability.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/client_config.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/client_configs/.claude-opencode-fallback.json +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/client_configs/.claude-stdio.json +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/client_configs/.cursor-mcp.json +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/client_configs/.opencode-bridge.json +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/client_configs/.opencode.json +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/client_configs/.vscode-mcp.json +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/mcp_client.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/setup.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/sync.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/sync_conflicts.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/sync_state.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/team_sync.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/cloud/thinkstack-mcp-bridge.js +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/codex/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/codex/__main__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/codex/capture.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/codex/proxy.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/concept_catalog.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/consolidation/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/consolidation/synthesizer.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/consolidation/workflow.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/daemon/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/daemon/supervisor.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/daemon/watcher.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/dashboard_api.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/deltaf.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/disclosure.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/divergence/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/divergence/detector.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/gateway/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/gateway/key_manager.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/gateway/metrics_webhook.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/gateway/policy.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/gateway/server.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/gateway/sso.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/github/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/github/app.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/github/comment_builder.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/github/pat.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/github/pr_parser.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/github/pr_reporter.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/gitlab/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hitl/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hitl/channels.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hitl/orchestrator.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hooks/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hooks/claude_code.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hooks/git_capture.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hooks/git_commit.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hooks/installer.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hooks/pre_commit.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/hooks/runner.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/identity/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/identity/agent.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/identity/providers.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/mcp/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/aggregate.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/calibrate.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/calibration.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/credibility.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/delivery_time.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/dhs.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/mcs.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/roi.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/session_writer.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/shadow_ai.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/metrics/sprint_writer.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/observability/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/observability/datadog.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/observability/formatter.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/observability/report.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/observability/servicenow.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/observability/splunk.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/observability/webhook.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/parser/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/parser/blocks.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/parser/inference.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/projects.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/prompt_artifact.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/proxy/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/proxy/routes/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/proxy/routes/anthropic.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/proxy/routes/azure_openai.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/proxy/routes/gemini.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/proxy/routes/groq.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/proxy/routes/ollama.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/proxy/routes/openai.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/proxy/server.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/query/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/query/grep.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/query/hybrid.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/query/semantic.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/rdp.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning/entry.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning/store.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/augmenter.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/capture.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/context.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/analytics.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/composer.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/conflicts.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/context_collector.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/cross_project.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/embeddings.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/evolution.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/extractor.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/filter_five_layer.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/models.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/org_store.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/pii.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/promotion.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/relevance.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/state.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/learning/theta_learning_bridge.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/reasoning_plus/prompt.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/rep.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/rep_network/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/rep_network/merge.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/rep_network/node.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/rep_network/server.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/rep_network/sync.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/serve.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/session/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/session/disagreement.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/session/models.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/session/orchestrator.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/session/planner.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/session/simulator.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/signing.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/sis.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/skills/pr-reviewer/SKILL.md +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/skills/thinkstack-auto-sync/SKILL.md +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/skills/thinkstack-session-start/SKILL.md +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/storage.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/engine.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/go.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/infra.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/library/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/library/api_design.md +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/library/bug_fix.md +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/library/decision_record.md +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/library/engine.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/library/security_review.md +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/python.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/react.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/templates/typescript.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/theta_synthesis.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/variance.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/wrapper/__init__.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/wrapper/anthropic.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/wrapper/base.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/wrapper/bedrock.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/wrapper/gemini.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/wrapper/ollama.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core/wrapper/openai.py +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core.egg-info/dependency_links.txt +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core.egg-info/entry_points.txt +0 -0
- {thinkstack_core-4.0.0 → thinkstack_core-4.1.0}/thinkstack_core.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thinkstack-core
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.0
|
|
4
4
|
Summary: ThinkStack — AI reasoning capture, audit trail, and governance for agent-assisted development (core library)
|
|
5
5
|
Author-email: Hemant Joshi <hemant@flotorch.ai>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -24,6 +24,7 @@ Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
|
24
24
|
Requires-Dist: boto3>=1.34.0; extra == "dev"
|
|
25
25
|
Requires-Dist: pyyaml>=6.0; extra == "dev"
|
|
26
26
|
Requires-Dist: pdfminer.six>=20221105; extra == "dev"
|
|
27
|
+
Requires-Dist: tiktoken>=0.7.0; extra == "dev"
|
|
27
28
|
Provides-Extra: wrapper
|
|
28
29
|
Requires-Dist: anthropic>=0.40.0; extra == "wrapper"
|
|
29
30
|
Requires-Dist: openai>=1.50.0; extra == "wrapper"
|
|
@@ -60,10 +61,10 @@ Requires-Dist: thinkstack-core[audit,cloud,embeddings,mcp,proxy,wrapper]; extra
|
|
|
60
61
|
|
|
61
62
|
**Local-first AI agent governance.** ThinkStack gives multi-agent systems a shared on-disk audit trail, coordination vector, privacy controls, and LLM integration layer — all without a remote service.
|
|
62
63
|
|
|
63
|
-
> **Latest release:** `thinkstack-core` and `thinkstack-cli` **
|
|
64
|
+
> **Latest release:** `thinkstack-core` and `thinkstack-cli` **4.0.0** — Multi-model reasoning parsers + TOON default serialization + RACP spec paper. Install or upgrade with `pip install -U thinkstack-cli`.
|
|
64
65
|
|
|
65
66
|
```bash
|
|
66
|
-
pip install -U thinkstack-cli>=
|
|
67
|
+
pip install -U thinkstack-cli>=4.0.0
|
|
67
68
|
thinkstack init
|
|
68
69
|
thinkstack setup # connects to Claude Code, Cursor, or Antigravity
|
|
69
70
|
thinkstack debug timeline
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
**Local-first AI agent governance.** ThinkStack gives multi-agent systems a shared on-disk audit trail, coordination vector, privacy controls, and LLM integration layer — all without a remote service.
|
|
4
4
|
|
|
5
|
-
> **Latest release:** `thinkstack-core` and `thinkstack-cli` **
|
|
5
|
+
> **Latest release:** `thinkstack-core` and `thinkstack-cli` **4.0.0** — Multi-model reasoning parsers + TOON default serialization + RACP spec paper. Install or upgrade with `pip install -U thinkstack-cli`.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pip install -U thinkstack-cli>=
|
|
8
|
+
pip install -U thinkstack-cli>=4.0.0
|
|
9
9
|
thinkstack init
|
|
10
10
|
thinkstack setup # connects to Claude Code, Cursor, or Antigravity
|
|
11
11
|
thinkstack debug timeline
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "thinkstack-core"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.1.0"
|
|
4
4
|
description = "ThinkStack — AI reasoning capture, audit trail, and governance for agent-assisted development (core library)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "Apache-2.0" }
|
|
@@ -26,7 +26,7 @@ dependencies = []
|
|
|
26
26
|
thinkstack-hook = "thinkstack_core.hooks.runner:cli"
|
|
27
27
|
|
|
28
28
|
[project.optional-dependencies]
|
|
29
|
-
dev = ["pytest>=8.0.0", "boto3>=1.34.0", "pyyaml>=6.0", "pdfminer.six>=20221105"]
|
|
29
|
+
dev = ["pytest>=8.0.0", "boto3>=1.34.0", "pyyaml>=6.0", "pdfminer.six>=20221105", "tiktoken>=0.7.0"]
|
|
30
30
|
wrapper = [
|
|
31
31
|
"anthropic>=0.40.0",
|
|
32
32
|
"openai>=1.50.0",
|
|
@@ -20,14 +20,14 @@ def test_commit_ids_are_deterministic_and_monotonic(tmp_path: Path) -> None:
|
|
|
20
20
|
commits_dir = gcc_dir / "commits"
|
|
21
21
|
|
|
22
22
|
# Expect two commits with deterministic, hex-encoded IDs starting from 1
|
|
23
|
-
files = sorted(p.name for p in commits_dir.glob("*.
|
|
24
|
-
assert files == ["00000001.
|
|
23
|
+
files = sorted(p.name for p in commits_dir.glob("*.toon"))
|
|
24
|
+
assert files == ["00000001.toon", "00000002.toon"]
|
|
25
25
|
|
|
26
26
|
# Re-open repo and commit once more: ID should continue monotonically
|
|
27
27
|
repo2 = GCCRepository.at(tmp_path)
|
|
28
28
|
repo2.commit("c3")
|
|
29
|
-
files = sorted(p.name for p in commits_dir.glob("*.
|
|
30
|
-
assert files == ["00000001.
|
|
29
|
+
files = sorted(p.name for p in commits_dir.glob("*.toon"))
|
|
30
|
+
assert files == ["00000001.toon", "00000002.toon", "00000003.toon"]
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
def test_branch_and_history(tmp_path: Path) -> None:
|
|
@@ -31,6 +31,7 @@ from thinkstack_core.reasoning_plus.learning import (
|
|
|
31
31
|
ReasoningCall,
|
|
32
32
|
workspace_state_hash,
|
|
33
33
|
)
|
|
34
|
+
from thinkstack_core.serialization import loads as toon_loads
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
# ---------------------------------------------------------------------------
|
|
@@ -433,7 +434,7 @@ class TestReasoningPlusLearning:
|
|
|
433
434
|
)
|
|
434
435
|
learnings = drpl.get_relevant_learnings("fix parse_date bug")
|
|
435
436
|
assert len(learnings) >= 1
|
|
436
|
-
assert "
|
|
437
|
+
assert "parse_date" in learnings[0].content
|
|
437
438
|
|
|
438
439
|
def test_compose_user_prompt(self, drpl):
|
|
439
440
|
learnings = [Learning(content="Check foo.py first")]
|
|
@@ -574,7 +575,7 @@ class TestMCPLearningTools:
|
|
|
574
575
|
repo = self._make_repo(temp_repo)
|
|
575
576
|
self._seed_learning(repo)
|
|
576
577
|
text = _dispatch_tool(repo, "thinkstack_reasoning_plus_learning_query", {"context": "fix parse_date bug"})
|
|
577
|
-
data =
|
|
578
|
+
data = toon_loads(text)
|
|
578
579
|
assert data["count"] >= 1
|
|
579
580
|
assert "parse_date" in data["learnings"][0]["content"]
|
|
580
581
|
|
|
@@ -590,7 +591,7 @@ class TestMCPLearningTools:
|
|
|
590
591
|
"thinkstack_reasoning_plus_learning_feedback",
|
|
591
592
|
{"learning_id": learning.id, "outcome": "success"},
|
|
592
593
|
)
|
|
593
|
-
data =
|
|
594
|
+
data = toon_loads(text)
|
|
594
595
|
assert data["confidence"] > 0.8
|
|
595
596
|
|
|
596
597
|
def test_learning_purge(self, temp_repo):
|
|
@@ -200,6 +200,7 @@ class TestSensitivityStore:
|
|
|
200
200
|
store.add(SensitivityEvent("n1", "schema_version", "", 0.9, "PUBLIC"))
|
|
201
201
|
store.add(SensitivityEvent("n2", "schema_version", "", 0.7, "PROTECTED"))
|
|
202
202
|
store.add(SensitivityEvent("n3", "pii_fields", "", 0.6, "PRIVATE"))
|
|
203
|
+
import json
|
|
203
204
|
index = json.loads((tmp_path / "sens" / "index.json").read_text())
|
|
204
205
|
assert len(index["schema_version"]) == 2
|
|
205
206
|
assert len(index["pii_fields"]) == 1
|
|
@@ -15,6 +15,7 @@ Tests:
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
|
+
from thinkstack_core.serialization import loads as toon_loads
|
|
18
19
|
import sys
|
|
19
20
|
import io
|
|
20
21
|
from pathlib import Path
|
|
@@ -287,13 +288,17 @@ class TestMCPServer(unittest.TestCase):
|
|
|
287
288
|
resp = responses[0]
|
|
288
289
|
self.assertIn("result", resp)
|
|
289
290
|
tools = resp["result"]["tools"]
|
|
290
|
-
#
|
|
291
|
-
self.assertEqual(len(tools),
|
|
291
|
+
# 23 new tools + 23 legacy devtorch_* aliases = 46
|
|
292
|
+
self.assertEqual(len(tools), 46)
|
|
292
293
|
names = {t["name"] for t in tools}
|
|
293
294
|
self.assertIn("thinkstack_commit", names)
|
|
295
|
+
self.assertIn("devtorch_commit", names)
|
|
294
296
|
self.assertIn("thinkstack_sensitivity_add", names)
|
|
297
|
+
self.assertIn("devtorch_sensitivity_add", names)
|
|
295
298
|
self.assertIn("thinkstack_context", names)
|
|
299
|
+
self.assertIn("devtorch_context", names)
|
|
296
300
|
self.assertIn("thinkstack_theta_read", names)
|
|
301
|
+
self.assertIn("devtorch_theta_read", names)
|
|
297
302
|
self.assertIn("thinkstack_reasoning_plus_config", names)
|
|
298
303
|
self.assertIn("thinkstack_reasoning_plus_set", names)
|
|
299
304
|
self.assertIn("thinkstack_reasoning_plus_learning_query", names)
|
|
@@ -384,7 +389,7 @@ class TestMCPServer(unittest.TestCase):
|
|
|
384
389
|
}
|
|
385
390
|
responses = self._call_jsonrpc(mock_repo, req)
|
|
386
391
|
text = responses[0]["result"]["content"][0]["text"]
|
|
387
|
-
parsed =
|
|
392
|
+
parsed = toon_loads(text)
|
|
388
393
|
self.assertIn("auth", parsed)
|
|
389
394
|
self.assertIn("api", parsed)
|
|
390
395
|
|
|
@@ -409,7 +414,7 @@ class TestMCPServer(unittest.TestCase):
|
|
|
409
414
|
}
|
|
410
415
|
responses = self._call_jsonrpc(mock_repo, req)
|
|
411
416
|
text = responses[0]["result"]["content"][0]["text"]
|
|
412
|
-
parsed =
|
|
417
|
+
parsed = toon_loads(text)
|
|
413
418
|
self.assertIn("auth", parsed)
|
|
414
419
|
self.assertIn("schema", parsed)
|
|
415
420
|
self.assertNotIn("api", parsed)
|
|
@@ -453,7 +458,7 @@ class TestMCPServer(unittest.TestCase):
|
|
|
453
458
|
}
|
|
454
459
|
responses = self._call_jsonrpc(repo, req)
|
|
455
460
|
text = responses[0]["result"]["content"][0]["text"]
|
|
456
|
-
parsed =
|
|
461
|
+
parsed = toon_loads(text)
|
|
457
462
|
self.assertIn("source", parsed)
|
|
458
463
|
self.assertIn("config", parsed)
|
|
459
464
|
self.assertIn("enabled", parsed["config"])
|
|
@@ -476,8 +481,8 @@ class TestMCPServer(unittest.TestCase):
|
|
|
476
481
|
}
|
|
477
482
|
responses = self._call_jsonrpc(repo, req)
|
|
478
483
|
text = responses[0]["result"]["content"][0]["text"]
|
|
479
|
-
self.assertIn("
|
|
480
|
-
settings = json.loads((repo.gcc_dir / "settings.json").read_text())
|
|
484
|
+
self.assertIn("enabled: false", text)
|
|
485
|
+
settings = toon_loads((repo.gcc_dir / "settings.toon").read_text()) if (repo.gcc_dir / "settings.toon").exists() else json.loads((repo.gcc_dir / "settings.json").read_text())
|
|
481
486
|
self.assertFalse(settings["reasoning_plus"]["enabled"])
|
|
482
487
|
self.assertEqual(settings["reasoning_plus"]["smart_top_n"], 5)
|
|
483
488
|
|
|
@@ -6,13 +6,15 @@ Tests for the S7 parser layer (thinkstack_core.parser).
|
|
|
6
6
|
Coverage
|
|
7
7
|
--------
|
|
8
8
|
* extract_racp_blocks — both block types, only commits, only sensitivities,
|
|
9
|
-
|
|
9
|
+
malformed XML, no blocks, attribute-order variation
|
|
10
10
|
* strip_racp_blocks — blocks removed, surrounding text preserved,
|
|
11
|
-
|
|
12
|
-
* extract_thinking_blocks — Anthropic format, OpenAI format,
|
|
11
|
+
paired tags, mixed content
|
|
12
|
+
* extract_thinking_blocks — Anthropic format, OpenAI format, Gemini format,
|
|
13
|
+
DeepSeek/generic, generic XML/bracket tags,
|
|
14
|
+
precedence order, nested tags, empty / missing
|
|
13
15
|
* thinking_to_commit_message — normal, empty, long text, whitespace collapse
|
|
14
16
|
* infer_sensitivity — schema keywords, auth keywords, empty text,
|
|
15
|
-
|
|
17
|
+
max_concepts limit, multi-match
|
|
16
18
|
* extract_decision_summary — normal sentence, markdown-heavy, very long,
|
|
17
19
|
empty, code-fence stripping
|
|
18
20
|
"""
|
|
@@ -104,14 +106,10 @@ class TestExtractRacpBlocks:
|
|
|
104
106
|
def test_malformed_tag_skipped_gracefully(self):
|
|
105
107
|
"""Malformed XML must not raise; valid blocks in same text are still extracted."""
|
|
106
108
|
text = (
|
|
107
|
-
# malformed: unquoted attribute value
|
|
108
109
|
'<thinkstack:commit message=not-quoted confidence="0.9"/>\n'
|
|
109
|
-
# valid block follows
|
|
110
110
|
'<thinkstack:commit message="valid block" confidence="0.8"/>\n'
|
|
111
111
|
)
|
|
112
|
-
# Should not raise
|
|
113
112
|
commits, sensitivities = extract_racp_blocks(text)
|
|
114
|
-
# The valid block should be parsed
|
|
115
113
|
valid = [c for c in commits if c.message == "valid block"]
|
|
116
114
|
assert len(valid) == 1
|
|
117
115
|
|
|
@@ -356,6 +354,7 @@ class TestExtractThinkingBlocks:
|
|
|
356
354
|
assert blocks == []
|
|
357
355
|
|
|
358
356
|
def test_anthropic_thinking_block_empty_text_skipped(self):
|
|
357
|
+
"""Empty thinking text should be skipped."""
|
|
359
358
|
response = {
|
|
360
359
|
"content": [
|
|
361
360
|
{"type": "thinking", "thinking": ""},
|
|
@@ -363,7 +362,6 @@ class TestExtractThinkingBlocks:
|
|
|
363
362
|
]
|
|
364
363
|
}
|
|
365
364
|
blocks = extract_thinking_blocks(response)
|
|
366
|
-
# Empty thinking text should be skipped
|
|
367
365
|
assert len(blocks) == 1
|
|
368
366
|
assert blocks[0].text == "Non-empty thought."
|
|
369
367
|
|
|
@@ -384,6 +382,332 @@ class TestExtractThinkingBlocks:
|
|
|
384
382
|
blocks = extract_thinking_blocks(response)
|
|
385
383
|
assert blocks == []
|
|
386
384
|
|
|
385
|
+
def test_gemini_format(self):
|
|
386
|
+
response = {
|
|
387
|
+
"candidates": [
|
|
388
|
+
{
|
|
389
|
+
"content": {
|
|
390
|
+
"parts": [
|
|
391
|
+
{"text": "I need to check the API limits first.", "thought": True},
|
|
392
|
+
{"text": "The answer is 42."},
|
|
393
|
+
]
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
]
|
|
397
|
+
}
|
|
398
|
+
blocks = extract_thinking_blocks(response)
|
|
399
|
+
assert len(blocks) == 1
|
|
400
|
+
assert blocks[0].text == "I need to check the API limits first."
|
|
401
|
+
assert blocks[0].source == "gemini_thought"
|
|
402
|
+
|
|
403
|
+
def test_gemini_format_multiple_thoughts(self):
|
|
404
|
+
response = {
|
|
405
|
+
"candidates": [
|
|
406
|
+
{
|
|
407
|
+
"content": {
|
|
408
|
+
"parts": [
|
|
409
|
+
{"text": "Thought one.", "thought": True},
|
|
410
|
+
{"text": "Thought two.", "thought": True},
|
|
411
|
+
{"text": "Final answer."},
|
|
412
|
+
]
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
}
|
|
417
|
+
blocks = extract_thinking_blocks(response)
|
|
418
|
+
assert len(blocks) == 2
|
|
419
|
+
assert blocks[0].text == "Thought one."
|
|
420
|
+
assert blocks[1].text == "Thought two."
|
|
421
|
+
|
|
422
|
+
def test_gemini_format_no_thoughts(self):
|
|
423
|
+
response = {
|
|
424
|
+
"candidates": [
|
|
425
|
+
{
|
|
426
|
+
"content": {
|
|
427
|
+
"parts": [
|
|
428
|
+
{"text": "Direct answer without thinking."},
|
|
429
|
+
]
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
]
|
|
433
|
+
}
|
|
434
|
+
blocks = extract_thinking_blocks(response)
|
|
435
|
+
assert blocks == []
|
|
436
|
+
|
|
437
|
+
def test_deepseek_reasoning_content(self):
|
|
438
|
+
"""DeepSeek / generic servers using reasoning_content field."""
|
|
439
|
+
response = {
|
|
440
|
+
"choices": [
|
|
441
|
+
{
|
|
442
|
+
"message": {
|
|
443
|
+
"role": "assistant",
|
|
444
|
+
"content": "The answer.",
|
|
445
|
+
"reasoning_content": "DeepSeek reasoning goes here.",
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
]
|
|
449
|
+
}
|
|
450
|
+
blocks = extract_thinking_blocks(response)
|
|
451
|
+
assert len(blocks) == 1
|
|
452
|
+
assert blocks[0].text == "DeepSeek reasoning goes here."
|
|
453
|
+
assert blocks[0].source == "reasoning_content"
|
|
454
|
+
|
|
455
|
+
def test_generic_reasoning_field_fallback(self):
|
|
456
|
+
"""Some servers use 'reasoning' instead of 'reasoning_content'."""
|
|
457
|
+
response = {
|
|
458
|
+
"choices": [
|
|
459
|
+
{
|
|
460
|
+
"message": {
|
|
461
|
+
"role": "assistant",
|
|
462
|
+
"content": "Final answer.",
|
|
463
|
+
"reasoning": "Alternative field name.",
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
blocks = extract_thinking_blocks(response)
|
|
469
|
+
assert len(blocks) == 1
|
|
470
|
+
assert blocks[0].text == "Alternative field name."
|
|
471
|
+
assert blocks[0].source == "reasoning"
|
|
472
|
+
|
|
473
|
+
def test_generic_chain_of_thought_field(self):
|
|
474
|
+
response = {
|
|
475
|
+
"choices": [
|
|
476
|
+
{
|
|
477
|
+
"message": {
|
|
478
|
+
"role": "assistant",
|
|
479
|
+
"content": "Final answer.",
|
|
480
|
+
"chain_of_thought": "Step-by-step reasoning.",
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
]
|
|
484
|
+
}
|
|
485
|
+
blocks = extract_thinking_blocks(response)
|
|
486
|
+
assert len(blocks) == 1
|
|
487
|
+
assert blocks[0].text == "Step-by-step reasoning."
|
|
488
|
+
assert blocks[0].source == "chain_of_thought"
|
|
489
|
+
|
|
490
|
+
def test_precedence_api_native_wins_over_generic_tags(self):
|
|
491
|
+
"""API-native reasoning_content takes priority over generic tags in text."""
|
|
492
|
+
response = {
|
|
493
|
+
"choices": [
|
|
494
|
+
{
|
|
495
|
+
"message": {
|
|
496
|
+
"role": "assistant",
|
|
497
|
+
"content": "<thinking>Should not be captured.</thinking>\nFinal answer.",
|
|
498
|
+
"reasoning_content": "API-native reasoning wins.",
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
]
|
|
502
|
+
}
|
|
503
|
+
blocks = extract_thinking_blocks(response)
|
|
504
|
+
assert len(blocks) == 1
|
|
505
|
+
assert blocks[0].text == "API-native reasoning wins."
|
|
506
|
+
assert blocks[0].source == "reasoning_content"
|
|
507
|
+
|
|
508
|
+
def test_generic_tag_thinking_in_output_text(self):
|
|
509
|
+
"""<thinking> tags in response text when no API-native fields present."""
|
|
510
|
+
response = {
|
|
511
|
+
"choices": [
|
|
512
|
+
{
|
|
513
|
+
"message": {
|
|
514
|
+
"role": "assistant",
|
|
515
|
+
"content": "<thinking>This is my reasoning.</thinking>\nFinal answer."
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
]
|
|
519
|
+
}
|
|
520
|
+
blocks = extract_thinking_blocks(response)
|
|
521
|
+
assert len(blocks) == 1
|
|
522
|
+
assert blocks[0].text == "This is my reasoning."
|
|
523
|
+
assert blocks[0].source == "tag_thinking"
|
|
524
|
+
|
|
525
|
+
def test_generic_tag_reasoning_in_output_text(self):
|
|
526
|
+
response = {
|
|
527
|
+
"choices": [
|
|
528
|
+
{
|
|
529
|
+
"message": {
|
|
530
|
+
"role": "assistant",
|
|
531
|
+
"content": "<reasoning>Step-by-step logic.</reasoning>\nOutput."
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
blocks = extract_thinking_blocks(response)
|
|
537
|
+
assert len(blocks) == 1
|
|
538
|
+
assert blocks[0].text == "Step-by-step logic."
|
|
539
|
+
assert blocks[0].source == "tag_reasoning"
|
|
540
|
+
|
|
541
|
+
def test_generic_bracket_thinking_in_output_text(self):
|
|
542
|
+
response = {
|
|
543
|
+
"choices": [
|
|
544
|
+
{
|
|
545
|
+
"message": {
|
|
546
|
+
"role": "assistant",
|
|
547
|
+
"content": "[thinking]Bracket reasoning.[/thinking]\nFinal."
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
]
|
|
551
|
+
}
|
|
552
|
+
blocks = extract_thinking_blocks(response)
|
|
553
|
+
assert len(blocks) == 1
|
|
554
|
+
assert blocks[0].text == "Bracket reasoning."
|
|
555
|
+
assert blocks[0].source == "bracket_thinking"
|
|
556
|
+
|
|
557
|
+
def test_generic_html_comment_with_reasoning_marker(self):
|
|
558
|
+
response = {
|
|
559
|
+
"choices": [
|
|
560
|
+
{
|
|
561
|
+
"message": {
|
|
562
|
+
"role": "assistant",
|
|
563
|
+
"content": "<!-- think: Reasoning inside HTML comment -->\nFinal answer."
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
]
|
|
567
|
+
}
|
|
568
|
+
blocks = extract_thinking_blocks(response)
|
|
569
|
+
assert len(blocks) == 1
|
|
570
|
+
assert "Reasoning inside HTML comment" in blocks[0].text
|
|
571
|
+
assert blocks[0].source == "html_comment"
|
|
572
|
+
|
|
573
|
+
def test_generic_html_comment_with_reasoning_marker_lowercase(self):
|
|
574
|
+
response = {
|
|
575
|
+
"choices": [
|
|
576
|
+
{
|
|
577
|
+
"message": {
|
|
578
|
+
"role": "assistant",
|
|
579
|
+
"content": "<!-- reasoning Step-by-step logic. -->\nFinal."
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
]
|
|
583
|
+
}
|
|
584
|
+
blocks = extract_thinking_blocks(response)
|
|
585
|
+
assert len(blocks) == 1
|
|
586
|
+
assert "Step-by-step logic." in blocks[0].text
|
|
587
|
+
assert blocks[0].source == "html_comment"
|
|
588
|
+
|
|
589
|
+
def test_generic_html_comment_false_positive_not_captured(self):
|
|
590
|
+
"""Plain HTML comments without a reasoning marker must NOT be captured."""
|
|
591
|
+
response = {
|
|
592
|
+
"choices": [
|
|
593
|
+
{
|
|
594
|
+
"message": {
|
|
595
|
+
"role": "assistant",
|
|
596
|
+
"content": "<!-- generated by tool -->\nFinal answer here."
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
]
|
|
600
|
+
}
|
|
601
|
+
blocks = extract_thinking_blocks(response)
|
|
602
|
+
assert blocks == []
|
|
603
|
+
|
|
604
|
+
def test_generic_html_comment_false_positive_pii_disclaimer(self):
|
|
605
|
+
response = {
|
|
606
|
+
"choices": [
|
|
607
|
+
{
|
|
608
|
+
"message": {
|
|
609
|
+
"role": "assistant",
|
|
610
|
+
"content": "<!-- TODO: fix security issue -->\nThe answer is 42."
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
]
|
|
614
|
+
}
|
|
615
|
+
blocks = extract_thinking_blocks(response)
|
|
616
|
+
assert blocks == []
|
|
617
|
+
|
|
618
|
+
def test_no_matching_generic_tag_returns_empty(self):
|
|
619
|
+
response = {
|
|
620
|
+
"choices": [
|
|
621
|
+
{
|
|
622
|
+
"message": {
|
|
623
|
+
"role": "assistant",
|
|
624
|
+
"content": "Just a normal response with no tags."
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
]
|
|
628
|
+
}
|
|
629
|
+
blocks = extract_thinking_blocks(response)
|
|
630
|
+
assert blocks == []
|
|
631
|
+
|
|
632
|
+
def test_nested_tags_first_closing_wins(self):
|
|
633
|
+
"""Nested/overlapping tags: first matching closing tag captures content."""
|
|
634
|
+
response = {
|
|
635
|
+
"choices": [
|
|
636
|
+
{
|
|
637
|
+
"message": {
|
|
638
|
+
"role": "assistant",
|
|
639
|
+
"content": "<thinking>Outer <reasoning>inner</reasoning> still outer</thinking>\nAnswer."
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
]
|
|
643
|
+
}
|
|
644
|
+
blocks = extract_thinking_blocks(response)
|
|
645
|
+
assert len(blocks) == 1
|
|
646
|
+
assert "Outer" in blocks[0].text
|
|
647
|
+
assert blocks[0].source == "tag_thinking"
|
|
648
|
+
|
|
649
|
+
def test_multi_format_dedup_first_pattern_wins(self):
|
|
650
|
+
"""When both <thinking> and [thinking] coexist, only first pattern used."""
|
|
651
|
+
response = {
|
|
652
|
+
"choices": [
|
|
653
|
+
{
|
|
654
|
+
"message": {
|
|
655
|
+
"role": "assistant",
|
|
656
|
+
"content": "<thinking>XML thinking.</thinking>\n[thinking]Bracket thinking.[/thinking]"
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
]
|
|
660
|
+
}
|
|
661
|
+
blocks = extract_thinking_blocks(response)
|
|
662
|
+
assert len(blocks) == 1
|
|
663
|
+
assert blocks[0].source == "tag_thinking"
|
|
664
|
+
|
|
665
|
+
def test_multiple_thinking_tags_same_pattern_all_captured(self):
|
|
666
|
+
"""Multiple occurrences of the SAME tag convention are all captured."""
|
|
667
|
+
response = {
|
|
668
|
+
"choices": [
|
|
669
|
+
{
|
|
670
|
+
"message": {
|
|
671
|
+
"role": "assistant",
|
|
672
|
+
"content": "<thinking>First thought.</thinking>\n<thinking>Second thought.</thinking>\nAnswer."
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
]
|
|
676
|
+
}
|
|
677
|
+
blocks = extract_thinking_blocks(response)
|
|
678
|
+
assert len(blocks) == 2
|
|
679
|
+
assert blocks[0].text == "First thought."
|
|
680
|
+
assert blocks[1].text == "Second thought."
|
|
681
|
+
|
|
682
|
+
def test_backward_compatibility_anthropic_still_works(self):
|
|
683
|
+
"""Existing Anthropic-only tests must still pass."""
|
|
684
|
+
response = {
|
|
685
|
+
"content": [
|
|
686
|
+
{"type": "thinking", "thinking": "Thinking content."},
|
|
687
|
+
{"type": "text", "text": "Answer."},
|
|
688
|
+
]
|
|
689
|
+
}
|
|
690
|
+
blocks = extract_thinking_blocks(response)
|
|
691
|
+
assert len(blocks) == 1
|
|
692
|
+
assert blocks[0].source == "thinking_content"
|
|
693
|
+
|
|
694
|
+
def test_backward_compatibility_openai_still_works(self):
|
|
695
|
+
"""Existing OpenAI-only tests must still pass."""
|
|
696
|
+
response = {
|
|
697
|
+
"choices": [
|
|
698
|
+
{
|
|
699
|
+
"message": {
|
|
700
|
+
"role": "assistant",
|
|
701
|
+
"content": "Answer.",
|
|
702
|
+
"reasoning_content": "OpenAI reasoning.",
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
]
|
|
706
|
+
}
|
|
707
|
+
blocks = extract_thinking_blocks(response)
|
|
708
|
+
assert len(blocks) == 1
|
|
709
|
+
assert blocks[0].source == "reasoning_content"
|
|
710
|
+
|
|
387
711
|
|
|
388
712
|
# ===========================================================================
|
|
389
713
|
# thinking_to_commit_message
|
|
@@ -401,10 +725,9 @@ class TestThinkingToCommitMessage:
|
|
|
401
725
|
assert result == "auto: (no thinking content)"
|
|
402
726
|
|
|
403
727
|
def test_truncation_at_max_chars(self):
|
|
404
|
-
long_text = "word " * 100
|
|
728
|
+
long_text = "word " * 100
|
|
405
729
|
blocks = [ThinkingBlock(text=long_text)]
|
|
406
730
|
result = thinking_to_commit_message(blocks, max_chars=50)
|
|
407
|
-
# "auto: " is 6 chars, the rest is <= 50 chars of content
|
|
408
731
|
content = result[len("auto: "):]
|
|
409
732
|
assert len(content) <= 50
|
|
410
733
|
|
|
@@ -470,7 +793,6 @@ class TestInferSensitivity:
|
|
|
470
793
|
text = "Update the environment configuration to point to the new endpoint."
|
|
471
794
|
results = infer_sensitivity(text)
|
|
472
795
|
concepts = [r.concept for r in results]
|
|
473
|
-
# Should match at least one of 'configuration' or 'api_interface'
|
|
474
796
|
assert any(c in concepts for c in ("configuration", "api_interface"))
|
|
475
797
|
|
|
476
798
|
def test_empty_text(self):
|
|
@@ -487,7 +809,6 @@ class TestInferSensitivity:
|
|
|
487
809
|
assert results == []
|
|
488
810
|
|
|
489
811
|
def test_max_concepts_limit(self):
|
|
490
|
-
# This text deliberately contains keywords for multiple concepts.
|
|
491
812
|
text = (
|
|
492
813
|
"The database migration affects the JWT auth token and the "
|
|
493
814
|
"deploy pipeline and the cache layer."
|
|
@@ -534,7 +855,6 @@ class TestExtractDecisionSummary:
|
|
|
534
855
|
)
|
|
535
856
|
result = extract_decision_summary(text)
|
|
536
857
|
assert result.startswith("auto: ")
|
|
537
|
-
# Markdown markers should be stripped
|
|
538
858
|
assert "##" not in result
|
|
539
859
|
assert "**" not in result
|
|
540
860
|
assert "_" not in result
|
|
@@ -545,9 +865,7 @@ class TestExtractDecisionSummary:
|
|
|
545
865
|
"The above code prints hello."
|
|
546
866
|
)
|
|
547
867
|
result = extract_decision_summary(text)
|
|
548
|
-
# Python syntax from inside the fence must be gone
|
|
549
868
|
assert "print('hello')" not in result
|
|
550
|
-
# The plain-English sentence following the fence should be captured
|
|
551
869
|
assert "prints hello" in result or result.startswith("auto: ")
|
|
552
870
|
|
|
553
871
|
def test_very_long_text_truncated(self):
|
|
@@ -575,7 +893,6 @@ class TestExtractDecisionSummary:
|
|
|
575
893
|
"We chose the first option."
|
|
576
894
|
)
|
|
577
895
|
result = extract_decision_summary(text)
|
|
578
|
-
# Bullet markers should not appear
|
|
579
896
|
assert "- " not in result
|
|
580
897
|
|
|
581
898
|
def test_numbered_list_stripped(self):
|
|
@@ -591,5 +908,4 @@ class TestExtractDecisionSummary:
|
|
|
591
908
|
text = "This is\na sentence\nspread over lines."
|
|
592
909
|
result = extract_decision_summary(text)
|
|
593
910
|
assert result.startswith("auto: ")
|
|
594
|
-
# Should contain the content (collapsed)
|
|
595
911
|
assert "\n" not in result
|
|
@@ -193,8 +193,8 @@ class TestMergePull:
|
|
|
193
193
|
result = merge_pull(gcc_dir, remote_delta, conflict_log)
|
|
194
194
|
assert result["counts"]["commits"] == 2
|
|
195
195
|
assert result["conflicts"] == []
|
|
196
|
-
assert (gcc_dir / "commits" / "c001.
|
|
197
|
-
assert (gcc_dir / "commits" / "c002.
|
|
196
|
+
assert (gcc_dir / "commits" / "c001.toon").exists()
|
|
197
|
+
assert (gcc_dir / "commits" / "c002.toon").exists()
|
|
198
198
|
|
|
199
199
|
def test_merge_pull_detects_commit_id_collision(
|
|
200
200
|
self, gcc_dir: Path, conflict_log: _MockConflictLog
|