emdash-core 0.1.7__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.
- emdash_core/__init__.py +3 -0
- emdash_core/agent/__init__.py +37 -0
- emdash_core/agent/agents.py +225 -0
- emdash_core/agent/code_reviewer.py +476 -0
- emdash_core/agent/compaction.py +143 -0
- emdash_core/agent/context_manager.py +140 -0
- emdash_core/agent/events.py +338 -0
- emdash_core/agent/handlers.py +224 -0
- emdash_core/agent/inprocess_subagent.py +377 -0
- emdash_core/agent/mcp/__init__.py +50 -0
- emdash_core/agent/mcp/client.py +346 -0
- emdash_core/agent/mcp/config.py +302 -0
- emdash_core/agent/mcp/manager.py +496 -0
- emdash_core/agent/mcp/tool_factory.py +213 -0
- emdash_core/agent/prompts/__init__.py +38 -0
- emdash_core/agent/prompts/main_agent.py +104 -0
- emdash_core/agent/prompts/subagents.py +131 -0
- emdash_core/agent/prompts/workflow.py +136 -0
- emdash_core/agent/providers/__init__.py +34 -0
- emdash_core/agent/providers/base.py +143 -0
- emdash_core/agent/providers/factory.py +80 -0
- emdash_core/agent/providers/models.py +220 -0
- emdash_core/agent/providers/openai_provider.py +463 -0
- emdash_core/agent/providers/transformers_provider.py +217 -0
- emdash_core/agent/research/__init__.py +81 -0
- emdash_core/agent/research/agent.py +143 -0
- emdash_core/agent/research/controller.py +254 -0
- emdash_core/agent/research/critic.py +428 -0
- emdash_core/agent/research/macros.py +469 -0
- emdash_core/agent/research/planner.py +449 -0
- emdash_core/agent/research/researcher.py +436 -0
- emdash_core/agent/research/state.py +523 -0
- emdash_core/agent/research/synthesizer.py +594 -0
- emdash_core/agent/reviewer_profile.py +475 -0
- emdash_core/agent/rules.py +123 -0
- emdash_core/agent/runner.py +601 -0
- emdash_core/agent/session.py +262 -0
- emdash_core/agent/spec_schema.py +66 -0
- emdash_core/agent/specification.py +479 -0
- emdash_core/agent/subagent.py +397 -0
- emdash_core/agent/subagent_prompts.py +13 -0
- emdash_core/agent/toolkit.py +482 -0
- emdash_core/agent/toolkits/__init__.py +64 -0
- emdash_core/agent/toolkits/base.py +96 -0
- emdash_core/agent/toolkits/explore.py +47 -0
- emdash_core/agent/toolkits/plan.py +55 -0
- emdash_core/agent/tools/__init__.py +141 -0
- emdash_core/agent/tools/analytics.py +436 -0
- emdash_core/agent/tools/base.py +131 -0
- emdash_core/agent/tools/coding.py +484 -0
- emdash_core/agent/tools/github_mcp.py +592 -0
- emdash_core/agent/tools/history.py +13 -0
- emdash_core/agent/tools/modes.py +153 -0
- emdash_core/agent/tools/plan.py +206 -0
- emdash_core/agent/tools/plan_write.py +135 -0
- emdash_core/agent/tools/search.py +412 -0
- emdash_core/agent/tools/spec.py +341 -0
- emdash_core/agent/tools/task.py +262 -0
- emdash_core/agent/tools/task_output.py +204 -0
- emdash_core/agent/tools/tasks.py +454 -0
- emdash_core/agent/tools/traversal.py +588 -0
- emdash_core/agent/tools/web.py +179 -0
- emdash_core/analytics/__init__.py +5 -0
- emdash_core/analytics/engine.py +1286 -0
- emdash_core/api/__init__.py +5 -0
- emdash_core/api/agent.py +308 -0
- emdash_core/api/agents.py +154 -0
- emdash_core/api/analyze.py +264 -0
- emdash_core/api/auth.py +173 -0
- emdash_core/api/context.py +77 -0
- emdash_core/api/db.py +121 -0
- emdash_core/api/embed.py +131 -0
- emdash_core/api/feature.py +143 -0
- emdash_core/api/health.py +93 -0
- emdash_core/api/index.py +162 -0
- emdash_core/api/plan.py +110 -0
- emdash_core/api/projectmd.py +210 -0
- emdash_core/api/query.py +320 -0
- emdash_core/api/research.py +122 -0
- emdash_core/api/review.py +161 -0
- emdash_core/api/router.py +76 -0
- emdash_core/api/rules.py +116 -0
- emdash_core/api/search.py +119 -0
- emdash_core/api/spec.py +99 -0
- emdash_core/api/swarm.py +223 -0
- emdash_core/api/tasks.py +109 -0
- emdash_core/api/team.py +120 -0
- emdash_core/auth/__init__.py +17 -0
- emdash_core/auth/github.py +389 -0
- emdash_core/config.py +74 -0
- emdash_core/context/__init__.py +52 -0
- emdash_core/context/models.py +50 -0
- emdash_core/context/providers/__init__.py +11 -0
- emdash_core/context/providers/base.py +74 -0
- emdash_core/context/providers/explored_areas.py +183 -0
- emdash_core/context/providers/touched_areas.py +360 -0
- emdash_core/context/registry.py +73 -0
- emdash_core/context/reranker.py +199 -0
- emdash_core/context/service.py +260 -0
- emdash_core/context/session.py +352 -0
- emdash_core/core/__init__.py +104 -0
- emdash_core/core/config.py +454 -0
- emdash_core/core/exceptions.py +55 -0
- emdash_core/core/models.py +265 -0
- emdash_core/core/review_config.py +57 -0
- emdash_core/db/__init__.py +67 -0
- emdash_core/db/auth.py +134 -0
- emdash_core/db/models.py +91 -0
- emdash_core/db/provider.py +222 -0
- emdash_core/db/providers/__init__.py +5 -0
- emdash_core/db/providers/supabase.py +452 -0
- emdash_core/embeddings/__init__.py +24 -0
- emdash_core/embeddings/indexer.py +534 -0
- emdash_core/embeddings/models.py +192 -0
- emdash_core/embeddings/providers/__init__.py +7 -0
- emdash_core/embeddings/providers/base.py +112 -0
- emdash_core/embeddings/providers/fireworks.py +141 -0
- emdash_core/embeddings/providers/openai.py +104 -0
- emdash_core/embeddings/registry.py +146 -0
- emdash_core/embeddings/service.py +215 -0
- emdash_core/graph/__init__.py +26 -0
- emdash_core/graph/builder.py +134 -0
- emdash_core/graph/connection.py +692 -0
- emdash_core/graph/schema.py +416 -0
- emdash_core/graph/writer.py +667 -0
- emdash_core/ingestion/__init__.py +7 -0
- emdash_core/ingestion/change_detector.py +150 -0
- emdash_core/ingestion/git/__init__.py +5 -0
- emdash_core/ingestion/git/commit_analyzer.py +196 -0
- emdash_core/ingestion/github/__init__.py +6 -0
- emdash_core/ingestion/github/pr_fetcher.py +296 -0
- emdash_core/ingestion/github/task_extractor.py +100 -0
- emdash_core/ingestion/orchestrator.py +540 -0
- emdash_core/ingestion/parsers/__init__.py +10 -0
- emdash_core/ingestion/parsers/base_parser.py +66 -0
- emdash_core/ingestion/parsers/call_graph_builder.py +121 -0
- emdash_core/ingestion/parsers/class_extractor.py +154 -0
- emdash_core/ingestion/parsers/function_extractor.py +202 -0
- emdash_core/ingestion/parsers/import_analyzer.py +119 -0
- emdash_core/ingestion/parsers/python_parser.py +123 -0
- emdash_core/ingestion/parsers/registry.py +72 -0
- emdash_core/ingestion/parsers/ts_ast_parser.js +313 -0
- emdash_core/ingestion/parsers/typescript_parser.py +278 -0
- emdash_core/ingestion/repository.py +346 -0
- emdash_core/models/__init__.py +38 -0
- emdash_core/models/agent.py +68 -0
- emdash_core/models/index.py +77 -0
- emdash_core/models/query.py +113 -0
- emdash_core/planning/__init__.py +7 -0
- emdash_core/planning/agent_api.py +413 -0
- emdash_core/planning/context_builder.py +265 -0
- emdash_core/planning/feature_context.py +232 -0
- emdash_core/planning/feature_expander.py +646 -0
- emdash_core/planning/llm_explainer.py +198 -0
- emdash_core/planning/similarity.py +509 -0
- emdash_core/planning/team_focus.py +821 -0
- emdash_core/server.py +153 -0
- emdash_core/sse/__init__.py +5 -0
- emdash_core/sse/stream.py +196 -0
- emdash_core/swarm/__init__.py +17 -0
- emdash_core/swarm/merge_agent.py +383 -0
- emdash_core/swarm/session_manager.py +274 -0
- emdash_core/swarm/swarm_runner.py +226 -0
- emdash_core/swarm/task_definition.py +137 -0
- emdash_core/swarm/worker_spawner.py +319 -0
- emdash_core/swarm/worktree_manager.py +278 -0
- emdash_core/templates/__init__.py +10 -0
- emdash_core/templates/defaults/agent-builder.md.template +82 -0
- emdash_core/templates/defaults/focus.md.template +115 -0
- emdash_core/templates/defaults/pr-review-enhanced.md.template +309 -0
- emdash_core/templates/defaults/pr-review.md.template +80 -0
- emdash_core/templates/defaults/project.md.template +85 -0
- emdash_core/templates/defaults/research_critic.md.template +112 -0
- emdash_core/templates/defaults/research_planner.md.template +85 -0
- emdash_core/templates/defaults/research_synthesizer.md.template +128 -0
- emdash_core/templates/defaults/reviewer.md.template +81 -0
- emdash_core/templates/defaults/spec.md.template +41 -0
- emdash_core/templates/defaults/tasks.md.template +78 -0
- emdash_core/templates/loader.py +296 -0
- emdash_core/utils/__init__.py +45 -0
- emdash_core/utils/git.py +84 -0
- emdash_core/utils/image.py +502 -0
- emdash_core/utils/logger.py +51 -0
- emdash_core-0.1.7.dist-info/METADATA +35 -0
- emdash_core-0.1.7.dist-info/RECORD +187 -0
- emdash_core-0.1.7.dist-info/WHEEL +4 -0
- emdash_core-0.1.7.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
"""SubAgentRunner - subprocess agent runner for focused tasks.
|
|
2
|
+
|
|
3
|
+
This module provides the SubAgentRunner class for executing sub-agents in
|
|
4
|
+
separate processes, plus a CLI entry point for subprocess execution.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
# From parent process via Task tool
|
|
8
|
+
result = subprocess.run([
|
|
9
|
+
sys.executable, "-m", "emdash_core.agent.subagent",
|
|
10
|
+
"--type", "Explore",
|
|
11
|
+
"--prompt", "Find authentication handlers",
|
|
12
|
+
"--repo-root", "/path/to/repo",
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
# Result is JSON on stdout
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import sys
|
|
22
|
+
import time
|
|
23
|
+
import uuid
|
|
24
|
+
from dataclasses import dataclass, asdict
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Optional
|
|
27
|
+
|
|
28
|
+
from .toolkits import get_toolkit
|
|
29
|
+
from .prompts import get_subagent_prompt
|
|
30
|
+
from .providers import get_provider
|
|
31
|
+
from .providers.models import ChatModel
|
|
32
|
+
from ..utils.logger import log
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Environment variables for model configuration
|
|
36
|
+
MODEL_ENV_VARS = {
|
|
37
|
+
"fast": "FAST_MODEL",
|
|
38
|
+
"model": "EMDASH_MODEL",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Default models for each tier
|
|
42
|
+
DEFAULT_MODELS = {
|
|
43
|
+
"fast": "accounts/fireworks/models/gpt-oss-120b",
|
|
44
|
+
"model": "accounts/fireworks/models/minimax-m2p1",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_model_for_tier(tier: str = "fast") -> str:
|
|
49
|
+
"""Get model name from environment variable.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
tier: Model tier (fast, standard, powerful)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Model name/path
|
|
56
|
+
"""
|
|
57
|
+
env_var = MODEL_ENV_VARS.get(tier, "FAST_MODEL")
|
|
58
|
+
default = DEFAULT_MODELS.get(tier, DEFAULT_MODELS["fast"])
|
|
59
|
+
return os.environ.get(env_var, default)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class SubAgentResult:
|
|
64
|
+
"""Result from a sub-agent execution."""
|
|
65
|
+
|
|
66
|
+
success: bool
|
|
67
|
+
agent_type: str
|
|
68
|
+
agent_id: str
|
|
69
|
+
task: str
|
|
70
|
+
|
|
71
|
+
# Findings
|
|
72
|
+
summary: str
|
|
73
|
+
files_explored: list[str]
|
|
74
|
+
findings: list[dict]
|
|
75
|
+
|
|
76
|
+
# Metadata
|
|
77
|
+
iterations: int
|
|
78
|
+
tools_used: list[str]
|
|
79
|
+
execution_time: float
|
|
80
|
+
error: Optional[str] = None
|
|
81
|
+
|
|
82
|
+
def to_dict(self) -> dict:
|
|
83
|
+
"""Convert to dictionary."""
|
|
84
|
+
return asdict(self)
|
|
85
|
+
|
|
86
|
+
def to_json(self) -> str:
|
|
87
|
+
"""Convert to JSON string."""
|
|
88
|
+
return json.dumps(self.to_dict(), indent=2)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class SubAgentRunner:
|
|
92
|
+
"""Subprocess agent runner for focused tasks.
|
|
93
|
+
|
|
94
|
+
Runs a lightweight agent loop with a type-specific toolkit.
|
|
95
|
+
Designed to be spawned as a subprocess from the parent agent.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def __init__(
|
|
99
|
+
self,
|
|
100
|
+
subagent_type: str,
|
|
101
|
+
repo_root: Path,
|
|
102
|
+
model_tier: str = "fast",
|
|
103
|
+
max_turns: int = 10,
|
|
104
|
+
agent_id: Optional[str] = None,
|
|
105
|
+
):
|
|
106
|
+
"""Initialize the sub-agent runner.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
subagent_type: Type of agent (Explore, Plan, etc.)
|
|
110
|
+
repo_root: Root directory of the repository
|
|
111
|
+
model_tier: Model tier (fast, standard, powerful)
|
|
112
|
+
max_turns: Maximum API round-trips before stopping
|
|
113
|
+
agent_id: Optional agent ID for resume support
|
|
114
|
+
"""
|
|
115
|
+
self.subagent_type = subagent_type
|
|
116
|
+
self.repo_root = repo_root.resolve()
|
|
117
|
+
self.max_turns = max_turns
|
|
118
|
+
self.agent_id = agent_id or str(uuid.uuid4())[:8]
|
|
119
|
+
|
|
120
|
+
# Get toolkit for this agent type
|
|
121
|
+
self.toolkit = get_toolkit(subagent_type, repo_root)
|
|
122
|
+
|
|
123
|
+
# Get model and create provider
|
|
124
|
+
model_name = get_model_for_tier(model_tier)
|
|
125
|
+
self.provider = get_provider(model_name)
|
|
126
|
+
|
|
127
|
+
# Get system prompt
|
|
128
|
+
self.system_prompt = get_subagent_prompt(subagent_type)
|
|
129
|
+
|
|
130
|
+
# Transcript for resume support
|
|
131
|
+
self.transcript_dir = repo_root / ".emdash" / "agents"
|
|
132
|
+
self.transcript_path = self.transcript_dir / f"{self.agent_id}.jsonl"
|
|
133
|
+
|
|
134
|
+
# Tracking
|
|
135
|
+
self.files_explored: set[str] = set()
|
|
136
|
+
self.tools_used: list[str] = []
|
|
137
|
+
|
|
138
|
+
def _emit_progress(self, event_type: str, data: dict) -> None:
|
|
139
|
+
"""Emit progress event to stderr for parent process to capture.
|
|
140
|
+
|
|
141
|
+
Events are JSON lines with format: {"event": "type", "data": {...}}
|
|
142
|
+
"""
|
|
143
|
+
import sys
|
|
144
|
+
event = {"event": event_type, "data": data}
|
|
145
|
+
print(json.dumps(event), file=sys.stderr, flush=True)
|
|
146
|
+
|
|
147
|
+
def run(self, prompt: str) -> SubAgentResult:
|
|
148
|
+
"""Execute the task and return results.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
prompt: The task to perform
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
SubAgentResult with findings
|
|
155
|
+
"""
|
|
156
|
+
start_time = time.time()
|
|
157
|
+
|
|
158
|
+
# Load existing transcript if resuming
|
|
159
|
+
messages = self._load_transcript()
|
|
160
|
+
|
|
161
|
+
# Add user message with prompt
|
|
162
|
+
if not messages or messages[-1].get("role") != "user":
|
|
163
|
+
messages.append({"role": "user", "content": prompt})
|
|
164
|
+
|
|
165
|
+
iterations = 0
|
|
166
|
+
last_content = ""
|
|
167
|
+
error = None
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
# Emit start event
|
|
171
|
+
self._emit_progress("agent_start", {
|
|
172
|
+
"agent_type": self.subagent_type,
|
|
173
|
+
"agent_id": self.agent_id,
|
|
174
|
+
"max_turns": self.max_turns,
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
# Agent loop
|
|
178
|
+
while iterations < self.max_turns:
|
|
179
|
+
iterations += 1
|
|
180
|
+
|
|
181
|
+
self._emit_progress("turn_start", {
|
|
182
|
+
"turn": iterations,
|
|
183
|
+
"max_turns": self.max_turns,
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
# Call LLM
|
|
187
|
+
response = self.provider.chat(
|
|
188
|
+
messages=messages,
|
|
189
|
+
tools=self.toolkit.get_all_schemas(),
|
|
190
|
+
system=self.system_prompt,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Add assistant response to messages
|
|
194
|
+
assistant_msg = self.provider.format_assistant_message(response)
|
|
195
|
+
if assistant_msg:
|
|
196
|
+
messages.append(assistant_msg)
|
|
197
|
+
|
|
198
|
+
# Save content
|
|
199
|
+
if response.content:
|
|
200
|
+
last_content = response.content
|
|
201
|
+
|
|
202
|
+
# Check if done (no tool calls)
|
|
203
|
+
if not response.tool_calls:
|
|
204
|
+
break
|
|
205
|
+
|
|
206
|
+
# Execute tool calls
|
|
207
|
+
for tool_call in response.tool_calls:
|
|
208
|
+
self.tools_used.append(tool_call.name)
|
|
209
|
+
|
|
210
|
+
# Parse arguments
|
|
211
|
+
try:
|
|
212
|
+
args = json.loads(tool_call.arguments) if tool_call.arguments else {}
|
|
213
|
+
except (json.JSONDecodeError, TypeError):
|
|
214
|
+
args = {}
|
|
215
|
+
|
|
216
|
+
# Emit tool start
|
|
217
|
+
self._emit_progress("tool_start", {
|
|
218
|
+
"name": tool_call.name,
|
|
219
|
+
"args": {k: str(v)[:100] for k, v in args.items()}, # Truncate args
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
# Track file operations
|
|
223
|
+
if "path" in args:
|
|
224
|
+
self.files_explored.add(args["path"])
|
|
225
|
+
|
|
226
|
+
# Execute tool
|
|
227
|
+
result = self.toolkit.execute(tool_call.name, **args)
|
|
228
|
+
|
|
229
|
+
# Emit tool result
|
|
230
|
+
self._emit_progress("tool_result", {
|
|
231
|
+
"name": tool_call.name,
|
|
232
|
+
"success": result.success,
|
|
233
|
+
"summary": str(result.data)[:200] if result.data else None,
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
# Add tool result to messages
|
|
237
|
+
tool_result_msg = self.provider.format_tool_result(
|
|
238
|
+
tool_call.id,
|
|
239
|
+
json.dumps(result.to_dict(), indent=2),
|
|
240
|
+
)
|
|
241
|
+
if tool_result_msg:
|
|
242
|
+
messages.append(tool_result_msg)
|
|
243
|
+
|
|
244
|
+
# Save transcript after each iteration
|
|
245
|
+
self._save_transcript(messages)
|
|
246
|
+
|
|
247
|
+
except Exception as e:
|
|
248
|
+
log.exception("Sub-agent execution failed")
|
|
249
|
+
error = str(e)
|
|
250
|
+
self._emit_progress("agent_error", {"error": str(e)})
|
|
251
|
+
|
|
252
|
+
execution_time = time.time() - start_time
|
|
253
|
+
|
|
254
|
+
# Emit completion event
|
|
255
|
+
self._emit_progress("agent_end", {
|
|
256
|
+
"success": error is None,
|
|
257
|
+
"iterations": iterations,
|
|
258
|
+
"files_explored": len(self.files_explored),
|
|
259
|
+
"tools_used": len(set(self.tools_used)),
|
|
260
|
+
"execution_time": execution_time,
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
# Build result
|
|
264
|
+
return SubAgentResult(
|
|
265
|
+
success=error is None,
|
|
266
|
+
agent_type=self.subagent_type,
|
|
267
|
+
agent_id=self.agent_id,
|
|
268
|
+
task=prompt,
|
|
269
|
+
summary=last_content or "No response generated",
|
|
270
|
+
files_explored=list(self.files_explored),
|
|
271
|
+
findings=self._extract_findings(messages),
|
|
272
|
+
iterations=iterations,
|
|
273
|
+
tools_used=list(set(self.tools_used)),
|
|
274
|
+
execution_time=execution_time,
|
|
275
|
+
error=error,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
def _load_transcript(self) -> list[dict]:
|
|
279
|
+
"""Load transcript from file for resume support."""
|
|
280
|
+
if not self.transcript_path.exists():
|
|
281
|
+
return []
|
|
282
|
+
|
|
283
|
+
messages = []
|
|
284
|
+
try:
|
|
285
|
+
with open(self.transcript_path) as f:
|
|
286
|
+
for line in f:
|
|
287
|
+
if line.strip():
|
|
288
|
+
messages.append(json.loads(line))
|
|
289
|
+
log.info(f"Resumed agent {self.agent_id} with {len(messages)} messages")
|
|
290
|
+
except Exception as e:
|
|
291
|
+
log.warning(f"Failed to load transcript: {e}")
|
|
292
|
+
return []
|
|
293
|
+
|
|
294
|
+
return messages
|
|
295
|
+
|
|
296
|
+
def _save_transcript(self, messages: list[dict]) -> None:
|
|
297
|
+
"""Save transcript to file for resume support."""
|
|
298
|
+
try:
|
|
299
|
+
self.transcript_dir.mkdir(parents=True, exist_ok=True)
|
|
300
|
+
with open(self.transcript_path, "w") as f:
|
|
301
|
+
for msg in messages:
|
|
302
|
+
f.write(json.dumps(msg) + "\n")
|
|
303
|
+
except Exception as e:
|
|
304
|
+
log.warning(f"Failed to save transcript: {e}")
|
|
305
|
+
|
|
306
|
+
def _extract_findings(self, messages: list[dict]) -> list[dict]:
|
|
307
|
+
"""Extract key findings from tool results.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
messages: Conversation messages
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
List of finding dicts
|
|
314
|
+
"""
|
|
315
|
+
findings = []
|
|
316
|
+
for msg in messages:
|
|
317
|
+
if msg and msg.get("role") == "tool":
|
|
318
|
+
try:
|
|
319
|
+
content = json.loads(msg.get("content", "{}"))
|
|
320
|
+
if content and content.get("success") and content.get("data"):
|
|
321
|
+
findings.append(content["data"])
|
|
322
|
+
except (json.JSONDecodeError, TypeError):
|
|
323
|
+
pass
|
|
324
|
+
return findings[-10:] # Limit to last 10 findings
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def main():
|
|
328
|
+
"""CLI entry point for subprocess execution."""
|
|
329
|
+
parser = argparse.ArgumentParser(
|
|
330
|
+
description="Run a sub-agent task",
|
|
331
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
332
|
+
)
|
|
333
|
+
parser.add_argument(
|
|
334
|
+
"--type",
|
|
335
|
+
required=True,
|
|
336
|
+
choices=["Explore", "Plan", "Bash", "Research"],
|
|
337
|
+
help="Type of sub-agent",
|
|
338
|
+
)
|
|
339
|
+
parser.add_argument(
|
|
340
|
+
"--prompt",
|
|
341
|
+
required=True,
|
|
342
|
+
help="Task prompt",
|
|
343
|
+
)
|
|
344
|
+
parser.add_argument(
|
|
345
|
+
"--repo-root",
|
|
346
|
+
required=True,
|
|
347
|
+
type=Path,
|
|
348
|
+
help="Repository root directory",
|
|
349
|
+
)
|
|
350
|
+
parser.add_argument(
|
|
351
|
+
"--model-tier",
|
|
352
|
+
default="fast",
|
|
353
|
+
choices=["fast", "model"],
|
|
354
|
+
help="Model tier to use (fast=cheap/quick, model=standard)",
|
|
355
|
+
)
|
|
356
|
+
parser.add_argument(
|
|
357
|
+
"--max-turns",
|
|
358
|
+
type=int,
|
|
359
|
+
default=10,
|
|
360
|
+
help="Maximum API round-trips",
|
|
361
|
+
)
|
|
362
|
+
parser.add_argument(
|
|
363
|
+
"--agent-id",
|
|
364
|
+
help="Agent ID (for resume)",
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
args = parser.parse_args()
|
|
368
|
+
|
|
369
|
+
try:
|
|
370
|
+
runner = SubAgentRunner(
|
|
371
|
+
subagent_type=args.type,
|
|
372
|
+
repo_root=args.repo_root,
|
|
373
|
+
model_tier=args.model_tier,
|
|
374
|
+
max_turns=args.max_turns,
|
|
375
|
+
agent_id=args.agent_id,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
result = runner.run(args.prompt)
|
|
379
|
+
|
|
380
|
+
# Output result as JSON to stdout
|
|
381
|
+
print(result.to_json())
|
|
382
|
+
sys.exit(0 if result.success else 1)
|
|
383
|
+
|
|
384
|
+
except Exception as e:
|
|
385
|
+
# Output error as JSON
|
|
386
|
+
error_result = {
|
|
387
|
+
"success": False,
|
|
388
|
+
"error": str(e),
|
|
389
|
+
"agent_type": args.type,
|
|
390
|
+
"task": args.prompt,
|
|
391
|
+
}
|
|
392
|
+
print(json.dumps(error_result, indent=2))
|
|
393
|
+
sys.exit(1)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
if __name__ == "__main__":
|
|
397
|
+
main()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""System prompts for sub-agent types.
|
|
2
|
+
|
|
3
|
+
DEPRECATED: This module is deprecated. Import from .prompts instead.
|
|
4
|
+
|
|
5
|
+
This file re-exports from .prompts.subagents for backwards compatibility.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .prompts.subagents import SUBAGENT_PROMPTS, get_subagent_prompt
|
|
9
|
+
|
|
10
|
+
# Backwards compatibility alias
|
|
11
|
+
get_system_prompt = get_subagent_prompt
|
|
12
|
+
|
|
13
|
+
__all__ = ["SUBAGENT_PROMPTS", "get_system_prompt", "get_subagent_prompt"]
|