A trust and safety agent that interacts with Osprey for investigation, real-time analysis, and prevention implementations

add additional caching layers

+78 -17
+37 -1
src/agent/agent.py
··· 69 "cache_control": {"type": "ephemeral"}, 70 } 71 ], 72 - "messages": messages, 73 } 74 75 if tools: ··· 97 content=content, 98 stop_reason=msg.stop_reason or "end_turn", # type: ignore TODO: fix this 99 ) 100 101 102 class OpenAICompatibleClient(AgentClient):
··· 69 "cache_control": {"type": "ephemeral"}, 70 } 71 ], 72 + "messages": self._inject_cache_breakpoints(messages), 73 } 74 75 if tools: ··· 97 content=content, 98 stop_reason=msg.stop_reason or "end_turn", # type: ignore TODO: fix this 99 ) 100 + 101 + @staticmethod 102 + def _inject_cache_breakpoints( 103 + messages: list[dict[str, Any]], 104 + ) -> list[dict[str, Any]]: 105 + """ 106 + a helper that adds cache_control breakpoints to the conversation so that 107 + the conversation prefix is cached across successive calls. we place a single 108 + breakpoint in th last message's content block, combined with the sys-prompt 109 + and tool defs breakpoints. ensures that we stay in the 4-breakpoint limit 110 + that ant requires 111 + """ 112 + if not messages: 113 + return messages 114 + 115 + # shallow-copy the list so we don't mutate the caller's conversation 116 + messages = list(messages) 117 + last_msg = dict(messages[-1]) 118 + content = last_msg["content"] 119 + 120 + if isinstance(content, str): 121 + last_msg["content"] = [ 122 + { 123 + "type": "text", 124 + "text": content, 125 + "cache_control": {"type": "ephemeral"}, 126 + } 127 + ] 128 + elif isinstance(content, list) and content: 129 + content = [dict(b) for b in content] 130 + content[-1] = dict(content[-1]) 131 + content[-1]["cache_control"] = {"type": "ephemeral"} 132 + last_msg["content"] = content 133 + 134 + messages[-1] = last_msg 135 + return messages 136 137 138 class OpenAICompatibleClient(AgentClient):
+21
src/agent/prompt.py
··· 208 """ 209 210 211 def build_system_prompt(): 212 """ 213 Here we put together the base system prompt for the agent. The system prompt does _not_ change based on inputs, so that proper caching across sessions can take place. ··· 226 # Osprey Documentation 227 228 {OSPREY_RULE_GUIDANCE} 229 """ 230 231 return system_prompt
··· 208 """ 209 210 211 + CLICKHOUSE_SQL_TIPS = """ 212 + # ClickHouse SQL Tips 213 + 214 + - **DateTime filtering**: The `__timestamp` column is `DateTime64(3)`. Do NOT use raw ISO strings. Use `parseDateTimeBestEffort()`: 215 + ```sql 216 + WHERE __timestamp >= parseDateTimeBestEffort('2026-02-06 04:30:00') 217 + ``` 218 + To compute a relative time in TypeScript, format it as `YYYY-MM-DD HH:MM:SS`: 219 + ```typescript 220 + const ts = new Date(Date.now() - 30 * 60 * 1000).toISOString().slice(0, 19).replace('T', ' '); 221 + ``` 222 + - **Array slicing**: ClickHouse does NOT support `array[1:5]` syntax. Use `arraySlice(array, offset, length)`: 223 + ```sql 224 + arraySlice(groupArray(DISTINCT UserId), 1, 5) as sample_accounts 225 + ``` 226 + - **Error handling**: When running multiple independent queries, use `Promise.allSettled()` instead of `Promise.all()` so one failure doesn't crash the rest. Check each result's `.status` field. 227 + """ 228 + 229 + 230 def build_system_prompt(): 231 """ 232 Here we put together the base system prompt for the agent. The system prompt does _not_ change based on inputs, so that proper caching across sessions can take place. ··· 245 # Osprey Documentation 246 247 {OSPREY_RULE_GUIDANCE} 248 + 249 + {CLICKHOUSE_SQL_TIPS} 250 """ 251 252 return system_prompt
+20
src/tools/deno/tools.ts
··· 9 query: (sql: string): Promise<unknown> => callTool("clickhouse.query", { sql }), 10 }; 11 12 export const domain = { 13 /** Lookup A, AAAA, NS, MX, TXT, CNAME, and SOA for a given input domain */ 14 checkDomain: (domain: string): Promise<unknown> => callTool("domain.checkDomain", { domain }), 15 }; 16 17 export const osprey = { ··· 41 /** Remove a moderation label from a subject (account or record) */ 42 removeLabel: (subject: string, label: string): Promise<unknown> => callTool("ozone.removeLabel", { subject, label }), 43 };
··· 9 query: (sql: string): Promise<unknown> => callTool("clickhouse.query", { sql }), 10 }; 11 12 + export const content = { 13 + /** Find similar posts in the network using ClickHouse's ngramDistance function. Useful for detecting coordinated spam, copypasta, or templated abuse content. Returns posts ordered by similarity score. */ 14 + similarity: (text: string, threshold?: number, limit?: number): Promise<unknown> => callTool("content.similarity", { text, threshold, limit }), 15 + }; 16 + 17 export const domain = { 18 /** Lookup A, AAAA, NS, MX, TXT, CNAME, and SOA for a given input domain */ 19 checkDomain: (domain: string): Promise<unknown> => callTool("domain.checkDomain", { domain }), 20 + }; 21 + 22 + export const ip = { 23 + /** GeoIP and ASN lookup for an IP address. Returns geographic location (country, region, city, coordinates, timezone), network information (ISP, org, ASN), and flags for mobile, proxy, and hosting IPs. */ 24 + lookup: (ip: string): Promise<unknown> => callTool("ip.lookup", { ip }), 25 }; 26 27 export const osprey = { ··· 51 /** Remove a moderation label from a subject (account or record) */ 52 removeLabel: (subject: string, label: string): Promise<unknown> => callTool("ozone.removeLabel", { subject, label }), 53 }; 54 + 55 + export const url = { 56 + /** Follow a URL through its redirect chain (up to 10 hops), recording each hop's URL and HTTP status code. Flags known URL shorteners. Useful for investigating obfuscated or shortened links in spam/phishing content. */ 57 + expand: (url: string): Promise<unknown> => callTool("url.expand", { url }), 58 + }; 59 + 60 + export const whois = { 61 + /** Look up WHOIS registration data for a domain. Returns registrar, creation/expiration dates, name servers, registrant info, and domain age in days. Domain age is a key T&S signal — newly registered domains are heavily used for spam and phishing. */ 62 + lookup: (domain: string): Promise<unknown> => callTool("whois.lookup", { domain }), 63 + };
-16
src/tools/executor.py
··· 302 {self._database_schema} 303 304 Use these exact column names when writing SQL queries. Do NOT guess column names. 305 - 306 - ## ClickHouse SQL Tips 307 - 308 - - **DateTime filtering**: The `__timestamp` column is `DateTime64(3)`. Do NOT use raw ISO strings. Use `parseDateTimeBestEffort()`: 309 - ```sql 310 - WHERE __timestamp >= parseDateTimeBestEffort('2026-02-06 04:30:00') 311 - ``` 312 - To compute a relative time in TypeScript, format it as `YYYY-MM-DD HH:MM:SS`: 313 - ```typescript 314 - const ts = new Date(Date.now() - 30 * 60 * 1000).toISOString().slice(0, 19).replace('T', ' '); 315 - ``` 316 - - **Array slicing**: ClickHouse does NOT support `array[1:5]` syntax. Use `arraySlice(array, offset, length)`: 317 - ```sql 318 - arraySlice(groupArray(DISTINCT UserId), 1, 5) as sample_accounts 319 - ``` 320 - - **Error handling**: When running multiple independent queries, use `Promise.allSettled()` instead of `Promise.all()` so one failure doesn't crash the rest. Check each result's `.status` field. 321 """ 322 323 osprey_section = ""
··· 302 {self._database_schema} 303 304 Use these exact column names when writing SQL queries. Do NOT guess column names. 305 """ 306 307 osprey_section = ""