@openclaw/line 2026.9.1-beta.1 → 2026.9.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 (46) hide show
  1. package/dist/account-helpers-BSUyZwLm.js +12 -0
  2. package/dist/{accounts-BqEVsDoB.js → accounts-BBEFMYbY.js} +2 -1
  3. package/dist/api.js +2 -2
  4. package/dist/card-command-pwvE48Wb.js +252 -0
  5. package/dist/{channel-CsQLcrsA.js → channel-BJvELOIg.js} +111 -380
  6. package/dist/channel-plugin-api.js +1 -1
  7. package/dist/channel.runtime-CZOszXXj.js +4 -0
  8. package/dist/contract-api.js +1 -1
  9. package/dist/doctor-contract-api.js +141 -0
  10. package/dist/index.js +5 -17
  11. package/dist/{markdown-to-line-BmZ4hhfC.js → markdown-to-line-B-sj-7II.js} +22 -23
  12. package/dist/{monitor-Bd9XDV1i.js → monitor-m9h5osFz.js} +339 -237
  13. package/dist/monitor.runtime-C90LL-P3.js +2 -0
  14. package/dist/outbound.runtime-CGqyJ-Ug.js +3 -0
  15. package/dist/probe.runtime-CbtMgU4s.js +2 -0
  16. package/dist/rich-messages-DY1AGKAJ.js +742 -0
  17. package/dist/runtime-api.js +14 -15
  18. package/dist/{send-Dce0m539.js → send-CQQxpxPp.js} +142 -112
  19. package/dist/{send-receipt-CCPTalf9.js → send-retry-ChpfJ-LM.js} +326 -86
  20. package/dist/setup-api.js +1 -1
  21. package/dist/{setup-surface-W0Dbgu58.js → setup-surface-CDlgkTKi.js} +47 -39
  22. package/dist/webhook-spool-contract-1rY8OJw8.js +59 -0
  23. package/node_modules/typebox/build/format/email.mjs +1 -1
  24. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
  25. package/node_modules/typebox/build/format/json_pointer.d.mts +2 -2
  26. package/node_modules/typebox/build/format/json_pointer.mjs +2 -2
  27. package/node_modules/typebox/build/format/json_pointer_uri_fragment.d.mts +2 -2
  28. package/node_modules/typebox/build/format/json_pointer_uri_fragment.mjs +2 -2
  29. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  30. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  31. package/node_modules/typebox/build/system/memory/assign.mjs +2 -1
  32. package/node_modules/typebox/build/system/memory/create.mjs +3 -4
  33. package/node_modules/typebox/build/system/memory/discard.mjs +2 -2
  34. package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
  35. package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
  36. package/node_modules/typebox/build/system/memory/update.mjs +3 -2
  37. package/node_modules/typebox/package.json +1 -1
  38. package/openclaw.plugin.json +16 -3
  39. package/package.json +5 -5
  40. package/dist/card-command-CmU47gD9.js +0 -230
  41. package/dist/channel.runtime-B83x7k_i.js +0 -4
  42. package/dist/media-control-cards-CksNB8Wk.js +0 -474
  43. package/dist/monitor.runtime-BCCa6qTX.js +0 -2
  44. package/dist/outbound.runtime-D4w0-Gkc.js +0 -3
  45. package/dist/probe-CM03wo6r.js +0 -28
  46. package/dist/probe.runtime-D13ZtXsd.js +0 -2
