tool_context

Shared context object passed to tools that need platform access.

Tools opt-in to receiving context by declaring a ctx parameter in their run() signature. The ToolRegistry inspects the handler at call-time and injects the context automatically – the LLM never sees or fills this parameter.

class tool_context.ToolCallRecord(record_id=<factory>, tool_name='', raw_arguments_json='', result_output='', success=True, execution_start=0.0, execution_end=0.0, duration_ms=0.0, order_index=0, round_number=0, turn_summary_id='')[source]

Bases: object

One tool call’s full execution trace.

Stored as a hidden Redis message after each inference turn so the agent can retrieve exact inputs/outputs on demand via the retrieve_tool_call_log tool.

Parameters:
  • record_id (str)

  • tool_name (str)

  • raw_arguments_json (str)

  • result_output (str)

  • success (bool)

  • execution_start (float)

  • execution_end (float)

  • duration_ms (float)

  • order_index (int)

  • round_number (int)

  • turn_summary_id (str)

record_id: str

Unique ID for this individual record (used as Redis key suffix).

tool_name: str = ''

Name of the tool that was called.

raw_arguments_json: str = ''

Exact JSON payload generated by the LLM (before parsing).

result_output: str = ''

Full execution output returned by the tool handler.

success: bool = True

Whether the tool call completed without error.

execution_start: float = 0.0

Wall-clock time.time() when execution began.

execution_end: float = 0.0

Wall-clock time.time() when execution finished.

duration_ms: float = 0.0

Elapsed wall-clock milliseconds.

order_index: int = 0

Sequential index within the turn (global across all rounds).

round_number: int = 0

Which tool-call round in the chat() loop (0-indexed).

turn_summary_id: str = ''

Back-reference to the parent Stargazer-System-Log summary (set after write).

class tool_context.ToolContext(platform='', channel_id='', user_id='', user_name='', guild_id='', adapter=None, message_id='', config=None, redis=None, message_cache=None, kg_manager=None, task_manager=None, threadweave=None, tool_registry=None, conversation_manager=None, openrouter=None, all_adapters=<factory>, adapters_by_name=<factory>, disclosed_skill_ids=<factory>, injected_tools=None, injected_tools_session=<factory>, sent_files=<factory>, sent_rich_messages=<factory>, tools_executed=<factory>, observability_request_id='', room_context=None, tool_call_records=<factory>, persona_pref_manager=None, visual_memory=None)[source]

Bases: object

Runtime context injected into tool handlers that opt in to it.

A single mutable bag of per-message state and shared service handles that the tool-execution layer hands to any tool whose run() declares a ctx parameter. It exists so tools can reach platform adapters, Redis, the knowledge graph, the conversation/message caches, the live OpenRouterClient, and sibling managers without importing them directly (which would create circular imports) and without the LLM ever seeing these fields — the registry inspects the handler signature and injects this object behind the model’s back.

Beyond carrying read-only handles, several fields are write-back channels: tool handlers append to injected_tools, sent_files, sent_rich_messages, and tools_executed during a round, and OpenRouterClient.chat() reads/clears them between rounds to merge newly injected tools and to surface media the bot emitted. Because parallel tools in one model round share the same instance and run under asyncio.gather, these mutations must stay append-only/atomic. Instances are constructed by the message-processing paths that drive tool calls — e.g. message_processor/generate_and_send.py, message_processor/channel_heartbeat.py, core/outbound_consumer.py, and kg_agentic_extraction.py — and by the swarm/test harnesses.

The individual fields are documented by their per-attribute docstrings below.

Parameters:
platform: str = ''

Short identifier for the originating platform ("discord", "matrix", …).

channel_id: str = ''

Platform-specific channel / room identifier.

user_id: str = ''

Platform-specific sender identifier.

user_name: str = ''

Human-readable display name of the sender.

guild_id: str = ''

Discord-specific guild (server) ID. Empty on non-Discord platforms.

adapter: PlatformAdapter | None = None

The PlatformAdapter instance for the originating platform.

For Discord this is a DiscordPlatform whose .client property exposes the underlying discord.Client.

message_id: str = ''

Platform-specific ID of the message that triggered this response.

config: Config | None = None

Global bot Config. Gives tools access to redis_url, model, and other settings.

redis: Any = None

Shared async Redis client (redis.asyncio.Redis).

None when Redis is not configured. Tools should guard access with an if ctx.redis is not None check.

message_cache: MessageCache | None = None

Shared MessageCache instance for retrieving cached messages as CachedMessage objects.

None when Redis is not configured.

kg_manager: KnowledgeGraphManager | None = None

