@inline-chat/hermes-agent-adapter 0.0.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.
@@ -75,6 +75,7 @@ _ACTION_MANIFEST = [
75
75
  ("unpin_message", "(chat_id?|user_id?, message_id?)", "Unpin an Inline message for the conversation."),
76
76
  ("list_pins", "(chat_id?)", "List pinned Inline message IDs for a chat or reply thread."),
77
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."),
78
79
  ("set_presence", "(chat_id?|user_id?, kind, comment?)", "Set the bot avatar presence/status message."),
79
80
  ]
80
81
  _ACTIONS = [name for name, _, _ in _ACTION_MANIFEST]
@@ -145,6 +146,7 @@ def tool_context_prompt(
145
146
  lines.append(f"- Triggering Inline message: `{message_id}`. Use it as `message_id` or `parent_message_id` when creating a reply thread.")
146
147
  if parent_message_id:
147
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.")
148
150
  lines.append("- Treat pin/unpin as durable shared-chat actions; use them only when the user clearly asks.")
149
151
  return "\n".join(lines)
150
152
 
@@ -159,6 +161,7 @@ INLINE_TOOL_SCHEMA = {
159
161
  "When chat_id is omitted, the tool uses the current Inline reply thread if present, otherwise the current chat. "
160
162
  "Do not use this tool to send the normal assistant reply; return text normally and Hermes will deliver it. "
161
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. "
162
165
  "Use search_messages for exact catch-up across older chat history. "
163
166
  "Use pin_message/unpin_message only when the user explicitly asks because pins are durable shared-chat state. "
164
167
  "Use set_presence only when explicitly changing the Inline avatar/status message. "
@@ -183,9 +186,20 @@ INLINE_TOOL_SCHEMA = {
183
186
  "type": "string",
184
187
  "description": "Parent message ID for create_thread. Defaults to the triggering message when available.",
185
188
  },
186
- "title": {"type": "string", "description": "Optional reply thread title for create_thread."},
187
- "description": {"type": "string", "description": "Optional reply thread description for create_thread."},
188
- "emoji": {"type": "string", "description": "Optional reply thread emoji for create_thread, or reaction emoji for reaction actions."},
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
+ },
189
203
  "query": {"type": "string", "description": "Search query for search_messages."},
190
204
  "text": {"type": "string", "description": "Message text for edit_message."},
191
205
  "parse_markdown": {"type": "boolean", "description": "Whether Inline should parse Markdown. Defaults to true."},
@@ -321,6 +335,27 @@ def _request_for_action(action: str, args: Dict[str, Any]) -> tuple[str, Dict[st
321
335
  body[key] = value
322
336
  return "/create-subthread", body
323
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
+
324
359
  if action == "set_presence":
325
360
  kind = _str(args.get("kind"))
326
361
  if kind not in _PRESENCE_KINDS:
@@ -474,10 +509,11 @@ def _compact_result(action: str, result: Dict[str, Any]) -> Dict[str, Any]:
474
509
  "pinnedMessageIds": pins if isinstance(pins, list) else [],
475
510
  "anchorMessage": _compact_message(result.get("anchorMessage")) if isinstance(result, dict) and result.get("anchorMessage") else None,
476
511
  }
477
- if action == "create_thread":
512
+ if action in {"create_thread", "create_chat"}:
478
513
  return {
479
514
  "chatId": _str(result.get("chatId")),
480
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,
481
517
  }
482
518
  return result
483
519
 
@@ -708,11 +744,28 @@ def _inline_id(value: Any) -> str:
708
744
  return ""
709
745
  if ":" in text:
710
746
  prefix, rest = text.split(":", 1)
711
- if prefix.lower() in {"chat", "thread", "user", "message", "msg"}:
747
+ if prefix.lower() in {"chat", "thread", "user", "space", "message", "msg"}:
712
748
  text = rest.strip()
713
749
  return text
714
750
 
715
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
+
716
769
  def _limit(value: Any) -> int:
717
770
  try:
718
771
  limit = int(value)