fix(runtime): refresh prompt for permission mode changes

Inject the active permission mode into the runtime system prompt so plan/default/full-auto state is visible to the model.

Refresh the engine model, effort, permission checker, and system prompt when runtime-affecting slash commands update settings.

Based-on: #268
This commit is contained in:
Mcy0618
2026-05-24 09:10:10 +00:00
committed by tjb-tech
parent 4c3bf5bbc9
commit 033dce67b1
4 changed files with 102 additions and 0 deletions
+25
View File
@@ -16,6 +16,7 @@ from openharness.memory import load_memory_prompt
from openharness.memory.relevance import format_relevant_memories, select_relevant_memories
from openharness.memory.usage import mark_memory_used
from openharness.personalization.rules import load_local_rules
from openharness.permissions.modes import PermissionMode
from openharness.prompts.claudemd import load_claude_md_prompt
from openharness.prompts.system_prompt import build_system_prompt
from openharness.skills.loader import load_skill_registry
@@ -76,6 +77,28 @@ def _build_delegation_section() -> str:
)
def _build_permission_mode_section(settings: Settings) -> str:
"""Build current permission-mode guidance for the model."""
mode = settings.permission.mode
if mode == PermissionMode.PLAN:
guidance = (
"Plan mode is enabled. Treat this session as read-only planning and analysis. "
"Do not call mutating tools such as file writes, edits, package installs, "
"state-changing shell commands, or task-spawning actions unless the user exits plan mode."
)
elif mode == PermissionMode.FULL_AUTO:
guidance = (
"Full-auto permission mode is enabled. You may use mutating tools when they are necessary "
"for the user's request, while still keeping changes scoped and intentional."
)
else:
guidance = (
"Default permission mode is enabled. Read-only tools can run directly; mutating tools "
"may require explicit user approval."
)
return f"# Current Permission Mode\n{guidance}"
def build_runtime_system_prompt(
settings: Settings,
*,
@@ -94,6 +117,8 @@ def build_runtime_system_prompt(
if not is_coordinator_mode() and settings.system_prompt is None:
sections[0] = build_system_prompt(cwd=str(cwd))
sections.append(_build_permission_mode_section(settings))
if settings.fast_mode:
sections.append(
"# Session Mode\nFast mode is enabled. Prefer concise replies, minimal tool use, and quicker progress over exhaustive exploration."
+11
View File
@@ -601,6 +601,17 @@ def refresh_runtime_client(bundle: RuntimeBundle) -> None:
default_model=settings.model,
)
bundle.engine.set_model(settings.model)
bundle.engine.set_effort(settings.effort)
bundle.engine.set_permission_checker(PermissionChecker(settings.permission))
system_prompt = build_runtime_system_prompt(
settings,
cwd=bundle.cwd,
latest_user_prompt=_last_user_text(bundle.engine.messages),
extra_skill_dirs=bundle.extra_skill_dirs,
extra_plugin_roots=bundle.extra_plugin_roots,
include_project_memory=bundle.include_project_memory,
)
bundle.engine.set_system_prompt(system_prompt)
sync_app_state(bundle)
+17
View File
@@ -58,6 +58,23 @@ def test_build_runtime_system_prompt_combines_sections(tmp_path: Path, monkeypat
assert "Memory" in prompt
def test_build_runtime_system_prompt_includes_plan_mode_guidance(tmp_path: Path, monkeypatch):
monkeypatch.setenv("OPENHARNESS_DATA_DIR", str(tmp_path / "data"))
repo = tmp_path / "repo"
repo.mkdir()
prompt = build_runtime_system_prompt(
Settings(permission={"mode": "plan"}),
cwd=repo,
latest_user_prompt="inspect only",
)
assert "Current Permission Mode" in prompt
assert "Plan mode is enabled" in prompt
assert "read-only planning and analysis" in prompt
assert "Do not call mutating tools" in prompt
def test_build_runtime_system_prompt_includes_project_context_and_fast_mode(tmp_path: Path, monkeypatch):
monkeypatch.setenv("OPENHARNESS_DATA_DIR", str(tmp_path / "data"))
repo = tmp_path / "repo"
+49
View File
@@ -0,0 +1,49 @@
from __future__ import annotations
from pathlib import Path
import pytest
from openharness.ui.runtime import build_runtime, close_runtime, handle_line
class _StaticApiClient:
async def stream_message(self, request):
del request
if False:
yield None
@pytest.mark.asyncio
async def test_plan_command_refreshes_engine_system_prompt(tmp_path: Path, monkeypatch):
monkeypatch.setenv("OPENHARNESS_CONFIG_DIR", str(tmp_path / "config"))
monkeypatch.setenv("OPENHARNESS_DATA_DIR", str(tmp_path / "data"))
monkeypatch.setenv("OPENHARNESS_LOGS_DIR", str(tmp_path / "logs"))
bundle = await build_runtime(cwd=str(tmp_path), api_client=_StaticApiClient())
try:
assert "Plan mode is enabled" not in bundle.engine.system_prompt
async def print_system(text: str) -> None:
del text
async def render_event(event) -> None:
del event
async def clear_output() -> None:
pass
should_continue = await handle_line(
bundle,
"/plan on",
print_system=print_system,
render_event=render_event,
clear_output=clear_output,
)
assert should_continue is True
assert bundle.app_state.get().permission_mode == "plan"
assert "Plan mode is enabled" in bundle.engine.system_prompt
assert "Do not call mutating tools" in bundle.engine.system_prompt
finally:
await close_runtime(bundle)