zag-agent 0.2.1__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.
- zag_agent-0.2.1/PKG-INFO +125 -0
- zag_agent-0.2.1/README.md +105 -0
- zag_agent-0.2.1/pyproject.toml +35 -0
- zag_agent-0.2.1/setup.cfg +4 -0
- zag_agent-0.2.1/src/zag/__init__.py +37 -0
- zag_agent-0.2.1/src/zag/builder.py +306 -0
- zag_agent-0.2.1/src/zag/process.py +176 -0
- zag_agent-0.2.1/src/zag/types.py +302 -0
- zag_agent-0.2.1/src/zag_agent.egg-info/PKG-INFO +125 -0
- zag_agent-0.2.1/src/zag_agent.egg-info/SOURCES.txt +11 -0
- zag_agent-0.2.1/src/zag_agent.egg-info/dependency_links.txt +1 -0
- zag_agent-0.2.1/src/zag_agent.egg-info/top_level.txt +1 -0
- zag_agent-0.2.1/tests/test_builder.py +276 -0
zag_agent-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zag-agent
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Python SDK for zag — a unified CLI for AI coding agents
|
|
5
|
+
Author: Niclas Lindstedt
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/niclaslindstedt/zag
|
|
8
|
+
Project-URL: Repository, https://github.com/niclaslindstedt/zag
|
|
9
|
+
Keywords: zag,ai,agent,claude,codex,gemini,copilot,ollama
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Zag Python Binding
|
|
22
|
+
|
|
23
|
+
Python binding for [zag](https://github.com/niclaslindstedt/zag) — a unified CLI for AI coding agents.
|
|
24
|
+
|
|
25
|
+
## Prerequisites
|
|
26
|
+
|
|
27
|
+
- Python 3.10+
|
|
28
|
+
- The `zag` CLI binary installed and on your `PATH` (or set via `ZAG_BIN` env var)
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install zag-agent
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For development from source:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
cd bindings/python
|
|
40
|
+
pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from zag import ZagBuilder
|
|
47
|
+
|
|
48
|
+
output = await ZagBuilder() \
|
|
49
|
+
.provider("claude") \
|
|
50
|
+
.model("sonnet") \
|
|
51
|
+
.auto_approve() \
|
|
52
|
+
.exec("write a hello world program")
|
|
53
|
+
|
|
54
|
+
print(output.result)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Streaming
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from zag import ZagBuilder
|
|
61
|
+
|
|
62
|
+
async for event in await ZagBuilder().provider("claude").stream("analyze code"):
|
|
63
|
+
print(event.type, event)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Builder methods
|
|
67
|
+
|
|
68
|
+
| Method | Description |
|
|
69
|
+
|--------|-------------|
|
|
70
|
+
| `.provider(name)` | Set provider: `"claude"`, `"codex"`, `"gemini"`, `"copilot"`, `"ollama"` |
|
|
71
|
+
| `.model(name)` | Set model name or size alias (`"small"`, `"medium"`, `"large"`) |
|
|
72
|
+
| `.system_prompt(text)` | Set a system prompt |
|
|
73
|
+
| `.root(path)` | Set the working directory |
|
|
74
|
+
| `.auto_approve()` | Skip permission prompts |
|
|
75
|
+
| `.add_dir(path)` | Add an additional directory (chainable) |
|
|
76
|
+
| `.json_mode()` | Request JSON output |
|
|
77
|
+
| `.json_schema(schema)` | Validate output against a JSON schema (implies `.json_mode()`) |
|
|
78
|
+
| `.json_stream()` | Enable streaming NDJSON output |
|
|
79
|
+
| `.worktree(name=None)` | Run in an isolated git worktree |
|
|
80
|
+
| `.sandbox(name=None)` | Run in a Docker sandbox |
|
|
81
|
+
| `.session_id(uuid)` | Use a specific session ID |
|
|
82
|
+
| `.output_format(fmt)` | Set output format (`"text"`, `"json"`, `"json-pretty"`, `"stream-json"`) |
|
|
83
|
+
| `.input_format(fmt)` | Set input format (`"text"`, `"stream-json"` — Claude only) |
|
|
84
|
+
| `.replay_user_messages()` | Re-emit user messages on stdout (Claude only) |
|
|
85
|
+
| `.include_partial_messages()` | Include partial message chunks (Claude only) |
|
|
86
|
+
| `.max_turns(n)` | Set the maximum number of agentic turns |
|
|
87
|
+
| `.show_usage()` | Show token usage statistics (JSON output mode) |
|
|
88
|
+
| `.size(size)` | Set Ollama model parameter size (e.g., `"2b"`, `"9b"`, `"35b"`) |
|
|
89
|
+
| `.verbose()` | Enable verbose output |
|
|
90
|
+
| `.quiet()` | Suppress non-essential output |
|
|
91
|
+
| `.debug()` | Enable debug logging |
|
|
92
|
+
| `.bin(path)` | Override the `zag` binary path |
|
|
93
|
+
|
|
94
|
+
## Terminal methods
|
|
95
|
+
|
|
96
|
+
| Method | Returns | Description |
|
|
97
|
+
|--------|---------|-------------|
|
|
98
|
+
| `.exec(prompt)` | `AgentOutput` | Run non-interactively, return structured output |
|
|
99
|
+
| `.stream(prompt)` | `AsyncGenerator[Event]` | Stream NDJSON events |
|
|
100
|
+
| `.exec_streaming(prompt)` | `StreamingSession` | Bidirectional streaming (Claude only) |
|
|
101
|
+
| `.run(prompt=None)` | `None` | Start an interactive session (inherits stdio) |
|
|
102
|
+
| `.resume(session_id)` | `None` | Resume a previous session by ID |
|
|
103
|
+
| `.continue_last()` | `None` | Resume the most recent session |
|
|
104
|
+
|
|
105
|
+
## How it works
|
|
106
|
+
|
|
107
|
+
The SDK spawns the `zag` CLI as a subprocess (`zag exec -o json` or `-o stream-json`) and parses the JSON/NDJSON output into typed dataclasses. Zero external dependencies — only the Python standard library.
|
|
108
|
+
|
|
109
|
+
## Testing
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pip install pytest pytest-asyncio
|
|
113
|
+
pytest
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## See also
|
|
117
|
+
|
|
118
|
+
- [TypeScript SDK](../typescript/README.md)
|
|
119
|
+
- [C# SDK](../csharp/README.md)
|
|
120
|
+
- [Rust API (zag-agent)](../../zag-agent/README.md)
|
|
121
|
+
- [All bindings](../README.md)
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Zag Python Binding
|
|
2
|
+
|
|
3
|
+
Python binding for [zag](https://github.com/niclaslindstedt/zag) — a unified CLI for AI coding agents.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Python 3.10+
|
|
8
|
+
- The `zag` CLI binary installed and on your `PATH` (or set via `ZAG_BIN` env var)
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install zag-agent
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
For development from source:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
cd bindings/python
|
|
20
|
+
pip install -e .
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from zag import ZagBuilder
|
|
27
|
+
|
|
28
|
+
output = await ZagBuilder() \
|
|
29
|
+
.provider("claude") \
|
|
30
|
+
.model("sonnet") \
|
|
31
|
+
.auto_approve() \
|
|
32
|
+
.exec("write a hello world program")
|
|
33
|
+
|
|
34
|
+
print(output.result)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Streaming
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from zag import ZagBuilder
|
|
41
|
+
|
|
42
|
+
async for event in await ZagBuilder().provider("claude").stream("analyze code"):
|
|
43
|
+
print(event.type, event)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Builder methods
|
|
47
|
+
|
|
48
|
+
| Method | Description |
|
|
49
|
+
|--------|-------------|
|
|
50
|
+
| `.provider(name)` | Set provider: `"claude"`, `"codex"`, `"gemini"`, `"copilot"`, `"ollama"` |
|
|
51
|
+
| `.model(name)` | Set model name or size alias (`"small"`, `"medium"`, `"large"`) |
|
|
52
|
+
| `.system_prompt(text)` | Set a system prompt |
|
|
53
|
+
| `.root(path)` | Set the working directory |
|
|
54
|
+
| `.auto_approve()` | Skip permission prompts |
|
|
55
|
+
| `.add_dir(path)` | Add an additional directory (chainable) |
|
|
56
|
+
| `.json_mode()` | Request JSON output |
|
|
57
|
+
| `.json_schema(schema)` | Validate output against a JSON schema (implies `.json_mode()`) |
|
|
58
|
+
| `.json_stream()` | Enable streaming NDJSON output |
|
|
59
|
+
| `.worktree(name=None)` | Run in an isolated git worktree |
|
|
60
|
+
| `.sandbox(name=None)` | Run in a Docker sandbox |
|
|
61
|
+
| `.session_id(uuid)` | Use a specific session ID |
|
|
62
|
+
| `.output_format(fmt)` | Set output format (`"text"`, `"json"`, `"json-pretty"`, `"stream-json"`) |
|
|
63
|
+
| `.input_format(fmt)` | Set input format (`"text"`, `"stream-json"` — Claude only) |
|
|
64
|
+
| `.replay_user_messages()` | Re-emit user messages on stdout (Claude only) |
|
|
65
|
+
| `.include_partial_messages()` | Include partial message chunks (Claude only) |
|
|
66
|
+
| `.max_turns(n)` | Set the maximum number of agentic turns |
|
|
67
|
+
| `.show_usage()` | Show token usage statistics (JSON output mode) |
|
|
68
|
+
| `.size(size)` | Set Ollama model parameter size (e.g., `"2b"`, `"9b"`, `"35b"`) |
|
|
69
|
+
| `.verbose()` | Enable verbose output |
|
|
70
|
+
| `.quiet()` | Suppress non-essential output |
|
|
71
|
+
| `.debug()` | Enable debug logging |
|
|
72
|
+
| `.bin(path)` | Override the `zag` binary path |
|
|
73
|
+
|
|
74
|
+
## Terminal methods
|
|
75
|
+
|
|
76
|
+
| Method | Returns | Description |
|
|
77
|
+
|--------|---------|-------------|
|
|
78
|
+
| `.exec(prompt)` | `AgentOutput` | Run non-interactively, return structured output |
|
|
79
|
+
| `.stream(prompt)` | `AsyncGenerator[Event]` | Stream NDJSON events |
|
|
80
|
+
| `.exec_streaming(prompt)` | `StreamingSession` | Bidirectional streaming (Claude only) |
|
|
81
|
+
| `.run(prompt=None)` | `None` | Start an interactive session (inherits stdio) |
|
|
82
|
+
| `.resume(session_id)` | `None` | Resume a previous session by ID |
|
|
83
|
+
| `.continue_last()` | `None` | Resume the most recent session |
|
|
84
|
+
|
|
85
|
+
## How it works
|
|
86
|
+
|
|
87
|
+
The SDK spawns the `zag` CLI as a subprocess (`zag exec -o json` or `-o stream-json`) and parses the JSON/NDJSON output into typed dataclasses. Zero external dependencies — only the Python standard library.
|
|
88
|
+
|
|
89
|
+
## Testing
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install pytest pytest-asyncio
|
|
93
|
+
pytest
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## See also
|
|
97
|
+
|
|
98
|
+
- [TypeScript SDK](../typescript/README.md)
|
|
99
|
+
- [C# SDK](../csharp/README.md)
|
|
100
|
+
- [Rust API (zag-agent)](../../zag-agent/README.md)
|
|
101
|
+
- [All bindings](../README.md)
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "zag-agent"
|
|
7
|
+
version = "0.2.1"
|
|
8
|
+
description = "Python SDK for zag — a unified CLI for AI coding agents"
|
|
9
|
+
license = "MIT"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
keywords = ["zag", "ai", "agent", "claude", "codex", "gemini", "copilot", "ollama"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Niclas Lindstedt" },
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/niclaslindstedt/zag"
|
|
29
|
+
Repository = "https://github.com/niclaslindstedt/zag"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Python SDK for zag — a unified CLI for AI coding agents."""
|
|
2
|
+
|
|
3
|
+
from .builder import ZagBuilder
|
|
4
|
+
from .types import (
|
|
5
|
+
AgentOutput,
|
|
6
|
+
AssistantMessageEvent,
|
|
7
|
+
ContentBlock,
|
|
8
|
+
ErrorEvent,
|
|
9
|
+
Event,
|
|
10
|
+
InitEvent,
|
|
11
|
+
PermissionRequestEvent,
|
|
12
|
+
ResultEvent,
|
|
13
|
+
TextBlock,
|
|
14
|
+
ToolExecutionEvent,
|
|
15
|
+
ToolResult,
|
|
16
|
+
ToolUseBlock,
|
|
17
|
+
Usage,
|
|
18
|
+
ZagError,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"ZagBuilder",
|
|
23
|
+
"AgentOutput",
|
|
24
|
+
"Usage",
|
|
25
|
+
"Event",
|
|
26
|
+
"InitEvent",
|
|
27
|
+
"AssistantMessageEvent",
|
|
28
|
+
"ToolExecutionEvent",
|
|
29
|
+
"ResultEvent",
|
|
30
|
+
"ErrorEvent",
|
|
31
|
+
"PermissionRequestEvent",
|
|
32
|
+
"ContentBlock",
|
|
33
|
+
"TextBlock",
|
|
34
|
+
"ToolUseBlock",
|
|
35
|
+
"ToolResult",
|
|
36
|
+
"ZagError",
|
|
37
|
+
]
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Fluent builder for configuring and running zag agent sessions.
|
|
2
|
+
|
|
3
|
+
Example::
|
|
4
|
+
|
|
5
|
+
from zag import ZagBuilder
|
|
6
|
+
|
|
7
|
+
output = await ZagBuilder() \\
|
|
8
|
+
.provider("claude") \\
|
|
9
|
+
.model("sonnet") \\
|
|
10
|
+
.auto_approve() \\
|
|
11
|
+
.exec("write a hello world program")
|
|
12
|
+
|
|
13
|
+
print(output.result)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
from collections.abc import AsyncGenerator
|
|
20
|
+
|
|
21
|
+
from .process import default_bin, exec_zag, run_zag, stream_zag, stream_with_input
|
|
22
|
+
from .types import AgentOutput, Event
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ZagBuilder:
|
|
26
|
+
"""Fluent builder for configuring and running zag agent sessions."""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
self._bin: str = default_bin()
|
|
30
|
+
self._provider: str | None = None
|
|
31
|
+
self._model: str | None = None
|
|
32
|
+
self._system_prompt: str | None = None
|
|
33
|
+
self._root: str | None = None
|
|
34
|
+
self._auto_approve: bool = False
|
|
35
|
+
self._add_dirs: list[str] = []
|
|
36
|
+
self._json: bool = False
|
|
37
|
+
self._json_schema: dict | None = None
|
|
38
|
+
self._json_stream: bool = False
|
|
39
|
+
self._worktree: str | bool | None = None
|
|
40
|
+
self._sandbox: str | bool | None = None
|
|
41
|
+
self._verbose: bool = False
|
|
42
|
+
self._quiet: bool = False
|
|
43
|
+
self._debug: bool = False
|
|
44
|
+
self._session_id: str | None = None
|
|
45
|
+
self._output_format: str | None = None
|
|
46
|
+
self._input_format: str | None = None
|
|
47
|
+
self._replay_user_messages: bool = False
|
|
48
|
+
self._include_partial_messages: bool = False
|
|
49
|
+
self._max_turns: int | None = None
|
|
50
|
+
self._show_usage: bool = False
|
|
51
|
+
self._size: str | None = None
|
|
52
|
+
|
|
53
|
+
# -- Configuration methods -----------------------------------------------
|
|
54
|
+
|
|
55
|
+
def bin(self, path: str) -> ZagBuilder:
|
|
56
|
+
"""Override the zag binary path (default: ``ZAG_BIN`` env or ``"zag"``)."""
|
|
57
|
+
self._bin = path
|
|
58
|
+
return self
|
|
59
|
+
|
|
60
|
+
def provider(self, p: str) -> ZagBuilder:
|
|
61
|
+
"""Set the provider (e.g., ``"claude"``, ``"codex"``, ``"gemini"``)."""
|
|
62
|
+
self._provider = p
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
def model(self, m: str) -> ZagBuilder:
|
|
66
|
+
"""Set the model (e.g., ``"sonnet"``, ``"opus"``, ``"small"``)."""
|
|
67
|
+
self._model = m
|
|
68
|
+
return self
|
|
69
|
+
|
|
70
|
+
def system_prompt(self, p: str) -> ZagBuilder:
|
|
71
|
+
"""Set a system prompt to configure agent behavior."""
|
|
72
|
+
self._system_prompt = p
|
|
73
|
+
return self
|
|
74
|
+
|
|
75
|
+
def root(self, r: str) -> ZagBuilder:
|
|
76
|
+
"""Set the root directory for the agent to operate in."""
|
|
77
|
+
self._root = r
|
|
78
|
+
return self
|
|
79
|
+
|
|
80
|
+
def auto_approve(self, a: bool = True) -> ZagBuilder:
|
|
81
|
+
"""Enable auto-approve mode (skip permission prompts)."""
|
|
82
|
+
self._auto_approve = a
|
|
83
|
+
return self
|
|
84
|
+
|
|
85
|
+
def add_dir(self, d: str) -> ZagBuilder:
|
|
86
|
+
"""Add an additional directory for the agent to include."""
|
|
87
|
+
self._add_dirs.append(d)
|
|
88
|
+
return self
|
|
89
|
+
|
|
90
|
+
def json_mode(self) -> ZagBuilder:
|
|
91
|
+
"""Request JSON output from the agent."""
|
|
92
|
+
self._json = True
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
def json_schema(self, s: dict) -> ZagBuilder:
|
|
96
|
+
"""Set a JSON schema for structured output validation. Implies ``json_mode()``."""
|
|
97
|
+
self._json_schema = s
|
|
98
|
+
self._json = True
|
|
99
|
+
return self
|
|
100
|
+
|
|
101
|
+
def json_stream(self) -> ZagBuilder:
|
|
102
|
+
"""Enable streaming JSON output (NDJSON format)."""
|
|
103
|
+
self._json_stream = True
|
|
104
|
+
return self
|
|
105
|
+
|
|
106
|
+
def worktree(self, name: str | None = None) -> ZagBuilder:
|
|
107
|
+
"""Enable worktree mode with an optional name."""
|
|
108
|
+
self._worktree = name if name is not None else True
|
|
109
|
+
return self
|
|
110
|
+
|
|
111
|
+
def sandbox(self, name: str | None = None) -> ZagBuilder:
|
|
112
|
+
"""Enable sandbox mode with an optional name."""
|
|
113
|
+
self._sandbox = name if name is not None else True
|
|
114
|
+
return self
|
|
115
|
+
|
|
116
|
+
def verbose(self, v: bool = True) -> ZagBuilder:
|
|
117
|
+
"""Enable verbose output."""
|
|
118
|
+
self._verbose = v
|
|
119
|
+
return self
|
|
120
|
+
|
|
121
|
+
def quiet(self, q: bool = True) -> ZagBuilder:
|
|
122
|
+
"""Enable quiet mode."""
|
|
123
|
+
self._quiet = q
|
|
124
|
+
return self
|
|
125
|
+
|
|
126
|
+
def debug(self, d: bool = True) -> ZagBuilder:
|
|
127
|
+
"""Enable debug logging."""
|
|
128
|
+
self._debug = d
|
|
129
|
+
return self
|
|
130
|
+
|
|
131
|
+
def session_id(self, id: str) -> ZagBuilder:
|
|
132
|
+
"""Pre-set a session ID (UUID)."""
|
|
133
|
+
self._session_id = id
|
|
134
|
+
return self
|
|
135
|
+
|
|
136
|
+
def output_format(self, f: str) -> ZagBuilder:
|
|
137
|
+
"""Set the output format (e.g., ``"text"``, ``"json"``, ``"stream-json"``)."""
|
|
138
|
+
self._output_format = f
|
|
139
|
+
return self
|
|
140
|
+
|
|
141
|
+
def input_format(self, f: str) -> ZagBuilder:
|
|
142
|
+
"""Set the input format (Claude only)."""
|
|
143
|
+
self._input_format = f
|
|
144
|
+
return self
|
|
145
|
+
|
|
146
|
+
def replay_user_messages(self, r: bool = True) -> ZagBuilder:
|
|
147
|
+
"""Re-emit user messages from stdin on stdout (Claude only)."""
|
|
148
|
+
self._replay_user_messages = r
|
|
149
|
+
return self
|
|
150
|
+
|
|
151
|
+
def include_partial_messages(self, i: bool = True) -> ZagBuilder:
|
|
152
|
+
"""Include partial message chunks in streaming output (Claude only)."""
|
|
153
|
+
self._include_partial_messages = i
|
|
154
|
+
return self
|
|
155
|
+
|
|
156
|
+
def max_turns(self, n: int) -> ZagBuilder:
|
|
157
|
+
"""Set the maximum number of agentic turns."""
|
|
158
|
+
self._max_turns = n
|
|
159
|
+
return self
|
|
160
|
+
|
|
161
|
+
def show_usage(self, s: bool = True) -> ZagBuilder:
|
|
162
|
+
"""Show token usage statistics (only applies to JSON output mode)."""
|
|
163
|
+
self._show_usage = s
|
|
164
|
+
return self
|
|
165
|
+
|
|
166
|
+
def size(self, s: str) -> ZagBuilder:
|
|
167
|
+
"""Set the Ollama model parameter size (e.g., ``"2b"``, ``"9b"``, ``"35b"``)."""
|
|
168
|
+
self._size = s
|
|
169
|
+
return self
|
|
170
|
+
|
|
171
|
+
# -- Arg building --------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
def _global_args(self) -> list[str]:
|
|
174
|
+
args: list[str] = []
|
|
175
|
+
if self._provider:
|
|
176
|
+
args.extend(["-p", self._provider])
|
|
177
|
+
if self._model:
|
|
178
|
+
args.extend(["--model", self._model])
|
|
179
|
+
if self._system_prompt:
|
|
180
|
+
args.extend(["--system-prompt", self._system_prompt])
|
|
181
|
+
if self._root:
|
|
182
|
+
args.extend(["--root", self._root])
|
|
183
|
+
if self._auto_approve:
|
|
184
|
+
args.append("--auto-approve")
|
|
185
|
+
for d in self._add_dirs:
|
|
186
|
+
args.extend(["--add-dir", d])
|
|
187
|
+
if self._worktree is True:
|
|
188
|
+
args.append("-w")
|
|
189
|
+
elif isinstance(self._worktree, str):
|
|
190
|
+
args.extend(["-w", self._worktree])
|
|
191
|
+
if self._sandbox is True:
|
|
192
|
+
args.append("--sandbox")
|
|
193
|
+
elif isinstance(self._sandbox, str):
|
|
194
|
+
args.extend(["--sandbox", self._sandbox])
|
|
195
|
+
if self._verbose:
|
|
196
|
+
args.append("--verbose")
|
|
197
|
+
if self._quiet:
|
|
198
|
+
args.append("--quiet")
|
|
199
|
+
if self._debug:
|
|
200
|
+
args.append("--debug")
|
|
201
|
+
if self._session_id:
|
|
202
|
+
args.extend(["--session", self._session_id])
|
|
203
|
+
if self._max_turns is not None:
|
|
204
|
+
args.extend(["--max-turns", str(self._max_turns)])
|
|
205
|
+
if self._show_usage:
|
|
206
|
+
args.append("--show-usage")
|
|
207
|
+
if self._size:
|
|
208
|
+
args.extend(["--size", self._size])
|
|
209
|
+
return args
|
|
210
|
+
|
|
211
|
+
def _exec_args(self, prompt: str, *, streaming: bool = False) -> list[str]:
|
|
212
|
+
args = self._global_args()
|
|
213
|
+
args.append("exec")
|
|
214
|
+
if self._json:
|
|
215
|
+
args.append("--json")
|
|
216
|
+
if self._json_schema:
|
|
217
|
+
args.extend(["--json-schema", json.dumps(self._json_schema)])
|
|
218
|
+
if self._json_stream or streaming:
|
|
219
|
+
args.append("--json-stream")
|
|
220
|
+
if self._output_format:
|
|
221
|
+
args.extend(["-o", self._output_format])
|
|
222
|
+
if self._input_format:
|
|
223
|
+
args.extend(["-i", self._input_format])
|
|
224
|
+
if self._replay_user_messages:
|
|
225
|
+
args.append("--replay-user-messages")
|
|
226
|
+
if self._include_partial_messages:
|
|
227
|
+
args.append("--include-partial-messages")
|
|
228
|
+
# Default to json output for structured parsing
|
|
229
|
+
if not streaming and not self._output_format and not self._json_stream:
|
|
230
|
+
args.extend(["-o", "json"])
|
|
231
|
+
args.append(prompt)
|
|
232
|
+
return args
|
|
233
|
+
|
|
234
|
+
# -- Terminal methods ----------------------------------------------------
|
|
235
|
+
|
|
236
|
+
async def exec(self, prompt: str) -> AgentOutput:
|
|
237
|
+
"""Run the agent non-interactively and return structured output.
|
|
238
|
+
|
|
239
|
+
Example::
|
|
240
|
+
|
|
241
|
+
output = await ZagBuilder().provider("claude").exec("say hello")
|
|
242
|
+
print(output.result)
|
|
243
|
+
"""
|
|
244
|
+
args = self._exec_args(prompt)
|
|
245
|
+
return await exec_zag(self._bin, args)
|
|
246
|
+
|
|
247
|
+
async def exec_streaming(self, prompt: str) -> "StreamingSession":
|
|
248
|
+
"""Run the agent with streaming input and output (Claude only).
|
|
249
|
+
|
|
250
|
+
Returns a StreamingSession for bidirectional communication.
|
|
251
|
+
|
|
252
|
+
Example::
|
|
253
|
+
|
|
254
|
+
session = await ZagBuilder().provider("claude").exec_streaming("hello")
|
|
255
|
+
await session.send_user_message("do something")
|
|
256
|
+
async for event in session.events():
|
|
257
|
+
print(event.type)
|
|
258
|
+
await session.wait()
|
|
259
|
+
"""
|
|
260
|
+
from .process import StreamingSession as _StreamingSession
|
|
261
|
+
|
|
262
|
+
args = self._global_args()
|
|
263
|
+
args.append("exec")
|
|
264
|
+
args.extend(["-i", "stream-json"])
|
|
265
|
+
args.extend(["-o", "stream-json"])
|
|
266
|
+
args.append("--replay-user-messages")
|
|
267
|
+
if self._include_partial_messages:
|
|
268
|
+
args.append("--include-partial-messages")
|
|
269
|
+
args.append(prompt)
|
|
270
|
+
return await _StreamingSession.create(self._bin, args)
|
|
271
|
+
|
|
272
|
+
async def stream(self, prompt: str) -> AsyncGenerator[Event, None]:
|
|
273
|
+
"""Run the agent in streaming mode, yielding events as they arrive.
|
|
274
|
+
|
|
275
|
+
Example::
|
|
276
|
+
|
|
277
|
+
async for event in await ZagBuilder().provider("claude").stream("analyze"):
|
|
278
|
+
print(event.type)
|
|
279
|
+
"""
|
|
280
|
+
args = self._exec_args(prompt, streaming=True)
|
|
281
|
+
async for event in stream_zag(self._bin, args):
|
|
282
|
+
yield event
|
|
283
|
+
|
|
284
|
+
async def run(self, prompt: str | None = None) -> None:
|
|
285
|
+
"""Start an interactive agent session (inherits stdio)."""
|
|
286
|
+
args = self._global_args()
|
|
287
|
+
args.append("run")
|
|
288
|
+
if self._json:
|
|
289
|
+
args.append("--json")
|
|
290
|
+
if self._json_schema:
|
|
291
|
+
args.extend(["--json-schema", json.dumps(self._json_schema)])
|
|
292
|
+
if prompt:
|
|
293
|
+
args.append(prompt)
|
|
294
|
+
await run_zag(self._bin, args)
|
|
295
|
+
|
|
296
|
+
async def resume(self, session_id: str) -> None:
|
|
297
|
+
"""Resume a previous session by ID."""
|
|
298
|
+
args = self._global_args()
|
|
299
|
+
args.extend(["run", "--resume", session_id])
|
|
300
|
+
await run_zag(self._bin, args)
|
|
301
|
+
|
|
302
|
+
async def continue_last(self) -> None:
|
|
303
|
+
"""Resume the most recent session."""
|
|
304
|
+
args = self._global_args()
|
|
305
|
+
args.extend(["run", "--continue"])
|
|
306
|
+
await run_zag(self._bin, args)
|