@inline-openclaw/inline 0.0.25 → 0.0.26

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/index.js CHANGED
@@ -5547,6 +5547,14 @@ var require_websocket_server = __commonJS((exports, module) => {
5547
5547
  }
5548
5548
  });
5549
5549
 
5550
+ // src/inline/channel.ts
5551
+ import {
5552
+ deleteAccountFromConfigSection,
5553
+ setAccountEnabledInConfigSection
5554
+ } from "openclaw/plugin-sdk/core";
5555
+ import { buildDmGroupAccountAllowlistAdapter } from "openclaw/plugin-sdk/allowlist-config-edit";
5556
+ import { buildTokenChannelStatusSummary } from "openclaw/plugin-sdk/status-helpers";
5557
+
5550
5558
  // ../protocol/dist/core.js
5551
5559
  var import_runtime = __toESM(require_commonjs(), 1);
5552
5560
  var import_runtime2 = __toESM(require_commonjs(), 1);
@@ -35176,6 +35184,7 @@ var REACTION_TARGET_LOOKUP_LIMIT = 8;
35176
35184
  var REPLY_TARGET_LOOKUP_LIMIT = 8;
35177
35185
  var ATTACHMENT_CONTEXT_LIMIT = 6;
35178
35186
  var DEFAULT_INLINE_MEDIA_MAX_BYTES = 300 * 1024 * 1024;
35187
+ var EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again.";
35179
35188
  var GET_MESSAGES_METHOD2 = typeof Method.GET_MESSAGES === "number" && Number.isInteger(Method.GET_MESSAGES) && Method.GET_MESSAGES > 0 ? Method.GET_MESSAGES : null;
