talon-agent 4.3.3 → 4.5.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.
- package/package.json +1 -1
- package/src/app.ts +1 -1
- package/src/backend/claude-sdk/state.ts +1 -1
- package/src/backend/codex/init.ts +1 -1
- package/src/backend/codex/state.ts +1 -1
- package/src/backend/openai-agents/init.ts +1 -1
- package/src/backend/openai-agents/state.ts +1 -1
- package/src/backend/remote-server/factory.ts +1 -1
- package/src/backend/remote-server/mcp.ts +1 -1
- package/src/backend/remote-server/server-bindings.ts +1 -1
- package/src/backend/remote-server/state.ts +1 -1
- package/src/backend/shared/system-prompt.ts +1 -1
- package/src/bootstrap.ts +2 -2
- package/src/cli/chat.ts +1 -1
- package/src/cli/config.ts +2 -2
- package/src/cli/index.ts +8 -0
- package/src/cli/memory.ts +197 -0
- package/src/cli/setup.ts +1 -1
- package/src/core/agent-runtime/backend-registry.ts +1 -1
- package/src/core/agent-runtime/model-ref.ts +2 -2
- package/src/{util/config.ts → core/config/index.ts} +8 -8
- package/src/core/engine/backend-controller/legacy.ts +1 -1
- package/src/core/engine/backend-controller/pool.ts +1 -1
- package/src/core/engine/backend-controller/rebind.ts +1 -1
- package/src/core/engine/backend-controller/state.ts +1 -1
- package/src/core/engine/gateway-actions/plugins.ts +1 -1
- package/src/core/engine/model-audit.ts +1 -1
- package/src/core/frontend-runtime/capabilities.ts +1 -1
- package/src/core/memory/import.ts +380 -0
- package/src/core/memory/render.ts +243 -0
- package/src/core/models/active-model.ts +1 -1
- package/src/core/plugin/builtins.ts +2 -2
- package/src/core/plugin/manage.ts +1 -1
- package/src/core/prompt/memory-view.ts +8 -8
- package/src/frontend/discord/admin.ts +1 -1
- package/src/frontend/discord/callbacks/components/index.ts +1 -1
- package/src/frontend/discord/callbacks/components/settings.ts +1 -1
- package/src/frontend/discord/callbacks/components/types.ts +1 -1
- package/src/frontend/discord/callbacks/modals.ts +1 -1
- package/src/frontend/discord/commands/admin.ts +1 -1
- package/src/frontend/discord/commands/definitions.ts +1 -1
- package/src/frontend/discord/commands/info.ts +1 -1
- package/src/frontend/discord/commands/router.ts +1 -1
- package/src/frontend/discord/commands/session.ts +1 -1
- package/src/frontend/discord/commands/settings.ts +1 -1
- package/src/frontend/discord/handlers/delivery.ts +1 -1
- package/src/frontend/discord/handlers/messages.ts +1 -1
- package/src/frontend/discord/handlers/queue.ts +1 -1
- package/src/frontend/discord/handlers/state.ts +1 -1
- package/src/frontend/discord/index.ts +1 -1
- package/src/frontend/discord/middleware.ts +1 -1
- package/src/frontend/discord/runtime.ts +1 -1
- package/src/frontend/native/extensions.ts +4 -1
- package/src/frontend/native/handlers.ts +5 -0
- package/src/frontend/native/index.ts +1 -1
- package/src/frontend/native/memory.ts +112 -0
- package/src/frontend/native/protocol.ts +45 -0
- package/src/frontend/native/routes/host.ts +9 -0
- package/src/frontend/native/routes/index.ts +2 -0
- package/src/frontend/native/routes/memory.ts +34 -0
- package/src/frontend/native/routes/table.ts +7 -0
- package/src/frontend/native/runtime.ts +1 -1
- package/src/frontend/native/settings.ts +1 -1
- package/src/frontend/shared/model-commands.ts +1 -1
- package/src/frontend/shared/plan-usage-report.ts +1 -1
- package/src/frontend/shared/reasoning-levels.ts +1 -1
- package/src/frontend/shared/session-status.ts +1 -1
- package/src/frontend/teams/index.ts +1 -1
- package/src/frontend/teams/runtime.ts +1 -1
- package/src/frontend/telegram/actions/chat-info.ts +1 -1
- package/src/frontend/telegram/actions/coerce.ts +15 -0
- package/src/frontend/telegram/actions/index.ts +1 -1
- package/src/frontend/telegram/actions/media.ts +1 -1
- package/src/frontend/telegram/actions/messaging.ts +3 -4
- package/src/frontend/telegram/actions/moderation/chat.ts +1 -1
- package/src/frontend/telegram/actions/moderation/index.ts +1 -1
- package/src/frontend/telegram/actions/moderation/topics.ts +1 -1
- package/src/frontend/telegram/actions/rich-messages.ts +51 -0
- package/src/frontend/telegram/actions/{shared.ts → send.ts} +9 -59
- package/src/frontend/telegram/admin/sessions.ts +1 -1
- package/src/frontend/telegram/admin.ts +1 -1
- package/src/frontend/telegram/callbacks/auth.ts +1 -1
- package/src/frontend/telegram/callbacks/effort.ts +1 -1
- package/src/frontend/telegram/callbacks/index.ts +3 -3
- package/src/frontend/telegram/callbacks/metrics.ts +1 -1
- package/src/frontend/telegram/callbacks/model/backend.ts +1 -1
- package/src/frontend/telegram/callbacks/model/control.ts +1 -1
- package/src/frontend/telegram/callbacks/model/select.ts +1 -1
- package/src/frontend/telegram/callbacks/model/types.ts +1 -1
- package/src/frontend/telegram/callbacks/model/views.ts +1 -1
- package/src/frontend/telegram/callbacks/model.ts +1 -1
- package/src/frontend/telegram/callbacks/pulse.ts +1 -1
- package/src/frontend/telegram/callbacks/{shared.ts → query.ts} +3 -2
- package/src/frontend/telegram/callbacks/settings.ts +1 -1
- package/src/frontend/telegram/callbacks/whatsapp.ts +1 -1
- package/src/frontend/telegram/commands/admin.ts +1 -1
- package/src/frontend/telegram/commands/definitions.ts +4 -0
- package/src/frontend/telegram/commands/index.ts +4 -1
- package/src/frontend/telegram/commands/info.ts +3 -0
- package/src/frontend/telegram/commands/memory.ts +112 -0
- package/src/frontend/telegram/commands/state.ts +1 -1
- package/src/frontend/telegram/handlers/context.ts +1 -1
- package/src/frontend/telegram/handlers/delivery.ts +2 -2
- package/src/frontend/telegram/handlers/messages.ts +1 -1
- package/src/frontend/telegram/handlers/queue.ts +1 -1
- package/src/frontend/telegram/handlers/state.ts +1 -1
- package/src/frontend/telegram/index.ts +1 -1
- package/src/frontend/telegram/middleware.ts +1 -1
- package/src/frontend/telegram/model-menu.ts +1 -1
- package/src/frontend/terminal/command-registry.ts +1 -1
- package/src/frontend/terminal/index.ts +1 -1
- package/src/frontend/whatsapp/index.ts +1 -1
- package/src/frontend/whatsapp/runtime.ts +1 -1
- package/src/storage/memory.ts +513 -0
- package/src/storage/repositories/memory-repo.ts +308 -0
- package/src/storage/sql/memory.sql +95 -0
- package/src/storage/sql/schema.sql +81 -0
- package/src/storage/sql/statements.generated.ts +146 -0
|
@@ -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 "./
|
|
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
|
+
}
|
|
@@ -25,7 +25,7 @@ import { chatInfoHandlers } from "./chat-info.js";
|
|
|
25
25
|
import { moderationHandlers } from "./moderation/index.js";
|
|
26
26
|
import type { TelegramActionContext, TelegramActionHandlers } from "./types.js";
|
|
27
27
|
|
|
28
|
-
export { sendText } from "./
|
|
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 "./
|
|
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
|
-
|
|
22
|
-
|
|
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
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* leave_chat.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { toPositiveId } from "../
|
|
6
|
+
import { toPositiveId } from "../coerce.js";
|
|
7
7
|
import { resolveMediaInput } from "../media.js";
|
|
8
8
|
import { toChatPermissions } from "./permissions.js";
|
|
9
9
|
import type { ModerationOps } from "./types.js";
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - `topics` — create / edit / close / reopen / delete_topic
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { toPositiveId } from "../
|
|
17
|
+
import { toPositiveId } from "../coerce.js";
|
|
18
18
|
import type { TelegramActionHandlers } from "../types.js";
|
|
19
19
|
import { memberOps } from "./members.js";
|
|
20
20
|
import { chatOps } from "./chat.js";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { Bot } from "grammy";
|
|
6
|
-
import { toPositiveId } from "../
|
|
6
|
+
import { toPositiveId } from "../coerce.js";
|
|
7
7
|
import type { ModerationOp, ModerationOps } from "./types.js";
|
|
8
8
|
|
|
9
9
|
/** An op on an existing topic: validates `thread_id`, then runs `call`. */
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rich Messages capability probe — whether native Rich Markdown delivery is
|
|
3
|
+
* worth attempting on this Bot API server, latched off on the first
|
|
4
|
+
* "method not found".
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { logWarn } from "../../../util/log.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Rich Messages arrived in Bot API 10.2. A self-hosted Bot API server (or a
|
|
11
|
+
* deployment pinned to an older release) rejects `sendRichMessage` outright,
|
|
12
|
+
* and retrying it per message would cost a doomed round-trip plus a warning
|
|
13
|
+
* line on every single send. Probe once and latch, mirroring the
|
|
14
|
+
* `draftsSupported` capability probe in `handlers/delivery.ts`.
|
|
15
|
+
*/
|
|
16
|
+
let richMessagesSupported = true;
|
|
17
|
+
|
|
18
|
+
/** Telegram's shape for "this build doesn't have that method". */
|
|
19
|
+
const METHOD_UNAVAILABLE_RE =
|
|
20
|
+
/method not found|not supported|unknown method|unsupported method/i;
|
|
21
|
+
|
|
22
|
+
/** True while native Rich Markdown delivery is still worth attempting. */
|
|
23
|
+
export function richMessagesAvailable(): boolean {
|
|
24
|
+
return richMessagesSupported;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Log a failed Rich Message call and latch the capability off when the failure
|
|
29
|
+
* says the method itself is missing. Payload-level rejections (a markdown
|
|
30
|
+
* string Telegram won't parse) stay one-off — the next message may be fine.
|
|
31
|
+
*/
|
|
32
|
+
export function noteRichMessageFailure(err: unknown, context: string): void {
|
|
33
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
34
|
+
if (METHOD_UNAVAILABLE_RE.test(msg)) {
|
|
35
|
+
richMessagesSupported = false;
|
|
36
|
+
logWarn(
|
|
37
|
+
"bot",
|
|
38
|
+
`Rich Messages unavailable on this Bot API server — using legacy HTML from now on (${context}): ${msg}`,
|
|
39
|
+
);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
logWarn(
|
|
43
|
+
"bot",
|
|
44
|
+
`Rich Markdown failed; falling back to HTML (${context}): ${msg}`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Test seam: re-arm the capability probe between cases. */
|
|
49
|
+
export function resetRichMessageSupport(): void {
|
|
50
|
+
richMessagesSupported = true;
|
|
51
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* native Rich Markdown
|
|
2
|
+
* Outbound Telegram sends: reply-parameter extraction, delivery modifiers,
|
|
3
|
+
* and text delivery with native Rich Markdown and legacy HTML / plain-text
|
|
4
|
+
* fallbacks.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import type { Bot } from "grammy";
|
|
@@ -8,6 +9,11 @@ import { markdownToTelegramHtml } from "../formatting.js";
|
|
|
8
9
|
import { logWarn } from "../../../util/log.js";
|
|
9
10
|
import { ambientThreadId, resolveThreadId } from "../topics.js";
|
|
10
11
|
import { TELEGRAM_MAX_TEXT } from "./types.js";
|
|
12
|
+
import { toPositiveId } from "./coerce.js";
|
|
13
|
+
import {
|
|
14
|
+
noteRichMessageFailure,
|
|
15
|
+
richMessagesAvailable,
|
|
16
|
+
} from "./rich-messages.js";
|
|
11
17
|
|
|
12
18
|
export function replyParams(
|
|
13
19
|
body: Record<string, unknown>,
|
|
@@ -62,62 +68,6 @@ export function sendOpts(
|
|
|
62
68
|
};
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
/**
|
|
66
|
-
* Telegram/GramJS APIs expect numeric IDs. `snowflakeOrIdSchema` normalizes IDs
|
|
67
|
-
* to digit-strings (so 17-19 digit Discord snowflakes survive validation), so a
|
|
68
|
-
* Telegram message/reply/offset ID can arrive here as a string. Coerce it back
|
|
69
|
-
* to a positive integer: Telegram IDs are well within 2^53, so this is lossless.
|
|
70
|
-
* Returns undefined for missing / non-positive / non-integer values.
|
|
71
|
-
*/
|
|
72
|
-
export function toPositiveId(v: unknown): number | undefined {
|
|
73
|
-
const n = typeof v === "number" ? v : typeof v === "string" ? Number(v) : NaN;
|
|
74
|
-
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Rich Messages arrived in Bot API 10.2. A self-hosted Bot API server (or a
|
|
79
|
-
* deployment pinned to an older release) rejects `sendRichMessage` outright,
|
|
80
|
-
* and retrying it per message would cost a doomed round-trip plus a warning
|
|
81
|
-
* line on every single send. Probe once and latch, mirroring the
|
|
82
|
-
* `draftsSupported` capability probe in `handlers/delivery.ts`.
|
|
83
|
-
*/
|
|
84
|
-
let richMessagesSupported = true;
|
|
85
|
-
|
|
86
|
-
/** Telegram's shape for "this build doesn't have that method". */
|
|
87
|
-
const METHOD_UNAVAILABLE_RE =
|
|
88
|
-
/method not found|not supported|unknown method|unsupported method/i;
|
|
89
|
-
|
|
90
|
-
/** True while native Rich Markdown delivery is still worth attempting. */
|
|
91
|
-
export function richMessagesAvailable(): boolean {
|
|
92
|
-
return richMessagesSupported;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Log a failed Rich Message call and latch the capability off when the failure
|
|
97
|
-
* says the method itself is missing. Payload-level rejections (a markdown
|
|
98
|
-
* string Telegram won't parse) stay one-off — the next message may be fine.
|
|
99
|
-
*/
|
|
100
|
-
export function noteRichMessageFailure(err: unknown, context: string): void {
|
|
101
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
102
|
-
if (METHOD_UNAVAILABLE_RE.test(msg)) {
|
|
103
|
-
richMessagesSupported = false;
|
|
104
|
-
logWarn(
|
|
105
|
-
"bot",
|
|
106
|
-
`Rich Messages unavailable on this Bot API server — using legacy HTML from now on (${context}): ${msg}`,
|
|
107
|
-
);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
logWarn(
|
|
111
|
-
"bot",
|
|
112
|
-
`Rich Markdown failed; falling back to HTML (${context}): ${msg}`,
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** Test seam: re-arm the capability probe between cases. */
|
|
117
|
-
export function resetRichMessageSupport(): void {
|
|
118
|
-
richMessagesSupported = true;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
71
|
export async function sendText(
|
|
122
72
|
bot: Bot,
|
|
123
73
|
chatId: number,
|
|
@@ -141,7 +91,7 @@ export async function sendText(
|
|
|
141
91
|
message_thread_id: ambientThreadId(chatId),
|
|
142
92
|
};
|
|
143
93
|
|
|
144
|
-
if (
|
|
94
|
+
if (richMessagesAvailable()) {
|
|
145
95
|
try {
|
|
146
96
|
const sent = await bot.api.sendRichMessage(
|
|
147
97
|
chatId,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { Bot, Context } from "grammy";
|
|
6
|
-
import type { TalonConfig } from "../../../
|
|
6
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
7
7
|
import { escapeHtml } from "../formatting.js";
|
|
8
8
|
import { resetSession, getAllSessions } from "../../../storage/sessions.js";
|
|
9
9
|
import { clearHistory } from "../../../storage/history.js";
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { Bot, Context } from "grammy";
|
|
12
|
-
import type { TalonConfig } from "../../
|
|
12
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
13
13
|
import { replyActiveChats, broadcast, killSession } from "./admin/sessions.js";
|
|
14
14
|
import {
|
|
15
15
|
replyStats,
|
|
@@ -12,7 +12,7 @@ import { activeLoginFlow } from "../../../core/auth/login-flow.js";
|
|
|
12
12
|
import { currentAuthPanel, driveLogin, isAuthProvider } from "../auth-panel.js";
|
|
13
13
|
import { loginBinariesFrom } from "../commands/auth.js";
|
|
14
14
|
import { isAuthorizedAdmin } from "../commands/state.js";
|
|
15
|
-
import { answerCallbackQuerySafe, type CallbackDeps } from "./
|
|
15
|
+
import { answerCallbackQuerySafe, type CallbackDeps } from "./query.js";
|
|
16
16
|
|
|
17
17
|
export async function handleAuthCallback(
|
|
18
18
|
ctx: Context,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
getActiveReasoningLevels,
|
|
17
17
|
supportsReasoningLevel,
|
|
18
18
|
} from "../../shared/reasoning-levels.js";
|
|
19
|
-
import { answerCallbackQuerySafe, type CallbackDeps } from "./
|
|
19
|
+
import { answerCallbackQuerySafe, type CallbackDeps } from "./query.js";
|
|
20
20
|
|
|
21
21
|
export async function handleEffortCallback(
|
|
22
22
|
ctx: Context,
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import type { Bot } from "grammy";
|
|
21
|
-
import type { TalonConfig } from "../../../
|
|
21
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
22
22
|
import type { Backend } from "../../../core/agent-runtime/capabilities.js";
|
|
23
23
|
import { handleCallbackQuery } from "../handlers/index.js";
|
|
24
|
-
import type { CallbackDeps } from "./
|
|
24
|
+
import type { CallbackDeps } from "./query.js";
|
|
25
25
|
import { handleSettingsCallback } from "./settings.js";
|
|
26
26
|
import { handlePulseCallback } from "./pulse.js";
|
|
27
27
|
import { handleEffortCallback } from "./effort.js";
|
|
@@ -30,7 +30,7 @@ import { handleModelCallback } from "./model.js";
|
|
|
30
30
|
import { handleAuthCallback } from "./auth.js";
|
|
31
31
|
import { handleWhatsAppCallback } from "./whatsapp.js";
|
|
32
32
|
|
|
33
|
-
export { answerCallbackQuerySafe } from "./
|
|
33
|
+
export { answerCallbackQuerySafe } from "./query.js";
|
|
34
34
|
|
|
35
35
|
export function registerCallbacks(
|
|
36
36
|
bot: Bot,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type MetricsView,
|
|
15
15
|
} from "../helpers/index.js";
|
|
16
16
|
import { isAuthorizedAdmin } from "../commands/state.js";
|
|
17
|
-
import { answerCallbackQuerySafe, editOrIgnoreSame } from "./
|
|
17
|
+
import { answerCallbackQuerySafe, editOrIgnoreSame } from "./query.js";
|
|
18
18
|
|
|
19
19
|
export async function handleMetricsCallback(
|
|
20
20
|
ctx: Context,
|
|
@@ -19,7 +19,7 @@ import { resolveActiveModelForChat } from "../../../../core/models/active-model.
|
|
|
19
19
|
import { logWarn } from "../../../../util/log.js";
|
|
20
20
|
import type { ModelCallback } from "../../model-callbacks.js";
|
|
21
21
|
import { resolveBackendForChat } from "../../model-menu.js";
|
|
22
|
-
import { answerCallbackQuerySafe, type CallbackDeps } from "../
|
|
22
|
+
import { answerCallbackQuerySafe, type CallbackDeps } from "../query.js";
|
|
23
23
|
import type { ModelOutcome } from "./types.js";
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Context } from "grammy";
|
|
7
|
-
import { answerCallbackQuerySafe } from "../
|
|
7
|
+
import { answerCallbackQuerySafe } from "../query.js";
|
|
8
8
|
import type { ModelOutcome } from "./types.js";
|
|
9
9
|
|
|
10
10
|
// Acknowledge fast (within Telegram's 30s window) so the user
|
|
@@ -15,7 +15,7 @@ import { getBackendIdForChat } from "../../../../core/engine/backend-controller/
|
|
|
15
15
|
import { resolveActiveModelForChat } from "../../../../core/models/active-model.js";
|
|
16
16
|
import type { ModelCallback } from "../../model-callbacks.js";
|
|
17
17
|
import { resolveBackendForChat, toggleChatFreeOnly } from "../../model-menu.js";
|
|
18
|
-
import { answerCallbackQuerySafe, type CallbackDeps } from "../
|
|
18
|
+
import { answerCallbackQuerySafe, type CallbackDeps } from "../query.js";
|
|
19
19
|
import type { ModelOutcome } from "./types.js";
|
|
20
20
|
|
|
21
21
|
export async function handleSelect(
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import type { Context } from "grammy";
|
|
12
12
|
import type { ModelCallback } from "../../model-callbacks.js";
|
|
13
|
-
import type { CallbackDeps } from "../
|
|
13
|
+
import type { CallbackDeps } from "../query.js";
|
|
14
14
|
|
|
15
15
|
export type ModelView =
|
|
16
16
|
| { kind: "menu" }
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
buildModelBrowseViewForChat,
|
|
18
18
|
buildBackendMenuViewForChat,
|
|
19
19
|
} from "../../model-menu.js";
|
|
20
|
-
import { editOrIgnoreSame, type CallbackDeps } from "../
|
|
20
|
+
import { editOrIgnoreSame, type CallbackDeps } from "../query.js";
|
|
21
21
|
import type { ModelView } from "./types.js";
|
|
22
22
|
|
|
23
23
|
export async function renderModelView(
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import type { Context } from "grammy";
|
|
19
19
|
import { parseModelCallback } from "../model-callbacks.js";
|
|
20
|
-
import { answerCallbackQuerySafe, type CallbackDeps } from "./
|
|
20
|
+
import { answerCallbackQuerySafe, type CallbackDeps } from "./query.js";
|
|
21
21
|
import { handleDone, handleNoop } from "./model/control.js";
|
|
22
22
|
import { handleSelect, handleReset, handleToggleFree } from "./model/select.js";
|
|
23
23
|
import {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
enablePulse,
|
|
10
10
|
isPulseEnabled,
|
|
11
11
|
} from "../../../core/background/pulse.js";
|
|
12
|
-
import { answerCallbackQuerySafe } from "./
|
|
12
|
+
import { answerCallbackQuerySafe } from "./query.js";
|
|
13
13
|
|
|
14
14
|
export async function handlePulseCallback(
|
|
15
15
|
ctx: Context,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Callback-query plumbing shared by the Telegram callback handlers: the
|
|
3
|
+
* dependency bundle, edit-or-ignore-same, and the safe query ack.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import type { Context } from "grammy";
|
|
6
|
-
import type { TalonConfig } from "../../../
|
|
7
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
7
8
|
import type { Backend } from "../../../core/agent-runtime/capabilities.js";
|
|
8
9
|
import { logWarn } from "../../../util/log.js";
|
|
9
10
|
import type { SettingsButton } from "../helpers/index.js";
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
getActiveReasoningLevels,
|
|
29
29
|
supportsReasoningLevel,
|
|
30
30
|
} from "../../shared/reasoning-levels.js";
|
|
31
|
-
import { answerCallbackQuerySafe, type CallbackDeps } from "./
|
|
31
|
+
import { answerCallbackQuerySafe, type CallbackDeps } from "./query.js";
|
|
32
32
|
|
|
33
33
|
export async function handleSettingsCallback(
|
|
34
34
|
ctx: Context,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
whatsAppPanel,
|
|
12
12
|
} from "../commands/whatsapp-pairing.js";
|
|
13
13
|
import { isAuthorizedAdmin } from "../commands/state.js";
|
|
14
|
-
import { answerCallbackQuerySafe } from "./
|
|
14
|
+
import { answerCallbackQuerySafe } from "./query.js";
|
|
15
15
|
|
|
16
16
|
export async function handleWhatsAppCallback(
|
|
17
17
|
ctx: Context,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Bot } from "grammy";
|
|
10
|
-
import type { TalonConfig } from "../../../
|
|
10
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
11
11
|
import { respawnSelf } from "../../../util/respawn.js";
|
|
12
12
|
import { isStaleCommand } from "../stale-command.js";
|
|
13
13
|
import {
|
|
@@ -44,6 +44,10 @@ export const TELEGRAM_COMMANDS: ReadonlyArray<{
|
|
|
44
44
|
description: "Environment and native-module health",
|
|
45
45
|
},
|
|
46
46
|
{ command: "dream", description: "Force memory consolidation" },
|
|
47
|
+
{
|
|
48
|
+
command: "memory",
|
|
49
|
+
description: "What Talon remembers — list, search, why <id>",
|
|
50
|
+
},
|
|
47
51
|
{ command: "plugins", description: "List loaded plugins" },
|
|
48
52
|
{ command: "help", description: "All commands and features" },
|
|
49
53
|
];
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* - `definitions` — the TELEGRAM_COMMANDS menu (single source of truth)
|
|
6
6
|
* - `state` — shared admin-id holder + admin guard
|
|
7
7
|
* - `info` — /start /help /ping /plugins
|
|
8
|
+
* - `memory` — /memory (read-only view of the typed memory store)
|
|
8
9
|
* - `session` — /reset /status
|
|
9
10
|
* - `settings` — /model /effort /pulse /settings
|
|
10
11
|
* - `admin` — /admin /metrics /doctor /dream /soul /restart /update
|
|
@@ -16,9 +17,10 @@
|
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
import type { Bot } from "grammy";
|
|
19
|
-
import type { TalonConfig } from "../../../
|
|
20
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
20
21
|
import type { Backend } from "../../../core/agent-runtime/capabilities.js";
|
|
21
22
|
import { registerInfoCommands } from "./info.js";
|
|
23
|
+
import { registerMemoryCommand } from "./memory.js";
|
|
22
24
|
import { registerSessionCommands } from "./session.js";
|
|
23
25
|
import { registerSettingsCommands } from "./settings.js";
|
|
24
26
|
import { registerAdminCommands } from "./admin.js";
|
|
@@ -35,6 +37,7 @@ export function registerCommands(
|
|
|
35
37
|
): void {
|
|
36
38
|
const deps = { config, gateway };
|
|
37
39
|
registerInfoCommands(bot);
|
|
40
|
+
registerMemoryCommand(bot);
|
|
38
41
|
registerSessionCommands(bot, deps);
|
|
39
42
|
registerSettingsCommands(bot, deps);
|
|
40
43
|
registerWhatsAppPairingCommand(bot);
|
|
@@ -50,6 +50,9 @@ export function registerInfoCommands(bot: Bot): void {
|
|
|
50
50
|
" /metrics -- aggregate performance metrics (admin)",
|
|
51
51
|
" /doctor -- environment and native-module health (admin)",
|
|
52
52
|
" /dream -- force memory consolidation now",
|
|
53
|
+
// Entity-escaped: this whole message is sent with parse_mode HTML,
|
|
54
|
+
// so a literal <id> would be read as a tag and 400 the reply.
|
|
55
|
+
" /memory -- what Talon remembers; /memory why <id> for provenance",
|
|
53
56
|
" /ping -- health check with latency",
|
|
54
57
|
" /mesh -- ping and list companion mesh devices",
|
|
55
58
|
" /reset -- clear session and start fresh",
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/memory` — a read-only window on the typed memory store.
|
|
3
|
+
*
|
|
4
|
+
* Four shapes, all reads: the ranked listing, a full-text search, one
|
|
5
|
+
* row's provenance (`why <id>`) and a per-kind listing. Nothing here
|
|
6
|
+
* writes: asserting, superseding and dropping are the write path's job
|
|
7
|
+
* (rollout PR 6), so the operator can always ask what Talon remembers
|
|
8
|
+
* without the answer being able to change it.
|
|
9
|
+
*
|
|
10
|
+
* Gated exactly like `/status` — not at all. The store holds the
|
|
11
|
+
* operator's own memory, and a read of it is the least privileged thing
|
|
12
|
+
* a chat can do.
|
|
13
|
+
*
|
|
14
|
+
* Every line is model- or user-authored text reaching an HTML-parsed
|
|
15
|
+
* send, so it goes through `escapeHtml` before it is joined; the reply
|
|
16
|
+
* is chunked because a listing of 15 rows can outgrow Telegram's
|
|
17
|
+
* 4096-char cap on its own.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { Bot, Context } from "grammy";
|
|
21
|
+
import { escapeHtml } from "../formatting.js";
|
|
22
|
+
import { replyHtmlChunked } from "../admin/chunked-reply.js";
|
|
23
|
+
import {
|
|
24
|
+
formatMemory,
|
|
25
|
+
getMemory,
|
|
26
|
+
isMemoryKind,
|
|
27
|
+
listMemories,
|
|
28
|
+
memoryHistory,
|
|
29
|
+
searchMemories,
|
|
30
|
+
MEMORY_KINDS,
|
|
31
|
+
type MemoryRow,
|
|
32
|
+
} from "../../../storage/memory.js";
|
|
33
|
+
|
|
34
|
+
/** Rows per reply — a chat listing is a glance, not an export. */
|
|
35
|
+
const LIST_LIMIT = 15;
|
|
36
|
+
|
|
37
|
+
export function registerMemoryCommand(bot: Bot): void {
|
|
38
|
+
bot.command("memory", async (ctx: Context) => {
|
|
39
|
+
const arg = (ctx.match ?? "").toString().trim();
|
|
40
|
+
await replyHtmlChunked(ctx, renderMemory(arg));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Route the argument to one of the four reads. Returns ready HTML. */
|
|
45
|
+
function renderMemory(arg: string): string {
|
|
46
|
+
const why = /^why\b\s*(.*)$/is.exec(arg);
|
|
47
|
+
if (why) return renderWhy(why[1]!.trim());
|
|
48
|
+
const kind = /^kind\b\s*(.*)$/is.exec(arg);
|
|
49
|
+
if (kind) return renderKind(kind[1]!.trim());
|
|
50
|
+
if (!arg)
|
|
51
|
+
return renderRows(
|
|
52
|
+
listMemories({ limit: LIST_LIMIT }),
|
|
53
|
+
"Nothing remembered yet.",
|
|
54
|
+
);
|
|
55
|
+
return renderRows(
|
|
56
|
+
searchMemories(arg, { limit: LIST_LIMIT }),
|
|
57
|
+
`No memories matching "${arg}".`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** One escaped line per row, or the (escaped) empty-case sentence. */
|
|
62
|
+
function renderRows(rows: MemoryRow[], empty: string): string {
|
|
63
|
+
if (rows.length === 0) return escapeHtml(empty);
|
|
64
|
+
return rows.map((row) => escapeHtml(formatMemory(row))).join("\n");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function renderKind(kind: string): string {
|
|
68
|
+
if (!isMemoryKind(kind))
|
|
69
|
+
return escapeHtml(
|
|
70
|
+
`No such kind "${kind}". Valid kinds: ${MEMORY_KINDS.join(", ")}.`,
|
|
71
|
+
);
|
|
72
|
+
return renderRows(
|
|
73
|
+
listMemories({ kind, limit: LIST_LIMIT }),
|
|
74
|
+
`Nothing remembered under ${kind}.`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Provenance for one row: the row itself, the numbers that decide where
|
|
80
|
+
* it ranks, and its audit trail. Reads by id rather than by the live
|
|
81
|
+
* listing, so a superseded or dropped row still explains itself.
|
|
82
|
+
*/
|
|
83
|
+
function renderWhy(raw: string): string {
|
|
84
|
+
const id = Number(raw);
|
|
85
|
+
if (!raw || !Number.isInteger(id))
|
|
86
|
+
return escapeHtml(`No memory with id ${raw || "(none given)"}.`);
|
|
87
|
+
const row = getMemory(id);
|
|
88
|
+
if (!row) return escapeHtml(`No memory with id ${id}.`);
|
|
89
|
+
const lines = [
|
|
90
|
+
escapeHtml(formatMemory(row)),
|
|
91
|
+
"",
|
|
92
|
+
escapeHtml(
|
|
93
|
+
`trust ${row.trust} · confidence ${row.confidence} · hits ${row.hitCount} · salience ${row.salience}`,
|
|
94
|
+
),
|
|
95
|
+
escapeHtml(
|
|
96
|
+
`created ${isoTime(row.createdAt)} · last seen ${isoTime(row.lastSeenAt)}`,
|
|
97
|
+
),
|
|
98
|
+
];
|
|
99
|
+
const history = memoryHistory(row.id);
|
|
100
|
+
if (history.length > 0) {
|
|
101
|
+
lines.push("", "<b>History</b>");
|
|
102
|
+
for (const entry of history) {
|
|
103
|
+
const reason = entry.reason ? ` — ${entry.reason}` : "";
|
|
104
|
+
lines.push(escapeHtml(`${isoTime(entry.at)} ${entry.op}${reason}`));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return lines.join("\n");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isoTime(ms: number): string {
|
|
111
|
+
return new Date(ms).toISOString();
|
|
112
|
+
}
|
|
@@ -29,6 +29,6 @@ export function isAuthorizedAdmin(ctx: Context): boolean {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export type RegisterDeps = {
|
|
32
|
-
config: import("../../../
|
|
32
|
+
config: import("../../../core/config/index.js").TalonConfig;
|
|
33
33
|
gateway?: { backend: Backend | null };
|
|
34
34
|
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Bot } from "grammy";
|
|
7
|
-
import type { TalonConfig } from "../../../
|
|
7
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
8
8
|
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
10
|
import { logWarn } from "../../../util/log.js";
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Bot } from "grammy";
|
|
7
|
-
import type { TalonConfig } from "../../../
|
|
7
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
8
8
|
import { execute } from "../../../core/engine/dispatcher.js";
|
|
9
9
|
import { toolInputToRecord } from "../../../core/agent-runtime/events.js";
|
|
10
10
|
import { appendDailyLogResponse } from "../../../storage/daily-log.js";
|
|
11
11
|
import { stripMcpPrefix } from "../../../core/tools/index.js";
|
|
12
12
|
import { logWarn } from "../../../util/log.js";
|
|
13
|
-
import { replyParamsFor, sendText } from "../actions/
|
|
13
|
+
import { replyParamsFor, sendText } from "../actions/send.js";
|
|
14
14
|
import { ambientThreadId } from "../topics.js";
|
|
15
15
|
import { trackDmUser } from "./access.js";
|
|
16
16
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { Bot, Context } from "grammy";
|
|
9
|
-
import type { TalonConfig } from "../../../
|
|
9
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
10
10
|
import { escapeHtml } from "../formatting.js";
|
|
11
11
|
import { friendlyMessage } from "../../../core/errors.js";
|
|
12
12
|
import { setMessageFilePath } from "../../../storage/history.js";
|