@gakr-gakr/discord 0.1.0

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 (353) hide show
  1. package/account-inspect-api.ts +6 -0
  2. package/action-runtime-api.ts +1 -0
  3. package/api.ts +130 -0
  4. package/autobot.plugin.json +15 -0
  5. package/channel-config-api.ts +1 -0
  6. package/channel-plugin-api.ts +3 -0
  7. package/config-api.ts +4 -0
  8. package/configured-state.ts +6 -0
  9. package/contract-api.ts +21 -0
  10. package/directory-contract-api.ts +4 -0
  11. package/doctor-contract-api.ts +1 -0
  12. package/index.ts +24 -0
  13. package/package.json +79 -0
  14. package/runtime-api.actions.ts +15 -0
  15. package/runtime-api.lookup.ts +22 -0
  16. package/runtime-api.monitor.ts +50 -0
  17. package/runtime-api.send.ts +79 -0
  18. package/runtime-api.threads.ts +31 -0
  19. package/runtime-api.ts +181 -0
  20. package/runtime-setter-api.ts +3 -0
  21. package/secret-contract-api.ts +4 -0
  22. package/security-audit-contract-api.ts +1 -0
  23. package/security-contract-api.ts +4 -0
  24. package/session-key-api.ts +1 -0
  25. package/setup-entry.ts +9 -0
  26. package/setup-plugin-api.ts +3 -0
  27. package/src/account-inspect.ts +131 -0
  28. package/src/accounts.ts +205 -0
  29. package/src/actions/handle-action.guild-admin.ts +421 -0
  30. package/src/actions/handle-action.ts +402 -0
  31. package/src/actions/runtime.guild.ts +446 -0
  32. package/src/actions/runtime.messaging.messages.ts +226 -0
  33. package/src/actions/runtime.messaging.reactions.ts +67 -0
  34. package/src/actions/runtime.messaging.runtime.ts +73 -0
  35. package/src/actions/runtime.messaging.send.ts +336 -0
  36. package/src/actions/runtime.messaging.shared.ts +97 -0
  37. package/src/actions/runtime.messaging.ts +37 -0
  38. package/src/actions/runtime.moderation-shared.ts +48 -0
  39. package/src/actions/runtime.moderation.ts +116 -0
  40. package/src/actions/runtime.presence.ts +117 -0
  41. package/src/actions/runtime.shared.ts +86 -0
  42. package/src/actions/runtime.ts +87 -0
  43. package/src/api.ts +219 -0
  44. package/src/approval-handler.runtime.ts +636 -0
  45. package/src/approval-native.ts +219 -0
  46. package/src/approval-runtime.ts +14 -0
  47. package/src/approval-shared.ts +56 -0
  48. package/src/audit-core.ts +178 -0
  49. package/src/audit.ts +32 -0
  50. package/src/channel-actions.runtime.ts +1 -0
  51. package/src/channel-actions.ts +254 -0
  52. package/src/channel-api.ts +29 -0
  53. package/src/channel.conversation.ts +159 -0
  54. package/src/channel.loaders.ts +50 -0
  55. package/src/channel.runtime.ts +1 -0
  56. package/src/channel.setup.ts +12 -0
  57. package/src/channel.ts +728 -0
  58. package/src/chunk.ts +321 -0
  59. package/src/client.ts +143 -0
  60. package/src/component-custom-id.ts +72 -0
  61. package/src/components-registry.ts +356 -0
  62. package/src/components.builders.ts +410 -0
  63. package/src/components.modal.ts +124 -0
  64. package/src/components.parse.ts +407 -0
  65. package/src/components.ts +54 -0
  66. package/src/components.types.ts +187 -0
  67. package/src/config-schema.ts +6 -0
  68. package/src/config-ui-hints.ts +354 -0
  69. package/src/conversation-identity.ts +58 -0
  70. package/src/delivery-retry.ts +56 -0
  71. package/src/directory-cache.ts +116 -0
  72. package/src/directory-config.ts +58 -0
  73. package/src/directory-live.ts +135 -0
  74. package/src/doctor-contract.ts +477 -0
  75. package/src/doctor-shared.ts +5 -0
  76. package/src/doctor.ts +340 -0
  77. package/src/draft-chunking.ts +43 -0
  78. package/src/draft-stream.ts +162 -0
  79. package/src/error-body.ts +38 -0
  80. package/src/exec-approvals.ts +110 -0
  81. package/src/gateway-logging.ts +67 -0
  82. package/src/group-policy.ts +113 -0
  83. package/src/guilds.ts +29 -0
  84. package/src/inbound-event-delivery.ts +135 -0
  85. package/src/interactive-dispatch.ts +104 -0
  86. package/src/internal/api.commands.ts +51 -0
  87. package/src/internal/api.guild.ts +164 -0
  88. package/src/internal/api.interactions.ts +53 -0
  89. package/src/internal/api.messages.ts +113 -0
  90. package/src/internal/api.reactions.ts +38 -0
  91. package/src/internal/api.ts +61 -0
  92. package/src/internal/api.users.ts +19 -0
  93. package/src/internal/api.webhooks.ts +13 -0
  94. package/src/internal/client.ts +310 -0
  95. package/src/internal/command-deploy.ts +352 -0
  96. package/src/internal/commands.ts +188 -0
  97. package/src/internal/components.base.ts +65 -0
  98. package/src/internal/components.message.ts +279 -0
  99. package/src/internal/components.modal.ts +95 -0
  100. package/src/internal/components.ts +31 -0
  101. package/src/internal/discord.ts +11 -0
  102. package/src/internal/embeds.ts +35 -0
  103. package/src/internal/entity-cache.ts +98 -0
  104. package/src/internal/event-queue.ts +185 -0
  105. package/src/internal/gateway-close-codes.ts +25 -0
  106. package/src/internal/gateway-dispatch.ts +96 -0
  107. package/src/internal/gateway-identify-limiter.ts +26 -0
  108. package/src/internal/gateway-lifecycle.ts +75 -0
  109. package/src/internal/gateway-rate-limit.ts +104 -0
  110. package/src/internal/gateway.ts +479 -0
  111. package/src/internal/interaction-dispatch.ts +162 -0
  112. package/src/internal/interaction-options.ts +98 -0
  113. package/src/internal/interaction-response.ts +53 -0
  114. package/src/internal/interactions.ts +378 -0
  115. package/src/internal/listeners.ts +91 -0
  116. package/src/internal/modal-fields.ts +95 -0
  117. package/src/internal/payload.ts +69 -0
  118. package/src/internal/rest-body.ts +115 -0
  119. package/src/internal/rest-errors.ts +88 -0
  120. package/src/internal/rest-routes.ts +50 -0
  121. package/src/internal/rest-scheduler.ts +557 -0
  122. package/src/internal/rest.ts +322 -0
  123. package/src/internal/schemas.ts +36 -0
  124. package/src/internal/structures.ts +280 -0
  125. package/src/internal/test-builders.test-support.ts +167 -0
  126. package/src/internal/voice.ts +49 -0
  127. package/src/media-detection.ts +28 -0
  128. package/src/mentions.ts +147 -0
  129. package/src/monitor/ack-reactions.ts +70 -0
  130. package/src/monitor/agent-components-auth.ts +7 -0
  131. package/src/monitor/agent-components-context.ts +154 -0
  132. package/src/monitor/agent-components-data.ts +224 -0
  133. package/src/monitor/agent-components-dm-auth.ts +177 -0
  134. package/src/monitor/agent-components-guild-auth.ts +322 -0
  135. package/src/monitor/agent-components-helpers.runtime.ts +3 -0
  136. package/src/monitor/agent-components-helpers.ts +34 -0
  137. package/src/monitor/agent-components-reply.ts +10 -0
  138. package/src/monitor/agent-components.deps.runtime.ts +2 -0
  139. package/src/monitor/agent-components.dispatch.ts +359 -0
  140. package/src/monitor/agent-components.handlers.ts +303 -0
  141. package/src/monitor/agent-components.modal.ts +160 -0
  142. package/src/monitor/agent-components.plugin-interactive.ts +187 -0
  143. package/src/monitor/agent-components.runtime.ts +14 -0
  144. package/src/monitor/agent-components.system-controls.ts +215 -0
  145. package/src/monitor/agent-components.ts +70 -0
  146. package/src/monitor/agent-components.types.ts +58 -0
  147. package/src/monitor/agent-components.wildcard-controls.ts +171 -0
  148. package/src/monitor/allow-list.ts +631 -0
  149. package/src/monitor/auto-presence.ts +356 -0
  150. package/src/monitor/channel-access.ts +102 -0
  151. package/src/monitor/commands.ts +9 -0
  152. package/src/monitor/dm-command-auth.ts +259 -0
  153. package/src/monitor/dm-command-decision.ts +49 -0
  154. package/src/monitor/exec-approvals.ts +161 -0
  155. package/src/monitor/format.ts +45 -0
  156. package/src/monitor/gateway-handle.ts +34 -0
  157. package/src/monitor/gateway-metadata.ts +298 -0
  158. package/src/monitor/gateway-plugin.ts +302 -0
  159. package/src/monitor/gateway-registry.ts +37 -0
  160. package/src/monitor/gateway-supervisor.ts +206 -0
  161. package/src/monitor/inbound-context.ts +95 -0
  162. package/src/monitor/inbound-dedupe.ts +79 -0
  163. package/src/monitor/inbound-job.ts +118 -0
  164. package/src/monitor/listeners.queue.ts +91 -0
  165. package/src/monitor/listeners.reactions.ts +594 -0
  166. package/src/monitor/listeners.ts +150 -0
  167. package/src/monitor/message-channel-info.ts +96 -0
  168. package/src/monitor/message-forwarded.ts +114 -0
  169. package/src/monitor/message-handler.batch-gate.ts +19 -0
  170. package/src/monitor/message-handler.context.ts +492 -0
  171. package/src/monitor/message-handler.dm-preflight.ts +119 -0
  172. package/src/monitor/message-handler.draft-preview.ts +436 -0
  173. package/src/monitor/message-handler.hydration.ts +198 -0
  174. package/src/monitor/message-handler.module-test-helpers.ts +31 -0
  175. package/src/monitor/message-handler.preflight-channel-access.ts +86 -0
  176. package/src/monitor/message-handler.preflight-channel-context.ts +58 -0
  177. package/src/monitor/message-handler.preflight-context.ts +54 -0
  178. package/src/monitor/message-handler.preflight-helpers.ts +164 -0
  179. package/src/monitor/message-handler.preflight-history.ts +23 -0
  180. package/src/monitor/message-handler.preflight-logging.ts +36 -0
  181. package/src/monitor/message-handler.preflight-pluralkit.ts +28 -0
  182. package/src/monitor/message-handler.preflight-runtime.ts +28 -0
  183. package/src/monitor/message-handler.preflight-thread.ts +49 -0
  184. package/src/monitor/message-handler.preflight.ts +822 -0
  185. package/src/monitor/message-handler.preflight.types.ts +115 -0
  186. package/src/monitor/message-handler.process.ts +1033 -0
  187. package/src/monitor/message-handler.routing-preflight.ts +112 -0
  188. package/src/monitor/message-handler.ts +309 -0
  189. package/src/monitor/message-media.ts +536 -0
  190. package/src/monitor/message-run-queue.ts +101 -0
  191. package/src/monitor/message-text.ts +171 -0
  192. package/src/monitor/message-utils.ts +34 -0
  193. package/src/monitor/model-picker-preferences.ts +184 -0
  194. package/src/monitor/model-picker.state.ts +364 -0
  195. package/src/monitor/model-picker.test-utils.ts +26 -0
  196. package/src/monitor/model-picker.ts +38 -0
  197. package/src/monitor/model-picker.view.ts +722 -0
  198. package/src/monitor/native-command-agent-reply.ts +125 -0
  199. package/src/monitor/native-command-arg-ui.ts +233 -0
  200. package/src/monitor/native-command-auth.ts +309 -0
  201. package/src/monitor/native-command-bypass.ts +13 -0
  202. package/src/monitor/native-command-context.ts +109 -0
  203. package/src/monitor/native-command-dispatch.ts +35 -0
  204. package/src/monitor/native-command-model-picker-apply.ts +209 -0
  205. package/src/monitor/native-command-model-picker-interaction.ts +516 -0
  206. package/src/monitor/native-command-model-picker-ui.ts +357 -0
  207. package/src/monitor/native-command-reply.ts +185 -0
  208. package/src/monitor/native-command-route.ts +91 -0
  209. package/src/monitor/native-command-status.ts +76 -0
  210. package/src/monitor/native-command-ui.ts +26 -0
  211. package/src/monitor/native-command-ui.types.ts +20 -0
  212. package/src/monitor/native-command.args.ts +45 -0
  213. package/src/monitor/native-command.options.ts +153 -0
  214. package/src/monitor/native-command.runtime.ts +51 -0
  215. package/src/monitor/native-command.ts +747 -0
  216. package/src/monitor/native-command.types.ts +9 -0
  217. package/src/monitor/native-interaction-channel-context.ts +50 -0
  218. package/src/monitor/preflight-audio.runtime.ts +9 -0
  219. package/src/monitor/preflight-audio.ts +130 -0
  220. package/src/monitor/presence-cache.ts +61 -0
  221. package/src/monitor/presence.ts +50 -0
  222. package/src/monitor/provider-session.runtime.ts +12 -0
  223. package/src/monitor/provider.acp.ts +89 -0
  224. package/src/monitor/provider.allowlist.ts +398 -0
  225. package/src/monitor/provider.cleanup.ts +41 -0
  226. package/src/monitor/provider.commands.ts +129 -0
  227. package/src/monitor/provider.config-log.ts +45 -0
  228. package/src/monitor/provider.deploy-errors.ts +362 -0
  229. package/src/monitor/provider.deploy.ts +221 -0
  230. package/src/monitor/provider.interactions.ts +160 -0
  231. package/src/monitor/provider.lifecycle.ts +562 -0
  232. package/src/monitor/provider.runtime.ts +1 -0
  233. package/src/monitor/provider.startup-log.ts +32 -0
  234. package/src/monitor/provider.startup.ts +323 -0
  235. package/src/monitor/provider.ts +688 -0
  236. package/src/monitor/reply-context.ts +64 -0
  237. package/src/monitor/reply-delivery.ts +216 -0
  238. package/src/monitor/reply-safety.ts +96 -0
  239. package/src/monitor/rest-fetch.ts +97 -0
  240. package/src/monitor/route-resolution.ts +140 -0
  241. package/src/monitor/sender-identity.ts +81 -0
  242. package/src/monitor/startup-status.ts +10 -0
  243. package/src/monitor/status.ts +22 -0
  244. package/src/monitor/system-events.ts +55 -0
  245. package/src/monitor/thread-bindings.config.ts +35 -0
  246. package/src/monitor/thread-bindings.discord-api.ts +310 -0
  247. package/src/monitor/thread-bindings.lifecycle.ts +354 -0
  248. package/src/monitor/thread-bindings.manager.ts +554 -0
  249. package/src/monitor/thread-bindings.messages.ts +6 -0
  250. package/src/monitor/thread-bindings.persona.ts +25 -0
  251. package/src/monitor/thread-bindings.session-adapter.ts +229 -0
  252. package/src/monitor/thread-bindings.session-shared.ts +59 -0
  253. package/src/monitor/thread-bindings.session-updates.ts +35 -0
  254. package/src/monitor/thread-bindings.state.ts +540 -0
  255. package/src/monitor/thread-bindings.ts +48 -0
  256. package/src/monitor/thread-bindings.types.ts +83 -0
  257. package/src/monitor/thread-channel-context.ts +112 -0
  258. package/src/monitor/thread-session-close.ts +63 -0
  259. package/src/monitor/thread-title.ts +181 -0
  260. package/src/monitor/threading.auto-thread.ts +287 -0
  261. package/src/monitor/threading.cache.ts +45 -0
  262. package/src/monitor/threading.starter.ts +288 -0
  263. package/src/monitor/threading.ts +20 -0
  264. package/src/monitor/threading.types.ts +102 -0
  265. package/src/monitor/timeouts.ts +84 -0
  266. package/src/monitor/typing.ts +17 -0
  267. package/src/monitor.gateway.ts +75 -0
  268. package/src/monitor.ts +28 -0
  269. package/src/network-config.ts +79 -0
  270. package/src/normalize.ts +86 -0
  271. package/src/outbound-adapter.ts +327 -0
  272. package/src/outbound-approval.ts +29 -0
  273. package/src/outbound-components.ts +86 -0
  274. package/src/outbound-payload.ts +208 -0
  275. package/src/outbound-send-context.ts +92 -0
  276. package/src/outbound-session-route.ts +72 -0
  277. package/src/pluralkit.ts +58 -0
  278. package/src/preview-streaming.ts +18 -0
  279. package/src/probe.runtime.ts +1 -0
  280. package/src/probe.ts +237 -0
  281. package/src/proxy-fetch.ts +92 -0
  282. package/src/proxy-request-client.ts +21 -0
  283. package/src/recipient-resolution.ts +39 -0
  284. package/src/resolve-allowlist-common.ts +39 -0
  285. package/src/resolve-channels.ts +369 -0
  286. package/src/resolve-users.ts +184 -0
  287. package/src/retry.ts +98 -0
  288. package/src/runtime-api.ts +64 -0
  289. package/src/runtime-config.ts +16 -0
  290. package/src/runtime.ts +23 -0
  291. package/src/secret-config-contract.ts +140 -0
  292. package/src/security-audit.runtime.ts +1 -0
  293. package/src/security-audit.ts +208 -0
  294. package/src/security-contract.ts +47 -0
  295. package/src/security-doctor.ts +20 -0
  296. package/src/security.ts +60 -0
  297. package/src/send-target-parsing.ts +14 -0
  298. package/src/send.channels.ts +139 -0
  299. package/src/send.components.ts +391 -0
  300. package/src/send.emojis-stickers.ts +57 -0
  301. package/src/send.guild.ts +170 -0
  302. package/src/send.message-request.ts +112 -0
  303. package/src/send.messages.ts +229 -0
  304. package/src/send.outbound.ts +459 -0
  305. package/src/send.permissions.ts +283 -0
  306. package/src/send.reactions.ts +155 -0
  307. package/src/send.receipt.ts +69 -0
  308. package/src/send.shared.ts +469 -0
  309. package/src/send.ts +82 -0
  310. package/src/send.types.ts +191 -0
  311. package/src/send.typing.ts +9 -0
  312. package/src/send.voice.ts +140 -0
  313. package/src/send.webhook.ts +137 -0
  314. package/src/session-contract.ts +3 -0
  315. package/src/session-key-normalization.ts +47 -0
  316. package/src/setup-account-state.ts +144 -0
  317. package/src/setup-adapter.ts +14 -0
  318. package/src/setup-core.ts +215 -0
  319. package/src/setup-runtime-helpers.ts +10 -0
  320. package/src/setup-surface.ts +132 -0
  321. package/src/shared-interactive.ts +167 -0
  322. package/src/shared.ts +197 -0
  323. package/src/status-issues.ts +201 -0
  324. package/src/subagent-hooks.ts +232 -0
  325. package/src/target-parsing.ts +70 -0
  326. package/src/target-resolver.ts +129 -0
  327. package/src/targets.ts +12 -0
  328. package/src/token.ts +107 -0
  329. package/src/ui-colors.ts +27 -0
  330. package/src/ui.ts +20 -0
  331. package/src/voice/access.ts +126 -0
  332. package/src/voice/audio.ts +249 -0
  333. package/src/voice/capture-state.ts +120 -0
  334. package/src/voice/command.ts +284 -0
  335. package/src/voice/config.ts +8 -0
  336. package/src/voice/ingress.ts +164 -0
  337. package/src/voice/manager.runtime.ts +14 -0
  338. package/src/voice/manager.ts +1155 -0
  339. package/src/voice/prompt.ts +22 -0
  340. package/src/voice/realtime.ts +1370 -0
  341. package/src/voice/receive-recovery.ts +159 -0
  342. package/src/voice/sanitize.ts +29 -0
  343. package/src/voice/sdk-runtime.ts +14 -0
  344. package/src/voice/segment.ts +160 -0
  345. package/src/voice/session.ts +81 -0
  346. package/src/voice/speaker-context.ts +127 -0
  347. package/src/voice/tts.ts +151 -0
  348. package/src/voice-message.ts +474 -0
  349. package/subagent-hooks-api.ts +27 -0
  350. package/test-api.ts +4 -0
  351. package/thread-binding-api.ts +1 -0
  352. package/timeouts.ts +6 -0
  353. package/tsconfig.json +16 -0
