teleton 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +26 -26
  2. package/dist/{chunk-D5I7GBV7.js → chunk-FNV5FF35.js} +22 -13
  3. package/dist/chunk-LRCPA7SC.js +149 -0
  4. package/dist/{chunk-ADCMUNYU.js → chunk-N3F7E7DR.js} +58 -53
  5. package/dist/chunk-ND2X5FWB.js +368 -0
  6. package/dist/chunk-NERLQY2H.js +421 -0
  7. package/dist/{chunk-YBA6IBGT.js → chunk-OCLG5GKI.js} +24 -24
  8. package/dist/{chunk-6L6KGATM.js → chunk-OGIG552S.js} +1397 -1688
  9. package/dist/chunk-RCMD3U65.js +141 -0
  10. package/dist/{chunk-4IPJ25HE.js → chunk-TCD4NZDA.js} +1045 -658
  11. package/dist/{chunk-ECSCVEQQ.js → chunk-UCN6TI25.js} +7 -3
  12. package/dist/{chunk-WL2Q3VRD.js → chunk-UDD7FYOU.js} +12 -4
  13. package/dist/chunk-VAUJSSD3.js +20 -0
  14. package/dist/chunk-XBE4JB7C.js +8 -0
  15. package/dist/{chunk-GDCODBNO.js → chunk-XBKSS6DM.js} +2 -16
  16. package/dist/cli/index.js +878 -433
  17. package/dist/client-3VWE7NC4.js +29 -0
  18. package/dist/{get-my-gifts-KVULMBJ3.js → get-my-gifts-RI7FAXAL.js} +3 -1
  19. package/dist/index.js +17 -11
  20. package/dist/{memory-TVDOGQXS.js → memory-RD7ZSTRV.js} +7 -5
  21. package/dist/{migrate-QIEMPOMT.js → migrate-GO4NOBT7.js} +14 -9
  22. package/dist/{server-RSWVCVY3.js → server-OWVEZTR3.js} +81 -84
  23. package/dist/setup-server-C7ZTPHD5.js +934 -0
  24. package/dist/{task-dependency-resolver-72DLY2HV.js → task-dependency-resolver-WKZWJLLM.js} +19 -15
  25. package/dist/{task-executor-VXB6DAV2.js → task-executor-PD3H4MLO.js} +4 -1
  26. package/dist/tool-adapter-Y3TCEQOC.js +145 -0
  27. package/dist/{tool-index-DKI2ZNOU.js → tool-index-MIVK3D7H.js} +14 -9
  28. package/dist/{transcript-7V4UNID4.js → transcript-UDJZP6NK.js} +2 -1
  29. package/dist/web/assets/complete-fZLnb5Ot.js +1 -0
  30. package/dist/web/assets/index-B_FcaX5D.css +1 -0
  31. package/dist/web/assets/index-CbeAP4_n.js +67 -0
  32. package/dist/web/assets/index.es-oXiZF7Hc.js +11 -0
  33. package/dist/web/assets/login-telegram-BP7CJDmx.js +1 -0
  34. package/dist/web/assets/run-DOrDowjK.js +1 -0
  35. package/dist/web/index.html +2 -2
  36. package/package.json +7 -3
  37. package/dist/chunk-2QUJLHCZ.js +0 -362
  38. package/dist/web/assets/index-BNhrx9S1.js +0 -67
  39. package/dist/web/assets/index-CqrrRLOh.css +0 -1
