threadkeeper 0.20.0__tar.gz → 0.21.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.
- {threadkeeper-0.20.0/threadkeeper.egg-info → threadkeeper-0.21.0}/PKG-INFO +39 -3
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/README.md +38 -2
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/pyproject.toml +1 -1
- threadkeeper-0.21.0/tests/test_brief_hook_size.py +69 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_brief_sections.py +43 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_curator.py +164 -90
- threadkeeper-0.21.0/tests/test_dialectic_drift_eval.py +97 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_evolve_applier.py +13 -1
- threadkeeper-0.21.0/tests/test_evolve_apply_48.py +92 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_forget.py +66 -0
- threadkeeper-0.21.0/tests/test_lesson_index.py +76 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_memory_eval.py +33 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_slim.py +104 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_watchdog.py +59 -10
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/_setup.py +10 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/authority.py +4 -2
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/brief.py +53 -1
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/curator.py +295 -149
- threadkeeper-0.21.0/threadkeeper/dialectic_drift_eval.py +321 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/dialectic_validator.py +5 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/forget.py +91 -1
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/instructions.py +5 -2
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/lessons.py +49 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/spawn_budget.py +38 -45
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/curator.py +51 -18
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/spawn.py +62 -2
- {threadkeeper-0.20.0 → threadkeeper-0.21.0/threadkeeper.egg-info}/PKG-INFO +39 -3
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper.egg-info/SOURCES.txt +5 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/LICENSE +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/setup.cfg +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_adapters.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_agent_status.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_antigravity_ingest.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_auto_update.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_backup.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_brief_footprint.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_candidate_reviewer.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_ci_parallel.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_concepts.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_config_settings.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_config_watcher.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_core_memory.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_daemon_host_config.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_daemon_host_integration.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_daemon_liveness.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_daemon_state.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dashboard.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dashboard_app.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_db_compact.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_db_contention.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_db_leak_guard.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_db_migration_fts.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_db_runtime.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_db_schema.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_delegated_search.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dialectic.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dialectic_feed_tools.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dialectic_miner.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dialectic_observation_resolve.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dialectic_recompute.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dialectic_tier.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dialectic_validator.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_dialog_fts_external.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_documentation_inventory.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_egress_policy.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_embed_routing.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_embedding_generation.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_embedding_model_revision.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_embedding_storage_dedup.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_error_paths.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_eval_harness.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_evolve_apply_2.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_evolve_apply_3.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_evolve_apply_43.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_evolve_apply_45.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_evolve_daemon.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_extract_daemon.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_extract_dedup.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_github_budget.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_github_safety.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_helpers.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_hook_child_guard.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_host_embed.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_host_env.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_host_main.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_host_wedged.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_hybrid_retrieval.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_i18n_multilang.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_identity.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_ingest_cursor.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_ingest_denylist.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_ingest_secret_redaction.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_ingest_status.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_ingest_transactions.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_lesson_violations.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_lessons.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_mcp_protocol_conformance.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_mcp_resources_prompts.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_mcp_sdk_compat.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_mcp_server_instructions.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_mcp_skills_extension.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_mcp_tasks.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_memory_authority_lifecycle.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_memory_compatibility.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_memory_guard.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_memory_guard_host.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_menubar_app.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_missed_spawns.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_model_catalog.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_model_router.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_model_router_digest.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_model_router_tools.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_model_routes.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_notify_daemon.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_nudges.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_onnx_embeddings.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_panel.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_permissions.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_pickup.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_probe_daemon.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_process_health.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_project_scope.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_release_workflows.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_retention.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_review_prompts.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_search_fts_punctuation.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_session_read_purity.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_setup.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_shadow_review.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_single_flight_prompt_prefixes.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_skill_hint.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_skill_passive_tier.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_skill_tier.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_skill_updater.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_skill_use_parser.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_skill_watcher.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_skills.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_background_cwd.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_budget.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_child_env.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_codex_stdin.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_config.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_hint.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_permission_gate.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_reap.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_result.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_worktree_isolation.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_spawn_wrap.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_storage.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_sync_capture.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_sync_identity.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_sync_migrate.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_sync_protocol.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_sync_reset_node.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_sync_transport.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_task_retention.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_task_spool.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_thin_session.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_thread_janitor.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_threads.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_tool_annotations.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_tool_profiles.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_tools_smoke.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_tracing.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_validate_threads.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_vec_search.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_verify_ingest.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/tests/test_workflow_retention.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/__init__.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/_mcp.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/_spawn_wrap.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/adapters/__init__.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/adapters/_hook_helpers.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/adapters/antigravity.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/adapters/base.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/adapters/claude_code.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/adapters/claude_desktop.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/adapters/codex.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/adapters/copilot.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/adapters/vscode.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/agent_metadata.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/agent_status.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/assets/macos-agent-status/Info.plist +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/assets/macos-agent-status/README.md +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/assets/macos-agent-status/ThreadKeeperAgentStatus.swift +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/assets/macos-agent-status/build.sh +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/assets/macos-agent-status/install.sh +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/auto_update.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/backup.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/candidate_reviewer.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/config.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/config_io.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/config_watcher.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/curator_snapshots.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/daemon_liveness.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/daemon_state.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/daemon_supervisor.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/dashboard_app.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/db.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/dialectic_miner.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/egress.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/elicitation.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/embeddings.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/eval/__init__.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/eval/__main__.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/eval/harness.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/evolve_applier.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/evolve_daemon.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/extract_daemon.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/github_budget.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/github_safety.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/harvest.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/helpers.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/host.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/host_embed.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/i18n.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/identity.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/ingest.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/lesson_violations.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/link_health.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/mcp_skills.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/mcp_tasks.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/memory_compat.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/memory_guard.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/menubar_app.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/migrate_embeddings.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/model_catalog.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/model_router.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/model_router_digest.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/model_routes.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/notify.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/nudges.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/permissions.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/probe_daemon.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/process_health.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/project_scope.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/protocol.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/read_context.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/retention.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/retrieval.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/review_prompts.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/search_proxy.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/server.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/shadow_review.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/skill_audit.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/skill_updater.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/skill_watcher.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/spawn_config.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/spawn_result.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/storage.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/sync/__init__.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/sync/capture.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/sync/daemon.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/sync/identity.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/sync/migrate.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/sync/protocol.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/sync/reset_node.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/sync/server.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/task_spool.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/thread_janitor.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tool_profiles.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tool_schemas.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/__init__.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/agent_status.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/authority.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/candidate_reviewer.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/capabilities.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/concepts.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/config_watch.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/consolidate.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/core_memory.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/correlation.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/dashboard.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/db_maintenance.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/dialectic.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/dialectic_feed.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/dialog.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/distill.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/evolve_applier.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/evolve_research.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/extract.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/forget.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/graph.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/invariants.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/lessons.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/memory_guard.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/missed_spawns.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/model_router.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/panel.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/peers.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/pickup.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/probes.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/process_health.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/project_scope.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/prompts.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/resources.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/session.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/shadow_review.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/skills.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/style.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/sync.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/threads.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tools/validate.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/tracing.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/trash.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/verify_ingest.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/wikilinks.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper/workflow_artifacts.py +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper.egg-info/dependency_links.txt +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper.egg-info/entry_points.txt +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper.egg-info/requires.txt +0 -0
- {threadkeeper-0.20.0 → threadkeeper-0.21.0}/threadkeeper.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: threadkeeper
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.0
|
|
4
4
|
Summary: Multi-agent shared brain across Claude Code/Desktop, Codex, Antigravity CLI, Copilot, and VS Code. Cross-session memory, self-improving skill loops, inter-agent signaling — one local MCP server.
|
|
5
5
|
Author: thread-keeper contributors
|
|
6
6
|
License: MIT
|
|
@@ -972,8 +972,12 @@ recovery snapshot right before its first mutating evaluator.
|
|
|
972
972
|
its token cost); the evaluator then judges currency from local evidence only.
|
|
973
973
|
A batch that holds no skills never gets a researcher: the fixed research topics
|
|
974
974
|
only concern skills, so its evaluator starts at once.
|
|
975
|
-
|
|
976
|
-
|
|
975
|
+
Curator pass and snapshot values are explicit, per-child launch data rather
|
|
976
|
+
than temporary server-process environment. Ordinary foreground and daemon
|
|
977
|
+
spawns strip stale inherited values. The private launcher binds each task,
|
|
978
|
+
child identity, pass, batch, phase, role, origin, and exact
|
|
979
|
+
report/research destination before launch; a watchdog retry restores only that
|
|
980
|
+
recorded data.
|
|
977
981
|
`curator_report_write` records its SHA-256 in `curator_report_provenance`.
|
|
978
982
|
This makes the report directory an untrusted transport: a stray or forged
|
|
979
983
|
`REPORT-*.md` file cannot acquire the provenance needed by the applier.
|
|
@@ -1055,6 +1059,18 @@ within an 8-second budget per call; a call that runs out falls back to the
|
|
|
1055
1059
|
lexical ranking and the next call resumes. Each line shows the lesson's summary. Without a query a session
|
|
1056
1060
|
would only ever see the newest few lessons, whatever the task.
|
|
1057
1061
|
|
|
1062
|
+
**Lesson index.** Agents rarely search lessons on their own, so every lesson
|
|
1063
|
+
write also refreshes `lesson-index.md` next to `lessons.md`
|
|
1064
|
+
(`~/.threadkeeper/lesson-index.md` by default), a sorted list of all lesson
|
|
1065
|
+
titles (about 35 000 characters for 550 lessons). The managed
|
|
1066
|
+
instructions import it (`@~/.threadkeeper/lesson-index.md`), so a CLI that
|
|
1067
|
+
supports imports, such as Claude Code, loads every title with its instructions;
|
|
1068
|
+
other CLIs are told to read the file at session start. Each title is the rule,
|
|
1069
|
+
and the agent opens the ones that apply with `lesson_get`. On 160 real user
|
|
1070
|
+
prompts an agent that saw only this index opened 65-70% of the lessons judged
|
|
1071
|
+
relevant, and 51-59% of its picks were relevant. Injecting the lessons whose
|
|
1072
|
+
embedding is closest to the prompt reached 25% recall at 12% precision.
|
|
1073
|
+
|
|
1058
1074
|
Lesson access is tracked the same way skill access is: `lesson_list` increments
|
|
1059
1075
|
`lesson_usage.view_count` for displayed rows and `lesson_get` increments
|
|
1060
1076
|
`lesson_usage.use_count` for the returned lesson. Only foreground reads count:
|
|
@@ -1448,6 +1464,13 @@ claim and its evidence but closes the old valid-time interval at the new claim's
|
|
|
1448
1464
|
slice; `dialectic_review(as_of=...)` and
|
|
1449
1465
|
`dialectic_synthesis(include_history=True)` expose past validity intervals.
|
|
1450
1466
|
|
|
1467
|
+
**Durable-preference drift pilot.** Run
|
|
1468
|
+
`python -m threadkeeper.dialectic_drift_eval --json` to replay the fixed,
|
|
1469
|
+
anonymized durable-change / task-exception / ambiguity / poisoning corpus. It
|
|
1470
|
+
reports supersession precision and recall, false durable-flip and clarification
|
|
1471
|
+
rates, poisoning containment, cited memory IDs, and counterfactuals. The pilot
|
|
1472
|
+
is read-only and does not change the live supersession policy.
|
|
1473
|
+
|
|
1451
1474
|
**Source-based evidence discount.** Each evidence row's effective weight
|
|
1452
1475
|
is `base_weight × discount(WRITE_ORIGIN)`. Foreground (direct user / human
|
|
1453
1476
|
signal) = 1.0. shadow_review / background_review / candidate_review /
|
|
@@ -2251,6 +2274,12 @@ It reports these headline groups over a fixed ground-truth set:
|
|
|
2251
2274
|
- **retrieval latency** — mean / p50 / p95 / max wall-clock milliseconds. With
|
|
2252
2275
|
`--semantic`, the backend is reported as `hybrid`, because dense candidates
|
|
2253
2276
|
augment rather than replace FTS.
|
|
2277
|
+
- **forgetting lifecycle** — the bundled mutation fixture writes a fact,
|
|
2278
|
+
corrects it twice, supersedes its dialectic claim, consolidates duplicate
|
|
2279
|
+
verbatim evidence, forgets the old source, and reopens the store in a new
|
|
2280
|
+
process. It reports a separate score for thread notes, verbatim memory,
|
|
2281
|
+
dialectic claims, and lessons or skills.
|
|
2282
|
+
|
|
2254
2283
|
- **outcome classification** — retrieval failure, reasoning failure,
|
|
2255
2284
|
stale-memory use, removed-memory use, and privacy-policy violation.
|
|
2256
2285
|
|
|
@@ -2286,6 +2315,13 @@ retrieved context, or replayed answer is written to stdout, artifacts, or the
|
|
|
2286
2315
|
repository. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the corpus
|
|
2287
2316
|
contract and migration-gate interpretation.
|
|
2288
2317
|
|
|
2318
|
+
The lifecycle score gives current evidence `+1` and subtracts `0.5` for every
|
|
2319
|
+
obsolete or invalidated fact that the same retrieval path returns. A current
|
|
2320
|
+
fact alongside stale evidence therefore does not receive full credit. The
|
|
2321
|
+
post-forgetting and post-restart stages must score `1.0` in both deterministic
|
|
2322
|
+
FTS and hybrid variants; the JSON report names the lifecycle stage and memory
|
|
2323
|
+
class for any release-gate failure.
|
|
2324
|
+
|
|
2289
2325
|
## Evaluating learning-loop decision quality
|
|
2290
2326
|
|
|
2291
2327
|
`verify_ingest` answers *"did we capture the data?"*. The decision-quality
|
|
@@ -925,8 +925,12 @@ recovery snapshot right before its first mutating evaluator.
|
|
|
925
925
|
its token cost); the evaluator then judges currency from local evidence only.
|
|
926
926
|
A batch that holds no skills never gets a researcher: the fixed research topics
|
|
927
927
|
only concern skills, so its evaluator starts at once.
|
|
928
|
-
|
|
929
|
-
|
|
928
|
+
Curator pass and snapshot values are explicit, per-child launch data rather
|
|
929
|
+
than temporary server-process environment. Ordinary foreground and daemon
|
|
930
|
+
spawns strip stale inherited values. The private launcher binds each task,
|
|
931
|
+
child identity, pass, batch, phase, role, origin, and exact
|
|
932
|
+
report/research destination before launch; a watchdog retry restores only that
|
|
933
|
+
recorded data.
|
|
930
934
|
`curator_report_write` records its SHA-256 in `curator_report_provenance`.
|
|
931
935
|
This makes the report directory an untrusted transport: a stray or forged
|
|
932
936
|
`REPORT-*.md` file cannot acquire the provenance needed by the applier.
|
|
@@ -1008,6 +1012,18 @@ within an 8-second budget per call; a call that runs out falls back to the
|
|
|
1008
1012
|
lexical ranking and the next call resumes. Each line shows the lesson's summary. Without a query a session
|
|
1009
1013
|
would only ever see the newest few lessons, whatever the task.
|
|
1010
1014
|
|
|
1015
|
+
**Lesson index.** Agents rarely search lessons on their own, so every lesson
|
|
1016
|
+
write also refreshes `lesson-index.md` next to `lessons.md`
|
|
1017
|
+
(`~/.threadkeeper/lesson-index.md` by default), a sorted list of all lesson
|
|
1018
|
+
titles (about 35 000 characters for 550 lessons). The managed
|
|
1019
|
+
instructions import it (`@~/.threadkeeper/lesson-index.md`), so a CLI that
|
|
1020
|
+
supports imports, such as Claude Code, loads every title with its instructions;
|
|
1021
|
+
other CLIs are told to read the file at session start. Each title is the rule,
|
|
1022
|
+
and the agent opens the ones that apply with `lesson_get`. On 160 real user
|
|
1023
|
+
prompts an agent that saw only this index opened 65-70% of the lessons judged
|
|
1024
|
+
relevant, and 51-59% of its picks were relevant. Injecting the lessons whose
|
|
1025
|
+
embedding is closest to the prompt reached 25% recall at 12% precision.
|
|
1026
|
+
|
|
1011
1027
|
Lesson access is tracked the same way skill access is: `lesson_list` increments
|
|
1012
1028
|
`lesson_usage.view_count` for displayed rows and `lesson_get` increments
|
|
1013
1029
|
`lesson_usage.use_count` for the returned lesson. Only foreground reads count:
|
|
@@ -1401,6 +1417,13 @@ claim and its evidence but closes the old valid-time interval at the new claim's
|
|
|
1401
1417
|
slice; `dialectic_review(as_of=...)` and
|
|
1402
1418
|
`dialectic_synthesis(include_history=True)` expose past validity intervals.
|
|
1403
1419
|
|
|
1420
|
+
**Durable-preference drift pilot.** Run
|
|
1421
|
+
`python -m threadkeeper.dialectic_drift_eval --json` to replay the fixed,
|
|
1422
|
+
anonymized durable-change / task-exception / ambiguity / poisoning corpus. It
|
|
1423
|
+
reports supersession precision and recall, false durable-flip and clarification
|
|
1424
|
+
rates, poisoning containment, cited memory IDs, and counterfactuals. The pilot
|
|
1425
|
+
is read-only and does not change the live supersession policy.
|
|
1426
|
+
|
|
1404
1427
|
**Source-based evidence discount.** Each evidence row's effective weight
|
|
1405
1428
|
is `base_weight × discount(WRITE_ORIGIN)`. Foreground (direct user / human
|
|
1406
1429
|
signal) = 1.0. shadow_review / background_review / candidate_review /
|
|
@@ -2204,6 +2227,12 @@ It reports these headline groups over a fixed ground-truth set:
|
|
|
2204
2227
|
- **retrieval latency** — mean / p50 / p95 / max wall-clock milliseconds. With
|
|
2205
2228
|
`--semantic`, the backend is reported as `hybrid`, because dense candidates
|
|
2206
2229
|
augment rather than replace FTS.
|
|
2230
|
+
- **forgetting lifecycle** — the bundled mutation fixture writes a fact,
|
|
2231
|
+
corrects it twice, supersedes its dialectic claim, consolidates duplicate
|
|
2232
|
+
verbatim evidence, forgets the old source, and reopens the store in a new
|
|
2233
|
+
process. It reports a separate score for thread notes, verbatim memory,
|
|
2234
|
+
dialectic claims, and lessons or skills.
|
|
2235
|
+
|
|
2207
2236
|
- **outcome classification** — retrieval failure, reasoning failure,
|
|
2208
2237
|
stale-memory use, removed-memory use, and privacy-policy violation.
|
|
2209
2238
|
|
|
@@ -2239,6 +2268,13 @@ retrieved context, or replayed answer is written to stdout, artifacts, or the
|
|
|
2239
2268
|
repository. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the corpus
|
|
2240
2269
|
contract and migration-gate interpretation.
|
|
2241
2270
|
|
|
2271
|
+
The lifecycle score gives current evidence `+1` and subtracts `0.5` for every
|
|
2272
|
+
obsolete or invalidated fact that the same retrieval path returns. A current
|
|
2273
|
+
fact alongside stale evidence therefore does not receive full credit. The
|
|
2274
|
+
post-forgetting and post-restart stages must score `1.0` in both deterministic
|
|
2275
|
+
FTS and hybrid variants; the JSON report names the lifecycle stage and memory
|
|
2276
|
+
class for any release-gate failure.
|
|
2277
|
+
|
|
2242
2278
|
## Evaluating learning-loop decision quality
|
|
2243
2279
|
|
|
2244
2280
|
`verify_ingest` answers *"did we capture the data?"*. The decision-quality
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "threadkeeper"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.21.0"
|
|
8
8
|
description = "Multi-agent shared brain across Claude Code/Desktop, Codex, Antigravity CLI, Copilot, and VS Code. Cross-session memory, self-improving skill loops, inter-agent signaling — one local MCP server."
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
authors = [{ name = "thread-keeper contributors" }]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""The SessionStart brief stays under the CLI's hook output limit."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
HOOK = Path(__file__).resolve().parents[1] / "scripts" / "hooks" / "tk-brief.sh"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _run_hook(tmp_path: Path, context_lines: int) -> str:
|
|
13
|
+
fake_python = tmp_path / "python"
|
|
14
|
+
body = "\\n".join(f"rule {i}: " + "x" * 60 for i in range(context_lines))
|
|
15
|
+
fake_python.write_text(
|
|
16
|
+
"#!/bin/sh\ncat >/dev/null\n"
|
|
17
|
+
f"printf 'STATUS\\tthread-keeper: ok\\nCONTEXT\\t{body}\\n'\n",
|
|
18
|
+
encoding="utf-8",
|
|
19
|
+
)
|
|
20
|
+
fake_python.chmod(0o700)
|
|
21
|
+
env = {
|
|
22
|
+
"PATH": os.environ.get("PATH", "/usr/bin:/bin"),
|
|
23
|
+
"HOME": str(tmp_path),
|
|
24
|
+
"THREADKEEPER_PYTHON": str(fake_python),
|
|
25
|
+
}
|
|
26
|
+
proc = subprocess.run(
|
|
27
|
+
["bash", str(HOOK)], input="{}", capture_output=True, text=True, env=env, timeout=30,
|
|
28
|
+
)
|
|
29
|
+
assert proc.returncode == 0, proc.stderr
|
|
30
|
+
return json.loads(proc.stdout)["hookSpecificOutput"]["additionalContext"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_oversized_brief_is_cut_at_a_line_break_under_the_hook_limit(tmp_path):
|
|
34
|
+
context = _run_hook(tmp_path, context_lines=400)
|
|
35
|
+
|
|
36
|
+
assert len(context) <= 9000
|
|
37
|
+
assert context.endswith("call brief() for the rest]")
|
|
38
|
+
assert "\nrule 0: " in context
|
|
39
|
+
kept = context.splitlines()[-2]
|
|
40
|
+
assert kept.startswith("rule ") and kept.endswith("x" * 60)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_brief_under_the_limit_is_passed_through_whole(tmp_path):
|
|
44
|
+
context = _run_hook(tmp_path, context_lines=20)
|
|
45
|
+
|
|
46
|
+
assert "rule 19: " in context
|
|
47
|
+
assert "trimmed" not in context
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_brief_without_line_breaks_is_cut_at_the_limit(tmp_path):
|
|
51
|
+
fake_python = tmp_path / "python"
|
|
52
|
+
fake_python.write_text(
|
|
53
|
+
"#!/bin/sh\ncat >/dev/null\n"
|
|
54
|
+
"printf 'STATUS\\tok\\nCONTEXT\\t%s\\n' \"$(head -c 20000 /dev/zero | tr '\\0' x)\"\n",
|
|
55
|
+
encoding="utf-8",
|
|
56
|
+
)
|
|
57
|
+
fake_python.chmod(0o700)
|
|
58
|
+
env = {
|
|
59
|
+
"PATH": os.environ.get("PATH", "/usr/bin:/bin"),
|
|
60
|
+
"HOME": str(tmp_path),
|
|
61
|
+
"THREADKEEPER_PYTHON": str(fake_python),
|
|
62
|
+
}
|
|
63
|
+
proc = subprocess.run(
|
|
64
|
+
["bash", str(HOOK)], input="{}", capture_output=True, text=True, env=env, timeout=30,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
context = json.loads(proc.stdout)["hookSpecificOutput"]["additionalContext"]
|
|
68
|
+
assert 8000 < len(context) <= 9000
|
|
69
|
+
assert context.endswith("call brief() for the rest]")
|
|
@@ -146,6 +146,49 @@ def test_consulted_skills_silent_without_events(mp_with_cid):
|
|
|
146
146
|
assert "consulted_skills" not in txt
|
|
147
147
|
|
|
148
148
|
|
|
149
|
+
def test_consulted_lessons_surface_recall_telemetry_and_keep_brief_sections(
|
|
150
|
+
mp_with_cid,
|
|
151
|
+
):
|
|
152
|
+
"""A recalled lesson keeps its read telemetry visible without dropping
|
|
153
|
+
the existing thread, evolve, or user-facing brief sections."""
|
|
154
|
+
pkg = mp_with_cid(_FAKE_CID)
|
|
155
|
+
lesson_append = _tool(pkg, "lesson_append")
|
|
156
|
+
lesson_list = _tool(pkg, "lesson_list")
|
|
157
|
+
lesson_get = _tool(pkg, "lesson_get")
|
|
158
|
+
open_thread = _tool(pkg, "open_thread")
|
|
159
|
+
|
|
160
|
+
assert lesson_append(
|
|
161
|
+
title="Recall telemetry", body="Use this lesson for brief coverage.",
|
|
162
|
+
source="shadow",
|
|
163
|
+
).startswith("ok")
|
|
164
|
+
lesson_list(k=1)
|
|
165
|
+
assert "Use this lesson for brief coverage." in lesson_get(
|
|
166
|
+
slug="recall-telemetry"
|
|
167
|
+
)
|
|
168
|
+
open_thread(question="keep the open section")
|
|
169
|
+
|
|
170
|
+
conn = pkg["db"].get_db()
|
|
171
|
+
conn.execute(
|
|
172
|
+
"INSERT INTO evolve (suggestion, rationale, applied, status, created_at) "
|
|
173
|
+
"VALUES (?, ?, 0, 'promoted', ?)",
|
|
174
|
+
("seeded promoted suggestion", "brief coverage", int(time.time())),
|
|
175
|
+
)
|
|
176
|
+
conn.commit()
|
|
177
|
+
|
|
178
|
+
txt = _brief_text(pkg)
|
|
179
|
+
|
|
180
|
+
assert "consulted_lessons" in txt
|
|
181
|
+
assert "recall-telemetry" in txt
|
|
182
|
+
assert "viewed×1" in txt
|
|
183
|
+
assert "used×1" in txt
|
|
184
|
+
assert "last_recalled=" in txt
|
|
185
|
+
assert "open" in txt
|
|
186
|
+
assert "keep the open section" in txt
|
|
187
|
+
assert "evolve_pending" in txt
|
|
188
|
+
assert "★ \"seeded promoted suggestion\"" in txt
|
|
189
|
+
assert "⚠️ user-facing: paraphrase plain." in txt
|
|
190
|
+
|
|
191
|
+
|
|
149
192
|
def test_skill_nudge_silent_after_materialization(mp_with_cid, monkeypatch):
|
|
150
193
|
monkeypatch.setenv("THREADKEEPER_SKILL_NUDGE_INTERVAL", "3")
|
|
151
194
|
pkg = mp_with_cid(_FAKE_CID)
|