agentabi 0.1.0__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.
Files changed (34) hide show
  1. agentabi-0.1.0/LICENSE +21 -0
  2. agentabi-0.1.0/PKG-INFO +145 -0
  3. agentabi-0.1.0/README.md +102 -0
  4. agentabi-0.1.0/pyproject.toml +99 -0
  5. agentabi-0.1.0/setup.cfg +4 -0
  6. agentabi-0.1.0/src/agentabi/__init__.py +95 -0
  7. agentabi-0.1.0/src/agentabi/auto_detect.py +68 -0
  8. agentabi-0.1.0/src/agentabi/providers/__init__.py +22 -0
  9. agentabi-0.1.0/src/agentabi/providers/base.py +141 -0
  10. agentabi-0.1.0/src/agentabi/providers/claude_native.py +312 -0
  11. agentabi-0.1.0/src/agentabi/providers/claude_sdk.py +244 -0
  12. agentabi-0.1.0/src/agentabi/providers/codex_sdk.py +280 -0
  13. agentabi-0.1.0/src/agentabi/providers/gemini_native.py +260 -0
  14. agentabi-0.1.0/src/agentabi/providers/gemini_sdk.py +205 -0
  15. agentabi-0.1.0/src/agentabi/providers/opencode_native.py +256 -0
  16. agentabi-0.1.0/src/agentabi/providers/registry.py +115 -0
  17. agentabi-0.1.0/src/agentabi/py.typed +0 -0
  18. agentabi-0.1.0/src/agentabi/session.py +197 -0
  19. agentabi-0.1.0/src/agentabi/types/__init__.py +3 -0
  20. agentabi-0.1.0/src/agentabi/types/ir/__init__.py +77 -0
  21. agentabi-0.1.0/src/agentabi/types/ir/capabilities.py +45 -0
  22. agentabi-0.1.0/src/agentabi/types/ir/events.py +227 -0
  23. agentabi-0.1.0/src/agentabi/types/ir/helpers.py +117 -0
  24. agentabi-0.1.0/src/agentabi/types/ir/permissions.py +60 -0
  25. agentabi-0.1.0/src/agentabi/types/ir/session.py +87 -0
  26. agentabi-0.1.0/src/agentabi/types/ir/task.py +84 -0
  27. agentabi-0.1.0/src/agentabi/types/ir/type_guards.py +106 -0
  28. agentabi-0.1.0/src/agentabi.egg-info/PKG-INFO +145 -0
  29. agentabi-0.1.0/src/agentabi.egg-info/SOURCES.txt +32 -0
  30. agentabi-0.1.0/src/agentabi.egg-info/dependency_links.txt +1 -0
  31. agentabi-0.1.0/src/agentabi.egg-info/requires.txt +21 -0
  32. agentabi-0.1.0/src/agentabi.egg-info/top_level.txt +1 -0
  33. agentabi-0.1.0/tests/test_ir_types.py +53 -0
  34. agentabi-0.1.0/tests/test_session.py +40 -0
