feat(terminal): tint terminal background per agent color
Add backend-agnostic terminal color support via OSC escape sequences: - New backend/terminal.py with palette_printf() and exec_shell_script() shared by both Docker and smolmachines bottle backends - Emits OSC 4 (indexed palette) + OSC 11 (default background tint) before launching; resets both on agent exit via OSC 104/111 - OSC 11 background tint is visible even when the TUI uses true/24-bit colors (which bypass the palette), as Codex does for its chrome - Fix Codex [tui] config: status_line=["model-with-reasoning"], theme="ansi" (dark-ansi and cwd/directory were invalid identifiers) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -80,7 +80,7 @@ class TestAgentProviderRuntime(unittest.TestCase):
|
||||
self.assertTrue(plan.has_prompt)
|
||||
self.assertEqual("Existing instructions.\n", prompt)
|
||||
self.assertIn("[tui]", config)
|
||||
self.assertIn('status_line = ["model", "cwd"]', config)
|
||||
self.assertIn('status_line = ["model-with-reasoning"]', config)
|
||||
self.assertIn('terminal_title = ["spinner", "project"]', config)
|
||||
|
||||
def test_codex_forward_host_credentials_adds_auth_and_verify(self):
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
"""Unit tests for backend/terminal.py palette and shell-script helpers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from bot_bottle.backend.terminal import exec_shell_script, palette_printf
|
||||
|
||||
|
||||
class TestPalettePrintf(unittest.TestCase):
|
||||
def test_known_color_returns_printf(self):
|
||||
cmd = palette_printf("red")
|
||||
self.assertTrue(cmd.startswith("printf '"))
|
||||
self.assertIn("\\033]4;1;", cmd) # normal red
|
||||
self.assertIn("\\033]4;9;", cmd) # bright red
|
||||
self.assertIn("\\033]11;", cmd) # default background tint
|
||||
|
||||
def test_bright_variant_sets_both_slots(self):
|
||||
cmd = palette_printf("bright-blue")
|
||||
self.assertIn("\\033]4;12;", cmd) # bright-blue
|
||||
self.assertIn("\\033]4;4;", cmd) # blue
|
||||
|
||||
def test_unknown_color_returns_empty(self):
|
||||
self.assertEqual("", palette_printf(""))
|
||||
self.assertEqual("", palette_printf("neon-pink"))
|
||||
|
||||
def test_all_named_colors_produce_output(self):
|
||||
colors = [
|
||||
"black", "red", "green", "yellow",
|
||||
"blue", "magenta", "cyan", "white",
|
||||
"bright-black", "bright-red", "bright-green", "bright-yellow",
|
||||
"bright-blue", "bright-magenta", "bright-cyan", "bright-white",
|
||||
]
|
||||
for color in colors:
|
||||
with self.subTest(color=color):
|
||||
self.assertTrue(palette_printf(color))
|
||||
|
||||
|
||||
class TestExecShellScript(unittest.TestCase):
|
||||
_ARGV = ["smolvm", "machine", "exec", "--name", "x", "--", "claude"]
|
||||
|
||||
def test_no_decoration_returns_none(self):
|
||||
self.assertIsNone(exec_shell_script(self._ARGV))
|
||||
self.assertIsNone(exec_shell_script(self._ARGV, terminal_title="", terminal_color=""))
|
||||
|
||||
def test_title_only_uses_exec(self):
|
||||
script = exec_shell_script(self._ARGV, terminal_title="my-agent")
|
||||
assert script is not None
|
||||
self.assertIn("printf", script)
|
||||
self.assertIn("my-agent", script)
|
||||
self.assertIn("exec ", script)
|
||||
# No palette reset when there's no color
|
||||
self.assertNotIn("\\033]104", script)
|
||||
|
||||
def test_color_only_sets_palette_and_resets(self):
|
||||
script = exec_shell_script(self._ARGV, terminal_color="green")
|
||||
assert script is not None
|
||||
self.assertIn("\\033]4;", script) # indexed palette
|
||||
self.assertIn("\\033]11;", script) # background tint
|
||||
self.assertIn("\\033]104", script) # palette reset
|
||||
self.assertIn("\\033]111", script) # background reset
|
||||
# No exec-replace when palette is active (shell must survive for reset)
|
||||
parts = script.split("; ")
|
||||
agent_part = next(p for p in parts if "smolvm" in p)
|
||||
self.assertFalse(agent_part.startswith("exec "))
|
||||
|
||||
def test_title_and_color_both_appear(self):
|
||||
script = exec_shell_script(self._ARGV, terminal_title="bot", terminal_color="cyan")
|
||||
assert script is not None
|
||||
self.assertIn("bot", script)
|
||||
self.assertIn("\\033]4;", script)
|
||||
self.assertIn("\\033]11;", script)
|
||||
self.assertIn("\\033]104", script)
|
||||
self.assertIn("\\033]111", script)
|
||||
|
||||
def test_title_with_special_chars_is_quoted(self):
|
||||
script = exec_shell_script(self._ARGV, terminal_title="my agent's label")
|
||||
assert script is not None
|
||||
self.assertNotIn("my agent's label", script) # must be shell-quoted
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -165,9 +165,9 @@ class TestCodexProvisionPrompt(unittest.TestCase):
|
||||
self.assertTrue(plan.has_prompt)
|
||||
self.assertEqual("Existing instructions.\n", prompt_text)
|
||||
self.assertIn("[tui]", config)
|
||||
self.assertIn('status_line = ["model", "cwd"]', config)
|
||||
self.assertIn('status_line = ["model-with-reasoning"]', config)
|
||||
self.assertIn('terminal_title = ["spinner", "project"]', config)
|
||||
self.assertIn('theme = "dark-ansi"', config)
|
||||
self.assertIn('theme = "ansi"', config)
|
||||
|
||||
|
||||
class TestCodexProvisionSkills(unittest.TestCase):
|
||||
|
||||
Reference in New Issue
Block a user