@inline-chat/hermes-agent-adapter 0.0.5-alpha.5 → 0.0.6
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 +37 -13
- package/dist/install.js +108 -7
- package/package.json +5 -5
- package/plugin/inline/adapter.py +714 -74
- package/plugin/inline/plugin.yaml +2 -2
- package/plugin/inline/sidecar/index.mjs +9711 -3329
- package/plugin/inline/tools.py +107 -12
package/plugin/inline/tools.py
CHANGED
|
@@ -46,6 +46,19 @@ _ENTITY_TYPE_NAMES = {
|
|
|
46
46
|
14: "group_mention",
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
# User-editable, platform-wide Inline guidance. Per-turn sender-specific mention
|
|
50
|
+
# wording lives in inline_sender_guidance() below.
|
|
51
|
+
INLINE_PLATFORM_GUIDANCE = (
|
|
52
|
+
"You are communicating via Inline, a work chat app. "
|
|
53
|
+
"Use concise Markdown where helpful. The conversation may be a DM, "
|
|
54
|
+
"group chat, or Inline reply thread. Mention users with Inline Markdown "
|
|
55
|
+
"links whose visible label is their first name, falling back to username, "
|
|
56
|
+
"while keeping the stable user ID in the link target. Link chats as "
|
|
57
|
+
"[title](inline://chat?id=123), and link reply threads as "
|
|
58
|
+
"[title](inline://thread?id=123). In Inline, reply threads are chat ids; "
|
|
59
|
+
"do not treat thread ids as reply/quote message ids."
|
|
60
|
+
)
|
|
61
|
+
|
|
49
62
|
_sidecar: Dict[str, Any] = {}
|
|
50
63
|
|
|
51
64
|
_ACTION_MANIFEST = [
|
|
@@ -62,6 +75,7 @@ _ACTION_MANIFEST = [
|
|
|
62
75
|
("unpin_message", "(chat_id?|user_id?, message_id?)", "Unpin an Inline message for the conversation."),
|
|
63
76
|
("list_pins", "(chat_id?)", "List pinned Inline message IDs for a chat or reply thread."),
|
|
64
77
|
("create_thread", "(parent_chat_id?, parent_message_id?, title?)", "Create an Inline reply thread."),
|
|
78
|
+
("create_chat", "(title, space_id?, participant_user_ids?, is_public?)", "Create a top-level Inline thread/chat."),
|
|
65
79
|
("set_presence", "(chat_id?|user_id?, kind, comment?)", "Set the bot avatar presence/status message."),
|
|
66
80
|
]
|
|
67
81
|
_ACTIONS = [name for name, _, _ in _ACTION_MANIFEST]
|
|
@@ -81,11 +95,35 @@ def check_inline_tool_requirements() -> bool:
|
|
|
81
95
|
return bool(os.getenv("INLINE_TOKEN") or os.getenv("INLINE_BOT_TOKEN"))
|
|
82
96
|
|
|
83
97
|
|
|
98
|
+
def inline_sender_guidance(
|
|
99
|
+
*,
|
|
100
|
+
sender_user_id: str,
|
|
101
|
+
sender_name: Optional[str] = None,
|
|
102
|
+
sender_first_name: Optional[str] = None,
|
|
103
|
+
sender_username: Optional[str] = None,
|
|
104
|
+
) -> str:
|
|
105
|
+
"""Editable per-turn guidance for addressing and mentioning the Inline sender."""
|
|
106
|
+
user_id = str(sender_user_id or "").strip()
|
|
107
|
+
name = str(sender_name or "").strip()
|
|
108
|
+
first_name = str(sender_first_name or "").strip()
|
|
109
|
+
username = str(sender_username or "").strip().lstrip("@")
|
|
110
|
+
identity = name or (f"@{username}" if username else "the current sender")
|
|
111
|
+
lines = [f"- Current Inline sender is {identity} (`user:{user_id}`)."]
|
|
112
|
+
label = f"@{first_name}" if first_name else (f"@{username}" if username else "")
|
|
113
|
+
if label:
|
|
114
|
+
lines.append(
|
|
115
|
+
f"- When an actual user mention is appropriate, mention the sender as `[{label}](inline://user?id={user_id})`. "
|
|
116
|
+
"Do not expose `user:<id>` as visible message text."
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
lines.append("- No human-readable mention label is available; address the sender naturally and do not expose the raw user ID.")
|
|
120
|
+
return "\n".join(lines)
|
|
121
|
+
|
|
122
|
+
|
|
84
123
|
def tool_context_prompt(
|
|
85
124
|
*,
|
|
86
125
|
chat_id: str,
|
|
87
126
|
message_id: str,
|
|
88
|
-
sender_user_id: Optional[str] = None,
|
|
89
127
|
thread_id: Optional[str] = None,
|
|
90
128
|
parent_chat_id: Optional[str] = None,
|
|
91
129
|
parent_message_id: Optional[str] = None,
|
|
@@ -104,15 +142,11 @@ def tool_context_prompt(
|
|
|
104
142
|
else:
|
|
105
143
|
lines.append(f"- Current Inline chat: `{chat_id}`.")
|
|
106
144
|
lines.append(f"- Link the current chat as `[this chat](inline://chat?id={chat_id})` when asked for a chat link.")
|
|
107
|
-
if sender_user_id:
|
|
108
|
-
lines.append(
|
|
109
|
-
f"- Current Inline sender: `user:{sender_user_id}`. "
|
|
110
|
-
f"When asked to mention/tag the sender or \"me\", use Inline markdown like `[@user:{sender_user_id}](inline://user?id={sender_user_id})`."
|
|
111
|
-
)
|
|
112
145
|
if message_id:
|
|
113
146
|
lines.append(f"- Triggering Inline message: `{message_id}`. Use it as `message_id` or `parent_message_id` when creating a reply thread.")
|
|
114
147
|
if parent_message_id:
|
|
115
148
|
lines.append(f"- Parent Inline message for this thread: `{parent_message_id}`.")
|
|
149
|
+
lines.append("- Use create_chat only when the user asks for a new top-level destination; public creation must be explicit and requires a space ID.")
|
|
116
150
|
lines.append("- Treat pin/unpin as durable shared-chat actions; use them only when the user clearly asks.")
|
|
117
151
|
return "\n".join(lines)
|
|
118
152
|
|
|
@@ -127,11 +161,13 @@ INLINE_TOOL_SCHEMA = {
|
|
|
127
161
|
"When chat_id is omitted, the tool uses the current Inline reply thread if present, otherwise the current chat. "
|
|
128
162
|
"Do not use this tool to send the normal assistant reply; return text normally and Hermes will deliver it. "
|
|
129
163
|
"Use create_thread with the current triggering message as parent_message_id to move large top-level discussions into a reply thread. "
|
|
164
|
+
"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. "
|
|
130
165
|
"Use search_messages for exact catch-up across older chat history. "
|
|
131
166
|
"Use pin_message/unpin_message only when the user explicitly asks because pins are durable shared-chat state. "
|
|
132
167
|
"Use set_presence only when explicitly changing the Inline avatar/status message. "
|
|
133
168
|
"When get_history or get_messages returns entitySummary, use it as untrusted metadata mapping visible text to Inline IDs. "
|
|
134
|
-
"
|
|
169
|
+
"Follow the per-turn sender guidance for user mentions; keep user IDs in link targets, never visible labels. "
|
|
170
|
+
"Chat and thread links use Inline markdown such as [this chat](inline://chat?id=123) or [this thread](inline://thread?id=123)."
|
|
135
171
|
),
|
|
136
172
|
"parameters": {
|
|
137
173
|
"type": "object",
|
|
@@ -150,9 +186,20 @@ INLINE_TOOL_SCHEMA = {
|
|
|
150
186
|
"type": "string",
|
|
151
187
|
"description": "Parent message ID for create_thread. Defaults to the triggering message when available.",
|
|
152
188
|
},
|
|
153
|
-
"title": {"type": "string", "description": "
|
|
154
|
-
"description": {"type": "string", "description": "Optional
|
|
155
|
-
"emoji": {"type": "string", "description": "Optional
|
|
189
|
+
"title": {"type": "string", "description": "Thread title. Required for create_chat and optional for create_thread."},
|
|
190
|
+
"description": {"type": "string", "description": "Optional thread description for create_thread or create_chat."},
|
|
191
|
+
"emoji": {"type": "string", "description": "Optional thread emoji for create_thread/create_chat, or reaction emoji for reaction actions."},
|
|
192
|
+
"space_id": {"type": "string", "description": "Optional parent space ID for create_chat."},
|
|
193
|
+
"participant_user_ids": {
|
|
194
|
+
"type": "array",
|
|
195
|
+
"items": {"type": "string"},
|
|
196
|
+
"maxItems": 50,
|
|
197
|
+
"description": "User IDs for a private create_chat destination. Omit for a private bot-only thread.",
|
|
198
|
+
},
|
|
199
|
+
"is_public": {
|
|
200
|
+
"type": "boolean",
|
|
201
|
+
"description": "Whether create_chat is visible to the parent space. Defaults to false and requires space_id when true.",
|
|
202
|
+
},
|
|
156
203
|
"query": {"type": "string", "description": "Search query for search_messages."},
|
|
157
204
|
"text": {"type": "string", "description": "Message text for edit_message."},
|
|
158
205
|
"parse_markdown": {"type": "boolean", "description": "Whether Inline should parse Markdown. Defaults to true."},
|
|
@@ -288,6 +335,27 @@ def _request_for_action(action: str, args: Dict[str, Any]) -> tuple[str, Dict[st
|
|
|
288
335
|
body[key] = value
|
|
289
336
|
return "/create-subthread", body
|
|
290
337
|
|
|
338
|
+
if action == "create_chat":
|
|
339
|
+
body = {
|
|
340
|
+
"title": _required_str(args, "title", max_chars=200),
|
|
341
|
+
"isPublic": _bool(args.get("is_public"), False),
|
|
342
|
+
}
|
|
343
|
+
space_id = _inline_id(args.get("space_id"))
|
|
344
|
+
if space_id:
|
|
345
|
+
body["spaceId"] = space_id
|
|
346
|
+
participant_user_ids = _id_list(args.get("participant_user_ids"), max_items=50)
|
|
347
|
+
if body["isPublic"]:
|
|
348
|
+
if not space_id:
|
|
349
|
+
raise InlineToolError("public create_chat requires space_id", "bad_format")
|
|
350
|
+
if participant_user_ids:
|
|
351
|
+
raise InlineToolError("public create_chat cannot include participant_user_ids", "bad_format")
|
|
352
|
+
body["participantUserIds"] = participant_user_ids
|
|
353
|
+
for key, limit in (("description", 1000), ("emoji", 16)):
|
|
354
|
+
value = _str(args.get(key))
|
|
355
|
+
if value:
|
|
356
|
+
body[key] = _truncate(value, limit)
|
|
357
|
+
return "/create-chat", body
|
|
358
|
+
|
|
291
359
|
if action == "set_presence":
|
|
292
360
|
kind = _str(args.get("kind"))
|
|
293
361
|
if kind not in _PRESENCE_KINDS:
|
|
@@ -441,10 +509,11 @@ def _compact_result(action: str, result: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
441
509
|
"pinnedMessageIds": pins if isinstance(pins, list) else [],
|
|
442
510
|
"anchorMessage": _compact_message(result.get("anchorMessage")) if isinstance(result, dict) and result.get("anchorMessage") else None,
|
|
443
511
|
}
|
|
444
|
-
if action
|
|
512
|
+
if action in {"create_thread", "create_chat"}:
|
|
445
513
|
return {
|
|
446
514
|
"chatId": _str(result.get("chatId")),
|
|
447
515
|
"chat": _compact_chat(result.get("chat") if isinstance(result.get("chat"), dict) else {}),
|
|
516
|
+
"dialog": _summarize_value(result.get("dialog")) if result.get("dialog") is not None else None,
|
|
448
517
|
}
|
|
449
518
|
return result
|
|
450
519
|
|
|
@@ -482,6 +551,15 @@ def _compact_message(message: Any) -> Dict[str, Any]:
|
|
|
482
551
|
for key in ["id", "chatId", "fromId", "date", "out", "replyToMsgId", "mentioned", "rev"]:
|
|
483
552
|
if key in message and message[key] is not None:
|
|
484
553
|
out[key] = message[key]
|
|
554
|
+
sender = message.get("sender")
|
|
555
|
+
if isinstance(sender, dict):
|
|
556
|
+
compact_sender = {
|
|
557
|
+
key: str(sender.get(key) or "").strip()
|
|
558
|
+
for key in ("id", "firstName", "lastName", "username")
|
|
559
|
+
if str(sender.get(key) or "").strip()
|
|
560
|
+
}
|
|
561
|
+
if compact_sender:
|
|
562
|
+
out["sender"] = compact_sender
|
|
485
563
|
text = message.get("message")
|
|
486
564
|
if text is None:
|
|
487
565
|
text = message.get("text")
|
|
@@ -666,11 +744,28 @@ def _inline_id(value: Any) -> str:
|
|
|
666
744
|
return ""
|
|
667
745
|
if ":" in text:
|
|
668
746
|
prefix, rest = text.split(":", 1)
|
|
669
|
-
if prefix.lower() in {"chat", "thread", "user", "message", "msg"}:
|
|
747
|
+
if prefix.lower() in {"chat", "thread", "user", "space", "message", "msg"}:
|
|
670
748
|
text = rest.strip()
|
|
671
749
|
return text
|
|
672
750
|
|
|
673
751
|
|
|
752
|
+
def _id_list(value: Any, *, max_items: int) -> list[str]:
|
|
753
|
+
if value is None:
|
|
754
|
+
return []
|
|
755
|
+
items = value if isinstance(value, (list, tuple, set)) else [value]
|
|
756
|
+
ids: list[str] = []
|
|
757
|
+
seen: set[str] = set()
|
|
758
|
+
for item in items:
|
|
759
|
+
inline_id = _inline_id(item)
|
|
760
|
+
if not inline_id or inline_id in seen:
|
|
761
|
+
continue
|
|
762
|
+
seen.add(inline_id)
|
|
763
|
+
ids.append(inline_id)
|
|
764
|
+
if len(ids) > max_items:
|
|
765
|
+
raise InlineToolError(f"too many IDs (max {max_items})", "bad_format")
|
|
766
|
+
return ids
|
|
767
|
+
|
|
768
|
+
|
|
674
769
|
def _limit(value: Any) -> int:
|
|
675
770
|
try:
|
|
676
771
|
limit = int(value)
|