"""Agent Platform Port (INV-G12 / docs/spec/X15) — the single seam to the platform. Paperclip is the *agent platform*: a replaceable shell, not the core. This module is the ONLY place in the web layer that may import the Paperclip client (``web.paperclip_client`` / ``web.paperclip_api``). ``web/app.py`` — and any future web code — imports the platform operations from HERE, never from the Paperclip modules directly. So the dependency points inward (Ports & Adapters / the Dependency Rule, Cockburn / Martin): swapping the platform means re-implementing this one module, not touching app.py. Enforced by the leak-guard (X15 §4 / R4): no file outside this Port imports ``paperclip_client`` / ``paperclip_api``. Two tiers: • **Lifecycle side-effects** — archive / restore / create a case's project, and the case-status notification — are also exposed under domain names (``archive_case_project`` …). These are the genuine domain events the app emits over a case's lifecycle; prefer them in new code. • **Issue / interaction / comment / agent operations** are re-exported under their existing ``pc_*`` names: they are request/response API calls, not events, so a faithful facade re-export — rather than an artificial "event" — is the honest treatment. Migrating these to domain verbs is a follow-up that can happen as call sites are touched; the import seam (the enforceable part of G12) holds regardless. """ from __future__ import annotations # ── the Paperclip shell — imported ONLY here (the Port is the single seam) ── from web.paperclip_api import ( emit_case_status_webhook, emit_export_complete_webhook, emit_missing_precedent_webhook, pc_request, require_paperclip_db_url, ) from web.agent_telemetry import instrument from web.paperclip_client import ( COMPANIES as PAPERCLIP_COMPANIES, accept_interaction as pc_accept_interaction, archive_project as pc_archive_project, create_project as pc_create_project, create_workflow_issue as pc_create_workflow_issue, get_agent_health as pc_get_agent_health, get_recent_escalations as pc_get_recent_escalations, get_predecessor_context as pc_get_predecessor_context, get_predecessor_for_case as pc_get_predecessor_for_case, get_agents_for_case as pc_get_agents_for_case, get_agents_for_company as pc_get_agents, get_case_issues as pc_get_case_issues, pick_default_comment_target as pc_pick_default_comment_target, get_issue_comments as pc_get_issue_comments, get_issue_interactions as pc_get_issue_interactions, get_project_url, get_run_events as pc_get_run_events, get_run_log as pc_get_run_log, list_live_runs as pc_list_live_runs, reject_interaction as pc_reject_interaction, respond_to_interaction as pc_respond_to_interaction, restore_project as pc_restore_project, update_project_name as pc_update_project_name, get_generation_run_status as pc_get_generation_run_status, # ── raw imports: wrapped with telemetry below (state-affecting ops, #219) ── cancel_interaction as _cancel_interaction, cancel_run as _cancel_run, escalate_issue as _escalate_issue, post_comment as _post_comment, reap_stale_interactions as _reap_stale_interactions, reset_agent_session as _reset_agent_session, reset_case_agents as _reset_case_agents, wake_analyst_for_appraiser_facts as _wake_analyst_for_appraiser_facts, wake_analyst_for_argument_aggregation as _wake_analyst_for_argument_aggregation, wake_analyst_for_protocol_analysis as _wake_analyst_for_protocol_analysis, wake_ceo_agent as _wake_ceo, wake_ceo_for_action as _wake_ceo_for_action, open_ceo_run as _open_ceo_run, wake_ceo_for_feedback_fold as _wake_ceo_for_feedback_fold, wake_curator_for_final as _wake_curator_for_final, wake_for_precedent_extraction as _wake_for_precedent_extraction, ) # ── telemetry-wrapped platform ops (#219 / docs/spec/X15) ─────────────────── # Every state-affecting platform op the Port exposes emits one structured event # through web.agent_telemetry, so recovery-loop behaviour (repeated wakeups, # stranded dispositions) is observable in real time rather than reconstructed # from the Paperclip DB. The wrappers preserve the public ``pc_*`` names and # signatures — call sites in app.py are unchanged. Read-only observability ops # (list_live_runs / get_run_log / get_issue_comments …) are intentionally NOT # instrumented: they neither wake agents nor change disposition state. pc_wake_ceo = instrument("agent.wakeup", role="ceo")(_wake_ceo) pc_wake_ceo_for_action = instrument( "agent.wakeup", role="ceo", keys=("case_number", "company_id", "action"), )(_wake_ceo_for_action) pc_open_ceo_run = instrument( "agent.wakeup", role="ceo", keys=("case_number", "company_id"), )(_open_ceo_run) pc_wake_ceo_for_feedback_fold = instrument( "agent.wakeup", role="ceo", keys=("feedback_id", "category", "block_id"), )(_wake_ceo_for_feedback_fold) pc_wake_curator_for_final = instrument( "agent.wakeup", role="curator", keys=("case_number", "company_id", "task"), )(_wake_curator_for_final) pc_wake_for_precedent_extraction = instrument( "agent.wakeup", role="ceo", keys=("case_law_id", "citation", "practice_area"), )(_wake_for_precedent_extraction) pc_wake_analyst_for_appraiser_facts = instrument( "agent.wakeup", role="analyst", )(_wake_analyst_for_appraiser_facts) pc_wake_analyst_for_argument_aggregation = instrument( "agent.wakeup", role="analyst", )(_wake_analyst_for_argument_aggregation) pc_wake_analyst_for_protocol_analysis = instrument( "agent.wakeup", role="analyst", keys=("case_number", "company_id", "document_id"), )(_wake_analyst_for_protocol_analysis) pc_post_comment = instrument( "agent.comment", keys=("issue_id", "company_id"), )(_post_comment) pc_cancel_interaction = instrument( "interaction.cancelled", keys=("issue_id", "interaction_id"), )(_cancel_interaction) # First-class escalation-to-chair (#218): the loop-safe alternative to leaving an # issue agent-owned+blocked. Emits a severity-tagged event so escalations are # countable per case in the same stream as the wakeups they replace. pc_escalate_issue = instrument( "agent.escalated", keys=("issue_id", "severity", "company_id", "reason"), )(_escalate_issue) pc_reap_stale_interactions = instrument( "interaction.reaped", result_keys=("cancelled",), )(_reap_stale_interactions) pc_cancel_run = instrument("run.cancelled", keys=("run_id",))(_cancel_run) pc_reset_agent_session = instrument( "agent.session_reset", keys=("agent_id",), )(_reset_agent_session) pc_reset_case_agents = instrument( "agent.session_reset", keys=("case_number",), )(_reset_case_agents) # ── domain-named lifecycle aliases (preferred for new call sites) ─────────── archive_case_project = pc_archive_project restore_case_project = pc_restore_project create_case_project = pc_create_project rename_case_project = pc_update_project_name notify_case_status = emit_case_status_webhook __all__ = [ # platform infrastructure "PAPERCLIP_COMPANIES", "pc_request", "require_paperclip_db_url", "get_project_url", # lifecycle — domain names (preferred) + legacy aliases "archive_case_project", "restore_case_project", "create_case_project", "rename_case_project", "notify_case_status", "emit_case_status_webhook", "emit_export_complete_webhook", "emit_missing_precedent_webhook", "pc_archive_project", "pc_restore_project", "pc_create_project", "pc_update_project_name", # issues / workflow "pc_create_workflow_issue", "pc_get_case_issues", "pc_pick_default_comment_target", # agents / wakeups "pc_get_agents_for_case", "pc_get_agents", "pc_wake_ceo", "pc_wake_ceo_for_action", "pc_open_ceo_run", "pc_wake_ceo_for_feedback_fold", "pc_wake_curator_for_final", "pc_wake_for_precedent_extraction", "pc_wake_analyst_for_appraiser_facts", "pc_wake_analyst_for_argument_aggregation", "pc_wake_analyst_for_protocol_analysis", "pc_get_generation_run_status", # comments / interactions "pc_post_comment", "pc_get_issue_comments", "pc_get_issue_interactions", "pc_accept_interaction", "pc_reject_interaction", "pc_respond_to_interaction", "pc_cancel_interaction", "pc_reap_stale_interactions", "pc_escalate_issue", # agent-run observability + control (live view + smart management) "pc_get_agent_health", "pc_get_recent_escalations", "pc_get_predecessor_context", "pc_get_predecessor_for_case", "pc_list_live_runs", "pc_get_run_log", "pc_get_run_events", "pc_cancel_run", "pc_reset_agent_session", "pc_reset_case_agents", ]