agentsonar 0.1.2__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.
Files changed (57) hide show
  1. agentsonar-0.1.2/.gitignore +16 -0
  2. agentsonar-0.1.2/PKG-INFO +463 -0
  3. agentsonar-0.1.2/README.md +431 -0
  4. agentsonar-0.1.2/docs/images/htmlreport.png +0 -0
  5. agentsonar-0.1.2/examples/crewai_clean_sequential.py +168 -0
  6. agentsonar-0.1.2/examples/crewai_delegation_ping_pong.py +265 -0
  7. agentsonar-0.1.2/examples/crewai_hello_world.py +138 -0
  8. agentsonar-0.1.2/examples/engine_only.py +121 -0
  9. agentsonar-0.1.2/examples/langgraph_clean_pipeline.py +86 -0
  10. agentsonar-0.1.2/examples/langgraph_cycle_and_repetitive.py +360 -0
  11. agentsonar-0.1.2/examples/langgraph_minimal.py +127 -0
  12. agentsonar-0.1.2/examples/langgraph_minimal_combo.py +126 -0
  13. agentsonar-0.1.2/examples/langgraph_minimal_rate_limit.py +86 -0
  14. agentsonar-0.1.2/examples/langgraph_reviewer_insufficient_loop.py +169 -0
  15. agentsonar-0.1.2/examples/warning_and_critical.py +135 -0
  16. agentsonar-0.1.2/pyproject.toml +114 -0
  17. agentsonar-0.1.2/src/agentsonar/__init__.py +52 -0
  18. agentsonar-0.1.2/src/agentsonar/_core/__init__.py +6 -0
  19. agentsonar-0.1.2/src/agentsonar/_core/constants.py +16 -0
  20. agentsonar-0.1.2/src/agentsonar/_core/detectors/__init__.py +22 -0
  21. agentsonar-0.1.2/src/agentsonar/_core/detectors/alert_state.py +202 -0
  22. agentsonar-0.1.2/src/agentsonar/_core/detectors/cycle_detector.py +81 -0
  23. agentsonar-0.1.2/src/agentsonar/_core/detectors/rate_limiter.py +297 -0
  24. agentsonar-0.1.2/src/agentsonar/_core/detectors/repetitive_detector.py +171 -0
  25. agentsonar-0.1.2/src/agentsonar/_core/detectors/scc_analyzer.py +83 -0
  26. agentsonar-0.1.2/src/agentsonar/_core/engine.py +960 -0
  27. agentsonar-0.1.2/src/agentsonar/_core/graph.py +47 -0
  28. agentsonar-0.1.2/src/agentsonar/_core/models.py +35 -0
  29. agentsonar-0.1.2/src/agentsonar/_core/noop_engine.py +145 -0
  30. agentsonar-0.1.2/src/agentsonar/_core/schema.py +565 -0
  31. agentsonar-0.1.2/src/agentsonar/_integrations/__init__.py +8 -0
  32. agentsonar-0.1.2/src/agentsonar/_integrations/_safe_engine.py +228 -0
  33. agentsonar-0.1.2/src/agentsonar/_integrations/crewai_listener.py +217 -0
  34. agentsonar-0.1.2/src/agentsonar/_integrations/langgraph_callback.py +431 -0
  35. agentsonar-0.1.2/src/agentsonar/_output/__init__.py +16 -0
  36. agentsonar-0.1.2/src/agentsonar/_output/_slug.py +127 -0
  37. agentsonar-0.1.2/src/agentsonar/_output/html_report.py +919 -0
  38. agentsonar-0.1.2/src/agentsonar/_output/json_export.py +161 -0
  39. agentsonar-0.1.2/src/agentsonar/_output/terminal.py +1052 -0
  40. agentsonar-0.1.2/tests/__init__.py +0 -0
  41. agentsonar-0.1.2/tests/test_clean_run_signals.py +170 -0
  42. agentsonar-0.1.2/tests/test_cross_platform.py +426 -0
  43. agentsonar-0.1.2/tests/test_detector.py +911 -0
  44. agentsonar-0.1.2/tests/test_exports.py +923 -0
  45. agentsonar-0.1.2/tests/test_host_safety.py +343 -0
  46. agentsonar-0.1.2/tests/test_langgraph.py +466 -0
  47. agentsonar-0.1.2/tests/test_logger.py +1341 -0
  48. agentsonar-0.1.2/tests/test_mid_execution_safety.py +499 -0
  49. agentsonar-0.1.2/tests/test_patterns.py +609 -0
  50. agentsonar-0.1.2/tests/test_performance.py +298 -0
  51. agentsonar-0.1.2/tests/test_public_api.py +301 -0
  52. agentsonar-0.1.2/tests/test_round2_fixes.py +665 -0
  53. agentsonar-0.1.2/tests/test_round3_fixes.py +522 -0
  54. agentsonar-0.1.2/tests/test_round4_fixes.py +321 -0
  55. agentsonar-0.1.2/tests/test_schema.py +927 -0
  56. agentsonar-0.1.2/tests/test_slug.py +161 -0
  57. agentsonar-0.1.2/tests/test_stress_and_edge_cases.py +608 -0
