talon-agent 3.34.1 → 3.36.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/LICENSE +1 -1
- package/README.md +0 -10
- package/package.json +3 -3
- package/src/backend/codex/auth.ts +40 -1
- package/src/backend/codex/handler/message.ts +16 -1
- package/src/backend/codex/plan-usage.ts +27 -2
- package/src/backend/kilo/handler/message.ts +2 -0
- package/src/backend/kilo/server.ts +1 -0
- package/src/backend/opencode/handler/message.ts +2 -0
- package/src/backend/opencode/server.ts +1 -0
- package/src/backend/remote-server/chat-turn.ts +13 -3
- package/src/backend/remote-server/lifecycle.ts +37 -0
- package/src/backend/remote-server/server-bindings.ts +12 -0
- package/src/backend/remote-server/state.ts +8 -0
- package/src/backend/remote-server/turn.ts +93 -23
- package/src/bootstrap.ts +24 -10
- package/src/core/auth/expiry-monitor.ts +89 -0
- package/src/core/auth/login-flow.ts +247 -0
- package/src/core/auth/status.ts +193 -0
- package/src/core/engine/gateway-actions/history.ts +12 -1
- package/src/core/errors.ts +1 -1
- package/src/core/tools/history.ts +10 -1
- package/src/frontend/native/chat-wire.ts +6 -2
- package/src/frontend/native/emit.ts +30 -11
- package/src/frontend/native/handlers.ts +62 -13
- package/src/frontend/native/history.ts +30 -6
- package/src/frontend/native/media.ts +271 -15
- package/src/frontend/native/protocol.ts +34 -1
- package/src/frontend/native/queue.ts +4 -7
- package/src/frontend/native/routes/chats.ts +30 -12
- package/src/frontend/native/routes/host.ts +17 -5
- package/src/frontend/native/routes/params.ts +21 -0
- package/src/frontend/native/runtime.ts +16 -4
- package/src/frontend/native/server.ts +2 -35
- package/src/frontend/native/status.ts +3 -1
- package/src/frontend/native/turn.ts +39 -17
- package/src/frontend/shared/model-commands.ts +400 -0
- package/src/frontend/telegram/auth-panel.ts +205 -0
- package/src/frontend/telegram/callbacks/auth.ts +70 -0
- package/src/frontend/telegram/callbacks/index.ts +16 -0
- package/src/frontend/telegram/callbacks/whatsapp.ts +50 -0
- package/src/frontend/telegram/commands/auth.ts +58 -0
- package/src/frontend/telegram/commands/definitions.ts +4 -0
- package/src/frontend/telegram/commands/index.ts +10 -4
- package/src/frontend/telegram/commands/whatsapp-pairing.ts +121 -79
- package/src/frontend/whatsapp/actions/chat-info.ts +2 -36
- package/src/frontend/whatsapp/actions/history.ts +100 -0
- package/src/frontend/whatsapp/actions/index.ts +4 -1
- package/src/frontend/whatsapp/commands.ts +371 -0
- package/src/frontend/whatsapp/inbound.ts +24 -46
- package/src/frontend/whatsapp/index.ts +12 -1
- package/src/frontend/whatsapp/media-store.ts +20 -1
- package/src/frontend/whatsapp/message-store.ts +101 -24
- package/src/storage/db.ts +7 -1
- package/src/storage/history.ts +26 -2
- package/src/storage/repositories/history-repo.ts +61 -1
- package/src/storage/repositories/whatsapp-messages-repo.ts +101 -0
- package/src/storage/sql/db.sql +6 -0
- package/src/storage/sql/history.sql +22 -9
- package/src/storage/sql/schema.sql +27 -1
- package/src/storage/sql/statements.generated.ts +70 -11
- package/src/storage/sql/whatsapp-messages.sql +29 -0
- package/src/storage/whatsapp-messages.ts +51 -0
|
@@ -25,7 +25,12 @@ CREATE TABLE IF NOT EXISTS history_messages (
|
|
|
25
25
|
timestamp INTEGER NOT NULL,
|
|
26
26
|
media_type TEXT,
|
|
27
27
|
sticker_file_id TEXT,
|
|
28
|
-
file_path TEXT
|
|
28
|
+
file_path TEXT,
|
|
29
|
+
-- Files attached to this message, as a JSON array of
|
|
30
|
+
-- {path,name,size,mimeType,image}. A message can carry several (the
|
|
31
|
+
-- companion's composer stages any number), so file_path above holds
|
|
32
|
+
-- only the first for the pre-multi-file row shape. Null when none.
|
|
33
|
+
attachments TEXT
|
|
29
34
|
);
|
|
30
35
|
CREATE INDEX IF NOT EXISTS idx_history_chat ON history_messages(chat_id, id);
|
|
31
36
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_history_chat_msg ON history_messages(chat_id, msg_id);
|
|
@@ -255,7 +260,28 @@ CREATE TABLE IF NOT EXISTS journal (
|
|
|
255
260
|
type TEXT NOT NULL,
|
|
256
261
|
payload TEXT NOT NULL
|
|
257
262
|
);
|
|
258
|
-
CREATE INDEX IF NOT EXISTS idx_journal_type ON journal(type, seq)
|
|
263
|
+
CREATE INDEX IF NOT EXISTS idx_journal_type ON journal(type, seq);
|
|
264
|
+
|
|
265
|
+
-- WhatsApp message keys: Talon's numeric message id ↔ WhatsApp's
|
|
266
|
+
-- (id, remoteJid, fromMe, participant) key, plus the full proto as JSON
|
|
267
|
+
-- so react/reply/forward/download keep working on messages from before
|
|
268
|
+
-- a restart and media past the CDN TTL can be re-requested.
|
|
269
|
+
CREATE TABLE IF NOT EXISTS whatsapp_messages (
|
|
270
|
+
chat_id TEXT NOT NULL,
|
|
271
|
+
msg_id INTEGER NOT NULL,
|
|
272
|
+
wa_id TEXT NOT NULL,
|
|
273
|
+
remote_jid TEXT NOT NULL,
|
|
274
|
+
from_me INTEGER NOT NULL DEFAULT 0,
|
|
275
|
+
participant TEXT,
|
|
276
|
+
sender_name TEXT NOT NULL DEFAULT '',
|
|
277
|
+
text TEXT NOT NULL DEFAULT '',
|
|
278
|
+
timestamp INTEGER NOT NULL,
|
|
279
|
+
message_json TEXT,
|
|
280
|
+
PRIMARY KEY (chat_id, msg_id)
|
|
281
|
+
);
|
|
282
|
+
CREATE INDEX IF NOT EXISTS idx_whatsapp_messages_msg ON whatsapp_messages(msg_id);
|
|
283
|
+
CREATE INDEX IF NOT EXISTS idx_whatsapp_messages_wa_id ON whatsapp_messages(wa_id);
|
|
284
|
+
CREATE INDEX IF NOT EXISTS idx_whatsapp_messages_time ON whatsapp_messages(timestamp);`;
|
|
259
285
|
|
|
260
286
|
export const chatSettingsSql = {
|
|
261
287
|
upsert: `INSERT OR REPLACE INTO chat_settings (chat_id, settings) VALUES (?, ?)`,
|
|
@@ -303,6 +329,10 @@ ALTER TABLE history_messages ADD COLUMN sender_handle TEXT`,
|
|
|
303
329
|
addSessionsMetricsColumn: `-- Column reconciliation for databases that shipped before per-session
|
|
304
330
|
-- metrics existed. Fresh databases get the column via schema.sql.
|
|
305
331
|
ALTER TABLE sessions ADD COLUMN metrics TEXT NOT NULL DEFAULT '{"lifetime":{"counters":{"queries":0,"toolCalls":0,"turnsWithTools":0,"apiCalls":0,"inputTokens":0,"outputTokens":0,"cacheReadTokens":0,"cacheWriteTokens":0,"failedTurns":0,"flowViolationRetries":0,"flowViolationCapExhausted":0,"trailingTextDropped":0},"latency":{"count":0,"sumMs":0,"minMs":null,"maxMs":0},"toolCallsByName":{},"backend":{},"cacheHitPercent":{"count":0,"sumMs":0,"minMs":null,"maxMs":0},"toolCallsPerTurn":{"count":0,"sumMs":0,"minMs":null,"maxMs":0},"apiCallsPerTurn":{"count":0,"sumMs":0,"minMs":null,"maxMs":0}},"buckets":{}}'`,
|
|
332
|
+
addHistoryAttachmentsColumn: `-- Column reconciliation for databases that shipped before a message could
|
|
333
|
+
-- carry more than one attachment. Fresh databases get the column via
|
|
334
|
+
-- schema.sql.
|
|
335
|
+
ALTER TABLE history_messages ADD COLUMN attachments TEXT`,
|
|
306
336
|
} as const;
|
|
307
337
|
|
|
308
338
|
export const goalsSql = {
|
|
@@ -334,22 +364,23 @@ WHERE chat_id = ? AND status IN (/* statuses */)`,
|
|
|
334
364
|
export const historySql = {
|
|
335
365
|
insert: `INSERT OR IGNORE INTO history_messages
|
|
336
366
|
(chat_id, msg_id, sender_id, sender_name, sender_handle, text,
|
|
337
|
-
reply_to_msg_id, timestamp, media_type, sticker_file_id, file_path
|
|
338
|
-
|
|
367
|
+
reply_to_msg_id, timestamp, media_type, sticker_file_id, file_path,
|
|
368
|
+
attachments)
|
|
369
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
339
370
|
recent: `SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
340
|
-
timestamp, media_type, sticker_file_id, file_path
|
|
371
|
+
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
341
372
|
FROM history_messages
|
|
342
373
|
WHERE chat_id = ? ORDER BY id DESC LIMIT ?`,
|
|
343
374
|
recentBefore: `-- Scroll-back pagination: the window of messages strictly older than a
|
|
344
375
|
-- given msg_id, newest-first (the repository reverses to chronological).
|
|
345
376
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
346
|
-
timestamp, media_type, sticker_file_id, file_path
|
|
377
|
+
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
347
378
|
FROM history_messages
|
|
348
379
|
WHERE chat_id = ? AND msg_id < ? ORDER BY id DESC LIMIT ?`,
|
|
349
380
|
recentBeforeTime: `-- Time-cursor variant of recentBefore for the read_history \`before\` date
|
|
350
381
|
-- parameter: the newest \`limit\` messages strictly older than a timestamp.
|
|
351
382
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
352
|
-
timestamp, media_type, sticker_file_id, file_path
|
|
383
|
+
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
353
384
|
FROM history_messages
|
|
354
385
|
WHERE chat_id = ? AND timestamp < ? ORDER BY id DESC LIMIT ?`,
|
|
355
386
|
setFilePath: `UPDATE history_messages SET file_path = ? WHERE chat_id = ? AND msg_id = ?`,
|
|
@@ -357,23 +388,33 @@ WHERE chat_id = ? AND timestamp < ? ORDER BY id DESC LIMIT ?`,
|
|
|
357
388
|
searchFts: `-- The match param must already be a valid FTS5 expression
|
|
358
389
|
-- (see history.ts ftsQuery).
|
|
359
390
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
360
|
-
timestamp, media_type, sticker_file_id, file_path
|
|
391
|
+
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
392
|
+
FROM history_messages
|
|
393
|
+
WHERE chat_id = ?
|
|
394
|
+
AND id IN (SELECT rowid FROM history_fts WHERE history_fts MATCH ?)
|
|
395
|
+
ORDER BY id DESC LIMIT ?`,
|
|
396
|
+
searchFtsBetween: `-- searchFts restricted to a timestamp window [after, before) — the
|
|
397
|
+
-- search_history \`after\` / \`before\` date parameters. Either bound may be
|
|
398
|
+
-- the open end of the range (0 / a far-future value).
|
|
399
|
+
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
400
|
+
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
361
401
|
FROM history_messages
|
|
362
402
|
WHERE chat_id = ?
|
|
363
403
|
AND id IN (SELECT rowid FROM history_fts WHERE history_fts MATCH ?)
|
|
404
|
+
AND timestamp >= ? AND timestamp < ?
|
|
364
405
|
ORDER BY id DESC LIMIT ?`,
|
|
365
406
|
bySenderName: `-- The fragment param is LIKE-escaped by the repository (backslash escape).
|
|
366
407
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
367
|
-
timestamp, media_type, sticker_file_id, file_path
|
|
408
|
+
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
368
409
|
FROM history_messages
|
|
369
410
|
WHERE chat_id = ? AND lower(sender_name) LIKE ? ESCAPE '\\'
|
|
370
411
|
ORDER BY id DESC LIMIT ?`,
|
|
371
412
|
byMsgId: `SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
372
|
-
timestamp, media_type, sticker_file_id, file_path
|
|
413
|
+
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
373
414
|
FROM history_messages
|
|
374
415
|
WHERE chat_id = ? AND msg_id = ? ORDER BY id DESC LIMIT 1`,
|
|
375
416
|
bySenderId: `SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
376
|
-
timestamp, media_type, sticker_file_id, file_path
|
|
417
|
+
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
377
418
|
FROM history_messages
|
|
378
419
|
WHERE chat_id = ? AND sender_id = ? ORDER BY id DESC LIMIT ?`,
|
|
379
420
|
latestMsgId: `SELECT msg_id FROM history_messages WHERE chat_id = ? ORDER BY id DESC LIMIT 1`,
|
|
@@ -548,3 +589,21 @@ WHERE chat_id = ?1
|
|
|
548
589
|
LIMIT ?2
|
|
549
590
|
)`,
|
|
550
591
|
} as const;
|
|
592
|
+
|
|
593
|
+
export const whatsappMessagesSql = {
|
|
594
|
+
insert: `INSERT OR IGNORE INTO whatsapp_messages
|
|
595
|
+
(chat_id, msg_id, wa_id, remote_jid, from_me, participant, sender_name,
|
|
596
|
+
text, timestamp, message_json)
|
|
597
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
598
|
+
byMsgId: `SELECT chat_id, msg_id, wa_id, remote_jid, from_me, participant, sender_name,
|
|
599
|
+
text, timestamp, message_json
|
|
600
|
+
FROM whatsapp_messages WHERE msg_id = ? LIMIT 1`,
|
|
601
|
+
byWaId: `-- Newest first: a WhatsApp id re-delivered on reconnect maps to the row
|
|
602
|
+
-- that already exists for it.
|
|
603
|
+
SELECT chat_id, msg_id, wa_id, remote_jid, from_me, participant, sender_name,
|
|
604
|
+
text, timestamp, message_json
|
|
605
|
+
FROM whatsapp_messages WHERE wa_id = ? ORDER BY msg_id DESC LIMIT 1`,
|
|
606
|
+
maxMsgId: `SELECT MAX(msg_id) AS max_id FROM whatsapp_messages`,
|
|
607
|
+
deleteOlderThan: `DELETE FROM whatsapp_messages WHERE timestamp < ?`,
|
|
608
|
+
deleteAll: `DELETE FROM whatsapp_messages`,
|
|
609
|
+
} as const;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- Statements for the whatsapp_messages table (see
|
|
2
|
+
-- repositories/whatsapp-messages-repo.ts for parameter order and mapping).
|
|
3
|
+
|
|
4
|
+
-- name: insert
|
|
5
|
+
INSERT OR IGNORE INTO whatsapp_messages
|
|
6
|
+
(chat_id, msg_id, wa_id, remote_jid, from_me, participant, sender_name,
|
|
7
|
+
text, timestamp, message_json)
|
|
8
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9
|
+
|
|
10
|
+
-- name: byMsgId
|
|
11
|
+
SELECT chat_id, msg_id, wa_id, remote_jid, from_me, participant, sender_name,
|
|
12
|
+
text, timestamp, message_json
|
|
13
|
+
FROM whatsapp_messages WHERE msg_id = ? LIMIT 1
|
|
14
|
+
|
|
15
|
+
-- name: byWaId
|
|
16
|
+
-- Newest first: a WhatsApp id re-delivered on reconnect maps to the row
|
|
17
|
+
-- that already exists for it.
|
|
18
|
+
SELECT chat_id, msg_id, wa_id, remote_jid, from_me, participant, sender_name,
|
|
19
|
+
text, timestamp, message_json
|
|
20
|
+
FROM whatsapp_messages WHERE wa_id = ? ORDER BY msg_id DESC LIMIT 1
|
|
21
|
+
|
|
22
|
+
-- name: maxMsgId
|
|
23
|
+
SELECT MAX(msg_id) AS max_id FROM whatsapp_messages
|
|
24
|
+
|
|
25
|
+
-- name: deleteOlderThan
|
|
26
|
+
DELETE FROM whatsapp_messages WHERE timestamp < ?
|
|
27
|
+
|
|
28
|
+
-- name: deleteAll
|
|
29
|
+
DELETE FROM whatsapp_messages
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent WhatsApp message keys.
|
|
3
|
+
*
|
|
4
|
+
* The WhatsApp frontend hands every message it sees a numeric Talon id
|
|
5
|
+
* and needs the WhatsApp key (and, for forwards and media re-downloads,
|
|
6
|
+
* the whole proto) back later. Its in-memory store is a bounded cache;
|
|
7
|
+
* this table is the record, so a reply, reaction or download addressed
|
|
8
|
+
* at a message from before the last restart still resolves.
|
|
9
|
+
*
|
|
10
|
+
* The proto travels as JSON — the frontend serialises and rehydrates it;
|
|
11
|
+
* storage never imports Baileys.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as repo from "./repositories/whatsapp-messages-repo.js";
|
|
15
|
+
|
|
16
|
+
export type { WhatsAppMessageRecord } from "./repositories/whatsapp-messages-repo.js";
|
|
17
|
+
|
|
18
|
+
/** How long a message's key (and retained proto) stays retrievable. */
|
|
19
|
+
const WHATSAPP_MESSAGE_RETENTION_MS = 180 * 24 * 60 * 60 * 1000;
|
|
20
|
+
|
|
21
|
+
/** Persist a message; a (chat, msg_id) already stored is left as is. */
|
|
22
|
+
export function saveWhatsAppMessage(record: repo.WhatsAppMessageRecord): void {
|
|
23
|
+
repo.insert(record);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getWhatsAppMessage(
|
|
27
|
+
msgId: number,
|
|
28
|
+
): repo.WhatsAppMessageRecord | undefined {
|
|
29
|
+
return repo.byMsgId(msgId);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getWhatsAppMessageByWaId(
|
|
33
|
+
waId: string,
|
|
34
|
+
): repo.WhatsAppMessageRecord | undefined {
|
|
35
|
+
return repo.byWaId(waId);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Highest Talon id ever persisted — the restart seed for the id counter. */
|
|
39
|
+
export function maxWhatsAppMsgId(): number | undefined {
|
|
40
|
+
return repo.maxMsgId();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Drop rows past the retention window; returns the number removed. */
|
|
44
|
+
export function pruneWhatsAppMessages(now = Date.now()): number {
|
|
45
|
+
return repo.deleteOlderThan(now - WHATSAPP_MESSAGE_RETENTION_MS);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Test seam: forget every persisted message. */
|
|
49
|
+
export function clearWhatsAppMessages(): void {
|
|
50
|
+
repo.deleteAll();
|
|
51
|
+
}
|