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
cli/commands/topic.py ADDED
@@ -0,0 +1,361 @@
1
+ """``map topic ...`` + ``map mention ...`` + ``map todo ...`` sub-apps — arch PR6.
2
+
3
+ Three sub-apps that share the "topic work items" domain:
4
+
5
+ * ``map topic ...`` — topic lifecycle (create / list / show / progress /
6
+ resolve / advance-round / comment / close / reopen / dismiss / read /
7
+ mark-seen / archive). The big one.
8
+ * ``map mention ...`` — personal @mention todos (dismiss / list /
9
+ dismiss-all / reconcile-stale stub).
10
+ * ``map todo ...`` — explicit_only todo partition clear router
11
+ (notification / action_item / my_open_topics / unread_change).
12
+
13
+ All command bodies lazy-import ``cli.main._run`` and friends to break
14
+ the ``cli.main ↔ cli.commands.*`` import cycle.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import uuid
19
+ from pathlib import Path
20
+ from typing import Any
21
+
22
+ import typer
23
+ from map_client.client import MAPClient
24
+
25
+ from cli.table_render import enum_value, format_datetime, render_table, short_uuid, truncate
26
+ from server.domain.schemas import (
27
+ TopicAdvanceRound,
28
+ TopicCommentCreate,
29
+ TopicUpdate,
30
+ )
31
+
32
+ topic_app = typer.Typer(help="Topic commands", rich_markup_mode=None)
33
+ mention_app = typer.Typer(help="Mention todo commands")
34
+ todo_app = typer.Typer(help="Todo partition clear routing (explicit_only buckets)")
35
+
36
+
37
+ # ---------------------------------------------------------------------------
38
+ # topic_app
39
+ # ---------------------------------------------------------------------------
40
+
41
+
42
+ @topic_app.command("create")
43
+ def topic_create(
44
+ title: str = typer.Option(..., "--title"),
45
+ project: uuid.UUID | None = typer.Option(None, "--project"),
46
+ project_key: str | None = typer.Option(None, "--project-key"),
47
+ description: str | None = typer.Option(None, "--description"),
48
+ ) -> None:
49
+ from cli.main import _resolve_project, _run
50
+ from server.domain.schemas import TopicCreate
51
+
52
+ payload = TopicCreate(title=title, description=description)
53
+
54
+ def action(c: MAPClient):
55
+ pid = _resolve_project(c, project, project_key)
56
+ return c.create_topic(pid, payload)
57
+
58
+ _run(action)
59
+
60
+
61
+ def _render_topic_table(topics: Any) -> str:
62
+ """Render a list of TopicSummaryRead as a compact table."""
63
+ headers = ["ID", "Title", "Status", "Round", "Comments", "Exps", "Creator", "Created"]
64
+ rows = []
65
+ for t in topics:
66
+ rows.append([
67
+ short_uuid(t.id),
68
+ truncate(t.title, 50),
69
+ enum_value(t.status),
70
+ enum_value(t.discussion_round),
71
+ str(t.comment_count),
72
+ str(t.experiment_count),
73
+ truncate(t.creator_name, 20),
74
+ format_datetime(t.created_at),
75
+ ])
76
+ return render_table(headers, rows)
77
+
78
+
79
+ @topic_app.command("list")
80
+ def topic_list(
81
+ project: uuid.UUID | None = typer.Option(None, "--project"),
82
+ project_key: str | None = typer.Option(None, "--project-key"),
83
+ status: str | None = typer.Option(None, "--status"),
84
+ creator: str | None = typer.Option(
85
+ None,
86
+ "--creator",
87
+ help="Filter by topic creator. Accepts agent_name (current project) or agent_id UUID; "
88
+ "alias for --creator-agent-id.",
89
+ ),
90
+ creator_agent_id: uuid.UUID | None = typer.Option(
91
+ None,
92
+ "--creator-agent-id",
93
+ help="Filter by creator agent_id UUID. Use --creator for name-or-id shorthand.",
94
+ ),
95
+ q: str | None = typer.Option(None, "--q"),
96
+ page: int = typer.Option(1, "--page", min=1),
97
+ page_size: int = typer.Option(100, "--page-size", min=1, max=100),
98
+ include_archived: bool = typer.Option(False, "--include-archived"),
99
+ ) -> None:
100
+ """List topics in the current project.
101
+
102
+ Defaults to a compact table view. Use ``--format yaml`` or
103
+ ``--format json`` for full structured output (scripts / piping).
104
+ """
105
+ from cli.main import _resolve_creator_agent_id, _resolve_project, _run
106
+ from server.domain.models import TopicStatus
107
+
108
+ def action(c: MAPClient):
109
+ pid = _resolve_project(c, project, project_key)
110
+ st = TopicStatus(status) if status else None
111
+ resolved_creator_id = _resolve_creator_agent_id(c, pid, creator, creator_agent_id)
112
+ return c.list_topics(
113
+ pid,
114
+ status=st,
115
+ creator_agent_id=resolved_creator_id,
116
+ q=q,
117
+ page=page,
118
+ page_size=page_size,
119
+ include_archived=include_archived,
120
+ )
121
+
122
+ _run(action, table_renderer=_render_topic_table)
123
+
124
+
125
+ @topic_app.command("show")
126
+ def topic_show(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
127
+ from cli.main import _run
128
+
129
+ _run(lambda c: c.get_topic(topic_id))
130
+
131
+
132
+ @topic_app.command("progress")
133
+ def topic_progress() -> None:
134
+ """Per-agent topic work items view (obligation + contextual); same source as todos topic buckets."""
135
+ from cli.main import _run
136
+
137
+ _run(lambda c: c.get_topic_progress())
138
+
139
+
140
+ @topic_app.command("resolve")
141
+ def topic_resolve(
142
+ topic_id: uuid.UUID = typer.Option(..., "--id"),
143
+ resolve_file: Path = typer.Option(..., "--file"),
144
+ ) -> None:
145
+ from cli.main import _load_topic_resolve_payload, _run
146
+
147
+ payload = _load_topic_resolve_payload(resolve_file)
148
+ _run(lambda c: c.resolve_topic(topic_id, payload))
149
+
150
+
151
+ @topic_app.command("advance-round")
152
+ def topic_advance_round(
153
+ topic_id: uuid.UUID = typer.Option(..., "--id"),
154
+ increment_summary: bool = typer.Option(
155
+ True,
156
+ "--increment-summary/--no-increment-summary",
157
+ help="Increment round_summary_count before advancing.",
158
+ ),
159
+ ack_ids: str | None = typer.Option(
160
+ None,
161
+ "--ack-ids",
162
+ help="Host: comma-separated participant agent UUIDs already acknowledged.",
163
+ ),
164
+ ack: str | None = typer.Option(
165
+ None,
166
+ "--ack",
167
+ help="Participant: accept, reject, or dismiss acknowledgement for the current round.",
168
+ ),
169
+ ) -> None:
170
+ from cli.main import _run
171
+
172
+ acknowledged_by: list[uuid.UUID] = []
173
+ if ack_ids:
174
+ acknowledged_by = [uuid.UUID(item.strip()) for item in ack_ids.split(",") if item.strip()]
175
+ payload = TopicAdvanceRound(
176
+ increment_summary=increment_summary,
177
+ acknowledged_by=acknowledged_by,
178
+ ack=ack, # type: ignore[arg-type]
179
+ )
180
+ _run(lambda c: c.advance_topic_round(topic_id, payload))
181
+
182
+
183
+ @topic_app.command("comment")
184
+ def topic_comment(
185
+ topic_id: uuid.UUID = typer.Option(..., "--id"),
186
+ body: str | None = typer.Option(None, "--body"),
187
+ body_file: Path | None = typer.Option(None, "--file"),
188
+ parent: uuid.UUID | None = typer.Option(None, "--parent"),
189
+ ) -> None:
190
+ from cli.main import _read_text_file, _run
191
+
192
+ if body is None and body_file is None:
193
+ typer.echo("Error: either --body or --file is required", err=True)
194
+ raise typer.Exit(2)
195
+ if body is not None and body_file is not None:
196
+ typer.echo("Error: use only one of --body or --file", err=True)
197
+ raise typer.Exit(2)
198
+ content = body if body is not None else _read_text_file(body_file, kind="comment")
199
+ payload = TopicCommentCreate(body=content, parent_id=parent)
200
+ _run(lambda c: c.create_topic_comment(topic_id, payload))
201
+
202
+
203
+ @topic_app.command("close")
204
+ def topic_close(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
205
+ from cli.main import _run
206
+
207
+ _run(lambda c: c.close_topic(topic_id))
208
+
209
+
210
+ @topic_app.command("reopen")
211
+ def topic_reopen(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
212
+ from cli.main import _run
213
+
214
+ _run(lambda c: c.reopen_topic(topic_id))
215
+
216
+
217
+ @topic_app.command("dismiss")
218
+ def topic_dismiss(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
219
+ """Hide an open topic from host todos until new activity (same as Web UI ✕)."""
220
+ from cli.main import _run
221
+
222
+ _run(lambda c: c.dismiss_topic(topic_id))
223
+
224
+
225
+ @topic_app.command("read")
226
+ def topic_read(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
227
+ """Mark contextual unread changes as seen; obligations still require reply/ack/mention handling."""
228
+ from cli.main import _run
229
+
230
+ _run(lambda c: c.mark_topic_read(topic_id))
231
+
232
+
233
+ @topic_app.command("mark-seen")
234
+ def topic_mark_seen(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
235
+ """Alias of topic read: clears contextual unread only, not reply/ack/mention obligations."""
236
+ from cli.main import _run
237
+
238
+ _run(lambda c: c.mark_topic_read(topic_id))
239
+
240
+
241
+ @topic_app.command(
242
+ "archive",
243
+ epilog="Use --undo or --unarchive to restore an archived topic.",
244
+ )
245
+ def topic_archive(
246
+ topic_id: uuid.UUID | None = typer.Option(None, "--id", help="Topic UUID."),
247
+ undo: bool = typer.Option(
248
+ False,
249
+ "--undo",
250
+ help="Unarchive instead of archive. Equivalent to --unarchive.",
251
+ ),
252
+ unarchive: bool = typer.Option(
253
+ False,
254
+ "--unarchive",
255
+ help="Alias of --undo: unarchive instead of archive.",
256
+ ),
257
+ ) -> None:
258
+ """Archive (or unarchive) a topic.
259
+
260
+ Thin wrapper around ``PATCH /topics/{id}`` with ``archived=true`` (or
261
+ ``false`` when ``--undo``/``--unarchive`` is set). Archive hides the
262
+ topic from ``topic list`` by default but ``topic show`` still returns
263
+ it including ``archived_at``. Archive is reversible — re-run with
264
+ ``--undo`` to restore.
265
+ """
266
+ from map_client.exceptions import MAPNotFoundError
267
+
268
+ from cli.main import _require_option_uuid, _run
269
+
270
+ topic_id = _require_option_uuid(topic_id)
271
+ payload = TopicUpdate(archived=not (undo or unarchive))
272
+ object_kind = "topic"
273
+
274
+ def action(c: MAPClient):
275
+ try:
276
+ return c.update_topic(topic_id, payload)
277
+ except MAPNotFoundError as exc:
278
+ typer.echo(
279
+ f"Error: {object_kind} {topic_id} not found",
280
+ err=True,
281
+ )
282
+ raise typer.Exit(1) from exc
283
+
284
+ _run(action)
285
+
286
+
287
+ # ---------------------------------------------------------------------------
288
+ # mention_app
289
+ # ---------------------------------------------------------------------------
290
+
291
+
292
+ @mention_app.command("dismiss")
293
+ def mention_dismiss(
294
+ mention_id: uuid.UUID = typer.Option(..., "--id", help="Mention UUID from `map todos`."),
295
+ ) -> None:
296
+ """Dismiss one @mention for the current persona (removes it from `map todos`).
297
+
298
+ Idempotent: dismissing an already-dismissed mention returns the same result.
299
+ """
300
+ from cli.main import _run
301
+
302
+ _run(lambda c: c.dismiss_mention(mention_id))
303
+
304
+
305
+ @mention_app.command("list")
306
+ def mention_list() -> None:
307
+ """List open @mentions for the current persona."""
308
+ from cli.main import _run
309
+
310
+ _run(lambda c: c.get_todos().mentions)
311
+
312
+
313
+ @mention_app.command("dismiss-all")
314
+ def mention_dismiss_all() -> None:
315
+ """Dismiss all open @mentions for the current persona."""
316
+ from cli.main import _run
317
+
318
+ _run(lambda c: c.dismiss_all_mentions())
319
+
320
+
321
+ @mention_app.command("reconcile-stale")
322
+ def mention_reconcile_stale() -> None:
323
+ """Admin stub: offline stale mention reconciliation (T1 D5 MVP — not implemented)."""
324
+ typer.echo(
325
+ "mention reconcile-stale: stub only — stale mentions are filtered in "
326
+ "topic-progress/todos projection; use write-path dismiss on comment."
327
+ )
328
+
329
+
330
+ # ---------------------------------------------------------------------------
331
+ # todo_app — explicit_only partition clear router
332
+ # ---------------------------------------------------------------------------
333
+
334
+
335
+ @todo_app.command("clear")
336
+ def todo_clear(
337
+ key: str = typer.Option(..., "--key", help="Work-item idempotency_key or partition id"),
338
+ ) -> None:
339
+ """Route explicit_only todo partitions to the canonical clear CLI (T1 D7)."""
340
+ from cli.main import _run
341
+
342
+ if key.startswith("notification:"):
343
+ notification_id = uuid.UUID(key.split(":", 1)[1])
344
+ _run(lambda c: c.mark_notification_read(notification_id))
345
+ return
346
+ if key.startswith("action_item:"):
347
+ item_id = uuid.UUID(key.split(":", 1)[1])
348
+ _run(lambda c: c.complete_action_item(item_id))
349
+ return
350
+ if key.startswith("my_open_topics:") or key.startswith("topic:"):
351
+ topic_id = uuid.UUID(key.rsplit(":", 1)[-1])
352
+ _run(lambda c: c.dismiss_topic(topic_id))
353
+ return
354
+ if key.startswith("unread_change:"):
355
+ topic_id = uuid.UUID(key.split(":", 2)[1])
356
+ _run(lambda c: c.mark_topic_read(topic_id))
357
+ return
358
+ raise typer.BadParameter(
359
+ f"unsupported todo clear key {key!r}; explicit_only: notification, action_item, "
360
+ "my_open_topics, unread_change"
361
+ )