@@ -1,13 +1,34 @@
1
+ import {
2
+ ConfigSchema,
3
+ getKeyPair,
4
+ getTonPrice,
5
+ getWalletAddress,
6
+ getWalletBalance,
7
+ loadWallet
8
+ } from "./chunk-NERLQY2H.js";
9
+ import {
10
+ getCachedHttpEndpoint
11
+ } from "./chunk-QUAPFI2N.js";
12
+ import {
13
+ require_BigInteger
14
+ } from "./chunk-TSKJCWQQ.js";
15
+ import {
16
+ getErrorMessage
17
+ } from "./chunk-XBE4JB7C.js";
18
+ import {
19
+ getProviderMetadata
20
+ } from "./chunk-LRCPA7SC.js";
1
21
  import {
2
22
  createDbWrapper,
3
23
  migrateFromMainDb,
4
24
  openModuleDb
5
- } from "./chunk-ECSCVEQQ.js";
25
+ } from "./chunk-UCN6TI25.js";
6
26
  import {
7
- COINGECKO_API_URL,
8
- fetchWithTimeout,
9
27
  tonapiFetch
10
- } from "./chunk-GDCODBNO.js";
28
+ } from "./chunk-XBKSS6DM.js";
29
+ import {
30
+ PAYMENT_TOLERANCE_RATIO
31
+ } from "./chunk-RO62LO6Z.js";
11
32
  import {
12
33
  RETRY_BLOCKCHAIN_BASE_DELAY_MS,
13
34
  RETRY_BLOCKCHAIN_MAX_DELAY_MS,
@@ -17,10 +38,6 @@ import {
17
38
  RETRY_DEFAULT_MAX_DELAY_MS,
18
39
  RETRY_DEFAULT_TIMEOUT_MS
19
40
  } from "./chunk-4DU3C27M.js";
20
- import {
21
- PAYMENT_TOLERANCE_RATIO,
22
- TELEGRAM_MAX_MESSAGE_LENGTH
23
- } from "./chunk-RO62LO6Z.js";
24
41
  import {
25
42
  ALLOWED_EXTENSIONS,
26
43
  TELETON_ROOT,
@@ -28,244 +45,19 @@ import {
28
45
  WORKSPACE_ROOT
29
46
  } from "./chunk-EYWNOHMJ.js";
30
47
  import {
31
- getCachedHttpEndpoint
32
- } from "./chunk-QUAPFI2N.js";
48
+ createLogger
49
+ } from "./chunk-RCMD3U65.js";
33
50
  import {
34
- __require
51
+ __require,
52
+ __toESM
35
53
  } from "./chunk-QGM4M3NI.js";
36
54
 
37
- // src/config/providers.ts
38
- var PROVIDER_REGISTRY = {
39
- anthropic: {
40
- id: "anthropic",
41
- displayName: "Anthropic (Claude)",
42
- envVar: "ANTHROPIC_API_KEY",
43
- keyPrefix: "sk-ant-",
44
- keyHint: "sk-ant-api03-...",
45
- consoleUrl: "https://console.anthropic.com/",
46
- defaultModel: "claude-opus-4-5-20251101",
47
- utilityModel: "claude-3-5-haiku-20241022",
48
- toolLimit: null,
49
- piAiProvider: "anthropic"
50
- },
51
- openai: {
52
- id: "openai",
53
- displayName: "OpenAI (GPT-4o)",
54
- envVar: "OPENAI_API_KEY",
55
- keyPrefix: "sk-",
56
- keyHint: "sk-proj-...",
57
- consoleUrl: "https://platform.openai.com/api-keys",
58
- defaultModel: "gpt-4o",
59
- utilityModel: "gpt-4o-mini",
60
- toolLimit: 128,
61
- piAiProvider: "openai"
62
- },
63
- google: {
64
- id: "google",
65
- displayName: "Google (Gemini)",
66
- envVar: "GOOGLE_API_KEY",
67
- keyPrefix: null,
68
- keyHint: "AIza...",
69
- consoleUrl: "https://aistudio.google.com/apikey",
70
- defaultModel: "gemini-2.5-flash",
71
- utilityModel: "gemini-2.0-flash-lite",
72
- toolLimit: 128,
73
- piAiProvider: "google"
74
- },
75
- xai: {
76
- id: "xai",
77
- displayName: "xAI (Grok)",
78
- envVar: "XAI_API_KEY",
79
- keyPrefix: "xai-",
80
- keyHint: "xai-...",
81
- consoleUrl: "https://console.x.ai/",
82
- defaultModel: "grok-3",
83
- utilityModel: "grok-3-mini-fast",
84
- toolLimit: 128,
85
- piAiProvider: "xai"
86
- },
87
- groq: {
88
- id: "groq",
89
- displayName: "Groq",
90
- envVar: "GROQ_API_KEY",
91
- keyPrefix: "gsk_",
92
- keyHint: "gsk_...",
93
- consoleUrl: "https://console.groq.com/keys",
94
- defaultModel: "llama-3.3-70b-versatile",
95
- utilityModel: "llama-3.1-8b-instant",
96
- toolLimit: 128,
97
- piAiProvider: "groq"
98
- },
99
- openrouter: {
100
- id: "openrouter",
101
- displayName: "OpenRouter",
102
- envVar: "OPENROUTER_API_KEY",
103
- keyPrefix: "sk-or-",
104
- keyHint: "sk-or-v1-...",
105
- consoleUrl: "https://openrouter.ai/keys",
106
- defaultModel: "anthropic/claude-opus-4.5",
107
- utilityModel: "google/gemini-2.5-flash-lite",
108
- toolLimit: 128,
109
- piAiProvider: "openrouter"
110
- }
111
- };
112
- function getProviderMetadata(provider) {
113
- const meta = PROVIDER_REGISTRY[provider];
114
- if (!meta) {
115
- throw new Error(`Unknown provider: ${provider}`);
116
- }
117
- return meta;
118
- }
119
- function getSupportedProviders() {
120
- return Object.values(PROVIDER_REGISTRY);
121
- }
122
- function validateApiKeyFormat(provider, key) {
123
- const meta = PROVIDER_REGISTRY[provider];
124
- if (!meta) return `Unknown provider: ${provider}`;
125
- if (!key || key.trim().length === 0) return "API key is required";
126
- if (meta.keyPrefix && !key.startsWith(meta.keyPrefix)) {
127
- return `Invalid format (should start with ${meta.keyPrefix})`;
128
- }
129
- return void 0;
130
- }
131
-
132
55
  // src/config/loader.ts
133
- import { readFileSync, existsSync, writeFileSync, mkdirSync, chmodSync } from "fs";
56
+ import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
134
57
  import { parse, stringify } from "yaml";
135
58
  import { homedir } from "os";
136
59
  import { dirname, join } from "path";
137
-
138
- // src/config/schema.ts
139
- import { z } from "zod";
140
- var DMPolicy = z.enum(["pairing", "allowlist", "open", "disabled"]);
141
- var GroupPolicy = z.enum(["open", "allowlist", "disabled"]);
142
- var SessionResetPolicySchema = z.object({
143
- daily_reset_enabled: z.boolean().default(true).describe("Enable daily session reset"),
144
- daily_reset_hour: z.number().min(0).max(23).default(4).describe("Hour of day (0-23) to reset sessions"),
145
- idle_expiry_enabled: z.boolean().default(true).describe("Enable session reset after idle period"),
146
- idle_expiry_minutes: z.number().default(1440).describe("Minutes of inactivity before session reset (default: 24h)")
147
- });
148
- var AgentConfigSchema = z.object({
149
- provider: z.enum(["anthropic", "openai", "google", "xai", "groq", "openrouter"]).default("anthropic"),
150
- api_key: z.string(),
151
- model: z.string().default("claude-opus-4-5-20251101"),
152
- utility_model: z.string().optional().describe("Cheap model for summarization (auto-detected if omitted)"),
153
- max_tokens: z.number().default(4096),
154
- temperature: z.number().default(0.7),
155
- system_prompt: z.string().nullable().default(null),
156
- max_agentic_iterations: z.number().default(5).describe("Maximum number of agentic loop iterations (tool call \u2192 result \u2192 tool call cycles)"),
157
- session_reset_policy: SessionResetPolicySchema.default(SessionResetPolicySchema.parse({}))
158
- });
159
- var TelegramConfigSchema = z.object({
160
- api_id: z.number(),
161
- api_hash: z.string(),
162
- phone: z.string(),
163
- session_name: z.string().default("teleton_session"),
164
- session_path: z.string().default("~/.teleton"),
165
- dm_policy: DMPolicy.default("pairing"),
166
- allow_from: z.array(z.number()).default([]),
167
- group_policy: GroupPolicy.default("open"),
168
- group_allow_from: z.array(z.number()).default([]),
169
- require_mention: z.boolean().default(true),
170
- max_message_length: z.number().default(TELEGRAM_MAX_MESSAGE_LENGTH),
171
- typing_simulation: z.boolean().default(true),
172
- rate_limit_messages_per_second: z.number().default(1),
173
- rate_limit_groups_per_minute: z.number().default(20),
174
- admin_ids: z.array(z.number()).default([]),
175
- agent_channel: z.string().nullable().default(null),
176
- owner_name: z.string().optional().describe("Owner's first name (e.g., 'Alex')"),
177
- owner_username: z.string().optional().describe("Owner's Telegram username (without @)"),
178
- owner_id: z.number().optional().describe("Owner's Telegram user ID"),
179
- debounce_ms: z.number().default(1500).describe("Debounce delay in milliseconds for group messages (0 = disabled)"),
180
- bot_token: z.string().optional().describe("Telegram Bot token from @BotFather for inline deal buttons"),
181
- bot_username: z.string().optional().describe("Bot username without @ (e.g., 'teleton_deals_bot')")
182
- });
183
- var StorageConfigSchema = z.object({
184
- sessions_file: z.string().default("~/.teleton/sessions.json"),
185
- pairing_file: z.string().default("~/.teleton/pairing.json"),
186
- memory_file: z.string().default("~/.teleton/memory.json"),
187
- history_limit: z.number().default(100)
188
- });
189
- var MetaConfigSchema = z.object({
190
- version: z.string().default("1.0.0"),
191
- created_at: z.string().optional(),
192
- last_modified_at: z.string().optional(),
193
- onboard_command: z.string().default("teleton setup")
194
- });
195
- var _DealsObject = z.object({
196
- enabled: z.boolean().default(true),
197
- expiry_seconds: z.number().default(120),
198
- buy_max_floor_percent: z.number().default(100),
199
- sell_min_floor_percent: z.number().default(105),
200
- poll_interval_ms: z.number().default(5e3),
201
- max_verification_retries: z.number().default(12),
202
- expiry_check_interval_ms: z.number().default(6e4)
203
- });
204
- var DealsConfigSchema = _DealsObject.default(_DealsObject.parse({}));
205
- var _WebUIObject = z.object({
206
- enabled: z.boolean().default(false).describe("Enable WebUI server"),
207
- port: z.number().default(7777).describe("HTTP server port"),
208
- host: z.string().default("127.0.0.1").describe("Bind address (localhost only for security)"),
209
- auth_token: z.string().optional().describe("Bearer token for API auth (auto-generated if omitted)"),
210
- cors_origins: z.array(z.string()).default(["http://localhost:5173", "http://localhost:7777"]).describe("Allowed CORS origins for development"),
211
- log_requests: z.boolean().default(false).describe("Log all HTTP requests")
212
- });
213
- var WebUIConfigSchema = _WebUIObject.default(_WebUIObject.parse({}));
214
- var _EmbeddingObject = z.object({
215
- provider: z.enum(["local", "anthropic", "none"]).default("local").describe("Embedding provider: local (ONNX), anthropic (API), or none (FTS5-only)"),
216
- model: z.string().optional().describe("Model override (default: Xenova/all-MiniLM-L6-v2 for local)")
217
- });
218
- var EmbeddingConfigSchema = _EmbeddingObject.default(_EmbeddingObject.parse({}));
219
- var _DevObject = z.object({
220
- hot_reload: z.boolean().default(false).describe("Enable plugin hot-reload (watches ~/.teleton/plugins/ for changes)")
221
- });
222
- var DevConfigSchema = _DevObject.default(_DevObject.parse({}));
223
- var McpServerSchema = z.object({
224
- command: z.string().optional().describe("Stdio command (e.g. 'npx @modelcontextprotocol/server-filesystem /tmp')"),
225
- args: z.array(z.string()).optional().describe("Explicit args array (overrides command splitting)"),
226
- env: z.record(z.string(), z.string()).optional().describe("Environment variables for stdio server"),
227
- url: z.string().url().optional().describe("SSE/HTTP endpoint URL (alternative to command)"),
228
- scope: z.enum(["always", "dm-only", "group-only", "admin-only"]).default("always").describe("Tool scope"),
229
- enabled: z.boolean().default(true).describe("Enable/disable this server")
230
- }).refine((s) => s.command || s.url, {
231
- message: "Each MCP server needs either 'command' (stdio) or 'url' (SSE/HTTP)"
232
- });
233
- var _McpObject = z.object({
234
- servers: z.record(z.string(), McpServerSchema).default({})
235
- });
236
- var McpConfigSchema = _McpObject.default(_McpObject.parse({}));
237
- var _ToolRagObject = z.object({
238
- enabled: z.boolean().default(false).describe("Enable semantic tool retrieval (Tool RAG)"),
239
- top_k: z.number().default(25).describe("Max tools to retrieve per LLM call"),
240
- always_include: z.array(z.string()).default([
241
- "telegram_send_message",
242
- "telegram_reply_message",
243
- "telegram_send_photo",
244
- "telegram_send_document",
245
- "journal_*",
246
- "workspace_*",
247
- "web_*"
248
- ]).describe("Tool name patterns always included (prefix glob with *)"),
249
- skip_unlimited_providers: z.boolean().default(false).describe("Skip Tool RAG for providers with no tool limit (e.g. Anthropic)")
250
- });
251
- var ToolRagConfigSchema = _ToolRagObject.default(_ToolRagObject.parse({}));
252
- var ConfigSchema = z.object({
253
- meta: MetaConfigSchema.default(MetaConfigSchema.parse({})),
254
- agent: AgentConfigSchema,
255
- telegram: TelegramConfigSchema,
256
- storage: StorageConfigSchema.default(StorageConfigSchema.parse({})),
257
- embedding: EmbeddingConfigSchema,
258
- deals: DealsConfigSchema,
259
- webui: WebUIConfigSchema,
260
- dev: DevConfigSchema,
261
- tool_rag: ToolRagConfigSchema,
262
- mcp: McpConfigSchema,
263
- plugins: z.record(z.string(), z.unknown()).default({}).describe("Per-plugin config (key = plugin name with underscores)"),
264
- tonapi_key: z.string().optional().describe("TonAPI key for higher rate limits (from @tonapi_bot)"),
265
- tavily_api_key: z.string().optional().describe("Tavily API key for web search & extract (free at https://tavily.com)")
266
- });
267
-
268
- // src/config/loader.ts
60
+ var log = createLogger("Config");
269
61
  var DEFAULT_CONFIG_PATH = join(TELETON_ROOT, "config.yaml");
270
62
  function expandPath(path) {
271
63
  if (path.startsWith("~")) {
@@ -292,7 +84,7 @@ Run 'teleton setup' to create one.`);
292
84
  throw new Error(`Invalid YAML in ${fullPath}: ${error.message}`);
293
85
  }
294
86
  if (raw && typeof raw === "object" && "market" in raw) {
295
- console.warn("\u26A0\uFE0F config.market is deprecated and ignored. Use market-api plugin instead.");
87
+ log.warn("config.market is deprecated and ignored. Use market-api plugin instead.");
296
88
  delete raw.market;
297
89
  }
298
90
  const result = ConfigSchema.safeParse(raw);
@@ -332,12 +124,28 @@ Run 'teleton setup' to create one.`);
332
124
  }
333
125
  if (process.env.TELETON_WEBUI_PORT) {
334
126
  const port = parseInt(process.env.TELETON_WEBUI_PORT, 10);
335
- if (!isNaN(port)) {
127
+ if (!isNaN(port) && port >= 1024 && port <= 65535) {
336
128
  config.webui.port = port;
337
129
  }
338
130
  }
339
131
  if (process.env.TELETON_WEBUI_HOST) {
340
132
  config.webui.host = process.env.TELETON_WEBUI_HOST;
133
+ if (!["127.0.0.1", "localhost", "::1"].includes(config.webui.host)) {
134
+ log.warn(
135
+ { host: config.webui.host },
136
+ "WebUI bound to non-loopback address \u2014 ensure auth_token is set"
137
+ );
138
+ }
139
+ }
140
+ if (process.env.TELETON_BASE_URL) {
141
+ try {
142
+ new URL(process.env.TELETON_BASE_URL);
143
+ config.agent.base_url = process.env.TELETON_BASE_URL;
144
+ } catch {
145
+ throw new Error(
146
+ `Invalid TELETON_BASE_URL: "${process.env.TELETON_BASE_URL}" is not a valid URL`
147
+ );
148
+ }
341
149
  }
342
150
  if (process.env.TELETON_TAVILY_API_KEY) {
343
151
  config.tavily_api_key = process.env.TELETON_TAVILY_API_KEY;
@@ -354,169 +162,447 @@ function getDefaultConfigPath() {
354
162
  return DEFAULT_CONFIG_PATH;
355
163
  }
356
164
 
357
- // src/ton/wallet-service.ts
358
- import { mnemonicNew, mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto";
359
- import { WalletContractV5R1, TonClient, fromNano } from "@ton/ton";
360
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync as chmodSync2 } from "fs";
361
- import { join as join2, dirname as dirname2 } from "path";
362
- var WALLET_FILE = join2(TELETON_ROOT, "wallet.json");
363
- var _walletCache;
364
- var _keyPairCache = null;
365
- async function generateWallet() {
366
- const mnemonic = await mnemonicNew(24);
367
- const keyPair = await mnemonicToPrivateKey(mnemonic);
368
- const wallet = WalletContractV5R1.create({
369
- workchain: 0,
370
- publicKey: keyPair.publicKey
371
- });
372
- const address = wallet.address.toString({ bounceable: true, testOnly: false });
165
+ // src/soul/loader.ts
166
+ import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
167
+
168
+ // src/memory/daily-logs.ts
169
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, appendFileSync, readFileSync as readFileSync2 } from "fs";
170
+ import { join as join2 } from "path";
171
+
172
+ // src/workspace/validator.ts
173
+ import { existsSync as existsSync2, lstatSync, readdirSync } from "fs";
174
+ import { resolve, normalize, relative, extname, basename } from "path";
175
+ import { homedir as homedir2 } from "os";
176
+ var WorkspaceSecurityError = class extends Error {
177
+ constructor(message, attemptedPath) {
178
+ super(message);
179
+ this.attemptedPath = attemptedPath;
180
+ this.name = "WorkspaceSecurityError";
181
+ }
182
+ };
183
+ function decodeRecursive(str) {
184
+ let decoded = str;
185
+ let prev = "";
186
+ let iterations = 0;
187
+ const maxIterations = 10;
188
+ while (decoded !== prev && iterations < maxIterations) {
189
+ prev = decoded;
190
+ try {
191
+ decoded = decodeURIComponent(decoded);
192
+ } catch {
193
+ break;
194
+ }
195
+ iterations++;
196
+ }
197
+ return decoded;
198
+ }
199
+ function validatePath(inputPath, allowCreate = false) {
200
+ if (!inputPath || inputPath.trim() === "") {
201
+ throw new WorkspaceSecurityError("Path cannot be empty.", inputPath);
202
+ }
203
+ const trimmedPath = inputPath.trim().replace(/\\/g, "/");
204
+ const decodedPath = decodeRecursive(trimmedPath);
205
+ let absolutePath;
206
+ if (decodedPath.startsWith("/")) {
207
+ absolutePath = resolve(normalize(decodedPath));
208
+ } else if (decodedPath.startsWith("~/")) {
209
+ const expanded = decodedPath.replace(/^~(?=$|[\\/])/, homedir2());
210
+ absolutePath = resolve(expanded);
211
+ } else {
212
+ absolutePath = resolve(WORKSPACE_ROOT, normalize(decodedPath));
213
+ }
214
+ const relativePath = relative(WORKSPACE_ROOT, absolutePath);
215
+ if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
216
+ throw new WorkspaceSecurityError(
217
+ `Access denied: Path '${inputPath}' is outside the workspace. Only files in ~/.teleton/workspace/ are accessible.`,
218
+ inputPath
219
+ );
220
+ }
221
+ const exists = existsSync2(absolutePath);
222
+ if (!exists && !allowCreate) {
223
+ throw new WorkspaceSecurityError(
224
+ `File not found: '${inputPath}' does not exist in workspace.`,
225
+ inputPath
226
+ );
227
+ }
228
+ if (exists) {
229
+ const stats = lstatSync(absolutePath);
230
+ if (stats.isSymbolicLink()) {
231
+ throw new WorkspaceSecurityError(
232
+ `Access denied: Symbolic links are not allowed for security reasons.`,
233
+ inputPath
234
+ );
235
+ }
236
+ }
373
237
  return {
374
- version: "w5r1",
375
- address,
376
- publicKey: keyPair.publicKey.toString("hex"),
377
- mnemonic,
378
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
238
+ absolutePath,
239
+ relativePath,
240
+ exists,
241
+ isDirectory: exists ? lstatSync(absolutePath).isDirectory() : false,
242
+ extension: extname(absolutePath).toLowerCase(),
243
+ filename: basename(absolutePath)
379
244
  };
380
245
  }
381
- function saveWallet(wallet) {
382
- const dir = dirname2(WALLET_FILE);
383
- if (!existsSync2(dir)) {
384
- mkdirSync2(dir, { recursive: true });
246
+ function validateReadPath(inputPath) {
247
+ const validated = validatePath(inputPath, false);
248
+ if (validated.isDirectory) {
249
+ throw new WorkspaceSecurityError(`Cannot read directory as file: '${inputPath}'`, inputPath);
385
250
  }
386
- writeFileSync2(WALLET_FILE, JSON.stringify(wallet, null, 2), "utf-8");
387
- chmodSync2(WALLET_FILE, 384);
388
- _walletCache = void 0;
389
- _keyPairCache = null;
251
+ return validated;
390
252
  }
391
- function loadWallet() {
392
- if (_walletCache !== void 0) return _walletCache;
393
- if (!existsSync2(WALLET_FILE)) {
394
- _walletCache = null;
395
- return null;
253
+ var IMMUTABLE_FILES = ["SOUL.md", "STRATEGY.md", "SECURITY.md"];
254
+ function validateWritePath(inputPath, fileType) {
255
+ const validated = validatePath(inputPath, true);
256
+ if (IMMUTABLE_FILES.includes(validated.filename)) {
257
+ throw new WorkspaceSecurityError(
258
+ `Cannot write to ${validated.filename}. This file is configured by the owner. Use memory_write instead.`,
259
+ inputPath
260
+ );
261
+ }
262
+ if (fileType && ALLOWED_EXTENSIONS[fileType]) {
263
+ const allowedExts = ALLOWED_EXTENSIONS[fileType];
264
+ if (!allowedExts.includes(validated.extension)) {
265
+ throw new WorkspaceSecurityError(
266
+ `Invalid file type: '${validated.extension}' is not allowed for ${fileType}. Allowed: ${allowedExts.join(", ")}`,
267
+ inputPath
268
+ );
269
+ }
396
270
  }
271
+ return validated;
272
+ }
273
+ function validateDirectory(inputPath) {
274
+ const validated = validatePath(inputPath, true);
275
+ if (validated.exists && !validated.isDirectory) {
276
+ throw new WorkspaceSecurityError(
277
+ `Path exists but is not a directory: '${inputPath}'`,
278
+ inputPath
279
+ );
280
+ }
281
+ return validated;
282
+ }
283
+
284
+ // src/memory/daily-logs.ts
285
+ var log2 = createLogger("Memory");
286
+ var MEMORY_DIR = WORKSPACE_PATHS.MEMORY_DIR;
287
+ function formatDate(date) {
288
+ const year = date.getFullYear();
289
+ const month = String(date.getMonth() + 1).padStart(2, "0");
290
+ const day = String(date.getDate()).padStart(2, "0");
291
+ return `${year}-${month}-${day}`;
292
+ }
293
+ function getDailyLogPath(date = /* @__PURE__ */ new Date()) {
294
+ return join2(MEMORY_DIR, `${formatDate(date)}.md`);
295
+ }
296
+ function ensureMemoryDir() {
297
+ if (!existsSync3(MEMORY_DIR)) {
298
+ mkdirSync2(MEMORY_DIR, { recursive: true });
299
+ }
300
+ }
301
+ function appendToDailyLog(content, date = /* @__PURE__ */ new Date()) {
397
302
  try {
398
- const content = readFileSync2(WALLET_FILE, "utf-8");
399
- _walletCache = JSON.parse(content);
400
- return _walletCache;
303
+ ensureMemoryDir();
304
+ const logPath = getDailyLogPath(date);
305
+ const timestamp = date.toLocaleTimeString("en-US", { hour12: false });
306
+ if (!existsSync3(logPath)) {
307
+ const header = `# Daily Log - ${formatDate(date)}
308
+
309
+ `;
310
+ appendFileSync(logPath, header, "utf-8");
311
+ }
312
+ const entry = `## ${timestamp}
313
+
314
+ ${content}
315
+
316
+ ---
317
+
318
+ `;
319
+ appendFileSync(logPath, entry, "utf-8");
320
+ log2.info(`Daily log updated: ${logPath}`);
401
321
  } catch (error) {
402
- console.error("Failed to load wallet:", error);
403
- _walletCache = null;
322
+ log2.error({ err: error }, "Failed to write daily log");
323
+ }
324
+ }
325
+ function readDailyLog(date = /* @__PURE__ */ new Date()) {
326
+ try {
327
+ const logPath = getDailyLogPath(date);
328
+ if (!existsSync3(logPath)) return null;
329
+ return readFileSync2(logPath, "utf-8");
330
+ } catch {
404
331
  return null;
405
332
  }
406
333
  }
407
- function walletExists() {
408
- return existsSync2(WALLET_FILE);
334
+ var DAILY_LOG_LINE_LIMIT = 100;
335
+ function truncateDailyLog(content) {
336
+ const lines = content.split("\n");
337
+ if (lines.length <= DAILY_LOG_LINE_LIMIT) return content;
338
+ const truncated = lines.slice(-DAILY_LOG_LINE_LIMIT).join("\n");
339
+ const dropped = lines.length - DAILY_LOG_LINE_LIMIT;
340
+ return `_[... ${dropped} earlier lines omitted]_
341
+
342
+ ${truncated}`;
409
343
  }
410
- async function importWallet(mnemonic) {
411
- const valid = await mnemonicValidate(mnemonic);
412
- if (!valid) {
413
- throw new Error("Invalid mnemonic: words do not form a valid TON seed phrase");
344
+ function readRecentMemory() {
345
+ const today = /* @__PURE__ */ new Date();
346
+ const yesterday = new Date(today);
347
+ yesterday.setDate(yesterday.getDate() - 1);
348
+ const parts = [];
349
+ const yesterdayLog = readDailyLog(yesterday);
350
+ if (yesterdayLog) {
351
+ parts.push(`## Yesterday (${formatDate(yesterday)})
352
+
353
+ ${truncateDailyLog(yesterdayLog)}`);
414
354
  }
415
- const keyPair = await mnemonicToPrivateKey(mnemonic);
416
- const wallet = WalletContractV5R1.create({
417
- workchain: 0,
418
- publicKey: keyPair.publicKey
419
- });
420
- const address = wallet.address.toString({ bounceable: true, testOnly: false });
421
- return {
422
- version: "w5r1",
423
- address,
424
- publicKey: keyPair.publicKey.toString("hex"),
425
- mnemonic,
426
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
427
- };
355
+ const todayLog = readDailyLog(today);
356
+ if (todayLog) {
357
+ parts.push(`## Today (${formatDate(today)})
358
+
359
+ ${truncateDailyLog(todayLog)}`);
360
+ }
361
+ if (parts.length === 0) {
362
+ return null;
363
+ }
364
+ return `# Recent Memory
365
+
366
+ ${parts.join("\n\n---\n\n")}`;
428
367
  }
429
- function getWalletAddress() {
430
- const wallet = loadWallet();
431
- return wallet?.address || null;
368
+ function writeSummaryToDailyLog(summary) {
369
+ appendToDailyLog(`### Memory Flush (Pre-Compaction)
370
+
371
+ ${summary}`);
432
372
  }
433
- async function getKeyPair() {
434
- if (_keyPairCache) return _keyPairCache;
435
- const wallet = loadWallet();
436
- if (!wallet) return null;
437
- _keyPairCache = await mnemonicToPrivateKey(wallet.mnemonic);
438
- return _keyPairCache;
373
+
374
+ // src/utils/sanitize.ts
375
+ function sanitizeForPrompt(text) {
376
+ return text.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").replace(/[\u00AD\u034F\u061C\u180E\u200B-\u200F\u2060-\u2064\uFEFF]/g, "").replace(/[\u202A-\u202E\u2066-\u2069]/g, "").replace(/[\r\n\u2028\u2029]+/g, " ").replace(/#{1,6}\s/g, "").replace(/<\/?[a-zA-Z_][^>]*>/g, "").replace(/`{3,}/g, "`").trim().slice(0, 128);
377
+ }
378
+ function sanitizeForContext(text) {
379
+ return text.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").replace(/[\u00AD\u034F\u061C\u180E\u200B-\u200F\u2060-\u2064\uFEFF]/g, "").replace(/[\u202A-\u202E\u2066-\u2069]/g, "").replace(/[\u2028\u2029]/g, "\n").replace(/<\/?[a-zA-Z_][^>]*>/g, "").replace(/`{3,}/g, "``").trim();
439
380
  }
440
- async function getWalletBalance(address) {
381
+
382
+ // src/soul/loader.ts
383
+ var SOUL_PATHS = [WORKSPACE_PATHS.SOUL];
384
+ var STRATEGY_PATHS = [WORKSPACE_PATHS.STRATEGY];
385
+ var SECURITY_PATHS = [WORKSPACE_PATHS.SECURITY];
386
+ var MEMORY_PATH = WORKSPACE_PATHS.MEMORY;
387
+ var DEFAULT_SOUL = `# Teleton AI
388
+
389
+ You are Teleton, a personal AI assistant that operates through Telegram.
390
+
391
+ ## Personality
392
+ - Helpful and concise
393
+ - Direct and honest
394
+ - Friendly but professional
395
+
396
+ ## Guidelines
397
+ - Keep responses short and actionable
398
+ - Use markdown when appropriate
399
+ - Respect user privacy
400
+ - Be transparent about capabilities and limitations
401
+ `;
402
+ var fileCache = /* @__PURE__ */ new Map();
403
+ var FILE_CACHE_TTL = 6e4;
404
+ function cachedReadFile(path) {
405
+ const now = Date.now();
406
+ const cached = fileCache.get(path);
407
+ if (cached && now < cached.expiry) return cached.content;
408
+ let content = null;
441
409
  try {
442
- const endpoint = await getCachedHttpEndpoint();
443
- const client = new TonClient({ endpoint });
444
- const { Address: Address2 } = await import("@ton/core");
445
- const addressObj = Address2.parse(address);
446
- const balance = await client.getBalance(addressObj);
447
- const balanceFormatted = fromNano(balance);
448
- return {
449
- balance: balanceFormatted,
450
- balanceNano: balance.toString()
451
- };
452
- } catch (error) {
453
- console.error("Failed to get balance:", error);
454
- return null;
410
+ if (existsSync4(path)) content = readFileSync3(path, "utf-8");
411
+ } catch {
455
412
  }
413
+ fileCache.set(path, { content, expiry: now + FILE_CACHE_TTL });
414
+ return content;
456
415
  }
457
- var TON_PRICE_CACHE_TTL_MS = 3e4;
458
- var _tonPriceCache = null;
459
- async function getTonPrice() {
460
- if (_tonPriceCache && Date.now() - _tonPriceCache.timestamp < TON_PRICE_CACHE_TTL_MS) {
461
- return { ..._tonPriceCache };
416
+ function clearPromptCache() {
417
+ fileCache.clear();
418
+ }
419
+ function loadSoul() {
420
+ for (const path of SOUL_PATHS) {
421
+ const content = cachedReadFile(path);
422
+ if (content) return content;
462
423
  }
463
- try {
464
- const response = await tonapiFetch(`/rates?tokens=ton&currencies=usd`);
465
- if (response.ok) {
466
- const data = await response.json();
467
- const price = data?.rates?.TON?.prices?.USD;
468
- if (typeof price === "number" && price > 0) {
469
- _tonPriceCache = { usd: price, source: "TonAPI", timestamp: Date.now() };
470
- return _tonPriceCache;
471
- }
424
+ return DEFAULT_SOUL;
425
+ }
426
+ function loadStrategy() {
427
+ for (const path of STRATEGY_PATHS) {
428
+ const content = cachedReadFile(path);
429
+ if (content) return content;
430
+ }
431
+ return null;
432
+ }
433
+ function loadSecurity() {
434
+ for (const path of SECURITY_PATHS) {
435
+ const content = cachedReadFile(path);
436
+ if (content) return content;
437
+ }
438
+ return null;
439
+ }
440
+ var MEMORY_HARD_LIMIT = 150;
441
+ function loadPersistentMemory() {
442
+ const content = cachedReadFile(MEMORY_PATH);
443
+ if (!content) return null;
444
+ const lines = content.split("\n");
445
+ if (lines.length <= MEMORY_HARD_LIMIT) {
446
+ return content;
447
+ }
448
+ const truncated = lines.slice(0, MEMORY_HARD_LIMIT).join("\n");
449
+ const remaining = lines.length - MEMORY_HARD_LIMIT;
450
+ return `${truncated}
451
+
452
+ _[... ${remaining} more lines not loaded. Consider consolidating MEMORY.md to keep it under ${MEMORY_HARD_LIMIT} lines.]_`;
453
+ }
454
+ function loadMemoryContext() {
455
+ const parts = [];
456
+ const persistentMemory = loadPersistentMemory();
457
+ if (persistentMemory) {
458
+ parts.push(`## Persistent Memory
459
+
460
+ ${sanitizeForContext(persistentMemory)}`);
461
+ }
462
+ const recentMemory = readRecentMemory();
463
+ if (recentMemory) {
464
+ parts.push(sanitizeForContext(recentMemory));
465
+ }
466
+ if (parts.length === 0) {
467
+ return null;
468
+ }
469
+ return parts.join("\n\n---\n\n");
470
+ }
471
+ function buildSystemPrompt(options) {
472
+ const soul = options.soul ?? loadSoul();
473
+ const parts = [soul];
474
+ const security = loadSecurity();
475
+ if (security) {
476
+ parts.push(`
477
+ ${security}`);
478
+ }
479
+ const includeStrategy = options.includeStrategy ?? true;
480
+ if (includeStrategy) {
481
+ const strategy = options.strategy ?? loadStrategy();
482
+ if (strategy) {
483
+ parts.push(`
484
+ ${strategy}`);
472
485
  }
473
- } catch {
474
486
  }
475
- try {
476
- const response = await fetchWithTimeout(
477
- `${COINGECKO_API_URL}/simple/price?ids=the-open-network&vs_currencies=usd`
487
+ parts.push(`
488
+ ## Your Workspace
489
+
490
+ You have a personal workspace at \`~/.teleton/workspace/\` where you can store and manage files.
491
+
492
+ **Structure:**
493
+ - \`SOUL.md\` - Your personality and behavior guidelines
494
+ - \`MEMORY.md\` - Persistent memory (long-term facts you've learned)
495
+ - \`STRATEGY.md\` - Business strategy and trading rules
496
+ - \`memory/\` - Daily logs (auto-created per day)
497
+ - \`downloads/\` - Media downloaded from Telegram
498
+ - \`uploads/\` - Files ready to send
499
+ - \`temp/\` - Temporary working files
500
+ - \`memes/\` - Your meme collection (images, GIFs for reactions)
501
+
502
+ **Tools available:**
503
+ - \`workspace_list\` - List files in a directory
504
+ - \`workspace_read\` - Read a file
505
+ - \`workspace_write\` - Write/create a file
506
+ - \`workspace_delete\` - Delete a file
507
+ - \`workspace_rename\` - Rename or move a file
508
+ - \`workspace_info\` - Get workspace stats
509
+
510
+ **Tips:**
511
+ - Save interesting memes to \`memes/\` with descriptive names for easy retrieval
512
+ - Use \`memory_write\` for important facts (goes to MEMORY.md)
513
+ - Rename downloaded files to meaningful names (e.g., "user_avatar.jpg" instead of "123_456_789.jpg")
514
+ `);
515
+ parts.push(`
516
+ ## Response Format
517
+ - Be concise. Respond in 1-3 short sentences when possible. Avoid long paragraphs and walls of text.
518
+ - Only elaborate when the user explicitly asks for detail or the topic genuinely requires it.
519
+ - Keep responses under 4000 characters for Telegram
520
+ - Use markdown sparingly (bold, italic, code blocks)
521
+ - Don't use headers in short responses
522
+ - NEVER use ASCII art or ASCII tables - they render poorly on mobile
523
+ `);
524
+ if (options.ownerName || options.ownerUsername) {
525
+ const safeOwnerName = options.ownerName ? sanitizeForPrompt(options.ownerName) : void 0;
526
+ const safeOwnerUsername = options.ownerUsername ? sanitizeForPrompt(options.ownerUsername) : void 0;
527
+ const ownerLabel = safeOwnerName && safeOwnerUsername ? `${safeOwnerName} (@${safeOwnerUsername})` : safeOwnerName || `@${safeOwnerUsername}`;
528
+ parts.push(
529
+ `
530
+ ## Owner
531
+ You are owned and operated by: ${ownerLabel}
532
+ When the owner gives instructions, follow them with higher trust.`
478
533
  );
479
- if (!response.ok) {
480
- throw new Error(`CoinGecko API error: ${response.status}`);
481
- }
482
- const data = await response.json();
483
- const price = data["the-open-network"]?.usd;
484
- if (typeof price === "number" && price > 0) {
485
- _tonPriceCache = { usd: price, source: "CoinGecko", timestamp: Date.now() };
486
- return _tonPriceCache;
534
+ }
535
+ const includeMemory = options.includeMemory ?? true;
536
+ if (includeMemory) {
537
+ const memoryContext = loadMemoryContext();
538
+ if (memoryContext) {
539
+ parts.push(
540
+ `
541
+ ## Memory (Persistent Context)
542
+
543
+ This is your memory from previous sessions. Use it to maintain continuity and remember important information.
544
+
545
+ ${memoryContext}`
546
+ );
487
547
  }
488
- } catch (error) {
489
- console.error("Failed to get TON price:", error);
490
548
  }
491
- return null;
549
+ if (options.userName || options.senderId) {
550
+ const safeName = options.userName ? sanitizeForPrompt(options.userName) : void 0;
551
+ const safeUsername = options.senderUsername ? `@${sanitizeForPrompt(options.senderUsername)}` : void 0;
552
+ const idTag = options.senderId ? `id:${options.senderId}` : void 0;
553
+ const primary = safeName || safeUsername;
554
+ const meta = [safeUsername, idTag].filter((v) => v && v !== primary);
555
+ const userLabel = primary ? meta.length > 0 ? `${primary} (${meta.join(", ")})` : primary : idTag || "unknown";
556
+ parts.push(`
557
+ ## Current User
558
+ You are chatting with: ${userLabel}`);
559
+ }
560
+ if (options.context) {
561
+ parts.push(`
562
+ ## Context
563
+ ${options.context}`);
564
+ }
565
+ if (options.memoryFlushWarning) {
566
+ parts.push(`
567
+ ## Memory Flush Warning
568
+
569
+ Your conversation context is approaching the limit and may be compacted soon.
570
+ **Always respond to the user's message first.** Then, if there's anything important worth preserving, consider using \`memory_write\` alongside your response:
571
+
572
+ - \`target: "persistent"\` for facts, lessons, contacts, decisions
573
+ - \`target: "daily"\` for session notes, events, temporary context
574
+ `);
575
+ }
576
+ return parts.join("\n");
492
577
  }
493
578
 
494
579
  // src/agent/tools/plugin-loader.ts
495
- import { readdirSync, readFileSync as readFileSync4, existsSync as existsSync4, statSync } from "fs";
580
+ import { readdirSync as readdirSync2, readFileSync as readFileSync5, existsSync as existsSync6, statSync } from "fs";
496
581
  import { join as join4 } from "path";
497
582
  import { pathToFileURL } from "url";
498
583
  import { execFile } from "child_process";
499
584
  import { promisify } from "util";
500
585
 
501
586
  // src/agent/tools/plugin-validator.ts
502
- import { z as z2 } from "zod";
503
- var ManifestSchema = z2.object({
504
- name: z2.string().min(1).max(64).regex(
587
+ import { z } from "zod";
588
+ var log3 = createLogger("PluginValidator");
589
+ var ManifestSchema = z.object({
590
+ name: z.string().min(1).max(64).regex(
505
591
  /^[a-z0-9][a-z0-9-]*$/,
506
592
  "Must be lowercase alphanumeric with hyphens, starting with a letter or number"
507
593
  ),
508
- version: z2.string().regex(/^\d+\.\d+\.\d+$/, "Must be semver (e.g., 1.0.0)"),
509
- author: z2.string().max(128).optional(),
510
- description: z2.string().max(256).optional(),
511
- dependencies: z2.array(z2.string()).optional(),
512
- defaultConfig: z2.record(z2.string(), z2.unknown()).optional(),
513
- sdkVersion: z2.string().max(32).optional(),
514
- secrets: z2.record(
515
- z2.string(),
516
- z2.object({
517
- required: z2.boolean(),
518
- description: z2.string().max(256),
519
- env: z2.string().max(128).optional()
594
+ version: z.string().regex(/^\d+\.\d+\.\d+$/, "Must be semver (e.g., 1.0.0)"),
595
+ author: z.string().max(128).optional(),
596
+ description: z.string().max(256).optional(),
597
+ dependencies: z.array(z.string()).optional(),
598
+ defaultConfig: z.record(z.string(), z.unknown()).optional(),
599
+ sdkVersion: z.string().max(32).optional(),
600
+ secrets: z.record(
601
+ z.string(),
602
+ z.object({
603
+ required: z.boolean(),
604
+ description: z.string().max(256),
605
+ env: z.string().max(128).optional()
520
606
  })
521
607
  ).optional()
522
608
  });
@@ -527,20 +613,20 @@ function validateToolDefs(defs, pluginName) {
527
613
  const valid = [];
528
614
  for (const def of defs) {
529
615
  if (!def || typeof def !== "object") {
530
- console.warn(`\u26A0\uFE0F [${pluginName}] tool is not an object, skipping`);
616
+ log3.warn(`[${pluginName}] tool is not an object, skipping`);
531
617
  continue;
532
618
  }
533
619
  const t = def;
534
620
  if (!t.name || typeof t.name !== "string") {
535
- console.warn(`\u26A0\uFE0F [${pluginName}] tool missing 'name', skipping`);
621
+ log3.warn(`[${pluginName}] tool missing 'name', skipping`);
536
622
  continue;
537
623
  }
538
624
  if (!t.description || typeof t.description !== "string") {
539
- console.warn(`\u26A0\uFE0F [${pluginName}] tool "${t.name}" missing 'description', skipping`);
625
+ log3.warn(`[${pluginName}] tool "${t.name}" missing 'description', skipping`);
540
626
  continue;
541
627
  }
542
628
  if (!t.execute || typeof t.execute !== "function") {
543
- console.warn(`\u26A0\uFE0F [${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
629
+ log3.warn(`[${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
544
630
  continue;
545
631
  }
546
632
  valid.push(t);
@@ -572,35 +658,40 @@ var PluginSDKError = class extends Error {
572
658
  var SDK_VERSION = "1.0.0";
573
659
 
574
660
  // src/ton/transfer.ts
575
- import { WalletContractV5R1 as WalletContractV5R12, TonClient as TonClient2, toNano, internal } from "@ton/ton";
661
+ import { WalletContractV5R1, TonClient, toNano, internal } from "@ton/ton";
576
662
  import { Address, SendMode } from "@ton/core";
663
+ var log4 = createLogger("TON");
577
664
  async function sendTon(params) {
578
665
  try {
579
666
  const { toAddress, amount, comment = "", bounce = false } = params;
667
+ if (!Number.isFinite(amount) || amount <= 0) {
668
+ log4.error({ amount }, "Invalid transfer amount");
669
+ return null;
670
+ }
580
671
  let recipientAddress;
581
672
  try {
582
673
  recipientAddress = Address.parse(toAddress);
583
674
  } catch (e) {
584
- console.error(`Invalid recipient address: ${toAddress}`, e);
675
+ log4.error({ err: e }, `Invalid recipient address: ${toAddress}`);
585
676
  return null;
586
677
  }
587
678
  const keyPair = await getKeyPair();
588
679
  if (!keyPair) {
589
- console.error("Wallet not initialized");
680
+ log4.error("Wallet not initialized");
590
681
  return null;
591
682
  }
592
- const wallet = WalletContractV5R12.create({
683
+ const wallet = WalletContractV5R1.create({
593
684
  workchain: 0,
594
685
  publicKey: keyPair.publicKey
595
686
  });
596
687
  const endpoint = await getCachedHttpEndpoint();
597
- const client = new TonClient2({ endpoint });
688
+ const client = new TonClient({ endpoint });
598
689
  const contract = client.open(wallet);
599
690
  const seqno = await contract.getSeqno();
600
691
  await contract.sendTransfer({
601
692
  seqno,
602
693
  secretKey: keyPair.secretKey,
603
- sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
694
+ sendMode: SendMode.PAY_GAS_SEPARATELY,
604
695
  messages: [
605
696
  internal({
606
697
  to: recipientAddress,
@@ -611,15 +702,16 @@ async function sendTon(params) {
611
702
  ]
612
703
  });
613
704
  const pseudoHash = `${seqno}_${Date.now()}_${amount.toFixed(2)}`;
614
- console.log(`\u{1F4B8} [TON] Sent ${amount} TON to ${toAddress.slice(0, 8)}... - seqno: ${seqno}`);
705
+ log4.info(`Sent ${amount} TON to ${toAddress.slice(0, 8)}... - seqno: ${seqno}`);
615
706
  return pseudoHash;
616
707
  } catch (error) {
617
- console.error("Error sending TON:", error);
708
+ log4.error({ err: error }, "Error sending TON");
618
709
  return null;
619
710
  }
620
711
  }
621
712
 
622
713
  // src/utils/retry.ts
714
+ var log5 = createLogger("Utils");
623
715
  var DEFAULT_OPTIONS = {
624
716
  maxAttempts: RETRY_DEFAULT_MAX_ATTEMPTS,
625
717
  baseDelayMs: RETRY_DEFAULT_BASE_DELAY_MS,
@@ -643,7 +735,7 @@ async function withRetry(fn, options = {}) {
643
735
  return result;
644
736
  } catch (error) {
645
737
  lastError = error instanceof Error ? error : new Error(String(error));
646
- console.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed:`, lastError.message);
738
+ log5.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed: ${lastError.message}`);
647
739
  if (attempt < opts.maxAttempts) {
648
740
  const delay = Math.min(opts.baseDelayMs * Math.pow(2, attempt - 1), opts.maxDelayMs);
649
741
  await sleep(delay);
@@ -661,7 +753,7 @@ async function withBlockchainRetry(fn, operation = "blockchain operation") {
661
753
  timeout: RETRY_BLOCKCHAIN_TIMEOUT_MS
662
754
  });
663
755
  } catch (error) {
664
- const message = error instanceof Error ? error.message : String(error);
756
+ const message = getErrorMessage(error);
665
757
  throw new Error(`${operation} failed after retries: ${message}`);
666
758
  }
667
759
  }
@@ -670,25 +762,25 @@ async function withBlockchainRetry(fn, operation = "blockchain operation") {
670
762
  var DEFAULT_MAX_AGE_MINUTES = 10;
671
763
  var DEFAULT_TX_RETENTION_DAYS = 30;
672
764
  var CLEANUP_PROBABILITY = 0.1;
673
- function cleanupOldTransactions(db, retentionDays, log) {
765
+ function cleanupOldTransactions(db, retentionDays, log7) {
674
766
  if (Math.random() > CLEANUP_PROBABILITY) return;
675
767
  try {
676
768
  const cutoff = Math.floor(Date.now() / 1e3) - retentionDays * 24 * 60 * 60;
677
769
  const result = db.prepare("DELETE FROM used_transactions WHERE used_at < ?").run(cutoff);
678
770
  if (result.changes > 0) {
679
- log.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
771
+ log7.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
680
772
  }
681
773
  } catch (err) {
682
- log.error("Transaction cleanup failed:", err);
774
+ log7.error("Transaction cleanup failed:", err);
683
775
  }
684
776
  }
685
- function createTonSDK(log, db) {
777
+ function createTonSDK(log7, db) {
686
778
  return {
687
779
  getAddress() {
688
780
  try {
689
781
  return getWalletAddress();
690
782
  } catch (err) {
691
- log.error("ton.getAddress() failed:", err);
783
+ log7.error("ton.getAddress() failed:", err);
692
784
  return null;
693
785
  }
694
786
  },
@@ -698,7 +790,7 @@ function createTonSDK(log, db) {
698
790
  if (!addr) return null;
699
791
  return await getWalletBalance(addr);
700
792
  } catch (err) {
701
- log.error("ton.getBalance() failed:", err);
793
+ log7.error("ton.getBalance() failed:", err);
702
794
  return null;
703
795
  }
704
796
  },
@@ -706,7 +798,7 @@ function createTonSDK(log, db) {
706
798
  try {
707
799
  return await getTonPrice();
708
800
  } catch (err) {
709
- log.error("ton.getPrice() failed:", err);
801
+ log7.error("ton.getPrice() failed:", err);
710
802
  return null;
711
803
  }
712
804
  },
@@ -748,13 +840,13 @@ function createTonSDK(log, db) {
748
840
  },
749
841
  async getTransactions(address, limit) {
750
842
  try {
751
- const { TonClient: TonClient3 } = await import("@ton/ton");
843
+ const { TonClient: TonClient2 } = await import("@ton/ton");
752
844
  const { Address: Address2 } = await import("@ton/core");
753
845
  const { getCachedHttpEndpoint: getCachedHttpEndpoint2 } = await import("./endpoint-FLYNEZ2F.js");
754
846
  const { formatTransactions } = await import("./format-transactions-FD74HI5N.js");
755
847
  const addressObj = Address2.parse(address);
756
848
  const endpoint = await getCachedHttpEndpoint2();
757
- const client = new TonClient3({ endpoint });
849
+ const client = new TonClient2({ endpoint });
758
850
  const transactions = await withBlockchainRetry(
759
851
  () => client.getTransactions(addressObj, {
760
852
  limit: Math.min(limit ?? 10, 50)
@@ -763,7 +855,7 @@ function createTonSDK(log, db) {
763
855
  );
764
856
  return formatTransactions(transactions);
765
857
  } catch (err) {
766
- log.error("ton.getTransactions() failed:", err);
858
+ log7.error("ton.getTransactions() failed:", err);
767
859
  return [];
768
860
  }
769
861
  },
@@ -779,7 +871,7 @@ function createTonSDK(log, db) {
779
871
  throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
780
872
  }
781
873
  const maxAgeMinutes = params.maxAgeMinutes ?? DEFAULT_MAX_AGE_MINUTES;
782
- cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log);
874
+ cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log7);
783
875
  try {
784
876
  const txs = await this.getTransactions(address, 20);
785
877
  for (const tx of txs) {
@@ -813,7 +905,7 @@ function createTonSDK(log, db) {
813
905
  };
814
906
  } catch (err) {
815
907
  if (err instanceof PluginSDKError) throw err;
816
- log.error("ton.verifyPayment() failed:", err);
908
+ log7.error("ton.verifyPayment() failed:", err);
817
909
  return {
818
910
  verified: false,
819
911
  error: `Verification failed: ${err instanceof Error ? err.message : String(err)}`
@@ -827,7 +919,7 @@ function createTonSDK(log, db) {
827
919
  if (!addr) return [];
828
920
  const response = await tonapiFetch(`/accounts/${addr}/jettons`);
829
921
  if (!response.ok) {
830
- log.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
922
+ log7.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
831
923
  return [];
832
924
  }
833
925
  const data = await response.json();
@@ -855,7 +947,7 @@ function createTonSDK(log, db) {
855
947
  }
856
948
  return balances;
857
949
  } catch (err) {
858
- log.error("ton.getJettonBalances() failed:", err);
950
+ log7.error("ton.getJettonBalances() failed:", err);
859
951
  return [];
860
952
  }
861
953
  },
@@ -864,7 +956,7 @@ function createTonSDK(log, db) {
864
956
  const response = await tonapiFetch(`/jettons/${jettonAddress}`);
865
957
  if (response.status === 404) return null;
866
958
  if (!response.ok) {
867
- log.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
959
+ log7.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
868
960
  return null;
869
961
  }
870
962
  const data = await response.json();
@@ -882,13 +974,13 @@ function createTonSDK(log, db) {
882
974
  image: data.preview || metadata.image || void 0
883
975
  };
884
976
  } catch (err) {
885
- log.error("ton.getJettonInfo() failed:", err);
977
+ log7.error("ton.getJettonInfo() failed:", err);
886
978
  return null;
887
979
  }
888
980
  },
889
981
  async sendJetton(jettonAddress, to, amount, opts) {
890
982
  const { Address: Address2, beginCell, SendMode: SendMode2 } = await import("@ton/core");
891
- const { WalletContractV5R1: WalletContractV5R13, TonClient: TonClient3, toNano: toNano2, internal: internal2 } = await import("@ton/ton");
983
+ const { WalletContractV5R1: WalletContractV5R12, TonClient: TonClient2, toNano: toNano2, internal: internal2 } = await import("@ton/ton");
892
984
  const { getCachedHttpEndpoint: getCachedHttpEndpoint2 } = await import("./endpoint-FLYNEZ2F.js");
893
985
  const walletData = loadWallet();
894
986
  if (!walletData) {
@@ -922,7 +1014,9 @@ function createTonSDK(log, db) {
922
1014
  const senderJettonWallet = jettonBalance.wallet_address.address;
923
1015
  const decimals = jettonBalance.jetton.decimals || 9;
924
1016
  const currentBalance = BigInt(jettonBalance.balance);
925
- const amountInUnits = BigInt(Math.floor(amount * 10 ** decimals));
1017
+ const amountStr = amount.toFixed(decimals);
1018
+ const [whole, frac = ""] = amountStr.split(".");
1019
+ const amountInUnits = BigInt(whole + (frac + "0".repeat(decimals)).slice(0, decimals));
926
1020
  if (amountInUnits > currentBalance) {
927
1021
  throw new PluginSDKError(
928
1022
  `Insufficient balance. Have ${Number(currentBalance) / 10 ** decimals}, need ${amount}`,
@@ -940,12 +1034,12 @@ function createTonSDK(log, db) {
940
1034
  if (!keyPair) {
941
1035
  throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
942
1036
  }
943
- const wallet = WalletContractV5R13.create({
1037
+ const wallet = WalletContractV5R12.create({
944
1038
  workchain: 0,
945
1039
  publicKey: keyPair.publicKey
946
1040
  });
947
1041
  const endpoint = await getCachedHttpEndpoint2();
948
- const client = new TonClient3({ endpoint });
1042
+ const client = new TonClient2({ endpoint });
949
1043
  const walletContract = client.open(wallet);
950
1044
  const seqno = await walletContract.getSeqno();
951
1045
  await walletContract.sendTransfer({
@@ -967,7 +1061,7 @@ function createTonSDK(log, db) {
967
1061
  try {
968
1062
  const response = await tonapiFetch(`/accounts/${ownerAddress}/jettons`);
969
1063
  if (!response.ok) {
970
- log.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
1064
+ log7.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
971
1065
  return null;
972
1066
  }
973
1067
  const { Address: Address2 } = await import("@ton/core");
@@ -977,7 +1071,7 @@ function createTonSDK(log, db) {
977
1071
  );
978
1072
  return match ? match.wallet_address.address : null;
979
1073
  } catch (err) {
980
- log.error("ton.getJettonWalletAddress() failed:", err);
1074
+ log7.error("ton.getJettonWalletAddress() failed:", err);
981
1075
  return null;
982
1076
  }
983
1077
  },
@@ -990,14 +1084,14 @@ function createTonSDK(log, db) {
990
1084
  `/accounts/${encodeURIComponent(addr)}/nfts?limit=100&indirect_ownership=true`
991
1085
  );
992
1086
  if (!response.ok) {
993
- log.error(`ton.getNftItems() TonAPI error: ${response.status}`);
1087
+ log7.error(`ton.getNftItems() TonAPI error: ${response.status}`);
994
1088
  return [];
995
1089
  }
996
1090
  const data = await response.json();
997
1091
  if (!Array.isArray(data.nft_items)) return [];
998
1092
  return data.nft_items.filter((item) => item.trust !== "blacklist").map((item) => mapNftItem(item));
999
1093
  } catch (err) {
1000
- log.error("ton.getNftItems() failed:", err);
1094
+ log7.error("ton.getNftItems() failed:", err);
1001
1095
  return [];
1002
1096
  }
1003
1097
  },
@@ -1006,13 +1100,13 @@ function createTonSDK(log, db) {
1006
1100
  const response = await tonapiFetch(`/nfts/${nftAddress}`);
1007
1101
  if (response.status === 404) return null;
1008
1102
  if (!response.ok) {
1009
- log.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
1103
+ log7.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
1010
1104
  return null;
1011
1105
  }
1012
1106
  const item = await response.json();
1013
1107
  return mapNftItem(item);
1014
1108
  } catch (err) {
1015
- log.error("ton.getNftInfo() failed:", err);
1109
+ log7.error("ton.getNftInfo() failed:", err);
1016
1110
  return null;
1017
1111
  }
1018
1112
  },
@@ -1061,12 +1155,18 @@ function mapNftItem(item) {
1061
1155
  };
1062
1156
  }
1063
1157
 
1064
- // src/sdk/telegram.ts
1065
- import { randomBytes as randomBytes3 } from "crypto";
1158
+ // src/utils/gramjs-bigint.ts
1159
+ var import_big_integer = __toESM(require_BigInteger(), 1);
1160
+ import { randomBytes } from "crypto";
1161
+ function toLong(value) {
1162
+ return (0, import_big_integer.default)(String(value));
1163
+ }
1164
+ function randomLong() {
1165
+ return toLong(randomBytes(8).readBigUInt64BE());
1166
+ }
1066
1167
 
1067
1168
  // src/sdk/telegram-messages.ts
1068
- import { randomBytes } from "crypto";
1069
- function createTelegramMessagesSDK(bridge, log) {
1169
+ function createTelegramMessagesSDK(bridge, log7) {
1070
1170
  function requireBridge() {
1071
1171
  if (!bridge.isAvailable()) {
1072
1172
  throw new PluginSDKError(
@@ -1128,12 +1228,11 @@ function createTelegramMessagesSDK(bridge, log) {
1128
1228
  fromPeer: fromChatId,
1129
1229
  toPeer: toChatId,
1130
1230
  id: [messageId],
1131
- randomId: [randomBytes(8).readBigUInt64BE()]
1231
+ randomId: [randomLong()]
1132
1232
  })
1133
1233
  );
1134
- const updates = result;
1135
- if (updates.updates) {
1136
- for (const update of updates.updates) {
1234
+ if (result instanceof Api.Updates || result instanceof Api.UpdatesCombined) {
1235
+ for (const update of result.updates) {
1137
1236
  if (update.className === "UpdateNewMessage" || update.className === "UpdateNewChannelMessage") {
1138
1237
  return update.message.id;
1139
1238
  }
@@ -1187,7 +1286,7 @@ function createTelegramMessagesSDK(bridge, log) {
1187
1286
  return (resultData.messages ?? []).map(toSimpleMessage);
1188
1287
  } catch (err) {
1189
1288
  if (err instanceof PluginSDKError) throw err;
1190
- log.error("telegram.searchMessages() failed:", err);
1289
+ log7.error("telegram.searchMessages() failed:", err);
1191
1290
  return [];
1192
1291
  }
1193
1292
  },
@@ -1213,7 +1312,7 @@ function createTelegramMessagesSDK(bridge, log) {
1213
1312
  try {
1214
1313
  const gramJsClient = getClient();
1215
1314
  const { Api } = await import("telegram");
1216
- const bigInt = (await import("./BigInteger-DQ33LTTE.js")).default;
1315
+ const bigInt2 = (await import("./BigInteger-DQ33LTTE.js")).default;
1217
1316
  const peer = await gramJsClient.getInputEntity(chatId);
1218
1317
  const result = await gramJsClient.invoke(
1219
1318
  new Api.messages.GetReplies({
@@ -1225,7 +1324,7 @@ function createTelegramMessagesSDK(bridge, log) {
1225
1324
  limit,
1226
1325
  maxId: 0,
1227
1326
  minId: 0,
1228
- hash: bigInt(0)
1327
+ hash: bigInt2(0)
1229
1328
  })
1230
1329
  );
1231
1330
  const messages = [];
@@ -1412,8 +1511,7 @@ function createTelegramMessagesSDK(bridge, log) {
1412
1511
  }
1413
1512
 
1414
1513
  // src/sdk/telegram-social.ts
1415
- import { randomBytes as randomBytes2 } from "crypto";
1416
- function createTelegramSocialSDK(bridge, log) {
1514
+ function createTelegramSocialSDK(bridge, log7) {
1417
1515
  function requireBridge() {
1418
1516
  if (!bridge.isAvailable()) {
1419
1517
  throw new PluginSDKError(
@@ -1455,9 +1553,7 @@ function createTelegramSocialSDK(bridge, log) {
1455
1553
  let description;
1456
1554
  let membersCount;
1457
1555
  try {
1458
- const fullChannel = await client.invoke(
1459
- new Api.channels.GetFullChannel({ channel: entity })
1460
- );
1556
+ const fullChannel = await client.invoke(new Api.channels.GetFullChannel({ channel }));
1461
1557
  const fullChat = fullChannel.fullChat;
1462
1558
  description = fullChat.about || void 0;
1463
1559
  membersCount = fullChat.participantsCount || void 0;
@@ -1495,7 +1591,7 @@ function createTelegramSocialSDK(bridge, log) {
1495
1591
  return null;
1496
1592
  } catch (err) {
1497
1593
  if (err instanceof PluginSDKError) throw err;
1498
- log.error("telegram.getChatInfo() failed:", err);
1594
+ log7.error("telegram.getChatInfo() failed:", err);
1499
1595
  return null;
1500
1596
  }
1501
1597
  },
@@ -1548,12 +1644,14 @@ function createTelegramSocialSDK(bridge, log) {
1548
1644
  }
1549
1645
  if (result.users && result.users.length > 0) {
1550
1646
  const user = result.users[0];
1551
- return {
1552
- id: Number(user.id),
1553
- type: "user",
1554
- username: user.username || void 0,
1555
- title: user.firstName || void 0
1556
- };
1647
+ if (user instanceof Api.User) {
1648
+ return {
1649
+ id: Number(user.id),
1650
+ type: "user",
1651
+ username: user.username || void 0,
1652
+ title: user.firstName || void 0
1653
+ };
1654
+ }
1557
1655
  }
1558
1656
  if (result.chats && result.chats.length > 0) {
1559
1657
  const chat = result.chats[0];
@@ -1561,8 +1659,8 @@ function createTelegramSocialSDK(bridge, log) {
1561
1659
  return {
1562
1660
  id: Number(chat.id),
1563
1661
  type,
1564
- username: chat.username || void 0,
1565
- title: chat.title || void 0
1662
+ username: chat instanceof Api.Channel ? chat.username || void 0 : void 0,
1663
+ title: chat instanceof Api.Channel || chat instanceof Api.Chat ? chat.title || void 0 : void 0
1566
1664
  };
1567
1665
  }
1568
1666
  return null;
@@ -1586,20 +1684,23 @@ function createTelegramSocialSDK(bridge, log) {
1586
1684
  filter: new Api.ChannelParticipantsRecent(),
1587
1685
  offset: 0,
1588
1686
  limit: limit ?? 100,
1589
- hash: 0
1687
+ hash: toLong(0)
1590
1688
  })
1591
1689
  );
1592
1690
  const resultData = result;
1593
- return (resultData.users || []).map((user) => ({
1594
- id: Number(user.id),
1595
- firstName: user.firstName || "",
1596
- lastName: user.lastName || void 0,
1597
- username: user.username || void 0,
1598
- isBot: user.bot || false
1599
- }));
1691
+ return (resultData.users || []).map((user) => {
1692
+ const u = user;
1693
+ return {
1694
+ id: Number(u.id),
1695
+ firstName: u.firstName || "",
1696
+ lastName: u.lastName || void 0,
1697
+ username: u.username || void 0,
1698
+ isBot: u.bot || false
1699
+ };
1700
+ });
1600
1701
  } catch (err) {
1601
1702
  if (err instanceof PluginSDKError) throw err;
1602
- log.error("telegram.getParticipants() failed:", err);
1703
+ log7.error("telegram.getParticipants() failed:", err);
1603
1704
  return [];
1604
1705
  }
1605
1706
  },
@@ -1618,7 +1719,7 @@ function createTelegramSocialSDK(bridge, log) {
1618
1719
  const anonymous = opts?.isAnonymous ?? true;
1619
1720
  const multipleChoice = opts?.multipleChoice ?? false;
1620
1721
  const poll = new Api.Poll({
1621
- id: randomBytes2(8).readBigUInt64BE(),
1722
+ id: randomLong(),
1622
1723
  question: new Api.TextWithEntities({ text: question, entities: [] }),
1623
1724
  answers: answers.map(
1624
1725
  (opt, idx) => new Api.PollAnswer({
@@ -1634,10 +1735,10 @@ function createTelegramSocialSDK(bridge, log) {
1634
1735
  peer: chatId,
1635
1736
  media: new Api.InputMediaPoll({ poll }),
1636
1737
  message: "",
1637
- randomId: randomBytes2(8).readBigUInt64BE()
1738
+ randomId: randomLong()
1638
1739
  })
1639
1740
  );
1640
- if (result.className === "Updates" || result.className === "UpdatesCombined") {
1741
+ if (result instanceof Api.Updates || result instanceof Api.UpdatesCombined) {
1641
1742
  for (const update of result.updates) {
1642
1743
  if (update.className === "UpdateNewMessage" || update.className === "UpdateNewChannelMessage") {
1643
1744
  return update.message?.id ?? 0;
@@ -1671,7 +1772,7 @@ function createTelegramSocialSDK(bridge, log) {
1671
1772
  const client = getClient();
1672
1773
  const { Api } = await import("telegram");
1673
1774
  const poll = new Api.Poll({
1674
- id: randomBytes2(8).readBigUInt64BE(),
1775
+ id: randomLong(),
1675
1776
  question: new Api.TextWithEntities({ text: question, entities: [] }),
1676
1777
  answers: answers.map(
1677
1778
  (opt, idx) => new Api.PollAnswer({
@@ -1693,10 +1794,10 @@ function createTelegramSocialSDK(bridge, log) {
1693
1794
  solutionEntities: []
1694
1795
  }),
1695
1796
  message: "",
1696
- randomId: randomBytes2(8).readBigUInt64BE()
1797
+ randomId: randomLong()
1697
1798
  })
1698
1799
  );
1699
- if (result.className === "Updates" || result.className === "UpdatesCombined") {
1800
+ if (result instanceof Api.Updates || result instanceof Api.UpdatesCombined) {
1700
1801
  for (const update of result.updates) {
1701
1802
  if (update.className === "UpdateNewMessage" || update.className === "UpdateNewChannelMessage") {
1702
1803
  return update.message?.id ?? 0;
@@ -1962,12 +2063,32 @@ function createTelegramSocialSDK(bridge, log) {
1962
2063
  const client = getClient();
1963
2064
  const { Api, helpers } = await import("telegram");
1964
2065
  const { CustomFile } = await import("telegram/client/uploads.js");
1965
- const { readFileSync: readFileSync5, statSync: statSync2 } = await import("fs");
2066
+ const { readFileSync: readFileSync7, statSync: statSync2 } = await import("fs");
1966
2067
  const { basename: basename2 } = await import("path");
1967
- const filePath = mediaPath;
2068
+ const { resolve: resolve2, normalize: normalize2 } = await import("path");
2069
+ const { homedir: homedir3 } = await import("os");
2070
+ const { realpathSync } = await import("fs");
2071
+ const filePath = realpathSync(resolve2(normalize2(mediaPath)));
2072
+ const home = homedir3();
2073
+ const teletonWorkspace = `${home}/.teleton/workspace/`;
2074
+ const allowedPrefixes = [
2075
+ "/tmp/",
2076
+ `${home}/Downloads/`,
2077
+ `${home}/Pictures/`,
2078
+ `${home}/Videos/`,
2079
+ `${teletonWorkspace}uploads/`,
2080
+ `${teletonWorkspace}downloads/`,
2081
+ `${teletonWorkspace}memes/`
2082
+ ];
2083
+ if (!allowedPrefixes.some((p) => filePath.startsWith(p))) {
2084
+ throw new PluginSDKError(
2085
+ "sendStory: media path must be within /tmp, Downloads, Pictures, or Videos",
2086
+ "OPERATION_FAILED"
2087
+ );
2088
+ }
1968
2089
  const fileName = basename2(filePath);
1969
2090
  const fileSize = statSync2(filePath).size;
1970
- const fileBuffer = readFileSync5(filePath);
2091
+ const fileBuffer = readFileSync7(filePath);
1971
2092
  const isVideo = filePath.toLowerCase().match(/\.(mp4|mov|avi)$/);
1972
2093
  const customFile = new CustomFile(fileName, fileSize, filePath, fileBuffer);
1973
2094
  const uploadedFile = await client.uploadFile({
@@ -2004,7 +2125,8 @@ function createTelegramSocialSDK(bridge, log) {
2004
2125
  randomId: helpers.generateRandomBigInt()
2005
2126
  })
2006
2127
  );
2007
- return result.id || 0;
2128
+ const storyUpdate = result instanceof Api.Updates ? result.updates.find((u) => u.className === "UpdateStory") : void 0;
2129
+ return storyUpdate?.story?.id ?? 0;
2008
2130
  } catch (err) {
2009
2131
  if (err instanceof PluginSDKError) throw err;
2010
2132
  throw new PluginSDKError(
@@ -2017,7 +2139,7 @@ function createTelegramSocialSDK(bridge, log) {
2017
2139
  }
2018
2140
 
2019
2141
  // src/sdk/telegram.ts
2020
- function createTelegramSDK(bridge, log) {
2142
+ function createTelegramSDK(bridge, log7) {
2021
2143
  function requireBridge() {
2022
2144
  if (!bridge.isAvailable()) {
2023
2145
  throw new PluginSDKError(
@@ -2073,17 +2195,17 @@ function createTelegramSDK(bridge, log) {
2073
2195
  peer: chatId,
2074
2196
  media: new Api.InputMediaDice({ emoticon }),
2075
2197
  message: "",
2076
- randomId: randomBytes3(8).readBigUInt64BE(),
2198
+ randomId: randomLong(),
2077
2199
  replyTo: replyToId ? new Api.InputReplyToMessage({ replyToMsgId: replyToId }) : void 0
2078
2200
  })
2079
2201
  );
2080
2202
  let value;
2081
2203
  let messageId;
2082
- if (result.className === "Updates" || result.className === "UpdatesCombined") {
2204
+ if (result instanceof Api.Updates || result instanceof Api.UpdatesCombined) {
2083
2205
  for (const update of result.updates) {
2084
2206
  if (update.className === "UpdateNewMessage" || update.className === "UpdateNewChannelMessage") {
2085
2207
  const msg = update.message;
2086
- if (msg?.media?.className === "MessageMediaDice") {
2208
+ if (msg instanceof Api.Message && msg.media?.className === "MessageMediaDice") {
2087
2209
  value = msg.media.value;
2088
2210
  messageId = msg.id;
2089
2211
  break;
@@ -2127,7 +2249,7 @@ function createTelegramSDK(bridge, log) {
2127
2249
  timestamp: m.timestamp
2128
2250
  }));
2129
2251
  } catch (err) {
2130
- log.error("telegram.getMessages() failed:", err);
2252
+ log7.error("telegram.getMessages() failed:", err);
2131
2253
  return [];
2132
2254
  }
2133
2255
  },
@@ -2149,6 +2271,7 @@ function createTelegramSDK(bridge, log) {
2149
2271
  return bridge.isAvailable();
2150
2272
  },
2151
2273
  getRawClient() {
2274
+ log7.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
2152
2275
  if (!bridge.isAvailable()) return null;
2153
2276
  try {
2154
2277
  return bridge.getClient().getClient();
@@ -2157,13 +2280,13 @@ function createTelegramSDK(bridge, log) {
2157
2280
  }
2158
2281
  },
2159
2282
  // Spread extended methods from sub-modules
2160
- ...createTelegramMessagesSDK(bridge, log),
2161
- ...createTelegramSocialSDK(bridge, log)
2283
+ ...createTelegramMessagesSDK(bridge, log7),
2284
+ ...createTelegramSocialSDK(bridge, log7)
2162
2285
  };
2163
2286
  }
2164
2287
 
2165
2288
  // src/sdk/secrets.ts
2166
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3 } from "fs";
2289
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync5 } from "fs";
2167
2290
  import { join as join3 } from "path";
2168
2291
  var SECRETS_DIR = join3(TELETON_ROOT, "plugins", "data");
2169
2292
  function getSecretsPath(pluginName) {
@@ -2172,8 +2295,8 @@ function getSecretsPath(pluginName) {
2172
2295
  function readSecretsFile(pluginName) {
2173
2296
  const filePath = getSecretsPath(pluginName);
2174
2297
  try {
2175
- if (!existsSync3(filePath)) return {};
2176
- const raw = readFileSync3(filePath, "utf-8");
2298
+ if (!existsSync5(filePath)) return {};
2299
+ const raw = readFileSync4(filePath, "utf-8");
2177
2300
  const parsed = JSON.parse(raw);
2178
2301
  if (typeof parsed !== "object" || parsed === null) return {};
2179
2302
  return parsed;
@@ -2182,40 +2305,40 @@ function readSecretsFile(pluginName) {
2182
2305
  }
2183
2306
  }
2184
2307
  function writePluginSecret(pluginName, key, value) {
2185
- mkdirSync3(SECRETS_DIR, { recursive: true });
2308
+ mkdirSync3(SECRETS_DIR, { recursive: true, mode: 448 });
2186
2309
  const filePath = getSecretsPath(pluginName);
2187
2310
  const existing = readSecretsFile(pluginName);
2188
2311
  existing[key] = value;
2189
- writeFileSync3(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
2312
+ writeFileSync2(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
2190
2313
  }
2191
2314
  function deletePluginSecret(pluginName, key) {
2192
2315
  const existing = readSecretsFile(pluginName);
2193
2316
  if (!(key in existing)) return false;
2194
2317
  delete existing[key];
2195
2318
  const filePath = getSecretsPath(pluginName);
2196
- writeFileSync3(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
2319
+ writeFileSync2(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
2197
2320
  return true;
2198
2321
  }
2199
2322
  function listPluginSecretKeys(pluginName) {
2200
2323
  return Object.keys(readSecretsFile(pluginName));
2201
2324
  }
2202
- function createSecretsSDK(pluginName, pluginConfig, log) {
2325
+ function createSecretsSDK(pluginName, pluginConfig, log7) {
2203
2326
  const envPrefix = pluginName.replace(/-/g, "_").toUpperCase();
2204
2327
  function get(key) {
2205
2328
  const envKey = `${envPrefix}_${key.toUpperCase()}`;
2206
2329
  const envValue = process.env[envKey];
2207
2330
  if (envValue) {
2208
- log.debug(`Secret "${key}" resolved from env var ${envKey}`);
2331
+ log7.debug(`Secret "${key}" resolved from env var ${envKey}`);
2209
2332
  return envValue;
2210
2333
  }
2211
2334
  const stored = readSecretsFile(pluginName);
2212
2335
  if (key in stored && stored[key]) {
2213
- log.debug(`Secret "${key}" resolved from secrets store`);
2336
+ log7.debug(`Secret "${key}" resolved from secrets store`);
2214
2337
  return stored[key];
2215
2338
  }
2216
2339
  const configValue = pluginConfig[key];
2217
2340
  if (configValue !== void 0 && configValue !== null) {
2218
- log.debug(`Secret "${key}" resolved from pluginConfig`);
2341
+ log7.debug(`Secret "${key}" resolved from pluginConfig`);
2219
2342
  return String(configValue);
2220
2343
  }
2221
2344
  return void 0;
@@ -2307,38 +2430,61 @@ function createStorageSDK(db) {
2307
2430
  }
2308
2431
 
2309
2432
  // src/sdk/index.ts
2433
+ var sdkLog = createLogger("SDK");
2434
+ var BLOCKED_SQL_RE = /\b(ATTACH|DETACH)\s+DATABASE\b/i;
2435
+ function createSafeDb(db) {
2436
+ return new Proxy(db, {
2437
+ get(target, prop, receiver) {
2438
+ const value = Reflect.get(target, prop, receiver);
2439
+ if (prop === "exec") {
2440
+ return (sql) => {
2441
+ if (BLOCKED_SQL_RE.test(sql)) {
2442
+ throw new Error("ATTACH/DETACH DATABASE is not allowed in plugin context");
2443
+ }
2444
+ return target.exec(sql);
2445
+ };
2446
+ }
2447
+ if (prop === "prepare") {
2448
+ return (sql) => {
2449
+ if (BLOCKED_SQL_RE.test(sql)) {
2450
+ throw new Error("ATTACH/DETACH DATABASE is not allowed in plugin context");
2451
+ }
2452
+ return target.prepare(sql);
2453
+ };
2454
+ }
2455
+ return typeof value === "function" ? value.bind(target) : value;
2456
+ }
2457
+ });
2458
+ }
2310
2459
  function createPluginSDK(deps, opts) {
2311
- const log = createLogger(opts.pluginName);
2312
- const ton = Object.freeze(createTonSDK(log, opts.db));
2313
- const telegram = Object.freeze(createTelegramSDK(deps.bridge, log));
2314
- const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log));
2315
- const storage = opts.db ? Object.freeze(createStorageSDK(opts.db)) : null;
2316
- const frozenLog = Object.freeze(log);
2460
+ const log7 = createLogger2(opts.pluginName);
2461
+ const safeDb = opts.db ? createSafeDb(opts.db) : null;
2462
+ const ton = Object.freeze(createTonSDK(log7, safeDb));
2463
+ const telegram = Object.freeze(createTelegramSDK(deps.bridge, log7));
2464
+ const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log7));
2465
+ const storage = safeDb ? Object.freeze(createStorageSDK(safeDb)) : null;
2466
+ const frozenLog = Object.freeze(log7);
2317
2467
  const frozenConfig = Object.freeze(opts.sanitizedConfig);
2318
- const frozenPluginConfig = Object.freeze(opts.pluginConfig);
2468
+ const frozenPluginConfig = Object.freeze(JSON.parse(JSON.stringify(opts.pluginConfig ?? {})));
2319
2469
  return Object.freeze({
2320
2470
  version: SDK_VERSION,
2321
2471
  ton,
2322
2472
  telegram,
2323
2473
  secrets,
2324
2474
  storage,
2325
- db: opts.db,
2475
+ db: safeDb,
2326
2476
  config: frozenConfig,
2327
2477
  pluginConfig: frozenPluginConfig,
2328
2478
  log: frozenLog
2329
2479
  });
2330
2480
  }
2331
- function createLogger(pluginName) {
2332
- const prefix = `[${pluginName}]`;
2481
+ function createLogger2(pluginName) {
2482
+ const pinoChild = createLogger(`plugin:${pluginName}`);
2333
2483
  return {
2334
- info: (...args) => console.log(prefix, ...args),
2335
- warn: (...args) => console.warn(`\u26A0\uFE0F ${prefix}`, ...args),
2336
- error: (...args) => console.error(`\u274C ${prefix}`, ...args),
2337
- debug: (...args) => {
2338
- if (process.env.DEBUG || process.env.VERBOSE) {
2339
- console.log(`\u{1F50D} ${prefix}`, ...args);
2340
- }
2341
- }
2484
+ info: (...args) => pinoChild.info(args.map(String).join(" ")),
2485
+ warn: (...args) => pinoChild.warn(args.map(String).join(" ")),
2486
+ error: (...args) => pinoChild.error(args.map(String).join(" ")),
2487
+ debug: (...args) => pinoChild.debug(args.map(String).join(" "))
2342
2488
  };
2343
2489
  }
2344
2490
  function parseSemver(v) {
@@ -2358,22 +2504,22 @@ function semverGte(a, b) {
2358
2504
  function semverSatisfies(current, range) {
2359
2505
  const cur = parseSemver(current);
2360
2506
  if (!cur) {
2361
- console.warn(`\u26A0\uFE0F [SDK] Could not parse current version "${current}", skipping check`);
2362
- return true;
2507
+ sdkLog.warn(`[SDK] Could not parse current version "${current}", rejecting`);
2508
+ return false;
2363
2509
  }
2364
2510
  if (range.startsWith(">=")) {
2365
2511
  const req2 = parseSemver(range.slice(2));
2366
2512
  if (!req2) {
2367
- console.warn(`\u26A0\uFE0F [SDK] Malformed sdkVersion range "${range}", skipping check`);
2368
- return true;
2513
+ sdkLog.warn(`[SDK] Malformed sdkVersion range "${range}", rejecting`);
2514
+ return false;
2369
2515
  }
2370
2516
  return semverGte(cur, req2);
2371
2517
  }
2372
2518
  if (range.startsWith("^")) {
2373
2519
  const req2 = parseSemver(range.slice(1));
2374
2520
  if (!req2) {
2375
- console.warn(`\u26A0\uFE0F [SDK] Malformed sdkVersion range "${range}", skipping check`);
2376
- return true;
2521
+ sdkLog.warn(`[SDK] Malformed sdkVersion range "${range}", rejecting`);
2522
+ return false;
2377
2523
  }
2378
2524
  if (req2.major === 0) {
2379
2525
  return cur.major === 0 && cur.minor === req2.minor && semverGte(cur, req2);
@@ -2382,14 +2528,15 @@ function semverSatisfies(current, range) {
2382
2528
  }
2383
2529
  const req = parseSemver(range);
2384
2530
  if (!req) {
2385
- console.warn(`\u26A0\uFE0F [SDK] Malformed sdkVersion "${range}", skipping check`);
2386
- return true;
2531
+ sdkLog.warn(`[SDK] Malformed sdkVersion "${range}", rejecting`);
2532
+ return false;
2387
2533
  }
2388
2534
  return cur.major === req.major && cur.minor === req.minor && cur.patch === req.patch;
2389
2535
  }
2390
2536
 
2391
2537
  // src/agent/tools/plugin-loader.ts
2392
2538
  var execFileAsync = promisify(execFile);
2539
+ var log6 = createLogger("PluginLoader");
2393
2540
  var PLUGIN_DATA_DIR = join4(TELETON_ROOT, "plugins", "data");
2394
2541
  function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
2395
2542
  let manifest = null;
@@ -2397,17 +2544,16 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
2397
2544
  try {
2398
2545
  manifest = validateManifest(raw.manifest);
2399
2546
  } catch (err) {
2400
- console.warn(
2401
- `\u26A0\uFE0F [${entryName}] invalid manifest, ignoring:`,
2402
- err instanceof Error ? err.message : err
2547
+ log6.warn(
2548
+ `[${entryName}] invalid manifest, ignoring: ${err instanceof Error ? err.message : err}`
2403
2549
  );
2404
2550
  }
2405
2551
  }
2406
2552
  if (!manifest) {
2407
2553
  const manifestPath = join4(WORKSPACE_PATHS.PLUGINS_DIR, entryName, "manifest.json");
2408
2554
  try {
2409
- if (existsSync4(manifestPath)) {
2410
- const diskManifest = JSON.parse(readFileSync4(manifestPath, "utf-8"));
2555
+ if (existsSync6(manifestPath)) {
2556
+ const diskManifest = JSON.parse(readFileSync5(manifestPath, "utf-8"));
2411
2557
  if (diskManifest && typeof diskManifest.version === "string") {
2412
2558
  manifest = {
2413
2559
  name: entryName,
@@ -2439,12 +2585,13 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
2439
2585
  const pluginConfigKey = pluginName.replace(/-/g, "_");
2440
2586
  const rawPluginConfig = config.plugins?.[pluginConfigKey] ?? {};
2441
2587
  const pluginConfig = { ...manifest?.defaultConfig, ...rawPluginConfig };
2442
- const log = (...args) => console.log(`[${pluginName}]`, ...args);
2588
+ const pluginLog = createLogger(`Plugin:${pluginName}`);
2589
+ const logFn = (...args) => pluginLog.info(args.map(String).join(" "));
2443
2590
  if (manifest?.secrets) {
2444
2591
  const dummyLogger = {
2445
- info: log,
2446
- warn: (...a) => console.warn(`\u26A0\uFE0F [${pluginName}]`, ...a),
2447
- error: (...a) => console.error(`\u274C [${pluginName}]`, ...a),
2592
+ info: (...a) => pluginLog.info(a.map(String).join(" ")),
2593
+ warn: (...a) => pluginLog.warn(a.map(String).join(" ")),
2594
+ error: (...a) => pluginLog.error(a.map(String).join(" ")),
2448
2595
  debug: () => {
2449
2596
  }
2450
2597
  };
@@ -2458,8 +2605,8 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
2458
2605
  }
2459
2606
  }
2460
2607
  if (missing.length > 0) {
2461
- console.warn(
2462
- `\u26A0\uFE0F [${pluginName}] Missing required secrets:
2608
+ pluginLog.warn(
2609
+ `Missing required secrets:
2463
2610
  ` + missing.map((m) => ` \u2022 ${m}`).join("\n") + `
2464
2611
  Set via: /plugin set ${pluginName} <key> <value>`
2465
2612
  );
@@ -2492,10 +2639,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
2492
2639
  }
2493
2640
  }
2494
2641
  } catch (err) {
2495
- console.error(
2496
- `\u274C [${pluginName}] migrate() failed:`,
2497
- err instanceof Error ? err.message : err
2498
- );
2642
+ pluginLog.error(`migrate() failed: ${err instanceof Error ? err.message : err}`);
2499
2643
  if (pluginDb) {
2500
2644
  try {
2501
2645
  pluginDb.close();
@@ -2546,10 +2690,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
2546
2690
  };
2547
2691
  });
2548
2692
  } catch (err) {
2549
- console.error(
2550
- `\u274C [${pluginName}] tools() failed:`,
2551
- err instanceof Error ? err.message : err
2552
- );
2693
+ pluginLog.error(`tools() failed: ${err instanceof Error ? err.message : err}`);
2553
2694
  return [];
2554
2695
  }
2555
2696
  },
@@ -2561,24 +2702,18 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
2561
2702
  db: pluginDb ?? null,
2562
2703
  config: sanitizedConfig,
2563
2704
  pluginConfig,
2564
- log
2705
+ log: logFn
2565
2706
  };
2566
2707
  await raw.start(enhancedContext);
2567
2708
  } catch (err) {
2568
- console.error(
2569
- `\u274C [${pluginName}] start() failed:`,
2570
- err instanceof Error ? err.message : err
2571
- );
2709
+ pluginLog.error(`start() failed: ${err instanceof Error ? err.message : err}`);
2572
2710
  }
2573
2711
  },
2574
2712
  async stop() {
2575
2713
  try {
2576
2714
  await raw.stop?.();
2577
2715
  } catch (err) {
2578
- console.error(
2579
- `\u274C [${pluginName}] stop() failed:`,
2580
- err instanceof Error ? err.message : err
2581
- );
2716
+ pluginLog.error(`stop() failed: ${err instanceof Error ? err.message : err}`);
2582
2717
  } finally {
2583
2718
  if (pluginDb) {
2584
2719
  try {
@@ -2596,35 +2731,35 @@ async function ensurePluginDeps(pluginDir, pluginEntry) {
2596
2731
  const pkgJson = join4(pluginDir, "package.json");
2597
2732
  const lockfile = join4(pluginDir, "package-lock.json");
2598
2733
  const nodeModules = join4(pluginDir, "node_modules");
2599
- if (!existsSync4(pkgJson)) return;
2600
- if (!existsSync4(lockfile)) {
2601
- console.warn(
2602
- `\u26A0\uFE0F [${pluginEntry}] package.json without package-lock.json \u2014 skipping (lockfile required)`
2734
+ if (!existsSync6(pkgJson)) return;
2735
+ if (!existsSync6(lockfile)) {
2736
+ log6.warn(
2737
+ `[${pluginEntry}] package.json without package-lock.json \u2014 skipping (lockfile required)`
2603
2738
  );
2604
2739
  return;
2605
2740
  }
2606
- if (existsSync4(nodeModules)) {
2741
+ if (existsSync6(nodeModules)) {
2607
2742
  const marker = join4(nodeModules, ".package-lock.json");
2608
- if (existsSync4(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
2743
+ if (existsSync6(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
2609
2744
  }
2610
- console.log(`\u{1F4E6} [${pluginEntry}] Installing dependencies...`);
2745
+ log6.info(`[${pluginEntry}] Installing dependencies...`);
2611
2746
  try {
2612
2747
  await execFileAsync("npm", ["ci", "--ignore-scripts", "--no-audit", "--no-fund"], {
2613
2748
  cwd: pluginDir,
2614
2749
  timeout: 6e4,
2615
2750
  env: { ...process.env, NODE_ENV: "production" }
2616
2751
  });
2617
- console.log(`\u{1F4E6} [${pluginEntry}] Dependencies installed`);
2752
+ log6.info(`[${pluginEntry}] Dependencies installed`);
2618
2753
  } catch (err) {
2619
- console.error(`\u274C [${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
2754
+ log6.error(`[${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
2620
2755
  }
2621
2756
  }
2622
2757
  async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps) {
2623
2758
  const pluginsDir = WORKSPACE_PATHS.PLUGINS_DIR;
2624
- if (!existsSync4(pluginsDir)) {
2759
+ if (!existsSync6(pluginsDir)) {
2625
2760
  return [];
2626
2761
  }
2627
- const entries = readdirSync(pluginsDir);
2762
+ const entries = readdirSync2(pluginsDir);
2628
2763
  const modules = [];
2629
2764
  const loadedNames = /* @__PURE__ */ new Set();
2630
2765
  const pluginPaths = [];
@@ -2638,7 +2773,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps) {
2638
2773
  modulePath = entryPath;
2639
2774
  } else if (stat.isDirectory()) {
2640
2775
  const indexPath = join4(entryPath, "index.js");
2641
- if (existsSync4(indexPath)) {
2776
+ if (existsSync6(indexPath)) {
2642
2777
  modulePath = indexPath;
2643
2778
  }
2644
2779
  }
@@ -2661,156 +2796,402 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps) {
2661
2796
  );
2662
2797
  for (const result of loadResults) {
2663
2798
  if (result.status === "rejected") {
2664
- console.error(
2665
- `\u274C Plugin failed to load:`,
2666
- result.reason instanceof Error ? result.reason.message : result.reason
2799
+ log6.error(
2800
+ `Plugin failed to load: ${result.reason instanceof Error ? result.reason.message : result.reason}`
2667
2801
  );
2668
2802
  continue;
2669
2803
  }
2670
2804
  const { entry, mod } = result.value;
2671
2805
  try {
2672
2806
  if (!mod.tools || typeof mod.tools !== "function" && !Array.isArray(mod.tools)) {
2673
- console.warn(`\u26A0\uFE0F Plugin "${entry}": no 'tools' array or function exported, skipping`);
2807
+ log6.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
2674
2808
  continue;
2675
2809
  }
2676
2810
  const adapted = adaptPlugin(mod, entry, config, loadedModuleNames, sdkDeps);
2677
2811
  if (loadedNames.has(adapted.name)) {
2678
- console.warn(
2679
- `\u26A0\uFE0F Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`
2680
- );
2812
+ log6.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
2681
2813
  continue;
2682
2814
  }
2683
2815
  loadedNames.add(adapted.name);
2684
2816
  modules.push(adapted);
2685
2817
  } catch (err) {
2686
- console.error(
2687
- `\u274C Plugin "${entry}" failed to adapt:`,
2688
- err instanceof Error ? err.message : err
2689
- );
2818
+ log6.error(`Plugin "${entry}" failed to adapt: ${err instanceof Error ? err.message : err}`);
2690
2819
  }
2691
2820
  }
2692
2821
  return modules;
2693
2822
  }
2694
2823
 
2695
- // src/workspace/validator.ts
2696
- import { existsSync as existsSync5, lstatSync, readdirSync as readdirSync2 } from "fs";
2697
- import { resolve, normalize, relative, extname, basename } from "path";
2698
- import { homedir as homedir2 } from "os";
2699
- var WorkspaceSecurityError = class extends Error {
2700
- constructor(message, attemptedPath) {
2701
- super(message);
2702
- this.attemptedPath = attemptedPath;
2703
- this.name = "WorkspaceSecurityError";
2824
+ // src/config/configurable-keys.ts
2825
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7 } from "fs";
2826
+ import { parse as parse2, stringify as stringify2 } from "yaml";
2827
+ var noValidation = () => void 0;
2828
+ var identity = (v) => v;
2829
+ var nonEmpty = (v) => v.length > 0 ? void 0 : "Must not be empty";
2830
+ function numberInRange(min, max) {
2831
+ return (v) => {
2832
+ const n = Number(v);
2833
+ if (isNaN(n)) return "Must be a number";
2834
+ if (n < min || n > max) return `Must be between ${min} and ${max}`;
2835
+ return void 0;
2836
+ };
2837
+ }
2838
+ function enumValidator(options) {
2839
+ return (v) => options.includes(v) ? void 0 : `Must be one of: ${options.join(", ")}`;
2840
+ }
2841
+ var CONFIGURABLE_KEYS = {
2842
+ // ─── API Keys ──────────────────────────────────────────────────────
2843
+ "agent.api_key": {
2844
+ type: "string",
2845
+ category: "API Keys",
2846
+ description: "LLM provider API key",
2847
+ sensitive: true,
2848
+ validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
2849
+ mask: (v) => v.slice(0, 8) + "****",
2850
+ parse: identity
2851
+ },
2852
+ tavily_api_key: {
2853
+ type: "string",
2854
+ category: "API Keys",
2855
+ description: "Tavily API key for web search",
2856
+ sensitive: true,
2857
+ validate: (v) => v.startsWith("tvly-") ? void 0 : "Must start with 'tvly-'",
2858
+ mask: (v) => v.slice(0, 9) + "****",
2859
+ parse: identity
2860
+ },
2861
+ tonapi_key: {
2862
+ type: "string",
2863
+ category: "API Keys",
2864
+ description: "TonAPI key for higher rate limits",
2865
+ sensitive: true,
2866
+ validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
2867
+ mask: (v) => v.slice(0, 10) + "****",
2868
+ parse: identity
2869
+ },
2870
+ "telegram.bot_token": {
2871
+ type: "string",
2872
+ category: "API Keys",
2873
+ description: "Bot token from @BotFather",
2874
+ sensitive: true,
2875
+ validate: (v) => v.includes(":") ? void 0 : "Must contain ':' (e.g., 123456:ABC...)",
2876
+ mask: (v) => v.split(":")[0] + ":****",
2877
+ parse: identity
2878
+ },
2879
+ // ─── Agent ─────────────────────────────────────────────────────────
2880
+ "agent.provider": {
2881
+ type: "enum",
2882
+ category: "Agent",
2883
+ description: "LLM provider",
2884
+ sensitive: false,
2885
+ options: [
2886
+ "anthropic",
2887
+ "openai",
2888
+ "google",
2889
+ "xai",
2890
+ "groq",
2891
+ "openrouter",
2892
+ "moonshot",
2893
+ "mistral",
2894
+ "cocoon",
2895
+ "local"
2896
+ ],
2897
+ validate: enumValidator([
2898
+ "anthropic",
2899
+ "openai",
2900
+ "google",
2901
+ "xai",
2902
+ "groq",
2903
+ "openrouter",
2904
+ "moonshot",
2905
+ "mistral",
2906
+ "cocoon",
2907
+ "local"
2908
+ ]),
2909
+ mask: identity,
2910
+ parse: identity
2911
+ },
2912
+ "agent.model": {
2913
+ type: "string",
2914
+ category: "Agent",
2915
+ description: "Main LLM model ID",
2916
+ sensitive: false,
2917
+ validate: nonEmpty,
2918
+ mask: identity,
2919
+ parse: identity
2920
+ },
2921
+ "agent.utility_model": {
2922
+ type: "string",
2923
+ category: "Agent",
2924
+ description: "Cheap model for summarization (auto-detected if empty)",
2925
+ sensitive: false,
2926
+ validate: noValidation,
2927
+ mask: identity,
2928
+ parse: identity
2929
+ },
2930
+ "agent.temperature": {
2931
+ type: "number",
2932
+ category: "Agent",
2933
+ description: "Response creativity (0.0 = deterministic, 2.0 = max)",
2934
+ sensitive: false,
2935
+ validate: numberInRange(0, 2),
2936
+ mask: identity,
2937
+ parse: (v) => Number(v)
2938
+ },
2939
+ "agent.max_tokens": {
2940
+ type: "number",
2941
+ category: "Agent",
2942
+ description: "Maximum response length in tokens",
2943
+ sensitive: false,
2944
+ validate: numberInRange(256, 128e3),
2945
+ mask: identity,
2946
+ parse: (v) => Number(v)
2947
+ },
2948
+ "agent.max_agentic_iterations": {
2949
+ type: "number",
2950
+ category: "Agent",
2951
+ description: "Max tool-call loop iterations per message",
2952
+ sensitive: false,
2953
+ validate: numberInRange(1, 20),
2954
+ mask: identity,
2955
+ parse: (v) => Number(v)
2956
+ },
2957
+ // ─── Session ───────────────────────────────────────────────────
2958
+ "agent.session_reset_policy.daily_reset_enabled": {
2959
+ type: "boolean",
2960
+ category: "Session",
2961
+ description: "Enable daily session reset at specified hour",
2962
+ sensitive: false,
2963
+ validate: enumValidator(["true", "false"]),
2964
+ mask: identity,
2965
+ parse: (v) => v === "true"
2966
+ },
2967
+ "agent.session_reset_policy.daily_reset_hour": {
2968
+ type: "number",
2969
+ category: "Session",
2970
+ description: "Hour (0-23 UTC) for daily session reset",
2971
+ sensitive: false,
2972
+ validate: numberInRange(0, 23),
2973
+ mask: identity,
2974
+ parse: (v) => Number(v)
2975
+ },
2976
+ "agent.session_reset_policy.idle_expiry_enabled": {
2977
+ type: "boolean",
2978
+ category: "Session",
2979
+ description: "Enable automatic session expiry after idle period",
2980
+ sensitive: false,
2981
+ validate: enumValidator(["true", "false"]),
2982
+ mask: identity,
2983
+ parse: (v) => v === "true"
2984
+ },
2985
+ "agent.session_reset_policy.idle_expiry_minutes": {
2986
+ type: "number",
2987
+ category: "Session",
2988
+ description: "Idle minutes before session expires (minimum 1)",
2989
+ sensitive: false,
2990
+ validate: numberInRange(1, Number.MAX_SAFE_INTEGER),
2991
+ mask: identity,
2992
+ parse: (v) => Number(v)
2993
+ },
2994
+ // ─── Telegram ──────────────────────────────────────────────────────
2995
+ "telegram.bot_username": {
2996
+ type: "string",
2997
+ category: "Telegram",
2998
+ description: "Bot username without @",
2999
+ sensitive: false,
3000
+ validate: (v) => v.length >= 3 ? void 0 : "Must be at least 3 characters",
3001
+ mask: identity,
3002
+ parse: identity
3003
+ },
3004
+ "telegram.dm_policy": {
3005
+ type: "enum",
3006
+ category: "Telegram",
3007
+ description: "DM access policy",
3008
+ sensitive: false,
3009
+ options: ["pairing", "allowlist", "open", "disabled"],
3010
+ validate: enumValidator(["pairing", "allowlist", "open", "disabled"]),
3011
+ mask: identity,
3012
+ parse: identity
3013
+ },
3014
+ "telegram.group_policy": {
3015
+ type: "enum",
3016
+ category: "Telegram",
3017
+ description: "Group access policy",
3018
+ sensitive: false,
3019
+ options: ["open", "allowlist", "disabled"],
3020
+ validate: enumValidator(["open", "allowlist", "disabled"]),
3021
+ mask: identity,
3022
+ parse: identity
3023
+ },
3024
+ "telegram.require_mention": {
3025
+ type: "boolean",
3026
+ category: "Telegram",
3027
+ description: "Require @mention in groups to respond",
3028
+ sensitive: false,
3029
+ validate: enumValidator(["true", "false"]),
3030
+ mask: identity,
3031
+ parse: (v) => v === "true"
3032
+ },
3033
+ "telegram.owner_name": {
3034
+ type: "string",
3035
+ category: "Telegram",
3036
+ description: "Owner's first name (used in system prompt)",
3037
+ sensitive: false,
3038
+ validate: noValidation,
3039
+ mask: identity,
3040
+ parse: identity
3041
+ },
3042
+ "telegram.owner_username": {
3043
+ type: "string",
3044
+ category: "Telegram",
3045
+ description: "Owner's Telegram username (without @)",
3046
+ sensitive: false,
3047
+ validate: noValidation,
3048
+ mask: identity,
3049
+ parse: identity
3050
+ },
3051
+ "telegram.debounce_ms": {
3052
+ type: "number",
3053
+ category: "Telegram",
3054
+ description: "Group message debounce delay in ms (0 = disabled)",
3055
+ sensitive: false,
3056
+ validate: numberInRange(0, 1e4),
3057
+ mask: identity,
3058
+ parse: (v) => Number(v)
3059
+ },
3060
+ "telegram.agent_channel": {
3061
+ type: "string",
3062
+ category: "Telegram",
3063
+ description: "Channel username for auto-publishing",
3064
+ sensitive: false,
3065
+ validate: noValidation,
3066
+ mask: identity,
3067
+ parse: identity
3068
+ },
3069
+ "telegram.typing_simulation": {
3070
+ type: "boolean",
3071
+ category: "Telegram",
3072
+ description: "Simulate typing indicator before sending replies",
3073
+ sensitive: false,
3074
+ validate: enumValidator(["true", "false"]),
3075
+ mask: identity,
3076
+ parse: (v) => v === "true"
3077
+ },
3078
+ // ─── Embedding ─────────────────────────────────────────────────────
3079
+ "embedding.provider": {
3080
+ type: "enum",
3081
+ category: "Embedding",
3082
+ description: "Embedding provider for RAG",
3083
+ sensitive: false,
3084
+ options: ["local", "anthropic", "none"],
3085
+ validate: enumValidator(["local", "anthropic", "none"]),
3086
+ mask: identity,
3087
+ parse: identity
3088
+ },
3089
+ // ─── WebUI ─────────────────────────────────────────────────────────
3090
+ "webui.port": {
3091
+ type: "number",
3092
+ category: "WebUI",
3093
+ description: "HTTP server port (requires restart)",
3094
+ sensitive: false,
3095
+ validate: numberInRange(1024, 65535),
3096
+ mask: identity,
3097
+ parse: (v) => Number(v)
3098
+ },
3099
+ "webui.log_requests": {
3100
+ type: "boolean",
3101
+ category: "WebUI",
3102
+ description: "Log all HTTP requests to console",
3103
+ sensitive: false,
3104
+ validate: enumValidator(["true", "false"]),
3105
+ mask: identity,
3106
+ parse: (v) => v === "true"
3107
+ },
3108
+ // ─── Deals ─────────────────────────────────────────────────────────
3109
+ "deals.enabled": {
3110
+ type: "boolean",
3111
+ category: "Deals",
3112
+ description: "Enable the deals/escrow module",
3113
+ sensitive: false,
3114
+ validate: enumValidator(["true", "false"]),
3115
+ mask: identity,
3116
+ parse: (v) => v === "true"
3117
+ },
3118
+ // ─── Developer ─────────────────────────────────────────────────────
3119
+ "dev.hot_reload": {
3120
+ type: "boolean",
3121
+ category: "Developer",
3122
+ description: "Watch ~/.teleton/plugins/ for live changes",
3123
+ sensitive: false,
3124
+ validate: enumValidator(["true", "false"]),
3125
+ mask: identity,
3126
+ parse: (v) => v === "true"
2704
3127
  }
2705
3128
  };
2706
- function decodeRecursive(str) {
2707
- let decoded = str;
2708
- let prev = "";
2709
- let iterations = 0;
2710
- const maxIterations = 10;
2711
- while (decoded !== prev && iterations < maxIterations) {
2712
- prev = decoded;
2713
- try {
2714
- decoded = decodeURIComponent(decoded);
2715
- } catch {
2716
- break;
2717
- }
2718
- iterations++;
3129
+ var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
3130
+ function assertSafePath(parts) {
3131
+ if (parts.some((p) => FORBIDDEN_SEGMENTS.has(p))) {
3132
+ throw new Error("Invalid config path: forbidden segment");
2719
3133
  }
2720
- return decoded;
2721
3134
  }
2722
- function validatePath(inputPath, allowCreate = false) {
2723
- if (!inputPath || inputPath.trim() === "") {
2724
- throw new WorkspaceSecurityError("Path cannot be empty.", inputPath);
2725
- }
2726
- const trimmedPath = inputPath.trim().replace(/\\/g, "/");
2727
- const decodedPath = decodeRecursive(trimmedPath);
2728
- let absolutePath;
2729
- if (decodedPath.startsWith("/")) {
2730
- absolutePath = resolve(normalize(decodedPath));
2731
- } else if (decodedPath.startsWith("~/")) {
2732
- const expanded = decodedPath.replace(/^~(?=$|[\\/])/, homedir2());
2733
- absolutePath = resolve(expanded);
2734
- } else {
2735
- absolutePath = resolve(WORKSPACE_ROOT, normalize(decodedPath));
2736
- }
2737
- const relativePath = relative(WORKSPACE_ROOT, absolutePath);
2738
- if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
2739
- throw new WorkspaceSecurityError(
2740
- `Access denied: Path '${inputPath}' is outside the workspace. Only files in ~/.teleton/workspace/ are accessible.`,
2741
- inputPath
2742
- );
2743
- }
2744
- const exists = existsSync5(absolutePath);
2745
- if (!exists && !allowCreate) {
2746
- throw new WorkspaceSecurityError(
2747
- `File not found: '${inputPath}' does not exist in workspace.`,
2748
- inputPath
2749
- );
2750
- }
2751
- if (exists) {
2752
- const stats = lstatSync(absolutePath);
2753
- if (stats.isSymbolicLink()) {
2754
- throw new WorkspaceSecurityError(
2755
- `Access denied: Symbolic links are not allowed for security reasons.`,
2756
- inputPath
2757
- );
3135
+ function getNestedValue(obj, path) {
3136
+ const parts = path.split(".");
3137
+ assertSafePath(parts);
3138
+ let current = obj;
3139
+ for (const part of parts) {
3140
+ if (current == null || typeof current !== "object") return void 0;
3141
+ current = current[part];
3142
+ }
3143
+ return current;
3144
+ }
3145
+ function setNestedValue(obj, path, value) {
3146
+ const parts = path.split(".");
3147
+ assertSafePath(parts);
3148
+ let current = obj;
3149
+ for (let i = 0; i < parts.length - 1; i++) {
3150
+ if (current[parts[i]] == null || typeof current[parts[i]] !== "object") {
3151
+ current[parts[i]] = {};
2758
3152
  }
3153
+ current = current[parts[i]];
2759
3154
  }
2760
- return {
2761
- absolutePath,
2762
- relativePath,
2763
- exists,
2764
- isDirectory: exists ? lstatSync(absolutePath).isDirectory() : false,
2765
- extension: extname(absolutePath).toLowerCase(),
2766
- filename: basename(absolutePath)
2767
- };
3155
+ current[parts[parts.length - 1]] = value;
2768
3156
  }
2769
- function validateReadPath(inputPath) {
2770
- const validated = validatePath(inputPath, false);
2771
- if (validated.isDirectory) {
2772
- throw new WorkspaceSecurityError(`Cannot read directory as file: '${inputPath}'`, inputPath);
3157
+ function deleteNestedValue(obj, path) {
3158
+ const parts = path.split(".");
3159
+ assertSafePath(parts);
3160
+ let current = obj;
3161
+ for (let i = 0; i < parts.length - 1; i++) {
3162
+ if (current == null || typeof current !== "object") return;
3163
+ current = current[parts[i]];
3164
+ }
3165
+ if (current != null && typeof current === "object") {
3166
+ delete current[parts[parts.length - 1]];
2773
3167
  }
2774
- return validated;
2775
3168
  }
2776
- var IMMUTABLE_FILES = ["SOUL.md", "STRATEGY.md", "SECURITY.md"];
2777
- function validateWritePath(inputPath, fileType) {
2778
- const validated = validatePath(inputPath, true);
2779
- if (IMMUTABLE_FILES.includes(validated.filename)) {
2780
- throw new WorkspaceSecurityError(
2781
- `Cannot write to ${validated.filename}. This file is configured by the owner. Use memory_write instead.`,
2782
- inputPath
2783
- );
3169
+ function readRawConfig(configPath) {
3170
+ const fullPath = expandPath(configPath);
3171
+ if (!existsSync7(fullPath)) {
3172
+ throw new Error(`Config file not found: ${fullPath}
3173
+ Run 'teleton setup' to create one.`);
2784
3174
  }
2785
- if (fileType && ALLOWED_EXTENSIONS[fileType]) {
2786
- const allowedExts = ALLOWED_EXTENSIONS[fileType];
2787
- if (!allowedExts.includes(validated.extension)) {
2788
- throw new WorkspaceSecurityError(
2789
- `Invalid file type: '${validated.extension}' is not allowed for ${fileType}. Allowed: ${allowedExts.join(", ")}`,
2790
- inputPath
2791
- );
2792
- }
3175
+ const raw = parse2(readFileSync6(fullPath, "utf-8"));
3176
+ if (!raw || typeof raw !== "object") {
3177
+ throw new Error(`Invalid config file: ${fullPath}`);
2793
3178
  }
2794
- return validated;
3179
+ return raw;
2795
3180
  }
2796
- function validateDirectory(inputPath) {
2797
- const validated = validatePath(inputPath, true);
2798
- if (validated.exists && !validated.isDirectory) {
2799
- throw new WorkspaceSecurityError(
2800
- `Path exists but is not a directory: '${inputPath}'`,
2801
- inputPath
2802
- );
3181
+ function writeRawConfig(raw, configPath) {
3182
+ const clone = { ...raw };
3183
+ delete clone.market;
3184
+ const result = ConfigSchema.safeParse(clone);
3185
+ if (!result.success) {
3186
+ throw new Error(`Refusing to save invalid config: ${result.error.message}`);
2803
3187
  }
2804
- return validated;
3188
+ raw.meta = raw.meta ?? {};
3189
+ raw.meta.last_modified_at = (/* @__PURE__ */ new Date()).toISOString();
3190
+ const fullPath = expandPath(configPath);
3191
+ writeFileSync3(fullPath, stringify2(raw), { encoding: "utf-8", mode: 384 });
2805
3192
  }
2806
3193
 
2807
3194
  export {
2808
- DealsConfigSchema,
2809
- ConfigSchema,
2810
- getProviderMetadata,
2811
- getSupportedProviders,
2812
- validateApiKeyFormat,
2813
- expandPath,
2814
3195
  loadConfig,
2815
3196
  configExists,
2816
3197
  getDefaultConfigPath,
@@ -2819,21 +3200,27 @@ export {
2819
3200
  validateReadPath,
2820
3201
  validateWritePath,
2821
3202
  validateDirectory,
2822
- generateWallet,
2823
- saveWallet,
2824
- loadWallet,
2825
- walletExists,
2826
- importWallet,
2827
- getWalletAddress,
2828
- getKeyPair,
2829
- getWalletBalance,
2830
- getTonPrice,
3203
+ appendToDailyLog,
3204
+ writeSummaryToDailyLog,
3205
+ sanitizeForPrompt,
3206
+ sanitizeForContext,
3207
+ clearPromptCache,
3208
+ loadSoul,
3209
+ buildSystemPrompt,
2831
3210
  writePluginSecret,
2832
3211
  deletePluginSecret,
2833
3212
  listPluginSecretKeys,
3213
+ toLong,
3214
+ randomLong,
2834
3215
  withBlockchainRetry,
2835
3216
  sendTon,
2836
3217
  adaptPlugin,
2837
3218
  ensurePluginDeps,
2838
- loadEnhancedPlugins
3219
+ loadEnhancedPlugins,
3220
+ CONFIGURABLE_KEYS,
3221
+ getNestedValue,
3222
+ setNestedValue,
3223
+ deleteNestedValue,
3224
+ readRawConfig,
3225
+ writeRawConfig
2839
3226
  };