@@ -0,0 +1,357 @@
1
+ import { resolveDefaultModelForAgent } from "autobot/plugin-sdk/agent-runtime";
2
+ import {
3
+ resolveStoredModelOverride,
4
+ serializeCommandArgs,
5
+ type ChatCommandDefinition,
6
+ type CommandArgs,
7
+ } from "autobot/plugin-sdk/command-auth-native";
8
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
9
+ import type { ResolvedAgentRoute } from "autobot/plugin-sdk/routing";
10
+ import { loadSessionStore, resolveStorePath } from "autobot/plugin-sdk/session-store-runtime";
11
+ import {
12
+ normalizeLowercaseStringOrEmpty,
13
+ normalizeOptionalString,
14
+ } from "autobot/plugin-sdk/string-coerce-runtime";
15
+ import {
16
+ Container,
17
+ TextDisplay,
18
+ type AutocompleteInteraction,
19
+ type ButtonInteraction,
20
+ type CommandInteraction,
21
+ type StringSelectMenuInteraction,
22
+ } from "../internal/discord.js";
23
+ import {
24
+ readDiscordModelPickerRecentModels,
25
+ type DiscordModelPickerPreferenceScope,
26
+ } from "./model-picker-preferences.js";
27
+ import {
28
+ loadDiscordModelPickerData,
29
+ renderDiscordModelPickerModelsView,
30
+ toDiscordModelPickerMessagePayload,
31
+ type DiscordModelPickerCommandContext,
32
+ } from "./model-picker.js";
33
+ import { resolveDiscordNativeInteractionRouteState } from "./native-command-route.js";
34
+ import type { SafeDiscordInteractionCall } from "./native-command-ui.types.js";
35
+ import { resolveDiscordNativeInteractionChannelContext } from "./native-interaction-channel-context.js";
36
+ import type { ThreadBindingManager } from "./thread-bindings.js";
37
+
38
+ type DiscordNativeChoiceInteraction =
39
+ | AutocompleteInteraction
40
+ | CommandInteraction
41
+ | ButtonInteraction
42
+ | StringSelectMenuInteraction;
43
+
44
+ function resolveDiscordModelPickerCommandContext(
45
+ command: ChatCommandDefinition,
46
+ ): DiscordModelPickerCommandContext | null {
47
+ const normalized = normalizeLowercaseStringOrEmpty(command.nativeName ?? command.key);
48
+ if (normalized === "model" || normalized === "models") {
49
+ return normalized;
50
+ }
51
+ return null;
52
+ }
53
+
54
+ function resolveCommandArgStringValue(args: CommandArgs | undefined, key: string): string {
55
+ const value = args?.values?.[key];
56
+ if (typeof value !== "string") {
57
+ return "";
58
+ }
59
+ return value.trim();
60
+ }
61
+
62
+ export function shouldOpenDiscordModelPickerFromCommand(params: {
63
+ command: ChatCommandDefinition;
64
+ commandArgs?: CommandArgs;
65
+ }): DiscordModelPickerCommandContext | null {
66
+ const context = resolveDiscordModelPickerCommandContext(params.command);
67
+ if (!context) {
68
+ return null;
69
+ }
70
+
71
+ const serializedArgs =
72
+ normalizeOptionalString(serializeCommandArgs(params.command, params.commandArgs)) ?? "";
73
+ if (context === "model") {
74
+ const modelValue = resolveCommandArgStringValue(params.commandArgs, "model");
75
+ return !modelValue && !serializedArgs ? context : null;
76
+ }
77
+
78
+ return serializedArgs ? null : context;
79
+ }
80
+
81
+ function buildDiscordModelPickerCurrentModel(
82
+ defaultProvider: string,
83
+ defaultModel: string,
84
+ ): string {
85
+ return `${defaultProvider}/${defaultModel}`;
86
+ }
87
+
88
+ export function buildDiscordModelPickerAllowedModelRefs(
89
+ data: Awaited<ReturnType<typeof loadDiscordModelPickerData>>,
90
+ ): Set<string> {
91
+ const out = new Set<string>();
92
+ for (const provider of data.providers) {
93
+ const models = data.byProvider.get(provider);
94
+ if (!models) {
95
+ continue;
96
+ }
97
+ for (const model of models) {
98
+ out.add(`${provider}/${model}`);
99
+ }
100
+ }
101
+ return out;
102
+ }
103
+
104
+ export function resolveDiscordModelPickerPreferenceScope(params: {
105
+ interaction: CommandInteraction | ButtonInteraction | StringSelectMenuInteraction;
106
+ accountId: string;
107
+ userId: string;
108
+ }): DiscordModelPickerPreferenceScope {
109
+ return {
110
+ accountId: params.accountId,
111
+ guildId: params.interaction.guild?.id ?? undefined,
112
+ userId: params.userId,
113
+ };
114
+ }
115
+
116
+ export function buildDiscordModelPickerNoticePayload(message: string): { components: Container[] } {
117
+ return {
118
+ components: [new Container([new TextDisplay(message)])],
119
+ };
120
+ }
121
+
122
+ async function resolveDiscordModelPickerRouteState(params: {
123
+ interaction:
124
+ | CommandInteraction
125
+ | ButtonInteraction
126
+ | StringSelectMenuInteraction
127
+ | AutocompleteInteraction;
128
+ cfg: AutoBotConfig;
129
+ accountId: string;
130
+ threadBindings: ThreadBindingManager;
131
+ enforceConfiguredBindingReadiness?: boolean;
132
+ }) {
133
+ const { interaction, cfg, accountId } = params;
134
+ const { isDirectMessage, isGroupDm, isThreadChannel, rawChannelId, threadParentId } =
135
+ await resolveDiscordNativeInteractionChannelContext({
136
+ channel: interaction.channel,
137
+ client: interaction.client,
138
+ hasGuild: Boolean(interaction.guild),
139
+ channelIdFallback: "unknown",
140
+ });
141
+ const memberRoleIds = Array.isArray(interaction.rawData.member?.roles)
142
+ ? interaction.rawData.member.roles.map((roleId: string) => roleId)
143
+ : [];
144
+
145
+ const threadBinding = isThreadChannel
146
+ ? params.threadBindings.getByThreadId(rawChannelId)
147
+ : undefined;
148
+ return await resolveDiscordNativeInteractionRouteState({
149
+ cfg,
150
+ accountId,
151
+ guildId: interaction.guild?.id ?? undefined,
152
+ memberRoleIds,
153
+ isDirectMessage,
154
+ isGroupDm,
155
+ directUserId: interaction.user?.id ?? rawChannelId,
156
+ conversationId: rawChannelId,
157
+ parentConversationId: threadParentId,
158
+ threadBinding,
159
+ enforceConfiguredBindingReadiness: params.enforceConfiguredBindingReadiness,
160
+ });
161
+ }
162
+
163
+ export async function resolveDiscordModelPickerRoute(params: {
164
+ interaction:
165
+ | CommandInteraction
166
+ | ButtonInteraction
167
+ | StringSelectMenuInteraction
168
+ | AutocompleteInteraction;
169
+ cfg: AutoBotConfig;
170
+ accountId: string;
171
+ threadBindings: ThreadBindingManager;
172
+ }) {
173
+ const resolved = await resolveDiscordModelPickerRouteState(params);
174
+ return resolved.effectiveRoute;
175
+ }
176
+
177
+ export async function resolveDiscordNativeChoiceContext(params: {
178
+ interaction: DiscordNativeChoiceInteraction;
179
+ cfg: AutoBotConfig;
180
+ accountId: string;
181
+ threadBindings: ThreadBindingManager;
182
+ }): Promise<{ provider?: string; model?: string } | null> {
183
+ try {
184
+ const resolved = await resolveDiscordModelPickerRouteState({
185
+ interaction: params.interaction,
186
+ cfg: params.cfg,
187
+ accountId: params.accountId,
188
+ threadBindings: params.threadBindings,
189
+ enforceConfiguredBindingReadiness: true,
190
+ });
191
+ if (resolved.bindingReadiness && !resolved.bindingReadiness.ok) {
192
+ return null;
193
+ }
194
+ const route = resolved.effectiveRoute;
195
+ const fallback = resolveDefaultModelForAgent({
196
+ cfg: params.cfg,
197
+ agentId: route.agentId,
198
+ });
199
+ const storePath = resolveStorePath(params.cfg.session?.store, {
200
+ agentId: route.agentId,
201
+ });
202
+ const sessionStore = loadSessionStore(storePath);
203
+ const sessionEntry = sessionStore[route.sessionKey];
204
+ const override = resolveStoredModelOverride({
205
+ sessionEntry,
206
+ sessionStore,
207
+ sessionKey: route.sessionKey,
208
+ defaultProvider: fallback.provider,
209
+ });
210
+ if (!override?.model) {
211
+ return {
212
+ provider: fallback.provider,
213
+ model: fallback.model,
214
+ };
215
+ }
216
+ return {
217
+ provider: override.provider || fallback.provider,
218
+ model: override.model,
219
+ };
220
+ } catch {
221
+ return null;
222
+ }
223
+ }
224
+
225
+ export function resolveDiscordModelPickerCurrentModel(params: {
226
+ cfg: AutoBotConfig;
227
+ route: ResolvedAgentRoute;
228
+ data: Awaited<ReturnType<typeof loadDiscordModelPickerData>>;
229
+ }): string {
230
+ const fallback = buildDiscordModelPickerCurrentModel(
231
+ params.data.resolvedDefault.provider,
232
+ params.data.resolvedDefault.model,
233
+ );
234
+ try {
235
+ const storePath = resolveStorePath(params.cfg.session?.store, {
236
+ agentId: params.route.agentId,
237
+ });
238
+ const sessionStore = loadSessionStore(storePath, { skipCache: true });
239
+ const sessionEntry = sessionStore[params.route.sessionKey];
240
+ const override = resolveStoredModelOverride({
241
+ sessionEntry,
242
+ sessionStore,
243
+ sessionKey: params.route.sessionKey,
244
+ defaultProvider: params.data.resolvedDefault.provider,
245
+ });
246
+ if (!override?.model) {
247
+ return fallback;
248
+ }
249
+ const provider = (override.provider || params.data.resolvedDefault.provider).trim();
250
+ if (!provider) {
251
+ return fallback;
252
+ }
253
+ return `${provider}/${override.model}`;
254
+ } catch {
255
+ return fallback;
256
+ }
257
+ }
258
+
259
+ export function resolveDiscordModelPickerCurrentRuntime(params: {
260
+ cfg: AutoBotConfig;
261
+ route: ResolvedAgentRoute;
262
+ }): string {
263
+ try {
264
+ const storePath = resolveStorePath(params.cfg.session?.store, {
265
+ agentId: params.route.agentId,
266
+ });
267
+ const sessionStore = loadSessionStore(storePath, { skipCache: true });
268
+ const sessionRuntime = normalizeOptionalString(
269
+ sessionStore[params.route.sessionKey]?.agentRuntimeOverride,
270
+ );
271
+ if (sessionRuntime) {
272
+ return sessionRuntime;
273
+ }
274
+ } catch {}
275
+
276
+ return "auto";
277
+ }
278
+
279
+ export async function replyWithDiscordModelPickerProviders(params: {
280
+ interaction: CommandInteraction | ButtonInteraction | StringSelectMenuInteraction;
281
+ cfg: AutoBotConfig;
282
+ command: DiscordModelPickerCommandContext;
283
+ userId: string;
284
+ accountId: string;
285
+ threadBindings: ThreadBindingManager;
286
+ preferFollowUp: boolean;
287
+ safeInteractionCall: SafeDiscordInteractionCall;
288
+ }) {
289
+ const route = await resolveDiscordModelPickerRoute({
290
+ interaction: params.interaction,
291
+ cfg: params.cfg,
292
+ accountId: params.accountId,
293
+ threadBindings: params.threadBindings,
294
+ });
295
+ const data = await loadDiscordModelPickerData(params.cfg, route.agentId);
296
+ const currentModel = resolveDiscordModelPickerCurrentModel({
297
+ cfg: params.cfg,
298
+ route,
299
+ data,
300
+ });
301
+ const currentRuntime = resolveDiscordModelPickerCurrentRuntime({
302
+ cfg: params.cfg,
303
+ route,
304
+ });
305
+ const quickModels = await readDiscordModelPickerRecentModels({
306
+ scope: resolveDiscordModelPickerPreferenceScope({
307
+ interaction: params.interaction,
308
+ accountId: params.accountId,
309
+ userId: params.userId,
310
+ }),
311
+ allowedModelRefs: buildDiscordModelPickerAllowedModelRefs(data),
312
+ limit: 5,
313
+ });
314
+ const currentProvider = splitDiscordModelRef(currentModel ?? "")?.provider;
315
+ const initialProvider =
316
+ currentProvider && data.byProvider.has(currentProvider)
317
+ ? currentProvider
318
+ : (data.providers[0] ?? data.resolvedDefault.provider);
319
+
320
+ const rendered = renderDiscordModelPickerModelsView({
321
+ command: params.command,
322
+ userId: params.userId,
323
+ data,
324
+ provider: initialProvider,
325
+ page: 1,
326
+ providerPage: 1,
327
+ currentModel,
328
+ currentRuntime,
329
+ quickModels,
330
+ });
331
+ const payload = {
332
+ ...toDiscordModelPickerMessagePayload(rendered),
333
+ ephemeral: true,
334
+ };
335
+
336
+ await params.safeInteractionCall("model picker reply", async () => {
337
+ if (params.preferFollowUp) {
338
+ await params.interaction.followUp(payload);
339
+ return;
340
+ }
341
+ await params.interaction.reply(payload);
342
+ });
343
+ }
344
+
345
+ export function splitDiscordModelRef(modelRef: string): { provider: string; model: string } | null {
346
+ const trimmed = modelRef.trim();
347
+ const slashIndex = trimmed.indexOf("/");
348
+ if (slashIndex <= 0 || slashIndex >= trimmed.length - 1) {
349
+ return null;
350
+ }
351
+ const provider = trimmed.slice(0, slashIndex).trim();
352
+ const model = trimmed.slice(slashIndex + 1).trim();
353
+ if (!provider || !model) {
354
+ return null;
355
+ }
356
+ return { provider, model };
357
+ }
@@ -0,0 +1,185 @@
1
+ import type { ReplyPayload } from "autobot/plugin-sdk/reply-dispatch-runtime";
2
+ import {
3
+ resolveSendableOutboundReplyParts,
4
+ resolveTextChunksWithFallback,
5
+ } from "autobot/plugin-sdk/reply-payload";
6
+ import { logVerbose } from "autobot/plugin-sdk/runtime-env";
7
+ import { loadWebMedia } from "autobot/plugin-sdk/web-media";
8
+ import { chunkDiscordTextWithMode } from "../chunk.js";
9
+ import type {
10
+ ButtonInteraction,
11
+ CommandInteraction,
12
+ StringSelectMenuInteraction,
13
+ TopLevelComponents,
14
+ } from "../internal/discord.js";
15
+
16
+ export const DISCORD_EMPTY_VISIBLE_REPLY_WARNING = "⚠️ Command produced no visible reply.";
17
+
18
+ export function isDiscordUnknownInteraction(error: unknown): boolean {
19
+ if (!error || typeof error !== "object") {
20
+ return false;
21
+ }
22
+ const err = error as {
23
+ discordCode?: number;
24
+ status?: number;
25
+ message?: string;
26
+ rawBody?: { code?: number; message?: string };
27
+ };
28
+ if (err.discordCode === 10062 || err.rawBody?.code === 10062) {
29
+ return true;
30
+ }
31
+ if (err.status === 404 && /Unknown interaction/i.test(err.message ?? "")) {
32
+ return true;
33
+ }
34
+ if (/Unknown interaction/i.test(err.rawBody?.message ?? "")) {
35
+ return true;
36
+ }
37
+ return false;
38
+ }
39
+
40
+ export function hasRenderableReplyPayload(payload: ReplyPayload): boolean {
41
+ if (resolveSendableOutboundReplyParts(payload).hasContent) {
42
+ return true;
43
+ }
44
+ const discordData = payload.channelData?.discord as
45
+ | { components?: TopLevelComponents[] }
46
+ | undefined;
47
+ if (Array.isArray(discordData?.components) && discordData.components.length > 0) {
48
+ return true;
49
+ }
50
+ return false;
51
+ }
52
+
53
+ export async function safeDiscordInteractionCall<T>(
54
+ label: string,
55
+ fn: () => Promise<T>,
56
+ ): Promise<T | null> {
57
+ try {
58
+ return await fn();
59
+ } catch (error) {
60
+ if (isDiscordUnknownInteraction(error)) {
61
+ logVerbose(`discord: ${label} skipped (interaction expired)`);
62
+ return null;
63
+ }
64
+ throw error;
65
+ }
66
+ }
67
+
68
+ export async function deliverDiscordInteractionReply(params: {
69
+ interaction: CommandInteraction | ButtonInteraction | StringSelectMenuInteraction;
70
+ payload: ReplyPayload;
71
+ mediaLocalRoots?: readonly string[];
72
+ textLimit: number;
73
+ maxLinesPerMessage?: number;
74
+ preferFollowUp: boolean;
75
+ responseEphemeral?: boolean;
76
+ chunkMode: "length" | "newline";
77
+ }) {
78
+ const { interaction, payload, textLimit, maxLinesPerMessage, preferFollowUp, chunkMode } = params;
79
+ const reply = resolveSendableOutboundReplyParts(payload);
80
+ const discordData = payload.channelData?.discord as
81
+ | { components?: TopLevelComponents[] }
82
+ | undefined;
83
+ let firstMessageComponents =
84
+ Array.isArray(discordData?.components) && discordData.components.length > 0
85
+ ? discordData.components
86
+ : undefined;
87
+
88
+ let hasReplied = false;
89
+ const sendMessage = async (
90
+ content: string,
91
+ files?: { name: string; data: Buffer }[],
92
+ components?: TopLevelComponents[],
93
+ ) => {
94
+ const contentPayload = content ? { content } : {};
95
+ const payload =
96
+ files && files.length > 0
97
+ ? {
98
+ ...contentPayload,
99
+ ...(components ? { components } : {}),
100
+ ...(params.responseEphemeral !== undefined
101
+ ? { ephemeral: params.responseEphemeral }
102
+ : {}),
103
+ files: files.map((file) => {
104
+ if (file.data instanceof Blob) {
105
+ return { name: file.name, data: file.data };
106
+ }
107
+ const arrayBuffer = Uint8Array.from(file.data).buffer;
108
+ return { name: file.name, data: new Blob([arrayBuffer]) };
109
+ }),
110
+ }
111
+ : {
112
+ ...contentPayload,
113
+ ...(components ? { components } : {}),
114
+ ...(params.responseEphemeral !== undefined
115
+ ? { ephemeral: params.responseEphemeral }
116
+ : {}),
117
+ };
118
+ await safeDiscordInteractionCall("interaction send", async () => {
119
+ if (!preferFollowUp && !hasReplied) {
120
+ await interaction.reply(payload);
121
+ hasReplied = true;
122
+ firstMessageComponents = undefined;
123
+ return;
124
+ }
125
+ await interaction.followUp(payload);
126
+ hasReplied = true;
127
+ firstMessageComponents = undefined;
128
+ });
129
+ };
130
+
131
+ if (reply.hasMedia) {
132
+ const media = await Promise.all(
133
+ reply.mediaUrls.map(async (url) => {
134
+ const loaded = await loadWebMedia(url, {
135
+ localRoots: params.mediaLocalRoots,
136
+ });
137
+ return {
138
+ name: loaded.fileName ?? "upload",
139
+ data: loaded.buffer,
140
+ };
141
+ }),
142
+ );
143
+ const chunks = resolveTextChunksWithFallback(
144
+ reply.text,
145
+ chunkDiscordTextWithMode(reply.text, {
146
+ maxChars: textLimit,
147
+ maxLines: maxLinesPerMessage,
148
+ chunkMode,
149
+ }),
150
+ );
151
+ const caption = chunks[0] ?? "";
152
+ await sendMessage(caption, media, firstMessageComponents);
153
+ for (const chunk of chunks.slice(1)) {
154
+ if (!chunk.trim()) {
155
+ continue;
156
+ }
157
+ await sendMessage(chunk);
158
+ }
159
+ return;
160
+ }
161
+
162
+ if (!reply.hasText && !firstMessageComponents) {
163
+ return;
164
+ }
165
+ let chunks =
166
+ reply.text || firstMessageComponents
167
+ ? resolveTextChunksWithFallback(
168
+ reply.text,
169
+ chunkDiscordTextWithMode(reply.text, {
170
+ maxChars: textLimit,
171
+ maxLines: maxLinesPerMessage,
172
+ chunkMode,
173
+ }),
174
+ )
175
+ : [];
176
+ if (chunks.length === 0 && firstMessageComponents) {
177
+ chunks = [""];
178
+ }
179
+ for (const chunk of chunks) {
180
+ if (!chunk.trim() && !firstMessageComponents) {
181
+ continue;
182
+ }
183
+ await sendMessage(chunk, undefined, firstMessageComponents);
184
+ }
185
+ }
@@ -0,0 +1,91 @@
1
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
2
+ import * as conversationRuntime from "autobot/plugin-sdk/conversation-binding-runtime";
3
+ import type { ResolvedAgentRoute } from "autobot/plugin-sdk/routing";
4
+ import { normalizeOptionalString } from "autobot/plugin-sdk/string-coerce-runtime";
5
+ import {
6
+ resolveDiscordBoundConversationRoute,
7
+ resolveDiscordEffectiveRoute,
8
+ } from "./route-resolution.js";
9
+ import type { ThreadBindingRecord } from "./thread-bindings.js";
10
+
11
+ type ResolvedConfiguredBindingRoute = ReturnType<
12
+ typeof conversationRuntime.resolveConfiguredBindingRoute
13
+ >;
14
+ type ConfiguredBindingResolution = NonNullable<
15
+ NonNullable<ResolvedConfiguredBindingRoute>["bindingResolution"]
16
+ >;
17
+
18
+ type DiscordNativeInteractionRouteState = {
19
+ route: ResolvedAgentRoute;
20
+ effectiveRoute: ResolvedAgentRoute;
21
+ boundSessionKey?: string;
22
+ configuredRoute: ResolvedConfiguredBindingRoute | null;
23
+ configuredBinding: ConfiguredBindingResolution | null;
24
+ bindingReadiness: Awaited<
25
+ ReturnType<typeof conversationRuntime.ensureConfiguredBindingRouteReady>
26
+ > | null;
27
+ };
28
+
29
+ export async function resolveDiscordNativeInteractionRouteState(params: {
30
+ cfg: AutoBotConfig;
31
+ accountId: string;
32
+ guildId?: string;
33
+ memberRoleIds?: string[];
34
+ isDirectMessage: boolean;
35
+ isGroupDm: boolean;
36
+ directUserId?: string;
37
+ conversationId: string;
38
+ parentConversationId?: string;
39
+ threadBinding?: ThreadBindingRecord;
40
+ enforceConfiguredBindingReadiness?: boolean;
41
+ }): Promise<DiscordNativeInteractionRouteState> {
42
+ const route = resolveDiscordBoundConversationRoute({
43
+ cfg: params.cfg,
44
+ accountId: params.accountId,
45
+ guildId: params.guildId,
46
+ memberRoleIds: params.memberRoleIds,
47
+ isDirectMessage: params.isDirectMessage,
48
+ isGroupDm: params.isGroupDm,
49
+ directUserId: params.directUserId,
50
+ conversationId: params.conversationId,
51
+ parentConversationId: params.parentConversationId,
52
+ });
53
+ const configuredRoute =
54
+ params.threadBinding == null
55
+ ? conversationRuntime.resolveConfiguredBindingRoute({
56
+ cfg: params.cfg,
57
+ route,
58
+ conversation: {
59
+ channel: "discord",
60
+ accountId: params.accountId,
61
+ conversationId: params.conversationId,
62
+ parentConversationId: params.parentConversationId,
63
+ },
64
+ })
65
+ : null;
66
+ const configuredBinding = configuredRoute?.bindingResolution ?? null;
67
+ const configuredBoundSessionKey = normalizeOptionalString(configuredRoute?.boundSessionKey);
68
+ const boundSessionKey =
69
+ normalizeOptionalString(params.threadBinding?.targetSessionKey) ?? configuredBoundSessionKey;
70
+ const effectiveRoute = resolveDiscordEffectiveRoute({
71
+ route,
72
+ boundSessionKey,
73
+ configuredRoute,
74
+ matchedBy: configuredBinding ? "binding.channel" : undefined,
75
+ });
76
+ const bindingReadiness =
77
+ params.enforceConfiguredBindingReadiness && configuredBinding
78
+ ? await conversationRuntime.ensureConfiguredBindingRouteReady({
79
+ cfg: params.cfg,
80
+ bindingResolution: configuredBinding,
81
+ })
82
+ : null;
83
+ return {
84
+ route,
85
+ effectiveRoute,
86
+ boundSessionKey,
87
+ configuredRoute,
88
+ configuredBinding,
89
+ bindingReadiness,
90
+ };
91
+ }
@@ -0,0 +1,76 @@
1
+ import { resolveDirectStatusReplyForSession } from "autobot/plugin-sdk/command-status-runtime";
2
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
3
+ import { resolveChunkMode, resolveTextChunkLimit } from "autobot/plugin-sdk/reply-chunking";
4
+ import type { ResolvedAgentRoute } from "autobot/plugin-sdk/routing";
5
+ import { resolveDiscordMaxLinesPerMessage } from "../accounts.js";
6
+ import type {
7
+ ButtonInteraction,
8
+ CommandInteraction,
9
+ StringSelectMenuInteraction,
10
+ } from "../internal/discord.js";
11
+ import type { DispatchDiscordCommandInteractionResult } from "./native-command-dispatch.js";
12
+ import {
13
+ deliverDiscordInteractionReply,
14
+ hasRenderableReplyPayload,
15
+ } from "./native-command-reply.js";
16
+ import type { DiscordConfig } from "./native-command.types.js";
17
+
18
+ type ResolveDirectStatusReplyForSession = typeof resolveDirectStatusReplyForSession;
19
+
20
+ export async function maybeDeliverDiscordDirectStatus(params: {
21
+ commandName: string;
22
+ suppressReplies?: boolean;
23
+ resolveDirectStatusReplyForSession: ResolveDirectStatusReplyForSession;
24
+ cfg: AutoBotConfig;
25
+ discordConfig: DiscordConfig;
26
+ accountId: string;
27
+ sessionKey: string;
28
+ commandTargetSessionKey?: string | null;
29
+ channel: "discord";
30
+ senderId: string;
31
+ senderIsOwner: boolean;
32
+ isAuthorizedSender: boolean;
33
+ isGroup: boolean;
34
+ defaultGroupActivation: () => "always" | "mention";
35
+ interaction: CommandInteraction | ButtonInteraction | StringSelectMenuInteraction;
36
+ mediaLocalRoots: readonly string[];
37
+ preferFollowUp: boolean;
38
+ responseEphemeral?: boolean;
39
+ effectiveRoute: ResolvedAgentRoute;
40
+ respond: (content: string, options?: { ephemeral?: boolean }) => Promise<void>;
41
+ }): Promise<DispatchDiscordCommandInteractionResult | null> {
42
+ if (params.suppressReplies || params.commandName !== "status") {
43
+ return null;
44
+ }
45
+ const statusReply = await params.resolveDirectStatusReplyForSession({
46
+ cfg: params.cfg,
47
+ sessionKey: params.commandTargetSessionKey?.trim() || params.sessionKey,
48
+ channel: params.channel,
49
+ senderId: params.senderId,
50
+ senderIsOwner: params.senderIsOwner,
51
+ isAuthorizedSender: params.isAuthorizedSender,
52
+ isGroup: params.isGroup,
53
+ defaultGroupActivation: params.defaultGroupActivation,
54
+ });
55
+ if (statusReply && hasRenderableReplyPayload(statusReply)) {
56
+ await deliverDiscordInteractionReply({
57
+ interaction: params.interaction,
58
+ payload: statusReply,
59
+ mediaLocalRoots: params.mediaLocalRoots,
60
+ textLimit: resolveTextChunkLimit(params.cfg, "discord", params.accountId, {
61
+ fallbackLimit: 2000,
62
+ }),
63
+ maxLinesPerMessage: resolveDiscordMaxLinesPerMessage({
64
+ cfg: params.cfg,
65
+ discordConfig: params.discordConfig,
66
+ accountId: params.accountId,
67
+ }),
68
+ preferFollowUp: params.preferFollowUp,
69
+ responseEphemeral: params.responseEphemeral,
70
+ chunkMode: resolveChunkMode(params.cfg, "discord", params.accountId),
71
+ });
72
+ return { accepted: true, effectiveRoute: params.effectiveRoute };
73
+ }
74
+ await params.respond("Status unavailable.");
75
+ return { accepted: true, effectiveRoute: params.effectiveRoute };
76
+ }