feat: updated agent harness

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-04-28 09:22:19 -07:00
parent 9ec9b64348
commit 31a372bb84
139 changed files with 12583 additions and 1111 deletions

View file

@ -0,0 +1,52 @@
"""
The ``invalid`` fallback tool.
When the model emits a tool call whose name doesn't match any registered
tool, :class:`ToolCallNameRepairMiddleware` rewrites the call to ``invalid``
with the original name and a parser/validation error string. This tool's
execution then returns that error to the model so it can self-correct.
Mirrors ``opencode/packages/opencode/src/tool/invalid.ts``. Tier 1.6 in
the OpenCode-port plan.
Critically, the :class:`ToolDefinition` for this tool is **excluded** from
the system-prompt tool list and from ``LLMToolSelectorMiddleware`` selection
(see ``ToolDefinition.always_include`` filtering in the registry) the
model never advertises ``invalid`` as a callable. It only ever shows up
in the tool registry so LangGraph can dispatch the rewritten call.
"""
from __future__ import annotations
from langchain_core.tools import tool
INVALID_TOOL_NAME = "invalid"
INVALID_TOOL_DESCRIPTION = "Do not use"
def _format_invalid_message(tool: str | None, error: str | None) -> str:
"""Return the user-visible error string. Mirrors ``invalid.ts``."""
name = tool or "<unknown>"
detail = error or "(no error message provided)"
return (
f"The arguments provided to the tool `{name}` are invalid: {detail}\n"
f"Read the tool's docstring carefully and try again with valid arguments."
)
@tool(name_or_callable=INVALID_TOOL_NAME, description=INVALID_TOOL_DESCRIPTION)
def invalid_tool(tool: str | None = None, error: str | None = None) -> str:
"""Return a human-readable explanation of a tool-call validation failure.
Activated only when :class:`ToolCallNameRepairMiddleware` rewrites a
failed tool call to ``invalid`` with the original tool name and the
error message produced during validation.
"""
return _format_invalid_message(tool, error)
__all__ = [
"INVALID_TOOL_DESCRIPTION",
"INVALID_TOOL_NAME",
"invalid_tool",
]

View file

