codex-autorunner 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.
- codex_autorunner/__init__.py +3 -0
- codex_autorunner/bootstrap.py +151 -0
- codex_autorunner/cli.py +886 -0
- codex_autorunner/codex_cli.py +79 -0
- codex_autorunner/codex_runner.py +17 -0
- codex_autorunner/core/__init__.py +1 -0
- codex_autorunner/core/about_car.py +125 -0
- codex_autorunner/core/codex_runner.py +100 -0
- codex_autorunner/core/config.py +1465 -0
- codex_autorunner/core/doc_chat.py +547 -0
- codex_autorunner/core/docs.py +37 -0
- codex_autorunner/core/engine.py +720 -0
- codex_autorunner/core/git_utils.py +206 -0
- codex_autorunner/core/hub.py +756 -0
- codex_autorunner/core/injected_context.py +9 -0
- codex_autorunner/core/locks.py +57 -0
- codex_autorunner/core/logging_utils.py +158 -0
- codex_autorunner/core/notifications.py +465 -0
- codex_autorunner/core/optional_dependencies.py +41 -0
- codex_autorunner/core/prompt.py +107 -0
- codex_autorunner/core/prompts.py +275 -0
- codex_autorunner/core/request_context.py +21 -0
- codex_autorunner/core/runner_controller.py +116 -0
- codex_autorunner/core/runner_process.py +29 -0
- codex_autorunner/core/snapshot.py +576 -0
- codex_autorunner/core/state.py +156 -0
- codex_autorunner/core/update.py +567 -0
- codex_autorunner/core/update_runner.py +44 -0
- codex_autorunner/core/usage.py +1221 -0
- codex_autorunner/core/utils.py +108 -0
- codex_autorunner/discovery.py +102 -0
- codex_autorunner/housekeeping.py +423 -0
- codex_autorunner/integrations/__init__.py +1 -0
- codex_autorunner/integrations/app_server/__init__.py +6 -0
- codex_autorunner/integrations/app_server/client.py +1386 -0
- codex_autorunner/integrations/app_server/supervisor.py +206 -0
- codex_autorunner/integrations/github/__init__.py +10 -0
- codex_autorunner/integrations/github/service.py +889 -0
- codex_autorunner/integrations/telegram/__init__.py +1 -0
- codex_autorunner/integrations/telegram/adapter.py +1401 -0
- codex_autorunner/integrations/telegram/commands_registry.py +104 -0
- codex_autorunner/integrations/telegram/config.py +450 -0
- codex_autorunner/integrations/telegram/constants.py +154 -0
- codex_autorunner/integrations/telegram/dispatch.py +162 -0
- codex_autorunner/integrations/telegram/handlers/__init__.py +0 -0
- codex_autorunner/integrations/telegram/handlers/approvals.py +241 -0
- codex_autorunner/integrations/telegram/handlers/callbacks.py +72 -0
- codex_autorunner/integrations/telegram/handlers/commands.py +160 -0
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +5262 -0
- codex_autorunner/integrations/telegram/handlers/messages.py +477 -0
- codex_autorunner/integrations/telegram/handlers/selections.py +545 -0
- codex_autorunner/integrations/telegram/helpers.py +2084 -0
- codex_autorunner/integrations/telegram/notifications.py +164 -0
- codex_autorunner/integrations/telegram/outbox.py +174 -0
- codex_autorunner/integrations/telegram/rendering.py +102 -0
- codex_autorunner/integrations/telegram/retry.py +37 -0
- codex_autorunner/integrations/telegram/runtime.py +270 -0
- codex_autorunner/integrations/telegram/service.py +921 -0
- codex_autorunner/integrations/telegram/state.py +1223 -0
- codex_autorunner/integrations/telegram/transport.py +318 -0
- codex_autorunner/integrations/telegram/types.py +57 -0
- codex_autorunner/integrations/telegram/voice.py +413 -0
- codex_autorunner/manifest.py +150 -0
- codex_autorunner/routes/__init__.py +53 -0
- codex_autorunner/routes/base.py +470 -0
- codex_autorunner/routes/docs.py +275 -0
- codex_autorunner/routes/github.py +197 -0
- codex_autorunner/routes/repos.py +121 -0
- codex_autorunner/routes/sessions.py +137 -0
- codex_autorunner/routes/shared.py +137 -0
- codex_autorunner/routes/system.py +175 -0
- codex_autorunner/routes/terminal_images.py +107 -0
- codex_autorunner/routes/voice.py +128 -0
- codex_autorunner/server.py +23 -0
- codex_autorunner/spec_ingest.py +113 -0
- codex_autorunner/static/app.js +95 -0
- codex_autorunner/static/autoRefresh.js +209 -0
- codex_autorunner/static/bootstrap.js +105 -0
- codex_autorunner/static/bus.js +23 -0
- codex_autorunner/static/cache.js +52 -0
- codex_autorunner/static/constants.js +48 -0
- codex_autorunner/static/dashboard.js +795 -0
- codex_autorunner/static/docs.js +1514 -0
- codex_autorunner/static/env.js +99 -0
- codex_autorunner/static/github.js +168 -0
- codex_autorunner/static/hub.js +1511 -0
- codex_autorunner/static/index.html +622 -0
- codex_autorunner/static/loader.js +28 -0
- codex_autorunner/static/logs.js +690 -0
- codex_autorunner/static/mobileCompact.js +300 -0
- codex_autorunner/static/snapshot.js +116 -0
- codex_autorunner/static/state.js +87 -0
- codex_autorunner/static/styles.css +4966 -0
- codex_autorunner/static/tabs.js +50 -0
- codex_autorunner/static/terminal.js +21 -0
- codex_autorunner/static/terminalManager.js +3535 -0
- codex_autorunner/static/todoPreview.js +25 -0
- codex_autorunner/static/types.d.ts +8 -0
- codex_autorunner/static/utils.js +597 -0
- codex_autorunner/static/vendor/LICENSE.xterm +24 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-400-cyrillic-ext.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-400-cyrillic.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-400-greek.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-400-latin-ext.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-400-latin.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-400-vietnamese.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-500-cyrillic-ext.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-500-cyrillic.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-500-greek.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-500-latin-ext.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-500-latin.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-500-vietnamese.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-600-cyrillic-ext.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-600-cyrillic.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-600-greek.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-600-latin-ext.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-600-latin.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/JetBrainsMono-600-vietnamese.woff2 +0 -0
- codex_autorunner/static/vendor/fonts/jetbrains-mono/OFL.txt +93 -0
- codex_autorunner/static/vendor/xterm-addon-fit.js +2 -0
- codex_autorunner/static/vendor/xterm.css +209 -0
- codex_autorunner/static/vendor/xterm.js +2 -0
- codex_autorunner/static/voice.js +591 -0
- codex_autorunner/voice/__init__.py +39 -0
- codex_autorunner/voice/capture.py +349 -0
- codex_autorunner/voice/config.py +167 -0
- codex_autorunner/voice/provider.py +66 -0
- codex_autorunner/voice/providers/__init__.py +7 -0
- codex_autorunner/voice/providers/openai_whisper.py +345 -0
- codex_autorunner/voice/resolver.py +36 -0
- codex_autorunner/voice/service.py +210 -0
- codex_autorunner/web/__init__.py +1 -0
- codex_autorunner/web/app.py +1037 -0
- codex_autorunner/web/hub_jobs.py +181 -0
- codex_autorunner/web/middleware.py +552 -0
- codex_autorunner/web/pty_session.py +357 -0
- codex_autorunner/web/runner_manager.py +25 -0
- codex_autorunner/web/schemas.py +253 -0
- codex_autorunner/web/static_assets.py +430 -0
- codex_autorunner/web/terminal_sessions.py +78 -0
- codex_autorunner/workspace.py +16 -0
- codex_autorunner-0.1.0.dist-info/METADATA +240 -0
- codex_autorunner-0.1.0.dist-info/RECORD +147 -0
- codex_autorunner-0.1.0.dist-info/WHEEL +5 -0
- codex_autorunner-0.1.0.dist-info/entry_points.txt +3 -0
- codex_autorunner-0.1.0.dist-info/licenses/LICENSE +21 -0
- codex_autorunner-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import secrets
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from ...core.logging_utils import log_event
|
|
8
|
+
from ...core.state import now_iso
|
|
9
|
+
from .adapter import TelegramCallbackQuery
|
|
10
|
+
from .constants import PLACEHOLDER_TEXT, TELEGRAM_MAX_MESSAGE_LENGTH
|
|
11
|
+
from .helpers import _format_turn_metrics, _should_trace_message, _with_conversation_id
|
|
12
|
+
from .state import OutboxRecord
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TelegramMessageTransport:
|
|
16
|
+
async def _send_message_with_outbox(
|
|
17
|
+
self,
|
|
18
|
+
chat_id: int,
|
|
19
|
+
text: str,
|
|
20
|
+
*,
|
|
21
|
+
thread_id: Optional[int],
|
|
22
|
+
reply_to: Optional[int],
|
|
23
|
+
placeholder_id: Optional[int] = None,
|
|
24
|
+
) -> bool:
|
|
25
|
+
record = OutboxRecord(
|
|
26
|
+
record_id=secrets.token_hex(8),
|
|
27
|
+
chat_id=chat_id,
|
|
28
|
+
thread_id=thread_id,
|
|
29
|
+
reply_to_message_id=reply_to,
|
|
30
|
+
placeholder_message_id=placeholder_id,
|
|
31
|
+
text=text,
|
|
32
|
+
created_at=now_iso(),
|
|
33
|
+
)
|
|
34
|
+
return await self._outbox_manager.send_message_with_outbox(record)
|
|
35
|
+
|
|
36
|
+
async def _edit_message_text(
|
|
37
|
+
self,
|
|
38
|
+
chat_id: int,
|
|
39
|
+
message_id: int,
|
|
40
|
+
text: str,
|
|
41
|
+
*,
|
|
42
|
+
reply_markup: Optional[dict[str, Any]] = None,
|
|
43
|
+
) -> bool:
|
|
44
|
+
try:
|
|
45
|
+
payload_text, parse_mode = self._prepare_message(text)
|
|
46
|
+
await self._bot.edit_message_text(
|
|
47
|
+
chat_id,
|
|
48
|
+
message_id,
|
|
49
|
+
payload_text,
|
|
50
|
+
reply_markup=reply_markup,
|
|
51
|
+
parse_mode=parse_mode,
|
|
52
|
+
)
|
|
53
|
+
except Exception:
|
|
54
|
+
return False
|
|
55
|
+
return True
|
|
56
|
+
|
|
57
|
+
async def _delete_message(self, chat_id: int, message_id: Optional[int]) -> bool:
|
|
58
|
+
if message_id is None:
|
|
59
|
+
return False
|
|
60
|
+
try:
|
|
61
|
+
return bool(await self._bot.delete_message(chat_id, message_id))
|
|
62
|
+
except Exception:
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
async def _edit_callback_message(
|
|
66
|
+
self,
|
|
67
|
+
callback: TelegramCallbackQuery,
|
|
68
|
+
text: str,
|
|
69
|
+
*,
|
|
70
|
+
reply_markup: Optional[dict[str, Any]] = None,
|
|
71
|
+
) -> bool:
|
|
72
|
+
if callback.chat_id is None or callback.message_id is None:
|
|
73
|
+
return False
|
|
74
|
+
return await self._edit_message_text(
|
|
75
|
+
callback.chat_id,
|
|
76
|
+
callback.message_id,
|
|
77
|
+
text,
|
|
78
|
+
reply_markup=reply_markup,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def _format_voice_transcript_message(self, text: str, agent_status: str) -> str:
|
|
82
|
+
header = "User:\n"
|
|
83
|
+
footer = f"\n\nAgent:\n{agent_status}"
|
|
84
|
+
max_len = TELEGRAM_MAX_MESSAGE_LENGTH
|
|
85
|
+
available = max_len - len(header) - len(footer)
|
|
86
|
+
if available <= 0:
|
|
87
|
+
return f"{header}{footer.lstrip()}"
|
|
88
|
+
transcript = text
|
|
89
|
+
truncation_note = "\n\n...(truncated)"
|
|
90
|
+
if len(transcript) > available:
|
|
91
|
+
remaining = available - len(truncation_note)
|
|
92
|
+
if remaining < 0:
|
|
93
|
+
remaining = 0
|
|
94
|
+
transcript = transcript[:remaining].rstrip()
|
|
95
|
+
transcript = f"{transcript}{truncation_note}"
|
|
96
|
+
return f"{header}{transcript}{footer}"
|
|
97
|
+
|
|
98
|
+
async def _send_voice_transcript_message(
|
|
99
|
+
self,
|
|
100
|
+
chat_id: int,
|
|
101
|
+
text: str,
|
|
102
|
+
*,
|
|
103
|
+
thread_id: Optional[int],
|
|
104
|
+
reply_to: Optional[int],
|
|
105
|
+
) -> Optional[int]:
|
|
106
|
+
payload_text, parse_mode = self._prepare_outgoing_text(
|
|
107
|
+
text,
|
|
108
|
+
chat_id=chat_id,
|
|
109
|
+
thread_id=thread_id,
|
|
110
|
+
reply_to=reply_to,
|
|
111
|
+
)
|
|
112
|
+
response = await self._bot.send_message(
|
|
113
|
+
chat_id,
|
|
114
|
+
payload_text,
|
|
115
|
+
message_thread_id=thread_id,
|
|
116
|
+
reply_to_message_id=reply_to,
|
|
117
|
+
parse_mode=parse_mode,
|
|
118
|
+
)
|
|
119
|
+
message_id = response.get("message_id") if isinstance(response, dict) else None
|
|
120
|
+
return message_id if isinstance(message_id, int) else None
|
|
121
|
+
|
|
122
|
+
async def _finalize_voice_transcript(
|
|
123
|
+
self,
|
|
124
|
+
chat_id: int,
|
|
125
|
+
message_id: Optional[int],
|
|
126
|
+
transcript_text: Optional[str],
|
|
127
|
+
) -> None:
|
|
128
|
+
if message_id is None or transcript_text is None:
|
|
129
|
+
return
|
|
130
|
+
final_message = self._format_voice_transcript_message(
|
|
131
|
+
transcript_text,
|
|
132
|
+
"Reply below.",
|
|
133
|
+
)
|
|
134
|
+
await self._edit_message_text(chat_id, message_id, final_message)
|
|
135
|
+
|
|
136
|
+
async def _send_placeholder(
|
|
137
|
+
self,
|
|
138
|
+
chat_id: int,
|
|
139
|
+
*,
|
|
140
|
+
thread_id: Optional[int],
|
|
141
|
+
reply_to: Optional[int],
|
|
142
|
+
) -> Optional[int]:
|
|
143
|
+
try:
|
|
144
|
+
payload_text, parse_mode = self._prepare_outgoing_text(
|
|
145
|
+
PLACEHOLDER_TEXT,
|
|
146
|
+
chat_id=chat_id,
|
|
147
|
+
thread_id=thread_id,
|
|
148
|
+
reply_to=reply_to,
|
|
149
|
+
)
|
|
150
|
+
response = await self._bot.send_message(
|
|
151
|
+
chat_id,
|
|
152
|
+
payload_text,
|
|
153
|
+
message_thread_id=thread_id,
|
|
154
|
+
reply_to_message_id=reply_to,
|
|
155
|
+
parse_mode=parse_mode,
|
|
156
|
+
)
|
|
157
|
+
except Exception as exc:
|
|
158
|
+
log_event(
|
|
159
|
+
self._logger,
|
|
160
|
+
logging.WARNING,
|
|
161
|
+
"telegram.placeholder.failed",
|
|
162
|
+
chat_id=chat_id,
|
|
163
|
+
thread_id=thread_id,
|
|
164
|
+
reply_to_message_id=reply_to,
|
|
165
|
+
exc=exc,
|
|
166
|
+
)
|
|
167
|
+
return None
|
|
168
|
+
message_id = response.get("message_id") if isinstance(response, dict) else None
|
|
169
|
+
return message_id if isinstance(message_id, int) else None
|
|
170
|
+
|
|
171
|
+
async def _deliver_turn_response(
|
|
172
|
+
self,
|
|
173
|
+
*,
|
|
174
|
+
chat_id: int,
|
|
175
|
+
thread_id: Optional[int],
|
|
176
|
+
reply_to: Optional[int],
|
|
177
|
+
placeholder_id: Optional[int],
|
|
178
|
+
response: str,
|
|
179
|
+
) -> bool:
|
|
180
|
+
return await self._send_message_with_outbox(
|
|
181
|
+
chat_id,
|
|
182
|
+
response,
|
|
183
|
+
thread_id=thread_id,
|
|
184
|
+
reply_to=reply_to,
|
|
185
|
+
placeholder_id=placeholder_id,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
async def _send_turn_metrics(
|
|
189
|
+
self,
|
|
190
|
+
*,
|
|
191
|
+
chat_id: int,
|
|
192
|
+
thread_id: Optional[int],
|
|
193
|
+
reply_to: Optional[int],
|
|
194
|
+
elapsed_seconds: Optional[float],
|
|
195
|
+
token_usage: Optional[dict[str, Any]],
|
|
196
|
+
) -> bool:
|
|
197
|
+
metrics = _format_turn_metrics(token_usage, elapsed_seconds)
|
|
198
|
+
if not metrics:
|
|
199
|
+
return False
|
|
200
|
+
return await self._send_message_with_outbox(
|
|
201
|
+
chat_id,
|
|
202
|
+
metrics,
|
|
203
|
+
thread_id=thread_id,
|
|
204
|
+
reply_to=reply_to,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
async def _send_message(
|
|
208
|
+
self,
|
|
209
|
+
chat_id: int,
|
|
210
|
+
text: str,
|
|
211
|
+
*,
|
|
212
|
+
thread_id: Optional[int] = None,
|
|
213
|
+
reply_to: Optional[int] = None,
|
|
214
|
+
reply_markup: Optional[dict[str, Any]] = None,
|
|
215
|
+
) -> None:
|
|
216
|
+
if _should_trace_message(text):
|
|
217
|
+
text = _with_conversation_id(
|
|
218
|
+
text,
|
|
219
|
+
chat_id=chat_id,
|
|
220
|
+
thread_id=thread_id,
|
|
221
|
+
)
|
|
222
|
+
prefix = self._build_debug_prefix(
|
|
223
|
+
chat_id=chat_id,
|
|
224
|
+
thread_id=thread_id,
|
|
225
|
+
reply_to=reply_to,
|
|
226
|
+
)
|
|
227
|
+
if prefix:
|
|
228
|
+
text = f"{prefix}{text}"
|
|
229
|
+
parse_mode = self._config.parse_mode
|
|
230
|
+
if parse_mode:
|
|
231
|
+
rendered, used_mode = self._render_message(text)
|
|
232
|
+
if used_mode and len(rendered) > TELEGRAM_MAX_MESSAGE_LENGTH:
|
|
233
|
+
extension = "txt"
|
|
234
|
+
if used_mode in ("Markdown", "MarkdownV2"):
|
|
235
|
+
extension = "md"
|
|
236
|
+
elif used_mode == "HTML":
|
|
237
|
+
extension = "html"
|
|
238
|
+
await self._send_document(
|
|
239
|
+
chat_id,
|
|
240
|
+
text.encode("utf-8"),
|
|
241
|
+
filename=f"response.{extension}",
|
|
242
|
+
thread_id=thread_id,
|
|
243
|
+
reply_to=reply_to,
|
|
244
|
+
caption="Response too long; see attached.",
|
|
245
|
+
)
|
|
246
|
+
return
|
|
247
|
+
payload_text = rendered if used_mode else text
|
|
248
|
+
await self._bot.send_message_chunks(
|
|
249
|
+
chat_id,
|
|
250
|
+
payload_text,
|
|
251
|
+
message_thread_id=thread_id,
|
|
252
|
+
reply_to_message_id=reply_to,
|
|
253
|
+
reply_markup=reply_markup,
|
|
254
|
+
parse_mode=used_mode,
|
|
255
|
+
)
|
|
256
|
+
return
|
|
257
|
+
payload_text, parse_mode = self._prepare_outgoing_text(
|
|
258
|
+
text,
|
|
259
|
+
chat_id=chat_id,
|
|
260
|
+
thread_id=thread_id,
|
|
261
|
+
reply_to=reply_to,
|
|
262
|
+
)
|
|
263
|
+
await self._bot.send_message_chunks(
|
|
264
|
+
chat_id,
|
|
265
|
+
payload_text,
|
|
266
|
+
message_thread_id=thread_id,
|
|
267
|
+
reply_to_message_id=reply_to,
|
|
268
|
+
reply_markup=reply_markup,
|
|
269
|
+
parse_mode=parse_mode,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
async def _send_document(
|
|
273
|
+
self,
|
|
274
|
+
chat_id: int,
|
|
275
|
+
data: bytes,
|
|
276
|
+
*,
|
|
277
|
+
filename: str,
|
|
278
|
+
thread_id: Optional[int] = None,
|
|
279
|
+
reply_to: Optional[int] = None,
|
|
280
|
+
caption: Optional[str] = None,
|
|
281
|
+
) -> None:
|
|
282
|
+
try:
|
|
283
|
+
await self._bot.send_document(
|
|
284
|
+
chat_id,
|
|
285
|
+
data,
|
|
286
|
+
filename=filename,
|
|
287
|
+
message_thread_id=thread_id,
|
|
288
|
+
reply_to_message_id=reply_to,
|
|
289
|
+
caption=caption,
|
|
290
|
+
)
|
|
291
|
+
except Exception as exc:
|
|
292
|
+
log_event(
|
|
293
|
+
self._logger,
|
|
294
|
+
logging.WARNING,
|
|
295
|
+
"telegram.send_document.failed",
|
|
296
|
+
chat_id=chat_id,
|
|
297
|
+
thread_id=thread_id,
|
|
298
|
+
reply_to_message_id=reply_to,
|
|
299
|
+
exc=exc,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
async def _answer_callback(
|
|
303
|
+
self, callback: Optional[TelegramCallbackQuery], text: str
|
|
304
|
+
) -> None:
|
|
305
|
+
if callback is None:
|
|
306
|
+
return
|
|
307
|
+
try:
|
|
308
|
+
await self._bot.answer_callback_query(callback.callback_id, text=text)
|
|
309
|
+
except Exception as exc:
|
|
310
|
+
log_event(
|
|
311
|
+
self._logger,
|
|
312
|
+
logging.WARNING,
|
|
313
|
+
"telegram.answer_callback.failed",
|
|
314
|
+
chat_id=callback.chat_id,
|
|
315
|
+
thread_id=callback.thread_id,
|
|
316
|
+
callback_id=callback.callback_id,
|
|
317
|
+
exc=exc,
|
|
318
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import dataclasses
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from ..app_server.client import ApprovalDecision
|
|
9
|
+
from .helpers import ModelOption
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class PendingApproval:
|
|
14
|
+
request_id: str
|
|
15
|
+
turn_id: str
|
|
16
|
+
codex_thread_id: Optional[str]
|
|
17
|
+
chat_id: int
|
|
18
|
+
thread_id: Optional[int]
|
|
19
|
+
topic_key: Optional[str]
|
|
20
|
+
message_id: Optional[int]
|
|
21
|
+
created_at: str
|
|
22
|
+
future: asyncio.Future[ApprovalDecision]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class TurnContext:
|
|
27
|
+
topic_key: str
|
|
28
|
+
chat_id: int
|
|
29
|
+
thread_id: Optional[int]
|
|
30
|
+
codex_thread_id: Optional[str]
|
|
31
|
+
reply_to_message_id: Optional[int]
|
|
32
|
+
placeholder_message_id: Optional[int] = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class CompactState:
|
|
37
|
+
summary_text: str
|
|
38
|
+
display_text: str
|
|
39
|
+
message_id: int
|
|
40
|
+
created_at: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class SelectionState:
|
|
45
|
+
items: list[tuple[str, str]]
|
|
46
|
+
page: int = 0
|
|
47
|
+
button_labels: Optional[dict[str, str]] = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class ReviewCommitSelectionState(SelectionState):
|
|
52
|
+
delivery: str = "inline"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class ModelPickerState(SelectionState):
|
|
57
|
+
options: dict[str, ModelOption] = dataclasses.field(default_factory=dict)
|