talon-agent 5.26.2 → 5.26.4
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/README.md +1 -0
- package/package.json +1 -1
- package/src/backend/remote-server/sessions.ts +82 -13
- package/src/backend/runtime/turn/handle-retry.ts +6 -5
- package/src/cli/commands/backup.ts +7 -3
- package/src/cli/commands/history.ts +152 -0
- package/src/cli/index.ts +6 -0
- package/src/core/backup/archive/verify.ts +53 -0
- package/src/core/backup/plan.ts +3 -0
- package/src/core/backup/retention/policy.ts +195 -0
- package/src/core/backup/scheduler.ts +10 -4
- package/src/core/backup/snapshot.ts +3 -0
- package/src/core/backup/status.ts +7 -0
- package/src/core/backup/store.ts +20 -25
- package/src/core/backup/types.ts +14 -0
- package/src/core/backup/upload.ts +37 -20
- package/src/core/config/index.ts +26 -1
- package/src/core/errors.ts +13 -2
- package/src/frontend/discord/admin.ts +2 -2
- package/src/frontend/discord/callbacks/components/backend-select.ts +4 -4
- package/src/frontend/native/chats/chat-lifecycle.ts +5 -2
- package/src/frontend/native/chats/chats.ts +14 -2
- package/src/frontend/native/chats/empty-chat-sweep.ts +4 -1
- package/src/frontend/native/chats/reset.ts +39 -17
- package/src/frontend/native/commands/session.ts +2 -2
- package/src/frontend/native/surface/models.ts +3 -3
- package/src/frontend/native/turn/turn-meta.ts +0 -6
- package/src/frontend/presentation/backup-panel.ts +7 -1
- package/src/frontend/presentation/model-commands.ts +8 -11
- package/src/frontend/presentation/session-status.ts +12 -13
- package/src/frontend/presentation/text-commands.ts +6 -9
- package/src/frontend/telegram/admin/sessions.ts +2 -1
- package/src/frontend/telegram/callbacks/model/backend.ts +5 -6
- package/src/frontend/whatsapp/commands.ts +5 -8
- package/src/storage/history.ts +167 -17
- package/src/storage/repositories/history-repo.ts +75 -7
- package/src/storage/sql/history.sql +45 -4
- package/src/storage/sql/schema.sql +13 -0
- package/src/storage/sql/statements.generated.ts +49 -5
- package/src/storage/turn-meta.ts +1 -1
|
@@ -9,10 +9,13 @@ INSERT OR IGNORE INTO history_messages
|
|
|
9
9
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
10
10
|
|
|
11
11
|
-- name: recent
|
|
12
|
+
-- The `id > ?` floor is the chat's context-reset marker (0 for none; see
|
|
13
|
+
-- chatState below): rows at or under it stay stored and searchable but
|
|
14
|
+
-- are no longer the chat's current conversation.
|
|
12
15
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
13
16
|
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
14
17
|
FROM history_messages
|
|
15
|
-
WHERE chat_id = ? ORDER BY id DESC LIMIT ?
|
|
18
|
+
WHERE chat_id = ? AND id > ? ORDER BY id DESC LIMIT ?
|
|
16
19
|
|
|
17
20
|
-- name: recentBefore
|
|
18
21
|
-- Scroll-back pagination: the window of messages strictly older than a
|
|
@@ -20,7 +23,7 @@ WHERE chat_id = ? ORDER BY id DESC LIMIT ?
|
|
|
20
23
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
21
24
|
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
22
25
|
FROM history_messages
|
|
23
|
-
WHERE chat_id = ? AND msg_id < ? ORDER BY id DESC LIMIT ?
|
|
26
|
+
WHERE chat_id = ? AND msg_id < ? AND id > ? ORDER BY id DESC LIMIT ?
|
|
24
27
|
|
|
25
28
|
-- name: recentBeforeTime
|
|
26
29
|
-- Time-cursor variant of recentBefore for the read_history `before` date
|
|
@@ -28,14 +31,52 @@ WHERE chat_id = ? AND msg_id < ? ORDER BY id DESC LIMIT ?
|
|
|
28
31
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
29
32
|
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
30
33
|
FROM history_messages
|
|
31
|
-
WHERE chat_id = ? AND timestamp < ? ORDER BY id DESC LIMIT ?
|
|
34
|
+
WHERE chat_id = ? AND timestamp < ? AND id > ? ORDER BY id DESC LIMIT ?
|
|
32
35
|
|
|
33
36
|
-- name: setFilePath
|
|
34
37
|
UPDATE history_messages SET file_path = ? WHERE chat_id = ? AND msg_id = ?
|
|
35
38
|
|
|
36
|
-
-- name:
|
|
39
|
+
-- name: purgeChat
|
|
40
|
+
-- The ONLY statement that deletes history rows. Reached solely through the
|
|
41
|
+
-- operator's explicit `talon history purge` (history.ts purgeChatHistory);
|
|
42
|
+
-- resets, backend switches and chat deletion never delete rows.
|
|
37
43
|
DELETE FROM history_messages WHERE chat_id = ?
|
|
38
44
|
|
|
45
|
+
-- name: purgeChatState
|
|
46
|
+
DELETE FROM history_chat_state WHERE chat_id = ?
|
|
47
|
+
|
|
48
|
+
-- name: chatState
|
|
49
|
+
SELECT cleared_through_id, cleared_at, hidden_at
|
|
50
|
+
FROM history_chat_state WHERE chat_id = ?
|
|
51
|
+
|
|
52
|
+
-- name: markCleared
|
|
53
|
+
-- Soft reset: move the chat's context floor to its newest stored row. The
|
|
54
|
+
-- rows stay; readers that build the bot's context skip everything at or
|
|
55
|
+
-- under the floor. Parameters: chat_id, chat_id, cleared_at.
|
|
56
|
+
INSERT INTO history_chat_state (chat_id, cleared_through_id, cleared_at)
|
|
57
|
+
VALUES (?, (SELECT COALESCE(MAX(id), 0) FROM history_messages WHERE chat_id = ?), ?)
|
|
58
|
+
ON CONFLICT(chat_id) DO UPDATE SET
|
|
59
|
+
cleared_through_id = excluded.cleared_through_id,
|
|
60
|
+
cleared_at = excluded.cleared_at
|
|
61
|
+
|
|
62
|
+
-- name: markHidden
|
|
63
|
+
-- Soft delete: a chat the user deleted is hidden (and its context floor
|
|
64
|
+
-- moved, so a chat that reappears under the same id starts fresh). The
|
|
65
|
+
-- rows stay. Parameters: chat_id, chat_id, cleared_at, hidden_at.
|
|
66
|
+
INSERT INTO history_chat_state (chat_id, cleared_through_id, cleared_at, hidden_at)
|
|
67
|
+
VALUES (?, (SELECT COALESCE(MAX(id), 0) FROM history_messages WHERE chat_id = ?), ?, ?)
|
|
68
|
+
ON CONFLICT(chat_id) DO UPDATE SET
|
|
69
|
+
cleared_through_id = excluded.cleared_through_id,
|
|
70
|
+
cleared_at = excluded.cleared_at,
|
|
71
|
+
hidden_at = excluded.hidden_at
|
|
72
|
+
|
|
73
|
+
-- name: hiddenChats
|
|
74
|
+
SELECT s.chat_id, s.hidden_at,
|
|
75
|
+
(SELECT COUNT(*) FROM history_messages h WHERE h.chat_id = s.chat_id) AS total
|
|
76
|
+
FROM history_chat_state s
|
|
77
|
+
WHERE s.hidden_at IS NOT NULL
|
|
78
|
+
ORDER BY s.hidden_at DESC
|
|
79
|
+
|
|
39
80
|
-- name: searchFts
|
|
40
81
|
-- The match param must already be a valid FTS5 expression
|
|
41
82
|
-- (see history.ts ftsQuery).
|
|
@@ -52,6 +52,19 @@ CREATE TRIGGER IF NOT EXISTS history_au AFTER UPDATE OF text, sender_name ON his
|
|
|
52
52
|
VALUES (new.id, new.text, new.sender_name);
|
|
53
53
|
END;
|
|
54
54
|
|
|
55
|
+
-- Per-chat history state. Chat history is never deleted by a reset, a
|
|
56
|
+
-- backend switch or a chat deletion: a reset records a context floor
|
|
57
|
+
-- (`cleared_through_id`, the newest history_messages.id at reset time) so
|
|
58
|
+
-- the bot's context starts fresh after it while every row stays stored
|
|
59
|
+
-- and searchable; deleting a chat in a client sets `hidden_at`. Only the
|
|
60
|
+
-- operator's explicit `talon history purge` removes rows.
|
|
61
|
+
CREATE TABLE IF NOT EXISTS history_chat_state (
|
|
62
|
+
chat_id TEXT PRIMARY KEY,
|
|
63
|
+
cleared_through_id INTEGER NOT NULL DEFAULT 0,
|
|
64
|
+
cleared_at INTEGER,
|
|
65
|
+
hidden_at INTEGER
|
|
66
|
+
);
|
|
67
|
+
|
|
55
68
|
-- Typed memory: one row per claim, with an FTS5 index over subject +
|
|
56
69
|
-- text. Kinds are lifecycles, not labels (docs/memory-persona-plan.md
|
|
57
70
|
-- §3.1): `directive` is durable human intent, `fact` is durable and
|
|
@@ -57,6 +57,19 @@ CREATE TRIGGER IF NOT EXISTS history_au AFTER UPDATE OF text, sender_name ON his
|
|
|
57
57
|
VALUES (new.id, new.text, new.sender_name);
|
|
58
58
|
END;
|
|
59
59
|
|
|
60
|
+
-- Per-chat history state. Chat history is never deleted by a reset, a
|
|
61
|
+
-- backend switch or a chat deletion: a reset records a context floor
|
|
62
|
+
-- (\`cleared_through_id\`, the newest history_messages.id at reset time) so
|
|
63
|
+
-- the bot's context starts fresh after it while every row stays stored
|
|
64
|
+
-- and searchable; deleting a chat in a client sets \`hidden_at\`. Only the
|
|
65
|
+
-- operator's explicit \`talon history purge\` removes rows.
|
|
66
|
+
CREATE TABLE IF NOT EXISTS history_chat_state (
|
|
67
|
+
chat_id TEXT PRIMARY KEY,
|
|
68
|
+
cleared_through_id INTEGER NOT NULL DEFAULT 0,
|
|
69
|
+
cleared_at INTEGER,
|
|
70
|
+
hidden_at INTEGER
|
|
71
|
+
);
|
|
72
|
+
|
|
60
73
|
-- Typed memory: one row per claim, with an FTS5 index over subject +
|
|
61
74
|
-- text. Kinds are lifecycles, not labels (docs/memory-persona-plan.md
|
|
62
75
|
-- §3.1): \`directive\` is durable human intent, \`fact\` is durable and
|
|
@@ -518,24 +531,55 @@ export const historySql = {
|
|
|
518
531
|
reply_to_msg_id, timestamp, media_type, sticker_file_id, file_path,
|
|
519
532
|
attachments)
|
|
520
533
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
521
|
-
recent:
|
|
534
|
+
recent: `-- The \`id > ?\` floor is the chat's context-reset marker (0 for none; see
|
|
535
|
+
-- chatState below): rows at or under it stay stored and searchable but
|
|
536
|
+
-- are no longer the chat's current conversation.
|
|
537
|
+
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
522
538
|
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
523
539
|
FROM history_messages
|
|
524
|
-
WHERE chat_id = ? ORDER BY id DESC LIMIT ?`,
|
|
540
|
+
WHERE chat_id = ? AND id > ? ORDER BY id DESC LIMIT ?`,
|
|
525
541
|
recentBefore: `-- Scroll-back pagination: the window of messages strictly older than a
|
|
526
542
|
-- given msg_id, newest-first (the repository reverses to chronological).
|
|
527
543
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
528
544
|
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
529
545
|
FROM history_messages
|
|
530
|
-
WHERE chat_id = ? AND msg_id < ? ORDER BY id DESC LIMIT ?`,
|
|
546
|
+
WHERE chat_id = ? AND msg_id < ? AND id > ? ORDER BY id DESC LIMIT ?`,
|
|
531
547
|
recentBeforeTime: `-- Time-cursor variant of recentBefore for the read_history \`before\` date
|
|
532
548
|
-- parameter: the newest \`limit\` messages strictly older than a timestamp.
|
|
533
549
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
|
534
550
|
timestamp, media_type, sticker_file_id, file_path, attachments
|
|
535
551
|
FROM history_messages
|
|
536
|
-
WHERE chat_id = ? AND timestamp < ? ORDER BY id DESC LIMIT ?`,
|
|
552
|
+
WHERE chat_id = ? AND timestamp < ? AND id > ? ORDER BY id DESC LIMIT ?`,
|
|
537
553
|
setFilePath: `UPDATE history_messages SET file_path = ? WHERE chat_id = ? AND msg_id = ?`,
|
|
538
|
-
|
|
554
|
+
purgeChat: `-- The ONLY statement that deletes history rows. Reached solely through the
|
|
555
|
+
-- operator's explicit \`talon history purge\` (history.ts purgeChatHistory);
|
|
556
|
+
-- resets, backend switches and chat deletion never delete rows.
|
|
557
|
+
DELETE FROM history_messages WHERE chat_id = ?`,
|
|
558
|
+
purgeChatState: `DELETE FROM history_chat_state WHERE chat_id = ?`,
|
|
559
|
+
chatState: `SELECT cleared_through_id, cleared_at, hidden_at
|
|
560
|
+
FROM history_chat_state WHERE chat_id = ?`,
|
|
561
|
+
markCleared: `-- Soft reset: move the chat's context floor to its newest stored row. The
|
|
562
|
+
-- rows stay; readers that build the bot's context skip everything at or
|
|
563
|
+
-- under the floor. Parameters: chat_id, chat_id, cleared_at.
|
|
564
|
+
INSERT INTO history_chat_state (chat_id, cleared_through_id, cleared_at)
|
|
565
|
+
VALUES (?, (SELECT COALESCE(MAX(id), 0) FROM history_messages WHERE chat_id = ?), ?)
|
|
566
|
+
ON CONFLICT(chat_id) DO UPDATE SET
|
|
567
|
+
cleared_through_id = excluded.cleared_through_id,
|
|
568
|
+
cleared_at = excluded.cleared_at`,
|
|
569
|
+
markHidden: `-- Soft delete: a chat the user deleted is hidden (and its context floor
|
|
570
|
+
-- moved, so a chat that reappears under the same id starts fresh). The
|
|
571
|
+
-- rows stay. Parameters: chat_id, chat_id, cleared_at, hidden_at.
|
|
572
|
+
INSERT INTO history_chat_state (chat_id, cleared_through_id, cleared_at, hidden_at)
|
|
573
|
+
VALUES (?, (SELECT COALESCE(MAX(id), 0) FROM history_messages WHERE chat_id = ?), ?, ?)
|
|
574
|
+
ON CONFLICT(chat_id) DO UPDATE SET
|
|
575
|
+
cleared_through_id = excluded.cleared_through_id,
|
|
576
|
+
cleared_at = excluded.cleared_at,
|
|
577
|
+
hidden_at = excluded.hidden_at`,
|
|
578
|
+
hiddenChats: `SELECT s.chat_id, s.hidden_at,
|
|
579
|
+
(SELECT COUNT(*) FROM history_messages h WHERE h.chat_id = s.chat_id) AS total
|
|
580
|
+
FROM history_chat_state s
|
|
581
|
+
WHERE s.hidden_at IS NOT NULL
|
|
582
|
+
ORDER BY s.hidden_at DESC`,
|
|
539
583
|
searchFts: `-- The match param must already be a valid FTS5 expression
|
|
540
584
|
-- (see history.ts ftsQuery).
|
|
541
585
|
SELECT msg_id, sender_id, sender_name, sender_handle, text, reply_to_msg_id,
|
package/src/storage/turn-meta.ts
CHANGED
|
@@ -121,7 +121,7 @@ export function getTurnMeta<T>(chatId: string, msgId: string): T | null {
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
/** Forget a chat
|
|
124
|
+
/** Forget a chat's turn meta — only the operator's history purge does. */
|
|
125
125
|
export function clearTurnMeta(chatId: string): void {
|
|
126
126
|
ensureLoaded();
|
|
127
127
|
try {
|