@@ -1,25 +1,24 @@
1
- import { i as resolveLineAccount, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-BqEVsDoB.js";
2
- import { i as hasLineCredentials, r as lineSetupContract, t as lineSetupWizard } from "./setup-surface-W0Dbgu58.js";
3
- import { a as resolveExactLineGroupConfigKey, d as LineChannelConfigSchema, i as buildLineQuickReplyFallbackText, l as getLineRuntime, n as createDeviceControlCard, r as createMediaPlayerCard, t as createAppleTvRemoteCard } from "./media-control-cards-CksNB8Wk.js";
4
- import { _ as hasLineSpecificMediaOptions, a as createActionCard, g as buildLineMediaMessage, n as createAgendaCard, p as postbackAction, r as createEventCard, t as createLineSendReceipt, u as messageAction, v as resolveLineOutboundMedia } from "./send-receipt-CCPTalf9.js";
1
+ import { i as resolveLineAccount, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-BBEFMYbY.js";
2
+ import { t as hasLineCredentials } from "./account-helpers-BSUyZwLm.js";
3
+ import { _ as LineChannelConfigSchema, a as renderLineCard, d as resolveExactLineGroupConfigKey, h as getLineRuntime, n as createLineQuickReply, o as renderLinePresentation, p as resolveLineGroupLookupIds, r as lineMessageActions, t as LINE_PRESENTATION_CAPABILITIES, u as buildLineQuickReplyFallbackText } from "./rich-messages-DY1AGKAJ.js";
4
+ import { s as createLineSendReceipt, t as explainLineRefusal, w as buildLineMediaMessage } from "./send-retry-ChpfJ-LM.js";
5
+ import { r as lineSetupContract, t as lineSetupWizard } from "./setup-surface-CDlgkTKi.js";
5
6
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
6
7
  import { buildDmGroupAccountAllowlistAdapter, createFlatAllowlistOverrideResolver } from "openclaw/plugin-sdk/allowlist-config-edit";
7
8
  import { buildChannelOutboundSessionRoute, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
8
9
  import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
9
10
  import { buildChannelGroupsScopeTree, createRestrictSendersChannelSecurity, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
10
- import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
11
+ import { createChannelDirectoryAdapter, createResolvedDirectoryEntriesLister } from "openclaw/plugin-sdk/directory-runtime";
11
12
  import { describeWebhookAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
12
13
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
13
- import { createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
14
- import { isRecord, normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
14
+ import { clearAccountFieldsFromConfigSection, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
15
+ import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
15
16
  import { createAccountStatusSink, defineChannelMessageAdapter, listMessageReceiptPlatformIds } from "openclaw/plugin-sdk/channel-outbound";
16
- import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
17
- import { createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
17
+ import { createChannelPartialDeliveryError, isChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
18
18
  import { createAttachedChannelResultAdapter, createEmptyChannelResult } from "openclaw/plugin-sdk/channel-send-result";
19
+ import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime";
19
20
  import { resolveOutboundMediaUrls } from "openclaw/plugin-sdk/reply-payload";
20
21
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
21
- import { resolveMessagePresentationButtonAction, resolveMessagePresentationOptionAction } from "openclaw/plugin-sdk/interactive-runtime";
22
- import { Type } from "typebox";
23
22
  import { buildTokenChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, createDependentCredentialStatusIssueCollector } from "openclaw/plugin-sdk/status-helpers";
24
23
  //#region extensions/line/src/bindings.ts
25
24
  function normalizeLineConversationId(raw) {
@@ -120,8 +119,8 @@ const lineChannelPluginCommon = {
120
119
  };
121
120
  //#endregion
122
121
  //#region extensions/line/src/gateway.ts
123
- const loadLineProbeRuntime$1 = createLazyRuntimeModule(() => import("./probe.runtime-D13ZtXsd.js"));
124
- const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-BCCa6qTX.js"));
122
+ const loadLineProbeRuntime$1 = createLazyRuntimeModule(() => import("./probe.runtime-CbtMgU4s.js"));
123
+ const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-C90LL-P3.js"));
125
124
  const lineGatewayAdapter = {
126
125
  startAccount: async (ctx) => {
127
126
  const account = ctx.account;
@@ -157,22 +156,9 @@ const lineGatewayAdapter = {
157
156
  },
158
157
  logoutAccount: async ({ accountId, cfg }) => {
159
158
  const envToken = process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim() ?? "";
160
- const nextCfg = { ...cfg };
161
- const nextLine = { ...cfg.channels?.line ?? {} };
162
- let cleared = false;
163
- let changed = false;
164
- if (accountId === DEFAULT_ACCOUNT_ID) {
165
- if (nextLine.channelAccessToken || nextLine.channelSecret || nextLine.tokenFile || nextLine.secretFile) {
166
- delete nextLine.channelAccessToken;
167
- delete nextLine.channelSecret;
168
- delete nextLine.tokenFile;
169
- delete nextLine.secretFile;
170
- cleared = true;
171
- changed = true;
172
- }
173
- }
174
- const accountCleanup = clearAccountEntryFields({
175
- accounts: nextLine.accounts,
159
+ const { nextConfig, changed, cleared } = clearAccountFieldsFromConfigSection({
160
+ cfg,
161
+ sectionKey: "line",
176
162
  accountId,
177
163
  fields: [
178
164
  "channelAccessToken",
@@ -182,30 +168,12 @@ const lineGatewayAdapter = {
182
168
  ],
183
169
  markClearedOnFieldPresence: true
184
170
  });
185
- if (accountCleanup.changed) {
186
- changed = true;
187
- if (accountCleanup.cleared) cleared = true;
188
- if (accountCleanup.nextAccounts) nextLine.accounts = accountCleanup.nextAccounts;
189
- else delete nextLine.accounts;
190
- }
191
- if (changed) {
192
- if (Object.keys(nextLine).length > 0) nextCfg.channels = {
193
- ...nextCfg.channels,
194
- line: nextLine
195
- };
196
- else {
197
- const nextChannels = { ...nextCfg.channels };
198
- delete nextChannels.line;
199
- if (Object.keys(nextChannels).length > 0) nextCfg.channels = nextChannels;
200
- else delete nextCfg.channels;
201
- }
202
- await getLineRuntime().config.replaceConfigFile({
203
- nextConfig: nextCfg,
204
- afterWrite: { mode: "auto" }
205
- });
206
- }
171
+ if (changed) await getLineRuntime().config.replaceConfigFile({
172
+ nextConfig,
173
+ afterWrite: { mode: "auto" }
174
+ });
207
175
  const loggedOut = resolveLineAccount({
208
- cfg: changed ? nextCfg : cfg,
176
+ cfg: nextConfig,
209
177
  accountId
210
178
  }).tokenSource === "none";
211
179
  return {
@@ -242,252 +210,8 @@ function inferLineTargetChatType(target) {
242
210
  return /^[CR][a-f0-9]{32}$/i.test(normalized) ? "group" : void 0;
243
211
  }
244
212
  //#endregion
245
- //#region extensions/line/src/rich-messages.ts
246
- const nonempty = () => Type.String({ minLength: 1 });
247
- const closed = (properties) => Type.Object(properties, { additionalProperties: false });
248
- const lineCardSchema = Type.Union([
249
- closed({
250
- type: Type.Literal("media_player"),
251
- title: nonempty(),
252
- artist: Type.Optional(nonempty()),
253
- source: Type.Optional(nonempty()),
254
- imageUrl: Type.Optional(Type.String({ pattern: "^https://" })),
255
- status: Type.Optional(Type.Union([Type.Literal("playing"), Type.Literal("paused")]))
256
- }),
257
- closed({
258
- type: Type.Literal("event"),
259
- title: nonempty(),
260
- date: nonempty(),
261
- time: Type.Optional(nonempty()),
262
- location: Type.Optional(nonempty()),
263
- description: Type.Optional(nonempty())
264
- }),
265
- closed({
266
- type: Type.Literal("agenda"),
267
- title: nonempty(),
268
- events: Type.Array(closed({
269
- title: nonempty(),
270
- time: Type.Optional(nonempty()),
271
- location: Type.Optional(nonempty())
272
- }), {
273
- minItems: 1,
274
- maxItems: 6
275
- })
276
- }),
277
- closed({
278
- type: Type.Literal("device"),
279
- name: nonempty(),
280
- deviceType: Type.Optional(nonempty()),
281
- status: Type.Optional(nonempty()),
282
- controls: Type.Optional(Type.Array(closed({
283
- label: nonempty(),
284
- action: nonempty()
285
- }), { maxItems: 6 }))
286
- }),
287
- closed({
288
- type: Type.Literal("appletv_remote"),
289
- name: Type.Optional(nonempty()),
290
- status: Type.Optional(nonempty())
291
- })
292
- ]);
293
- const lineChannelDataSchema = Type.Optional(closed({ line: closed({
294
- location: Type.Optional(closed({
295
- title: nonempty(),
296
- address: nonempty(),
297
- latitude: Type.Number({
298
- minimum: -90,
299
- maximum: 90
300
- }),
301
- longitude: Type.Number({
302
- minimum: -180,
303
- maximum: 180
304
- })
305
- })),
306
- card: Type.Optional(lineCardSchema),
307
- mediaKind: Type.Optional(Type.Union([
308
- Type.Literal("image"),
309
- Type.Literal("video"),
310
- Type.Literal("audio")
311
- ])),
312
- previewImageUrl: Type.Optional(Type.String({ pattern: "^https://" })),
313
- durationMs: Type.Optional(Type.Integer({ minimum: 1 })),
314
- trackingId: Type.Optional(nonempty())
315
- }) }));
316
- const lineMessageActions = {
317
- describeMessageTool: ({ cfg, accountId }) => {
318
- const account = resolveLineAccount({
319
- cfg,
320
- accountId: accountId ?? void 0
321
- });
322
- return account.enabled && hasLineCredentials(account) ? {
323
- actions: ["send"],
324
- capabilities: ["presentation"],
325
- schema: {
326
- actions: ["send"],
327
- properties: { channelData: lineChannelDataSchema }
328
- }
329
- } : {
330
- actions: [],
331
- capabilities: [],
332
- schema: null
333
- };
334
- },
335
- prepareSendPayload: ({ payload }) => payload
336
- };
337
- const LINE_PRESENTATION_CAPABILITIES = {
338
- supported: true,
339
- buttons: true,
340
- selects: true,
341
- context: true,
342
- limits: {
343
- actions: {
344
- maxActions: 4,
345
- maxActionsPerRow: 1,
346
- maxRows: 4,
347
- maxLabelLength: 40
348
- },
349
- selects: {
350
- maxOptions: 13,
351
- maxLabelLength: 20,
352
- maxValueBytes: 300
353
- },
354
- text: { markdownDialect: "plain" }
355
- }
356
- };
357
- function toLineAction(button) {
358
- const normalized = resolveMessagePresentationButtonAction(button);
359
- const { label } = button;
360
- if (normalized?.type === "command") return {
361
- type: "message",
362
- label,
363
- text: normalized.command
364
- };
365
- if (normalized?.type === "callback") return {
366
- type: "postback",
367
- label,
368
- data: normalized.value,
369
- displayText: label
370
- };
371
- if (normalized?.type === "url") return {
372
- type: "uri",
373
- label,
374
- uri: normalized.url
375
- };
376
- if (normalized?.type === "web-app" && normalized.url) return {
377
- type: "uri",
378
- label,
379
- uri: normalized.url
380
- };
381
- }
382
- function renderLinePresentation(payload, presentation) {
383
- const buttons = presentation.blocks.flatMap((block) => block.type === "buttons" ? block.buttons : []);
384
- const buttonActions = buttons.map(toLineAction);
385
- const options = presentation.blocks.flatMap((block) => block.type === "select" ? block.options : []);
386
- const quickReplyItems = options.flatMap((option) => {
387
- const action = resolveMessagePresentationOptionAction(option);
388
- return action?.type === "command" || action?.type === "callback" ? [{
389
- label: option.label,
390
- action
391
- }] : [];
392
- });
393
- if (buttons.length > 0 && buttonActions.some((action) => !action) || quickReplyItems.length !== options.length || buttons.length === 0 && options.length === 0) return null;
394
- const lineData = isRecord(payload.channelData?.line) ? payload.channelData.line : {};
395
- const text = presentation.blocks.flatMap((block) => block.type === "text" || block.type === "context" ? [block.text] : []).join("\n");
396
- const title = presentation.title || "Choose an option";
397
- const flexMessage = buttonActions.length > 0 ? {
398
- altText: title,
399
- contents: createActionCard(title, text || "Choose an option.", buttons.map((button, index) => ({
400
- label: button.label,
401
- action: buttonActions[index]
402
- })))
403
- } : void 0;
404
- return {
405
- ...payload,
406
- channelData: {
407
- ...payload.channelData,
408
- line: {
409
- ...lineData,
410
- ...flexMessage ? { flexMessage } : {},
411
- quickReplyItems
412
- }
413
- }
414
- };
415
- }
416
- const toSlug = (value) => normalizeLowercaseStringOrEmpty(value).replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "device";
417
- const lineActionData = (action, device) => `line.action=${encodeURIComponent(action)}&line.device=${encodeURIComponent(device)}`;
418
- function renderLineCard(card) {
419
- if (card.type === "media_player") {
420
- const device = toSlug(card.source || card.title);
421
- return {
422
- altText: `🎵 ${card.title}${card.artist ? ` - ${card.artist}` : ""}`,
423
- contents: createMediaPlayerCard({
424
- title: card.title,
425
- subtitle: card.artist,
426
- source: card.source,
427
- imageUrl: card.imageUrl,
428
- isPlaying: card.status ? card.status === "playing" : void 0,
429
- controls: Object.fromEntries([
430
- "previous",
431
- "play",
432
- "pause",
433
- "next"
434
- ].map((action) => [action, { data: lineActionData(action, device) }]))
435
- })
436
- };
437
- }
438
- if (card.type === "event") return {
439
- altText: `📅 ${card.title} - ${card.date}${card.time ? ` ${card.time}` : ""}`,
440
- contents: createEventCard(card)
441
- };
442
- if (card.type === "agenda") return {
443
- altText: `📋 ${card.title} (${card.events.length} events)`,
444
- contents: createAgendaCard(card)
445
- };
446
- const device = toSlug(card.type === "device" ? card.name : card.name || "apple_tv");
447
- if (card.type === "device") return {
448
- altText: `📱 ${card.name}${card.status ? `: ${card.status}` : ""}`,
449
- contents: createDeviceControlCard({
450
- deviceName: card.name,
451
- deviceType: card.deviceType,
452
- status: card.status,
453
- controls: (card.controls ?? []).map((control) => ({
454
- label: control.label,
455
- data: lineActionData(control.action, device)
456
- }))
457
- })
458
- };
459
- const actionData = {
460
- up: lineActionData("up", device),
461
- down: lineActionData("down", device),
462
- left: lineActionData("left", device),
463
- right: lineActionData("right", device),
464
- select: lineActionData("select", device),
465
- menu: lineActionData("menu", device),
466
- home: lineActionData("home", device),
467
- play: lineActionData("play", device),
468
- pause: lineActionData("pause", device),
469
- volumeUp: lineActionData("volume_up", device),
470
- volumeDown: lineActionData("volume_down", device),
471
- mute: lineActionData("mute", device)
472
- };
473
- return {
474
- altText: `📺 ${card.name || "Apple TV"} Remote`,
475
- contents: createAppleTvRemoteCard({
476
- deviceName: card.name || "Apple TV",
477
- status: card.status,
478
- actionData
479
- })
480
- };
481
- }
482
- function createLineQuickReply(items) {
483
- return { items: items.slice(0, 13).map((item) => ({
484
- type: "action",
485
- action: item.action.type === "command" ? messageAction(item.label, item.action.command) : postbackAction(item.label, item.action.value, item.label)
486
- })) };
487
- }
488
- //#endregion
489
213
  //#region extensions/line/src/outbound.ts
490
- const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-D4w0-Gkc.js"));
214
+ const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-CGqyJ-Ug.js"));
491
215
  const lineOutboundAdapter = {
492
216
  deliveryMode: "direct",
493
217
  chunker: (text, limit) => getLineRuntime().channel.text.chunkMarkdownText(text, limit),
@@ -513,9 +237,27 @@ const lineOutboundAdapter = {
513
237
  const sendLocation = lineRuntime?.pushLocationMessage ?? outboundRuntime.pushLocationMessage;
514
238
  const sendQuickReplies = lineRuntime?.pushTextMessageWithQuickReplies ?? outboundRuntime.pushTextMessageWithQuickReplies;
515
239
  const buildTemplate = lineRuntime?.buildTemplateMessageFromPayload ?? outboundRuntime.buildTemplateMessageFromPayload;
240
+ const sendOptions = {
241
+ verbose: false,
242
+ cfg,
243
+ accountId: accountId ?? void 0
244
+ };
516
245
  let lastResult = null;
517
246
  const recordResult = async (resultPromise) => {
518
- const result = await resultPromise;
247
+ let result;
248
+ try {
249
+ result = await resultPromise;
250
+ } catch (error) {
251
+ const refusal = lastResult !== null || isChannelPartialDeliveryError(error) ? void 0 : await explainLineRefusal({
252
+ error,
253
+ cfg,
254
+ accountId
255
+ });
256
+ throw refusal?.retryable !== void 0 ? new PlatformMessageNotDispatchedError(refusal.reason, {
257
+ cause: error,
258
+ retryable: refusal.retryable
259
+ }) : error;
260
+ }
519
261
  lastResult = result;
520
262
  try {
521
263
  await onDeliveryResult?.(createEmptyChannelResult("line", { ...result }));
@@ -537,11 +279,7 @@ const lineOutboundAdapter = {
537
279
  if (messages.length === 0) return;
538
280
  for (let i = 0; i < messages.length; i += 5) {
539
281
  const batch = messages.slice(i, i + 5);
540
- await recordResult(sendBatch(to, batch, {
541
- verbose: false,
542
- cfg,
543
- accountId: accountId ?? void 0
544
- }));
282
+ await recordResult(sendBatch(to, batch, sendOptions));
545
283
  }
546
284
  };
547
285
  const sendTextWithQuickReply = async (text) => {
@@ -553,11 +291,7 @@ const lineOutboundAdapter = {
553
291
  }]);
554
292
  return;
555
293
  }
556
- await recordResult(sendQuickReplies(to, text, quickReplies, {
557
- verbose: false,
558
- cfg,
559
- accountId: accountId ?? void 0
560
- }));
294
+ await recordResult(sendQuickReplies(to, text, quickReplies, sendOptions));
561
295
  };
562
296
  const processed = payload.text ? outboundRuntime.processLineMessage(payload.text) : {
563
297
  text: "",
@@ -570,9 +304,8 @@ const lineOutboundAdapter = {
570
304
  })));
571
305
  const chunks = orderedMessages ? orderedMessages.flatMap((message) => message.type === "text" ? [message.text] : []) : processed.text ? runtime.channel.text.chunkMarkdownText(processed.text, chunkLimit) : [];
572
306
  const mediaUrls = resolveOutboundMediaUrls(payload);
573
- const useLineSpecificMedia = hasLineSpecificMediaOptions(lineData);
574
307
  const mediaOptions = {
575
- mediaKind: useLineSpecificMedia ? lineData.mediaKind : "image",
308
+ mediaKind: lineData.mediaKind,
576
309
  previewImageUrl: lineData.previewImageUrl,
577
310
  durationMs: lineData.durationMs,
578
311
  trackingId: lineData.trackingId
@@ -582,82 +315,40 @@ const lineOutboundAdapter = {
582
315
  for (const url of mediaUrls) {
583
316
  const trimmed = url?.trim();
584
317
  if (!trimmed) continue;
585
- if (!useLineSpecificMedia) {
586
- await recordResult((lineRuntime?.sendMessageLine ?? outboundRuntime.sendMessageLine)(to, "", {
587
- verbose: false,
588
- mediaUrl: trimmed,
589
- cfg,
590
- accountId: accountId ?? void 0
591
- }));
592
- continue;
593
- }
594
- const resolved = await resolveLineOutboundMedia(trimmed, mediaOptions);
595
318
  await recordResult((lineRuntime?.sendMessageLine ?? outboundRuntime.sendMessageLine)(to, "", {
596
- verbose: false,
597
- mediaUrl: resolved.mediaUrl,
598
- mediaKind: resolved.mediaKind,
599
- previewImageUrl: resolved.previewImageUrl,
600
- durationMs: resolved.durationMs,
601
- trackingId: resolved.trackingId,
602
- cfg,
603
- accountId: accountId ?? void 0
319
+ ...sendOptions,
320
+ ...mediaOptions,
321
+ mediaUrl: trimmed
604
322
  }));
605
323
  }
606
324
  };
607
325
  if (!shouldSendQuickRepliesInline) {
608
326
  if (lineData.flexMessage) {
609
327
  const flexContents = lineData.flexMessage.contents;
610
- await recordResult(sendFlex(to, lineData.flexMessage.altText, flexContents, {
611
- verbose: false,
612
- cfg,
613
- accountId: accountId ?? void 0
614
- }));
328
+ await recordResult(sendFlex(to, lineData.flexMessage.altText, flexContents, sendOptions));
615
329
  }
616
330
  if (lineData.templateMessage) {
617
331
  const template = buildTemplate(lineData.templateMessage);
618
- if (template) await recordResult(sendTemplate(to, template, {
619
- verbose: false,
620
- cfg,
621
- accountId: accountId ?? void 0
622
- }));
332
+ if (template) await recordResult(sendTemplate(to, template, sendOptions));
623
333
  }
624
- if (location) await recordResult(sendLocation(to, location, {
625
- verbose: false,
626
- cfg,
627
- accountId: accountId ?? void 0
628
- }));
629
- if (!orderedMessages) for (const flexMsg of processed.flexMessages) await recordResult(sendFlex(to, flexMsg.altText, flexMsg.contents, {
630
- verbose: false,
631
- cfg,
632
- accountId: accountId ?? void 0
633
- }));
334
+ if (location) await recordResult(sendLocation(to, location, sendOptions));
335
+ if (!orderedMessages) for (const flexMsg of processed.flexMessages) await recordResult(sendFlex(to, flexMsg.altText, flexMsg.contents, sendOptions));
634
336
  }
635
337
  const sendMediaAfterText = !(hasQuickReplies && chunks.length > 0);
636
338
  if (mediaUrls.length > 0 && !shouldSendQuickRepliesInline && !sendMediaAfterText) await sendMediaMessages();
637
339
  if (orderedMessages && !shouldSendQuickRepliesInline) for (const [index, message] of orderedMessages.entries()) {
638
340
  const isLast = index === orderedMessages.length - 1;
639
- if (message.type === "flex") if (isLast && quickReply) await sendMessageBatch([{
640
- ...message,
641
- quickReply
642
- }]);
643
- else await recordResult(sendFlex(to, message.altText, message.contents, {
644
- verbose: false,
645
- cfg,
646
- accountId: accountId ?? void 0
647
- }));
648
- else if (isLast && hasQuickReplies) await sendTextWithQuickReply(message.text);
649
- else await recordResult(sendText(to, message.text, {
650
- verbose: false,
651
- cfg,
652
- accountId: accountId ?? void 0
653
- }));
341
+ if (message.type === "flex") {
342
+ if (isLast && quickReply) await sendMessageBatch([{
343
+ ...message,
344
+ quickReply
345
+ }]);
346
+ else await recordResult(sendFlex(to, message.altText, message.contents, sendOptions));
347
+ } else if (isLast && hasQuickReplies) await sendTextWithQuickReply(message.text);
348
+ else await recordResult(sendText(to, message.text, sendOptions));
654
349
  }
655
350
  else if (chunks.length > 0) for (const [i, chunk] of chunks.entries()) if (i === chunks.length - 1 && hasQuickReplies) await sendTextWithQuickReply(chunk);
656
- else await recordResult(sendText(to, chunk, {
657
- verbose: false,
658
- cfg,
659
- accountId: accountId ?? void 0
660
- }));
351
+ else await recordResult(sendText(to, chunk, sendOptions));
661
352
  else if (shouldSendQuickRepliesInline) {
662
353
  const quickReplyMessages = [];
663
354
  if (lineData.flexMessage) quickReplyMessages.push(outboundRuntime.createFlexMessage(lineData.flexMessage.altText, lineData.flexMessage.contents));
@@ -757,7 +448,7 @@ const lineMessageAdapter = defineChannelMessageAdapter({
757
448
  });
758
449
  //#endregion
759
450
  //#region extensions/line/src/status.ts
760
- const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime-D13ZtXsd.js"));
451
+ const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime-CbtMgU4s.js"));
761
452
  const collectLineStatusIssues = createDependentCredentialStatusIssueCollector({
762
453
  channel: "line",
763
454
  dependencySourceKey: "tokenSource",
@@ -769,7 +460,7 @@ const lineStatusAdapter = createComputedAccountStatusAdapter({
769
460
  collectStatusIssues: collectLineStatusIssues,
770
461
  buildChannelSummary: ({ snapshot }) => buildTokenChannelStatusSummary(snapshot),
771
462
  probeAccount: async ({ account, timeoutMs }) => await (await loadLineProbeRuntime()).probeLineBot(account.channelAccessToken, timeoutMs),
772
- resolveAccountSnapshot: ({ account }) => ({
463
+ resolveAccountSnapshot: ({ account, probe }) => ({
773
464
  accountId: account.accountId,
774
465
  name: account.name,
775
466
  enabled: account.enabled,
@@ -779,13 +470,14 @@ const lineStatusAdapter = createComputedAccountStatusAdapter({
779
470
  signingSecretSource: account.signingSecretSource,
780
471
  tokenStatus: account.tokenStatus,
781
472
  signingSecretStatus: account.signingSecretStatus,
782
- mode: "webhook"
473
+ mode: "webhook",
474
+ ...probe?.quota ? { quota: probe.quota } : {}
783
475
  }
784
476
  })
785
477
  });
786
478
  //#endregion
787
479
  //#region extensions/line/src/channel.ts
788
- const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-B83x7k_i.js"));
480
+ const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-CZOszXXj.js"));
789
481
  const lineSecurityAdapter = createRestrictSendersChannelSecurity({
790
482
  channelKey: "line",
791
483
  resolveDmPolicy: (account) => account.config.dmPolicy,
@@ -801,6 +493,10 @@ const lineSecurityAdapter = createRestrictSendersChannelSecurity({
801
493
  approveHint: "openclaw pairing approve line <code>",
802
494
  normalizeDmEntry: (raw) => raw.replace(/^line:(?:user:)?/i, "")
803
495
  });
496
+ function normalizeLineDirectoryId(entry, kind) {
497
+ const id = normalizeLineMessagingTarget(entry);
498
+ return id && inferLineTargetChatType(id) === kind ? id : null;
499
+ }
804
500
  const linePlugin = createChatChannelPlugin({
805
501
  base: {
806
502
  id: "line",
@@ -862,21 +558,56 @@ const linePlugin = createChatChannelPlugin({
862
558
  hint: "<userId|groupId|roomId>"
863
559
  }
864
560
  },
865
- directory: createEmptyChannelDirectoryAdapter(),
561
+ directory: createChannelDirectoryAdapter({
562
+ listPeers: createResolvedDirectoryEntriesLister({
563
+ kind: "user",
564
+ resolveAccount: (cfg, accountId) => resolveLineAccount({
565
+ cfg,
566
+ accountId: accountId ?? void 0
567
+ }),
568
+ resolveSources: ({ config }) => [
569
+ config.allowFrom ?? [],
570
+ config.groupAllowFrom ?? [],
571
+ ...Object.values(config.groups ?? {}).map((group) => group?.allowFrom ?? [])
572
+ ],
573
+ normalizeId: (entry) => normalizeLineDirectoryId(entry, "direct")
574
+ }),
575
+ listGroups: createResolvedDirectoryEntriesLister({
576
+ kind: "group",
577
+ resolveAccount: (cfg, accountId) => resolveLineAccount({
578
+ cfg,
579
+ accountId: accountId ?? void 0
580
+ }),
581
+ resolveSources: ({ config }) => [Object.keys(config.groups ?? {})],
582
+ normalizeId: (entry) => normalizeLineDirectoryId(resolveLineGroupLookupIds(entry)[0] ?? "", "group")
583
+ })
584
+ }),
866
585
  setupContract: lineSetupContract,
867
586
  status: lineStatusAdapter,
868
587
  gateway: lineGatewayAdapter,
588
+ heartbeat: { sendTyping: async ({ cfg, to, accountId }) => {
589
+ const chatId = normalizeLineMessagingTarget(to);
590
+ if (!chatId || inferLineTargetChatType(chatId) !== "direct") return;
591
+ const { showLoadingAnimation } = await loadLineChannelRuntime();
592
+ await showLoadingAnimation(chatId, {
593
+ cfg,
594
+ accountId: accountId ?? void 0
595
+ });
596
+ } },
869
597
  message: lineMessageAdapter,
870
598
  actions: lineMessageActions,
871
599
  bindings: lineBindingsAdapter,
872
600
  conversationBindings: { defaultTopLevelPlacement: "current" },
873
- agentPrompt: { messageToolHints: () => [
874
- "",
875
- "### LINE structured output",
876
- "Use `presentation.blocks` for buttons, yes/no choices, and selectable options; LINE maps them to Flex controls or quick replies.",
877
- "Use `channelData.line.location` for a location pin and `channelData.line.card` for one LINE-specific card. Supported card types are `media_player`, `event`, `agenda`, `device`, and `appletv_remote`.",
878
- "Send rich output with the structured message fields. Double-bracket marker text has no special meaning."
879
- ] }
601
+ agentPrompt: {
602
+ messageToolCapabilities: () => ["inlineButtons"],
603
+ messageToolHints: () => [
604
+ "",
605
+ "### LINE structured output",
606
+ "Use `presentation.blocks` for buttons, yes/no choices, and selectable options; LINE maps them to Flex controls or quick replies.",
607
+ "Use `channelData.line.location` for a location pin and `channelData.line.card` for one LINE-specific card. Supported card types are `media_player`, `event`, `agenda`, `device`, and `appletv_remote`.",
608
+ "Send rich output with the structured message fields. Double-bracket marker text has no special meaning."
609
+ ]
610
+ }
880
611
  },
881
612
  pairing: { text: {
882
613
  idLabel: "lineUserId",
@@ -1,2 +1,2 @@
1
- import { t as linePlugin } from "./channel-CsQLcrsA.js";
1
+ import { t as linePlugin } from "./channel-BJvELOIg.js";
2
2
  export { linePlugin };
@@ -0,0 +1,4 @@
1
+ import { a as probeLineBot } from "./send-retry-ChpfJ-LM.js";
2
+ import { t as monitorLineProvider } from "./monitor-m9h5osFz.js";
3
+ import { _ as showLoadingAnimation, d as pushMessageLine } from "./send-CQQxpxPp.js";
4
+ export { monitorLineProvider, probeLineBot, pushMessageLine, showLoadingAnimation };
@@ -1,2 +1,2 @@
1
- import { i as resolveLineAccount, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-BqEVsDoB.js";
1
+ import { i as resolveLineAccount, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-BBEFMYbY.js";
2
2
  export { listLineAccountIds, resolveDefaultLineAccountId, resolveLineAccount };