talon-agent 4.3.2 → 4.4.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 (146) hide show
  1. package/package.json +1 -1
  2. package/src/app.ts +1 -1
  3. package/src/backend/claude-sdk/doctor.ts +1 -1
  4. package/src/backend/claude-sdk/state.ts +1 -1
  5. package/src/backend/codex/doctor.ts +1 -1
  6. package/src/backend/codex/init.ts +1 -1
  7. package/src/backend/codex/state.ts +1 -1
  8. package/src/backend/openai-agents/doctor.ts +1 -1
  9. package/src/backend/openai-agents/init.ts +1 -1
  10. package/src/backend/openai-agents/state.ts +1 -1
  11. package/src/backend/remote-server/factory.ts +1 -1
  12. package/src/backend/remote-server/mcp.ts +1 -1
  13. package/src/backend/remote-server/server-bindings.ts +1 -1
  14. package/src/backend/remote-server/state.ts +1 -1
  15. package/src/backend/shared/system-prompt.ts +1 -1
  16. package/src/bootstrap.ts +3 -3
  17. package/src/cli/chat.ts +1 -1
  18. package/src/cli/config.ts +2 -2
  19. package/src/cli/doctor.ts +1 -1
  20. package/src/cli/index.ts +8 -0
  21. package/src/cli/memory.ts +166 -0
  22. package/src/cli/setup.ts +1 -1
  23. package/src/core/agent-runtime/backend-registry.ts +2 -2
  24. package/src/core/agent-runtime/model-ref.ts +2 -2
  25. package/src/core/auth/expiry-monitor.ts +1 -1
  26. package/src/{util/config.ts → core/config/index.ts} +8 -8
  27. package/src/core/{doctor.ts → doctor/index.ts} +11 -11
  28. package/src/core/engine/backend-controller/legacy.ts +1 -1
  29. package/src/core/engine/backend-controller/pool.ts +1 -1
  30. package/src/core/engine/backend-controller/rebind.ts +1 -1
  31. package/src/core/engine/backend-controller/state.ts +1 -1
  32. package/src/core/engine/gateway-actions/plugins.ts +1 -1
  33. package/src/core/engine/model-audit.ts +1 -1
  34. package/src/core/{notify.ts → frontend-runtime/admin-notify.ts} +1 -1
  35. package/src/core/frontend-runtime/capabilities.ts +1 -1
  36. package/src/core/models/active-model.ts +1 -1
  37. package/src/core/plugin/builtins.ts +2 -2
  38. package/src/core/plugin/manage.ts +1 -1
  39. package/src/core/plugin/native-runtimes.ts +1 -1
  40. package/src/frontend/discord/admin.ts +1 -1
  41. package/src/frontend/discord/callbacks/components/index.ts +1 -1
  42. package/src/frontend/discord/callbacks/components/settings.ts +1 -1
  43. package/src/frontend/discord/callbacks/components/types.ts +1 -1
  44. package/src/frontend/discord/callbacks/modals.ts +1 -1
  45. package/src/frontend/discord/commands/admin.ts +2 -2
  46. package/src/frontend/discord/commands/definitions.ts +1 -1
  47. package/src/frontend/discord/commands/info.ts +1 -1
  48. package/src/frontend/discord/commands/router.ts +1 -1
  49. package/src/frontend/discord/commands/session.ts +1 -1
  50. package/src/frontend/discord/commands/settings.ts +1 -1
  51. package/src/frontend/discord/handlers/delivery.ts +1 -1
  52. package/src/frontend/discord/handlers/messages.ts +1 -1
  53. package/src/frontend/discord/handlers/queue.ts +1 -1
  54. package/src/frontend/discord/handlers/state.ts +1 -1
  55. package/src/frontend/discord/index.ts +1 -1
  56. package/src/frontend/discord/middleware.ts +1 -1
  57. package/src/frontend/discord/render.ts +1 -1
  58. package/src/frontend/discord/runtime.ts +1 -1
  59. package/src/frontend/native/extensions.ts +4 -1
  60. package/src/frontend/native/index.ts +1 -1
  61. package/src/frontend/native/runtime.ts +1 -1
  62. package/src/frontend/native/settings.ts +1 -1
  63. package/src/frontend/shared/model-commands.ts +1 -1
  64. package/src/frontend/shared/plan-usage-report.ts +1 -1
  65. package/src/frontend/shared/reasoning-levels.ts +1 -1
  66. package/src/frontend/shared/session-status.ts +1 -1
  67. package/src/frontend/teams/index.ts +1 -1
  68. package/src/frontend/teams/runtime.ts +1 -1
  69. package/src/frontend/telegram/actions/chat-info.ts +1 -1
  70. package/src/frontend/telegram/actions/coerce.ts +15 -0
  71. package/src/frontend/telegram/actions/index.ts +2 -2
  72. package/src/frontend/telegram/actions/media.ts +1 -1
  73. package/src/frontend/telegram/actions/messaging.ts +3 -4
  74. package/src/frontend/telegram/actions/moderation/chat.ts +63 -0
  75. package/src/frontend/telegram/actions/moderation/index.ts +74 -0
  76. package/src/frontend/telegram/actions/moderation/members.ts +77 -0
  77. package/src/frontend/telegram/actions/moderation/permissions.ts +69 -0
  78. package/src/frontend/telegram/actions/moderation/topics.ts +48 -0
  79. package/src/frontend/telegram/actions/moderation/types.ts +16 -0
  80. package/src/frontend/telegram/actions/rich-messages.ts +51 -0
  81. package/src/frontend/telegram/actions/{shared.ts → send.ts} +9 -59
  82. package/src/frontend/telegram/admin/background.ts +66 -0
  83. package/src/frontend/telegram/admin/chunked-reply.ts +20 -0
  84. package/src/frontend/telegram/admin/health.ts +81 -0
  85. package/src/frontend/telegram/admin/sessions.ts +105 -0
  86. package/src/frontend/telegram/admin.ts +57 -262
  87. package/src/frontend/telegram/callbacks/auth.ts +1 -1
  88. package/src/frontend/telegram/callbacks/effort.ts +1 -1
  89. package/src/frontend/telegram/callbacks/index.ts +3 -3
  90. package/src/frontend/telegram/callbacks/metrics.ts +1 -1
  91. package/src/frontend/telegram/callbacks/model/backend.ts +156 -0
  92. package/src/frontend/telegram/callbacks/model/control.ts +29 -0
  93. package/src/frontend/telegram/callbacks/model/nav.ts +54 -0
  94. package/src/frontend/telegram/callbacks/model/select.ts +100 -0
  95. package/src/frontend/telegram/callbacks/model/types.ts +39 -0
  96. package/src/frontend/telegram/callbacks/model/views.ts +116 -0
  97. package/src/frontend/telegram/callbacks/model.ts +61 -331
  98. package/src/frontend/telegram/callbacks/pulse.ts +1 -1
  99. package/src/frontend/telegram/callbacks/{shared.ts → query.ts} +3 -2
  100. package/src/frontend/telegram/callbacks/settings.ts +1 -1
  101. package/src/frontend/telegram/callbacks/whatsapp.ts +1 -1
  102. package/src/frontend/telegram/commands/admin.ts +108 -77
  103. package/src/frontend/telegram/commands/index.ts +1 -1
  104. package/src/frontend/telegram/commands/settings.ts +65 -43
  105. package/src/frontend/telegram/commands/state.ts +1 -1
  106. package/src/frontend/telegram/commands/whatsapp-pairing.ts +1 -1
  107. package/src/frontend/telegram/handlers/context.ts +1 -1
  108. package/src/frontend/telegram/handlers/delivery.ts +2 -2
  109. package/src/frontend/telegram/handlers/messages.ts +1 -1
  110. package/src/frontend/telegram/handlers/queue.ts +1 -1
  111. package/src/frontend/telegram/handlers/state.ts +1 -1
  112. package/src/frontend/telegram/helpers/diagnostics.ts +1 -1
  113. package/src/frontend/telegram/index.ts +1 -1
  114. package/src/frontend/telegram/middleware.ts +167 -202
  115. package/src/frontend/telegram/model-menu.ts +1 -1
  116. package/src/frontend/terminal/builtins/context.ts +184 -0
  117. package/src/frontend/terminal/builtins/help.ts +35 -0
  118. package/src/frontend/terminal/builtins/index.ts +23 -0
  119. package/src/frontend/terminal/builtins/model.ts +198 -0
  120. package/src/frontend/terminal/builtins/session.ts +101 -0
  121. package/src/frontend/terminal/builtins/status.ts +202 -0
  122. package/src/frontend/terminal/command-registry.ts +80 -0
  123. package/src/frontend/terminal/commands.ts +11 -674
  124. package/src/frontend/terminal/index.ts +1 -1
  125. package/src/frontend/terminal/input/history.ts +64 -0
  126. package/src/frontend/terminal/input/keys.ts +143 -0
  127. package/src/frontend/terminal/input/parts.ts +33 -0
  128. package/src/frontend/terminal/input/paste.ts +53 -0
  129. package/src/frontend/terminal/input/render.ts +42 -0
  130. package/src/frontend/terminal/input/state.ts +60 -0
  131. package/src/frontend/terminal/input.ts +27 -342
  132. package/src/frontend/whatsapp/connection.ts +1 -1
  133. package/src/frontend/whatsapp/index.ts +2 -2
  134. package/src/frontend/whatsapp/pairing-service.ts +1 -1
  135. package/src/frontend/whatsapp/runtime.ts +1 -1
  136. package/src/plugins/github/provision.ts +1 -1
  137. package/src/plugins/mempalace/provision.ts +1 -1
  138. package/src/plugins/playwright/provision.ts +1 -1
  139. package/src/storage/memory.ts +505 -0
  140. package/src/storage/repositories/memory-repo.ts +308 -0
  141. package/src/storage/sql/memory.sql +95 -0
  142. package/src/storage/sql/schema.sql +81 -0
  143. package/src/storage/sql/statements.generated.ts +146 -0
  144. package/src/frontend/telegram/actions/moderation.ts +0 -255
  145. /package/src/core/{doctor-types.ts → doctor/types.ts} +0 -0
  146. /package/src/core/{pairing-broker.ts → frontend-runtime/pairing-broker.ts} +0 -0
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  import type { Backend } from "../agent-runtime/capabilities.js";
22
- import type { TalonConfig } from "../../util/config.js";
22
+ import type { TalonConfig } from "../config/index.js";
23
23
  import type { ReasoningEffortLevel } from "../types.js";
