4848 "--permission-mode" ,
4949 "bypassPermissions" ,
5050]
51+ DEFAULT_AGENT = "codex"
52+ DEFAULT_TIMEOUT_SECONDS = 12000
5153
5254LIVE_OUTPUT_EVENT_TYPES = {
5355 "assistant" ,
@@ -237,10 +239,10 @@ class Task:
237239 created_at : Optional [str ] = None
238240 updated_at : Optional [str ] = None
239241 tags : str = "" # comma-separated
240- agent : str = "claude"
242+ agent : str = DEFAULT_AGENT
241243 question : Optional [str ] = None # question the agent asked
242244 answer : Optional [str ] = None # user's answer
243- session_id : Optional [str ] = None # claude session id for -- resume
245+ session_id : Optional [str ] = None # agent session/thread id for resume
244246 prompt_images : list = field (default_factory = list ) # [{media_type, data, name}]
245247 image_paths : list = field (default_factory = list ) # list of local image file paths
246248 dag_id : Optional [str ] = None # optional DAG workflow group label
@@ -258,7 +260,7 @@ class Heartbeat:
258260 interval_seconds : Optional [int ] = None
259261 check_prompt : str = ""
260262 action_prompt_template : str = ""
261- default_agent : str = "claude"
263+ default_agent : str = DEFAULT_AGENT
262264 cooldown_seconds : int = 0
263265 next_run_at : Optional [str ] = None
264266 last_tick_at : Optional [str ] = None
@@ -302,14 +304,14 @@ def _init_db(self):
302304 created_at TEXT DEFAULT (datetime('now')),
303305 updated_at TEXT DEFAULT (datetime('now')),
304306 tags TEXT DEFAULT '',
305- agent TEXT DEFAULT 'claude ',
307+ agent TEXT DEFAULT 'codex ',
306308 question TEXT,
307309 answer TEXT
308310 )
309311 """ )
310312 # Migration: add agent column if it doesn't exist (for existing DBs)
311313 try :
312- self .conn .execute ("ALTER TABLE tasks ADD COLUMN agent TEXT DEFAULT 'claude '" )
314+ self .conn .execute ("ALTER TABLE tasks ADD COLUMN agent TEXT DEFAULT 'codex '" )
313315 self .conn .commit ()
314316 except sqlite3 .OperationalError :
315317 pass # Column already exists
@@ -387,7 +389,7 @@ def _init_db(self):
387389 interval_seconds INTEGER,
388390 check_prompt TEXT NOT NULL,
389391 action_prompt_template TEXT DEFAULT '',
390- default_agent TEXT DEFAULT 'claude ',
392+ default_agent TEXT DEFAULT 'codex ',
391393 cooldown_seconds INTEGER DEFAULT 0,
392394 next_run_at TEXT,
393395 last_tick_at TEXT,
@@ -1597,7 +1599,10 @@ class AgentExecutor:
15971599
15981600 @staticmethod
15991601 def run (
1600- prompt : str , working_dir : str = "." , timeout : int = 600 , image_paths : list [str ] = None
1602+ prompt : str ,
1603+ working_dir : str = "." ,
1604+ timeout : int = DEFAULT_TIMEOUT_SECONDS ,
1605+ image_paths : list [str ] = None ,
16011606 ) -> tuple [bool , str ]:
16021607 """
16031608 Run a prompt through Claude Code CLI.
@@ -2021,7 +2026,7 @@ def run_skill_sweep(self, agent: Optional[str] = None, full: bool = False) -> di
20212026 agent = (
20222027 agent
20232028 or self .db .get_setting ("skill_sweep_agent" , None )
2024- or self .db .get_setting ("default_agent" , "claude" )
2029+ or self .db .get_setting ("default_agent" , DEFAULT_AGENT )
20252030 )
20262031 watermark = self .db .get_setting ("skill_sweep_watermark" , "" ) or ""
20272032 if full :
@@ -2196,7 +2201,7 @@ def distill_skill_draft(self, pattern_id: int, agent: Optional[str] = None) -> d
21962201 agent = (
21972202 agent
21982203 or self .db .get_setting ("skill_sweep_agent" , None )
2199- or self .db .get_setting ("default_agent" , "claude" )
2204+ or self .db .get_setting ("default_agent" , DEFAULT_AGENT )
22002205 )
22012206 try :
22022207 tids = json .loads (pattern ["contributing_task_ids" ]) or []
@@ -2426,7 +2431,7 @@ def _run_agent_command(
24262431 return False , f"{ agent } CLI not found"
24272432 except OSError as e :
24282433 return False , str (e )
2429- timeout_secs = int (self .db .get_setting ("timeout" , "600" ))
2434+ timeout_secs = int (self .db .get_setting ("timeout" , str ( DEFAULT_TIMEOUT_SECONDS ) ))
24302435 stdout_chunks = []
24312436 stderr_chunks = []
24322437
@@ -2549,7 +2554,7 @@ def _execute_heartbeat(self, heartbeat: dict):
25492554 interval_seconds = heartbeat .get ("interval_seconds" ),
25502555 check_prompt = heartbeat ["check_prompt" ],
25512556 action_prompt_template = heartbeat .get ("action_prompt_template" ) or "" ,
2552- default_agent = heartbeat .get ("default_agent" ) or "claude" ,
2557+ default_agent = heartbeat .get ("default_agent" ) or DEFAULT_AGENT ,
25532558 cooldown_seconds = int (heartbeat .get ("cooldown_seconds" ) or 0 ),
25542559 ),
25552560 now ,
@@ -2560,7 +2565,7 @@ def _append_tick_output(line: str):
25602565 output_chunks .append (line )
25612566 self ._live_heartbeat_output [tick_id ] = "" .join (output_chunks )
25622567
2563- agent = heartbeat .get ("default_agent" ) or "claude"
2568+ agent = heartbeat .get ("default_agent" ) or DEFAULT_AGENT
25642569 prompt = self ._render_heartbeat_check_prompt (heartbeat )
25652570 working_dir_expanded = os .path .expanduser (heartbeat ["working_dir" ])
25662571 if agent == "codex" :
@@ -2616,7 +2621,7 @@ def _append_tick_output(line: str):
26162621 prompt = task_prompt ,
26172622 working_dir = heartbeat ["working_dir" ],
26182623 schedule_type = ScheduleType .IMMEDIATE ,
2619- agent = heartbeat .get ("default_agent" ) or "claude" ,
2624+ agent = heartbeat .get ("default_agent" ) or DEFAULT_AGENT ,
26202625 tags = "heartbeat" ,
26212626 )
26222627 task_id = self .submit_task (task )
@@ -3237,7 +3242,7 @@ def _is_safe_image_path(path: str) -> bool:
32373242 except Exception as e :
32383243 logger .error (f"Task { tid } : Failed to load image { img_path } : { e } " )
32393244
3240- agent = task .get ("agent" , "claude" )
3245+ agent = task .get ("agent" ) or DEFAULT_AGENT
32413246 use_stdin = bool (prompt_images ) and agent == "claude"
32423247
32433248 if agent == "codex" :
@@ -3285,7 +3290,7 @@ def _is_safe_image_path(path: str) -> bool:
32853290 # Popen itself raises (e.g. CLI not found) before the timer is armed.
32863291 timed_out = [False ]
32873292 try :
3288- timeout_secs = int (self .db .get_setting ("timeout" , "600" ))
3293+ timeout_secs = int (self .db .get_setting ("timeout" , str ( DEFAULT_TIMEOUT_SECONDS ) ))
32893294 start_time = time .time ()
32903295 proc = subprocess .Popen (
32913296 cmd ,
@@ -3771,7 +3776,7 @@ def resume_heartbeat(self, heartbeat_id: int):
37713776 interval_seconds = heartbeat .get ("interval_seconds" ),
37723777 check_prompt = heartbeat ["check_prompt" ],
37733778 action_prompt_template = heartbeat .get ("action_prompt_template" ) or "" ,
3774- default_agent = heartbeat .get ("default_agent" ) or "claude" ,
3779+ default_agent = heartbeat .get ("default_agent" ) or DEFAULT_AGENT ,
37753780 cooldown_seconds = int (heartbeat .get ("cooldown_seconds" ) or 0 ),
37763781 ),
37773782 datetime .now (),
@@ -3936,7 +3941,7 @@ def _coerce_bool(value):
39363941 "default_agent" ,
39373942 existing .get ("default_agent" )
39383943 if existing
3939- else self .db .get_setting ("default_agent" , "claude" ),
3944+ else self .db .get_setting ("default_agent" , DEFAULT_AGENT ),
39403945 )
39413946 ),
39423947 cooldown_seconds = cooldown_seconds ,
@@ -4112,11 +4117,11 @@ def do_GET(self):
41124117 elif path == "/api/settings" :
41134118 self ._json_response (
41144119 {
4115- "default_agent" : self .db .get_setting ("default_agent" , "claude" ),
4116- "timeout" : int (self .db .get_setting ("timeout" , "600" )),
4120+ "default_agent" : self .db .get_setting ("default_agent" , DEFAULT_AGENT ),
4121+ "timeout" : int (self .db .get_setting ("timeout" , str ( DEFAULT_TIMEOUT_SECONDS ) )),
41174122 "skill_library_enabled" : self .db .get_setting ("skill_library_enabled" , "0" )
41184123 == "1" ,
4119- "skill_sweep_agent" : self .db .get_setting ("skill_sweep_agent" , "claude" ),
4124+ "skill_sweep_agent" : self .db .get_setting ("skill_sweep_agent" , DEFAULT_AGENT ),
41204125 "skill_sweep_cron" : self .db .get_setting ("skill_sweep_cron" , "0 3 * * *" ),
41214126 }
41224127 )
@@ -4361,7 +4366,7 @@ def do_POST(self):
43614366 next_run_at = body .get ("next_run_at" ), # Allow setting next_run_at directly
43624367 max_runs = body .get ("max_runs" ),
43634368 tags = body .get ("tags" , "" ),
4364- agent = body .get ("agent" ) or self .db .get_setting ("default_agent" , "claude" ),
4369+ agent = body .get ("agent" ) or self .db .get_setting ("default_agent" , DEFAULT_AGENT ),
43654370 prompt_images = prompt_images ,
43664371 image_paths = image_paths ,
43674372 dag_id = body .get ("dag_id" ),
@@ -4596,7 +4601,7 @@ def do_POST(self):
45964601 next_run_at = tdef .get ("next_run_at" ),
45974602 max_runs = tdef .get ("max_runs" ),
45984603 tags = tdef .get ("tags" , "" ),
4599- agent = tdef .get ("agent" ) or self .db .get_setting ("default_agent" , "claude" ),
4604+ agent = tdef .get ("agent" ) or self .db .get_setting ("default_agent" , DEFAULT_AGENT ),
46004605 prompt_images = prompt_images ,
46014606 dag_id = dag_id ,
46024607 )
@@ -4945,15 +4950,15 @@ def do_DELETE(self):
49454950
49464951 MAX_BODY_SIZE = 10 * 1024 * 1024 # 10 MB
49474952
4948- def _drain_body (self ):
4949- """Consume and discard the request body (capped) so an early rejection
4950- doesn't desync the keep-alive connection and surface to the client as a
4953+ def _drain_body (self , length : Optional [ int ] = None ):
4954+ """Consume and discard the request body so an early rejection doesn't
4955+ desync the keep-alive connection and surface to the client as a
49514956 connection reset instead of the intended response."""
49524957 try :
4953- length = int (self .headers .get ("Content-Length" , 0 ))
4958+ length = int (self .headers .get ("Content-Length" , 0 )) if length is None else length
49544959 except (TypeError , ValueError ):
49554960 return
4956- remaining = min (length , self . MAX_BODY_SIZE )
4961+ remaining = max (length , 0 )
49574962 while remaining > 0 :
49584963 chunk = self .rfile .read (min (remaining , 65536 ))
49594964 if not chunk :
@@ -4973,6 +4978,7 @@ def _read_body(self):
49734978 except (TypeError , ValueError ):
49744979 length = 0
49754980 if length > self .MAX_BODY_SIZE :
4981+ self ._drain_body (length )
49764982 self ._json_response ({"error" : "request body too large" }, 413 )
49774983 return None
49784984 raw = self .rfile .read (length ) if length > 0 else b""
0 commit comments