@ -43,6 +43,9 @@ from typing import Any
from langchain_core.tools import BaseTool
from app.agents.new_chat.middleware.dedup_tool_calls import (
wrap_dedup_key_by_arg_name,
)
from app.db import ChatVisibility
from .confluence import (
@ -125,6 +128,14 @@ class ToolDefinition:
enabled_by_default: Whether the tool is enabled when no explicit config is provided
required_connector: Searchable type string (e.g. ``"LINEAR_CONNECTOR"``)
that must be in ``available_connectors`` for the tool to be enabled.
dedup_key: Optional callable that maps a tool's ``args`` dict to a
string signature used by :class:`DedupHITLToolCallsMiddleware`
to drop duplicate calls. Replaces the legacy hardcoded
``_NATIVE_HITL_TOOL_DEDUP_KEYS`` map (Tier 2.3 in the
OpenCode-port plan).
reverse: Optional callable that, given the tool's ``(args, result)``,
returns a ``ReverseDescriptor`` describing the inverse tool
invocation. Consumed by the snapshot/revert pipeline (Tier 5).
"""
@ -135,6 +146,8 @@ class ToolDefinition:
enabled_by_default: bool = True
hidden: bool = False
required_connector: str | None = None
dedup_key: Callable[[dict[str, Any]], str] | None = None
reverse: Callable[[dict[str, Any], Any], dict[str, Any]] | None = None
# =============================================================================
@ -288,6 +301,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="NOTION_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("title"),
),
ToolDefinition(
name="update_notion_page",
@ -299,6 +313,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="NOTION_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("page_title"),
),
ToolDefinition(
name="delete_notion_page",
@ -310,6 +325,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="NOTION_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("page_title"),
),
# =========================================================================
# GOOGLE DRIVE TOOLS - create files, delete files
@ -325,6 +341,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="GOOGLE_DRIVE_FILE",
dedup_key=wrap_dedup_key_by_arg_name("file_name"),
),
ToolDefinition(
name="delete_google_drive_file",
@ -336,6 +353,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="GOOGLE_DRIVE_FILE",
dedup_key=wrap_dedup_key_by_arg_name("file_name"),
),
# =========================================================================
# DROPBOX TOOLS - create and trash files
@ -351,6 +369,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="DROPBOX_FILE",
dedup_key=wrap_dedup_key_by_arg_name("file_name"),
),
ToolDefinition(
name="delete_dropbox_file",
@ -362,6 +381,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="DROPBOX_FILE",
dedup_key=wrap_dedup_key_by_arg_name("file_name"),
),
# =========================================================================
# ONEDRIVE TOOLS - create and trash files
@ -377,6 +397,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="ONEDRIVE_FILE",
dedup_key=wrap_dedup_key_by_arg_name("file_name"),
),
ToolDefinition(
name="delete_onedrive_file",
@ -388,6 +409,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="ONEDRIVE_FILE",
dedup_key=wrap_dedup_key_by_arg_name("file_name"),
),
# =========================================================================
# GOOGLE CALENDAR TOOLS - search, create, update, delete events
@ -414,6 +436,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="GOOGLE_CALENDAR_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("title"),
),
ToolDefinition(
name="update_calendar_event",
@ -425,6 +448,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="GOOGLE_CALENDAR_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("event_title_or_id"),
),
ToolDefinition(
name="delete_calendar_event",
@ -436,6 +460,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="GOOGLE_CALENDAR_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("event_title_or_id"),
),
# =========================================================================
# GMAIL TOOLS - search, read, create drafts, update drafts, send, trash
@ -473,6 +498,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="GOOGLE_GMAIL_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("subject"),
),
ToolDefinition(
name="send_gmail_email",
@ -484,6 +510,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="GOOGLE_GMAIL_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("subject"),
),
ToolDefinition(
name="trash_gmail_email",
@ -495,6 +522,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="GOOGLE_GMAIL_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("email_subject_or_id"),
),
ToolDefinition(
name="update_gmail_draft",
@ -506,6 +534,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="GOOGLE_GMAIL_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("draft_subject_or_id"),
),
# =========================================================================
# CONFLUENCE TOOLS - create, update, delete pages
@ -521,6 +550,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="CONFLUENCE_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("title"),
),
ToolDefinition(
name="update_confluence_page",
@ -532,6 +562,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="CONFLUENCE_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("page_title_or_id"),
),
ToolDefinition(
name="delete_confluence_page",
@ -543,6 +574,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
requires=["db_session", "search_space_id", "user_id"],
required_connector="CONFLUENCE_CONNECTOR",
dedup_key=wrap_dedup_key_by_arg_name("page_title_or_id"),
),
# =========================================================================
# DISCORD TOOLS - list channels, read messages, send messages
@ -755,6 +787,24 @@ def build_tools(
# Create the tool
tool = tool_def.factory(dependencies)
# Propagate the registry-level metadata so middleware (e.g.
# ``DedupHITLToolCallsMiddleware``) and the action-log/revert
# pipeline can pick the resolvers up via ``tool.metadata`` without
# re-importing :data:`BUILTIN_TOOLS`.
if tool_def.dedup_key is not None or tool_def.reverse is not None:
existing_meta = getattr(tool, "metadata", None) or {}
merged_meta = dict(existing_meta)
if tool_def.dedup_key is not None:
merged_meta.setdefault("dedup_key", tool_def.dedup_key)
if tool_def.reverse is not None:
merged_meta.setdefault("reverse", tool_def.reverse)
try:
tool.metadata = merged_meta
except Exception:
logger.debug(
"Tool %s rejected metadata mutation; relying on registry lookup",
tool_def.name,
)
tools.append(tool)
# Add any additional custom tools