@inline-chat/hermes-agent-adapter 1.0.0-alpha.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/install.js +95 -95
- package/package.json +10 -4
- package/plugin/inline/adapter.py +104 -74
- package/plugin/inline/cli.py +9 -2
- package/plugin/inline/plugin.yaml +1 -1
- package/plugin/inline/sidecar/index.mjs +846 -801
- package/plugin/inline/tools.py +32 -8
package/plugin/inline/tools.py
CHANGED
|
@@ -88,12 +88,13 @@ _TURN_MESSAGES_MAX = 1000
|
|
|
88
88
|
|
|
89
89
|
def remember_turn_message(
|
|
90
90
|
*, session_chat_id: str, thread_id: str, message_id: str, chat_id: str, sender_id: str, sender_is_bot: bool,
|
|
91
|
-
chat_type: str = "",
|
|
91
|
+
chat_type: str = "", parent_chat_id: str = "",
|
|
92
92
|
) -> None:
|
|
93
|
+
"""``parent_chat_id`` is set only when the message itself was sent inside a reply thread."""
|
|
93
94
|
key = (str(hermes_home()), str(session_chat_id or ""), str(thread_id or ""), str(message_id or ""))
|
|
94
95
|
_turn_messages[key] = {
|
|
95
96
|
"chatId": str(chat_id), "senderId": str(sender_id or ""), "senderIsBot": bool(sender_is_bot),
|
|
96
|
-
"chatType": str(chat_type or ""), "
|
|
97
|
+
"chatType": str(chat_type or ""), "parentChatId": str(parent_chat_id or ""),
|
|
97
98
|
}
|
|
98
99
|
_turn_messages.move_to_end(key)
|
|
99
100
|
while len(_turn_messages) > _TURN_MESSAGES_MAX:
|
|
@@ -108,14 +109,15 @@ def _current_turn() -> Dict[str, Any]:
|
|
|
108
109
|
known = _turn_messages.get((str(hermes_home()), session_chat, thread_id, message_id)) or {}
|
|
109
110
|
# The turn already runs in a thread when reply threads moved it there (its message lives
|
|
110
111
|
# in the parent chat) or when its message was sent inside the thread.
|
|
111
|
-
|
|
112
|
+
parent_chat_id = known.get("parentChatId") or ""
|
|
113
|
+
in_thread = bool(thread_id) and (bool(parent_chat_id) or (known.get("chatId") or session_chat) != thread_id)
|
|
112
114
|
return {
|
|
113
115
|
"messageId": message_id,
|
|
114
116
|
"chatId": known.get("chatId") or session_chat,
|
|
115
117
|
"senderId": known.get("senderId") or "",
|
|
116
118
|
"senderIsBot": known.get("senderIsBot"),
|
|
117
119
|
"chatType": known.get("chatType") or "",
|
|
118
|
-
"
|
|
120
|
+
"parentChatId": parent_chat_id,
|
|
119
121
|
"threadId": thread_id if in_thread else "",
|
|
120
122
|
}
|
|
121
123
|
|
|
@@ -243,7 +245,10 @@ def tool_context_prompt(
|
|
|
243
245
|
lines.append(f"- Current Inline reply thread: `{thread_id}`. Use this as `chat_id` for thread-scoped reads.")
|
|
244
246
|
lines.append(f"- Link the current thread as `[this thread](inline://thread?id={thread_id})` when asked for a thread link.")
|
|
245
247
|
if parent_chat_id:
|
|
246
|
-
lines.append(
|
|
248
|
+
lines.append(
|
|
249
|
+
f"- Parent Inline chat: `{parent_chat_id}`. Earlier conversation lives there; "
|
|
250
|
+
"read it with get_history or search_messages using that chat_id."
|
|
251
|
+
)
|
|
247
252
|
else:
|
|
248
253
|
lines.append(f"- Current Inline chat: `{chat_id}`.")
|
|
249
254
|
lines.append(f"- Link the current chat as `[this chat](inline://chat?id={chat_id})` when asked for a chat link.")
|
|
@@ -265,8 +270,9 @@ INLINE_TOOL_SCHEMA = {
|
|
|
265
270
|
+ "\n\n"
|
|
266
271
|
"When chat_id is omitted, the tool uses the current Inline reply thread if present, otherwise the current chat. "
|
|
267
272
|
"Return normal assistant replies as text. Use send_message only for a button card or a different destination. "
|
|
273
|
+
"To hand work to another bot, mention it in your normal reply; its answer comes back to you as a turn that doesn't need a response, and asking it again takes a new mention. "
|
|
268
274
|
"Agent-owned button presses return as ordinary turns, and the normal response replaces the source card while clearing omitted buttons. "
|
|
269
|
-
"Use create_thread on the current triggering message when someone asks to continue in a thread; your answer then goes into it, so don't also post it in the chat. "
|
|
275
|
+
"Use create_thread on the current triggering message when someone asks to continue in a thread; your answer then goes into it, so don't also post it in the chat, and it starts with a short summary of the conversation so far. "
|
|
270
276
|
"Use set_reply_threads when someone asks you to always or never answer in threads here. "
|
|
271
277
|
"Use create_chat to create a new top-level destination outside the current conversation; it defaults to private, and public creation must explicitly set is_public with a space_id. "
|
|
272
278
|
"Use search_messages for exact catch-up across older chat history. "
|
|
@@ -616,11 +622,20 @@ def _set_reply_threads(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
616
622
|
turn = _current_turn()
|
|
617
623
|
if not turn.get("senderId"):
|
|
618
624
|
raise InlineToolError("Only the person who asked can change reply threads, and this turn has no sender", "forbidden")
|
|
625
|
+
parent_chat_id = turn.get("parentChatId") or ""
|
|
626
|
+
chat_type = turn.get("chatType") or "group"
|
|
627
|
+
if parent_chat_id:
|
|
628
|
+
# The change lands on the parent, so the parent's own type decides who may make it:
|
|
629
|
+
# someone added to a thread under a DM is in a group here but not in that DM.
|
|
630
|
+
parent = _sidecar_call("/chat", {"target": {"chatId": parent_chat_id}}).get("result")
|
|
631
|
+
if not isinstance(parent, dict) or not parent:
|
|
632
|
+
raise InlineToolError("parent chat access could not be confirmed", "forbidden")
|
|
633
|
+
chat_type = owner.chat_type_from_info(parent)
|
|
619
634
|
try:
|
|
620
635
|
return owner.set_reply_threads_from_tool(
|
|
621
636
|
chat_id=turn["chatId"],
|
|
622
|
-
chat_type=
|
|
623
|
-
|
|
637
|
+
chat_type=chat_type,
|
|
638
|
+
parent_chat_id=parent_chat_id,
|
|
624
639
|
actor_id=turn["senderId"],
|
|
625
640
|
actor_is_bot=bool(turn.get("senderIsBot")),
|
|
626
641
|
mode=mode,
|
|
@@ -647,9 +662,18 @@ def _create_thread(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
647
662
|
thread_id = _inline_id(result.get("chatId"))
|
|
648
663
|
if for_this_turn and owner is not None and thread_id:
|
|
649
664
|
owner.note_turn_thread(turn["chatId"], turn["messageId"], thread_id)
|
|
665
|
+
result["message"] = _NEW_TURN_THREAD_MESSAGE
|
|
650
666
|
return result
|
|
651
667
|
|
|
652
668
|
|
|
669
|
+
# Later messages in a thread opened mid-turn start a new session that reads the thread's
|
|
670
|
+
# history, so the answer that opens it carries what matters from this conversation.
|
|
671
|
+
_NEW_TURN_THREAD_MESSAGE = (
|
|
672
|
+
"Your answer goes into this thread. Start it with a short summary of what matters from the "
|
|
673
|
+
"conversation so far; later messages here start a new session that reads this thread."
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
|
|
653
677
|
_FOLLOW_CONFIRMATIONS = {
|
|
654
678
|
"following": "Following this chat.",
|
|
655
679
|
"unfollowed": "Unfollowed; mentions and replies still reach me.",
|