codex-autorunner 0.1.2__py3-none-any.whl → 1.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 +12 -1
- codex_autorunner/__main__.py +4 -0
- codex_autorunner/agents/codex/harness.py +1 -1
- codex_autorunner/agents/opencode/client.py +68 -35
- codex_autorunner/agents/opencode/constants.py +3 -0
- codex_autorunner/agents/opencode/harness.py +6 -1
- codex_autorunner/agents/opencode/logging.py +21 -5
- codex_autorunner/agents/opencode/run_prompt.py +1 -0
- codex_autorunner/agents/opencode/runtime.py +176 -47
- codex_autorunner/agents/opencode/supervisor.py +36 -48
- codex_autorunner/agents/registry.py +155 -8
- codex_autorunner/api.py +25 -0
- codex_autorunner/bootstrap.py +22 -37
- codex_autorunner/cli.py +5 -1156
- codex_autorunner/codex_cli.py +20 -84
- codex_autorunner/core/__init__.py +4 -0
- codex_autorunner/core/about_car.py +49 -32
- codex_autorunner/core/adapter_utils.py +21 -0
- codex_autorunner/core/app_server_ids.py +59 -0
- codex_autorunner/core/app_server_logging.py +7 -3
- codex_autorunner/core/app_server_prompts.py +27 -260
- codex_autorunner/core/app_server_threads.py +26 -28
- codex_autorunner/core/app_server_utils.py +165 -0
- codex_autorunner/core/archive.py +349 -0
- codex_autorunner/core/codex_runner.py +12 -2
- codex_autorunner/core/config.py +587 -103
- codex_autorunner/core/docs.py +10 -2
- codex_autorunner/core/drafts.py +136 -0
- codex_autorunner/core/engine.py +1531 -866
- codex_autorunner/core/exceptions.py +4 -0
- codex_autorunner/core/flows/__init__.py +25 -0
- codex_autorunner/core/flows/controller.py +202 -0
- codex_autorunner/core/flows/definition.py +82 -0
- codex_autorunner/core/flows/models.py +88 -0
- codex_autorunner/core/flows/reasons.py +52 -0
- codex_autorunner/core/flows/reconciler.py +131 -0
- codex_autorunner/core/flows/runtime.py +382 -0
- codex_autorunner/core/flows/store.py +568 -0
- codex_autorunner/core/flows/transition.py +138 -0
- codex_autorunner/core/flows/ux_helpers.py +257 -0
- codex_autorunner/core/flows/worker_process.py +242 -0
- codex_autorunner/core/git_utils.py +62 -0
- codex_autorunner/core/hub.py +136 -16
- codex_autorunner/core/locks.py +4 -0
- codex_autorunner/core/notifications.py +14 -2
- codex_autorunner/core/ports/__init__.py +28 -0
- codex_autorunner/core/ports/agent_backend.py +150 -0
- codex_autorunner/core/ports/backend_orchestrator.py +41 -0
- codex_autorunner/core/ports/run_event.py +91 -0
- codex_autorunner/core/prompt.py +15 -7
- codex_autorunner/core/redaction.py +29 -0
- codex_autorunner/core/review_context.py +5 -8
- codex_autorunner/core/run_index.py +6 -0
- codex_autorunner/core/runner_process.py +5 -2
- codex_autorunner/core/state.py +0 -88
- codex_autorunner/core/state_roots.py +57 -0
- codex_autorunner/core/supervisor_protocol.py +15 -0
- codex_autorunner/core/supervisor_utils.py +67 -0
- codex_autorunner/core/text_delta_coalescer.py +54 -0
- codex_autorunner/core/ticket_linter_cli.py +201 -0
- codex_autorunner/core/ticket_manager_cli.py +432 -0
- codex_autorunner/core/update.py +24 -16
- codex_autorunner/core/update_paths.py +28 -0
- codex_autorunner/core/update_runner.py +2 -0
- codex_autorunner/core/usage.py +164 -12
- codex_autorunner/core/utils.py +120 -11
- codex_autorunner/discovery.py +2 -4
- codex_autorunner/flows/review/__init__.py +17 -0
- codex_autorunner/{core/review.py → flows/review/service.py} +15 -10
- codex_autorunner/flows/ticket_flow/__init__.py +3 -0
- codex_autorunner/flows/ticket_flow/definition.py +98 -0
- codex_autorunner/integrations/agents/__init__.py +17 -0
- codex_autorunner/integrations/agents/backend_orchestrator.py +284 -0
- codex_autorunner/integrations/agents/codex_adapter.py +90 -0
- codex_autorunner/integrations/agents/codex_backend.py +448 -0
- codex_autorunner/integrations/agents/opencode_adapter.py +108 -0
- codex_autorunner/integrations/agents/opencode_backend.py +598 -0
- codex_autorunner/integrations/agents/runner.py +91 -0
- codex_autorunner/integrations/agents/wiring.py +271 -0
- codex_autorunner/integrations/app_server/client.py +583 -152
- codex_autorunner/integrations/app_server/env.py +2 -107
- codex_autorunner/{core/app_server_events.py → integrations/app_server/event_buffer.py} +15 -8
- codex_autorunner/integrations/app_server/supervisor.py +59 -33
- codex_autorunner/integrations/telegram/adapter.py +204 -165
- codex_autorunner/integrations/telegram/api_schemas.py +120 -0
- codex_autorunner/integrations/telegram/config.py +221 -0
- codex_autorunner/integrations/telegram/constants.py +17 -2
- codex_autorunner/integrations/telegram/dispatch.py +17 -0
- codex_autorunner/integrations/telegram/doctor.py +47 -0
- codex_autorunner/integrations/telegram/handlers/callbacks.py +7 -4
- codex_autorunner/integrations/telegram/handlers/commands/__init__.py +2 -0
- codex_autorunner/integrations/telegram/handlers/commands/execution.py +53 -57
- codex_autorunner/integrations/telegram/handlers/commands/files.py +2 -6
- codex_autorunner/integrations/telegram/handlers/commands/flows.py +1364 -0
- codex_autorunner/integrations/telegram/handlers/commands/formatting.py +1 -1
- codex_autorunner/integrations/telegram/handlers/commands/github.py +41 -582
- codex_autorunner/integrations/telegram/handlers/commands/workspace.py +8 -8
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +137 -478
- codex_autorunner/integrations/telegram/handlers/commands_spec.py +17 -4
- codex_autorunner/integrations/telegram/handlers/messages.py +121 -9
- codex_autorunner/integrations/telegram/handlers/selections.py +61 -1
- codex_autorunner/integrations/telegram/helpers.py +111 -16
- codex_autorunner/integrations/telegram/outbox.py +208 -37
- codex_autorunner/integrations/telegram/progress_stream.py +3 -10
- codex_autorunner/integrations/telegram/service.py +221 -42
- codex_autorunner/integrations/telegram/state.py +100 -2
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +611 -0
- codex_autorunner/integrations/telegram/transport.py +39 -4
- codex_autorunner/integrations/telegram/trigger_mode.py +53 -0
- codex_autorunner/manifest.py +2 -0
- codex_autorunner/plugin_api.py +22 -0
- codex_autorunner/routes/__init__.py +37 -67
- codex_autorunner/routes/agents.py +2 -137
- codex_autorunner/routes/analytics.py +3 -0
- codex_autorunner/routes/app_server.py +2 -131
- codex_autorunner/routes/base.py +2 -624
- codex_autorunner/routes/file_chat.py +7 -0
- codex_autorunner/routes/flows.py +7 -0
- codex_autorunner/routes/messages.py +7 -0
- codex_autorunner/routes/repos.py +2 -196
- codex_autorunner/routes/review.py +2 -147
- codex_autorunner/routes/sessions.py +2 -175
- codex_autorunner/routes/settings.py +2 -168
- codex_autorunner/routes/shared.py +2 -275
- codex_autorunner/routes/system.py +4 -188
- codex_autorunner/routes/usage.py +3 -0
- codex_autorunner/routes/voice.py +2 -119
- codex_autorunner/routes/workspace.py +3 -0
- codex_autorunner/server.py +3 -2
- codex_autorunner/static/agentControls.js +41 -11
- codex_autorunner/static/agentEvents.js +248 -0
- codex_autorunner/static/app.js +35 -24
- codex_autorunner/static/archive.js +826 -0
- codex_autorunner/static/archiveApi.js +37 -0
- codex_autorunner/static/autoRefresh.js +36 -8
- codex_autorunner/static/bootstrap.js +1 -0
- codex_autorunner/static/bus.js +1 -0
- codex_autorunner/static/cache.js +1 -0
- codex_autorunner/static/constants.js +20 -4
- codex_autorunner/static/dashboard.js +344 -325
- codex_autorunner/static/diffRenderer.js +37 -0
- codex_autorunner/static/docChatCore.js +324 -0
- codex_autorunner/static/docChatStorage.js +65 -0
- codex_autorunner/static/docChatVoice.js +65 -0
- codex_autorunner/static/docEditor.js +133 -0
- codex_autorunner/static/env.js +1 -0
- codex_autorunner/static/eventSummarizer.js +166 -0
- codex_autorunner/static/fileChat.js +182 -0
- codex_autorunner/static/health.js +155 -0
- codex_autorunner/static/hub.js +126 -185
- codex_autorunner/static/index.html +839 -863
- codex_autorunner/static/liveUpdates.js +1 -0
- codex_autorunner/static/loader.js +1 -0
- codex_autorunner/static/messages.js +873 -0
- codex_autorunner/static/mobileCompact.js +2 -1
- codex_autorunner/static/preserve.js +17 -0
- codex_autorunner/static/settings.js +149 -217
- codex_autorunner/static/smartRefresh.js +52 -0
- codex_autorunner/static/styles.css +8850 -3876
- codex_autorunner/static/tabs.js +175 -11
- codex_autorunner/static/terminal.js +32 -0
- codex_autorunner/static/terminalManager.js +34 -59
- codex_autorunner/static/ticketChatActions.js +333 -0
- codex_autorunner/static/ticketChatEvents.js +16 -0
- codex_autorunner/static/ticketChatStorage.js +16 -0
- codex_autorunner/static/ticketChatStream.js +264 -0
- codex_autorunner/static/ticketEditor.js +844 -0
- codex_autorunner/static/ticketVoice.js +9 -0
- codex_autorunner/static/tickets.js +1988 -0
- codex_autorunner/static/utils.js +43 -3
- codex_autorunner/static/voice.js +1 -0
- codex_autorunner/static/workspace.js +765 -0
- codex_autorunner/static/workspaceApi.js +53 -0
- codex_autorunner/static/workspaceFileBrowser.js +504 -0
- codex_autorunner/surfaces/__init__.py +5 -0
- codex_autorunner/surfaces/cli/__init__.py +6 -0
- codex_autorunner/surfaces/cli/cli.py +1224 -0
- codex_autorunner/surfaces/cli/codex_cli.py +20 -0
- codex_autorunner/surfaces/telegram/__init__.py +3 -0
- codex_autorunner/surfaces/web/__init__.py +1 -0
- codex_autorunner/surfaces/web/app.py +2019 -0
- codex_autorunner/surfaces/web/hub_jobs.py +192 -0
- codex_autorunner/surfaces/web/middleware.py +587 -0
- codex_autorunner/surfaces/web/pty_session.py +370 -0
- codex_autorunner/surfaces/web/review.py +6 -0
- codex_autorunner/surfaces/web/routes/__init__.py +78 -0
- codex_autorunner/surfaces/web/routes/agents.py +138 -0
- codex_autorunner/surfaces/web/routes/analytics.py +277 -0
- codex_autorunner/surfaces/web/routes/app_server.py +132 -0
- codex_autorunner/surfaces/web/routes/archive.py +357 -0
- codex_autorunner/surfaces/web/routes/base.py +615 -0
- codex_autorunner/surfaces/web/routes/file_chat.py +836 -0
- codex_autorunner/surfaces/web/routes/flows.py +1164 -0
- codex_autorunner/surfaces/web/routes/messages.py +459 -0
- codex_autorunner/surfaces/web/routes/repos.py +197 -0
- codex_autorunner/surfaces/web/routes/review.py +148 -0
- codex_autorunner/surfaces/web/routes/sessions.py +176 -0
- codex_autorunner/surfaces/web/routes/settings.py +169 -0
- codex_autorunner/surfaces/web/routes/shared.py +280 -0
- codex_autorunner/surfaces/web/routes/system.py +196 -0
- codex_autorunner/surfaces/web/routes/usage.py +89 -0
- codex_autorunner/surfaces/web/routes/voice.py +120 -0
- codex_autorunner/surfaces/web/routes/workspace.py +271 -0
- codex_autorunner/surfaces/web/runner_manager.py +25 -0
- codex_autorunner/surfaces/web/schemas.py +417 -0
- codex_autorunner/surfaces/web/static_assets.py +490 -0
- codex_autorunner/surfaces/web/static_refresh.py +86 -0
- codex_autorunner/surfaces/web/terminal_sessions.py +78 -0
- codex_autorunner/tickets/__init__.py +27 -0
- codex_autorunner/tickets/agent_pool.py +399 -0
- codex_autorunner/tickets/files.py +89 -0
- codex_autorunner/tickets/frontmatter.py +55 -0
- codex_autorunner/tickets/lint.py +102 -0
- codex_autorunner/tickets/models.py +97 -0
- codex_autorunner/tickets/outbox.py +244 -0
- codex_autorunner/tickets/replies.py +179 -0
- codex_autorunner/tickets/runner.py +881 -0
- codex_autorunner/tickets/spec_ingest.py +77 -0
- codex_autorunner/web/__init__.py +5 -1
- codex_autorunner/web/app.py +2 -1771
- codex_autorunner/web/hub_jobs.py +2 -191
- codex_autorunner/web/middleware.py +2 -587
- codex_autorunner/web/pty_session.py +2 -369
- codex_autorunner/web/runner_manager.py +2 -24
- codex_autorunner/web/schemas.py +2 -396
- codex_autorunner/web/static_assets.py +4 -484
- codex_autorunner/web/static_refresh.py +2 -85
- codex_autorunner/web/terminal_sessions.py +2 -77
- codex_autorunner/workspace/__init__.py +40 -0
- codex_autorunner/workspace/paths.py +335 -0
- codex_autorunner-1.1.0.dist-info/METADATA +154 -0
- codex_autorunner-1.1.0.dist-info/RECORD +308 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/WHEEL +1 -1
- codex_autorunner/agents/execution/policy.py +0 -292
- codex_autorunner/agents/factory.py +0 -52
- codex_autorunner/agents/orchestrator.py +0 -358
- codex_autorunner/core/doc_chat.py +0 -1446
- codex_autorunner/core/snapshot.py +0 -580
- codex_autorunner/integrations/github/chatops.py +0 -268
- codex_autorunner/integrations/github/pr_flow.py +0 -1314
- codex_autorunner/routes/docs.py +0 -381
- codex_autorunner/routes/github.py +0 -327
- codex_autorunner/routes/runs.py +0 -250
- codex_autorunner/spec_ingest.py +0 -812
- codex_autorunner/static/docChatActions.js +0 -287
- codex_autorunner/static/docChatEvents.js +0 -300
- codex_autorunner/static/docChatRender.js +0 -205
- codex_autorunner/static/docChatStream.js +0 -361
- codex_autorunner/static/docs.js +0 -20
- codex_autorunner/static/docsClipboard.js +0 -69
- codex_autorunner/static/docsCrud.js +0 -257
- codex_autorunner/static/docsDocUpdates.js +0 -62
- codex_autorunner/static/docsDrafts.js +0 -16
- codex_autorunner/static/docsElements.js +0 -69
- codex_autorunner/static/docsInit.js +0 -285
- codex_autorunner/static/docsParse.js +0 -160
- codex_autorunner/static/docsSnapshot.js +0 -87
- codex_autorunner/static/docsSpecIngest.js +0 -263
- codex_autorunner/static/docsState.js +0 -127
- codex_autorunner/static/docsThreadRegistry.js +0 -44
- codex_autorunner/static/docsUi.js +0 -153
- codex_autorunner/static/docsVoice.js +0 -56
- codex_autorunner/static/github.js +0 -504
- codex_autorunner/static/logs.js +0 -678
- codex_autorunner/static/review.js +0 -157
- codex_autorunner/static/runs.js +0 -418
- codex_autorunner/static/snapshot.js +0 -124
- codex_autorunner/static/state.js +0 -94
- codex_autorunner/static/todoPreview.js +0 -27
- codex_autorunner/workspace.py +0 -16
- codex_autorunner-0.1.2.dist-info/METADATA +0 -249
- codex_autorunner-0.1.2.dist-info/RECORD +0 -222
- /codex_autorunner/{routes → surfaces/web/routes}/terminal_images.py +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -41,14 +41,33 @@ class TelegramMessageTransport:
|
|
|
41
41
|
message_id: int,
|
|
42
42
|
text: str,
|
|
43
43
|
*,
|
|
44
|
+
message_thread_id: Optional[int] = None,
|
|
44
45
|
reply_markup: Optional[dict[str, Any]] = None,
|
|
45
46
|
) -> bool:
|
|
46
47
|
try:
|
|
47
48
|
payload_text, parse_mode = self._prepare_message(text)
|
|
49
|
+
if len(payload_text) > TELEGRAM_MAX_MESSAGE_LENGTH:
|
|
50
|
+
trimmed = trim_markdown_message(
|
|
51
|
+
payload_text,
|
|
52
|
+
max_len=TELEGRAM_MAX_MESSAGE_LENGTH,
|
|
53
|
+
render=(
|
|
54
|
+
_format_telegram_html
|
|
55
|
+
if parse_mode == "HTML"
|
|
56
|
+
else (
|
|
57
|
+
lambda v: (
|
|
58
|
+
_format_telegram_markdown(v, parse_mode)
|
|
59
|
+
if parse_mode in ("Markdown", "MarkdownV2")
|
|
60
|
+
else v
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
payload_text = trimmed
|
|
48
66
|
await self._bot.edit_message_text(
|
|
49
67
|
chat_id,
|
|
50
68
|
message_id,
|
|
51
69
|
payload_text,
|
|
70
|
+
message_thread_id=message_thread_id,
|
|
52
71
|
reply_markup=reply_markup,
|
|
53
72
|
parse_mode=parse_mode,
|
|
54
73
|
)
|
|
@@ -56,11 +75,17 @@ class TelegramMessageTransport:
|
|
|
56
75
|
return False
|
|
57
76
|
return True
|
|
58
77
|
|
|
59
|
-
async def _delete_message(
|
|
78
|
+
async def _delete_message(
|
|
79
|
+
self, chat_id: int, message_id: Optional[int], thread_id: Optional[int] = None
|
|
80
|
+
) -> bool:
|
|
60
81
|
if message_id is None:
|
|
61
82
|
return False
|
|
62
83
|
try:
|
|
63
|
-
return bool(
|
|
84
|
+
return bool(
|
|
85
|
+
await self._bot.delete_message(
|
|
86
|
+
chat_id, message_id, message_thread_id=thread_id
|
|
87
|
+
)
|
|
88
|
+
)
|
|
64
89
|
except Exception:
|
|
65
90
|
return False
|
|
66
91
|
|
|
@@ -77,6 +102,7 @@ class TelegramMessageTransport:
|
|
|
77
102
|
callback.chat_id,
|
|
78
103
|
callback.message_id,
|
|
79
104
|
text,
|
|
105
|
+
message_thread_id=callback.thread_id,
|
|
80
106
|
reply_markup=reply_markup,
|
|
81
107
|
)
|
|
82
108
|
|
|
@@ -358,7 +384,7 @@ class TelegramMessageTransport:
|
|
|
358
384
|
thread_id: Optional[int] = None,
|
|
359
385
|
reply_to: Optional[int] = None,
|
|
360
386
|
caption: Optional[str] = None,
|
|
361
|
-
) ->
|
|
387
|
+
) -> bool:
|
|
362
388
|
try:
|
|
363
389
|
await self._bot.send_document(
|
|
364
390
|
chat_id,
|
|
@@ -368,6 +394,7 @@ class TelegramMessageTransport:
|
|
|
368
394
|
reply_to_message_id=reply_to,
|
|
369
395
|
caption=caption,
|
|
370
396
|
)
|
|
397
|
+
return True
|
|
371
398
|
except Exception as exc:
|
|
372
399
|
log_event(
|
|
373
400
|
self._logger,
|
|
@@ -378,6 +405,7 @@ class TelegramMessageTransport:
|
|
|
378
405
|
reply_to_message_id=reply_to,
|
|
379
406
|
exc=exc,
|
|
380
407
|
)
|
|
408
|
+
return False
|
|
381
409
|
|
|
382
410
|
async def _answer_callback(
|
|
383
411
|
self, callback: Optional[TelegramCallbackQuery], text: str
|
|
@@ -385,7 +413,13 @@ class TelegramMessageTransport:
|
|
|
385
413
|
if callback is None:
|
|
386
414
|
return
|
|
387
415
|
try:
|
|
388
|
-
await self._bot.answer_callback_query(
|
|
416
|
+
await self._bot.answer_callback_query(
|
|
417
|
+
callback.callback_id,
|
|
418
|
+
chat_id=callback.chat_id,
|
|
419
|
+
thread_id=callback.thread_id,
|
|
420
|
+
message_id=callback.message_id,
|
|
421
|
+
text=text,
|
|
422
|
+
)
|
|
389
423
|
except Exception as exc:
|
|
390
424
|
log_event(
|
|
391
425
|
self._logger,
|
|
@@ -393,6 +427,7 @@ class TelegramMessageTransport:
|
|
|
393
427
|
"telegram.answer_callback.failed",
|
|
394
428
|
chat_id=callback.chat_id,
|
|
395
429
|
thread_id=callback.thread_id,
|
|
430
|
+
message_id=callback.message_id,
|
|
396
431
|
callback_id=callback.callback_id,
|
|
397
432
|
exc=exc,
|
|
398
433
|
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal, Optional
|
|
4
|
+
|
|
5
|
+
from .adapter import TelegramMessage
|
|
6
|
+
|
|
7
|
+
TriggerMode = Literal["all", "mentions"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def should_trigger_run(
|
|
11
|
+
message: TelegramMessage,
|
|
12
|
+
*,
|
|
13
|
+
text: str,
|
|
14
|
+
bot_username: Optional[str],
|
|
15
|
+
) -> bool:
|
|
16
|
+
"""Return True if this message should start a run in mentions-only mode.
|
|
17
|
+
|
|
18
|
+
This mirrors Takopi's "mentions" trigger mode semantics (subset):
|
|
19
|
+
|
|
20
|
+
- Always trigger in private chats.
|
|
21
|
+
- Trigger when the bot is explicitly mentioned: "@<bot_username>" anywhere in the text.
|
|
22
|
+
- Trigger when replying to a bot message (but ignore the common forum-topic
|
|
23
|
+
"implicit root reply" case where clients set reply_to_message_id == thread_id).
|
|
24
|
+
- Otherwise, do not trigger (commands and other explicit affordances are handled elsewhere).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
if message.chat_type == "private":
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
lowered = (text or "").lower()
|
|
31
|
+
if bot_username:
|
|
32
|
+
needle = f"@{bot_username}".lower()
|
|
33
|
+
if needle in lowered:
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
implicit_topic_reply = (
|
|
37
|
+
message.thread_id is not None
|
|
38
|
+
and message.reply_to_message_id is not None
|
|
39
|
+
and message.reply_to_message_id == message.thread_id
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if message.reply_to_is_bot and not implicit_topic_reply:
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
bot_username
|
|
47
|
+
and message.reply_to_username
|
|
48
|
+
and message.reply_to_username.lower() == bot_username.lower()
|
|
49
|
+
and not implicit_topic_reply
|
|
50
|
+
):
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
return False
|
codex_autorunner/manifest.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing import Any, Dict, List, Optional, cast
|
|
|
7
7
|
import yaml
|
|
8
8
|
|
|
9
9
|
MANIFEST_VERSION = 2
|
|
10
|
+
MANIFEST_HEADER = "# GENERATED by CAR - DO NOT EDIT\n"
|
|
10
11
|
_SAFE_REPO_ID_PATTERN = re.compile(r"^[A-Za-z0-9._-]+$")
|
|
11
12
|
_SANITIZE_REPO_ID_PATTERN = re.compile(r"[^A-Za-z0-9._-]+")
|
|
12
13
|
|
|
@@ -194,4 +195,5 @@ def save_manifest(manifest_path: Path, manifest: Manifest, hub_root: Path) -> No
|
|
|
194
195
|
"repos": [repo.to_dict(hub_root) for repo in manifest.repos],
|
|
195
196
|
}
|
|
196
197
|
with manifest_path.open("w", encoding="utf-8") as f:
|
|
198
|
+
f.write(MANIFEST_HEADER)
|
|
197
199
|
yaml.safe_dump(payload, f, sort_keys=False)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Codex Autorunner plugin API metadata.
|
|
4
|
+
|
|
5
|
+
This module is intentionally small and stable. External plugins SHOULD depend
|
|
6
|
+
only on the public API in `codex_autorunner.api` + this version constant.
|
|
7
|
+
|
|
8
|
+
Notes:
|
|
9
|
+
- Backwards-incompatible changes to the plugin API MUST bump
|
|
10
|
+
`CAR_PLUGIN_API_VERSION`.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
CAR_PLUGIN_API_VERSION = 1
|
|
14
|
+
|
|
15
|
+
# Entry point groups (Python packaging entry points).
|
|
16
|
+
#
|
|
17
|
+
# Plugins can publish new agent backends by defining an entry point:
|
|
18
|
+
#
|
|
19
|
+
# [project.entry-points."codex_autorunner.agent_backends"]
|
|
20
|
+
# myagent = "my_package.my_module:AGENT_BACKEND"
|
|
21
|
+
#
|
|
22
|
+
CAR_AGENT_ENTRYPOINT_GROUP = "codex_autorunner.agent_backends"
|
|
@@ -1,67 +1,37 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"""
|
|
39
|
-
Build the complete API router by combining all route modules.
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
static_dir: Path to the static assets directory
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
Combined APIRouter with all endpoints
|
|
46
|
-
"""
|
|
47
|
-
router = APIRouter()
|
|
48
|
-
|
|
49
|
-
# Include all route modules
|
|
50
|
-
router.include_router(build_base_routes(static_dir))
|
|
51
|
-
router.include_router(build_agents_routes())
|
|
52
|
-
router.include_router(build_app_server_routes())
|
|
53
|
-
router.include_router(build_docs_routes())
|
|
54
|
-
router.include_router(build_github_routes())
|
|
55
|
-
router.include_router(build_repos_routes())
|
|
56
|
-
router.include_router(build_review_routes())
|
|
57
|
-
router.include_router(build_runs_routes())
|
|
58
|
-
router.include_router(build_sessions_routes())
|
|
59
|
-
router.include_router(build_settings_routes())
|
|
60
|
-
router.include_router(build_system_routes())
|
|
61
|
-
router.include_router(build_terminal_image_routes())
|
|
62
|
-
router.include_router(build_voice_routes())
|
|
63
|
-
|
|
64
|
-
return router
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
__all__ = ["build_repo_router"]
|
|
1
|
+
"""Backward-compatible route exports."""
|
|
2
|
+
|
|
3
|
+
from . import ( # noqa: F401
|
|
4
|
+
analytics,
|
|
5
|
+
app_server,
|
|
6
|
+
base,
|
|
7
|
+
file_chat,
|
|
8
|
+
flows,
|
|
9
|
+
messages,
|
|
10
|
+
repos,
|
|
11
|
+
review,
|
|
12
|
+
sessions,
|
|
13
|
+
settings,
|
|
14
|
+
shared,
|
|
15
|
+
system,
|
|
16
|
+
usage,
|
|
17
|
+
voice,
|
|
18
|
+
workspace,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"analytics",
|
|
23
|
+
"app_server",
|
|
24
|
+
"base",
|
|
25
|
+
"file_chat",
|
|
26
|
+
"flows",
|
|
27
|
+
"messages",
|
|
28
|
+
"repos",
|
|
29
|
+
"review",
|
|
30
|
+
"sessions",
|
|
31
|
+
"settings",
|
|
32
|
+
"shared",
|
|
33
|
+
"system",
|
|
34
|
+
"usage",
|
|
35
|
+
"voice",
|
|
36
|
+
"workspace",
|
|
37
|
+
]
|
|
@@ -1,138 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Agent harness support routes (models + event streaming).
|
|
3
|
-
"""
|
|
1
|
+
"""Backward-compatible agent routes."""
|
|
4
2
|
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
from typing import Any, Optional
|
|
8
|
-
|
|
9
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
10
|
-
from fastapi.responses import StreamingResponse
|
|
11
|
-
|
|
12
|
-
from ..agents.codex.harness import CodexHarness
|
|
13
|
-
from ..agents.opencode.harness import OpenCodeHarness
|
|
14
|
-
from ..agents.opencode.supervisor import OpenCodeSupervisorError
|
|
15
|
-
from ..agents.types import ModelCatalog
|
|
16
|
-
from .shared import SSE_HEADERS
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _available_agents(request: Request) -> tuple[list[dict[str, str]], str]:
|
|
20
|
-
agents: list[dict[str, str]] = []
|
|
21
|
-
default_agent: Optional[str] = None
|
|
22
|
-
|
|
23
|
-
if getattr(request.app.state, "app_server_supervisor", None) is not None:
|
|
24
|
-
agents.append({"id": "codex", "name": "Codex", "protocol_version": "2.0"})
|
|
25
|
-
default_agent = "codex"
|
|
26
|
-
|
|
27
|
-
if getattr(request.app.state, "opencode_supervisor", None) is not None:
|
|
28
|
-
supervisor = getattr(request.app.state, "opencode_supervisor", None)
|
|
29
|
-
version = None
|
|
30
|
-
if supervisor and hasattr(supervisor, "_handles"):
|
|
31
|
-
handles = supervisor._handles
|
|
32
|
-
if handles:
|
|
33
|
-
first_handle = next(iter(handles.values()), None)
|
|
34
|
-
if first_handle:
|
|
35
|
-
version = getattr(first_handle, "version", None)
|
|
36
|
-
agent_data = {"id": "opencode", "name": "OpenCode"}
|
|
37
|
-
if version:
|
|
38
|
-
agent_data["version"] = str(version)
|
|
39
|
-
agents.append(agent_data)
|
|
40
|
-
if default_agent is None:
|
|
41
|
-
default_agent = "opencode"
|
|
42
|
-
|
|
43
|
-
if not agents:
|
|
44
|
-
agents = [{"id": "codex", "name": "Codex", "protocol_version": "2.0"}]
|
|
45
|
-
default_agent = "codex"
|
|
46
|
-
|
|
47
|
-
return agents, default_agent or "codex"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def _serialize_model_catalog(catalog: ModelCatalog) -> dict[str, Any]:
|
|
51
|
-
return {
|
|
52
|
-
"default_model": catalog.default_model,
|
|
53
|
-
"models": [
|
|
54
|
-
{
|
|
55
|
-
"id": model.id,
|
|
56
|
-
"display_name": model.display_name,
|
|
57
|
-
"supports_reasoning": model.supports_reasoning,
|
|
58
|
-
"reasoning_options": list(model.reasoning_options),
|
|
59
|
-
}
|
|
60
|
-
for model in catalog.models
|
|
61
|
-
],
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def build_agents_routes() -> APIRouter:
|
|
66
|
-
router = APIRouter()
|
|
67
|
-
|
|
68
|
-
@router.get("/api/agents")
|
|
69
|
-
def list_agents(request: Request) -> dict[str, Any]:
|
|
70
|
-
agents, default_agent = _available_agents(request)
|
|
71
|
-
return {"agents": agents, "default": default_agent}
|
|
72
|
-
|
|
73
|
-
@router.get("/api/agents/{agent}/models")
|
|
74
|
-
async def list_agent_models(agent: str, request: Request):
|
|
75
|
-
agent_id = (agent or "").strip().lower()
|
|
76
|
-
engine = request.app.state.engine
|
|
77
|
-
if agent_id == "codex":
|
|
78
|
-
supervisor = request.app.state.app_server_supervisor
|
|
79
|
-
events = request.app.state.app_server_events
|
|
80
|
-
if supervisor is None:
|
|
81
|
-
raise HTTPException(status_code=404, detail="Codex harness unavailable")
|
|
82
|
-
codex_harness = CodexHarness(supervisor, events)
|
|
83
|
-
catalog = await codex_harness.model_catalog(engine.repo_root)
|
|
84
|
-
return _serialize_model_catalog(catalog)
|
|
85
|
-
if agent_id == "opencode":
|
|
86
|
-
supervisor = getattr(request.app.state, "opencode_supervisor", None)
|
|
87
|
-
if supervisor is None:
|
|
88
|
-
raise HTTPException(
|
|
89
|
-
status_code=404, detail="OpenCode harness unavailable"
|
|
90
|
-
)
|
|
91
|
-
try:
|
|
92
|
-
opencode_harness = OpenCodeHarness(supervisor)
|
|
93
|
-
catalog = await opencode_harness.model_catalog(engine.repo_root)
|
|
94
|
-
return _serialize_model_catalog(catalog)
|
|
95
|
-
except OpenCodeSupervisorError as exc:
|
|
96
|
-
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
97
|
-
except Exception as exc:
|
|
98
|
-
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
99
|
-
raise HTTPException(status_code=404, detail="Unknown agent")
|
|
100
|
-
|
|
101
|
-
@router.get("/api/agents/{agent}/turns/{turn_id}/events")
|
|
102
|
-
async def stream_agent_turn_events(
|
|
103
|
-
agent: str, turn_id: str, request: Request, thread_id: Optional[str] = None
|
|
104
|
-
):
|
|
105
|
-
agent_id = (agent or "").strip().lower()
|
|
106
|
-
if agent_id == "codex":
|
|
107
|
-
events = getattr(request.app.state, "app_server_events", None)
|
|
108
|
-
if events is None:
|
|
109
|
-
raise HTTPException(status_code=404, detail="Codex events unavailable")
|
|
110
|
-
if not thread_id:
|
|
111
|
-
raise HTTPException(status_code=400, detail="thread_id is required")
|
|
112
|
-
return StreamingResponse(
|
|
113
|
-
events.stream(thread_id, turn_id),
|
|
114
|
-
media_type="text/event-stream",
|
|
115
|
-
headers=SSE_HEADERS,
|
|
116
|
-
)
|
|
117
|
-
if agent_id == "opencode":
|
|
118
|
-
if not thread_id:
|
|
119
|
-
raise HTTPException(status_code=400, detail="thread_id is required")
|
|
120
|
-
supervisor = getattr(request.app.state, "opencode_supervisor", None)
|
|
121
|
-
if supervisor is None:
|
|
122
|
-
raise HTTPException(
|
|
123
|
-
status_code=404, detail="OpenCode events unavailable"
|
|
124
|
-
)
|
|
125
|
-
harness = OpenCodeHarness(supervisor)
|
|
126
|
-
return StreamingResponse(
|
|
127
|
-
harness.stream_events(
|
|
128
|
-
request.app.state.engine.repo_root, thread_id, turn_id
|
|
129
|
-
),
|
|
130
|
-
media_type="text/event-stream",
|
|
131
|
-
headers=SSE_HEADERS,
|
|
132
|
-
)
|
|
133
|
-
raise HTTPException(status_code=404, detail="Unknown agent")
|
|
134
|
-
|
|
135
|
-
return router
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
__all__ = ["build_agents_routes"]
|
|
3
|
+
from ..surfaces.web.routes.agents import * # noqa: F401,F403
|
|
@@ -1,132 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
App-server support routes (thread registry).
|
|
3
|
-
"""
|
|
1
|
+
"""Backward-compatible app-server routes."""
|
|
4
2
|
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
8
|
-
from fastapi.responses import FileResponse, StreamingResponse
|
|
9
|
-
|
|
10
|
-
from ..core.app_server_threads import normalize_feature_key
|
|
11
|
-
from ..core.utils import is_within
|
|
12
|
-
from ..integrations.app_server.client import CodexAppServerError
|
|
13
|
-
from ..web.schemas import (
|
|
14
|
-
AppServerThreadArchiveRequest,
|
|
15
|
-
AppServerThreadArchiveResponse,
|
|
16
|
-
AppServerThreadResetAllResponse,
|
|
17
|
-
AppServerThreadResetRequest,
|
|
18
|
-
AppServerThreadResetResponse,
|
|
19
|
-
AppServerThreadsResponse,
|
|
20
|
-
)
|
|
21
|
-
from .shared import SSE_HEADERS
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def build_app_server_routes() -> APIRouter:
|
|
25
|
-
router = APIRouter()
|
|
26
|
-
|
|
27
|
-
@router.get("/api/app-server/turns/{turn_id}/events")
|
|
28
|
-
async def stream_app_server_turn_events(
|
|
29
|
-
turn_id: str, request: Request, thread_id: str
|
|
30
|
-
):
|
|
31
|
-
events = getattr(request.app.state, "app_server_events", None)
|
|
32
|
-
if events is None:
|
|
33
|
-
raise HTTPException(status_code=404, detail="App-server events unavailable")
|
|
34
|
-
if not thread_id:
|
|
35
|
-
raise HTTPException(status_code=400, detail="thread_id is required")
|
|
36
|
-
return StreamingResponse(
|
|
37
|
-
events.stream(thread_id, turn_id),
|
|
38
|
-
media_type="text/event-stream",
|
|
39
|
-
headers=SSE_HEADERS,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
@router.get("/api/app-server/threads", response_model=AppServerThreadsResponse)
|
|
43
|
-
def app_server_threads(request: Request):
|
|
44
|
-
registry = request.app.state.app_server_threads
|
|
45
|
-
return registry.feature_map()
|
|
46
|
-
|
|
47
|
-
@router.get("/api/app-server/models")
|
|
48
|
-
async def app_server_models(request: Request):
|
|
49
|
-
engine = request.app.state.engine
|
|
50
|
-
supervisor = request.app.state.app_server_supervisor
|
|
51
|
-
try:
|
|
52
|
-
client = await supervisor.get_client(engine.repo_root)
|
|
53
|
-
return await client.model_list()
|
|
54
|
-
except CodexAppServerError as exc:
|
|
55
|
-
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
56
|
-
|
|
57
|
-
@router.post(
|
|
58
|
-
"/api/app-server/threads/reset", response_model=AppServerThreadResetResponse
|
|
59
|
-
)
|
|
60
|
-
def reset_app_server_thread(request: Request, payload: AppServerThreadResetRequest):
|
|
61
|
-
registry = request.app.state.app_server_threads
|
|
62
|
-
try:
|
|
63
|
-
key = normalize_feature_key(payload.key)
|
|
64
|
-
except ValueError as exc:
|
|
65
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
66
|
-
cleared = registry.reset_thread(key)
|
|
67
|
-
return {"status": "ok", "key": key, "cleared": cleared}
|
|
68
|
-
|
|
69
|
-
@router.post(
|
|
70
|
-
"/api/app-server/threads/archive",
|
|
71
|
-
response_model=AppServerThreadArchiveResponse,
|
|
72
|
-
)
|
|
73
|
-
async def archive_app_server_thread(
|
|
74
|
-
request: Request, payload: AppServerThreadArchiveRequest
|
|
75
|
-
):
|
|
76
|
-
thread_id = payload.thread_id.strip()
|
|
77
|
-
if not thread_id:
|
|
78
|
-
raise HTTPException(status_code=400, detail="thread_id is required")
|
|
79
|
-
engine = request.app.state.engine
|
|
80
|
-
supervisor = request.app.state.app_server_supervisor
|
|
81
|
-
try:
|
|
82
|
-
client = await supervisor.get_client(engine.repo_root)
|
|
83
|
-
await client.thread_archive(thread_id)
|
|
84
|
-
except CodexAppServerError as exc:
|
|
85
|
-
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
86
|
-
return {"status": "ok", "thread_id": thread_id, "archived": True}
|
|
87
|
-
|
|
88
|
-
@router.post(
|
|
89
|
-
"/api/app-server/threads/reset-all",
|
|
90
|
-
response_model=AppServerThreadResetAllResponse,
|
|
91
|
-
)
|
|
92
|
-
def reset_app_server_threads(request: Request):
|
|
93
|
-
registry = request.app.state.app_server_threads
|
|
94
|
-
registry.reset_all()
|
|
95
|
-
return {"status": "ok", "cleared": True}
|
|
96
|
-
|
|
97
|
-
@router.get("/api/app-server/threads/backup")
|
|
98
|
-
def download_app_server_threads_backup(request: Request):
|
|
99
|
-
registry = request.app.state.app_server_threads
|
|
100
|
-
notice = registry.corruption_notice() or {}
|
|
101
|
-
backup_path = notice.get("backup_path")
|
|
102
|
-
if not isinstance(backup_path, str) or not backup_path:
|
|
103
|
-
raise HTTPException(status_code=404, detail="No backup available")
|
|
104
|
-
path = Path(backup_path)
|
|
105
|
-
engine = request.app.state.engine
|
|
106
|
-
if not is_within(engine.repo_root, path):
|
|
107
|
-
raise HTTPException(status_code=400, detail="Invalid backup path")
|
|
108
|
-
if not path.exists():
|
|
109
|
-
raise HTTPException(status_code=404, detail="Backup not found")
|
|
110
|
-
return FileResponse(path, filename=path.name)
|
|
111
|
-
|
|
112
|
-
@router.get("/api/app-server/account")
|
|
113
|
-
async def app_server_account(request: Request):
|
|
114
|
-
engine = request.app.state.engine
|
|
115
|
-
supervisor = request.app.state.app_server_supervisor
|
|
116
|
-
try:
|
|
117
|
-
client = await supervisor.get_client(engine.repo_root)
|
|
118
|
-
return await client.account_read()
|
|
119
|
-
except CodexAppServerError as exc:
|
|
120
|
-
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
121
|
-
|
|
122
|
-
@router.get("/api/app-server/rate-limits")
|
|
123
|
-
async def app_server_rate_limits(request: Request):
|
|
124
|
-
engine = request.app.state.engine
|
|
125
|
-
supervisor = request.app.state.app_server_supervisor
|
|
126
|
-
try:
|
|
127
|
-
client = await supervisor.get_client(engine.repo_root)
|
|
128
|
-
return await client.rate_limits_read()
|
|
129
|
-
except CodexAppServerError as exc:
|
|
130
|
-
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
131
|
-
|
|
132
|
-
return router
|
|
3
|
+
from ..surfaces.web.routes.app_server import * # noqa: F401,F403
|