sibyl-dev 0.2.2__tar.gz → 0.2.4__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.
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/PKG-INFO +1 -1
- sibyl_dev-0.2.4/src/sibyl_cli/agent.py +431 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/client.py +116 -0
- sibyl_dev-0.2.4/src/sibyl_cli/debug.py +277 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/local.py +2 -1
- sibyl_dev-0.2.4/src/sibyl_cli/logs.py +245 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/main.py +9 -2
- sibyl_dev-0.2.4/src/sibyl_cli/runner.py +285 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/.gitignore +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/README.md +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/pyproject.toml +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/__init__.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/auth.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/auth_store.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/common.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/config_cmd.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/config_store.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/context.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/crawl.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/document.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/entity.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/epic.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/explore.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/onboarding.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/org.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/project.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/source.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/state.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/src/sibyl_cli/task.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/tests/__init__.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/tests/test_auth_store.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/tests/test_config_store.py +0 -0
- {sibyl_dev-0.2.2 → sibyl_dev-0.2.4}/tests/test_epic.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sibyl-dev
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: CLI for Sibyl - Collective Intelligence Runtime for AI agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/hyperb1iss/sibyl
|
|
6
6
|
Project-URL: Repository, https://github.com/hyperb1iss/sibyl
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"""Agent communication CLI commands.
|
|
2
|
+
|
|
3
|
+
Commands for inter-agent messaging during distributed execution:
|
|
4
|
+
- progress: Report progress to parent agent
|
|
5
|
+
- blocker: Signal a blocker
|
|
6
|
+
- query: Ask another agent a question
|
|
7
|
+
- delegate: Delegate work to another agent
|
|
8
|
+
- review: Request code review
|
|
9
|
+
- inbox: Check pending messages
|
|
10
|
+
- respond: Respond to a message
|
|
11
|
+
- conversation: View conversation history with another agent
|
|
12
|
+
|
|
13
|
+
These commands enable Claude Code agents to communicate and coordinate through Sibyl.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from typing import Annotated
|
|
17
|
+
|
|
18
|
+
import typer
|
|
19
|
+
|
|
20
|
+
from sibyl_cli.client import SibylClientError, get_client
|
|
21
|
+
from sibyl_cli.common import (
|
|
22
|
+
CORAL,
|
|
23
|
+
ELECTRIC_PURPLE,
|
|
24
|
+
NEON_CYAN,
|
|
25
|
+
console,
|
|
26
|
+
create_table,
|
|
27
|
+
handle_client_error,
|
|
28
|
+
info,
|
|
29
|
+
print_json,
|
|
30
|
+
run_async,
|
|
31
|
+
success,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
app = typer.Typer(
|
|
35
|
+
name="agent",
|
|
36
|
+
help="Inter-agent communication for distributed execution",
|
|
37
|
+
no_args_is_help=True,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _validate_agent_id(agent_id: str) -> str:
|
|
42
|
+
"""Validate agent ID format."""
|
|
43
|
+
if not agent_id.startswith("agent_"):
|
|
44
|
+
raise SibylClientError(
|
|
45
|
+
f"Invalid agent ID format: {agent_id}. Expected format: agent_<hex>",
|
|
46
|
+
status_code=400,
|
|
47
|
+
detail=f"Invalid agent ID: {agent_id}",
|
|
48
|
+
)
|
|
49
|
+
return agent_id
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _format_message_type(msg_type: str) -> str:
|
|
53
|
+
"""Format message type for display."""
|
|
54
|
+
type_colors = {
|
|
55
|
+
"progress": "green",
|
|
56
|
+
"query": "cyan",
|
|
57
|
+
"response": "blue",
|
|
58
|
+
"review_request": "yellow",
|
|
59
|
+
"review_result": "yellow",
|
|
60
|
+
"blocker": "red",
|
|
61
|
+
"delegation": "magenta",
|
|
62
|
+
"system": "dim",
|
|
63
|
+
}
|
|
64
|
+
color = type_colors.get(msg_type, "white")
|
|
65
|
+
return f"[{color}]{msg_type}[/{color}]"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@app.command()
|
|
69
|
+
def progress(
|
|
70
|
+
agent_id: Annotated[str, typer.Argument(help="Your agent ID")],
|
|
71
|
+
message: Annotated[str, typer.Argument(help="Progress message")],
|
|
72
|
+
percent: Annotated[
|
|
73
|
+
int | None, typer.Option("--percent", "-p", help="Progress percentage (0-100)")
|
|
74
|
+
] = None,
|
|
75
|
+
to: Annotated[
|
|
76
|
+
str | None, typer.Option("--to", "-t", help="Target agent (default: orchestrator)")
|
|
77
|
+
] = None,
|
|
78
|
+
json_out: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
|
79
|
+
) -> None:
|
|
80
|
+
"""Report progress to orchestrator or another agent.
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
sibyl agent progress agent_abc123 "Completed code review" --percent 75
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
@run_async
|
|
87
|
+
async def _run() -> None:
|
|
88
|
+
client = get_client()
|
|
89
|
+
context = {}
|
|
90
|
+
if percent is not None:
|
|
91
|
+
context["progress_percent"] = percent
|
|
92
|
+
|
|
93
|
+
result = await client.send_agent_message(
|
|
94
|
+
from_agent_id=agent_id,
|
|
95
|
+
message_type="progress",
|
|
96
|
+
subject=f"Progress: {percent}%" if percent else "Progress update",
|
|
97
|
+
content=message,
|
|
98
|
+
to_agent_id=to,
|
|
99
|
+
context=context if context else None,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
if json_out:
|
|
103
|
+
print_json(result)
|
|
104
|
+
else:
|
|
105
|
+
success(f"Progress reported: {message[:50]}{'...' if len(message) > 50 else ''}")
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
_run()
|
|
109
|
+
except SibylClientError as e:
|
|
110
|
+
handle_client_error(e)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@app.command()
|
|
114
|
+
def blocker(
|
|
115
|
+
agent_id: Annotated[str, typer.Argument(help="Your agent ID")],
|
|
116
|
+
subject: Annotated[str, typer.Argument(help="Short blocker description")],
|
|
117
|
+
details: Annotated[str, typer.Argument(help="Full details about the blocker")],
|
|
118
|
+
resource: Annotated[
|
|
119
|
+
str | None, typer.Option("--resource", "-r", help="Blocking resource (file, API, etc.)")
|
|
120
|
+
] = None,
|
|
121
|
+
json_out: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
|
122
|
+
) -> None:
|
|
123
|
+
"""Signal a blocker to the orchestrator.
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
sibyl agent blocker agent_abc123 "API rate limit" "Hit GitHub API rate limit, need to wait"
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
@run_async
|
|
130
|
+
async def _run() -> None:
|
|
131
|
+
client = get_client()
|
|
132
|
+
context = {}
|
|
133
|
+
if resource:
|
|
134
|
+
context["blocking_resource"] = resource
|
|
135
|
+
|
|
136
|
+
result = await client.send_agent_message(
|
|
137
|
+
from_agent_id=agent_id,
|
|
138
|
+
message_type="blocker",
|
|
139
|
+
subject=subject,
|
|
140
|
+
content=details,
|
|
141
|
+
priority=7, # Blockers are high priority
|
|
142
|
+
context=context if context else None,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if json_out:
|
|
146
|
+
print_json(result)
|
|
147
|
+
else:
|
|
148
|
+
console.print(f"[red]Blocker signaled:[/red] {subject}")
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
_run()
|
|
152
|
+
except SibylClientError as e:
|
|
153
|
+
handle_client_error(e)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@app.command()
|
|
157
|
+
def query(
|
|
158
|
+
agent_id: Annotated[str, typer.Argument(help="Your agent ID")],
|
|
159
|
+
to_agent: Annotated[str, typer.Argument(help="Target agent ID to query")],
|
|
160
|
+
subject: Annotated[str, typer.Argument(help="Query subject")],
|
|
161
|
+
question: Annotated[str, typer.Argument(help="Your question")],
|
|
162
|
+
json_out: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Ask another agent a question (requires response).
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
sibyl agent query agent_abc123 agent_def456 "Auth approach" "How should I handle token refresh?"
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
@run_async
|
|
171
|
+
async def _run() -> None:
|
|
172
|
+
client = get_client()
|
|
173
|
+
result = await client.send_agent_message(
|
|
174
|
+
from_agent_id=agent_id,
|
|
175
|
+
message_type="query",
|
|
176
|
+
subject=subject,
|
|
177
|
+
content=question,
|
|
178
|
+
to_agent_id=to_agent,
|
|
179
|
+
requires_response=True,
|
|
180
|
+
priority=5,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if json_out:
|
|
184
|
+
print_json(result)
|
|
185
|
+
else:
|
|
186
|
+
msg_id = result.get("id", "unknown")
|
|
187
|
+
console.print(f"[{NEON_CYAN}]Query sent to {to_agent}[/{NEON_CYAN}]")
|
|
188
|
+
console.print(f"Message ID: [{CORAL}]{msg_id}[/{CORAL}]")
|
|
189
|
+
info("Check inbox later for response")
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
_run()
|
|
193
|
+
except SibylClientError as e:
|
|
194
|
+
handle_client_error(e)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@app.command()
|
|
198
|
+
def delegate(
|
|
199
|
+
agent_id: Annotated[str, typer.Argument(help="Your agent ID")],
|
|
200
|
+
to_agent: Annotated[str, typer.Argument(help="Agent to delegate to")],
|
|
201
|
+
subject: Annotated[str, typer.Argument(help="Delegation title")],
|
|
202
|
+
work: Annotated[str, typer.Argument(help="Work description")],
|
|
203
|
+
task_id: Annotated[str | None, typer.Option("--task", "-t", help="Associated task ID")] = None,
|
|
204
|
+
json_out: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
|
205
|
+
) -> None:
|
|
206
|
+
"""Delegate work to another agent.
|
|
207
|
+
|
|
208
|
+
Example:
|
|
209
|
+
sibyl agent delegate agent_abc123 agent_def456 "Write tests" "Add unit tests for auth module"
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
@run_async
|
|
213
|
+
async def _run() -> None:
|
|
214
|
+
client = get_client()
|
|
215
|
+
context = {}
|
|
216
|
+
if task_id:
|
|
217
|
+
context["task_id"] = task_id
|
|
218
|
+
|
|
219
|
+
result = await client.send_agent_message(
|
|
220
|
+
from_agent_id=agent_id,
|
|
221
|
+
message_type="delegation",
|
|
222
|
+
subject=subject,
|
|
223
|
+
content=work,
|
|
224
|
+
to_agent_id=to_agent,
|
|
225
|
+
priority=5,
|
|
226
|
+
context=context if context else None,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
if json_out:
|
|
230
|
+
print_json(result)
|
|
231
|
+
else:
|
|
232
|
+
console.print(f"[{ELECTRIC_PURPLE}]Work delegated to {to_agent}[/{ELECTRIC_PURPLE}]")
|
|
233
|
+
console.print(f"Subject: {subject}")
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
_run()
|
|
237
|
+
except SibylClientError as e:
|
|
238
|
+
handle_client_error(e)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@app.command()
|
|
242
|
+
def review(
|
|
243
|
+
agent_id: Annotated[str, typer.Argument(help="Your agent ID")],
|
|
244
|
+
to_agent: Annotated[str, typer.Argument(help="Agent to request review from")],
|
|
245
|
+
subject: Annotated[str, typer.Argument(help="Review request title")],
|
|
246
|
+
description: Annotated[str, typer.Argument(help="What to review and why")],
|
|
247
|
+
files: Annotated[
|
|
248
|
+
str | None, typer.Option("--files", "-f", help="Comma-separated list of files")
|
|
249
|
+
] = None,
|
|
250
|
+
json_out: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
|
251
|
+
) -> None:
|
|
252
|
+
"""Request code review from another agent.
|
|
253
|
+
|
|
254
|
+
Example:
|
|
255
|
+
sibyl agent review agent_abc123 agent_def456 "Auth changes" "Please review the OAuth implementation" --files "auth.py,login.py"
|
|
256
|
+
"""
|
|
257
|
+
|
|
258
|
+
@run_async
|
|
259
|
+
async def _run() -> None:
|
|
260
|
+
client = get_client()
|
|
261
|
+
context = {}
|
|
262
|
+
if files:
|
|
263
|
+
context["files"] = [f.strip() for f in files.split(",")]
|
|
264
|
+
|
|
265
|
+
result = await client.send_agent_message(
|
|
266
|
+
from_agent_id=agent_id,
|
|
267
|
+
message_type="review_request",
|
|
268
|
+
subject=subject,
|
|
269
|
+
content=description,
|
|
270
|
+
to_agent_id=to_agent,
|
|
271
|
+
requires_response=True,
|
|
272
|
+
priority=5,
|
|
273
|
+
context=context if context else None,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if json_out:
|
|
277
|
+
print_json(result)
|
|
278
|
+
else:
|
|
279
|
+
msg_id = result.get("id", "unknown")
|
|
280
|
+
console.print(f"[yellow]Review requested from {to_agent}[/yellow]")
|
|
281
|
+
console.print(f"Message ID: [{CORAL}]{msg_id}[/{CORAL}]")
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
_run()
|
|
285
|
+
except SibylClientError as e:
|
|
286
|
+
handle_client_error(e)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@app.command()
|
|
290
|
+
def inbox(
|
|
291
|
+
agent_id: Annotated[str, typer.Argument(help="Your agent ID")],
|
|
292
|
+
limit: Annotated[int, typer.Option("--limit", "-l", help="Max messages to show")] = 20,
|
|
293
|
+
include_read: Annotated[
|
|
294
|
+
bool, typer.Option("--all", "-a", help="Include read messages")
|
|
295
|
+
] = False,
|
|
296
|
+
digest: Annotated[
|
|
297
|
+
bool, typer.Option("--digest", "-d", help="Output as formatted digest")
|
|
298
|
+
] = False,
|
|
299
|
+
json_out: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
|
300
|
+
) -> None:
|
|
301
|
+
"""Check pending messages for an agent.
|
|
302
|
+
|
|
303
|
+
Example:
|
|
304
|
+
sibyl agent inbox agent_abc123
|
|
305
|
+
sibyl agent inbox agent_abc123 --digest # For Claude Code injection
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
@run_async
|
|
309
|
+
async def _run() -> None:
|
|
310
|
+
client = get_client()
|
|
311
|
+
|
|
312
|
+
if digest:
|
|
313
|
+
result = await client.get_message_digest(agent_id, limit=limit)
|
|
314
|
+
if json_out:
|
|
315
|
+
print_json(result)
|
|
316
|
+
else:
|
|
317
|
+
digest_text = result.get("digest", "")
|
|
318
|
+
if digest_text:
|
|
319
|
+
console.print(digest_text)
|
|
320
|
+
else:
|
|
321
|
+
info("No pending messages")
|
|
322
|
+
return
|
|
323
|
+
|
|
324
|
+
result = await client.get_pending_messages(agent_id, limit=limit, include_read=include_read)
|
|
325
|
+
|
|
326
|
+
if json_out:
|
|
327
|
+
print_json(result)
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
messages = result.get("messages", [])
|
|
331
|
+
if not messages:
|
|
332
|
+
info("No pending messages")
|
|
333
|
+
return
|
|
334
|
+
|
|
335
|
+
table = create_table(f"Messages for {agent_id}", "Subject", "From", "Type", "Priority")
|
|
336
|
+
for msg in messages:
|
|
337
|
+
table.add_row(
|
|
338
|
+
msg.get("subject", "")[:40],
|
|
339
|
+
msg.get("from_agent_id", "")[:15],
|
|
340
|
+
_format_message_type(msg.get("message_type", "")),
|
|
341
|
+
str(msg.get("priority", 0)),
|
|
342
|
+
)
|
|
343
|
+
console.print(table)
|
|
344
|
+
console.print(f"\nTotal: {result.get('count', 0)} messages")
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
_run()
|
|
348
|
+
except SibylClientError as e:
|
|
349
|
+
handle_client_error(e)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@app.command()
|
|
353
|
+
def respond(
|
|
354
|
+
agent_id: Annotated[str, typer.Argument(help="Your agent ID")],
|
|
355
|
+
message_id: Annotated[str, typer.Argument(help="Message ID to respond to")],
|
|
356
|
+
response: Annotated[str, typer.Argument(help="Your response")],
|
|
357
|
+
json_out: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
|
358
|
+
) -> None:
|
|
359
|
+
"""Respond to a message.
|
|
360
|
+
|
|
361
|
+
Example:
|
|
362
|
+
sibyl agent respond agent_abc123 <message-uuid> "Use the retry pattern with exponential backoff"
|
|
363
|
+
"""
|
|
364
|
+
|
|
365
|
+
@run_async
|
|
366
|
+
async def _run() -> None:
|
|
367
|
+
client = get_client()
|
|
368
|
+
result = await client.respond_to_message(
|
|
369
|
+
message_id=message_id,
|
|
370
|
+
from_agent_id=agent_id,
|
|
371
|
+
content=response,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
if json_out:
|
|
375
|
+
print_json(result)
|
|
376
|
+
else:
|
|
377
|
+
success(f"Response sent (ID: {result.get('id', 'unknown')[:12]}...)")
|
|
378
|
+
|
|
379
|
+
try:
|
|
380
|
+
_run()
|
|
381
|
+
except SibylClientError as e:
|
|
382
|
+
handle_client_error(e)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
@app.command()
|
|
386
|
+
def conversation(
|
|
387
|
+
agent_id: Annotated[str, typer.Argument(help="Your agent ID")],
|
|
388
|
+
other_agent: Annotated[str, typer.Argument(help="Other agent ID")],
|
|
389
|
+
limit: Annotated[int, typer.Option("--limit", "-l", help="Max messages to show")] = 50,
|
|
390
|
+
json_out: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
|
391
|
+
) -> None:
|
|
392
|
+
"""View conversation history with another agent.
|
|
393
|
+
|
|
394
|
+
Example:
|
|
395
|
+
sibyl agent conversation agent_abc123 agent_def456
|
|
396
|
+
"""
|
|
397
|
+
|
|
398
|
+
@run_async
|
|
399
|
+
async def _run() -> None:
|
|
400
|
+
client = get_client()
|
|
401
|
+
result = await client.get_agent_conversation(agent_id, other_agent, limit=limit)
|
|
402
|
+
|
|
403
|
+
if json_out:
|
|
404
|
+
print_json(result)
|
|
405
|
+
return
|
|
406
|
+
|
|
407
|
+
messages = result.get("messages", [])
|
|
408
|
+
if not messages:
|
|
409
|
+
info(f"No conversation history with {other_agent}")
|
|
410
|
+
return
|
|
411
|
+
|
|
412
|
+
console.print(f"\n[bold]Conversation: {agent_id} <-> {other_agent}[/bold]\n")
|
|
413
|
+
for msg in messages:
|
|
414
|
+
sender = msg.get("from_agent_id", "")
|
|
415
|
+
is_you = sender == agent_id
|
|
416
|
+
color = NEON_CYAN if is_you else CORAL
|
|
417
|
+
label = "You" if is_you else sender[:12]
|
|
418
|
+
|
|
419
|
+
console.print(f"[{color}]{label}[/{color}] [{msg.get('message_type', '')}]")
|
|
420
|
+
console.print(f" {msg.get('subject', '')}")
|
|
421
|
+
content = msg.get("content", "")
|
|
422
|
+
if len(content) > 100:
|
|
423
|
+
content = content[:100] + "..."
|
|
424
|
+
console.print(f" [dim]{content}[/dim]\n")
|
|
425
|
+
|
|
426
|
+
console.print(f"Total: {result.get('count', 0)} messages")
|
|
427
|
+
|
|
428
|
+
try:
|
|
429
|
+
_run()
|
|
430
|
+
except SibylClientError as e:
|
|
431
|
+
handle_client_error(e)
|
|
@@ -863,6 +863,122 @@ class SibylClient:
|
|
|
863
863
|
"""
|
|
864
864
|
return await self._request("GET", "/sources/link-graph/status")
|
|
865
865
|
|
|
866
|
+
# =========================================================================
|
|
867
|
+
# Inter-Agent Messaging
|
|
868
|
+
# =========================================================================
|
|
869
|
+
|
|
870
|
+
async def send_agent_message(
|
|
871
|
+
self,
|
|
872
|
+
from_agent_id: str,
|
|
873
|
+
message_type: str,
|
|
874
|
+
subject: str,
|
|
875
|
+
content: str,
|
|
876
|
+
*,
|
|
877
|
+
to_agent_id: str | None = None,
|
|
878
|
+
requires_response: bool = False,
|
|
879
|
+
priority: int = 0,
|
|
880
|
+
context: dict[str, Any] | None = None,
|
|
881
|
+
) -> dict[str, Any]:
|
|
882
|
+
"""Send an inter-agent message.
|
|
883
|
+
|
|
884
|
+
Args:
|
|
885
|
+
from_agent_id: Sender agent ID
|
|
886
|
+
message_type: Type of message (progress, query, blocker, etc.)
|
|
887
|
+
subject: Short subject line
|
|
888
|
+
content: Full message content
|
|
889
|
+
to_agent_id: Target agent (None = orchestrator)
|
|
890
|
+
requires_response: True if sender waits for response
|
|
891
|
+
priority: 0-10, higher = more urgent
|
|
892
|
+
context: Additional context data
|
|
893
|
+
"""
|
|
894
|
+
data = {
|
|
895
|
+
"from_agent_id": from_agent_id,
|
|
896
|
+
"message_type": message_type,
|
|
897
|
+
"subject": subject,
|
|
898
|
+
"content": content,
|
|
899
|
+
"requires_response": requires_response,
|
|
900
|
+
"priority": priority,
|
|
901
|
+
}
|
|
902
|
+
if to_agent_id:
|
|
903
|
+
data["to_agent_id"] = to_agent_id
|
|
904
|
+
if context:
|
|
905
|
+
data["context"] = context
|
|
906
|
+
return await self._request("POST", "/agent-messages", json=data)
|
|
907
|
+
|
|
908
|
+
async def get_pending_messages(
|
|
909
|
+
self,
|
|
910
|
+
agent_id: str,
|
|
911
|
+
*,
|
|
912
|
+
limit: int = 50,
|
|
913
|
+
include_read: bool = False,
|
|
914
|
+
) -> dict[str, Any]:
|
|
915
|
+
"""Get pending messages for an agent.
|
|
916
|
+
|
|
917
|
+
Args:
|
|
918
|
+
agent_id: Agent to get messages for
|
|
919
|
+
limit: Max messages to return
|
|
920
|
+
include_read: Include already-read messages
|
|
921
|
+
"""
|
|
922
|
+
params = {"limit": limit, "include_read": str(include_read).lower()}
|
|
923
|
+
return await self._request("GET", f"/agent-messages/pending/{agent_id}", params=params)
|
|
924
|
+
|
|
925
|
+
async def get_message_digest(
|
|
926
|
+
self,
|
|
927
|
+
agent_id: str,
|
|
928
|
+
*,
|
|
929
|
+
limit: int = 50,
|
|
930
|
+
) -> dict[str, Any]:
|
|
931
|
+
"""Get pending messages formatted as digest for injection.
|
|
932
|
+
|
|
933
|
+
Args:
|
|
934
|
+
agent_id: Agent to get messages for
|
|
935
|
+
limit: Max messages to return
|
|
936
|
+
"""
|
|
937
|
+
params = {"limit": limit}
|
|
938
|
+
return await self._request(
|
|
939
|
+
"GET", f"/agent-messages/pending/{agent_id}/digest", params=params
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
async def respond_to_message(
|
|
943
|
+
self,
|
|
944
|
+
message_id: str,
|
|
945
|
+
from_agent_id: str,
|
|
946
|
+
content: str,
|
|
947
|
+
*,
|
|
948
|
+
context: dict[str, Any] | None = None,
|
|
949
|
+
) -> dict[str, Any]:
|
|
950
|
+
"""Respond to an inter-agent message.
|
|
951
|
+
|
|
952
|
+
Args:
|
|
953
|
+
message_id: Message to respond to
|
|
954
|
+
from_agent_id: Agent sending response
|
|
955
|
+
content: Response content
|
|
956
|
+
context: Additional context
|
|
957
|
+
"""
|
|
958
|
+
data: dict[str, Any] = {
|
|
959
|
+
"from_agent_id": from_agent_id,
|
|
960
|
+
"content": content,
|
|
961
|
+
}
|
|
962
|
+
if context:
|
|
963
|
+
data["context"] = context
|
|
964
|
+
return await self._request("POST", f"/agent-messages/{message_id}/respond", json=data)
|
|
965
|
+
|
|
966
|
+
async def mark_message_read(self, message_id: str) -> dict[str, Any]:
|
|
967
|
+
"""Mark a message as read."""
|
|
968
|
+
return await self._request("POST", f"/agent-messages/{message_id}/read")
|
|
969
|
+
|
|
970
|
+
async def get_agent_conversation(
|
|
971
|
+
self,
|
|
972
|
+
agent_id: str,
|
|
973
|
+
other_agent_id: str,
|
|
974
|
+
*,
|
|
975
|
+
limit: int = 100,
|
|
976
|
+
) -> dict[str, Any]:
|
|
977
|
+
"""Get conversation history between two agents."""
|
|
978
|
+
params = {"limit": limit}
|
|
979
|
+
return await self._request(
|
|
980
|
+
"GET", f"/agent-messages/conversation/{agent_id}/{other_agent_id}", params=params
|
|
981
|
+
)
|
|
866
982
|
|
|
867
983
|
# Client cache by context name (None = default/active context)
|
|
868
984
|
_clients: dict[str | None, SibylClient] = {}
|