devduck 0.1.0__py3-none-any.whl → 0.1.1766644714__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.

Potentially problematic release.


This version of devduck might be problematic. Click here for more details.

Files changed (37) hide show
  1. devduck/__init__.py +1439 -483
  2. devduck/__main__.py +7 -0
  3. devduck/_version.py +34 -0
  4. devduck/agentcore_handler.py +76 -0
  5. devduck/test_redduck.py +0 -1
  6. devduck/tools/__init__.py +47 -0
  7. devduck/tools/_ambient_input.py +423 -0
  8. devduck/tools/_tray_app.py +530 -0
  9. devduck/tools/agentcore_agents.py +197 -0
  10. devduck/tools/agentcore_config.py +441 -0
  11. devduck/tools/agentcore_invoke.py +423 -0
  12. devduck/tools/agentcore_logs.py +320 -0
  13. devduck/tools/ambient.py +157 -0
  14. devduck/tools/create_subagent.py +659 -0
  15. devduck/tools/fetch_github_tool.py +201 -0
  16. devduck/tools/install_tools.py +409 -0
  17. devduck/tools/ipc.py +546 -0
  18. devduck/tools/mcp_server.py +600 -0
  19. devduck/tools/scraper.py +935 -0
  20. devduck/tools/speech_to_speech.py +850 -0
  21. devduck/tools/state_manager.py +292 -0
  22. devduck/tools/store_in_kb.py +187 -0
  23. devduck/tools/system_prompt.py +608 -0
  24. devduck/tools/tcp.py +263 -94
  25. devduck/tools/tray.py +247 -0
  26. devduck/tools/use_github.py +438 -0
  27. devduck/tools/websocket.py +498 -0
  28. devduck-0.1.1766644714.dist-info/METADATA +717 -0
  29. devduck-0.1.1766644714.dist-info/RECORD +33 -0
  30. {devduck-0.1.0.dist-info → devduck-0.1.1766644714.dist-info}/entry_points.txt +1 -0
  31. devduck-0.1.1766644714.dist-info/licenses/LICENSE +201 -0
  32. devduck/install.sh +0 -42
  33. devduck-0.1.0.dist-info/METADATA +0 -106
  34. devduck-0.1.0.dist-info/RECORD +0 -11
  35. devduck-0.1.0.dist-info/licenses/LICENSE +0 -21
  36. {devduck-0.1.0.dist-info → devduck-0.1.1766644714.dist-info}/WHEEL +0 -0
  37. {devduck-0.1.0.dist-info → devduck-0.1.1766644714.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,659 @@
1
+ """Sub-agent creation and management tool for Strands Agents.
2
+
3
+ This module provides a tool for creating and managing sub-agents by triggering GitHub Actions
4
+ workflows. It enables task delegation and parallel processing by handing off tasks to
5
+ specialized agents running in separate GitHub Actions workflows.
6
+
7
+ Key Features:
8
+ 1. Create sub-agents by triggering GitHub Actions workflows
9
+ 2. Pass context and tasks to sub-agents
10
+ 3. Configure model selection (model ID, provider, max tokens) for granular control
11
+ 4. Configure tool selection with comma-separated tool names
12
+ 5. Prevent throttling by using different models for different sub-agents
13
+ 6. Track sub-agent status and results
14
+ 7. Set specific system prompts for sub-agents
15
+
16
+ Model Selection:
17
+ - Use us.anthropic.claude-sonnet-4-20250514-v1:0 for tasks
18
+ - Use us.anthropic.claude-opus-4-20250514-v1:0 for advanced reasoning tasks
19
+ - Specify provider (bedrock, anthropic, openai) and max_tokens for fine-tuned control
20
+
21
+ Tool Selection:
22
+ - Pass comma-separated tool names to control which tools the sub-agent has access to
23
+ - Available tools include: current_time,editor,environment,file_read,file_write,http_request,load_tool,
24
+ python_repl,retrieve,shell,slack,think,use_agent,calculator,use_aws,generate_image,image_reader,memory,swarm,workflow
25
+ - Examples: "file_read,file_write,shell" or "python_repl,calculator,http_request"
26
+
27
+ Usage Examples:
28
+ ```python
29
+ from strands import Agent
30
+ from strands_action import create_subagent
31
+
32
+ agent = Agent(tools=[create_subagent])
33
+
34
+ # Create a sub-agent with specific model and tools for data analysis
35
+ result = agent.tool.create_subagent(
36
+ repository="owner/repo",
37
+ workflow_id="agent.yml",
38
+ task="Analyze this dataset and provide insights",
39
+ model="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
40
+ provider="bedrock",
41
+ max_tokens=60000,
42
+ tools="file_read,use_github,create_subagent,system_prompt,python_repl,calculator",
43
+ )
44
+
45
+ # Create a sub-agent with powerful model for complex reasoning with limited tools
46
+ result = agent.tool.create_subagent(
47
+ repository="owner/repo",
48
+ workflow_id="agent.yml",
49
+ task="Complex multi-step analysis requiring deep reasoning",
50
+ model="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
51
+ provider="bedrock",
52
+ max_tokens=60000,
53
+ tools="think,use_agent,retrieve,use_github,scraper",
54
+ )
55
+ ```
56
+ """
57
+
58
+ import os
59
+ import re
60
+ from typing import Any
61
+
62
+ import requests
63
+ from strands import tool
64
+
65
+
66
+ def _get_github_token() -> str:
67
+ """Get GitHub token from environment variable."""
68
+ return os.environ.get("GITHUB_TOKEN", "")
69
+
70
+
71
+ def _dispatch_workflow(
72
+ repository: str,
73
+ workflow_id: str,
74
+ inputs: dict[str, str],
75
+ token: str,
76
+ branch: str = "main",
77
+ ) -> dict[str, Any]:
78
+ """Dispatch a GitHub Actions workflow with inputs.
79
+
80
+ Args:
81
+ repository: The repository in format "owner/repo"
82
+ workflow_id: The workflow file name or ID
83
+ inputs: Dictionary of input parameters to pass to the workflow
84
+ token: GitHub token
85
+ branch: The branch to run the workflow on (default: "main")
86
+
87
+ Returns:
88
+ Dictionary with success status and message
89
+ """
90
+ # GitHub API endpoint for workflow dispatch
91
+ url = f"https://api.github.com/repos/{repository}/actions/workflows/{workflow_id}/dispatches"
92
+
93
+ headers = {
94
+ "Accept": "application/vnd.github+json",
95
+ "Authorization": f"Bearer {token}",
96
+ "X-GitHub-Api-Version": "2022-11-28",
97
+ }
98
+
99
+ # Prepare payload
100
+ data = {"ref": branch, "inputs": inputs}
101
+
102
+ try:
103
+ response = requests.post(url, headers=headers, json=data, timeout=30)
104
+
105
+ if response.status_code == 204:
106
+ return {
107
+ "success": True,
108
+ "message": f"Workflow {workflow_id} dispatched successfully",
109
+ }
110
+ else:
111
+ error_message = (
112
+ f"Failed to dispatch workflow: {response.status_code} - {response.text}"
113
+ )
114
+ return {"success": False, "message": error_message}
115
+ except Exception as e:
116
+ return {"success": False, "message": f"Error dispatching workflow: {e!s}"}
117
+
118
+
119
+ def _check_workflow_run_status(
120
+ repository: str,
121
+ run_id: str,
122
+ token: str,
123
+ ) -> dict[str, Any]:
124
+ """Check the status of a specific workflow run.
125
+
126
+ Args:
127
+ repository: The repository in format "owner/repo"
128
+ run_id: The run ID of the specific workflow execution
129
+ token: GitHub token
130
+
131
+ Returns:
132
+ Dictionary with status information
133
+ """
134
+ # GitHub API endpoint for workflow run
135
+ url = f"https://api.github.com/repos/{repository}/actions/runs/{run_id}"
136
+
137
+ headers = {
138
+ "Accept": "application/vnd.github+json",
139
+ "Authorization": f"Bearer {token}",
140
+ "X-GitHub-Api-Version": "2022-11-28",
141
+ }
142
+
143
+ try:
144
+ response = requests.get(url, headers=headers, timeout=30)
145
+
146
+ if response.status_code == 200:
147
+ data = response.json()
148
+ return {
149
+ "success": True,
150
+ "status": data.get("status"),
151
+ "conclusion": data.get("conclusion"),
152
+ "html_url": data.get("html_url"),
153
+ "name": data.get("name"),
154
+ "workflow_id": data.get("workflow_id"),
155
+ "created_at": data.get("created_at"),
156
+ "updated_at": data.get("updated_at"),
157
+ }
158
+ else:
159
+ error_message = f"Failed to check workflow run status: {response.status_code} - {response.text}"
160
+ return {"success": False, "message": error_message}
161
+ except Exception as e:
162
+ return {"success": False, "message": f"Error checking workflow run: {e!s}"}
163
+
164
+
165
+ def _get_workflow_run_logs(
166
+ repository: str,
167
+ run_id: str,
168
+ token: str,
169
+ ) -> dict[str, Any]:
170
+ """Get logs for a workflow run.
171
+
172
+ Args:
173
+ repository: The repository in format "owner/repo"
174
+ run_id: The run ID of the specific workflow execution
175
+ token: GitHub token
176
+
177
+ Returns:
178
+ Dictionary with logs information
179
+ """
180
+ # GitHub API endpoint for workflow run logs
181
+ url = f"https://api.github.com/repos/{repository}/actions/runs/{run_id}/logs"
182
+
183
+ headers = {
184
+ "Accept": "application/vnd.github+json",
185
+ "Authorization": f"Bearer {token}",
186
+ "X-GitHub-Api-Version": "2022-11-28",
187
+ }
188
+
189
+ try:
190
+ # This endpoint returns a redirect to download the logs
191
+ response = requests.get(url, headers=headers, allow_redirects=False, timeout=30)
192
+
193
+ if response.status_code == 302:
194
+ download_url = response.headers.get("Location")
195
+ if download_url:
196
+ # Follow redirect to get logs
197
+ log_response = requests.get(download_url, timeout=30)
198
+ if log_response.status_code == 200:
199
+ return {"success": True, "logs": log_response.text}
200
+ else:
201
+ return {
202
+ "success": False,
203
+ "message": f"Failed to download logs: {log_response.status_code}",
204
+ }
205
+ else:
206
+ return {
207
+ "success": False,
208
+ "message": "No download URL found in response",
209
+ }
210
+ else:
211
+ error_message = f"Failed to get workflow run logs: {response.status_code} - {response.text}"
212
+ return {"success": False, "message": error_message}
213
+ except Exception as e:
214
+ return {"success": False, "message": f"Error getting workflow run logs: {e!s}"}
215
+
216
+
217
+ def _parse_agent_responses_from_logs(logs: str) -> list[dict[str, str]]:
218
+ """Parse agent responses from workflow logs.
219
+
220
+ This function extracts the actual agent responses from the log output
221
+ by looking for specially formatted output blocks.
222
+
223
+ Args:
224
+ logs: The raw workflow logs text
225
+
226
+ Returns:
227
+ List of dictionaries containing agent responses
228
+ """
229
+ responses = []
230
+
231
+ # Look for patterns like "Event: <message>" and "python -c" command execution
232
+ # This is a simplified pattern that may need adjustment based on actual log format
233
+ pattern = r"Event:\s*(.*?)\n.*?python -c"
234
+ message_matches = re.findall(pattern, logs, re.DOTALL)
235
+
236
+ if message_matches:
237
+ for message in message_matches:
238
+ responses.append({"prompt": message.strip()})
239
+
240
+ return responses
241
+
242
+
243
+ def _list_workflow_runs(
244
+ repository: str,
245
+ workflow_id: str,
246
+ token: str,
247
+ per_page: int = 5,
248
+ ) -> dict[str, Any]:
249
+ """List runs of a specific workflow.
250
+
251
+ Args:
252
+ repository: The repository in format "owner/repo"
253
+ workflow_id: The workflow file name or ID
254
+ token: GitHub token
255
+ per_page: Number of runs to return (default: 5)
256
+
257
+ Returns:
258
+ Dictionary with list of workflow runs
259
+ """
260
+ # GitHub API endpoint for workflow runs
261
+ url = f"https://api.github.com/repos/{repository}/actions/workflows/{workflow_id}/runs"
262
+
263
+ headers = {
264
+ "Accept": "application/vnd.github+json",
265
+ "Authorization": f"Bearer {token}",
266
+ "X-GitHub-Api-Version": "2022-11-28",
267
+ }
268
+
269
+ params = {"per_page": per_page}
270
+
271
+ try:
272
+ response = requests.get(url, headers=headers, params=params, timeout=30)
273
+
274
+ if response.status_code == 200:
275
+ data = response.json()
276
+ runs = []
277
+
278
+ for run in data.get("workflow_runs", []):
279
+ runs.append(
280
+ {
281
+ "id": run.get("id"),
282
+ "name": run.get("name"),
283
+ "status": run.get("status"),
284
+ "conclusion": run.get("conclusion"),
285
+ "created_at": run.get("created_at"),
286
+ "updated_at": run.get("updated_at"),
287
+ "html_url": run.get("html_url"),
288
+ }
289
+ )
290
+
291
+ return {
292
+ "success": True,
293
+ "runs": runs,
294
+ "total_count": data.get("total_count", 0),
295
+ }
296
+ else:
297
+ error_message = f"Failed to list workflow runs: {response.status_code} - {response.text}"
298
+ return {"success": False, "message": error_message}
299
+ except Exception as e:
300
+ return {"success": False, "message": f"Error listing workflow runs: {e!s}"}
301
+
302
+
303
+ @tool
304
+ def create_subagent(
305
+ repository: str,
306
+ workflow_id: str,
307
+ task: str | None = None,
308
+ system_prompt: str | None = None,
309
+ context: str | None = None,
310
+ branch: str = "main",
311
+ action: str = "create",
312
+ run_id: str | None = None,
313
+ model: str | None = None,
314
+ provider: str | None = None,
315
+ max_tokens: int | None = None,
316
+ tools: str | None = None,
317
+ agent_runner: str | None = None,
318
+ ) -> dict[str, Any]:
319
+ """Create and manage sub-agents via GitHub Actions workflows.
320
+
321
+ This tool allows creating and managing sub-agents by triggering GitHub Actions
322
+ workflows. It helps with task delegation and parallel processing by handing off
323
+ tasks to specialized agents running in separate GitHub Actions workflows.
324
+
325
+ Args:
326
+ repository: GitHub repository in format "owner/repo" where the workflow lives
327
+ workflow_id: The workflow file name (e.g., "agent.yml") or ID
328
+ task: The task to delegate to the sub-agent (required only for "create" action)
329
+ system_prompt: Optional system prompt to set for the sub-agent
330
+ context: Optional context information to provide to the sub-agent
331
+ branch: The branch to run the workflow on (default: "main")
332
+ action: The action to perform. One of:
333
+ - "create": Create a new sub-agent (trigger workflow)
334
+ - "status": Check status of a sub-agent run
335
+ - "list": List recent sub-agent runs
336
+ run_id: The run ID when checking status of a specific run
337
+ model: The model ID to use for the sub-agent. Available models:
338
+ - "us.anthropic.claude-3-7-sonnet-20250219-v1:0" (max 32768 tokens) - 10000 ideal
339
+ - "us.anthropic.claude-sonnet-4-20250514-v1:0" (max 65536 tokens) - 10000 ideal
340
+ provider: The model provider to use (e.g., "bedrock" - only bedrock is enabled)
341
+ max_tokens: Maximum number of tokens for the sub-agent model.
342
+ tools: Comma-separated list of tools to enable for the sub-agent. Available tools:
343
+ current_time,editor,environment,file_read,file_write,http_request,load_tool,
344
+ python_repl,retrieve,shell,slack,think,use_llm,calculator,use_aws,generate_image,
345
+ image_reader,memory,swarm,workflow. Example: "file_read,python_repl,calculator"
346
+ agent_runner: Optional custom agent runner script URL to use for the sub-agent.
347
+ Falls back to repository variable AGENT_RUNNER, then default gist if not provided.
348
+ Example: "https://gist.githubusercontent.com/myuser/custom-agent.py"
349
+
350
+ Returns:
351
+ A dictionary with the operation status and information
352
+
353
+ Example:
354
+ ```python
355
+ # Create a sub-agent with Claude Opus 4 for complex reasoning (smartest)
356
+ result = create_subagent(
357
+ repository="owner/repo",
358
+ workflow_id="agent.yml",
359
+ task="Complex multi-step analysis requiring deep reasoning",
360
+ model="us.anthropic.claude-opus-4-20250514-v1:0",
361
+ provider="bedrock",
362
+ max_tokens=10000,
363
+ tools="think,use_agent,retrieve,shell,retrieve",
364
+ )
365
+
366
+ # Create a sub-agent with Claude Sonnet 4 for balanced performance (suggested)
367
+ result = create_subagent(
368
+ repository="owner/repo",
369
+ workflow_id="agent.yml",
370
+ task="Analyze this dataset and provide comprehensive insights",
371
+ model="us.anthropic.claude-sonnet-4-20250514-v1:0",
372
+ provider="bedrock",
373
+ max_tokens=10000,
374
+ tools="file_read,python_repl,calculator,http_request,retrieve",
375
+ )
376
+
377
+ # Create a specialized development sub-agent
378
+ result = create_subagent(
379
+ repository="owner/repo",
380
+ workflow_id="agent.yml",
381
+ task="Review code and suggest improvements",
382
+ tools="file_read,editor,python_repl,shell",
383
+ system_prompt="You are a senior code reviewer focused on best practices",
384
+ )
385
+
386
+
387
+ # Create a sub-agent with custom agent runner
388
+ result = create_subagent(
389
+ repository="owner/repo",
390
+ workflow_id="agent.yml",
391
+ task="Use custom agent logic for this task",
392
+ agent_runner="https://gist.githubusercontent.com/myuser/custom-agent.py",
393
+ tools="file_read,python_repl",
394
+ )
395
+ # Check status of a specific run
396
+ result = create_subagent(
397
+ action="status",
398
+ repository="owner/repo",
399
+ workflow_id="agent.yml",
400
+ run_id="12345678",
401
+ )
402
+
403
+ # List recent runs
404
+ result = create_subagent(
405
+ action="list",
406
+ repository="owner/repo",
407
+ workflow_id="agent.yml",
408
+ )
409
+ ```
410
+ """
411
+ try:
412
+ token = _get_github_token()
413
+ if not token:
414
+ return {
415
+ "status": "error",
416
+ "content": [
417
+ {
418
+ "text": "Error: GitHub token not available. Cannot create or manage sub-agents."
419
+ }
420
+ ],
421
+ }
422
+
423
+ if action == "create":
424
+ # Validate that task is provided for create action
425
+ if not task:
426
+ return {
427
+ "status": "error",
428
+ "content": [
429
+ {
430
+ "text": "Error: 'task' parameter is required for the create action"
431
+ }
432
+ ],
433
+ }
434
+
435
+ # Prepare inputs for the workflow - only include valid inputs based on workflow definition
436
+ inputs = {}
437
+
438
+ # Create task context combining task and context
439
+ task_context = task
440
+ if context is not None:
441
+ task_context += f"\nContext: {context}"
442
+ inputs["task"] = task_context
443
+
444
+ # Only add system_prompt if it's provided
445
+ if system_prompt is not None:
446
+ inputs["system_prompt"] = str(
447
+ system_prompt
448
+ ) # Explicitly convert to string
449
+
450
+ # Add model configuration parameters if provided
451
+ if model is not None:
452
+ inputs["model"] = str(model)
453
+
454
+ if provider is not None:
455
+ inputs["provider"] = str(provider)
456
+
457
+ if max_tokens is not None:
458
+ inputs["max_tokens"] = str(
459
+ max_tokens
460
+ ) # Convert to string for GitHub Actions input
461
+
462
+ # Add tools configuration if provided
463
+ if tools is not None:
464
+ inputs["tools"] = str(tools) # Pass comma-separated tools string
465
+ # Add agent_runner configuration if provided
466
+ if agent_runner is not None:
467
+ inputs["agent_runner"] = str(agent_runner)
468
+
469
+ # Dispatch the workflow
470
+ result = _dispatch_workflow(
471
+ repository=repository,
472
+ workflow_id=workflow_id,
473
+ inputs=inputs,
474
+ token=token,
475
+ branch=branch,
476
+ )
477
+
478
+ if result["success"]:
479
+ content = [
480
+ {"text": f"Sub-agent created successfully: {result['message']}"},
481
+ {"text": f"Task delegated: {task}"},
482
+ {"text": f"Repository: {repository}"},
483
+ {"text": f"Workflow: {workflow_id}"},
484
+ {"text": f"Branch: {branch}"},
485
+ ]
486
+
487
+ # Add model configuration info if provided
488
+ if model is not None:
489
+ content.append({"text": f"Model: {model}"})
490
+ if provider is not None:
491
+ content.append({"text": f"Provider: {provider}"})
492
+ if max_tokens is not None:
493
+ content.append({"text": f"Max Tokens: {max_tokens}"})
494
+ if tools is not None:
495
+ content.append({"text": f"Tools: {tools}"})
496
+ if agent_runner is not None:
497
+ content.append({"text": f"Agent Runner: {agent_runner}"})
498
+
499
+ content.append(
500
+ {
501
+ "text": "To check status, use the 'list' action to find the run ID, then use 'status' action."
502
+ }
503
+ )
504
+
505
+ return {
506
+ "status": "success",
507
+ "content": content,
508
+ }
509
+ else:
510
+ return {
511
+ "status": "error",
512
+ "content": [
513
+ {"text": f"Failed to create sub-agent: {result['message']}"}
514
+ ],
515
+ }
516
+
517
+ elif action == "status":
518
+ if not run_id:
519
+ return {
520
+ "status": "error",
521
+ "content": [
522
+ {
523
+ "text": "Error: run_id parameter is required for the status action"
524
+ }
525
+ ],
526
+ }
527
+
528
+ result = _check_workflow_run_status(
529
+ repository=repository, run_id=run_id, token=token
530
+ )
531
+
532
+ if result["success"]:
533
+ status_text = f"Status: {result.get('status')}"
534
+ if result.get("conclusion"):
535
+ status_text += f", Conclusion: {result.get('conclusion')}"
536
+
537
+ content = [
538
+ {"text": f"Sub-agent run status: {status_text}"},
539
+ {"text": f"Name: {result.get('name', 'N/A')}"},
540
+ {"text": f"Created: {result.get('created_at', 'N/A')}"},
541
+ {"text": f"Updated: {result.get('updated_at', 'N/A')}"},
542
+ {"text": f"Run URL: {result.get('html_url', 'N/A')}"},
543
+ ]
544
+
545
+ # If the run is complete, try to get logs and extract meaningful information
546
+ if result.get("status") == "completed":
547
+ logs_result = _get_workflow_run_logs(
548
+ repository=repository, run_id=run_id, token=token
549
+ )
550
+
551
+ if logs_result["success"]:
552
+ logs = logs_result.get("logs", "")
553
+
554
+ # Try to extract agent responses from logs
555
+ agent_responses = _parse_agent_responses_from_logs(logs)
556
+
557
+ if agent_responses:
558
+ content.append({"text": "Agent Tasks:"})
559
+ for idx, response in enumerate(agent_responses, 1):
560
+ content.append(
561
+ {
562
+ "text": f"Task {idx}: {response.get('prompt', 'N/A')}"
563
+ }
564
+ )
565
+
566
+ # Extract event output message
567
+ event_pattern = r"Event:\s*(.*?)(?:\n|\r\n)"
568
+ event_match = re.search(event_pattern, logs)
569
+ if event_match:
570
+ content.append(
571
+ {"text": f"Event: {event_match.group(1).strip()}"}
572
+ )
573
+
574
+ # Also show a truncated version of the logs for debugging
575
+ if len(logs) > 1000:
576
+ logs_excerpt = (
577
+ logs[:1000] + "...\n[Logs truncated due to length]"
578
+ )
579
+ else:
580
+ logs_excerpt = logs
581
+
582
+ content.append(
583
+ {"text": f"Run Logs (excerpt):\n```\n{logs_excerpt}\n```"}
584
+ )
585
+
586
+ return {"status": "success", "content": content}
587
+ else:
588
+ return {
589
+ "status": "error",
590
+ "content": [
591
+ {
592
+ "text": f"Failed to check sub-agent status: {result.get('message')}"
593
+ }
594
+ ],
595
+ }
596
+
597
+ elif action == "list":
598
+ result = _list_workflow_runs(
599
+ repository=repository, workflow_id=workflow_id, token=token
600
+ )
601
+
602
+ if result["success"]:
603
+ runs = result.get("runs", [])
604
+ total_count = result.get("total_count", 0)
605
+
606
+ if not runs:
607
+ return {
608
+ "status": "success",
609
+ "content": [{"text": "No recent sub-agent runs found"}],
610
+ }
611
+
612
+ content = [
613
+ {
614
+ "text": f"Recent sub-agent runs (showing {len(runs)} of {total_count}):"
615
+ }
616
+ ]
617
+ for run in runs:
618
+ run_info = (
619
+ f"Run ID: {run.get('id')}\n"
620
+ f"Name: {run.get('name', 'N/A')}\n"
621
+ f"Status: {run.get('status')}\n"
622
+ f"Conclusion: {run.get('conclusion', 'N/A')}\n"
623
+ f"Created: {run.get('created_at')}\n"
624
+ f"Updated: {run.get('updated_at', 'N/A')}\n"
625
+ f"URL: {run.get('html_url')}\n"
626
+ )
627
+ content.append({"text": run_info})
628
+
629
+ content.append(
630
+ {
631
+ "text": "To check the status of a specific run, use: "
632
+ 'create_subagent(action="status", repository="owner/repo", '
633
+ 'workflow_id="workflow.yml", run_id="RUN_ID")'
634
+ }
635
+ )
636
+
637
+ return {"status": "success", "content": content}
638
+ else:
639
+ return {
640
+ "status": "error",
641
+ "content": [
642
+ {
643
+ "text": f"Failed to list sub-agent runs: {result.get('message')}"
644
+ }
645
+ ],
646
+ }
647
+
648
+ else:
649
+ return {
650
+ "status": "error",
651
+ "content": [
652
+ {
653
+ "text": f"Error: Unknown action '{action}'. Valid actions are create, status, list"
654
+ }
655
+ ],
656
+ }
657
+
658
+ except Exception as e:
659
+ return {"status": "error", "content": [{"text": f"Error: {e!s}"}]}