|
126 | 126 | "</mcp-server-instructions>" |
127 | 127 | "")))) |
128 | 128 |
|
129 | | -(defn build-chat-instructions [refined-contexts rules skills repo-map* agent-name config chat-id all-tools db] |
130 | | - (let [selmer-ctx (->base-selmer-ctx all-tools chat-id db)] |
| 129 | +(def ^:private volatile-context-types |
| 130 | + "Context types that change between turns and belong in the dynamic prompt block." |
| 131 | + #{:cursor :mcpResource}) |
| 132 | + |
| 133 | +(defn build-static-instructions |
| 134 | + "Builds the stable portion of the system prompt: agent prompt, rules, skills, |
| 135 | + stable contexts, and additional system info. Stable within a session — callers |
| 136 | + should cache the result in [:chats chat-id :prompt-cache :static]." |
| 137 | + [refined-contexts rules skills repo-map* agent-name config chat-id all-tools db] |
| 138 | + (let [selmer-ctx (->base-selmer-ctx all-tools chat-id db) |
| 139 | + stable-contexts (remove #(volatile-context-types (:type %)) refined-contexts)] |
131 | 140 | (multi-str |
132 | 141 | (selmer/render (eca-chat-prompt agent-name config) selmer-ctx) |
133 | 142 | (when (seq rules) |
|
152 | 161 | skills) |
153 | 162 | "</skills>" |
154 | 163 | ""]) |
155 | | - (when (seq refined-contexts) |
| 164 | + (when (seq stable-contexts) |
156 | 165 | ["## Contexts" |
157 | 166 | "" |
158 | | - (contexts-str refined-contexts repo-map* (get-in db [:chats chat-id :startup-context]))]) |
159 | | - (mcp-instructions-section db) |
| 167 | + (contexts-str stable-contexts repo-map* (get-in db [:chats chat-id :startup-context]))]) |
160 | 168 | "" |
161 | 169 | (selmer/render (load-builtin-prompt "additional_system_info.md") selmer-ctx)))) |
162 | 170 |
|
| 171 | +(defn build-dynamic-instructions |
| 172 | + "Builds the volatile portion of the system prompt: cursor/MCP resource contexts |
| 173 | + and MCP server instructions. Recomputed every turn. Returns nil when empty." |
| 174 | + [refined-contexts db] |
| 175 | + (let [volatile-contexts (filter #(volatile-context-types (:type %)) refined-contexts) |
| 176 | + result (multi-str |
| 177 | + (when (seq volatile-contexts) |
| 178 | + (contexts-str volatile-contexts nil nil)) |
| 179 | + (mcp-instructions-section db))] |
| 180 | + (when-not (string/blank? result) result))) |
| 181 | + |
| 182 | +(defn build-chat-instructions |
| 183 | + "Returns {:static \"...\" :dynamic \"...\"}. |
| 184 | + Static content (agent prompt, rules, skills, stable contexts) is stable within a session. |
| 185 | + Dynamic content (cursor, MCP resources, MCP instructions) is recomputed every turn. |
| 186 | + Callers should cache :static in [:chats chat-id :prompt-cache :static] for |
| 187 | + Anthropic API cache prefix stability across turns." |
| 188 | + [refined-contexts rules skills repo-map* agent-name config chat-id all-tools db] |
| 189 | + {:static (build-static-instructions refined-contexts rules skills repo-map* |
| 190 | + agent-name config chat-id all-tools db) |
| 191 | + :dynamic (build-dynamic-instructions refined-contexts db)}) |
| 192 | + |
| 193 | +(defn instructions->str |
| 194 | + "Flattens a {:static :dynamic} instructions map into a single string. |
| 195 | + Used by non-Anthropic providers that don't support split system prompt blocks." |
| 196 | + [{:keys [static dynamic]}] |
| 197 | + (if dynamic |
| 198 | + (multi-str static dynamic) |
| 199 | + static)) |
| 200 | + |
163 | 201 | (defn build-rewrite-instructions [text path full-text range all-tools config db] |
164 | 202 | (let [legacy-prompt-file (-> config :rewrite :systemPromptFile) |
165 | 203 | legacy-config-prompt (-> config :rewrite :systemPrompt) |
|
0 commit comments