35180
35189
  function normalizeAllowEntry(raw) {
35181
35190
  return raw.trim().replace(/^inline:/i, "").replace(/^user:/i, "");
@@ -36488,193 +36497,287 @@ ${currentEntityText}` : null
36488
36497
  failed: false,
36489
36498
  opChain: Promise.resolve()
36490
36499
  };
36500
+ let finalDeliveredForCurrentAssistantMessage = false;
36501
+ const resetEditStreamForAssistantMessage = async () => {
36502
+ await editStreamState.opChain;
36503
+ const hasActiveState = editStreamState.messageId != null || editStreamState.accumulatedText.length > 0 || editStreamState.lastPartialText.length > 0 || editStreamState.finalTextAccumulator.length > 0;
36504
+ if (!hasActiveState)
36505
+ return;
36506
+ editStreamState.messageId = null;
36507
+ editStreamState.accumulatedText = "";
36508
+ editStreamState.lastPartialText = "";
36509
+ editStreamState.finalTextAccumulator = "";
36510
+ editStreamState.failed = false;
36511
+ finalDeliveredForCurrentAssistantMessage = false;
36512
+ };
36513
+ const resetEditStreamOnBoundary = async () => {
36514
+ if (!streamViaEditMessage)
36515
+ return;
36516
+ await resetEditStreamForAssistantMessage();
36517
+ };
36518
+ const handlePartialStreamPayload = async (payload) => {
36519
+ if (editStreamState.failed)
36520
+ return;
36521
+ if ((payload.mediaUrls?.length ?? 0) > 0)
36522
+ return;
36523
+ const partialText = typeof payload.text === "string" ? payload.text : "";
36524
+ if (!partialText || partialText === editStreamState.lastPartialText)
36525
+ return;
36526
+ editStreamState.lastPartialText = partialText;
36527
+ const nextText = rewriteNumericMentionsToUsernames(extractCompleteParagraphText(partialText), senderProfilesById).trim();
36528
+ if (!nextText || nextText === editStreamState.accumulatedText)
36529
+ return;
36530
+ editStreamState.opChain = editStreamState.opChain.then(async () => {
36531
+ if (editStreamState.failed)
36532
+ return;
36533
+ if (!nextText || nextText === editStreamState.accumulatedText)
36534
+ return;
36535
+ try {
36536
+ if (editStreamState.messageId == null) {
36537
+ const sent = await client.sendMessage({
36538
+ chatId,
36539
+ text: nextText,
36540
+ ...defaultReplyToMsgId != null ? { replyToMsgId: defaultReplyToMsgId } : {},
36541
+ parseMarkdown
36542
+ });
36543
+ if (sent.messageId == null) {
36544
+ throw new Error("inline edit stream: sendMessage returned no messageId");
36545
+ }
36546
+ editStreamState.messageId = sent.messageId;
36547
+ rememberBotMessageId(botMessageIdsByChat, chatId, sent.messageId);
36548
+ } else {
36549
+ const result = await client.invokeRaw(Method.EDIT_MESSAGE, {
36550
+ oneofKind: "editMessage",
36551
+ editMessage: {
36552
+ messageId: editStreamState.messageId,
36553
+ peerId: buildChatPeer2(chatId),
36554
+ text: nextText,
36555
+ parseMarkdown
36556
+ }
36557
+ });
36558
+ if (result.oneofKind !== "editMessage") {
36559
+ throw new Error(`inline edit stream: expected editMessage result, got ${String(result.oneofKind)}`);
36560
+ }
36561
+ }
36562
+ editStreamState.accumulatedText = nextText;
36563
+ statusSink?.({ lastOutboundAt: Date.now() });
36564
+ } catch (error48) {
36565
+ editStreamState.failed = true;
36566
+ runtime2.error?.(`inline edit stream failed: ${String(error48)}`);
36567
+ }
36568
+ });
36569
+ await editStreamState.opChain;
36570
+ };
36491
36571
  const replyOptions = {
36492
36572
  ...onModelSelected ? { onModelSelected } : {},
36493
36573
  blockReplyTimeoutMs: 25000,
36574
+ ...streamViaEditMessage ? {
36575
+ onAssistantMessageStart: async () => {
36576
+ await resetEditStreamOnBoundary();
36577
+ }
36578
+ } : {},
36494
36579
  ...streamViaEditMessage ? {
36495
36580
  onPartialReply: async (payload) => {
36496
- if (editStreamState.failed)
36497
- return;
36498
- if ((payload.mediaUrls?.length ?? 0) > 0)
36499
- return;
36500
- const partialText = typeof payload.text === "string" ? payload.text : "";
36501
- if (!partialText || partialText === editStreamState.lastPartialText)
36502
- return;
36503
- editStreamState.lastPartialText = partialText;
36504
- const nextText = rewriteNumericMentionsToUsernames(extractCompleteParagraphText(partialText), senderProfilesById).trim();
36505
- if (!nextText || nextText === editStreamState.accumulatedText)
36506
- return;
36507
- editStreamState.opChain = editStreamState.opChain.then(async () => {
36508
- if (editStreamState.failed)
36509
- return;
36510
- if (!nextText || nextText === editStreamState.accumulatedText)
36511
- return;
36512
- try {
36513
- if (editStreamState.messageId == null) {
36581
+ await handlePartialStreamPayload(payload);
36582
+ }
36583
+ } : {},
36584
+ ...streamViaEditMessage ? {
36585
+ onReasoningStream: async (payload) => {
36586
+ await handlePartialStreamPayload(payload);
36587
+ }
36588
+ } : {},
36589
+ ...streamViaEditMessage ? {
36590
+ onReasoningEnd: async () => {
36591
+ await editStreamState.opChain;
36592
+ }
36593
+ } : {},
36594
+ ...streamViaEditMessage ? {
36595
+ onToolStart: async () => {
36596
+ await resetEditStreamOnBoundary();
36597
+ }
36598
+ } : {},
36599
+ ...streamViaEditMessage ? {
36600
+ onCompactionStart: async () => {
36601
+ await resetEditStreamOnBoundary();
36602
+ }
36603
+ } : {},
36604
+ ...streamViaEditMessage ? {
36605
+ onCompactionEnd: async () => {
36606
+ await resetEditStreamOnBoundary();
36607
+ }
36608
+ } : {},
36609
+ ...typeof disableBlockStreaming === "boolean" ? { disableBlockStreaming } : {}
36610
+ };
36611
+ try {
36612
+ let delivered = false;
36613
+ let skippedNonSilent = false;
36614
+ let failedNonSilent = false;
36615
+ let dispatchError = null;
36616
+ try {
36617
+ await core3.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
36618
+ ctx: ctxPayload,
36619
+ cfg,
36620
+ dispatcherOptions: {
36621
+ ...prefixOptions,
36622
+ ...typingCallbacks ? { typingCallbacks } : {},
36623
+ deliver: async (payload, info) => {
36624
+ const rawText = payload.text ?? "";
36625
+ const mediaList = payload.mediaUrls?.length ? payload.mediaUrls : payload.mediaUrl ? [payload.mediaUrl] : [];
36626
+ const outboundText = rewriteNumericMentionsToUsernames(rawText, senderProfilesById);
36627
+ const outboundActions = resolveInlineReplyActions(payload);
36628
+ const infoKind = typeof info?.kind === "string" ? info.kind : undefined;
36629
+ let replyToMsgId;
36630
+ if (payload.replyToId != null) {
36631
+ try {
36632
+ replyToMsgId = BigInt(payload.replyToId);
36633
+ } catch {}
36634
+ }
36635
+ if (replyToMsgId == null && isGroup && msg.replyToMsgId != null) {
36636
+ replyToMsgId = msg.id;
36637
+ }
36638
+ const rememberSent = (messageId) => {
36639
+ if (messageId != null) {
36640
+ rememberBotMessageId(botMessageIdsByChat, chatId, messageId);
36641
+ }
36642
+ };
36643
+ const sendTextFallback = async (text, includeReplyTo, includeActions) => {
36644
+ if (!text.trim())
36645
+ return;
36514
36646
  const sent = await client.sendMessage({
36515
36647
  chatId,
36516
- text: nextText,
36517
- ...defaultReplyToMsgId != null ? { replyToMsgId: defaultReplyToMsgId } : {},
36648
+ text,
36649
+ ...includeReplyTo && replyToMsgId != null ? { replyToMsgId } : {},
36650
+ ...includeActions && outboundActions !== undefined ? { actions: outboundActions } : {},
36518
36651
  parseMarkdown
36519
36652
  });
36520
- if (sent.messageId == null) {
36521
- throw new Error("inline edit stream: sendMessage returned no messageId");
36522
- }
36523
- editStreamState.messageId = sent.messageId;
36524
- rememberBotMessageId(botMessageIdsByChat, chatId, sent.messageId);
36525
- } else {
36653
+ rememberSent(sent.messageId);
36654
+ delivered = true;
36655
+ };
36656
+ const updateStreamedMessage = async (text, actions) => {
36657
+ await editStreamState.opChain;
36658
+ if (editStreamState.messageId == null)
36659
+ return false;
36660
+ const nextText = text.trim();
36661
+ const textForEdit = nextText || editStreamState.accumulatedText;
36662
+ if (!textForEdit)
36663
+ return true;
36664
+ const shouldSkipTextUpdate = !editStreamState.failed && textForEdit === editStreamState.accumulatedText;
36665
+ if (shouldSkipTextUpdate && actions === undefined)
36666
+ return true;
36526
36667
  const result = await client.invokeRaw(Method.EDIT_MESSAGE, {
36527
36668
  oneofKind: "editMessage",
36528
36669
  editMessage: {
36529
36670
  messageId: editStreamState.messageId,
36530
36671
  peerId: buildChatPeer2(chatId),
36531
- text: nextText,
36532
- parseMarkdown
36672
+ text: textForEdit,
36673
+ parseMarkdown,
36674
+ ...actions !== undefined ? { actions } : {}
36533
36675
  }
36534
36676
  });
36535
36677
  if (result.oneofKind !== "editMessage") {
36536
36678
  throw new Error(`inline edit stream: expected editMessage result, got ${String(result.oneofKind)}`);
36537
36679
  }
36538
- }
36539
- editStreamState.accumulatedText = nextText;
36540
- statusSink?.({ lastOutboundAt: Date.now() });
36541
- } catch (error48) {
36542
- editStreamState.failed = true;
36543
- runtime2.error?.(`inline edit stream failed: ${String(error48)}`);
36544
- }
36545
- });
36546
- await editStreamState.opChain;
36547
- }
36548
- } : {},
36549
- ...typeof disableBlockStreaming === "boolean" ? { disableBlockStreaming } : {}
36550
- };
36551
- try {
36552
- await core3.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
36553
- ctx: ctxPayload,
36554
- cfg,
36555
- dispatcherOptions: {
36556
- ...prefixOptions,
36557
- ...typingCallbacks ? { typingCallbacks } : {},
36558
- deliver: async (payload) => {
36559
- const rawText = payload.text ?? "";
36560
- const mediaList = payload.mediaUrls?.length ? payload.mediaUrls : payload.mediaUrl ? [payload.mediaUrl] : [];
36561
- const outboundText = rewriteNumericMentionsToUsernames(rawText, senderProfilesById);
36562
- const outboundActions = resolveInlineReplyActions(payload);
36563
- let replyToMsgId;
36564
- if (payload.replyToId != null) {
36565
- try {
36566
- replyToMsgId = BigInt(payload.replyToId);
36567
- } catch {}
36568
- }
36569
- if (replyToMsgId == null && isGroup && msg.replyToMsgId != null) {
36570
- replyToMsgId = msg.id;
36571
- }
36572
- const rememberSent = (messageId) => {
36573
- if (messageId != null) {
36574
- rememberBotMessageId(botMessageIdsByChat, chatId, messageId);
36575
- }
36576
- };
36577
- const sendTextFallback = async (text, includeReplyTo, includeActions) => {
36578
- if (!text.trim())
36579
- return;
36580
- const sent = await client.sendMessage({
36581
- chatId,
36582
- text,
36583
- ...includeReplyTo && replyToMsgId != null ? { replyToMsgId } : {},
36584
- ...includeActions && outboundActions !== undefined ? { actions: outboundActions } : {},
36585
- parseMarkdown
36586
- });
36587
- rememberSent(sent.messageId);
36588
- };
36589
- const updateStreamedMessage = async (text, actions) => {
36590
- await editStreamState.opChain;
36591
- if (editStreamState.messageId == null)
36592
- return false;
36593
- const nextText = text.trim();
36594
- const textForEdit = nextText || editStreamState.accumulatedText;
36595
- if (!textForEdit)
36596
- return true;
36597
- const shouldSkipTextUpdate = !editStreamState.failed && textForEdit === editStreamState.accumulatedText;
36598
- if (shouldSkipTextUpdate && actions === undefined)
36599
- return true;
36600
- const result = await client.invokeRaw(Method.EDIT_MESSAGE, {
36601
- oneofKind: "editMessage",
36602
- editMessage: {
36603
- messageId: editStreamState.messageId,
36604
- peerId: buildChatPeer2(chatId),
36605
- text: textForEdit,
36606
- parseMarkdown,
36607
- ...actions !== undefined ? { actions } : {}
36680
+ if (!shouldSkipTextUpdate) {
36681
+ editStreamState.accumulatedText = textForEdit;
36682
+ editStreamState.lastPartialText = textForEdit;
36608
36683
  }
36609
- });
36610
- if (result.oneofKind !== "editMessage") {
36611
- throw new Error(`inline edit stream: expected editMessage result, got ${String(result.oneofKind)}`);
36612
- }
36613
- if (!shouldSkipTextUpdate) {
36614
- editStreamState.accumulatedText = textForEdit;
36615
- editStreamState.lastPartialText = textForEdit;
36616
- }
36617
- editStreamState.failed = false;
36618
- return true;
36619
- };
36620
- if (mediaList.length === 0) {
36621
- if (streamViaEditMessage && editStreamState.messageId != null) {
36622
- if (outboundText.trim()) {
36623
- editStreamState.finalTextAccumulator += outboundText;
36684
+ editStreamState.failed = false;
36685
+ return true;
36686
+ };
36687
+ if (mediaList.length === 0) {
36688
+ if (streamViaEditMessage && infoKind === "final" && finalDeliveredForCurrentAssistantMessage && editStreamState.messageId != null) {
36689
+ await resetEditStreamForAssistantMessage();
36624
36690
  }
36625
- if (!editStreamState.finalTextAccumulator.trim() && outboundActions === undefined) {
36691
+ if (streamViaEditMessage && editStreamState.messageId != null) {
36692
+ if (outboundText.trim()) {
36693
+ editStreamState.finalTextAccumulator += outboundText;
36694
+ }
36695
+ if (!editStreamState.finalTextAccumulator.trim() && outboundActions === undefined) {
36696
+ return;
36697
+ }
36698
+ await updateStreamedMessage(editStreamState.finalTextAccumulator, outboundActions);
36699
+ delivered = true;
36700
+ if (infoKind === "final") {
36701
+ finalDeliveredForCurrentAssistantMessage = true;
36702
+ }
36703
+ statusSink?.({ lastOutboundAt: Date.now() });
36626
36704
  return;
36627
36705
  }
36628
- await updateStreamedMessage(editStreamState.finalTextAccumulator, outboundActions);
36706
+ if (!outboundText.trim())
36707
+ return;
36708
+ await sendTextFallback(outboundText, true, true);
36629
36709
  statusSink?.({ lastOutboundAt: Date.now() });
36630
36710
  return;
36631
36711
  }
36632
- if (!outboundText.trim())
36633
- return;
36634
- await sendTextFallback(outboundText, true, true);
36635
- statusSink?.({ lastOutboundAt: Date.now() });
36636
- return;
36637
- }
36638
- if (streamViaEditMessage && editStreamState.messageId != null && outboundText.trim()) {
36639
- await updateStreamedMessage(outboundText, outboundActions);
36640
- }
36641
- for (let index = 0;index < mediaList.length; index++) {
36642
- const mediaUrl = mediaList[index];
36643
- if (!mediaUrl?.trim())
36644
- continue;
36645
- const isFirst = index === 0;
36646
- const shouldAttachActionsToMedia = isFirst && (!(streamViaEditMessage && editStreamState.messageId != null) || !outboundText.trim());
36647
- const caption = isFirst && !(streamViaEditMessage && editStreamState.messageId != null) ? outboundText : "";
36648
- try {
36649
- const media = await uploadInlineMediaFromUrl({
36650
- client,
36651
- cfg,
36652
- accountId: account.accountId,
36653
- mediaUrl
36654
- });
36655
- const sent = await client.sendMessage({
36656
- chatId,
36657
- ...caption ? { text: caption } : {},
36658
- media,
36659
- ...isFirst && replyToMsgId != null ? { replyToMsgId } : {},
36660
- ...shouldAttachActionsToMedia && outboundActions !== undefined ? { actions: outboundActions } : {},
36661
- ...caption ? { parseMarkdown } : {}
36662
- });
36663
- rememberSent(sent.messageId);
36664
- } catch (error48) {
36665
- runtime2.error?.(`inline media upload failed; falling back to url text (${String(error48)})`);
36666
- const fallbackText = caption ? `${caption}
36712
+ if (streamViaEditMessage && editStreamState.messageId != null && outboundText.trim()) {
36713
+ await updateStreamedMessage(outboundText, outboundActions);
36714
+ }
36715
+ for (let index = 0;index < mediaList.length; index++) {
36716
+ const mediaUrl = mediaList[index];
36717
+ if (!mediaUrl?.trim())
36718
+ continue;
36719
+ const isFirst = index === 0;
36720
+ const shouldAttachActionsToMedia = isFirst && (!(streamViaEditMessage && editStreamState.messageId != null) || !outboundText.trim());
36721
+ const caption = isFirst && !(streamViaEditMessage && editStreamState.messageId != null) ? outboundText : "";
36722
+ try {
36723
+ const media = await uploadInlineMediaFromUrl({
36724
+ client,
36725
+ cfg,
36726
+ accountId: account.accountId,
36727
+ mediaUrl
36728
+ });
36729
+ const sent = await client.sendMessage({
36730
+ chatId,
36731
+ ...caption ? { text: caption } : {},
36732
+ media,
36733
+ ...isFirst && replyToMsgId != null ? { replyToMsgId } : {},
36734
+ ...shouldAttachActionsToMedia && outboundActions !== undefined ? { actions: outboundActions } : {},
36735
+ ...caption ? { parseMarkdown } : {}
36736
+ });
36737
+ rememberSent(sent.messageId);
36738
+ delivered = true;
36739
+ } catch (error48) {
36740
+ runtime2.error?.(`inline media upload failed; falling back to url text (${String(error48)})`);
36741
+ const fallbackText = caption ? `${caption}
36667
36742
 
36668
36743
  Attachment: ${mediaUrl}` : `Attachment: ${mediaUrl}`;
36669
- await sendTextFallback(fallbackText, isFirst, isFirst);
36744
+ await sendTextFallback(fallbackText, isFirst, isFirst);
36745
+ }
36746
+ }
36747
+ statusSink?.({ lastOutboundAt: Date.now() });
36748
+ },
36749
+ onSkip: (_payload, info) => {
36750
+ if (info?.reason !== "silent") {
36751
+ skippedNonSilent = true;
36670
36752
  }
36753
+ },
36754
+ onError: (err, info) => {
36755
+ failedNonSilent = true;
36756
+ runtime2.error?.(`inline ${info?.kind ?? "final"} reply failed: ${String(err)}`);
36671
36757
  }
