multi-agent-platform 0.1.0__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.
Files changed (144) hide show
  1. cli/__init__.py +0 -0
  2. cli/action_item_escalation.py +177 -0
  3. cli/agent_client.py +554 -0
  4. cli/bridge_state.py +43 -0
  5. cli/commands/__init__.py +13 -0
  6. cli/commands/action.py +142 -0
  7. cli/commands/agent.py +117 -0
  8. cli/commands/audit.py +68 -0
  9. cli/commands/docs.py +179 -0
  10. cli/commands/experiment.py +755 -0
  11. cli/commands/feedback.py +106 -0
  12. cli/commands/notification.py +213 -0
  13. cli/commands/persona.py +63 -0
  14. cli/commands/project.py +87 -0
  15. cli/commands/runtime.py +105 -0
  16. cli/commands/topic.py +361 -0
  17. cli/e2e_collab.py +602 -0
  18. cli/git_checkpoint.py +68 -0
  19. cli/host_worker_types.py +151 -0
  20. cli/main.py +1553 -0
  21. cli/map_command_client.py +497 -0
  22. cli/participant_worker.py +255 -0
  23. cli/reviewer_worker.py +263 -0
  24. cli/runtime/__init__.py +5 -0
  25. cli/runtime/run_lock.py +497 -0
  26. cli/runtime_chat.py +317 -0
  27. cli/session_wake_log.py +235 -0
  28. cli/simple_waker.py +950 -0
  29. cli/table_render.py +113 -0
  30. cli/wake_backend.py +236 -0
  31. cli/worker_cycle_log.py +36 -0
  32. map_client/__init__.py +37 -0
  33. map_client/bootstrap.py +193 -0
  34. map_client/client.py +1045 -0
  35. map_client/config.py +21 -0
  36. map_client/errors.py +283 -0
  37. map_client/exceptions.py +130 -0
  38. map_client/plan_evidence.py +159 -0
  39. map_client/project_config.py +153 -0
  40. map_client/result_template.py +167 -0
  41. map_client/testing.py +27 -0
  42. map_mcp/__init__.py +4 -0
  43. map_mcp/_utils.py +28 -0
  44. map_mcp/auth.py +34 -0
  45. map_mcp/config.py +50 -0
  46. map_mcp/context.py +39 -0
  47. map_mcp/main.py +75 -0
  48. map_mcp/server.py +573 -0
  49. map_mcp/session.py +79 -0
  50. map_sdk/__init__.py +29 -0
  51. map_sdk/evidence.py +68 -0
  52. map_types/__init__.py +203 -0
  53. map_types/enums.py +199 -0
  54. map_types/schemas.py +1351 -0
  55. multi_agent_platform-0.1.0.dist-info/METADATA +298 -0
  56. multi_agent_platform-0.1.0.dist-info/RECORD +144 -0
  57. multi_agent_platform-0.1.0.dist-info/WHEEL +5 -0
  58. multi_agent_platform-0.1.0.dist-info/entry_points.txt +6 -0
  59. multi_agent_platform-0.1.0.dist-info/licenses/LICENSE +21 -0
  60. multi_agent_platform-0.1.0.dist-info/top_level.txt +6 -0
  61. server/__init__.py +0 -0
  62. server/__version__.py +14 -0
  63. server/api/__init__.py +0 -0
  64. server/api/action_items.py +138 -0
  65. server/api/agents.py +412 -0
  66. server/api/audit.py +54 -0
  67. server/api/background_tasks.py +18 -0
  68. server/api/common.py +117 -0
  69. server/api/deps.py +30 -0
  70. server/api/experiments.py +858 -0
  71. server/api/feedback.py +75 -0
  72. server/api/notifications.py +22 -0
  73. server/api/projects.py +209 -0
  74. server/api/router.py +25 -0
  75. server/api/status.py +33 -0
  76. server/api/topics.py +302 -0
  77. server/api/webhooks.py +74 -0
  78. server/auth/__init__.py +8 -0
  79. server/auth/experiment_access.py +66 -0
  80. server/config.py +38 -0
  81. server/db/__init__.py +3 -0
  82. server/db/base.py +5 -0
  83. server/db/deadlock_retry.py +146 -0
  84. server/db/session.py +41 -0
  85. server/domain/__init__.py +3 -0
  86. server/domain/encrypted_types.py +63 -0
  87. server/domain/models.py +713 -0
  88. server/domain/schemas.py +3 -0
  89. server/domain/state_machine.py +79 -0
  90. server/domain/topic_ack_constants.py +9 -0
  91. server/main.py +148 -0
  92. server/scripts/__init__.py +0 -0
  93. server/scripts/migrate_notification_unique.py +231 -0
  94. server/scripts/purge_audit_pollution.py +116 -0
  95. server/services/__init__.py +0 -0
  96. server/services/_lookups.py +26 -0
  97. server/services/acceptance_service.py +90 -0
  98. server/services/action_item_migration_service.py +190 -0
  99. server/services/action_item_service.py +200 -0
  100. server/services/agent_work_service.py +405 -0
  101. server/services/archive_lint_service.py +156 -0
  102. server/services/audit_service.py +457 -0
  103. server/services/auth.py +66 -0
  104. server/services/comment_service.py +173 -0
  105. server/services/errors.py +65 -0
  106. server/services/escalation_resolver.py +248 -0
  107. server/services/evidence_service.py +88 -0
  108. server/services/experiment_capabilities_service.py +277 -0
  109. server/services/inbound_event_service.py +111 -0
  110. server/services/lock_service.py +273 -0
  111. server/services/log_service.py +202 -0
  112. server/services/mention_service.py +730 -0
  113. server/services/notification_service.py +939 -0
  114. server/services/notification_stream.py +138 -0
  115. server/services/permissions.py +147 -0
  116. server/services/persona_activity_service.py +108 -0
  117. server/services/phase_owner_resolver.py +95 -0
  118. server/services/phase_service.py +381 -0
  119. server/services/plan_marker_service.py +235 -0
  120. server/services/plan_service.py +186 -0
  121. server/services/platform_feedback_service.py +114 -0
  122. server/services/project_service.py +534 -0
  123. server/services/project_status_service.py +132 -0
  124. server/services/review_service.py +707 -0
  125. server/services/secret_encryption.py +97 -0
  126. server/services/similarity_service.py +119 -0
  127. server/services/sse_event_schemas.py +17 -0
  128. server/services/status_service.py +68 -0
  129. server/services/template_service.py +134 -0
  130. server/services/text_utils.py +19 -0
  131. server/services/thread_activity.py +180 -0
  132. server/services/todo_persona_filter.py +73 -0
  133. server/services/todo_service.py +604 -0
  134. server/services/topic_ack_service.py +312 -0
  135. server/services/topic_action_item_ops.py +538 -0
  136. server/services/topic_comment_kind.py +14 -0
  137. server/services/topic_comment_service.py +237 -0
  138. server/services/topic_helpers.py +32 -0
  139. server/services/topic_lifecycle_service.py +478 -0
  140. server/services/topic_progress_service.py +40 -0
  141. server/services/topic_resolve_service.py +234 -0
  142. server/services/topic_service.py +102 -0
  143. server/services/topic_work_item_service.py +570 -0
  144. server/services/webhook_service.py +273 -0
