mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-08-05 02:45:28 +00:00
157 lines
16 KiB
Markdown
157 lines
16 KiB
Markdown
# Agent Tools
|
|
|
|
Last updated: dev@df2fad2 | 2026-07-12
|
|
|
|
## Scope
|
|
|
|
This spec covers agent/tool behavior in:
|
|
|
|
- `src/agent_loop.py`;
|
|
- `src/llm_core.py`;
|
|
- `src/tool_schemas.py`;
|
|
- `src/tool_execution.py`;
|
|
- `src/tool_policy.py`;
|
|
- `src/tool_index.py`;
|
|
- `src/tool_parsing.py`;
|
|
- `src/tool_security.py`;
|
|
- `src/attachment_refs.py` and shared upload lifecycle helpers in
|
|
`src/upload_handler.py` / `src/tool_utils.py`;
|
|
- `src/tool_implementations.py`;
|
|
- `src/tools/*.py`;
|
|
- `src/builtin_actions.py`;
|
|
- `src/ai_interaction.py`;
|
|
- `src/action_intents.py`;
|
|
- `src/goal_based_extractor.py`;
|
|
- `src/teacher_escalation.py`;
|
|
- `src/agent_tools/` modules and compatibility facade;
|
|
- `src/mcp_manager.py`;
|
|
- `src/builtin_mcp.py`;
|
|
- `src/bg_jobs.py` and `src/bg_monitor.py`;
|
|
- `routes/chat_routes.py`, `routes/chat_helpers.py`, `routes/model_routes.py`, `routes/skills_routes.py`, `routes/mcp_routes.py`, and `routes/workspace_routes.py`;
|
|
- `mcp_servers/*.py`;
|
|
- frontend stream/admin/settings files that display tool events, workspaces, and disabled tools;
|
|
- `tests/test_agent_loop.py`, `tests/test_tool_*`, and focused MCP/public-policy/schema tests.
|
|
|
|
## Agent Loop
|
|
|
|
`src.agent_loop` owns agent prompt assembly, request-local current date/time insertion, tool retrieval, prompted tool-block handling, native tool-call consumption after `llm_core` normalizes provider events, multi-round execution, tool result insertion, final metrics, and fallback responses. It requests context from documents, skills, tool retrieval, and messages; it should not own domain-specific business logic for every tool. Its prompt rules now bias structured/long-form writing toward living documents, route active compose/email drafts back into existing email documents, and prefer first-class `web_search`/`web_fetch` tools over shell/Python/curl for current web lookups when web tools are enabled.
|
|
|
|
`src.llm_core` owns provider payloads, native tool-schema emission, and provider stream parsing. `agent_loop` consumes normalized tool-call events and decides whether and how to execute them.
|
|
|
|
Agent mode enters through chat routes, including auto-escalation from intent helpers, detached `agent_runs` streaming, resume/stop behavior, and frontend tool-event rendering.
|
|
|
|
Guide-only/no-tools turns are runtime policy, not prompt advice. `src.tool_policy` detects strong latest-turn directives such as guide-only mode, no-tools mode, and explicit requests not to use tools; it builds a `ToolPolicy` that hides schemas, disables known native tools, disables MCP for that turn, skips tool retrieval, suppresses local/workspace context injection, blocks document streaming/teacher escalation, and gives `tool_execution` a final execution backstop.
|
|
|
|
Plan mode is a read-only investigation path inside the same loop. It adds a denylist for known mutating tools, filters write/unknown MCP tools, prepends plan-mode instructions, and uses the `update_plan` tool only after a plan is approved for execution. The backend path still exists for compatibility, but current browser chat forces incoming `plan_mode` off and the old plan-window UI module is gone.
|
|
|
|
Workspace mode is request-scoped. Admin chat can send a workspace directory selected through `static/js/workspace.js`; `agent_loop` injects that fact early in the prompt and `tool_execution` confines bash, python, read/write/edit-file, and code-navigation tools to that root. `routes.workspace_routes` owns admin-only browse/vet APIs, skips hidden/symlink directory traversal, caps listings, and rejects sensitive/root paths before a workspace reaches chat.
|
|
|
|
## Tool Registry
|
|
|
|
Tool registration is split:
|
|
|
|
- `src.agent_tools` is now a package/facade. `TOOL_HANDLERS` maps native tool names to handler functions across filesystem, subprocess, web, document, interaction, model-interaction, background-job, session, and admin modules, while `TOOL_TAGS` keeps compatibility metadata and the global MCP manager handle;
|
|
- `src.tools` owns domain do_* implementations for calendar, contacts, Cookbook, image, notes, research, search, system, and vault tools. `src.tool_implementations` is now a compatibility facade that re-exports those symbols and lazy-loads admin manage_* symbols to avoid circular imports;
|
|
- `src.agent_tools.admin_tools` owns admin manage_* tools for endpoints, MCP, webhooks, tokens, and settings, including command validation for `manage_mcp`;
|
|
- `src.tool_parsing._TOOL_NAME_MAP` owns aliases and prompted-block parsing;
|
|
- `src.tool_schemas.FUNCTION_TOOL_SCHEMAS` and `function_call_to_tool_block()` own native schema and native-call conversion;
|
|
- `src.tool_index.BUILTIN_TOOL_DESCRIPTIONS` owns retrieval text;
|
|
- `src.tool_execution.execute_tool_block()` owns dispatch and hard execution gates;
|
|
- `routes.model_routes.py` and frontend settings/admin surfaces expose global disabled-tool controls.
|
|
|
|
When adding, removing, or renaming a tool, update the registry chain, execution dispatch, retrieval text, prompt wording, disabled-tool UI, and tests together.
|
|
|
|
`src.tool_index.ALWAYS_AVAILABLE` is the retrieval catalog for high-frequency tools such as shell/python, web search/fetch, read/write/edit-file, code-nav, `manage_memory`, `ask_user`, `update_plan`, selected Cookbook serve controls, and `app_api`. Current prompt/schema assembly preserves only selected base tools unconditionally, then adds intent-, skill-, and retrieval-relevant tools so unrelated schemas do not flood small contexts.
|
|
|
|
## Tool Retrieval And Execution
|
|
|
|
`src.tool_index.ToolIndex` owns candidate retrieval using embeddings/keywords and cached index data. Security filtering is not its hard boundary: `agent_loop` hides unavailable schemas, and `tool_execution` blocks disabled, admin-only, and public-restricted calls before dispatch.
|
|
|
|
`src.tool_execution` owns built-in tool execution, MCP dispatch, path confinement, background markers, output truncation, internal HTTP loopback, owner/admin checks, policy-blocked execution results, and formatting tool results for the model/UI. File tools support exact edit diffs, full-file writes, read line ranges, and workspace confinement. Code-navigation tools (`grep`, `glob`, `ls`) prefer `rg`/structured filesystem traversal over ad hoc shell commands. Uploaded-file context uses stable `attachment_ref` manifests and owner-checked URIs; a compatibility local path is exposed only after upload-root and tool-root confinement. Shared truncation, upload-handler registration, and MCP manager compatibility helpers live in `src.tool_utils`.
|
|
|
|
Tool retrieval has domain-specific hooks beyond generic similarity: contact queries can surface `resolve_contact`/`manage_contact`; matched skills can add `manage_skills` and their required toolsets to the relevant tool set; explicit admin intents can include admin schemas so prompt text and native schema emission match.
|
|
|
|
Interaction/session/model helper tools are native first-class tools, not prompt-only conventions. `ask_user` and `update_plan` live in `src.agent_tools.interaction_tools`, model delegation/listing helpers live in `model_interaction_tools`, session creation/list/send/manage helpers live in `session_tools`, and `manage_bg_jobs` lives in `bg_job_tools`.
|
|
|
|
Prompted-tool parsing includes recovery paths for local/provider text leaks:
|
|
bare JSON after a web-tool mention, OpenAI-style raw
|
|
`{"function": ...}` payloads, StepFun/Gemma/DSML markup, and
|
|
`<function_model><function_call>...</function_call><parameters>...</parameters></function_model>`
|
|
wrappers from local MLX/Exo models. Non-dict JSON arguments are rejected back to
|
|
empty args instead of crashing the turn, common `tex` typos normalize to
|
|
`text`, and delimiter scans are forward-only so unterminated tool markup cannot
|
|
drive quadratic regex rescans. Executed raw tool JSON is stripped from assistant
|
|
text afterward; this is still not a general-purpose JSON-command parser.
|
|
|
|
Current call sites include:
|
|
|
|
- agent mode tool calls from `src.agent_loop`;
|
|
- MCP route configuration and built-in MCP registration;
|
|
- background job monitoring and auto-continue;
|
|
- skill tests, teacher escalation, scheduled tasks, and background follow-up loops;
|
|
- UI-control and AI interaction helpers.
|
|
|
|
## Streaming And Continuations
|
|
|
|
Agent streaming emits normal content plus tool progress/output, document stream/update, ask-user choices, plan updates, budget, round exhaustion, loop-breaker, intent-nudge exhaustion, metrics, teacher escalation, research anchor, and finish/error events. Frontend chat stream code and detached replay depend on stable event names. If the stream generator closes while awaiting an in-flight tool, the loop cancels and awaits that tool task so subprocess-backed work is not left orphaned.
|
|
|
|
Long-running bash jobs can be detached with background markers. `src.bg_jobs` owns persistent job state/result files; `src.bg_monitor` owns auto-continuation when jobs finish. Detached chat runs are in-memory and do not survive server restart, while background job state is disk-backed.
|
|
|
|
Loop-breaker final-answer rounds, explicit repeated-tool/intent-nudge guard events, round-cap continuation signals, optional verifier retries, and teacher escalation are recovery behavior owned by `agent_loop` and `src.teacher_escalation`.
|
|
|
|
## Security And Policy
|
|
|
|
- `src.tool_security` owns non-admin blocked-tool decisions.
|
|
- Non-admin users must not reach admin tools through agent mode, MCP, retrieval, or loopback calls.
|
|
- Agent owner is passed from chat route `get_current_user(request)`. In `AUTH_ENABLED=false` mode this is `None`, not the `""` value returned by route dependencies. `blocked_tools_for_owner()`, schema hiding, and `execute_tool_block()` all use that owner.
|
|
- Current dev tool security treats explicit `AUTH_ENABLED=false` as single-user even when an auth store exists, while auth-enabled pre-setup callers remain non-admin.
|
|
- Path-based tools must remain confined to allowed roots and reject sensitive paths. Sensitive-path checks are case-insensitive and apply to direct file tools and code-navigation tools; `grep`/`glob`/`ls` must not become existence or content oracles for `.env`, SSH/GPG material, `id_rsa`, and similar denylisted paths.
|
|
- Tool output is bounded/truncated where native execution owns the path, including displayed agent-tool output through the shared truncation helper. MCP output must be treated as untrusted; central MCP-output truncation before model re-entry remains a gap.
|
|
- Provider-emitted native tool calls are requests, not authorization. `tool_execution` and route-level policy remain the authority.
|
|
- Attachment-bearing document, note, and calendar tools owner-reserve internal
|
|
upload references before durable writes and fail without mutation when the
|
|
referenced upload is unavailable.
|
|
- Guide-only/no-tools mode blocks tools before prompt assembly, before execution, and in chat preprocessing paths that would otherwise fetch context or start tool-backed research.
|
|
- Plan mode is policy, not prompt advice: mutating native tools are disabled through schema-derived detection plus a static backstop, and write/unknown MCP tools are hidden and runtime-blocked for that turn.
|
|
|
|
## Internal Loopback
|
|
|
|
`do_app_api()` is implemented in `src.tools.system` and re-exported by `src.tool_implementations`. It owns generic app API loopback, OpenAPI discovery, method/path blocklists, and fixed local target behavior. `_internal_headers()` adds the process-secret internal-tool token and optional `X-Odysseus-Owner`; `core.middleware.require_admin()` and auth middleware own the corresponding bypass and owner-stamping rules. Route-specific owner handling must still be audited.
|
|
|
|
## MCP
|
|
|
|
`src.mcp_manager` owns configured MCP server lifecycle, discovered tool state, qualified MCP names, OpenAI schema conversion, call routing, generation invalidation, and connect/disconnect status. It supports stdio, SSE, and Streamable HTTP transports; Streamable HTTP can publish a `needs_auth` state and uses `src.mcp_oauth` for OAuth/OIDC-style authorization, token refresh, and encrypted token storage. `src.builtin_mcp` owns built-in server registration and the native-vs-MCP split. `mcp_servers/` owns server-specific tools for email, image generation, memory, RAG, and optional browser tooling.
|
|
|
|
Native bash, python, file, web search, and web fetch tools continue through native fallback even when MCP is unavailable. Browser MCP is optional and can be skipped when cached Playwright/NPX packages are missing. Public users get no MCP schemas, and any `mcp__*` execution attempt must be blocked.
|
|
|
|
MCP prompt/schema rendering includes server-provided input schemas, but names, types, and parameter hint text are sanitized and length-capped before entering the prompt. Per-server disabled tools filter listings, prompt descriptions, and function schemas; execution-time disabled-tool enforcement remains a separate hardening item.
|
|
|
|
## Intent And Recovery Helpers
|
|
|
|
`src.action_intents` owns deterministic chat-to-agent promotion hints and returns a category/reason so route logs can explain auto-escalation decisions. Explicit web-search language is category `web`; it can promote the turn into agent mode and narrow tools toward web search/fetch, but route policy requires explicit web-search enablement and honors explicit denial. It must avoid promoting explanatory questions into agent mode. `src.builtin_actions` owns scheduler/background actions outside the normal live agent loop. `src.teacher_escalation` owns recovery/escalation and skill-creation flows. `src.goal_based_extractor` is research-adjacent and should stay cross-referenced from research behavior rather than treated as ordinary tool execution.
|
|
|
|
When an email reader is active, browser chat passes active email metadata and the agent loop injects it as protected, untrusted context so default reply/draft behavior targets the selected message. Active email compose documents are handled as existing email drafts rather than generic new-document requests.
|
|
|
|
## Degraded Behavior
|
|
|
|
- ToolIndex can degrade to keyword selection when embeddings, Chroma, index
|
|
warmup, or vector retrieval timeouts fail.
|
|
- Agent mode can degrade from native function schemas to prompted fenced-block parsing based on provider/tool-support heuristics. Local Ollama `/v1` and native `/api` endpoints default to text tools unless the endpoint explicitly advertises `supports_tools`; `gpt-oss` remains text-tool by default unless the endpoint opts in.
|
|
- MCP startup failure is non-critical; route/status surfaces expose per-server errors.
|
|
- `ODYSSEUS_DISABLE_MCP`, missing `mcp`, uncached browser MCP packages, and per-server disabled tools can remove tools without blocking the app.
|
|
- Global `builtin_browser` disable behavior may not currently match qualified `mcp__builtin_browser__*` tool names.
|
|
|
|
## Current Gaps
|
|
|
|
- Tool descriptions are duplicated across `FUNCTION_TOOL_SCHEMAS`, agent prompt sections, and `BUILTIN_TOOL_DESCRIPTIONS`.
|
|
- Agent prompts remain heavy for small local context windows.
|
|
- Some AI-control helpers are still globally wired from app startup rather than a narrower service layer.
|
|
- Tool registry consistency is manual across handler maps, tags, aliases, schemas, retrieval descriptions, execution dispatch, settings/model routes, and frontend toggles.
|
|
- MCP disabled-tool changes can stale-cache tool retrieval because disabled maps are not always an index generation input.
|
|
- External MCP output truncation and tool-result prompt-injection wrapping need stronger guarantees.
|
|
- Auth-disabled/no-login owner propagation is inconsistent between route dependencies and chat/agent execution, so tool-security and native tool storage behavior need dedicated regression coverage.
|
|
- Agent tests mostly cover helpers and targeted regressions, including round-cap
|
|
and disconnect cancellation paths, but not an end-to-end fake-LLM
|
|
`stream_agent_loop` path with retrieval, native schemas, prompted blocks,
|
|
disabled/admin hiding, MCP tools, plan/workspace state, user-time context, and
|
|
tool-result SSE.
|