36672
- statusSink?.({ lastOutboundAt: Date.now() });
36673
36758
  },
36674
- onError: (err, info) => runtime2.error?.(`inline ${info.kind} reply failed: ${String(err)}`)
36675
- },
36676
- replyOptions
36677
- });
36759
+ replyOptions
36760
+ });
36761
+ } catch (error48) {
36762
+ dispatchError = error48;
36763
+ runtime2.error?.(`inline dispatch failed: ${String(error48)}`);
36764
+ }
36765
+ if (!delivered && streamViaEditMessage && editStreamState.messageId != null) {
36766
+ delivered = true;
36767
+ }
36768
+ if (!delivered && (dispatchError != null || skippedNonSilent || failedNonSilent)) {
36769
+ const fallbackText = dispatchError != null ? "Something went wrong while processing your request. Please try again." : EMPTY_RESPONSE_FALLBACK;
36770
+ const sent = await client.sendMessage({
36771
+ chatId,
36772
+ text: fallbackText,
36773
+ ...defaultReplyToMsgId != null ? { replyToMsgId: defaultReplyToMsgId } : {},
36774
+ parseMarkdown
36775
+ });
36776
+ if (sent.messageId != null) {
36777
+ rememberBotMessageId(botMessageIdsByChat, chatId, sent.messageId);
36778
+ }
36779
+ statusSink?.({ lastOutboundAt: Date.now() });
36780
+ }
36678
36781
  } finally {
36679
36782
  if (callbackActionEvent && !callbackActionAnswered) {
36680
36783
  try {
@@ -36936,12 +37039,33 @@ function parseInlineId(raw, label) {
36936
37039
  throw new Error(`inline action: missing ${label}`);
36937
37040
  }
36938
37041
  if (!/^[0-9]+$/.test(trimmed)) {
37042
+ if (/message/i.test(label)) {
37043
+ const prefixed = trimmed.match(/^(?:message|msg)\s*#?\s*([0-9]+)$/i)?.[1];
37044
+ if (prefixed) {
37045
+ return BigInt(prefixed);
37046
+ }
37047
+ }
36939
37048
  throw new Error(`inline action: invalid ${label} "${raw}"`);
36940
37049
  }
36941
37050
  return BigInt(trimmed);
36942
37051
  }
36943
37052
  throw new Error(`inline action: missing ${label}`);
36944
37053
  }
37054
+ function resolveReactionMessageId(params) {
37055
+ const explicit = readFlexibleId(params.args, "messageId") ?? readStringParam(params.args, "messageId");
37056
+ if (explicit) {
37057
+ return explicit;
37058
+ }
37059
+ const fromContext = params.toolContext?.currentMessageId;
37060
+ if (typeof fromContext === "number" && Number.isFinite(fromContext)) {
37061
+ return String(Math.trunc(fromContext));
37062
+ }
37063
+ if (typeof fromContext === "string") {
37064
+ const trimmed = fromContext.trim();
37065
+ return trimmed || undefined;
37066
+ }
37067
+ return;
37068
+ }
36945
37069
  function parseOptionalInlineId(raw, label) {
36946
37070
  if (raw == null)
36947
37071
  return;
@@ -37195,6 +37319,7 @@ function mapChatEntry(params) {
37195
37319
  }
37196
37320
  return {
37197
37321
  id: String(params.chat.id),
37322
+ target: `chat:${String(params.chat.id)}`,
37198
37323
  title: params.chat.title,
37199
37324
  spaceId: params.chat.spaceId != null ? String(params.chat.spaceId) : null,
37200
37325
  isPublic: params.chat.isPublic ?? false,
@@ -37206,11 +37331,29 @@ function mapChatEntry(params) {
37206
37331
  peer: peer?.oneofKind === "user" ? {
37207
37332
  kind: "user",
37208
37333
  id: String(peer.user.userId),
37334
+ target: `user:${String(peer.user.userId)}`,
37209
37335
  username: peerUser?.username ?? null,
37210
37336
  name: peerUser ? buildInlineUserDisplayName(peerUser) : null
37211
- } : peer?.oneofKind === "chat" ? { kind: "chat", id: String(peer.chat.chatId) } : null
37337
+ } : peer?.oneofKind === "chat" ? { kind: "chat", id: String(peer.chat.chatId), target: `chat:${String(peer.chat.chatId)}` } : null
37212
37338
  };
37213
37339
  }
37340
+ function normalizeInlineListQuery(query) {
37341
+ return query?.trim().toLowerCase() ?? "";
37342
+ }
37343
+ function mapUserPeerEntry(user) {
37344
+ return {
37345
+ id: String(user.id),
37346
+ target: `user:${String(user.id)}`,
37347
+ username: user.username ?? null,
37348
+ name: buildInlineUserDisplayName(user),
37349
+ bot: user.bot ?? false
37350
+ };
37351
+ }
37352
+ function matchesInlineListQuery(text, query) {
37353
+ if (!query)
37354
+ return true;
37355
+ return text.toLowerCase().includes(query);
37356
+ }
37214
37357
  async function loadMessageReactions(params) {
37215
37358
  const target = await findMessageById({
37216
37359
  client: params.client,
@@ -37239,6 +37382,28 @@ async function loadMessageReactions(params) {
37239
37382
  }
37240
37383
  return Array.from(byEmoji.values());
37241
37384
  }
37385
+ function getErrorMessage(error48) {
37386
+ if (error48 instanceof Error)
37387
+ return error48.message;
37388
+ if (typeof error48 === "string")
37389
+ return error48;
37390
+ if (error48 && typeof error48 === "object" && "message" in error48 && typeof error48.message === "string") {
37391
+ return error48.message;
37392
+ }
37393
+ return String(error48);
37394
+ }
37395
+ function isDuplicateReactionError(error48) {
37396
+ const text = getErrorMessage(error48).toLowerCase();
37397
+ return text.includes("unique_reaction_per_emoji") || text.includes("duplicate") && text.includes("reaction") || text.includes("duplicate key value violates unique constraint");
37398
+ }
37399
+ async function reactionAlreadyExists(params) {
37400
+ const me = await params.client.getMe().catch(() => null);
37401
+ if (!me?.userId)
37402
+ return false;
37403
+ const myId = String(me.userId);
37404
+ const reactions = await loadMessageReactions(params).catch(() => []);
37405
+ return reactions.some((reaction) => reaction.emoji === params.emoji && reaction.userIds.includes(myId));
37406
+ }
37242
37407
  async function findMessageById(params) {
37243
37408
  const directResult = GET_MESSAGES_METHOD3 == null ? null : await params.client.invokeRaw(GET_MESSAGES_METHOD3, {
37244
37409
  oneofKind: "getMessages",
@@ -37423,11 +37588,18 @@ var inlineMessageActions = {
37423
37588
  return null;
37424
37589
  return { to: normalized };
37425
37590
  },
37426
- handleAction: async ({ action, params, cfg, accountId }) => {
37591
+ handleAction: async ({ action, params, cfg, accountId, toolContext }) => {
37427
37592
  if (!SUPPORTED_ACTIONS.includes(action)) {
37428
37593
  throw new Error(`Action ${action} is not supported for provider inline.`);
37429
37594
  }
37430
37595
  if (!isActionEnabled({ cfg, accountId: accountId ?? null, action })) {
37596
+ if (action === "react") {
37597
+ return jsonResult({
37598
+ ok: false,
37599
+ reason: "disabled",
37600
+ hint: "Inline reactions are disabled via channels.inline.actions.reactions. Do not retry."
37601
+ });
37602
+ }
37431
37603
  throw new Error(`inline action: ${action} is disabled by channels.inline.actions`);
37432
37604
  }
37433
37605
  const normalizedAction = action;
@@ -37552,7 +37724,24 @@ var inlineMessageActions = {
37552
37724
  accountId,
37553
37725
  fn: async (client) => {
37554
37726
  const chatId = resolveChatIdFromParams(params);
37555
- const messageId = parseInlineId(readFlexibleId(params, "messageId") ?? readStringParam(params, "messageId", { required: true }), "messageId");
37727
+ const rawMessageId = resolveReactionMessageId(toolContext != null ? { args: params, toolContext } : { args: params });
37728
+ if (!rawMessageId) {
37729
+ return jsonResult({
37730
+ ok: false,
37731
+ reason: "missing_message_id",
37732
+ hint: "Inline reaction requires a valid messageId (or inbound context fallback). Do not retry."
37733
+ });
37734
+ }
37735
+ let messageId;
37736
+ try {
37737
+ messageId = parseInlineId(rawMessageId, "messageId");
37738
+ } catch {
37739
+ return jsonResult({
37740
+ ok: false,
37741
+ reason: "missing_message_id",
37742
+ hint: "Inline reaction requires a valid messageId (or inbound context fallback). Do not retry."
37743
+ });
37744
+ }
37556
37745
  const { emoji: emoji3, remove, isEmpty } = readReactionParams(params, {
37557
37746
  removeErrorMessage: "Emoji is required to remove an Inline reaction."
37558
37747
  });
@@ -37560,28 +37749,72 @@ var inlineMessageActions = {
37560
37749
  throw new Error("inline action: react requires emoji");
37561
37750
  }
37562
37751
  if (remove) {
37563
- const result = await client.invokeRaw(Method.DELETE_REACTION, {
37564
- oneofKind: "deleteReaction",
37565
- deleteReaction: {
37566
- emoji: emoji3,
37567
- peerId: buildChatPeer3(chatId),
37568
- messageId
37752
+ try {
37753
+ const result = await client.invokeRaw(Method.DELETE_REACTION, {
37754
+ oneofKind: "deleteReaction",
37755
+ deleteReaction: {
37756
+ emoji: emoji3,
37757
+ peerId: buildChatPeer3(chatId),
37758
+ messageId
37759
+ }
37760
+ });
37761
+ if (result.oneofKind !== "deleteReaction") {
37762
+ throw new Error(`inline action: expected deleteReaction result, got ${String(result.oneofKind)}`);
37569
37763
  }
37570
- });
37571
- if (result.oneofKind !== "deleteReaction") {
37572
- throw new Error(`inline action: expected deleteReaction result, got ${String(result.oneofKind)}`);
37764
+ } catch {
37765
+ return jsonResult({
37766
+ ok: false,
37767
+ reason: "error",
37768
+ emoji: emoji3,
37769
+ remove: true,
37770
+ hint: "Reaction failed. Do not retry."
37771
+ });
37573
37772
  }
37574
37773
  } else {
37575
- const result = await client.invokeRaw(Method.ADD_REACTION, {
37576
- oneofKind: "addReaction",
37577
- addReaction: {
37774
+ if (await reactionAlreadyExists({
37775
+ client,
37776
+ chatId,
37777
+ messageId,
37778
+ emoji: emoji3
37779
+ })) {
37780
+ return jsonResult({
37781
+ ok: true,
37782
+ chatId: String(chatId),
37783
+ messageId: String(messageId),
37578
37784
  emoji: emoji3,
37579
- messageId,
37580
- peerId: buildChatPeer3(chatId)
37785
+ remove: false,
37786
+ alreadyPresent: true
37787
+ });
37788
+ }
37789
+ try {
37790
+ const result = await client.invokeRaw(Method.ADD_REACTION, {
37791
+ oneofKind: "addReaction",
37792
+ addReaction: {
37793
+ emoji: emoji3,
37794
+ messageId,
37795
+ peerId: buildChatPeer3(chatId)
37796
+ }
37797
+ });
37798
+ if (result.oneofKind !== "addReaction") {
37799
+ throw new Error(`inline action: expected addReaction result, got ${String(result.oneofKind)}`);
37581
37800
  }
37582
- });
37583
- if (result.oneofKind !== "addReaction") {
37584
- throw new Error(`inline action: expected addReaction result, got ${String(result.oneofKind)}`);
37801
+ } catch (error48) {
37802
+ if (!isDuplicateReactionError(error48)) {
37803
+ return jsonResult({
37804
+ ok: false,
37805
+ reason: "error",
37806
+ emoji: emoji3,
37807
+ hint: "Reaction failed. Do not retry."
37808
+ });
37809
+ }
37810
+ return jsonResult({
37811
+ ok: true,
37812
+ chatId: String(chatId),
37813
+ messageId: String(messageId),
37814
+ emoji: emoji3,
37815
+ remove: false,
37816
+ alreadyPresent: true
37817
+ });
37585
37818
  }
37586
37819
  }
37587
37820
  return jsonResult({
@@ -37755,6 +37988,7 @@ var inlineMessageActions = {
37755
37988
  fn: async (client) => {
37756
37989
  const query = readStringParam(params, "query") ?? readStringParam(params, "q") ?? undefined;
37757
37990
  const limit = Math.max(1, Math.min(200, readNumberParam(params, "limit", { integer: true }) ?? 50));
37991
+ const scope = (readStringParam(params, "scope") ?? readStringParam(params, "kind") ?? "all").toLowerCase();
37758
37992
  const result = await client.invokeRaw(Method.GET_CHATS, {
37759
37993
  oneofKind: "getChats",
37760
37994
  getChats: {}
@@ -37764,23 +37998,26 @@ var inlineMessageActions = {
37764
37998
  }
37765
37999
  const dialogByChatId = buildDialogMap(result.getChats.dialogs ?? []);
37766
38000
  const usersById = buildUserMap2(result.getChats.users ?? []);
37767
- const entries = (result.getChats.chats ?? []).map((chat) => mapChatEntry({ chat, dialogByChatId, usersById }));
37768
- const normalizedQuery = query?.trim().toLowerCase() ?? "";
37769
- const filtered = normalizedQuery ? entries.filter((entry) => {
37770
- const haystack = [
37771
- entry.id,
37772
- entry.title,
37773
- entry.peer?.kind === "user" ? entry.peer.username ?? "" : "",
37774
- entry.peer?.kind === "user" ? entry.peer.name ?? "" : ""
37775
- ].join(`
37776
- `).toLowerCase();
37777
- return haystack.includes(normalizedQuery);
37778
- }) : entries;
38001
+ const chats = (result.getChats.chats ?? []).map((chat) => mapChatEntry({ chat, dialogByChatId, usersById }));
38002
+ const groups = chats.filter((entry) => entry.peer?.kind !== "user");
38003
+ const peers = (result.getChats.users ?? []).map((user) => mapUserPeerEntry(user));
38004
+ const normalizedQuery = normalizeInlineListQuery(query);
38005
+ const filteredChats = chats.filter((entry) => matchesInlineListQuery([entry.id, entry.target, entry.title, entry.peer?.kind === "user" ? entry.peer.username ?? "" : "", entry.peer?.kind === "user" ? entry.peer.name ?? "" : ""].join(`
38006
+ `), normalizedQuery));
38007
+ const filteredGroups = groups.filter((entry) => matchesInlineListQuery([entry.id, entry.target, entry.title].join(`
38008
+ `), normalizedQuery));
38009
+ const filteredPeers = peers.filter((entry) => matchesInlineListQuery([entry.id, entry.target, entry.username ?? "", entry.name ?? ""].join(`
38010
+ `), normalizedQuery));
37779
38011
  return jsonResult(toJsonSafe({
37780
38012
  ok: true,
38013
+ scope,
37781
38014
  query: query ?? null,
37782
- count: filtered.length,
37783
- chats: filtered.slice(0, limit)
38015
+ count: filteredChats.length,
38016
+ groupsCount: filteredGroups.length,
38017
+ peersCount: filteredPeers.length,
38018
+ chats: scope === "groups" || scope === "group" || scope === "channels" || scope === "channel" ? [] : scope === "peers" || scope === "peer" || scope === "members" || scope === "member" || scope === "users" || scope === "user" ? [] : filteredChats.slice(0, limit),
38019
+ groups: scope === "peers" || scope === "peer" || scope === "members" || scope === "member" || scope === "users" || scope === "user" ? [] : filteredGroups.slice(0, limit),
38020
+ peers: scope === "groups" || scope === "group" || scope === "channels" || scope === "channel" ? [] : filteredPeers.slice(0, limit)
37784
38021
  }));
37785
38022
  }
37786
38023
  });
@@ -38204,6 +38441,239 @@ var inlineMessageActions = {
38204
38441
  };
38205
38442
  var inlineSupportedActions = listAllActions();
38206
38443
 
38444
+ // src/inline/setup-core.ts
38445
+ import { createEnvPatchedAccountSetupAdapter } from "openclaw/plugin-sdk/setup";
38446
+ var channel = "inline";
38447
+ var INLINE_TOKEN_HELP_LINES = [
38448
+ "1) Open Inline and generate a bot token for your workspace/account",
38449
+ "2) Copy the token",
38450
+ "3) Paste it here, or set INLINE_TOKEN in your environment",
38451
+ "Docs: https://inline.chat/docs/openclaw",
38452
+ "Website: https://openclaw.ai"
38453
+ ];
38454
+ var inlineSetupAdapter = createEnvPatchedAccountSetupAdapter({
38455
+ channelKey: channel,
38456
+ defaultAccountOnlyEnvError: "INLINE_TOKEN can only be used for the default account.",
38457
+ missingCredentialError: "Inline requires token or --token-file (or --use-env).",
38458
+ hasCredentials: (input) => Boolean(input.token || input.tokenFile),
38459
+ buildPatch: (input) => input.tokenFile ? { tokenFile: input.tokenFile } : input.token ? { token: input.token } : {}
38460
+ });
38461
+
38462
+ // src/inline/setup-surface.ts
38463
+ import {
38464
+ DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID2,
38465
+ setSetupChannelEnabled
38466
+ } from "openclaw/plugin-sdk/setup";
38467
+ var channel2 = "inline";
38468
+ var inlineSetupWizard = {
38469
+ channel: channel2,
38470
+ status: {
38471
+ configuredLabel: "configured",
38472
+ unconfiguredLabel: "needs token",
38473
+ configuredHint: "configured",
38474
+ unconfiguredHint: "recommended",
38475
+ configuredScore: 1,
38476
+ unconfiguredScore: 10,
38477
+ resolveConfigured: ({ cfg }) => listInlineAccountIds(cfg).some((accountId) => resolveInlineAccount({ cfg, accountId }).configured)
38478
+ },
38479
+ credentials: [
38480
+ {
38481
+ inputKey: "token",
38482
+ providerHint: channel2,
38483
+ credentialLabel: "Inline token",
38484
+ preferredEnvVar: "INLINE_TOKEN",
38485
+ helpTitle: "Inline token",
38486
+ helpLines: INLINE_TOKEN_HELP_LINES,
38487
+ envPrompt: "INLINE_TOKEN detected. Use env var?",
38488
+ keepPrompt: "Inline token already configured. Keep it?",
38489
+ inputPrompt: "Enter Inline token",
38490
+ allowEnv: ({ accountId }) => accountId === DEFAULT_ACCOUNT_ID2,
38491
+ inspect: ({ cfg, accountId }) => {
38492
+ const resolved = resolveInlineAccount({ cfg, accountId });
38493
+ const hasConfiguredValue = Boolean((resolved.config.token ?? "").trim() || (resolved.config.tokenFile ?? "").trim());
38494
+ const resolvedValue = resolved.token?.trim();
38495
+ const envValue = accountId === DEFAULT_ACCOUNT_ID2 ? process.env.INLINE_TOKEN?.trim() : undefined;
38496
+ return {
38497
+ accountConfigured: resolved.configured || hasConfiguredValue,
38498
+ hasConfiguredValue,
38499
+ ...resolvedValue ? { resolvedValue } : {},
38500
+ ...envValue ? { envValue } : {}
38501
+ };
38502
+ }
38503
+ }
38504
+ ],
38505
+ disable: (cfg) => setSetupChannelEnabled(cfg, channel2, false)
38506
+ };
38507
+
38508
+ // src/inline/probe.ts
38509
+ function formatInlineProbeUserName(user) {
38510
+ const explicit = [user.firstName?.trim(), user.lastName?.trim()].filter(Boolean).join(" ");
38511
+ if (explicit)
38512
+ return explicit;
38513
+ const username = user.username?.trim();
38514
+ if (username)
38515
+ return `@${username}`;
38516
+ return "Unknown";
38517
+ }
38518
+ async function withTimeout(promise2, timeoutMs) {
38519
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
38520
+ return await promise2;
38521
+ }
38522
+ return await new Promise((resolve, reject) => {
38523
+ const timer = setTimeout(() => {
38524
+ reject(new Error(`probe timeout after ${Math.trunc(timeoutMs)}ms`));
38525
+ }, timeoutMs);
38526
+ promise2.then((value) => {
38527
+ clearTimeout(timer);
38528
+ resolve(value);
38529
+ }, (error48) => {
38530
+ clearTimeout(timer);
38531
+ reject(error48);
38532
+ });
38533
+ });
38534
+ }
38535
+ async function probeInlineAccountDirect(account) {
38536
+ if (!account.baseUrl?.trim()) {
38537
+ throw new Error("missing baseUrl");
38538
+ }
38539
+ const token = await resolveInlineToken(account);
38540
+ const client = new InlineSdkClient({
38541
+ baseUrl: account.baseUrl,
38542
+ token
38543
+ });
38544
+ await client.connect();
38545
+ try {
38546
+ const result = await client.invokeRaw(Method.GET_ME, {
38547
+ oneofKind: "getMe",
38548
+ getMe: {}
38549
+ });
38550
+ if (result.oneofKind !== "getMe") {
38551
+ throw new Error(`expected getMe result, got ${String(result.oneofKind)}`);
38552
+ }
38553
+ if (!result.getMe.user) {
38554
+ throw new Error("missing current user from getMe");
38555
+ }
38556
+ const user = result.getMe.user;
38557
+ return {
38558
+ ok: true,
38559
+ accountId: account.accountId,
38560
+ baseUrl: account.baseUrl,
38561
+ user: {
38562
+ id: String(user.id),
38563
+ username: user.username?.trim() || null,
38564
+ name: formatInlineProbeUserName(user),
38565
+ bot: user.bot ?? false
38566
+ }
38567
+ };
38568
+ } finally {
38569
+ await client.close().catch(() => {});
38570
+ }
38571
+ }
38572
+ function toErrorText(error48) {
38573
+ if (error48 instanceof Error && error48.message.trim()) {
38574
+ return error48.message;
38575
+ }
38576
+ return String(error48);
38577
+ }
38578
+ async function probeInlineAccount(account, timeoutMs) {
38579
+ if (!account.configured) {
38580
+ return {
38581
+ ok: false,
38582
+ accountId: account.accountId,
38583
+ baseUrl: account.baseUrl,
38584
+ error: "missing token"
38585
+ };
38586
+ }
38587
+ try {
38588
+ return await withTimeout(probeInlineAccountDirect(account), timeoutMs);
38589
+ } catch (error48) {
38590
+ return {
38591
+ ok: false,
38592
+ accountId: account.accountId,
38593
+ baseUrl: account.baseUrl,
38594
+ error: toErrorText(error48)
38595
+ };
38596
+ }
38597
+ }
38598
+
38599
+ // src/inline/status-issues.ts
38600
+ import { asString, isRecord as isRecord5 } from "openclaw/plugin-sdk/status-helpers";
38601
+ function readInlineProbeSummary(value) {
38602
+ if (!isRecord5(value)) {
38603
+ return {};
38604
+ }
38605
+ const summary = {};
38606
+ if (typeof value.ok === "boolean") {
38607
+ summary.ok = value.ok;
38608
+ }
38609
+ const error48 = asString(value.error);
38610
+ if (error48) {
38611
+ summary.error = error48;
38612
+ }
38613
+ return summary;
38614
+ }
38615
+ function looksLikeAuthError(text) {
38616
+ return /(401|403|unauth|forbidden|invalid token|token invalid|unauthorized)/i.test(text);
38617
+ }
38618
+ function collectInlineStatusIssues(accounts) {
38619
+ const issues = [];
38620
+ for (const entry of accounts) {
38621
+ if (!isRecord5(entry)) {
38622
+ continue;
38623
+ }
38624
+ const accountId = asString(entry.accountId);
38625
+ if (!accountId) {
38626
+ continue;
38627
+ }
38628
+ const enabled = entry.enabled !== false;
38629
+ if (!enabled) {
38630
+ continue;
38631
+ }
38632
+ const configured = entry.configured !== false;
38633
+ if (!configured) {
38634
+ issues.push({
38635
+ channel: "inline",
38636
+ accountId,
38637
+ kind: "config",
38638
+ message: "Inline account is enabled but missing token/tokenFile.",
38639
+ fix: "Set channels.inline.token (or tokenFile), then restart the gateway."
38640
+ });
38641
+ continue;
38642
+ }
38643
+ const baseUrl = asString(entry.baseUrl);
38644
+ if (!baseUrl || baseUrl === "[missing]") {
38645
+ issues.push({
38646
+ channel: "inline",
38647
+ accountId,
38648
+ kind: "config",
38649
+ message: "Inline account is configured but baseUrl is missing.",
38650
+ fix: 'Set channels.inline.baseUrl (for example "https://api.inline.chat"), then restart the gateway.'
38651
+ });
38652
+ }
38653
+ const lastError = asString(entry.lastError);
38654
+ if (lastError) {
38655
+ issues.push({
38656
+ channel: "inline",
38657
+ accountId,
38658
+ kind: looksLikeAuthError(lastError) ? "auth" : "runtime",
38659
+ message: `Inline runtime error: ${lastError}`,
38660
+ fix: "Verify token/baseUrl and restart the gateway."
38661
+ });
38662
+ }
38663
+ const probe = readInlineProbeSummary(entry.probe);
38664
+ if (probe.ok === false && probe.error) {
38665
+ issues.push({
38666
+ channel: "inline",
38667
+ accountId,
38668
+ kind: looksLikeAuthError(probe.error) ? "auth" : "runtime",
38669
+ message: `Inline probe failed: ${probe.error}`,
38670
+ fix: "Verify token/baseUrl connectivity, then re-run channel status."
38671
+ });
38672
+ }
38673
+ }
38674
+ return issues;
38675
+ }
38676
+
38207
38677
  // src/inline/channel.ts
38208
38678
  var activeMonitors = new Map;
38209
38679
  var meta3 = {
@@ -38242,6 +38712,82 @@ function parseInlineId2(raw) {
38242
38712
  }
38243
38713
  return;
38244
38714
  }
38715
+ function asRecord(value) {
38716
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
38717
+ return null;
38718
+ }
38719
+ return value;
38720
+ }
38721
+ function clearInlineCredentialFields(record2) {
38722
+ let changed = false;
38723
+ let cleared = false;
38724
+ for (const key of ["token", "tokenFile"]) {
38725
+ if (!Object.hasOwn(record2, key)) {
38726
+ continue;
38727
+ }
38728
+ const raw = record2[key];
38729
+ if (typeof raw === "string" && raw.trim()) {
38730
+ cleared = true;
38731
+ }
38732
+ delete record2[key];
38733
+ changed = true;
38734
+ }
38735
+ return { changed, cleared };
38736
+ }
38737
+ function clearInlineAccountCredentials(params) {
38738
+ const channels = asRecord(params.cfg.channels);
38739
+ const inline = asRecord(channels?.inline);
38740
+ if (!channels || !inline) {
38741
+ return { cfg: params.cfg, changed: false, cleared: false };
38742
+ }
38743
+ const nextInline = { ...inline };
38744
+ let changed = false;
38745
+ let cleared = false;
38746
+ if (params.accountId === DEFAULT_ACCOUNT_ID) {
38747
+ const base = clearInlineCredentialFields(nextInline);
38748
+ changed = changed || base.changed;
38749
+ cleared = cleared || base.cleared;
38750
+ }
38751
+ const accounts = asRecord(nextInline.accounts);
38752
+ if (accounts) {
38753
+ const nextAccounts = { ...accounts };
38754
+ const accountEntry = asRecord(nextAccounts[params.accountId]);
38755
+ if (accountEntry) {
38756
+ const nextAccountEntry = { ...accountEntry };
38757
+ const entry = clearInlineCredentialFields(nextAccountEntry);
38758
+ if (entry.changed) {
38759
+ changed = true;
38760
+ cleared = cleared || entry.cleared;
38761
+ if (Object.keys(nextAccountEntry).length > 0) {
38762
+ nextAccounts[params.accountId] = nextAccountEntry;
38763
+ } else {
38764
+ delete nextAccounts[params.accountId];
38765
+ }
38766
+ }
38767
+ }
38768
+ if (changed) {
38769
+ if (Object.keys(nextAccounts).length > 0) {
38770
+ nextInline.accounts = nextAccounts;
38771
+ } else {
38772
+ delete nextInline.accounts;
38773
+ }
38774
+ }
38775
+ }
38776
+ if (!changed) {
38777
+ return { cfg: params.cfg, changed: false, cleared: false };
38778
+ }
38779
+ return {
38780
+ cfg: {
38781
+ ...params.cfg,
38782
+ channels: {
38783
+ ...channels,
38784
+ inline: nextInline
38785
+ }
38786
+ },
38787
+ changed: true,
38788
+ cleared
38789
+ };
38790
+ }
38245
38791
  function parseInlineOutboundTarget(params) {
38246
38792
  let normalizedTarget = params.raw.trim();
38247
38793
  const hadInlinePrefix = /^inline:/i.test(normalizedTarget);
@@ -38277,6 +38823,46 @@ function parseInlineOutboundTarget(params) {
38277
38823
  raw: params.raw
38278
38824
  };
38279
38825
  }
38826
+ function parseInlineExplicitTarget(raw) {
38827
+ const parsed = parseInlineOutboundTarget({
38828
+ raw,
38829
+ context: "sendText"
38830
+ });
38831
+ if (parsed.kind === "user") {
38832
+ return { to: `user:${parsed.normalizedNumeric}`, chatType: "direct" };
38833
+ }
38834
+ return { to: `chat:${parsed.normalizedNumeric}`, chatType: "group" };
38835
+ }
38836
+ function formatInlineTargetDisplay(params) {
38837
+ const explicit = params.display?.trim();
38838
+ if (explicit) {
38839
+ return explicit;
38840
+ }
38841
+ const parsed = parseInlineExplicitTarget(params.target.trim());
38842
+ if (!parsed) {
38843
+ return params.target.trim();
38844
+ }
38845
+ if (parsed.chatType === "direct" || params.kind === "user") {
38846
+ return parsed.to;
38847
+ }
38848
+ return parsed.to;
38849
+ }
38850
+ function normalizeInlineConversationId(raw) {
38851
+ const trimmed = raw.trim();
38852
+ if (!trimmed)
38853
+ return null;
38854
+ const withoutProvider = trimmed.replace(/^inline:/i, "").trim();
38855
+ if (!withoutProvider)
38856
+ return null;
38857
+ try {
38858
+ const parsed = parseInlineExplicitTarget(withoutProvider);
38859
+ if (!parsed)
38860
+ return null;
38861
+ return `inline:${parsed.to}`;
38862
+ } catch {
38863
+ return null;
38864
+ }
38865
+ }
38280
38866
  async function listInlineTargetIds(client) {
38281
38867
  const result = await client.invokeRaw(Method.GET_CHATS, {
38282
38868
  oneofKind: "getChats",
@@ -38350,6 +38936,31 @@ function buildInlineDisplayName(params) {
38350
38936
  return `@${username}`;
38351
38937
  return "Unknown";
38352
38938
  }
38939
+ function formatInlineCapabilitiesProbeLines(probe) {
38940
+ const details = probe;
38941
+ if (!details) {
38942
+ return [];
38943
+ }
38944
+ if (!details.ok) {
38945
+ if (details.error?.trim()) {
38946
+ return [{ text: `Probe failed: ${details.error}`, tone: "error" }];
38947
+ }
38948
+ return [{ text: "Probe failed", tone: "error" }];
38949
+ }
38950
+ const lines = [];
38951
+ if (details.user) {
38952
+ const username = details.user.username ? ` @${details.user.username}` : "";
38953
+ const botLabel = details.user.bot ? " [bot]" : "";
38954
+ lines.push({
38955
+ text: `Identity: ${details.user.name}${username} (${details.user.id})${botLabel}`,
38956
+ tone: "success"
38957
+ });
38958
+ }
38959
+ if (details.baseUrl) {
38960
+ lines.push({ text: `Base URL: ${details.baseUrl}` });
38961
+ }
38962
+ return lines;
38963
+ }
38353
38964
  function toInlineUserDirectoryEntry(user) {
38354
38965
  return {
38355
38966
  kind: "user",
@@ -38653,10 +39264,25 @@ var inlineChannelPlugin = {
38653
39264
  },
38654
39265
  reload: { configPrefixes: ["channels.inline"] },
38655
39266
  configSchema: buildChannelConfigSchema(InlineConfigSchema),
39267
+ setup: inlineSetupAdapter,
39268
+ setupWizard: inlineSetupWizard,
38656
39269
  config: {
38657
39270
  listAccountIds: (cfg) => listInlineAccountIds(cfg),
38658
39271
  resolveAccount: (cfg, accountId) => resolveInlineAccount({ cfg, accountId: accountId ?? null }),
38659
39272
  defaultAccountId: (cfg) => resolveDefaultInlineAccountId(cfg),
39273
+ setAccountEnabled: ({ cfg, accountId, enabled }) => setAccountEnabledInConfigSection({
39274
+ cfg,
39275
+ sectionKey: "inline",
39276
+ accountId,
39277
+ enabled,
39278
+ allowTopLevel: true
39279
+ }),
39280
+ deleteAccount: ({ cfg, accountId }) => deleteAccountFromConfigSection({
39281
+ cfg,
39282
+ sectionKey: "inline",
39283
+ accountId,
39284
+ clearBaseFields: ["token", "tokenFile", "name", "enabled"]
39285
+ }),
38660
39286
  isConfigured: (account) => account.configured,
38661
39287
  describeAccount: (account) => ({
38662
39288
  accountId: account.accountId,
@@ -38707,6 +39333,47 @@ var inlineChannelPlugin = {
38707
39333
  ];
38708
39334
  }
38709
39335
  },
39336
+ allowlist: buildDmGroupAccountAllowlistAdapter({
39337
+ channelId: "inline",
39338
+ resolveAccount: ({ cfg, accountId }) => resolveInlineAccount({ cfg, accountId: accountId ?? null }),
39339
+ normalize: ({ values }) => values.map((entry) => String(entry).trim()).filter(Boolean).map((entry) => normalizeInlineAllowEntry(entry)),
39340
+ resolveDmAllowFrom: (account) => account.config.allowFrom ?? [],
39341
+ resolveGroupAllowFrom: (account) => account.config.groupAllowFrom ?? [],
39342
+ resolveDmPolicy: (account) => account.config.dmPolicy,
39343
+ resolveGroupPolicy: (account) => account.config.groupPolicy
39344
+ }),
39345
+ bindings: {
39346
+ compileConfiguredBinding: ({ conversationId }) => {
39347
+ const normalized = normalizeInlineConversationId(conversationId);
39348
+ if (!normalized) {
39349
+ return null;
39350
+ }
39351
+ return { conversationId: normalized };
39352
+ },
39353
+ matchInboundConversation: ({ compiledBinding, conversationId, parentConversationId }) => {
39354
+ const expected = normalizeInlineConversationId(compiledBinding.conversationId);
39355
+ if (!expected) {
39356
+ return null;
39357
+ }
39358
+ const incoming = normalizeInlineConversationId(conversationId);
39359
+ const parent = parentConversationId ? normalizeInlineConversationId(parentConversationId) : null;
39360
+ if (incoming && incoming === expected) {
39361
+ return {
39362
+ conversationId: incoming,
39363
+ ...parent ? { parentConversationId: parent } : {},
39364
+ matchPriority: 2
39365
+ };
39366
+ }
39367
+ if (incoming && parent && parent === expected) {
39368
+ return {
39369
+ conversationId: incoming,
39370
+ parentConversationId: parent,
39371
+ matchPriority: 1
39372
+ };
39373
+ }
39374
+ return null;
39375
+ }
39376
+ },
38710
39377
  groups: {
38711
39378
  resolveRequireMention: ({ cfg, accountId, groupId }) => {
38712
39379
  const resolved = resolveInlineAccount({ cfg, accountId: accountId ?? null });
@@ -38759,7 +39426,9 @@ var inlineChannelPlugin = {
38759
39426
  messageToolHints: ({ cfg, accountId }) => [
38760
39427
  "- Inline targeting: omit `target` to reply in the current chat.",
38761
39428
  "- Inline explicit targets: `chat:<chatId>` for chats and `user:<userId>` for direct users. Prefer `user:` for DM user targets.",
38762
- "- Inline history tools: `read` and `search` return media-aware message payloads (`media`, `attachments`, `attachmentUrls`) so image-only history remains discoverable.",
39429
+ "- Inline discovery: use `channel-list` to discover available chats and users. Use `scope: groups|peers|all` when helpful, and reuse returned `target` values.",
39430
+ "- Inline reactions: pass `messageId` for `react` when you have it; on inbound turns, the current inbound message id can be used as fallback.",
39431
+ "- Inline history tools: `read` and `search` return media-aware message payloads (`media`, `attachments`, `attachmentUrls`) so image-only history remains discoverable. `search` is chat-scoped; run it per chat.",
38763
39432
  "- Inline special tools: use `inline_nudge` to send a nudge, and `inline_forward` to forward message ids between chats or users.",
38764
39433
  ...isInlineReplyThreadsEnabled({ cfg, accountId: accountId ?? null }) ? [
38765
39434
  "- Inline reply threads are enabled: use `thread-reply` to send into a real reply thread, with `threadId` set to the reply-thread chat id."
@@ -38768,6 +39437,29 @@ var inlineChannelPlugin = {
38768
39437
  },
38769
39438
  messaging: {
38770
39439
  normalizeTarget: normalizeInlineTarget,
39440
+ parseExplicitTarget: ({ raw }) => {
39441
+ try {
39442
+ const parsed = parseInlineExplicitTarget(raw);
39443
+ if (!parsed)
39444
+ return null;
39445
+ return { to: parsed.to, chatType: parsed.chatType };
39446
+ } catch {
39447
+ return null;
39448
+ }
39449
+ },
39450
+ inferTargetChatType: ({ to }) => {
39451
+ try {
39452
+ const parsed = parseInlineExplicitTarget(to);
39453
+ return parsed?.chatType;
39454
+ } catch {
39455
+ return;
39456
+ }
39457
+ },
39458
+ formatTargetDisplay: ({ target, display, kind }) => formatInlineTargetDisplay({
39459
+ target,
39460
+ ...display !== undefined ? { display } : {},
39461
+ ...kind !== undefined ? { kind } : {}
39462
+ }),
38771
39463
  targetResolver: {
38772
39464
  looksLikeId: looksLikeInlineTargetId,
38773
39465
  hint: "<chatId | chat:<chatId> | user:<userId>>"
@@ -38985,18 +39677,14 @@ var inlineChannelPlugin = {
38985
39677
  running: false,
38986
39678
  lastStartAt: null,
38987
39679
  lastStopAt: null,
38988
- lastError: null
39680
+ lastError: null,
39681
+ lastProbeAt: null
38989
39682
  },
38990
- buildChannelSummary: ({ snapshot }) => ({
38991
- configured: snapshot.configured ?? false,
38992
- running: snapshot.running ?? false,
38993
- lastStartAt: snapshot.lastStartAt ?? null,
38994
- lastStopAt: snapshot.lastStopAt ?? null,
38995
- lastError: snapshot.lastError ?? null,
38996
- lastInboundAt: snapshot.lastInboundAt ?? null,
38997
- lastOutboundAt: snapshot.lastOutboundAt ?? null
38998
- }),
38999
- buildAccountSnapshot: ({ account, runtime: runtime2 }) => ({
39683
+ collectStatusIssues: collectInlineStatusIssues,
39684
+ buildChannelSummary: ({ snapshot }) => buildTokenChannelStatusSummary(snapshot),
39685
+ probeAccount: async ({ account, timeoutMs }) => await probeInlineAccount(account, timeoutMs),
39686
+ formatCapabilitiesProbe: ({ probe }) => formatInlineCapabilitiesProbeLines(probe),
39687
+ buildAccountSnapshot: ({ account, runtime: runtime2, probe }) => ({
39000
39688
  accountId: account.accountId,
39001
39689
  name: account.name,
39002
39690
  enabled: account.enabled,
@@ -39008,7 +39696,9 @@ var inlineChannelPlugin = {
39008
39696
  lastStopAt: runtime2?.lastStopAt ?? null,
39009
39697
  lastError: runtime2?.lastError ?? null,
39010
39698
  lastInboundAt: runtime2?.lastInboundAt ?? null,
39011
- lastOutboundAt: runtime2?.lastOutboundAt ?? null
39699
+ lastOutboundAt: runtime2?.lastOutboundAt ?? null,
39700
+ lastProbeAt: runtime2?.lastProbeAt ?? null,
39701
+ ...probe !== undefined ? { probe } : {}
39012
39702
  })
39013
39703
  },
39014
39704
  gateway: {
@@ -39061,6 +39751,30 @@ var inlineChannelPlugin = {
39061
39751
  running: false,
39062
39752
  lastStopAt: Date.now()
39063
39753
  });
39754
+ },
39755
+ logoutAccount: async ({ accountId, cfg }) => {
39756
+ const cleanup = clearInlineAccountCredentials({ cfg, accountId });
39757
+ if (cleanup.changed) {
39758
+ return {
39759
+ cleared: cleanup.cleared,
39760
+ loggedOut: cleanup.cleared,
39761
+ cfg: cleanup.cfg,
39762
+ message: cleanup.cleared ? "Inline credentials cleared from config. Restart gateway to apply." : "Inline credential fields removed from config."
39763
+ };
39764
+ }
39765
+ const envToken = process.env.INLINE_TOKEN?.trim() ?? "";
39766
+ if (accountId === DEFAULT_ACCOUNT_ID && envToken) {
39767
+ return {
39768
+ cleared: false,
39769
+ loggedOut: false,
39770
+ message: "No Inline credentials found in config. INLINE_TOKEN is set in env; unset it and restart gateway to fully log out."
39771
+ };
39772
+ }
39773
+ return {
39774
+ cleared: false,
39775
+ loggedOut: false,
39776
+ message: "No Inline credentials found in config for this account."
39777
+ };
39064
39778
  }
39065
39779
  }
39066
39780
  };
@@ -40130,5 +40844,5 @@ export {
40130
40844
  src_default as default
40131
40845
  };
40132
40846
 
40133
- //# debugId=DA562915BABAD00064756E2164756E21
40847
+ //# debugId=AB3E531CE13839CA64756E2164756E21
40134
40848
  //# sourceMappingURL=index.js.map