agentabi-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peng Ding
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,145 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentabi
3
+ Version: 0.1.0
4
+ Summary: Unified async Python API for driving coding agent CLIs — Claude Code, Codex, Gemini CLI, and more
5
+ Author-email: Peng Ding <pding.dp@foxmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Documentation, https://agentabi.readthedocs.io
8
+ Project-URL: Repository, https://github.com/oaklight/agentabi
9
+ Project-URL: Issues, https://github.com/oaklight/agentabi/issues
10
+ Keywords: ai,agent,coding-agent,cli,claude-code,codex,gemini-cli,opencode,llm,async,subprocess,orchestration,streaming,multi-agent,automation
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: typing_extensions>=4.0.0
27
+ Provides-Extra: claude
28
+ Requires-Dist: claude-agent-sdk>=0.1.0; extra == "claude"
29
+ Provides-Extra: codex
30
+ Requires-Dist: codex-sdk-python>=0.1.0; extra == "codex"
31
+ Provides-Extra: gemini
32
+ Requires-Dist: gemini-cli-sdk>=0.5.0; extra == "gemini"
33
+ Provides-Extra: all
34
+ Requires-Dist: claude-agent-sdk>=0.1.0; extra == "all"
35
+ Requires-Dist: codex-sdk-python>=0.1.0; extra == "all"
36
+ Requires-Dist: gemini-cli-sdk>=0.5.0; extra == "all"
37
+ Provides-Extra: dev
38
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
39
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
40
+ Requires-Dist: pytest-cov; extra == "dev"
41
+ Requires-Dist: ruff; extra == "dev"
42
+ Dynamic: license-file
43
+
44
+ # agentabi
45
+
46
+ Unified interface layer for agentic coding CLIs.
47
+
48
+ One interface. Any coding agent.
49
+
50
+ ## What is agentabi?
51
+
52
+ `agentabi` provides a stable, unified interface (an "ABI") for interacting with different agentic coding CLIs. Write your integration once, swap agents with a config change.
53
+
54
+ ### Supported Agents
55
+
56
+ | Agent | Provider | Status |
57
+ |-------|----------|--------|
58
+ | [Claude Code](https://github.com/anthropics/claude-code) | Anthropic | Implemented |
59
+ | [Codex](https://github.com/openai/codex) | OpenAI | Implemented |
60
+ | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Google | Implemented |
61
+ | [OpenCode](https://opencode.ai) | Open Source | Implemented |
62
+
63
+ ## Installation
64
+
65
+ ```bash
66
+ pip install agentabi
67
+ ```
68
+
69
+ With optional SDK integrations:
70
+
71
+ ```bash
72
+ pip install agentabi[claude] # Claude Code SDK support
73
+ pip install agentabi[codex] # Codex SDK support
74
+ pip install agentabi[gemini] # Gemini CLI SDK support
75
+ pip install agentabi[all] # All optional SDKs
76
+ ```
77
+
78
+ > **Note:** Each agent's CLI must be installed separately (e.g., `claude`, `codex`, `gemini`, `opencode`).
79
+
80
+ ## Quick Start
81
+
82
+ ### Run a task
83
+
84
+ ```python
85
+ import asyncio
86
+ from agentabi import Session
87
+
88
+ async def main():
89
+ session = Session(agent="claude_code")
90
+ result = await session.run(prompt="Fix the bug in auth.py")
91
+ print(result["status"]) # "success"
92
+ print(result["result_text"]) # agent's response
93
+
94
+ asyncio.run(main())
95
+ ```
96
+
97
+ ### Stream events
98
+
99
+ ```python
100
+ async for event in session.stream(prompt="Explain this code"):
101
+ if event["type"] == "message_delta":
102
+ print(event["text"], end="")
103
+ ```
104
+
105
+ ### Sync convenience
106
+
107
+ ```python
108
+ from agentabi import run_sync
109
+
110
+ result = run_sync(prompt="List Python files", agent="codex")
111
+ ```
112
+
113
+ ### Discover available agents
114
+
115
+ ```python
116
+ from agentabi import detect_agents, get_agent_capabilities
117
+
118
+ agents = detect_agents() # ["claude_code", "codex", ...]
119
+ caps = get_agent_capabilities("claude_code")
120
+ print(caps["supports_streaming"]) # True
121
+ ```
122
+
123
+ ## Use Cases
124
+
125
+ - **Fleet Management** — Unified entry point for managing multiple coding agents
126
+ - **Agent-to-Agent Calls** — Translation layer for inter-agent invocation
127
+ - **Benchmarking** — Run the same task across agents, compare results
128
+ - **Fallback & Routing** — Automatic failover and cost-aware routing
129
+ - **Middleware Pipeline** — Inject logging, metering, security scanning, audit trails
130
+ - **CI/CD Integration** — Vendor-agnostic agent integration for pipelines
131
+
132
+ ## Ecosystem
133
+
134
+ `agentabi` is part of a layered stack:
135
+
136
+ ```
137
+ agentabi → Agent CLI unified interface → like an OS ABI
138
+ llmir → LLM API format conversion → like a compiler IR
139
+ ```
140
+
141
+ - [llmir](https://github.com/Oaklight/llmir) — LLM Intermediate Representation for converting between LLM provider API formats (OpenAI, Anthropic, Google)
142
+
143
+ ## License
144
+
145
+ [MIT](LICENSE)
@@ -0,0 +1,102 @@
1
+ # agentabi
2
+
3
+ Unified interface layer for agentic coding CLIs.
4
+
5
+ One interface. Any coding agent.
6
+
7
+ ## What is agentabi?
8
+
9
+ `agentabi` provides a stable, unified interface (an "ABI") for interacting with different agentic coding CLIs. Write your integration once, swap agents with a config change.
10
+
11
+ ### Supported Agents
12
+
13
+ | Agent | Provider | Status |
14
+ |-------|----------|--------|
15
+ | [Claude Code](https://github.com/anthropics/claude-code) | Anthropic | Implemented |
16
+ | [Codex](https://github.com/openai/codex) | OpenAI | Implemented |
17
+ | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Google | Implemented |
18
+ | [OpenCode](https://opencode.ai) | Open Source | Implemented |
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install agentabi
24
+ ```
25
+
26
+ With optional SDK integrations:
27
+
28
+ ```bash
29
+ pip install agentabi[claude] # Claude Code SDK support
30
+ pip install agentabi[codex] # Codex SDK support
31
+ pip install agentabi[gemini] # Gemini CLI SDK support
32
+ pip install agentabi[all] # All optional SDKs
33
+ ```
34
+
35
+ > **Note:** Each agent's CLI must be installed separately (e.g., `claude`, `codex`, `gemini`, `opencode`).
36
+
37
+ ## Quick Start
38
+
39
+ ### Run a task
40
+
41
+ ```python
42
+ import asyncio
43
+ from agentabi import Session
44
+
45
+ async def main():
46
+ session = Session(agent="claude_code")
47
+ result = await session.run(prompt="Fix the bug in auth.py")
48
+ print(result["status"]) # "success"
49
+ print(result["result_text"]) # agent's response
50
+
51
+ asyncio.run(main())
52
+ ```
53
+
54
+ ### Stream events
55
+
56
+ ```python
57
+ async for event in session.stream(prompt="Explain this code"):
58
+ if event["type"] == "message_delta":
59
+ print(event["text"], end="")
60
+ ```
61
+
62
+ ### Sync convenience
63
+
64
+ ```python
65
+ from agentabi import run_sync
66
+
67
+ result = run_sync(prompt="List Python files", agent="codex")
68
+ ```
69
+
70
+ ### Discover available agents
71
+
72
+ ```python
73
+ from agentabi import detect_agents, get_agent_capabilities
74
+
75
+ agents = detect_agents() # ["claude_code", "codex", ...]
76
+ caps = get_agent_capabilities("claude_code")
77
+ print(caps["supports_streaming"]) # True
78
+ ```
79
+
80
+ ## Use Cases
81
+
82
+ - **Fleet Management** — Unified entry point for managing multiple coding agents
83
+ - **Agent-to-Agent Calls** — Translation layer for inter-agent invocation
84
+ - **Benchmarking** — Run the same task across agents, compare results
85
+ - **Fallback & Routing** — Automatic failover and cost-aware routing
86
+ - **Middleware Pipeline** — Inject logging, metering, security scanning, audit trails
87
+ - **CI/CD Integration** — Vendor-agnostic agent integration for pipelines
88
+
89
+ ## Ecosystem
90
+
91
+ `agentabi` is part of a layered stack:
92
+
93
+ ```
94
+ agentabi → Agent CLI unified interface → like an OS ABI
95
+ llmir → LLM API format conversion → like a compiler IR
96
+ ```
97
+
98
+ - [llmir](https://github.com/Oaklight/llmir) — LLM Intermediate Representation for converting between LLM provider API formats (OpenAI, Anthropic, Google)
99
+
100
+ ## License
101
+
102
+ [MIT](LICENSE)
@@ -0,0 +1,99 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "agentabi"
7
+ dynamic = ["version"]
8
+ authors = [{name = "Peng Ding", email = "pding.dp@foxmail.com"}]
9
+ description = "Unified async Python API for driving coding agent CLIs — Claude Code, Codex, Gemini CLI, and more"
10
+ readme = "README.md"
11
+ requires-python = ">=3.9"
12
+ license = "MIT"
13
+ keywords = [
14
+ "ai",
15
+ "agent",
16
+ "coding-agent",
17
+ "cli",
18
+ "claude-code",
19
+ "codex",
20
+ "gemini-cli",
21
+ "opencode",
22
+ "llm",
23
+ "async",
24
+ "subprocess",
25
+ "orchestration",
26
+ "streaming",
27
+ "multi-agent",
28
+ "automation",
29
+ ]
30
+ classifiers = [
31
+ "Development Status :: 3 - Alpha",
32
+ "Intended Audience :: Developers",
33
+ "Programming Language :: Python :: 3",
34
+ "Programming Language :: Python :: 3.9",
35
+ "Programming Language :: Python :: 3.10",
36
+ "Programming Language :: Python :: 3.11",
37
+ "Programming Language :: Python :: 3.12",
38
+ "Programming Language :: Python :: 3.13",
39
+ "Operating System :: OS Independent",
40
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
41
+ "Topic :: Software Development :: Libraries :: Python Modules",
42
+ "Typing :: Typed",
43
+ ]
44
+ dependencies = [
45
+ "typing_extensions>=4.0.0",
46
+ ]
47
+
48
+ [project.optional-dependencies]
49
+ claude = ["claude-agent-sdk>=0.1.0"]
50
+ codex = ["codex-sdk-python>=0.1.0"]
51
+ gemini = ["gemini-cli-sdk>=0.5.0"]
52
+ all = [
53
+ "claude-agent-sdk>=0.1.0",
54
+ "codex-sdk-python>=0.1.0",
55
+ "gemini-cli-sdk>=0.5.0",
56
+ ]
57
+ dev = [
58
+ "pytest>=7.0.0",
59
+ "pytest-asyncio>=0.21.0",
60
+ "pytest-cov",
61
+ "ruff",
62
+ ]
63
+
64
+ [project.urls]
65
+ Documentation = "https://agentabi.readthedocs.io"
66
+ Repository = "https://github.com/oaklight/agentabi"
67
+ Issues = "https://github.com/oaklight/agentabi/issues"
68
+
69
+ [tool.setuptools.dynamic]
70
+ version = {attr = "agentabi.__version__"}
71
+
72
+ [tool.setuptools.packages.find]
73
+ where = ["src"]
74
+
75
+ [tool.setuptools.package-data]
76
+ "agentabi" = ["py.typed"]
77
+
78
+ [tool.ruff]
79
+ line-length = 88
80
+ target-version = "py39"
81
+
82
+ [tool.ruff.lint]
83
+ select = ["E", "F", "I", "W"]
84
+
85
+ [tool.pytest.ini_options]
86
+ asyncio_mode = "auto"
87
+ markers = [
88
+ "integration: Real CLI integration tests (require installed CLIs)",
89
+ "claude: Tests requiring Claude Code CLI",
90
+ "codex: Tests requiring Codex CLI",
91
+ "gemini: Tests requiring Gemini CLI",
92
+ "opencode: Tests requiring OpenCode CLI",
93
+ "cross_cli: Cross-CLI consistency tests",
94
+ ]
95
+
96
+ [tool.ty]
97
+ # Optional SDK deps are not installed during type checking
98
+ [tool.ty.rules]
99
+ unresolved-import = "ignore"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,95 @@
1
+ """
2
+ agentabi - Unified interface layer for agentic coding CLIs.
3
+
4
+ agentabi wraps multiple coding agent CLIs (Claude Code, Codex, Gemini CLI,
5
+ OpenCode) behind a unified async Python API with streaming support.
6
+
7
+ Quick start:
8
+ from agentabi import Session
9
+
10
+ session = Session(agent="claude_code")
11
+ result = await session.run(prompt="Fix the bug in auth.py")
12
+
13
+ Streaming:
14
+ async for event in session.stream(prompt="Explain this code"):
15
+ if event["type"] == "message_delta":
16
+ print(event["text"], end="")
17
+
18
+ Sync convenience:
19
+ from agentabi import run_sync
20
+ result = run_sync(prompt="List Python files", agent="claude_code")
21
+
22
+ Discovery:
23
+ from agentabi import detect_agents, get_agent_capabilities
24
+ agents = detect_agents()
25
+ caps = get_agent_capabilities("claude_code")
26
+ """
27
+
28
+ from .auto_detect import detect_agents, get_agent_capabilities, get_default_agent
29
+ from .providers.base import Provider
30
+ from .providers.registry import AgentNotAvailable, get_provider
31
+ from .session import Session, run_sync
32
+ from .types.ir import (
33
+ AgentCapabilities,
34
+ AgentType,
35
+ ErrorEvent,
36
+ FileDiffEvent,
37
+ IREvent,
38
+ MessageDeltaEvent,
39
+ MessageEndEvent,
40
+ MessageStartEvent,
41
+ PermissionConfig,
42
+ PermissionLevel,
43
+ PermissionRequest,
44
+ PermissionRequestEvent,
45
+ PermissionResponseEvent,
46
+ SessionEndEvent,
47
+ SessionResult,
48
+ SessionStartEvent,
49
+ SessionStatus,
50
+ TaskConfig,
51
+ ToolResultEvent,
52
+ ToolUseEvent,
53
+ UsageEvent,
54
+ )
55
+
56
+ __version__ = "0.1.0"
57
+
58
+ __all__ = [
59
+ # Consumer API
60
+ "Session",
61
+ "run_sync",
62
+ # Discovery
63
+ "detect_agents",
64
+ "get_agent_capabilities",
65
+ "get_default_agent",
66
+ # Provider
67
+ "Provider",
68
+ "get_provider",
69
+ "AgentNotAvailable",
70
+ # IR types - Task
71
+ "TaskConfig",
72
+ "AgentType",
73
+ # IR types - Events
74
+ "IREvent",
75
+ "SessionStartEvent",
76
+ "SessionEndEvent",
77
+ "MessageStartEvent",
78
+ "MessageDeltaEvent",
79
+ "MessageEndEvent",
80
+ "ToolUseEvent",
81
+ "ToolResultEvent",
82
+ "PermissionRequestEvent",
83
+ "PermissionResponseEvent",
84
+ "UsageEvent",
85
+ "ErrorEvent",
86
+ "FileDiffEvent",
87
+ # IR types - Session
88
+ "SessionResult",
89
+ "SessionStatus",
90
+ # IR types - Capabilities & Permissions
91
+ "AgentCapabilities",
92
+ "PermissionConfig",
93
+ "PermissionLevel",
94
+ "PermissionRequest",
95
+ ]
@@ -0,0 +1,68 @@
1
+ """
2
+ agentabi - Auto Detection
3
+
4
+ Utility functions for auto-detecting available agent CLIs
5
+ and getting agent capabilities.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import List
11
+
12
+ from .providers.registry import (
13
+ AgentNotAvailable,
14
+ get_provider,
15
+ list_available_agents,
16
+ )
17
+ from .types.ir.capabilities import AgentCapabilities
18
+
19
+
20
+ def detect_agents() -> List[str]:
21
+ """Detect which agents have at least one available provider.
22
+
23
+ Returns:
24
+ List of available agent type identifiers.
25
+
26
+ Examples:
27
+ >>> agents = detect_agents()
28
+ >>> "claude_code" in agents # True if `claude` is in PATH
29
+ """
30
+ return list_available_agents()
31
+
32
+
33
+ def get_agent_capabilities(agent: str) -> AgentCapabilities:
34
+ """Get the capabilities of a specific agent.
35
+
36
+ Args:
37
+ agent: Agent type identifier.
38
+
39
+ Returns:
40
+ AgentCapabilities for the agent's active provider.
41
+
42
+ Raises:
43
+ AgentNotAvailable: If no provider is available.
44
+ """
45
+ provider = get_provider(agent)
46
+ return provider.capabilities()
47
+
48
+
49
+ def get_default_agent() -> str:
50
+ """Get the first available agent.
51
+
52
+ Returns:
53
+ The first detected agent type identifier.
54
+
55
+ Raises:
56
+ AgentNotAvailable: If no agents are available.
57
+ """
58
+ available = detect_agents()
59
+ if not available:
60
+ raise AgentNotAvailable("(auto-detect)")
61
+ return available[0]
62
+
63
+
64
+ __all__ = [
65
+ "detect_agents",
66
+ "get_agent_capabilities",
67
+ "get_default_agent",
68
+ ]
@@ -0,0 +1,22 @@
1
+ """
2
+ agentabi - Providers
3
+
4
+ Flat provider model: each provider implements the same Protocol
5
+ for driving a specific agent CLI or SDK.
6
+ """
7
+
8
+ from .base import Provider
9
+ from .registry import (
10
+ AgentNotAvailable,
11
+ get_provider,
12
+ list_agents,
13
+ resolve_provider,
14
+ )
15
+
16
+ __all__ = [
17
+ "Provider",
18
+ "AgentNotAvailable",
19
+ "resolve_provider",
20
+ "get_provider",
21
+ "list_agents",
22
+ ]
@@ -0,0 +1,141 @@
1
+ """
2
+ agentabi - Provider Protocol
3
+
4
+ Single interface for all agent backends. Providers are the only
5
+ abstraction layer between Session and agent CLIs/SDKs.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, AsyncIterator, List
11
+
12
+ from typing_extensions import Protocol, runtime_checkable
13
+
14
+ from ..types.ir.capabilities import AgentCapabilities
15
+ from ..types.ir.events import IREvent, UsageInfo
16
+ from ..types.ir.session import SessionResult, SessionStatus
17
+ from ..types.ir.task import TaskConfig
18
+
19
+
20
+ @runtime_checkable
21
+ class Provider(Protocol):
22
+ """Single interface for all agent backends.
23
+
24
+ Each provider wraps one agent CLI or SDK and translates
25
+ between TaskConfig/IREvent and the agent's native format.
26
+
27
+ Providers form fallback chains per agent (e.g., NativeProvider
28
+ is tried before SDKProvider). The first available provider wins.
29
+ """
30
+
31
+ @staticmethod
32
+ def is_available() -> bool:
33
+ """Check if this provider can be used (CLI/SDK installed).
34
+
35
+ Returns:
36
+ True if the provider's dependencies are satisfied.
37
+ """
38
+ ...
39
+
40
+ def capabilities(self) -> AgentCapabilities:
41
+ """Declare supported features.
42
+
43
+ Returns:
44
+ AgentCapabilities describing what this provider supports.
45
+ """
46
+ ...
47
+
48
+ def stream(self, task: TaskConfig) -> AsyncIterator[IREvent]:
49
+ """Run task and yield IR events as they arrive.
50
+
51
+ Args:
52
+ task: Unified task configuration.
53
+
54
+ Yields:
55
+ IR events produced by the agent.
56
+ """
57
+ ...
58
+
59
+ async def run(self, task: TaskConfig) -> SessionResult:
60
+ """Run task and return aggregated result.
61
+
62
+ Default implementation consumes stream() and aggregates events.
63
+
64
+ Args:
65
+ task: Unified task configuration.
66
+
67
+ Returns:
68
+ Aggregated session result.
69
+ """
70
+ ...
71
+
72
+
73
+ async def default_run(provider: Any, task: TaskConfig) -> SessionResult:
74
+ """Default run() implementation that aggregates stream() events.
75
+
76
+ Providers can use this as their run() body to avoid duplication.
77
+
78
+ Args:
79
+ provider: The provider instance (any object with stream()).
80
+ task: Unified task configuration.
81
+
82
+ Returns:
83
+ Aggregated SessionResult built from stream events.
84
+ """
85
+ session_id = ""
86
+ delta_parts: List[str] = []
87
+ result_text = ""
88
+ status: SessionStatus = "success"
89
+ model = ""
90
+ cost_usd = 0.0
91
+ errors: List[str] = []
92
+ usage: UsageInfo = {}
93
+
94
+ async for event in provider.stream(task):
95
+ etype = event.get("type")
96
+ if etype == "session_start":
97
+ session_id = event.get("session_id", "")
98
+ model = event.get("model", "")
99
+ elif etype == "message_delta":
100
+ text = event.get("text", "")
101
+ if text:
102
+ delta_parts.append(text)
103
+ elif etype == "message_end":
104
+ text = event.get("text")
105
+ if text:
106
+ result_text = text
107
+ elif delta_parts:
108
+ result_text = "".join(delta_parts)
109
+ delta_parts = []
110
+ elif etype == "usage":
111
+ usage = event.get("usage", {})
112
+ cost = event.get("cost_usd")
113
+ if cost is not None:
114
+ cost_usd = cost
115
+ elif etype == "error":
116
+ errors.append(event.get("error", ""))
117
+ if event.get("is_fatal"):
118
+ status = "error"
119
+
120
+ result: SessionResult = {
121
+ "session_id": session_id,
122
+ "status": status,
123
+ }
124
+ if model:
125
+ result["model"] = model
126
+ if result_text:
127
+ result["result_text"] = result_text
128
+ if usage:
129
+ result["usage"] = usage
130
+ if cost_usd:
131
+ result["cost_usd"] = cost_usd
132
+ if errors:
133
+ result["errors"] = errors
134
+
135
+ return result
136
+
137
+
138
+ __all__ = [
139
+ "Provider",
140
+ "default_run",
141
+ ]