@openclaw/line 2026.7.2-beta.5 → 2026.8.1-beta.2

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.
Files changed (146) hide show
  1. package/dist/{accounts-CSH5sk8U.js → accounts-BqEVsDoB.js} +24 -88
  2. package/dist/api.js +2 -3
  3. package/dist/{card-command-BEMIm8D8.js → card-command-DALdUSnc.js} +14 -22
  4. package/dist/{channel-CTaWrzfl.js → channel-CAdQJRw7.js} +75 -77
  5. package/dist/channel-plugin-api.js +1 -1
  6. package/dist/channel.runtime-CuKoy9xw.js +4 -0
  7. package/dist/contract-api.js +1 -1
  8. package/dist/{flex-templates-DVy_wog4.js → flex-templates-BBBlNqpV.js} +108 -83
  9. package/dist/index.js +1 -1
  10. package/dist/markdown-to-line-D4I-6lm6.js +499 -0
  11. package/dist/{monitor-DzFU5UPV.js → monitor-QEh_ZqyR.js} +114 -94
  12. package/dist/monitor.runtime-72isajCp.js +2 -0
  13. package/dist/outbound.runtime-CWQMIE7N.js +3 -0
  14. package/dist/{reply-payload-transform-BRM2fRvK.js → reply-payload-transform-BOPGHVPo.js} +4 -3
  15. package/dist/runtime-api.js +9 -7
  16. package/dist/send-CFFr0-rZ.js +366 -0
  17. package/dist/setup-api.js +1 -1
  18. package/dist/{setup-surface-BHQOSD8R.js → setup-surface-W0Dbgu58.js} +51 -68
  19. package/openclaw.plugin.json +7 -0
  20. package/package.json +9 -5
  21. package/dist/channel.runtime-D4Paci2y.js +0 -4
  22. package/dist/markdown-to-line-Dn00k1bq.js +0 -799
  23. package/dist/monitor.runtime-B8xjVqev.js +0 -2
  24. package/dist/outbound.runtime-eHl7NNHu.js +0 -2
  25. package/dist/send-receipt-CY2ey8XH.js +0 -107
  26. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/LICENSE +0 -0
  27. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/README.md +0 -0
  28. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/assert/strict.d.ts +0 -0
  29. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/assert.d.ts +0 -0
  30. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/async_hooks.d.ts +0 -0
  31. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/buffer.buffer.d.ts +0 -0
  32. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/buffer.d.ts +0 -0
  33. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/child_process.d.ts +0 -0
  34. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/cluster.d.ts +0 -0
  35. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/compatibility/iterators.d.ts +0 -0
  36. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/console.d.ts +0 -0
  37. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/constants.d.ts +0 -0
  38. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/crypto.d.ts +0 -0
  39. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/dgram.d.ts +0 -0
  40. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/diagnostics_channel.d.ts +0 -0
  41. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/dns/promises.d.ts +0 -0
  42. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/dns.d.ts +0 -0
  43. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/domain.d.ts +0 -0
  44. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/events.d.ts +0 -0
  45. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/fs/promises.d.ts +0 -0
  46. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/fs.d.ts +0 -0
  47. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/globals.d.ts +0 -0
  48. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/globals.typedarray.d.ts +0 -0
  49. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/http.d.ts +0 -0
  50. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/http2.d.ts +0 -0
  51. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/https.d.ts +0 -0
  52. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/index.d.ts +0 -0
  53. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/inspector.d.ts +0 -0
  54. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/inspector.generated.d.ts +0 -0
  55. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/module.d.ts +0 -0
  56. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/net.d.ts +0 -0
  57. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/os.d.ts +0 -0
  58. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/package.json +0 -0
  59. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/path.d.ts +0 -0
  60. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/perf_hooks.d.ts +0 -0
  61. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/process.d.ts +0 -0
  62. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/punycode.d.ts +0 -0
  63. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/querystring.d.ts +0 -0
  64. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/readline/promises.d.ts +0 -0
  65. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/readline.d.ts +0 -0
  66. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/repl.d.ts +0 -0
  67. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/sea.d.ts +0 -0
  68. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/sqlite.d.ts +0 -0
  69. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/stream/consumers.d.ts +0 -0
  70. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/stream/promises.d.ts +0 -0
  71. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/stream/web.d.ts +0 -0
  72. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/stream.d.ts +0 -0
  73. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/string_decoder.d.ts +0 -0
  74. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/test.d.ts +0 -0
  75. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/timers/promises.d.ts +0 -0
  76. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/timers.d.ts +0 -0
  77. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/tls.d.ts +0 -0
  78. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/trace_events.d.ts +0 -0
  79. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.6/buffer.buffer.d.ts +0 -0
  80. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.6/compatibility/float16array.d.ts +0 -0
  81. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.6/globals.typedarray.d.ts +0 -0
  82. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.6/index.d.ts +0 -0
  83. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.7/compatibility/float16array.d.ts +0 -0
  84. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.7/index.d.ts +0 -0
  85. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/tty.d.ts +0 -0
  86. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/url.d.ts +0 -0
  87. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/util.d.ts +0 -0
  88. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/v8.d.ts +0 -0
  89. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/vm.d.ts +0 -0
  90. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/wasi.d.ts +0 -0
  91. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/abortcontroller.d.ts +0 -0
  92. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/crypto.d.ts +0 -0
  93. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/domexception.d.ts +0 -0
  94. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/events.d.ts +0 -0
  95. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/fetch.d.ts +0 -0
  96. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/navigator.d.ts +0 -0
  97. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/storage.d.ts +0 -0
  98. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/streams.d.ts +0 -0
  99. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/worker_threads.d.ts +0 -0
  100. /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/zlib.d.ts +0 -0
  101. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/LICENSE +0 -0
  102. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/README.md +0 -0
  103. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/agent.d.ts +0 -0
  104. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/api.d.ts +0 -0
  105. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/balanced-pool.d.ts +0 -0
  106. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/cache-interceptor.d.ts +0 -0
  107. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/cache.d.ts +0 -0
  108. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/client-stats.d.ts +0 -0
  109. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/client.d.ts +0 -0
  110. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/connector.d.ts +0 -0
  111. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/content-type.d.ts +0 -0
  112. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/cookies.d.ts +0 -0
  113. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/diagnostics-channel.d.ts +0 -0
  114. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/dispatcher.d.ts +0 -0
  115. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/env-http-proxy-agent.d.ts +0 -0
  116. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/errors.d.ts +0 -0
  117. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/eventsource.d.ts +0 -0
  118. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/fetch.d.ts +0 -0
  119. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/formdata.d.ts +0 -0
  120. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/global-dispatcher.d.ts +0 -0
  121. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/global-origin.d.ts +0 -0
  122. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/h2c-client.d.ts +0 -0
  123. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/handlers.d.ts +0 -0
  124. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/header.d.ts +0 -0
  125. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/index.d.ts +0 -0
  126. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/interceptors.d.ts +0 -0
  127. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-agent.d.ts +0 -0
  128. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-call-history.d.ts +0 -0
  129. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-client.d.ts +0 -0
  130. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-errors.d.ts +0 -0
  131. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-interceptor.d.ts +0 -0
  132. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-pool.d.ts +0 -0
  133. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/package.json +0 -0
  134. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/patch.d.ts +0 -0
  135. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/pool-stats.d.ts +0 -0
  136. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/pool.d.ts +0 -0
  137. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/proxy-agent.d.ts +0 -0
  138. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/readable.d.ts +0 -0
  139. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/retry-agent.d.ts +0 -0
  140. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/retry-handler.d.ts +0 -0
  141. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/round-robin-pool.d.ts +0 -0
  142. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/snapshot-agent.d.ts +0 -0
  143. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/util.d.ts +0 -0
  144. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/utility.d.ts +0 -0
  145. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/webidl.d.ts +0 -0
  146. /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/websocket.d.ts +0 -0
