personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'hopper-oh7fmgss-cogitate-coder-mode'

# Conflicts:
# muse/coder.md
# think/call.py
# think/providers/anthropic.py
# think/providers/google.py

+132 -6
+1 -1
.codex/rules/solstone.rules
··· 5 5 prefix_rule( 6 6 pattern=["sol", "call"], 7 7 decision="allow", 8 - justification="sol call invokes read-only journal query commands", 8 + justification="sol call invokes journal query and agent handoff commands", 9 9 match=["sol call todos list", "sol call entities list"], 10 10 not_match=["sol restart-convey"], 11 11 )
+22 -1
tests/test_anthropic_cli.py
··· 17 17 18 18 19 19 def _anthropic_provider(): 20 - return importlib.import_module("think.providers.anthropic") 20 + return importlib.reload(importlib.import_module("think.providers.anthropic")) 21 + 22 + 23 + def _assert_write_mode_bypasses_restrictions(make_runner): 24 + provider = _anthropic_provider() 25 + MockCLIRunner = make_runner() 26 + with ( 27 + patch("think.providers.anthropic.CLIRunner", MockCLIRunner), 28 + patch("think.providers.anthropic.check_cli_binary"), 29 + ): 30 + asyncio.run( 31 + provider.run_cogitate( 32 + {"prompt": "hello", "model": "claude-sonnet-4", "write": True}, 33 + lambda e: None, 34 + ) 35 + ) 36 + cmd = MockCLIRunner.last_instance.cmd 37 + assert cmd[cmd.index("--permission-mode") + 1] == "bypassPermissions" 38 + assert "--allowedTools" not in cmd 21 39 22 40 23 41 @pytest.fixture ··· 391 409 cmd = MockCLIRunner.last_instance.cmd 392 410 assert cmd[cmd.index("--permission-mode") + 1] == "plan" 393 411 assert cmd[cmd.index("--allowedTools") + 1] == "Bash(sol call *)" 412 + 413 + def test_write_mode_bypasses_restrictions(self): 414 + _assert_write_mode_bypasses_restrictions(self._mock_runner)
+19 -1
tests/test_google_cli.py
··· 13 13 14 14 15 15 def _google_provider(): 16 - return importlib.import_module("think.providers.google") 16 + return importlib.reload(importlib.import_module("think.providers.google")) 17 + 18 + 19 + def _assert_write_mode_removes_allowed_tools(make_runner): 20 + provider = _google_provider() 21 + MockCLIRunner = make_runner() 22 + with patch("think.providers.google.CLIRunner", MockCLIRunner): 23 + asyncio.run( 24 + provider.run_cogitate( 25 + {"prompt": "hello", "model": "gemini-2.5-flash", "write": True}, 26 + lambda e: None, 27 + ) 28 + ) 29 + cmd = MockCLIRunner.last_instance.cmd 30 + assert "--yolo" in cmd 31 + assert "--allowed-tools" not in cmd 17 32 18 33 19 34 class TestTranslateGemini: ··· 326 341 cmd = MockCLIRunner.last_instance.cmd 327 342 assert "--yolo" in cmd 328 343 assert cmd[cmd.index("--allowed-tools") + 1] == "run_shell_command(sol call)" 344 + 345 + def test_write_mode_removes_allowed_tools(self): 346 + _assert_write_mode_removes_allowed_tools(self._mock_runner)
+56
tests/test_handoff.py
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2026 sol pbc 3 + 4 + """Tests for the handoff CLI command.""" 5 + 6 + import importlib 7 + from unittest.mock import patch 8 + 9 + from typer.testing import CliRunner 10 + 11 + runner = CliRunner() 12 + 13 + 14 + def _call_app(): 15 + call_mod = importlib.reload(importlib.import_module("think.call")) 16 + return call_mod.call_app 17 + 18 + 19 + def _invoke_handoff(*args, input_text=""): 20 + return runner.invoke(_call_app(), ["handoff", *args], input=input_text) 21 + 22 + 23 + def _assert_handoff_success(): 24 + with patch( 25 + "think.cortex_client.cortex_request", return_value="agent-123" 26 + ) as mock_cr: 27 + result = _invoke_handoff("coder", input_text="fix the bug\n") 28 + assert result.exit_code == 0 29 + assert "agent-123" in result.output 30 + mock_cr.assert_called_once_with(prompt="fix the bug", name="coder") 31 + 32 + 33 + def _assert_handoff_empty_stdin(): 34 + result = _invoke_handoff("coder", input_text="") 35 + assert result.exit_code == 1 36 + assert ( 37 + "no prompt" in result.output.lower() 38 + or "no prompt" in (result.stderr or "").lower() 39 + ) 40 + 41 + 42 + def _assert_handoff_cortex_failure(): 43 + with patch("think.cortex_client.cortex_request", return_value=None): 44 + result = _invoke_handoff("coder", input_text="fix the bug\n") 45 + assert result.exit_code == 1 46 + 47 + 48 + class TestHandoff: 49 + def test_success(self): 50 + _assert_handoff_success() 51 + 52 + def test_empty_stdin(self): 53 + _assert_handoff_empty_stdin() 54 + 55 + def test_cortex_failure(self): 56 + _assert_handoff_cortex_failure()
+32 -1
tests/test_openai.py
··· 12 12 13 13 14 14 def _openai_provider(): 15 - return importlib.import_module("think.providers.openai") 15 + return importlib.reload(importlib.import_module("think.providers.openai")) 16 + 17 + 18 + def _assert_write_mode_sandbox(): 19 + provider = _openai_provider() 20 + 21 + class MockCLIRunner: 22 + last_instance = None 23 + 24 + def __init__(self, **kwargs): 25 + self.kwargs = kwargs 26 + self.cmd = kwargs["cmd"] 27 + self.prompt_text = kwargs["prompt_text"] 28 + self.cli_session_id = "test-session-id" 29 + self.run = AsyncMock(return_value="test result") 30 + MockCLIRunner.last_instance = self 31 + 32 + with patch("think.providers.openai.CLIRunner", MockCLIRunner): 33 + asyncio.run( 34 + provider.run_cogitate( 35 + {"prompt": "hello", "model": GPT_5, "write": True}, 36 + lambda e: None, 37 + ) 38 + ) 39 + 40 + cmd = MockCLIRunner.last_instance.cmd 41 + assert "-s" in cmd 42 + s_idx = cmd.index("-s") 43 + assert cmd[s_idx + 1] == "write" 16 44 17 45 18 46 def _make_test_harness(): ··· 284 312 == f'model_reasoning_effort="{expected_effort}"' 285 313 ) 286 314 assert MockCLIRunner.last_instance.cmd[-1] == "-" 315 + 316 + def test_write_mode_sandbox(self): 317 + _assert_write_mode_sandbox() 287 318 288 319 def test_resume_command(self): 289 320 provider = _openai_provider()
+1 -2
think/providers/google.py
··· 597 597 prompt_body = system_instruction + "\n\n" + prompt_body 598 598 599 599 # Build CLI command — yolo mode auto-approves all tool calls 600 - # (required for headless subprocess use). Allowed shell commands 601 - # are constrained by --allowed-tools prefix matching. 600 + # (required for headless subprocess use). 602 601 cmd = [ 603 602 "gemini", 604 603 "-p",
+1
think/providers/openai.py
··· 180 180 sandbox = "write" if config.get("write") else "read-only" 181 181 182 182 session_id = config.get("session_id") 183 + sandbox = "write" if config.get("write") else "read-only" 183 184 if session_id: 184 185 cmd = [ 185 186 "codex",