tg-agent 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +14 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)}function
|
|
3
|
-
`)}function
|
|
4
|
-
`)
|
|
5
|
-
`)}async function Q(e,t,n){return n?await y.sendMessage(e,t,{parse_mode:n}):await y.sendMessage(e,t)}async function I(e,t,n,o){if(o){await y.editMessageText(n,{chat_id:e,message_id:t,parse_mode:o});return}await y.editMessageText(n,{chat_id:e,message_id:t})}async function ee(e,t){let n;for(const o of t)try{return await e(o)}catch(s){n=s;const a=s instanceof Error?s.message:String(s);console.warn(`[tg-agent] parse_mode ${o} failed, trying fallback: ${a}`)}try{return await e(void 0)}catch(o){if(n){const s=o instanceof Error?o.message:String(o);console.warn(`[tg-agent] parse_mode fallback to plain failed: ${s}`)}throw o}}async function et(e,t){return await ee(n=>Q(e,t,n),V(t))}async function tt(e,t,n){await ee(o=>I(e,t,n,o),V(n))}async function l(e,t){const n=ve(t,3900);for(const o of n)await et(e,o)}const nt=3900,ot=1200;function N(e,t=nt){return e.length<=t?{text:e,truncated:!1}:{text:`${e.slice(0,Math.max(0,t-15))}...
|
|
1
|
+
import N from"node:path";import W from"node:fs/promises";import me from"node-telegram-bot-api";import{discoverAuthStorage as fe,discoverModels as ge}from"@mariozechner/pi-coding-agent";import{getOAuthProviders as pe}from"@mariozechner/pi-ai";import{config as m,assertConfig as we}from"./config.js";import{resolveApiKeyForProvider as he,resolveProxyInfo as ye}from"./auth.js";import{applyFetchProxy as $e}from"./proxy.js";import{runPiAgentPrompt as ve}from"./piAgentRunner.js";import{readCodexOAuth as Me}from"./codexAuth.js";import{formatMcpTarget as Se,freezeMcpCatalog as X,getMcpCatalog as H,loadMcpServers as _e,loadMcpServersSync as Ae,probeMcpServer as Pe,refreshMcpCatalog as Te}from"./mcp.js";import{appendMessage as K,closeSession as xe,createSession as O,deleteSessionFile as b,getActiveSession as C,listSessions as V,loadUserState as q,pruneExpiredSessions as G,resetSession as Ee,saveUserState as y,setActiveSession as Ce}from"./sessionStore.js";import{chunkText as ke,createQueueMap as Re,createSemaphore as be,ensureDir as U,nowMs as _,shortId as Q}from"./utils.js";we();const p=new me(m.telegramToken,{polling:!0}),J=Re(),De=be(m.maxConcurrent),z=new Map;function Le(e,t){z.set(e,t)}function Ne(e){z.delete(e)}function Oe(e){const t=e.trim();if(t){if(t==="Markdown"||t==="MarkdownV2"||t==="HTML")return t;console.warn(`[tg-agent] invalid TELEGRAM_PARSE_MODE=${t}, ignoring`)}}const Ue="image/jpeg";function ze(){return N.join(m.agentDir,"uploads")}function je(e){return e.length===0?null:e.reduce((t,n)=>{const o=t.file_size??t.width*t.height;return(n.file_size??n.width*n.height)>o?n:t})}function Be(e){return!!e?.startsWith("image/")}function Fe(e){switch(N.extname(e).toLowerCase()){case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".webp":return"image/webp";case".gif":return"image/gif";default:return}}function We(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB"];let n=e,o=0;for(;n>=1024&&o<t.length-1;)n/=1024,o+=1;return`${n.toFixed(n>=10||o===0?0:1)} ${t[o]}`}function Xe(e){return e.length===0?"":e.map(t=>{const n=[];t.mimeType&&n.push(t.mimeType),t.bytes>0&&n.push(We(t.bytes));const o=n.length>0?` (${n.join(", ")})`:"";return`Attachment saved: ${t.path}${o}`}).join(`
|
|
2
|
+
`)}function He(e){const t=[];if(e.photo&&e.photo.length>0){const n=je(e.photo);n&&t.push({kind:"photo",fileId:n.file_id,fileSize:n.file_size})}return e.document&&t.push({kind:"document",fileId:e.document.file_id,fileName:e.document.file_name,mimeType:e.document.mime_type,fileSize:e.document.file_size}),t}async function Ke(e){if(e.length===0)return{resolved:[],images:[]};const t=ze();await U(t);const n=[],o=[];for(const r of e)try{const a=await p.downloadFile(r.fileId,t),s=await W.stat(a),i=r.fileName??N.basename(a),l=r.mimeType??Fe(a),c=r.kind==="photo"||Be(l),u={path:a,name:i,mimeType:l,bytes:s.size,isImage:c};if(n.push(u),c){const f=await W.readFile(a);o.push({type:"image",data:f.toString("base64"),mimeType:l??Ue})}}catch(a){console.warn("[tg-agent] attachment download failed",a)}return{resolved:n,images:o}}const Ve=new Set(["_","*","[","]","(",")","~","`",">","#","+","-","=","|","{","}",".","!"]);function k(e,t){let n=0;for(let o=0;o<e.length;o+=1){const r=e[o];if(r==="\\"){o+=1;continue}r===t&&(n+=1)}return n}function qe(e){if(k(e,"*")%2!==0||k(e,"_")%2!==0||k(e,"`")%2!==0)return!1;const r=k(e,"["),a=k(e,"]");return r===a}function Ge(e){for(let t=0;t<e.length;t+=1){const n=e[t];if(n==="\\"){t+=1;continue}if(Ve.has(n))return!1}return!0}function Y(e){const t=Oe(m.telegramParseMode);return t?[t]:Ge(e)?["MarkdownV2","Markdown"]:qe(e)?["Markdown"]:[]}const Qe=600*1e3,S=new Map,j=new Set,Je=new Map([["codex","openai-codex"],["antigravity","google-antigravity"],["gemini","google-gemini-cli"],["gemini-cli","google-gemini-cli"]]),Ye={"codex-mini-latest":"gpt-5.1-codex-mini","codex-max-latest":"gpt-5.1-codex-max","codex-latest":"gpt-5.2-codex"};function T(e){const t=e.trim().toLowerCase();return Je.get(t)??t}function Ze(e,t){return e!=="openai-codex"?t:Ye[t]??t}async function x(){await U(m.agentDir);const e=fe(m.agentDir),t=Me();t&&e.setRuntimeApiKey("openai-codex",t.accessToken);const n=ge(e,m.agentDir);return{authStorage:e,modelRegistry:n}}function Z(e){const t=S.get(e);t&&(clearTimeout(t.timeoutId),S.delete(e))}function Ie(e,t){const n=S.get(e);n&&(clearTimeout(n.timeoutId),S.delete(e),n.reject(new Error(t)))}async function I(e,t,n){if(S.has(e))throw new Error("Login is already awaiting input.");const o=n.placeholder?`${n.message} (${n.placeholder})`:n.message;return await d(t,o),await new Promise((r,a)=>{const s=setTimeout(()=>{S.delete(e),a(new Error("Login prompt timed out."))},Qe);S.set(e,{resolve:r,reject:a,timeoutId:s,chatId:t})})}async function ee(e,t){const n=z.get(t);if(!n){await d(e,"No active request to stop.");return}n.cancelRequested=!0,n.abortRequested=!0,n.status&&n.status.update("Cancelled by user.",!0),n.abort&&n.abort(),(!n.status||n.chatId!==e)&&await d(e,"Stopping current request...")}function et(){const e={https_proxy:process.env.https_proxy??process.env.HTTPS_PROXY??"",http_proxy:process.env.http_proxy??process.env.HTTP_PROXY??"",all_proxy:process.env.all_proxy??process.env.ALL_PROXY??"",no_proxy:process.env.no_proxy??process.env.NO_PROXY??""},t={NODE_EXTRA_CA_CERTS:process.env.NODE_EXTRA_CA_CERTS??"",NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED??""};console.log(`[tg-agent] modelProvider=${m.modelProvider} modelRef=${m.modelRef} sessionDir=${m.sessionDir} maxConcurrent=${m.maxConcurrent}`),console.log(`[tg-agent] agentDir=${m.agentDir} workspaceDir=${m.workspaceDir}`);const n=Ae(m.agentDir),o=n.length>0?`on (${n.length})`:"off";console.log(`[tg-agent] tools fetchMaxBytes=${m.fetchMaxBytes} fetchTimeoutMs=${m.fetchTimeoutMs} mcp=${o}`),console.log(`[tg-agent] proxy https=${e.https_proxy||"(empty)"} http=${e.http_proxy||"(empty)"} all=${e.all_proxy||"(empty)"} no=${e.no_proxy||"(empty)"}`),console.log(`[tg-agent] tls extra_ca=${t.NODE_EXTRA_CA_CERTS||"(empty)"} reject_unauthorized=${t.NODE_TLS_REJECT_UNAUTHORIZED||"(empty)"}`);try{const{source:a}=he(m.modelProvider);console.log(`[tg-agent] authSource=${a}`)}catch(a){const s=a instanceof Error?a.message:String(a);console.warn(`[tg-agent] authSource=missing (${s})`)}const r=ye();console.log(r?`[tg-agent] proxyUrl=${r.url} kind=${r.kind} source=${r.source}`:"[tg-agent] proxyUrl=(none)")}et(),U(m.agentDir).catch(e=>{console.warn(`[tg-agent] ensure agentDir failed: ${e instanceof Error?e.message:String(e)}`)}),(async()=>{try{await H(m.agentDir,{timeoutMs:3e3,maxBytes:m.fetchMaxBytes,maxChars:2e3}),X()}catch(e){const t=e instanceof Error?e.message:String(e);console.warn(`[tg-agent] mcp catalog warmup failed: ${t}`)}})();const D=$e();console.log(D?`[tg-agent] fetchProxy=${D.url} kind=${D.kind} source=${D.source}`:"[tg-agent] fetchProxy=(none)");function B(e){if(!e.startsWith("/"))return null;const t=e.trim(),[n,...o]=t.split(" ");return{command:n.split("@")[0].slice(1).toLowerCase(),args:o.join(" ").trim()}}async function te(e,t){let n=C(e);return n||(n=O(e,""),await y(e),await d(t,`Created session ${n.id}.`)),n}function tt(e,t){const n=e.getAll(),o=new Map;for(const r of n){const a=o.get(r.provider)??{count:0,auth:t.hasAuth(r.provider)};a.count+=1,o.set(r.provider,a)}return Array.from(o.entries()).sort((r,a)=>r[0].localeCompare(a[0])).map(([r,a])=>({label:`${r} (${a.count}, ${a.auth?"ok":"no auth"})`,command:`/provider ${r}`}))}function nt(e,t,n=[]){const o=e.getAll().filter(i=>i.provider===t).sort((i,l)=>i.id.localeCompare(l.id));if(n.length===0)return o.map(i=>({label:i.id,command:`/model ${t}/${i.id}`}));const r=new Map(o.map(i=>[i.id,i])),a=new Set,s=[];for(const i of n){const l=r.get(i);!l||a.has(l.id)||(a.add(l.id),s.push(l))}for(const i of o)a.has(i.id)||s.push(i);return s.map(i=>({label:i.id,command:`/model ${t}/${i.id}`}))}function ot(e,t,n=3){const o=new Set,r=[],a=V(e);for(const s of a){if(s.modelProvider!==t)continue;const i=s.modelId;if(!(!i||o.has(i))&&(o.add(i),r.push(i),r.length>=n))break}return r}function rt(e){const t=e.trim();if(!t)return null;if(t.includes("/")){const[n,o]=t.split("/",2);return!n||!o?null:{provider:T(n),modelId:o.trim()}}return{modelId:t}}const st=6e4,ne=new Map;function oe(e,t){const n=Date.now(),o=ne.get(e)??0;n-o<st||(ne.set(e,n),console.warn(`[tg-agent] unauthorized user=${e} chat=${t}`))}function re(e){const t=m.telegramAllowedUsers;return!t||t.size===0?!0:t.has(e)}function it(e){return e.length===0?"No sessions found.":["Sessions:",...e.map(n=>{const o=new Date(n.updatedAt).toISOString();return`- ${n.id} | ${n.title} | updated ${o}`})].join(`
|
|
3
|
+
`)}async function se(e,t,n){return n?await p.sendMessage(e,t,{parse_mode:n}):await p.sendMessage(e,t)}async function ie(e,t,n,o){if(o){await p.editMessageText(n,{chat_id:e,message_id:t,parse_mode:o});return}await p.editMessageText(n,{chat_id:e,message_id:t})}async function ae(e,t){let n;for(const o of t)try{return await e(o)}catch(r){n=r;const a=r instanceof Error?r.message:String(r);console.warn(`[tg-agent] parse_mode ${o} failed, trying fallback: ${a}`)}try{return await e(void 0)}catch(o){if(n){const r=o instanceof Error?o.message:String(o);console.warn(`[tg-agent] parse_mode fallback to plain failed: ${r}`)}throw o}}async function at(e,t){return await ae(n=>se(e,t,n),Y(t))}async function ct(e,t,n){await ae(o=>ie(e,t,n,o),Y(n))}async function d(e,t){const n=ke(t,3900);for(const o of n)await at(e,o)}const ce=1800*1e3,le=2e3,lt=60,$=new Map;function dt(){const e=_()-ce;for(const[o,r]of $)r.createdAt<e&&$.delete(o);if($.size<=le)return;const t=Array.from($.entries()).sort((o,r)=>o[1].createdAt-r[1].createdAt),n=$.size-le;for(let o=0;o<n;o+=1)$.delete(t[o][0])}function ut(e){dt();let t=Q();for(;$.has(t);)t=Q();return $.set(t,{command:e,createdAt:_()}),t}function mt(e){if(!e.startsWith("cmd:"))return null;const t=e.slice(4),n=$.get(t);return n?_()-n.createdAt>ce?($.delete(t),null):n.command:null}function ft(e,t){if(e.length<=t)return[e];const n=[];for(let o=0;o<e.length;o+=t)n.push(e.slice(o,o+t));return n}function gt(e,t){const n=[];let o=[];for(const r of e){const a=ut(r.command);o.push({text:r.label,callback_data:`cmd:${a}`}),o.length>=t&&(n.push(o),o=[])}return o.length>0&&n.push(o),n}async function pt(e,t,n){return await p.sendMessage(e,t,{reply_markup:{inline_keyboard:n}})}async function de(e,t,n,o){const r=o?.perRow??1,a=o?.pageSize??lt,s=o?.footer,i=ft(n,a);for(let l=0;l<i.length;l+=1){const c=i[l],u=i.length>1?` (page ${l+1}/${i.length})`:"",f=[`${t}${u}`];s&&f.push(s);const g=gt(c,r);await pt(e,f.join(`
|
|
4
|
+
`),g)}}const wt=3900,ht=1200;function F(e,t=wt){return e.length<=t?{text:e,truncated:!1}:{text:`${e.slice(0,Math.max(0,t-15))}...
|
|
6
5
|
|
|
7
|
-
[truncated]`,truncated:!0}}async function
|
|
8
|
-
args: ${
|
|
9
|
-
`)}async function
|
|
10
|
-
`);await
|
|
6
|
+
[truncated]`,truncated:!0}}async function yt(e){const t="Working...",o=(await se(e,t)).message_id;let r=t,a=0,s=Promise.resolve();const i=(c,u=!1)=>{const f=Date.now();!u&&f-a<ht||c!==r&&(r=c,a=f,s=s.then(async()=>{await ie(e,o,c)}).catch(g=>{console.warn("[tg-agent] status edit failed",g)}))},l=c=>{r=c,a=Date.now(),s=s.then(async()=>{await ct(e,o,c)}).catch(u=>{console.warn("[tg-agent] status edit failed",u)})};return{update:(c,u=!1)=>{const f=F(c).text;i(f,u)},finalize:async c=>{const u=F(c).text;l(u),await s},fail:async c=>{const u=F(c).text;l(u),await s}}}function $t(e){switch(e.type){case"agent_start":return"Working...";case"tool_start":return`Running tool: ${e.name}
|
|
7
|
+
args: ${vt(e.args)}`;case"tool_end":return e.ok?`Tool finished: ${e.name} (${e.durationMs}ms)`:`Tool failed: ${e.name} (${e.durationMs}ms)`;case"message_start":return e.role==="assistant"?"Generating reply...":null;case"message_end":return e.role==="assistant"?"Finalizing reply...":null;case"heartbeat":return`Working... ${Math.max(1,Math.round(e.elapsedMs/1e3))}s`;default:return null}}function vt(e){try{const t=new WeakSet,n=200,o=12,r=12,a=3,s=(c,u)=>{if(c==null)return c;if(typeof c=="string")return c.length>n?`${c.slice(0,n)}...`:c;if(typeof c=="number"||typeof c=="boolean")return c;if(typeof c=="bigint")return c.toString();if(typeof c=="function")return"[function]";if(typeof c!="object")return String(c);if(t.has(c))return"[circular]";if(u>=a)return"[truncated]";if(t.add(c),Array.isArray(c)){const A=c.slice(0,r).map(w=>s(w,u+1));return c.length>r&&A.push("[truncated]"),A}const f=Object.entries(c),g={};for(const[A,w]of f.slice(0,o))g[A]=s(w,u+1);return f.length>o&&(g._truncated=!0),g},i=s(e,0),l=JSON.stringify(i);return l?l.length>700?`${l.slice(0,700)}...`:l:"null"}catch{return"[unavailable]"}}function L(e){const t=e instanceof Error?e.message:String(e),n=e?.cause;return n instanceof Error?`${t} (${n.message})`:n?`${t} (${String(n)})`:t}function Mt(e,t=140){return e.length<=t?e:`${e.slice(0,t-3)}...`}async function St(){const e=await _e(m.agentDir);if(e.length===0)return"No MCP servers configured. Add [mcp_servers.*] to ~/.tg-agent/config.toml.";const t=await Promise.all(e.map(o=>Pe(o))),n=["MCP servers:"];for(let o=0;o<e.length;o+=1){const r=e[o],a=t[o],s=a.ok?"ok":`error: ${Mt(a.error??"unknown")}`;n.push(`- ${r.name} (${r.type}) ${Se(r)} status=${s} (${a.durationMs}ms)`)}return n.join(`
|
|
8
|
+
`)}async function ue(e,t,n,o){const r=await q(t),a=G(r);switch(a.length>0&&(await y(r),await Promise.all(a.map(s=>b(t,s).catch(i=>{console.warn(`[tg-agent] cleanup session file failed id=${s}`,i)})))),n){case"start":case"help":{const s=["Commands:","/new [title] - create a new session","/list - list sessions","/use <id> - switch active session","/close [id] - close a session (default: active)","/reset - clear active session history","/providers - list available providers","/models [provider] - list models for provider","/provider <name> - set provider for current session","/model <provider>/<model> - set model for current session","/mcp - list configured MCP servers","/mcp refresh - reload MCP catalog","/status - show session model settings","/login <provider> - login to OAuth provider","/logout <provider> - logout from provider","/stop - stop the current running request","","Tips:","Send images or files to attach them to the prompt."].join(`
|
|
9
|
+
`);await d(e,s);return}case"new":{try{const i=O(r,o||"");await y(r),await d(e,`Created session ${i.id} (${i.title}).`)}catch(s){await d(e,s.message)}return}case"list":{await y(r),await d(e,it(V(r)));return}case"use":{if(!o){await d(e,"Usage: /use <id>");return}if(!Ce(r,o)){await d(e,`Session not found: ${o}`);return}await y(r),await d(e,`Active session set to ${o}.`);return}case"close":{const s=o||r.activeSessionId;if(!s){await d(e,"No active session to close.");return}if(!xe(r,s)){await d(e,`Session not found: ${s}`);return}await y(r),await b(t,s).catch(l=>{console.warn(`[tg-agent] delete session file failed id=${s}`,l)}),await d(e,`Closed session ${s}.`);return}case"reset":{const s=C(r);if(!s){await d(e,"No active session.");return}Ee(s),await y(r),await b(t,s.id).catch(i=>{console.warn(`[tg-agent] reset session file failed id=${s.id}`,i)}),await d(e,`Reset session ${s.id}.`);return}case"providers":{const{authStorage:s,modelRegistry:i}=await x(),l=i.getError(),c=tt(i,s);if(c.length===0){const f=l?`Warning: ${l}
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
`)
|
|
14
|
-
`)
|
|
15
|
-
`))}
|
|
11
|
+
No providers found.`:"No providers found.";await d(e,f);return}const u=[];l&&u.push(`Warning: ${l}`),u.push("Providers:"),await de(e,u.join(`
|
|
12
|
+
`),c,{perRow:1,footer:"Tap a provider to set it. Use /models <provider> to list models."});return}case"models":{const{authStorage:s,modelRegistry:i}=await x();let l=o?T(o):"";if(!l){const g=C(r);if(!g?.modelProvider){await d(e,"Usage: /models <provider>");return}l=g.modelProvider}const c=s.hasAuth(l),u=ot(r,l),f=nt(i,l,u);if(f.length===0){await d(e,`No models found for provider ${l}.`);return}await de(e,`Models for ${l} (auth: ${c?"ok":"missing"}):`,f,{perRow:1,footer:"Tap a model to set it."});return}case"provider":{if(!o){await d(e,"Usage: /provider <name>");return}const s=T(o),{modelRegistry:i}=await x();if(!i.getAll().some(u=>u.provider===s)){await d(e,`Unknown provider: ${s}`);return}const c=await te(r,e);c.modelProvider=s,c.modelId=void 0,c.updatedAt=_(),await y(r),await d(e,`Session ${c.id} provider set to ${s}.`);return}case"model":{const s=rt(o);if(!s){await d(e,"Usage: /model <provider>/<model>");return}const i=await te(r,e),l=T(s.provider??i.modelProvider??"");if(!l){await d(e,"Usage: /model <provider>/<model>");return}const{modelRegistry:c}=await x(),u=Ze(l,s.modelId);if(!c.find(l,u)){await d(e,`Model not found: ${l}/${u}`);return}i.modelProvider=l,i.modelId=u,i.updatedAt=_(),await y(r),await d(e,`Session ${i.id} model set to ${l}/${u}.`);return}case"status":{const s=C(r);if(!s){await d(e,"No active session.");return}const i=m.modelRef.includes("/")?m.modelRef:`${m.modelProvider}/${m.modelRef}`,l=s.modelProvider||s.modelId?`Session model: ${s.modelProvider??"?"}/${s.modelId??"?"}`:`Default model: ${i}`,c=[`Session: ${s.id} (${s.title})`,l].join(`
|
|
13
|
+
`);await d(e,c);return}case"mcp":{if(o.trim().toLowerCase()==="refresh"){Te(),await H(m.agentDir,{timeoutMs:5e3,maxBytes:m.fetchMaxBytes,maxChars:3e3}),X(),await d(e,"MCP catalog refreshed.");return}const s=await St();await d(e,s);return}case"login":{const s=pe();if(!o){const c=s.map(u=>`- ${u.id} (${u.name})`);await d(e,["OAuth providers:",...c].join(`
|
|
14
|
+
`));return}const i=T(o);if(j.has(t)){await d(e,"Login already in progress.");return}const l=s.find(c=>c.id===i);if(!l){await d(e,`Unknown OAuth provider: ${i}`);return}j.add(t);try{const{authStorage:c}=await x();await d(e,`Starting login for ${l.id}...`),await c.login(l.id,{onAuth:({url:u,instructions:f})=>{const g=["Open this URL in your browser:",u];f&&g.push("",f),d(e,g.join(`
|
|
15
|
+
`))},onPrompt:u=>I(t,e,u),onProgress:u=>{d(e,u)},onManualCodeInput:()=>I(t,e,{message:"Paste the authorization code:"})}),await d(e,`Login completed for ${l.id}.`)}catch(c){const u=L(c);await d(e,`Login failed: ${u}`)}finally{j.delete(t),Z(t)}return}case"logout":{if(!o){await d(e,"Usage: /logout <provider>");return}const s=T(o),{authStorage:i}=await x();i.logout(s),await d(e,`Logged out from ${s}.`);return}default:await d(e,`Unknown command: ${n}`)}}async function _t(e,t,n,o=[]){const r=B(n);if(r){await ue(e,t,r.command,r.args);return}const a=await q(t),s=G(a);s.length>0&&(await y(a),await Promise.all(s.map(h=>b(t,h).catch(P=>{console.warn(`[tg-agent] cleanup session file failed id=${h}`,P)}))));let i=C(a);if(!i)try{i=O(a,""),await y(a),await d(e,`Created session ${i.id}.`)}catch(h){await d(e,h.message);return}const{resolved:l,images:c}=await Ke(o);if(o.length>0&&l.length===0&&!n.trim()){await d(e,"Failed to download attachments.");return}const u=Xe(l),f=[];n.trim()&&f.push(n.trim()),u&&f.push(u);const g=f.join(`
|
|
16
16
|
|
|
17
|
-
`)||"Attachment received.",
|
|
17
|
+
`)||"Attachment received.",A={role:"user",content:g,ts:_()};K(i,A,m.maxHistoryMessages),await y(a),console.log(`[tg-agent] request user=${t} session=${i.id} messages=${i.messages.length} textLen=${g.length} attachments=${l.length} provider=${i.modelProvider??"-"} model=${i.modelId??"-"}`);let w=null;try{w=await yt(e)}catch(h){console.warn("[tg-agent] status message failed",h)}const E={sessionId:i.id,chatId:e,status:w,abortRequested:!1,cancelRequested:!1};Le(t,E);try{const h=await De(async()=>{const R=await ve({userId:t,sessionId:i.id,prompt:g,images:c,systemPrompt:m.systemPrompt,modelProvider:i.modelProvider,modelId:i.modelId,telegram:{chatId:e,sendPhoto:async(v,M)=>{await p.sendPhoto(e,v,M?{caption:M}:void 0)},sendDocument:async(v,M)=>{await p.sendDocument(e,v,M?{caption:M}:void 0)}},onAbortReady:v=>{E.abort=v,E.abortRequested&&v()},onStatus:w?v=>{const M=$t(v);M&&w?.update(M)}:void 0});return console.log(`[tg-agent] response session=${i.id} model=${R.modelProvider}/${R.modelId} file=${R.sessionFile}`),R.text});if(E.cancelRequested){w?await w.finalize("Cancelled by user."):await d(e,"Cancelled by user.");return}const P={role:"assistant",content:h,ts:_()};K(i,P,m.maxHistoryMessages),await y(a),w?await w.finalize(h):await d(e,h)}catch(h){if(console.error("[tg-agent] runModel error",h),E.cancelRequested){w?await w.fail("Cancelled by user."):await d(e,"Cancelled by user.");return}const P=L(h);w?await w.fail(`Error: ${P}`):await d(e,`Error: ${P}`)}finally{Ne(t)}}p.on("callback_query",e=>{const t=e.data??"",n=e.message?.chat.id;if(!e.from?.id||!n){p.answerCallbackQuery(e.id);return}const o=String(e.from.id);if(!re(o)){oe(o,n),p.answerCallbackQuery(e.id);return}if(!t.startsWith("cmd:")){p.answerCallbackQuery(e.id);return}const r=mt(t);if(!r){p.answerCallbackQuery(e.id,{text:"Command expired."});return}p.answerCallbackQuery(e.id),J(o,async()=>{const a=B(r);if(!a){await d(n,"Invalid command.");return}if(a.command==="stop"){await ee(n,o);return}await ue(n,o,a.command,a.args)}).catch(async a=>{console.error("[tg-agent] runModel error",a);const s=L(a);await d(n,`Error: ${s}`)})}),p.on("message",e=>{if(e.chat.type!=="private"){p.sendMessage(e.chat.id,"This bot only supports direct messages.");return}if(!e.from?.id)return;const t=String(e.from.id),n=e.chat.id;if(!re(t)){oe(t,n);return}const o=(e.text??e.caption??"").trim(),r=He(e);if(!o&&r.length===0)return;const a=S.get(t);if(a){if(o==="/stop"||o==="/cancel"){Ie(t,"Login cancelled by user."),d(n,"Login cancelled.");return}if(!o){d(n,"Login expects a text response.");return}Z(t),a.resolve(o);return}if(B(o)?.command==="stop"){ee(n,t);return}J(t,()=>_t(n,t,o,r)).catch(async i=>{console.error("[tg-agent] runModel error",i);const l=L(i);await d(n,`Error: ${l}`)})}),p.on("polling_error",e=>{console.error("Polling error",e)}),console.log("tg-agent started");
|