claude-mpm 5.4.85__py3-none-any.whl → 5.6.1__py3-none-any.whl
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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
- claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
- claude_mpm/agents/PM_INSTRUCTIONS.md +101 -703
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +46 -0
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/executor.py +119 -16
- claude_mpm/cli/parsers/base_parser.py +71 -1
- claude_mpm/cli/parsers/commander_parser.py +83 -0
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/startup.py +54 -16
- claude_mpm/cli/startup_display.py +72 -5
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +72 -0
- claude_mpm/commander/adapters/__init__.py +31 -0
- claude_mpm/commander/adapters/base.py +191 -0
- claude_mpm/commander/adapters/claude_code.py +361 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +105 -0
- claude_mpm/commander/api/errors.py +112 -0
- claude_mpm/commander/api/routes/__init__.py +8 -0
- claude_mpm/commander/api/routes/events.py +184 -0
- claude_mpm/commander/api/routes/inbox.py +171 -0
- claude_mpm/commander/api/routes/messages.py +148 -0
- claude_mpm/commander/api/routes/projects.py +271 -0
- claude_mpm/commander/api/routes/sessions.py +215 -0
- claude_mpm/commander/api/routes/work.py +260 -0
- claude_mpm/commander/api/schemas.py +182 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +107 -0
- claude_mpm/commander/chat/commands.py +96 -0
- claude_mpm/commander/chat/repl.py +310 -0
- claude_mpm/commander/config.py +49 -0
- claude_mpm/commander/config_loader.py +115 -0
- claude_mpm/commander/daemon.py +398 -0
- claude_mpm/commander/events/__init__.py +26 -0
- claude_mpm/commander/events/manager.py +332 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +143 -0
- claude_mpm/commander/frameworks/claude_code.py +58 -0
- claude_mpm/commander/frameworks/mpm.py +62 -0
- claude_mpm/commander/inbox/__init__.py +16 -0
- claude_mpm/commander/inbox/dedup.py +128 -0
- claude_mpm/commander/inbox/inbox.py +224 -0
- claude_mpm/commander/inbox/models.py +70 -0
- claude_mpm/commander/instance_manager.py +337 -0
- claude_mpm/commander/llm/__init__.py +6 -0
- claude_mpm/commander/llm/openrouter_client.py +167 -0
- claude_mpm/commander/llm/summarizer.py +70 -0
- claude_mpm/commander/models/__init__.py +18 -0
- claude_mpm/commander/models/events.py +121 -0
- claude_mpm/commander/models/project.py +162 -0
- claude_mpm/commander/models/work.py +214 -0
- claude_mpm/commander/parsing/__init__.py +20 -0
- claude_mpm/commander/parsing/extractor.py +132 -0
- claude_mpm/commander/parsing/output_parser.py +270 -0
- claude_mpm/commander/parsing/patterns.py +100 -0
- claude_mpm/commander/persistence/__init__.py +11 -0
- claude_mpm/commander/persistence/event_store.py +274 -0
- claude_mpm/commander/persistence/state_store.py +309 -0
- claude_mpm/commander/persistence/work_store.py +164 -0
- claude_mpm/commander/polling/__init__.py +13 -0
- claude_mpm/commander/polling/event_detector.py +104 -0
- claude_mpm/commander/polling/output_buffer.py +49 -0
- claude_mpm/commander/polling/output_poller.py +153 -0
- claude_mpm/commander/project_session.py +268 -0
- claude_mpm/commander/proxy/__init__.py +12 -0
- claude_mpm/commander/proxy/formatter.py +89 -0
- claude_mpm/commander/proxy/output_handler.py +191 -0
- claude_mpm/commander/proxy/relay.py +155 -0
- claude_mpm/commander/registry.py +404 -0
- claude_mpm/commander/runtime/__init__.py +10 -0
- claude_mpm/commander/runtime/executor.py +191 -0
- claude_mpm/commander/runtime/monitor.py +316 -0
- claude_mpm/commander/session/__init__.py +6 -0
- claude_mpm/commander/session/context.py +81 -0
- claude_mpm/commander/session/manager.py +59 -0
- claude_mpm/commander/tmux_orchestrator.py +361 -0
- claude_mpm/commander/web/__init__.py +1 -0
- claude_mpm/commander/work/__init__.py +30 -0
- claude_mpm/commander/work/executor.py +189 -0
- claude_mpm/commander/work/queue.py +405 -0
- claude_mpm/commander/workflow/__init__.py +27 -0
- claude_mpm/commander/workflow/event_handler.py +219 -0
- claude_mpm/commander/workflow/notifier.py +146 -0
- claude_mpm/commands/mpm-config.md +8 -0
- claude_mpm/commands/mpm-doctor.md +8 -0
- claude_mpm/commands/mpm-help.md +8 -0
- claude_mpm/commands/mpm-init.md +8 -0
- claude_mpm/commands/mpm-monitor.md +8 -0
- claude_mpm/commands/mpm-organize.md +8 -0
- claude_mpm/commands/mpm-postmortem.md +8 -0
- claude_mpm/commands/mpm-session-resume.md +9 -1
- claude_mpm/commands/mpm-status.md +8 -0
- claude_mpm/commands/mpm-ticket-view.md +8 -0
- claude_mpm/commands/mpm-version.md +8 -0
- claude_mpm/commands/mpm.md +8 -0
- claude_mpm/config/agent_presets.py +8 -7
- claude_mpm/core/config.py +5 -0
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/logger.py +10 -7
- claude_mpm/core/logging_utils.py +4 -2
- claude_mpm/core/output_style_manager.py +15 -5
- claude_mpm/core/unified_config.py +10 -6
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +9 -9
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +250 -11
- claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +69 -5
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +20 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +14 -77
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +30 -6
- claude_mpm/hooks/session_resume_hook.py +85 -1
- claude_mpm/init.py +1 -1
- claude_mpm/scripts/claude-hook-handler.sh +36 -10
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +325 -0
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +106 -16
- claude_mpm/services/pm_skills_deployer.py +259 -87
- claude_mpm/services/skills/git_skill_source_manager.py +51 -2
- claude_mpm/services/skills/selective_skill_deployer.py +114 -16
- claude_mpm/services/skills/skill_discovery_service.py +57 -3
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
- claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
- claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
- claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
- claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
- claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
- claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
- claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm-5.6.1.dist-info/METADATA +391 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/RECORD +244 -145
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
- claude_mpm-5.4.85.dist-info/METADATA +0 -1023
- /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
- /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""REST API routes for event resolution.
|
|
2
|
+
|
|
3
|
+
Provides HTTP endpoints for resolving events and managing event responses.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
from ...events.manager import EventManager
|
|
12
|
+
|
|
13
|
+
router = APIRouter()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_event_manager() -> EventManager:
|
|
17
|
+
"""Dependency to get the global event manager instance.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
The global event manager instance
|
|
21
|
+
|
|
22
|
+
Raises:
|
|
23
|
+
RuntimeError: If event manager is not initialized
|
|
24
|
+
"""
|
|
25
|
+
from ..app import event_manager
|
|
26
|
+
|
|
27
|
+
if event_manager is None:
|
|
28
|
+
raise RuntimeError("Event manager not initialized")
|
|
29
|
+
return event_manager
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_event_handler():
|
|
33
|
+
"""Dependency to get the global event handler instance.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
The global event handler instance
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
RuntimeError: If event handler is not initialized
|
|
40
|
+
"""
|
|
41
|
+
from ..app import event_handler
|
|
42
|
+
|
|
43
|
+
if event_handler is None:
|
|
44
|
+
raise RuntimeError("Event handler not initialized")
|
|
45
|
+
return event_handler
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class EventResponse(BaseModel):
|
|
49
|
+
"""Request model for event resolution.
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
response: User's response to the event
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
response: str
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class EventResolutionResponse(BaseModel):
|
|
59
|
+
"""Response model for event resolution.
|
|
60
|
+
|
|
61
|
+
Attributes:
|
|
62
|
+
event_id: ID of resolved event
|
|
63
|
+
status: Resolution status
|
|
64
|
+
session_resumed: Whether project session was resumed
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
event_id: str
|
|
68
|
+
status: str
|
|
69
|
+
session_resumed: bool
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PendingEventResponse(BaseModel):
|
|
73
|
+
"""Response model for pending event.
|
|
74
|
+
|
|
75
|
+
Attributes:
|
|
76
|
+
event_id: Unique event identifier
|
|
77
|
+
project_id: Project that raised this event
|
|
78
|
+
event_type: Type of event
|
|
79
|
+
priority: Urgency level
|
|
80
|
+
status: Current lifecycle status
|
|
81
|
+
title: Short event summary
|
|
82
|
+
content: Detailed event message
|
|
83
|
+
options: For DECISION_NEEDED events, list of choices
|
|
84
|
+
is_blocking: Whether event blocks progress
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
event_id: str
|
|
88
|
+
project_id: str
|
|
89
|
+
event_type: str
|
|
90
|
+
priority: str
|
|
91
|
+
status: str
|
|
92
|
+
title: str
|
|
93
|
+
content: str
|
|
94
|
+
options: Optional[List[str]]
|
|
95
|
+
is_blocking: bool
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@router.post("/events/{event_id}/resolve", response_model=EventResolutionResponse)
|
|
99
|
+
async def resolve_event(
|
|
100
|
+
event_id: str,
|
|
101
|
+
response: EventResponse,
|
|
102
|
+
event_handler=Depends(get_event_handler),
|
|
103
|
+
) -> EventResolutionResponse:
|
|
104
|
+
"""Resolve an event with user response.
|
|
105
|
+
|
|
106
|
+
Marks the event as resolved and resumes the project session if it was
|
|
107
|
+
paused for this event.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
event_id: ID of event to resolve
|
|
111
|
+
response: User's response to the event
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Resolution status and whether session was resumed
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
HTTPException: If event not found (404) or resolution fails (500)
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
POST /api/events/evt_123/resolve
|
|
121
|
+
{
|
|
122
|
+
"response": "Use authlib for OAuth2"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
Response:
|
|
126
|
+
{
|
|
127
|
+
"event_id": "evt_123",
|
|
128
|
+
"status": "resolved",
|
|
129
|
+
"session_resumed": true
|
|
130
|
+
}
|
|
131
|
+
"""
|
|
132
|
+
try:
|
|
133
|
+
session_resumed = await event_handler.resolve_event(event_id, response.response)
|
|
134
|
+
return EventResolutionResponse(
|
|
135
|
+
event_id=event_id,
|
|
136
|
+
status="resolved",
|
|
137
|
+
session_resumed=session_resumed,
|
|
138
|
+
)
|
|
139
|
+
except KeyError as e:
|
|
140
|
+
raise HTTPException(
|
|
141
|
+
status_code=404, detail=f"Event not found: {event_id}"
|
|
142
|
+
) from e
|
|
143
|
+
except Exception as e:
|
|
144
|
+
raise HTTPException(
|
|
145
|
+
status_code=500, detail=f"Failed to resolve event: {e!s}"
|
|
146
|
+
) from e
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@router.get("/events/pending", response_model=List[PendingEventResponse])
|
|
150
|
+
async def get_pending_events(
|
|
151
|
+
project_id: Optional[str] = Query(None, description="Filter by project ID"),
|
|
152
|
+
event_handler=Depends(get_event_handler),
|
|
153
|
+
) -> List[PendingEventResponse]:
|
|
154
|
+
"""Get pending events requiring resolution.
|
|
155
|
+
|
|
156
|
+
Returns all unresolved events, optionally filtered by project.
|
|
157
|
+
Events are sorted by priority (high to low) then created time (old to new).
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
project_id: If provided, only return events for this project
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
List of pending events
|
|
164
|
+
|
|
165
|
+
Example:
|
|
166
|
+
GET /api/events/pending
|
|
167
|
+
GET /api/events/pending?project_id=proj_123
|
|
168
|
+
"""
|
|
169
|
+
events = await event_handler.get_pending_events(project_id)
|
|
170
|
+
|
|
171
|
+
return [
|
|
172
|
+
PendingEventResponse(
|
|
173
|
+
event_id=event.id,
|
|
174
|
+
project_id=event.project_id,
|
|
175
|
+
event_type=event.type.value,
|
|
176
|
+
priority=event.priority.value,
|
|
177
|
+
status=event.status.value,
|
|
178
|
+
title=event.title,
|
|
179
|
+
content=event.content,
|
|
180
|
+
options=event.options,
|
|
181
|
+
is_blocking=event_handler.is_blocking(event),
|
|
182
|
+
)
|
|
183
|
+
for event in events
|
|
184
|
+
]
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""REST API routes for inbox system.
|
|
2
|
+
|
|
3
|
+
Provides HTTP endpoints for querying and managing the event inbox.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
|
|
9
|
+
from fastapi import APIRouter, Depends, Query
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
from ...inbox import Inbox
|
|
13
|
+
from ...models.events import EventPriority, EventType
|
|
14
|
+
|
|
15
|
+
router = APIRouter()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_inbox() -> Inbox:
|
|
19
|
+
"""Dependency to get the global inbox instance.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
The global inbox instance
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
RuntimeError: If inbox is not initialized
|
|
26
|
+
"""
|
|
27
|
+
from ..app import inbox
|
|
28
|
+
|
|
29
|
+
if inbox is None:
|
|
30
|
+
raise RuntimeError("Inbox not initialized")
|
|
31
|
+
return inbox
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class InboxItemResponse(BaseModel):
|
|
35
|
+
"""Response model for inbox item.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
event_id: Unique event identifier
|
|
39
|
+
project_id: Project that raised this event
|
|
40
|
+
project_name: Human-readable project name
|
|
41
|
+
project_path: Filesystem path to project
|
|
42
|
+
event_type: Type of event (decision, error, status, etc.)
|
|
43
|
+
priority: Urgency level (critical, high, normal, low, info)
|
|
44
|
+
status: Current lifecycle status
|
|
45
|
+
title: Short event summary
|
|
46
|
+
content: Detailed event message
|
|
47
|
+
options: For DECISION_NEEDED events, list of choices
|
|
48
|
+
age_display: Human-readable age (e.g., "5m ago")
|
|
49
|
+
created_at: When event was created
|
|
50
|
+
session_runtime: Optional session runtime identifier
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
event_id: str
|
|
54
|
+
project_id: str
|
|
55
|
+
project_name: str
|
|
56
|
+
project_path: str
|
|
57
|
+
event_type: str
|
|
58
|
+
priority: str
|
|
59
|
+
status: str
|
|
60
|
+
title: str
|
|
61
|
+
content: str
|
|
62
|
+
options: Optional[List[str]]
|
|
63
|
+
age_display: str
|
|
64
|
+
created_at: datetime
|
|
65
|
+
session_runtime: Optional[str]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class InboxCountsResponse(BaseModel):
|
|
69
|
+
"""Response model for inbox counts.
|
|
70
|
+
|
|
71
|
+
Attributes:
|
|
72
|
+
critical: Count of CRITICAL priority events
|
|
73
|
+
high: Count of HIGH priority events
|
|
74
|
+
normal: Count of NORMAL priority events
|
|
75
|
+
low: Count of LOW priority events
|
|
76
|
+
info: Count of INFO priority events
|
|
77
|
+
total: Total count of all pending events
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
critical: int
|
|
81
|
+
high: int
|
|
82
|
+
normal: int
|
|
83
|
+
low: int
|
|
84
|
+
info: int
|
|
85
|
+
total: int
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@router.get("/inbox", response_model=List[InboxItemResponse])
|
|
89
|
+
async def get_inbox_items(
|
|
90
|
+
limit: int = Query(50, ge=1, le=100, description="Maximum items to return"),
|
|
91
|
+
offset: int = Query(0, ge=0, description="Number of items to skip"),
|
|
92
|
+
priority: Optional[str] = Query(None, description="Filter by priority level"),
|
|
93
|
+
project_id: Optional[str] = Query(None, description="Filter by project ID"),
|
|
94
|
+
event_type: Optional[str] = Query(None, description="Filter by event type"),
|
|
95
|
+
inbox: Inbox = Depends(get_inbox),
|
|
96
|
+
) -> List[InboxItemResponse]:
|
|
97
|
+
"""Get inbox items with optional filtering and pagination.
|
|
98
|
+
|
|
99
|
+
Returns a list of inbox items sorted by priority (high to low) then
|
|
100
|
+
created time (old to new). Supports filtering by priority, project,
|
|
101
|
+
and event type.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
limit: Maximum number of items to return (1-100, default: 50)
|
|
105
|
+
offset: Number of items to skip for pagination (default: 0)
|
|
106
|
+
priority: Filter by priority (critical, high, normal, low, info)
|
|
107
|
+
project_id: Filter by project ID
|
|
108
|
+
event_type: Filter by event type (decision_needed, error, etc.)
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
List of inbox items matching the filters
|
|
112
|
+
|
|
113
|
+
Example:
|
|
114
|
+
GET /api/inbox?limit=20&priority=critical
|
|
115
|
+
GET /api/inbox?project_id=proj_123&event_type=error
|
|
116
|
+
GET /api/inbox?offset=50&limit=50 # Pagination
|
|
117
|
+
"""
|
|
118
|
+
# Parse enum values if provided
|
|
119
|
+
pri = EventPriority(priority) if priority else None
|
|
120
|
+
evt = EventType(event_type) if event_type else None
|
|
121
|
+
|
|
122
|
+
items = inbox.get_items(
|
|
123
|
+
limit=limit,
|
|
124
|
+
offset=offset,
|
|
125
|
+
priority=pri,
|
|
126
|
+
project_id=project_id,
|
|
127
|
+
event_type=evt,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return [
|
|
131
|
+
InboxItemResponse(
|
|
132
|
+
event_id=item.event.id,
|
|
133
|
+
project_id=item.event.project_id,
|
|
134
|
+
project_name=item.project_name,
|
|
135
|
+
project_path=item.project_path,
|
|
136
|
+
event_type=item.event.type.value,
|
|
137
|
+
priority=item.event.priority.value,
|
|
138
|
+
status=item.event.status.value,
|
|
139
|
+
title=item.event.title,
|
|
140
|
+
content=item.event.content,
|
|
141
|
+
options=item.event.options,
|
|
142
|
+
age_display=item.age_display,
|
|
143
|
+
created_at=item.event.created_at,
|
|
144
|
+
session_runtime=item.session_runtime,
|
|
145
|
+
)
|
|
146
|
+
for item in items
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@router.get("/inbox/counts", response_model=InboxCountsResponse)
|
|
151
|
+
async def get_inbox_counts(
|
|
152
|
+
project_id: Optional[str] = Query(None, description="Filter by project ID"),
|
|
153
|
+
inbox: Inbox = Depends(get_inbox),
|
|
154
|
+
) -> InboxCountsResponse:
|
|
155
|
+
"""Get count of pending events by priority.
|
|
156
|
+
|
|
157
|
+
Returns summary statistics showing how many events exist at each
|
|
158
|
+
priority level. Optionally filtered to a specific project.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
project_id: If provided, only count events for this project
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Breakdown of event counts by priority
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
GET /api/inbox/counts
|
|
168
|
+
GET /api/inbox/counts?project_id=proj_123
|
|
169
|
+
"""
|
|
170
|
+
counts = inbox.get_counts(project_id)
|
|
171
|
+
return InboxCountsResponse(**counts.__dict__)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Message and thread management endpoints for MPM Commander API.
|
|
2
|
+
|
|
3
|
+
This module implements REST endpoints for sending messages and retrieving
|
|
4
|
+
conversation threads for projects.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import uuid
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
from fastapi import APIRouter
|
|
11
|
+
|
|
12
|
+
from ...models import ThreadMessage
|
|
13
|
+
from ..errors import ProjectNotFoundError
|
|
14
|
+
from ..schemas import MessageResponse, SendMessageRequest
|
|
15
|
+
|
|
16
|
+
router = APIRouter()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _get_registry():
|
|
20
|
+
"""Get registry instance from app global."""
|
|
21
|
+
from ..app import registry
|
|
22
|
+
|
|
23
|
+
if registry is None:
|
|
24
|
+
raise RuntimeError("Registry not initialized")
|
|
25
|
+
return registry
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _message_to_response(message: ThreadMessage) -> MessageResponse:
|
|
29
|
+
"""Convert ThreadMessage model to MessageResponse schema.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
message: ThreadMessage instance
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
MessageResponse with message data
|
|
36
|
+
"""
|
|
37
|
+
return MessageResponse(
|
|
38
|
+
id=message.id,
|
|
39
|
+
role=message.role,
|
|
40
|
+
content=message.content,
|
|
41
|
+
session_id=message.session_id,
|
|
42
|
+
timestamp=message.timestamp,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@router.get("/projects/{project_id}/thread", response_model=List[MessageResponse])
|
|
47
|
+
async def get_thread(project_id: str) -> List[MessageResponse]:
|
|
48
|
+
"""Get conversation thread for a project.
|
|
49
|
+
|
|
50
|
+
Returns all messages in chronological order.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
project_id: Unique project identifier
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
List of messages in thread (may be empty)
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
ProjectNotFoundError: If project_id doesn't exist
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
GET /api/projects/abc-123/thread
|
|
63
|
+
Response: [
|
|
64
|
+
{
|
|
65
|
+
"id": "msg-1",
|
|
66
|
+
"role": "user",
|
|
67
|
+
"content": "Fix the login bug",
|
|
68
|
+
"session_id": null,
|
|
69
|
+
"timestamp": "2025-01-12T10:00:00Z"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "msg-2",
|
|
73
|
+
"role": "assistant",
|
|
74
|
+
"content": "I'll investigate the login issue",
|
|
75
|
+
"session_id": "sess-456",
|
|
76
|
+
"timestamp": "2025-01-12T10:00:30Z"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
"""
|
|
80
|
+
registry = _get_registry()
|
|
81
|
+
project = registry.get(project_id)
|
|
82
|
+
|
|
83
|
+
if project is None:
|
|
84
|
+
raise ProjectNotFoundError(project_id)
|
|
85
|
+
|
|
86
|
+
# Convert thread messages to responses
|
|
87
|
+
return [_message_to_response(m) for m in project.thread]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@router.post(
|
|
91
|
+
"/projects/{project_id}/messages", response_model=MessageResponse, status_code=201
|
|
92
|
+
)
|
|
93
|
+
async def send_message(project_id: str, req: SendMessageRequest) -> MessageResponse:
|
|
94
|
+
"""Send a message to a project's active session.
|
|
95
|
+
|
|
96
|
+
Adds message to conversation thread and sends to specified or active session.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
project_id: Unique project identifier
|
|
100
|
+
req: Message request with content and optional session_id
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Created message information
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
ProjectNotFoundError: If project_id doesn't exist
|
|
107
|
+
|
|
108
|
+
Example:
|
|
109
|
+
POST /api/projects/abc-123/messages
|
|
110
|
+
Body: {
|
|
111
|
+
"content": "Fix the login bug",
|
|
112
|
+
"session_id": "sess-456"
|
|
113
|
+
}
|
|
114
|
+
Response: {
|
|
115
|
+
"id": "msg-1",
|
|
116
|
+
"role": "user",
|
|
117
|
+
"content": "Fix the login bug",
|
|
118
|
+
"session_id": "sess-456",
|
|
119
|
+
"timestamp": "2025-01-12T10:00:00Z"
|
|
120
|
+
}
|
|
121
|
+
"""
|
|
122
|
+
registry = _get_registry()
|
|
123
|
+
project = registry.get(project_id)
|
|
124
|
+
|
|
125
|
+
if project is None:
|
|
126
|
+
raise ProjectNotFoundError(project_id)
|
|
127
|
+
|
|
128
|
+
# Generate message ID
|
|
129
|
+
message_id = str(uuid.uuid4())
|
|
130
|
+
|
|
131
|
+
# Create message object
|
|
132
|
+
message = ThreadMessage(
|
|
133
|
+
id=message_id,
|
|
134
|
+
role="user",
|
|
135
|
+
content=req.content,
|
|
136
|
+
session_id=req.session_id,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Add to project thread
|
|
140
|
+
project.thread.append(message)
|
|
141
|
+
|
|
142
|
+
# Update last activity
|
|
143
|
+
registry.touch(project_id)
|
|
144
|
+
|
|
145
|
+
# TODO: Send to session/runtime adapter (Phase 2)
|
|
146
|
+
# For Phase 1, message is just stored in thread
|
|
147
|
+
|
|
148
|
+
return _message_to_response(message)
|