Skip to content

Commit 2f4fa0c

Browse files
author
Chojan Shang
committed
chore: some minor fix for mini-swe-agent
Signed-off-by: Chojan Shang <chojan.shang@vesoft.com>
1 parent 745a674 commit 2f4fa0c

File tree

3 files changed

+66
-11
lines changed

3 files changed

+66
-11
lines changed

docs/mini-swe-agent.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
# Mini SWE Agent bridge
22

3-
This example wraps mini-swe-agent behind ACP so Zed can run it as an external agent over stdio. It also includes a local Textual UI client connected via a duet launcher.
3+
> Just a show of the bridge in action. Not a best-effort or absolutely-correct implementation of the agent.
4+
5+
This example wraps mini-swe-agent behind ACP so Zed can run it as an external agent over stdio. It also includes a local Textual UI client connected via a duet launcher
46

57
## Behavior
68

7-
- Prompts: text blocks are concatenated into a single task string; referenced resources (`resource_link` / `resource`) are surfaced as hints in the task.
8-
- Streaming: incremental text is sent via `session/update` with `agent_message_chunk`.
9+
- Prompts: text blocks are concatenated into a single task string. (Resource embedding is not used in this example.)
10+
- Streaming: only LM output is streamed via `session/update` `agent_message_chunk`.
911
- Tool calls: when the agent executes a shell command, the bridge sends:
1012
- `tool_call` with `kind=execute`, pending status, and a bash code block containing the command
1113
- `tool_call_update` upon completion, including output and a `rawOutput` object with `output` and `returncode`
1214
- Final result: on task submission (mini-swe-agent prints `COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT` as the first line), a final `agent_message_chunk` with the submission content is sent.
1315

14-
Non-terminating events (e.g. user rejection or timeouts) are surfaced both as a `tool_call_update` with `status=cancelled|failed` and as a text chunk so the session can continue.
15-
1616
## Configuration
1717

1818
Environment variables control the model:
@@ -27,7 +27,7 @@ If `mini-swe-agent` is not installed in the venv, the bridge attempts to import
2727

2828
## How to run
2929

30-
- In Zed (editor integration): configure an agent server to launch `examples/mini_swe_agent/agent.py` and set the environment variables there.
30+
- In Zed (editor integration): configure an agent server to launch `examples/mini_swe_agent/agent.py` and set the environment variables there. Use Zed’s “Open ACP Logs” to inspect `tool_call`/`tool_call_update` and message chunks.
3131
- In terminal (local TUI): run the duet launcher to start both the agent and the Textual client with the same environment and dedicated pipes:
3232

3333
```bash
@@ -36,6 +36,12 @@ python examples/mini_swe_agent/duet.py
3636

3737
The launcher loads `.env` from the repo root (using python-dotenv) so both processes share the same configuration.
3838

39+
### TUI usage
40+
41+
- Hotkeys: `y` → YOLO, `c` → Confirm, `u` → Human, `Enter` → Continue.
42+
- In Human mode, you’ll be prompted for a bash command; it will be executed and streamed back as a tool call.
43+
- Each executed command appears in the “TOOL CALLS” section with live status and output.
44+
3945
## Files
4046

4147
- Agent entry: [`examples/mini_swe_agent/agent.py`](https://github.com/psiace/agent-client-protocol-python/blob/main/examples/mini_swe_agent/agent.py)

examples/mini_swe_agent/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Mini SWE Agent (Python) — ACP Bridge
22

3+
> Just a show of the bridge in action. Not a best-effort or absolutely-correct implementation of the agent.
4+
35
A minimal Agent Client Protocol (ACP) bridge that wraps mini-swe-agent so it can be run by Zed as an external agent over stdio, and also provides a local Textual UI client.
46

57
## Configure in Zed (recommended for editor integration)

examples/mini_swe_agent/agent.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
PromptRequest,
2121
PromptResponse,
2222
SessionNotification,
23+
SetSessionModeRequest,
24+
SetSessionModeResponse,
2325
stdio_streams,
2426
PROTOCOL_VERSION,
2527
)
@@ -138,15 +140,51 @@ def _send_cost_hint(self) -> None:
138140
except RuntimeError:
139141
self._schedule(self._send(hint))
140142

143+
async def on_tool_start(self, title: str, command: str, tool_call_id: str) -> None:
144+
"""Send a tool_call start notification for a bash command."""
145+
update = SessionUpdate4(
146+
sessionUpdate="tool_call",
147+
toolCallId=tool_call_id,
148+
title=title,
149+
kind="execute",
150+
status="pending",
151+
content=[
152+
ToolCallContent1(
153+
type="content", content=ContentBlock1(type="text", text=f"```bash\n{command}\n```")
154+
)
155+
],
156+
rawInput={"command": command},
157+
)
158+
await self._send(update)
159+
160+
async def on_tool_complete(
161+
self,
162+
tool_call_id: str,
163+
output: str,
164+
returncode: int,
165+
*,
166+
status: str = "completed",
167+
) -> None:
168+
"""Send a tool_call_update with the final output and return code."""
169+
update = SessionUpdate5(
170+
sessionUpdate="tool_call_update",
171+
toolCallId=tool_call_id,
172+
status=status,
173+
content=[
174+
ToolCallContent1(
175+
type="content", content=ContentBlock1(type="text", text=f"```ansi\n{output}\n```")
176+
)
177+
],
178+
rawOutput={"output": output, "returncode": returncode},
179+
)
180+
await self._send(update)
181+
141182
def add_message(self, role: str, content: str, **kwargs):
142183
super().add_message(role, content, **kwargs)
143-
# Only the client should send user_message_chunk. The agent reports its own text via agent_message_chunk.
144-
if not getattr(self, "_emit_updates", True):
184+
# Only stream LM output as agent_message_chunk; tool output is handled via tool_call_update.
185+
if not getattr(self, "_emit_updates", True) or role != "assistant":
145186
return
146-
# Avoid duplicating tool outputs as a separate "Observation" agent message; rely on tool_call_update
147187
text = str(content)
148-
if role == "user" and text.strip().startswith("Observation:"):
149-
return
150188
block = ContentBlock1(type="text", text=text)
151189
update = SessionUpdate2(sessionUpdate="agent_message_chunk", content=block)
152190
try:
@@ -324,6 +362,15 @@ async def loadSession(self, params) -> None: # type: ignore[override]
324362
async def authenticate(self, _params: AuthenticateRequest) -> None:
325363
return None
326364

365+
async def setSessionMode(self, params: SetSessionModeRequest) -> SetSessionModeResponse | None: # type: ignore[override]
366+
sess = self._sessions.get(params.sessionId)
367+
if not sess:
368+
return SetSessionModeResponse()
369+
mode = params.modeId.lower()
370+
if mode in ("confirm", "yolo", "human"):
371+
sess["config"].mode = mode # type: ignore[attr-defined]
372+
return SetSessionModeResponse()
373+
327374
def _extract_mode_from_blocks(self, blocks) -> Literal["confirm", "yolo", "human"] | None:
328375
for b in blocks:
329376
if getattr(b, "type", None) == "text":

0 commit comments

Comments
 (0)