@@ -0,0 +1,237 @@
1
+ """Topic comment CRUD and tree/seq helpers.
2
+
3
+ 拆分自 ``topic_service.py``(P1 #1)以缓解 1100+ 行单文件可读性问题。
4
+ 本模块只负责 topic 评论的写入与读取:
5
+ - ``create_topic_comment``:写评论 + bump ``topic.updated_at`` + 触发
6
+ round-summary 检测 + mention 处理。
7
+ - ``list_topic_comments`` / ``topic_comment_read``:序列化(含 author_name、
8
+ ``comment_seq`` 兜底、可选 tree)。
9
+ - ``_build_comment_tree`` / ``_ensure_comment_seq_values`` /
10
+ ``_next_topic_comment_seq``:tree 组装与 seq 分配(兼容历史无 seq 行)。
11
+
12
+ ``_get_topic`` 与 ``_agent_names_by_ids`` 是从 ``topic_service`` 复制的极短
13
+ helper(避免新模块依赖 topic_service 的私有 API 引入循环);这两个函数
14
+ 保持单行为,未来若 topic_service 公开了等价 API 可统一。
15
+
16
+ 向后兼容:``topic_service`` 仍 re-export 本模块的公开函数,调用方
17
+ ``from server.services.topic_service import create_topic_comment`` 无需修改。
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import uuid
23
+ from datetime import UTC, datetime
24
+
25
+ from map_types.enums import AgentRole, TopicCommentKind
26
+ from sqlalchemy import func, select
27
+ from sqlalchemy.orm import Session
28
+
29
+ from server.domain.models import Agent, Topic, TopicComment
30
+ from server.domain.schemas import (
31
+ TopicCommentCreate,
32
+ TopicCommentRead,
33
+ TopicCommentTreeNode,
34
+ )
35
+ from server.services import mention_service, topic_ack_service
36
+ from server.services.errors import NotFoundError
37
+ from server.services.thread_activity import topic_comment_order_clauses
38
+ from server.services.topic_comment_kind import resolve_topic_comment_kind
39
+
40
+
41
+ def _get_topic(db: Session, topic_id: uuid.UUID) -> Topic:
42
+ topic = db.get(Topic, topic_id)
43
+ if topic is None or topic.deleted_at is not None:
44
+ raise NotFoundError("Topic not found")
45
+ return topic
46
+
47
+
48
+ def _agent_names_by_ids(db: Session, agent_ids: set[uuid.UUID]) -> dict[uuid.UUID, str]:
49
+ if not agent_ids:
50
+ return {}
51
+ rows = db.execute(
52
+ select(Agent.id, Agent.name).where(Agent.id.in_(agent_ids))
53
+ )
54
+ return {agent_id: name for agent_id, name in rows}
55
+
56
+
57
+ def _comment_kind(comment: TopicComment) -> TopicCommentKind:
58
+ return comment.kind or TopicCommentKind.user
59
+
60
+
61
+ def _build_comment_tree(
62
+ comments: list[TopicComment],
63
+ author_names: dict[uuid.UUID, str],
64
+ ) -> list[TopicCommentTreeNode]:
65
+ _ensure_comment_seq_values(comments)
66
+ nodes: dict[uuid.UUID, TopicCommentTreeNode] = {}
67
+ for comment in comments:
68
+ nodes[comment.id] = TopicCommentTreeNode(
69
+ id=comment.id,
70
+ topic_id=comment.topic_id,
71
+ author_agent_id=comment.author_agent_id,
72
+ author_name=author_names.get(comment.author_agent_id),
73
+ parent_comment_id=comment.parent_comment_id,
74
+ body=comment.body,
75
+ kind=_comment_kind(comment),
76
+ comment_seq=comment.comment_seq,
77
+ created_at=comment.created_at,
78
+ children=[],
79
+ )
80
+ roots: list[TopicCommentTreeNode] = []
81
+ for comment in comments:
82
+ node = nodes[comment.id]
83
+ if comment.parent_comment_id and comment.parent_comment_id in nodes:
84
+ nodes[comment.parent_comment_id].children.append(node)
85
+ else:
86
+ roots.append(node)
87
+ return roots
88
+
89
+
90
+ def _ensure_comment_seq_values(comments: list[TopicComment]) -> None:
91
+ if all(comment.comment_seq is not None for comment in comments):
92
+ return
93
+ by_topic: dict[uuid.UUID, list[TopicComment]] = {}
94
+ for comment in comments:
95
+ by_topic.setdefault(comment.topic_id, []).append(comment)
96
+ for topic_comments in by_topic.values():
97
+ ordered = sorted(
98
+ topic_comments,
99
+ key=lambda comment: (
100
+ comment.created_at,
101
+ comment.comment_seq if comment.comment_seq is not None else 0,
102
+ comment.id,
103
+ ),
104
+ )
105
+ for index, comment in enumerate(ordered, start=1):
106
+ if comment.comment_seq is None:
107
+ comment.comment_seq = index
108
+
109
+
110
+ def _next_topic_comment_seq(db: Session, topic_id: uuid.UUID) -> int:
111
+ current = db.scalar(
112
+ select(func.coalesce(func.max(TopicComment.comment_seq), 0)).where(
113
+ TopicComment.topic_id == topic_id
114
+ )
115
+ )
116
+ return int(current or 0) + 1
117
+
118
+
119
+ def create_topic_comment(
120
+ db: Session,
121
+ topic_id: uuid.UUID,
122
+ author: Agent,
123
+ payload: TopicCommentCreate,
124
+ *,
125
+ commit: bool = True,
126
+ ) -> tuple[TopicComment, list[str]]:
127
+ topic = _get_topic(db, topic_id)
128
+ if payload.parent_id is not None:
129
+ parent = db.scalar(
130
+ select(TopicComment).where(
131
+ TopicComment.id == payload.parent_id, TopicComment.topic_id == topic_id
132
+ )
133
+ )
134
+ if parent is None:
135
+ raise NotFoundError("Parent comment not found")
136
+ comment = TopicComment(
137
+ topic_id=topic_id,
138
+ author_agent_id=author.id,
139
+ parent_comment_id=payload.parent_id,
140
+ body=payload.body,
141
+ kind=resolve_topic_comment_kind(payload.body),
142
+ comment_seq=_next_topic_comment_seq(db, topic_id),
143
+ )
144
+ db.add(comment)
145
+ # Bump topic.updated_at so any prior host-side dismiss on this topic
146
+ # is automatically un-dismissed — there's new activity worth seeing.
147
+ topic.updated_at = datetime.now(UTC)
148
+ if (
149
+ author.id == topic.creator_agent_id
150
+ and payload.parent_id is None
151
+ and topic_ack_service.is_round_summary_comment(payload.body)
152
+ ):
153
+ topic_ack_service.mark_round_ack_pending(topic)
154
+ db.flush()
155
+ db.refresh(comment)
156
+
157
+ unresolved = mention_service.process_topic_comment_mentions(
158
+ db, comment=comment, author=author, topic=topic, commit=False
159
+ )
160
+ mention_service.auto_dismiss_mentions_after_comment(
161
+ db,
162
+ new_comment_author=author,
163
+ experiment_id=None,
164
+ topic_id=topic_id,
165
+ new_comment_id=comment.id,
166
+ comment_body=payload.body,
167
+ commit=False,
168
+ )
169
+ if commit:
170
+ db.commit()
171
+ else:
172
+ db.flush()
173
+ return comment, unresolved
174
+
175
+
176
+ def topic_comment_read(
177
+ db: Session, comment: TopicComment, *, unresolved_mentions: list[str] | None = None
178
+ ) -> TopicCommentRead:
179
+ author_names = _agent_names_by_ids(db, {comment.author_agent_id})
180
+ _ensure_comment_seq_values([comment])
181
+ return TopicCommentRead(
182
+ id=comment.id,
183
+ topic_id=comment.topic_id,
184
+ author_agent_id=comment.author_agent_id,
185
+ author_name=author_names.get(comment.author_agent_id),
186
+ parent_comment_id=comment.parent_comment_id,
187
+ body=comment.body,
188
+ kind=_comment_kind(comment),
189
+ comment_seq=comment.comment_seq,
190
+ created_at=comment.created_at,
191
+ unresolved_mentions=list(unresolved_mentions or ()),
192
+ )
193
+
194
+
195
+ def list_topic_comments(
196
+ db: Session,
197
+ topic_id: uuid.UUID,
198
+ *,
199
+ tree: bool = False,
200
+ limit: int = 100,
201
+ ) -> list[TopicCommentRead] | list[TopicCommentTreeNode]:
202
+ _get_topic(db, topic_id)
203
+ stmt = (
204
+ select(TopicComment)
205
+ .where(TopicComment.topic_id == topic_id)
206
+ .order_by(*topic_comment_order_clauses())
207
+ .limit(max(1, min(limit, 500)))
208
+ )
209
+ comments = list(db.scalars(stmt))
210
+ _ensure_comment_seq_values(comments)
211
+ author_names = _agent_names_by_ids(db, {comment.author_agent_id for comment in comments})
212
+ if tree:
213
+ return _build_comment_tree(comments, author_names)
214
+ return [
215
+ TopicCommentRead(
216
+ id=comment.id,
217
+ topic_id=comment.topic_id,
218
+ author_agent_id=comment.author_agent_id,
219
+ author_name=author_names.get(comment.author_agent_id),
220
+ parent_comment_id=comment.parent_comment_id,
221
+ body=comment.body,
222
+ kind=_comment_kind(comment),
223
+ comment_seq=comment.comment_seq,
224
+ created_at=comment.created_at,
225
+ )
226
+ for comment in comments
227
+ ]
228
+
229
+
230
+ # Suppress unused-import warning for AgentRole — re-exported for backwards
231
+ # compatibility with callers that imported it from topic_service.
232
+ __all__ = [
233
+ "AgentRole",
234
+ "create_topic_comment",
235
+ "list_topic_comments",
236
+ "topic_comment_read",
237
+ ]
@@ -0,0 +1,32 @@
1
+ """Shared topic lookup helpers.
2
+
3
+ Split out so lifecycle / resolve / action-item modules can share
4
+ ``_get_topic`` and ``_agent_names_by_ids`` without circular imports.
5
+ ``topic_service`` re-exports both for existing private callers
6
+ (e.g. ``topic_work_item_service``).
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import uuid
11
+
12
+ from sqlalchemy import select
13
+ from sqlalchemy.orm import Session
14
+
15
+ from server.domain.models import Agent, Topic
16
+ from server.services.errors import NotFoundError
17
+
18
+
19
+ def _get_topic(db: Session, topic_id: uuid.UUID) -> Topic:
20
+ topic = db.get(Topic, topic_id)
21
+ if topic is None or topic.deleted_at is not None:
22
+ raise NotFoundError("Topic not found")
23
+ return topic
24
+
25
+
26
+ def _agent_names_by_ids(db: Session, agent_ids: set[uuid.UUID]) -> dict[uuid.UUID, str]:
27
+ if not agent_ids:
28
+ return {}
29
+ return {
30
+ agent.id: agent.name
31
+ for agent in db.scalars(select(Agent).where(Agent.id.in_(agent_ids)))
32
+ }