codex-autorunner 1.1.0__py3-none-any.whl → 1.2.1__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/agents/opencode/client.py +113 -4
- codex_autorunner/agents/opencode/supervisor.py +4 -0
- codex_autorunner/agents/registry.py +17 -7
- codex_autorunner/bootstrap.py +219 -1
- codex_autorunner/core/__init__.py +17 -1
- codex_autorunner/core/about_car.py +124 -11
- codex_autorunner/core/app_server_threads.py +6 -0
- codex_autorunner/core/config.py +238 -3
- codex_autorunner/core/context_awareness.py +39 -0
- codex_autorunner/core/docs.py +0 -122
- codex_autorunner/core/filebox.py +265 -0
- codex_autorunner/core/flows/controller.py +71 -1
- codex_autorunner/core/flows/reconciler.py +4 -1
- codex_autorunner/core/flows/runtime.py +22 -0
- codex_autorunner/core/flows/store.py +61 -9
- codex_autorunner/core/flows/transition.py +23 -16
- codex_autorunner/core/flows/ux_helpers.py +18 -3
- codex_autorunner/core/flows/worker_process.py +32 -6
- codex_autorunner/core/hub.py +198 -41
- codex_autorunner/core/lifecycle_events.py +253 -0
- codex_autorunner/core/path_utils.py +2 -1
- codex_autorunner/core/pma_audit.py +224 -0
- codex_autorunner/core/pma_context.py +683 -0
- codex_autorunner/core/pma_dispatch_interceptor.py +284 -0
- codex_autorunner/core/pma_lifecycle.py +527 -0
- codex_autorunner/core/pma_queue.py +367 -0
- codex_autorunner/core/pma_safety.py +221 -0
- codex_autorunner/core/pma_state.py +115 -0
- codex_autorunner/core/ports/agent_backend.py +2 -5
- codex_autorunner/core/ports/run_event.py +1 -4
- codex_autorunner/core/prompt.py +0 -80
- codex_autorunner/core/prompts.py +56 -172
- codex_autorunner/core/redaction.py +0 -4
- codex_autorunner/core/review_context.py +11 -9
- codex_autorunner/core/runner_controller.py +35 -33
- codex_autorunner/core/runner_state.py +147 -0
- codex_autorunner/core/runtime.py +829 -0
- codex_autorunner/core/sqlite_utils.py +13 -4
- codex_autorunner/core/state.py +7 -10
- codex_autorunner/core/state_roots.py +5 -0
- codex_autorunner/core/templates/__init__.py +39 -0
- codex_autorunner/core/templates/git_mirror.py +234 -0
- codex_autorunner/core/templates/provenance.py +56 -0
- codex_autorunner/core/templates/scan_cache.py +120 -0
- codex_autorunner/core/ticket_linter_cli.py +17 -0
- codex_autorunner/core/ticket_manager_cli.py +154 -92
- codex_autorunner/core/time_utils.py +11 -0
- codex_autorunner/core/types.py +18 -0
- codex_autorunner/core/utils.py +34 -6
- codex_autorunner/flows/review/service.py +23 -25
- codex_autorunner/flows/ticket_flow/definition.py +43 -1
- codex_autorunner/integrations/agents/__init__.py +2 -0
- codex_autorunner/integrations/agents/backend_orchestrator.py +18 -0
- codex_autorunner/integrations/agents/codex_backend.py +19 -8
- codex_autorunner/integrations/agents/runner.py +3 -8
- codex_autorunner/integrations/agents/wiring.py +8 -0
- codex_autorunner/integrations/telegram/adapter.py +1 -1
- codex_autorunner/integrations/telegram/config.py +1 -1
- codex_autorunner/integrations/telegram/doctor.py +228 -6
- codex_autorunner/integrations/telegram/handlers/commands/execution.py +236 -74
- codex_autorunner/integrations/telegram/handlers/commands/files.py +314 -75
- codex_autorunner/integrations/telegram/handlers/commands/flows.py +346 -58
- codex_autorunner/integrations/telegram/handlers/commands/workspace.py +498 -37
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +202 -45
- codex_autorunner/integrations/telegram/handlers/commands_spec.py +18 -7
- codex_autorunner/integrations/telegram/handlers/messages.py +34 -3
- codex_autorunner/integrations/telegram/helpers.py +1 -3
- codex_autorunner/integrations/telegram/runtime.py +9 -4
- codex_autorunner/integrations/telegram/service.py +30 -0
- codex_autorunner/integrations/telegram/state.py +38 -0
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +10 -4
- codex_autorunner/integrations/telegram/transport.py +10 -3
- codex_autorunner/integrations/templates/__init__.py +27 -0
- codex_autorunner/integrations/templates/scan_agent.py +312 -0
- codex_autorunner/server.py +2 -2
- codex_autorunner/static/agentControls.js +21 -5
- codex_autorunner/static/app.js +115 -11
- codex_autorunner/static/archive.js +274 -81
- codex_autorunner/static/archiveApi.js +21 -0
- codex_autorunner/static/chatUploads.js +137 -0
- codex_autorunner/static/constants.js +1 -1
- codex_autorunner/static/docChatCore.js +185 -13
- codex_autorunner/static/fileChat.js +68 -40
- codex_autorunner/static/fileboxUi.js +159 -0
- codex_autorunner/static/hub.js +46 -81
- codex_autorunner/static/index.html +303 -24
- codex_autorunner/static/messages.js +82 -4
- codex_autorunner/static/notifications.js +288 -0
- codex_autorunner/static/pma.js +1167 -0
- codex_autorunner/static/settings.js +3 -0
- codex_autorunner/static/streamUtils.js +57 -0
- codex_autorunner/static/styles.css +9141 -6742
- codex_autorunner/static/templateReposSettings.js +225 -0
- codex_autorunner/static/terminalManager.js +22 -3
- codex_autorunner/static/ticketChatActions.js +165 -3
- codex_autorunner/static/ticketChatStream.js +17 -119
- codex_autorunner/static/ticketEditor.js +41 -13
- codex_autorunner/static/ticketTemplates.js +798 -0
- codex_autorunner/static/tickets.js +69 -19
- codex_autorunner/static/turnEvents.js +27 -0
- codex_autorunner/static/turnResume.js +33 -0
- codex_autorunner/static/utils.js +28 -0
- codex_autorunner/static/workspace.js +258 -44
- codex_autorunner/static/workspaceFileBrowser.js +6 -4
- codex_autorunner/surfaces/cli/cli.py +1465 -155
- codex_autorunner/surfaces/cli/pma_cli.py +817 -0
- codex_autorunner/surfaces/web/app.py +253 -49
- codex_autorunner/surfaces/web/routes/__init__.py +4 -0
- codex_autorunner/surfaces/web/routes/analytics.py +29 -22
- codex_autorunner/surfaces/web/routes/archive.py +197 -0
- codex_autorunner/surfaces/web/routes/file_chat.py +297 -36
- codex_autorunner/surfaces/web/routes/filebox.py +227 -0
- codex_autorunner/surfaces/web/routes/flows.py +219 -29
- codex_autorunner/surfaces/web/routes/messages.py +70 -39
- codex_autorunner/surfaces/web/routes/pma.py +1652 -0
- codex_autorunner/surfaces/web/routes/repos.py +1 -1
- codex_autorunner/surfaces/web/routes/shared.py +0 -3
- codex_autorunner/surfaces/web/routes/templates.py +634 -0
- codex_autorunner/surfaces/web/runner_manager.py +2 -2
- codex_autorunner/surfaces/web/schemas.py +81 -18
- codex_autorunner/tickets/agent_pool.py +27 -0
- codex_autorunner/tickets/files.py +33 -16
- codex_autorunner/tickets/lint.py +50 -0
- codex_autorunner/tickets/models.py +3 -0
- codex_autorunner/tickets/outbox.py +41 -5
- codex_autorunner/tickets/runner.py +350 -69
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.1.dist-info}/METADATA +15 -19
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.1.dist-info}/RECORD +132 -101
- codex_autorunner/core/adapter_utils.py +0 -21
- codex_autorunner/core/engine.py +0 -3302
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.1.dist-info}/WHEEL +0 -0
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.1.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.1.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.1.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,6 @@ from ...agents.opencode.run_prompt import OpenCodeRunConfig, run_opencode_prompt
|
|
|
14
14
|
from ...agents.opencode.supervisor import OpenCodeSupervisor
|
|
15
15
|
from ...agents.registry import has_capability, validate_agent_id
|
|
16
16
|
from ...core.config import RepoConfig
|
|
17
|
-
from ...core.engine import Engine
|
|
18
17
|
from ...core.locks import (
|
|
19
18
|
FileLock,
|
|
20
19
|
FileLockBusy,
|
|
@@ -22,6 +21,7 @@ from ...core.locks import (
|
|
|
22
21
|
process_alive,
|
|
23
22
|
read_lock_info,
|
|
24
23
|
)
|
|
24
|
+
from ...core.runtime import RuntimeContext
|
|
25
25
|
from ...core.state import now_iso
|
|
26
26
|
from ...core.utils import atomic_write, read_json
|
|
27
27
|
|
|
@@ -252,7 +252,7 @@ Stop digging deeper when:
|
|
|
252
252
|
"""
|
|
253
253
|
REVIEW_PROMPT_SPEC_PROGRESS = """# Autorunner Final Review (Spec + Progress Focus)
|
|
254
254
|
|
|
255
|
-
You are coordinating a multi-agent review immediately after an autorunner loop completes. The goal is to validate work against
|
|
255
|
+
You are coordinating a multi-agent review immediately after an autorunner loop completes. The goal is to validate work against spec.md, not to suggest generic code style fixes.
|
|
256
256
|
|
|
257
257
|
## Required Scratchpad + Output
|
|
258
258
|
|
|
@@ -269,16 +269,16 @@ You are coordinating a multi-agent review immediately after an autorunner loop c
|
|
|
269
269
|
|
|
270
270
|
## Source of Truth + Focus
|
|
271
271
|
|
|
272
|
-
* Read AUTORUNNER_CONTEXT.md first. It contains the exit reason,
|
|
273
|
-
* Treat
|
|
272
|
+
* Read AUTORUNNER_CONTEXT.md first. It contains the exit reason, spec.md, and last-run artifacts (output/diff/plan excerpts).
|
|
273
|
+
* Treat spec.md as the contract: extract explicit requirements, promised validation steps, and open questions.
|
|
274
274
|
* Derive must-hold invariants directly from SPEC (not generic guesses).
|
|
275
275
|
* Buckets should be anchored to spec sections mapped to implementation areas, plus any recently changed/critical modules from AUTORUNNER_CONTEXT.md.
|
|
276
276
|
|
|
277
277
|
## North Star
|
|
278
278
|
|
|
279
|
-
* Spec compliance
|
|
280
|
-
* High-signal risks and regressions that would violate
|
|
281
|
-
* Trackable output that can be turned into
|
|
279
|
+
* Spec compliance over style.
|
|
280
|
+
* High-signal risks and regressions that would violate spec commitments.
|
|
281
|
+
* Trackable output that can be turned into tickets.
|
|
282
282
|
|
|
283
283
|
## Phase 0: Setup (Coordinator)
|
|
284
284
|
|
|
@@ -288,8 +288,7 @@ Prepare scratchpad files under {{scratchpad_dir}} (see list above).
|
|
|
288
288
|
|
|
289
289
|
1) Read AUTORUNNER_CONTEXT.md and summarize:
|
|
290
290
|
* Project shape and runtime assumptions
|
|
291
|
-
*
|
|
292
|
-
* PROGRESS claims + validation evidence
|
|
291
|
+
* Spec requirements + invariants
|
|
293
292
|
* Open questions/gaps
|
|
294
293
|
2) Define buckets in BUCKETS.md:
|
|
295
294
|
* Buckets by review dimension + code areas (spec sections → code paths)
|
|
@@ -299,7 +298,6 @@ Prepare scratchpad files under {{scratchpad_dir}} (see list above).
|
|
|
299
298
|
|
|
300
299
|
Launch subagents by review dimension:
|
|
301
300
|
* Spec compliance agent: requirement → evidence mapping.
|
|
302
|
-
* Progress verification agent: PROGRESS claims → repo/tests evidence.
|
|
303
301
|
* Risk & regression agent: likely failure modes introduced by recent changes.
|
|
304
302
|
* Optional: Test adequacy agent if tests are in-scope.
|
|
305
303
|
|
|
@@ -320,8 +318,8 @@ Create the final report at {{final_output_path}} with this structure:
|
|
|
320
318
|
## Spec-to-Implementation Matrix
|
|
321
319
|
| Spec item | Status (met/partial/missing) | Evidence | Notes |
|
|
322
320
|
|
|
323
|
-
##
|
|
324
|
-
-
|
|
321
|
+
## Spec Verification
|
|
322
|
+
- Spec requirement: ... (cite spec section)
|
|
325
323
|
- Verified by: ...
|
|
326
324
|
- Not verified because: ...
|
|
327
325
|
|
|
@@ -393,19 +391,19 @@ def _default_state() -> dict[str, Any]:
|
|
|
393
391
|
class ReviewService:
|
|
394
392
|
def __init__(
|
|
395
393
|
self,
|
|
396
|
-
|
|
394
|
+
ctx: RuntimeContext,
|
|
397
395
|
*,
|
|
398
396
|
opencode_supervisor: Optional[OpenCodeSupervisor] = None,
|
|
399
397
|
app_server_supervisor: Optional[Any] = None,
|
|
400
398
|
logger: Optional[logging.Logger] = None,
|
|
401
399
|
) -> None:
|
|
402
|
-
self.
|
|
400
|
+
self.ctx = ctx
|
|
403
401
|
self._opencode_supervisor = opencode_supervisor
|
|
404
402
|
self._app_server_supervisor = app_server_supervisor
|
|
405
403
|
self._logger = logger or logging.getLogger("codex_autorunner.review")
|
|
406
|
-
self._state_path = _workflow_root(
|
|
404
|
+
self._state_path = _workflow_root(self.ctx.repo_root) / "state.json"
|
|
407
405
|
self._lock_path = (
|
|
408
|
-
|
|
406
|
+
self.ctx.repo_root / ".codex-autorunner" / "locks" / "review.lock"
|
|
409
407
|
)
|
|
410
408
|
self._thread: Optional[threading.Thread] = None
|
|
411
409
|
self._thread_lock = threading.Lock()
|
|
@@ -413,9 +411,9 @@ class ReviewService:
|
|
|
413
411
|
self._lock_handle: Optional[FileLock] = None
|
|
414
412
|
|
|
415
413
|
def _repo_config(self) -> RepoConfig:
|
|
416
|
-
if not isinstance(self.
|
|
414
|
+
if not isinstance(self.ctx.config, RepoConfig):
|
|
417
415
|
raise ReviewError("Review requires a repo workspace config")
|
|
418
|
-
return self.
|
|
416
|
+
return self.ctx.config
|
|
419
417
|
|
|
420
418
|
def status(self) -> dict[str, Any]:
|
|
421
419
|
state = self._load_state()
|
|
@@ -438,7 +436,7 @@ class ReviewService:
|
|
|
438
436
|
raise ReviewBusyError("Review already running", status_code=409)
|
|
439
437
|
if self._thread and self._thread.is_alive():
|
|
440
438
|
raise ReviewBusyError("Review already running", status_code=409)
|
|
441
|
-
busy_reason = self.
|
|
439
|
+
busy_reason = self.ctx.repo_busy_reason()
|
|
442
440
|
if busy_reason:
|
|
443
441
|
raise ReviewConflictError(
|
|
444
442
|
f"Cannot start review: {busy_reason}", status_code=409
|
|
@@ -475,7 +473,7 @@ class ReviewService:
|
|
|
475
473
|
state = self.status()
|
|
476
474
|
if state.get("status") in ("running", "stopping"):
|
|
477
475
|
raise ReviewBusyError("Review already running", status_code=409)
|
|
478
|
-
busy_reason = self.
|
|
476
|
+
busy_reason = self.ctx.repo_busy_reason()
|
|
479
477
|
if busy_reason and not ignore_repo_busy:
|
|
480
478
|
raise ReviewConflictError(
|
|
481
479
|
f"Cannot start review: {busy_reason}", status_code=409
|
|
@@ -680,7 +678,7 @@ class ReviewService:
|
|
|
680
678
|
)
|
|
681
679
|
|
|
682
680
|
run_id = state["id"]
|
|
683
|
-
runs_dir = _workflow_root(self.
|
|
681
|
+
runs_dir = _workflow_root(self.ctx.repo_root) / "runs"
|
|
684
682
|
run_dir = runs_dir / run_id
|
|
685
683
|
run_dir.mkdir(parents=True, exist_ok=True)
|
|
686
684
|
|
|
@@ -735,7 +733,7 @@ class ReviewService:
|
|
|
735
733
|
if agent_id == "codex":
|
|
736
734
|
if self._app_server_supervisor is None:
|
|
737
735
|
raise ReviewError("Codex backend is not configured")
|
|
738
|
-
client = await self._app_server_supervisor.get_client(self.
|
|
736
|
+
client = await self._app_server_supervisor.get_client(self.ctx.repo_root)
|
|
739
737
|
thread_id = uuid.uuid4().hex
|
|
740
738
|
review_kwargs: dict[str, Any] = {}
|
|
741
739
|
if state.get("model"):
|
|
@@ -746,7 +744,7 @@ class ReviewService:
|
|
|
746
744
|
thread_id=thread_id,
|
|
747
745
|
target={"type": "custom", "instructions": prompt},
|
|
748
746
|
delivery="inline",
|
|
749
|
-
cwd=str(self.
|
|
747
|
+
cwd=str(self.ctx.repo_root),
|
|
750
748
|
**review_kwargs,
|
|
751
749
|
)
|
|
752
750
|
|
|
@@ -802,7 +800,7 @@ class ReviewService:
|
|
|
802
800
|
subagent_model = review_cfg.get("subagent_model")
|
|
803
801
|
if subagent_agent_id:
|
|
804
802
|
await self._opencode_supervisor.ensure_subagent_config(
|
|
805
|
-
workspace_root=self.
|
|
803
|
+
workspace_root=self.ctx.repo_root,
|
|
806
804
|
agent_id=subagent_agent_id,
|
|
807
805
|
model=subagent_model,
|
|
808
806
|
)
|
|
@@ -812,7 +810,7 @@ class ReviewService:
|
|
|
812
810
|
model=state["model"],
|
|
813
811
|
reasoning=state.get("reasoning"),
|
|
814
812
|
prompt=prompt,
|
|
815
|
-
workspace_root=str(self.
|
|
813
|
+
workspace_root=str(self.ctx.repo_root),
|
|
816
814
|
timeout_seconds=timeout_seconds,
|
|
817
815
|
interrupt_grace_seconds=REVIEW_INTERRUPT_GRACE_SECONDS,
|
|
818
816
|
permission_policy="allow",
|
|
@@ -6,6 +6,7 @@ from typing import Any, Dict, Optional
|
|
|
6
6
|
from ...core.flows.definition import EmitEventFn, FlowDefinition, StepOutcome
|
|
7
7
|
from ...core.flows.models import FlowEventType, FlowRunRecord
|
|
8
8
|
from ...core.utils import find_repo_root
|
|
9
|
+
from ...manifest import ManifestError, load_manifest
|
|
9
10
|
from ...tickets import (
|
|
10
11
|
DEFAULT_MAX_TOTAL_TURNS,
|
|
11
12
|
AgentPool,
|
|
@@ -35,18 +36,36 @@ def build_ticket_flow_definition(*, agent_pool: AgentPool) -> FlowDefinition:
|
|
|
35
36
|
engine_state = dict(engine_state) if isinstance(engine_state, dict) else {}
|
|
36
37
|
|
|
37
38
|
repo_root = find_repo_root()
|
|
38
|
-
|
|
39
|
+
raw_workspace = input_data.get("workspace_root") or repo_root
|
|
40
|
+
workspace_root = Path(raw_workspace)
|
|
41
|
+
if not workspace_root.is_absolute():
|
|
42
|
+
workspace_root = (Path(repo_root) / workspace_root).resolve()
|
|
43
|
+
else:
|
|
44
|
+
workspace_root = workspace_root.resolve()
|
|
45
|
+
|
|
39
46
|
ticket_dir = Path(input_data.get("ticket_dir") or ".codex-autorunner/tickets")
|
|
47
|
+
if not ticket_dir.is_absolute():
|
|
48
|
+
ticket_dir = (workspace_root / ticket_dir).resolve()
|
|
49
|
+
|
|
40
50
|
runs_dir = Path(input_data.get("runs_dir") or ".codex-autorunner/runs")
|
|
51
|
+
if not runs_dir.is_absolute():
|
|
52
|
+
runs_dir = (workspace_root / runs_dir).resolve()
|
|
41
53
|
max_total_turns = int(
|
|
42
54
|
input_data.get("max_total_turns") or DEFAULT_MAX_TOTAL_TURNS
|
|
43
55
|
)
|
|
44
56
|
max_lint_retries = int(input_data.get("max_lint_retries") or 3)
|
|
45
57
|
max_commit_retries = int(input_data.get("max_commit_retries") or 2)
|
|
58
|
+
max_network_retries = int(input_data.get("max_network_retries") or 5)
|
|
46
59
|
auto_commit = bool(
|
|
47
60
|
input_data.get("auto_commit") if "auto_commit" in input_data else True
|
|
48
61
|
)
|
|
62
|
+
include_previous_ticket_context = bool(
|
|
63
|
+
input_data.get("include_previous_ticket_context")
|
|
64
|
+
if "include_previous_ticket_context" in input_data
|
|
65
|
+
else False
|
|
66
|
+
)
|
|
49
67
|
|
|
68
|
+
repo_id = _resolve_ticket_flow_repo_id(workspace_root)
|
|
50
69
|
runner = TicketRunner(
|
|
51
70
|
workspace_root=workspace_root,
|
|
52
71
|
run_id=str(record.id),
|
|
@@ -56,9 +75,12 @@ def build_ticket_flow_definition(*, agent_pool: AgentPool) -> FlowDefinition:
|
|
|
56
75
|
max_total_turns=max_total_turns,
|
|
57
76
|
max_lint_retries=max_lint_retries,
|
|
58
77
|
max_commit_retries=max_commit_retries,
|
|
78
|
+
max_network_retries=max_network_retries,
|
|
59
79
|
auto_commit=auto_commit,
|
|
80
|
+
include_previous_ticket_context=include_previous_ticket_context,
|
|
60
81
|
),
|
|
61
82
|
agent_pool=agent_pool,
|
|
83
|
+
repo_id=repo_id,
|
|
62
84
|
)
|
|
63
85
|
|
|
64
86
|
if emit_event is not None:
|
|
@@ -91,8 +113,28 @@ def build_ticket_flow_definition(*, agent_pool: AgentPool) -> FlowDefinition:
|
|
|
91
113
|
"max_total_turns": {"type": "integer"},
|
|
92
114
|
"max_lint_retries": {"type": "integer"},
|
|
93
115
|
"max_commit_retries": {"type": "integer"},
|
|
116
|
+
"max_network_retries": {"type": "integer"},
|
|
94
117
|
"auto_commit": {"type": "boolean"},
|
|
118
|
+
"include_previous_ticket_context": {"type": "boolean"},
|
|
95
119
|
},
|
|
96
120
|
},
|
|
97
121
|
steps={"ticket_turn": _ticket_turn_step},
|
|
98
122
|
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _resolve_ticket_flow_repo_id(workspace_root: Path) -> str:
|
|
126
|
+
current = workspace_root
|
|
127
|
+
for _ in range(5):
|
|
128
|
+
manifest_path = current / ".codex-autorunner" / "manifest.yml"
|
|
129
|
+
if manifest_path.exists():
|
|
130
|
+
try:
|
|
131
|
+
manifest = load_manifest(manifest_path, current)
|
|
132
|
+
except ManifestError:
|
|
133
|
+
return ""
|
|
134
|
+
entry = manifest.get_by_path(current, workspace_root)
|
|
135
|
+
return entry.id if entry else ""
|
|
136
|
+
parent = current.parent
|
|
137
|
+
if parent == current:
|
|
138
|
+
break
|
|
139
|
+
current = parent
|
|
140
|
+
return ""
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from .backend_orchestrator import build_backend_orchestrator
|
|
1
2
|
from .codex_adapter import CodexAdapterOrchestrator
|
|
2
3
|
from .codex_backend import CodexAppServerBackend
|
|
3
4
|
from .opencode_adapter import OpenCodeAdapterOrchestrator
|
|
@@ -14,4 +15,5 @@ __all__ = [
|
|
|
14
15
|
"OpenCodeBackend",
|
|
15
16
|
"build_agent_backend_factory",
|
|
16
17
|
"build_app_server_supervisor_factory",
|
|
18
|
+
"build_backend_orchestrator",
|
|
17
19
|
]
|
|
@@ -20,6 +20,9 @@ from ...core.app_server_threads import (
|
|
|
20
20
|
)
|
|
21
21
|
from ...core.config import RepoConfig
|
|
22
22
|
from ...core.ports.agent_backend import AgentBackend
|
|
23
|
+
from ...core.ports.backend_orchestrator import (
|
|
24
|
+
BackendOrchestrator as BackendOrchestratorProtocol,
|
|
25
|
+
)
|
|
23
26
|
from ...core.ports.run_event import RunEvent
|
|
24
27
|
from ...core.state import RunnerState
|
|
25
28
|
from .codex_backend import CodexAppServerBackend
|
|
@@ -278,7 +281,22 @@ class BackendOrchestrator:
|
|
|
278
281
|
return factory_fn(event_prefix, notification_handler)
|
|
279
282
|
|
|
280
283
|
|
|
284
|
+
def build_backend_orchestrator(
|
|
285
|
+
repo_root: Path, config: RepoConfig
|
|
286
|
+
) -> BackendOrchestratorProtocol:
|
|
287
|
+
"""
|
|
288
|
+
Build a BackendOrchestrator for protocol-agnostic backend management.
|
|
289
|
+
"""
|
|
290
|
+
return BackendOrchestrator(
|
|
291
|
+
repo_root=repo_root,
|
|
292
|
+
config=config,
|
|
293
|
+
notification_handler=None,
|
|
294
|
+
logger=logging.getLogger("codex_autorunner.backend"),
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
281
298
|
__all__ = [
|
|
282
299
|
"BackendOrchestrator",
|
|
283
300
|
"BackendContext",
|
|
301
|
+
"build_backend_orchestrator",
|
|
284
302
|
]
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import hashlib
|
|
2
3
|
import logging
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Any, AsyncGenerator, Awaitable, Callable, Dict, Optional, Union
|
|
5
6
|
|
|
6
7
|
from ...core.circuit_breaker import CircuitBreaker
|
|
8
|
+
from ...core.logging_utils import log_event
|
|
7
9
|
from ...core.ports.agent_backend import AgentBackend, AgentEvent, now_iso
|
|
8
10
|
from ...core.ports.run_event import (
|
|
9
11
|
ApprovalRequested,
|
|
@@ -172,10 +174,14 @@ class CodexAppServerBackend(AgentBackend):
|
|
|
172
174
|
if not self._thread_id:
|
|
173
175
|
await self.start_session(target={}, context={})
|
|
174
176
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
self.
|
|
178
|
-
|
|
177
|
+
message_hash = hashlib.sha256(message.encode()).hexdigest()[:16]
|
|
178
|
+
log_event(
|
|
179
|
+
self._logger,
|
|
180
|
+
logging.INFO,
|
|
181
|
+
"agent.turn_started",
|
|
182
|
+
thread_id=self._thread_id,
|
|
183
|
+
message_length=len(message),
|
|
184
|
+
message_hash=message_hash,
|
|
179
185
|
)
|
|
180
186
|
|
|
181
187
|
turn_kwargs: Dict[str, Any] = {}
|
|
@@ -220,10 +226,15 @@ class CodexAppServerBackend(AgentBackend):
|
|
|
220
226
|
else:
|
|
221
227
|
actual_session_id = self._thread_id
|
|
222
228
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
229
|
+
message_hash = hashlib.sha256(message.encode()).hexdigest()[:16]
|
|
230
|
+
log_event(
|
|
231
|
+
self._logger,
|
|
232
|
+
logging.INFO,
|
|
233
|
+
"agent.turn_events_started",
|
|
234
|
+
thread_id=actual_session_id,
|
|
235
|
+
turn_id=self._turn_id,
|
|
236
|
+
message_length=len(message),
|
|
237
|
+
message_hash=message_hash,
|
|
227
238
|
)
|
|
228
239
|
|
|
229
240
|
yield Started(
|
|
@@ -9,6 +9,7 @@ from ...core.ports.run_event import (
|
|
|
9
9
|
OutputDelta,
|
|
10
10
|
RunEvent,
|
|
11
11
|
Started,
|
|
12
|
+
now_iso,
|
|
12
13
|
)
|
|
13
14
|
|
|
14
15
|
_logger = logging.getLogger(__name__)
|
|
@@ -36,7 +37,7 @@ async def run_turn_with_backend(
|
|
|
36
37
|
session_id = await backend.start_session(target={}, context={})
|
|
37
38
|
|
|
38
39
|
if event_callback:
|
|
39
|
-
event_callback(Started(timestamp=
|
|
40
|
+
event_callback(Started(timestamp=now_iso(), session_id=session_id))
|
|
40
41
|
|
|
41
42
|
if log_handler:
|
|
42
43
|
log_handler(message)
|
|
@@ -64,7 +65,7 @@ async def run_turn_with_backend(
|
|
|
64
65
|
except Exception as exc:
|
|
65
66
|
_logger.error("Turn execution failed: %s", exc)
|
|
66
67
|
if event_callback:
|
|
67
|
-
event_callback(Failed(timestamp=
|
|
68
|
+
event_callback(Failed(timestamp=now_iso(), error_message=str(exc)))
|
|
68
69
|
return 1
|
|
69
70
|
|
|
70
71
|
|
|
@@ -83,9 +84,3 @@ async def stream_turn_events(
|
|
|
83
84
|
yield event
|
|
84
85
|
else:
|
|
85
86
|
yield AgentEvent.stream_delta(content="", delta_type="noop")
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def timestamp() -> str:
|
|
89
|
-
from datetime import datetime, timezone
|
|
90
|
-
|
|
91
|
-
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
@@ -192,6 +192,7 @@ class AgentBackendFactory:
|
|
|
192
192
|
max_handles=self._config.app_server.max_handles,
|
|
193
193
|
idle_ttl_seconds=self._config.app_server.idle_ttl_seconds,
|
|
194
194
|
session_stall_timeout_seconds=self._config.opencode.session_stall_timeout_seconds,
|
|
195
|
+
max_text_chars=self._config.opencode.max_text_chars,
|
|
195
196
|
base_env=None,
|
|
196
197
|
subagent_models=subagent_models,
|
|
197
198
|
)
|
|
@@ -269,3 +270,10 @@ def build_app_server_supervisor_factory(
|
|
|
269
270
|
)
|
|
270
271
|
|
|
271
272
|
return factory
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
__all__ = [
|
|
276
|
+
"AgentBackendFactory",
|
|
277
|
+
"build_agent_backend_factory",
|
|
278
|
+
"build_app_server_supervisor_factory",
|
|
279
|
+
]
|
|
@@ -1370,7 +1370,7 @@ class TelegramBotClient:
|
|
|
1370
1370
|
return bool(result) if isinstance(result, bool) else False
|
|
1371
1371
|
|
|
1372
1372
|
async def download_file(
|
|
1373
|
-
self, file_path: str, max_size_bytes: int =
|
|
1373
|
+
self, file_path: str, max_size_bytes: int = 100 * 1024 * 1024
|
|
1374
1374
|
) -> bytes:
|
|
1375
1375
|
safe_path = file_path.lstrip("/")
|
|
1376
1376
|
url = f"{self._file_base_url}/{safe_path}"
|
|
@@ -44,7 +44,7 @@ DEFAULT_APP_SERVER_TURN_TIMEOUT_SECONDS = 28800
|
|
|
44
44
|
DEFAULT_APPROVAL_TIMEOUT_SECONDS = 300.0
|
|
45
45
|
DEFAULT_MEDIA_MAX_IMAGE_BYTES = 10 * 1024 * 1024
|
|
46
46
|
DEFAULT_MEDIA_MAX_VOICE_BYTES = 10 * 1024 * 1024
|
|
47
|
-
DEFAULT_MEDIA_MAX_FILE_BYTES =
|
|
47
|
+
DEFAULT_MEDIA_MAX_FILE_BYTES = 100 * 1024 * 1024
|
|
48
48
|
DEFAULT_MEDIA_IMAGE_PROMPT = (
|
|
49
49
|
"The user sent an image with no caption. Use it to continue the "
|
|
50
50
|
"conversation; if no clear task, describe the image and ask what they want."
|