personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Pass day through MCP calling stack and refactor get_agent_info

- Rename _get_actor_info() to get_agent_info() returning dict with
agent_name, agent_id, and day fields
- Add day parameter to ToolLoggingHooks (google) and ToolExecutor
(anthropic) for passing via meta dict
- Add X-Agent-Day HTTP header for openai provider
- Update log_tool_action to use context day as fallback
- Update apps/chat/tools.py to use new dict-based API

MCP tools can now access the agent's operating day via
get_agent_info(context)["day"] for day-scoped operations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+56 -27
+3 -2
apps/chat/tools.py
··· 9 9 10 10 from fastmcp import Context 11 11 12 - from think.facets import _get_actor_info 12 + from think.facets import get_agent_info 13 13 from think.mcp import HINTS, register_tool 14 14 from think.utils import get_journal 15 15 ··· 66 66 title = generate_chat_title(body) 67 67 68 68 # Extract caller's agent identity from context 69 - actor, caller_agent_id = _get_actor_info(context) 69 + agent_info = get_agent_info(context) 70 + caller_agent_id = agent_info["agent_id"] 70 71 71 72 chat_record = { 72 73 "ts": int(agent_id), # agent_id is already the timestamp
+34 -21
think/facets.py
··· 114 114 return name_part 115 115 116 116 117 - def _get_actor_info(context: Context | None = None) -> tuple[str, str | None]: 118 - """Extract actor (name) and agent_id from meta or HTTP headers. 117 + def get_agent_info(context: Context | None = None) -> dict[str, Any]: 118 + """Extract agent identity from MCP context meta or HTTP headers. 119 119 120 120 Priority: meta (stdio/anthropic/google) > HTTP headers (openai) 121 121 ··· 123 123 context: Optional FastMCP context with request metadata 124 124 125 125 Returns: 126 - Tuple of (actor, agent_id) where actor defaults to "mcp" 127 - and agent_id may be None. 126 + Dictionary with keys: 127 + - agent_name: Agent name (defaults to "mcp") 128 + - agent_id: Agent ID or None 129 + - day: Day in YYYYMMDD format or None 128 130 """ 131 + result: dict[str, Any] = {"agent_name": "mcp", "agent_id": None, "day": None} 132 + 129 133 # First try meta from context (stdio transport) 130 134 if context is not None: 131 135 try: ··· 135 139 meta_dict = { 136 140 k: v for k, v in meta.model_dump().items() if v is not None 137 141 } 138 - name = meta_dict.get("name") 139 - agent_id = meta_dict.get("agent_id") 140 - if name or agent_id: 141 - actor = name if name else "mcp" 142 - return actor, agent_id 142 + if meta_dict.get("name"): 143 + result["agent_name"] = meta_dict["name"] 144 + if meta_dict.get("agent_id"): 145 + result["agent_id"] = meta_dict["agent_id"] 146 + if meta_dict.get("day"): 147 + result["day"] = meta_dict["day"] 148 + # If we got any agent info from meta, return it 149 + if result["agent_name"] != "mcp" or result["agent_id"] or result["day"]: 150 + return result 143 151 except Exception: 144 152 pass 145 153 ··· 149 157 # Normalize headers to lowercase for case-insensitive lookup 150 158 headers_lower = {k.lower(): v for k, v in headers.items()} 151 159 152 - name = headers_lower.get("x-agent-name") 153 - agent_id = headers_lower.get("x-agent-id") 154 - 155 - actor = name if name else "mcp" 156 - return actor, agent_id 160 + if headers_lower.get("x-agent-name"): 161 + result["agent_name"] = headers_lower["x-agent-name"] 162 + if headers_lower.get("x-agent-id"): 163 + result["agent_id"] = headers_lower["x-agent-id"] 164 + if headers_lower.get("x-agent-day"): 165 + result["day"] = headers_lower["x-agent-day"] 157 166 except Exception: 158 167 # Not in HTTP context (stdio, tests) 159 - return "mcp", None 168 + pass 169 + 170 + return result 160 171 161 172 162 173 def _write_action_log( ··· 231 242 """Log an agent-initiated action from an MCP tool. 232 243 233 244 Creates a JSONL log entry for tracking successful modifications made via 234 - MCP tools. Automatically extracts actor identity (agent name) from FastMCP 245 + MCP tools. Automatically extracts agent identity (agent name) from FastMCP 235 246 context. 236 247 237 248 When facet is provided, writes to facets/{facet}/logs/{day}.jsonl. ··· 243 254 action: Action type (e.g., "todo_add", "entity_attach") 244 255 params: Dictionary of action-specific parameters 245 256 context: Optional FastMCP context for extracting agent name/agent_id 246 - day: Day in YYYYMMDD format (defaults to today) 257 + day: Day in YYYYMMDD format (defaults to context day, then today) 247 258 """ 248 - actor, agent_id = _get_actor_info(context) 259 + agent_info = get_agent_info(context) 260 + # Use explicit day if provided, otherwise fall back to context day 261 + effective_day = day if day is not None else agent_info["day"] 249 262 _write_action_log( 250 263 facet=facet, 251 264 action=action, 252 265 params=params, 253 266 source="tool", 254 - actor=actor, 255 - day=day, 256 - agent_id=agent_id, 267 + actor=agent_info["agent_name"], 268 + day=effective_day, 269 + agent_id=agent_info["agent_id"], 257 270 ) 258 271 259 272
+7 -2
think/providers/anthropic.py
··· 142 142 callback: JSONEventCallback, 143 143 agent_id: str | None = None, 144 144 name: str | None = None, 145 + day: str | None = None, 145 146 ) -> None: 146 147 self.mcp = mcp_client 147 148 self.callback = callback 148 149 self.agent_id = agent_id 149 150 self.name = name 151 + self.day = day 150 152 151 153 async def execute_tool(self, tool_use: ToolUseBlock) -> dict: 152 154 """Execute ``tool_use`` and return a Claude ``tool_result`` block.""" ··· 160 162 } 161 163 ) 162 164 163 - # Build _meta dict for passing agent identity 165 + # Build _meta dict for passing agent identity and context 164 166 meta = {} 165 167 if self.agent_id: 166 168 meta["agent_id"] = self.agent_id 167 169 if self.name: 168 170 meta["name"] = self.name 171 + if self.day: 172 + meta["day"] = self.day 169 173 170 174 try: 171 175 try: ··· 271 275 continue_from = config.get("continue_from") 272 276 agent_id = config.get("agent_id") 273 277 name = config.get("name") 278 + day = config.get("day") 274 279 275 280 callback = JSONEventCallback(on_event) 276 281 ··· 311 316 312 317 tools = await _get_mcp_tools(mcp, tools_filter) 313 318 tool_executor = ToolExecutor( 314 - mcp, callback, agent_id=agent_id, name=name 319 + mcp, callback, agent_id=agent_id, name=name, day=day 315 320 ) 316 321 317 322 thinking_budget, effective_max_tokens = _resolve_agent_thinking_params(
+9 -2
think/providers/google.py
··· 475 475 writer: JSONEventCallback, 476 476 agent_id: str | None = None, 477 477 name: str | None = None, 478 + day: str | None = None, 478 479 ) -> None: 479 480 self.writer = writer 480 481 self._counter = 0 481 482 self.session = None 482 483 self.agent_id = agent_id 483 484 self.name = name 485 + self.day = day 484 486 485 487 def attach(self, session: Any) -> None: 486 488 self.session = session ··· 498 500 } 499 501 ) 500 502 501 - # Build _meta dict for passing agent identity 503 + # Build _meta dict for passing agent identity and context 502 504 meta = {} 503 505 if self.agent_id: 504 506 meta["agent_id"] = self.agent_id 505 507 if self.name: 506 508 meta["name"] = self.name 509 + if self.day: 510 + meta["day"] = self.day 507 511 508 512 result = await original( 509 513 name=name, ··· 553 557 continue_from = config.get("continue_from") 554 558 agent_id = config.get("agent_id") 555 559 name = config.get("name") 560 + day = config.get("day") 556 561 557 562 callback = JSONEventCallback(on_event) 558 563 ··· 617 622 # Create MCP client and attach hooks 618 623 async with create_mcp_client(str(mcp_server_url)) as mcp: 619 624 # Attach tool logging hooks to the MCP session 620 - tool_hooks = ToolLoggingHooks(callback, agent_id=agent_id, name=name) 625 + tool_hooks = ToolLoggingHooks( 626 + callback, agent_id=agent_id, name=name, day=day 627 + ) 621 628 tool_hooks.attach(mcp.session) 622 629 623 630 # Configure function calling mode based on tool filtering
+3
think/providers/openai.py
··· 235 235 continue_from = config.get("continue_from") 236 236 agent_id = config.get("agent_id") 237 237 name = config.get("name") 238 + day = config.get("day") 238 239 max_turns = config.get("max_turns", _DEFAULT_MAX_TURNS) 239 240 240 241 LOG.info("Running agent with model %s", model) ··· 270 271 headers["X-Agent-Id"] = str(agent_id) 271 272 if name: 272 273 headers["X-Agent-Name"] = name 274 + if day: 275 + headers["X-Agent-Day"] = day 273 276 if headers: 274 277 mcp_params["headers"] = headers 275 278