33import pathlib
44from collections .abc import Mapping
55from datetime import datetime
6- from typing import Any
6+ from typing import Any , NamedTuple
77
88from pydantic import (
99 BaseModel ,
3939PROMPT_DIR = pathlib .Path (__file__ ).parent / "prompts" / "templates"
4040
4141
42+ class ResolvedDynamicData (NamedTuple ):
43+ """Dynamic-tier inputs resolved once, shared by the legacy ``.j2`` renderer and
44+ the section registry (skills gated by model family, secrets merged)."""
45+
46+ repo_skills : list [Skill ]
47+ available_skills_prompt : str
48+ secret_infos : list [dict [str , str | None ]]
49+ formatted_datetime : str | None
50+
51+
4252class AgentContext (BaseModel ):
4353 """Central structure for managing prompt extension.
4454
@@ -300,6 +310,40 @@ def get_system_message_suffix(
300310 - Legacy with trigger=None: Full content in <REPO_CONTEXT> (always active)
301311 - Legacy with triggers: Listed in <available_skills>, injected on trigger
302312 """
313+ data = self ._resolve_dynamic_data (
314+ llm_model , llm_model_canonical , additional_secret_infos
315+ )
316+ has_content = (
317+ data .repo_skills
318+ or self .system_message_suffix
319+ or data .secret_infos
320+ or data .available_skills_prompt
321+ or data .formatted_datetime
322+ )
323+ if has_content :
324+ formatted_text = render_template (
325+ prompt_dir = str (PROMPT_DIR ),
326+ template_name = "system_message_suffix.j2" ,
327+ repo_skills = data .repo_skills ,
328+ system_message_suffix = self .system_message_suffix or "" ,
329+ secret_infos = data .secret_infos ,
330+ available_skills_prompt = data .available_skills_prompt ,
331+ current_datetime = data .formatted_datetime ,
332+ ).strip ()
333+ return formatted_text
334+ elif self .system_message_suffix and self .system_message_suffix .strip ():
335+ return self .system_message_suffix .strip ()
336+ return None
337+
338+ def _resolve_dynamic_data (
339+ self ,
340+ llm_model : str | None = None ,
341+ llm_model_canonical : str | None = None ,
342+ additional_secret_infos : list [dict [str , str | None ]] | None = None ,
343+ ) -> ResolvedDynamicData :
344+ """Resolve the dynamic-tier inputs shared by :meth:`get_system_message_suffix`
345+ and the section registry: model-gated repo skills, the available-skills
346+ prompt, merged secret infos, and the formatted datetime. Pure (no render)."""
303347 repo_skills , available_skills = self ._partition_skills ()
304348
305349 # Gate vendor-specific repo skills based on model family.
@@ -323,45 +367,28 @@ def get_system_message_suffix(
323367
324368 logger .debug (f"Loaded { len (repo_skills )} repository skills: { repo_skills } " )
325369
326- # Generate available skills prompt
327370 available_skills_prompt = ""
328371 if available_skills :
329372 available_skills_prompt = to_prompt (available_skills )
330373 logger .debug (
331374 f"Generated available skills prompt for { len (available_skills )} skills"
332375 )
333376
334- # Build the workspace context information
335- # Merge agent_context secrets with additional secrets from registry
377+ # Merge agent_context secrets with additional secrets from the registry
378+ # ( additional override by name).
336379 secret_infos = self .get_secret_infos ()
337380 if additional_secret_infos :
338- # Merge: additional secrets override agent_context secrets by name
339381 secret_dict = {s ["name" ]: s for s in secret_infos }
340382 for additional in additional_secret_infos :
341383 secret_dict [additional ["name" ]] = additional
342384 secret_infos = list (secret_dict .values ())
343- formatted_datetime = self .get_formatted_datetime ()
344- has_content = (
345- repo_skills
346- or self .system_message_suffix
347- or secret_infos
348- or available_skills_prompt
349- or formatted_datetime
385+
386+ return ResolvedDynamicData (
387+ repo_skills = repo_skills ,
388+ available_skills_prompt = available_skills_prompt ,
389+ secret_infos = secret_infos ,
390+ formatted_datetime = self .get_formatted_datetime (),
350391 )
351- if has_content :
352- formatted_text = render_template (
353- prompt_dir = str (PROMPT_DIR ),
354- template_name = "system_message_suffix.j2" ,
355- repo_skills = repo_skills ,
356- system_message_suffix = self .system_message_suffix or "" ,
357- secret_infos = secret_infos ,
358- available_skills_prompt = available_skills_prompt ,
359- current_datetime = formatted_datetime ,
360- ).strip ()
361- return formatted_text
362- elif self .system_message_suffix and self .system_message_suffix .strip ():
363- return self .system_message_suffix .strip ()
364- return None
365392
366393 def validate_acp_compatibility (self ) -> None :
367394 """Raise if this context uses fields unsupported by ACP prompt mode.
0 commit comments