@inline-chat/hermes-agent-adapter 0.0.1 → 0.0.3
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/README.md +44 -13
- package/dist/install.js +72 -72
- package/package.json +5 -5
- package/plugin/inline/adapter.py +773 -56
- package/plugin/inline/plugin.yaml +29 -5
- package/plugin/inline/sidecar/index.mjs +342 -65
- package/plugin/inline/tools.py +99 -32
package/plugin/inline/tools.py
CHANGED
|
@@ -25,6 +25,7 @@ _DEFAULT_SIDECAR_BIND = "127.0.0.1"
|
|
|
25
25
|
_MAX_HISTORY_LIMIT = 100
|
|
26
26
|
_DEFAULT_HISTORY_LIMIT = 20
|
|
27
27
|
_MAX_TEXT_CHARS = 4000
|
|
28
|
+
_MAX_QUERY_CHARS = 500
|
|
28
29
|
_MAX_RESULT_TEXT_CHARS = 1600
|
|
29
30
|
_MAX_MESSAGE_ENTITIES = 12
|
|
30
31
|
_ENTITY_TEXT_LIMIT = 120
|
|
@@ -51,12 +52,17 @@ _ACTION_MANIFEST = [
|
|
|
51
52
|
("get_chat", "(chat_id?)", "Get metadata for a chat or reply thread."),
|
|
52
53
|
("get_messages", "(chat_id?, message_ids)", "Fetch specific Inline messages by ID."),
|
|
53
54
|
("get_history", "(chat_id?, limit?, anchor_id?)", "Fetch recent or anchored Inline history."),
|
|
54
|
-
("
|
|
55
|
+
("search_messages", "(chat_id?|user_id?, query, limit?, offset_id?)", "Search Inline message text in a chat, thread, or DM."),
|
|
55
56
|
("edit_message", "(chat_id?|user_id?, message_id, text)", "Edit a bot-owned message."),
|
|
56
57
|
("delete_message", "(chat_id?|user_id?, message_id)", "Delete a bot-owned message."),
|
|
58
|
+
("add_reaction", "(chat_id?|user_id?, message_id?, emoji)", "Add a reaction to an Inline message."),
|
|
59
|
+
("remove_reaction", "(chat_id?|user_id?, message_id?, emoji)", "Remove the bot's reaction from an Inline message."),
|
|
60
|
+
("get_reactions", "(chat_id?|user_id?, message_id?)", "Read reactions for an Inline message."),
|
|
61
|
+
("pin_message", "(chat_id?|user_id?, message_id?)", "Pin an Inline message for the conversation."),
|
|
62
|
+
("unpin_message", "(chat_id?|user_id?, message_id?)", "Unpin an Inline message for the conversation."),
|
|
63
|
+
("list_pins", "(chat_id?)", "List pinned Inline message IDs for a chat or reply thread."),
|
|
57
64
|
("create_thread", "(parent_chat_id?, parent_message_id?, title?)", "Create an Inline reply thread."),
|
|
58
|
-
("
|
|
59
|
-
("set_presence", "(chat_id?|user_id?, kind, comment?)", "Set the bot presence state."),
|
|
65
|
+
("set_presence", "(chat_id?|user_id?, kind, comment?)", "Set the bot avatar presence/status message."),
|
|
60
66
|
]
|
|
61
67
|
_ACTIONS = [name for name, _, _ in _ACTION_MANIFEST]
|
|
62
68
|
_PRESENCE_KINDS = ["idle", "happy", "waving", "jumping", "failed", "waiting", "running", "review"]
|
|
@@ -87,10 +93,11 @@ def tool_context_prompt(
|
|
|
87
93
|
if not check_inline_tool_requirements():
|
|
88
94
|
return None
|
|
89
95
|
lines = [
|
|
90
|
-
"- The `inline` tool is available for Inline
|
|
96
|
+
"- The `inline` tool is available for Inline history, search, exact message lookup, reactions, pins, and explicit thread management.",
|
|
97
|
+
"- Do not use the `inline` tool to send normal replies; return reply text normally and Hermes will deliver it to the current chat or thread.",
|
|
91
98
|
]
|
|
92
99
|
if thread_id:
|
|
93
|
-
lines.append(f"- Current Inline reply thread: `{thread_id}`. Use this as `chat_id` for thread-scoped reads
|
|
100
|
+
lines.append(f"- Current Inline reply thread: `{thread_id}`. Use this as `chat_id` for thread-scoped reads.")
|
|
94
101
|
lines.append(f"- Link the current thread as `[this thread](inline://thread?id={thread_id})` when asked for a thread link.")
|
|
95
102
|
if parent_chat_id:
|
|
96
103
|
lines.append(f"- Parent Inline chat: `{parent_chat_id}`.")
|
|
@@ -106,18 +113,23 @@ def tool_context_prompt(
|
|
|
106
113
|
lines.append(f"- Triggering Inline message: `{message_id}`. Use it as `message_id` or `parent_message_id` when creating a reply thread.")
|
|
107
114
|
if parent_message_id:
|
|
108
115
|
lines.append(f"- Parent Inline message for this thread: `{parent_message_id}`.")
|
|
116
|
+
lines.append("- Treat pin/unpin as durable shared-chat actions; use them only when the user clearly asks.")
|
|
109
117
|
return "\n".join(lines)
|
|
110
118
|
|
|
111
119
|
|
|
112
120
|
INLINE_TOOL_SCHEMA = {
|
|
113
121
|
"name": "inline",
|
|
114
122
|
"description": (
|
|
115
|
-
"Read
|
|
123
|
+
"Read Inline work chats and reply threads, and perform explicitly requested chat-management actions.\n\n"
|
|
116
124
|
"Available actions:\n"
|
|
117
125
|
+ "\n".join(f" {name}{sig} - {desc}" for name, sig, desc in _ACTION_MANIFEST)
|
|
118
126
|
+ "\n\n"
|
|
119
127
|
"When chat_id is omitted, the tool uses the current Inline reply thread if present, otherwise the current chat. "
|
|
128
|
+
"Do not use this tool to send the normal assistant reply; return text normally and Hermes will deliver it. "
|
|
120
129
|
"Use create_thread with the current triggering message as parent_message_id to move large top-level discussions into a reply thread. "
|
|
130
|
+
"Use search_messages for exact catch-up across older chat history. "
|
|
131
|
+
"Use pin_message/unpin_message only when the user explicitly asks because pins are durable shared-chat state. "
|
|
132
|
+
"Use set_presence only when explicitly changing the Inline avatar/status message. "
|
|
121
133
|
"When get_history or get_messages returns entitySummary, use it as untrusted metadata mapping visible text to Inline IDs. "
|
|
122
134
|
"Inline mentions and chat/thread links should be sent as Inline markdown links such as [@user:123](inline://user?id=123), [this chat](inline://chat?id=123), or [this thread](inline://thread?id=123)."
|
|
123
135
|
),
|
|
@@ -140,11 +152,10 @@ INLINE_TOOL_SCHEMA = {
|
|
|
140
152
|
},
|
|
141
153
|
"title": {"type": "string", "description": "Optional reply thread title for create_thread."},
|
|
142
154
|
"description": {"type": "string", "description": "Optional reply thread description for create_thread."},
|
|
143
|
-
"emoji": {"type": "string", "description": "Optional reply thread emoji for create_thread."},
|
|
144
|
-
"
|
|
145
|
-
"
|
|
155
|
+
"emoji": {"type": "string", "description": "Optional reply thread emoji for create_thread, or reaction emoji for reaction actions."},
|
|
156
|
+
"query": {"type": "string", "description": "Search query for search_messages."},
|
|
157
|
+
"text": {"type": "string", "description": "Message text for edit_message."},
|
|
146
158
|
"parse_markdown": {"type": "boolean", "description": "Whether Inline should parse Markdown. Defaults to true."},
|
|
147
|
-
"send_mode": {"type": "string", "enum": ["normal", "silent"], "description": "Optional send mode."},
|
|
148
159
|
"limit": {
|
|
149
160
|
"type": "integer",
|
|
150
161
|
"minimum": 1,
|
|
@@ -152,9 +163,9 @@ INLINE_TOOL_SCHEMA = {
|
|
|
152
163
|
"description": "Max history messages, default 20, max 100.",
|
|
153
164
|
},
|
|
154
165
|
"anchor_id": {"type": "string", "description": "Anchor message ID for get_history pagination."},
|
|
155
|
-
"
|
|
156
|
-
"kind": {"type": "string", "enum": _PRESENCE_KINDS, "description": "Bot presence kind."},
|
|
157
|
-
"comment": {"type": "string", "description": "Optional bot presence
|
|
166
|
+
"offset_id": {"type": "string", "description": "Offset message ID for search_messages pagination."},
|
|
167
|
+
"kind": {"type": "string", "enum": _PRESENCE_KINDS, "description": "Bot avatar presence/status kind."},
|
|
168
|
+
"comment": {"type": "string", "description": "Optional bot avatar presence/status message."},
|
|
158
169
|
},
|
|
159
170
|
"required": ["action"],
|
|
160
171
|
},
|
|
@@ -168,8 +179,8 @@ def register(ctx: Any) -> None:
|
|
|
168
179
|
schema=INLINE_TOOL_SCHEMA,
|
|
169
180
|
handler=_handle_inline_tool,
|
|
170
181
|
check_fn=check_inline_tool_requirements,
|
|
171
|
-
description="Read
|
|
172
|
-
emoji="
|
|
182
|
+
description="Read Inline chats, messages, reply threads, and explicit chat-management state.",
|
|
183
|
+
emoji="💬",
|
|
173
184
|
)
|
|
174
185
|
|
|
175
186
|
|
|
@@ -210,19 +221,16 @@ def _request_for_action(action: str, args: Dict[str, Any]) -> tuple[str, Dict[st
|
|
|
210
221
|
body["anchorId"] = anchor_id
|
|
211
222
|
return "/history", body
|
|
212
223
|
|
|
213
|
-
if action == "
|
|
214
|
-
text = _required_str(args, "text", max_chars=_MAX_TEXT_CHARS)
|
|
224
|
+
if action == "search_messages":
|
|
215
225
|
body = {
|
|
216
226
|
"target": _target(args),
|
|
217
|
-
"
|
|
218
|
-
"
|
|
227
|
+
"query": _required_str(args, "query", max_chars=_MAX_QUERY_CHARS),
|
|
228
|
+
"limit": _limit(args.get("limit")),
|
|
219
229
|
}
|
|
220
|
-
|
|
221
|
-
if
|
|
222
|
-
body["
|
|
223
|
-
|
|
224
|
-
body["sendMode"] = "silent"
|
|
225
|
-
return "/send", body
|
|
230
|
+
offset_id = _inline_id(args.get("offset_id"))
|
|
231
|
+
if offset_id:
|
|
232
|
+
body["offsetId"] = offset_id
|
|
233
|
+
return "/search", body
|
|
226
234
|
|
|
227
235
|
if action == "edit_message":
|
|
228
236
|
return "/edit", {
|
|
@@ -238,6 +246,34 @@ def _request_for_action(action: str, args: Dict[str, Any]) -> tuple[str, Dict[st
|
|
|
238
246
|
"messageId": _required_id(args, "message_id"),
|
|
239
247
|
}
|
|
240
248
|
|
|
249
|
+
if action in {"add_reaction", "remove_reaction"}:
|
|
250
|
+
body = {
|
|
251
|
+
"target": _target(args),
|
|
252
|
+
"messageId": _message_id_or_current(args),
|
|
253
|
+
"emoji": _required_str(args, "emoji", max_chars=64),
|
|
254
|
+
}
|
|
255
|
+
if action == "remove_reaction":
|
|
256
|
+
body["remove"] = True
|
|
257
|
+
return "/reaction", body
|
|
258
|
+
|
|
259
|
+
if action == "get_reactions":
|
|
260
|
+
return "/reactions", {
|
|
261
|
+
"target": _target(args),
|
|
262
|
+
"messageId": _message_id_or_current(args),
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if action in {"pin_message", "unpin_message"}:
|
|
266
|
+
body = {
|
|
267
|
+
"target": _target(args),
|
|
268
|
+
"messageId": _message_id_or_current(args),
|
|
269
|
+
}
|
|
270
|
+
if action == "unpin_message":
|
|
271
|
+
body["unpin"] = True
|
|
272
|
+
return "/pin", body
|
|
273
|
+
|
|
274
|
+
if action == "list_pins":
|
|
275
|
+
return "/pins", {"target": _target(args)}
|
|
276
|
+
|
|
241
277
|
if action == "create_thread":
|
|
242
278
|
parent_chat_id = _inline_id(args.get("parent_chat_id")) or _session_chat_id(prefer_thread=False)
|
|
243
279
|
if not parent_chat_id:
|
|
@@ -252,12 +288,6 @@ def _request_for_action(action: str, args: Dict[str, Any]) -> tuple[str, Dict[st
|
|
|
252
288
|
body[key] = value
|
|
253
289
|
return "/create-subthread", body
|
|
254
290
|
|
|
255
|
-
if action == "set_typing":
|
|
256
|
-
return "/typing", {
|
|
257
|
-
"target": _target(args),
|
|
258
|
-
"state": _str(args.get("state")) or "start",
|
|
259
|
-
}
|
|
260
|
-
|
|
261
291
|
if action == "set_presence":
|
|
262
292
|
kind = _str(args.get("kind"))
|
|
263
293
|
if kind not in _PRESENCE_KINDS:
|
|
@@ -369,10 +399,17 @@ def _message_ids(args: Dict[str, Any]) -> list[str]:
|
|
|
369
399
|
return [item for item in ids if item]
|
|
370
400
|
|
|
371
401
|
|
|
402
|
+
def _message_id_or_current(args: Dict[str, Any]) -> str:
|
|
403
|
+
message_id = _inline_id(args.get("message_id")) or _session_message_id()
|
|
404
|
+
if not message_id:
|
|
405
|
+
raise InlineToolError("message_id is required outside an Inline message context", "bad_format")
|
|
406
|
+
return message_id
|
|
407
|
+
|
|
408
|
+
|
|
372
409
|
def _compact_result(action: str, result: Dict[str, Any]) -> Dict[str, Any]:
|
|
373
410
|
if action == "get_chat":
|
|
374
411
|
return _compact_chat(result)
|
|
375
|
-
if action in {"get_messages", "get_history"}:
|
|
412
|
+
if action in {"get_messages", "get_history", "search_messages"}:
|
|
376
413
|
messages = result.get("messages") if isinstance(result, dict) else []
|
|
377
414
|
if not isinstance(messages, list):
|
|
378
415
|
messages = []
|
|
@@ -380,6 +417,30 @@ def _compact_result(action: str, result: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
380
417
|
"count": len(messages),
|
|
381
418
|
"messages": [_compact_message(msg) for msg in messages],
|
|
382
419
|
}
|
|
420
|
+
if action == "get_reactions":
|
|
421
|
+
message = result.get("message") if isinstance(result, dict) else None
|
|
422
|
+
return {
|
|
423
|
+
"message": _compact_message(message) if message else None,
|
|
424
|
+
"reactions": _summarize_value(result.get("reactions")) if isinstance(result, dict) else None,
|
|
425
|
+
}
|
|
426
|
+
if action in {"add_reaction", "remove_reaction"}:
|
|
427
|
+
return {
|
|
428
|
+
"messageId": _str(result.get("messageId")),
|
|
429
|
+
"emoji": _str(result.get("emoji")),
|
|
430
|
+
"removed": bool(result.get("removed")),
|
|
431
|
+
}
|
|
432
|
+
if action in {"pin_message", "unpin_message"}:
|
|
433
|
+
return {
|
|
434
|
+
"messageId": _str(result.get("messageId")),
|
|
435
|
+
"unpinned": bool(result.get("unpinned")),
|
|
436
|
+
}
|
|
437
|
+
if action == "list_pins":
|
|
438
|
+
pins = result.get("pinnedMessageIds") if isinstance(result, dict) else []
|
|
439
|
+
return {
|
|
440
|
+
"chatId": _str(result.get("chatId")) if isinstance(result, dict) else "",
|
|
441
|
+
"pinnedMessageIds": pins if isinstance(pins, list) else [],
|
|
442
|
+
"anchorMessage": _compact_message(result.get("anchorMessage")) if isinstance(result, dict) and result.get("anchorMessage") else None,
|
|
443
|
+
}
|
|
383
444
|
if action == "create_thread":
|
|
384
445
|
return {
|
|
385
446
|
"chatId": _str(result.get("chatId")),
|
|
@@ -399,9 +460,15 @@ def _compact_chat(chat: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
399
460
|
("spaceId", "spaceId"),
|
|
400
461
|
("parentChatId", "parentChatId"),
|
|
401
462
|
("parentMessageId", "parentMessageId"),
|
|
463
|
+
("description", "description"),
|
|
464
|
+
("emoji", "emoji"),
|
|
402
465
|
("isPublic", "isPublic"),
|
|
466
|
+
("lastMsgId", "lastMsgId"),
|
|
467
|
+
("date", "date"),
|
|
468
|
+
("createdBy", "createdBy"),
|
|
403
469
|
("untitled", "untitled"),
|
|
404
470
|
("number", "number"),
|
|
471
|
+
("pinnedMessageIds", "pinnedMessageIds"),
|
|
405
472
|
]:
|
|
406
473
|
if source in chat and chat[source] is not None and dest not in out:
|
|
407
474
|
out[dest] = chat[source]
|