From f92fc8a71889311bae9edd48ffabbb4da9e3637a Mon Sep 17 00:00:00 2001 From: y2keui Date: Wed, 10 Jun 2026 11:20:07 +0900 Subject: [PATCH] Run Telegram turns with full access by default --- .env.example | 2 +- README.md | 17 +++++++++-------- app/bot.py | 32 +++++++++++++++++++++++--------- app/config.py | 4 ++-- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/.env.example b/.env.example index c4d8b98..6e7b0a8 100644 --- a/.env.example +++ b/.env.example @@ -17,7 +17,7 @@ CODEX_APP_SERVER_URL=ws://127.0.0.1:4500 CODEX_APP_SERVER_MODEL=gpt-5.5 CODEX_MODEL_REASONING_EFFORT=high CODEX_SPEED_MODE=standard -CODEX_READONLY_SANDBOX=read-only +CODEX_READONLY_SANDBOX=danger-full-access CODEX_WRITE_SANDBOX=danger-full-access CODEX_RESUME_JSON_FLAG_STYLE=before_resume diff --git a/README.md b/README.md index 824a12c..7713d0f 100644 --- a/README.md +++ b/README.md @@ -35,14 +35,14 @@ only keeps connection-management commands. /unarchive_session ``` -`ALLOW_PLAIN_TEXT=true` makes regular Telegram messages behave like `/ask`. -Use `/work ` for write-capable tasks such as editing files, committing, -pushing, or opening pull requests. Plain text and `/ask` stay read-oriented by -default. +`ALLOW_PLAIN_TEXT=true` makes regular Telegram messages run as full-access Codex +turns. Use `/work ` when you want to be explicit, but regular messages +and `/ask` use the same write-capable sandbox by default. -On Synology/NAS Docker deployments, keep `CODEX_WRITE_SANDBOX=danger-full-access` -for `/work`. The stricter `workspace-write` sandbox may fail with bubblewrap -namespace errors inside the container. +On Synology/NAS Docker deployments, keep `CODEX_READONLY_SANDBOX` and +`CODEX_WRITE_SANDBOX` set to `danger-full-access`. The stricter +`workspace-write` sandbox may fail with bubblewrap namespace errors inside the +container. If no `/repo` is set, the bridge tries to auto-select a workspace folder when a Telegram prompt mentions an exact folder name such as `card_game` or @@ -147,6 +147,7 @@ Example Telegram workflow: - Codex state and auth live under the mounted `codex-home` volume. - The app-server listens on `ws://127.0.0.1:4500` inside the container. -- Normal chat messages use a persistent app-server WebSocket per Telegram chat. +- Normal chat messages use full-access Codex turns with a persistent app-server + WebSocket per Telegram chat when available. - Idle persistent WebSockets are closed after `PERSISTENT_WS_IDLE_SECONDS`. - Session commands call app-server thread APIs. diff --git a/app/bot.py b/app/bot.py index c08ab67..3276c37 100644 --- a/app/bot.py +++ b/app/bot.py @@ -254,7 +254,11 @@ class TelegramCodexBot: if not prompt: await self._reply(update, "사용법: /ask <요청 내용>") return - await self._run_codex_from_update(update, prompt) + await self._run_codex_from_update( + update, + prompt, + sandbox=self.config.write_sandbox, + ) async def work(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: if not await self._guard(update): @@ -969,7 +973,7 @@ class TelegramCodexBot: ) return - selected_sandbox = sandbox or self.config.readonly_sandbox + selected_sandbox = sandbox or self.config.write_sandbox auto_repo_path = self._auto_select_repo_for_prompt(chat_id, prompt) if auto_repo_path is not None: await self._close_client_for_chat(chat_id) @@ -988,7 +992,7 @@ class TelegramCodexBot: prompt_preview=preview, sandbox=selected_sandbox, ) - mode = "쓰기 작업" if selected_sandbox != self.config.readonly_sandbox else "조회 작업" + mode = _sandbox_mode_label(selected_sandbox, self.config) repo_note = f"\n자동 작업폴더: {auto_repo_path}" if auto_repo_path else "" await self._reply( update, @@ -1205,7 +1209,7 @@ class TelegramCodexBot: result, fallback_note = await self._run_codex_for_chat( chat_id=chat_id, prompt=prompt, - sandbox=self.config.readonly_sandbox, + sandbox=self.config.write_sandbox, update_chat_state=False, session_id_override=schedule.get("thread_id") or None, ) @@ -1293,7 +1297,7 @@ class TelegramCodexBot: return "현재작업=없음" elapsed = int(time.monotonic() - job.started_monotonic) minutes, seconds = divmod(elapsed, 60) - mode = "write" if job.sandbox != self.config.readonly_sandbox else "read-only" + mode = _sandbox_mode_label(job.sandbox, self.config) return ( "현재작업=진행 중\n" f"작업모드={mode}\n" @@ -1335,9 +1339,9 @@ class TelegramCodexBot: "use the gitea_* MCP tools. Never reveal GITEA_TOKEN or print commands that embed it. " "For HTTPS git clone/fetch/push, the container may provide GIT_ASKPASS from the " "configured Gitea environment.\n" - "Only edit files, commit, or push when this turn is running with a write-capable " - "sandbox, typically from the Telegram /work command. In read-only turns, inspect and " - "explain instead of attempting file edits.\n" + "This bridge is configured for full-access turns by default. You may edit files " + "when the user asks for work that requires changes. Commit or push only when the " + "user explicitly asks for commit/push or when the request clearly includes publishing.\n" "If the user asks to schedule, remind, monitor, or notify later, use the " "telegram_create_schedule MCP tool. Use the default chat_id above and do not ask " "for a destination unless the user explicitly wants a different chat. The schedule " @@ -1376,7 +1380,7 @@ class TelegramCodexBot: def _should_use_app_server(self, sandbox: str) -> bool: if self.config.codex_backend == "exec": return False - return sandbox == self.config.readonly_sandbox + return True def _auto_select_repo_for_prompt(self, chat_id: int, prompt: str) -> Path | None: chat_state = self.state.get_chat(chat_id) @@ -1441,6 +1445,16 @@ def _display_speed(speed_mode: str) -> str: return "fast(고속)" if speed_mode == "fast" else "standard(보통)" +def _sandbox_mode_label(sandbox: str, config: Config) -> str: + if sandbox == "danger-full-access" or sandbox == config.write_sandbox: + return "full-access" + if sandbox == "workspace-write": + return "workspace-write" + if sandbox == "read-only" or sandbox == config.readonly_sandbox: + return "read-only" + return sandbox + + def _args_text(context: ContextTypes.DEFAULT_TYPE) -> str: return " ".join(context.args).strip() diff --git a/app/config.py b/app/config.py index dd8fae6..4bdf2fe 100644 --- a/app/config.py +++ b/app/config.py @@ -90,8 +90,8 @@ class Config: app_server_model=app_server_model, app_server_reasoning_effort=app_server_reasoning_effort, app_server_speed_mode=app_server_speed_mode, - readonly_sandbox=os.getenv("CODEX_READONLY_SANDBOX", "read-only"), - write_sandbox=os.getenv("CODEX_WRITE_SANDBOX", "workspace-write"), + readonly_sandbox=os.getenv("CODEX_READONLY_SANDBOX", "danger-full-access"), + write_sandbox=os.getenv("CODEX_WRITE_SANDBOX", "danger-full-access"), resume_json_flag_style=os.getenv( "CODEX_RESUME_JSON_FLAG_STYLE", "before_resume" ),