@inline-openclaw/inline 0.0.24 → 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);
@@ -35170,11 +35178,13 @@ function resolveInlineCompatNativeCommandMenu(commandBody) {
35170
35178
  var CHANNEL_ID = "inline";
35171
35179
  var DEFAULT_DM_HISTORY_LIMIT = 6;
35172
35180
  var HISTORY_LINE_MAX_CHARS = 280;
35181
+ var URL_LIKE_PATTERN = /https?:\/\/\S+/i;
35173
35182
  var BOT_MESSAGE_CACHE_LIMIT = 500;
35174
35183
  var REACTION_TARGET_LOOKUP_LIMIT = 8;
35175
35184
  var REPLY_TARGET_LOOKUP_LIMIT = 8;
35176
35185
  var ATTACHMENT_CONTEXT_LIMIT = 6;
35177
35186
  var DEFAULT_INLINE_MEDIA_MAX_BYTES = 300 * 1024 * 1024;
35187
+ var EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again.";
35178
35188
  var GET_MESSAGES_METHOD2 = typeof Method.GET_MESSAGES === "number" && Number.isInteger(Method.GET_MESSAGES) && Method.GET_MESSAGES > 0 ? Method.GET_MESSAGES : null;
35179
35189
  function normalizeAllowEntry(raw) {
35180
35190
  return raw.trim().replace(/^inline:/i, "").replace(/^user:/i, "");
@@ -35479,6 +35489,8 @@ function normalizeHistoryText(raw) {
35479
35489
  return "";
35480
35490
  if (compact.length <= HISTORY_LINE_MAX_CHARS)
35481
35491
  return compact;
35492
+ if (URL_LIKE_PATTERN.test(compact))
35493
+ return compact;
35482
35494
  return `${compact.slice(0, HISTORY_LINE_MAX_CHARS - 1)}…`;
35483
35495
  }
35484
35496
  function drainCompleteParagraphs(buffer) {
@@ -36485,193 +36497,287 @@ ${currentEntityText}` : null
36485
36497
  failed: false,
36486
36498
  opChain: Promise.resolve()
36487
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
+ };
36488
36571
  const replyOptions = {
36489
36572
  ...onModelSelected ? { onModelSelected } : {},
36490
36573
  blockReplyTimeoutMs: 25000,
36574
+ ...streamViaEditMessage ? {
36575
+ onAssistantMessageStart: async () => {
36576
+ await resetEditStreamOnBoundary();
36577
+ }
36578
+ } : {},
36491
36579
  ...streamViaEditMessage ? {
36492
36580
  onPartialReply: async (payload) => {
36493
- if (editStreamState.failed)
36494
- return;
36495
- if ((payload.mediaUrls?.length ?? 0) > 0)
36496
- return;
36497
- const partialText = typeof payload.text === "string" ? payload.text : "";
36498
- if (!partialText || partialText === editStreamState.lastPartialText)
36499
- return;
36500
- editStreamState.lastPartialText = partialText;
36501
- const nextText = rewriteNumericMentionsToUsernames(extractCompleteParagraphText(partialText), senderProfilesById).trim();
36502
- if (!nextText || nextText === editStreamState.accumulatedText)
36503
- return;
36504
- editStreamState.opChain = editStreamState.opChain.then(async () => {
36505
- if (editStreamState.failed)
36506
- return;
36507
- if (!nextText || nextText === editStreamState.accumulatedText)
36508
- return;
36509
- try {
36510
- 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;
36511
36646
  const sent = await client.sendMessage({
36512
36647
  chatId,
36513
- text: nextText,
36514
- ...defaultReplyToMsgId != null ? { replyToMsgId: defaultReplyToMsgId } : {},
36648
+ text,
36649
+ ...includeReplyTo && replyToMsgId != null ? { replyToMsgId } : {},
36650
+ ...includeActions && outboundActions !== undefined ? { actions: outboundActions } : {},
36515
36651
  parseMarkdown
36516
36652
  });
36517
- if (sent.messageId == null) {
36518
- throw new Error("inline edit stream: sendMessage returned no messageId");
36519
- }
36520
- editStreamState.messageId = sent.messageId;
36521
- rememberBotMessageId(botMessageIdsByChat, chatId, sent.messageId);
36522
- } 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;
36523
36667
  const result = await client.invokeRaw(Method.EDIT_MESSAGE, {
36524
36668
  oneofKind: "editMessage",
36525
36669
  editMessage: {
36526
36670
  messageId: editStreamState.messageId,
36527
36671
  peerId: buildChatPeer2(chatId),
36528
- text: nextText,
36529
- parseMarkdown
36672
+ text: textForEdit,
36673
+ parseMarkdown,
36674
+ ...actions !== undefined ? { actions } : {}
36530
36675
  }
36531
36676
  });
36532
36677
  if (result.oneofKind !== "editMessage") {
36533
36678
  throw new Error(`inline edit stream: expected editMessage result, got ${String(result.oneofKind)}`);
36534
36679
  }
36535
- }
36536
- editStreamState.accumulatedText = nextText;
36537
- statusSink?.({ lastOutboundAt: Date.now() });
36538
- } catch (error48) {
36539
- editStreamState.failed = true;
36540
- runtime2.error?.(`inline edit stream failed: ${String(error48)}`);
36541
- }
36542
- });
36543
- await editStreamState.opChain;
36544
- }
36545
- } : {},
36546
- ...typeof disableBlockStreaming === "boolean" ? { disableBlockStreaming } : {}
36547
- };
36548
- try {
36549
- await core3.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
36550
- ctx: ctxPayload,
36551
- cfg,
36552
- dispatcherOptions: {
36553
- ...prefixOptions,
36554
- ...typingCallbacks ? { typingCallbacks } : {},
36555
- deliver: async (payload) => {
36556
- const rawText = payload.text ?? "";
36557
- const mediaList = payload.mediaUrls?.length ? payload.mediaUrls : payload.mediaUrl ? [payload.mediaUrl] : [];
36558
- const outboundText = rewriteNumericMentionsToUsernames(rawText, senderProfilesById);
36559
- const outboundActions = resolveInlineReplyActions(payload);
36560
- let replyToMsgId;
36561
- if (payload.replyToId != null) {
36562
- try {
36563
- replyToMsgId = BigInt(payload.replyToId);
36564
- } catch {}
36565
- }
36566
- if (replyToMsgId == null && isGroup && msg.replyToMsgId != null) {
36567
- replyToMsgId = msg.id;
36568
- }
36569
- const rememberSent = (messageId) => {
36570
- if (messageId != null) {
36571
- rememberBotMessageId(botMessageIdsByChat, chatId, messageId);
36572
- }
36573
- };
36574
- const sendTextFallback = async (text, includeReplyTo, includeActions) => {
36575
- if (!text.trim())
36576
- return;
36577
- const sent = await client.sendMessage({
36578
- chatId,
36579
- text,
36580
- ...includeReplyTo && replyToMsgId != null ? { replyToMsgId } : {},
36581
- ...includeActions && outboundActions !== undefined ? { actions: outboundActions } : {},
36582
- parseMarkdown
36583
- });
36584
- rememberSent(sent.messageId);
36585
- };
36586
- const updateStreamedMessage = async (text, actions) => {
36587
- await editStreamState.opChain;
36588
- if (editStreamState.messageId == null)
36589
- return false;
36590
- const nextText = text.trim();
36591
- const textForEdit = nextText || editStreamState.accumulatedText;
36592
- if (!textForEdit)
36593
- return true;
36594
- const shouldSkipTextUpdate = !editStreamState.failed && textForEdit === editStreamState.accumulatedText;
36595
- if (shouldSkipTextUpdate && actions === undefined)
36596
- return true;
36597
- const result = await client.invokeRaw(Method.EDIT_MESSAGE, {
36598
- oneofKind: "editMessage",
36599
- editMessage: {
36600
- messageId: editStreamState.messageId,
36601
- peerId: buildChatPeer2(chatId),
36602
- text: textForEdit,
36603
- parseMarkdown,
36604
- ...actions !== undefined ? { actions } : {}
36680
+ if (!shouldSkipTextUpdate) {
36681
+ editStreamState.accumulatedText = textForEdit;
36682
+ editStreamState.lastPartialText = textForEdit;
36605
36683
  }
36606
- });
36607
- if (result.oneofKind !== "editMessage") {
36608
- throw new Error(`inline edit stream: expected editMessage result, got ${String(result.oneofKind)}`);
36609
- }
36610
- if (!shouldSkipTextUpdate) {
36611
- editStreamState.accumulatedText = textForEdit;
36612
- editStreamState.lastPartialText = textForEdit;
36613
- }
36614
- editStreamState.failed = false;
36615
- return true;
36616
- };
36617
- if (mediaList.length === 0) {
36618
- if (streamViaEditMessage && editStreamState.messageId != null) {
36619
- if (outboundText.trim()) {
36620
- 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();
36621
36690
  }
36622
- 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() });
36623
36704
  return;
36624
36705
  }
36625
- await updateStreamedMessage(editStreamState.finalTextAccumulator, outboundActions);
36706
+ if (!outboundText.trim())
36707
+ return;
36708
+ await sendTextFallback(outboundText, true, true);
36626
36709
  statusSink?.({ lastOutboundAt: Date.now() });
36627
36710
  return;
36628
36711
  }
36629
- if (!outboundText.trim())
36630
- return;
36631
- await sendTextFallback(outboundText, true, true);
36632
- statusSink?.({ lastOutboundAt: Date.now() });
36633
- return;
36634
- }
36635
- if (streamViaEditMessage && editStreamState.messageId != null && outboundText.trim()) {
36636
- await updateStreamedMessage(outboundText, outboundActions);
36637
- }
36638
- for (let index = 0;index < mediaList.length; index++) {
36639
- const mediaUrl = mediaList[index];
36640
- if (!mediaUrl?.trim())
36641
- continue;
36642
- const isFirst = index === 0;
36643
- const shouldAttachActionsToMedia = isFirst && (!(streamViaEditMessage && editStreamState.messageId != null) || !outboundText.trim());
36644
- const caption = isFirst && !(streamViaEditMessage && editStreamState.messageId != null) ? outboundText : "";
36645
- try {
36646
- const media = await uploadInlineMediaFromUrl({
36647
- client,
36648
- cfg,
36649
- accountId: account.accountId,
36650
- mediaUrl
36651
- });
36652
- const sent = await client.sendMessage({
36653
- chatId,
36654
- ...caption ? { text: caption } : {},
36655
- media,
36656
- ...isFirst && replyToMsgId != null ? { replyToMsgId } : {},
36657
- ...shouldAttachActionsToMedia && outboundActions !== undefined ? { actions: outboundActions } : {},
36658
- ...caption ? { parseMarkdown } : {}
36659
- });
36660
- rememberSent(sent.messageId);
36661
- } catch (error48) {
36662
- runtime2.error?.(`inline media upload failed; falling back to url text (${String(error48)})`);
36663
- 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}
36664
36742
 
36665
36743
  Attachment: ${mediaUrl}` : `Attachment: ${mediaUrl}`;
36666
- 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;
36667
36752
  }
36753
+ },
36754
+ onError: (err, info) => {
36755
+ failedNonSilent = true;
36756
+ runtime2.error?.(`inline ${info?.kind ?? "final"} reply failed: ${String(err)}`);
36668
36757
  }
36669
- statusSink?.({ lastOutboundAt: Date.now() });
36670
36758
  },
