talon-agent 4.3.0 → 4.3.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.
- package/package.json +1 -1
- package/src/app.ts +1 -1
- package/src/bootstrap.ts +5 -2
- package/src/cli/skill.ts +2 -2
- package/src/core/background/cron-spec.ts +1 -1
- package/src/core/background/cron.ts +3 -5
- package/src/core/background/heartbeat/agent.ts +1 -1
- package/src/core/background/triggers/command.ts +1 -1
- package/src/core/background/triggers/exit.ts +1 -1
- package/src/core/background/triggers/output.ts +1 -1
- package/src/core/background/triggers/resume.ts +1 -1
- package/src/core/background/triggers/spawn.ts +1 -1
- package/src/core/engine/gateway-actions/cron.ts +18 -18
- package/src/core/engine/gateway-actions/goals.ts +10 -10
- package/src/core/engine/gateway-actions/history.ts +10 -17
- package/src/core/engine/gateway-actions/index.ts +9 -2
- package/src/core/engine/gateway-actions/models.ts +6 -6
- package/src/core/engine/gateway-actions/plugins.ts +2 -8
- package/src/core/engine/gateway-actions/scripts.ts +1 -1
- package/src/core/engine/gateway-actions/skills.ts +1 -1
- package/src/core/engine/gateway-actions/triggers.ts +18 -27
- package/src/core/engine/gateway-actions/types.ts +11 -1
- package/src/core/engine/gateway-actions/{shared.ts → validation.ts} +5 -6
- package/src/core/engine/gateway.ts +12 -2
- package/src/core/prompt/assemble.ts +2 -2
- package/src/core/scripts/runner.ts +1 -1
- package/src/core/soul/forgetting.ts +2 -2
- package/src/core/soul/salience.ts +8 -4
- package/src/core/weaver/loom.ts +11 -0
- package/src/frontend/discord/actions/{shared.ts → buttons.ts} +3 -56
- package/src/frontend/discord/actions/channels.ts +34 -0
- package/src/frontend/discord/actions/chat-info.ts +1 -1
- package/src/frontend/discord/actions/index.ts +1 -1
- package/src/frontend/discord/actions/media.ts +1 -1
- package/src/frontend/discord/actions/messaging.ts +4 -2
- package/src/frontend/discord/actions/try-action.ts +24 -0
- package/src/frontend/discord/admin.ts +2 -2
- package/src/frontend/discord/callbacks/components/settings.ts +1 -1
- package/src/frontend/discord/callbacks/modals.ts +1 -1
- package/src/frontend/discord/commands/admin.ts +2 -2
- package/src/frontend/discord/commands/info.ts +2 -2
- package/src/frontend/discord/commands/{shared.ts → interaction.ts} +2 -1
- package/src/frontend/discord/commands/router.ts +1 -1
- package/src/frontend/discord/commands/session.ts +2 -2
- package/src/frontend/discord/commands/settings.ts +2 -2
- package/src/frontend/discord/{helpers.ts → render.ts} +2 -1
- package/src/frontend/native/extensions.ts +1 -1
- package/src/frontend/teams/chat-discovery.ts +69 -0
- package/src/frontend/teams/commands.ts +141 -0
- package/src/frontend/teams/index.ts +24 -350
- package/src/frontend/teams/outbound.ts +44 -0
- package/src/frontend/teams/poll.ts +101 -0
- package/src/frontend/teams/runtime.ts +51 -0
- package/src/frontend/teams/turn.ts +97 -0
- package/src/frontend/telegram/actions/messaging.ts +1 -1
- package/src/frontend/telegram/actions/shared.ts +4 -4
- package/src/frontend/telegram/admin.ts +1 -1
- package/src/frontend/telegram/sticker-library.ts +2 -2
- package/src/frontend/whatsapp/actions/account.ts +3 -6
- package/src/frontend/whatsapp/actions/chat-info.ts +2 -1
- package/src/frontend/whatsapp/actions/coerce.ts +30 -0
- package/src/frontend/whatsapp/actions/history.ts +1 -1
- package/src/frontend/whatsapp/actions/media-upload.ts +82 -0
- package/src/frontend/whatsapp/actions/media.ts +3 -8
- package/src/frontend/whatsapp/actions/messaging.ts +2 -7
- package/src/frontend/whatsapp/actions/moderation.ts +2 -1
- package/src/frontend/whatsapp/actions/{shared.ts → send.ts} +4 -124
- package/src/frontend/whatsapp/actions/try-action.ts +24 -0
- package/src/frontend/whatsapp/commands.ts +1 -1
- package/src/frontend/whatsapp/inbound.ts +1 -1
- package/src/frontend/whatsapp/index.ts +1 -1
- package/src/storage/repositories/cron-repo.ts +1 -1
- package/src/storage/repositories/goals-repo.ts +1 -1
- package/src/storage/repositories/scripts-repo.ts +1 -1
- package/src/storage/repositories/triggers-repo.ts +1 -1
- package/src/storage/sql/schema.sql +1 -1
- package/src/storage/sql/statements.generated.ts +2 -2
- package/src/storage/sql/triggers.sql +1 -1
- package/src/util/chat-id.ts +13 -0
- /package/src/storage/{cron-store.ts → cron.ts} +0 -0
- /package/src/storage/{goal-store.ts → goals.ts} +0 -0
- /package/src/storage/{scheduled-store.ts → scheduled.ts} +0 -0
- /package/src/storage/{script-store.ts → scripts.ts} +0 -0
- /package/src/storage/{skill-store.ts → skills.ts} +0 -0
- /package/src/storage/{sticker-store.ts → stickers.ts} +0 -0
- /package/src/storage/{trigger-store.ts → triggers.ts} +0 -0
|
@@ -21,17 +21,17 @@ import {
|
|
|
21
21
|
DEFAULT_TIMEOUT_SECONDS,
|
|
22
22
|
MAX_ACTIVE_PER_CHAT,
|
|
23
23
|
type TriggerLanguage,
|
|
24
|
-
} from "../../../storage/
|
|
24
|
+
} from "../../../storage/triggers.js";
|
|
25
25
|
import {
|
|
26
26
|
cancelTrigger,
|
|
27
27
|
spawnTrigger,
|
|
28
28
|
} from "../../background/triggers/index.js";
|
|
29
29
|
import { log } from "../../../util/log.js";
|
|
30
|
-
import { validateJobModelOverride } from "./
|
|
30
|
+
import { validateJobModelOverride } from "./validation.js";
|
|
31
31
|
import type { SharedActionHandlers } from "./types.js";
|
|
32
32
|
|
|
33
33
|
export const triggerHandlers: SharedActionHandlers = {
|
|
34
|
-
trigger_create: async (body, chatId) => {
|
|
34
|
+
trigger_create: async (body, chatId, _backend, chatKey) => {
|
|
35
35
|
const name = String(body.name ?? "").trim();
|
|
36
36
|
const language = body.language;
|
|
37
37
|
const script = String(body.script ?? "");
|
|
@@ -55,14 +55,13 @@ export const triggerHandlers: SharedActionHandlers = {
|
|
|
55
55
|
const timeoutErr = validateTimeout(timeoutSeconds);
|
|
56
56
|
if (timeoutErr) return { ok: false, error: timeoutErr };
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
if (getTriggerByName(chatIdStr, name)) {
|
|
58
|
+
if (getTriggerByName(chatKey, name)) {
|
|
60
59
|
return {
|
|
61
60
|
ok: false,
|
|
62
61
|
error: `A trigger named "${name}" already exists in this chat. Cancel it first or pick a different name.`,
|
|
63
62
|
};
|
|
64
63
|
}
|
|
65
|
-
const active = getActiveTriggersForChat(
|
|
64
|
+
const active = getActiveTriggersForChat(chatKey);
|
|
66
65
|
if (active.length >= MAX_ACTIVE_PER_CHAT) {
|
|
67
66
|
return {
|
|
68
67
|
ok: false,
|
|
@@ -70,18 +69,10 @@ export const triggerHandlers: SharedActionHandlers = {
|
|
|
70
69
|
};
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
const numericChatId = Number(chatId);
|
|
74
|
-
if (!Number.isFinite(numericChatId)) {
|
|
75
|
-
return {
|
|
76
|
-
ok: false,
|
|
77
|
-
error: `Cannot derive numeric chatId from ${chatId}`,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
72
|
// Validate the model up front so a bad id is rejected here instead of
|
|
82
73
|
// silently failing at fire time.
|
|
83
74
|
if (model) {
|
|
84
|
-
const modelErr = await validateJobModelOverride(
|
|
75
|
+
const modelErr = await validateJobModelOverride(chatKey, model);
|
|
85
76
|
if (modelErr) return { ok: false, error: modelErr };
|
|
86
77
|
}
|
|
87
78
|
|
|
@@ -89,19 +80,19 @@ export const triggerHandlers: SharedActionHandlers = {
|
|
|
89
80
|
const lang = language as TriggerLanguage;
|
|
90
81
|
let scriptPath: string;
|
|
91
82
|
try {
|
|
92
|
-
scriptPath = writeScriptFile(
|
|
83
|
+
scriptPath = writeScriptFile(chatKey, id, lang, script);
|
|
93
84
|
} catch (err) {
|
|
94
85
|
return {
|
|
95
86
|
ok: false,
|
|
96
87
|
error: `Failed to write script: ${err instanceof Error ? err.message : err}`,
|
|
97
88
|
};
|
|
98
89
|
}
|
|
99
|
-
const logPath = triggerLogPath(
|
|
90
|
+
const logPath = triggerLogPath(chatKey, id);
|
|
100
91
|
|
|
101
92
|
const trigger = {
|
|
102
93
|
id,
|
|
103
|
-
chatId:
|
|
104
|
-
numericChatId,
|
|
94
|
+
chatId: chatKey,
|
|
95
|
+
numericChatId: chatId,
|
|
105
96
|
name,
|
|
106
97
|
language: lang,
|
|
107
98
|
scriptPath,
|
|
@@ -151,8 +142,8 @@ export const triggerHandlers: SharedActionHandlers = {
|
|
|
151
142
|
};
|
|
152
143
|
},
|
|
153
144
|
|
|
154
|
-
trigger_list: (body, chatId) => {
|
|
155
|
-
const triggers = getTriggersForChat(
|
|
145
|
+
trigger_list: (body, chatId, _backend, chatKey) => {
|
|
146
|
+
const triggers = getTriggersForChat(chatKey);
|
|
156
147
|
if (triggers.length === 0)
|
|
157
148
|
return { ok: true, text: "No triggers in this chat." };
|
|
158
149
|
const lines = triggers.map((t) => {
|
|
@@ -181,12 +172,12 @@ export const triggerHandlers: SharedActionHandlers = {
|
|
|
181
172
|
};
|
|
182
173
|
},
|
|
183
174
|
|
|
184
|
-
trigger_cancel: (body, chatId) => {
|
|
175
|
+
trigger_cancel: (body, chatId, _backend, chatKey) => {
|
|
185
176
|
const triggerId = String(body.trigger_id ?? "");
|
|
186
177
|
if (!triggerId) return { ok: false, error: "Missing trigger_id" };
|
|
187
178
|
const t = getTrigger(triggerId);
|
|
188
179
|
if (!t) return { ok: false, error: `Trigger ${triggerId} not found` };
|
|
189
|
-
if (t.chatId !==
|
|
180
|
+
if (t.chatId !== chatKey)
|
|
190
181
|
return { ok: false, error: "Trigger belongs to a different chat" };
|
|
191
182
|
const wasRunning = cancelTrigger(triggerId);
|
|
192
183
|
if (!wasRunning) {
|
|
@@ -201,12 +192,12 @@ export const triggerHandlers: SharedActionHandlers = {
|
|
|
201
192
|
};
|
|
202
193
|
},
|
|
203
194
|
|
|
204
|
-
trigger_logs: (body, chatId) => {
|
|
195
|
+
trigger_logs: (body, chatId, _backend, chatKey) => {
|
|
205
196
|
const triggerId = String(body.trigger_id ?? "");
|
|
206
197
|
if (!triggerId) return { ok: false, error: "Missing trigger_id" };
|
|
207
198
|
const t = getTrigger(triggerId);
|
|
208
199
|
if (!t) return { ok: false, error: `Trigger ${triggerId} not found` };
|
|
209
|
-
if (t.chatId !==
|
|
200
|
+
if (t.chatId !== chatKey)
|
|
210
201
|
return { ok: false, error: "Trigger belongs to a different chat" };
|
|
211
202
|
const lines = Math.min(500, Math.max(1, Number(body.lines ?? 80)));
|
|
212
203
|
const { tail, truncated } = readTriggerLogTail(t.logPath, lines);
|
|
@@ -219,12 +210,12 @@ export const triggerHandlers: SharedActionHandlers = {
|
|
|
219
210
|
};
|
|
220
211
|
},
|
|
221
212
|
|
|
222
|
-
trigger_delete: (body, chatId) => {
|
|
213
|
+
trigger_delete: (body, chatId, _backend, chatKey) => {
|
|
223
214
|
const triggerId = String(body.trigger_id ?? "");
|
|
224
215
|
if (!triggerId) return { ok: false, error: "Missing trigger_id" };
|
|
225
216
|
const t = getTrigger(triggerId);
|
|
226
217
|
if (!t) return { ok: false, error: `Trigger ${triggerId} not found` };
|
|
227
|
-
if (t.chatId !==
|
|
218
|
+
if (t.chatId !== chatKey)
|
|
228
219
|
return { ok: false, error: "Trigger belongs to a different chat" };
|
|
229
220
|
// Cancel first if it's still running so we don't orphan a child
|
|
230
221
|
cancelTrigger(triggerId);
|
|
@@ -9,10 +9,20 @@
|
|
|
9
9
|
import type { ActionResult } from "../../types.js";
|
|
10
10
|
import type { Backend } from "../../agent-runtime/capabilities.js";
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* `chatId` is the frontend's numeric id — the only identity the HTTP bridge
|
|
14
|
+
* carries. `chatKey` is the chat's canonical string id (the dispatcher's
|
|
15
|
+
* `chatId`, which every store is keyed on): for Telegram it is just
|
|
16
|
+
* `String(chatId)`, for every other frontend it is the real id the numeric
|
|
17
|
+
* one was derived from (`d_…`, `wa_…`, `discord_…`). Handlers that persist,
|
|
18
|
+
* look up, or route by chat must use `chatKey`, never `String(chatId)` —
|
|
19
|
+
* a `d_…` chat's jobs stored under its numeric id fire into Telegram.
|
|
20
|
+
*/
|
|
12
21
|
type SharedActionHandler = (
|
|
13
22
|
body: Record<string, unknown>,
|
|
14
23
|
chatId: number,
|
|
15
|
-
backend
|
|
24
|
+
backend: Backend | null | undefined,
|
|
25
|
+
chatKey: string,
|
|
16
26
|
) => Promise<ActionResult> | ActionResult;
|
|
17
27
|
|
|
18
28
|
export type SharedActionHandlers = Record<string, SharedActionHandler>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* model-override validation.
|
|
2
|
+
* Field validation shared by the goal, cron and trigger actions: due-date
|
|
3
|
+
* parsing and per-job model-override validation.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { resolveExplicitModelRef } from "../../models/active-model.js";
|
|
@@ -30,15 +30,14 @@ export function parseDueDate(value: unknown): number | undefined {
|
|
|
30
30
|
* when it isn't, or `null` when it's valid.
|
|
31
31
|
*/
|
|
32
32
|
export async function validateJobModelOverride(
|
|
33
|
-
|
|
33
|
+
chatKey: string,
|
|
34
34
|
model: string,
|
|
35
35
|
): Promise<string | null> {
|
|
36
36
|
try {
|
|
37
|
-
const chatIdStr = String(chatId);
|
|
38
37
|
const ref = await resolveExplicitModelRef(
|
|
39
38
|
model,
|
|
40
|
-
getBackendForChat(
|
|
41
|
-
getBackendIdForChat(
|
|
39
|
+
getBackendForChat(chatKey),
|
|
40
|
+
getBackendIdForChat(chatKey),
|
|
42
41
|
);
|
|
43
42
|
if (!ref) {
|
|
44
43
|
return (
|
|
@@ -307,6 +307,11 @@ export class Gateway {
|
|
|
307
307
|
const action = typeof body.action === "string" ? body.action : "";
|
|
308
308
|
if (!action) return { ok: false, error: "Missing action" };
|
|
309
309
|
const t0 = Date.now();
|
|
310
|
+
// The canonical string id for this chat. The bridge only carries the
|
|
311
|
+
// numeric id, but the turn's Thread is keyed by the dispatcher's string
|
|
312
|
+
// chatId — hand that to the handlers so a `d_…`/`wa_…` chat's cron jobs,
|
|
313
|
+
// triggers and history land under the id the rest of the engine uses.
|
|
314
|
+
const chatKey = this.loom.stringIdForNumeric(chatId) ?? String(chatId);
|
|
310
315
|
|
|
311
316
|
try {
|
|
312
317
|
// Try frontend first — it has richer implementations (e.g. userbot history)
|
|
@@ -326,7 +331,7 @@ export class Gateway {
|
|
|
326
331
|
}
|
|
327
332
|
|
|
328
333
|
// Try plugin actions (loaded from external plugin packages)
|
|
329
|
-
const pluginResult = await handlePluginAction(body,
|
|
334
|
+
const pluginResult = await handlePluginAction(body, chatKey);
|
|
330
335
|
if (pluginResult) {
|
|
331
336
|
logDebug(
|
|
332
337
|
"gateway",
|
|
@@ -336,7 +341,12 @@ export class Gateway {
|
|
|
336
341
|
}
|
|
337
342
|
|
|
338
343
|
// Shared actions last — provides in-memory fallbacks for history, cron, etc.
|
|
339
|
-
const shared = await handleSharedAction(
|
|
344
|
+
const shared = await handleSharedAction(
|
|
345
|
+
body,
|
|
346
|
+
chatId,
|
|
347
|
+
this.backend,
|
|
348
|
+
chatKey,
|
|
349
|
+
);
|
|
340
350
|
if (shared) {
|
|
341
351
|
logDebug(
|
|
342
352
|
"gateway",
|
|
@@ -63,8 +63,8 @@ import { log } from "../../util/log.js";
|
|
|
63
63
|
import { loadSystemTemplate } from "./templates.js";
|
|
64
64
|
import { renderMemoryView } from "./memory-view.js";
|
|
65
65
|
import { renderWorkspaceListing } from "./workspace-listing.js";
|
|
66
|
-
import { renderSkillsPrompt } from "../../storage/
|
|
67
|
-
import { renderStickerLibraryPrompt } from "../../storage/
|
|
66
|
+
import { renderSkillsPrompt } from "../../storage/skills.js";
|
|
67
|
+
import { renderStickerLibraryPrompt } from "../../storage/stickers.js";
|
|
68
68
|
import { getSoul } from "../soul/service.js";
|
|
69
69
|
|
|
70
70
|
// ── Types ───────────────────────────────────────────────────────────────────
|
|
@@ -16,7 +16,7 @@ import { spawn } from "node:child_process";
|
|
|
16
16
|
import { dirs } from "../../util/paths.js";
|
|
17
17
|
import { log } from "../../util/log.js";
|
|
18
18
|
import { commandForLanguage } from "../background/triggers/index.js";
|
|
19
|
-
import type { Script } from "../../storage/
|
|
19
|
+
import type { Script } from "../../storage/scripts.js";
|
|
20
20
|
|
|
21
21
|
export const DEFAULT_SCRIPT_TIMEOUT_SECONDS = 60;
|
|
22
22
|
const MAX_SCRIPT_TIMEOUT_SECONDS = 300;
|
|
@@ -93,7 +93,7 @@ export function effectiveStrength(
|
|
|
93
93
|
);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
export interface
|
|
96
|
+
export interface FsrsReinforceOptions {
|
|
97
97
|
readonly now: number;
|
|
98
98
|
readonly cfg: SoulConfig;
|
|
99
99
|
readonly amount: number;
|
|
@@ -111,7 +111,7 @@ export interface FsrsReinforceOpts {
|
|
|
111
111
|
*/
|
|
112
112
|
export function reinforceFsrs(
|
|
113
113
|
state: ActivationState,
|
|
114
|
-
opts:
|
|
114
|
+
opts: FsrsReinforceOptions,
|
|
115
115
|
): ActivationState {
|
|
116
116
|
if (!Number.isFinite(halfLifeForKind(opts.cfg, opts.kind))) {
|
|
117
117
|
state.salience += opts.amount;
|
|
@@ -60,7 +60,7 @@ export function confidence(state: ActivationState): number {
|
|
|
60
60
|
return Math.tanh(state.evidence / 4);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export interface
|
|
63
|
+
export interface ReinforceOptions {
|
|
64
64
|
readonly now: number;
|
|
65
65
|
readonly halfLifeMs: number;
|
|
66
66
|
/** Salience increment for this activation (>= 0). */
|
|
@@ -74,7 +74,11 @@ export interface ReinforceOpts {
|
|
|
74
74
|
* first (so the additive bump composes correctly with time), then adds the
|
|
75
75
|
* increment and accumulates signed evidence.
|
|
76
76
|
*/
|
|
77
|
-
export function reinforce(
|
|
77
|
+
export function reinforce(
|
|
78
|
+
dag: SoulDag,
|
|
79
|
+
hash: Hash,
|
|
80
|
+
opts: ReinforceOptions,
|
|
81
|
+
): void {
|
|
78
82
|
const s = dag.stateOf(hash);
|
|
79
83
|
s.salience = effectiveSalience(s, opts.now, opts.halfLifeMs) + opts.amount;
|
|
80
84
|
s.evidence += opts.valence;
|
|
@@ -111,7 +115,7 @@ export function coactivate(
|
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
117
|
|
|
114
|
-
export interface
|
|
118
|
+
export interface PredictionErrorOptions {
|
|
115
119
|
readonly now: number;
|
|
116
120
|
readonly halfLifeMs: number;
|
|
117
121
|
/** How strongly the observed signal corrects the prediction, in (0, 1]. */
|
|
@@ -129,7 +133,7 @@ export function applyPredictionError(
|
|
|
129
133
|
dag: SoulDag,
|
|
130
134
|
hash: Hash,
|
|
131
135
|
observed: number,
|
|
132
|
-
opts:
|
|
136
|
+
opts: PredictionErrorOptions,
|
|
133
137
|
): number {
|
|
134
138
|
const s = dag.stateOf(hash);
|
|
135
139
|
const predicted = effectiveSalience(s, opts.now, opts.halfLifeMs);
|
package/src/core/weaver/loom.ts
CHANGED
|
@@ -22,6 +22,8 @@ export interface ContextRegistry {
|
|
|
22
22
|
activeContextCount(): number;
|
|
23
23
|
/** Resolve a string chat id to the numeric id of its active context. */
|
|
24
24
|
numericForStringId(stringId: string): number | null;
|
|
25
|
+
/** Resolve a numeric id to the string chat id of its active context. */
|
|
26
|
+
stringIdForNumeric(numericChatId: number): string | null;
|
|
25
27
|
/** Number of live Threads in the registry. */
|
|
26
28
|
size(): number;
|
|
27
29
|
}
|
|
@@ -146,6 +148,15 @@ export class Loom implements ContextRegistry {
|
|
|
146
148
|
return thread?.contextActive ? (thread.numericChatId ?? null) : null;
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Resolve a numeric id to the string chat id of its active context — the
|
|
153
|
+
* Thread key, which is the dispatcher's `chatId` (see `acquireContext`).
|
|
154
|
+
* Null outside a turn: the numeric index only mirrors held contexts.
|
|
155
|
+
*/
|
|
156
|
+
stringIdForNumeric(numericChatId: number): string | null {
|
|
157
|
+
return this.byNumeric.get(numericChatId)?.chatId ?? null;
|
|
158
|
+
}
|
|
159
|
+
|
|
149
160
|
private resolve(id: number | string): Thread | undefined {
|
|
150
161
|
if (typeof id === "number") return this.byNumeric.get(id);
|
|
151
162
|
const direct = this.threads.get(id);
|
|
@@ -1,63 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Button-row construction for model-authored inline buttons (Discord
|
|
3
|
+
* components), with custom_id namespacing and de-duplication.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
type Client,
|
|
8
|
-
type TextBasedChannel,
|
|
9
|
-
ActionRowBuilder,
|
|
10
|
-
ButtonBuilder,
|
|
11
|
-
ButtonStyle,
|
|
12
|
-
} from "discord.js";
|
|
13
|
-
import type { ActionResult } from "../../../core/types.js";
|
|
14
|
-
import { lookupDiscordChat } from "../handlers/index.js";
|
|
6
|
+
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
|
|
15
7
|
import { safeSlice } from "../formatting.js";
|
|
16
|
-
import { logWarn } from "../../../util/log.js";
|
|
17
|
-
import { mapDiscordError } from "../errors.js";
|
|
18
|
-
|
|
19
|
-
/** Run a Discord action; convert DiscordAPIError into a clean ActionResult. */
|
|
20
|
-
export async function tryAction(
|
|
21
|
-
context: string,
|
|
22
|
-
fn: () => Promise<ActionResult>,
|
|
23
|
-
): Promise<ActionResult> {
|
|
24
|
-
try {
|
|
25
|
-
return await fn();
|
|
26
|
-
} catch (err) {
|
|
27
|
-
const mapped = mapDiscordError(err, context);
|
|
28
|
-
if (mapped) return mapped;
|
|
29
|
-
return {
|
|
30
|
-
ok: false,
|
|
31
|
-
error: `${context} failed: ${err instanceof Error ? err.message : err}`,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function resolveChannel(
|
|
37
|
-
client: Client,
|
|
38
|
-
numericChatId: number,
|
|
39
|
-
): Promise<TextBasedChannel | null> {
|
|
40
|
-
const info = lookupDiscordChat(numericChatId);
|
|
41
|
-
if (!info) return null;
|
|
42
|
-
try {
|
|
43
|
-
const ch = await client.channels.fetch(info.channelId);
|
|
44
|
-
if (ch && "send" in ch && (ch as TextBasedChannel).isSendable?.()) {
|
|
45
|
-
return ch as TextBasedChannel;
|
|
46
|
-
}
|
|
47
|
-
if (info.userId) {
|
|
48
|
-
const user = await client.users.fetch(info.userId);
|
|
49
|
-
const dm = await user.createDM();
|
|
50
|
-
return dm as TextBasedChannel;
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
53
|
-
} catch (err) {
|
|
54
|
-
logWarn(
|
|
55
|
-
"discord",
|
|
56
|
-
`resolveChannel failed for chat ${numericChatId}: ${err instanceof Error ? err.message : err}`,
|
|
57
|
-
);
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
8
|
|
|
62
9
|
export function buildButtonRows(
|
|
63
10
|
rows: Array<
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a registered Discord chat to a sendable channel (guild channel or
|
|
3
|
+
* the user's DM).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Client, TextBasedChannel } from "discord.js";
|
|
7
|
+
import { lookupDiscordChat } from "../handlers/index.js";
|
|
8
|
+
import { logWarn } from "../../../util/log.js";
|
|
9
|
+
|
|
10
|
+
export async function resolveChannel(
|
|
11
|
+
client: Client,
|
|
12
|
+
numericChatId: number,
|
|
13
|
+
): Promise<TextBasedChannel | null> {
|
|
14
|
+
const info = lookupDiscordChat(numericChatId);
|
|
15
|
+
if (!info) return null;
|
|
16
|
+
try {
|
|
17
|
+
const ch = await client.channels.fetch(info.channelId);
|
|
18
|
+
if (ch && "send" in ch && (ch as TextBasedChannel).isSendable?.()) {
|
|
19
|
+
return ch as TextBasedChannel;
|
|
20
|
+
}
|
|
21
|
+
if (info.userId) {
|
|
22
|
+
const user = await client.users.fetch(info.userId);
|
|
23
|
+
const dm = await user.createDM();
|
|
24
|
+
return dm as TextBasedChannel;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
logWarn(
|
|
29
|
+
"discord",
|
|
30
|
+
`resolveChannel failed for chat ${numericChatId}: ${err instanceof Error ? err.message : err}`,
|
|
31
|
+
);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { ChannelType } from "discord.js";
|
|
8
8
|
import { logWarn } from "../../../util/log.js";
|
|
9
|
-
import { tryAction } from "./
|
|
9
|
+
import { tryAction } from "./try-action.js";
|
|
10
10
|
import type { DiscordActionHandlers } from "./types.js";
|
|
11
11
|
|
|
12
12
|
export const chatInfoHandlers: DiscordActionHandlers = {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import type { Client } from "discord.js";
|
|
24
24
|
import type { Gateway } from "../../../core/engine/gateway.js";
|
|
25
25
|
import type { ActionResult } from "../../../core/types.js";
|
|
26
|
-
import { resolveChannel } from "./
|
|
26
|
+
import { resolveChannel } from "./channels.js";
|
|
27
27
|
import { messagingHandlers, restoreScheduledMessages } from "./messaging.js";
|
|
28
28
|
import { mediaHandlers } from "./media.js";
|
|
29
29
|
import { chatInfoHandlers } from "./chat-info.js";
|
|
@@ -11,7 +11,7 @@ import { withRetry } from "../../../core/engine/gateway.js";
|
|
|
11
11
|
import { sendChunked } from "../handlers/index.js";
|
|
12
12
|
import { suppressMentions, DISCORD_MAX_TEXT } from "../formatting.js";
|
|
13
13
|
import { maxAttachmentBytes } from "../errors.js";
|
|
14
|
-
import { tryAction } from "./
|
|
14
|
+
import { tryAction } from "./try-action.js";
|
|
15
15
|
import type { DiscordActionHandlers } from "./types.js";
|
|
16
16
|
|
|
17
17
|
const sendAttachment: DiscordActionHandlers[string] = (
|
|
@@ -19,8 +19,10 @@ import {
|
|
|
19
19
|
listScheduledForChat,
|
|
20
20
|
MAX_OVERDUE_MS,
|
|
21
21
|
type ScheduledMessage,
|
|
22
|
-
} from "../../../storage/scheduled
|
|
23
|
-
import { tryAction
|
|
22
|
+
} from "../../../storage/scheduled.js";
|
|
23
|
+
import { tryAction } from "./try-action.js";
|
|
24
|
+
import { resolveChannel } from "./channels.js";
|
|
25
|
+
import { buildButtonRows } from "./buttons.js";
|
|
24
26
|
import type { DiscordActionHandlers } from "./types.js";
|
|
25
27
|
|
|
26
28
|
// ── Scheduled sends (persistent) ─────────────────────────────────────────────
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run a Discord action with uniform error mapping: DiscordAPIError becomes a
|
|
3
|
+
* clean ActionResult, anything else a labelled failure.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ActionResult } from "../../../core/types.js";
|
|
7
|
+
import { mapDiscordError } from "../errors.js";
|
|
8
|
+
|
|
9
|
+
/** Run a Discord action; convert DiscordAPIError into a clean ActionResult. */
|
|
10
|
+
export async function tryAction(
|
|
11
|
+
context: string,
|
|
12
|
+
fn: () => Promise<ActionResult>,
|
|
13
|
+
): Promise<ActionResult> {
|
|
14
|
+
try {
|
|
15
|
+
return await fn();
|
|
16
|
+
} catch (err) {
|
|
17
|
+
const mapped = mapDiscordError(err, context);
|
|
18
|
+
if (mapped) return mapped;
|
|
19
|
+
return {
|
|
20
|
+
ok: false,
|
|
21
|
+
error: `${context} failed: ${err instanceof Error ? err.message : err}`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -19,11 +19,11 @@ import {
|
|
|
19
19
|
getAllCronJobs,
|
|
20
20
|
describeSchedule,
|
|
21
21
|
nextRunAt,
|
|
22
|
-
} from "../../storage/cron
|
|
22
|
+
} from "../../storage/cron.js";
|
|
23
23
|
import { getActiveCount } from "../../core/engine/dispatcher.js";
|
|
24
24
|
import { getPulseStatus } from "../../core/background/pulse.js";
|
|
25
25
|
import { getHealthStatus, getRecentErrors } from "../../util/watchdog.js";
|
|
26
|
-
import { formatDuration, formatModelLabel } from "./
|
|
26
|
+
import { formatDuration, formatModelLabel } from "./render.js";
|
|
27
27
|
import {
|
|
28
28
|
DISCORD_MAX_TEXT,
|
|
29
29
|
DISCORD_SAFE_RESERVE,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
enablePulse,
|
|
32
32
|
isPulseEnabled,
|
|
33
33
|
} from "../../../../core/background/pulse.js";
|
|
34
|
-
import { renderSettingsText, EFFORT_DESCRIPTIONS } from "../../
|
|
34
|
+
import { renderSettingsText, EFFORT_DESCRIPTIONS } from "../../render.js";
|
|
35
35
|
import {
|
|
36
36
|
getBackendIdForChat,
|
|
37
37
|
resolveChatBackend,
|
|
@@ -8,7 +8,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
10
|
import { isInteractionAllowed } from "../handlers/index.js";
|
|
11
|
-
import { parseInterval, formatDuration } from "../
|
|
11
|
+
import { parseInterval, formatDuration } from "../render.js";
|
|
12
12
|
import { logError } from "../../../util/log.js";
|
|
13
13
|
|
|
14
14
|
export async function handleModalSubmit(
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
formatDuration,
|
|
19
19
|
renderMetricsMessages,
|
|
20
20
|
renderDoctorMessages,
|
|
21
|
-
} from "../
|
|
21
|
+
} from "../render.js";
|
|
22
22
|
import { collectDoctorReport } from "../../../core/doctor.js";
|
|
23
23
|
import { getSoul } from "../../../core/soul/service.js";
|
|
24
24
|
import { getMetrics, getTodayMetrics } from "../../../storage/metrics.js";
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
getRepoRoot,
|
|
35
35
|
runSelfUpdate,
|
|
36
36
|
} from "../../../core/update/self-update.js";
|
|
37
|
-
import { reply } from "./
|
|
37
|
+
import { reply } from "./interaction.js";
|
|
38
38
|
|
|
39
39
|
export async function handleRestart(
|
|
40
40
|
i: ChatInputCommandInteraction,
|
|
@@ -11,13 +11,13 @@ import {
|
|
|
11
11
|
formatDuration,
|
|
12
12
|
renderMeshReport,
|
|
13
13
|
renderUsageMessage,
|
|
14
|
-
} from "../
|
|
14
|
+
} from "../render.js";
|
|
15
15
|
import type { TalonConfig } from "../../../util/config.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";
|
|
19
19
|
import type { MeshPingResult } from "../../../core/mesh/service.js";
|
|
20
|
-
import { reply } from "./
|
|
20
|
+
import { reply } from "./interaction.js";
|
|
21
21
|
|
|
22
22
|
export async function handleStart(
|
|
23
23
|
i: ChatInputCommandInteraction,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Interaction accessors for the Discord slash-command handlers + router:
|
|
3
|
+
* chat-id derivation, chunked reply, client.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import { type ChatInputCommandInteraction, MessageFlags } from "discord.js";
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
handleComponentInteraction,
|
|
22
22
|
handleModalSubmit,
|
|
23
23
|
} from "../callbacks/index.js";
|
|
24
|
-
import { chatIdFromInteraction, reply, client } from "./
|
|
24
|
+
import { chatIdFromInteraction, reply, client } from "./interaction.js";
|
|
25
25
|
import {
|
|
26
26
|
handleStart,
|
|
27
27
|
handleHelp,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
formatTokenCount,
|
|
15
15
|
formatBytes,
|
|
16
16
|
formatUsd,
|
|
17
|
-
} from "../
|
|
17
|
+
} from "../render.js";
|
|
18
18
|
import {
|
|
19
19
|
getBackendIdForChat,
|
|
20
20
|
resolveChatBackend,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
collectSessionStatus,
|
|
25
25
|
} from "../../shared/session-status.js";
|
|
26
26
|
import { stopCurrentTurn } from "../../../core/engine/dispatcher.js";
|
|
27
|
-
import { reply } from "./
|
|
27
|
+
import { reply } from "./interaction.js";
|
|
28
28
|
|
|
29
29
|
export async function handleReset(
|
|
30
30
|
i: ChatInputCommandInteraction,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
parseInterval,
|
|
34
34
|
renderSettingsText,
|
|
35
35
|
EFFORT_DESCRIPTIONS,
|
|
36
|
-
} from "../
|
|
36
|
+
} from "../render.js";
|
|
37
37
|
import {
|
|
38
38
|
displayReasoningEffort,
|
|
39
39
|
getActiveReasoningLevels,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
} from "../../../core/engine/backend-controller/index.js";
|
|
46
46
|
import { resolveActiveModelForChat } from "../../../core/models/active-model.js";
|
|
47
47
|
import { safeSlice } from "../formatting.js";
|
|
48
|
-
import { reply } from "./
|
|
48
|
+
import { reply } from "./interaction.js";
|
|
49
49
|
import {
|
|
50
50
|
buildModelPickerView,
|
|
51
51
|
MODEL_NAV_PREFIX,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Message rendering for Discord commands, callbacks, and the settings panel:
|
|
3
|
+
* metrics, doctor, mesh, usage and settings text, sized to Discord's limits.
|
|
3
4
|
*
|
|
4
5
|
* Discord-specific quirks vs the Telegram helpers:
|
|
5
6
|
* - settings panel uses Components (Buttons + Select Menus), not inline keyboard.
|
|
@@ -22,7 +22,7 @@ import { performPluginReload } from "../../core/engine/gateway-actions/plugins.j
|
|
|
22
22
|
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
|
-
import { listSkills, setSkillEnabled } from "../../storage/
|
|
25
|
+
import { listSkills, setSkillEnabled } from "../../storage/skills.js";
|
|
26
26
|
import { rebuildSystemPrompt, type TalonConfig } from "../../util/config.js";
|
|
27
27
|
import { log } from "../../util/log.js";
|
|
28
28
|
import type { PluginItem, SkillItem, ToggleResult } from "./protocol.js";
|