|
@@ -4693,6 +4693,38 @@ CHATTO_SEND_ROOM_SCHEMA = {
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+CHATTO_MARK_THREAD_AS_READ_SCHEMA = {
|
|
|
|
|
+ "name": "chatto_mark_thread_as_read",
|
|
|
|
|
+ "description": (
|
|
|
|
|
+ "Mark a thread as read for the bot. This updates the server's read "
|
|
|
|
|
+ "marker so Chatto clients show the thread as read. Useful after the bot "
|
|
|
|
|
+ "has processed a thread's full context."
|
|
|
|
|
+ ),
|
|
|
|
|
+ "parameters": {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "room_id": {
|
|
|
|
|
+ "type": "string",
|
|
|
|
|
+ "description": "The Chatto room ID of the thread.",
|
|
|
|
|
+ },
|
|
|
|
|
+ "room_name": {
|
|
|
|
|
+ "type": "string",
|
|
|
|
|
+ "description": (
|
|
|
|
|
+ "Optional: room name — one of ``room_id`` or ``room_name`` is required."
|
|
|
|
|
+ ),
|
|
|
|
|
+ },
|
|
|
|
|
+ "thread_root_event_id": {
|
|
|
|
|
+ "type": "string",
|
|
|
|
|
+ "description": (
|
|
|
|
|
+ "The event ID of the thread root message."
|
|
|
|
|
+ ),
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": ["thread_root_event_id"],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ },
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
|
|
|
CHATTO_GET_MESSAGE_SCHEMA = {
|
|
CHATTO_GET_MESSAGE_SCHEMA = {
|
|
|
"name": "chatto_get_message",
|
|
"name": "chatto_get_message",
|
|
@@ -5940,6 +5972,36 @@ async def _fetch_link_preview(args: dict) -> str:
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+async def _mark_thread_as_read(args: dict) -> str:
|
|
|
|
|
+ """Mark a thread as read. See _handle_chatto_mark_thread_as_read."""
|
|
|
|
|
+ room_id = args.get("room_id") or ""
|
|
|
|
|
+ room_name = args.get("room_name") or ""
|
|
|
|
|
+ thread_root_event_id = args.get("thread_root_event_id") or ""
|
|
|
|
|
+
|
|
|
|
|
+ if not room_id and not room_name:
|
|
|
|
|
+ raise _refuse("Exactly one of room_id or room_name is required")
|
|
|
|
|
+ if not thread_root_event_id:
|
|
|
|
|
+ raise _refuse("thread_root_event_id is required")
|
|
|
|
|
+
|
|
|
|
|
+ async with _tool_client(_chatto_config_for_tools()) as client:
|
|
|
|
|
+ if not room_id:
|
|
|
|
|
+ resolved = await _resolve_room(client, room_id, room_name)
|
|
|
|
|
+ room_id = resolved.room.id
|
|
|
|
|
+ prev_last_read = await client.mark_thread_as_read(
|
|
|
|
|
+ room_id, thread_root_event_id
|
|
|
|
|
+ )
|
|
|
|
|
+ return json.dumps(
|
|
|
|
|
+ {
|
|
|
|
|
+ "success": True,
|
|
|
|
|
+ "room_id": room_id,
|
|
|
|
|
+ "thread_root_event_id": thread_root_event_id,
|
|
|
|
|
+ "previous_last_read_at": (
|
|
|
|
|
+ _iso_or_none(prev_last_read) if prev_last_read else None
|
|
|
|
|
+ ),
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
async def _handle_chatto_fetch_link_preview(args: dict) -> str:
|
|
async def _handle_chatto_fetch_link_preview(args: dict) -> str:
|
|
|
"""Tool handler for chatto_fetch_link_preview.
|
|
"""Tool handler for chatto_fetch_link_preview.
|
|
|
|
|
|
|
@@ -5958,6 +6020,24 @@ async def _handle_chatto_fetch_link_preview(args: dict) -> str:
|
|
|
return json.dumps({"success": False, "error": f"Chatto API error: {exc}"})
|
|
return json.dumps({"success": False, "error": f"Chatto API error: {exc}"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+async def _handle_chatto_mark_thread_as_read(args: dict) -> str:
|
|
|
|
|
+ """Tool handler for chatto_mark_thread_as_read.
|
|
|
|
|
+
|
|
|
|
|
+ Returns a JSON string: Hermes' tool contract accepts no other type, and a
|
|
|
|
|
+ dict (or an unflagged coroutine) fails at dispatch with "unsupported result
|
|
|
|
|
+ type". Every early exit arrives here as a _ToolRefusal and leaves as that
|
|
|
|
|
+ refusal's own JSON, so no path escapes as an unhandled error.
|
|
|
|
|
+ """
|
|
|
|
|
+ try:
|
|
|
|
|
+ return await _mark_thread_as_read(args)
|
|
|
|
|
+ except _ToolRefusal as refusal:
|
|
|
|
|
+ return refusal.result_json
|
|
|
|
|
+ except ChattoAuthError as exc:
|
|
|
|
|
+ return json.dumps({"success": False, "error": f"Authentication error: {exc}"})
|
|
|
|
|
+ except ChattoError as exc:
|
|
|
|
|
+ return json.dumps({"success": False, "error": f"Chatto API error: {exc}"})
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
async def _handle_chatto_get_message(args: dict) -> str:
|
|
async def _handle_chatto_get_message(args: dict) -> str:
|
|
|
"""Tool handler for chatto_get_message.
|
|
"""Tool handler for chatto_get_message.
|
|
|
|
|
|
|
@@ -6116,6 +6196,11 @@ _PLUGIN_TOOLS = (
|
|
|
CHATTO_FETCH_LINK_PREVIEW_SCHEMA,
|
|
CHATTO_FETCH_LINK_PREVIEW_SCHEMA,
|
|
|
_handle_chatto_fetch_link_preview,
|
|
_handle_chatto_fetch_link_preview,
|
|
|
),
|
|
),
|
|
|
|
|
+ (
|
|
|
|
|
+ "chatto_mark_thread_as_read",
|
|
|
|
|
+ CHATTO_MARK_THREAD_AS_READ_SCHEMA,
|
|
|
|
|
+ _handle_chatto_mark_thread_as_read,
|
|
|
|
|
+ ),
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|