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.
Files changed (143) hide show
  1. windcode/__init__.py +6 -0
  2. windcode/__main__.py +4 -0
  3. windcode/auth/__init__.py +11 -0
  4. windcode/auth/store.py +90 -0
  5. windcode/cli.py +181 -0
  6. windcode/config/__init__.py +41 -0
  7. windcode/config/loader.py +117 -0
  8. windcode/config/models.py +203 -0
  9. windcode/config/writer.py +74 -0
  10. windcode/context/__init__.py +18 -0
  11. windcode/context/compactor.py +72 -0
  12. windcode/context/estimator.py +89 -0
  13. windcode/context/truncation.py +87 -0
  14. windcode/domain/__init__.py +1 -0
  15. windcode/domain/errors.py +33 -0
  16. windcode/domain/events.py +597 -0
  17. windcode/domain/messages.py +226 -0
  18. windcode/domain/models.py +73 -0
  19. windcode/domain/subagents.py +263 -0
  20. windcode/domain/tools.py +55 -0
  21. windcode/extensions/__init__.py +27 -0
  22. windcode/extensions/commands.py +25 -0
  23. windcode/extensions/discovery.py +139 -0
  24. windcode/extensions/events.py +58 -0
  25. windcode/extensions/hooks/__init__.py +4 -0
  26. windcode/extensions/hooks/dispatcher.py +107 -0
  27. windcode/extensions/hooks/executor.py +49 -0
  28. windcode/extensions/hooks/loader.py +101 -0
  29. windcode/extensions/hooks/models.py +109 -0
  30. windcode/extensions/mcp/__init__.py +10 -0
  31. windcode/extensions/mcp/adapter.py +101 -0
  32. windcode/extensions/mcp/catalog.py +153 -0
  33. windcode/extensions/mcp/client.py +273 -0
  34. windcode/extensions/mcp/runtime.py +144 -0
  35. windcode/extensions/mcp/tools.py +570 -0
  36. windcode/extensions/models.py +168 -0
  37. windcode/extensions/paths.py +71 -0
  38. windcode/extensions/plugins/__init__.py +3 -0
  39. windcode/extensions/plugins/installer.py +93 -0
  40. windcode/extensions/plugins/manifest.py +166 -0
  41. windcode/extensions/runtime.py +366 -0
  42. windcode/extensions/service.py +437 -0
  43. windcode/extensions/skills/__init__.py +3 -0
  44. windcode/extensions/skills/loader.py +67 -0
  45. windcode/extensions/skills/parser.py +57 -0
  46. windcode/extensions/skills/tools.py +213 -0
  47. windcode/extensions/snapshot.py +57 -0
  48. windcode/extensions/state.py +158 -0
  49. windcode/instructions/__init__.py +8 -0
  50. windcode/instructions/loader.py +50 -0
  51. windcode/memory/__init__.py +57 -0
  52. windcode/memory/extraction.py +83 -0
  53. windcode/memory/models.py +218 -0
  54. windcode/memory/refiner.py +189 -0
  55. windcode/memory/security.py +23 -0
  56. windcode/memory/service.py +179 -0
  57. windcode/memory/store.py +362 -0
  58. windcode/observability/__init__.py +4 -0
  59. windcode/observability/redaction.py +95 -0
  60. windcode/observability/trace.py +115 -0
  61. windcode/policy/__init__.py +22 -0
  62. windcode/policy/engine.py +137 -0
  63. windcode/policy/models.py +69 -0
  64. windcode/providers/__init__.py +29 -0
  65. windcode/providers/_utils.py +19 -0
  66. windcode/providers/anthropic.py +215 -0
  67. windcode/providers/base.py +46 -0
  68. windcode/providers/catalog.py +119 -0
  69. windcode/providers/errors.py +96 -0
  70. windcode/providers/openai_compat.py +231 -0
  71. windcode/providers/openai_responses.py +189 -0
  72. windcode/providers/registry.py +105 -0
  73. windcode/runtime/__init__.py +15 -0
  74. windcode/runtime/control.py +89 -0
  75. windcode/runtime/event_bus.py +67 -0
  76. windcode/runtime/loop.py +510 -0
  77. windcode/runtime/prompts.py +132 -0
  78. windcode/runtime/report.py +64 -0
  79. windcode/runtime/retry.py +73 -0
  80. windcode/runtime/scheduler.py +194 -0
  81. windcode/runtime/subagents/__init__.py +28 -0
  82. windcode/runtime/subagents/approvals.py +88 -0
  83. windcode/runtime/subagents/budgets.py +79 -0
  84. windcode/runtime/subagents/coordinator.py +714 -0
  85. windcode/runtime/subagents/factory.py +311 -0
  86. windcode/runtime/subagents/roles.py +74 -0
  87. windcode/runtime/subagents/verification.py +53 -0
  88. windcode/sandbox/__init__.py +3 -0
  89. windcode/sandbox/bwrap.py +79 -0
  90. windcode/sdk.py +1259 -0
  91. windcode/sessions/__init__.py +23 -0
  92. windcode/sessions/artifacts.py +69 -0
  93. windcode/sessions/models.py +104 -0
  94. windcode/sessions/store.py +167 -0
  95. windcode/sessions/tree.py +35 -0
  96. windcode/tools/__init__.py +10 -0
  97. windcode/tools/apply_patch.py +191 -0
  98. windcode/tools/ask_user.py +58 -0
  99. windcode/tools/builtins.py +44 -0
  100. windcode/tools/edit_file.py +54 -0
  101. windcode/tools/filesystem.py +77 -0
  102. windcode/tools/glob.py +35 -0
  103. windcode/tools/grep.py +66 -0
  104. windcode/tools/memory.py +400 -0
  105. windcode/tools/read_file.py +54 -0
  106. windcode/tools/registry.py +120 -0
  107. windcode/tools/shell.py +159 -0
  108. windcode/tools/subagents/__init__.py +31 -0
  109. windcode/tools/subagents/cancel.py +35 -0
  110. windcode/tools/subagents/integrate.py +54 -0
  111. windcode/tools/subagents/list.py +46 -0
  112. windcode/tools/subagents/spawn.py +86 -0
  113. windcode/tools/subagents/wait.py +74 -0
  114. windcode/tools/write_file.py +61 -0
  115. windcode/tui/__init__.py +3 -0
  116. windcode/tui/app.py +1015 -0
  117. windcode/tui/commands.py +90 -0
  118. windcode/tui/permission_display.py +24 -0
  119. windcode/tui/styles.tcss +591 -0
  120. windcode/tui/widgets/__init__.py +31 -0
  121. windcode/tui/widgets/approval.py +98 -0
  122. windcode/tui/widgets/command_menu.py +90 -0
  123. windcode/tui/widgets/extensions.py +48 -0
  124. windcode/tui/widgets/input.py +110 -0
  125. windcode/tui/widgets/memory.py +143 -0
  126. windcode/tui/widgets/messages.py +299 -0
  127. windcode/tui/widgets/models.py +565 -0
  128. windcode/tui/widgets/question.py +46 -0
  129. windcode/tui/widgets/sessions.py +68 -0
  130. windcode/tui/widgets/status.py +46 -0
  131. windcode/tui/widgets/subagents.py +195 -0
  132. windcode/tui/widgets/tools.py +66 -0
  133. windcode/tui/widgets/welcome.py +149 -0
  134. windcode/types.py +80 -0
  135. windcode/worktrees/__init__.py +24 -0
  136. windcode/worktrees/git.py +92 -0
  137. windcode/worktrees/manager.py +265 -0
  138. windcode/worktrees/models.py +62 -0
  139. windcode-0.1.0.dist-info/METADATA +207 -0
  140. windcode-0.1.0.dist-info/RECORD +143 -0
  141. windcode-0.1.0.dist-info/WHEEL +4 -0
  142. windcode-0.1.0.dist-info/entry_points.txt +2 -0
  143. 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}")