7 Commits

7 changed files with 149 additions and 55 deletions
+65 -46
View File
@@ -286,37 +286,53 @@ def _list_anthropic_models() -> list[str]:
TOOL_NAME = "run_powershell" TOOL_NAME = "run_powershell"
_agent_tools: dict = {}
def set_agent_tools(tools: dict):
global _agent_tools, _CACHED_ANTHROPIC_TOOLS
_agent_tools = tools
_CACHED_ANTHROPIC_TOOLS = None
def _build_anthropic_tools() -> list[dict]: def _build_anthropic_tools() -> list[dict]:
"""Build the full Anthropic tools list: run_powershell + MCP file tools.""" """Build the full Anthropic tools list: run_powershell + MCP file tools."""
mcp_tools = [] mcp_tools = []
for spec in mcp_client.MCP_TOOL_SPECS: for spec in mcp_client.MCP_TOOL_SPECS:
mcp_tools.append({ if _agent_tools.get(spec["name"], True):
"name": spec["name"], mcp_tools.append({
"description": spec["description"], "name": spec["name"],
"input_schema": spec["parameters"], "description": spec["description"],
}) "input_schema": spec["parameters"],
powershell_tool = { })
"name": TOOL_NAME,
"description": ( tools_list = mcp_tools
"Run a PowerShell script within the project base_dir. " if _agent_tools.get(TOOL_NAME, True):
"Use this to create, edit, rename, or delete files and directories. " powershell_tool = {
"The working directory is set to base_dir automatically. " "name": TOOL_NAME,
"Always prefer targeted edits over full rewrites where possible. " "description": (
"stdout and stderr are returned to you as the result." "Run a PowerShell script within the project base_dir. "
), "Use this to create, edit, rename, or delete files and directories. "
"input_schema": { "The working directory is set to base_dir automatically. "
"type": "object", "Always prefer targeted edits over full rewrites where possible. "
"properties": { "stdout and stderr are returned to you as the result."
"script": { ),
"type": "string", "input_schema": {
"description": "The PowerShell script to execute." "type": "object",
} "properties": {
"script": {
"type": "string",
"description": "The PowerShell script to execute."
}
},
"required": ["script"]
}, },
"required": ["script"] "cache_control": {"type": "ephemeral"},
}, }
"cache_control": {"type": "ephemeral"}, tools_list.append(powershell_tool)
} elif tools_list:
return mcp_tools + [powershell_tool] # Anthropic requires the LAST tool to have cache_control for the prefix caching to work optimally on tools
tools_list[-1]["cache_control"] = {"type": "ephemeral"}
return tools_list
_ANTHROPIC_TOOLS = _build_anthropic_tools() _ANTHROPIC_TOOLS = _build_anthropic_tools()
@@ -338,6 +354,8 @@ def _gemini_tool_declaration():
# MCP file tools # MCP file tools
for spec in mcp_client.MCP_TOOL_SPECS: for spec in mcp_client.MCP_TOOL_SPECS:
if not _agent_tools.get(spec["name"], True):
continue
props = {} props = {}
for pname, pdef in spec["parameters"].get("properties", {}).items(): for pname, pdef in spec["parameters"].get("properties", {}).items():
props[pname] = types.Schema( props[pname] = types.Schema(
@@ -355,27 +373,28 @@ def _gemini_tool_declaration():
)) ))
# PowerShell tool # PowerShell tool
declarations.append(types.FunctionDeclaration( if _agent_tools.get(TOOL_NAME, True):
name=TOOL_NAME, declarations.append(types.FunctionDeclaration(
description=( name=TOOL_NAME,
"Run a PowerShell script within the project base_dir. " description=(
"Use this to create, edit, rename, or delete files and directories. " "Run a PowerShell script within the project base_dir. "
"The working directory is set to base_dir automatically. " "Use this to create, edit, rename, or delete files and directories. "
"stdout and stderr are returned to you as the result." "The working directory is set to base_dir automatically. "
), "stdout and stderr are returned to you as the result."
parameters=types.Schema( ),
type=types.Type.OBJECT, parameters=types.Schema(
properties={ type=types.Type.OBJECT,
"script": types.Schema( properties={
type=types.Type.STRING, "script": types.Schema(
description="The PowerShell script to execute." type=types.Type.STRING,
) description="The PowerShell script to execute."
}, )
required=["script"] },
), required=["script"]
)) ),
))
return types.Tool(function_declarations=declarations) return types.Tool(function_declarations=declarations) if declarations else None
def _run_script(script: str, base_dir: str) -> str: def _run_script(script: str, base_dir: str) -> str:
+1 -1
View File
@@ -4,5 +4,5 @@ This file tracks all major tracks for the project. Each track has its own detail
--- ---
- [ ] **Track: Implement context visualization and memory management improvements** - [x] **Track: Implement context visualization and memory management improvements**
*Link: [./tracks/context_management_20260223/](./tracks/context_management_20260223/)* *Link: [./tracks/context_management_20260223/](./tracks/context_management_20260223/)*
@@ -1,19 +1,19 @@
# Implementation Plan # Implementation Plan
## Phase 1: Context Memory and Token Visualization ## Phase 1: Context Memory and Token Visualization [checkpoint: a88311b]
- [x] Task: Implement token usage summary widget e34ff7e - [x] Task: Implement token usage summary widget e34ff7e
- [ ] Sub-task: Write Tests - [ ] Sub-task: Write Tests
- [ ] Sub-task: Implement Feature - [ ] Sub-task: Implement Feature
- [x] Task: Expose history truncation controls in the Discussion panel 94fe904 - [x] Task: Expose history truncation controls in the Discussion panel 94fe904
- [ ] Sub-task: Write Tests - [ ] Sub-task: Write Tests
- [ ] Sub-task: Implement Feature - [ ] Sub-task: Implement Feature
- [ ] Task: Conductor - User Manual Verification 'Phase 1: Context Memory and Token Visualization' (Protocol in workflow.md) - [x] Task: Conductor - User Manual Verification 'Phase 1: Context Memory and Token Visualization' (Protocol in workflow.md) a88311b
## Phase 2: Agent Capability Configuration ## Phase 2: Agent Capability Configuration [checkpoint: 1ac6eb9]
- [ ] Task: Add UI toggles for available tools per-project - [x] Task: Add UI toggles for available tools per-project 1677d25
- [x] Sub-task: Write Tests
- [x] Sub-task: Implement Feature
- [x] Task: Wire tool toggles to AI provider tool declaration payload 92aa33c
- [ ] Sub-task: Write Tests - [ ] Sub-task: Write Tests
- [ ] Sub-task: Implement Feature - [ ] Sub-task: Implement Feature
- [ ] Task: Wire tool toggles to AI provider tool declaration payload - [x] Task: Conductor - User Manual Verification 'Phase 2: Agent Capability Configuration' (Protocol in workflow.md) 1ac6eb9
- [ ] Sub-task: Write Tests
- [ ] Sub-task: Implement Feature
- [ ] Task: Conductor - User Manual Verification 'Phase 2: Agent Capability Configuration' (Protocol in workflow.md)
+24
View File
@@ -589,6 +589,13 @@ class App:
dpg.set_value("auto_add_history", proj.get("discussion", {}).get("auto_add", False)) dpg.set_value("auto_add_history", proj.get("discussion", {}).get("auto_add", False))
if dpg.does_item_exist("project_word_wrap"): if dpg.does_item_exist("project_word_wrap"):
dpg.set_value("project_word_wrap", proj.get("project", {}).get("word_wrap", True)) dpg.set_value("project_word_wrap", proj.get("project", {}).get("word_wrap", True))
agent_tools = proj.get("agent", {}).get("tools", {})
for t_name in ["run_powershell", "read_file", "list_directory", "search_files", "get_file_summary", "web_search", "fetch_url"]:
tag = f"tool_toggle_{t_name}"
if dpg.does_item_exist(tag):
dpg.set_value(tag, agent_tools.get(t_name, True))
self.cb_word_wrap_toggled(app_data=proj.get("project", {}).get("word_wrap", True)) self.cb_word_wrap_toggled(app_data=proj.get("project", {}).get("word_wrap", True))
def _save_active_project(self): def _save_active_project(self):
@@ -867,6 +874,13 @@ class App:
if dpg.does_item_exist("project_word_wrap"): if dpg.does_item_exist("project_word_wrap"):
proj["project"]["word_wrap"] = dpg.get_value("project_word_wrap") proj["project"]["word_wrap"] = dpg.get_value("project_word_wrap")
# Agent tools
proj.setdefault("agent", {}).setdefault("tools", {})
for t_name in ["run_powershell", "read_file", "list_directory", "search_files", "get_file_summary", "web_search", "fetch_url"]:
tag = f"tool_toggle_{t_name}"
if dpg.does_item_exist(tag):
proj["agent"]["tools"][t_name] = dpg.get_value(tag)
# Discussion # Discussion
self._flush_disc_entries_to_project() self._flush_disc_entries_to_project()
disc_sec = proj.setdefault("discussion", {}) disc_sec = proj.setdefault("discussion", {})
@@ -1189,6 +1203,7 @@ class App:
if global_sp: combined_sp.append(global_sp.strip()) if global_sp: combined_sp.append(global_sp.strip())
if project_sp: combined_sp.append(project_sp.strip()) if project_sp: combined_sp.append(project_sp.strip())
ai_client.set_custom_system_prompt("\n\n".join(combined_sp)) ai_client.set_custom_system_prompt("\n\n".join(combined_sp))
ai_client.set_agent_tools(self.project.get("agent", {}).get("tools", {}))
temp = dpg.get_value("ai_temperature") if dpg.does_item_exist("ai_temperature") else 0.0 temp = dpg.get_value("ai_temperature") if dpg.does_item_exist("ai_temperature") else 0.0
max_tok = dpg.get_value("ai_max_tokens") if dpg.does_item_exist("ai_max_tokens") else 8192 max_tok = dpg.get_value("ai_max_tokens") if dpg.does_item_exist("ai_max_tokens") else 8192
trunc = dpg.get_value("ai_history_trunc") if dpg.does_item_exist("ai_history_trunc") else 8000 trunc = dpg.get_value("ai_history_trunc") if dpg.does_item_exist("ai_history_trunc") else 8000
@@ -1699,6 +1714,15 @@ class App:
default_value=self.project.get("project", {}).get("word_wrap", True), default_value=self.project.get("project", {}).get("word_wrap", True),
callback=self.cb_word_wrap_toggled callback=self.cb_word_wrap_toggled
) )
dpg.add_separator()
dpg.add_text("Agent Capabilities")
agent_tools = self.project.get("agent", {}).get("tools", {})
for t_name in ["run_powershell", "read_file", "list_directory", "search_files", "get_file_summary", "web_search", "fetch_url"]:
dpg.add_checkbox(
tag=f"tool_toggle_{t_name}",
label=f"Enable {t_name}",
default_value=agent_tools.get(t_name, True)
)
# ---- Files panel ---- # ---- Files panel ----
with dpg.window( with dpg.window(
+11
View File
@@ -100,6 +100,17 @@ def default_project(name: str = "unnamed") -> dict:
"output": {"output_dir": "./md_gen"}, "output": {"output_dir": "./md_gen"},
"files": {"base_dir": ".", "paths": []}, "files": {"base_dir": ".", "paths": []},
"screenshots": {"base_dir": ".", "paths": []}, "screenshots": {"base_dir": ".", "paths": []},
"agent": {
"tools": {
"run_powershell": True,
"read_file": True,
"list_directory": True,
"search_files": True,
"get_file_summary": True,
"web_search": True,
"fetch_url": True
}
},
"discussion": { "discussion": {
"roles": ["User", "AI", "Vendor API", "System"], "roles": ["User", "AI", "Vendor API", "System"],
"active": "main", "active": "main",
+17
View File
@@ -0,0 +1,17 @@
import pytest
def test_agent_capabilities_config():
# A dummy test to fulfill the Red Phase for Agent Capability Configuration.
# The new function in gui.py should be get_active_tools() or we check the project dict.
from project_manager import default_project
proj = default_project("test_proj")
# We expect 'agent' config to exist in a default project and list tools
assert "agent" in proj
assert "tools" in proj["agent"]
# By default, all tools should probably be True or defined
tools = proj["agent"]["tools"]
assert "run_powershell" in tools
assert tools["run_powershell"] is True
+23
View File
@@ -0,0 +1,23 @@
import pytest
from ai_client import set_agent_tools, _build_anthropic_tools
def test_agent_tools_wiring():
# Only enable read_file and run_powershell
agent_tools = {
"run_powershell": True,
"read_file": True,
"list_directory": False,
"search_files": False,
"get_file_summary": False,
"web_search": False,
"fetch_url": False
}
set_agent_tools(agent_tools)
anth_tools = _build_anthropic_tools()
tool_names = [t["name"] for t in anth_tools]
assert "read_file" in tool_names
assert "run_powershell" in tool_names
assert "list_directory" not in tool_names
assert "web_search" not in tool_names