teleton 0.8.1 → 0.8.2
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/{chunk-5FNWBZ5K.js → chunk-2IZU3REP.js} +147 -82
- package/dist/chunk-3UFPFWYP.js +12 -0
- package/dist/{chunk-KVXV7EF7.js → chunk-55SKE6YH.js} +2 -2
- package/dist/{setup-server-32XGDPE6.js → chunk-57URFK6M.js} +7 -206
- package/dist/chunk-5SEMA47R.js +75 -0
- package/dist/{chunk-S6PHGKOC.js → chunk-7YKSXOQQ.js} +10 -2
- package/dist/{chunk-UP55PXFH.js → chunk-C4NKJT2Z.js} +8 -0
- package/dist/{chunk-CGOXE4WP.js → chunk-GGXJLMOH.js} +404 -730
- package/dist/{chunk-QBHRXLZS.js → chunk-H7MFXJZK.js} +2 -2
- package/dist/{chunk-3S4GGLLR.js → chunk-HEDJCLA6.js} +58 -19
- package/dist/{chunk-AYWEJCDB.js → chunk-J73TA3UM.js} +7 -7
- package/dist/{chunk-QV2GLOTK.js → chunk-LC4TV3KL.js} +1 -1
- package/dist/{chunk-RCMD3U65.js → chunk-NQ6FZKCE.js} +13 -0
- package/dist/{chunk-7U7BOHCL.js → chunk-VYKW7FMV.js} +147 -63
- package/dist/chunk-W25Z7CM6.js +487 -0
- package/dist/{chunk-OJCLKU5Z.js → chunk-WFTC3JJW.js} +16 -0
- package/dist/{server-3FHI2SEB.js → chunk-XBSCYMKM.js} +23 -369
- package/dist/{chunk-PHSAHTK4.js → chunk-YOSUPUAJ.js} +75 -7
- package/dist/cli/index.js +61 -17
- package/dist/{client-MPHPIZB6.js → client-YOOHI776.js} +4 -4
- package/dist/{get-my-gifts-CC6HAVWB.js → get-my-gifts-Y7EN7RK4.js} +3 -3
- package/dist/index.js +14 -13
- package/dist/{memory-UBHM7ILG.js → memory-Q6EWGK2S.js} +6 -4
- package/dist/memory-hook-WUXJNVT5.js +18 -0
- package/dist/{migrate-UBBEJ5BL.js → migrate-WFU6COBN.js} +4 -4
- package/dist/server-GYZXKIKU.js +787 -0
- package/dist/server-YODFBZKG.js +392 -0
- package/dist/setup-server-IZBUOJRU.js +215 -0
- package/dist/{store-M5IMUQCL.js → store-7M4XV6M5.js} +5 -5
- package/dist/{task-dependency-resolver-RR2O5S7B.js → task-dependency-resolver-L6UUMTHK.js} +2 -2
- package/dist/{task-executor-6W5HRX5C.js → task-executor-XBNJLUCS.js} +2 -2
- package/dist/{tool-adapter-IH5VGBOO.js → tool-adapter-IVX2XQJE.js} +1 -1
- package/dist/{tool-index-PMAOXWUA.js → tool-index-NYH57UWP.js} +3 -3
- package/dist/{transcript-NGDPSNIH.js → transcript-IM7G25OS.js} +2 -2
- package/package.json +4 -2
- package/dist/chunk-XBE4JB7C.js +0 -8
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
getWalletBalance,
|
|
8
8
|
invalidateTonClientCache,
|
|
9
9
|
loadWallet
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-7YKSXOQQ.js";
|
|
11
11
|
import {
|
|
12
12
|
getOrCreateSession,
|
|
13
13
|
getSession,
|
|
@@ -15,7 +15,15 @@ import {
|
|
|
15
15
|
resetSessionWithPolicy,
|
|
16
16
|
shouldResetSession,
|
|
17
17
|
updateSession
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-55SKE6YH.js";
|
|
19
|
+
import {
|
|
20
|
+
saveSessionMemory,
|
|
21
|
+
summarizeWithFallback
|
|
22
|
+
} from "./chunk-W25Z7CM6.js";
|
|
23
|
+
import {
|
|
24
|
+
getErrorMessage,
|
|
25
|
+
isHttpError
|
|
26
|
+
} from "./chunk-3UFPFWYP.js";
|
|
19
27
|
import {
|
|
20
28
|
Mi,
|
|
21
29
|
Mn,
|
|
@@ -26,41 +34,33 @@ import {
|
|
|
26
34
|
ut,
|
|
27
35
|
ye
|
|
28
36
|
} from "./chunk-7TECSLJ4.js";
|
|
29
|
-
import {
|
|
30
|
-
getErrorMessage
|
|
31
|
-
} from "./chunk-XBE4JB7C.js";
|
|
32
37
|
import {
|
|
33
38
|
chatWithContext,
|
|
34
39
|
getEffectiveApiKey,
|
|
35
40
|
getProviderModel,
|
|
36
|
-
getUtilityModel,
|
|
37
41
|
loadContextFromTranscript
|
|
38
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-J73TA3UM.js";
|
|
39
43
|
import {
|
|
40
44
|
getProviderMetadata,
|
|
41
45
|
getSupportedProviders
|
|
42
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-YOSUPUAJ.js";
|
|
43
47
|
import {
|
|
44
48
|
appendToTranscript,
|
|
45
49
|
archiveTranscript,
|
|
46
50
|
transcriptExists
|
|
47
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-LC4TV3KL.js";
|
|
48
52
|
import {
|
|
49
53
|
ContextBuilder,
|
|
50
54
|
createDbWrapper,
|
|
51
55
|
getDatabase,
|
|
52
56
|
migrateFromMainDb,
|
|
53
57
|
openModuleDb
|
|
54
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-VYKW7FMV.js";
|
|
55
59
|
import {
|
|
56
60
|
GECKOTERMINAL_API_URL,
|
|
57
61
|
tonapiFetch
|
|
58
62
|
} from "./chunk-VFA7QMCZ.js";
|
|
59
63
|
import {
|
|
60
|
-
ADAPTIVE_CHUNK_RATIO_BASE,
|
|
61
|
-
ADAPTIVE_CHUNK_RATIO_MIN,
|
|
62
|
-
ADAPTIVE_CHUNK_RATIO_TRIGGER,
|
|
63
|
-
CHARS_PER_TOKEN_ESTIMATE,
|
|
64
64
|
COMPACTION_KEEP_RECENT,
|
|
65
65
|
COMPACTION_MAX_MESSAGES,
|
|
66
66
|
COMPACTION_MAX_TOKENS_RATIO,
|
|
@@ -72,21 +72,18 @@ import {
|
|
|
72
72
|
DEFAULT_MAX_SUMMARY_TOKENS,
|
|
73
73
|
DEFAULT_MAX_TOKENS,
|
|
74
74
|
DEFAULT_SOFT_THRESHOLD_TOKENS,
|
|
75
|
-
|
|
75
|
+
EMBEDDING_QUERY_MAX_CHARS,
|
|
76
76
|
FALLBACK_SOFT_THRESHOLD_TOKENS,
|
|
77
77
|
MASKING_KEEP_RECENT_COUNT,
|
|
78
78
|
MAX_TOOL_RESULT_SIZE,
|
|
79
79
|
MEMORY_FLUSH_RECENT_MESSAGES,
|
|
80
|
-
OVERSIZED_MESSAGE_RATIO,
|
|
81
80
|
PAYMENT_TOLERANCE_RATIO,
|
|
82
81
|
RATE_LIMIT_MAX_RETRIES,
|
|
83
82
|
RESULT_TRUNCATION_KEEP_CHARS,
|
|
84
83
|
RESULT_TRUNCATION_THRESHOLD,
|
|
85
84
|
SERVER_ERROR_MAX_RETRIES,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
TOKEN_ESTIMATE_SAFETY_MARGIN
|
|
89
|
-
} from "./chunk-UP55PXFH.js";
|
|
85
|
+
TOOL_CONCURRENCY_LIMIT
|
|
86
|
+
} from "./chunk-C4NKJT2Z.js";
|
|
90
87
|
import {
|
|
91
88
|
fetchWithTimeout
|
|
92
89
|
} from "./chunk-XQUHC3JZ.js";
|
|
@@ -107,7 +104,7 @@ import {
|
|
|
107
104
|
} from "./chunk-EYWNOHMJ.js";
|
|
108
105
|
import {
|
|
109
106
|
createLogger
|
|
110
|
-
} from "./chunk-
|
|
107
|
+
} from "./chunk-NQ6FZKCE.js";
|
|
111
108
|
|
|
112
109
|
// src/config/loader.ts
|
|
113
110
|
import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
|
|
@@ -193,6 +190,17 @@ Run 'teleton setup' to create one.`);
|
|
|
193
190
|
);
|
|
194
191
|
}
|
|
195
192
|
}
|
|
193
|
+
if (process.env.TELETON_API_ENABLED) {
|
|
194
|
+
if (!config.api) config.api = { enabled: false, port: 7778, key_hash: "", allowed_ips: [] };
|
|
195
|
+
config.api.enabled = process.env.TELETON_API_ENABLED === "true";
|
|
196
|
+
}
|
|
197
|
+
if (process.env.TELETON_API_PORT) {
|
|
198
|
+
const port = parseInt(process.env.TELETON_API_PORT, 10);
|
|
199
|
+
if (!isNaN(port) && port >= 1024 && port <= 65535) {
|
|
200
|
+
if (!config.api) config.api = { enabled: false, port: 7778, key_hash: "", allowed_ips: [] };
|
|
201
|
+
config.api.port = port;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
196
204
|
if (process.env.TELETON_BASE_URL) {
|
|
197
205
|
try {
|
|
198
206
|
new URL(process.env.TELETON_BASE_URL);
|
|
@@ -366,7 +374,7 @@ function appendToDailyLog(content, date = /* @__PURE__ */ new Date()) {
|
|
|
366
374
|
const header = `# Daily Log - ${formatDate(date)}
|
|
367
375
|
|
|
368
376
|
`;
|
|
369
|
-
appendFileSync(logPath, header, "utf-8");
|
|
377
|
+
appendFileSync(logPath, header, { encoding: "utf-8", mode: 384 });
|
|
370
378
|
}
|
|
371
379
|
const entry = `## ${timestamp}
|
|
372
380
|
|
|
@@ -741,397 +749,6 @@ ${body}`;
|
|
|
741
749
|
|
|
742
750
|
// src/memory/compaction.ts
|
|
743
751
|
import { randomUUID } from "crypto";
|
|
744
|
-
|
|
745
|
-
// src/memory/ai-summarization.ts
|
|
746
|
-
import {
|
|
747
|
-
complete
|
|
748
|
-
} from "@mariozechner/pi-ai";
|
|
749
|
-
var log3 = createLogger("Memory");
|
|
750
|
-
function estimateMessageTokens(content) {
|
|
751
|
-
return Math.ceil(content.length / CHARS_PER_TOKEN_ESTIMATE * TOKEN_ESTIMATE_SAFETY_MARGIN);
|
|
752
|
-
}
|
|
753
|
-
function splitMessagesByTokens(messages, maxChunkTokens) {
|
|
754
|
-
if (messages.length === 0) {
|
|
755
|
-
return [];
|
|
756
|
-
}
|
|
757
|
-
const chunks = [];
|
|
758
|
-
let currentChunk = [];
|
|
759
|
-
let currentTokens = 0;
|
|
760
|
-
for (const message of messages) {
|
|
761
|
-
const content = extractMessageContent(message);
|
|
762
|
-
const messageTokens = estimateMessageTokens(content);
|
|
763
|
-
if (currentChunk.length > 0 && currentTokens + messageTokens > maxChunkTokens) {
|
|
764
|
-
chunks.push(currentChunk);
|
|
765
|
-
currentChunk = [];
|
|
766
|
-
currentTokens = 0;
|
|
767
|
-
}
|
|
768
|
-
currentChunk.push(message);
|
|
769
|
-
currentTokens += messageTokens;
|
|
770
|
-
if (messageTokens > maxChunkTokens && currentChunk.length === 1) {
|
|
771
|
-
chunks.push(currentChunk);
|
|
772
|
-
currentChunk = [];
|
|
773
|
-
currentTokens = 0;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
if (currentChunk.length > 0) {
|
|
777
|
-
chunks.push(currentChunk);
|
|
778
|
-
}
|
|
779
|
-
return chunks;
|
|
780
|
-
}
|
|
781
|
-
function extractMessageContent(message) {
|
|
782
|
-
if (message.role === "user") {
|
|
783
|
-
return typeof message.content === "string" ? message.content : "[complex content]";
|
|
784
|
-
} else if (message.role === "assistant") {
|
|
785
|
-
return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
786
|
-
}
|
|
787
|
-
return "";
|
|
788
|
-
}
|
|
789
|
-
function formatMessagesForSummary(messages) {
|
|
790
|
-
const formatted = [];
|
|
791
|
-
for (const msg of messages) {
|
|
792
|
-
if (msg.role === "user") {
|
|
793
|
-
const content = typeof msg.content === "string" ? msg.content : "[complex]";
|
|
794
|
-
const bodyMatch = content.match(/\] (.+)/s);
|
|
795
|
-
const body = bodyMatch ? bodyMatch[1] : content;
|
|
796
|
-
formatted.push(`User: ${body}`);
|
|
797
|
-
} else if (msg.role === "assistant") {
|
|
798
|
-
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
799
|
-
if (textBlocks.length > 0) {
|
|
800
|
-
const text = textBlocks.map((b) => b.text).join("\n");
|
|
801
|
-
formatted.push(`Assistant: ${text}`);
|
|
802
|
-
}
|
|
803
|
-
const toolCalls = msg.content.filter((b) => b.type === "toolCall");
|
|
804
|
-
if (toolCalls.length > 0) {
|
|
805
|
-
const toolNames = toolCalls.map((b) => b.name).join(", ");
|
|
806
|
-
formatted.push(`[Used tools: ${toolNames}]`);
|
|
807
|
-
}
|
|
808
|
-
} else if (msg.role === "toolResult") {
|
|
809
|
-
formatted.push(`[Tool result: ${msg.toolName}]`);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
return formatted.join("\n\n");
|
|
813
|
-
}
|
|
814
|
-
function isOversizedForSummary(message, contextWindow) {
|
|
815
|
-
const content = extractMessageContent(message);
|
|
816
|
-
const tokens = estimateMessageTokens(content);
|
|
817
|
-
return tokens > contextWindow * OVERSIZED_MESSAGE_RATIO;
|
|
818
|
-
}
|
|
819
|
-
function computeAdaptiveChunkRatio(messages, contextWindow) {
|
|
820
|
-
const BASE_CHUNK_RATIO = ADAPTIVE_CHUNK_RATIO_BASE;
|
|
821
|
-
const MIN_CHUNK_RATIO = ADAPTIVE_CHUNK_RATIO_MIN;
|
|
822
|
-
if (messages.length === 0) {
|
|
823
|
-
return BASE_CHUNK_RATIO;
|
|
824
|
-
}
|
|
825
|
-
let totalTokens = 0;
|
|
826
|
-
for (const msg of messages) {
|
|
827
|
-
const content = extractMessageContent(msg);
|
|
828
|
-
totalTokens += estimateMessageTokens(content);
|
|
829
|
-
}
|
|
830
|
-
const avgTokens = totalTokens / messages.length;
|
|
831
|
-
const avgRatio = avgTokens / contextWindow;
|
|
832
|
-
if (avgRatio > ADAPTIVE_CHUNK_RATIO_TRIGGER) {
|
|
833
|
-
const reduction = Math.min(avgRatio * 2, BASE_CHUNK_RATIO - MIN_CHUNK_RATIO);
|
|
834
|
-
return Math.max(MIN_CHUNK_RATIO, BASE_CHUNK_RATIO - reduction);
|
|
835
|
-
}
|
|
836
|
-
return BASE_CHUNK_RATIO;
|
|
837
|
-
}
|
|
838
|
-
async function summarizeViaClaude(params) {
|
|
839
|
-
const provider = params.provider || "anthropic";
|
|
840
|
-
const model = getUtilityModel(provider, params.utilityModel);
|
|
841
|
-
const maxTokens = params.maxSummaryTokens ?? DEFAULT_SUMMARY_FALLBACK_TOKENS;
|
|
842
|
-
const formatted = formatMessagesForSummary(params.messages);
|
|
843
|
-
if (!formatted.trim()) {
|
|
844
|
-
return "No conversation content to summarize.";
|
|
845
|
-
}
|
|
846
|
-
const defaultInstructions = `Summarize this conversation concisely. Focus on:
|
|
847
|
-
- Key decisions made
|
|
848
|
-
- Action items and TODOs
|
|
849
|
-
- Open questions
|
|
850
|
-
- Important context and constraints
|
|
851
|
-
- Technical details that matter
|
|
852
|
-
|
|
853
|
-
Be specific but concise. Preserve critical information.`;
|
|
854
|
-
const instructions = params.customInstructions ? `${defaultInstructions}
|
|
855
|
-
|
|
856
|
-
Additional focus:
|
|
857
|
-
${params.customInstructions}` : defaultInstructions;
|
|
858
|
-
try {
|
|
859
|
-
const context = {
|
|
860
|
-
messages: [
|
|
861
|
-
{
|
|
862
|
-
role: "user",
|
|
863
|
-
content: `${instructions}
|
|
864
|
-
|
|
865
|
-
Conversation:
|
|
866
|
-
${formatted}`,
|
|
867
|
-
timestamp: Date.now()
|
|
868
|
-
}
|
|
869
|
-
]
|
|
870
|
-
};
|
|
871
|
-
const response = await complete(model, context, {
|
|
872
|
-
apiKey: params.apiKey,
|
|
873
|
-
maxTokens
|
|
874
|
-
});
|
|
875
|
-
const textContent = response.content.find((block) => block.type === "text");
|
|
876
|
-
const summary = textContent?.type === "text" ? textContent.text : "";
|
|
877
|
-
return summary.trim() || "Unable to generate summary.";
|
|
878
|
-
} catch (error) {
|
|
879
|
-
log3.error({ err: error }, "Summarization error");
|
|
880
|
-
throw new Error(`Summarization failed: ${getErrorMessage(error)}`);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
async function summarizeInChunks(params) {
|
|
884
|
-
if (params.messages.length === 0) {
|
|
885
|
-
return {
|
|
886
|
-
summary: "No messages to summarize.",
|
|
887
|
-
tokensUsed: 0,
|
|
888
|
-
chunksProcessed: 0
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
const chunks = splitMessagesByTokens(params.messages, params.maxChunkTokens);
|
|
892
|
-
log3.info(`Splitting into ${chunks.length} chunks for summarization`);
|
|
893
|
-
if (chunks.length === 1) {
|
|
894
|
-
const summary = await summarizeViaClaude({
|
|
895
|
-
messages: chunks[0],
|
|
896
|
-
apiKey: params.apiKey,
|
|
897
|
-
maxSummaryTokens: params.maxSummaryTokens,
|
|
898
|
-
customInstructions: params.customInstructions,
|
|
899
|
-
provider: params.provider,
|
|
900
|
-
utilityModel: params.utilityModel
|
|
901
|
-
});
|
|
902
|
-
return {
|
|
903
|
-
summary,
|
|
904
|
-
tokensUsed: estimateMessageTokens(summary),
|
|
905
|
-
chunksProcessed: 1
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
const partialSummaries = [];
|
|
909
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
910
|
-
log3.info(`Summarizing chunk ${i + 1}/${chunks.length} (${chunks[i].length} messages)`);
|
|
911
|
-
const partial = await summarizeViaClaude({
|
|
912
|
-
messages: chunks[i],
|
|
913
|
-
apiKey: params.apiKey,
|
|
914
|
-
maxSummaryTokens: Math.floor(
|
|
915
|
-
(params.maxSummaryTokens ?? DEFAULT_SUMMARY_FALLBACK_TOKENS) / 2
|
|
916
|
-
),
|
|
917
|
-
customInstructions: params.customInstructions,
|
|
918
|
-
provider: params.provider,
|
|
919
|
-
utilityModel: params.utilityModel
|
|
920
|
-
});
|
|
921
|
-
partialSummaries.push(partial);
|
|
922
|
-
}
|
|
923
|
-
log3.info(`Merging ${partialSummaries.length} partial summaries`);
|
|
924
|
-
const provider = params.provider || "anthropic";
|
|
925
|
-
const model = getUtilityModel(provider, params.utilityModel);
|
|
926
|
-
const mergeContext = {
|
|
927
|
-
messages: [
|
|
928
|
-
{
|
|
929
|
-
role: "user",
|
|
930
|
-
content: `Merge these partial conversation summaries into one cohesive summary.
|
|
931
|
-
Preserve all key decisions, action items, open questions, and important context.
|
|
932
|
-
Do not add new information - only synthesize what's provided.
|
|
933
|
-
|
|
934
|
-
Partial summaries:
|
|
935
|
-
|
|
936
|
-
${partialSummaries.map((s, i) => `Part ${i + 1}:
|
|
937
|
-
${s}`).join("\n\n---\n\n")}`,
|
|
938
|
-
timestamp: Date.now()
|
|
939
|
-
}
|
|
940
|
-
]
|
|
941
|
-
};
|
|
942
|
-
const mergeResponse = await complete(model, mergeContext, {
|
|
943
|
-
apiKey: params.apiKey,
|
|
944
|
-
maxTokens: params.maxSummaryTokens ?? DEFAULT_SUMMARY_FALLBACK_TOKENS
|
|
945
|
-
});
|
|
946
|
-
const textContent = mergeResponse.content.find((block) => block.type === "text");
|
|
947
|
-
const merged = textContent?.type === "text" ? textContent.text : "";
|
|
948
|
-
return {
|
|
949
|
-
summary: merged.trim() || "Unable to merge summaries.",
|
|
950
|
-
tokensUsed: estimateMessageTokens(merged),
|
|
951
|
-
chunksProcessed: chunks.length
|
|
952
|
-
};
|
|
953
|
-
}
|
|
954
|
-
async function summarizeWithFallback(params) {
|
|
955
|
-
if (params.messages.length === 0) {
|
|
956
|
-
return {
|
|
957
|
-
summary: "No messages to summarize.",
|
|
958
|
-
tokensUsed: 0,
|
|
959
|
-
chunksProcessed: 0
|
|
960
|
-
};
|
|
961
|
-
}
|
|
962
|
-
const chunkRatio = computeAdaptiveChunkRatio(params.messages, params.contextWindow);
|
|
963
|
-
const maxChunkTokens = Math.floor(params.contextWindow * chunkRatio);
|
|
964
|
-
log3.info(
|
|
965
|
-
`AI Summarization: ${params.messages.length} messages, chunk ratio: ${(chunkRatio * 100).toFixed(0)}%`
|
|
966
|
-
);
|
|
967
|
-
try {
|
|
968
|
-
return await summarizeInChunks({
|
|
969
|
-
messages: params.messages,
|
|
970
|
-
apiKey: params.apiKey,
|
|
971
|
-
maxChunkTokens,
|
|
972
|
-
maxSummaryTokens: params.maxSummaryTokens,
|
|
973
|
-
customInstructions: params.customInstructions,
|
|
974
|
-
provider: params.provider,
|
|
975
|
-
utilityModel: params.utilityModel
|
|
976
|
-
});
|
|
977
|
-
} catch (fullError) {
|
|
978
|
-
log3.warn(
|
|
979
|
-
`Full summarization failed: ${fullError instanceof Error ? fullError.message : String(fullError)}`
|
|
980
|
-
);
|
|
981
|
-
}
|
|
982
|
-
const smallMessages = [];
|
|
983
|
-
const oversizedNotes = [];
|
|
984
|
-
for (const msg of params.messages) {
|
|
985
|
-
if (isOversizedForSummary(msg, params.contextWindow)) {
|
|
986
|
-
const content = extractMessageContent(msg);
|
|
987
|
-
const tokens = estimateMessageTokens(content);
|
|
988
|
-
oversizedNotes.push(
|
|
989
|
-
`[Large ${msg.role} message (~${Math.round(tokens / 1e3)}K tokens) omitted from summary]`
|
|
990
|
-
);
|
|
991
|
-
} else {
|
|
992
|
-
smallMessages.push(msg);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
log3.info(
|
|
996
|
-
`Fallback: Processing ${smallMessages.length} messages, skipping ${oversizedNotes.length} oversized`
|
|
997
|
-
);
|
|
998
|
-
if (smallMessages.length > 0) {
|
|
999
|
-
try {
|
|
1000
|
-
const result = await summarizeInChunks({
|
|
1001
|
-
messages: smallMessages,
|
|
1002
|
-
apiKey: params.apiKey,
|
|
1003
|
-
maxChunkTokens,
|
|
1004
|
-
maxSummaryTokens: params.maxSummaryTokens,
|
|
1005
|
-
customInstructions: params.customInstructions,
|
|
1006
|
-
provider: params.provider,
|
|
1007
|
-
utilityModel: params.utilityModel
|
|
1008
|
-
});
|
|
1009
|
-
const notes = oversizedNotes.length > 0 ? `
|
|
1010
|
-
|
|
1011
|
-
${oversizedNotes.join("\n")}` : "";
|
|
1012
|
-
return {
|
|
1013
|
-
summary: result.summary + notes,
|
|
1014
|
-
tokensUsed: result.tokensUsed,
|
|
1015
|
-
chunksProcessed: result.chunksProcessed
|
|
1016
|
-
};
|
|
1017
|
-
} catch (partialError) {
|
|
1018
|
-
log3.warn(
|
|
1019
|
-
`Partial summarization also failed: ${partialError instanceof Error ? partialError.message : String(partialError)}`
|
|
1020
|
-
);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
const note = `Context contained ${params.messages.length} messages (${oversizedNotes.length} were oversized). AI summarization unavailable due to size constraints. Recent conversation history was preserved.`;
|
|
1024
|
-
return {
|
|
1025
|
-
summary: note,
|
|
1026
|
-
tokensUsed: estimateMessageTokens(note),
|
|
1027
|
-
chunksProcessed: 0
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
// src/session/memory-hook.ts
|
|
1032
|
-
import { writeFile, mkdir } from "fs/promises";
|
|
1033
|
-
import { join as join3 } from "path";
|
|
1034
|
-
import { complete as complete2 } from "@mariozechner/pi-ai";
|
|
1035
|
-
var log4 = createLogger("Session");
|
|
1036
|
-
async function generateSlugViaClaude(params) {
|
|
1037
|
-
const provider = params.provider || "anthropic";
|
|
1038
|
-
const model = getUtilityModel(provider, params.utilityModel);
|
|
1039
|
-
const formatted = formatMessagesForSummary(params.messages.slice(-SESSION_SLUG_RECENT_MESSAGES));
|
|
1040
|
-
if (!formatted.trim()) {
|
|
1041
|
-
return "empty-session";
|
|
1042
|
-
}
|
|
1043
|
-
try {
|
|
1044
|
-
const context = {
|
|
1045
|
-
messages: [
|
|
1046
|
-
{
|
|
1047
|
-
role: "user",
|
|
1048
|
-
content: `Generate a short, descriptive slug (2-4 words, kebab-case) for this conversation.
|
|
1049
|
-
Examples: "gift-transfer-fix", "context-overflow-debug", "telegram-integration"
|
|
1050
|
-
|
|
1051
|
-
Conversation:
|
|
1052
|
-
${formatted}
|
|
1053
|
-
|
|
1054
|
-
Slug:`,
|
|
1055
|
-
timestamp: Date.now()
|
|
1056
|
-
}
|
|
1057
|
-
]
|
|
1058
|
-
};
|
|
1059
|
-
const response = await complete2(model, context, {
|
|
1060
|
-
apiKey: params.apiKey,
|
|
1061
|
-
maxTokens: SESSION_SLUG_MAX_TOKENS
|
|
1062
|
-
});
|
|
1063
|
-
const textContent = response.content.find((block) => block.type === "text");
|
|
1064
|
-
const slug = textContent?.type === "text" ? textContent.text.trim() : "";
|
|
1065
|
-
return slug.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 50) || "session";
|
|
1066
|
-
} catch (error) {
|
|
1067
|
-
log4.warn({ err: error }, "Slug generation failed, using fallback");
|
|
1068
|
-
const now = /* @__PURE__ */ new Date();
|
|
1069
|
-
return `session-${now.getHours().toString().padStart(2, "0")}${now.getMinutes().toString().padStart(2, "0")}`;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
async function saveSessionMemory(params) {
|
|
1073
|
-
try {
|
|
1074
|
-
const { TELETON_ROOT: TELETON_ROOT2 } = await import("./paths-XA2RJH4S.js");
|
|
1075
|
-
const memoryDir = join3(TELETON_ROOT2, "memory");
|
|
1076
|
-
await mkdir(memoryDir, { recursive: true });
|
|
1077
|
-
const now = /* @__PURE__ */ new Date();
|
|
1078
|
-
const dateStr = now.toISOString().split("T")[0];
|
|
1079
|
-
log4.info("Generating semantic slug for session memory...");
|
|
1080
|
-
const slug = await generateSlugViaClaude({
|
|
1081
|
-
messages: params.context.messages,
|
|
1082
|
-
apiKey: params.apiKey,
|
|
1083
|
-
provider: params.provider,
|
|
1084
|
-
utilityModel: params.utilityModel
|
|
1085
|
-
});
|
|
1086
|
-
const filename = `${dateStr}-${slug}.md`;
|
|
1087
|
-
const filepath = join3(memoryDir, filename);
|
|
1088
|
-
const timeStr = now.toISOString().split("T")[1].split(".")[0];
|
|
1089
|
-
log4.info("Generating session summary...");
|
|
1090
|
-
let summary;
|
|
1091
|
-
try {
|
|
1092
|
-
summary = await summarizeViaClaude({
|
|
1093
|
-
messages: params.context.messages,
|
|
1094
|
-
apiKey: params.apiKey,
|
|
1095
|
-
maxSummaryTokens: DEFAULT_MAX_SUMMARY_TOKENS,
|
|
1096
|
-
customInstructions: "Summarize this session comprehensively. Include key topics, decisions made, problems solved, and important context.",
|
|
1097
|
-
provider: params.provider,
|
|
1098
|
-
utilityModel: params.utilityModel
|
|
1099
|
-
});
|
|
1100
|
-
} catch (error) {
|
|
1101
|
-
log4.warn({ err: error }, "Session summary generation failed");
|
|
1102
|
-
summary = `Session contained ${params.context.messages.length} messages. Summary generation failed.`;
|
|
1103
|
-
}
|
|
1104
|
-
const content = `# Session Memory: ${dateStr} ${timeStr} UTC
|
|
1105
|
-
|
|
1106
|
-
## Metadata
|
|
1107
|
-
|
|
1108
|
-
- **Old Session ID**: \`${params.oldSessionId}\`
|
|
1109
|
-
- **New Session ID**: \`${params.newSessionId}\`
|
|
1110
|
-
- **Chat ID**: \`${params.chatId}\`
|
|
1111
|
-
- **Timestamp**: ${now.toISOString()}
|
|
1112
|
-
- **Message Count**: ${params.context.messages.length}
|
|
1113
|
-
|
|
1114
|
-
## Session Summary
|
|
1115
|
-
|
|
1116
|
-
${summary}
|
|
1117
|
-
|
|
1118
|
-
## Context
|
|
1119
|
-
|
|
1120
|
-
This session was compacted and migrated to a new session ID. The summary above preserves key information for continuity.
|
|
1121
|
-
|
|
1122
|
-
---
|
|
1123
|
-
|
|
1124
|
-
*Generated automatically by Teleton-AI session memory hook*
|
|
1125
|
-
`;
|
|
1126
|
-
await writeFile(filepath, content, "utf-8");
|
|
1127
|
-
const relPath = filepath.replace(TELETON_ROOT2, "~/.teleton");
|
|
1128
|
-
log4.info(`Session memory saved: ${relPath}`);
|
|
1129
|
-
} catch (error) {
|
|
1130
|
-
log4.error({ err: error }, "Failed to save session memory");
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
// src/memory/compaction.ts
|
|
1135
752
|
var DEFAULT_COMPACTION_CONFIG = {
|
|
1136
753
|
enabled: true,
|
|
1137
754
|
maxMessages: COMPACTION_MAX_MESSAGES,
|
|
@@ -1140,7 +757,7 @@ var DEFAULT_COMPACTION_CONFIG = {
|
|
|
1140
757
|
memoryFlushEnabled: true,
|
|
1141
758
|
softThresholdTokens: DEFAULT_SOFT_THRESHOLD_TOKENS
|
|
1142
759
|
};
|
|
1143
|
-
var
|
|
760
|
+
var log3 = createLogger("Memory");
|
|
1144
761
|
function estimateContextTokens(context) {
|
|
1145
762
|
let charCount = 0;
|
|
1146
763
|
if (context.systemPrompt) {
|
|
@@ -1172,7 +789,7 @@ function shouldFlushMemory(context, config, tokenCount) {
|
|
|
1172
789
|
const tokens = tokenCount ?? estimateContextTokens(context);
|
|
1173
790
|
const softThreshold = config.softThresholdTokens ?? FALLBACK_SOFT_THRESHOLD_TOKENS;
|
|
1174
791
|
if (tokens >= softThreshold) {
|
|
1175
|
-
|
|
792
|
+
log3.info(`Memory flush needed: ~${tokens} tokens (soft threshold: ${softThreshold})`);
|
|
1176
793
|
return true;
|
|
1177
794
|
}
|
|
1178
795
|
return false;
|
|
@@ -1194,7 +811,7 @@ function flushMemoryToDailyLog(context) {
|
|
|
1194
811
|
}
|
|
1195
812
|
}
|
|
1196
813
|
writeSummaryToDailyLog(summary.join("\n"));
|
|
1197
|
-
|
|
814
|
+
log3.info(`Memory flushed to daily log`);
|
|
1198
815
|
}
|
|
1199
816
|
function shouldCompact(context, config, tokenCount) {
|
|
1200
817
|
if (!config.enabled) {
|
|
@@ -1202,13 +819,13 @@ function shouldCompact(context, config, tokenCount) {
|
|
|
1202
819
|
}
|
|
1203
820
|
const messageCount = context.messages.length;
|
|
1204
821
|
if (config.maxMessages && messageCount >= config.maxMessages) {
|
|
1205
|
-
|
|
822
|
+
log3.info(`Compaction needed: ${messageCount} messages (max: ${config.maxMessages})`);
|
|
1206
823
|
return true;
|
|
1207
824
|
}
|
|
1208
825
|
if (config.maxTokens) {
|
|
1209
826
|
const tokens = tokenCount ?? estimateContextTokens(context);
|
|
1210
827
|
if (tokens >= config.maxTokens) {
|
|
1211
|
-
|
|
828
|
+
log3.info(`Compaction needed: ~${tokens} tokens (max: ${config.maxTokens})`);
|
|
1212
829
|
return true;
|
|
1213
830
|
}
|
|
1214
831
|
}
|
|
@@ -1254,12 +871,12 @@ async function compactContext(context, config, apiKey, provider, utilityModel) {
|
|
|
1254
871
|
iterations++;
|
|
1255
872
|
}
|
|
1256
873
|
if (hasOrphanedToolResults(context.messages.slice(cutIndex))) {
|
|
1257
|
-
|
|
874
|
+
log3.warn(`Compaction: couldn't find clean cut point, keeping all messages`);
|
|
1258
875
|
return context;
|
|
1259
876
|
}
|
|
1260
877
|
const recentMessages = context.messages.slice(cutIndex);
|
|
1261
878
|
const oldMessages = context.messages.slice(0, cutIndex);
|
|
1262
|
-
|
|
879
|
+
log3.info(
|
|
1263
880
|
`Compacting ${oldMessages.length} old messages, keeping ${recentMessages.length} recent (cut at clean boundary)`
|
|
1264
881
|
);
|
|
1265
882
|
try {
|
|
@@ -1289,7 +906,7 @@ Keep each section concise. Omit a section if empty. Preserve specific names, num
|
|
|
1289
906
|
provider,
|
|
1290
907
|
utilityModel
|
|
1291
908
|
});
|
|
1292
|
-
|
|
909
|
+
log3.info(`AI Summary: ${result.tokensUsed} tokens, ${result.chunksProcessed} chunks processed`);
|
|
1293
910
|
const summaryText = `[Auto-compacted ${oldMessages.length} messages]
|
|
1294
911
|
|
|
1295
912
|
${result.summary}`;
|
|
@@ -1303,7 +920,7 @@ ${result.summary}`;
|
|
|
1303
920
|
messages: [summaryMessage, ...recentMessages]
|
|
1304
921
|
};
|
|
1305
922
|
} catch (error) {
|
|
1306
|
-
|
|
923
|
+
log3.error({ err: error }, "AI summarization failed, using fallback");
|
|
1307
924
|
const summaryText = `[Auto-compacted: ${oldMessages.length} earlier messages from this conversation]`;
|
|
1308
925
|
const summaryMessage = {
|
|
1309
926
|
role: "user",
|
|
@@ -1318,7 +935,7 @@ ${result.summary}`;
|
|
|
1318
935
|
}
|
|
1319
936
|
async function compactAndSaveTranscript(sessionId, context, config, apiKey, chatId, provider, utilityModel) {
|
|
1320
937
|
const newSessionId = randomUUID();
|
|
1321
|
-
|
|
938
|
+
log3.info(`Creating compacted transcript: ${sessionId} \u2192 ${newSessionId}`);
|
|
1322
939
|
if (chatId) {
|
|
1323
940
|
await saveSessionMemory({
|
|
1324
941
|
oldSessionId: sessionId,
|
|
@@ -1352,7 +969,7 @@ var CompactionManager = class {
|
|
|
1352
969
|
if (this.config.memoryFlushEnabled) {
|
|
1353
970
|
flushMemoryToDailyLog(context);
|
|
1354
971
|
}
|
|
1355
|
-
|
|
972
|
+
log3.info(`Auto-compacting session ${sessionId}`);
|
|
1356
973
|
const newSessionId = await compactAndSaveTranscript(
|
|
1357
974
|
sessionId,
|
|
1358
975
|
context,
|
|
@@ -1362,7 +979,7 @@ var CompactionManager = class {
|
|
|
1362
979
|
provider,
|
|
1363
980
|
utilityModel
|
|
1364
981
|
);
|
|
1365
|
-
|
|
982
|
+
log3.info(`Compaction complete: ${newSessionId}`);
|
|
1366
983
|
return newSessionId;
|
|
1367
984
|
}
|
|
1368
985
|
updateConfig(config) {
|
|
@@ -1494,7 +1111,7 @@ function maskOldToolResults(messages, options) {
|
|
|
1494
1111
|
}
|
|
1495
1112
|
|
|
1496
1113
|
// src/agent/runtime.ts
|
|
1497
|
-
var
|
|
1114
|
+
var log4 = createLogger("Agent");
|
|
1498
1115
|
var globalTokenUsage = { totalTokens: 0, totalCost: 0 };
|
|
1499
1116
|
function getTokenUsage() {
|
|
1500
1117
|
return { ...globalTokenUsage };
|
|
@@ -1607,7 +1224,7 @@ var AgentRuntime = class {
|
|
|
1607
1224
|
if (this.userHookEvaluator) {
|
|
1608
1225
|
const hookResult = this.userHookEvaluator.evaluate(userMessage);
|
|
1609
1226
|
if (hookResult.blocked) {
|
|
1610
|
-
|
|
1227
|
+
log4.info("Message blocked by keyword filter");
|
|
1611
1228
|
return { content: hookResult.blockMessage ?? "", toolCalls: [] };
|
|
1612
1229
|
}
|
|
1613
1230
|
if (hookResult.additionalContext) {
|
|
@@ -1633,7 +1250,7 @@ var AgentRuntime = class {
|
|
|
1633
1250
|
};
|
|
1634
1251
|
await this.hookRunner.runModifyingHook("message:receive", msgEvent);
|
|
1635
1252
|
if (msgEvent.block) {
|
|
1636
|
-
|
|
1253
|
+
log4.info(`\u{1F6AB} Message blocked by hook: ${msgEvent.blockReason || "no reason"}`);
|
|
1637
1254
|
return { content: "", toolCalls: [] };
|
|
1638
1255
|
}
|
|
1639
1256
|
effectiveMessage = sanitizeForContext(msgEvent.text);
|
|
@@ -1645,7 +1262,7 @@ var AgentRuntime = class {
|
|
|
1645
1262
|
const now = timestamp ?? Date.now();
|
|
1646
1263
|
const resetPolicy = this.config.agent.session_reset_policy;
|
|
1647
1264
|
if (shouldResetSession(session, resetPolicy)) {
|
|
1648
|
-
|
|
1265
|
+
log4.info(`\u{1F504} Auto-resetting session based on policy`);
|
|
1649
1266
|
if (this.hookRunner) {
|
|
1650
1267
|
await this.hookRunner.runObservingHook("session:end", {
|
|
1651
1268
|
sessionId: session.sessionId,
|
|
@@ -1655,7 +1272,7 @@ var AgentRuntime = class {
|
|
|
1655
1272
|
}
|
|
1656
1273
|
if (transcriptExists(session.sessionId)) {
|
|
1657
1274
|
try {
|
|
1658
|
-
|
|
1275
|
+
log4.info(`\u{1F4BE} Saving memory before daily reset...`);
|
|
1659
1276
|
const oldContext = loadContextFromTranscript(session.sessionId);
|
|
1660
1277
|
await saveSessionMemory({
|
|
1661
1278
|
oldSessionId: session.sessionId,
|
|
@@ -1666,9 +1283,9 @@ var AgentRuntime = class {
|
|
|
1666
1283
|
provider: this.config.agent.provider,
|
|
1667
1284
|
utilityModel: this.config.agent.utility_model
|
|
1668
1285
|
});
|
|
1669
|
-
|
|
1286
|
+
log4.info(`\u2705 Memory saved before reset`);
|
|
1670
1287
|
} catch (error) {
|
|
1671
|
-
|
|
1288
|
+
log4.warn({ err: error }, `\u26A0\uFE0F Failed to save memory before reset`);
|
|
1672
1289
|
}
|
|
1673
1290
|
}
|
|
1674
1291
|
session = resetSessionWithPolicy(chatId, resetPolicy);
|
|
@@ -1676,9 +1293,9 @@ var AgentRuntime = class {
|
|
|
1676
1293
|
let context = loadContextFromTranscript(session.sessionId);
|
|
1677
1294
|
const isNewSession = context.messages.length === 0;
|
|
1678
1295
|
if (!isNewSession) {
|
|
1679
|
-
|
|
1296
|
+
log4.info(`\u{1F4D6} Loading existing session: ${session.sessionId}`);
|
|
1680
1297
|
} else {
|
|
1681
|
-
|
|
1298
|
+
log4.info(`\u{1F195} Starting new session: ${session.sessionId}`);
|
|
1682
1299
|
}
|
|
1683
1300
|
if (this.hookRunner) {
|
|
1684
1301
|
await this.hookRunner.runObservingHook("session:start", {
|
|
@@ -1707,21 +1324,32 @@ var AgentRuntime = class {
|
|
|
1707
1324
|
formattedMessage = `${pendingContext}
|
|
1708
1325
|
|
|
1709
1326
|
${formattedMessage}`;
|
|
1710
|
-
|
|
1327
|
+
log4.debug(`\u{1F4CB} Including ${pendingContext.split("\n").length - 1} pending messages`);
|
|
1711
1328
|
}
|
|
1712
|
-
|
|
1329
|
+
log4.debug(`\u{1F4E8} Formatted message: ${formattedMessage.substring(0, 100)}...`);
|
|
1713
1330
|
const preview = formattedMessage.slice(0, 50).replace(/\n/g, " ");
|
|
1714
1331
|
const who = senderUsername ? `@${senderUsername}` : userName;
|
|
1715
1332
|
const msgType = isGroup ? `Group ${chatId} ${who}` : `DM ${who}`;
|
|
1716
|
-
|
|
1333
|
+
log4.info(`\u{1F4E8} ${msgType}: "${preview}${formattedMessage.length > 50 ? "..." : ""}"`);
|
|
1717
1334
|
let relevantContext = "";
|
|
1718
1335
|
let queryEmbedding;
|
|
1719
1336
|
const isNonTrivial = !isTrivialMessage(effectiveMessage);
|
|
1720
1337
|
if (this.embedder && isNonTrivial) {
|
|
1721
1338
|
try {
|
|
1722
|
-
|
|
1339
|
+
let searchQuery = effectiveMessage;
|
|
1340
|
+
const recentUserMsgs = context.messages.filter((m) => m.role === "user" && typeof m.content === "string").slice(-3).map((m) => {
|
|
1341
|
+
const text = m.content;
|
|
1342
|
+
const bodyMatch = text.match(/\] (.+)/s);
|
|
1343
|
+
return (bodyMatch ? bodyMatch[1] : text).trim();
|
|
1344
|
+
}).filter((t) => t.length > 0);
|
|
1345
|
+
if (recentUserMsgs.length > 0) {
|
|
1346
|
+
searchQuery = recentUserMsgs.join(" ") + " " + effectiveMessage;
|
|
1347
|
+
}
|
|
1348
|
+
queryEmbedding = await this.embedder.embedQuery(
|
|
1349
|
+
searchQuery.slice(0, EMBEDDING_QUERY_MAX_CHARS)
|
|
1350
|
+
);
|
|
1723
1351
|
} catch (error) {
|
|
1724
|
-
|
|
1352
|
+
log4.warn({ err: error }, "Embedding computation failed");
|
|
1725
1353
|
}
|
|
1726
1354
|
}
|
|
1727
1355
|
if (this.contextBuilder && isNonTrivial) {
|
|
@@ -1755,12 +1383,12 @@ ${sanitizedFeed.join("\n")}`
|
|
|
1755
1383
|
}
|
|
1756
1384
|
if (contextParts.length > 0) {
|
|
1757
1385
|
relevantContext = contextParts.join("\n\n");
|
|
1758
|
-
|
|
1386
|
+
log4.debug(
|
|
1759
1387
|
`\u{1F50D} Found ${dbContext.relevantKnowledge.length} knowledge chunks, ${dbContext.relevantFeed.length} feed messages`
|
|
1760
1388
|
);
|
|
1761
1389
|
}
|
|
1762
1390
|
} catch (error) {
|
|
1763
|
-
|
|
1391
|
+
log4.warn({ err: error }, "Context building failed");
|
|
1764
1392
|
}
|
|
1765
1393
|
}
|
|
1766
1394
|
const memoryStats = this.getMemoryStats();
|
|
@@ -1828,7 +1456,7 @@ ${allHookContext}` : "");
|
|
|
1828
1456
|
this.config.agent.utility_model
|
|
1829
1457
|
);
|
|
1830
1458
|
if (preemptiveCompaction) {
|
|
1831
|
-
|
|
1459
|
+
log4.info(`\u{1F5DC}\uFE0F Preemptive compaction triggered, reloading session...`);
|
|
1832
1460
|
session = getSession(chatId);
|
|
1833
1461
|
context = loadContextFromTranscript(session.sessionId);
|
|
1834
1462
|
context.messages.push(userMsg);
|
|
@@ -1850,7 +1478,7 @@ ${allHookContext}` : "");
|
|
|
1850
1478
|
chatId,
|
|
1851
1479
|
isAdmin
|
|
1852
1480
|
);
|
|
1853
|
-
|
|
1481
|
+
log4.info(`\u{1F50D} Tool RAG: ${tools.length}/${this.toolRegistry.count} tools selected`);
|
|
1854
1482
|
} else {
|
|
1855
1483
|
tools = this.toolRegistry?.getForContext(
|
|
1856
1484
|
effectiveIsGroup,
|
|
@@ -1869,9 +1497,10 @@ ${allHookContext}` : "");
|
|
|
1869
1497
|
const totalToolCalls = [];
|
|
1870
1498
|
const accumulatedTexts = [];
|
|
1871
1499
|
const accumulatedUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalCost: 0 };
|
|
1500
|
+
const seenToolSignatures = /* @__PURE__ */ new Set();
|
|
1872
1501
|
while (iteration < maxIterations) {
|
|
1873
1502
|
iteration++;
|
|
1874
|
-
|
|
1503
|
+
log4.debug(`\u{1F504} Agentic iteration ${iteration}/${maxIterations}`);
|
|
1875
1504
|
const iterationStartIndex = context.messages.length;
|
|
1876
1505
|
const maskedMessages = maskOldToolResults(context.messages, {
|
|
1877
1506
|
toolRegistry: this.toolRegistry ?? void 0,
|
|
@@ -1910,35 +1539,35 @@ ${allHookContext}` : "");
|
|
|
1910
1539
|
"Context overflow persists after session reset. Message may be too large for the model's context window."
|
|
1911
1540
|
);
|
|
1912
1541
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1542
|
+
log4.error(`\u{1F6A8} Context overflow detected: ${errorMsg}`);
|
|
1543
|
+
log4.info(`\u{1F4BE} Saving session memory before reset...`);
|
|
1915
1544
|
const summary = extractContextSummary(context, CONTEXT_OVERFLOW_SUMMARY_MESSAGES);
|
|
1916
1545
|
appendToDailyLog(summary);
|
|
1917
|
-
|
|
1546
|
+
log4.info(`\u2705 Memory saved to daily log`);
|
|
1918
1547
|
const archived = archiveTranscript(session.sessionId);
|
|
1919
1548
|
if (!archived) {
|
|
1920
|
-
|
|
1549
|
+
log4.error(
|
|
1921
1550
|
`\u26A0\uFE0F Failed to archive transcript ${session.sessionId}, proceeding with reset anyway`
|
|
1922
1551
|
);
|
|
1923
1552
|
}
|
|
1924
|
-
|
|
1553
|
+
log4.info(`\u{1F504} Resetting session due to context overflow...`);
|
|
1925
1554
|
session = resetSession(chatId);
|
|
1926
1555
|
context = { messages: [userMsg] };
|
|
1927
1556
|
appendToTranscript(session.sessionId, userMsg);
|
|
1928
|
-
|
|
1557
|
+
log4.info(`\u{1F504} Retrying with fresh context...`);
|
|
1929
1558
|
continue;
|
|
1930
1559
|
} else if (errorMsg.toLowerCase().includes("rate") || errorMsg.includes("429")) {
|
|
1931
1560
|
rateLimitRetries++;
|
|
1932
1561
|
if (rateLimitRetries <= RATE_LIMIT_MAX_RETRIES) {
|
|
1933
1562
|
const delay = 1e3 * Math.pow(2, rateLimitRetries - 1);
|
|
1934
|
-
|
|
1563
|
+
log4.warn(
|
|
1935
1564
|
`\u{1F6AB} Rate limited, retrying in ${delay}ms (attempt ${rateLimitRetries}/${RATE_LIMIT_MAX_RETRIES})...`
|
|
1936
1565
|
);
|
|
1937
1566
|
await new Promise((r3) => setTimeout(r3, delay));
|
|
1938
1567
|
iteration--;
|
|
1939
1568
|
continue;
|
|
1940
1569
|
}
|
|
1941
|
-
|
|
1570
|
+
log4.error(`\u{1F6AB} Rate limited after ${RATE_LIMIT_MAX_RETRIES} retries: ${errorMsg}`);
|
|
1942
1571
|
throw new Error(
|
|
1943
1572
|
`API rate limited after ${RATE_LIMIT_MAX_RETRIES} retries. Please try again later.`
|
|
1944
1573
|
);
|
|
@@ -1946,19 +1575,19 @@ ${allHookContext}` : "");
|
|
|
1946
1575
|
serverErrorRetries++;
|
|
1947
1576
|
if (serverErrorRetries <= SERVER_ERROR_MAX_RETRIES) {
|
|
1948
1577
|
const delay = 2e3 * Math.pow(2, serverErrorRetries - 1);
|
|
1949
|
-
|
|
1578
|
+
log4.warn(
|
|
1950
1579
|
`\u{1F504} Server error, retrying in ${delay}ms (attempt ${serverErrorRetries}/${SERVER_ERROR_MAX_RETRIES})...`
|
|
1951
1580
|
);
|
|
1952
1581
|
await new Promise((r3) => setTimeout(r3, delay));
|
|
1953
1582
|
iteration--;
|
|
1954
1583
|
continue;
|
|
1955
1584
|
}
|
|
1956
|
-
|
|
1585
|
+
log4.error(`\u{1F6A8} Server error after ${SERVER_ERROR_MAX_RETRIES} retries: ${errorMsg}`);
|
|
1957
1586
|
throw new Error(
|
|
1958
1587
|
`API server error after ${SERVER_ERROR_MAX_RETRIES} retries. The provider may be experiencing issues.`
|
|
1959
1588
|
);
|
|
1960
1589
|
} else {
|
|
1961
|
-
|
|
1590
|
+
log4.error(`\u{1F6A8} API error: ${errorMsg}`);
|
|
1962
1591
|
throw new Error(`API error: ${errorMsg || "Unknown error"}`);
|
|
1963
1592
|
}
|
|
1964
1593
|
}
|
|
@@ -1975,24 +1604,25 @@ ${allHookContext}` : "");
|
|
|
1975
1604
|
}
|
|
1976
1605
|
const toolCalls = response2.message.content.filter((block) => block.type === "toolCall");
|
|
1977
1606
|
if (toolCalls.length === 0) {
|
|
1978
|
-
|
|
1607
|
+
log4.info(`\u{1F504} ${iteration}/${maxIterations} \u2192 done`);
|
|
1979
1608
|
finalResponse = response2;
|
|
1980
1609
|
break;
|
|
1981
1610
|
}
|
|
1982
1611
|
if (!this.toolRegistry || !toolContext) {
|
|
1983
|
-
|
|
1612
|
+
log4.error("\u26A0\uFE0F Cannot execute tools: registry or context missing");
|
|
1984
1613
|
break;
|
|
1985
1614
|
}
|
|
1986
|
-
|
|
1615
|
+
log4.debug(`\u{1F527} Executing ${toolCalls.length} tool call(s)`);
|
|
1987
1616
|
context.messages.push(response2.message);
|
|
1988
1617
|
const iterationToolNames = [];
|
|
1618
|
+
const fullContext = {
|
|
1619
|
+
...toolContext,
|
|
1620
|
+
chatId,
|
|
1621
|
+
isGroup: effectiveIsGroup
|
|
1622
|
+
};
|
|
1623
|
+
const toolPlans = [];
|
|
1989
1624
|
for (const block of toolCalls) {
|
|
1990
1625
|
if (block.type !== "toolCall") continue;
|
|
1991
|
-
const fullContext = {
|
|
1992
|
-
...toolContext,
|
|
1993
|
-
chatId,
|
|
1994
|
-
isGroup: effectiveIsGroup
|
|
1995
|
-
};
|
|
1996
1626
|
let toolParams = block.arguments ?? {};
|
|
1997
1627
|
let blocked = false;
|
|
1998
1628
|
let blockReason = "";
|
|
@@ -2013,74 +1643,88 @@ ${allHookContext}` : "");
|
|
|
2013
1643
|
toolParams = structuredClone(beforeEvent.params);
|
|
2014
1644
|
}
|
|
2015
1645
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
toolName: block.name,
|
|
2047
|
-
params: structuredClone(toolParams),
|
|
2048
|
-
error: errMsg,
|
|
2049
|
-
// Note: stack traces are exposed to plugins for debugging — accepted tradeoff
|
|
2050
|
-
stack: errStack,
|
|
2051
|
-
chatId,
|
|
2052
|
-
isGroup: effectiveIsGroup,
|
|
2053
|
-
durationMs: durationMs2
|
|
1646
|
+
toolPlans.push({ block, blocked, blockReason, params: toolParams });
|
|
1647
|
+
}
|
|
1648
|
+
const execResults = new Array(toolPlans.length);
|
|
1649
|
+
{
|
|
1650
|
+
let cursor = 0;
|
|
1651
|
+
const runWorker = async () => {
|
|
1652
|
+
while (cursor < toolPlans.length) {
|
|
1653
|
+
const idx = cursor++;
|
|
1654
|
+
const plan = toolPlans[idx];
|
|
1655
|
+
if (plan.blocked) {
|
|
1656
|
+
execResults[idx] = {
|
|
1657
|
+
result: { success: false, error: plan.blockReason },
|
|
1658
|
+
durationMs: 0
|
|
1659
|
+
};
|
|
1660
|
+
continue;
|
|
1661
|
+
}
|
|
1662
|
+
const startTime = Date.now();
|
|
1663
|
+
try {
|
|
1664
|
+
const result = await this.toolRegistry.execute(
|
|
1665
|
+
{ ...plan.block, arguments: plan.params },
|
|
1666
|
+
fullContext
|
|
1667
|
+
);
|
|
1668
|
+
execResults[idx] = { result, durationMs: Date.now() - startTime };
|
|
1669
|
+
} catch (execErr) {
|
|
1670
|
+
const errMsg = execErr instanceof Error ? execErr.message : String(execErr);
|
|
1671
|
+
const errStack = execErr instanceof Error ? execErr.stack : void 0;
|
|
1672
|
+
execResults[idx] = {
|
|
1673
|
+
result: { success: false, error: errMsg },
|
|
1674
|
+
durationMs: Date.now() - startTime,
|
|
1675
|
+
execError: { message: errMsg, stack: errStack }
|
|
2054
1676
|
};
|
|
2055
|
-
await this.hookRunner.runObservingHook("tool:error", errorEvent);
|
|
2056
1677
|
}
|
|
2057
1678
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
1679
|
+
};
|
|
1680
|
+
const workers = Math.min(TOOL_CONCURRENCY_LIMIT, toolPlans.length);
|
|
1681
|
+
await Promise.all(Array.from({ length: workers }, () => runWorker()));
|
|
1682
|
+
}
|
|
1683
|
+
for (let i = 0; i < toolPlans.length; i++) {
|
|
1684
|
+
const plan = toolPlans[i];
|
|
1685
|
+
const { block } = plan;
|
|
1686
|
+
const exec = execResults[i];
|
|
1687
|
+
if (exec.execError && this.hookRunner) {
|
|
1688
|
+
const errorEvent = {
|
|
1689
|
+
toolName: block.name,
|
|
1690
|
+
params: structuredClone(plan.params),
|
|
1691
|
+
error: exec.execError.message,
|
|
1692
|
+
stack: exec.execError.stack,
|
|
1693
|
+
chatId,
|
|
1694
|
+
isGroup: effectiveIsGroup,
|
|
1695
|
+
durationMs: exec.durationMs
|
|
1696
|
+
};
|
|
1697
|
+
await this.hookRunner.runObservingHook("tool:error", errorEvent);
|
|
1698
|
+
}
|
|
1699
|
+
if (this.hookRunner) {
|
|
1700
|
+
const afterEvent = {
|
|
1701
|
+
toolName: block.name,
|
|
1702
|
+
params: structuredClone(plan.params),
|
|
1703
|
+
result: {
|
|
1704
|
+
success: exec.result.success,
|
|
1705
|
+
data: exec.result.data,
|
|
1706
|
+
error: exec.result.error
|
|
1707
|
+
},
|
|
1708
|
+
durationMs: exec.durationMs,
|
|
1709
|
+
chatId,
|
|
1710
|
+
isGroup: effectiveIsGroup,
|
|
1711
|
+
...plan.blocked ? { blocked: true, blockReason: plan.blockReason } : {}
|
|
1712
|
+
};
|
|
1713
|
+
await this.hookRunner.runObservingHook("tool:after", afterEvent);
|
|
2070
1714
|
}
|
|
2071
|
-
|
|
2072
|
-
iterationToolNames.push(`${block.name} ${result.success ? "\u2713" : "\u2717"}`);
|
|
1715
|
+
log4.debug(`${block.name}: ${exec.result.success ? "\u2713" : "\u2717"} ${exec.result.error || ""}`);
|
|
1716
|
+
iterationToolNames.push(`${block.name} ${exec.result.success ? "\u2713" : "\u2717"}`);
|
|
2073
1717
|
totalToolCalls.push({
|
|
2074
1718
|
name: block.name,
|
|
2075
1719
|
input: block.arguments
|
|
2076
1720
|
});
|
|
2077
|
-
let resultText = JSON.stringify(result);
|
|
1721
|
+
let resultText = JSON.stringify(exec.result);
|
|
2078
1722
|
if (resultText.length > MAX_TOOL_RESULT_SIZE) {
|
|
2079
|
-
|
|
2080
|
-
const data = result.data;
|
|
1723
|
+
log4.warn(`\u26A0\uFE0F Tool result too large (${resultText.length} chars), truncating...`);
|
|
1724
|
+
const data = exec.result.data;
|
|
2081
1725
|
if (data?.summary || data?.message) {
|
|
2082
1726
|
resultText = JSON.stringify({
|
|
2083
|
-
success: result.success,
|
|
1727
|
+
success: exec.result.success,
|
|
2084
1728
|
data: {
|
|
2085
1729
|
summary: data.summary || data.message,
|
|
2086
1730
|
_truncated: true,
|
|
@@ -2089,11 +1733,27 @@ ${allHookContext}` : "");
|
|
|
2089
1733
|
}
|
|
2090
1734
|
});
|
|
2091
1735
|
} else {
|
|
2092
|
-
|
|
1736
|
+
const summarized = {
|
|
1737
|
+
_truncated: true,
|
|
1738
|
+
_originalSize: resultText.length,
|
|
1739
|
+
_message: "Full data truncated. Use limit parameter for smaller results."
|
|
1740
|
+
};
|
|
1741
|
+
if (data && typeof data === "object") {
|
|
1742
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1743
|
+
if (Array.isArray(value)) {
|
|
1744
|
+
summarized[key] = `[${value.length} items]`;
|
|
1745
|
+
} else if (typeof value === "string" && value.length > 500) {
|
|
1746
|
+
summarized[key] = value.slice(0, 500) + "...[truncated]";
|
|
1747
|
+
} else {
|
|
1748
|
+
summarized[key] = value;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
resultText = JSON.stringify({ success: exec.result.success, data: summarized });
|
|
2093
1753
|
}
|
|
2094
1754
|
}
|
|
2095
1755
|
if (provider === "cocoon") {
|
|
2096
|
-
const { wrapToolResult } = await import("./tool-adapter-
|
|
1756
|
+
const { wrapToolResult } = await import("./tool-adapter-IVX2XQJE.js");
|
|
2097
1757
|
const cocoonResultMsg = {
|
|
2098
1758
|
role: "user",
|
|
2099
1759
|
content: [
|
|
@@ -2117,21 +1777,33 @@ ${allHookContext}` : "");
|
|
|
2117
1777
|
text: resultText
|
|
2118
1778
|
}
|
|
2119
1779
|
],
|
|
2120
|
-
isError: !result.success,
|
|
1780
|
+
isError: !exec.result.success,
|
|
2121
1781
|
timestamp: Date.now()
|
|
2122
1782
|
};
|
|
2123
1783
|
context.messages.push(toolResultMsg);
|
|
2124
1784
|
appendToTranscript(session.sessionId, toolResultMsg);
|
|
2125
1785
|
}
|
|
2126
1786
|
}
|
|
2127
|
-
|
|
1787
|
+
log4.info(`\u{1F504} ${iteration}/${maxIterations} \u2192 ${iterationToolNames.join(", ")}`);
|
|
1788
|
+
const iterSignatures = toolPlans.map(
|
|
1789
|
+
(p2) => `${p2.block.name}:${JSON.stringify(p2.params, Object.keys(p2.params).sort())}`
|
|
1790
|
+
);
|
|
1791
|
+
const allDuplicates = iterSignatures.length > 0 && iterSignatures.every((sig) => seenToolSignatures.has(sig));
|
|
1792
|
+
for (const sig of iterSignatures) seenToolSignatures.add(sig);
|
|
1793
|
+
if (allDuplicates) {
|
|
1794
|
+
log4.warn(
|
|
1795
|
+
`\u{1F501} Loop stall detected: all ${iterSignatures.length} tool call(s) are repeats \u2014 breaking early`
|
|
1796
|
+
);
|
|
1797
|
+
finalResponse = response2;
|
|
1798
|
+
break;
|
|
1799
|
+
}
|
|
2128
1800
|
if (iteration === maxIterations) {
|
|
2129
|
-
|
|
1801
|
+
log4.info(`\u26A0\uFE0F Max iterations reached (${maxIterations})`);
|
|
2130
1802
|
finalResponse = response2;
|
|
2131
1803
|
}
|
|
2132
1804
|
}
|
|
2133
1805
|
if (!finalResponse) {
|
|
2134
|
-
|
|
1806
|
+
log4.error("\u26A0\uFE0F Agentic loop exited early without final response");
|
|
2135
1807
|
return {
|
|
2136
1808
|
content: "Internal error: Agent loop failed to produce a response.",
|
|
2137
1809
|
toolCalls: []
|
|
@@ -2142,14 +1814,6 @@ ${allHookContext}` : "");
|
|
|
2142
1814
|
if (lastMsg?.role !== "assistant") {
|
|
2143
1815
|
context.messages.push(response.message);
|
|
2144
1816
|
}
|
|
2145
|
-
const newSessionId = await this.compactionManager.checkAndCompact(
|
|
2146
|
-
session.sessionId,
|
|
2147
|
-
context,
|
|
2148
|
-
getEffectiveApiKey(this.config.agent.provider, this.config.agent.api_key),
|
|
2149
|
-
chatId,
|
|
2150
|
-
this.config.agent.provider,
|
|
2151
|
-
this.config.agent.utility_model
|
|
2152
|
-
);
|
|
2153
1817
|
const sessionUpdate = {
|
|
2154
1818
|
updatedAt: Date.now(),
|
|
2155
1819
|
messageCount: session.messageCount + 1,
|
|
@@ -2158,9 +1822,6 @@ ${allHookContext}` : "");
|
|
|
2158
1822
|
inputTokens: (session.inputTokens ?? 0) + accumulatedUsage.input + accumulatedUsage.cacheRead + accumulatedUsage.cacheWrite,
|
|
2159
1823
|
outputTokens: (session.outputTokens ?? 0) + accumulatedUsage.output
|
|
2160
1824
|
};
|
|
2161
|
-
if (newSessionId) {
|
|
2162
|
-
sessionUpdate.sessionId = newSessionId;
|
|
2163
|
-
}
|
|
2164
1825
|
updateSession(chatId, sessionUpdate);
|
|
2165
1826
|
if (accumulatedUsage.input > 0 || accumulatedUsage.output > 0) {
|
|
2166
1827
|
const u = accumulatedUsage;
|
|
@@ -2170,20 +1831,20 @@ ${allHookContext}` : "");
|
|
|
2170
1831
|
if (u.cacheRead) cacheParts.push(`${(u.cacheRead / 1e3).toFixed(1)}K cached`);
|
|
2171
1832
|
if (u.cacheWrite) cacheParts.push(`${(u.cacheWrite / 1e3).toFixed(1)}K new`);
|
|
2172
1833
|
const cacheInfo = cacheParts.length > 0 ? ` (${cacheParts.join(", ")})` : "";
|
|
2173
|
-
|
|
1834
|
+
log4.info(`\u{1F4B0} ${inK}K in${cacheInfo}, ${u.output} out | $${u.totalCost.toFixed(3)}`);
|
|
2174
1835
|
globalTokenUsage.totalTokens += u.input + u.output + u.cacheRead + u.cacheWrite;
|
|
2175
1836
|
globalTokenUsage.totalCost += u.totalCost;
|
|
2176
1837
|
}
|
|
2177
1838
|
let content = accumulatedTexts.join("\n").trim() || response.text;
|
|
2178
1839
|
const usedTelegramSendTool = totalToolCalls.some((tc) => TELEGRAM_SEND_TOOLS.has(tc.name));
|
|
2179
1840
|
if (!content && totalToolCalls.length > 0 && !usedTelegramSendTool) {
|
|
2180
|
-
|
|
1841
|
+
log4.warn("\u26A0\uFE0F Empty response after tool calls - generating fallback");
|
|
2181
1842
|
content = "I executed the requested action but couldn't generate a response. Please try again.";
|
|
2182
1843
|
} else if (!content && usedTelegramSendTool) {
|
|
2183
|
-
|
|
1844
|
+
log4.info("\u2705 Response sent via Telegram tool - no additional text needed");
|
|
2184
1845
|
content = "";
|
|
2185
1846
|
} else if (!content && accumulatedUsage.input === 0 && accumulatedUsage.output === 0) {
|
|
2186
|
-
|
|
1847
|
+
log4.warn("\u26A0\uFE0F Empty response with zero tokens - possible API issue");
|
|
2187
1848
|
content = "I couldn't process your request. Please try again.";
|
|
2188
1849
|
}
|
|
2189
1850
|
let responseMetadata = {};
|
|
@@ -2200,7 +1861,7 @@ ${allHookContext}` : "");
|
|
|
2200
1861
|
};
|
|
2201
1862
|
await this.hookRunner.runModifyingHook("response:before", responseBeforeEvent);
|
|
2202
1863
|
if (responseBeforeEvent.block) {
|
|
2203
|
-
|
|
1864
|
+
log4.info(
|
|
2204
1865
|
`\u{1F6AB} Response blocked by hook: ${responseBeforeEvent.blockReason || "no reason"}`
|
|
2205
1866
|
);
|
|
2206
1867
|
content = "";
|
|
@@ -2227,7 +1888,7 @@ ${allHookContext}` : "");
|
|
|
2227
1888
|
toolCalls: totalToolCalls
|
|
2228
1889
|
};
|
|
2229
1890
|
} catch (error) {
|
|
2230
|
-
|
|
1891
|
+
log4.error({ err: error }, "Agent error");
|
|
2231
1892
|
throw error;
|
|
2232
1893
|
}
|
|
2233
1894
|
}
|
|
@@ -2240,7 +1901,7 @@ ${allHookContext}` : "");
|
|
|
2240
1901
|
).run(chatId);
|
|
2241
1902
|
db.prepare(`DELETE FROM tg_messages WHERE chat_id = ?`).run(chatId);
|
|
2242
1903
|
resetSession(chatId);
|
|
2243
|
-
|
|
1904
|
+
log4.info(`\u{1F5D1}\uFE0F Cleared history for chat ${chatId}`);
|
|
2244
1905
|
}
|
|
2245
1906
|
getConfig() {
|
|
2246
1907
|
return this.config;
|
|
@@ -2261,7 +1922,7 @@ ${allHookContext}` : "");
|
|
|
2261
1922
|
}
|
|
2262
1923
|
configureCompaction(config) {
|
|
2263
1924
|
this.compactionManager.updateConfig(config);
|
|
2264
|
-
|
|
1925
|
+
log4.info({ config: this.compactionManager.getConfig() }, `\u{1F5DC}\uFE0F Compaction config updated`);
|
|
2265
1926
|
}
|
|
2266
1927
|
getCompactionConfig() {
|
|
2267
1928
|
return this.compactionManager.getConfig();
|
|
@@ -2349,6 +2010,17 @@ function prefixButtons(rows, pluginName) {
|
|
|
2349
2010
|
|
|
2350
2011
|
// src/bot/services/html-parser.ts
|
|
2351
2012
|
import { Api as Api2 } from "telegram";
|
|
2013
|
+
|
|
2014
|
+
// src/utils/gramjs-bigint.ts
|
|
2015
|
+
import { randomBytes } from "crypto";
|
|
2016
|
+
function toLong(value) {
|
|
2017
|
+
return typeof value === "bigint" ? value : BigInt(value);
|
|
2018
|
+
}
|
|
2019
|
+
function randomLong() {
|
|
2020
|
+
return randomBytes(8).readBigUInt64BE();
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
// src/bot/services/html-parser.ts
|
|
2352
2024
|
function parseHtml(html) {
|
|
2353
2025
|
const entities = [];
|
|
2354
2026
|
let text = "";
|
|
@@ -2399,8 +2071,7 @@ function parseHtml(html) {
|
|
|
2399
2071
|
new Api2.MessageEntityCustomEmoji({
|
|
2400
2072
|
offset: open.offset,
|
|
2401
2073
|
length,
|
|
2402
|
-
|
|
2403
|
-
documentId: BigInt(open.emojiId)
|
|
2074
|
+
documentId: toLong(open.emojiId)
|
|
2404
2075
|
})
|
|
2405
2076
|
);
|
|
2406
2077
|
}
|
|
@@ -2461,7 +2132,7 @@ function unescapeHtml(text) {
|
|
|
2461
2132
|
}
|
|
2462
2133
|
|
|
2463
2134
|
// src/bot/inline-router.ts
|
|
2464
|
-
var
|
|
2135
|
+
var log5 = createLogger("InlineRouter");
|
|
2465
2136
|
var INLINE_TIMEOUT_MS = 5e3;
|
|
2466
2137
|
var CALLBACK_TIMEOUT_MS = 15e3;
|
|
2467
2138
|
function compileGlob(pattern) {
|
|
@@ -2482,11 +2153,11 @@ var InlineRouter = class {
|
|
|
2482
2153
|
}
|
|
2483
2154
|
registerPlugin(name, handlers) {
|
|
2484
2155
|
this.plugins.set(name, handlers);
|
|
2485
|
-
|
|
2156
|
+
log5.info(`Registered plugin "${name}" for inline routing`);
|
|
2486
2157
|
}
|
|
2487
2158
|
unregisterPlugin(name) {
|
|
2488
2159
|
this.plugins.delete(name);
|
|
2489
|
-
|
|
2160
|
+
log5.info(`Unregistered plugin "${name}" from inline routing`);
|
|
2490
2161
|
}
|
|
2491
2162
|
hasPlugin(name) {
|
|
2492
2163
|
return this.plugins.has(name);
|
|
@@ -2558,7 +2229,7 @@ var InlineRouter = class {
|
|
|
2558
2229
|
is_personal: true
|
|
2559
2230
|
});
|
|
2560
2231
|
} catch (error) {
|
|
2561
|
-
|
|
2232
|
+
log5.error({ err: error }, `Plugin "${pluginName}" inline query handler failed`);
|
|
2562
2233
|
try {
|
|
2563
2234
|
await ctx.answerInlineQuery([], { cache_time: 0, is_personal: true });
|
|
2564
2235
|
} catch {
|
|
@@ -2618,7 +2289,7 @@ var InlineRouter = class {
|
|
|
2618
2289
|
} catch (error) {
|
|
2619
2290
|
const errMsg = error?.errorMessage;
|
|
2620
2291
|
if (errMsg === "MESSAGE_NOT_MODIFIED") return;
|
|
2621
|
-
|
|
2292
|
+
log5.debug(`GramJS edit failed, falling back to Grammy: ${errMsg || error}`);
|
|
2622
2293
|
}
|
|
2623
2294
|
}
|
|
2624
2295
|
const replyMarkup = styledButtons ? toGrammyKeyboard(styledButtons) : void 0;
|
|
@@ -2638,7 +2309,7 @@ var InlineRouter = class {
|
|
|
2638
2309
|
await ctx.answerCallbackQuery();
|
|
2639
2310
|
}
|
|
2640
2311
|
} catch (error) {
|
|
2641
|
-
|
|
2312
|
+
log5.error({ err: error }, `Plugin "${pluginName}" callback handler failed`);
|
|
2642
2313
|
if (!answered) {
|
|
2643
2314
|
try {
|
|
2644
2315
|
await ctx.answerCallbackQuery({ text: "Error processing action" });
|
|
@@ -2661,7 +2332,7 @@ var InlineRouter = class {
|
|
|
2661
2332
|
};
|
|
2662
2333
|
await plugin.onChosenResult(crCtx);
|
|
2663
2334
|
} catch (error) {
|
|
2664
|
-
|
|
2335
|
+
log5.error({ err: error }, `Plugin "${pluginName}" chosen result handler failed`);
|
|
2665
2336
|
}
|
|
2666
2337
|
}
|
|
2667
2338
|
/**
|
|
@@ -2740,7 +2411,7 @@ function withTimeout(promise, ms, message) {
|
|
|
2740
2411
|
|
|
2741
2412
|
// src/agent/tools/plugin-loader.ts
|
|
2742
2413
|
import { readdirSync as readdirSync2, readFileSync as readFileSync5, existsSync as existsSync6, statSync } from "fs";
|
|
2743
|
-
import { join as
|
|
2414
|
+
import { join as join4 } from "path";
|
|
2744
2415
|
import { pathToFileURL } from "url";
|
|
2745
2416
|
import { execFile } from "child_process";
|
|
2746
2417
|
|
|
@@ -2771,7 +2442,7 @@ import { promisify } from "util";
|
|
|
2771
2442
|
|
|
2772
2443
|
// src/agent/tools/plugin-validator.ts
|
|
2773
2444
|
import { z } from "zod";
|
|
2774
|
-
var
|
|
2445
|
+
var log6 = createLogger("PluginValidator");
|
|
2775
2446
|
var ManifestSchema = z.object({
|
|
2776
2447
|
name: z.string().min(1).max(64).regex(
|
|
2777
2448
|
/^[a-z0-9][a-z0-9-]*$/,
|
|
@@ -2814,20 +2485,20 @@ function validateToolDefs(defs, pluginName) {
|
|
|
2814
2485
|
const valid = [];
|
|
2815
2486
|
for (const def of defs) {
|
|
2816
2487
|
if (!def || typeof def !== "object") {
|
|
2817
|
-
|
|
2488
|
+
log6.warn(`[${pluginName}] tool is not an object, skipping`);
|
|
2818
2489
|
continue;
|
|
2819
2490
|
}
|
|
2820
2491
|
const t = def;
|
|
2821
2492
|
if (!t.name || typeof t.name !== "string") {
|
|
2822
|
-
|
|
2493
|
+
log6.warn(`[${pluginName}] tool missing 'name', skipping`);
|
|
2823
2494
|
continue;
|
|
2824
2495
|
}
|
|
2825
2496
|
if (!t.description || typeof t.description !== "string") {
|
|
2826
|
-
|
|
2497
|
+
log6.warn(`[${pluginName}] tool "${t.name}" missing 'description', skipping`);
|
|
2827
2498
|
continue;
|
|
2828
2499
|
}
|
|
2829
2500
|
if (!t.execute || typeof t.execute !== "function") {
|
|
2830
|
-
|
|
2501
|
+
log6.warn(`[${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
|
|
2831
2502
|
continue;
|
|
2832
2503
|
}
|
|
2833
2504
|
valid.push(t);
|
|
@@ -2864,8 +2535,19 @@ import { Address, SendMode } from "@ton/core";
|
|
|
2864
2535
|
|
|
2865
2536
|
// src/ton/tx-lock.ts
|
|
2866
2537
|
var pending = Promise.resolve();
|
|
2538
|
+
var TX_LOCK_TIMEOUT_MS = 6e4;
|
|
2867
2539
|
function withTxLock(fn) {
|
|
2868
|
-
const
|
|
2540
|
+
const guarded = () => {
|
|
2541
|
+
let timerId;
|
|
2542
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
2543
|
+
timerId = setTimeout(
|
|
2544
|
+
() => reject(new Error("TON tx-lock timeout (60s)")),
|
|
2545
|
+
TX_LOCK_TIMEOUT_MS
|
|
2546
|
+
);
|
|
2547
|
+
});
|
|
2548
|
+
return Promise.race([fn(), timeoutPromise]).finally(() => clearTimeout(timerId));
|
|
2549
|
+
};
|
|
2550
|
+
const execute = pending.then(guarded, guarded);
|
|
2869
2551
|
pending = execute.then(
|
|
2870
2552
|
() => {
|
|
2871
2553
|
},
|
|
@@ -2876,25 +2558,25 @@ function withTxLock(fn) {
|
|
|
2876
2558
|
}
|
|
2877
2559
|
|
|
2878
2560
|
// src/ton/transfer.ts
|
|
2879
|
-
var
|
|
2561
|
+
var log7 = createLogger("TON");
|
|
2880
2562
|
async function sendTon(params) {
|
|
2881
2563
|
return withTxLock(async () => {
|
|
2882
2564
|
try {
|
|
2883
2565
|
const { toAddress: toAddress2, amount, comment = "", bounce = false } = params;
|
|
2884
2566
|
if (!Number.isFinite(amount) || amount <= 0) {
|
|
2885
|
-
|
|
2567
|
+
log7.error({ amount }, "Invalid transfer amount");
|
|
2886
2568
|
return null;
|
|
2887
2569
|
}
|
|
2888
2570
|
let recipientAddress;
|
|
2889
2571
|
try {
|
|
2890
2572
|
recipientAddress = Address.parse(toAddress2);
|
|
2891
2573
|
} catch (e) {
|
|
2892
|
-
|
|
2574
|
+
log7.error({ err: e }, `Invalid recipient address: ${toAddress2}`);
|
|
2893
2575
|
return null;
|
|
2894
2576
|
}
|
|
2895
2577
|
const keyPair = await getKeyPair();
|
|
2896
2578
|
if (!keyPair) {
|
|
2897
|
-
|
|
2579
|
+
log7.error("Wallet not initialized");
|
|
2898
2580
|
return null;
|
|
2899
2581
|
}
|
|
2900
2582
|
const wallet = WalletContractV5R1.create({
|
|
@@ -2918,7 +2600,7 @@ async function sendTon(params) {
|
|
|
2918
2600
|
]
|
|
2919
2601
|
});
|
|
2920
2602
|
const pseudoHash = `${seqno}_${Date.now()}_${amount.toFixed(2)}`;
|
|
2921
|
-
|
|
2603
|
+
log7.info(`Sent ${amount} TON to ${toAddress2.slice(0, 8)}... - seqno: ${seqno}`);
|
|
2922
2604
|
return pseudoHash;
|
|
2923
2605
|
} catch (error) {
|
|
2924
2606
|
const err = error;
|
|
@@ -2926,14 +2608,14 @@ async function sendTon(params) {
|
|
|
2926
2608
|
if (status === 429 || status !== void 0 && status >= 500) {
|
|
2927
2609
|
invalidateTonClientCache();
|
|
2928
2610
|
}
|
|
2929
|
-
|
|
2611
|
+
log7.error({ err: error }, "Error sending TON");
|
|
2930
2612
|
throw error;
|
|
2931
2613
|
}
|
|
2932
2614
|
});
|
|
2933
2615
|
}
|
|
2934
2616
|
|
|
2935
2617
|
// src/utils/retry.ts
|
|
2936
|
-
var
|
|
2618
|
+
var log8 = createLogger("Utils");
|
|
2937
2619
|
var DEFAULT_OPTIONS = {
|
|
2938
2620
|
maxAttempts: RETRY_DEFAULT_MAX_ATTEMPTS,
|
|
2939
2621
|
baseDelayMs: RETRY_DEFAULT_BASE_DELAY_MS,
|
|
@@ -2957,7 +2639,7 @@ async function withRetry(fn, options = {}) {
|
|
|
2957
2639
|
return result;
|
|
2958
2640
|
} catch (error) {
|
|
2959
2641
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2960
|
-
|
|
2642
|
+
log8.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed: ${lastError.message}`);
|
|
2961
2643
|
if (attempt < opts.maxAttempts) {
|
|
2962
2644
|
const delay = Math.min(opts.baseDelayMs * Math.pow(2, attempt - 1), opts.maxDelayMs);
|
|
2963
2645
|
await sleep(delay);
|
|
@@ -6435,7 +6117,7 @@ var DEDUST_GAS = {
|
|
|
6435
6117
|
var NATIVE_TON_ADDRESS = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
|
|
6436
6118
|
|
|
6437
6119
|
// src/agent/tools/dedust/asset-cache.ts
|
|
6438
|
-
var
|
|
6120
|
+
var log9 = createLogger("Tools");
|
|
6439
6121
|
var ASSET_LIST_URL = "https://assets.dedust.io/list.json";
|
|
6440
6122
|
var CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
6441
6123
|
var cachedAssets = [];
|
|
@@ -6454,7 +6136,7 @@ async function getAssetList() {
|
|
|
6454
6136
|
return cachedAssets;
|
|
6455
6137
|
} catch (error) {
|
|
6456
6138
|
if (cachedAssets.length > 0) {
|
|
6457
|
-
|
|
6139
|
+
log9.warn({ err: error }, "Asset list fetch failed, using stale cache");
|
|
6458
6140
|
return cachedAssets;
|
|
6459
6141
|
}
|
|
6460
6142
|
throw error;
|
|
@@ -6507,7 +6189,7 @@ var stonApiClient = new StonApiClient();
|
|
|
6507
6189
|
function isTon(asset) {
|
|
6508
6190
|
return asset.toLowerCase() === "ton";
|
|
6509
6191
|
}
|
|
6510
|
-
async function getStonfiQuote(fromAsset, toAsset, amount, slippage,
|
|
6192
|
+
async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log13) {
|
|
6511
6193
|
try {
|
|
6512
6194
|
const isTonInput = isTon(fromAsset);
|
|
6513
6195
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6538,11 +6220,11 @@ async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log15) {
|
|
|
6538
6220
|
fee: feeAmount.toFixed(6)
|
|
6539
6221
|
};
|
|
6540
6222
|
} catch (err) {
|
|
6541
|
-
|
|
6223
|
+
log13.debug("dex.quoteSTONfi() failed:", err);
|
|
6542
6224
|
return null;
|
|
6543
6225
|
}
|
|
6544
6226
|
}
|
|
6545
|
-
async function getDedustQuote(fromAsset, toAsset, amount, slippage,
|
|
6227
|
+
async function getDedustQuote(fromAsset, toAsset, amount, slippage, log13) {
|
|
6546
6228
|
try {
|
|
6547
6229
|
const isTonInput = isTon(fromAsset);
|
|
6548
6230
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6576,7 +6258,7 @@ async function getDedustQuote(fromAsset, toAsset, amount, slippage, log15) {
|
|
|
6576
6258
|
poolType
|
|
6577
6259
|
};
|
|
6578
6260
|
} catch (err) {
|
|
6579
|
-
|
|
6261
|
+
log13.debug("dex.quoteDeDust() failed:", err);
|
|
6580
6262
|
return null;
|
|
6581
6263
|
}
|
|
6582
6264
|
}
|
|
@@ -6748,14 +6430,14 @@ function validateDexParams(amount, slippage) {
|
|
|
6748
6430
|
throw new PluginSDKError("Slippage must be between 0 and 1", "OPERATION_FAILED");
|
|
6749
6431
|
}
|
|
6750
6432
|
}
|
|
6751
|
-
function createDexSDK(
|
|
6433
|
+
function createDexSDK(log13) {
|
|
6752
6434
|
return {
|
|
6753
6435
|
async quote(params) {
|
|
6754
6436
|
validateDexParams(params.amount, params.slippage);
|
|
6755
6437
|
const slippage = params.slippage ?? 0.01;
|
|
6756
6438
|
const [stonfi, dedust] = await Promise.all([
|
|
6757
|
-
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
6758
|
-
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
6439
|
+
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage, log13),
|
|
6440
|
+
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage, log13)
|
|
6759
6441
|
]);
|
|
6760
6442
|
if (!stonfi && !dedust) {
|
|
6761
6443
|
throw new PluginSDKError("No DEX has liquidity for this pair", "OPERATION_FAILED");
|
|
@@ -6789,7 +6471,7 @@ function createDexSDK(log15) {
|
|
|
6789
6471
|
params.toAsset,
|
|
6790
6472
|
params.amount,
|
|
6791
6473
|
params.slippage ?? 0.01,
|
|
6792
|
-
|
|
6474
|
+
log13
|
|
6793
6475
|
);
|
|
6794
6476
|
},
|
|
6795
6477
|
async quoteDeDust(params) {
|
|
@@ -6798,25 +6480,25 @@ function createDexSDK(log15) {
|
|
|
6798
6480
|
params.toAsset,
|
|
6799
6481
|
params.amount,
|
|
6800
6482
|
params.slippage ?? 0.01,
|
|
6801
|
-
|
|
6483
|
+
log13
|
|
6802
6484
|
);
|
|
6803
6485
|
},
|
|
6804
6486
|
async swap(params) {
|
|
6805
6487
|
validateDexParams(params.amount, params.slippage);
|
|
6806
6488
|
if (params.dex === "stonfi") {
|
|
6807
|
-
return executeSTONfiSwap(params,
|
|
6489
|
+
return executeSTONfiSwap(params, log13);
|
|
6808
6490
|
}
|
|
6809
6491
|
if (params.dex === "dedust") {
|
|
6810
|
-
return executeDedustSwap(params,
|
|
6492
|
+
return executeDedustSwap(params, log13);
|
|
6811
6493
|
}
|
|
6812
6494
|
const quoteResult = await this.quote(params);
|
|
6813
|
-
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params,
|
|
6495
|
+
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params, log13) : executeDedustSwap(params, log13);
|
|
6814
6496
|
},
|
|
6815
6497
|
async swapSTONfi(params) {
|
|
6816
|
-
return executeSTONfiSwap(params,
|
|
6498
|
+
return executeSTONfiSwap(params, log13);
|
|
6817
6499
|
},
|
|
6818
6500
|
async swapDeDust(params) {
|
|
6819
|
-
return executeDedustSwap(params,
|
|
6501
|
+
return executeDedustSwap(params, log13);
|
|
6820
6502
|
}
|
|
6821
6503
|
};
|
|
6822
6504
|
}
|
|
@@ -6853,7 +6535,7 @@ function normalizeDomain(domain) {
|
|
|
6853
6535
|
if (!d.endsWith(".ton")) d += ".ton";
|
|
6854
6536
|
return d;
|
|
6855
6537
|
}
|
|
6856
|
-
function createDnsSDK(
|
|
6538
|
+
function createDnsSDK(log13) {
|
|
6857
6539
|
return {
|
|
6858
6540
|
async check(domain) {
|
|
6859
6541
|
const normalized = normalizeDomain(domain);
|
|
@@ -6876,7 +6558,7 @@ function createDnsSDK(log15) {
|
|
|
6876
6558
|
};
|
|
6877
6559
|
} catch (err) {
|
|
6878
6560
|
if (err instanceof PluginSDKError) throw err;
|
|
6879
|
-
|
|
6561
|
+
log13.debug("dns.check() failed:", err);
|
|
6880
6562
|
throw new PluginSDKError(
|
|
6881
6563
|
`Failed to check domain: ${err instanceof Error ? err.message : String(err)}`,
|
|
6882
6564
|
"OPERATION_FAILED"
|
|
@@ -6889,7 +6571,7 @@ function createDnsSDK(log15) {
|
|
|
6889
6571
|
const response = await tonapiFetch(`/dns/${encodeURIComponent(normalized)}`);
|
|
6890
6572
|
if (response.status === 404) return null;
|
|
6891
6573
|
if (!response.ok) {
|
|
6892
|
-
|
|
6574
|
+
log13.debug(`dns.resolve() TonAPI error: ${response.status}`);
|
|
6893
6575
|
return null;
|
|
6894
6576
|
}
|
|
6895
6577
|
const data = await response.json();
|
|
@@ -6901,7 +6583,7 @@ function createDnsSDK(log15) {
|
|
|
6901
6583
|
expirationDate: data.expiring_at || void 0
|
|
6902
6584
|
};
|
|
6903
6585
|
} catch (err) {
|
|
6904
|
-
|
|
6586
|
+
log13.debug("dns.resolve() failed:", err);
|
|
6905
6587
|
return null;
|
|
6906
6588
|
}
|
|
6907
6589
|
},
|
|
@@ -6911,7 +6593,7 @@ function createDnsSDK(log15) {
|
|
|
6911
6593
|
`/dns/auctions?tld=ton&limit=${Math.min(limit ?? 20, 100)}`
|
|
6912
6594
|
);
|
|
6913
6595
|
if (!response.ok) {
|
|
6914
|
-
|
|
6596
|
+
log13.debug(`dns.getAuctions() TonAPI error: ${response.status}`);
|
|
6915
6597
|
return [];
|
|
6916
6598
|
}
|
|
6917
6599
|
const data = await response.json();
|
|
@@ -6924,7 +6606,7 @@ function createDnsSDK(log15) {
|
|
|
6924
6606
|
bids: a.bids || 0
|
|
6925
6607
|
}));
|
|
6926
6608
|
} catch (err) {
|
|
6927
|
-
|
|
6609
|
+
log13.debug("dns.getAuctions() failed:", err);
|
|
6928
6610
|
return [];
|
|
6929
6611
|
}
|
|
6930
6612
|
},
|
|
@@ -7238,25 +6920,25 @@ function findJettonBalance(balances, jettonAddress) {
|
|
|
7238
6920
|
}
|
|
7239
6921
|
});
|
|
7240
6922
|
}
|
|
7241
|
-
function cleanupOldTransactions(db, retentionDays,
|
|
6923
|
+
function cleanupOldTransactions(db, retentionDays, log13) {
|
|
7242
6924
|
if (Math.random() > CLEANUP_PROBABILITY) return;
|
|
7243
6925
|
try {
|
|
7244
6926
|
const cutoff = Math.floor(Date.now() / 1e3) - retentionDays * 24 * 60 * 60;
|
|
7245
6927
|
const result = db.prepare("DELETE FROM used_transactions WHERE used_at < ?").run(cutoff);
|
|
7246
6928
|
if (result.changes > 0) {
|
|
7247
|
-
|
|
6929
|
+
log13.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
|
|
7248
6930
|
}
|
|
7249
6931
|
} catch (err) {
|
|
7250
|
-
|
|
6932
|
+
log13.error("Transaction cleanup failed:", err);
|
|
7251
6933
|
}
|
|
7252
6934
|
}
|
|
7253
|
-
function createTonSDK(
|
|
6935
|
+
function createTonSDK(log13, db) {
|
|
7254
6936
|
return {
|
|
7255
6937
|
getAddress() {
|
|
7256
6938
|
try {
|
|
7257
6939
|
return getWalletAddress();
|
|
7258
6940
|
} catch (err) {
|
|
7259
|
-
|
|
6941
|
+
log13.error("ton.getAddress() failed:", err);
|
|
7260
6942
|
return null;
|
|
7261
6943
|
}
|
|
7262
6944
|
},
|
|
@@ -7266,7 +6948,7 @@ function createTonSDK(log15, db) {
|
|
|
7266
6948
|
if (!addr) return null;
|
|
7267
6949
|
return await getWalletBalance(addr);
|
|
7268
6950
|
} catch (err) {
|
|
7269
|
-
|
|
6951
|
+
log13.error("ton.getBalance() failed:", err);
|
|
7270
6952
|
return null;
|
|
7271
6953
|
}
|
|
7272
6954
|
},
|
|
@@ -7274,7 +6956,7 @@ function createTonSDK(log15, db) {
|
|
|
7274
6956
|
try {
|
|
7275
6957
|
return await getTonPrice();
|
|
7276
6958
|
} catch (err) {
|
|
7277
|
-
|
|
6959
|
+
log13.error("ton.getPrice() failed:", err);
|
|
7278
6960
|
return null;
|
|
7279
6961
|
}
|
|
7280
6962
|
},
|
|
@@ -7325,7 +7007,7 @@ function createTonSDK(log15, db) {
|
|
|
7325
7007
|
);
|
|
7326
7008
|
return formatTransactions(transactions);
|
|
7327
7009
|
} catch (err) {
|
|
7328
|
-
|
|
7010
|
+
log13.error("ton.getTransactions() failed:", err);
|
|
7329
7011
|
return [];
|
|
7330
7012
|
}
|
|
7331
7013
|
},
|
|
@@ -7348,7 +7030,7 @@ function createTonSDK(log15, db) {
|
|
|
7348
7030
|
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
7349
7031
|
}
|
|
7350
7032
|
const maxAgeMinutes = params.maxAgeMinutes ?? DEFAULT_MAX_AGE_MINUTES;
|
|
7351
|
-
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS,
|
|
7033
|
+
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log13);
|
|
7352
7034
|
try {
|
|
7353
7035
|
const txs = await this.getTransactions(address4, 20);
|
|
7354
7036
|
for (const tx of txs) {
|
|
@@ -7382,7 +7064,7 @@ function createTonSDK(log15, db) {
|
|
|
7382
7064
|
};
|
|
7383
7065
|
} catch (err) {
|
|
7384
7066
|
if (err instanceof PluginSDKError) throw err;
|
|
7385
|
-
|
|
7067
|
+
log13.error("ton.verifyPayment() failed:", err);
|
|
7386
7068
|
return {
|
|
7387
7069
|
verified: false,
|
|
7388
7070
|
error: `Verification failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -7396,7 +7078,7 @@ function createTonSDK(log15, db) {
|
|
|
7396
7078
|
if (!addr) return [];
|
|
7397
7079
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(addr)}/jettons`);
|
|
7398
7080
|
if (!response.ok) {
|
|
7399
|
-
|
|
7081
|
+
log13.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
|
|
7400
7082
|
return [];
|
|
7401
7083
|
}
|
|
7402
7084
|
const data = await response.json();
|
|
@@ -7420,7 +7102,7 @@ function createTonSDK(log15, db) {
|
|
|
7420
7102
|
}
|
|
7421
7103
|
return balances;
|
|
7422
7104
|
} catch (err) {
|
|
7423
|
-
|
|
7105
|
+
log13.error("ton.getJettonBalances() failed:", err);
|
|
7424
7106
|
return [];
|
|
7425
7107
|
}
|
|
7426
7108
|
},
|
|
@@ -7429,7 +7111,7 @@ function createTonSDK(log15, db) {
|
|
|
7429
7111
|
const response = await tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`);
|
|
7430
7112
|
if (response.status === 404) return null;
|
|
7431
7113
|
if (!response.ok) {
|
|
7432
|
-
|
|
7114
|
+
log13.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
|
|
7433
7115
|
return null;
|
|
7434
7116
|
}
|
|
7435
7117
|
const data = await response.json();
|
|
@@ -7447,7 +7129,7 @@ function createTonSDK(log15, db) {
|
|
|
7447
7129
|
image: data.preview || metadata.image || void 0
|
|
7448
7130
|
};
|
|
7449
7131
|
} catch (err) {
|
|
7450
|
-
|
|
7132
|
+
log13.error("ton.getJettonInfo() failed:", err);
|
|
7451
7133
|
return null;
|
|
7452
7134
|
}
|
|
7453
7135
|
},
|
|
@@ -7534,12 +7216,13 @@ function createTonSDK(log15, db) {
|
|
|
7534
7216
|
return seq;
|
|
7535
7217
|
} catch (err) {
|
|
7536
7218
|
lastErr = err;
|
|
7537
|
-
const
|
|
7538
|
-
const
|
|
7219
|
+
const httpErr = isHttpError(err) ? err : void 0;
|
|
7220
|
+
const status = httpErr?.status || httpErr?.response?.status;
|
|
7221
|
+
const respData = httpErr?.response?.data;
|
|
7539
7222
|
if (status === 429 || status && status >= 500) {
|
|
7540
7223
|
invalidateTonClientCache();
|
|
7541
7224
|
if (attempt < MAX_SEND_ATTEMPTS) {
|
|
7542
|
-
|
|
7225
|
+
log13.warn(
|
|
7543
7226
|
`sendJetton attempt ${attempt} failed (${status}): ${JSON.stringify(respData ?? err.message)}, retrying...`
|
|
7544
7227
|
);
|
|
7545
7228
|
await new Promise((r3) => setTimeout(r3, 1e3 * attempt));
|
|
@@ -7553,7 +7236,8 @@ function createTonSDK(log15, db) {
|
|
|
7553
7236
|
});
|
|
7554
7237
|
return { success: true, seqno };
|
|
7555
7238
|
} catch (err) {
|
|
7556
|
-
const
|
|
7239
|
+
const outerHttpErr = isHttpError(err) ? err : void 0;
|
|
7240
|
+
const status = outerHttpErr?.status || outerHttpErr?.response?.status;
|
|
7557
7241
|
if (status === 429 || status && status >= 500) {
|
|
7558
7242
|
invalidateTonClientCache();
|
|
7559
7243
|
}
|
|
@@ -7568,14 +7252,14 @@ function createTonSDK(log15, db) {
|
|
|
7568
7252
|
try {
|
|
7569
7253
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(ownerAddress)}/jettons`);
|
|
7570
7254
|
if (!response.ok) {
|
|
7571
|
-
|
|
7255
|
+
log13.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
|
|
7572
7256
|
return null;
|
|
7573
7257
|
}
|
|
7574
7258
|
const data = await response.json();
|
|
7575
7259
|
const match = findJettonBalance(data.balances ?? [], jettonAddress);
|
|
7576
7260
|
return match ? match.wallet_address.address : null;
|
|
7577
7261
|
} catch (err) {
|
|
7578
|
-
|
|
7262
|
+
log13.error("ton.getJettonWalletAddress() failed:", err);
|
|
7579
7263
|
return null;
|
|
7580
7264
|
}
|
|
7581
7265
|
},
|
|
@@ -7752,7 +7436,7 @@ function createTonSDK(log15, db) {
|
|
|
7752
7436
|
const wallet = loadWallet();
|
|
7753
7437
|
return wallet?.publicKey ?? null;
|
|
7754
7438
|
} catch (err) {
|
|
7755
|
-
|
|
7439
|
+
log13.error("ton.getPublicKey() failed:", err);
|
|
7756
7440
|
return null;
|
|
7757
7441
|
}
|
|
7758
7442
|
},
|
|
@@ -7768,14 +7452,14 @@ function createTonSDK(log15, db) {
|
|
|
7768
7452
|
`/accounts/${encodeURIComponent(addr)}/nfts?limit=100&indirect_ownership=true`
|
|
7769
7453
|
);
|
|
7770
7454
|
if (!response.ok) {
|
|
7771
|
-
|
|
7455
|
+
log13.error(`ton.getNftItems() TonAPI error: ${response.status}`);
|
|
7772
7456
|
return [];
|
|
7773
7457
|
}
|
|
7774
7458
|
const data = await response.json();
|
|
7775
7459
|
if (!Array.isArray(data.nft_items)) return [];
|
|
7776
7460
|
return data.nft_items.filter((item) => item.trust !== "blacklist").map((item) => mapNftItem(item));
|
|
7777
7461
|
} catch (err) {
|
|
7778
|
-
|
|
7462
|
+
log13.error("ton.getNftItems() failed:", err);
|
|
7779
7463
|
return [];
|
|
7780
7464
|
}
|
|
7781
7465
|
},
|
|
@@ -7784,13 +7468,13 @@ function createTonSDK(log15, db) {
|
|
|
7784
7468
|
const response = await tonapiFetch(`/nfts/${encodeURIComponent(nftAddress)}`);
|
|
7785
7469
|
if (response.status === 404) return null;
|
|
7786
7470
|
if (!response.ok) {
|
|
7787
|
-
|
|
7471
|
+
log13.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
|
|
7788
7472
|
return null;
|
|
7789
7473
|
}
|
|
7790
7474
|
const item = await response.json();
|
|
7791
7475
|
return mapNftItem(item);
|
|
7792
7476
|
} catch (err) {
|
|
7793
|
-
|
|
7477
|
+
log13.error("ton.getNftInfo() failed:", err);
|
|
7794
7478
|
return null;
|
|
7795
7479
|
}
|
|
7796
7480
|
},
|
|
@@ -7823,7 +7507,7 @@ function createTonSDK(log15, db) {
|
|
|
7823
7507
|
`/rates?tokens=${encodeURIComponent(jettonAddress)}¤cies=usd,ton`
|
|
7824
7508
|
);
|
|
7825
7509
|
if (!response.ok) {
|
|
7826
|
-
|
|
7510
|
+
log13.debug(`ton.getJettonPrice() TonAPI error: ${response.status}`);
|
|
7827
7511
|
return null;
|
|
7828
7512
|
}
|
|
7829
7513
|
const data = await response.json();
|
|
@@ -7837,7 +7521,7 @@ function createTonSDK(log15, db) {
|
|
|
7837
7521
|
change30d: rateData.diff_30d?.USD ?? null
|
|
7838
7522
|
};
|
|
7839
7523
|
} catch (err) {
|
|
7840
|
-
|
|
7524
|
+
log13.debug("ton.getJettonPrice() failed:", err);
|
|
7841
7525
|
return null;
|
|
7842
7526
|
}
|
|
7843
7527
|
},
|
|
@@ -7851,7 +7535,7 @@ function createTonSDK(log15, db) {
|
|
|
7851
7535
|
tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`)
|
|
7852
7536
|
]);
|
|
7853
7537
|
if (!holdersResponse.ok) {
|
|
7854
|
-
|
|
7538
|
+
log13.debug(`ton.getJettonHolders() TonAPI error: ${holdersResponse.status}`);
|
|
7855
7539
|
return [];
|
|
7856
7540
|
}
|
|
7857
7541
|
const data = await holdersResponse.json();
|
|
@@ -7871,7 +7555,7 @@ function createTonSDK(log15, db) {
|
|
|
7871
7555
|
};
|
|
7872
7556
|
});
|
|
7873
7557
|
} catch (err) {
|
|
7874
|
-
|
|
7558
|
+
log13.debug("ton.getJettonHolders() failed:", err);
|
|
7875
7559
|
return [];
|
|
7876
7560
|
}
|
|
7877
7561
|
},
|
|
@@ -7943,13 +7627,13 @@ function createTonSDK(log15, db) {
|
|
|
7943
7627
|
holders: holdersCount
|
|
7944
7628
|
};
|
|
7945
7629
|
} catch (err) {
|
|
7946
|
-
|
|
7630
|
+
log13.debug("ton.getJettonHistory() failed:", err);
|
|
7947
7631
|
return null;
|
|
7948
7632
|
}
|
|
7949
7633
|
},
|
|
7950
7634
|
// ─── Sub-namespaces ───────────────────────────────────────────
|
|
7951
|
-
dex: Object.freeze(createDexSDK(
|
|
7952
|
-
dns: Object.freeze(createDnsSDK(
|
|
7635
|
+
dex: Object.freeze(createDexSDK(log13)),
|
|
7636
|
+
dns: Object.freeze(createDnsSDK(log13))
|
|
7953
7637
|
};
|
|
7954
7638
|
}
|
|
7955
7639
|
function mapNftItem(item) {
|
|
@@ -7973,15 +7657,6 @@ function mapNftItem(item) {
|
|
|
7973
7657
|
// src/sdk/telegram.ts
|
|
7974
7658
|
import { Api as Api3 } from "telegram";
|
|
7975
7659
|
|
|
7976
|
-
// src/utils/gramjs-bigint.ts
|
|
7977
|
-
import { randomBytes } from "crypto";
|
|
7978
|
-
function toLong(value) {
|
|
7979
|
-
return typeof value === "bigint" ? value : BigInt(value);
|
|
7980
|
-
}
|
|
7981
|
-
function randomLong() {
|
|
7982
|
-
return randomBytes(8).readBigUInt64BE();
|
|
7983
|
-
}
|
|
7984
|
-
|
|
7985
7660
|
// src/sdk/telegram-utils.ts
|
|
7986
7661
|
function requireBridge(bridge) {
|
|
7987
7662
|
if (!bridge.isAvailable()) {
|
|
@@ -7998,7 +7673,6 @@ function toSimpleMessage(msg) {
|
|
|
7998
7673
|
return {
|
|
7999
7674
|
id: msg.id,
|
|
8000
7675
|
text: msg.message ?? "",
|
|
8001
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- GramJS fromId is a union of untyped peer types
|
|
8002
7676
|
senderId: Number(msg.fromId?.userId ?? msg.fromId?.channelId ?? 0),
|
|
8003
7677
|
timestamp: new Date(msg.date * 1e3)
|
|
8004
7678
|
};
|
|
@@ -8012,7 +7686,7 @@ async function getApi() {
|
|
|
8012
7686
|
}
|
|
8013
7687
|
|
|
8014
7688
|
// src/sdk/telegram-messages.ts
|
|
8015
|
-
function createTelegramMessagesSDK(bridge,
|
|
7689
|
+
function createTelegramMessagesSDK(bridge, log13) {
|
|
8016
7690
|
function requireBridge2() {
|
|
8017
7691
|
requireBridge(bridge);
|
|
8018
7692
|
}
|
|
@@ -8119,7 +7793,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8119
7793
|
return (resultData.messages ?? []).map(toSimpleMessage);
|
|
8120
7794
|
} catch (err) {
|
|
8121
7795
|
if (err instanceof PluginSDKError) throw err;
|
|
8122
|
-
|
|
7796
|
+
log13.error("telegram.searchMessages() failed:", err);
|
|
8123
7797
|
return [];
|
|
8124
7798
|
}
|
|
8125
7799
|
},
|
|
@@ -8156,8 +7830,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8156
7830
|
limit,
|
|
8157
7831
|
maxId: 0,
|
|
8158
7832
|
minId: 0,
|
|
8159
|
-
|
|
8160
|
-
hash: 0n
|
|
7833
|
+
hash: toLong(0n)
|
|
8161
7834
|
})
|
|
8162
7835
|
);
|
|
8163
7836
|
const messages = [];
|
|
@@ -8320,10 +7993,12 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8320
7993
|
if (!messages || messages.length === 0 || !messages[0].media) {
|
|
8321
7994
|
return null;
|
|
8322
7995
|
}
|
|
8323
|
-
const
|
|
8324
|
-
|
|
7996
|
+
const media = messages[0].media;
|
|
7997
|
+
const doc = media && "document" in media ? media.document : void 0;
|
|
7998
|
+
const docSize = doc && "size" in doc ? doc.size : void 0;
|
|
7999
|
+
if (docSize && Number(docSize) > MAX_DOWNLOAD_SIZE) {
|
|
8325
8000
|
throw new PluginSDKError(
|
|
8326
|
-
`File too large (${Math.round(Number(
|
|
8001
|
+
`File too large (${Math.round(Number(docSize) / 1024 / 1024)}MB). Max: 50MB`,
|
|
8327
8002
|
"OPERATION_FAILED"
|
|
8328
8003
|
);
|
|
8329
8004
|
}
|
|
@@ -8347,8 +8022,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8347
8022
|
const result = await gramJsClient.invoke(
|
|
8348
8023
|
new Api4.messages.GetScheduledHistory({
|
|
8349
8024
|
peer,
|
|
8350
|
-
|
|
8351
|
-
hash: 0n
|
|
8025
|
+
hash: toLong(0n)
|
|
8352
8026
|
})
|
|
8353
8027
|
);
|
|
8354
8028
|
const messages = [];
|
|
@@ -8362,7 +8036,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8362
8036
|
return messages;
|
|
8363
8037
|
} catch (err) {
|
|
8364
8038
|
if (err instanceof PluginSDKError) throw err;
|
|
8365
|
-
|
|
8039
|
+
log13.error("telegram.getScheduledMessages() failed:", err);
|
|
8366
8040
|
return [];
|
|
8367
8041
|
}
|
|
8368
8042
|
},
|
|
@@ -8423,7 +8097,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8423
8097
|
}
|
|
8424
8098
|
|
|
8425
8099
|
// src/sdk/telegram-social.ts
|
|
8426
|
-
function createTelegramSocialSDK(bridge,
|
|
8100
|
+
function createTelegramSocialSDK(bridge, log13) {
|
|
8427
8101
|
function requireBridge2() {
|
|
8428
8102
|
requireBridge(bridge);
|
|
8429
8103
|
}
|
|
@@ -8498,7 +8172,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8498
8172
|
return null;
|
|
8499
8173
|
} catch (err) {
|
|
8500
8174
|
if (err instanceof PluginSDKError) throw err;
|
|
8501
|
-
|
|
8175
|
+
log13.error("telegram.getChatInfo() failed:", err);
|
|
8502
8176
|
return null;
|
|
8503
8177
|
}
|
|
8504
8178
|
},
|
|
@@ -8612,7 +8286,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8612
8286
|
});
|
|
8613
8287
|
} catch (err) {
|
|
8614
8288
|
if (err instanceof PluginSDKError) throw err;
|
|
8615
|
-
|
|
8289
|
+
log13.error("telegram.getParticipants() failed:", err);
|
|
8616
8290
|
return [];
|
|
8617
8291
|
}
|
|
8618
8292
|
},
|
|
@@ -8829,7 +8503,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8829
8503
|
const user = await client.getInputEntity(userId.toString());
|
|
8830
8504
|
const invoiceData = {
|
|
8831
8505
|
peer: user,
|
|
8832
|
-
giftId:
|
|
8506
|
+
giftId: toLong(giftId),
|
|
8833
8507
|
hideName: opts?.anonymous ?? false,
|
|
8834
8508
|
message: opts?.message ? new Api4.TextWithEntities({ text: opts.message, entities: [] }) : void 0
|
|
8835
8509
|
};
|
|
@@ -8913,7 +8587,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8913
8587
|
const Api4 = await getApi();
|
|
8914
8588
|
const result = await client.invoke(
|
|
8915
8589
|
new Api4.payments.GetResaleStarGifts({
|
|
8916
|
-
giftId:
|
|
8590
|
+
giftId: toLong(giftId),
|
|
8917
8591
|
offset: "",
|
|
8918
8592
|
limit: limit ?? 50
|
|
8919
8593
|
})
|
|
@@ -8974,7 +8648,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8974
8648
|
}));
|
|
8975
8649
|
} catch (err) {
|
|
8976
8650
|
if (err instanceof PluginSDKError) throw err;
|
|
8977
|
-
|
|
8651
|
+
log13.error("telegram.getDialogs() failed:", err);
|
|
8978
8652
|
return [];
|
|
8979
8653
|
}
|
|
8980
8654
|
},
|
|
@@ -8988,7 +8662,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8988
8662
|
return messages.map(toSimpleMessage);
|
|
8989
8663
|
} catch (err) {
|
|
8990
8664
|
if (err instanceof PluginSDKError) throw err;
|
|
8991
|
-
|
|
8665
|
+
log13.error("telegram.getHistory() failed:", err);
|
|
8992
8666
|
return [];
|
|
8993
8667
|
}
|
|
8994
8668
|
},
|
|
@@ -9019,7 +8693,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9019
8693
|
}));
|
|
9020
8694
|
} catch (err) {
|
|
9021
8695
|
if (err instanceof PluginSDKError) throw err;
|
|
9022
|
-
|
|
8696
|
+
log13.error("telegram.getStarsTransactions() failed:", err);
|
|
9023
8697
|
return [];
|
|
9024
8698
|
}
|
|
9025
8699
|
},
|
|
@@ -9072,7 +8746,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9072
8746
|
new Api4.payments.UpdateStarGiftPrice({
|
|
9073
8747
|
stargift: new Api4.InputSavedStarGiftUser({ msgId }),
|
|
9074
8748
|
resellAmount: new Api4.StarsAmount({
|
|
9075
|
-
amount:
|
|
8749
|
+
amount: toLong(price),
|
|
9076
8750
|
nanos: 0
|
|
9077
8751
|
})
|
|
9078
8752
|
})
|
|
@@ -9124,7 +8798,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9124
8798
|
};
|
|
9125
8799
|
} catch (err) {
|
|
9126
8800
|
if (err instanceof PluginSDKError) throw err;
|
|
9127
|
-
|
|
8801
|
+
log13.error("telegram.getCollectibleInfo() failed:", err);
|
|
9128
8802
|
return null;
|
|
9129
8803
|
}
|
|
9130
8804
|
},
|
|
@@ -9162,7 +8836,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9162
8836
|
} catch (err) {
|
|
9163
8837
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
9164
8838
|
if (err instanceof PluginSDKError) throw err;
|
|
9165
|
-
|
|
8839
|
+
log13.error("telegram.getUniqueGift() failed:", err);
|
|
9166
8840
|
return null;
|
|
9167
8841
|
}
|
|
9168
8842
|
},
|
|
@@ -9188,7 +8862,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9188
8862
|
} catch (err) {
|
|
9189
8863
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
9190
8864
|
if (err instanceof PluginSDKError) throw err;
|
|
9191
|
-
|
|
8865
|
+
log13.error("telegram.getUniqueGiftValue() failed:", err);
|
|
9192
8866
|
return null;
|
|
9193
8867
|
}
|
|
9194
8868
|
},
|
|
@@ -9203,7 +8877,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9203
8877
|
new Api4.payments.SendStarGiftOffer({
|
|
9204
8878
|
peer,
|
|
9205
8879
|
slug: giftSlug,
|
|
9206
|
-
price: new Api4.StarsAmount({ amount:
|
|
8880
|
+
price: new Api4.StarsAmount({ amount: toLong(price), nanos: 0 }),
|
|
9207
8881
|
duration,
|
|
9208
8882
|
randomId: randomLong()
|
|
9209
8883
|
})
|
|
@@ -9299,7 +8973,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9299
8973
|
}
|
|
9300
8974
|
|
|
9301
8975
|
// src/sdk/telegram.ts
|
|
9302
|
-
function createTelegramSDK(bridge,
|
|
8976
|
+
function createTelegramSDK(bridge, log13) {
|
|
9303
8977
|
function requireBridge2() {
|
|
9304
8978
|
requireBridge(bridge);
|
|
9305
8979
|
}
|
|
@@ -9403,7 +9077,7 @@ function createTelegramSDK(bridge, log15) {
|
|
|
9403
9077
|
timestamp: m.timestamp
|
|
9404
9078
|
}));
|
|
9405
9079
|
} catch (err) {
|
|
9406
|
-
|
|
9080
|
+
log13.error("telegram.getMessages() failed:", err);
|
|
9407
9081
|
return [];
|
|
9408
9082
|
}
|
|
9409
9083
|
},
|
|
@@ -9425,7 +9099,7 @@ function createTelegramSDK(bridge, log15) {
|
|
|
9425
9099
|
return bridge.isAvailable();
|
|
9426
9100
|
},
|
|
9427
9101
|
getRawClient() {
|
|
9428
|
-
|
|
9102
|
+
log13.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
|
|
9429
9103
|
if (!bridge.isAvailable()) return null;
|
|
9430
9104
|
try {
|
|
9431
9105
|
return bridge.getClient().getClient();
|
|
@@ -9434,17 +9108,17 @@ function createTelegramSDK(bridge, log15) {
|
|
|
9434
9108
|
}
|
|
9435
9109
|
},
|
|
9436
9110
|
// Spread extended methods from sub-modules
|
|
9437
|
-
...createTelegramMessagesSDK(bridge,
|
|
9438
|
-
...createTelegramSocialSDK(bridge,
|
|
9111
|
+
...createTelegramMessagesSDK(bridge, log13),
|
|
9112
|
+
...createTelegramSocialSDK(bridge, log13)
|
|
9439
9113
|
};
|
|
9440
9114
|
}
|
|
9441
9115
|
|
|
9442
9116
|
// src/sdk/secrets.ts
|
|
9443
9117
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync5 } from "fs";
|
|
9444
|
-
import { join as
|
|
9445
|
-
var SECRETS_DIR =
|
|
9118
|
+
import { join as join3 } from "path";
|
|
9119
|
+
var SECRETS_DIR = join3(TELETON_ROOT, "plugins", "data");
|
|
9446
9120
|
function getSecretsPath(pluginName) {
|
|
9447
|
-
return
|
|
9121
|
+
return join3(SECRETS_DIR, `${pluginName}.secrets.json`);
|
|
9448
9122
|
}
|
|
9449
9123
|
function readSecretsFile(pluginName) {
|
|
9450
9124
|
const filePath = getSecretsPath(pluginName);
|
|
@@ -9476,23 +9150,23 @@ function deletePluginSecret(pluginName, key) {
|
|
|
9476
9150
|
function listPluginSecretKeys(pluginName) {
|
|
9477
9151
|
return Object.keys(readSecretsFile(pluginName));
|
|
9478
9152
|
}
|
|
9479
|
-
function createSecretsSDK(pluginName, pluginConfig,
|
|
9153
|
+
function createSecretsSDK(pluginName, pluginConfig, log13) {
|
|
9480
9154
|
const envPrefix = pluginName.replace(/-/g, "_").toUpperCase();
|
|
9481
9155
|
function get(key) {
|
|
9482
9156
|
const envKey = `${envPrefix}_${key.toUpperCase()}`;
|
|
9483
9157
|
const envValue = process.env[envKey];
|
|
9484
9158
|
if (envValue) {
|
|
9485
|
-
|
|
9159
|
+
log13.debug(`Secret "${key}" resolved from env var ${envKey}`);
|
|
9486
9160
|
return envValue;
|
|
9487
9161
|
}
|
|
9488
9162
|
const stored = readSecretsFile(pluginName);
|
|
9489
9163
|
if (key in stored && stored[key]) {
|
|
9490
|
-
|
|
9164
|
+
log13.debug(`Secret "${key}" resolved from secrets store`);
|
|
9491
9165
|
return stored[key];
|
|
9492
9166
|
}
|
|
9493
9167
|
const configValue = pluginConfig[key];
|
|
9494
9168
|
if (configValue !== void 0 && configValue !== null) {
|
|
9495
|
-
|
|
9169
|
+
log13.debug(`Secret "${key}" resolved from pluginConfig`);
|
|
9496
9170
|
return String(configValue);
|
|
9497
9171
|
}
|
|
9498
9172
|
return void 0;
|
|
@@ -9594,7 +9268,7 @@ function createStorageSDK(db) {
|
|
|
9594
9268
|
}
|
|
9595
9269
|
|
|
9596
9270
|
// src/sdk/bot.ts
|
|
9597
|
-
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter,
|
|
9271
|
+
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter, log13) {
|
|
9598
9272
|
if (!router || !manifest || !manifest.inline && !manifest.callbacks) {
|
|
9599
9273
|
return null;
|
|
9600
9274
|
}
|
|
@@ -9617,7 +9291,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9617
9291
|
},
|
|
9618
9292
|
onInlineQuery(handler) {
|
|
9619
9293
|
if (handlers.onInlineQuery) {
|
|
9620
|
-
|
|
9294
|
+
log13.warn("onInlineQuery called again \u2014 overwriting previous handler");
|
|
9621
9295
|
}
|
|
9622
9296
|
handlers.onInlineQuery = async (ctx) => {
|
|
9623
9297
|
if (rateLimiter) {
|
|
@@ -9663,7 +9337,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9663
9337
|
return;
|
|
9664
9338
|
} catch (error) {
|
|
9665
9339
|
if (error?.errorMessage === "MESSAGE_NOT_MODIFIED") return;
|
|
9666
|
-
|
|
9340
|
+
log13.warn(`GramJS edit failed, falling back to Grammy: ${error?.errorMessage || error}`);
|
|
9667
9341
|
}
|
|
9668
9342
|
}
|
|
9669
9343
|
if (grammyBot) {
|
|
@@ -9676,7 +9350,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9676
9350
|
});
|
|
9677
9351
|
} catch (error) {
|
|
9678
9352
|
if (error?.description?.includes("message is not modified")) return;
|
|
9679
|
-
|
|
9353
|
+
log13.error(`Failed to edit inline message: ${error?.description || error}`);
|
|
9680
9354
|
}
|
|
9681
9355
|
}
|
|
9682
9356
|
},
|
|
@@ -9735,13 +9409,13 @@ function createSafeDb(db) {
|
|
|
9735
9409
|
});
|
|
9736
9410
|
}
|
|
9737
9411
|
function createPluginSDK(deps, opts) {
|
|
9738
|
-
const
|
|
9412
|
+
const log13 = createLogger2(opts.pluginName);
|
|
9739
9413
|
const safeDb = opts.db ? createSafeDb(opts.db) : null;
|
|
9740
|
-
const ton = Object.freeze(createTonSDK(
|
|
9741
|
-
const telegram = Object.freeze(createTelegramSDK(deps.bridge,
|
|
9742
|
-
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig,
|
|
9414
|
+
const ton = Object.freeze(createTonSDK(log13, safeDb));
|
|
9415
|
+
const telegram = Object.freeze(createTelegramSDK(deps.bridge, log13));
|
|
9416
|
+
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log13));
|
|
9743
9417
|
const storage = safeDb ? Object.freeze(createStorageSDK(safeDb)) : null;
|
|
9744
|
-
const frozenLog = Object.freeze(
|
|
9418
|
+
const frozenLog = Object.freeze(log13);
|
|
9745
9419
|
const frozenConfig = Object.freeze(JSON.parse(JSON.stringify(opts.sanitizedConfig ?? {})));
|
|
9746
9420
|
const frozenPluginConfig = Object.freeze(JSON.parse(JSON.stringify(opts.pluginConfig ?? {})));
|
|
9747
9421
|
let cachedBot;
|
|
@@ -9772,20 +9446,20 @@ function createPluginSDK(deps, opts) {
|
|
|
9772
9446
|
},
|
|
9773
9447
|
on(hookName, handler, onOpts) {
|
|
9774
9448
|
if (!opts.hookRegistry) {
|
|
9775
|
-
|
|
9449
|
+
log13.warn(`Hook registration unavailable \u2014 sdk.on() ignored`);
|
|
9776
9450
|
return;
|
|
9777
9451
|
}
|
|
9778
9452
|
if (opts.declaredHooks) {
|
|
9779
9453
|
const declared = opts.declaredHooks.some((h2) => h2.name === hookName);
|
|
9780
9454
|
if (!declared) {
|
|
9781
|
-
|
|
9455
|
+
log13.warn(`Hook "${hookName}" not declared in manifest \u2014 registration rejected`);
|
|
9782
9456
|
return;
|
|
9783
9457
|
}
|
|
9784
9458
|
}
|
|
9785
9459
|
const rawPriority = Number(onOpts?.priority) || 0;
|
|
9786
9460
|
const clampedPriority = Math.max(-1e3, Math.min(1e3, rawPriority));
|
|
9787
9461
|
if (rawPriority !== clampedPriority) {
|
|
9788
|
-
|
|
9462
|
+
log13.debug(`Hook "${hookName}" priority ${rawPriority} clamped to ${clampedPriority}`);
|
|
9789
9463
|
}
|
|
9790
9464
|
const registered = opts.hookRegistry.register({
|
|
9791
9465
|
pluginId: opts.pluginName,
|
|
@@ -9795,7 +9469,7 @@ function createPluginSDK(deps, opts) {
|
|
|
9795
9469
|
globalPriority: opts.globalPriority ?? 0
|
|
9796
9470
|
});
|
|
9797
9471
|
if (!registered) {
|
|
9798
|
-
|
|
9472
|
+
log13.warn(
|
|
9799
9473
|
`Hook registration limit reached for plugin "${opts.pluginName}" \u2014 "${hookName}" rejected`
|
|
9800
9474
|
);
|
|
9801
9475
|
}
|
|
@@ -9914,21 +9588,21 @@ var HookRegistry = class {
|
|
|
9914
9588
|
|
|
9915
9589
|
// src/agent/tools/plugin-loader.ts
|
|
9916
9590
|
var execFileAsync = promisify(execFile);
|
|
9917
|
-
var
|
|
9918
|
-
var PLUGIN_DATA_DIR =
|
|
9591
|
+
var log10 = createLogger("PluginLoader");
|
|
9592
|
+
var PLUGIN_DATA_DIR = join4(TELETON_ROOT, "plugins", "data");
|
|
9919
9593
|
function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookRegistry, pluginPriorities) {
|
|
9920
9594
|
let manifest = null;
|
|
9921
9595
|
if (raw.manifest) {
|
|
9922
9596
|
try {
|
|
9923
9597
|
manifest = validateManifest(raw.manifest);
|
|
9924
9598
|
} catch (err) {
|
|
9925
|
-
|
|
9599
|
+
log10.warn(
|
|
9926
9600
|
`[${entryName}] invalid manifest, ignoring: ${err instanceof Error ? err.message : err}`
|
|
9927
9601
|
);
|
|
9928
9602
|
}
|
|
9929
9603
|
}
|
|
9930
9604
|
if (!manifest) {
|
|
9931
|
-
const manifestPath =
|
|
9605
|
+
const manifestPath = join4(WORKSPACE_PATHS.PLUGINS_DIR, entryName, "manifest.json");
|
|
9932
9606
|
try {
|
|
9933
9607
|
if (existsSync6(manifestPath)) {
|
|
9934
9608
|
const diskManifest = JSON.parse(readFileSync5(manifestPath, "utf-8"));
|
|
@@ -10006,7 +9680,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookReg
|
|
|
10006
9680
|
},
|
|
10007
9681
|
migrate() {
|
|
10008
9682
|
try {
|
|
10009
|
-
const dbPath =
|
|
9683
|
+
const dbPath = join4(PLUGIN_DATA_DIR, `${pluginName}.db`);
|
|
10010
9684
|
pluginDb = openModuleDb(dbPath);
|
|
10011
9685
|
if (hasMigrate) {
|
|
10012
9686
|
raw.migrate?.(pluginDb);
|
|
@@ -10111,30 +9785,30 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookReg
|
|
|
10111
9785
|
return module;
|
|
10112
9786
|
}
|
|
10113
9787
|
async function ensurePluginDeps(pluginDir, pluginEntry) {
|
|
10114
|
-
const pkgJson =
|
|
10115
|
-
const lockfile =
|
|
10116
|
-
const nodeModules =
|
|
9788
|
+
const pkgJson = join4(pluginDir, "package.json");
|
|
9789
|
+
const lockfile = join4(pluginDir, "package-lock.json");
|
|
9790
|
+
const nodeModules = join4(pluginDir, "node_modules");
|
|
10117
9791
|
if (!existsSync6(pkgJson)) return;
|
|
10118
9792
|
if (!existsSync6(lockfile)) {
|
|
10119
|
-
|
|
9793
|
+
log10.warn(
|
|
10120
9794
|
`[${pluginEntry}] package.json without package-lock.json \u2014 skipping (lockfile required)`
|
|
10121
9795
|
);
|
|
10122
9796
|
return;
|
|
10123
9797
|
}
|
|
10124
9798
|
if (existsSync6(nodeModules)) {
|
|
10125
|
-
const marker =
|
|
9799
|
+
const marker = join4(nodeModules, ".package-lock.json");
|
|
10126
9800
|
if (existsSync6(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
|
|
10127
9801
|
}
|
|
10128
|
-
|
|
9802
|
+
log10.info(`[${pluginEntry}] Installing dependencies...`);
|
|
10129
9803
|
try {
|
|
10130
9804
|
await execFileAsync("npm", ["ci", "--ignore-scripts", "--no-audit", "--no-fund"], {
|
|
10131
9805
|
cwd: pluginDir,
|
|
10132
9806
|
timeout: 6e4,
|
|
10133
9807
|
env: { ...process.env, NODE_ENV: "production" }
|
|
10134
9808
|
});
|
|
10135
|
-
|
|
9809
|
+
log10.info(`[${pluginEntry}] Dependencies installed`);
|
|
10136
9810
|
} catch (err) {
|
|
10137
|
-
|
|
9811
|
+
log10.error(`[${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
|
|
10138
9812
|
}
|
|
10139
9813
|
}
|
|
10140
9814
|
async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
@@ -10156,14 +9830,14 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10156
9830
|
const pluginPaths = [];
|
|
10157
9831
|
for (const entry of entries) {
|
|
10158
9832
|
if (entry === "data") continue;
|
|
10159
|
-
const entryPath =
|
|
9833
|
+
const entryPath = join4(pluginsDir, entry);
|
|
10160
9834
|
let modulePath = null;
|
|
10161
9835
|
try {
|
|
10162
9836
|
const stat = statSync(entryPath);
|
|
10163
9837
|
if (stat.isFile() && entry.endsWith(".js")) {
|
|
10164
9838
|
modulePath = entryPath;
|
|
10165
9839
|
} else if (stat.isDirectory()) {
|
|
10166
|
-
const indexPath =
|
|
9840
|
+
const indexPath = join4(entryPath, "index.js");
|
|
10167
9841
|
if (existsSync6(indexPath)) {
|
|
10168
9842
|
modulePath = indexPath;
|
|
10169
9843
|
}
|
|
@@ -10176,7 +9850,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10176
9850
|
}
|
|
10177
9851
|
}
|
|
10178
9852
|
await Promise.allSettled(
|
|
10179
|
-
pluginPaths.filter(({ path }) => path.endsWith("index.js")).map(({ entry }) => ensurePluginDeps(
|
|
9853
|
+
pluginPaths.filter(({ path }) => path.endsWith("index.js")).map(({ entry }) => ensurePluginDeps(join4(pluginsDir, entry), entry))
|
|
10180
9854
|
);
|
|
10181
9855
|
const loadResults = await Promise.allSettled(
|
|
10182
9856
|
pluginPaths.map(async ({ entry, path }) => {
|
|
@@ -10187,7 +9861,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10187
9861
|
);
|
|
10188
9862
|
for (const result of loadResults) {
|
|
10189
9863
|
if (result.status === "rejected") {
|
|
10190
|
-
|
|
9864
|
+
log10.error(
|
|
10191
9865
|
`Plugin failed to load: ${result.reason instanceof Error ? result.reason.message : result.reason}`
|
|
10192
9866
|
);
|
|
10193
9867
|
continue;
|
|
@@ -10195,7 +9869,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10195
9869
|
const { entry, mod } = result.value;
|
|
10196
9870
|
try {
|
|
10197
9871
|
if (!mod.tools || typeof mod.tools !== "function" && !Array.isArray(mod.tools)) {
|
|
10198
|
-
|
|
9872
|
+
log10.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
|
|
10199
9873
|
continue;
|
|
10200
9874
|
}
|
|
10201
9875
|
const adapted = adaptPlugin(
|
|
@@ -10208,13 +9882,13 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10208
9882
|
pluginPriorities
|
|
10209
9883
|
);
|
|
10210
9884
|
if (loadedNames.has(adapted.name)) {
|
|
10211
|
-
|
|
9885
|
+
log10.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
|
|
10212
9886
|
continue;
|
|
10213
9887
|
}
|
|
10214
9888
|
loadedNames.add(adapted.name);
|
|
10215
9889
|
modules.push(adapted);
|
|
10216
9890
|
} catch (err) {
|
|
10217
|
-
|
|
9891
|
+
log10.error(`Plugin "${entry}" failed to adapt: ${err instanceof Error ? err.message : err}`);
|
|
10218
9892
|
}
|
|
10219
9893
|
}
|
|
10220
9894
|
return { modules, hookRegistry };
|
|
@@ -10874,13 +10548,13 @@ import {
|
|
|
10874
10548
|
writeFileSync as writeFileSync4,
|
|
10875
10549
|
unlinkSync
|
|
10876
10550
|
} from "fs";
|
|
10877
|
-
import { mkdir
|
|
10878
|
-
import { join as
|
|
10551
|
+
import { mkdir } from "fs/promises";
|
|
10552
|
+
import { join as join5 } from "path";
|
|
10879
10553
|
import { pipeline } from "stream/promises";
|
|
10880
|
-
var
|
|
10554
|
+
var log11 = createLogger("TonProxy");
|
|
10881
10555
|
var GITHUB_REPO = "xssnick/Tonutils-Proxy";
|
|
10882
|
-
var BINARY_DIR =
|
|
10883
|
-
var PID_FILE =
|
|
10556
|
+
var BINARY_DIR = join5(TELETON_ROOT, "bin");
|
|
10557
|
+
var PID_FILE = join5(TELETON_ROOT, "ton-proxy.pid");
|
|
10884
10558
|
var HEALTH_CHECK_INTERVAL_MS = 3e4;
|
|
10885
10559
|
var HEALTH_CHECK_TIMEOUT_MS = 5e3;
|
|
10886
10560
|
var KILL_GRACE_MS = 5e3;
|
|
@@ -10896,7 +10570,7 @@ var TonProxyManager = class {
|
|
|
10896
10570
|
/** Resolve the binary path — user-specified or auto-detected */
|
|
10897
10571
|
getBinaryPath() {
|
|
10898
10572
|
if (this.config.binary_path) return this.config.binary_path;
|
|
10899
|
-
return
|
|
10573
|
+
return join5(BINARY_DIR, getBinaryName());
|
|
10900
10574
|
}
|
|
10901
10575
|
/** Check if the binary exists on disk */
|
|
10902
10576
|
isInstalled() {
|
|
@@ -10912,8 +10586,8 @@ var TonProxyManager = class {
|
|
|
10912
10586
|
*/
|
|
10913
10587
|
async install() {
|
|
10914
10588
|
const binaryName = getBinaryName();
|
|
10915
|
-
|
|
10916
|
-
await
|
|
10589
|
+
log11.info(`Downloading TON Proxy binary (${binaryName})...`);
|
|
10590
|
+
await mkdir(BINARY_DIR, { recursive: true });
|
|
10917
10591
|
const releaseUrl = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
10918
10592
|
const releaseRes = await fetch(releaseUrl, {
|
|
10919
10593
|
headers: { Accept: "application/vnd.github.v3+json" }
|
|
@@ -10924,7 +10598,7 @@ var TonProxyManager = class {
|
|
|
10924
10598
|
const release = await releaseRes.json();
|
|
10925
10599
|
const tag = release.tag_name;
|
|
10926
10600
|
const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${binaryName}`;
|
|
10927
|
-
|
|
10601
|
+
log11.info(`Downloading ${downloadUrl}`);
|
|
10928
10602
|
const res = await fetch(downloadUrl);
|
|
10929
10603
|
if (!res.ok || !res.body) {
|
|
10930
10604
|
throw new Error(`Download failed: ${res.status} ${res.statusText}`);
|
|
@@ -10933,7 +10607,7 @@ var TonProxyManager = class {
|
|
|
10933
10607
|
const fileStream = createWriteStream(dest);
|
|
10934
10608
|
await pipeline(res.body, fileStream);
|
|
10935
10609
|
chmodSync(dest, 493);
|
|
10936
|
-
|
|
10610
|
+
log11.info(`TON Proxy installed: ${dest} (${tag})`);
|
|
10937
10611
|
}
|
|
10938
10612
|
/** Kill any orphan proxy process from a previous session */
|
|
10939
10613
|
killOrphan() {
|
|
@@ -10943,7 +10617,7 @@ var TonProxyManager = class {
|
|
|
10943
10617
|
if (pid && !isNaN(pid)) {
|
|
10944
10618
|
try {
|
|
10945
10619
|
process.kill(pid, 0);
|
|
10946
|
-
|
|
10620
|
+
log11.warn(`Killing orphan TON Proxy (PID ${pid}) from previous session`);
|
|
10947
10621
|
process.kill(pid, "SIGTERM");
|
|
10948
10622
|
} catch {
|
|
10949
10623
|
}
|
|
@@ -10960,7 +10634,7 @@ var TonProxyManager = class {
|
|
|
10960
10634
|
const pidMatch = out.match(/pid=(\d+)/);
|
|
10961
10635
|
if (pidMatch) {
|
|
10962
10636
|
const pid = parseInt(pidMatch[1], 10);
|
|
10963
|
-
|
|
10637
|
+
log11.warn(`Port ${this.config.port} occupied by PID ${pid}, killing it`);
|
|
10964
10638
|
try {
|
|
10965
10639
|
process.kill(pid, "SIGTERM");
|
|
10966
10640
|
} catch {
|
|
@@ -10975,7 +10649,7 @@ var TonProxyManager = class {
|
|
|
10975
10649
|
try {
|
|
10976
10650
|
writeFileSync4(PID_FILE, String(pid), { mode: 384 });
|
|
10977
10651
|
} catch {
|
|
10978
|
-
|
|
10652
|
+
log11.warn("Failed to write TON Proxy PID file");
|
|
10979
10653
|
}
|
|
10980
10654
|
}
|
|
10981
10655
|
/** Remove PID file */
|
|
@@ -10988,7 +10662,7 @@ var TonProxyManager = class {
|
|
|
10988
10662
|
/** Start the proxy process */
|
|
10989
10663
|
async start() {
|
|
10990
10664
|
if (this.isRunning()) {
|
|
10991
|
-
|
|
10665
|
+
log11.warn("TON Proxy is already running");
|
|
10992
10666
|
return;
|
|
10993
10667
|
}
|
|
10994
10668
|
this.restartCount = 0;
|
|
@@ -10999,7 +10673,7 @@ var TonProxyManager = class {
|
|
|
10999
10673
|
}
|
|
11000
10674
|
const binaryPath = this.getBinaryPath();
|
|
11001
10675
|
const port = String(this.config.port);
|
|
11002
|
-
|
|
10676
|
+
log11.info(`Starting TON Proxy on 127.0.0.1:${port}`);
|
|
11003
10677
|
this.process = spawn(binaryPath, ["-addr", `127.0.0.1:${port}`], {
|
|
11004
10678
|
cwd: BINARY_DIR,
|
|
11005
10679
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -11007,24 +10681,24 @@ var TonProxyManager = class {
|
|
|
11007
10681
|
});
|
|
11008
10682
|
this.process.stdout?.on("data", (chunk) => {
|
|
11009
10683
|
const line = chunk.toString().trim();
|
|
11010
|
-
if (line)
|
|
10684
|
+
if (line) log11.debug(`[proxy] ${line}`);
|
|
11011
10685
|
});
|
|
11012
10686
|
this.process.stderr?.on("data", (chunk) => {
|
|
11013
10687
|
const line = chunk.toString().trim();
|
|
11014
|
-
if (line)
|
|
10688
|
+
if (line) log11.warn(`[proxy:err] ${line}`);
|
|
11015
10689
|
});
|
|
11016
10690
|
this.process.on("exit", (code, signal) => {
|
|
11017
|
-
|
|
10691
|
+
log11.info(`TON Proxy exited (code=${code}, signal=${signal})`);
|
|
11018
10692
|
this.process = null;
|
|
11019
10693
|
this.removePidFile();
|
|
11020
10694
|
if (code !== 0 && code !== null && this.restartCount < this.maxRestarts) {
|
|
11021
10695
|
this.restartCount++;
|
|
11022
|
-
|
|
11023
|
-
this.start().catch((err) =>
|
|
10696
|
+
log11.warn(`Auto-restarting TON Proxy (attempt ${this.restartCount}/${this.maxRestarts})`);
|
|
10697
|
+
this.start().catch((err) => log11.error({ err }, "Failed to auto-restart TON Proxy"));
|
|
11024
10698
|
}
|
|
11025
10699
|
});
|
|
11026
10700
|
this.process.on("error", (err) => {
|
|
11027
|
-
|
|
10701
|
+
log11.error({ err }, "TON Proxy process error");
|
|
11028
10702
|
this.process = null;
|
|
11029
10703
|
});
|
|
11030
10704
|
this.startHealthCheck();
|
|
@@ -11042,14 +10716,14 @@ var TonProxyManager = class {
|
|
|
11042
10716
|
});
|
|
11043
10717
|
});
|
|
11044
10718
|
if (this.process?.pid) this.writePidFile(this.process.pid);
|
|
11045
|
-
|
|
10719
|
+
log11.info(`TON Proxy running on 127.0.0.1:${port} (PID ${this.process?.pid})`);
|
|
11046
10720
|
}
|
|
11047
10721
|
/** Stop the proxy process gracefully */
|
|
11048
10722
|
async stop() {
|
|
11049
10723
|
this.stopHealthCheck();
|
|
11050
10724
|
if (!this.process) return;
|
|
11051
10725
|
this.maxRestarts = 0;
|
|
11052
|
-
|
|
10726
|
+
log11.info("Stopping TON Proxy...");
|
|
11053
10727
|
return new Promise((resolve2) => {
|
|
11054
10728
|
if (!this.process) {
|
|
11055
10729
|
resolve2();
|
|
@@ -11057,7 +10731,7 @@ var TonProxyManager = class {
|
|
|
11057
10731
|
}
|
|
11058
10732
|
const forceKill = setTimeout(() => {
|
|
11059
10733
|
if (this.process) {
|
|
11060
|
-
|
|
10734
|
+
log11.warn("TON Proxy did not exit gracefully, sending SIGKILL");
|
|
11061
10735
|
this.process.kill("SIGKILL");
|
|
11062
10736
|
}
|
|
11063
10737
|
}, KILL_GRACE_MS);
|
|
@@ -11079,7 +10753,7 @@ var TonProxyManager = class {
|
|
|
11079
10753
|
if (existsSync8(binaryPath)) {
|
|
11080
10754
|
const { unlink } = await import("fs/promises");
|
|
11081
10755
|
await unlink(binaryPath);
|
|
11082
|
-
|
|
10756
|
+
log11.info(`TON Proxy binary removed: ${binaryPath}`);
|
|
11083
10757
|
}
|
|
11084
10758
|
}
|
|
11085
10759
|
/** Get proxy status for WebUI / tools */
|
|
@@ -11113,7 +10787,7 @@ var TonProxyManager = class {
|
|
|
11113
10787
|
}).catch(() => null);
|
|
11114
10788
|
clearTimeout(timeout);
|
|
11115
10789
|
if (!res) {
|
|
11116
|
-
|
|
10790
|
+
log11.warn("TON Proxy health check failed (no response)");
|
|
11117
10791
|
}
|
|
11118
10792
|
} catch {
|
|
11119
10793
|
}
|
|
@@ -11170,7 +10844,7 @@ var tonProxyStatusExecutor = async () => {
|
|
|
11170
10844
|
};
|
|
11171
10845
|
|
|
11172
10846
|
// src/ton-proxy/module.ts
|
|
11173
|
-
var
|
|
10847
|
+
var log12 = createLogger("TonProxyModule");
|
|
11174
10848
|
var manager = null;
|
|
11175
10849
|
function getTonProxyManager() {
|
|
11176
10850
|
return manager;
|
|
@@ -11197,9 +10871,9 @@ var tonProxyModule = {
|
|
|
11197
10871
|
setProxyManager(manager);
|
|
11198
10872
|
try {
|
|
11199
10873
|
await manager.start();
|
|
11200
|
-
|
|
10874
|
+
log12.info(`TON Proxy started on port ${proxyConfig.port}`);
|
|
11201
10875
|
} catch (err) {
|
|
11202
|
-
|
|
10876
|
+
log12.error({ err }, "Failed to start TON Proxy");
|
|
11203
10877
|
manager = null;
|
|
11204
10878
|
}
|
|
11205
10879
|
},
|