24
24
  import {
25
25
  normalizeReasoningLevels,
@@ -14,7 +14,7 @@
14
14
  * watches. Alerts about a dead frontend must travel over a LIVE one.
15
15
  */
16
16
 
17
- import { log, logWarn } from "./../util/log.js";
17
+ import { log, logWarn } from "../../util/log.js";
18
18
 
19
19
  let deliver: ((text: string) => Promise<void>) | null = null;
20
20
 
@@ -25,7 +25,7 @@
25
25
  */
26
26
 
27
27
  import type { ContextManager } from "../types.js";
28
- import type { TalonConfig } from "../../util/config.js";
28
+ import type { TalonConfig } from "../config/index.js";
29
29
  import type { Gateway } from "../engine/gateway.js";
30
30
  import type { FrontendDescriptor } from "./registry.js";
31
31
 
@@ -73,7 +73,7 @@ import {
73
73
  type ModelSource,
74
74
  } from "../agent-runtime/model-ref.js";
75
75
  import type { UnifiedModelInfo } from "../types.js";
76
- import type { TalonConfig } from "../../util/config.js";
76
+ import type { TalonConfig } from "../config/index.js";
77
77
  import { logWarn } from "../../util/log.js";
78
78
 
79
79
  /**
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { log, logError, logWarn } from "../../util/log.js";
7
- import type { TalonConfig } from "../../util/config.js";
7
+ import type { TalonConfig } from "../config/index.js";
8
8
  import { registry, reloadState } from "./registry.js";
9
9
  import type { ProvisionOutcome } from "./provision.js";
10
10
  import { NATIVE_RUNTIMES, type NativePluginId } from "./native-runtimes.js";
@@ -226,7 +226,7 @@ export async function reloadPlugins(
226
226
  ): Promise<{ names: string[]; config: TalonConfig }> {
227
227
  // Validate config BEFORE tearing down existing plugins. If the config is
228
228
  // malformed the error propagates and current plugins stay intact.
229
- const { loadConfig, getFrontends } = await import("../../util/config.js");
229
+ const { loadConfig, getFrontends } = await import("../config/index.js");
230
230
  const config = loadConfig();
231
231
 
232
232
  // Derive frontends from config if not explicitly provided
@@ -8,7 +8,7 @@
8
8
  * talon.json so the surface owns its own write path.
9
9
  */
10
10
 
11
- import type { TalonConfig } from "../../util/config.js";
11
+ import type { TalonConfig } from "../config/index.js";
12
12
  import { isPathPlugin, type PluginEntry } from "./types.js";
13
13
  import {
14
14
  BUILTIN_PLUGINS,
@@ -13,7 +13,7 @@
13
13
  * import-cheap for consumers that only need the id list.
14
14
  */
15
15
 
16
- import type { DoctorCheck } from "../doctor.js";
16
+ import type { DoctorCheck } from "../doctor/index.js";
17
17
  import type { ProvisionOutcome } from "./provision.js";
18
18
  import type { MempalaceSection } from "../../plugins/mempalace/provision.js";
19
19
  import type { PlaywrightSection } from "../../plugins/playwright/provision.js";
@@ -9,7 +9,7 @@
9
9
  import { readFileSync } from "node:fs";
10
10
  import { files, dirs } from "../../util/paths.js";
11
11
  import { tailFile } from "../../util/tail-file.js";
12
- import type { TalonConfig } from "../../util/config.js";
12
+ import type { TalonConfig } from "../../core/config/index.js";
13
13
  import type { Gateway } from "../../core/engine/gateway.js";
14
14
  import { resetSession, getAllSessions } from "../../storage/sessions.js";
15
15
  import { clearHistory } from "../../storage/history.js";
@@ -22,7 +22,7 @@
22
22
  */
23
23
 
24
24
  import { MessageFlags } from "discord.js";
25
- import type { TalonConfig } from "../../../../util/config.js";
25
+ import type { TalonConfig } from "../../../../core/config/index.js";
26
26
  import type { Gateway } from "../../../../core/engine/gateway.js";
27
27
  import { deriveNumericChatId } from "../../../../util/chat-id.js";
28
28
  import { logError } from "../../../../util/log.js";
@@ -15,7 +15,7 @@ import {
15
15
  StringSelectMenuBuilder,
16
16
  MessageFlags,
17
17
  } from "discord.js";
18
- import type { TalonConfig } from "../../../../util/config.js";
18
+ import type { TalonConfig } from "../../../../core/config/index.js";
19
19
  import type { Gateway } from "../../../../core/engine/gateway.js";
20
20
  import {
21
21
  getChatSettings,
@@ -12,7 +12,7 @@ import type {
12
12
  ButtonInteraction,
13
13
  StringSelectMenuInteraction,
14
14
  } from "discord.js";
15
- import type { TalonConfig } from "../../../../util/config.js";
15
+ import type { TalonConfig } from "../../../../core/config/index.js";
16
16
  import type { Gateway } from "../../../../core/engine/gateway.js";
17
17
 
18
18
  export type ComponentInteraction =
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { type ModalSubmitInteraction, MessageFlags } from "discord.js";
6
- import type { TalonConfig } from "../../../util/config.js";
6
+ import type { TalonConfig } from "../../../core/config/index.js";
7
7
  import type { Gateway } from "../../../core/engine/gateway.js";
8
8
  import { setChatPulseInterval } from "../../../storage/chat-settings.js";
9
9
  import { registerChat, enablePulse } from "../../../core/background/pulse.js";
@@ -10,7 +10,7 @@ import {
10
10
  ButtonStyle,
11
11
  MessageFlags,
12
12
  } from "discord.js";
13
- import type { TalonConfig } from "../../../util/config.js";
13
+ import type { TalonConfig } from "../../../core/config/index.js";
14
14
  import type { Gateway } from "../../../core/engine/gateway.js";
15
15
  import { respawnSelf } from "../../../util/respawn.js";
16
16
  import { forceDream } from "../../../core/background/dream.js";
@@ -19,7 +19,7 @@ import {
19
19
  renderMetricsMessages,
20
20
  renderDoctorMessages,
21
21
  } from "../render.js";
22
- import { collectDoctorReport } from "../../../core/doctor.js";
22
+ import { collectDoctorReport } from "../../../core/doctor/index.js";
23
23
  import { getSoul } from "../../../core/soul/service.js";
24
24
  import { getMetrics, getTodayMetrics } from "../../../storage/metrics.js";
25
25
  import { handleAdminSubcommand } from "../admin.js";
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import { type Client, REST, Routes, SlashCommandBuilder } from "discord.js";
16
- import type { TalonConfig } from "../../../util/config.js";
16
+ import type { TalonConfig } from "../../../core/config/index.js";
17
17
  import { log, logError, logWarn } from "../../../util/log.js";
18
18
  import { getRepoRoot } from "../../../core/update/self-update.js";
19
19
 
@@ -12,7 +12,7 @@ import {
12
12
  renderMeshReport,
13
13
  renderUsageMessage,
14
14
  } from "../render.js";
15
- import type { TalonConfig } from "../../../util/config.js";
15
+ import type { TalonConfig } from "../../../core/config/index.js";
16
16
  import { collectPlanUsage } from "../../shared/plan-usage-report.js";
17
17
  import { getLoadedPlugins } from "../../../core/plugin/index.js";
18
18
  import { getMeshService } from "../../../core/mesh/index.js";
@@ -9,7 +9,7 @@ import {
9
9
  type Interaction,
10
10
  MessageFlags,
11
11
  } from "discord.js";
12
- import type { TalonConfig } from "../../../util/config.js";
12
+ import type { TalonConfig } from "../../../core/config/index.js";
13
13
  import type { Gateway } from "../../../core/engine/gateway.js";
14
14
  import { logError } from "../../../util/log.js";
15
15
  import {
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { type ChatInputCommandInteraction, MessageFlags } from "discord.js";
9
- import type { TalonConfig } from "../../../util/config.js";
9
+ import type { TalonConfig } from "../../../core/config/index.js";
10
10
  import type { Gateway } from "../../../core/engine/gateway.js";
11
11
  import {
12
12
  formatModelLabel,
@@ -10,7 +10,7 @@ import {
10
10
  StringSelectMenuBuilder,
11
11
  MessageFlags,
12
12
  } from "discord.js";
13
- import type { TalonConfig } from "../../../util/config.js";
13
+ import type { TalonConfig } from "../../../core/config/index.js";
14
14
  import type { Gateway } from "../../../core/engine/gateway.js";
15
15
  import {
16
16
  getChatSettings,
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { TextBasedChannel } from "discord.js";
8
- import type { TalonConfig } from "../../../util/config.js";
8
+ import type { TalonConfig } from "../../../core/config/index.js";
9
9
  import { execute } from "../../../core/engine/dispatcher.js";
10
10
  import { toolInputToRecord } from "../../../core/agent-runtime/events.js";
11
11
  import { appendDailyLogResponse } from "../../../storage/daily-log.js";
@@ -6,7 +6,7 @@
6
6
 
7
7
  import type { Client, Message } from "discord.js";
8
8
  import { ChannelType } from "discord.js";
9
- import type { TalonConfig } from "../../../util/config.js";
9
+ import type { TalonConfig } from "../../../core/config/index.js";
10
10
  import { setMessageFilePath } from "../../../storage/history.js";
11
11
  import { addMedia } from "../../../storage/media-index.js";
12
12
  import { deriveNumericChatId } from "../../../util/chat-id.js";
@@ -25,7 +25,7 @@ import {
25
25
  MAX_QUEUED_PER_CHAT,
26
26
  type QueuedMessage,
27
27
  } from "./state.js";
28
- import type { TalonConfig } from "../../../util/config.js";
28
+ import type { TalonConfig } from "../../../core/config/index.js";
29
29
 
30
30
  export function enqueueMessage(
31
31
  config: TalonConfig,
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { TextBasedChannel } from "discord.js";
9
- import type { TalonConfig } from "../../../util/config.js";
9
+ import type { TalonConfig } from "../../../core/config/index.js";
10
10
  import {
11
11
  createDmUserTracker,
12
12
  createNoticeCooldown,
@@ -18,7 +18,7 @@
18
18
  * outbound, connection, and the handlers/commands/callbacks trees.
19
19
  */
20
20
 
21
- import type { TalonConfig } from "../../util/config.js";
21
+ import type { TalonConfig } from "../../core/config/index.js";
22
22
  import type { ContextManager } from "../../core/types.js";
23
23
  import type { Gateway } from "../../core/engine/gateway.js";
24
24
  import { log } from "../../util/log.js";
@@ -16,7 +16,7 @@
16
16
 
17
17
  import type { Client, Message } from "discord.js";
18
18
  import { ChannelType } from "discord.js";
19
- import type { TalonConfig } from "../../util/config.js";
19
+ import type { TalonConfig } from "../../core/config/index.js";
20
20
  import { pushMessage } from "../../storage/history.js";
21
21
  import { registerChat } from "../../core/background/pulse.js";
22
22
  import { deriveNumericChatId } from "../../util/chat-id.js";
@@ -14,7 +14,7 @@ import {
14
14
  DISCORD_SAFE_RESERVE,
15
15
  splitMessage,
16
16
  } from "./formatting.js";
17
- import type { DoctorReport } from "../../core/doctor.js";
17
+ import type { DoctorReport } from "../../core/doctor/index.js";
18
18
  import type { MeshPingResult } from "../../core/mesh/service.js";
19
19
  import type { BackendUsageEntry } from "../shared/plan-usage-report.js";
20
20
  import {
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { Client, GatewayIntentBits, Partials } from "discord.js";
12
- import type { TalonConfig } from "../../util/config.js";
12
+ import type { TalonConfig } from "../../core/config/index.js";
13
13
  import type { Gateway } from "../../core/engine/gateway.js";
14
14
 
15
15
  /** `config.discord`, narrowed once — the factory refuses to build without it. */
@@ -23,7 +23,10 @@ import { getPluginPromptAdditions } from "../../core/plugin/index.js";
23
23
  import { listPluginItems, setPluginEnabled } from "../../core/plugin/manage.js";
24
24
  import { notifyPromptInputsChanged } from "../../core/prompt/invalidation.js";
25
25
  import { listSkills, setSkillEnabled } from "../../storage/skills.js";
26
- import { rebuildSystemPrompt, type TalonConfig } from "../../util/config.js";
26
+ import {
27
+ rebuildSystemPrompt,
28
+ type TalonConfig,
29
+ } from "../../core/config/index.js";
27
30
  import { log } from "../../util/log.js";
28
31
  import type { PluginItem, SkillItem, ToggleResult } from "./protocol.js";
29
32
  import { persistConfigPatch } from "./settings.js";
@@ -14,7 +14,7 @@
14
14
  * delegate to — chat-wire, context, emit, turn, models, history, ….
15
15
  */
16
16
 
17
- import type { TalonConfig } from "../../util/config.js";
17
+ import type { TalonConfig } from "../../core/config/index.js";
18
18
  import type { ContextManager } from "../../core/types.js";
19
19
  import type { Gateway } from "../../core/engine/gateway.js";
20
20
  import { log, logError } from "../../util/log.js";
@@ -8,7 +8,7 @@
8
8
  * message-id minter and the broadcast sink; the modules own the behaviour.
9
9
  */
10
10
 
11
- import type { TalonConfig } from "../../util/config.js";
11
+ import type { TalonConfig } from "../../core/config/index.js";
12
12
  import type { Gateway } from "../../core/engine/gateway.js";
13
13
  import { getMeshService, type MeshService } from "../../core/mesh/index.js";
14
14
  import { NativeChats } from "./chats.js";
@@ -14,7 +14,7 @@
14
14
  import { existsSync, readFileSync, mkdirSync } from "node:fs";
15
15
  import writeFileAtomic from "write-file-atomic";
16
16
  import { dirname } from "node:path";
17
- import type { TalonConfig } from "../../util/config.js";
17
+ import type { TalonConfig } from "../../core/config/index.js";
18
18
  import { files as pathFiles } from "../../util/paths.js";
19
19
  import { setTimezone } from "../../util/time.js";
20
20
  import { resolveModel } from "../../core/models/catalog.js";
@@ -10,7 +10,7 @@
10
10
  * about a socket, a bot API, or a keyboard.
11
11
  */
12
12
 
13
- import type { TalonConfig } from "../../util/config.js";
13
+ import type { TalonConfig } from "../../core/config/index.js";
14
14
  import type { Backend } from "../../core/agent-runtime/capabilities.js";
15
15
  import type { ReasoningEffortLevel } from "../../core/types.js";
16
16
  import {
@@ -9,7 +9,7 @@
9
9
  * never silence.
10
10
  */
11
11
 
12
- import type { TalonConfig } from "../../util/config.js";
12
+ import type { TalonConfig } from "../../core/config/index.js";
13
13
  import type { PlanUsage } from "../../core/agent-runtime/capabilities.js";
14
14
  import {
15
15
  listAvailableBackends,
@@ -4,7 +4,7 @@ import type {
4
4
  } from "../../core/types.js";
5
5
  import type { Backend } from "../../core/agent-runtime/capabilities.js";
6
6
  import { resolveActiveModelForChat } from "../../core/models/active-model.js";
7
- import type { TalonConfig } from "../../util/config.js";
7
+ import type { TalonConfig } from "../../core/config/index.js";
8
8
  import {
9
9
  normalizeReasoningLevels,
10
10
  supportsReasoningLevel,
@@ -9,7 +9,7 @@
9
9
  * markup.
10
10
  */
11
11
 
12
- import type { TalonConfig } from "../../util/config.js";
12
+ import type { TalonConfig } from "../../core/config/index.js";
13
13
  import type { Backend } from "../../core/agent-runtime/capabilities.js";
14
14
  import {
15
15
  resetSession,
@@ -12,7 +12,7 @@
12
12
  * chat-discovery, poll, commands, turn, outbound.
13
13
  */
14
14
 
15
- import type { TalonConfig } from "../../util/config.js";
15
+ import type { TalonConfig } from "../../core/config/index.js";
16
16
  import type { ContextManager } from "../../core/types.js";
17
17
  import type { Gateway } from "../../core/engine/gateway.js";
18
18
  import { log } from "../../util/log.js";
@@ -7,7 +7,7 @@
7
7
  * poll, commands, turn, outbound) takes as its first parameter.
8
8
  */
9
9
 
10
- import type { TalonConfig } from "../../util/config.js";
10
+ import type { TalonConfig } from "../../core/config/index.js";
11
11
  import type { Gateway } from "../../core/engine/gateway.js";
12
12
  import type { GraphClient } from "./graph.js";
13
13
 
@@ -22,7 +22,7 @@ import {
22
22
  getOnlineCount as userbotOnlineCount,
23
23
  } from "../userbot.js";
24
24
  import { savePackToLibrary } from "../sticker-library.js";
25
- import { toPositiveId } from "./shared.js";
25
+ import { toPositiveId } from "./coerce.js";
26
26
  import type { TelegramActionHandlers } from "./types.js";
27
27
 
28
28
  export const chatInfoHandlers: TelegramActionHandlers = {
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Id coercion for the Telegram action handlers.
3
+ */
4
+
5
+ /**
6
+ * Telegram/GramJS APIs expect numeric IDs. `snowflakeOrIdSchema` normalizes IDs
7
+ * to digit-strings (so 17-19 digit Discord snowflakes survive validation), so a
8
+ * Telegram message/reply/offset ID can arrive here as a string. Coerce it back
9
+ * to a positive integer: Telegram IDs are well within 2^53, so this is lossless.
10
+ * Returns undefined for missing / non-positive / non-integer values.
11
+ */
12
+ export function toPositiveId(v: unknown): number | undefined {
13
+ const n = typeof v === "number" ? v : typeof v === "string" ? Number(v) : NaN;
14
+ return Number.isInteger(n) && n > 0 ? n : undefined;
15
+ }
@@ -22,10 +22,10 @@ import type { ActionResult } from "../../../core/types.js";
22
22
  import { messagingHandlers, restoreScheduledMessages } from "./messaging.js";
23
23
  import { mediaHandlers } from "./media.js";
24
24
  import { chatInfoHandlers } from "./chat-info.js";
25
- import { moderationHandlers } from "./moderation.js";
25
+ import { moderationHandlers } from "./moderation/index.js";
26
26
  import type { TelegramActionContext, TelegramActionHandlers } from "./types.js";
27
27
 
28
- export { sendText } from "./shared.js";
28
+ export { sendText } from "./send.js";
29
29
 
30
30
  // Null-prototype so a request `action` of "toString" / "constructor" / etc.
31
31
  // can't resolve an inherited Object.prototype method via `handlers[action]`.
@@ -9,7 +9,7 @@ import { expandFsPath } from "../../../util/fs-path.js";
9
9
  import { markdownToTelegramHtml } from "../formatting.js";
10
10
  import { withRetry } from "../../../core/engine/gateway.js";
11
11
  import { resolveStickerByEmoji } from "../sticker-library.js";
12
- import { replyParams, sendOpts } from "./shared.js";
12
+ import { replyParams, sendOpts } from "./send.js";
13
13
  import type { TelegramActionHandlers } from "./types.js";
14
14
 
15
15
  type MediaSource = {
@@ -15,13 +15,12 @@ import {
15
15
  MAX_OVERDUE_MS,
16
16
  type ScheduledMessage,
17
17
  } from "../../../storage/scheduled.js";
18
+ import { sendOpts, sendText } from "./send.js";
18
19
  import {
19
20
  noteRichMessageFailure,
20
21
  richMessagesAvailable,
21
- sendOpts,
22
- sendText,
23
- toPositiveId,
24
- } from "./shared.js";
22
+ } from "./rich-messages.js";
23
+ import { toPositiveId } from "./coerce.js";
25
24
  import { resolveThreadId } from "../topics.js";
26
25
  import { TELEGRAM_MAX_TEXT, type TelegramActionHandlers } from "./types.js";
27
26
 
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Chat-level ops — permissions, invite links, chat photo, unpin_all,
3
+ * leave_chat.
4
+ */
5
+
6
+ import { toPositiveId } from "../coerce.js";
7
+ import { resolveMediaInput } from "../media.js";
8
+ import { toChatPermissions } from "./permissions.js";
9
+ import type { ModerationOps } from "./types.js";
10
+
11
+ export const chatOps: ModerationOps = {
12
+ set_permissions: async ({ body, chatId, ctx: { bot } }) => {
13
+ const raw = body.permissions;
14
+ if (!raw || typeof raw !== "object")
15
+ return { ok: false, error: "set_permissions: permissions required" };
16
+ await bot.api.setChatPermissions(
17
+ chatId,
18
+ toChatPermissions(raw as Record<string, boolean>),
19
+ );
20
+ return { ok: true };
21
+ },
22
+ create_invite_link: async ({ body, chatId, ctx: { bot } }) => {
23
+ const expireMin = Number(body.expire_minutes);
24
+ const link = await bot.api.createChatInviteLink(chatId, {
25
+ name: body.title ? String(body.title) : undefined,
26
+ expire_date:
27
+ Number.isFinite(expireMin) && expireMin > 0
28
+ ? Math.floor(Date.now() / 1000) + Math.round(expireMin * 60)
29
+ : undefined,
30
+ member_limit: toPositiveId(body.member_limit),
31
+ });
32
+ return { ok: true, invite_link: link.invite_link };
33
+ },
34
+ revoke_invite_link: async ({ body, chatId, ctx: { bot } }) => {
35
+ if (!body.link)
36
+ return { ok: false, error: "revoke_invite_link: link required" };
37
+ await bot.api.revokeChatInviteLink(chatId, String(body.link));
38
+ return { ok: true };
39
+ },
40
+ set_chat_photo: async ({ body, chatId, ctx: { bot, InputFileClass } }) => {
41
+ const resolved = resolveMediaInput(body, "set_chat_photo", InputFileClass);
42
+ if ("error" in resolved) return { ok: false, error: resolved.error };
43
+ if (typeof resolved.file === "string")
44
+ return {
45
+ ok: false,
46
+ error: "set_chat_photo needs a local file_path (no url/file_id)",
47
+ };
48
+ await bot.api.setChatPhoto(chatId, resolved.file);
49
+ return { ok: true };
50
+ },
51
+ delete_chat_photo: async ({ chatId, ctx: { bot } }) => {
52
+ await bot.api.deleteChatPhoto(chatId);
53
+ return { ok: true };
54
+ },
55
+ unpin_all: async ({ chatId, ctx: { bot } }) => {
56
+ await bot.api.unpinAllChatMessages(chatId);
57
+ return { ok: true };
58
+ },
59
+ leave_chat: async ({ chatId, ctx: { bot } }) => {
60
+ await bot.api.leaveChat(chatId);
61
+ return { ok: true };
62
+ },
63
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Moderation actions — the `moderate` tool's member/chat/topic operations,
3
+ * plus profile-photo lookup.
4
+ *
5
+ * Handlers call the Bot API directly and let Telegram's own errors surface
6
+ * (missing admin rights come back as descriptive 400s the model can read);
7
+ * pre-checking rights here would just race the real check.
8
+ *
9
+ * `moderate` dispatches on `body.op` through the table below:
10
+ * - `members` — ban / unban / mute / unmute / promote / demote /
11
+ * set_admin_title / *_join_request(s)
12
+ * - `chat` — set_permissions / *_invite_link / *_chat_photo /
13
+ * unpin_all / leave_chat
14
+ * - `topics` — create / edit / close / reopen / delete_topic
15
+ */
16
+
17
+ import { toPositiveId } from "../coerce.js";
18
+ import type { TelegramActionHandlers } from "../types.js";
19
+ import { memberOps } from "./members.js";
20
+ import { chatOps } from "./chat.js";
21
+ import { topicOps } from "./topics.js";
22
+ import type { ModerationOps } from "./types.js";
23
+
24
+ // Null-prototype so an `op` of "toString" / "constructor" / etc. can't
25
+ // resolve an inherited Object.prototype method via `ops[op]`.
26
+ export const MODERATION_OPS: ModerationOps = Object.assign(
27
+ Object.create(null),
28
+ {
29
+ ...memberOps,
30
+ ...chatOps,
31
+ ...topicOps,
32
+ },
33
+ );
34
+
35
+ /** Ops that fail up front without a `user_id`. */
36
+ export const OPS_NEEDING_USER = [
37
+ "ban",
38
+ "unban",
39
+ "mute",
40
+ "unmute",
41
+ "promote",
42
+ "demote",
43
+ "set_admin_title",
44
+ "approve_join_request",
45
+ "decline_join_request",
46
+ ];
47
+
48
+ export const moderationHandlers: TelegramActionHandlers = {
49
+ moderate: async (body, chatId, ctx) => {
50
+ const op = String(body.op ?? "");
51
+ const userId = toPositiveId(body.user_id);
52
+ if (OPS_NEEDING_USER.includes(op) && userId === undefined)
53
+ return { ok: false, error: `${op}: user_id is required` };
54
+ const run = MODERATION_OPS[op];
55
+ if (!run) return { ok: false, error: `Unknown moderation op: ${op}` };
56
+ return run({ op, body, chatId, ctx, userId });
57
+ },
58
+
59
+ get_user_profile_photos: async (body, _chatId, { bot }) => {
60
+ const userId = toPositiveId(body.user_id);
61
+ if (userId === undefined)
62
+ return { ok: false, error: "user_id is required" };
63
+ const limit = toPositiveId(body.limit) ?? 5;
64
+ const photos = await bot.api.getUserProfilePhotos(userId, {
65
+ limit: Math.min(limit, 100),
66
+ });
67
+ return {
68
+ ok: true,
69
+ total: photos.total_count,
70
+ // Largest size of each photo; its file_id sends/downloads like media.
71
+ file_ids: photos.photos.map((sizes) => sizes[sizes.length - 1].file_id),
72
+ };
73
+ },
74
+ };