36671
- onError: (err, info) => runtime2.error?.(`inline ${info.kind} reply failed: ${String(err)}`)
36672
- },
36673
- replyOptions
36674
- });
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
+ }
36675
36781
  } finally {
36676
36782
  if (callbackActionEvent && !callbackActionAnswered) {
36677
36783
  try {
@@ -36933,12 +37039,33 @@ function parseInlineId(raw, label) {
36933
37039
  throw new Error(`inline action: missing ${label}`);
36934
37040
  }
36935
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
+ }
36936
37048
  throw new Error(`inline action: invalid ${label} "${raw}"`);
36937
37049
  }
36938
37050
  return BigInt(trimmed);
36939
37051
  }
36940
37052
  throw new Error(`inline action: missing ${label}`);
36941
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
+ }
36942
37069
  function parseOptionalInlineId(raw, label) {
36943
37070
  if (raw == null)
36944
37071
  return;
@@ -37192,6 +37319,7 @@ function mapChatEntry(params) {
37192
37319
  }
37193
37320
  return {
37194
37321
  id: String(params.chat.id),
37322
+ target: `chat:${String(params.chat.id)}`,
37195
37323
  title: params.chat.title,
37196
37324
  spaceId: params.chat.spaceId != null ? String(params.chat.spaceId) : null,
37197
37325
  isPublic: params.chat.isPublic ?? false,
@@ -37203,11 +37331,29 @@ function mapChatEntry(params) {
37203
37331
  peer: peer?.oneofKind === "user" ? {
37204
37332
  kind: "user",
37205
37333
  id: String(peer.user.userId),
37334
+ target: `user:${String(peer.user.userId)}`,
37206
37335
  username: peerUser?.username ?? null,
37207
37336
  name: peerUser ? buildInlineUserDisplayName(peerUser) : null
37208
- } : 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
37209
37338
  };
37210
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
+ }
37211
37357
  async function loadMessageReactions(params) {
37212
37358
  const target = await findMessageById({
37213
37359
  client: params.client,
@@ -37236,6 +37382,28 @@ async function loadMessageReactions(params) {
37236
37382
  }
37237
37383
  return Array.from(byEmoji.values());
37238
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
+ }
37239
37407
  async function findMessageById(params) {
37240
37408
  const directResult = GET_MESSAGES_METHOD3 == null ? null : await params.client.invokeRaw(GET_MESSAGES_METHOD3, {
37241
37409
  oneofKind: "getMessages",
@@ -37420,11 +37588,18 @@ var inlineMessageActions = {
37420
37588
  return null;
37421
37589
  return { to: normalized };
37422
37590
  },
37423
- handleAction: async ({ action, params, cfg, accountId }) => {
37591
+ handleAction: async ({ action, params, cfg, accountId, toolContext }) => {
37424
37592
  if (!SUPPORTED_ACTIONS.includes(action)) {
37425
37593
  throw new Error(`Action ${action} is not supported for provider inline.`);
37426
37594
  }
37427
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
+ }
37428
37603
  throw new Error(`inline action: ${action} is disabled by channels.inline.actions`);
37429
37604
  }
37430
37605
  const normalizedAction = action;
@@ -37549,7 +37724,24 @@ var inlineMessageActions = {
37549
37724
  accountId,
37550
37725
  fn: async (client) => {
37551
37726
  const chatId = resolveChatIdFromParams(params);
37552
- 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
+ }
37553
37745
  const { emoji: emoji3, remove, isEmpty } = readReactionParams(params, {
37554
37746
  removeErrorMessage: "Emoji is required to remove an Inline reaction."
37555
37747
  });
@@ -37557,28 +37749,72 @@ var inlineMessageActions = {
37557
37749
  throw new Error("inline action: react requires emoji");
37558
37750
  }
37559
37751
  if (remove) {
37560
- const result = await client.invokeRaw(Method.DELETE_REACTION, {
37561
- oneofKind: "deleteReaction",
37562
- deleteReaction: {
37563
- emoji: emoji3,
37564
- peerId: buildChatPeer3(chatId),
37565
- 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)}`);
37566
37763
  }
37567
- });
37568
- if (result.oneofKind !== "deleteReaction") {
37569
- 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
+ });
37570
37772
  }
37571
37773
  } else {
37572
- const result = await client.invokeRaw(Method.ADD_REACTION, {
37573
- oneofKind: "addReaction",
37574
- 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),
37575
37784
  emoji: emoji3,
37576
- messageId,
37577
- 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)}`);
37578
37800
  }
37579
- });
37580
- if (result.oneofKind !== "addReaction") {
37581
- 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
+ });
37582
37818
  }
37583
37819
  }
37584
37820
  return jsonResult({
@@ -37752,6 +37988,7 @@ var inlineMessageActions = {
37752
37988
  fn: async (client) => {
37753
37989
  const query = readStringParam(params, "query") ?? readStringParam(params, "q") ?? undefined;
37754
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();
37755
37992
  const result = await client.invokeRaw(Method.GET_CHATS, {
37756
37993
  oneofKind: "getChats",
37757
37994
  getChats: {}
@@ -37761,23 +37998,26 @@ var inlineMessageActions = {
37761
37998
  }
37762
37999
  const dialogByChatId = buildDialogMap(result.getChats.dialogs ?? []);
37763
38000
  const usersById = buildUserMap2(result.getChats.users ?? []);
37764
- const entries = (result.getChats.chats ?? []).map((chat) => mapChatEntry({ chat, dialogByChatId, usersById }));
37765
- const normalizedQuery = query?.trim().toLowerCase() ?? "";
37766
- const filtered = normalizedQuery ? entries.filter((entry) => {
37767
- const haystack = [
37768
- entry.id,
37769
- entry.title,
37770
- entry.peer?.kind === "user" ? entry.peer.username ?? "" : "",
37771
- entry.peer?.kind === "user" ? entry.peer.name ?? "" : ""
37772
- ].join(`
37773
- `).toLowerCase();
37774
- return haystack.includes(normalizedQuery);
37775
- }) : 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));
37776
38011
  return jsonResult(toJsonSafe({
37777
38012
  ok: true,
38013
+ scope,
37778
38014
  query: query ?? null,
37779
- count: filtered.length,
37780
- 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)
37781
38021
  }));
37782
38022
  }
37783
38023
  });
@@ -38201,6 +38441,239 @@ var inlineMessageActions = {
38201
38441
  };
38202
38442
  var inlineSupportedActions = listAllActions();
38203
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
+
38204
38677
  // src/inline/channel.ts
38205
38678
  var activeMonitors = new Map;
38206
38679
  var meta3 = {
@@ -38239,6 +38712,82 @@ function parseInlineId2(raw) {
38239
38712
  }
38240
38713
  return;
38241
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
+ }
38242
38791
  function parseInlineOutboundTarget(params) {
38243
38792
  let normalizedTarget = params.raw.trim();
38244
38793
  const hadInlinePrefix = /^inline:/i.test(normalizedTarget);
@@ -38274,6 +38823,46 @@ function parseInlineOutboundTarget(params) {
38274
38823
  raw: params.raw
38275
38824
  };
38276
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
+ }
38277
38866
  async function listInlineTargetIds(client) {
38278
38867
  const result = await client.invokeRaw(Method.GET_CHATS, {
38279
38868
  oneofKind: "getChats",
@@ -38347,6 +38936,31 @@ function buildInlineDisplayName(params) {
38347
38936
  return `@${username}`;
38348
38937
  return "Unknown";
38349
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
+ }
38350
38964
  function toInlineUserDirectoryEntry(user) {
38351
38965
  return {
38352
38966
  kind: "user",
@@ -38650,10 +39264,25 @@ var inlineChannelPlugin = {
38650
39264
  },
38651
39265
  reload: { configPrefixes: ["channels.inline"] },
38652
39266
  configSchema: buildChannelConfigSchema(InlineConfigSchema),
39267
+ setup: inlineSetupAdapter,
39268
+ setupWizard: inlineSetupWizard,
38653
39269
  config: {
38654
39270
  listAccountIds: (cfg) => listInlineAccountIds(cfg),
38655
39271
  resolveAccount: (cfg, accountId) => resolveInlineAccount({ cfg, accountId: accountId ?? null }),
38656
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
+ }),
38657
39286
  isConfigured: (account) => account.configured,
38658
39287
  describeAccount: (account) => ({
38659
39288
  accountId: account.accountId,
@@ -38704,6 +39333,47 @@ var inlineChannelPlugin = {
38704
39333
  ];
38705
39334
  }
38706
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
+ },
38707
39377
  groups: {
38708
39378
  resolveRequireMention: ({ cfg, accountId, groupId }) => {
38709
39379
  const resolved = resolveInlineAccount({ cfg, accountId: accountId ?? null });
@@ -38756,7 +39426,9 @@ var inlineChannelPlugin = {
38756
39426
  messageToolHints: ({ cfg, accountId }) => [
38757
39427
  "- Inline targeting: omit `target` to reply in the current chat.",
38758
39428
  "- Inline explicit targets: `chat:<chatId>` for chats and `user:<userId>` for direct users. Prefer `user:` for DM user targets.",
38759
- "- 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.",
38760
39432
  "- Inline special tools: use `inline_nudge` to send a nudge, and `inline_forward` to forward message ids between chats or users.",
38761
39433
  ...isInlineReplyThreadsEnabled({ cfg, accountId: accountId ?? null }) ? [
38762
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."
@@ -38765,6 +39437,29 @@ var inlineChannelPlugin = {
38765
39437
  },
38766
39438
  messaging: {
38767
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
+ }),
38768
39463
  targetResolver: {
38769
39464
  looksLikeId: looksLikeInlineTargetId,
38770
39465
  hint: "<chatId | chat:<chatId> | user:<userId>>"
@@ -38982,18 +39677,14 @@ var inlineChannelPlugin = {
38982
39677
  running: false,
38983
39678
  lastStartAt: null,
38984
39679
  lastStopAt: null,
38985
- lastError: null
39680
+ lastError: null,
39681
+ lastProbeAt: null
38986
39682
  },
38987
- buildChannelSummary: ({ snapshot }) => ({
38988
- configured: snapshot.configured ?? false,
38989
- running: snapshot.running ?? false,
38990
- lastStartAt: snapshot.lastStartAt ?? null,
38991
- lastStopAt: snapshot.lastStopAt ?? null,
38992
- lastError: snapshot.lastError ?? null,
38993
- lastInboundAt: snapshot.lastInboundAt ?? null,
38994
- lastOutboundAt: snapshot.lastOutboundAt ?? null
38995
- }),
38996
- 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 }) => ({
38997
39688
  accountId: account.accountId,
38998
39689
  name: account.name,
38999
39690
  enabled: account.enabled,
@@ -39005,7 +39696,9 @@ var inlineChannelPlugin = {
39005
39696
  lastStopAt: runtime2?.lastStopAt ?? null,
39006
39697
  lastError: runtime2?.lastError ?? null,
39007
39698
  lastInboundAt: runtime2?.lastInboundAt ?? null,
39008
- lastOutboundAt: runtime2?.lastOutboundAt ?? null
39699
+ lastOutboundAt: runtime2?.lastOutboundAt ?? null,
39700
+ lastProbeAt: runtime2?.lastProbeAt ?? null,
39701
+ ...probe !== undefined ? { probe } : {}
39009
39702
  })
39010
39703
  },
39011
39704
  gateway: {
@@ -39058,6 +39751,30 @@ var inlineChannelPlugin = {
39058
39751
  running: false,
39059
39752
  lastStopAt: Date.now()
39060
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
+ };
39061
39778
  }
39062
39779
  }
39063
39780
  };
@@ -40127,5 +40844,5 @@ export {
40127
40844
  src_default as default
40128
40845
  };
40129
40846
 
40130
- //# debugId=608084D1D5C29A8D64756E2164756E21
40847
+ //# debugId=AB3E531CE13839CA64756E2164756E21
40131
40848
  //# sourceMappingURL=index.js.map