heylead 0.10.332__tar.gz → 0.10.334__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.
- {heylead-0.10.332 → heylead-0.10.334}/AGENTS.md +2 -2
- {heylead-0.10.332 → heylead-0.10.334}/PKG-INFO +1 -1
- {heylead-0.10.332 → heylead-0.10.334}/SKILL.md +2 -2
- {heylead-0.10.332 → heylead-0.10.334}/clawhub/SKILL.md +2 -2
- {heylead-0.10.332 → heylead-0.10.334}/clawhub.json +1 -1
- heylead-0.10.334/docs/superpowers/plans/2026-09-09-agent-commons-heartbeat.md +53 -0
- heylead-0.10.334/docs/superpowers/plans/2026-09-09-agent-coordinator.md +18 -0
- heylead-0.10.334/docs/superpowers/specs/2026-09-09-agent-commons-heartbeat-design.md +84 -0
- heylead-0.10.334/docs/superpowers/specs/2026-09-09-agent-coordinator-design.md +83 -0
- {heylead-0.10.332 → heylead-0.10.334}/pyproject.toml +1 -1
- {heylead-0.10.332 → heylead-0.10.334}/server.json +2 -2
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/agent_loop.py +16 -1
- heylead-0.10.334/src/heylead/ai/coordinator.py +19 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/schemas.py +35 -4
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/constants.py +6 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/schema.py +44 -1
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/server.py +24 -4
- heylead-0.10.334/src/heylead/services/agent_commons.py +327 -0
- heylead-0.10.334/src/heylead/services/coordinator.py +237 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/hot_lead_closer.py +13 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/icp_research.py +10 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/reply_agent.py +12 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/strategist_replan.py +13 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/edit_campaign.py +10 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/inspect.py +137 -3
- {heylead-0.10.332 → heylead-0.10.334}/.cursorrules +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/.github/workflows/publish.yml +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/.github/workflows/test.yml +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/.gitignore +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/Dockerfile +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/LICENSE +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/README.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/clawhub/clawhub.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/AUDIT-2026-08.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/CLI_AND_DX_IMPROVEMENTS.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/EXPLICIT_IMPROVEMENT_PLAN.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/HANDOFF-2026-08-06.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/QUALITY_AND_SATISFACTION_ASSESSMENT.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/TOOL_TEST_REPORT.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/USER_STORY_TEST.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/product-listing-copy.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/plans/2026-08-07-cloud-sync-push.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/00-shared-context.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/01-first-degree-client.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/02-first-degree-backend-dashboard.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/03-duplicate-sends-hardening.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/04-reply-context-backend.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/05-reply-context-client.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/06-decision-maker-targeting.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/07-kb-port-and-goal-icp-match.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/08-stop-feedback-learning-loop.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/prompts/2026-09-09-quontex-feedback/README.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-08-23-premium-mixed-outreach-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-08-23-unanswered-lead-alerts-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-09-08-campaign-prospect-provenance-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-09-09-edit-campaign-agent-flags-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-09-09-host-ops-playbook-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-09-09-hot-lead-closer-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-09-09-icp-research-agent-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-09-09-inspect-agent-ops-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-09-09-inspect-jobs-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-09-09-reply-exception-agent-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/docs/superpowers/specs/2026-09-09-strategist-replan-agent-design.md +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/llms-full.txt +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/llms.txt +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/scripts/find_query_contracts.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/scripts/fix_stuck_prospects.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/scripts/reactivate_existing_network.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/scripts/rekey_unipile_account.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/scripts/release.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/scripts/seed_shared_directory.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/scripts/test_reaction_live.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/smithery.yaml +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/__main__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/brand_strategist.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/brief_builder.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/comment_generator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/context_summarizer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/draft_guard.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/email_generator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/embeddings.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/evidence.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/followup_generator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/followup_schemas.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/hot_lead_closer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/hume_client.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/hume_voice.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/icp_generator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/icp_generator_v2.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/icp_pipeline.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/icp_research.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/icp_schemas.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/inbound_qualifier.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/ingestion/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/ingestion/chunker.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/ingestion/crawler.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/ingestion/store.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/intent.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/length_fixer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/linkedin_enricher.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/llm.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/llm_router.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/llm_validator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/memory_extractor.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/memory_formatter.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/message_fixer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/message_generator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/message_improver.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/message_validator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/news_service.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/persona_cards.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/post_analyzer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/prompt_loader.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/prospect_analyzer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/referral_extractor.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/reply_agent.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/reply_generator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/reply_pipeline.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/sentiment.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/signal_classifier.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/strategist_prompts.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/strategist_replan.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/strategy_prompts.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/targeting_recheck.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/vector_search.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/voice_analyzer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ai/voice_memo_generator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/author_identity.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/cli/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/cli/log_analyzer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/comment_mining_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/company_follower_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/company_page_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/competitor_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/distributed_post_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/hiring_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/job_change_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/keyword_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/network_post_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/news_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/post_intent_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/profile_view_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/prospect_post_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/collectors/watchlist_web_collector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/config.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/correlation.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/daemon.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/brazil.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/france.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/germany.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/india.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/mexico.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/nigeria.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/philippines.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/poland.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/ukraine.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/united_kingdom.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/data/country_signals/vietnam.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/aio.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/async_bridge.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/backup.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/connection_queries.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/global_contact_queries.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/post_queries.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/queries.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/signal_queries.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/strategist_queries.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/db/strategy_queries.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/flags.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/formatter.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/guardrails.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/api_metrics.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/backend_client.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/chat_lookup_cache.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/circuit_breaker.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/experience.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/messaging.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/profile.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/profile_normalize.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/rate_limiter.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/relations.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/search.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/search_traffic.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/unipile.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/linkedin/voyager_health.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/logging_setup.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/ops_log.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/comment_main.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/filter_candidates.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/followup_fix.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/followup_message.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/followup_message_buy.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/followup_news.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/followup_reasoning.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/followup_reasoning_buy.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/followup_validate.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/followup_validate_buy.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/fragments/voice_rules.txt +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/message_fix.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/message_improve.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_inmail.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_inmail_buy.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_invitation.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_invitation_buy.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_job_search.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_response.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_response_buy.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_system.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_system_buy.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_system_partner.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_system_recruit.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_validate.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/outreach_validate_buy.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/prospect_analysis.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/prompts/voice_analysis.json +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/scheduler/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/scheduler/engine.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/scheduler/enqueue_gate.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/scheduler/executors.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/scheduler/leader.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/scheduler/planner.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/scheduler/trace.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/action_health.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/action_timeline.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/action_verifier.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/anomaly_detector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/brand_service.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/calendar_booker.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/campaign_optimizer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/campaign_refill_service.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/campaign_spawner.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/channel_selector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/cloud_sync.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/communication_strategist.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/company_name.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/connection_sync.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/conversation_enricher.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/daily_digest.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/dedup_service.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/directory_card.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/dm_connection_guard.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/engagement_verifier.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/experiment_service.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/fit_gate.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/health_score.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/hubspot_service.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/icp_match_scorer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/icp_search.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/inbound_helpers.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/inbound_pipeline.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/inbound_policy.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/inbound_relationship.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/inbound_service.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/inbox_match.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/intent_signals.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/live_thread_enroll.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/outreach_channel.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/own_identity.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/own_profile_sync.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/pattern_detector.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/post_research_service.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/process_caches.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/profile_signals.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/project_brief.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/prospect_email.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/prospect_timezone.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/provider_id_repair.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/provider_id_resolver.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/recover_stuck_outreaches.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/referral_enroll.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/revenue_estimator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/search_account_resolver.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/seed_shared_directory.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/session_health.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/signal_activator.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/signal_analytics.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/signal_linker.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/signal_optimizer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/signal_scorer.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/signal_service.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/strategy_engine.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/tabular_reader.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/timezone_resolver.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/unanswered_lead_alerts.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/services/unipile_email.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/textutil.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tier.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/timeutil.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/__init__.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/account.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/analytics.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/archive_campaign.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/backfill_inbox.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/book_meeting.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/brand_strategy.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/campaign.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/campaign_control.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/campaign_report.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/check_replies.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/close_outreach.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/compare_campaigns.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/contacts.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/create_campaign.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/create_post.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/crm_sync.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/delete_campaign.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/delete_message.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/dismiss_lead.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/emergency_stop.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/engage_prospect.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/experiment_loop.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/export_campaign.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/generate_icp.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/generate_send.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/icp.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/import_prospects.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/inbox.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/manage_watchlist.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/network.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/organization.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/partner_followup.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/profile_editor.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/profile_history.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/profile_signals.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/prospect.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/prospect_timeline.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/reply_to_prospect.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/retry_failed.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/scheduler.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/scheduler_status.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/send_email.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/send_followup.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/send_inmail.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/send_message.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/send_voice_memo.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/setup_profile.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/show_conversation.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/show_signals.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/show_status.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/show_strategy.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/signal_report.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/signals.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/skip_prospect.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/suggest_next_action.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/switch_account.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tools/unlink_account.py +0 -0
- {heylead-0.10.332 → heylead-0.10.334}/src/heylead/tracing.py +0 -0
|
@@ -33,7 +33,7 @@ HeyLead is an autonomous LinkedIn SDR (Sales Development Representative) that ru
|
|
|
33
33
|
|
|
34
34
|
## Agent ops
|
|
35
35
|
|
|
36
|
-
When the user asks what the agents did, who is held, or why a reply was skipped, call `inspect()` first. It is read-only and never writes.
|
|
36
|
+
When the user asks what the agents did, who is held, or why a reply was skipped, call `inspect()` first. It is read-only and never writes. If they ask what the agents left for the next tick, what the swarm thinks, or who went dark, call `inspect(action='commons')`.
|
|
37
37
|
|
|
38
38
|
A hold: `prospect(action="conversation", outreach_id="...")` then `send_message(action="reply", outreach_id="...")`. Operator replies skip the reply agent.
|
|
39
39
|
|
|
@@ -41,7 +41,7 @@ Never paste model-authored text as the LinkedIn message. The send tools generate
|
|
|
41
41
|
|
|
42
42
|
Never launch a draft unless the user asked.
|
|
43
43
|
|
|
44
|
-
In-process agents default to observe. Turn act on with `edit_campaign(enable_reply_agent="on")`, `edit_campaign(enable_strategist_replan_agent="on")`, or `edit_campaign(
|
|
44
|
+
In-process agents default to observe. Turn act on with `edit_campaign(enable_reply_agent="on")`, `edit_campaign(enable_strategist_replan_agent="on")`, `edit_campaign(enable_hot_lead_closer="on")`, or `edit_campaign(enable_coordinator_agent="on")`. Use `"observe"` to return to logging-only, `"off"` to disable.
|
|
45
45
|
|
|
46
46
|
## Authentication
|
|
47
47
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: heylead
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.334
|
|
4
4
|
Summary: MCP-native autonomous LinkedIn SDR. Your AI sales rep, one command to fill your pipeline.
|
|
5
5
|
Project-URL: Homepage, https://github.com/D4umak/heylead
|
|
6
6
|
Project-URL: Repository, https://github.com/D4umak/heylead
|
|
@@ -77,7 +77,7 @@ After adding the MCP server, tell your OpenClaw agent:
|
|
|
77
77
|
|
|
78
78
|
## Agent ops
|
|
79
79
|
|
|
80
|
-
When the user asks what the agents did, who is held, or why a reply was skipped, call `inspect()` first. It is read-only and never writes.
|
|
80
|
+
When the user asks what the agents did, who is held, or why a reply was skipped, call `inspect()` first. It is read-only and never writes. If they ask what the agents left for the next tick, what the swarm thinks, or who went dark, call `inspect(action='commons')`.
|
|
81
81
|
|
|
82
82
|
A hold: `prospect(action="conversation", outreach_id="...")` then `send_message(action="reply", outreach_id="...")`. Operator replies skip the reply agent.
|
|
83
83
|
|
|
@@ -85,7 +85,7 @@ Never paste model-authored text as the LinkedIn message. The send tools generate
|
|
|
85
85
|
|
|
86
86
|
Never launch a draft unless the user asked.
|
|
87
87
|
|
|
88
|
-
In-process agents default to observe. Turn act on with `edit_campaign(enable_reply_agent="on")`, `edit_campaign(enable_strategist_replan_agent="on")`, or `edit_campaign(
|
|
88
|
+
In-process agents default to observe. Turn act on with `edit_campaign(enable_reply_agent="on")`, `edit_campaign(enable_strategist_replan_agent="on")`, `edit_campaign(enable_hot_lead_closer="on")`, or `edit_campaign(enable_coordinator_agent="on")`. Use `"observe"` to return to logging-only, `"off"` to disable.
|
|
89
89
|
|
|
90
90
|
## Safety Model
|
|
91
91
|
|
|
@@ -80,7 +80,7 @@ After adding the MCP server, tell your OpenClaw agent:
|
|
|
80
80
|
|
|
81
81
|
## Agent ops
|
|
82
82
|
|
|
83
|
-
When the user asks what the agents did, who is held, or why a reply was skipped, call `inspect()` first. It is read-only and never writes.
|
|
83
|
+
When the user asks what the agents did, who is held, or why a reply was skipped, call `inspect()` first. It is read-only and never writes. If they ask what the agents left for the next tick, what the swarm thinks, or who went dark, call `inspect(action='commons')`.
|
|
84
84
|
|
|
85
85
|
A hold: `prospect(action="conversation", outreach_id="...")` then `send_message(action="reply", outreach_id="...")`. Operator replies skip the reply agent.
|
|
86
86
|
|
|
@@ -88,7 +88,7 @@ Never paste model-authored text as the LinkedIn message. The send tools generate
|
|
|
88
88
|
|
|
89
89
|
Never launch a draft unless the user asked.
|
|
90
90
|
|
|
91
|
-
In-process agents default to observe. Turn act on with `edit_campaign(enable_reply_agent="on")`, `edit_campaign(enable_strategist_replan_agent="on")`, or `edit_campaign(
|
|
91
|
+
In-process agents default to observe. Turn act on with `edit_campaign(enable_reply_agent="on")`, `edit_campaign(enable_strategist_replan_agent="on")`, `edit_campaign(enable_hot_lead_closer="on")`, or `edit_campaign(enable_coordinator_agent="on")`. Use `"observe"` to return to logging-only, `"off"` to disable.
|
|
92
92
|
|
|
93
93
|
## Safety Model
|
|
94
94
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Agent Commons + Heartbeat Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Official inspectable scratch so later ticks can read earlier notes, and a host can see when a sibling agent went dark.
|
|
6
|
+
|
|
7
|
+
**Architecture:** New `agent_commons` table (beat upsert per agent+campaign; append-only notes with 24h expiry). Sidecar `record_agent_beat` after each of the four `run_agent_loop` returns. Kernel passes `reason`/`note` into tools that accept them. `inspect(action='commons')` is read-only. Client SQLite only.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python 3.12, SQLite, existing `run_agent_loop` / `inspect` / pytest (`PYTHONPATH=src python3.12 -m pytest`).
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-09-09-agent-commons-heartbeat-design.md`
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Files
|
|
16
|
+
|
|
17
|
+
- Create: `src/heylead/services/agent_commons.py`
|
|
18
|
+
- Create: `tests/test_agent_commons.py`
|
|
19
|
+
- Modify: `src/heylead/db/schema.py` (`SCHEMA_VERSION` 9→10, `_SCHEMA_SQL`, `_apply_migrations`)
|
|
20
|
+
- Modify: `tests/test_migration_backup_gating.py` (`EXPECTED_MIGRATION_STATEMENTS`)
|
|
21
|
+
- Modify: `src/heylead/ai/agent_loop.py` (pass kwargs into tools)
|
|
22
|
+
- Modify: `src/heylead/ai/schemas.py` (four step enums + `note`)
|
|
23
|
+
- Modify: `src/heylead/services/reply_agent.py`, `strategist_replan.py`, `hot_lead_closer.py`, `icp_research.py`
|
|
24
|
+
- Modify: `src/heylead/tools/inspect.py`, `src/heylead/server.py`
|
|
25
|
+
- Modify: `tests/test_agent_loop.py`, `tests/test_inspect.py`, `tests/test_host_ops_playbook.py`
|
|
26
|
+
- Modify: `AGENTS.md`, `SKILL.md`, `clawhub/SKILL.md`
|
|
27
|
+
|
|
28
|
+
Do not commit unless asked. Do not bump the package version here.
|
|
29
|
+
|
|
30
|
+
### Task 1: Failing tests
|
|
31
|
+
|
|
32
|
+
- [ ] `tests/test_agent_commons.py` — beat upsert, off skips beat, one note per run, expiry, 500-char refuse, inspect commons/agents, inspect does not write
|
|
33
|
+
- [ ] Extend `test_agent_loop.py` — tool receives `reason`/`note`
|
|
34
|
+
- [ ] Extend inspect unknown-action + no-write lists with `commons`
|
|
35
|
+
- [ ] Playbook requires `inspect(action='commons')`
|
|
36
|
+
|
|
37
|
+
### Task 2: Schema + service + kernel
|
|
38
|
+
|
|
39
|
+
- [ ] Table + SCHEMA_VERSION 10
|
|
40
|
+
- [ ] `agent_commons` service
|
|
41
|
+
- [ ] Kernel `_invoke_tool`
|
|
42
|
+
|
|
43
|
+
### Task 3: Hook four loops + inspect + playbook
|
|
44
|
+
|
|
45
|
+
- [ ] Tools + `record_agent_beat` after each loop
|
|
46
|
+
- [ ] `inspect(action='commons')` and agents one-liner
|
|
47
|
+
- [ ] Playbook / MCP instructions / inspect docstring
|
|
48
|
+
|
|
49
|
+
### Task 4: Verify
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
PYTHONPATH=src python3.12 -m pytest tests/test_agent_commons.py tests/test_agent_loop.py tests/test_inspect.py tests/test_host_ops_playbook.py tests/test_migration_backup_gating.py tests/test_reply_agent.py tests/test_icp_research.py tests/test_strategist_replan.py tests/test_hot_lead_closer.py -q
|
|
53
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Agent Coordinator Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** Use executing-plans. Checkbox steps below.
|
|
4
|
+
|
|
5
|
+
**Goal:** After each sibling loop, post an inspectable digest and optionally a coordinator note/hold.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Sidecar upserts `coordinator` beat + one `digest` per campaign. A 2-step kernel loop (off skips the loop) may note or, in act, persist a campaign `hold`. Never calls the other four. Never sends LinkedIn.
|
|
8
|
+
|
|
9
|
+
**Spec:** `docs/superpowers/specs/2026-09-09-agent-coordinator-design.md`
|
|
10
|
+
|
|
11
|
+
- [x] Failing tests in `tests/test_agent_coordinator.py`
|
|
12
|
+
- [x] Schema step + constants + `services/coordinator.py`
|
|
13
|
+
- [x] Hook four siblings via `after_sibling_loop`
|
|
14
|
+
- [x] `edit_campaign(enable_coordinator_agent=...)` + MCP wrapper
|
|
15
|
+
- [x] `inspect` digest / coordinator hold
|
|
16
|
+
- [x] Playbook names coordinator flag + `inspect(action='commons')`
|
|
17
|
+
|
|
18
|
+
Do not commit unless asked. Do not bump the package version here.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Agent commons + heartbeat
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-09
|
|
4
|
+
|
|
5
|
+
The four in-process agents write `actions_log`. They barely read each other, and a host cannot see whether a sibling went dark. This spec adds an official, inspectable scratch — not a covert channel.
|
|
6
|
+
|
|
7
|
+
Inspiration is the May–June 2026 OpenAI wiki swarm (cross-run notes, heartbeat, adapt when the world changes). We copy that *shape*. We do not copy covert GET-writes, fighting the operator, impersonation, or sandbox escape.
|
|
8
|
+
|
|
9
|
+
First slice of a larger program (coordinator and `heylead-api` port come later). Client SQLite only.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- Table `agent_commons` with two kinds of row: `beat` and `note`.
|
|
14
|
+
- Python sidecar upserts a beat after every `run_agent_loop` return (observe or act).
|
|
15
|
+
- Kernel tools `read_commons` and `write_commons` on all four registries. One note per run.
|
|
16
|
+
- `inspect(action='commons')` lists beats, ages, live notes, and stale agents.
|
|
17
|
+
- `inspect(action='agents')` gains one commons/liveness line.
|
|
18
|
+
- Playbook: if they ask what the agents left for the next tick, or who went dark, call `inspect(action='commons')`.
|
|
19
|
+
- Notes expire in 24h. Body ≤ 500 characters.
|
|
20
|
+
- Stale = beat older than 6 hours and that agent is not `off`.
|
|
21
|
+
|
|
22
|
+
## Non-goals
|
|
23
|
+
|
|
24
|
+
- Coordinator agent.
|
|
25
|
+
- Porting loops to `heylead-api`.
|
|
26
|
+
- Syncing commons to the backend.
|
|
27
|
+
- Operator-delete tool (v1: turn the agent `off`, or wait 24h). `inspect` stays read-only.
|
|
28
|
+
- Recreating a forgotten note under a new key (`ZZZ…`).
|
|
29
|
+
- LinkedIn / email / calendar writes from commons.
|
|
30
|
+
- Changing default observe behavior or act flags.
|
|
31
|
+
- New MCP tool.
|
|
32
|
+
|
|
33
|
+
## Data
|
|
34
|
+
|
|
35
|
+
`agent` is `reply` | `strategist` | `closer` | `icp_research`.
|
|
36
|
+
|
|
37
|
+
| kind | uniqueness | fields |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `beat` | one current row per `(campaign_id, agent)` | `decision`, `reason` (≤240 chars), `timestamp`. ICP research uses `campaign_id=""`. |
|
|
40
|
+
| `note` | append | `body`, optional `outreach_id`, `created_at`, `expires_at` (= created + 24h) |
|
|
41
|
+
|
|
42
|
+
`off` does not run the loop, so the beat is not refreshed and ages. That is the liveness signal.
|
|
43
|
+
|
|
44
|
+
## Kernel
|
|
45
|
+
|
|
46
|
+
Today tools are invoked as `tool()` with no arguments. `write_commons` needs the note text.
|
|
47
|
+
|
|
48
|
+
`run_agent_loop` passes `reason` and extras into a tool when the callable accepts them (`reason=`, `note=` from extras, otherwise ignored). Each vertical step schema adds `read_commons` and `write_commons` to its `action` enum, plus a `note` string (empty = use `reason`).
|
|
49
|
+
|
|
50
|
+
`write_commons` refuses a second call in the same run. Empty / overlong body is refused. It writes only to `agent_commons`.
|
|
51
|
+
|
|
52
|
+
`read_commons` returns current beats plus unexpired notes, scoped to the campaign (and outreach when the loop has one).
|
|
53
|
+
|
|
54
|
+
Sidecar `record_agent_beat` runs after the loop returns, including `none` / exhausted. It does not run when the vertical is `off` (loop skipped).
|
|
55
|
+
|
|
56
|
+
## Inspect
|
|
57
|
+
|
|
58
|
+
`inspect(action='commons')` — never writes, no LLM, no LinkedIn.
|
|
59
|
+
|
|
60
|
+
- Last beat per agent: age, last decision, short reason.
|
|
61
|
+
- Live notes (unexpired).
|
|
62
|
+
- Stale line when a beat is older than 6 hours and mode is not `off`.
|
|
63
|
+
|
|
64
|
+
`inspect(action='agents')` adds one line: live note count and the newest beat age (e.g. `commons: 2 live notes, reply beat 12m ago`).
|
|
65
|
+
|
|
66
|
+
Unknown action still lists the valid set (now includes `commons`).
|
|
67
|
+
|
|
68
|
+
## Failure
|
|
69
|
+
|
|
70
|
+
- Query failure → short error; nothing written.
|
|
71
|
+
- `write_commons` refused → tool result string, loop continues.
|
|
72
|
+
- Missing table on old DBs → create via existing schema init (`CREATE TABLE IF NOT EXISTS`).
|
|
73
|
+
|
|
74
|
+
## Tests
|
|
75
|
+
|
|
76
|
+
- Beat upserted after a reply (and strategist / closer / ICP) loop.
|
|
77
|
+
- `off` does not refresh the beat.
|
|
78
|
+
- `write_commons` first call persists; second call in the same run is refused.
|
|
79
|
+
- Expired notes are hidden from `read_commons` and `inspect(action='commons')`.
|
|
80
|
+
- Body over 500 characters is refused.
|
|
81
|
+
- `inspect(action='commons')` does not write.
|
|
82
|
+
- `inspect(action='agents')` mentions commons / beat.
|
|
83
|
+
- MCP `inspect` signature / dispatch includes `commons`.
|
|
84
|
+
- Playbook names `inspect(action='commons')`.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Agent coordinator
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-09
|
|
4
|
+
|
|
5
|
+
Commons + heartbeat shipped (`agent_commons` beats/notes, `inspect(action='commons')`). The four loops still do not post a shared state. This spec adds a fifth vertical: a coordinator that reads that scratch and writes one current digest the host can inspect.
|
|
6
|
+
|
|
7
|
+
Same inspiration as commons (cross-run shared state). Same refusals: no covert channel, no calling the other four, no LinkedIn, no fighting the operator.
|
|
8
|
+
|
|
9
|
+
Client SQLite only. API port stays later.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- After each sibling `run_agent_loop` return (reply, strategist, closer, ICP research), run coordinator. Not when that sibling was `off`. Never after itself.
|
|
14
|
+
- Sidecar always: upsert `coordinator` beat; upsert one `kind=digest` row per `campaign_id` (sibling decisions, live note count, stale flags).
|
|
15
|
+
- 2-step kernel loop when mode is not `off`. Tools: `read_commons`, `read_digest`, `write_commons`. Decisions: `none` | `note` | `hold`.
|
|
16
|
+
- `edit_campaign(enable_coordinator_agent="")` — `on` | `off` | `observe`. Empty = no change. Unset = observe. Same boolean/`observe`-deletes-keys pattern as the other three campaign flags.
|
|
17
|
+
- **observe:** loop may append one extra note; `hold` is logged on the beat only.
|
|
18
|
+
- **act:** `hold` also upserts `kind=hold` on that campaign (campaign-level coordinator hold).
|
|
19
|
+
- **off:** no loop. Digest and beat still refresh.
|
|
20
|
+
- `inspect(action='commons')` shows digest + coordinator beat + coordinator hold.
|
|
21
|
+
- `inspect(action='holds')` lists campaign-level coordinator holds as well as outreach `hold_for_operator` rows.
|
|
22
|
+
- `inspect(action='agents')` one-liner includes the coordinator beat age.
|
|
23
|
+
- Playbook: if they ask what the swarm thinks, or whether a sibling went dark, `inspect(action='commons')`.
|
|
24
|
+
- `VALID_AGENTS` includes `coordinator`. Notes still ≤500 chars, 24h expiry, one `write_commons` per coordinator run.
|
|
25
|
+
|
|
26
|
+
## Non-goals
|
|
27
|
+
|
|
28
|
+
- Invoking reply / strategist / closer / ICP from the coordinator.
|
|
29
|
+
- LinkedIn, email, calendar, or turning sibling flags on/off.
|
|
30
|
+
- Porting to `heylead-api`.
|
|
31
|
+
- New MCP tool.
|
|
32
|
+
- Operator-delete of digest/hold (turn coordinator `off`, or wait for the next sibling to refresh the digest; hold clears when a later act-run decides `none` or `note`).
|
|
33
|
+
|
|
34
|
+
## Trigger
|
|
35
|
+
|
|
36
|
+
Call `maybe_run_coordinator(campaign_id, trigger_agent)` after `record_agent_beat` in the four sibling services.
|
|
37
|
+
|
|
38
|
+
- ICP research uses `campaign_id=""`.
|
|
39
|
+
- Empty `campaign_id`: sidecar still runs; loop defaults to observe; `hold` cannot persist (no campaign). Log only.
|
|
40
|
+
- If the sibling returned without running the loop (`off`), do not call the coordinator.
|
|
41
|
+
|
|
42
|
+
## Data
|
|
43
|
+
|
|
44
|
+
| kind | uniqueness | writer |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `beat` | one current `(campaign_id, agent)` including `coordinator` | sidecar |
|
|
47
|
+
| `digest` | one current `campaign_id` | sidecar (overwrite) |
|
|
48
|
+
| `note` | append, 24h | loop `write_commons` only |
|
|
49
|
+
| `hold` | one current `campaign_id` | act + decision `hold`. Cleared when a later act-run decides `none` or `note`. |
|
|
50
|
+
|
|
51
|
+
Digest body is Python-authored (not model text): agent → last decision, ages, live note count, stale yes/no. Cap ~800 chars.
|
|
52
|
+
|
|
53
|
+
## Kernel
|
|
54
|
+
|
|
55
|
+
`COORDINATOR_AGENT_STEP`: actions `read_commons` | `read_digest` | `write_commons` | `decide`. Decisions `none` | `note` | `hold`. Extra `note` string.
|
|
56
|
+
|
|
57
|
+
Budget: 2 steps, same style as other verticals (small token/timeout constants).
|
|
58
|
+
|
|
59
|
+
`write_commons` is the existing one-per-run tool, agent=`coordinator`.
|
|
60
|
+
|
|
61
|
+
## Inspect
|
|
62
|
+
|
|
63
|
+
- `commons`: digest block, coordinator beat, live notes, STALE lines, coordinator hold if present.
|
|
64
|
+
- `holds`: existing outreach holds, then coordinator holds (`campaign` + reason).
|
|
65
|
+
- `agents`: commons line includes coordinator beat age when present.
|
|
66
|
+
- `inspect` still never writes.
|
|
67
|
+
|
|
68
|
+
## Failure
|
|
69
|
+
|
|
70
|
+
Sibling success must not depend on the coordinator. Coordinator exception → log, skip, sibling outcome unchanged.
|
|
71
|
+
|
|
72
|
+
## Tests
|
|
73
|
+
|
|
74
|
+
- After a reply loop, coordinator beat + digest exist.
|
|
75
|
+
- Sibling `off` does not run coordinator.
|
|
76
|
+
- Coordinator does not re-enter itself.
|
|
77
|
+
- Observe `hold` does not write `kind=hold`; act `hold` does.
|
|
78
|
+
- Act `none`/`note` clears a previous hold.
|
|
79
|
+
- Empty `campaign_id`: no hold row.
|
|
80
|
+
- `write_commons` second call in the same coordinator run refused.
|
|
81
|
+
- `inspect(action='commons')` shows digest; `holds` shows act hold; inspect does not write.
|
|
82
|
+
- `edit_campaign(enable_coordinator_agent=...)` on/off/observe + MCP signature.
|
|
83
|
+
- Playbook names `inspect(action='commons')` for swarm / dark sibling (already true; keep it).
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.D4umak/heylead",
|
|
4
4
|
"title": "HeyLead \u2014 Autonomous LinkedIn SDR",
|
|
5
5
|
"description": "Autonomous LinkedIn SDR \u2014 voice-matched outreach, ICP generation, and campaign management.",
|
|
6
|
-
"version": "0.10.
|
|
6
|
+
"version": "0.10.334",
|
|
7
7
|
"websiteUrl": "https://github.com/D4umak/heylead",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/D4umak/heylead",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{
|
|
14
14
|
"registryType": "pypi",
|
|
15
15
|
"identifier": "heylead",
|
|
16
|
-
"version": "0.10.
|
|
16
|
+
"version": "0.10.334",
|
|
17
17
|
"runtimeHint": "uvx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
@@ -100,7 +100,7 @@ async def run_agent_loop(
|
|
|
100
100
|
transcript += f"\n\nTOOL {action}: unknown tool"
|
|
101
101
|
continue
|
|
102
102
|
try:
|
|
103
|
-
result = tool
|
|
103
|
+
result = _invoke_tool(tool, reason=reason, extras=extras)
|
|
104
104
|
if inspect.isawaitable(result):
|
|
105
105
|
result = await result
|
|
106
106
|
text = str(result)
|
|
@@ -137,3 +137,18 @@ def _parse_step(raw: str, valid_decisions: frozenset[str]) -> dict[str, Any]:
|
|
|
137
137
|
"done": done,
|
|
138
138
|
"extras": extras,
|
|
139
139
|
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _invoke_tool(tool: ToolFn, *, reason: str, extras: dict[str, str]) -> str | Awaitable[str]:
|
|
143
|
+
kwargs: dict[str, str] = {}
|
|
144
|
+
try:
|
|
145
|
+
sig = inspect.signature(tool)
|
|
146
|
+
except (TypeError, ValueError):
|
|
147
|
+
return tool()
|
|
148
|
+
params = sig.parameters
|
|
149
|
+
accepts_var = any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values())
|
|
150
|
+
if accepts_var or "reason" in params:
|
|
151
|
+
kwargs["reason"] = reason
|
|
152
|
+
if accepts_var or "note" in params:
|
|
153
|
+
kwargs["note"] = extras.get("note") or ""
|
|
154
|
+
return tool(**kwargs) if kwargs else tool()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Prompts for the agent coordinator."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
COORDINATOR_SYSTEM = """You coordinate HeyLead in-process agents.
|
|
6
|
+
Read the commons digest, then decide.
|
|
7
|
+
|
|
8
|
+
Decisions:
|
|
9
|
+
- none: sidecar digest is enough
|
|
10
|
+
- note: leave one short note for the next tick
|
|
11
|
+
- hold: a human should look (stale sibling, conflicting notes, or you are unsure)
|
|
12
|
+
|
|
13
|
+
Do not write LinkedIn messages. Do not turn other agents on or off.
|
|
14
|
+
Default to none when nothing is wrong."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_coordinator_context(*, trigger_agent: str, campaign_id: str) -> str:
|
|
18
|
+
camp = campaign_id or "(account)"
|
|
19
|
+
return f"Trigger: {trigger_agent}\nCampaign: {camp}\nRead tools if you need the digest."
|
|
@@ -67,7 +67,10 @@ SENTIMENT = _obj({
|
|
|
67
67
|
AGENT_STEP = _obj({
|
|
68
68
|
"action": {
|
|
69
69
|
"type": "string",
|
|
70
|
-
"enum": [
|
|
70
|
+
"enum": [
|
|
71
|
+
"read_thread", "read_timeline", "read_icp", "read_fit",
|
|
72
|
+
"read_commons", "write_commons", "decide",
|
|
73
|
+
],
|
|
71
74
|
},
|
|
72
75
|
"decision": {
|
|
73
76
|
"type": "string",
|
|
@@ -75,13 +78,17 @@ AGENT_STEP = _obj({
|
|
|
75
78
|
},
|
|
76
79
|
"reason": _STR,
|
|
77
80
|
"done": {"type": "boolean"},
|
|
81
|
+
"note": _STR,
|
|
78
82
|
})
|
|
79
83
|
|
|
80
84
|
# ICP research vertical — extra strings are a flat filter patch (empty = no change).
|
|
81
85
|
ICP_AGENT_STEP = _obj({
|
|
82
86
|
"action": {
|
|
83
87
|
"type": "string",
|
|
84
|
-
"enum": [
|
|
88
|
+
"enum": [
|
|
89
|
+
"read_icp", "preview_search", "read_filters",
|
|
90
|
+
"read_commons", "write_commons", "decide",
|
|
91
|
+
],
|
|
85
92
|
},
|
|
86
93
|
"decision": {
|
|
87
94
|
"type": "string",
|
|
@@ -89,6 +96,7 @@ ICP_AGENT_STEP = _obj({
|
|
|
89
96
|
},
|
|
90
97
|
"reason": _STR,
|
|
91
98
|
"done": {"type": "boolean"},
|
|
99
|
+
"note": _STR,
|
|
92
100
|
"titles_include": _STR,
|
|
93
101
|
"titles_exclude": _STR,
|
|
94
102
|
"locations_include": _STR,
|
|
@@ -101,7 +109,10 @@ ICP_AGENT_STEP = _obj({
|
|
|
101
109
|
REPLAN_AGENT_STEP = _obj({
|
|
102
110
|
"action": {
|
|
103
111
|
"type": "string",
|
|
104
|
-
"enum": [
|
|
112
|
+
"enum": [
|
|
113
|
+
"read_plan", "read_signals", "read_thread", "read_status",
|
|
114
|
+
"read_commons", "write_commons", "decide",
|
|
115
|
+
],
|
|
105
116
|
},
|
|
106
117
|
"decision": {
|
|
107
118
|
"type": "string",
|
|
@@ -109,6 +120,7 @@ REPLAN_AGENT_STEP = _obj({
|
|
|
109
120
|
},
|
|
110
121
|
"reason": _STR,
|
|
111
122
|
"done": {"type": "boolean"},
|
|
123
|
+
"note": _STR,
|
|
112
124
|
"remaining_json": _STR,
|
|
113
125
|
})
|
|
114
126
|
|
|
@@ -116,7 +128,10 @@ REPLAN_AGENT_STEP = _obj({
|
|
|
116
128
|
CLOSER_AGENT_STEP = _obj({
|
|
117
129
|
"action": {
|
|
118
130
|
"type": "string",
|
|
119
|
-
"enum": [
|
|
131
|
+
"enum": [
|
|
132
|
+
"read_thread", "read_contact", "read_calendar_target",
|
|
133
|
+
"read_commons", "write_commons", "decide",
|
|
134
|
+
],
|
|
120
135
|
},
|
|
121
136
|
"decision": {
|
|
122
137
|
"type": "string",
|
|
@@ -124,7 +139,23 @@ CLOSER_AGENT_STEP = _obj({
|
|
|
124
139
|
},
|
|
125
140
|
"reason": _STR,
|
|
126
141
|
"done": {"type": "boolean"},
|
|
142
|
+
"note": _STR,
|
|
127
143
|
"attendee_email": _STR,
|
|
128
144
|
"start_iso": _STR,
|
|
129
145
|
"duration_minutes": _STR,
|
|
130
146
|
})
|
|
147
|
+
|
|
148
|
+
# Coordinator — digest is sidecar-authored; the loop may note or hold.
|
|
149
|
+
COORDINATOR_AGENT_STEP = _obj({
|
|
150
|
+
"action": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"enum": ["read_commons", "read_digest", "write_commons", "decide"],
|
|
153
|
+
},
|
|
154
|
+
"decision": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"enum": ["none", "note", "hold"],
|
|
157
|
+
},
|
|
158
|
+
"reason": _STR,
|
|
159
|
+
"done": {"type": "boolean"},
|
|
160
|
+
"note": _STR,
|
|
161
|
+
})
|
|
@@ -1355,6 +1355,12 @@ HOT_LEAD_CLOSER_RESULT_CHARS = 2000
|
|
|
1355
1355
|
HOT_LEAD_CLOSER_MAX_BOOKS_PER_DAY = 5
|
|
1356
1356
|
HOT_LEAD_CLOSER_VALID_DECISIONS = frozenset({"book", "hold", "none"})
|
|
1357
1357
|
|
|
1358
|
+
COORDINATOR_MAX_STEPS = 2
|
|
1359
|
+
COORDINATOR_MAX_TOKENS = 600
|
|
1360
|
+
COORDINATOR_TIMEOUT_SECONDS = 20
|
|
1361
|
+
COORDINATOR_RESULT_CHARS = 1500
|
|
1362
|
+
COORDINATOR_VALID_DECISIONS = frozenset({"none", "note", "hold"})
|
|
1363
|
+
|
|
1358
1364
|
STRATEGIST_AVAILABLE_ACTIONS = frozenset({
|
|
1359
1365
|
"profile_view",
|
|
1360
1366
|
"follow",
|
|
@@ -35,7 +35,7 @@ _DB_RETRY_BACKOFF = 0.1 # seconds, doubles each attempt
|
|
|
35
35
|
# dedup read it as "already in a campaign" and never let the person back in.
|
|
36
36
|
# 7: outreaches.chat_id (and headline A/B columns) — a v6 stamp skipped the
|
|
37
37
|
# ALTER, so planning died with `no such column: o.chat_id`.
|
|
38
|
-
SCHEMA_VERSION =
|
|
38
|
+
SCHEMA_VERSION = 10 # 10: agent_commons (beats + notes); 9: contacts.timezone
|
|
39
39
|
|
|
40
40
|
# Singleton connection — avoids opening multiple connections per process
|
|
41
41
|
# which causes "database is locked" errors with WAL mode.
|
|
@@ -605,6 +605,24 @@ CREATE TABLE IF NOT EXISTS company_posts_tracked (
|
|
|
605
605
|
);
|
|
606
606
|
CREATE INDEX IF NOT EXISTS idx_cpt_watchlist ON company_posts_tracked(watchlist_id);
|
|
607
607
|
CREATE INDEX IF NOT EXISTS idx_cpt_post_id ON company_posts_tracked(post_id);
|
|
608
|
+
|
|
609
|
+
-- In-process agent scratch (heartbeat + short notes for the next tick)
|
|
610
|
+
CREATE TABLE IF NOT EXISTS agent_commons (
|
|
611
|
+
id TEXT PRIMARY KEY,
|
|
612
|
+
kind TEXT NOT NULL,
|
|
613
|
+
agent TEXT NOT NULL,
|
|
614
|
+
campaign_id TEXT NOT NULL DEFAULT '',
|
|
615
|
+
outreach_id TEXT,
|
|
616
|
+
body TEXT,
|
|
617
|
+
decision TEXT,
|
|
618
|
+
reason TEXT,
|
|
619
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
|
620
|
+
expires_at INTEGER
|
|
621
|
+
);
|
|
622
|
+
CREATE INDEX IF NOT EXISTS idx_agent_commons_beat
|
|
623
|
+
ON agent_commons(campaign_id, agent, kind);
|
|
624
|
+
CREATE INDEX IF NOT EXISTS idx_agent_commons_notes
|
|
625
|
+
ON agent_commons(campaign_id, kind, expires_at);
|
|
608
626
|
"""
|
|
609
627
|
|
|
610
628
|
|
|
@@ -2278,6 +2296,31 @@ def _apply_migrations(conn: sqlite3.Connection) -> None:
|
|
|
2278
2296
|
_reraise_if_migration_interrupted(e)
|
|
2279
2297
|
logger.warning("Migration: outreach dedup failed: %s", e)
|
|
2280
2298
|
|
|
2299
|
+
try:
|
|
2300
|
+
conn.execute("SELECT 1 FROM agent_commons LIMIT 0")
|
|
2301
|
+
except sqlite3.OperationalError as e:
|
|
2302
|
+
_reraise_if_migration_interrupted(e)
|
|
2303
|
+
conn.executescript("""
|
|
2304
|
+
CREATE TABLE IF NOT EXISTS agent_commons (
|
|
2305
|
+
id TEXT PRIMARY KEY,
|
|
2306
|
+
kind TEXT NOT NULL,
|
|
2307
|
+
agent TEXT NOT NULL,
|
|
2308
|
+
campaign_id TEXT NOT NULL DEFAULT '',
|
|
2309
|
+
outreach_id TEXT,
|
|
2310
|
+
body TEXT,
|
|
2311
|
+
decision TEXT,
|
|
2312
|
+
reason TEXT,
|
|
2313
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
|
2314
|
+
expires_at INTEGER
|
|
2315
|
+
);
|
|
2316
|
+
CREATE INDEX IF NOT EXISTS idx_agent_commons_beat
|
|
2317
|
+
ON agent_commons(campaign_id, agent, kind);
|
|
2318
|
+
CREATE INDEX IF NOT EXISTS idx_agent_commons_notes
|
|
2319
|
+
ON agent_commons(campaign_id, kind, expires_at);
|
|
2320
|
+
""")
|
|
2321
|
+
conn.commit()
|
|
2322
|
+
logger.info("Migration: added agent_commons table")
|
|
2323
|
+
|
|
2281
2324
|
|
|
2282
2325
|
def _backfill_global_contacts(conn: sqlite3.Connection) -> None:
|
|
2283
2326
|
"""One-time backfill: create global_contacts from existing campaign contacts.
|
|
@@ -28,6 +28,18 @@ from .ops_log import run_traced
|
|
|
28
28
|
_CHANGELOG = """\
|
|
29
29
|
# HeyLead Changelog
|
|
30
30
|
|
|
31
|
+
## v0.10.334 (2026-09-09)
|
|
32
|
+
- New: after each sibling loop a coordinator upserts a digest and
|
|
33
|
+
beat. inspect(action='commons') shows it; act can persist a
|
|
34
|
+
campaign hold. edit_campaign(enable_coordinator_agent=on).
|
|
35
|
+
Never sends LinkedIn (#279)
|
|
36
|
+
|
|
37
|
+
## v0.10.333 (2026-09-09)
|
|
38
|
+
- New: in-process agents leave an inspectable commons — a heartbeat after
|
|
39
|
+
every loop, and one short note per run for the next tick.
|
|
40
|
+
inspect(action='commons') shows beats, live notes, and who went dark
|
|
41
|
+
(#275)
|
|
42
|
+
|
|
31
43
|
## v0.10.332 (2026-09-09)
|
|
32
44
|
- New: brand_strategy(action="set_headline", focus="...") sets the LinkedIn
|
|
33
45
|
headline to exactly the text you pass. execute and makeover both write a
|
|
@@ -1234,9 +1246,12 @@ mcp = FastMCP(
|
|
|
1234
1246
|
" Never paste model-authored text as the LinkedIn message; the send\n"
|
|
1235
1247
|
" tools generate it. Agents default to observe. Turn act on with\n"
|
|
1236
1248
|
" edit_campaign(enable_reply_agent='on'),\n"
|
|
1237
|
-
" edit_campaign(enable_strategist_replan_agent='on')
|
|
1238
|
-
" edit_campaign(enable_hot_lead_closer='on')
|
|
1239
|
-
"
|
|
1249
|
+
" edit_campaign(enable_strategist_replan_agent='on'),\n"
|
|
1250
|
+
" edit_campaign(enable_hot_lead_closer='on'), or\n"
|
|
1251
|
+
" edit_campaign(enable_coordinator_agent='on'). Use 'observe' to\n"
|
|
1252
|
+
" return to logging-only, 'off' to disable. If they ask what the\n"
|
|
1253
|
+
" agents left for the next tick, what the swarm thinks, or who\n"
|
|
1254
|
+
" went dark, call inspect(action='commons').\n"
|
|
1240
1255
|
" - book_meeting() — put an agreed call on your Google Calendar and invite them\n"
|
|
1241
1256
|
" - suggest_next_action() — AI-recommended next step\n"
|
|
1242
1257
|
" - analytics(action='report') — detailed analytics with outcomes and stale lead warnings\n"
|
|
@@ -2058,6 +2073,7 @@ async def edit_campaign(
|
|
|
2058
2073
|
enable_reply_agent: str = "",
|
|
2059
2074
|
enable_strategist_replan_agent: str = "",
|
|
2060
2075
|
enable_hot_lead_closer: str = "",
|
|
2076
|
+
enable_coordinator_agent: str = "",
|
|
2061
2077
|
engagement_mode: str = "",
|
|
2062
2078
|
max_followups: int = 0,
|
|
2063
2079
|
followup_delay_days: str = "",
|
|
@@ -2114,6 +2130,8 @@ async def edit_campaign(
|
|
|
2114
2130
|
"observe". Empty keeps the current value.
|
|
2115
2131
|
enable_hot_lead_closer: Hot-lead closer: "on", "off", or "observe".
|
|
2116
2132
|
Empty keeps the current value.
|
|
2133
|
+
enable_coordinator_agent: Coordinator digest/hold: "on", "off", or
|
|
2134
|
+
"observe". Empty keeps the current value.
|
|
2117
2135
|
engagement_mode: Engagement style: "auto" (30% react / 70% comment),
|
|
2118
2136
|
"comment_only", or "react_only".
|
|
2119
2137
|
max_followups: Max follow-up messages (1-5). 0 to keep current.
|
|
@@ -2162,6 +2180,7 @@ async def edit_campaign(
|
|
|
2162
2180
|
enable_reply_agent=enable_reply_agent,
|
|
2163
2181
|
enable_strategist_replan_agent=enable_strategist_replan_agent,
|
|
2164
2182
|
enable_hot_lead_closer=enable_hot_lead_closer,
|
|
2183
|
+
enable_coordinator_agent=enable_coordinator_agent,
|
|
2165
2184
|
engagement_mode=engagement_mode,
|
|
2166
2185
|
max_followups=max_followups,
|
|
2167
2186
|
followup_delay_days=followup_delay_days,
|
|
@@ -2294,11 +2313,12 @@ async def inspect(
|
|
|
2294
2313
|
Args:
|
|
2295
2314
|
action: What to show:
|
|
2296
2315
|
"agents" — one-screen digest of holds, replans, closer, skips, jobs (default)
|
|
2297
|
-
"holds" — fresh hold_for_operator rows
|
|
2316
|
+
"holds" — fresh hold_for_operator rows and coordinator campaign holds
|
|
2298
2317
|
"replans" — today's strategist_replan_decision rows
|
|
2299
2318
|
"closer" — today's hot_lead_closer_decision rows
|
|
2300
2319
|
"skips" — recent reply skips (hard gates, cap, dedup, agent skip)
|
|
2301
2320
|
"jobs" — pending scheduler jobs and recent gated-job refusals
|
|
2321
|
+
"commons" — digest, beats, live notes, coordinator hold, stale liveness
|
|
2302
2322
|
campaign_id: Optional campaign filter (full id or prefix).
|
|
2303
2323
|
outreach_id: Optional outreach filter (full id or prefix).
|
|
2304
2324
|
limit: Max rows per slice, 1-100 (default 20).
|