agentpool-cli 0.1.0__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.
Files changed (60) hide show
  1. agentpool/__init__.py +3 -0
  2. agentpool/agent_io.py +134 -0
  3. agentpool/artifacts.py +151 -0
  4. agentpool/cli.py +1199 -0
  5. agentpool/config.py +373 -0
  6. agentpool/docs/agentpool-skill.md +85 -0
  7. agentpool/docs/onboarding.md +169 -0
  8. agentpool/event_detection.py +150 -0
  9. agentpool/fixtures/__init__.py +1 -0
  10. agentpool/fixtures/fake_agents/__init__.py +1 -0
  11. agentpool/fixtures/fake_agents/fake_approval_agent.py +16 -0
  12. agentpool/fixtures/fake_agents/fake_common.py +44 -0
  13. agentpool/fixtures/fake_agents/fake_completed_agent.py +13 -0
  14. agentpool/fixtures/fake_agents/fake_idle_agent.py +16 -0
  15. agentpool/fixtures/fake_agents/fake_limit_agent.py +14 -0
  16. agentpool/fixtures/fake_agents/fake_patch_agent.py +17 -0
  17. agentpool/fixtures/fake_agents/fake_question_agent.py +16 -0
  18. agentpool/git_worktree.py +144 -0
  19. agentpool/mcp/__init__.py +1 -0
  20. agentpool/mcp/resources.py +64 -0
  21. agentpool/mcp/tools.py +259 -0
  22. agentpool/mcp_server.py +487 -0
  23. agentpool/models.py +310 -0
  24. agentpool/onboarding.py +1279 -0
  25. agentpool/policy.py +63 -0
  26. agentpool/provider_model_catalog.json +997 -0
  27. agentpool/providers/__init__.py +3 -0
  28. agentpool/providers/base.py +411 -0
  29. agentpool/providers/registry.py +139 -0
  30. agentpool/redaction.py +30 -0
  31. agentpool/runtimes/__init__.py +3 -0
  32. agentpool/runtimes/base.py +36 -0
  33. agentpool/runtimes/tmux.py +133 -0
  34. agentpool/session_manager.py +1061 -0
  35. agentpool/stats/__init__.py +6 -0
  36. agentpool/stats/card.py +74 -0
  37. agentpool/stats/compute.py +496 -0
  38. agentpool/stats/queries.py +138 -0
  39. agentpool/stats/render.py +103 -0
  40. agentpool/stats/window.py +85 -0
  41. agentpool/store.py +478 -0
  42. agentpool/usage/__init__.py +1 -0
  43. agentpool/usage/_common.py +223 -0
  44. agentpool/usage/ccusage.py +130 -0
  45. agentpool/usage/claude.py +23 -0
  46. agentpool/usage/codex.py +210 -0
  47. agentpool/usage/codexbar.py +186 -0
  48. agentpool/usage/combine.py +71 -0
  49. agentpool/usage/copilot.py +146 -0
  50. agentpool/usage/devin.py +265 -0
  51. agentpool/usage/parsers.py +41 -0
  52. agentpool/usage/probes.py +52 -0
  53. agentpool/usage/provider_parsers.py +276 -0
  54. agentpool/usage/summary.py +166 -0
  55. agentpool/utils.py +59 -0
  56. agentpool_cli-0.1.0.dist-info/METADATA +292 -0
  57. agentpool_cli-0.1.0.dist-info/RECORD +60 -0
  58. agentpool_cli-0.1.0.dist-info/WHEEL +4 -0
  59. agentpool_cli-0.1.0.dist-info/entry_points.txt +2 -0
  60. agentpool_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
