windcode 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.
- windcode/__init__.py +6 -0
- windcode/__main__.py +4 -0
- windcode/auth/__init__.py +11 -0
- windcode/auth/store.py +90 -0
- windcode/cli.py +181 -0
- windcode/config/__init__.py +41 -0
- windcode/config/loader.py +117 -0
- windcode/config/models.py +203 -0
- windcode/config/writer.py +74 -0
- windcode/context/__init__.py +18 -0
- windcode/context/compactor.py +72 -0
- windcode/context/estimator.py +89 -0
- windcode/context/truncation.py +87 -0
- windcode/domain/__init__.py +1 -0
- windcode/domain/errors.py +33 -0
- windcode/domain/events.py +597 -0
- windcode/domain/messages.py +226 -0
- windcode/domain/models.py +73 -0
- windcode/domain/subagents.py +263 -0
- windcode/domain/tools.py +55 -0
- windcode/extensions/__init__.py +27 -0
- windcode/extensions/commands.py +25 -0
- windcode/extensions/discovery.py +139 -0
- windcode/extensions/events.py +58 -0
- windcode/extensions/hooks/__init__.py +4 -0
- windcode/extensions/hooks/dispatcher.py +107 -0
- windcode/extensions/hooks/executor.py +49 -0
- windcode/extensions/hooks/loader.py +101 -0
- windcode/extensions/hooks/models.py +109 -0
- windcode/extensions/mcp/__init__.py +10 -0
- windcode/extensions/mcp/adapter.py +101 -0
- windcode/extensions/mcp/catalog.py +153 -0
- windcode/extensions/mcp/client.py +273 -0
- windcode/extensions/mcp/runtime.py +144 -0
- windcode/extensions/mcp/tools.py +570 -0
- windcode/extensions/models.py +168 -0
- windcode/extensions/paths.py +71 -0
- windcode/extensions/plugins/__init__.py +3 -0
- windcode/extensions/plugins/installer.py +93 -0
- windcode/extensions/plugins/manifest.py +166 -0
- windcode/extensions/runtime.py +366 -0
- windcode/extensions/service.py +437 -0
- windcode/extensions/skills/__init__.py +3 -0
- windcode/extensions/skills/loader.py +67 -0
- windcode/extensions/skills/parser.py +57 -0
- windcode/extensions/skills/tools.py +213 -0
- windcode/extensions/snapshot.py +57 -0
- windcode/extensions/state.py +158 -0
- windcode/instructions/__init__.py +8 -0
- windcode/instructions/loader.py +50 -0
- windcode/memory/__init__.py +57 -0
- windcode/memory/extraction.py +83 -0
- windcode/memory/models.py +218 -0
- windcode/memory/refiner.py +189 -0
- windcode/memory/security.py +23 -0
- windcode/memory/service.py +179 -0
- windcode/memory/store.py +362 -0
- windcode/observability/__init__.py +4 -0
- windcode/observability/redaction.py +95 -0
- windcode/observability/trace.py +115 -0
- windcode/policy/__init__.py +22 -0
- windcode/policy/engine.py +137 -0
- windcode/policy/models.py +69 -0
- windcode/providers/__init__.py +29 -0
- windcode/providers/_utils.py +19 -0
- windcode/providers/anthropic.py +215 -0
- windcode/providers/base.py +46 -0
- windcode/providers/catalog.py +119 -0
- windcode/providers/errors.py +96 -0
- windcode/providers/openai_compat.py +231 -0
- windcode/providers/openai_responses.py +189 -0
- windcode/providers/registry.py +105 -0
- windcode/runtime/__init__.py +15 -0
- windcode/runtime/control.py +89 -0
- windcode/runtime/event_bus.py +67 -0
- windcode/runtime/loop.py +510 -0
- windcode/runtime/prompts.py +132 -0
- windcode/runtime/report.py +64 -0
- windcode/runtime/retry.py +73 -0
- windcode/runtime/scheduler.py +194 -0
- windcode/runtime/subagents/__init__.py +28 -0
- windcode/runtime/subagents/approvals.py +88 -0
- windcode/runtime/subagents/budgets.py +79 -0
- windcode/runtime/subagents/coordinator.py +714 -0
- windcode/runtime/subagents/factory.py +311 -0
- windcode/runtime/subagents/roles.py +74 -0
- windcode/runtime/subagents/verification.py +53 -0
- windcode/sandbox/__init__.py +3 -0
- windcode/sandbox/bwrap.py +79 -0
- windcode/sdk.py +1259 -0
- windcode/sessions/__init__.py +23 -0
- windcode/sessions/artifacts.py +69 -0
- windcode/sessions/models.py +104 -0
- windcode/sessions/store.py +167 -0
- windcode/sessions/tree.py +35 -0
- windcode/tools/__init__.py +10 -0
- windcode/tools/apply_patch.py +191 -0
- windcode/tools/ask_user.py +58 -0
- windcode/tools/builtins.py +44 -0
- windcode/tools/edit_file.py +54 -0
- windcode/tools/filesystem.py +77 -0
- windcode/tools/glob.py +35 -0
- windcode/tools/grep.py +66 -0
- windcode/tools/memory.py +400 -0
- windcode/tools/read_file.py +54 -0
- windcode/tools/registry.py +120 -0
- windcode/tools/shell.py +159 -0
- windcode/tools/subagents/__init__.py +31 -0
- windcode/tools/subagents/cancel.py +35 -0
- windcode/tools/subagents/integrate.py +54 -0
- windcode/tools/subagents/list.py +46 -0
- windcode/tools/subagents/spawn.py +86 -0
- windcode/tools/subagents/wait.py +74 -0
- windcode/tools/write_file.py +61 -0
- windcode/tui/__init__.py +3 -0
- windcode/tui/app.py +1015 -0
- windcode/tui/commands.py +90 -0
- windcode/tui/permission_display.py +24 -0
- windcode/tui/styles.tcss +591 -0
- windcode/tui/widgets/__init__.py +31 -0
- windcode/tui/widgets/approval.py +98 -0
- windcode/tui/widgets/command_menu.py +90 -0
- windcode/tui/widgets/extensions.py +48 -0
- windcode/tui/widgets/input.py +110 -0
- windcode/tui/widgets/memory.py +143 -0
- windcode/tui/widgets/messages.py +299 -0
- windcode/tui/widgets/models.py +565 -0
- windcode/tui/widgets/question.py +46 -0
- windcode/tui/widgets/sessions.py +68 -0
- windcode/tui/widgets/status.py +46 -0
- windcode/tui/widgets/subagents.py +195 -0
- windcode/tui/widgets/tools.py +66 -0
- windcode/tui/widgets/welcome.py +149 -0
- windcode/types.py +80 -0
- windcode/worktrees/__init__.py +24 -0
- windcode/worktrees/git.py +92 -0
- windcode/worktrees/manager.py +265 -0
- windcode/worktrees/models.py +62 -0
- windcode-0.1.0.dist-info/METADATA +207 -0
- windcode-0.1.0.dist-info/RECORD +143 -0
- windcode-0.1.0.dist-info/WHEEL +4 -0
- windcode-0.1.0.dist-info/entry_points.txt +2 -0
- windcode-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import asdict, dataclass, field, is_dataclass
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, ClassVar, cast
|
|
9
|
+
|
|
10
|
+
from windcode.domain.models import Usage
|
|
11
|
+
from windcode.domain.tools import ToolResult
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True, slots=True)
|
|
15
|
+
class RunRequest:
|
|
16
|
+
prompt: str
|
|
17
|
+
workspace: Path
|
|
18
|
+
session_id: str | None = None
|
|
19
|
+
model: str | None = None
|
|
20
|
+
permission_mode: str | None = None
|
|
21
|
+
compact_before_run: bool = False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True, slots=True)
|
|
25
|
+
class ApprovalResponse:
|
|
26
|
+
request_id: str
|
|
27
|
+
decision: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class UserResponse:
|
|
32
|
+
request_id: str
|
|
33
|
+
answers: dict[str, str]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
RunResponse = ApprovalResponse | UserResponse
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True, slots=True)
|
|
40
|
+
class RunResult:
|
|
41
|
+
status: str
|
|
42
|
+
final_text: str = ""
|
|
43
|
+
changed_files: tuple[str, ...] = ()
|
|
44
|
+
verification: tuple[str, ...] = ()
|
|
45
|
+
usage: Usage = field(default_factory=Usage)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True, slots=True)
|
|
49
|
+
class AgentEvent:
|
|
50
|
+
event_id: str
|
|
51
|
+
session_id: str
|
|
52
|
+
run_id: str
|
|
53
|
+
turn: int
|
|
54
|
+
created_at: datetime = field(default_factory=lambda: datetime.now(UTC))
|
|
55
|
+
sequence: int | None = None
|
|
56
|
+
kind: ClassVar[str] = "event"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True, slots=True)
|
|
60
|
+
class RunStarted(AgentEvent):
|
|
61
|
+
kind: ClassVar[str] = "run_started"
|
|
62
|
+
prompt: str = ""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True, slots=True)
|
|
66
|
+
class ModelStarted(AgentEvent):
|
|
67
|
+
kind: ClassVar[str] = "model_started"
|
|
68
|
+
model: str = ""
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True, slots=True)
|
|
72
|
+
class TextDeltaEvent(AgentEvent):
|
|
73
|
+
kind: ClassVar[str] = "text_delta"
|
|
74
|
+
text: str = ""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True, slots=True)
|
|
78
|
+
class ReasoningStatus(AgentEvent):
|
|
79
|
+
kind: ClassVar[str] = "reasoning_status"
|
|
80
|
+
status: str = ""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(frozen=True, slots=True)
|
|
84
|
+
class ToolStarted(AgentEvent):
|
|
85
|
+
kind: ClassVar[str] = "tool_started"
|
|
86
|
+
call_id: str = ""
|
|
87
|
+
tool_name: str = ""
|
|
88
|
+
arguments: dict[str, Any] = field(default_factory=dict[str, Any])
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True, slots=True)
|
|
92
|
+
class ToolProgress(AgentEvent):
|
|
93
|
+
kind: ClassVar[str] = "tool_progress"
|
|
94
|
+
call_id: str = ""
|
|
95
|
+
message: str = ""
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass(frozen=True, slots=True)
|
|
99
|
+
class ToolFinished(AgentEvent):
|
|
100
|
+
kind: ClassVar[str] = "tool_finished"
|
|
101
|
+
call_id: str = ""
|
|
102
|
+
result: ToolResult = field(default_factory=lambda: ToolResult(output=""))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True, slots=True)
|
|
106
|
+
class ApprovalRequested(AgentEvent):
|
|
107
|
+
kind: ClassVar[str] = "approval_requested"
|
|
108
|
+
request_id: str = ""
|
|
109
|
+
summary: str = ""
|
|
110
|
+
risk: str = ""
|
|
111
|
+
choices: tuple[str, ...] = ()
|
|
112
|
+
subagent_id: str | None = None
|
|
113
|
+
subagent_role: str | None = None
|
|
114
|
+
tool_name: str | None = None
|
|
115
|
+
arguments_summary: str | None = None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass(frozen=True, slots=True)
|
|
119
|
+
class UserInputRequested(AgentEvent):
|
|
120
|
+
kind: ClassVar[str] = "user_input_requested"
|
|
121
|
+
request_id: str = ""
|
|
122
|
+
questions: tuple[dict[str, Any], ...] = ()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass(frozen=True, slots=True)
|
|
126
|
+
class UsageUpdated(AgentEvent):
|
|
127
|
+
kind: ClassVar[str] = "usage_updated"
|
|
128
|
+
usage: Usage = field(default_factory=Usage)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass(frozen=True, slots=True)
|
|
132
|
+
class ModelRetrying(AgentEvent):
|
|
133
|
+
kind: ClassVar[str] = "model_retrying"
|
|
134
|
+
model: str = ""
|
|
135
|
+
attempt: int = 0
|
|
136
|
+
reason: str = ""
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass(frozen=True, slots=True)
|
|
140
|
+
class ModelFallback(AgentEvent):
|
|
141
|
+
kind: ClassVar[str] = "model_fallback"
|
|
142
|
+
from_model: str = ""
|
|
143
|
+
to_model: str = ""
|
|
144
|
+
reason: str = ""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@dataclass(frozen=True, slots=True)
|
|
148
|
+
class ContextCompacted(AgentEvent):
|
|
149
|
+
kind: ClassVar[str] = "context_compacted"
|
|
150
|
+
before_tokens: int = 0
|
|
151
|
+
after_tokens: int = 0
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@dataclass(frozen=True, slots=True)
|
|
155
|
+
class RunCompleted(AgentEvent):
|
|
156
|
+
kind: ClassVar[str] = "run_completed"
|
|
157
|
+
result: RunResult = field(default_factory=lambda: RunResult(status="completed"))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@dataclass(frozen=True, slots=True)
|
|
161
|
+
class RunFailed(AgentEvent):
|
|
162
|
+
kind: ClassVar[str] = "run_failed"
|
|
163
|
+
message: str = ""
|
|
164
|
+
category: str = "internal"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@dataclass(frozen=True, slots=True)
|
|
168
|
+
class RunCancelled(AgentEvent):
|
|
169
|
+
kind: ClassVar[str] = "run_cancelled"
|
|
170
|
+
reason: str = "cancelled by user"
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclass(frozen=True, slots=True)
|
|
174
|
+
class ExtensionEvent(AgentEvent):
|
|
175
|
+
kind: ClassVar[str] = "extension_event"
|
|
176
|
+
action: str = "diagnostic"
|
|
177
|
+
snapshot_generation: int = 0
|
|
178
|
+
extension_id: str = ""
|
|
179
|
+
source_id: str = ""
|
|
180
|
+
server_id: str | None = None
|
|
181
|
+
hook_id: str | None = None
|
|
182
|
+
call_id: str | None = None
|
|
183
|
+
status: str = ""
|
|
184
|
+
details: dict[str, Any] = field(default_factory=dict[str, Any])
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass(frozen=True, slots=True)
|
|
188
|
+
class MemoryEvent(AgentEvent):
|
|
189
|
+
kind: ClassVar[str] = "memory_event"
|
|
190
|
+
action: str = "recalled"
|
|
191
|
+
memory_id: str | None = None
|
|
192
|
+
memory_kind: str | None = None
|
|
193
|
+
scope: str | None = None
|
|
194
|
+
status: str = ""
|
|
195
|
+
details: dict[str, Any] = field(default_factory=dict[str, Any])
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@dataclass(frozen=True, slots=True)
|
|
199
|
+
class SubagentEvent(AgentEvent):
|
|
200
|
+
parent_run_id: str = ""
|
|
201
|
+
subagent_id: str = ""
|
|
202
|
+
task_index: int = 0
|
|
203
|
+
role: str = ""
|
|
204
|
+
task_name: str = ""
|
|
205
|
+
summary: str = ""
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@dataclass(frozen=True, slots=True)
|
|
209
|
+
class SubagentQueued(SubagentEvent):
|
|
210
|
+
kind: ClassVar[str] = "subagent_queued"
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@dataclass(frozen=True, slots=True)
|
|
214
|
+
class SubagentStarted(SubagentEvent):
|
|
215
|
+
kind: ClassVar[str] = "subagent_started"
|
|
216
|
+
workspace: str = ""
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@dataclass(frozen=True, slots=True)
|
|
220
|
+
class SubagentProgress(SubagentEvent):
|
|
221
|
+
kind: ClassVar[str] = "subagent_progress"
|
|
222
|
+
activity: str = ""
|
|
223
|
+
usage: Usage = field(default_factory=Usage)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@dataclass(frozen=True, slots=True)
|
|
227
|
+
class SubagentBlocked(SubagentEvent):
|
|
228
|
+
kind: ClassVar[str] = "subagent_blocked"
|
|
229
|
+
reason: str = ""
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@dataclass(frozen=True, slots=True)
|
|
233
|
+
class SubagentCompleted(SubagentEvent):
|
|
234
|
+
kind: ClassVar[str] = "subagent_completed"
|
|
235
|
+
commit: str | None = None
|
|
236
|
+
changed_files: tuple[str, ...] = ()
|
|
237
|
+
verification: tuple[str, ...] = ()
|
|
238
|
+
usage: Usage = field(default_factory=Usage)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@dataclass(frozen=True, slots=True)
|
|
242
|
+
class SubagentFailed(SubagentEvent):
|
|
243
|
+
kind: ClassVar[str] = "subagent_failed"
|
|
244
|
+
message: str = ""
|
|
245
|
+
category: str = "internal"
|
|
246
|
+
usage: Usage = field(default_factory=Usage)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@dataclass(frozen=True, slots=True)
|
|
250
|
+
class SubagentCancelled(SubagentEvent):
|
|
251
|
+
kind: ClassVar[str] = "subagent_cancelled"
|
|
252
|
+
reason: str = "cancelled"
|
|
253
|
+
usage: Usage = field(default_factory=Usage)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@dataclass(frozen=True, slots=True)
|
|
257
|
+
class SubagentIntegrated(SubagentEvent):
|
|
258
|
+
kind: ClassVar[str] = "subagent_integrated"
|
|
259
|
+
commit: str = ""
|
|
260
|
+
verification: tuple[str, ...] = ()
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@dataclass(frozen=True, slots=True)
|
|
264
|
+
class SubagentConflict(SubagentEvent):
|
|
265
|
+
kind: ClassVar[str] = "subagent_conflict"
|
|
266
|
+
conflict_files: tuple[str, ...] = ()
|
|
267
|
+
message: str = ""
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass(frozen=True, slots=True)
|
|
271
|
+
class SubagentCleanup(SubagentEvent):
|
|
272
|
+
kind: ClassVar[str] = "subagent_cleanup"
|
|
273
|
+
removed: bool = False
|
|
274
|
+
retained_path: str | None = None
|
|
275
|
+
reason: str | None = None
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
AgentEventType = (
|
|
279
|
+
RunStarted
|
|
280
|
+
| ModelStarted
|
|
281
|
+
| TextDeltaEvent
|
|
282
|
+
| ReasoningStatus
|
|
283
|
+
| ToolStarted
|
|
284
|
+
| ToolProgress
|
|
285
|
+
| ToolFinished
|
|
286
|
+
| ApprovalRequested
|
|
287
|
+
| UserInputRequested
|
|
288
|
+
| UsageUpdated
|
|
289
|
+
| ModelRetrying
|
|
290
|
+
| ModelFallback
|
|
291
|
+
| ContextCompacted
|
|
292
|
+
| RunCompleted
|
|
293
|
+
| RunFailed
|
|
294
|
+
| RunCancelled
|
|
295
|
+
| ExtensionEvent
|
|
296
|
+
| MemoryEvent
|
|
297
|
+
| SubagentQueued
|
|
298
|
+
| SubagentStarted
|
|
299
|
+
| SubagentProgress
|
|
300
|
+
| SubagentBlocked
|
|
301
|
+
| SubagentCompleted
|
|
302
|
+
| SubagentFailed
|
|
303
|
+
| SubagentCancelled
|
|
304
|
+
| SubagentIntegrated
|
|
305
|
+
| SubagentConflict
|
|
306
|
+
| SubagentCleanup
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _json_value(value: object) -> object:
|
|
311
|
+
if isinstance(value, datetime):
|
|
312
|
+
return value.isoformat()
|
|
313
|
+
if isinstance(value, Path):
|
|
314
|
+
return str(value)
|
|
315
|
+
if isinstance(value, Enum):
|
|
316
|
+
return value.value
|
|
317
|
+
if is_dataclass(value) and not isinstance(value, type):
|
|
318
|
+
raw = cast(dict[str, object], asdict(value))
|
|
319
|
+
return {key: _json_value(item) for key, item in raw.items()}
|
|
320
|
+
if isinstance(value, dict):
|
|
321
|
+
raw_mapping = cast(dict[object, object], value)
|
|
322
|
+
return {str(key): _json_value(item) for key, item in raw_mapping.items()}
|
|
323
|
+
if isinstance(value, (tuple, list)):
|
|
324
|
+
raw_sequence = cast(list[object] | tuple[object, ...], value)
|
|
325
|
+
return [_json_value(item) for item in raw_sequence]
|
|
326
|
+
return value
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def event_to_dict(event: AgentEventType) -> dict[str, Any]:
|
|
330
|
+
payload = cast(dict[str, Any], _json_value(event))
|
|
331
|
+
return {"kind": event.kind, **payload}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _mapping(value: object) -> dict[str, object]:
|
|
335
|
+
if not isinstance(value, Mapping):
|
|
336
|
+
return {}
|
|
337
|
+
raw = cast(Mapping[object, object], value)
|
|
338
|
+
return {str(key): item for key, item in raw.items()}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _int_value(value: object, default: int = 0) -> int:
|
|
342
|
+
if isinstance(value, int) and not isinstance(value, bool):
|
|
343
|
+
return value
|
|
344
|
+
if isinstance(value, str):
|
|
345
|
+
return int(value)
|
|
346
|
+
return default
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _float_value(value: object, default: float = 0.0) -> float:
|
|
350
|
+
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
351
|
+
return float(value)
|
|
352
|
+
if isinstance(value, str):
|
|
353
|
+
return float(value)
|
|
354
|
+
return default
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _usage(value: object) -> Usage:
|
|
358
|
+
raw = _mapping(value)
|
|
359
|
+
return Usage(
|
|
360
|
+
input_tokens=_int_value(raw.get("input_tokens")),
|
|
361
|
+
output_tokens=_int_value(raw.get("output_tokens")),
|
|
362
|
+
cache_read_tokens=_int_value(raw.get("cache_read_tokens")),
|
|
363
|
+
cache_write_tokens=_int_value(raw.get("cache_write_tokens")),
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _common(raw: Mapping[str, object]) -> dict[str, Any]:
|
|
368
|
+
created = raw.get("created_at")
|
|
369
|
+
sequence = raw.get("sequence")
|
|
370
|
+
return {
|
|
371
|
+
"event_id": str(raw["event_id"]),
|
|
372
|
+
"session_id": str(raw["session_id"]),
|
|
373
|
+
"run_id": str(raw["run_id"]),
|
|
374
|
+
"turn": _int_value(raw["turn"]),
|
|
375
|
+
"created_at": (
|
|
376
|
+
datetime.fromisoformat(created) if isinstance(created, str) else datetime.now(UTC)
|
|
377
|
+
),
|
|
378
|
+
"sequence": int(sequence) if isinstance(sequence, int) else None,
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _subagent_common(raw: Mapping[str, object]) -> dict[str, Any]:
|
|
383
|
+
return {
|
|
384
|
+
**_common(raw),
|
|
385
|
+
"parent_run_id": str(raw.get("parent_run_id", "")),
|
|
386
|
+
"subagent_id": str(raw.get("subagent_id", "")),
|
|
387
|
+
"task_index": _int_value(raw.get("task_index")),
|
|
388
|
+
"role": str(raw.get("role", "")),
|
|
389
|
+
"task_name": str(raw.get("task_name", "")),
|
|
390
|
+
"summary": str(raw.get("summary", "")),
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def event_from_dict(value: Mapping[str, object]) -> AgentEventType:
|
|
395
|
+
raw = {str(key): item for key, item in value.items()}
|
|
396
|
+
kind = str(raw.get("kind", ""))
|
|
397
|
+
common = _common(raw)
|
|
398
|
+
if kind == RunStarted.kind:
|
|
399
|
+
return RunStarted(**common, prompt=str(raw.get("prompt", "")))
|
|
400
|
+
if kind == ModelStarted.kind:
|
|
401
|
+
return ModelStarted(**common, model=str(raw.get("model", "")))
|
|
402
|
+
if kind == TextDeltaEvent.kind:
|
|
403
|
+
return TextDeltaEvent(**common, text=str(raw.get("text", "")))
|
|
404
|
+
if kind == ReasoningStatus.kind:
|
|
405
|
+
return ReasoningStatus(**common, status=str(raw.get("status", "")))
|
|
406
|
+
if kind == ToolStarted.kind:
|
|
407
|
+
return ToolStarted(
|
|
408
|
+
**common,
|
|
409
|
+
call_id=str(raw.get("call_id", "")),
|
|
410
|
+
tool_name=str(raw.get("tool_name", "")),
|
|
411
|
+
arguments=_mapping(raw.get("arguments")),
|
|
412
|
+
)
|
|
413
|
+
if kind == ToolProgress.kind:
|
|
414
|
+
return ToolProgress(
|
|
415
|
+
**common,
|
|
416
|
+
call_id=str(raw.get("call_id", "")),
|
|
417
|
+
message=str(raw.get("message", "")),
|
|
418
|
+
)
|
|
419
|
+
if kind == ToolFinished.kind:
|
|
420
|
+
result = _mapping(raw.get("result"))
|
|
421
|
+
return ToolFinished(
|
|
422
|
+
**common,
|
|
423
|
+
call_id=str(raw.get("call_id", "")),
|
|
424
|
+
result=ToolResult(
|
|
425
|
+
output=str(result.get("output", "")),
|
|
426
|
+
is_error=bool(result.get("is_error", False)),
|
|
427
|
+
artifact_ref=(
|
|
428
|
+
None if result.get("artifact_ref") is None else str(result.get("artifact_ref"))
|
|
429
|
+
),
|
|
430
|
+
elapsed_seconds=_float_value(result.get("elapsed_seconds")),
|
|
431
|
+
data=_mapping(result.get("data")),
|
|
432
|
+
),
|
|
433
|
+
)
|
|
434
|
+
if kind == ApprovalRequested.kind:
|
|
435
|
+
choices = raw.get("choices", ())
|
|
436
|
+
return ApprovalRequested(
|
|
437
|
+
**common,
|
|
438
|
+
request_id=str(raw.get("request_id", "")),
|
|
439
|
+
summary=str(raw.get("summary", "")),
|
|
440
|
+
risk=str(raw.get("risk", "")),
|
|
441
|
+
choices=tuple(str(item) for item in cast(list[object] | tuple[object, ...], choices)),
|
|
442
|
+
subagent_id=(None if raw.get("subagent_id") is None else str(raw.get("subagent_id"))),
|
|
443
|
+
subagent_role=(
|
|
444
|
+
None if raw.get("subagent_role") is None else str(raw.get("subagent_role"))
|
|
445
|
+
),
|
|
446
|
+
tool_name=None if raw.get("tool_name") is None else str(raw.get("tool_name")),
|
|
447
|
+
arguments_summary=(
|
|
448
|
+
None if raw.get("arguments_summary") is None else str(raw.get("arguments_summary"))
|
|
449
|
+
),
|
|
450
|
+
)
|
|
451
|
+
if kind == UserInputRequested.kind:
|
|
452
|
+
questions = raw.get("questions", ())
|
|
453
|
+
return UserInputRequested(
|
|
454
|
+
**common,
|
|
455
|
+
request_id=str(raw.get("request_id", "")),
|
|
456
|
+
questions=tuple(
|
|
457
|
+
_mapping(item) for item in cast(list[object] | tuple[object, ...], questions)
|
|
458
|
+
),
|
|
459
|
+
)
|
|
460
|
+
if kind == UsageUpdated.kind:
|
|
461
|
+
return UsageUpdated(**common, usage=_usage(raw.get("usage")))
|
|
462
|
+
if kind == ModelRetrying.kind:
|
|
463
|
+
return ModelRetrying(
|
|
464
|
+
**common,
|
|
465
|
+
model=str(raw.get("model", "")),
|
|
466
|
+
attempt=_int_value(raw.get("attempt")),
|
|
467
|
+
reason=str(raw.get("reason", "")),
|
|
468
|
+
)
|
|
469
|
+
if kind == ModelFallback.kind:
|
|
470
|
+
return ModelFallback(
|
|
471
|
+
**common,
|
|
472
|
+
from_model=str(raw.get("from_model", "")),
|
|
473
|
+
to_model=str(raw.get("to_model", "")),
|
|
474
|
+
reason=str(raw.get("reason", "")),
|
|
475
|
+
)
|
|
476
|
+
if kind == ContextCompacted.kind:
|
|
477
|
+
return ContextCompacted(
|
|
478
|
+
**common,
|
|
479
|
+
before_tokens=_int_value(raw.get("before_tokens")),
|
|
480
|
+
after_tokens=_int_value(raw.get("after_tokens")),
|
|
481
|
+
)
|
|
482
|
+
if kind == RunCompleted.kind:
|
|
483
|
+
result = _mapping(raw.get("result"))
|
|
484
|
+
changed = result.get("changed_files", ())
|
|
485
|
+
verification = result.get("verification", ())
|
|
486
|
+
return RunCompleted(
|
|
487
|
+
**common,
|
|
488
|
+
result=RunResult(
|
|
489
|
+
status=str(result.get("status", "completed")),
|
|
490
|
+
final_text=str(result.get("final_text", "")),
|
|
491
|
+
changed_files=tuple(
|
|
492
|
+
str(item) for item in cast(list[object] | tuple[object, ...], changed)
|
|
493
|
+
),
|
|
494
|
+
verification=tuple(
|
|
495
|
+
str(item) for item in cast(list[object] | tuple[object, ...], verification)
|
|
496
|
+
),
|
|
497
|
+
usage=_usage(result.get("usage")),
|
|
498
|
+
),
|
|
499
|
+
)
|
|
500
|
+
if kind == RunFailed.kind:
|
|
501
|
+
return RunFailed(
|
|
502
|
+
**common,
|
|
503
|
+
message=str(raw.get("message", "")),
|
|
504
|
+
category=str(raw.get("category", "internal")),
|
|
505
|
+
)
|
|
506
|
+
if kind == RunCancelled.kind:
|
|
507
|
+
return RunCancelled(**common, reason=str(raw.get("reason", "cancelled by user")))
|
|
508
|
+
if kind == ExtensionEvent.kind:
|
|
509
|
+
return ExtensionEvent(
|
|
510
|
+
**common,
|
|
511
|
+
action=str(raw.get("action", "diagnostic")),
|
|
512
|
+
snapshot_generation=_int_value(raw.get("snapshot_generation")),
|
|
513
|
+
extension_id=str(raw.get("extension_id", "")),
|
|
514
|
+
source_id=str(raw.get("source_id", "")),
|
|
515
|
+
server_id=None if raw.get("server_id") is None else str(raw.get("server_id")),
|
|
516
|
+
hook_id=None if raw.get("hook_id") is None else str(raw.get("hook_id")),
|
|
517
|
+
call_id=None if raw.get("call_id") is None else str(raw.get("call_id")),
|
|
518
|
+
status=str(raw.get("status", "")),
|
|
519
|
+
details=_mapping(raw.get("details")),
|
|
520
|
+
)
|
|
521
|
+
if kind == MemoryEvent.kind:
|
|
522
|
+
return MemoryEvent(
|
|
523
|
+
**common,
|
|
524
|
+
action=str(raw.get("action", "recalled")),
|
|
525
|
+
memory_id=None if raw.get("memory_id") is None else str(raw.get("memory_id")),
|
|
526
|
+
memory_kind=(None if raw.get("memory_kind") is None else str(raw.get("memory_kind"))),
|
|
527
|
+
scope=None if raw.get("scope") is None else str(raw.get("scope")),
|
|
528
|
+
status=str(raw.get("status", "")),
|
|
529
|
+
details=_mapping(raw.get("details")),
|
|
530
|
+
)
|
|
531
|
+
if kind == SubagentQueued.kind:
|
|
532
|
+
return SubagentQueued(**_subagent_common(raw))
|
|
533
|
+
if kind == SubagentStarted.kind:
|
|
534
|
+
return SubagentStarted(**_subagent_common(raw), workspace=str(raw.get("workspace", "")))
|
|
535
|
+
if kind == SubagentProgress.kind:
|
|
536
|
+
return SubagentProgress(
|
|
537
|
+
**_subagent_common(raw),
|
|
538
|
+
activity=str(raw.get("activity", "")),
|
|
539
|
+
usage=_usage(raw.get("usage")),
|
|
540
|
+
)
|
|
541
|
+
if kind == SubagentBlocked.kind:
|
|
542
|
+
return SubagentBlocked(**_subagent_common(raw), reason=str(raw.get("reason", "")))
|
|
543
|
+
if kind == SubagentCompleted.kind:
|
|
544
|
+
changed = raw.get("changed_files", ())
|
|
545
|
+
verification = raw.get("verification", ())
|
|
546
|
+
return SubagentCompleted(
|
|
547
|
+
**_subagent_common(raw),
|
|
548
|
+
commit=None if raw.get("commit") is None else str(raw.get("commit")),
|
|
549
|
+
changed_files=tuple(
|
|
550
|
+
str(item) for item in cast(list[object] | tuple[object, ...], changed)
|
|
551
|
+
),
|
|
552
|
+
verification=tuple(
|
|
553
|
+
str(item) for item in cast(list[object] | tuple[object, ...], verification)
|
|
554
|
+
),
|
|
555
|
+
usage=_usage(raw.get("usage")),
|
|
556
|
+
)
|
|
557
|
+
if kind == SubagentFailed.kind:
|
|
558
|
+
return SubagentFailed(
|
|
559
|
+
**_subagent_common(raw),
|
|
560
|
+
message=str(raw.get("message", "")),
|
|
561
|
+
category=str(raw.get("category", "internal")),
|
|
562
|
+
usage=_usage(raw.get("usage")),
|
|
563
|
+
)
|
|
564
|
+
if kind == SubagentCancelled.kind:
|
|
565
|
+
return SubagentCancelled(
|
|
566
|
+
**_subagent_common(raw),
|
|
567
|
+
reason=str(raw.get("reason", "cancelled")),
|
|
568
|
+
usage=_usage(raw.get("usage")),
|
|
569
|
+
)
|
|
570
|
+
if kind == SubagentIntegrated.kind:
|
|
571
|
+
verification = raw.get("verification", ())
|
|
572
|
+
return SubagentIntegrated(
|
|
573
|
+
**_subagent_common(raw),
|
|
574
|
+
commit=str(raw.get("commit", "")),
|
|
575
|
+
verification=tuple(
|
|
576
|
+
str(item) for item in cast(list[object] | tuple[object, ...], verification)
|
|
577
|
+
),
|
|
578
|
+
)
|
|
579
|
+
if kind == SubagentConflict.kind:
|
|
580
|
+
conflict_files = raw.get("conflict_files", ())
|
|
581
|
+
return SubagentConflict(
|
|
582
|
+
**_subagent_common(raw),
|
|
583
|
+
conflict_files=tuple(
|
|
584
|
+
str(item) for item in cast(list[object] | tuple[object, ...], conflict_files)
|
|
585
|
+
),
|
|
586
|
+
message=str(raw.get("message", "")),
|
|
587
|
+
)
|
|
588
|
+
if kind == SubagentCleanup.kind:
|
|
589
|
+
return SubagentCleanup(
|
|
590
|
+
**_subagent_common(raw),
|
|
591
|
+
removed=bool(raw.get("removed", False)),
|
|
592
|
+
retained_path=(
|
|
593
|
+
None if raw.get("retained_path") is None else str(raw.get("retained_path"))
|
|
594
|
+
),
|
|
595
|
+
reason=None if raw.get("reason") is None else str(raw.get("reason")),
|
|
596
|
+
)
|
|
597
|
+
raise ValueError(f"unknown agent event kind: {kind}")
|