Executes tasks from the daemon's task queue using the full agent pipeline. Each task is run via run_agent() in a thread executor so the asyncio event loop stays responsive during blocking inference calls. AGENT_CONFIG is temporarily patched to suppress interactive prompts and enfo
| 53 | |
| 54 | |
| 55 | class TaskExecutor: |
| 56 | """ |
| 57 | Executes tasks from the daemon's task queue using the full agent pipeline. |
| 58 | |
| 59 | Each task is run via run_agent() in a thread executor so the asyncio event |
| 60 | loop stays responsive during blocking inference calls. AGENT_CONFIG is |
| 61 | temporarily patched to suppress interactive prompts and enforce the daemon |
| 62 | shell allowlist, then restored unconditionally in a finally block. |
| 63 | """ |
| 64 | |
| 65 | def __init__(self, state: StateStore, config: DaemonConfig): |
| 66 | self.state = state |
| 67 | self.config = config |
| 68 | self.current_task: Optional[Dict] = None |
| 69 | |
| 70 | # ------------------------------------------------------------------ |
| 71 | # Core execution — delegates to the full run_agent() pipeline |
| 72 | # ------------------------------------------------------------------ |
| 73 | |
| 74 | async def _execute_task(self, prompt: str) -> str: |
| 75 | """ |
| 76 | Execute a single task using the full run_agent() pipeline. |
| 77 | |
| 78 | Installs a daemon shell guard and disables interactive confirmations |
| 79 | for the duration of the call, then unconditionally restores the |
| 80 | previous AGENT_CONFIG values so interactive sessions are unaffected. |
| 81 | |
| 82 | The prompt should already be the enriched step string produced by |
| 83 | daemon._handle_command (includes original task + step number). |
| 84 | """ |
| 85 | start_inference() |
| 86 | try: |
| 87 | from core.agent import run_agent |
| 88 | from prompts.layered_prompt import invalidate_prompt_cache |
| 89 | |
| 90 | # Fresh file context for every step — previous steps may have |
| 91 | # written files that must appear in this step's system prompt. |
| 92 | invalidate_prompt_cache() |
| 93 | |
| 94 | # Clear working memory between daemon steps — previous step's |
| 95 | # files and turn counter bleed into this step causing stale |
| 96 | # context and premature LRU eviction of files we haven't seen yet. |
| 97 | from core.memory import memory as _mem |
| 98 | _mem.clear() |
| 99 | |
| 100 | # Save and override AGENT_CONFIG for daemon execution. |
| 101 | _saved = { |
| 102 | "_shell_fn": AGENT_CONFIG.get("_shell_fn"), |
| 103 | "confirm_shell": AGENT_CONFIG.get("confirm_shell"), |
| 104 | "confirm_write": AGENT_CONFIG.get("confirm_write"), |
| 105 | } |
| 106 | AGENT_CONFIG["_shell_fn"] = self._daemon_shell |
| 107 | AGENT_CONFIG["confirm_shell"] = False # guard is in _daemon_shell |
| 108 | AGENT_CONFIG["confirm_write"] = False # daemon writes without prompting |
| 109 | |
| 110 | try: |
| 111 | loop = asyncio.get_event_loop() |
| 112 | response, _ = await loop.run_in_executor( |
no outgoing calls
no test coverage detected