Skip to content

Commit 2cdec5a

Browse files
factory-davidgufactory-droid[bot]varin-nair-factory
authored
feat: release 0.1.3 + expand jsonrpc protocol support + fix issues (#6)
* feat: expand Droid JSON-RPC protocol support Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * chore: bump version to 0.1.3 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: validate client requests and clear closed sessions Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --------- Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> Co-authored-by: User <varin@factory.ai>
1 parent 3d99c1e commit 2cdec5a

16 files changed

Lines changed: 2718 additions & 79 deletions

README.md

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,9 @@ def handle_message(msg: StreamMessage) -> None:
174174
print(f"Tool call: {msg.tool_name}({msg.tool_input})")
175175
elif isinstance(msg, ToolResult):
176176
status = "" if msg.is_error else ""
177-
print(f"{status} {msg.content}")
177+
# tool_use_id correlates the result with its ToolUse; tool_name is
178+
# backfilled from that call (None if the call was never seen).
179+
print(f"{status} [{msg.tool_use_id}] {msg.tool_name}: {msg.content}")
178180
elif isinstance(msg, ToolProgress):
179181
print(f"{msg.tool_name}: {msg.content}")
180182
elif isinstance(msg, WorkingStateChanged):
@@ -184,7 +186,9 @@ def handle_message(msg: StreamMessage) -> None:
184186
elif isinstance(msg, TurnComplete):
185187
print("\n--- Turn complete ---")
186188
elif isinstance(msg, ErrorEvent):
187-
print(f"Error [{msg.error_type}]: {msg.message}")
189+
# error_type is often the unhelpful "Error"; error_name exposes the
190+
# nested error.name (e.g. "LLMInvalidRequestError") to branch on.
191+
print(f"Error [{msg.error_name or msg.error_type}]: {msg.message}")
188192
```
189193

190194
## Permission Handler
@@ -264,12 +268,52 @@ async def main():
264268
The main client class. Wraps a transport and provides typed async methods for all `droid.*` RPC methods.
265269

266270
**Session methods:**
267-
- `initialize_session(...)` — Create a new session
271+
- `initialize_session(...)` — Create a new session (supports `enabled_tool_ids` and `disabled_tool_ids`)
268272
- `load_session(session_id=...)` — Load an existing session
269-
- `add_user_message(text=...)` — Send a user message
273+
- `add_user_message(text=..., output_format=...)` — Send a user message, optionally with a structured-output (JSON Schema) contract
270274
- `interrupt_session()` — Interrupt the current session
271275
- `kill_worker_session(worker_session_id=...)` — Kill a worker session
272-
- `update_session_settings(...)` — Update session settings
276+
- `update_session_settings(...)` — Update session settings (supports `enabled_tool_ids`/`disabled_tool_ids`)
277+
- `close_session(reason=...)` — Close the active session
278+
- `compact_session(custom_instructions=...)` — Compact the conversation to reclaim context
279+
- `fork_session(title=..., tags=...)` — Fork the session into a new one
280+
- `rename_session(title=...)` — Rename the session
281+
282+
**Discovery methods:**
283+
- `list_tools(...)` — List native CLI tools with `default_allowed`/`currently_allowed` (useful for locking the tool set down)
284+
- `list_commands()` — List custom slash commands
285+
286+
**Context and rewind methods:**
287+
- `get_context_stats()` — Context-window usage (used/remaining/limit)
288+
- `get_context_breakdown()` — Per-category/skill/MCP/droid token breakdown
289+
- `get_rewind_info(message_id=...)` — Restorable/created/evicted files for a rewind point
290+
- `execute_rewind(...)` — Rewind to a message, forking the session
291+
292+
**Locking the tool set down:**
293+
294+
```python
295+
# enabled_tool_ids is additive, so pass an explicit disable list to
296+
# actually restrict native tools. list_tools() lets you verify the result.
297+
catalog = await client.list_tools()
298+
tool_ids = [t.id for t in catalog.tools]
299+
await client.update_session_settings(enabled_tool_ids=[], disabled_tool_ids=tool_ids)
300+
```
301+
302+
**Structured output:**
303+
304+
```python
305+
await client.add_user_message(
306+
text="Return an answer.",
307+
output_format={
308+
"type": "json_schema",
309+
"schema": {
310+
"type": "object",
311+
"properties": {"answer": {"type": "integer"}},
312+
"required": ["answer"],
313+
},
314+
},
315+
)
316+
```
273317

274318
**MCP methods:**
275319
- `toggle_mcp_server(...)` — Enable/disable an MCP server
@@ -308,7 +352,16 @@ Protocol (interface) that all transport implementations must satisfy. Use this t
308352
uv sync
309353

310354
# Run tests
311-
uv run pytest
355+
uv run --group dev python -m pytest
356+
357+
# Run opt-in tests against the installed, authenticated droid exec CLI.
358+
# These create real sessions and consume model usage.
359+
DROID_LIVE_TESTS=1 uv run --group dev python -m pytest \
360+
tests/test_live_droid_exec.py -v
361+
362+
# Override the executable path when droid is not on PATH.
363+
DROID_LIVE_TESTS=1 DROID_EXEC_PATH=/path/to/droid \
364+
uv run --group dev python -m pytest tests/test_live_droid_exec.py -v
312365

313366
# Type check (strict mode)
314367
uv run mypy --strict src/

examples/interactive_session.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,8 @@ def handle_permission(params: dict[str, object]) -> str:
337337
)
338338
return ToolConfirmationOutcome.ProceedOnce.value
339339

340+
client.set_permission_handler(handle_permission)
341+
340342
# Initialize session
341343
print(f"Executable: {exec_path}")
342344
print(f"Working directory: {cwd}")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "droid-sdk"
3-
version = "0.1.2"
3+
version = "0.1.3"
44
description = "Python asyncio SDK for Factory Droid"
55
readme = "README.md"
66
license = {text = "Apache-2.0"}

0 commit comments

Comments
 (0)