@@ -1,14 +1,16 @@
1
- import { i as resolveLineAccount, r as resolveDefaultLineAccountId } from "./accounts-CSH5sk8U.js";
1
+ import { i as resolveLineAccount, r as resolveDefaultLineAccountId } from "./accounts-BqEVsDoB.js";
2
2
  import { a as resolveLineGroupConfigEntry, n as getLineRuntime, t as buildLineQuickReplyFallbackText } from "./quick-reply-fallback-D86FwAfr.js";
3
- import { n as buildLineMediaMessage, r as hasLineSpecificMediaOptions } from "./send-receipt-CY2ey8XH.js";
4
- import { E as buildTemplateMessageFromPayload, S as replyMessageLine, i as processLineMessage, l as createLocationMessage, p as getUserDisplayName, s as createFlexMessage, u as createQuickReplyItems, v as pushMessageLine, w as showLoadingAnimation, y as pushMessagesLine } from "./markdown-to-line-Dn00k1bq.js";
3
+ import { v as buildLineMediaMessage, y as hasLineSpecificMediaOptions } from "./flex-templates-BBBlNqpV.js";
4
+ import { i as processLineMessage, o as buildTemplateMessageFromPayload } from "./markdown-to-line-D4I-6lm6.js";
5
+ import { _ as replyMessageLine, a as createQuickReplyItems, c as getUserDisplayName, i as createLocationMessage, m as pushMessagesLine, n as createFlexMessage, p as pushMessageLine, y as showLoadingAnimation } from "./send-CFFr0-rZ.js";
5
6
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
6
- import { normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
7
- import { DEFAULT_INGRESS_ADOPTION_STALL_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, bindIngressLifecycleToReplyOptions, createChannelIngressMonitor } from "openclaw/plugin-sdk/channel-outbound";
7
+ import { normalizeNullableString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
8
+ import { DEFAULT_INGRESS_ADOPTION_STALL_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor } from "openclaw/plugin-sdk/channel-outbound";
9
+ import { buildChannelInboundEventContext, buildMentionRegexes, formatInboundEnvelope, formatInboundMediaUnavailableText, formatLocationText, hasFinalInboundReplyDispatch, isChannelPartialDeliveryError, matchesMentionPatterns, resolveInboundSessionEnvelopeContext, toInboundMediaFactsWithMetadata, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
8
10
  import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
9
11
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
10
- import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
11
12
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
13
+ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
12
14
  import { firstDefined } from "openclaw/plugin-sdk/allow-from";
13
15
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
14
16
  import { MediaFetchError } from "openclaw/plugin-sdk/media-runtime";
@@ -16,11 +18,13 @@ import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
16
18
  import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, waitForAbortSignal, warn } from "openclaw/plugin-sdk/runtime-env";
17
19
  import { fetchWithRuntimeDispatcherOrMockedGlobal } from "openclaw/plugin-sdk/runtime-fetch";
18
20
  import { HTTPFetchError } from "@line/bot-sdk";
21
+ import { collectErrorGraphCandidates, extractErrorCode, formatErrorMessage, readErrorName, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
19
22
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
20
- import { buildChannelInboundEventContext, buildMentionRegexes, formatInboundEnvelope, formatInboundMediaUnavailableText, formatLocationText, hasFinalInboundReplyDispatch, matchesMentionPatterns, resolveInboundSessionEnvelopeContext, toInboundMediaFacts, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
23
+ import { channelReadyPatch, channelStoppedPatch } from "openclaw/plugin-sdk/gateway-runtime";
21
24
  import { chunkMarkdownText } from "openclaw/plugin-sdk/reply-runtime";
22
- import { isRequestBodyLimitError, normalizePluginHttpPath, normalizeWebhookPath, registerWebhookTargetWithPluginRoute, requestBodyErrorToText, resolveSingleWebhookTarget } from "openclaw/plugin-sdk/webhook-ingress";
25
+ import { canonicalizeWebhookRouteKey, isRequestBodyLimitError, normalizePluginHttpPath, normalizeWebhookPath, registerWebhookTargetWithPluginRoute, requestBodyErrorToText, resolveSingleWebhookTarget } from "openclaw/plugin-sdk/webhook-ingress";
23
26
  import { beginWebhookRequestPipelineOrReject, createWebhookInFlightLimiter, isRequestBodyLimitError as isRequestBodyLimitError$1, readRequestBodyWithLimit, requestBodyErrorToText as requestBodyErrorToText$1, runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guards";
27
+ import { classifyTransientNetworkErrorCode } from "openclaw/plugin-sdk/retry-runtime";
24
28
  import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
25
29
  import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
26
30
  import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
@@ -146,8 +150,15 @@ function toLineDeliveryError(error) {
146
150
  return error instanceof Error ? error : new Error("LINE message send failed", { cause: error });
147
151
  }
148
152
  function getLineHttpError(error) {
149
- if (error instanceof HTTPFetchError) return error;
150
- return error instanceof Error && error.cause instanceof HTTPFetchError ? error.cause : void 0;
153
+ return collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]).find((candidate) => candidate instanceof HTTPFetchError);
154
+ }
155
+ function canFallbackAfterLineReplyFailure(error) {
156
+ const httpError = getLineHttpError(error);
157
+ if (httpError) return httpError.status >= 400 && httpError.status < 500 && httpError.status !== 408;
158
+ const candidates = collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]);
159
+ if (candidates.some((candidate) => readErrorName(candidate) === "AbortError" || classifyTransientNetworkErrorCode(extractErrorCode(candidate)) === "ambiguous")) return false;
160
+ if (candidates.some((candidate) => classifyTransientNetworkErrorCode(extractErrorCode(candidate)) === "pre-connect")) return true;
161
+ return !candidates.some((candidate) => candidate instanceof TypeError && candidate.message === "fetch failed");
151
162
  }
152
163
  function markLineVisibleDeliveryError(error) {
153
164
  const deliveryError = toLineDeliveryError(error);
@@ -175,6 +186,7 @@ async function deliverLineAutoReply(params) {
175
186
  visibleReplySent = true;
176
187
  return result;
177
188
  } catch (error) {
189
+ if (isChannelPartialDeliveryError(error)) visibleReplySent = true;
178
190
  if (visibleReplySent) throw markLineVisibleDeliveryError(error);
179
191
  throw error;
180
192
  }
@@ -191,7 +203,7 @@ async function deliverLineAutoReply(params) {
191
203
  accountId
192
204
  }));