@@ -0,0 +1,16 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # AgentSonar runtime output — JSONL logs and HTML/JSON reports produced
13
+ # when running examples or user code in the repo root.
14
+ agentsonar_*.log
15
+ agentsonar_*.json
16
+ agentsonar_*.html
@@ -0,0 +1,463 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentsonar
3
+ Version: 0.1.2
4
+ Summary: Detect coordination failures in multi-agent AI systems (CrewAI, LangGraph, and more)
5
+ Author: AgentSonar
6
+ License: Apache-2.0
7
+ Keywords: agents,ai,crewai,detection,langgraph,monitoring,multi-agent,observability
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Topic :: System :: Monitoring
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: networkx>=3.0
22
+ Provides-Extra: all
23
+ Requires-Dist: crewai>=0.70.0; extra == 'all'
24
+ Requires-Dist: langchain-core>=0.3.0; extra == 'all'
25
+ Requires-Dist: langgraph>=0.2.0; extra == 'all'
26
+ Provides-Extra: crewai
27
+ Requires-Dist: crewai>=0.70.0; extra == 'crewai'
28
+ Provides-Extra: langgraph
29
+ Requires-Dist: langchain-core>=0.3.0; extra == 'langgraph'
30
+ Requires-Dist: langgraph>=0.2.0; extra == 'langgraph'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # AgentSonar
34
+
35
+ AgentSonar detects coordination failures in multi-agent AI systems —
36
+ cycles, repetitive delegation, and runaway throughput — before they
37
+ burn through your token budget. **Two lines of code to integrate.**
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install agentsonar[crewai] # for CrewAI
43
+ pip install agentsonar[langgraph] # for LangGraph / LangChain
44
+ pip install agentsonar[all] # both
45
+ ```
46
+
47
+ Framework integrations are optional extras — install only what you need.
48
+
49
+ ## Usage
50
+
51
+ ### CrewAI
52
+
53
+ ```python
54
+ from agentsonar import AgentSonarListener
55
+
56
+ sonar = AgentSonarListener()
57
+ # ...run your crew normally. Detection happens automatically.
58
+ ```
59
+
60
+ → Full runnable example: [CrewAI minimal setup](#crewai-minimal-setup).
61
+
62
+ ### LangGraph / LangChain
63
+
64
+ Two equivalent patterns — pick whichever fits your existing code better.
65
+
66
+ **Pattern 1: `monitor()` wrapper (recommended if you already have callbacks)**
67
+
68
+ ```python
69
+ from agentsonar import monitor
70
+
71
+ graph = monitor(graph)
72
+ result = graph.invoke(input)
73
+ ```
74
+
75
+ `monitor()` wraps the compiled graph so every `invoke`/`stream`/`ainvoke`/
76
+ `astream` call auto-injects AgentSonar's callback into your config — without
77
+ overriding any callbacks you pass. If you already have your own callbacks:
78
+
79
+ ```python
80
+ graph = monitor(graph)
81
+ result = graph.invoke(input, config={"callbacks": [my_cb]})
82
+ # Both callbacks run: [my_cb, AgentSonarCallback()]
83
+ ```
84
+
85
+ **Pattern 2: direct callback injection**
86
+
87
+ ```python
88
+ from agentsonar import AgentSonarCallback
89
+
90
+ result = graph.invoke(
91
+ input,
92
+ config={"callbacks": [AgentSonarCallback()]},
93
+ )
94
+ ```
95
+
96
+ Use this pattern when you want explicit control over callback order. You're
97
+ responsible for merging AgentSonar with any existing callbacks yourself —
98
+ the `monitor()` wrapper handles that automatically.
99
+
100
+ → Full runnable example: [LangGraph minimal setup](#langgraph-minimal-setup).
101
+
102
+ ---
103
+
104
+ That's the whole API. Zero config required to get started — no API keys,
105
+ no accounts. Alerts stream to stderr as they fire and land in per-run
106
+ log files on disk. See [Output](#output) for everything AgentSonar writes
107
+ and [Configuration](#configuration) for the knobs you can tune.
108
+
109
+ <a id="output"></a>
110
+ ## Output
111
+
112
+ Every run creates its **own session directory** under `agentsonar_logs/`
113
+ in your working directory. All artifacts for a single run live together:
114
+
115
+ ```
116
+ your_project/
117
+ └── agentsonar_logs/
118
+ ├── .gitignore # auto-written, contains *
119
+ ├── latest # plain text: current run dir name
120
+ ├── run-2026-04-11_07-03-23-ancient-ember/ # most recent run
121
+ │ ├── timeline.jsonl # every event, JSONL
122
+ │ ├── alerts.log # human-readable signal-only
123
+ │ ├── report.json # structured summary report
124
+ │ └── report.html # standalone HTML report
125
+ └── run-2026-04-11_07-03-13-quiet-blossom/ # previous run
126
+ └── ...
127
+ ```
128
+
129
+ Live alerts also stream to **stderr** as they fire, prefixed with
130
+ `[SONAR HH:MM:SS.mmm]`.
131
+
132
+ ### Calling shutdown()
133
+
134
+ The JSON and HTML reports are generated on `shutdown()` — so **how**
135
+ you end your run determines whether those two files get written.
136
+
137
+ **LangGraph / LangChain** — call `shutdown()` yourself when the run
138
+ completes. Both wrapper patterns expose it:
139
+
140
+ ```python
141
+ # Pattern 1: monitor() wrapper — shutdown lives on the wrapper
142
+ graph = monitor(graph)
143
+ graph.invoke(input)
144
+ graph.shutdown() # ← writes report.json + report.html, closes log files
145
+ ```
146
+
147
+ ```python
148
+ # Pattern 2: direct AgentSonarCallback — shutdown lives on the callback
149
+ sonar = AgentSonarCallback()
150
+ graph.invoke(input, config={"callbacks": [sonar]})
151
+ sonar.shutdown() # ← writes report.json + report.html, closes log files
152
+ ```
153
+
154
+ If you forget to call it, `timeline.jsonl` and the stderr stream are
155
+ still captured (they flush event-by-event), but the structured
156
+ `report.json` / `report.html` summary files won't be generated for that run.
157
+
158
+ **CrewAI** — no teardown needed. `AgentSonarListener` hooks
159
+ `CrewKickoffCompletedEvent` on the CrewAI event bus and runs
160
+ `shutdown()` automatically when `crew.kickoff()` finishes. You get the
161
+ full output set — including `report.json` and `report.html` — without
162
+ any extra code.
163
+
164
+ ### What `report.html` looks like
165
+
166
+ The standalone HTML report (`report.html`) is a self-contained page —
167
+ no external CSS or JavaScript, no network requests, safe to email,
168
+ archive, or commit as a debugging artifact. Each coordination event
169
+ renders as a card with its severity, failure class (with hover
170
+ tooltip), summary, fingerprint, and expandable topology / thresholds
171
+ blocks. Dark mode respects your system preference and persists
172
+ across runs. Open it with your browser — or forward to a colleague
173
+ and they'll see the same interactive view without any install steps.
174
+
175
+ ### Realtime tailing
176
+
177
+ `timeline.jsonl` is flushed on every event, so you can watch coordination
178
+ problems land as they happen — useful when a long-running crew is behaving
179
+ oddly and you want to catch the exact delegation that triggered an alert:
180
+
181
+ ```bash
182
+ # Unix / macOS — tail the current run's timeline
183
+ tail -f "agentsonar_logs/$(cat agentsonar_logs/latest)/timeline.jsonl"
184
+
185
+ # Only the signal-only (alerts) view
186
+ tail -f "agentsonar_logs/$(cat agentsonar_logs/latest)/alerts.log"
187
+ ```
188
+ ```powershell
189
+ # Windows PowerShell — -Wait is the tail-follow equivalent
190
+ Get-Content "agentsonar_logs/$(Get-Content agentsonar_logs/latest)/timeline.jsonl" -Wait
191
+ ```
192
+
193
+ Each JSONL line is a self-contained record with `ts`, `level`, `event`,
194
+ and a `data` payload — easy to pipe through `jq` or a custom parser if
195
+ you want realtime dashboards during a run.
196
+
197
+ <a id="clean-run-signals"></a>
198
+ ### Clean-run signals
199
+
200
+ When the run completes without any WARNING or CRITICAL alerts, AgentSonar
201
+ tells you explicitly in every channel instead of leaving you to wonder:
202
+
203
+ - **HTML report** — a green "✓ No coordination failures detected" banner
204
+ replaces the event cards.
205
+ - **`timeline.jsonl`** — the final `session_end` record carries
206
+ `"clean_run": true`, `"warning_count": 0`, `"critical_count": 0`, and
207
+ a plain-English `message` field. Downstream parsers can gate on the
208
+ single boolean instead of walking the whole alerts list.
209
+ - **stderr** — a green `✓ No coordination failures detected — clean run.`
210
+ line prints right under the summary banner.
211
+
212
+ Non-clean runs flip `clean_run` to `false` and the `message` includes
213
+ the severity breakdown (e.g. `"2 CRITICAL, 5 WARNING coordination alert(s)
214
+ detected during this run."`).
215
+
216
+ ### Run naming, latest pointer, retention
217
+
218
+ Run directories are named `run-<ISO date>_<time>-<adjective>-<noun>`
219
+ (e.g. `run-2026-04-11_07-03-23-ancient-ember`). The timestamp sorts
220
+ chronologically; the slug is memorable enough to say out loud and is
221
+ deterministic from the session id.
222
+
223
+ `agentsonar_logs/latest` is a plain text file pointing at the newest
224
+ run directory name. Open the newest HTML report with:
225
+
226
+ ```bash
227
+ # Unix / macOS
228
+ open "agentsonar_logs/$(cat agentsonar_logs/latest)/report.html"
229
+ ```
230
+ ```powershell
231
+ # Windows PowerShell
232
+ Invoke-Item "agentsonar_logs/$(Get-Content agentsonar_logs/latest)/report.html"
233
+ ```
234
+
235
+ AgentSonar keeps the **20 most recent runs** by default and prunes
236
+ older ones on every new session. Configure via `AGENTSONAR_KEEP_RUNS`
237
+ or `config={"keep_runs": N}`; set to `0` to disable pruning.
238
+
239
+ The auto-written `.gitignore` inside `agentsonar_logs/` contains `*`,
240
+ so every log and report is git-ignored by default.
241
+
242
+ ### Opt-out
243
+
244
+ If you don't want the JSON/HTML reports, disable them in the config:
245
+
246
+ ```python
247
+ sonar = AgentSonarCallback(config={"auto_export_on_shutdown": False})
248
+ ```
249
+
250
+ ### Manual export
251
+
252
+ If you want to export at multiple checkpoints during a run, or want the
253
+ full timeline view (`dedupe=False`), call the exporters explicitly:
254
+
255
+ ```python
256
+ from agentsonar._output.json_export import export_json
257
+ from agentsonar._output.html_report import export_html
258
+
259
+ events = sonar.engine.get_recent_events()
260
+
261
+ # Summary view (default): one line per root cause
262
+ export_json(events)
263
+ export_html(events, title="Checkpoint 1")
264
+
265
+ # Timeline view: every state transition preserved
266
+ export_json(events, dedupe=False)
267
+ ```
268
+
269
+ <a id="configuration"></a>
270
+ <details>
271
+ <summary><strong>Configuration reference</strong></summary>
272
+
273
+ Every entry point — `monitor()`, `AgentSonarCallback()`, and
274
+ `AgentSonarListener()` — accepts an optional `config` dict to override
275
+ the defaults. Pass only the keys you want to change; everything else
276
+ keeps its default.
277
+
278
+ ```python
279
+ graph = monitor(graph, config={
280
+ # Rate limits — raise these when you're running at demo speeds
281
+ # (much faster than any real LLM workload) and don't want the
282
+ # circuit breaker to trip before detection has a chance to see
283
+ # the full pattern.
284
+ "per_edge_limit": 99999, # default 10 events per edge per window
285
+ "global_limit": 99999, # default 200 events total per window
286
+ "window_size": 180.0, # default 180.0 seconds
287
+
288
+ # Alert severity thresholds — how many rotations/events before
289
+ # WARNING and CRITICAL fire. Lower them for tight tests, raise
290
+ # them for noisy production workloads.
291
+ "warning_threshold": 5, # default 5
292
+ "critical_threshold": 15, # default 15
293
+
294
+ # Output location and retention
295
+ "log_dir": ".", # parent directory for agentsonar_logs/
296
+ "keep_runs": 20, # most recent run dirs to keep (0 = no pruning)
297
+ "console_output": True, # stream colored alerts to stderr
298
+ "file_output": True, # write timeline.jsonl + alerts.log
299
+ "auto_export_on_shutdown": True, # write report.json + report.html
300
+ })
301
+ ```
302
+
303
+ The same dict works for every entry point:
304
+
305
+ ```python
306
+ AgentSonarCallback(config={"per_edge_limit": 99999})
307
+ AgentSonarListener(config={"warning_threshold": 3})
308
+ ```
309
+
310
+ The rate-limit knobs are the most common override. AgentSonar's default
311
+ circuit breaker is tuned for real LLM workloads (a few hundred ms per
312
+ call); scripted demos and unit tests fire events orders of magnitude
313
+ faster, which trips the breaker before the downstream cycle/repetition
314
+ detectors get a chance to fire. Bumping `per_edge_limit` and
315
+ `global_limit` to a large value disables that short-circuit for local
316
+ runs.
317
+
318
+ </details>
319
+
320
+ ## What gets detected
321
+
322
+ Every detected event carries a `failure_class` string that names the kind
323
+ of problem. The classes AgentSonar currently surfaces:
324
+
325
+ - **`cyclic_delegation`** — Agents are stuck in a loop. A delegates to B,
326
+ B to C, C back to A. Usually means an exit condition is missing — a
327
+ reviewer that never approves, a planner that always says "revise".
328
+
329
+ - **`repetitive_delegation`** — One agent keeps calling another without
330
+ making progress. `A → B` fires many times in a short window with no
331
+ `B → A` return. Usually means A can't make a decision without B and
332
+ isn't getting what it needs.
333
+
334
+ - **`resource_exhaustion`** — The system is processing events faster
335
+ than it can sustain. Either one edge is being hammered or total
336
+ throughput is over budget. Indicates a runaway agent or throttling
337
+ that's too loose.
338
+
339
+ **Coming soon** (reserved class names; no-op today):
340
+ `cascade_failure`, `authority_violation`, `deadlock`, `agent_stall`,
341
+ `token_velocity_anomaly`.
342
+
343
+ The HTML report shows a hover tooltip on every failure class badge with
344
+ the same plain-English description.
345
+
346
+ ## Coordination fingerprint
347
+
348
+ Every detected failure carries a `coordination_fingerprint` like
349
+ `sha256:5c102a66e1104c47`. It's a stable ID for the failure pattern:
350
+ the same failure (same agents in the same shape) always produces the
351
+ same fingerprint, regardless of when it's detected or how many times
352
+ it re-escalates. You use it for:
353
+
354
+ - **Dedup.** `WARNING → CRITICAL` escalations share a fingerprint, so
355
+ the summary view collapses to one row at the highest severity.
356
+ - **Root-cause grouping.** When a cycle is firing, the repetitive-edge
357
+ alerts on each cycle edge are suppressed in the summary view.
358
+ - **Cross-run correlation.** Grep any log file for the fingerprint to
359
+ find every time the same pattern fired, across sessions.
360
+
361
+ You never compute fingerprints yourself — they're just stable IDs
362
+ you can use to talk about "the same failure" across time.
363
+
364
+ ## Host safety
365
+
366
+ AgentSonar never crashes your app. If anything inside the SDK fails,
367
+ detection degrades silently to a no-op and your crew / graph / API
368
+ keeps running. The kill switch is `AGENTSONAR_DISABLED=1` (also accepts
369
+ `true`, `yes`, `on`, `enabled`, case-insensitive) — set it in the
370
+ environment to disable AgentSonar without editing code. When running
371
+ in degraded mode, `get_summary()` returns `{"degraded": True, ...}`
372
+ so you can alert on it from your own dashboards.
373
+
374
+ ## Full minimal setup
375
+
376
+ Copy-pasteable starting points. The AgentSonar lines are marked — the
377
+ rest is plain framework code. Both examples are self-contained and
378
+ runnable as-is.
379
+
380
+ <a id="langgraph-minimal-setup"></a>
381
+ ### LangGraph minimal setup
382
+
383
+ ```python
384
+ # pip install agentsonar[langgraph]
385
+ import operator
386
+ from typing import Literal
387
+ from langchain_core.messages import AIMessage, AnyMessage, HumanMessage
388
+ from langgraph.graph import END, START, StateGraph
389
+ from typing_extensions import Annotated, TypedDict
390
+
391
+ from agentsonar import monitor # ← AgentSonar
392
+
393
+ class State(TypedDict):
394
+ messages: Annotated[list[AnyMessage], operator.add]
395
+ iteration: int
396
+
397
+ def planner(s): return {"messages": [AIMessage(content="plan")]}
398
+ def researcher(s): return {"messages": [AIMessage(content="research")]}
399
+ def reviewer(s): return {"messages": [AIMessage(content="revise")],
400
+ "iteration": s.get("iteration", 0) + 1}
401
+ def loop(s) -> Literal["planner", "__end__"]:
402
+ return END if s.get("iteration", 0) >= 8 else "planner"
403
+
404
+ b = StateGraph(State)
405
+ for name, fn in [("planner", planner), ("researcher", researcher), ("reviewer", reviewer)]:
406
+ b.add_node(name, fn)
407
+ b.add_edge(START, "planner")
408
+ b.add_edge("planner", "researcher")
409
+ b.add_edge("researcher", "reviewer")
410
+ b.add_conditional_edges("reviewer", loop)
411
+
412
+ graph = monitor(b.compile()) # ← AgentSonar
413
+ graph.invoke({"messages": [HumanMessage(content="go")], "iteration": 0},
414
+ config={"recursion_limit": 50})
415
+ graph.shutdown() # ← AgentSonar
416
+ ```
417
+
418
+ <a id="crewai-minimal-setup"></a>
419
+ ### CrewAI minimal setup
420
+
421
+ ```python
422
+ # pip install agentsonar[crewai]
423
+ # export OPENAI_API_KEY=sk-...
424
+ from crewai import Agent, Crew, Process, Task
425
+
426
+ from agentsonar import AgentSonarListener # ← AgentSonar
427
+ sonar = AgentSonarListener() # ← AgentSonar
428
+
429
+ researcher = Agent(role="Researcher", goal="Gather info on the topic.",
430
+ backstory="Senior researcher.", allow_delegation=False)
431
+ writer = Agent(role="Writer", goal="Write a short summary.",
432
+ backstory="Technical writer.", allow_delegation=False)
433
+ manager = Agent(role="Manager", goal="Coordinate researcher and writer.",
434
+ backstory="Project manager.", allow_delegation=True)
435
+
436
+ task = Task(
437
+ description="Write a 3-sentence summary of multi-agent coordination. "
438
+ "Delegate research, then writing.",
439
+ expected_output="A 3-sentence summary.",
440
+ agent=manager,
441
+ )
442
+
443
+ # In hierarchical mode the manager goes ONLY in `manager_agent`,
444
+ # never in `agents`. Workers go in `agents`.
445
+ Crew(agents=[researcher, writer], tasks=[task],
446
+ process=Process.hierarchical, manager_agent=manager).kickoff()
447
+ # AgentSonarListener auto-shuts down on CrewKickoffCompletedEvent.
448
+ ```
449
+
450
+ After either script finishes, open the HTML report:
451
+
452
+ ```bash
453
+ open "agentsonar_logs/$(cat agentsonar_logs/latest)/report.html"
454
+ ```
455
+
456
+ ## Status
457
+
458
+ Closed beta. Schema, public API, and output formats are stable for
459
+ design partners.
460
+
461
+ ## License
462
+
463
+ Apache-2.0