Shared KnowledgeGraphManager for the knowledge graph system.

None when Redis is not configured.

task_manager: TaskManager | None = None

Shared TaskManager for checking background task results. Injected by the message processor so tools like check_task can access it.

threadweave: ThreadweaveManager | None = None

Shared ThreadweaveManager for the Threadweave persistent knowledge system (DNA Vault, Persistent Weave, Shadow Memory).

None when Redis is not configured.

tool_registry: ToolRegistry | None = None

The live ToolRegistry that holds all loaded tools.

Allows meta-tools like list_all_tools and reload_tools to inspect or modify the running tool set.

conversation_manager: ConversationManager | None = None

Shared ConversationManager for per-channel conversation histories.

Allows tools to read or manipulate the in-memory context window for any channel.

openrouter: Any = None

The OpenRouterClient instance for the current session.

Used for shared HTTP/registry access. Effective tool-round limits for the active chat() call are scoped via contextvars (see get_tool_round_limit_box()); extend_tool_loop mutates that per-invocation state, not openrouter fields.

all_adapters: list[Any]

All running PlatformAdapter instances.

Allows cross-platform tools (e.g. list_active_servers) to query every connected platform, not just the one that triggered the current message.

adapters_by_name: dict[str, Any]

All platform adapters keyed by name (e.g. "discord", "discord-self", "matrix").

Allows tools to look up a specific adapter directly:

selfbot = ctx.adapters_by_name.get("discord-self")
disclosed_skill_ids: list[str]

Agent Skill ids disclosed in tier-1 catalog for this message (for activate_skill).

injected_tools: list[str] | None = None

Per-round staging list for request_tool_injection.

Tool handlers append here; after each tool round OpenRouterClient.chat() merges these names into the live tool_names list passed to the LLM and then clears this field. Each round starts with injected_tools = [] so parallel tools in one round share one list safely.

The model keeps seeing merged tools on every later round of the same chat() call because the client extends the in-memory tool_names list — not because this attribute stays populated.

When several tools run in the same model round they share this ToolContext and execute concurrently via asyncio.gather. Prefer list.extend / append; avoid non-atomic read-modify-write patterns. Backgrounded tools (task manager) may still race with later rounds on the same ctx—keep mutations minimal.

injected_tools_session: list[str]

All tool names merged via injection during the current chat() loop.

Appended when the client merges injected_tools into tool_names; cleared at the start of chat() when record_executed_tools is true (same as tools_executed). Use this to introspect which extra tools stayed active for the whole generation.

sent_files: list[dict[str, Any]]

Media files sent to the channel during tool execution.

Each entry has keys: data (raw bytes), filename (str), mimetype (str), and optionally file_url (str). After the LLM call, the message processor converts these into real multimodal content parts (image_url, input_audio, etc.) via media_to_content_parts() and appends them to conversation history so the bot can see its own visual and audio outputs on subsequent turns.

Parallel tools in one LLM round share this list; appends are safe, but do not assume ordering of entries matches tool-call order unless you serialize tool execution.

sent_rich_messages: list[dict[str, Any]]

Rich Discord messages sent during tool execution.

Each entry has keys: text (serialized LLM-visible content) and optionally message_id. The message processor appends these to assistant history/cache so embed-only sends remain visible later.

tools_executed: list[str]

Tool names executed during the current top-level OpenRouterClient.chat() loop, in first-seen order (each name at most once).

Populated by the client when record_executed_tools is true; cleared at the start of each such call. Nested chat() calls (e.g. subagents) should pass record_executed_tools=False to avoid polluting this list.

observability_request_id: str = ''

Correlation id for one user message → LLM → reply (Redis observability).

room_context: dict[str, Any] | None = None

Prompt room_context for the active channel (when built).

Used by tools that need parity with the main system prompt, e.g. swarm subagent rendering of dominant_emotions / narrative_tone.

tool_call_records: list[ToolCallRecord]

Per-turn tool call execution traces.

Populated by OpenRouterClient.chat() after each tool round completes (post-gather barrier). Consumed by run_generate_and_send() to write hidden Redis messages and the Stargazer-System-Log summary. Cleared at the start of each chat() call when record_executed_tools is true.

persona_pref_manager: Any = None

Shared PersonaPreferenceManager for the persona preference memory system.

Allows persona preference tools (record, review, evolve, retract) to access the manager without a circular import.

None when the feature is disabled or Redis is not configured.

visual_memory: Any = None

Shared VisualMemoryEngine for the visual pattern recognition memory system.

Allows visual memory tools (repost_image, label_visual_entity) to access the engine for image retrieval and entity management.

None when visual memory is disabled or not initialized.