193
205
  } catch (error) {
194
- if (typeof error === "object" && error !== null) failedPushSegments.set(error, {
206
+ if (!isChannelPartialDeliveryError(error) && typeof error === "object" && error !== null) failedPushSegments.set(error, {
195
207
  allowFailedBatchTextRecovery,
196
208
  failedBatch: batch,
197
209
  unattemptedTail: [...messages.slice(i + batch.length), ...externalTail]
@@ -211,6 +223,7 @@ async function deliverLineAutoReply(params) {
211
223
  accountId
212
224
  });
213
225
  } catch (err) {
226
+ if (isChannelPartialDeliveryError(err) || !canFallbackAfterLineReplyFailure(err)) throw err;
214
227
  deps.onReplyError?.(err);
215
228
  await pushLineMessages(replyBatch, getLineHttpError(err)?.status === 400, remaining.slice(replyBatch.length));
216
229
  } finally {
@@ -222,19 +235,26 @@ async function deliverLineAutoReply(params) {
222
235
  };
223
236
  const richMessages = [];
224
237
  const hasQuickReplies = Boolean(lineData.quickReplies?.length);
225
- if (lineData.flexMessage) richMessages.push(deps.createFlexMessage(truncateUtf16Safe(lineData.flexMessage.altText, 400), lineData.flexMessage.contents));
238
+ if (lineData.flexMessage) richMessages.push(deps.createFlexMessage(lineData.flexMessage.altText, lineData.flexMessage.contents));
226
239
  if (lineData.templateMessage) {
227
240
  const templateMsg = deps.buildTemplateMessageFromPayload(lineData.templateMessage);
228
241
  if (templateMsg) richMessages.push(templateMsg);
229
242
  }
230
- if (lineData.location) richMessages.push(deps.createLocationMessage(lineData.location));
243
+ if (lineData.location) {
244
+ const locationMessage = deps.createLocationMessage(lineData.location);
245
+ if (locationMessage) richMessages.push(locationMessage);
246
+ }
231
247
  const visibleText = payload.text ? sanitizeAssistantVisibleText(payload.text) : "";
232
248
  const processed = visibleText ? deps.processLineMessage(visibleText) : {
233
249
  text: "",
234
250
  flexMessages: []
235
251
  };
236
- for (const flexMsg of processed.flexMessages) richMessages.push(deps.createFlexMessage(truncateUtf16Safe(flexMsg.altText, 400), flexMsg.contents));
237
- const chunks = processed.text ? deps.chunkMarkdownText(processed.text, textLimit) : [];
252
+ if (!processed.segments) for (const flexMsg of processed.flexMessages) richMessages.push(deps.createFlexMessage(flexMsg.altText, flexMsg.contents));
253
+ const orderedMessages = processed.segments?.flatMap((segment) => segment.type === "flex" ? [deps.createFlexMessage(segment.message.altText, segment.message.contents)] : deps.chunkMarkdownText(segment.text, textLimit).map((text) => ({
254
+ type: "text",
255
+ text
256
+ })));
257
+ const chunks = orderedMessages ? orderedMessages.flatMap((message) => message.type === "text" ? [message.text] : []) : processed.text ? deps.chunkMarkdownText(processed.text, textLimit) : [];
238
258
  const mediaUrls = resolveSendableOutboundReplyParts(payload).mediaUrls;
239
259
  const mediaOpts = {
240
260
  mediaKind: hasLineSpecificMediaOptions(lineData) ? lineData.mediaKind : "image",
@@ -263,14 +283,14 @@ async function deliverLineAutoReply(params) {
263
283
  text: buildLineQuickReplyFallbackText(lineData.quickReplies)
264
284
  });
265
285
  if (hasQuickReplies) {
266
- const targetMessages = textMessages.length > 0 ? textMessages : richMediaMessages;
286
+ const targetMessages = orderedMessages?.length ? orderedMessages : textMessages.length > 0 ? textMessages : richMediaMessages;
267
287
  const lastIndex = targetMessages.length - 1;
268
288
  targetMessages[lastIndex] = {
269
289
  ...expectDefined(targetMessages[lastIndex], "last LINE auto-reply message"),
270
290
  quickReply: deps.createQuickReplyItems(lineData.quickReplies)
271
291
  };
272
292
  }
273
- const messages = hasQuickReplies ? [...richMediaMessages, ...textMessages] : [...textMessages, ...richMediaMessages];
293
+ const messages = hasQuickReplies ? [...richMediaMessages, ...orderedMessages ?? textMessages] : [...orderedMessages ?? textMessages, ...richMediaMessages];
274
294
  try {
275
295
  await sendLineMessages(messages, true);
276
296
  } catch (err) {
@@ -451,7 +471,7 @@ async function finalizeLineInboundContext(params) {
451
471
  sessionKey: params.route.sessionKey
452
472
  });
453
473
  const agentBody = params.agentBody ?? params.rawBody;
454
- const media = toInboundMediaFacts(params.media);
474
+ const media = await toInboundMediaFactsWithMetadata(params.media);
455
475
  const body = formatInboundEnvelope({
456
476
  channel: "LINE",
457
477
  from: conversationLabel,
@@ -462,7 +482,8 @@ async function finalizeLineInboundContext(params) {
462
482
  previousTimestamp,
463
483
  envelope: envelopeOptions
464
484
  });
465
- const ctxPayload = buildChannelInboundEventContext({
485
+ const ctxPayload = (params.buildContext ?? buildChannelInboundEventContext)({
486
+ channelIngress: params.channelIngress,
466
487
  channel: "line",
467
488
  accountId: params.route.accountId,
468
489
  messageId: params.messageSid,
@@ -592,6 +613,13 @@ async function buildLineMessageContext(params) {
592
613
  timestamp,
593
614
  messageSid: messageId,
594
615
  commandAuthorized,
616
+ channelIngress: params.resolveChannelIngress ? await params.resolveChannelIngress({
617
+ agentId: route.agentId,
618
+ sessionKey: route.sessionKey,
619
+ messageId,
620
+ inboundEventKind: "user_request"
621
+ }) : params.channelIngress,
622
+ buildContext: params.buildContext,
595
623
  media: mediaFacts,
596
624
  locationContext,
597
625
  verboseLog: {
@@ -648,6 +676,13 @@ async function buildLinePostbackContext(params) {
648
676
  timestamp,
649
677
  messageSid,
650
678
  commandAuthorized,
679
+ channelIngress: params.resolveChannelIngress ? await params.resolveChannelIngress({
680
+ agentId: route.agentId,
681
+ sessionKey: route.sessionKey,
682
+ messageId: messageSid,
683
+ inboundEventKind: "user_request"
684
+ }) : params.channelIngress,
685
+ buildContext: params.buildContext,
651
686
  media: [],
652
687
  verboseLog: { kind: "postback" }
653
688
  });
@@ -756,6 +791,7 @@ async function sendLinePairingReply(params) {
756
791
  return;
757
792
  } catch (err) {
758
793
  logVerbose(`line pairing reply failed for ${senderId}: ${String(err)}`);
794
+ if (isChannelPartialDeliveryError(err)) return;
759
795
  }
760
796
  try {
761
797
  await pushMessageLine(`line:${senderId}`, text, {
@@ -813,7 +849,7 @@ async function shouldProcessLineEvent(event, context) {
813
849
  hasAnyMention: hasAnyLineMention(event.message)
814
850
  };
815
851
  })();
816
- const access = await resolveStableChannelMessageIngress({
852
+ const resolveAccess = async (contextBinding) => await resolveStableChannelMessageIngress({
817
853
  channelId: "line",
818
854
  accountId: account.accountId,
819
855
  identity: {
@@ -829,6 +865,7 @@ async function shouldProcessLineEvent(event, context) {
829
865
  kind: isGroup ? "group" : "direct",
830
866
  id: (groupId ?? roomId ?? senderId) || "unknown"
831
867
  },
868
+ ...contextBinding ? { contextBinding } : {},
832
869
  ...isGroup && groupConfig?.enabled === false ? { route: {
833
870
  id: "line:group-config",
834
871
  enabled: false
@@ -856,13 +893,17 @@ async function shouldProcessLineEvent(event, context) {
856
893
  groupOwnerAllowFrom: "none"
857
894
  }
858
895
  });
896
+ const access = await resolveAccess();
859
897
  warnMissingProviderGroupPolicyFallbackOnce({
860
898
  providerMissingFallbackApplied,
861
899
  providerKey: "line",
862
900
  accountId: account.accountId,
863
901
  log: (message) => logVerbose(message)
864
902
  });
865
- if (access.senderAccess.decision === "allow" && (access.ingress.admission === "dispatch" || access.ingress.admission === "observe" || access.ingress.admission === "skip")) return access;
903
+ if (access.senderAccess.decision === "allow" && (access.ingress.admission === "dispatch" || access.ingress.admission === "observe" || access.ingress.admission === "skip")) return {
904
+ access,
905
+ resolveBoundAccess: resolveAccess
906
+ };
866
907
  if (access.senderAccess.decision === "allow") {
867
908
  logVerbose(`Blocked line event (${access.ingress.reasonCode})`);
868
909
  return null;
@@ -933,7 +974,7 @@ async function handleMessageEvent(event, context) {
933
974
  const decision = await shouldProcessLineEvent(event, context);
934
975
  if (!decision) return;
935
976
  const { isGroup, groupId, roomId } = getLineSourceInfo(event.source);
936
- if (isGroup && decision.activationAccess.shouldSkip) {
977
+ if (isGroup && decision.access.activationAccess.shouldSkip) {
937
978
  const rawText = message.type === "text" ? message.text : "";
938
979
  const sourceInfo = getLineSourceInfo(event.source);
939
980
  logVerbose(`line: skipping group message (requireMention, not mentioned)`);
@@ -975,8 +1016,10 @@ async function handleMessageEvent(event, context) {
975
1016
  mediaUnavailable,
976
1017
  cfg,
977
1018
  account,
978
- commandAuthorized: decision.commandAccess.authorized,
979
- inboundHistory: historyReservation.inboundHistory
1019
+ commandAuthorized: decision.access.commandAccess.authorized,
1020
+ resolveChannelIngress: decision.resolveBoundAccess,
1021
+ inboundHistory: historyReservation.inboundHistory,
1022
+ buildContext: context.buildContext
980
1023
  });
981
1024
  if (!messageContext) {
982
1025
  logVerbose("line: skipping empty message");
@@ -1013,7 +1056,9 @@ async function handlePostbackEvent(event, context) {
1013
1056
  event,
1014
1057
  cfg: context.cfg,
1015
1058
  account: context.account,
1016
- commandAuthorized: decision.commandAccess.authorized
1059
+ commandAuthorized: decision.access.commandAccess.authorized,
1060
+ resolveChannelIngress: decision.resolveBoundAccess,
1061
+ buildContext: context.buildContext
1017
1062
  });
1018
1063
  if (!postbackContext) return;
1019
1064
  await context.processMessage(postbackContext, context.turnAdoptionLifecycle ? { turnAdoptionLifecycle: context.turnAdoptionLifecycle } : {});
@@ -1026,7 +1071,7 @@ async function handleLineWebhookEvents(events, context) {
1026
1071
  context.runtime.error?.(danger(`line: event handler failed: ${String(err)}`));
1027
1072
  firstError ??= err;
1028
1073
  }
1029
- if (firstError) throw toLintErrorObject(firstError, "Non-Error thrown");
1074
+ if (firstError) throw toErrorObject(firstError, "Non-Error thrown");
1030
1075
  }
1031
1076
  async function handleLineWebhookEvent(event, context) {
1032
1077
  switch (event.type) {
@@ -1051,13 +1096,6 @@ async function handleLineWebhookEvent(event, context) {
1051
1096
  default: logVerbose(`line: unhandled event type: ${event.type}`);
1052
1097
  }
1053
1098
  }
1054
- function toLintErrorObject(value, fallbackMessage) {
1055
- if (value instanceof Error) return value;
1056
- if (typeof value === "string") return new Error(value);
1057
- const error = new Error(fallbackMessage, { cause: value });
1058
- if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
1059
- return error;
1060
- }
1061
1099
  //#endregion
1062
1100
  //#region extensions/line/src/webhook-spool.ts
1063
1101
  const LINE_WEBHOOK_SPOOL_VERSION = 1;
@@ -1065,14 +1103,7 @@ const LINE_WEBHOOK_DRAIN_INTERVAL_MS = 500;
1065
1103
  const LINE_WEBHOOK_MAX_CONCURRENT_DELIVERIES = 8;
1066
1104
  const LINE_WEBHOOK_DRAIN_SCAN_LIMIT = 100;
1067
1105
  const LINE_WEBHOOK_ACTIVE_DELIVERY_STOP_GRACE_MS = 5e3;
1068
- const LINE_WEBHOOK_TOMBSTONE_TTL_MS = 720 * 60 * 6e4;
1069
- const LINE_WEBHOOK_TOMBSTONE_MAX_ENTRIES = 4096;
1070
- var LineWebhookPayloadError = class extends Error {
1071
- constructor(message, options) {
1072
- super(message, options);
1073
- this.name = "LineWebhookPayloadError";
1074
- }
1075
- };
1106
+ const LineWebhookPayloadError = createChannelIngressError("LineWebhookPayloadError");
1076
1107
  var LineWebhookTerminalDeliveryError = class extends Error {
1077
1108
  constructor(message, options) {
1078
1109
  super(message, options);
@@ -1080,18 +1111,15 @@ var LineWebhookTerminalDeliveryError = class extends Error {
1080
1111
  this.name = "LineWebhookTerminalDeliveryError";
1081
1112
  }
1082
1113
  };
1083
- function nonEmptyString(value) {
1084
- return typeof value === "string" && value.trim() ? value.trim() : null;
1085
- }
1086
1114
  /** Message ids preserve the shipped replay-guard keyspace; other events use LINE's delivery id. */
1087
1115
  function eventIdFor(event) {
1088
1116
  if (!event || typeof event !== "object") throw new LineWebhookPayloadError("LINE webhook event must be an object.");
1089
1117
  const candidate = event;
1090
1118
  if (candidate.type === "message") {
1091
- const messageId = nonEmptyString(candidate.message?.id);
1119
+ const messageId = normalizeNullableString(candidate.message?.id);
1092
1120
  if (messageId) return `message:${messageId}`;
1093
1121
  }
1094
- const webhookEventId = nonEmptyString(candidate.webhookEventId);
1122
+ const webhookEventId = normalizeNullableString(candidate.webhookEventId);
1095
1123
  if (webhookEventId) return `event:${webhookEventId}`;
1096
1124
  throw new LineWebhookPayloadError("LINE webhook event is missing a stable delivery id.");
1097
1125
  }
@@ -1099,15 +1127,15 @@ function laneKeyFor(event, eventId) {
1099
1127
  if (!event || typeof event !== "object") return eventId;
1100
1128
  const source = event.source;
1101
1129
  if (source?.type === "group") {
1102
- const groupId = nonEmptyString(source.groupId);
1130
+ const groupId = normalizeNullableString(source.groupId);
1103
1131
  if (groupId) return `group:${groupId}`;
1104
1132
  }
1105
1133
  if (source?.type === "room") {
1106
- const roomId = nonEmptyString(source.roomId);
1134
+ const roomId = normalizeNullableString(source.roomId);
1107
1135
  if (roomId) return `room:${roomId}`;
1108
1136
  }
1109
1137
  if (source?.type === "user") {
1110
- const userId = nonEmptyString(source.userId);
1138
+ const userId = normalizeNullableString(source.userId);
1111
1139
  if (userId) return `user:${userId}`;
1112
1140
  }
1113
1141
  return eventId;
@@ -1121,9 +1149,6 @@ function parseStoredEvent(rawEvent) {
1121
1149
  }
1122
1150
  return event;
1123
1151
  }
1124
- function errorText(error) {
1125
- return error instanceof Error ? error.message : String(error);
1126
- }
1127
1152
  function isLineAuthenticationFailure(error) {
1128
1153
  if (!error || typeof error !== "object") return false;
1129
1154
  const status = error.status;
@@ -1143,7 +1168,6 @@ async function waitForActiveDeliveriesBeforeDispose(activeDeliveries) {
1143
1168
  function createLineWebhookSpool(options) {
1144
1169
  const queue = options.queue ?? getLineRuntime().state.openChannelIngressQueue({ accountId: options.accountId });
1145
1170
  const activeDeliveries = /* @__PURE__ */ new Set();
1146
- const deferredClaims = /* @__PURE__ */ new Map();
1147
1171
  let acceptsDeferredClaims = true;
1148
1172
  const monitor = createChannelIngressMonitor({
1149
1173
  queue,
@@ -1181,49 +1205,28 @@ function createLineWebhookSpool(options) {
1181
1205
  },
1182
1206
  createClaimError: (kind) => new LineWebhookPayloadError(kind === "invalid-version" ? "LINE webhook spool payload is invalid." : "LINE webhook event identity changed after durable admission.")
1183
1207
  },
1184
- deliver: async ({ event, destination }, lifecycle, claim) => {
1208
+ deliver: async ({ event, destination }, lifecycle) => {
1185
1209
  const boundLifecycle = bindIngressLifecycleToReplyOptions(lifecycle).turnAdoptionLifecycle;
1186
1210
  let handedOff = false;
1187
- let resolveDeferredClaim;
1188
- const deferredClaim = new Promise((resolve) => {
1189
- resolveDeferredClaim = resolve;
1190
- });
1191
- let deferredClaimSettled = false;
1192
- const settleDeferredClaim = () => {
1193
- if (deferredClaimSettled) return;
1194
- deferredClaimSettled = true;
1195
- if (deferredClaims.get(claim.id) === deferredClaim) deferredClaims.delete(claim.id);
1196
- resolveDeferredClaim();
1197
- };
1198
1211
  const delivery = options.deliver(event, destination, { turnAdoptionLifecycle: {
1199
1212
  ...boundLifecycle,
1200
1213
  onAdopted: async () => {
1201
1214
  handedOff = true;
1202
- try {
1203
- await boundLifecycle.onAdopted();
1204
- } finally {
1205
- settleDeferredClaim();
1206
- }
1215
+ await boundLifecycle.onAdopted();
1207
1216
  },
1208
1217
  onDeferred: () => {
1209
1218
  handedOff = true;
1210
1219
  if (!acceptsDeferredClaims) {
1211
- settleDeferredClaim();
1212
1220
  Promise.resolve().then(() => boundLifecycle.onAbandoned()).catch((error) => {
1213
- options.runtime.error?.(danger(`line: failed to abandon a late webhook delivery: ${errorText(error)}`));
1221
+ options.runtime.error?.(danger(`line: failed to abandon a late webhook delivery: ${formatErrorMessage(error)}`));
1214
1222
  });
1215
1223
  return;
1216
1224
  }
1217
- if (!deferredClaimSettled) deferredClaims.set(claim.id, deferredClaim);
1218
1225
  boundLifecycle.onDeferred();
1219
1226
  },
1220
1227
  onAbandoned: async () => {
1221
1228
  handedOff = true;
1222
- try {
1223
- await boundLifecycle.onAbandoned();
1224
- } finally {
1225
- settleDeferredClaim();
1226
- }
1229
+ await boundLifecycle.onAbandoned();
1227
1230
  }
1228
1231
  } });
1229
1232
  activeDeliveries.add(delivery);
@@ -1240,14 +1243,13 @@ function createLineWebhookSpool(options) {
1240
1243
  pollIntervalMs: LINE_WEBHOOK_DRAIN_INTERVAL_MS,
1241
1244
  retention: {
1242
1245
  pruneIntervalMs: 0,
1243
- completedTtlMs: LINE_WEBHOOK_TOMBSTONE_TTL_MS,
1244
- completedMaxEntries: LINE_WEBHOOK_TOMBSTONE_MAX_ENTRIES,
1245
- failedTtlMs: LINE_WEBHOOK_TOMBSTONE_TTL_MS,
1246
- failedMaxEntries: LINE_WEBHOOK_TOMBSTONE_MAX_ENTRIES
1246
+ completedMaxEntries: 4096,
1247
+ failedMaxEntries: 4096
1247
1248
  },
1248
1249
  appendRetryDelaysMs: [0],
1249
1250
  waitForDeliveryIdleBeforeRepump: false,
1250
1251
  waitForDeliveryIdleOnStop: false,
1252
+ deferredClaims: "manual",
1251
1253
  runPumpTask: runDetachedWebhookWork,
1252
1254
  admissionMode: "durable-after-stop",
1253
1255
  drain: {
@@ -1270,14 +1272,14 @@ function createLineWebhookSpool(options) {
1270
1272
  };
1271
1273
  if (isLineAuthenticationFailure(error)) return {
1272
1274
  reason: "authentication-failed",
1273
- message: errorText(error)
1275
+ message: formatErrorMessage(error)
1274
1276
  };
1275
1277
  return null;
1276
1278
  },
1277
1279
  onLog: (message) => options.runtime.error?.(danger(`line: ${message}`))
1278
1280
  },
1279
1281
  createStoppedError: () => /* @__PURE__ */ new Error("LINE webhook spool is stopped."),
1280
- onError: (error) => options.runtime.error?.(danger(`line: webhook spool drain failed: ${errorText(error)}`))
1282
+ onError: (error) => options.runtime.error?.(danger(`line: webhook spool drain failed: ${formatErrorMessage(error)}`))
1281
1283
  });
1282
1284
  let stopTask;
1283
1285
  return {
@@ -1298,7 +1300,7 @@ function createLineWebhookSpool(options) {
1298
1300
  try {
1299
1301
  const deliveriesSettled = await waitForActiveDeliveriesBeforeDispose(activeDeliveries);
1300
1302
  if (!deliveriesSettled) options.runtime.log(warn(`line: timed out after ${LINE_WEBHOOK_ACTIVE_DELIVERY_STOP_GRACE_MS}ms waiting for active webhook deliveries; releasing drain ownership`));
1301
- while (deferredClaims.size > 0) await Promise.allSettled(deferredClaims.values());
1303
+ await monitor.waitForDeferredClaims();
1302
1304
  acceptsDeferredClaims = false;
1303
1305
  if (deliveriesSettled) await monitor.waitForIdle();
1304
1306
  } finally {
@@ -1311,6 +1313,7 @@ function createLineWebhookSpool(options) {
1311
1313
  }
1312
1314
  //#endregion
1313
1315
  //#region extensions/line/src/bot.ts
1316
+ const DEFAULT_MEDIA_MAX_MB = 10;
1314
1317
  function createLineBot(opts) {
1315
1318
  const runtime = opts.runtime ?? createNonExitingRuntime();
1316
1319
  const cfg = opts.config ?? getRuntimeConfig();
@@ -1318,7 +1321,7 @@ function createLineBot(opts) {
1318
1321
  cfg,
1319
1322
  accountId: opts.accountId
1320
1323
  });
1321
- const mediaMaxBytes = (opts.mediaMaxMb ?? account.config.mediaMaxMb ?? 10) * 1024 * 1024;
1324
+ const mediaMaxBytes = ([opts.mediaMaxMb, account.config.mediaMaxMb].find((value) => typeof value === "number" && value > 0) ?? DEFAULT_MEDIA_MAX_MB) * 1024 * 1024;
1322
1325
  const processMessage = opts.onMessage ?? (async () => {
1323
1326
  logVerbose("line: no message handler configured");
1324
1327
  });
@@ -1330,6 +1333,7 @@ function createLineBot(opts) {
1330
1333
  cfg,
1331
1334
  account,
1332
1335
  runtime,
1336
+ buildContext: opts.buildContext,
1333
1337
  mediaMaxBytes,
1334
1338
  processMessage,
1335
1339
  ...control.turnAdoptionLifecycle ? { turnAdoptionLifecycle: control.turnAdoptionLifecycle } : {},
@@ -1451,7 +1455,7 @@ function createLineNodeWebhookHandler(params) {
1451
1455
  res.end(JSON.stringify({ error: requestBodyErrorToText$1("REQUEST_BODY_TIMEOUT") }));
1452
1456
  return;
1453
1457
  }
1454
- params.runtime.error?.(danger(`line webhook error: ${String(err)}`));
1458
+ params.runtime.error?.(danger(`line webhook error: ${formatErrorMessage(err)}`));
1455
1459
  if (!res.headersSent) {
1456
1460
  res.statusCode = 500;
1457
1461
  res.setHeader("Content-Type", "application/json");
@@ -1465,6 +1469,14 @@ function createLineNodeWebhookHandler(params) {
1465
1469
  const lineWebhookInFlightLimiter = createWebhookInFlightLimiter();
1466
1470
  const LINE_WEBHOOK_PREAUTH_MAX_BODY_BYTES = 64 * 1024;
1467
1471
  const LINE_WEBHOOK_PREAUTH_BODY_TIMEOUT_MS = 5e3;
1472
+ async function registerLineWebhookTarget(params, bot) {
1473
+ try {
1474
+ return registerWebhookTargetWithPluginRoute(params);
1475
+ } catch (error) {
1476
+ await Promise.allSettled([bot.stop()]);
1477
+ throw error;
1478
+ }
1479
+ }
1468
1480
  const lineWebhookTargets = /* @__PURE__ */ new Map();
1469
1481
  function startLineLoadingKeepalive(params) {
1470
1482
  const intervalMs = params.intervalMs ?? 18e3;
@@ -1487,7 +1499,7 @@ function startLineLoadingKeepalive(params) {
1487
1499
  };
1488
1500
  }
1489
1501
  async function monitorLineProvider(opts) {
1490
- const { channelAccessToken, channelSecret, accountId, config, runtime, abortSignal, webhookPath } = opts;
1502
+ const { channelAccessToken, channelSecret, accountId, config, runtime, buildContext, abortSignal, webhookPath, statusSink } = opts;
1491
1503
  const resolvedAccountId = accountId ?? resolveDefaultLineAccountId(config);
1492
1504
  const token = channelAccessToken.trim();
1493
1505
  const secret = channelSecret.trim();
@@ -1498,6 +1510,7 @@ async function monitorLineProvider(opts) {
1498
1510
  channelSecret: secret,
1499
1511
  accountId,
1500
1512
  runtime,
1513
+ buildContext,
1501
1514
  config,
1502
1515
  onMessage: async (ctx, deliveryControl) => {
1503
1516
  if (!ctx) return;
@@ -1607,12 +1620,13 @@ async function monitorLineProvider(opts) {
1607
1620
  }
1608
1621
  });
1609
1622
  const normalizedPath = normalizeWebhookPath(normalizePluginHttpPath(webhookPath, "/line/webhook") ?? "/line/webhook");
1623
+ const webhookRouteKey = canonicalizeWebhookRouteKey(normalizedPath);
1610
1624
  const createScopedLineWebhookHandler = (target) => createLineNodeWebhookHandler({
1611
1625
  channelSecret: target.channelSecret,
1612
1626
  bot: target.bot,
1613
1627
  runtime: target.runtime
1614
1628
  });
1615
- const { unregister: unregisterHttp } = registerWebhookTargetWithPluginRoute({
1629
+ const { unregister: unregisterHttp } = await registerLineWebhookTarget({
1616
1630
  targetsByPath: lineWebhookTargets,
1617
1631
  target: {
1618
1632
  accountId: resolvedAccountId,
@@ -1624,10 +1638,12 @@ async function monitorLineProvider(opts) {
1624
1638
  route: {
1625
1639
  auth: "plugin",
1626
1640
  pluginId: "line",
1641
+ source: "line-webhook",
1627
1642
  accountId: resolvedAccountId,
1628
1643
  log: (msg) => logVerbose(msg),
1644
+ throwOnFailure: true,
1629
1645
  handler: async (req, res) => {
1630
- const targets = lineWebhookTargets.get(normalizedPath) ?? [];
1646
+ const targets = lineWebhookTargets.get(webhookRouteKey) ?? [];
1631
1647
  const firstTarget = targets[0];
1632
1648
  if (req.method !== "POST") {
1633
1649
  if (!firstTarget) {
@@ -1642,7 +1658,7 @@ async function monitorLineProvider(opts) {
1642
1658
  req,
1643
1659
  res,
1644
1660
  inFlightLimiter: lineWebhookInFlightLimiter,
1645
- inFlightKey: `line:${normalizedPath}`
1661
+ inFlightKey: `line:${webhookRouteKey}`
1646
1662
  });
1647
1663
  if (!requestLifecycle.ok) return;
1648
1664
  try {
@@ -1681,6 +1697,7 @@ async function monitorLineProvider(opts) {
1681
1697
  if (body.events && body.events.length > 0) {
1682
1698
  logVerbose(`line: received ${body.events.length} webhook events`);
1683
1699
  await match.target.bot.handleWebhook(body);
1700
+ res.setHeader("x-openclaw-delivery-accepted", "durable");
1684
1701
  }
1685
1702
  res.statusCode = 200;
1686
1703
  res.setHeader("Content-Type", "application/json");
@@ -1698,7 +1715,7 @@ async function monitorLineProvider(opts) {
1698
1715
  res.end(JSON.stringify({ error: requestBodyErrorToText("REQUEST_BODY_TIMEOUT") }));
1699
1716
  return;
1700
1717
  }
1701
- runtime.error?.(danger(`line webhook error: ${String(err)}`));
1718
+ runtime.error?.(danger(`line webhook error: ${formatErrorMessage(err)}`));
1702
1719
  if (!res.headersSent) {
1703
1720
  res.statusCode = 500;
1704
1721
  res.setHeader("Content-Type", "application/json");
@@ -1709,8 +1726,9 @@ async function monitorLineProvider(opts) {
1709
1726
  }
1710
1727
  }
1711
1728
  }
1712
- });
1729
+ }, bot);
1713
1730
  logVerbose(`line: registered webhook handler at ${normalizedPath}`);
1731
+ statusSink?.(channelReadyPatch());
1714
1732
  let stopped = false;
1715
1733
  let stopPromise;
1716
1734
  const stopHandler = () => {
@@ -1719,7 +1737,9 @@ async function monitorLineProvider(opts) {
1719
1737
  stopped = true;
1720
1738
  logVerbose(`line: stopping provider for account ${resolvedAccountId}`);
1721
1739
  unregisterHttp();
1722
- stopPromise = bot.stop();
1740
+ stopPromise = bot.stop().finally(() => {
1741
+ statusSink?.(channelStoppedPatch());
1742
+ });
1723
1743
  return stopPromise;
1724
1744
  };
1725
1745
  const stopOnAbort = () => void stopHandler();
@@ -0,0 +1,2 @@
1
+ import { t as monitorLineProvider } from "./monitor-QEh_ZqyR.js";
2
+ export { monitorLineProvider };
@@ -0,0 +1,3 @@
1
+ import { i as processLineMessage, o as buildTemplateMessageFromPayload } from "./markdown-to-line-D4I-6lm6.js";
2
+ import { a as createQuickReplyItems, f as pushLocationMessage, g as pushTextMessageWithQuickReplies, h as pushTemplateMessage, i as createLocationMessage, m as pushMessagesLine, n as createFlexMessage, p as pushMessageLine, u as pushFlexMessage, v as sendMessageLine } from "./send-CFFr0-rZ.js";
3
+ export { buildTemplateMessageFromPayload, createFlexMessage, createLocationMessage, createQuickReplyItems, processLineMessage, pushFlexMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, sendMessageLine };
@@ -1,4 +1,4 @@
1
- import { a as createEventCard, i as createAgendaCard, n as createDeviceControlCard, r as createMediaPlayerCard, t as createAppleTvRemoteCard } from "./flex-templates-DVy_wog4.js";
1
+ import { a as createEventCard, i as createAgendaCard, n as createDeviceControlCard, r as createMediaPlayerCard, t as createAppleTvRemoteCard } from "./flex-templates-BBBlNqpV.js";
2
2
  import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { DmPolicySchema, GroupPolicySchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
4
4
  import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared";
@@ -15,6 +15,7 @@ const ThreadBindingsSchema = z.object({
15
15
  }).strict();
16
16
  const LineCommonConfigSchemaBase = z.object({
17
17
  enabled: z.boolean().optional(),
18
+ configWrites: z.boolean().optional(),
18
19
  channelAccessToken: z.string().optional(),
19
20
  channelSecret: z.string().optional(),
20
21
  tokenFile: z.string().optional(),
@@ -100,9 +101,9 @@ function parseLineDirectives(payload) {
100
101
  const lonStr = expectDefined(parts[3], "location longitude field");
101
102
  const latitude = parseStrictFiniteNumber(latStr);
102
103
  const longitude = parseStrictFiniteNumber(lonStr);
103
- if (latitude !== void 0 && longitude !== void 0) lineData.location = {
104
+ if (address && latitude !== void 0 && longitude !== void 0) lineData.location = {
104
105
  title: title || "Location",
105
- address: address || "",
106
+ address,
106
107
  latitude,
107
108
  longitude
108
109
  };