switchroom 0.18.32 → 0.19.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/dist/auth-broker/index.js +17 -1
- package/dist/cli/switchroom.js +847 -729
- package/dist/host-control/main.js +18 -2
- package/dist/vault/approvals/kernel-server.js +17 -1
- package/dist/vault/broker/server.js +44 -2
- package/package.json +2 -2
- package/profiles/_base/start.sh.hbs +105 -18
- package/telegram-plugin/dist/gateway/gateway.js +60612 -56998
- package/telegram-plugin/gateway/agent-button-callback-handler.ts +237 -0
- package/telegram-plugin/gateway/ask-callback-handler.ts +92 -0
- package/telegram-plugin/gateway/attachment-message-handlers.ts +152 -0
- package/telegram-plugin/gateway/boot-card.ts +169 -1
- package/telegram-plugin/gateway/bot-commands-model-effort.ts +209 -0
- package/telegram-plugin/gateway/bot-commands-start-info.ts +108 -0
- package/telegram-plugin/gateway/callback-query-handlers.ts +124 -0
- package/telegram-plugin/gateway/card-approval-keyboards.test.ts +28 -0
- package/telegram-plugin/gateway/card-tool-handlers.ts +639 -0
- package/telegram-plugin/gateway/checklist-message-handler.ts +107 -0
- package/telegram-plugin/gateway/delivery-confirm-wiring.ts +133 -0
- package/telegram-plugin/gateway/gateway.ts +1347 -6758
- package/telegram-plugin/gateway/inbound-interceptors.ts +1133 -0
- package/telegram-plugin/gateway/inbound-router.ts +400 -0
- package/telegram-plugin/gateway/liveness-wiring.ts +440 -0
- package/telegram-plugin/gateway/media-message-handlers.ts +256 -0
- package/telegram-plugin/gateway/mental-model-propose-card.ts +16 -0
- package/telegram-plugin/gateway/model-command.ts +23 -0
- package/telegram-plugin/gateway/narrative-lane.ts +865 -0
- package/telegram-plugin/gateway/obligation-wiring.ts +333 -0
- package/telegram-plugin/gateway/photo-message-handler.ts +80 -0
- package/telegram-plugin/gateway/pinned-message-handler.ts +86 -0
- package/telegram-plugin/gateway/secret-request-card.test.ts +46 -0
- package/telegram-plugin/gateway/secret-request-card.ts +45 -0
- package/telegram-plugin/gateway/stream-render.ts +2166 -0
- package/telegram-plugin/gateway/turn-end.ts +606 -0
- package/telegram-plugin/gateway/turn-start-surfaces.ts +298 -0
- package/telegram-plugin/gateway/vault-request-access-card.ts +16 -0
- package/telegram-plugin/gateway/vault-request-save-card.test.ts +49 -0
- package/telegram-plugin/gateway/vault-request-save-card.ts +52 -0
- package/telegram-plugin/gateway/voice-message-handler.ts +123 -0
- package/telegram-plugin/gateway/voice-ondemand-callback-handler.ts +204 -0
- package/telegram-plugin/gateway/worker-feed-dispatch.ts +40 -0
- package/telegram-plugin/narrative-dedup.ts +24 -1
- package/telegram-plugin/narrative-flush.ts +2 -2
- package/telegram-plugin/render/render.ts +25 -1
- package/telegram-plugin/status-no-truncate.ts +13 -0
- package/telegram-plugin/subagent-watcher.ts +186 -3
- package/telegram-plugin/tests/activity-card-wiring.test.ts +8 -3
- package/telegram-plugin/tests/activity-ever-opened-sticky.test.ts +18 -3
- package/telegram-plugin/tests/agent-button-callback-handler.test.ts +149 -0
- package/telegram-plugin/tests/ask-callback-handler.test.ts +118 -0
- package/telegram-plugin/tests/attachment-message-handlers.test.ts +135 -0
- package/telegram-plugin/tests/boot-card-routing.test.ts +139 -0
- package/telegram-plugin/tests/bot-commands-model-effort.test.ts +189 -0
- package/telegram-plugin/tests/bot-commands-start-info.test.ts +240 -0
- package/telegram-plugin/tests/buffer-gate-broadened.test.ts +15 -6
- package/telegram-plugin/tests/busy-ack-wiring.test.ts +6 -1
- package/telegram-plugin/tests/button-tap-turn-gated.test.ts +18 -9
- package/telegram-plugin/tests/callback-query-handlers.test.ts +101 -0
- package/telegram-plugin/tests/card-tool-handlers.test.ts +497 -0
- package/telegram-plugin/tests/catch-all-unhandled-message.test.ts +5 -2
- package/telegram-plugin/tests/checklist-message-handler.test.ts +160 -0
- package/telegram-plugin/tests/emission-authority-facade.test.ts +47 -10
- package/telegram-plugin/tests/emission-determinism-wiring.test.ts +27 -9
- package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +30 -7
- package/telegram-plugin/tests/gateway-boot-side-effect-gating.test.ts +39 -18
- package/telegram-plugin/tests/gateway-boot-smoke.test.ts +160 -0
- package/telegram-plugin/tests/gateway-handler-registration-wiring.test.ts +3 -7
- package/telegram-plugin/tests/gateway-loopback-paste-redact.test.ts +44 -29
- package/telegram-plugin/tests/gateway-outbound-redact.test.ts +8 -2
- package/telegram-plugin/tests/gateway-request-secret.test.ts +7 -3
- package/telegram-plugin/tests/gateway-secret-detect.test.ts +20 -10
- package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +8 -2
- package/telegram-plugin/tests/inbound-emit-after-intercepts.test.ts +14 -3
- package/telegram-plugin/tests/inbound-message-types.test.ts +52 -16
- package/telegram-plugin/tests/media-message-handlers.test.ts +276 -0
- package/telegram-plugin/tests/mental-model-propose-callback-gate.test.ts +8 -4
- package/telegram-plugin/tests/model-command.test.ts +30 -0
- package/telegram-plugin/tests/multitopic-routing-wiring.test.ts +27 -9
- package/telegram-plugin/tests/narrative-dedup.test.ts +32 -0
- package/telegram-plugin/tests/narrative-flush.test.ts +6 -2
- package/telegram-plugin/tests/narrative-lane-golden.test.ts +458 -0
- package/telegram-plugin/tests/no-reply-bounded-drain.test.ts +14 -3
- package/telegram-plugin/tests/pending-card-durability-wiring.test.ts +16 -7
- package/telegram-plugin/tests/per-topic-current-turn.test.ts +32 -8
- package/telegram-plugin/tests/photo-message-handler.test.ts +114 -0
- package/telegram-plugin/tests/pinned-message-handler.test.ts +108 -0
- package/telegram-plugin/tests/render/render.test.ts +42 -0
- package/telegram-plugin/tests/secret-detect-delete-must-surface-failures.test.ts +8 -4
- package/telegram-plugin/tests/secret-detect-fail-closed.test.ts +38 -28
- package/telegram-plugin/tests/secret-detect-oauth-code.test.ts +28 -18
- package/telegram-plugin/tests/silence-liveness-wiring.test.ts +22 -8
- package/telegram-plugin/tests/status-pin-service-message-suppression.test.ts +42 -49
- package/telegram-plugin/tests/stop-command.test.ts +22 -12
- package/telegram-plugin/tests/stream-render-golden.test.ts +424 -0
- package/telegram-plugin/tests/subagent-watcher-boot-skip-dead.test.ts +218 -0
- package/telegram-plugin/tests/subagent-watcher-resume-reregister.test.ts +14 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +35 -3
- package/telegram-plugin/tests/turn-flush-safety.test.ts +183 -5
- package/telegram-plugin/tests/turn-flush-suppression-wiring.test.ts +9 -4
- package/telegram-plugin/tests/vault-approval-posture.test.ts +8 -2
- package/telegram-plugin/tests/vault-grant-union.test.ts +4 -1
- package/telegram-plugin/tests/vault-key-regex-allows-slash.test.ts +16 -5
- package/telegram-plugin/tests/vault-request-access-tool.test.ts +10 -5
- package/telegram-plugin/tests/vault-request-access-unlock-resume.test.ts +4 -1
- package/telegram-plugin/tests/vault-subcommands.test.ts +6 -1
- package/telegram-plugin/tests/voice-message-handler.test.ts +111 -0
- package/telegram-plugin/tests/voice-ondemand-callback-handler.test.ts +140 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +86 -19
- package/telegram-plugin/tests/worker-feed-coalesce.test.ts +110 -20
- package/telegram-plugin/tests/worker-feed-resume-guard.test.ts +86 -0
- package/telegram-plugin/tool-activity-summary.ts +83 -35
- package/telegram-plugin/turn-flush-safety.ts +80 -14
- package/telegram-plugin/uat/restart-capability.ts +76 -0
- package/telegram-plugin/uat/scenarios/bg-sub-agent-dispatch-dm.test.ts +14 -4
- package/telegram-plugin/uat/scenarios/bridge-flap-resilience-dm.test.ts +11 -1
- package/telegram-plugin/uat/scenarios/cross-turn-pending-progress-dm.test.ts +19 -2
- package/telegram-plugin/uat/scenarios/jtbd-always-on-after-restart-dm.test.ts +6 -12
- package/telegram-plugin/uat/scenarios/jtbd-deliberate-restart-resumes-dm.test.ts +6 -12
- package/telegram-plugin/uat/scenarios/jtbd-interrupted-turn-resumes-dm.test.ts +6 -12
- package/telegram-plugin/uat/scenarios/jtbd-multipart-render-dm.test.ts +47 -13
- package/telegram-plugin/worker-activity-feed.ts +10 -4
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media-envelope inbound handlers (switchroom#2996 P6 cluster A).
|
|
3
|
+
*
|
|
4
|
+
* The "envelope" `message:*` content types that carry structured metadata the
|
|
5
|
+
* gateway renders into a compact text envelope for the agent — contact,
|
|
6
|
+
* location, venue, poll, web_app_data, users_shared, chat_shared — plus the
|
|
7
|
+
* two money/identity outliers that are deliberately NOT forwarded to the agent
|
|
8
|
+
* (successful_payment → log-and-ack, passport_data → refuse), plus the
|
|
9
|
+
* situational ack-only leaves (dice/game/story/paid_media → log-and-react,
|
|
10
|
+
* switchroom#2996 P6 cluster B).
|
|
11
|
+
*
|
|
12
|
+
* Extracted VERBATIM out of gateway.ts (the P0a `registerGatewayHandlers`
|
|
13
|
+
* block) so the rendering logic is unit-testable against a fake ctx + mock
|
|
14
|
+
* dispatch deps, instead of living inline in a side-effecting module that
|
|
15
|
+
* cannot be imported. The `bot.on('message:*')` registration lines stay in
|
|
16
|
+
* gateway.ts (their ORDER is a load-bearing invariant pinned by
|
|
17
|
+
* gateway-handler-registration-wiring.test.ts); each now delegates to the
|
|
18
|
+
* matching handler here.
|
|
19
|
+
*
|
|
20
|
+
* Dispatch surfaces (`handleInbound`, `handleAckOnly`, `handleRefusal`) and
|
|
21
|
+
* the stderr log sink are injected via `MediaEnvelopeDeps` — no gateway
|
|
22
|
+
* module-scope globals are read here (switchroom#2996 Amendment 9).
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type { Context, Filter } from 'grammy'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Sanitize a user-supplied string for inclusion in a text envelope: strips
|
|
29
|
+
* the characters that could break the envelope framing or inject markup
|
|
30
|
+
* (`<>[]`, CR/LF, `;`). Shared with the attachment handlers (photo/document/
|
|
31
|
+
* audio/video/animation) that still live inline in gateway.ts — imported
|
|
32
|
+
* there — so it lives here as the single owner.
|
|
33
|
+
*/
|
|
34
|
+
export function safeName(s: string | undefined): string | undefined {
|
|
35
|
+
return s?.replace(/[<>\[\]\r\n;]/g, '_')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Polite, fixed refusal for inbound Telegram Passport data — a regulated
|
|
40
|
+
* identity-document flow we explicitly do not process (never forwarded to the
|
|
41
|
+
* LLM-driven agent).
|
|
42
|
+
*/
|
|
43
|
+
export const PASSPORT_REFUSAL_TEXT =
|
|
44
|
+
"Sorry, I don't handle Telegram Passport data. Please share identity documents through a supported channel — your agent does not process encrypted Passport payloads."
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Injected dispatch + logging surfaces. `handleInbound` is the normal inbound
|
|
48
|
+
* pipeline (same access gating / forward-origin parsing as every content
|
|
49
|
+
* handler); `handleAckOnly` reacts without forwarding; `handleRefusal` replies
|
|
50
|
+
* a refusal and never forwards; `log` is the stderr sink.
|
|
51
|
+
*/
|
|
52
|
+
export interface MediaEnvelopeDeps {
|
|
53
|
+
handleInbound: (ctx: Context, text: string, downloadImage: undefined) => Promise<void>
|
|
54
|
+
handleAckOnly: (
|
|
55
|
+
ctx: Context,
|
|
56
|
+
kind: string,
|
|
57
|
+
opts?: { emoji?: string; warn?: boolean },
|
|
58
|
+
) => Promise<void>
|
|
59
|
+
handleRefusal: (ctx: Context, kind: string, refusalText: string) => Promise<void>
|
|
60
|
+
log: (line: string) => void
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function handleContactMessage(
|
|
64
|
+
ctx: Filter<Context, 'message:contact'>,
|
|
65
|
+
deps: MediaEnvelopeDeps,
|
|
66
|
+
): Promise<void> {
|
|
67
|
+
try {
|
|
68
|
+
const c = ctx.message.contact
|
|
69
|
+
const phone = safeName(c.phone_number) ?? '?'
|
|
70
|
+
const first = safeName(c.first_name) ?? ''
|
|
71
|
+
const last = safeName(c.last_name) ?? ''
|
|
72
|
+
const name = [first, last].filter(Boolean).join(' ') || '?'
|
|
73
|
+
const userIdPart = c.user_id != null ? ` user_id=${c.user_id}` : ''
|
|
74
|
+
const text = `(contact: name="${name}" phone="${phone}"${userIdPart})`
|
|
75
|
+
deps.log(`telegram gateway: inbound contact from chat=${ctx.chat?.id ?? '?'}\n`)
|
|
76
|
+
await deps.handleInbound(ctx, text, undefined)
|
|
77
|
+
} catch (err) {
|
|
78
|
+
deps.log(`telegram gateway: contact handler error: ${(err as Error).message}\n`)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function handleLocationMessage(
|
|
83
|
+
ctx: Filter<Context, 'message:location'>,
|
|
84
|
+
deps: MediaEnvelopeDeps,
|
|
85
|
+
): Promise<void> {
|
|
86
|
+
try {
|
|
87
|
+
const loc = ctx.message.location
|
|
88
|
+
const lat = typeof loc.latitude === 'number' ? loc.latitude.toFixed(6) : '?'
|
|
89
|
+
const lon = typeof loc.longitude === 'number' ? loc.longitude.toFixed(6) : '?'
|
|
90
|
+
const live = (loc as { live_period?: number }).live_period != null
|
|
91
|
+
? ` live_period=${(loc as { live_period?: number }).live_period}s`
|
|
92
|
+
: ''
|
|
93
|
+
const text = `(location: lat=${lat} lon=${lon}${live})`
|
|
94
|
+
deps.log(`telegram gateway: inbound location from chat=${ctx.chat?.id ?? '?'}\n`)
|
|
95
|
+
await deps.handleInbound(ctx, text, undefined)
|
|
96
|
+
} catch (err) {
|
|
97
|
+
deps.log(`telegram gateway: location handler error: ${(err as Error).message}\n`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function handleVenueMessage(
|
|
102
|
+
ctx: Filter<Context, 'message:venue'>,
|
|
103
|
+
deps: MediaEnvelopeDeps,
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
try {
|
|
106
|
+
const v = ctx.message.venue
|
|
107
|
+
const title = safeName(v.title) ?? '?'
|
|
108
|
+
const address = safeName(v.address) ?? '?'
|
|
109
|
+
const lat = typeof v.location?.latitude === 'number' ? v.location.latitude.toFixed(6) : '?'
|
|
110
|
+
const lon = typeof v.location?.longitude === 'number' ? v.location.longitude.toFixed(6) : '?'
|
|
111
|
+
const text = `(venue: title="${title}" address="${address}" lat=${lat} lon=${lon})`
|
|
112
|
+
deps.log(`telegram gateway: inbound venue from chat=${ctx.chat?.id ?? '?'}\n`)
|
|
113
|
+
await deps.handleInbound(ctx, text, undefined)
|
|
114
|
+
} catch (err) {
|
|
115
|
+
deps.log(`telegram gateway: venue handler error: ${(err as Error).message}\n`)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function handlePollMessage(
|
|
120
|
+
ctx: Filter<Context, 'message:poll'>,
|
|
121
|
+
deps: MediaEnvelopeDeps,
|
|
122
|
+
): Promise<void> {
|
|
123
|
+
try {
|
|
124
|
+
const p = ctx.message.poll
|
|
125
|
+
const q = safeName(p.question) ?? '?'
|
|
126
|
+
const optsCount = Array.isArray(p.options) ? p.options.length : 0
|
|
127
|
+
const optsList = Array.isArray(p.options)
|
|
128
|
+
? p.options.slice(0, 10).map(o => safeName((o as { text?: string }).text) ?? '?').join(' | ')
|
|
129
|
+
: ''
|
|
130
|
+
const anon = p.is_anonymous ? ' anonymous' : ''
|
|
131
|
+
const text = `(poll: question="${q}" options=${optsCount}${anon}${optsList ? ` choices=[${optsList}]` : ''})`
|
|
132
|
+
deps.log(`telegram gateway: inbound poll from chat=${ctx.chat?.id ?? '?'}\n`)
|
|
133
|
+
await deps.handleInbound(ctx, text, undefined)
|
|
134
|
+
} catch (err) {
|
|
135
|
+
deps.log(`telegram gateway: poll handler error: ${(err as Error).message}\n`)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function handleWebAppDataMessage(
|
|
140
|
+
ctx: Filter<Context, 'message:web_app_data'>,
|
|
141
|
+
deps: MediaEnvelopeDeps,
|
|
142
|
+
): Promise<void> {
|
|
143
|
+
try {
|
|
144
|
+
const w = ctx.message.web_app_data
|
|
145
|
+
// web_app_data.data is arbitrary user-supplied string from the
|
|
146
|
+
// mini-app — pass it through but cap length so a malicious mini-app
|
|
147
|
+
// can't blast the agent with multi-MB payloads.
|
|
148
|
+
const raw = typeof w.data === 'string' ? w.data : ''
|
|
149
|
+
const data = raw.length > 4096 ? raw.slice(0, 4096) + '…(truncated)' : raw
|
|
150
|
+
const button = safeName(w.button_text) ?? '?'
|
|
151
|
+
const text = `(web_app_data: button="${button}" data=${JSON.stringify(data)})`
|
|
152
|
+
deps.log(`telegram gateway: inbound web_app_data from chat=${ctx.chat?.id ?? '?'} button="${button}" bytes=${raw.length}\n`)
|
|
153
|
+
await deps.handleInbound(ctx, text, undefined)
|
|
154
|
+
} catch (err) {
|
|
155
|
+
deps.log(`telegram gateway: web_app_data handler error: ${(err as Error).message}\n`)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function handleUsersSharedMessage(
|
|
160
|
+
ctx: Filter<Context, 'message:users_shared'>,
|
|
161
|
+
deps: MediaEnvelopeDeps,
|
|
162
|
+
): Promise<void> {
|
|
163
|
+
try {
|
|
164
|
+
const u = ctx.message.users_shared
|
|
165
|
+
const users = Array.isArray(u.users) ? u.users : []
|
|
166
|
+
const ids = users.map(usr => String((usr as { user_id?: number }).user_id ?? '?')).join(',')
|
|
167
|
+
const text = `(users_shared: request_id=${u.request_id ?? '?'} user_ids=[${ids}] count=${users.length})`
|
|
168
|
+
deps.log(`telegram gateway: inbound users_shared from chat=${ctx.chat?.id ?? '?'} count=${users.length}\n`)
|
|
169
|
+
await deps.handleInbound(ctx, text, undefined)
|
|
170
|
+
} catch (err) {
|
|
171
|
+
deps.log(`telegram gateway: users_shared handler error: ${(err as Error).message}\n`)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function handleChatSharedMessage(
|
|
176
|
+
ctx: Filter<Context, 'message:chat_shared'>,
|
|
177
|
+
deps: MediaEnvelopeDeps,
|
|
178
|
+
): Promise<void> {
|
|
179
|
+
try {
|
|
180
|
+
const c = ctx.message.chat_shared
|
|
181
|
+
const title = safeName((c as { title?: string }).title) ?? ''
|
|
182
|
+
const titlePart = title ? ` title="${title}"` : ''
|
|
183
|
+
const text = `(chat_shared: request_id=${c.request_id ?? '?'} chat_id=${c.chat_id ?? '?'}${titlePart})`
|
|
184
|
+
deps.log(`telegram gateway: inbound chat_shared from chat=${ctx.chat?.id ?? '?'} shared_chat_id=${c.chat_id ?? '?'}\n`)
|
|
185
|
+
await deps.handleInbound(ctx, text, undefined)
|
|
186
|
+
} catch (err) {
|
|
187
|
+
deps.log(`telegram gateway: chat_shared handler error: ${(err as Error).message}\n`)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function handleSuccessfulPaymentMessage(
|
|
192
|
+
ctx: Filter<Context, 'message:successful_payment'>,
|
|
193
|
+
deps: MediaEnvelopeDeps,
|
|
194
|
+
): Promise<void> {
|
|
195
|
+
// Money has changed hands — log loudly with the structured fields a
|
|
196
|
+
// reconciliation script would want. Do NOT forward to the agent; an
|
|
197
|
+
// LLM should not be in the loop for confirming receipts.
|
|
198
|
+
try {
|
|
199
|
+
const p = ctx.message.successful_payment
|
|
200
|
+
deps.log(
|
|
201
|
+
`telegram gateway: inbound successful_payment from chat=${ctx.chat?.id ?? '?'} ` +
|
|
202
|
+
`currency=${p.currency ?? '?'} total_amount=${p.total_amount ?? '?'} ` +
|
|
203
|
+
`payload="${safeName(p.invoice_payload) ?? ''}" ` +
|
|
204
|
+
`provider_charge=${safeName(p.provider_payment_charge_id) ?? '?'} ` +
|
|
205
|
+
`telegram_charge=${safeName(p.telegram_payment_charge_id) ?? '?'}\n`,
|
|
206
|
+
)
|
|
207
|
+
} catch (err) {
|
|
208
|
+
deps.log(`telegram gateway: successful_payment log failed: ${(err as Error).message}\n`)
|
|
209
|
+
}
|
|
210
|
+
await deps.handleAckOnly(ctx, 'successful_payment', { warn: true })
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function handleDiceMessage(
|
|
214
|
+
ctx: Filter<Context, 'message:dice'>,
|
|
215
|
+
deps: MediaEnvelopeDeps,
|
|
216
|
+
): Promise<void> {
|
|
217
|
+
deps.log(`telegram gateway: inbound dice from chat=${ctx.chat?.id ?? '?'}\n`)
|
|
218
|
+
await deps.handleAckOnly(ctx, 'dice', { emoji: '🎲' })
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export async function handleGameMessage(
|
|
222
|
+
ctx: Filter<Context, 'message:game'>,
|
|
223
|
+
deps: MediaEnvelopeDeps,
|
|
224
|
+
): Promise<void> {
|
|
225
|
+
deps.log(`telegram gateway: inbound game from chat=${ctx.chat?.id ?? '?'}\n`)
|
|
226
|
+
await deps.handleAckOnly(ctx, 'game')
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export async function handleStoryMessage(
|
|
230
|
+
ctx: Filter<Context, 'message:story'>,
|
|
231
|
+
deps: MediaEnvelopeDeps,
|
|
232
|
+
): Promise<void> {
|
|
233
|
+
deps.log(`telegram gateway: inbound story from chat=${ctx.chat?.id ?? '?'}\n`)
|
|
234
|
+
await deps.handleAckOnly(ctx, 'story')
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export async function handlePaidMediaMessage(
|
|
238
|
+
ctx: Filter<Context, 'message:paid_media'>,
|
|
239
|
+
deps: MediaEnvelopeDeps,
|
|
240
|
+
): Promise<void> {
|
|
241
|
+
deps.log(`telegram gateway: inbound paid_media from chat=${ctx.chat?.id ?? '?'}\n`)
|
|
242
|
+
await deps.handleAckOnly(ctx, 'paid_media', { warn: true })
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export async function handlePassportDataMessage(
|
|
246
|
+
ctx: Filter<Context, 'message:passport_data'>,
|
|
247
|
+
deps: MediaEnvelopeDeps,
|
|
248
|
+
): Promise<void> {
|
|
249
|
+
// Telegram Passport is a regulated identity-document flow. Forwarding
|
|
250
|
+
// the encrypted credentials to an LLM-driven agent would be reckless;
|
|
251
|
+
// we explicitly refuse to handle it and tell the user so they can
|
|
252
|
+
// route via the proper channel. Logged at SECURITY level so operators
|
|
253
|
+
// can see if a passport_data inbound ever lands here (an outlier worth
|
|
254
|
+
// investigating).
|
|
255
|
+
await deps.handleRefusal(ctx, 'passport_data', PASSPORT_REFUSAL_TEXT)
|
|
256
|
+
}
|
|
@@ -67,3 +67,19 @@ export function renderMentalModelProposeCard(
|
|
|
67
67
|
// richMessage, bypassing the switchroomReply chokepoint.
|
|
68
68
|
return hardenCardBreaks(lines.join("\n"));
|
|
69
69
|
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Inline keyboard for the mental-model proposal card (hindsight Phase 5).
|
|
73
|
+
* Callback prefix `mmp:`, handled by handleMentalModelProposeCallback in
|
|
74
|
+
* callback-query-handlers.ts. Extracted verbatim from gateway.ts (#2996 P5).
|
|
75
|
+
*/
|
|
76
|
+
export function buildMentalModelProposeKeyboard(stageId: string): { inline_keyboard: Array<Array<{ text: string; callback_data: string }>> } {
|
|
77
|
+
return {
|
|
78
|
+
inline_keyboard: [
|
|
79
|
+
[
|
|
80
|
+
{ text: '✅ Approve', callback_data: `mmp:approve:${stageId}` },
|
|
81
|
+
{ text: '🚫 Deny', callback_data: `mmp:deny:${stageId}` },
|
|
82
|
+
],
|
|
83
|
+
],
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -666,6 +666,16 @@ export const SR_MODEL_LABELS: Record<string, string> = {
|
|
|
666
666
|
'sr-gemini-flash-lite': 'Gemini 3.1 Flash Lite',
|
|
667
667
|
'sr-minimax-m3': 'MiniMax M3',
|
|
668
668
|
'sr-deepseek-v4-flash': 'DeepSeek V4 Flash',
|
|
669
|
+
// xAI Grok / Moonshot Kimi / OpenAI GPT-5.6 (added 2026-07-19, pairs with the
|
|
670
|
+
// live litellm sr-* model_name additions). The `.5`/`.7`/`.6` version dots are
|
|
671
|
+
// valid model-arg chars (MODEL_ARG_RE) and match the sr-* ids in the proxy config.
|
|
672
|
+
'sr-grok-4.5': 'Grok 4.5',
|
|
673
|
+
'sr-grok-4.3': 'Grok 4.3',
|
|
674
|
+
'sr-kimi-k3': 'Kimi K3',
|
|
675
|
+
'sr-kimi-k2.7-code': 'Kimi K2.7 Code',
|
|
676
|
+
'sr-gpt-5.6-sol': 'GPT-5.6 Sol',
|
|
677
|
+
'sr-gpt-5.6-terra': 'GPT-5.6 Terra',
|
|
678
|
+
'sr-gpt-5.6-luna': 'GPT-5.6 Luna',
|
|
669
679
|
}
|
|
670
680
|
|
|
671
681
|
/**
|
|
@@ -681,6 +691,19 @@ export const SR_MODEL_ALIASES: Record<string, string> = {
|
|
|
681
691
|
r1: 'sr-deepseek-r1',
|
|
682
692
|
glm: 'sr-glm-5',
|
|
683
693
|
codex: 'sr-codex-5.5',
|
|
694
|
+
// Flagship keyboard buttons for the three providers added 2026-07-19. Each
|
|
695
|
+
// points at that provider's top tier; the cheaper tiers (grok-4.3,
|
|
696
|
+
// kimi-k2.7-code, gpt-5.6-terra/luna) stay typeable via `/model sr-<id>` and
|
|
697
|
+
// keep a friendly label above, matching the "labels are a superset of buttons"
|
|
698
|
+
// design (SR_MODEL_LABELS doc comment).
|
|
699
|
+
grok: 'sr-grok-4.5',
|
|
700
|
+
kimi: 'sr-kimi-k3',
|
|
701
|
+
gpt: 'sr-gpt-5.6-sol',
|
|
702
|
+
// Per-tier buttons for the GPT-5.6 line so each is a one-word shortcut
|
|
703
|
+
// (`gpt` stays the flagship-Sol default). Added 2026-07-19.
|
|
704
|
+
sol: 'sr-gpt-5.6-sol',
|
|
705
|
+
terra: 'sr-gpt-5.6-terra',
|
|
706
|
+
luna: 'sr-gpt-5.6-luna',
|
|
684
707
|
}
|
|
685
708
|
|
|
686
709
|
/** Expand a short alias (case-insensitive) to its full sr-* id, or return the original. */
|