feat(api): inject folder manifest into task brief agent
Add _fetch_project_manifest helper that calls read_project_folder_manifest via execute_on_client. Wire it into run_task_brief_research_stream (new optional project_id param) so the <linked_folder> block is prepended to the system prompt when the task belongs to a linked project. Also bind FOLDER_TOOLS into the task-brief tool palette so the agent can read folder files. device_ws extracts project_id / projectId from the task_brief_request frame and forwards it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -454,10 +454,11 @@ async def _handle_task_brief_request(
|
||||
request_id = frame.get("request_id") or str(uuid4())
|
||||
session_id = frame.get("session_id") or str(uuid4())
|
||||
task_id: str = frame.get("task_id") or frame.get("taskId") or ""
|
||||
project_id: str | None = frame.get("project_id") or frame.get("projectId") or None
|
||||
|
||||
logger.info(
|
||||
"device_ws: task_brief_request_start user=%s req=%s task=%s [cache_miss]",
|
||||
user_id, request_id, task_id,
|
||||
"device_ws: task_brief_request_start user=%s req=%s task=%s project=%s [cache_miss]",
|
||||
user_id, request_id, task_id, project_id,
|
||||
)
|
||||
|
||||
if not task_id:
|
||||
@@ -486,7 +487,7 @@ async def _handle_task_brief_request(
|
||||
response_chunks: list[str] = []
|
||||
|
||||
try:
|
||||
event_stream = run_task_brief_research_stream(user_id, task_id, context)
|
||||
event_stream = run_task_brief_research_stream(user_id, task_id, context, project_id=project_id)
|
||||
formatter = StreamFormatter(request_id=request_id)
|
||||
async for ws_frame in formatter.format(event_stream):
|
||||
if ws_frame.type == "stream_text": # type: ignore[union-attr]
|
||||
|
||||
@@ -95,6 +95,21 @@ def format_folder_manifest(manifest: dict | None) -> str:
|
||||
return header + body + "</linked_folder>"
|
||||
|
||||
|
||||
async def _fetch_project_manifest(project_id: str) -> dict | None:
|
||||
"""Fetch manifest from Electron via execute_on_client. Returns None if unlinked or error."""
|
||||
from app.core.ws_context import execute_on_client
|
||||
try:
|
||||
result = await execute_on_client(
|
||||
action="read_project_folder_manifest",
|
||||
data={"projectId": project_id},
|
||||
)
|
||||
if not result or not result.get("folderPath"):
|
||||
return None
|
||||
return result
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _datetime_context_injection(context: dict[str, Any]) -> str:
|
||||
"""Build a comprehensive DATE CONTEXT block with pre-computed ms-epoch boundaries for common ranges."""
|
||||
fp = context.get("format_prefs")
|
||||
@@ -1456,6 +1471,7 @@ async def run_task_brief_research_stream(
|
||||
user_id: str,
|
||||
task_id: str,
|
||||
context: dict[str, Any],
|
||||
project_id: str | None = None,
|
||||
) -> AsyncGenerator[tuple[str, Any], None]:
|
||||
"""Stage-1 executive assistant: deep research for one task.
|
||||
|
||||
@@ -1463,8 +1479,10 @@ async def run_task_brief_research_stream(
|
||||
The final concatenated text may contain a ``<canvas kind="...">...</canvas>`` block
|
||||
which the WS handler strips and emits as a ``canvas_draft`` mutation.
|
||||
"""
|
||||
from app.agents.folder_agent import FOLDER_TOOLS
|
||||
|
||||
prepared_context = await _prepare_context(f"task:{task_id}", context)
|
||||
tools = _brief_research_tools(user_id, _trace_id_from_context(prepared_context))
|
||||
tools = [*_brief_research_tools(user_id, _trace_id_from_context(prepared_context)), *FOLDER_TOOLS]
|
||||
|
||||
# Inject task_id so the agent knows what to look up first.
|
||||
research_message = (
|
||||
@@ -1481,6 +1499,12 @@ async def run_task_brief_research_stream(
|
||||
prepared_context,
|
||||
)
|
||||
|
||||
manifest_block = ""
|
||||
if project_id:
|
||||
manifest = await _fetch_project_manifest(project_id)
|
||||
manifest_block = format_folder_manifest(manifest)
|
||||
system_prompt = system_prompt + ("\n\n" + manifest_block if manifest_block else "")
|
||||
|
||||
async for event in _run_single_agent_stream(
|
||||
user_id=user_id,
|
||||
system_prompt=system_prompt,
|
||||
|
||||
Reference in New Issue
Block a user