crabcode 0.1.6__py3-none-win_amd64.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.
- crabcode-0.1.6.dist-info/METADATA +63 -0
- crabcode-0.1.6.dist-info/RECORD +205 -0
- crabcode-0.1.6.dist-info/WHEEL +4 -0
- crabcode-0.1.6.dist-info/entry_points.txt +2 -0
- crabcode_cli/__init__.py +7 -0
- crabcode_cli/__main__.py +4 -0
- crabcode_cli/_native/crabcode-computer-use-host.exe +0 -0
- crabcode_cli/_native/runtime.json +6 -0
- crabcode_cli/app.py +776 -0
- crabcode_cli/banner.py +39 -0
- crabcode_cli/computer_use.py +286 -0
- crabcode_cli/model_manager.py +649 -0
- crabcode_cli/model_picker.py +168 -0
- crabcode_cli/pipe.py +148 -0
- crabcode_cli/render/__init__.py +0 -0
- crabcode_cli/render/messages.py +19 -0
- crabcode_cli/repl.py +4841 -0
- crabcode_cli/session_picker.py +239 -0
- crabcode_core/__init__.py +7 -0
- crabcode_core/_version.py +3 -0
- crabcode_core/_windows_process.py +68 -0
- crabcode_core/agent_manager.py +1731 -0
- crabcode_core/api/__init__.py +20 -0
- crabcode_core/api/anthropic_adapter.py +648 -0
- crabcode_core/api/azure_adapter.py +69 -0
- crabcode_core/api/base.py +233 -0
- crabcode_core/api/codex_adapter.py +1327 -0
- crabcode_core/api/error_logging.py +132 -0
- crabcode_core/api/gemini_adapter.py +26 -0
- crabcode_core/api/model_info.py +257 -0
- crabcode_core/api/network.py +62 -0
- crabcode_core/api/ollama_adapter.py +27 -0
- crabcode_core/api/openai_adapter.py +323 -0
- crabcode_core/api/registry.py +58 -0
- crabcode_core/command_line.py +18 -0
- crabcode_core/compact/__init__.py +17 -0
- crabcode_core/compact/compact.py +514 -0
- crabcode_core/compact/context_tokens.py +165 -0
- crabcode_core/computer_use_mode.py +38 -0
- crabcode_core/computer_use_observation.py +144 -0
- crabcode_core/config/__init__.py +3 -0
- crabcode_core/config/claudemd.py +61 -0
- crabcode_core/config/manager.py +237 -0
- crabcode_core/config/model_catalog.py +256 -0
- crabcode_core/events.py +4896 -0
- crabcode_core/file_lock.py +81 -0
- crabcode_core/filesystem.py +22 -0
- crabcode_core/goal.py +149 -0
- crabcode_core/hooks/__init__.py +5 -0
- crabcode_core/hooks/manager.py +290 -0
- crabcode_core/image_io.py +111 -0
- crabcode_core/io_worker.py +211 -0
- crabcode_core/logging_utils.py +168 -0
- crabcode_core/lsp/__init__.py +6 -0
- crabcode_core/lsp/client.py +786 -0
- crabcode_core/lsp/diagnostics.py +150 -0
- crabcode_core/lsp/manager.py +583 -0
- crabcode_core/mcp/__init__.py +4 -0
- crabcode_core/mcp/client.py +169 -0
- crabcode_core/mcp/config.py +57 -0
- crabcode_core/path_validation.py +34 -0
- crabcode_core/paths.py +24 -0
- crabcode_core/peer/__init__.py +10 -0
- crabcode_core/peer/runtime.py +518 -0
- crabcode_core/permissions/__init__.py +7 -0
- crabcode_core/permissions/ai_reviewer.py +184 -0
- crabcode_core/permissions/manager.py +187 -0
- crabcode_core/plan/__init__.py +5 -0
- crabcode_core/plan/executor.py +270 -0
- crabcode_core/plan/types.py +169 -0
- crabcode_core/prompts/__init__.py +0 -0
- crabcode_core/prompts/blocks.py +10 -0
- crabcode_core/prompts/context.py +229 -0
- crabcode_core/prompts/library.py +92 -0
- crabcode_core/prompts/profile.py +79 -0
- crabcode_core/prompts/system.py +356 -0
- crabcode_core/prompts/templates.py +104 -0
- crabcode_core/query/__init__.py +0 -0
- crabcode_core/query/computer_use_history.py +83 -0
- crabcode_core/query/loop.py +2446 -0
- crabcode_core/query/retry.py +116 -0
- crabcode_core/schedule/__init__.py +13 -0
- crabcode_core/schedule/manager.py +631 -0
- crabcode_core/schedule/models.py +204 -0
- crabcode_core/schedule/store.py +558 -0
- crabcode_core/schedule/timing.py +287 -0
- crabcode_core/session/__init__.py +12 -0
- crabcode_core/session/export.py +141 -0
- crabcode_core/session/meta_db.py +684 -0
- crabcode_core/session/storage.py +2032 -0
- crabcode_core/session/title_gen.py +52 -0
- crabcode_core/skills/__init__.py +6 -0
- crabcode_core/skills/context.py +23 -0
- crabcode_core/skills/loader.py +214 -0
- crabcode_core/skills/matcher.py +203 -0
- crabcode_core/snapshot/__init__.py +21 -0
- crabcode_core/snapshot/snapshot.py +436 -0
- crabcode_core/snapshot/tracker.py +359 -0
- crabcode_core/subprocess_utils.py +260 -0
- crabcode_core/team/__init__.py +27 -0
- crabcode_core/team/bridge.py +66 -0
- crabcode_core/team/inbox.py +237 -0
- crabcode_core/team/manager.py +1660 -0
- crabcode_core/team/message_bus.py +821 -0
- crabcode_core/team/models.py +191 -0
- crabcode_core/team/recovery.py +192 -0
- crabcode_core/text_io.py +52 -0
- crabcode_core/tools/__init__.py +112 -0
- crabcode_core/tools/_input_helpers.py +39 -0
- crabcode_core/tools/_input_schema.py +130 -0
- crabcode_core/tools/agent.py +413 -0
- crabcode_core/tools/apply_patch.py +587 -0
- crabcode_core/tools/ask_user.py +122 -0
- crabcode_core/tools/bash.py +154 -0
- crabcode_core/tools/browser.py +678 -0
- crabcode_core/tools/checklist.py +241 -0
- crabcode_core/tools/checkpoint.py +78 -0
- crabcode_core/tools/computer_use.py +599 -0
- crabcode_core/tools/diff_utils.py +123 -0
- crabcode_core/tools/file_edit.py +240 -0
- crabcode_core/tools/file_read.py +187 -0
- crabcode_core/tools/file_write.py +154 -0
- crabcode_core/tools/glob.py +123 -0
- crabcode_core/tools/goal.py +134 -0
- crabcode_core/tools/grep.py +247 -0
- crabcode_core/tools/image.py +173 -0
- crabcode_core/tools/image_generate.py +115 -0
- crabcode_core/tools/lint.py +388 -0
- crabcode_core/tools/loading.py +187 -0
- crabcode_core/tools/memory.py +456 -0
- crabcode_core/tools/monitor.py +677 -0
- crabcode_core/tools/peer.py +103 -0
- crabcode_core/tools/revert.py +90 -0
- crabcode_core/tools/schedule.py +683 -0
- crabcode_core/tools/skill.py +108 -0
- crabcode_core/tools/switch_mode.py +213 -0
- crabcode_core/tools/team.py +329 -0
- crabcode_core/tools/tool_search.py +64 -0
- crabcode_core/tools/web_search.py +444 -0
- crabcode_core/turn_details.py +64 -0
- crabcode_core/types/__init__.py +95 -0
- crabcode_core/types/config.py +434 -0
- crabcode_core/types/event.py +334 -0
- crabcode_core/types/message.py +263 -0
- crabcode_core/types/tool.py +208 -0
- crabcode_core/usage.py +289 -0
- crabcode_core/utf8_sanitize.py +29 -0
- crabcode_debugger/__init__.py +5 -0
- crabcode_debugger/adapters.py +332 -0
- crabcode_debugger/dap.py +304 -0
- crabcode_debugger/memory.py +1806 -0
- crabcode_debugger/process.py +707 -0
- crabcode_debugger/runtime.py +58 -0
- crabcode_debugger/sessions.py +387 -0
- crabcode_debugger/tool.py +731 -0
- crabcode_gateway/__init__.py +18 -0
- crabcode_gateway/acp/__init__.py +13 -0
- crabcode_gateway/acp/agent.py +1581 -0
- crabcode_gateway/acp/session.py +114 -0
- crabcode_gateway/acp/transport.py +39 -0
- crabcode_gateway/acp/types.py +90 -0
- crabcode_gateway/adapter.py +30 -0
- crabcode_gateway/auth.py +139 -0
- crabcode_gateway/computer_use.py +364 -0
- crabcode_gateway/document_engine.py +637 -0
- crabcode_gateway/document_worker.py +301 -0
- crabcode_gateway/event_bus.py +655 -0
- crabcode_gateway/grpc/__init__.py +0 -0
- crabcode_gateway/grpc/crabcode_pb2.py +70 -0
- crabcode_gateway/grpc/crabcode_pb2_grpc.py +474 -0
- crabcode_gateway/grpc/proto/crabcode.proto +160 -0
- crabcode_gateway/grpc/server.py +765 -0
- crabcode_gateway/middleware.py +344 -0
- crabcode_gateway/protocol.py +17 -0
- crabcode_gateway/routes/__init__.py +0 -0
- crabcode_gateway/routes/agent.py +392 -0
- crabcode_gateway/routes/auth.py +150 -0
- crabcode_gateway/routes/computer_use.py +101 -0
- crabcode_gateway/routes/config.py +1698 -0
- crabcode_gateway/routes/document.py +1810 -0
- crabcode_gateway/routes/event.py +3801 -0
- crabcode_gateway/routes/git_review.py +232 -0
- crabcode_gateway/routes/health.py +14 -0
- crabcode_gateway/routes/peer.py +78 -0
- crabcode_gateway/routes/permission.py +121 -0
- crabcode_gateway/routes/schedule.py +300 -0
- crabcode_gateway/routes/session.py +1496 -0
- crabcode_gateway/routes/snapshot.py +211 -0
- crabcode_gateway/routes/tasks.py +318 -0
- crabcode_gateway/routes/team.py +392 -0
- crabcode_gateway/routes/usage.py +49 -0
- crabcode_gateway/routes/workspace.py +299 -0
- crabcode_gateway/safe_file.py +63 -0
- crabcode_gateway/schemas.py +1820 -0
- crabcode_gateway/server.py +514 -0
- crabcode_gateway/session_registry.py +36 -0
- crabcode_gateway/task_registry.py +708 -0
- crabcode_gateway/turn_details.py +60 -0
- crabcode_search/__init__.py +11 -0
- crabcode_search/background.py +368 -0
- crabcode_search/chunker.py +517 -0
- crabcode_search/embedder.py +497 -0
- crabcode_search/indexer.py +215 -0
- crabcode_search/store.py +222 -0
- crabcode_search/tool.py +268 -0
|
@@ -0,0 +1,1820 @@
|
|
|
1
|
+
"""Pydantic request/response schemas for the gateway API.
|
|
2
|
+
|
|
3
|
+
Maps crabcode_core CoreEvent dataclasses to serializable Pydantic models
|
|
4
|
+
for HTTP JSON and SSE transport.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import base64
|
|
10
|
+
import binascii
|
|
11
|
+
from typing import Any, Literal, Union
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
14
|
+
|
|
15
|
+
from crabcode_core import VERSION
|
|
16
|
+
from crabcode_gateway.protocol import (
|
|
17
|
+
GATEWAY_MAX_PROTOCOL_VERSION,
|
|
18
|
+
GATEWAY_MIN_PROTOCOL_VERSION,
|
|
19
|
+
GATEWAY_PROTOCOL_VERSION,
|
|
20
|
+
)
|
|
21
|
+
from crabcode_core.types.config import DEFAULT_COMPACT_BUFFER_TOKENS, PermissionRule, ReasoningEffort
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class UsageDayResponse(BaseModel):
|
|
25
|
+
date: str
|
|
26
|
+
input_tokens: int
|
|
27
|
+
output_tokens: int
|
|
28
|
+
total_tokens: int | None
|
|
29
|
+
request_count: int
|
|
30
|
+
unknown_requests: int
|
|
31
|
+
missing_requests: int
|
|
32
|
+
partial_requests: int
|
|
33
|
+
coverage: Literal["complete", "partial", "unavailable"]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class UsagePointResponse(BaseModel):
|
|
37
|
+
date: str
|
|
38
|
+
total_tokens: int | None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class UsageModelResponse(BaseModel):
|
|
42
|
+
key: str
|
|
43
|
+
provider: str
|
|
44
|
+
model_id: str
|
|
45
|
+
model: str
|
|
46
|
+
total_tokens: int
|
|
47
|
+
recorded_request_count: int
|
|
48
|
+
points: list[UsagePointResponse]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class UsageSummaryResponse(BaseModel):
|
|
52
|
+
input_tokens: int
|
|
53
|
+
output_tokens: int
|
|
54
|
+
total_tokens: int
|
|
55
|
+
request_count: int
|
|
56
|
+
unknown_requests: int
|
|
57
|
+
missing_requests: int
|
|
58
|
+
partial_requests: int
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class UsageDailyResponse(BaseModel):
|
|
62
|
+
start: str
|
|
63
|
+
end: str
|
|
64
|
+
timezone: str
|
|
65
|
+
scope: Literal["global", "project"]
|
|
66
|
+
generated_at: str
|
|
67
|
+
tracking_started_at: str | None
|
|
68
|
+
first_recorded_at: str | None
|
|
69
|
+
last_recorded_at: str | None
|
|
70
|
+
days: list[UsageDayResponse]
|
|
71
|
+
models: list[UsageModelResponse]
|
|
72
|
+
summary: UsageSummaryResponse
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ── Request schemas ──────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
MAX_IMAGE_ATTACHMENT_BYTES = 20 * 1024 * 1024
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ImageAttachment(BaseModel):
|
|
82
|
+
media_type: str
|
|
83
|
+
data: str
|
|
84
|
+
description: str = ""
|
|
85
|
+
|
|
86
|
+
@field_validator("media_type")
|
|
87
|
+
@classmethod
|
|
88
|
+
def validate_media_type(cls, value: str) -> str:
|
|
89
|
+
media_type = value.strip()
|
|
90
|
+
if not media_type.lower().startswith("image/"):
|
|
91
|
+
raise ValueError("media_type must be an image MIME type")
|
|
92
|
+
return media_type
|
|
93
|
+
|
|
94
|
+
@field_validator("data")
|
|
95
|
+
@classmethod
|
|
96
|
+
def validate_data(cls, value: str) -> str:
|
|
97
|
+
# Reject obviously oversized input before decoding it. A base64
|
|
98
|
+
# string for N bytes is at most 4 * ceil(N / 3) characters.
|
|
99
|
+
max_encoded_chars = 4 * ((MAX_IMAGE_ATTACHMENT_BYTES + 2) // 3)
|
|
100
|
+
if len(value) > max_encoded_chars:
|
|
101
|
+
raise ValueError("image data exceeds the 20MB limit")
|
|
102
|
+
try:
|
|
103
|
+
decoded = base64.b64decode(value, validate=True)
|
|
104
|
+
except (binascii.Error, ValueError) as exc:
|
|
105
|
+
raise ValueError("data must be valid base64") from exc
|
|
106
|
+
if len(decoded) > MAX_IMAGE_ATTACHMENT_BYTES:
|
|
107
|
+
raise ValueError("image data exceeds the 20MB limit")
|
|
108
|
+
return value
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class SendMessageRequest(BaseModel):
|
|
112
|
+
text: str
|
|
113
|
+
max_turns: int = Field(default=0, ge=0, strict=True)
|
|
114
|
+
session_id: str | None = None
|
|
115
|
+
operation_id: str | None = Field(default=None, min_length=1)
|
|
116
|
+
images: list[ImageAttachment] = Field(default_factory=list)
|
|
117
|
+
computer_use_enabled: bool | None = None
|
|
118
|
+
computer_use_mode: Literal["background_app", "foreground_desktop"] | None = None
|
|
119
|
+
computer_use_target_scope: Literal["app_window", "desktop"] | None = None
|
|
120
|
+
computer_use_delivery_policy: Literal["strict_background", "allow_foreground"] | None = None
|
|
121
|
+
|
|
122
|
+
@model_validator(mode="after")
|
|
123
|
+
def validate_content(self) -> "SendMessageRequest":
|
|
124
|
+
if not self.text.strip() and not self.images:
|
|
125
|
+
raise ValueError("text or at least one image is required")
|
|
126
|
+
return self
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class NewSessionRequest(BaseModel):
|
|
130
|
+
cwd: str | None = None
|
|
131
|
+
additional_directories: list[str] = Field(default_factory=list)
|
|
132
|
+
model: str | None = None
|
|
133
|
+
provider: str | None = None
|
|
134
|
+
base_url: str | None = None
|
|
135
|
+
api_format: str | None = None
|
|
136
|
+
model_profile: str | None = None
|
|
137
|
+
reasoning_effort: ReasoningEffort | None = None
|
|
138
|
+
ultra_mode: bool | None = None
|
|
139
|
+
mode: Literal["agent", "plan"] | None = None
|
|
140
|
+
permission_mode: Literal[
|
|
141
|
+
"default",
|
|
142
|
+
"ask",
|
|
143
|
+
"run_everything",
|
|
144
|
+
"bypassPermissions",
|
|
145
|
+
"ai_review",
|
|
146
|
+
"aiReview",
|
|
147
|
+
] | None = None
|
|
148
|
+
computer_use_host_id: str | None = Field(default=None, min_length=1, max_length=200)
|
|
149
|
+
computer_use_enabled: bool | None = None
|
|
150
|
+
computer_use_mode: Literal["background_app", "foreground_desktop"] | None = None
|
|
151
|
+
computer_use_target_scope: Literal["app_window", "desktop"] | None = None
|
|
152
|
+
computer_use_delivery_policy: Literal["strict_background", "allow_foreground"] | None = None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class ResumeSessionRequest(BaseModel):
|
|
156
|
+
session_id: str
|
|
157
|
+
additional_directories: list[str] = Field(default_factory=list)
|
|
158
|
+
model: str | None = None
|
|
159
|
+
provider: str | None = None
|
|
160
|
+
base_url: str | None = None
|
|
161
|
+
api_format: str | None = None
|
|
162
|
+
model_profile: str | None = None
|
|
163
|
+
computer_use_host_id: str | None = Field(default=None, min_length=1, max_length=200)
|
|
164
|
+
computer_use_enabled: bool | None = None
|
|
165
|
+
computer_use_mode: Literal["background_app", "foreground_desktop"] | None = None
|
|
166
|
+
computer_use_target_scope: Literal["app_window", "desktop"] | None = None
|
|
167
|
+
computer_use_delivery_policy: Literal["strict_background", "allow_foreground"] | None = None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class ForkSessionRequest(BaseModel):
|
|
171
|
+
session_id: str
|
|
172
|
+
message_uuid: str | None = Field(default=None, min_length=1)
|
|
173
|
+
title: str | None = Field(default=None, max_length=200)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class CompactRequest(BaseModel):
|
|
177
|
+
session_id: str
|
|
178
|
+
custom_instructions: str | None = None
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class ClearSessionRequest(BaseModel):
|
|
182
|
+
session_id: str
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class InterruptRequest(BaseModel):
|
|
186
|
+
session_id: str
|
|
187
|
+
operation_id: str | None = Field(default=None, min_length=1)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class PermissionResponseRequest(BaseModel):
|
|
191
|
+
tool_use_id: str
|
|
192
|
+
allowed: bool
|
|
193
|
+
always_allow: bool = False
|
|
194
|
+
agent_id: str | None = None
|
|
195
|
+
feedback: str | None = None
|
|
196
|
+
# Optional for multi-session clients; omitted keeps default-session behavior.
|
|
197
|
+
session_id: str | None = None
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class ChoiceResponseRequest(BaseModel):
|
|
201
|
+
tool_use_id: str
|
|
202
|
+
selected: list[str]
|
|
203
|
+
cancelled: bool = False
|
|
204
|
+
agent_id: str | None = None
|
|
205
|
+
# Optional for multi-session clients; omitted keeps default-session behavior.
|
|
206
|
+
session_id: str | None = None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class SpawnAgentRequest(BaseModel):
|
|
210
|
+
prompt: str = Field(min_length=1)
|
|
211
|
+
subagent_type: str = Field(default="generalPurpose", min_length=1)
|
|
212
|
+
name: str | None = None
|
|
213
|
+
model_profile: str | None = None
|
|
214
|
+
callback: bool = False
|
|
215
|
+
session_id: str | None = None
|
|
216
|
+
|
|
217
|
+
@field_validator("prompt", "subagent_type")
|
|
218
|
+
@classmethod
|
|
219
|
+
def validate_required_text(cls, value: str) -> str:
|
|
220
|
+
if not value.strip():
|
|
221
|
+
raise ValueError("value must not be blank")
|
|
222
|
+
return value
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class AgentInputRequest(BaseModel):
|
|
226
|
+
prompt: str = Field(min_length=1)
|
|
227
|
+
interrupt: bool = False
|
|
228
|
+
session_id: str | None = None
|
|
229
|
+
|
|
230
|
+
@field_validator("prompt")
|
|
231
|
+
@classmethod
|
|
232
|
+
def validate_prompt(cls, value: str) -> str:
|
|
233
|
+
if not value.strip():
|
|
234
|
+
raise ValueError("prompt must not be blank")
|
|
235
|
+
return value
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class WaitAgentRequest(BaseModel):
|
|
239
|
+
agent_id: str | list[str]
|
|
240
|
+
timeout_ms: int | None = Field(default=None, ge=0, strict=True)
|
|
241
|
+
session_id: str | None = None
|
|
242
|
+
|
|
243
|
+
@field_validator("agent_id")
|
|
244
|
+
@classmethod
|
|
245
|
+
def validate_agent_ids(cls, value: str | list[str]) -> str | list[str]:
|
|
246
|
+
values = [value] if isinstance(value, str) else value
|
|
247
|
+
if not values or any(not isinstance(item, str) or not item.strip() for item in values):
|
|
248
|
+
raise ValueError("agent_id must contain at least one non-blank ID")
|
|
249
|
+
return value
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class SwitchModelRequest(BaseModel):
|
|
253
|
+
name: str
|
|
254
|
+
session_id: str | None = None
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class SwitchModeRequest(BaseModel):
|
|
258
|
+
mode: Literal["agent", "plan"]
|
|
259
|
+
session_id: str | None = None
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class SetReasoningEffortRequest(BaseModel):
|
|
263
|
+
effort: ReasoningEffort | Literal["auto"]
|
|
264
|
+
session_id: str | None = None
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class SetUltraModeRequest(BaseModel):
|
|
268
|
+
# Omitted/null means toggle; an explicit boolean is idempotent.
|
|
269
|
+
enabled: bool | None = None
|
|
270
|
+
session_id: str | None = None
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class SetPermissionModeRequest(BaseModel):
|
|
274
|
+
"""Client-side tool permission override used by chat surfaces."""
|
|
275
|
+
|
|
276
|
+
mode: Literal["default", "ask", "run_everything", "ai_review"]
|
|
277
|
+
session_id: str | None = None
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class ModelSettingsMutationRequest(BaseModel):
|
|
281
|
+
"""A focused mutation of the named model configuration catalog."""
|
|
282
|
+
|
|
283
|
+
action: Literal[
|
|
284
|
+
"upsert_model",
|
|
285
|
+
"delete_model",
|
|
286
|
+
"upsert_group",
|
|
287
|
+
"delete_group",
|
|
288
|
+
"set_default_model",
|
|
289
|
+
"clear_default_model",
|
|
290
|
+
]
|
|
291
|
+
source: Literal["userSettings", "projectSettings", "localSettings"] = "projectSettings"
|
|
292
|
+
cwd: str | None = None
|
|
293
|
+
name: str | None = None
|
|
294
|
+
previous_name: str | None = None
|
|
295
|
+
config: dict[str, Any] | None = None
|
|
296
|
+
remove_fields: list[str] = Field(default_factory=list)
|
|
297
|
+
|
|
298
|
+
@model_validator(mode="after")
|
|
299
|
+
def validate_mutation(self) -> "ModelSettingsMutationRequest":
|
|
300
|
+
needs_name = self.action not in {"clear_default_model"}
|
|
301
|
+
if needs_name and (self.name is None or not self.name.strip()):
|
|
302
|
+
raise ValueError("name is required for this model settings action")
|
|
303
|
+
if self.action in {"upsert_model", "upsert_group"} and self.config is None:
|
|
304
|
+
raise ValueError("config is required when creating or updating settings")
|
|
305
|
+
if self.name is not None:
|
|
306
|
+
self.name = self.name.strip()
|
|
307
|
+
return self
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class RuntimeSettingsMutationRequest(BaseModel):
|
|
311
|
+
"""A focused mutation for runtime, Computer Use, and extra-tool settings."""
|
|
312
|
+
|
|
313
|
+
action: Literal[
|
|
314
|
+
"set_snapshot",
|
|
315
|
+
"set_compaction",
|
|
316
|
+
"set_computer_use_mode",
|
|
317
|
+
"set_computer_use_options",
|
|
318
|
+
"add_extra_tool",
|
|
319
|
+
"remove_extra_tool",
|
|
320
|
+
]
|
|
321
|
+
source: Literal["userSettings", "projectSettings", "localSettings"] = "projectSettings"
|
|
322
|
+
cwd: str | None = None
|
|
323
|
+
snapshot_enabled: bool | None = None
|
|
324
|
+
snapshot_max_size_mb: int | None = Field(default=None, ge=1, le=1_048_576)
|
|
325
|
+
auto_compact_enabled: bool | None = None
|
|
326
|
+
compact_buffer_tokens: int | None = Field(default=None, ge=0, strict=True)
|
|
327
|
+
max_context_length: int | None = Field(default=None, ge=1, strict=True)
|
|
328
|
+
computer_use_mode: Literal["background_app", "foreground_desktop"] | None = None
|
|
329
|
+
computer_use_target_scope: Literal["app_window", "desktop"] | None = None
|
|
330
|
+
computer_use_delivery_policy: Literal["strict_background", "allow_foreground"] | None = None
|
|
331
|
+
tool_path: str | None = None
|
|
332
|
+
|
|
333
|
+
@model_validator(mode="after")
|
|
334
|
+
def validate_mutation(self) -> "RuntimeSettingsMutationRequest":
|
|
335
|
+
if self.action == "set_snapshot":
|
|
336
|
+
if self.snapshot_enabled is None and self.snapshot_max_size_mb is None:
|
|
337
|
+
raise ValueError("snapshot_enabled or snapshot_max_size_mb is required")
|
|
338
|
+
elif self.action == "set_compaction":
|
|
339
|
+
if (
|
|
340
|
+
self.auto_compact_enabled is None
|
|
341
|
+
and self.compact_buffer_tokens is None
|
|
342
|
+
and "max_context_length" not in self.model_fields_set
|
|
343
|
+
):
|
|
344
|
+
raise ValueError("At least one compaction setting is required")
|
|
345
|
+
elif self.action in {"set_computer_use_mode", "set_computer_use_options"}:
|
|
346
|
+
if self.action == "set_computer_use_mode" and self.computer_use_mode is None:
|
|
347
|
+
raise ValueError("computer_use_mode is required")
|
|
348
|
+
if self.action == "set_computer_use_options" and (
|
|
349
|
+
self.computer_use_target_scope is None and self.computer_use_delivery_policy is None
|
|
350
|
+
):
|
|
351
|
+
raise ValueError("computer_use_target_scope or computer_use_delivery_policy is required")
|
|
352
|
+
else:
|
|
353
|
+
if self.tool_path is None or not self.tool_path.strip():
|
|
354
|
+
raise ValueError("tool_path is required for extra tool mutations")
|
|
355
|
+
self.tool_path = self.tool_path.strip()
|
|
356
|
+
if len(self.tool_path) > 240:
|
|
357
|
+
raise ValueError("tool_path is too long")
|
|
358
|
+
return self
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
class PromptSettingsMutationRequest(BaseModel):
|
|
362
|
+
"""Create templates and choose which saved prompts are appended to user input."""
|
|
363
|
+
|
|
364
|
+
action: Literal[
|
|
365
|
+
"save_template",
|
|
366
|
+
"delete_template",
|
|
367
|
+
"set_active_template",
|
|
368
|
+
"add_user_prompt",
|
|
369
|
+
"set_user_prompt_enabled",
|
|
370
|
+
"delete_user_prompt",
|
|
371
|
+
]
|
|
372
|
+
source: Literal["userSettings", "projectSettings", "localSettings"] = "projectSettings"
|
|
373
|
+
cwd: str | None = None
|
|
374
|
+
template_id: str | None = None
|
|
375
|
+
template_name: str | None = None
|
|
376
|
+
sections: dict[str, str] | None = None
|
|
377
|
+
prompt_id: str | None = None
|
|
378
|
+
prompt_text: str | None = None
|
|
379
|
+
enabled: bool | None = None
|
|
380
|
+
|
|
381
|
+
@model_validator(mode="after")
|
|
382
|
+
def validate_mutation(self) -> "PromptSettingsMutationRequest":
|
|
383
|
+
if self.action == "save_template":
|
|
384
|
+
if self.template_name is None or not self.template_name.strip():
|
|
385
|
+
raise ValueError("template_name is required")
|
|
386
|
+
self.template_name = self.template_name.strip()
|
|
387
|
+
elif self.action in {"delete_template", "set_active_template"}:
|
|
388
|
+
if self.template_id is not None:
|
|
389
|
+
self.template_id = self.template_id.strip() or None
|
|
390
|
+
if self.action == "delete_template" and not self.template_id:
|
|
391
|
+
raise ValueError("template_id is required")
|
|
392
|
+
elif self.action == "add_user_prompt":
|
|
393
|
+
if self.prompt_text is None or not self.prompt_text.strip():
|
|
394
|
+
raise ValueError("prompt_text is required")
|
|
395
|
+
self.prompt_text = self.prompt_text.strip()
|
|
396
|
+
else:
|
|
397
|
+
if self.prompt_id is None or not self.prompt_id.strip():
|
|
398
|
+
raise ValueError("prompt_id is required")
|
|
399
|
+
self.prompt_id = self.prompt_id.strip()
|
|
400
|
+
if self.action == "set_user_prompt_enabled" and self.enabled is None:
|
|
401
|
+
raise ValueError("enabled is required")
|
|
402
|
+
return self
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
class GoalRequest(BaseModel):
|
|
406
|
+
action: Literal[
|
|
407
|
+
"set", "edit", "pause", "resume", "complete", "blocked", "clear"
|
|
408
|
+
] = "set"
|
|
409
|
+
objective: str | None = None
|
|
410
|
+
token_budget: int | None = Field(default=None, gt=0)
|
|
411
|
+
session_id: str | None = None
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class SkillExpandRequest(BaseModel):
|
|
415
|
+
name: str
|
|
416
|
+
user_input: str = ""
|
|
417
|
+
session_id: str | None = None
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class TaskStopRequest(BaseModel):
|
|
421
|
+
task_id: str
|
|
422
|
+
session_id: str | None = None
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class ScheduleCreateRequest(BaseModel):
|
|
426
|
+
name: str = Field(min_length=1)
|
|
427
|
+
prompt: str = Field(min_length=1)
|
|
428
|
+
schedule: str = Field(min_length=1)
|
|
429
|
+
schedule_type: Literal["cron", "interval", "once"]
|
|
430
|
+
cwd: str | None = None
|
|
431
|
+
enabled: bool = True
|
|
432
|
+
max_runs: int | None = Field(default=None, gt=0)
|
|
433
|
+
next_run: str | None = None
|
|
434
|
+
description: str = ""
|
|
435
|
+
tags: list[str] = Field(default_factory=list)
|
|
436
|
+
timeout: int | None = Field(default=None, gt=0)
|
|
437
|
+
model_profile: str | None = None
|
|
438
|
+
# The Gateway session that owns this request is ``session_id``. A job can
|
|
439
|
+
# independently reuse a session for its executions via ``job_session_id``.
|
|
440
|
+
job_session_id: str | None = Field(default=None, min_length=1)
|
|
441
|
+
extra: dict[str, Any] = Field(default_factory=dict)
|
|
442
|
+
session_id: str | None = None
|
|
443
|
+
|
|
444
|
+
@field_validator("name", "prompt", "schedule")
|
|
445
|
+
@classmethod
|
|
446
|
+
def validate_required_text(cls, value: str) -> str:
|
|
447
|
+
if not value.strip():
|
|
448
|
+
raise ValueError("value must not be blank")
|
|
449
|
+
return value
|
|
450
|
+
|
|
451
|
+
@field_validator("job_session_id")
|
|
452
|
+
@classmethod
|
|
453
|
+
def validate_job_session_id(cls, value: str | None) -> str | None:
|
|
454
|
+
if value is not None and not value.strip():
|
|
455
|
+
raise ValueError("job_session_id must not be blank")
|
|
456
|
+
return value
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class ScheduleJobRequest(BaseModel):
|
|
460
|
+
job_id: str = Field(min_length=1)
|
|
461
|
+
session_id: str | None = None
|
|
462
|
+
scope: Literal["global"] | None = None
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
class PeerSendRequest(BaseModel):
|
|
466
|
+
to: str = Field(min_length=1)
|
|
467
|
+
text: str = Field(min_length=1)
|
|
468
|
+
session_id: str | None = None
|
|
469
|
+
|
|
470
|
+
@field_validator("to", "text")
|
|
471
|
+
@classmethod
|
|
472
|
+
def validate_required_text(cls, value: str) -> str:
|
|
473
|
+
if not value.strip():
|
|
474
|
+
raise ValueError("value must not be blank")
|
|
475
|
+
return value
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
class TeamCreateRequest(BaseModel):
|
|
479
|
+
name: str = Field(min_length=1)
|
|
480
|
+
max_teammates: int | None = Field(default=None, gt=0)
|
|
481
|
+
session_id: str | None = None
|
|
482
|
+
|
|
483
|
+
@field_validator("name")
|
|
484
|
+
@classmethod
|
|
485
|
+
def validate_name(cls, value: str) -> str:
|
|
486
|
+
if not value.strip():
|
|
487
|
+
raise ValueError("name must not be blank")
|
|
488
|
+
return value
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
class TeamSpawnRequest(BaseModel):
|
|
492
|
+
team_id: str = Field(min_length=1)
|
|
493
|
+
prompt: str = Field(min_length=1)
|
|
494
|
+
role: Literal["lead", "worker", "researcher", "reviewer"] = "worker"
|
|
495
|
+
name: str | None = None
|
|
496
|
+
model_profile: str | None = None
|
|
497
|
+
session_id: str | None = None
|
|
498
|
+
|
|
499
|
+
@field_validator("team_id", "prompt")
|
|
500
|
+
@classmethod
|
|
501
|
+
def validate_required_text(cls, value: str) -> str:
|
|
502
|
+
if not value.strip():
|
|
503
|
+
raise ValueError("value must not be blank")
|
|
504
|
+
return value
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
class TeamMessageRequest(BaseModel):
|
|
508
|
+
team_id: str = Field(min_length=1)
|
|
509
|
+
to: str = Field(min_length=1)
|
|
510
|
+
text: str = Field(min_length=1)
|
|
511
|
+
from_agent: str = ""
|
|
512
|
+
session_id: str | None = None
|
|
513
|
+
|
|
514
|
+
@field_validator("team_id", "to", "text")
|
|
515
|
+
@classmethod
|
|
516
|
+
def validate_required_text(cls, value: str) -> str:
|
|
517
|
+
if not value.strip():
|
|
518
|
+
raise ValueError("value must not be blank")
|
|
519
|
+
return value
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
class TeamBroadcastRequest(BaseModel):
|
|
523
|
+
team_id: str = Field(min_length=1)
|
|
524
|
+
text: str = Field(min_length=1)
|
|
525
|
+
from_agent: str = ""
|
|
526
|
+
session_id: str | None = None
|
|
527
|
+
|
|
528
|
+
@field_validator("team_id", "text")
|
|
529
|
+
@classmethod
|
|
530
|
+
def validate_required_text(cls, value: str) -> str:
|
|
531
|
+
if not value.strip():
|
|
532
|
+
raise ValueError("value must not be blank")
|
|
533
|
+
return value
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
class TeamTaskAddRequest(BaseModel):
|
|
537
|
+
team_id: str = Field(min_length=1)
|
|
538
|
+
description: str = Field(min_length=1)
|
|
539
|
+
session_id: str | None = None
|
|
540
|
+
|
|
541
|
+
@field_validator("team_id", "description")
|
|
542
|
+
@classmethod
|
|
543
|
+
def validate_required_text(cls, value: str) -> str:
|
|
544
|
+
if not value.strip():
|
|
545
|
+
raise ValueError("value must not be blank")
|
|
546
|
+
return value
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
class TeamTaskClaimRequest(BaseModel):
|
|
550
|
+
team_id: str = Field(min_length=1)
|
|
551
|
+
task_id: str = Field(min_length=1)
|
|
552
|
+
agent_id: str = ""
|
|
553
|
+
session_id: str | None = None
|
|
554
|
+
|
|
555
|
+
@field_validator("team_id", "task_id")
|
|
556
|
+
@classmethod
|
|
557
|
+
def validate_required_text(cls, value: str) -> str:
|
|
558
|
+
if not value.strip():
|
|
559
|
+
raise ValueError("value must not be blank")
|
|
560
|
+
return value
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
class TeamTaskCompleteRequest(BaseModel):
|
|
564
|
+
team_id: str = Field(min_length=1)
|
|
565
|
+
task_id: str = Field(min_length=1)
|
|
566
|
+
result: str = ""
|
|
567
|
+
agent_id: str = ""
|
|
568
|
+
session_id: str | None = None
|
|
569
|
+
|
|
570
|
+
@field_validator("team_id", "task_id")
|
|
571
|
+
@classmethod
|
|
572
|
+
def validate_required_text(cls, value: str) -> str:
|
|
573
|
+
if not value.strip():
|
|
574
|
+
raise ValueError("value must not be blank")
|
|
575
|
+
return value
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class TeamTaskFailRequest(BaseModel):
|
|
579
|
+
team_id: str = Field(min_length=1)
|
|
580
|
+
task_id: str = Field(min_length=1)
|
|
581
|
+
reason: str = ""
|
|
582
|
+
agent_id: str = ""
|
|
583
|
+
session_id: str | None = None
|
|
584
|
+
|
|
585
|
+
@field_validator("team_id", "task_id")
|
|
586
|
+
@classmethod
|
|
587
|
+
def validate_required_text(cls, value: str) -> str:
|
|
588
|
+
if not value.strip():
|
|
589
|
+
raise ValueError("value must not be blank")
|
|
590
|
+
return value
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
class TeamRemoveRequest(BaseModel):
|
|
594
|
+
team_id: str = Field(min_length=1)
|
|
595
|
+
agent_id: str = Field(min_length=1)
|
|
596
|
+
session_id: str | None = None
|
|
597
|
+
|
|
598
|
+
@field_validator("team_id", "agent_id")
|
|
599
|
+
@classmethod
|
|
600
|
+
def validate_required_text(cls, value: str) -> str:
|
|
601
|
+
if not value.strip():
|
|
602
|
+
raise ValueError("value must not be blank")
|
|
603
|
+
return value
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
class TeamMessagesReadRequest(BaseModel):
|
|
607
|
+
team_id: str = Field(min_length=1)
|
|
608
|
+
agent_id: str = Field(min_length=1)
|
|
609
|
+
message_ids: list[str] | None = None
|
|
610
|
+
session_id: str | None = None
|
|
611
|
+
|
|
612
|
+
@field_validator("team_id", "agent_id")
|
|
613
|
+
@classmethod
|
|
614
|
+
def validate_required_text(cls, value: str) -> str:
|
|
615
|
+
if not value.strip():
|
|
616
|
+
raise ValueError("value must not be blank")
|
|
617
|
+
return value
|
|
618
|
+
|
|
619
|
+
@field_validator("message_ids")
|
|
620
|
+
@classmethod
|
|
621
|
+
def validate_message_ids(cls, value: list[str] | None) -> list[str] | None:
|
|
622
|
+
if value is not None and any(not item.strip() for item in value):
|
|
623
|
+
raise ValueError("message_ids must not contain blank IDs")
|
|
624
|
+
return value
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
class TeamBridgeRequest(BaseModel):
|
|
628
|
+
team_a: str = Field(min_length=1)
|
|
629
|
+
team_b: str = Field(min_length=1)
|
|
630
|
+
policy: Literal["allow_all", "allow_tagged", "deny"] = "allow_tagged"
|
|
631
|
+
session_id: str | None = None
|
|
632
|
+
|
|
633
|
+
@field_validator("team_a", "team_b")
|
|
634
|
+
@classmethod
|
|
635
|
+
def validate_team_ids(cls, value: str) -> str:
|
|
636
|
+
if not value.strip():
|
|
637
|
+
raise ValueError("team ID must not be blank")
|
|
638
|
+
return value
|
|
639
|
+
|
|
640
|
+
@model_validator(mode="after")
|
|
641
|
+
def validate_distinct_teams(self) -> "TeamBridgeRequest":
|
|
642
|
+
if self.team_a == self.team_b:
|
|
643
|
+
raise ValueError("a bridge requires two distinct teams")
|
|
644
|
+
return self
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
class TeamCrossMessageRequest(BaseModel):
|
|
648
|
+
from_team: str = Field(min_length=1)
|
|
649
|
+
to_team: str = Field(min_length=1)
|
|
650
|
+
text: str = Field(min_length=1)
|
|
651
|
+
from_agent: str = ""
|
|
652
|
+
to_agent: str = ""
|
|
653
|
+
session_id: str | None = None
|
|
654
|
+
|
|
655
|
+
@field_validator("from_team", "to_team", "text")
|
|
656
|
+
@classmethod
|
|
657
|
+
def validate_required_text(cls, value: str) -> str:
|
|
658
|
+
if not value.strip():
|
|
659
|
+
raise ValueError("value must not be blank")
|
|
660
|
+
return value
|
|
661
|
+
|
|
662
|
+
@model_validator(mode="after")
|
|
663
|
+
def validate_distinct_teams(self) -> "TeamCrossMessageRequest":
|
|
664
|
+
if self.from_team == self.to_team:
|
|
665
|
+
raise ValueError("cross-team messages require two distinct teams")
|
|
666
|
+
return self
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
class TeamShutdownRequest(BaseModel):
|
|
670
|
+
team_id: str = Field(min_length=1)
|
|
671
|
+
session_id: str | None = None
|
|
672
|
+
|
|
673
|
+
@field_validator("team_id")
|
|
674
|
+
@classmethod
|
|
675
|
+
def validate_team_id(cls, value: str) -> str:
|
|
676
|
+
if not value.strip():
|
|
677
|
+
raise ValueError("team_id must not be blank")
|
|
678
|
+
return value
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
class ContextPushRequest(BaseModel):
|
|
682
|
+
"""Client pushes workspace context to the server.
|
|
683
|
+
|
|
684
|
+
Used by VSCode extension to inform the gateway about the current
|
|
685
|
+
editor state (active file, selection, cursor position, etc.).
|
|
686
|
+
"""
|
|
687
|
+
|
|
688
|
+
session_id: str
|
|
689
|
+
active_file: str | None = None
|
|
690
|
+
selected_text: str | None = None
|
|
691
|
+
cursor_line: int | None = None
|
|
692
|
+
cursor_column: int | None = None
|
|
693
|
+
open_files: list[str] = Field(default_factory=list)
|
|
694
|
+
language_id: str | None = None
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
class CheckpointRequest(BaseModel):
|
|
698
|
+
"""Create a checkpoint with file snapshot."""
|
|
699
|
+
session_id: str
|
|
700
|
+
label: str = ""
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
class RevertRequest(BaseModel):
|
|
704
|
+
"""Revert files + conversation to a checkpoint."""
|
|
705
|
+
session_id: str
|
|
706
|
+
checkpoint_id: str
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
class SearchSessionsRequest(BaseModel):
|
|
710
|
+
query: str
|
|
711
|
+
limit: int = 20
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
class ArchiveSessionRequest(BaseModel):
|
|
715
|
+
session_id: str
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
class PruneSessionsRequest(BaseModel):
|
|
719
|
+
days: int = Field(default=30, ge=0, le=36500)
|
|
720
|
+
delete_files: bool = False
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
class ExportSessionRequest(BaseModel):
|
|
724
|
+
session_id: str
|
|
725
|
+
format: Literal["md", "json"] = "md"
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
class WorkspaceDirectoryCreateRequest(BaseModel):
|
|
729
|
+
path: str = Field(min_length=1)
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
# ── Response / event schemas ─────────────────────────────────────
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
class SessionInfo(BaseModel):
|
|
736
|
+
session_id: str
|
|
737
|
+
message_count: int = 0
|
|
738
|
+
model: str = ""
|
|
739
|
+
provider: str = ""
|
|
740
|
+
created_at: str = ""
|
|
741
|
+
title: str = ""
|
|
742
|
+
cwd: str = ""
|
|
743
|
+
tokens_used: int = 0
|
|
744
|
+
preview: str = ""
|
|
745
|
+
forked_from_session_id: str | None = None
|
|
746
|
+
forked_from_message_uuid: str | None = None
|
|
747
|
+
forked_from_title: str | None = None
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
class GatewayRuntimeInfo(BaseModel):
|
|
751
|
+
gateway_version: str
|
|
752
|
+
gateway_path: str
|
|
753
|
+
python_version: str
|
|
754
|
+
python_executable: str
|
|
755
|
+
python_prefix: str
|
|
756
|
+
environment_kind: Literal["venv", "conda", "system"]
|
|
757
|
+
platform: str
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
class WorkspaceInfo(BaseModel):
|
|
761
|
+
startup_cwd: str
|
|
762
|
+
runtime: GatewayRuntimeInfo | None = None
|
|
763
|
+
home: str
|
|
764
|
+
browse_roots: list[str] = Field(default_factory=list)
|
|
765
|
+
documents_dir: str = ""
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
class WorkspaceDirectoryEntry(BaseModel):
|
|
769
|
+
name: str
|
|
770
|
+
path: str
|
|
771
|
+
hidden: bool = False
|
|
772
|
+
is_symlink: bool = False
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
class WorkspaceFileEntry(BaseModel):
|
|
776
|
+
name: str
|
|
777
|
+
path: str
|
|
778
|
+
size: int = 0
|
|
779
|
+
hidden: bool = False
|
|
780
|
+
is_symlink: bool = False
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
class WorkspaceDirectoryListing(BaseModel):
|
|
784
|
+
path: str
|
|
785
|
+
parent: str | None = None
|
|
786
|
+
directories: list[WorkspaceDirectoryEntry] = Field(default_factory=list)
|
|
787
|
+
files: list[WorkspaceFileEntry] = Field(default_factory=list)
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
class SearchIndexStatus(BaseModel):
|
|
791
|
+
"""Observable state for the optional semantic-search indexer."""
|
|
792
|
+
|
|
793
|
+
state: str
|
|
794
|
+
chunks: int | None = None
|
|
795
|
+
files: int | None = None
|
|
796
|
+
done: int | None = None
|
|
797
|
+
total: int | None = None
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
class PromptBudget(BaseModel):
|
|
801
|
+
source: Literal["estimated"] = "estimated"
|
|
802
|
+
mode: Literal["eager", "discovery"]
|
|
803
|
+
system_tokens: int
|
|
804
|
+
tool_tokens: int
|
|
805
|
+
directory_tokens: int
|
|
806
|
+
loaded_tools: int
|
|
807
|
+
available_tools: int
|
|
808
|
+
loaded_names: list[str] = Field(default_factory=list)
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
class PermissionPolicyInfo(BaseModel):
|
|
812
|
+
"""Loaded permission policy, independent of the client's inheritance choice."""
|
|
813
|
+
|
|
814
|
+
configured_mode: str
|
|
815
|
+
effective_mode: str
|
|
816
|
+
allow: list[PermissionRule] = Field(default_factory=list)
|
|
817
|
+
ask: list[PermissionRule] = Field(default_factory=list)
|
|
818
|
+
deny: list[PermissionRule] = Field(default_factory=list)
|
|
819
|
+
runtime_allow_count: int = 0
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
class SessionRuntimeStatus(BaseModel):
|
|
823
|
+
"""Complete, non-secret runtime status shared by CLI-style clients."""
|
|
824
|
+
|
|
825
|
+
version: str = VERSION
|
|
826
|
+
session_id: str
|
|
827
|
+
cwd: str = ""
|
|
828
|
+
initialized: bool = False
|
|
829
|
+
message_count: int = 0
|
|
830
|
+
model: str = ""
|
|
831
|
+
model_profile: str | None = None
|
|
832
|
+
provider: str = ""
|
|
833
|
+
mode: Literal["agent", "plan"] = "agent"
|
|
834
|
+
reasoning_effort: ReasoningEffort | None = None
|
|
835
|
+
ultra_mode: bool = False
|
|
836
|
+
permission_mode: str = "default"
|
|
837
|
+
permission_policy: PermissionPolicyInfo | None = None
|
|
838
|
+
context_used_tokens: int = 0
|
|
839
|
+
context_window_tokens: int = 0
|
|
840
|
+
context_remaining_tokens: int = 0
|
|
841
|
+
context_used_percent: float = 0.0
|
|
842
|
+
context_token_source: Literal["server", "calibrated", "estimated"] = "estimated"
|
|
843
|
+
compact_count: int = 0
|
|
844
|
+
auto_compact_enabled: bool = True
|
|
845
|
+
compact_buffer_tokens: int = DEFAULT_COMPACT_BUFFER_TOKENS
|
|
846
|
+
max_context_length: int | None = None
|
|
847
|
+
compact_input_limit: int | None = None
|
|
848
|
+
thinking_enabled: bool = False
|
|
849
|
+
max_tokens: int = 0
|
|
850
|
+
tool_count: int | None = None
|
|
851
|
+
prompt_budget: PromptBudget | None = None
|
|
852
|
+
agent_total: int = 0
|
|
853
|
+
agent_active: int = 0
|
|
854
|
+
agent_failed: int = 0
|
|
855
|
+
agent_pending_callbacks: int = 0
|
|
856
|
+
agent_max_concurrency: int = 0
|
|
857
|
+
monitor_total: int = 0
|
|
858
|
+
monitor_active: int = 0
|
|
859
|
+
monitor_failed: int = 0
|
|
860
|
+
search_index: SearchIndexStatus | None = None
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
class GoalInfo(BaseModel):
|
|
864
|
+
objective: str
|
|
865
|
+
status: Literal["active", "paused", "complete", "blocked"]
|
|
866
|
+
token_budget: int | None = None
|
|
867
|
+
tokens_used: int = 0
|
|
868
|
+
created_at: str
|
|
869
|
+
updated_at: str
|
|
870
|
+
completed_at: str | None = None
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
class GoalState(BaseModel):
|
|
874
|
+
goal: GoalInfo | None = None
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
class AgentInfo(BaseModel):
|
|
878
|
+
agent_id: str
|
|
879
|
+
session_id: str = ""
|
|
880
|
+
parent_agent_id: str | None = None
|
|
881
|
+
parent_tool_use_id: str | None = None
|
|
882
|
+
title: str
|
|
883
|
+
subagent_type: str
|
|
884
|
+
status: str
|
|
885
|
+
model: str
|
|
886
|
+
model_profile: str | None = None
|
|
887
|
+
created_at: str
|
|
888
|
+
started_at: str | None = None
|
|
889
|
+
finished_at: str | None = None
|
|
890
|
+
updated_at: str = ""
|
|
891
|
+
usage: dict[str, Any] = Field(default_factory=dict)
|
|
892
|
+
final_result: str = ""
|
|
893
|
+
error: str = ""
|
|
894
|
+
depth: int = 0
|
|
895
|
+
transcript_path: str | None = None
|
|
896
|
+
callback_enabled: bool = False
|
|
897
|
+
callback_state: str = "disabled"
|
|
898
|
+
callback_message_id: str | None = None
|
|
899
|
+
callback_epoch: int = 0
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
class ToolInfo(BaseModel):
|
|
903
|
+
name: str
|
|
904
|
+
description: str = ""
|
|
905
|
+
is_read_only: bool = False
|
|
906
|
+
is_enabled: bool = True
|
|
907
|
+
is_loaded: bool | None = None
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
class SkillInfo(BaseModel):
|
|
911
|
+
name: str
|
|
912
|
+
description: str = ""
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
class SkillExpansion(BaseModel):
|
|
916
|
+
name: str
|
|
917
|
+
prompt: str
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
class MonitorInfo(BaseModel):
|
|
921
|
+
task_id: str
|
|
922
|
+
session_id: str = ""
|
|
923
|
+
description: str = ""
|
|
924
|
+
task_type: str = "local_bash"
|
|
925
|
+
source: str = ""
|
|
926
|
+
status: str = ""
|
|
927
|
+
output_file: str | None = None
|
|
928
|
+
timeout_ms: int = 0
|
|
929
|
+
persistent: bool = False
|
|
930
|
+
tool_use_id: str | None = None
|
|
931
|
+
created_at: str = ""
|
|
932
|
+
started_at: str | None = None
|
|
933
|
+
finished_at: str | None = None
|
|
934
|
+
updated_at: str = ""
|
|
935
|
+
sequence: int = 0
|
|
936
|
+
error: str = ""
|
|
937
|
+
exit_code: int | None = None
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
class BackgroundTaskInfo(BaseModel):
|
|
941
|
+
task_id: str
|
|
942
|
+
session_id: str = ""
|
|
943
|
+
cwd: str = ""
|
|
944
|
+
description: str = ""
|
|
945
|
+
task_type: str = ""
|
|
946
|
+
source: str = ""
|
|
947
|
+
status: str = ""
|
|
948
|
+
output_file: str | None = None
|
|
949
|
+
created_at: str = ""
|
|
950
|
+
started_at: str | None = None
|
|
951
|
+
finished_at: str | None = None
|
|
952
|
+
updated_at: str = ""
|
|
953
|
+
error: str = ""
|
|
954
|
+
exit_code: int | None = None
|
|
955
|
+
agent_id: str | None = None
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
class ScheduleJobInfo(BaseModel):
|
|
959
|
+
id: str
|
|
960
|
+
name: str
|
|
961
|
+
prompt: str
|
|
962
|
+
schedule: str
|
|
963
|
+
schedule_type: Literal["cron", "interval", "once"]
|
|
964
|
+
cwd: str | None = None
|
|
965
|
+
enabled: bool = True
|
|
966
|
+
status: str
|
|
967
|
+
last_run: str | None = None
|
|
968
|
+
next_run: str | None = None
|
|
969
|
+
run_count: int = 0
|
|
970
|
+
max_runs: int | None = None
|
|
971
|
+
created_at: str
|
|
972
|
+
session_id: str | None = None
|
|
973
|
+
description: str = ""
|
|
974
|
+
tags: list[str] = Field(default_factory=list)
|
|
975
|
+
timeout: int | None = None
|
|
976
|
+
model_profile: str | None = None
|
|
977
|
+
extra: dict[str, Any] = Field(default_factory=dict)
|
|
978
|
+
running: bool = False
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
class ScheduleRunInfo(BaseModel):
|
|
982
|
+
id: str
|
|
983
|
+
job_id: str
|
|
984
|
+
status: str
|
|
985
|
+
started_at: str | None = None
|
|
986
|
+
finished_at: str | None = None
|
|
987
|
+
duration_seconds: float | None = None
|
|
988
|
+
session_id: str | None = None
|
|
989
|
+
exit_code: int | None = None
|
|
990
|
+
error_message: str | None = None
|
|
991
|
+
result_summary: str = ""
|
|
992
|
+
tokens_used: int = 0
|
|
993
|
+
created_at: str
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
class PeerInfo(BaseModel):
|
|
997
|
+
version: int = 1
|
|
998
|
+
session_id: str
|
|
999
|
+
name: str
|
|
1000
|
+
cwd: str
|
|
1001
|
+
pid: int
|
|
1002
|
+
socket_path: str = ""
|
|
1003
|
+
permission_class: Literal["prompting", "bypass"] = "prompting"
|
|
1004
|
+
started_at: str = ""
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
class PeerDeliveryInfo(BaseModel):
|
|
1008
|
+
message_id: str = ""
|
|
1009
|
+
status: Literal["delivered", "held", "refused", "failed"]
|
|
1010
|
+
detail: str = ""
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
class TeamTeammateInfo(BaseModel):
|
|
1014
|
+
agent_id: str
|
|
1015
|
+
name: str | None = None
|
|
1016
|
+
role: str
|
|
1017
|
+
state: str
|
|
1018
|
+
model_profile: str | None = None
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
class TeamTaskInfo(BaseModel):
|
|
1022
|
+
id: str
|
|
1023
|
+
description: str = ""
|
|
1024
|
+
assignee: str | None = None
|
|
1025
|
+
status: str
|
|
1026
|
+
result: str = ""
|
|
1027
|
+
created_at: str = ""
|
|
1028
|
+
updated_at: str = ""
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
class TeamTaskSummary(BaseModel):
|
|
1032
|
+
total: int = 0
|
|
1033
|
+
pending: int = 0
|
|
1034
|
+
claimed: int = 0
|
|
1035
|
+
completed: int = 0
|
|
1036
|
+
failed: int = 0
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
class TeamStatusInfo(BaseModel):
|
|
1040
|
+
team_id: str
|
|
1041
|
+
state: str
|
|
1042
|
+
teammates: list[TeamTeammateInfo] = Field(default_factory=list)
|
|
1043
|
+
teammate_count: int = 0
|
|
1044
|
+
max_teammates: int = 0
|
|
1045
|
+
tasks: TeamTaskSummary = Field(default_factory=TeamTaskSummary)
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
class TeamMessageInfo(BaseModel):
|
|
1049
|
+
id: str
|
|
1050
|
+
from_agent: str = ""
|
|
1051
|
+
to_agent: str = ""
|
|
1052
|
+
text: str = ""
|
|
1053
|
+
timestamp: str = ""
|
|
1054
|
+
read: bool = False
|
|
1055
|
+
msg_type: str = "text"
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
class TeamBridgeInfo(BaseModel):
|
|
1059
|
+
team_a: str
|
|
1060
|
+
team_b: str
|
|
1061
|
+
policy: Literal["allow_all", "allow_tagged", "deny"]
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
class CrossTeamMessageInfo(BaseModel):
|
|
1065
|
+
id: str
|
|
1066
|
+
from_team: str
|
|
1067
|
+
from_agent: str = ""
|
|
1068
|
+
to_team: str
|
|
1069
|
+
to_agent: str = ""
|
|
1070
|
+
text: str
|
|
1071
|
+
timestamp: str = ""
|
|
1072
|
+
bridge_policy: Literal["allow_all", "allow_tagged", "deny"]
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
class LogInfo(BaseModel):
|
|
1076
|
+
name: str
|
|
1077
|
+
path: str
|
|
1078
|
+
updated_at: str | None = None
|
|
1079
|
+
state: str | None = None
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
class LogsResponse(BaseModel):
|
|
1083
|
+
logs: list[LogInfo] = Field(default_factory=list)
|
|
1084
|
+
lines: list[str] = Field(default_factory=list)
|
|
1085
|
+
name: str | None = None
|
|
1086
|
+
path: str | None = None
|
|
1087
|
+
truncated: bool = False
|
|
1088
|
+
note: str | None = None
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
class AgentTranscriptResponse(BaseModel):
|
|
1092
|
+
agent_id: str
|
|
1093
|
+
path: str | None = None
|
|
1094
|
+
lines: list[str] = Field(default_factory=list)
|
|
1095
|
+
truncated: bool = False
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
class ModelInfo(BaseModel):
|
|
1099
|
+
name: str
|
|
1100
|
+
description: str = ""
|
|
1101
|
+
group: str = "default"
|
|
1102
|
+
|
|
1103
|
+
|
|
1104
|
+
class ModelSettingsEntry(BaseModel):
|
|
1105
|
+
"""One named model as configured and after group inheritance."""
|
|
1106
|
+
|
|
1107
|
+
name: str
|
|
1108
|
+
group: str | None = None
|
|
1109
|
+
is_default: bool = False
|
|
1110
|
+
configured: dict[str, Any] = Field(default_factory=dict)
|
|
1111
|
+
effective: dict[str, Any] = Field(default_factory=dict)
|
|
1112
|
+
overridden_fields: list[str] = Field(default_factory=list)
|
|
1113
|
+
sources: list[str] = Field(default_factory=list)
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
class ModelSettingsSource(BaseModel):
|
|
1117
|
+
"""A settings layer that can be selected as a mutation target."""
|
|
1118
|
+
|
|
1119
|
+
id: Literal["userSettings", "projectSettings", "localSettings"]
|
|
1120
|
+
label: str
|
|
1121
|
+
path: str
|
|
1122
|
+
exists: bool = False
|
|
1123
|
+
writable: bool = True
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
class ModelSettingsResponse(BaseModel):
|
|
1127
|
+
"""Model configuration visible from one working directory."""
|
|
1128
|
+
|
|
1129
|
+
cwd: str
|
|
1130
|
+
default_model: str | None = None
|
|
1131
|
+
sources: list[str] = Field(default_factory=list)
|
|
1132
|
+
groups: dict[str, dict[str, Any]] = Field(default_factory=dict)
|
|
1133
|
+
group_sources: dict[str, list[str]] = Field(default_factory=dict)
|
|
1134
|
+
models: list[ModelSettingsEntry] = Field(default_factory=list)
|
|
1135
|
+
warnings: list[str] = Field(default_factory=list)
|
|
1136
|
+
editable_sources: list[ModelSettingsSource] = Field(default_factory=list)
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
class RuntimeSettingsResponse(BaseModel):
|
|
1140
|
+
"""Effective runtime and extra-tool settings visible from a workspace."""
|
|
1141
|
+
|
|
1142
|
+
cwd: str
|
|
1143
|
+
permission_policy: PermissionPolicyInfo | None = None
|
|
1144
|
+
snapshot_enabled: bool = True
|
|
1145
|
+
snapshot_max_size_mb: int = 1024
|
|
1146
|
+
auto_compact_enabled: bool = True
|
|
1147
|
+
compact_buffer_tokens: int = DEFAULT_COMPACT_BUFFER_TOKENS
|
|
1148
|
+
max_context_length: int | None = None
|
|
1149
|
+
computer_use_mode: Literal["background_app", "foreground_desktop"] = "background_app"
|
|
1150
|
+
computer_use_target_scope: Literal["app_window", "desktop"] = "app_window"
|
|
1151
|
+
computer_use_delivery_policy: Literal["strict_background", "allow_foreground"] = "allow_foreground"
|
|
1152
|
+
extra_tools: list[str] = Field(default_factory=list)
|
|
1153
|
+
extra_tools_by_source: dict[str, list[str]] = Field(default_factory=dict)
|
|
1154
|
+
sources: list[str] = Field(default_factory=list)
|
|
1155
|
+
warnings: list[str] = Field(default_factory=list)
|
|
1156
|
+
editable_sources: list[ModelSettingsSource] = Field(default_factory=list)
|
|
1157
|
+
|
|
1158
|
+
|
|
1159
|
+
class PromptSectionInfo(BaseModel):
|
|
1160
|
+
key: str
|
|
1161
|
+
label: str
|
|
1162
|
+
description: str | None = None
|
|
1163
|
+
default_text: str | None = None
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
class PromptTemplateView(BaseModel):
|
|
1167
|
+
id: str
|
|
1168
|
+
name: str
|
|
1169
|
+
sections: dict[str, str] = Field(default_factory=dict)
|
|
1170
|
+
source: str
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
class UserAppendPromptView(BaseModel):
|
|
1174
|
+
id: str
|
|
1175
|
+
text: str
|
|
1176
|
+
enabled: bool = False
|
|
1177
|
+
source: str
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
class PromptSettingsResponse(BaseModel):
|
|
1181
|
+
"""Prompt templates and user-input prompts visible from one workspace."""
|
|
1182
|
+
|
|
1183
|
+
cwd: str
|
|
1184
|
+
active_template_id: str | None = None
|
|
1185
|
+
templates: list[PromptTemplateView] = Field(default_factory=list)
|
|
1186
|
+
user_prompts: list[UserAppendPromptView] = Field(default_factory=list)
|
|
1187
|
+
sections: list[PromptSectionInfo] = Field(default_factory=list)
|
|
1188
|
+
warnings: list[str] = Field(default_factory=list)
|
|
1189
|
+
editable_sources: list[ModelSettingsSource] = Field(default_factory=list)
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
class HealthResponse(BaseModel):
|
|
1193
|
+
status: str = "ok"
|
|
1194
|
+
version: str = VERSION
|
|
1195
|
+
protocol_version: int = GATEWAY_PROTOCOL_VERSION
|
|
1196
|
+
min_protocol_version: int = GATEWAY_MIN_PROTOCOL_VERSION
|
|
1197
|
+
max_protocol_version: int = GATEWAY_MAX_PROTOCOL_VERSION
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
# ── CoreEvent serialization ──────────────────────────────────────
|
|
1201
|
+
# These models represent CoreEvent variants on the wire. They carry
|
|
1202
|
+
# a ``type`` discriminator so the client can dispatch correctly.
|
|
1203
|
+
|
|
1204
|
+
|
|
1205
|
+
class StreamTextPayload(BaseModel):
|
|
1206
|
+
type: Literal["stream_text"] = "stream_text"
|
|
1207
|
+
text: str
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
class ThinkingPayload(BaseModel):
|
|
1211
|
+
type: Literal["thinking"] = "thinking"
|
|
1212
|
+
text: str
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
class ToolUsePayload(BaseModel):
|
|
1216
|
+
type: Literal["tool_use"] = "tool_use"
|
|
1217
|
+
tool_name: str
|
|
1218
|
+
tool_input: dict[str, Any]
|
|
1219
|
+
tool_use_id: str
|
|
1220
|
+
agent_id: str | None = None
|
|
1221
|
+
|
|
1222
|
+
|
|
1223
|
+
class ToolResultPayload(BaseModel):
|
|
1224
|
+
type: Literal["tool_result"] = "tool_result"
|
|
1225
|
+
tool_use_id: str
|
|
1226
|
+
tool_name: str
|
|
1227
|
+
result: str
|
|
1228
|
+
is_error: bool = False
|
|
1229
|
+
result_for_display: str | None = None
|
|
1230
|
+
tool_input: dict[str, Any] = Field(default_factory=dict)
|
|
1231
|
+
agent_id: str | None = None
|
|
1232
|
+
images: list[ImageAttachment] = Field(default_factory=list)
|
|
1233
|
+
|
|
1234
|
+
|
|
1235
|
+
class PermissionRequestPayload(BaseModel):
|
|
1236
|
+
type: Literal["permission_request"] = "permission_request"
|
|
1237
|
+
tool_name: str
|
|
1238
|
+
tool_input: dict[str, Any]
|
|
1239
|
+
tool_use_id: str
|
|
1240
|
+
reason: str | None = None
|
|
1241
|
+
permission_key: str | None = None
|
|
1242
|
+
agent_id: str | None = None
|
|
1243
|
+
request_kind: Literal["tool", "peer_message"] = "tool"
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
class PermissionResponsePayload(BaseModel):
|
|
1247
|
+
type: Literal["permission_response"] = "permission_response"
|
|
1248
|
+
tool_use_id: str
|
|
1249
|
+
allowed: bool
|
|
1250
|
+
always_allow: bool = False
|
|
1251
|
+
agent_id: str | None = None
|
|
1252
|
+
feedback: str | None = None
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
class ChoiceRequestPayload(BaseModel):
|
|
1256
|
+
type: Literal["choice_request"] = "choice_request"
|
|
1257
|
+
tool_use_id: str
|
|
1258
|
+
question: str
|
|
1259
|
+
options: list[str]
|
|
1260
|
+
multiple: bool = False
|
|
1261
|
+
agent_id: str | None = None
|
|
1262
|
+
|
|
1263
|
+
|
|
1264
|
+
class ChoiceResponsePayload(BaseModel):
|
|
1265
|
+
type: Literal["choice_response"] = "choice_response"
|
|
1266
|
+
tool_use_id: str
|
|
1267
|
+
selected: list[str]
|
|
1268
|
+
cancelled: bool = False
|
|
1269
|
+
agent_id: str | None = None
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
class CompactPayload(BaseModel):
|
|
1273
|
+
type: Literal["compact"] = "compact"
|
|
1274
|
+
summary: str
|
|
1275
|
+
messages_before: int = 0
|
|
1276
|
+
messages_after: int = 0
|
|
1277
|
+
trigger: str = "auto"
|
|
1278
|
+
agent_id: str | None = None
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
class ErrorPayload(BaseModel):
|
|
1282
|
+
type: Literal["error"] = "error"
|
|
1283
|
+
message: str
|
|
1284
|
+
recoverable: bool = True
|
|
1285
|
+
error_type: str = ""
|
|
1286
|
+
agent_id: str | None = None
|
|
1287
|
+
# Errors generated while admitting/validating a transport command are
|
|
1288
|
+
# deliberately distinct from Core turn errors. They may carry the
|
|
1289
|
+
# command and target operation without claiming foreground terminal
|
|
1290
|
+
# ownership.
|
|
1291
|
+
command: str | None = None
|
|
1292
|
+
command_error: bool = False
|
|
1293
|
+
request_id: str | None = None
|
|
1294
|
+
|
|
1295
|
+
|
|
1296
|
+
class TurnDetails(BaseModel):
|
|
1297
|
+
"""Statistics for one input, stored on its final assistant message."""
|
|
1298
|
+
|
|
1299
|
+
session_id: str
|
|
1300
|
+
started_at: str | None = None
|
|
1301
|
+
ended_at: str | None = None
|
|
1302
|
+
duration_ms: int | None = None
|
|
1303
|
+
tool_call_count: int = 0
|
|
1304
|
+
thinking_count: int = 0
|
|
1305
|
+
request_count: int = 0
|
|
1306
|
+
retry_count: int | None = None
|
|
1307
|
+
compact_count: int | None = None
|
|
1308
|
+
model: str = ""
|
|
1309
|
+
provider: str = ""
|
|
1310
|
+
reason: str | None = None
|
|
1311
|
+
usage: dict[str, Any] = Field(default_factory=dict)
|
|
1312
|
+
source: Literal["recorded", "history"] = "recorded"
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
class TurnCompletePayload(BaseModel):
|
|
1316
|
+
type: Literal["turn_complete"] = "turn_complete"
|
|
1317
|
+
reason: str = "end_turn"
|
|
1318
|
+
turn_count: int = 0
|
|
1319
|
+
usage: dict[str, Any] = Field(default_factory=dict)
|
|
1320
|
+
context_used_tokens: int = 0
|
|
1321
|
+
context_window_tokens: int = 0
|
|
1322
|
+
context_remaining_tokens: int = 0
|
|
1323
|
+
context_used_percent: float = 0.0
|
|
1324
|
+
context_token_source: Literal["server", "calibrated", "estimated"] = "estimated"
|
|
1325
|
+
assistant_message_uuid: str | None = None
|
|
1326
|
+
turn_details: TurnDetails | None = None
|
|
1327
|
+
prompt_budget: PromptBudget | None = None
|
|
1328
|
+
|
|
1329
|
+
|
|
1330
|
+
class StreamModePayload(BaseModel):
|
|
1331
|
+
type: Literal["stream_mode"] = "stream_mode"
|
|
1332
|
+
mode: str
|
|
1333
|
+
agent_id: str | None = None
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
class StreamRetryPayload(BaseModel):
|
|
1337
|
+
type: Literal["stream_retry"] = "stream_retry"
|
|
1338
|
+
message: str
|
|
1339
|
+
error: str
|
|
1340
|
+
retry_count: int
|
|
1341
|
+
max_retries: int
|
|
1342
|
+
delay_seconds: float
|
|
1343
|
+
unbounded: bool = False
|
|
1344
|
+
transport_fallback: bool = False
|
|
1345
|
+
discarded_text_chars: int = 0
|
|
1346
|
+
agent_id: str | None = None
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
class SteeringAppliedPayload(BaseModel):
|
|
1350
|
+
type: Literal["steering_applied"] = "steering_applied"
|
|
1351
|
+
count: int = 1
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
class QueuedMessageStartedPayload(BaseModel):
|
|
1355
|
+
type: Literal["queued_message_started"] = "queued_message_started"
|
|
1356
|
+
text: str
|
|
1357
|
+
images: list[ImageAttachment] = Field(default_factory=list)
|
|
1358
|
+
request_id: str | None = None
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
class QueuedMessageUpdatedPayload(BaseModel):
|
|
1362
|
+
type: Literal["queued_message_updated"] = "queued_message_updated"
|
|
1363
|
+
request_id: str
|
|
1364
|
+
action: Literal["remove", "edit", "steer"]
|
|
1365
|
+
|
|
1366
|
+
|
|
1367
|
+
class DocumentJobPayload(BaseModel):
|
|
1368
|
+
type: Literal["document_job"] = "document_job"
|
|
1369
|
+
action: str
|
|
1370
|
+
status: str
|
|
1371
|
+
locale: str = ""
|
|
1372
|
+
language: str = ""
|
|
1373
|
+
source: str = ""
|
|
1374
|
+
current: int = 0
|
|
1375
|
+
total: int = 0
|
|
1376
|
+
message: str = ""
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
class DocumentSelectionTranslationPayload(BaseModel):
|
|
1380
|
+
type: Literal["document_selection_translation"] = "document_selection_translation"
|
|
1381
|
+
operation_id: str
|
|
1382
|
+
locale: str
|
|
1383
|
+
source_text: str
|
|
1384
|
+
translated_text: str
|
|
1385
|
+
status: str = "completed"
|
|
1386
|
+
message: str = ""
|
|
1387
|
+
|
|
1388
|
+
|
|
1389
|
+
class AgentStatePayload(BaseModel):
|
|
1390
|
+
type: Literal["agent_state"] = "agent_state"
|
|
1391
|
+
agent_id: str
|
|
1392
|
+
parent_agent_id: str | None = None
|
|
1393
|
+
status: str
|
|
1394
|
+
subagent_type: str
|
|
1395
|
+
title: str
|
|
1396
|
+
message: str = ""
|
|
1397
|
+
usage: dict[str, Any] = Field(default_factory=dict)
|
|
1398
|
+
|
|
1399
|
+
|
|
1400
|
+
class AgentOutputPayload(BaseModel):
|
|
1401
|
+
type: Literal["agent_output"] = "agent_output"
|
|
1402
|
+
agent_id: str
|
|
1403
|
+
stream: str
|
|
1404
|
+
text: str
|
|
1405
|
+
tool_name: str | None = None
|
|
1406
|
+
|
|
1407
|
+
|
|
1408
|
+
class ModeChangePayload(BaseModel):
|
|
1409
|
+
type: Literal["mode_change"] = "mode_change"
|
|
1410
|
+
mode: str
|
|
1411
|
+
reason: str = ""
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
class ModelChangePayload(BaseModel):
|
|
1415
|
+
type: Literal["model_change"] = "model_change"
|
|
1416
|
+
session_id: str
|
|
1417
|
+
model_profile: str
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
class PermissionModeChangePayload(BaseModel):
|
|
1421
|
+
type: Literal["permission_mode_change"] = "permission_mode_change"
|
|
1422
|
+
session_id: str
|
|
1423
|
+
permission_mode: Literal["default", "ask", "ai_review", "run_everything"]
|
|
1424
|
+
|
|
1425
|
+
|
|
1426
|
+
class PlanReadyPayload(BaseModel):
|
|
1427
|
+
type: Literal["plan_ready"] = "plan_ready"
|
|
1428
|
+
plan: dict[str, Any]
|
|
1429
|
+
|
|
1430
|
+
|
|
1431
|
+
class TeamMessagePayload(BaseModel):
|
|
1432
|
+
type: Literal["team_message"] = "team_message"
|
|
1433
|
+
team_id: str
|
|
1434
|
+
from_agent: str
|
|
1435
|
+
to_agent: str
|
|
1436
|
+
text: str
|
|
1437
|
+
msg_type: str = "text"
|
|
1438
|
+
message_id: str = ""
|
|
1439
|
+
|
|
1440
|
+
|
|
1441
|
+
class PeerMessagePayload(BaseModel):
|
|
1442
|
+
type: Literal["peer_message"] = "peer_message"
|
|
1443
|
+
message_id: str
|
|
1444
|
+
from_session_id: str
|
|
1445
|
+
from_name: str
|
|
1446
|
+
from_cwd: str
|
|
1447
|
+
text: str
|
|
1448
|
+
|
|
1449
|
+
|
|
1450
|
+
class TeamStatePayload(BaseModel):
|
|
1451
|
+
type: Literal["team_state"] = "team_state"
|
|
1452
|
+
team_id: str
|
|
1453
|
+
agent_id: str
|
|
1454
|
+
old_state: str
|
|
1455
|
+
new_state: str
|
|
1456
|
+
role: str = ""
|
|
1457
|
+
|
|
1458
|
+
|
|
1459
|
+
class TaskUpdatePayload(BaseModel):
|
|
1460
|
+
type: Literal["task_update"] = "task_update"
|
|
1461
|
+
team_id: str
|
|
1462
|
+
task_id: str
|
|
1463
|
+
status: str
|
|
1464
|
+
assignee: str | None = None
|
|
1465
|
+
description: str = ""
|
|
1466
|
+
|
|
1467
|
+
|
|
1468
|
+
class ScheduleRunPayload(BaseModel):
|
|
1469
|
+
"""A scheduled job execution result delivered to connected clients."""
|
|
1470
|
+
|
|
1471
|
+
type: Literal["schedule_run"] = "schedule_run"
|
|
1472
|
+
job_id: str
|
|
1473
|
+
run_id: str
|
|
1474
|
+
status: str
|
|
1475
|
+
duration_seconds: float = 0.0
|
|
1476
|
+
error_message: str = ""
|
|
1477
|
+
result_summary: str = ""
|
|
1478
|
+
next_run: str | None = None
|
|
1479
|
+
|
|
1480
|
+
|
|
1481
|
+
class FileChangePayload(BaseModel):
|
|
1482
|
+
"""File change notification for frontends (VSCode, etc.).
|
|
1483
|
+
|
|
1484
|
+
Emitted when a tool modifies the filesystem so the client can
|
|
1485
|
+
refresh its editor view, show diffs, etc.
|
|
1486
|
+
"""
|
|
1487
|
+
|
|
1488
|
+
type: Literal["file_change"] = "file_change"
|
|
1489
|
+
path: str
|
|
1490
|
+
action: Literal["create", "modify", "delete"]
|
|
1491
|
+
diff: str | None = None
|
|
1492
|
+
|
|
1493
|
+
|
|
1494
|
+
class SnapshotPayload(BaseModel):
|
|
1495
|
+
type: Literal["snapshot"] = "snapshot"
|
|
1496
|
+
snapshot_id: str
|
|
1497
|
+
tool_name: str
|
|
1498
|
+
files: list[str] = Field(default_factory=list)
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
class RevertPayload(BaseModel):
|
|
1502
|
+
type: Literal["revert"] = "revert"
|
|
1503
|
+
snapshot_id: str
|
|
1504
|
+
files_restored: list[str] = Field(default_factory=list)
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
class ServerConnectedPayload(BaseModel):
|
|
1508
|
+
type: Literal["server.connected"] = "server.connected"
|
|
1509
|
+
properties: dict[str, Any] = Field(default_factory=dict)
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
class ServerHeartbeatPayload(BaseModel):
|
|
1513
|
+
type: Literal["server.heartbeat"] = "server.heartbeat"
|
|
1514
|
+
properties: dict[str, Any] = Field(default_factory=dict)
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
class SessionMessagePayload(BaseModel):
|
|
1518
|
+
"""Complete persisted message shape used when replaying a session."""
|
|
1519
|
+
|
|
1520
|
+
uuid: str
|
|
1521
|
+
role: Literal["user", "assistant", "system"]
|
|
1522
|
+
content: str | list[dict[str, Any]]
|
|
1523
|
+
timestamp: str
|
|
1524
|
+
parent_uuid: str | None = None
|
|
1525
|
+
is_compact_summary: bool = False
|
|
1526
|
+
origin: str | None = None
|
|
1527
|
+
usage: dict[str, Any] | None = None
|
|
1528
|
+
tool_use_result: str | None = None
|
|
1529
|
+
source_tool_assistant_uuid: str | None = None
|
|
1530
|
+
reply_to_uuid: str | None = None
|
|
1531
|
+
api_error: str | None = None
|
|
1532
|
+
request_id: str | None = None
|
|
1533
|
+
turn_details: TurnDetails | None = None
|
|
1534
|
+
|
|
1535
|
+
|
|
1536
|
+
class SessionHistoryPayload(BaseModel):
|
|
1537
|
+
type: Literal["session_history"] = "session_history"
|
|
1538
|
+
session_id: str
|
|
1539
|
+
messages: list[SessionMessagePayload] = Field(default_factory=list)
|
|
1540
|
+
|
|
1541
|
+
|
|
1542
|
+
EventPayload = Union[
|
|
1543
|
+
StreamTextPayload,
|
|
1544
|
+
ThinkingPayload,
|
|
1545
|
+
ToolUsePayload,
|
|
1546
|
+
ToolResultPayload,
|
|
1547
|
+
PermissionRequestPayload,
|
|
1548
|
+
PermissionResponsePayload,
|
|
1549
|
+
ChoiceRequestPayload,
|
|
1550
|
+
ChoiceResponsePayload,
|
|
1551
|
+
CompactPayload,
|
|
1552
|
+
ErrorPayload,
|
|
1553
|
+
TurnCompletePayload,
|
|
1554
|
+
StreamModePayload,
|
|
1555
|
+
StreamRetryPayload,
|
|
1556
|
+
SteeringAppliedPayload,
|
|
1557
|
+
QueuedMessageStartedPayload,
|
|
1558
|
+
QueuedMessageUpdatedPayload,
|
|
1559
|
+
DocumentJobPayload,
|
|
1560
|
+
DocumentSelectionTranslationPayload,
|
|
1561
|
+
AgentStatePayload,
|
|
1562
|
+
AgentOutputPayload,
|
|
1563
|
+
ModeChangePayload,
|
|
1564
|
+
ModelChangePayload,
|
|
1565
|
+
PermissionModeChangePayload,
|
|
1566
|
+
PlanReadyPayload,
|
|
1567
|
+
PeerMessagePayload,
|
|
1568
|
+
TeamMessagePayload,
|
|
1569
|
+
TeamStatePayload,
|
|
1570
|
+
TaskUpdatePayload,
|
|
1571
|
+
ScheduleRunPayload,
|
|
1572
|
+
FileChangePayload,
|
|
1573
|
+
SnapshotPayload,
|
|
1574
|
+
RevertPayload,
|
|
1575
|
+
ServerConnectedPayload,
|
|
1576
|
+
ServerHeartbeatPayload,
|
|
1577
|
+
SessionHistoryPayload,
|
|
1578
|
+
]
|
|
1579
|
+
|
|
1580
|
+
|
|
1581
|
+
# ── CoreEvent → Pydantic conversion ──────────────────────────────
|
|
1582
|
+
|
|
1583
|
+
def core_event_to_payload(event: Any) -> EventPayload:
|
|
1584
|
+
"""Convert a crabcode_core CoreEvent dataclass to a Pydantic payload."""
|
|
1585
|
+
from crabcode_core.types.event import (
|
|
1586
|
+
AgentOutputEvent,
|
|
1587
|
+
AgentStateEvent,
|
|
1588
|
+
ChoiceRequestEvent,
|
|
1589
|
+
ChoiceResponseEvent,
|
|
1590
|
+
CompactEvent,
|
|
1591
|
+
DocumentJobEvent,
|
|
1592
|
+
DocumentSelectionTranslationEvent,
|
|
1593
|
+
ErrorEvent,
|
|
1594
|
+
ModeChangeEvent,
|
|
1595
|
+
PermissionRequestEvent,
|
|
1596
|
+
PermissionResponseEvent,
|
|
1597
|
+
PeerMessageEvent,
|
|
1598
|
+
PlanReadyEvent,
|
|
1599
|
+
ScheduleRunEvent,
|
|
1600
|
+
StreamModeEvent,
|
|
1601
|
+
StreamRetryEvent,
|
|
1602
|
+
SteeringAppliedEvent,
|
|
1603
|
+
QueuedMessageStartedEvent,
|
|
1604
|
+
QueuedMessageUpdatedEvent,
|
|
1605
|
+
StreamTextEvent,
|
|
1606
|
+
TaskUpdateEvent,
|
|
1607
|
+
TeamMessageEvent,
|
|
1608
|
+
TeamStateEvent,
|
|
1609
|
+
ThinkingEvent,
|
|
1610
|
+
ToolResultEvent,
|
|
1611
|
+
ToolUseEvent,
|
|
1612
|
+
TurnCompleteEvent,
|
|
1613
|
+
)
|
|
1614
|
+
|
|
1615
|
+
if isinstance(event, StreamTextEvent):
|
|
1616
|
+
return StreamTextPayload(text=event.text)
|
|
1617
|
+
if isinstance(event, ThinkingEvent):
|
|
1618
|
+
return ThinkingPayload(text=event.text)
|
|
1619
|
+
if isinstance(event, ToolUseEvent):
|
|
1620
|
+
return ToolUsePayload(
|
|
1621
|
+
tool_name=event.tool_name,
|
|
1622
|
+
tool_input=event.tool_input,
|
|
1623
|
+
tool_use_id=event.tool_use_id,
|
|
1624
|
+
agent_id=event.agent_id,
|
|
1625
|
+
)
|
|
1626
|
+
if isinstance(event, ToolResultEvent):
|
|
1627
|
+
return ToolResultPayload(
|
|
1628
|
+
tool_use_id=event.tool_use_id,
|
|
1629
|
+
tool_name=event.tool_name,
|
|
1630
|
+
result=event.result,
|
|
1631
|
+
is_error=event.is_error,
|
|
1632
|
+
result_for_display=event.result_for_display,
|
|
1633
|
+
tool_input=event.tool_input,
|
|
1634
|
+
agent_id=event.agent_id,
|
|
1635
|
+
images=event.images,
|
|
1636
|
+
)
|
|
1637
|
+
if isinstance(event, PermissionRequestEvent):
|
|
1638
|
+
return PermissionRequestPayload(
|
|
1639
|
+
tool_name=event.tool_name,
|
|
1640
|
+
tool_input=event.tool_input,
|
|
1641
|
+
tool_use_id=event.tool_use_id,
|
|
1642
|
+
reason=event.reason,
|
|
1643
|
+
permission_key=event.permission_key,
|
|
1644
|
+
agent_id=event.agent_id,
|
|
1645
|
+
request_kind=event.request_kind,
|
|
1646
|
+
)
|
|
1647
|
+
if isinstance(event, PermissionResponseEvent):
|
|
1648
|
+
return PermissionResponsePayload(
|
|
1649
|
+
tool_use_id=event.tool_use_id,
|
|
1650
|
+
allowed=event.allowed,
|
|
1651
|
+
always_allow=event.always_allow,
|
|
1652
|
+
agent_id=event.agent_id,
|
|
1653
|
+
feedback=event.feedback,
|
|
1654
|
+
)
|
|
1655
|
+
if isinstance(event, ChoiceRequestEvent):
|
|
1656
|
+
return ChoiceRequestPayload(
|
|
1657
|
+
tool_use_id=event.tool_use_id,
|
|
1658
|
+
question=event.question,
|
|
1659
|
+
options=event.options,
|
|
1660
|
+
multiple=event.multiple,
|
|
1661
|
+
agent_id=event.agent_id,
|
|
1662
|
+
)
|
|
1663
|
+
if isinstance(event, ChoiceResponseEvent):
|
|
1664
|
+
return ChoiceResponsePayload(
|
|
1665
|
+
tool_use_id=event.tool_use_id,
|
|
1666
|
+
selected=event.selected,
|
|
1667
|
+
cancelled=event.cancelled,
|
|
1668
|
+
agent_id=event.agent_id,
|
|
1669
|
+
)
|
|
1670
|
+
if isinstance(event, CompactEvent):
|
|
1671
|
+
return CompactPayload(
|
|
1672
|
+
summary=event.summary,
|
|
1673
|
+
messages_before=event.messages_before,
|
|
1674
|
+
messages_after=event.messages_after,
|
|
1675
|
+
trigger=event.trigger,
|
|
1676
|
+
agent_id=event.agent_id,
|
|
1677
|
+
)
|
|
1678
|
+
if isinstance(event, ErrorEvent):
|
|
1679
|
+
return ErrorPayload(
|
|
1680
|
+
message=event.message,
|
|
1681
|
+
recoverable=event.recoverable,
|
|
1682
|
+
error_type=event.error_type,
|
|
1683
|
+
agent_id=event.agent_id,
|
|
1684
|
+
)
|
|
1685
|
+
if isinstance(event, TurnCompleteEvent):
|
|
1686
|
+
return TurnCompletePayload(
|
|
1687
|
+
reason=event.reason,
|
|
1688
|
+
turn_count=event.turn_count,
|
|
1689
|
+
usage=event.usage,
|
|
1690
|
+
context_used_tokens=event.context_used_tokens,
|
|
1691
|
+
context_token_source=event.context_token_source,
|
|
1692
|
+
context_window_tokens=event.context_window_tokens,
|
|
1693
|
+
context_remaining_tokens=event.context_remaining_tokens,
|
|
1694
|
+
context_used_percent=event.context_used_percent,
|
|
1695
|
+
assistant_message_uuid=event.assistant_message_uuid,
|
|
1696
|
+
turn_details=event.turn_details,
|
|
1697
|
+
prompt_budget=event.prompt_budget or None,
|
|
1698
|
+
)
|
|
1699
|
+
if isinstance(event, StreamModeEvent):
|
|
1700
|
+
return StreamModePayload(mode=event.mode, agent_id=event.agent_id)
|
|
1701
|
+
if isinstance(event, StreamRetryEvent):
|
|
1702
|
+
return StreamRetryPayload(
|
|
1703
|
+
message=event.message,
|
|
1704
|
+
error=event.error,
|
|
1705
|
+
retry_count=event.retry_count,
|
|
1706
|
+
max_retries=event.max_retries,
|
|
1707
|
+
delay_seconds=event.delay_seconds,
|
|
1708
|
+
unbounded=event.unbounded,
|
|
1709
|
+
transport_fallback=event.transport_fallback,
|
|
1710
|
+
discarded_text_chars=event.discarded_text_chars,
|
|
1711
|
+
agent_id=event.agent_id,
|
|
1712
|
+
)
|
|
1713
|
+
if isinstance(event, SteeringAppliedEvent):
|
|
1714
|
+
return SteeringAppliedPayload(count=event.count)
|
|
1715
|
+
if isinstance(event, QueuedMessageStartedEvent):
|
|
1716
|
+
return QueuedMessageStartedPayload(text=event.text, images=event.images, request_id=event.request_id)
|
|
1717
|
+
if isinstance(event, QueuedMessageUpdatedEvent):
|
|
1718
|
+
return QueuedMessageUpdatedPayload(request_id=event.request_id, action=event.action)
|
|
1719
|
+
if isinstance(event, DocumentJobEvent):
|
|
1720
|
+
return DocumentJobPayload(
|
|
1721
|
+
action=event.action,
|
|
1722
|
+
status=event.status,
|
|
1723
|
+
locale=event.locale,
|
|
1724
|
+
language=event.language,
|
|
1725
|
+
source=event.source,
|
|
1726
|
+
current=event.current,
|
|
1727
|
+
total=event.total,
|
|
1728
|
+
message=event.message,
|
|
1729
|
+
)
|
|
1730
|
+
if isinstance(event, DocumentSelectionTranslationEvent):
|
|
1731
|
+
return DocumentSelectionTranslationPayload(
|
|
1732
|
+
operation_id=event.operation_id,
|
|
1733
|
+
locale=event.locale,
|
|
1734
|
+
source_text=event.source_text,
|
|
1735
|
+
translated_text=event.translated_text,
|
|
1736
|
+
status=event.status,
|
|
1737
|
+
message=event.message,
|
|
1738
|
+
)
|
|
1739
|
+
if isinstance(event, AgentStateEvent):
|
|
1740
|
+
return AgentStatePayload(
|
|
1741
|
+
agent_id=event.agent_id,
|
|
1742
|
+
parent_agent_id=event.parent_agent_id,
|
|
1743
|
+
status=event.status,
|
|
1744
|
+
subagent_type=event.subagent_type,
|
|
1745
|
+
title=event.title,
|
|
1746
|
+
message=event.message,
|
|
1747
|
+
usage=event.usage,
|
|
1748
|
+
)
|
|
1749
|
+
if isinstance(event, AgentOutputEvent):
|
|
1750
|
+
return AgentOutputPayload(
|
|
1751
|
+
agent_id=event.agent_id,
|
|
1752
|
+
stream=event.stream,
|
|
1753
|
+
text=event.text,
|
|
1754
|
+
tool_name=event.tool_name,
|
|
1755
|
+
)
|
|
1756
|
+
if isinstance(event, ModeChangeEvent):
|
|
1757
|
+
return ModeChangePayload(mode=event.mode, reason=event.reason)
|
|
1758
|
+
if isinstance(event, PlanReadyEvent):
|
|
1759
|
+
return PlanReadyPayload(plan=event.plan)
|
|
1760
|
+
if isinstance(event, PeerMessageEvent):
|
|
1761
|
+
return PeerMessagePayload(
|
|
1762
|
+
message_id=event.message_id,
|
|
1763
|
+
from_session_id=event.from_session_id,
|
|
1764
|
+
from_name=event.from_name,
|
|
1765
|
+
from_cwd=event.from_cwd,
|
|
1766
|
+
text=event.text,
|
|
1767
|
+
)
|
|
1768
|
+
if isinstance(event, TeamMessageEvent):
|
|
1769
|
+
return TeamMessagePayload(
|
|
1770
|
+
team_id=event.team_id,
|
|
1771
|
+
from_agent=event.from_agent,
|
|
1772
|
+
to_agent=event.to_agent,
|
|
1773
|
+
text=event.text,
|
|
1774
|
+
msg_type=event.msg_type,
|
|
1775
|
+
message_id=event.message_id,
|
|
1776
|
+
)
|
|
1777
|
+
if isinstance(event, TeamStateEvent):
|
|
1778
|
+
return TeamStatePayload(
|
|
1779
|
+
team_id=event.team_id,
|
|
1780
|
+
agent_id=event.agent_id,
|
|
1781
|
+
old_state=event.old_state,
|
|
1782
|
+
new_state=event.new_state,
|
|
1783
|
+
role=event.role,
|
|
1784
|
+
)
|
|
1785
|
+
if isinstance(event, TaskUpdateEvent):
|
|
1786
|
+
return TaskUpdatePayload(
|
|
1787
|
+
team_id=event.team_id,
|
|
1788
|
+
task_id=event.task_id,
|
|
1789
|
+
status=event.status,
|
|
1790
|
+
assignee=event.assignee,
|
|
1791
|
+
description=event.description,
|
|
1792
|
+
)
|
|
1793
|
+
if isinstance(event, ScheduleRunEvent):
|
|
1794
|
+
return ScheduleRunPayload(
|
|
1795
|
+
job_id=event.job_id,
|
|
1796
|
+
run_id=event.run_id,
|
|
1797
|
+
status=event.status,
|
|
1798
|
+
duration_seconds=event.duration_seconds,
|
|
1799
|
+
error_message=event.error_message,
|
|
1800
|
+
result_summary=event.result_summary,
|
|
1801
|
+
next_run=event.next_run,
|
|
1802
|
+
)
|
|
1803
|
+
from crabcode_core.types.event import SnapshotEvent, RevertEvent
|
|
1804
|
+
if isinstance(event, SnapshotEvent):
|
|
1805
|
+
return SnapshotPayload(
|
|
1806
|
+
snapshot_id=event.snapshot_id,
|
|
1807
|
+
tool_name=event.tool_name,
|
|
1808
|
+
files=event.files,
|
|
1809
|
+
)
|
|
1810
|
+
if isinstance(event, RevertEvent):
|
|
1811
|
+
return RevertPayload(
|
|
1812
|
+
snapshot_id=event.snapshot_id,
|
|
1813
|
+
files_restored=event.files_restored,
|
|
1814
|
+
)
|
|
1815
|
+
# Fallback: wrap as error
|
|
1816
|
+
return ErrorPayload(
|
|
1817
|
+
message=f"Unknown event type: {type(event).__name__}",
|
|
1818
|
+
recoverable=True,
|
|
1819
|
+
error_type="unknown",
|
|
1820
|
+
)
|