teleton 0.1.18 → 0.1.20

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 CHANGED
@@ -6,6 +6,7 @@
6
6
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
7
7
  <a href="https://nodejs.org/"><img src="https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen" alt="Node.js"></a>
8
8
  <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-5.7-blue" alt="TypeScript"></a>
9
+ <a href="https://ai.resistance.dog"><img src="https://img.shields.io/badge/Website-ai.resistance.dog-ff6600" alt="Website"></a>
9
10
  </p>
10
11
 
11
12
  ---
@@ -0,0 +1,184 @@
1
+ import {
2
+ TELETON_ROOT
3
+ } from "./chunk-EYWNOHMJ.js";
4
+
5
+ // src/session/transcript.ts
6
+ import {
7
+ appendFileSync,
8
+ readFileSync,
9
+ existsSync,
10
+ mkdirSync,
11
+ unlinkSync,
12
+ renameSync,
13
+ readdirSync,
14
+ statSync
15
+ } from "fs";
16
+ import { join } from "path";
17
+ var SESSIONS_DIR = join(TELETON_ROOT, "sessions");
18
+ function getTranscriptPath(sessionId) {
19
+ return join(SESSIONS_DIR, `${sessionId}.jsonl`);
20
+ }
21
+ function ensureSessionsDir() {
22
+ if (!existsSync(SESSIONS_DIR)) {
23
+ mkdirSync(SESSIONS_DIR, { recursive: true });
24
+ }
25
+ }
26
+ function appendToTranscript(sessionId, message) {
27
+ ensureSessionsDir();
28
+ const transcriptPath = getTranscriptPath(sessionId);
29
+ const line = JSON.stringify(message) + "\n";
30
+ try {
31
+ appendFileSync(transcriptPath, line, "utf-8");
32
+ } catch (error) {
33
+ console.error(`Failed to append to transcript ${sessionId}:`, error);
34
+ }
35
+ }
36
+ function extractToolCallIds(msg) {
37
+ const ids = /* @__PURE__ */ new Set();
38
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
39
+ for (const block of msg.content) {
40
+ const blockType = block.type;
41
+ if (blockType === "toolCall" || blockType === "tool_use") {
42
+ const id = block.id || block.toolCallId || block.tool_use_id;
43
+ if (id) ids.add(id);
44
+ }
45
+ }
46
+ }
47
+ return ids;
48
+ }
49
+ function sanitizeMessages(messages) {
50
+ const sanitized = [];
51
+ let pendingToolCallIds = /* @__PURE__ */ new Set();
52
+ let removedCount = 0;
53
+ for (let i = 0; i < messages.length; i++) {
54
+ const msg = messages[i];
55
+ if (msg.role === "assistant") {
56
+ const newToolIds = extractToolCallIds(msg);
57
+ if (pendingToolCallIds.size > 0 && newToolIds.size > 0) {
58
+ console.warn(
59
+ `\u26A0\uFE0F Found ${pendingToolCallIds.size} pending tool results that were never received`
60
+ );
61
+ }
62
+ pendingToolCallIds = newToolIds;
63
+ sanitized.push(msg);
64
+ } else if (msg.role === "toolResult" || msg.role === "tool_result") {
65
+ const toolCallId = msg.toolCallId || msg.tool_use_id || msg.tool_call_id;
66
+ if (toolCallId && pendingToolCallIds.has(toolCallId)) {
67
+ pendingToolCallIds.delete(toolCallId);
68
+ sanitized.push(msg);
69
+ } else {
70
+ removedCount++;
71
+ console.warn(
72
+ `\u{1F9F9} Removing out-of-order/orphaned toolResult: ${toolCallId?.slice(0, 20)}...`
73
+ );
74
+ continue;
75
+ }
76
+ } else if (msg.role === "user") {
77
+ if (pendingToolCallIds.size > 0) {
78
+ console.warn(
79
+ `\u26A0\uFE0F User message arrived while ${pendingToolCallIds.size} tool results pending - marking them as orphaned`
80
+ );
81
+ pendingToolCallIds.clear();
82
+ }
83
+ sanitized.push(msg);
84
+ } else {
85
+ sanitized.push(msg);
86
+ }
87
+ }
88
+ if (removedCount > 0) {
89
+ console.log(`\u{1F9F9} Sanitized ${removedCount} orphaned/out-of-order toolResult(s) from transcript`);
90
+ }
91
+ return sanitized;
92
+ }
93
+ function readTranscript(sessionId) {
94
+ const transcriptPath = getTranscriptPath(sessionId);
95
+ if (!existsSync(transcriptPath)) {
96
+ return [];
97
+ }
98
+ try {
99
+ const content = readFileSync(transcriptPath, "utf-8");
100
+ const lines = content.trim().split("\n").filter(Boolean);
101
+ const messages = lines.map((line) => JSON.parse(line));
102
+ return sanitizeMessages(messages);
103
+ } catch (error) {
104
+ console.error(`Failed to read transcript ${sessionId}:`, error);
105
+ return [];
106
+ }
107
+ }
108
+ function transcriptExists(sessionId) {
109
+ return existsSync(getTranscriptPath(sessionId));
110
+ }
111
+ function getTranscriptSize(sessionId) {
112
+ try {
113
+ const messages = readTranscript(sessionId);
114
+ return messages.length;
115
+ } catch {
116
+ return 0;
117
+ }
118
+ }
119
+ function deleteTranscript(sessionId) {
120
+ const transcriptPath = getTranscriptPath(sessionId);
121
+ if (!existsSync(transcriptPath)) {
122
+ return false;
123
+ }
124
+ try {
125
+ unlinkSync(transcriptPath);
126
+ console.log(`\u{1F5D1}\uFE0F Deleted transcript: ${sessionId}`);
127
+ return true;
128
+ } catch (error) {
129
+ console.error(`Failed to delete transcript ${sessionId}:`, error);
130
+ return false;
131
+ }
132
+ }
133
+ function archiveTranscript(sessionId) {
134
+ const transcriptPath = getTranscriptPath(sessionId);
135
+ const timestamp = Date.now();
136
+ const archivePath = `${transcriptPath}.${timestamp}.archived`;
137
+ if (!existsSync(transcriptPath)) {
138
+ return false;
139
+ }
140
+ try {
141
+ renameSync(transcriptPath, archivePath);
142
+ console.log(`\u{1F4E6} Archived transcript: ${sessionId} \u2192 ${timestamp}.archived`);
143
+ return true;
144
+ } catch (error) {
145
+ console.error(`Failed to archive transcript ${sessionId}:`, error);
146
+ return false;
147
+ }
148
+ }
149
+ function cleanupOldTranscripts(maxAgeDays = 30) {
150
+ if (!existsSync(SESSIONS_DIR)) return 0;
151
+ const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
152
+ let deleted = 0;
153
+ try {
154
+ for (const file of readdirSync(SESSIONS_DIR)) {
155
+ if (!file.endsWith(".jsonl") && !file.endsWith(".archived")) continue;
156
+ const filePath = join(SESSIONS_DIR, file);
157
+ try {
158
+ const mtime = statSync(filePath).mtimeMs;
159
+ if (mtime < cutoff) {
160
+ unlinkSync(filePath);
161
+ deleted++;
162
+ }
163
+ } catch {
164
+ }
165
+ }
166
+ } catch (error) {
167
+ console.error("Failed to cleanup old transcripts:", error);
168
+ }
169
+ if (deleted > 0) {
170
+ console.log(`\u{1F9F9} Cleaned up ${deleted} transcript(s) older than ${maxAgeDays} days`);
171
+ }
172
+ return deleted;
173
+ }
174
+
175
+ export {
176
+ getTranscriptPath,
177
+ appendToTranscript,
178
+ readTranscript,
179
+ transcriptExists,
180
+ getTranscriptSize,
181
+ deleteTranscript,
182
+ archiveTranscript,
183
+ cleanupOldTranscripts
184
+ };