agentpool/models.py ADDED
@@ -0,0 +1,310 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime, timezone
4
+ from enum import StrEnum
5
+ import re
6
+ from typing import Any, Literal
7
+
8
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
9
+
10
+
11
+ def now_utc() -> datetime:
12
+ return datetime.now(timezone.utc)
13
+
14
+
15
+ class Confidence(StrEnum):
16
+ OFFICIAL = "official"
17
+ LOCAL_CLI = "local_cli"
18
+ LOCAL_CONFIG = "local_config"
19
+ PROVIDER_WARNING = "provider_warning"
20
+ OBSERVED = "observed"
21
+ USER_CONFIGURED = "user_configured"
22
+ UNKNOWN = "unknown"
23
+
24
+
25
+ LEGACY_CONFIDENCE_MAP = {
26
+ "high": Confidence.OBSERVED,
27
+ "medium": Confidence.OBSERVED,
28
+ "low": Confidence.UNKNOWN,
29
+ }
30
+
31
+
32
+ PLACEHOLDER_TASK_PATTERNS = [
33
+ re.compile(r"@filename\b", re.I),
34
+ re.compile(r"<\s*filename\s*>", re.I),
35
+ re.compile(r"\{\s*filename\s*\}", re.I),
36
+ re.compile(r"<\s*task\s*>", re.I),
37
+ re.compile(r"\b(your task here|replace this|todo task)\b", re.I),
38
+ ]
39
+
40
+
41
+ def normalize_confidence(value: Any) -> Any:
42
+ if isinstance(value, str):
43
+ return LEGACY_CONFIDENCE_MAP.get(value.lower(), value)
44
+ return value
45
+
46
+
47
+ class RuntimeKind(StrEnum):
48
+ TMUX = "tmux"
49
+ PTY = "pty"
50
+ ACP = "acp"
51
+
52
+
53
+ class Capability(StrEnum):
54
+ LIVE_STEERING = "live_steering"
55
+ READ_ONLY_ADVISORY = "read_only_advisory"
56
+ WORKTREE_EDITS = "worktree_edits"
57
+ CAN_RUN_TESTS = "can_run_tests"
58
+ SUPPORTS_MODEL_ARG = "supports_model_arg"
59
+ SUPPORTS_RESUME = "supports_resume"
60
+ SUPPORTS_MCP_INJECTION = "supports_mcp_injection"
61
+ SUPPORTS_ACP = "supports_acp"
62
+ SUPPORTS_USAGE_PROBE = "supports_usage_probe"
63
+ SUPPORTS_APPROVAL_DETECTION = "supports_approval_detection"
64
+ SUPPORTS_ONE_SHOT_MODE = "supports_one_shot_mode"
65
+ SUPPORTS_INTERACTIVE_MODE = "supports_interactive_mode"
66
+
67
+
68
+ class UsageStatus(StrEnum):
69
+ AVAILABLE = "available"
70
+ NEAR_LIMIT = "near_limit"
71
+ LIMIT_REACHED = "limit_reached"
72
+ OVERAGE_POSSIBLE = "overage_possible"
73
+ UNAUTHENTICATED = "unauthenticated"
74
+ UNAVAILABLE = "unavailable"
75
+ UNKNOWN = "unknown"
76
+
77
+
78
+ class UsageWindowKind(StrEnum):
79
+ DAILY = "daily"
80
+ FIVE_HOUR = "5h"
81
+ WEEKLY = "weekly"
82
+ MONTHLY = "monthly"
83
+ SESSION = "session"
84
+ MODEL = "model"
85
+ CREDITS = "credits"
86
+ ON_DEMAND = "on_demand"
87
+ UNKNOWN = "unknown"
88
+
89
+
90
+ class SessionState(StrEnum):
91
+ STARTING = "STARTING"
92
+ READY = "READY"
93
+ RUNNING = "RUNNING"
94
+ AWAITING_USER_INPUT = "AWAITING_USER_INPUT"
95
+ AWAITING_APPROVAL = "AWAITING_APPROVAL"
96
+ IDLE = "IDLE"
97
+ COMPLETED = "COMPLETED"
98
+ FAILED = "FAILED"
99
+ CANCELLED = "CANCELLED"
100
+ UNKNOWN = "UNKNOWN"
101
+
102
+
103
+ class ObserveEvent(StrEnum):
104
+ NONE = "none"
105
+ QUESTION = "question"
106
+ APPROVAL_PROMPT = "approval_prompt"
107
+ IDLE = "idle"
108
+ COMPLETED = "completed"
109
+ ERROR = "error"
110
+ LIMIT_WARNING = "limit_warning"
111
+ OVERAGE_PROMPT = "overage_prompt"
112
+ SCREEN_CHANGED = "screen_changed"
113
+ TIMEOUT = "timeout"
114
+
115
+
116
+ class AuthStatus(BaseModel):
117
+ status: Literal["authenticated", "unauthenticated", "unknown", "unavailable"]
118
+ confidence: Confidence
119
+ reason: str | None = None
120
+ checked_at: datetime = Field(default_factory=now_utc)
121
+
122
+ _normalize_confidence = field_validator("confidence", mode="before")(normalize_confidence)
123
+
124
+
125
+ class ModelDescriptor(BaseModel):
126
+ id: str
127
+ display_name: str | None = None
128
+ source: Literal["cli_detected", "config", "default", "observed", "unknown"]
129
+ confidence: Confidence
130
+ aliases: list[str] = Field(default_factory=list)
131
+ metadata: dict[str, Any] = Field(default_factory=dict)
132
+
133
+ _normalize_confidence = field_validator("confidence", mode="before")(normalize_confidence)
134
+
135
+
136
+ class UsageWindow(BaseModel):
137
+ name: str
138
+ kind: UsageWindowKind = UsageWindowKind.UNKNOWN
139
+ status: str | None = None
140
+ remaining_percent: float | None = None
141
+ used_percent: float | None = None
142
+ remaining_units: float | None = None
143
+ used_units: float | None = None
144
+ reset_at: datetime | None = None
145
+ confidence: Confidence
146
+ raw_text: str | None = None
147
+
148
+ _normalize_confidence = field_validator("confidence", mode="before")(normalize_confidence)
149
+
150
+
151
+ class CapacitySnapshot(BaseModel):
152
+ provider_id: str
153
+ status: UsageStatus
154
+ confidence: Confidence
155
+ checked_at: datetime = Field(default_factory=now_utc)
156
+ windows: list[UsageWindow] = Field(default_factory=list)
157
+ premium_requests_remaining: int | None = None
158
+ credits_remaining: float | None = None
159
+ reset_at: datetime | None = None
160
+ warnings: list[str] = Field(default_factory=list)
161
+ raw: dict[str, Any] = Field(default_factory=dict)
162
+
163
+ _normalize_confidence = field_validator("confidence", mode="before")(normalize_confidence)
164
+
165
+
166
+ class AccountDescriptor(BaseModel):
167
+ id: str
168
+ display_name: str | None = None
169
+ confidence: Confidence = Confidence.UNKNOWN
170
+ metadata: dict[str, Any] = Field(default_factory=dict)
171
+
172
+ _normalize_confidence = field_validator("confidence", mode="before")(normalize_confidence)
173
+
174
+
175
+ class ProviderDescriptor(BaseModel):
176
+ id: str
177
+ display_name: str
178
+ vendor: str | None = None
179
+ harness: str
180
+ installed: bool
181
+ binary_path: str | None = None
182
+ version: str | None = None
183
+ auth: AuthStatus
184
+ models: list[ModelDescriptor] = Field(default_factory=list)
185
+ runtimes: list[RuntimeKind] = Field(default_factory=list)
186
+ capabilities: list[Capability] = Field(default_factory=list)
187
+ usage: CapacitySnapshot | None = None
188
+ accounts: list[AccountDescriptor] = Field(default_factory=list)
189
+ warnings: list[str] = Field(default_factory=list)
190
+ metadata: dict[str, Any] = Field(default_factory=dict)
191
+
192
+
193
+ class AgentTarget(BaseModel):
194
+ provider_id: str
195
+ model: str | None = None
196
+ harness: str | None = None
197
+ runtime: RuntimeKind = RuntimeKind.TMUX
198
+ account: str | None = None
199
+
200
+
201
+ class SpawnWorkerRequest(BaseModel):
202
+ provider_id: str
203
+ task: str
204
+ repo_path: str
205
+ role: Literal["explorer", "reviewer", "implementer", "tester", "custom"] = "explorer"
206
+ model: str | None = None
207
+ account: str | None = None
208
+ runtime: RuntimeKind = RuntimeKind.TMUX
209
+ isolation: Literal["read_only", "worktree", "shared"] = "read_only"
210
+ allowed_files: list[str] = Field(default_factory=list)
211
+ max_runtime_seconds: int | None = None
212
+ max_turns: int | None = None
213
+ supervision: Literal["interactive", "autonomous", "human_visible"] = "interactive"
214
+ initial_prompt_mode: Literal["provider_default", "send_after_launch", "arg", "stdin"] = "provider_default"
215
+ reasoning_effort: str | None = None
216
+ service_tier: str | None = None
217
+ metadata: dict[str, Any] = Field(default_factory=dict)
218
+
219
+ @field_validator("task")
220
+ @classmethod
221
+ def task_must_be_concrete(cls, value: str) -> str:
222
+ task = value.strip()
223
+ if not task:
224
+ raise ValueError("task must be a concrete non-empty instruction")
225
+ for pattern in PLACEHOLDER_TASK_PATTERNS:
226
+ if pattern.search(task):
227
+ raise ValueError("task contains placeholder text; pass the actual delegated task")
228
+ return task
229
+
230
+
231
+ class TmuxSessionRef(BaseModel):
232
+ session_name: str
233
+ window: str = "0"
234
+ pane: str = "0"
235
+
236
+ @property
237
+ def target(self) -> str:
238
+ return f"{self.session_name}:{self.window}.{self.pane}"
239
+
240
+
241
+ class AgentSession(BaseModel):
242
+ id: str
243
+ provider_id: str
244
+ model: str | None = None
245
+ harness: str
246
+ account: str | None = None
247
+ role: str
248
+ task: str
249
+ repo_path: str
250
+ worktree_path: str | None = None
251
+ runtime: RuntimeKind
252
+ state: SessionState
253
+ created_at: datetime
254
+ updated_at: datetime
255
+ ended_at: datetime | None = None
256
+ tmux: TmuxSessionRef | None = None
257
+ artifact_dir: str
258
+ transcript_path: str
259
+ events_path: str
260
+ metadata: dict[str, Any] = Field(default_factory=dict)
261
+
262
+
263
+ class ObserveWorkerResponse(BaseModel):
264
+ session_id: str
265
+ state: SessionState
266
+ event: ObserveEvent
267
+ screen_excerpt: str | None = None
268
+ recent_log: str | None = None
269
+ parsed_question: str | None = None
270
+ suggested_next_action: str | None = None
271
+ confidence: Confidence
272
+ observed_at: datetime = Field(default_factory=now_utc)
273
+ metadata: dict[str, Any] = Field(default_factory=dict)
274
+
275
+ _normalize_confidence = field_validator("confidence", mode="before")(normalize_confidence)
276
+
277
+
278
+ class ArtifactRecord(BaseModel):
279
+ kind: str
280
+ path: str
281
+ sha256: str | None = None
282
+ metadata: dict[str, Any] = Field(default_factory=dict)
283
+
284
+
285
+ class FileLease(BaseModel):
286
+ id: int | None = None
287
+ session_id: str
288
+ repo_path: str
289
+ file_path: str
290
+ mode: Literal["read", "write"] = "write"
291
+ expires_at: datetime | None = None
292
+ created_at: datetime = Field(default_factory=now_utc)
293
+ released_at: datetime | None = None
294
+ metadata: dict[str, Any] = Field(default_factory=dict)
295
+
296
+
297
+ class AgentPoolError(BaseModel):
298
+ code: str
299
+ message: str
300
+ details: dict[str, Any] = Field(default_factory=dict)
301
+
302
+
303
+ class ToolError(Exception):
304
+ def __init__(self, code: str, message: str, details: dict[str, Any] | None = None):
305
+ super().__init__(message)
306
+ self.error = AgentPoolError(code=code, message=message, details=details or {})
307
+
308
+
309
+ class JsonModel(BaseModel):
310
+ model_config = ConfigDict(use_enum_values=True)