create-panal-agent 0.20.0 → 0.21.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.
- package/LICENSE +21 -0
- package/dist/i18n.js +10 -10
- package/dist/index.js +1 -2
- package/package.json +3 -4
- package/template/_package.json +2 -2
- package/template/src/register.ts +5 -1
- package/template/src/salida.ts +101 -6
- package/template/src/server.ts +111 -24
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Panal and Kreno (founder: Gustavo Chura C.)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/i18n.js
CHANGED
|
@@ -84,7 +84,7 @@ The language is taken from --lang, then PANAL_LANG, then your system locale.`,
|
|
|
84
84
|
model: 'Your model, if your agent uses one. Pick a provider by name and put its key below.\nLLM_MODEL overrides the suggested one; LLM_BASE_URL is for any provider not on the list.\nTo read images a client attaches, the model has to be a multimodal one.',
|
|
85
85
|
rpc: 'Your own RPC, if the public one falls short (it caps at ~15 calls/s).',
|
|
86
86
|
data: 'Where to store delivered results.',
|
|
87
|
-
x402: 'Charge per call (optional). Leave it empty and your agent only takes escrow jobs.\nThe price is per request
|
|
87
|
+
x402: 'Charge per call (optional). Leave it empty and your agent only takes escrow jobs.\nThe price is per request and is read in the X402_TOKEN coin: PANAL (default), MON, GHO, USDC or AUSD. X402_PRICE=0.05 with USDC is five cents. GHO is the stable one nobody can freeze; USDC and AUSD can be frozen by their issuers. With MON the client sends a small transfer instead of signing, and pays its own gas.',
|
|
88
88
|
subcontrata: "Subcontracting (optional, off by default). Your agent can pay another one for what it cannot do itself (see ctx.consultar in agent.ts). Without a number here it never delegates.\nIt is in the x402 currency, NOT a cut of what you charge per task: a task is paid in MON and a question in $PANAL, and converting one into the other by eye would be inventing the budget. Put it BELOW what you charge per question (X402_PRICE): at or above it, every job where you delegate leaves you nothing and you still pay the gas — and that punishes exactly what you want your agent to do, because the better it recognises what it does not know, the more often it works for free. A third of your price is a sane start. The number alone is not enough: you also need the SUBCONTRATA_SKILLS list in agent.ts, which says WHAT it may buy. Empty — as it ships — it never delegates, because the marketplace search generalises when it finds nobody, and a code agent asking for \"python video encoding\" would end up searching \"video\" and paying an agent that makes videos.",
|
|
89
89
|
vigilante: "The watchman. Every 60 s it reviews your open tasks and recovers what got stuck: a delivery that was never anchored, work that died halfway, or a job that was paid for and never arrived.\nVIGILANTE=off turns it off. PUBLIC_URL is your https endpoint, used in the lost-job warning.",
|
|
90
90
|
retirada: "Automatic withdrawal. The escrow credits each approved job inside the contract and pays nothing until someone calls withdraw. Every 60 min this agent collects it: MON when gas is at most 2 % of the amount, $PANAL from 1000. It needs a little MON for gas, and never runs while a job is in progress.\nRETIRADA=off turns it off.",
|
|
@@ -193,7 +193,7 @@ El idioma sale de --lang, luego de PANAL_LANG, y si no del locale del sistema.`,
|
|
|
193
193
|
model: 'Tu modelo, si tu agente usa uno. Elige proveedor por su nombre y pon su clave abajo.\nLLM_MODEL manda sobre el sugerido; LLM_BASE_URL es para cualquiera que no esté en la lista.\nPara leer las imágenes que adjunte un cliente, el modelo tiene que ser multimodal.',
|
|
194
194
|
rpc: 'RPC propio, si el público se te queda corto (limita a ~15 llamadas/s).',
|
|
195
195
|
data: 'Dónde guardar los resultados entregados.',
|
|
196
|
-
x402: 'Cobro por llamada (opcional). Déjalo vacío y tu agente solo acepta encargos del escrow.\nEl precio es por petición
|
|
196
|
+
x402: 'Cobro por llamada (opcional). Déjalo vacío y tu agente solo acepta encargos del escrow.\nEl precio es por petición y se lee en la moneda de X402_TOKEN: PANAL (por defecto), MON, GHO, USDC o AUSD. X402_PRICE=0.05 con USDC son cinco céntimos. GHO es la estable que nadie puede congelar; USDC y AUSD las pueden congelar sus emisores. Con MON el cliente manda una pequeña transferencia en vez de firmar, y paga él su gas.',
|
|
197
197
|
subcontrata: "Subcontratar (opcional, apagado por defecto). Tu agente puede pagar a otro por lo que no sepa hacer (ver ctx.consultar en agent.ts). Sin un numero aqui, no delega nunca.\nVa en la moneda del x402 y NO se deduce de lo que cobras por tarea: una tarea se cobra en MON y una consulta en $PANAL, y convertir una en otra a ojo seria inventarse el presupuesto. Ponlo POR DEBAJO de lo que cobras por consulta (X402_PRICE): igual o por encima, cada encargo en el que delegues no te deja nada y encima pones el gas — y eso castiga justo lo que quieres que haga, porque cuanto mejor reconozca lo que no sabe, más veces trabaja gratis. Un tercio de tu precio es un buen punto de partida. Con el número no basta: hace falta además la lista SUBCONTRATA_SKILLS en agent.ts, que dice QUÉ puede comprar. Vacía —como viene— no delega nunca, porque el buscador del mercado generaliza cuando no encuentra a nadie, y un agente de código que pida \"python video encoding\" acabaría buscando \"video\" y pagándole a uno que hace vídeos.",
|
|
198
198
|
vigilante: "El vigilante. Cada 60 s repasa tus tareas abiertas y recupera lo que se quedo colgado: una entrega que no llego a anclarse, un trabajo que murio a medias, o un encargo pagado que nunca llego.\nVIGILANTE=off lo apaga. PUBLIC_URL es tu endpoint https, para el aviso de encargo perdido.",
|
|
199
199
|
retirada: "Retirada automatica. El escrow acredita cada encargo aprobado dentro del contrato y no paga nada hasta que alguien llama a withdraw. Cada 60 min este agente lo recoge: el MON cuando el gas no pasa del 2 % de lo retirado, el $PANAL a partir de 1000. Necesita un poco de MON para el gas y nunca corre con un encargo en marcha.\nRETIRADA=off la apaga.",
|
|
@@ -303,7 +303,7 @@ const zh = {
|
|
|
303
303
|
model: '你的模型(如果代理需要)。按名称选择服务商,并在下面填入其密钥。\nLLM_MODEL 优先于建议的模型;LLM_BASE_URL 用于列表之外的任何服务商。\n若要读取客户附带的图片,模型必须支持多模态。',
|
|
304
304
|
rpc: '你自己的 RPC,当公共节点不够用时(其上限约为每秒 15 次调用)。',
|
|
305
305
|
data: '交付结果的存放位置。',
|
|
306
|
-
x402: '按次收费(可选)。留空则你的代理只接受托管订单。\n
|
|
306
|
+
x402: '按次收费(可选)。留空则你的代理只接受托管订单。\n价格按每次请求计算,以 X402_TOKEN 指定的币种计价:PANAL(默认)、MON、GHO、USDC 或 AUSD。用 USDC 时 X402_PRICE=0.05 就是五美分。GHO 是没人能冻结的稳定币;USDC 和 AUSD 可被发行方冻结。用 MON 时,客户发送一笔小额转账而不是签名,并自付 gas。',
|
|
307
307
|
subcontrata: "转包(可选,默认关闭)。你的代理可以为自己做不了的事付钱给另一个代理(见 agent.ts 的 ctx.consultar)。这里没有数字就永远不会转包。\n它使用 x402 的币种,不是按任务收入的比例:任务用 MON 结算,提问用 $PANAL,凭感觉换算等于凭空编造预算。 把它设得低于你每次提问的收费(X402_PRICE):等于或高于该值时,每一笔你转包出去的活都赚不到钱,还要自付 gas——而这恰恰惩罚了你希望它做的事,因为它越善于识别自己不懂的部分,就越经常白干。取你价格的三分之一是个合理的起点。 光有数字还不够:还需要 agent.ts 里的 SUBCONTRATA_SKILLS 列表,它规定了可以买什么。默认为空,也就永远不会转包;因为市场搜索在找不到人时会自动泛化,一个请求 \"python video encoding\" 的代码代理最后会搜索 \"video\",把钱付给做视频的代理。",
|
|
308
308
|
vigilante: "守望者。每 60 秒检查一次你未完成的任务,并挽回卡住的部分:没有上链的交付、中途死掉的工作,或已付款却从未送达的委托。\nVIGILANTE=off 可关闭。PUBLIC_URL 是你的 https 端点,用于丢失委托的提醒。",
|
|
309
309
|
retirada: "自动提取。托管合约会把每个已批准的委托记在合约里,在有人调用 withdraw 之前不会付款。这个代理每 60 分钟收取一次:当 Gas 不超过提取金额的 2 % 时提取 MON,$PANAL 从 1000 起提取。需要少量 MON 支付 Gas,委托进行中时不会运行。\nRETIRADA=off 可关闭。",
|
|
@@ -408,7 +408,7 @@ const hi = {
|
|
|
408
408
|
model: 'आपका मॉडल, अगर एजेंट किसी का उपयोग करता है। नाम से प्रदाता चुनें और नीचे उसकी कुंजी डालें।\nLLM_MODEL सुझाए गए मॉडल पर भारी पड़ता है; सूची से बाहर के किसी भी प्रदाता के लिए LLM_BASE_URL है।\nक्लाइंट द्वारा भेजी गई छवियाँ पढ़ने के लिए मॉडल मल्टीमॉडल होना चाहिए।',
|
|
409
409
|
rpc: 'अपना RPC, अगर सार्वजनिक कम पड़े (इसकी सीमा ~15 कॉल/सेकंड है)।',
|
|
410
410
|
data: 'दिए गए परिणाम कहाँ सहेजें।',
|
|
411
|
-
x402: 'प्रति कॉल शुल्क (वैकल्पिक)। खाली छोड़ें तो आपका एजेंट केवल एस्क्रो वाले काम लेगा।\nकीमत प्रति अनुरोध
|
|
411
|
+
x402: 'प्रति कॉल शुल्क (वैकल्पिक)। खाली छोड़ें तो आपका एजेंट केवल एस्क्रो वाले काम लेगा।\nकीमत प्रति अनुरोध है और X402_TOKEN की मुद्रा में पढ़ी जाती है: PANAL (डिफ़ॉल्ट), MON, GHO, USDC या AUSD। USDC के साथ X402_PRICE=0.05 यानी पाँच सेंट। GHO वह स्टेबल है जिसे कोई फ़्रीज़ नहीं कर सकता; USDC और AUSD को उनके जारीकर्ता फ़्रीज़ कर सकते हैं। MON में ग्राहक हस्ताक्षर के बजाय एक छोटा ट्रांसफ़र भेजता है और गैस खुद देता है।',
|
|
412
412
|
subcontrata: "सबकॉन्ट्रैक्टिंग (वैकल्पिक, डिफ़ॉल्ट रूप से बंद)। आपका एजेंट जो खुद नहीं कर सकता, उसके लिए दूसरे को भुगतान कर सकता है (agent.ts में ctx.consultar देखें)। यहाँ संख्या के बिना वह कभी नहीं सौंपता।\nयह x402 की मुद्रा में है, प्रति कार्य आय का हिस्सा नहीं: कार्य MON में और प्रश्न $PANAL में चुकाया जाता है। इसे अपने प्रति-प्रश्न शुल्क (X402_PRICE) से कम रखें: बराबर या अधिक होने पर, जिस भी काम में आप दूसरे को सौंपेंगे उसमें कुछ नहीं बचेगा और गैस भी आपकी जेब से जाएगी — और यह ठीक उसी व्यवहार को दंडित करता है जो आप चाहते हैं, क्योंकि आपका एजेंट जितना बेहतर पहचानेगा कि उसे क्या नहीं आता, उतनी ही बार वह मुफ़्त काम करेगा। अपने दाम का एक तिहाई एक समझदार शुरुआत है। सिर्फ़ संख्या काफ़ी नहीं: agent.ts में SUBCONTRATA_SKILLS सूची भी चाहिए, जो बताती है कि वह क्या खरीद सकता है। खाली — जैसा यह आता है — तो वह कभी नहीं सौंपता, क्योंकि कोई न मिलने पर बाज़ार की खोज सामान्यीकरण कर देती है, और \"python video encoding\" माँगने वाला कोड एजेंट अंत में \"video\" खोजकर वीडियो बनाने वाले को भुगतान कर देगा।",
|
|
413
413
|
vigilante: "प्रहरी। हर 60 सेकंड में आपके खुले कार्यों की जाँच करता है और जो अटक गया उसे वापस लाता है: वह डिलीवरी जो चेन पर दर्ज नहीं हुई, अधूरा रह गया काम, या भुगतान किया गया आदेश जो कभी नहीं पहुँचा।\nVIGILANTE=off इसे बंद करता है। PUBLIC_URL आपका https एंडपॉइंट है।",
|
|
414
414
|
retirada: "स्वचालित निकासी। एस्क्रो हर स्वीकृत आदेश को कॉन्ट्रैक्ट के अंदर जमा करता है और जब तक कोई withdraw न बुलाए, कुछ नहीं भेजता। यह एजेंट हर 60 मिनट में इसे निकालता है: MON तब, जब गैस निकाली गई राशि के 2 % से अधिक न हो, और $PANAL 1000 से। गैस के लिए थोड़ा MON चाहिए, और कोई आदेश चलते समय यह कभी नहीं चलता।\nRETIRADA=off इसे बंद करता है।",
|
|
@@ -517,7 +517,7 @@ const ar = {
|
|
|
517
517
|
model: 'نموذجك، إن كان وكيلك يستخدم واحدًا. اختر المزوّد بالاسم وضع مفتاحه أدناه.\nمتغيّر LLM_MODEL يتقدّم على النموذج المقترح، و LLM_BASE_URL لأي مزوّد ليس في القائمة.\nلقراءة الصور التي يرفقها العميل، يجب أن يكون النموذج متعدّد الوسائط.',
|
|
518
518
|
rpc: 'عقدة RPC خاصة بك، إن لم تكفِ العامة (حدّها نحو 15 طلبًا في الثانية).',
|
|
519
519
|
data: 'مكان حفظ النتائج المُسلَّمة.',
|
|
520
|
-
x402: 'التحصيل لكل استدعاء (اختياري). اتركه فارغًا فيقبل وكيلك مهام الضمان فقط.\nالسعر لكل
|
|
520
|
+
x402: 'التحصيل لكل استدعاء (اختياري). اتركه فارغًا فيقبل وكيلك مهام الضمان فقط.\nالسعر لكل طلب ويُقرأ بعملة X402_TOKEN: PANAL (افتراضيًا) أو MON أو GHO أو USDC أو AUSD. X402_PRICE=0.05 مع USDC تعني خمسة سنتات. GHO هي العملة المستقرة التي لا يستطيع أحد تجميدها؛ أما USDC وAUSD فيمكن لمُصدريهما تجميدهما. مع MON يرسل العميل تحويلًا صغيرًا بدل التوقيع ويدفع رسوم الغاز بنفسه.',
|
|
521
521
|
subcontrata: "التعاقد من الباطن (اختياري، معطل افتراضيا). يمكن لوكيلك ان يدفع لوكيل اخر مقابل ما لا يجيده (انظر ctx.consultar في agent.ts). بدون رقم هنا لن يفوض ابدا.\nيكون بعملة x402، وليس نسبة مما تتقاضاه عن المهمة: المهمة تدفع بـ MON والسؤال بـ $PANAL. اجعله أقل من سعر السؤال لديك (X402_PRICE): فعند التساوي أو الزيادة، كل مهمة تُسنِد فيها جزءًا إلى غيرك لا تُبقي لك شيئًا وتدفع الغاز من جيبك — وهذا يعاقب تحديدًا ما تريده منه، لأنه كلما أحسن إدراك ما يجهله عمل مجانًا أكثر. ثلث سعرك بداية معقولة. الرقم وحده لا يكفي: تحتاج أيضًا إلى قائمة SUBCONTRATA_SKILLS في agent.ts، التي تحدد ما الذي يُسمح بشرائه. وهي فارغة كما تأتي، فلا يفوّض أبدًا؛ لأن بحث السوق يعمّم عندما لا يجد أحدًا، فوكيل برمجة يطلب \"python video encoding\" ينتهي به الأمر باحثًا عن \"video\" ودافعًا لوكيل يصنع الفيديو.",
|
|
522
522
|
vigilante: "الحارس. كل 60 ثانية يراجع مهامك المفتوحة ويستعيد ما تعطل: تسليم لم يثبت على السلسلة، او عمل مات في منتصفه، او طلب مدفوع لم يصل قط.\nVIGILANTE=off يوقفه. PUBLIC_URL هو نقطة الوصول https لديك.",
|
|
523
523
|
retirada: "السحب التلقائي. يقيد الضمان كل طلب معتمد داخل العقد ولا يدفع شيئا حتى يستدعي احد withdraw. يجمعه هذا الوكيل كل 60 دقيقة: MON عندما لا يتجاوز الغاز 2 % من المبلغ، و$PANAL ابتداء من 1000. يحتاج قليلا من MON للغاز، ولا يعمل ابدا اثناء تنفيذ طلب.\nRETIRADA=off يوقفه.",
|
|
@@ -625,7 +625,7 @@ La langue vient de --lang, puis de PANAL_LANG, puis de la locale du système.`,
|
|
|
625
625
|
model: 'Votre modèle, si votre agent en utilise un. Choisissez un fournisseur par son nom et mettez sa clé ci-dessous.\nLLM_MODEL prime sur le modèle suggéré ; LLM_BASE_URL sert pour tout fournisseur absent de la liste.\nPour lire les images jointes par un client, le modèle doit être multimodal.',
|
|
626
626
|
rpc: 'Votre propre RPC, si le public ne suffit pas (limité à ~15 appels/s).',
|
|
627
627
|
data: 'Où stocker les résultats livrés.',
|
|
628
|
-
x402: "Facturation à l'appel (facultatif). Laissez vide et votre agent ne prend que des missions sous entiercement.\nLe prix est par requête
|
|
628
|
+
x402: "Facturation à l'appel (facultatif). Laissez vide et votre agent ne prend que des missions sous entiercement.\nLe prix est par requête et se lit dans la monnaie de X402_TOKEN : PANAL (par défaut), MON, GHO, USDC ou AUSD. X402_PRICE=0.05 en USDC, ce sont cinq centimes. GHO est le stable que personne ne peut geler ; USDC et AUSD peuvent être gelés par leurs émetteurs. Avec MON, le client envoie un petit transfert au lieu de signer et paie son propre gas.",
|
|
629
629
|
subcontrata: "Sous-traitance (optionnelle, desactivee par defaut). Votre agent peut payer un autre pour ce qu il ne sait pas faire (voir ctx.consultar dans agent.ts). Sans un nombre ici, il ne delegue jamais.\nC est dans la devise x402, PAS une part de ce que vous facturez par tache : une tache se paie en MON et une question en $PANAL. Mettez-le EN DESSOUS de ce que vous facturez par question (X402_PRICE) : à égalité ou au-dessus, chaque travail où vous déléguez ne vous laisse rien et vous payez encore le gaz — et cela pénalise précisément ce que vous attendez de lui, car mieux il reconnaît ce qu'il ignore, plus souvent il travaille gratuitement. Un tiers de votre prix est un bon départ. Le nombre ne suffit pas : il faut aussi la liste SUBCONTRATA_SKILLS dans agent.ts, qui dit CE QU'il peut acheter. Vide — comme elle arrive — il ne délègue jamais, car la recherche du marché généralise quand elle ne trouve personne, et un agent de code demandant \"python video encoding\" finirait par chercher \"video\" et payer un agent qui fait des vidéos.",
|
|
630
630
|
vigilante: "La sentinelle. Toutes les 60 s, elle passe en revue vos taches ouvertes et recupere ce qui est reste bloque : une livraison jamais ancree, un travail mort a mi-chemin, ou une commande payee qui n est jamais arrivee.\nVIGILANTE=off la desactive. PUBLIC_URL est votre endpoint https.",
|
|
631
631
|
retirada: "Retrait automatique. L escrow credite chaque commande approuvee dans le contrat et ne verse rien tant que personne n appelle withdraw. Toutes les 60 min, cet agent le recupere : le MON quand le gas ne depasse pas 2 % du montant, le $PANAL a partir de 1000. Il faut un peu de MON pour le gas, et il ne tourne jamais pendant une commande en cours.\nRETIRADA=off le desactive.",
|
|
@@ -737,7 +737,7 @@ O idioma vem de --lang, depois de PANAL_LANG e, por fim, do locale do sistema.`,
|
|
|
737
737
|
model: 'O seu modelo, se o agente usar um. Escolha um fornecedor pelo nome e ponha a sua chave abaixo.\nLLM_MODEL manda sobre o sugerido; LLM_BASE_URL serve para qualquer fornecedor fora da lista.\nPara ler as imagens que um cliente anexe, o modelo tem de ser multimodal.',
|
|
738
738
|
rpc: 'RPC próprio, se o público não chegar (limita a ~15 chamadas/s).',
|
|
739
739
|
data: 'Onde guardar os resultados entregues.',
|
|
740
|
-
x402: 'Cobrança por chamada (opcional). Deixe vazio e o seu agente só aceita trabalhos do escrow.\nO preço é por pedido
|
|
740
|
+
x402: 'Cobrança por chamada (opcional). Deixe vazio e o seu agente só aceita trabalhos do escrow.\nO preço é por pedido e lê-se na moeda de X402_TOKEN: PANAL (por defeito), MON, GHO, USDC ou AUSD. X402_PRICE=0.05 com USDC são cinco cêntimos. GHO é a estável que ninguém pode congelar; USDC e AUSD podem ser congeladas pelos seus emissores. Com MON, o cliente envia uma pequena transferência em vez de assinar e paga o seu próprio gás.',
|
|
741
741
|
subcontrata: "Subcontratar (opcional, desligado por omissao). O seu agente pode pagar a outro pelo que nao sabe fazer (ver ctx.consultar em agent.ts). Sem um numero aqui, nunca delega.\nVai na moeda do x402 e NAO e uma fatia do que cobra por tarefa: uma tarefa paga-se em MON e uma pergunta em $PANAL. Põe-no ABAIXO do que cobras por pergunta (X402_PRICE): igual ou acima, cada trabalho em que delegues não te deixa nada e ainda pagas o gás — e isso penaliza exactamente o que queres que ele faça, porque quanto melhor reconhecer o que não sabe, mais vezes trabalha de graça. Um terço do teu preço é um começo sensato. O número não chega: é preciso também a lista SUBCONTRATA_SKILLS em agent.ts, que diz O QUE pode comprar. Vazia — como vem — nunca delega, porque a busca do mercado generaliza quando não encontra ninguém, e um agente de código que peça \"python video encoding\" acabaria a procurar \"video\" e a pagar a um que faz vídeos.",
|
|
742
742
|
vigilante: "O vigia. A cada 60 s reve as suas tarefas abertas e recupera o que ficou preso: uma entrega que nunca foi ancorada, trabalho que morreu a meio, ou uma encomenda paga que nunca chegou.\nVIGILANTE=off desliga-o. PUBLIC_URL e o seu endpoint https.",
|
|
743
743
|
retirada: "Levantamento automatico. O escrow credita cada encomenda aprovada dentro do contrato e nao paga nada ate alguem chamar withdraw. A cada 60 min este agente recolhe-o: o MON quando o gas nao passa de 2 % do montante, o $PANAL a partir de 1000. Precisa de um pouco de MON para o gas e nunca corre com uma encomenda em curso.\nRETIRADA=off desliga-o.",
|
|
@@ -847,7 +847,7 @@ const ru = {
|
|
|
847
847
|
model: 'Ваша модель, если агент её использует. Выберите провайдера по имени и укажите его ключ ниже.\nLLM_MODEL важнее предложенной модели; LLM_BASE_URL — для любого провайдера не из списка.\nЧтобы читать изображения, приложенные клиентом, модель должна быть мультимодальной.',
|
|
848
848
|
rpc: 'Свой RPC, если публичного не хватает (лимит ~15 запросов/с).',
|
|
849
849
|
data: 'Где хранить сданные результаты.',
|
|
850
|
-
x402: 'Плата за вызов (необязательно). Оставьте пустым — агент будет брать только заказы через эскроу.\nЦена за один
|
|
850
|
+
x402: 'Плата за вызов (необязательно). Оставьте пустым — агент будет брать только заказы через эскроу.\nЦена за один запрос и читается в монете X402_TOKEN: PANAL (по умолчанию), MON, GHO, USDC или AUSD. X402_PRICE=0.05 в USDC — это пять центов. GHO — стейблкоин, который никто не может заморозить; USDC и AUSD могут заморозить их эмитенты. С MON клиент вместо подписи отправляет небольшой перевод и сам платит за газ.',
|
|
851
851
|
subcontrata: "Субподряд (необязательно, по умолчанию выключен). Ваш агент может заплатить другому за то, чего не умеет сам (см. ctx.consultar в agent.ts). Без числа здесь он никогда не делегирует.\nУказывается в валюте x402, а НЕ долей от оплаты за задачу: задача оплачивается в MON, а вопрос в $PANAL. Поставьте НИЖЕ того, что берёте за вопрос (X402_PRICE): при равенстве или выше каждая работа, где вы делегируете, не приносит ничего, а газ вы платите сами — и это наказывает ровно то поведение, которого вы добиваетесь: чем лучше агент понимает, чего не знает, тем чаще работает даром. Треть вашей цены — разумное начало. Одного числа мало: нужен ещё список SUBCONTRATA_SKILLS в agent.ts, который говорит, ЧТО можно покупать. Пустой — а таким он и поставляется — и агент никогда не делегирует, потому что поиск по рынку обобщает запрос, если никого не нашёл: агент по коду, попросивший \"python video encoding\", в итоге станет искать \"video\" и заплатит тому, кто делает видео.",
|
|
852
852
|
vigilante: "Сторож. Каждые 60 с проверяет ваши открытые задачи и вытаскивает то, что застряло: доставку, которая не попала в блокчейн, работу, оборвавшуюся на середине, или оплаченный заказ, который так и не пришел.\nVIGILANTE=off выключает его. PUBLIC_URL ваш https-эндпоинт.",
|
|
853
853
|
retirada: "Автоматический вывод. Эскроу зачисляет каждый одобренный заказ внутри контракта и ничего не отправляет, пока кто-то не вызовет withdraw. Каждые 60 мин этот агент забирает средства: MON, когда газ не больше 2 % от суммы, $PANAL от 1000. Нужно немного MON на газ, и вывод никогда не запускается во время выполнения заказа.\nRETIRADA=off выключает его.",
|
|
@@ -958,7 +958,7 @@ const bn = {
|
|
|
958
958
|
model: 'আপনার মডেল, যদি এজেন্ট ব্যবহার করে। নাম দিয়ে প্রদানকারী বেছে নিন এবং নিচে তার কী দিন।\nLLM_MODEL প্রস্তাবিত মডেলের উপরে প্রাধান্য পায়; তালিকার বাইরের যেকোনো প্রদানকারীর জন্য LLM_BASE_URL।\nক্লায়েন্টের পাঠানো ছবি পড়তে হলে মডেলটিকে মাল্টিমোডাল হতে হবে।',
|
|
959
959
|
rpc: 'নিজস্ব RPC, যদি সর্বজনীনটি যথেষ্ট না হয় (সীমা প্রায় ১৫ কল/সেকেন্ড)।',
|
|
960
960
|
data: 'সরবরাহ করা ফলাফল কোথায় রাখা হবে।',
|
|
961
|
-
x402: 'প্রতি কলে চার্জ (ঐচ্ছিক)। ফাঁকা রাখলে আপনার এজেন্ট কেবল এসক্রো কাজ নেবে।\nদাম প্রতি অনুরোধে,
|
|
961
|
+
x402: 'প্রতি কলে চার্জ (ঐচ্ছিক)। ফাঁকা রাখলে আপনার এজেন্ট কেবল এসক্রো কাজ নেবে।\nদাম প্রতি অনুরোধে, আর তা X402_TOKEN-এর মুদ্রায় পড়া হয়: PANAL (ডিফল্ট), MON, GHO, USDC বা AUSD। USDC-তে X402_PRICE=0.05 মানে পাঁচ সেন্ট। GHO হলো সেই স্টেবল যা কেউ ফ্রিজ করতে পারে না; USDC ও AUSD তাদের ইস্যুকারী ফ্রিজ করতে পারে। MON-এ গ্রাহক সই করার বদলে একটি ছোট ট্রান্সফার পাঠায় এবং নিজেই গ্যাস দেয়।',
|
|
962
962
|
subcontrata: "সাবকন্ট্রাক্টিং (ঐচ্ছিক, ডিফল্টে বন্ধ)। আপনার এজেন্ট যা নিজে পারে না, তার জন্য অন্যকে অর্থ দিতে পারে (agent.ts-এ ctx.consultar দেখুন)। এখানে সংখ্যা না থাকলে সে কখনও দায়িত্ব দেয় না।\nএটি x402-এর মুদ্রায়, কাজপ্রতি আয়ের অংশ নয়: কাজের দাম MON-এ আর প্রশ্নের দাম $PANAL-এ। এটি আপনার প্রতি-প্রশ্ন মূল্যের (X402_PRICE) চেয়ে কম রাখুন: সমান বা বেশি হলে, যে কাজেই আপনি অন্যকে দেবেন তাতে কিছুই থাকবে না, গ্যাসও আপনার — আর এতে ঠিক সেই আচরণই শাস্তি পায় যা আপনি চান, কারণ এজেন্ট যত ভালোভাবে বুঝবে সে কী জানে না, তত বেশিবার সে বিনামূল্যে কাজ করবে। আপনার দামের এক-তৃতীয়াংশ একটি যুক্তিসঙ্গত শুরু। শুধু সংখ্যাই যথেষ্ট নয়: agent.ts-এ SUBCONTRATA_SKILLS তালিকাও লাগে, যা বলে দেয় সে কী কিনতে পারবে। খালি — যেমনটা আসে — হলে সে কখনও দায়িত্ব দেয় না, কারণ কাউকে না পেলে বাজারের খোঁজ সাধারণীকরণ করে, আর \"python video encoding\" চাওয়া কোড এজেন্ট শেষে \"video\" খুঁজে ভিডিও বানায় এমন কাউকে টাকা দিয়ে বসবে।",
|
|
963
963
|
vigilante: "প্রহরী। প্রতি ৬০ সেকেন্ডে আপনার খোলা কাজগুলি দেখে এবং আটকে যাওয়া জিনিস উদ্ধার করে: চেইনে না ওঠা ডেলিভারি, মাঝপথে মরে যাওয়া কাজ, বা টাকা দেওয়া হয়েছে অথচ কখনও পৌঁছায়নি এমন আদেশ।\nVIGILANTE=off এটি বন্ধ করে। PUBLIC_URL আপনার https এন্ডপয়েন্ট।",
|
|
964
964
|
retirada: "স্বয়ংক্রিয় উত্তোলন। এসক্রো প্রতিটি অনুমোদিত আদেশ চুক্তির ভিতরে জমা রাখে এবং কেউ withdraw না ডাকা পর্যন্ত কিছু পাঠায় না। এই এজেন্ট প্রতি ৬০ মিনিটে তা তোলে: MON, যখন গ্যাস তোলা অঙ্কের ২ % এর বেশি নয়, আর $PANAL ১০০০ থেকে। গ্যাসের জন্য সামান্য MON লাগে, এবং কোনো আদেশ চলার সময় এটি কখনও চলে না।\nRETIRADA=off এটি বন্ধ করে।",
|
|
@@ -1066,7 +1066,7 @@ const ur = {
|
|
|
1066
1066
|
model: 'آپ کا ماڈل، اگر ایجنٹ استعمال کرے۔ نام سے فراہم کنندہ چنیں اور نیچے اس کی کلید ڈالیں۔\nLLM_MODEL تجویز کردہ ماڈل پر مقدم ہے؛ فہرست سے باہر کسی بھی فراہم کنندہ کے لیے LLM_BASE_URL ہے۔\nکلائنٹ کی بھیجی ہوئی تصاویر پڑھنے کے لیے ماڈل کا ملٹی موڈل ہونا ضروری ہے۔',
|
|
1067
1067
|
rpc: 'اپنا RPC، اگر عوامی کم پڑ جائے (حد تقریباً 15 کالز فی سیکنڈ)۔',
|
|
1068
1068
|
data: 'فراہم کردہ نتائج کہاں محفوظ ہوں۔',
|
|
1069
|
-
x402: 'فی کال وصولی (اختیاری)۔ خالی چھوڑ دیں تو آپ کا ایجنٹ صرف ایسکرو کے کام لے گا۔\nقیمت فی درخواست
|
|
1069
|
+
x402: 'فی کال وصولی (اختیاری)۔ خالی چھوڑ دیں تو آپ کا ایجنٹ صرف ایسکرو کے کام لے گا۔\nقیمت فی درخواست ہے اور X402_TOKEN کی کرنسی میں پڑھی جاتی ہے: PANAL (ڈیفالٹ)، MON، GHO، USDC یا AUSD۔ USDC کے ساتھ X402_PRICE=0.05 یعنی پانچ سینٹ۔ GHO وہ اسٹیبل ہے جسے کوئی منجمد نہیں کر سکتا؛ USDC اور AUSD کو ان کے جاری کنندہ منجمد کر سکتے ہیں۔ MON میں کلائنٹ دستخط کے بجائے ایک چھوٹی منتقلی بھیجتا ہے اور گیس خود دیتا ہے۔',
|
|
1070
1070
|
subcontrata: "سب کنٹریکٹنگ (اختیاری، بذریعہ ڈیفالٹ بند)۔ آپ کا ایجنٹ جو خود نہیں کر سکتا اس کے لیے دوسرے کو ادائیگی کر سکتا ہے (agent.ts میں ctx.consultar دیکھیں)۔ یہاں نمبر کے بغیر وہ کبھی نہیں سونپتا۔\nیہ x402 کی کرنسی میں ہے، فی ٹاسک آمدنی کا حصہ نہیں: ٹاسک MON میں اور سوال $PANAL میں ادا ہوتا ہے۔ اسے اپنی فی سوال قیمت (X402_PRICE) سے کم رکھیں: برابر یا زیادہ ہونے پر، جس کام میں بھی آپ کسی اور کو دیں گے اس میں کچھ نہیں بچے گا اور گیس بھی آپ کی جیب سے جائے گی — اور یہ بالکل اسی رویّے کو سزا دیتا ہے جو آپ چاہتے ہیں، کیونکہ آپ کا ایجنٹ جتنا بہتر پہچانے گا کہ اسے کیا نہیں آتا، اتنی ہی زیادہ بار وہ مفت کام کرے گا۔ اپنی قیمت کا ایک تہائی معقول آغاز ہے۔ صرف عدد کافی نہیں: agent.ts میں SUBCONTRATA_SKILLS کی فہرست بھی چاہیے، جو بتاتی ہے کہ وہ کیا خرید سکتا ہے۔ خالی — جیسا کہ یہ آتا ہے — تو وہ کبھی کام نہیں سونپتا، کیونکہ کوئی نہ ملنے پر مارکیٹ کی تلاش عام کر دیتی ہے، اور \"python video encoding\" مانگنے والا کوڈ ایجنٹ آخر میں \"video\" ڈھونڈ کر ویڈیو بنانے والے کو ادائیگی کر بیٹھے گا۔",
|
|
1071
1071
|
vigilante: "نگہبان۔ ہر 60 سیکنڈ میں آپ کے کھلے کام دیکھتا ہے اور جو اٹکا ہے اسے بچاتا ہے: وہ ڈیلیوری جو چین پر درج نہ ہوئی، وہ کام جو بیچ میں مر گیا، یا ادا شدہ آرڈر جو کبھی نہ پہنچا۔\nVIGILANTE=off اسے بند کرتا ہے۔ PUBLIC_URL آپ کا https اینڈ پوائنٹ ہے۔",
|
|
1072
1072
|
retirada: "خودکار نکلوائی۔ ایسکرو ہر منظور شدہ آرڈر کو کنٹریکٹ کے اندر جمع کرتا ہے اور جب تک کوئی withdraw نہ بلائے کچھ نہیں بھیجتا۔ یہ ایجنٹ ہر 60 منٹ میں اسے نکالتا ہے: MON جب گیس نکالی گئی رقم کے 2 % سے زیادہ نہ ہو، اور $PANAL 1000 سے۔ گیس کے لیے تھوڑا MON چاہیے، اور کسی آرڈر کے دوران یہ کبھی نہیں چلتا۔\nRETIRADA=off اسے بند کرتا ہے۔",
|
package/dist/index.js
CHANGED
|
@@ -257,8 +257,7 @@ function writeEnvExample(dest, t) {
|
|
|
257
257
|
'',
|
|
258
258
|
bloque(t.env.x402),
|
|
259
259
|
'X402_PRICE=',
|
|
260
|
-
'# X402_TOKEN=
|
|
261
|
-
'# X402_SYMBOL=$PANAL',
|
|
260
|
+
'# X402_TOKEN=PANAL',
|
|
262
261
|
'# X402_DESCRIPTION=',
|
|
263
262
|
'',
|
|
264
263
|
bloque(t.env.subcontrata),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-panal-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Crea un agente de IA para Panal, funcionando y cobrando on-chain, en cinco minutos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,11 +30,10 @@
|
|
|
30
30
|
"node": ">=20"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@types/node": "^
|
|
33
|
+
"@types/node": "^26.6.4",
|
|
34
34
|
"tsx": "^4.19.0",
|
|
35
35
|
"typescript": "^5.6.0",
|
|
36
|
-
"unpdf": "^1.8.1"
|
|
37
|
-
"xlsx": "^0.18.5"
|
|
36
|
+
"unpdf": "^1.8.1"
|
|
38
37
|
},
|
|
39
38
|
"dependencies": {
|
|
40
39
|
"viem": "^2.21.0"
|
package/template/_package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"typecheck": "tsc --noEmit"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@panal/sdk": "^0.
|
|
13
|
+
"@panal/sdk": "^0.19.0",
|
|
14
14
|
"unpdf": "^1.8.1",
|
|
15
15
|
"dotenv": "^17.0.0",
|
|
16
16
|
"tsx": "^4.19.0",
|
|
@@ -18,6 +18,6 @@
|
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"typescript": "^5.6.0",
|
|
21
|
-
"@types/node": "^
|
|
21
|
+
"@types/node": "^26.6.4"
|
|
22
22
|
}
|
|
23
23
|
}
|
package/template/src/register.ts
CHANGED
|
@@ -410,7 +410,11 @@ async function reclamaTuNombre(account: ReturnType<typeof privateKeyToAccount>,
|
|
|
410
410
|
return;
|
|
411
411
|
}
|
|
412
412
|
|
|
413
|
-
|
|
413
|
+
// Con el gas fijado: Monad cobra el límite entero, y viem lo rellena con
|
|
414
|
+
// `eth_fillTransaction`, que el nodo a veces infla.
|
|
415
|
+
const reclamo = { address: donde, abi: NOMBRES_ABI, functionName: 'reclamar', args: [handle], account } as const;
|
|
416
|
+
const estimado = await publico.estimateContractGas(reclamo);
|
|
417
|
+
const hash = await cartera.writeContract({ ...reclamo, gas: (estimado * 11n + 9n) / 10n, chain });
|
|
414
418
|
await publico.waitForTransactionReceipt({ hash });
|
|
415
419
|
console.log(`\nTu nombre único en Panal: ${handle}`);
|
|
416
420
|
} catch (err) {
|
package/template/src/salida.ts
CHANGED
|
@@ -438,9 +438,22 @@ export function comoAdjunto(nombre: string): string {
|
|
|
438
438
|
const PIDE_UN_NOMBRE =
|
|
439
439
|
'You name files. Given a client request, reply with ONLY a file name for the deliverable.\n' +
|
|
440
440
|
'Two to five words. No extension, no quotes, no path, no explanation, no punctuation at the ends.\n' +
|
|
441
|
-
'Name the SUBJECT the work is about, never the action asked for
|
|
442
|
-
|
|
443
|
-
|
|
441
|
+
'Name the SUBJECT the work is about, never the action asked for.\n' +
|
|
442
|
+
// El ejemplo de antes enseñaba lo contrario de lo que pedía: a una petición
|
|
443
|
+
// en INGLÉS le contestaba en español («division de dos enteros»), y el
|
|
444
|
+
// encargo #95, escrito en inglés, salió como «contactos-en-json». Ahora hay
|
|
445
|
+
// un ejemplo por idioma, cada uno respondido en el suyo.
|
|
446
|
+
'Write it in the language of the client\'s INSTRUCTIONS — what they ask for — not the language of ' +
|
|
447
|
+
'the data they paste or of field names they spell out, and in their own script. No file-format ' +
|
|
448
|
+
'words (JSON, PDF, CSV). Examples: "write the test cases for a function that divides two integers" ' +
|
|
449
|
+
'-> "division of two integers"; "escribe los casos de prueba de una función que divide dos enteros" ' +
|
|
450
|
+
'-> "division de dos enteros".\n' +
|
|
451
|
+
// Decirlo no bastaba: con los datos en español y las instrucciones en
|
|
452
|
+
// inglés, el modelo seguía contestando en español. Obligarle a nombrar
|
|
453
|
+
// primero el idioma de las instrucciones es lo que lo corrige (medido).
|
|
454
|
+
'First decide the language of the instructions. Answer in exactly two lines:\n' +
|
|
455
|
+
'LANG: <ISO code of the instructions\' language>\n' +
|
|
456
|
+
'NAME: <the file name, in that language>';
|
|
444
457
|
|
|
445
458
|
/**
|
|
446
459
|
* El nombre del archivo que se entrega, sacado del TEMA del encargo.
|
|
@@ -461,7 +474,79 @@ const PIDE_UN_NOMBRE =
|
|
|
461
474
|
* devuelve algo que no sirve, se usa el nombre de siempre. Nombrar un archivo
|
|
462
475
|
* no puede impedir entregarlo: el pago ya está bloqueado.
|
|
463
476
|
*/
|
|
464
|
-
|
|
477
|
+
/**
|
|
478
|
+
* De «LANG: en\nNAME: contact list», la línea del nombre.
|
|
479
|
+
*
|
|
480
|
+
* Sin la etiqueta —un modelo que no siga el formato— se queda la respuesta
|
|
481
|
+
* entera, y `comoNombre` se queda con su primera línea, como antes.
|
|
482
|
+
*/
|
|
483
|
+
export function lineaDelNombre(crudo: string): string {
|
|
484
|
+
const m = crudo.match(/^\s*NAME\s*:\s*(.+)$/im);
|
|
485
|
+
return m ? m[1]! : crudo;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* EL IDIOMA DE LAS INSTRUCCIONES, sin los datos delante.
|
|
490
|
+
*
|
|
491
|
+
* Pedirle al modelo «escribe en el idioma del cliente» no funciona cuando el
|
|
492
|
+
* encargo trae datos en otro idioma: medido el 2026-09-28, con órdenes en
|
|
493
|
+
* inglés y una lista de contactos en español, las claves salían en español
|
|
494
|
+
* más de la mitad de las veces, y un detector que veía el encargo entero
|
|
495
|
+
* acertaba 19 de 30 (tomaba un encargo en francés por español, y uno en chino
|
|
496
|
+
* por inglés). El mismo detector con SOLO el primer párrafo acertó 39 de 40.
|
|
497
|
+
*
|
|
498
|
+
* El primer párrafo es lo que va antes de la primera línea en blanco, que es
|
|
499
|
+
* como se escribe casi siempre un encargo: primero qué hay que hacer, después
|
|
500
|
+
* los datos. Si es demasiado corto para decir nada, se usan los primeros 300
|
|
501
|
+
* caracteres.
|
|
502
|
+
*/
|
|
503
|
+
/**
|
|
504
|
+
* El nombre del idioma, en inglés, para decírselo al modelo.
|
|
505
|
+
*
|
|
506
|
+
* Con el código solo («"en"») el modelo seguía nombrando en español un encargo
|
|
507
|
+
* en inglés que citaba campos en español; con «Write the NAME in English» y el
|
|
508
|
+
* aviso de que ni los datos ni los campos citados deciden, acertó 12 de 12.
|
|
509
|
+
*/
|
|
510
|
+
const NOMBRES_DE_IDIOMA: Record<string, string> = {
|
|
511
|
+
en: 'English', es: 'Spanish', pt: 'Portuguese', fr: 'French', de: 'German', it: 'Italian',
|
|
512
|
+
nl: 'Dutch', ca: 'Catalan', zh: 'Chinese', ja: 'Japanese', ko: 'Korean', hi: 'Hindi',
|
|
513
|
+
bn: 'Bengali', ur: 'Urdu', ar: 'Arabic', fa: 'Persian', he: 'Hebrew', ru: 'Russian',
|
|
514
|
+
uk: 'Ukrainian', pl: 'Polish', tr: 'Turkish', vi: 'Vietnamese', id: 'Indonesian', th: 'Thai',
|
|
515
|
+
el: 'Greek', sv: 'Swedish',
|
|
516
|
+
};
|
|
517
|
+
export function nombreDeIdioma(codigo: string): string {
|
|
518
|
+
return NOMBRES_DE_IDIOMA[codigo] ?? `the language with ISO code "${codigo}"`;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const PIDE_EL_IDIOMA =
|
|
522
|
+
'You detect the language of a request. Reply with ONLY the ISO 639-1 code (two letters) of the ' +
|
|
523
|
+
'language the text is written in. Ignore field names in quotes. Nothing else.';
|
|
524
|
+
|
|
525
|
+
export function parrafoDeInstrucciones(brief: string): string {
|
|
526
|
+
const limpio = stripFilesManifest(brief).trim();
|
|
527
|
+
const primero = limpio.split(/\n\s*\n/)[0]!.trim();
|
|
528
|
+
return primero.length >= 12 ? primero : limpio.slice(0, 300);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** El código ISO del idioma en que están escritas las instrucciones, o null si no se pudo saber. */
|
|
532
|
+
export async function idiomaDeLasInstrucciones(brief: string): Promise<string | null> {
|
|
533
|
+
try {
|
|
534
|
+
const cfg = resolverLlm(process.env);
|
|
535
|
+
const r = await llmChat(
|
|
536
|
+
{ ...cfg, timeoutMs: 15_000, maxRetries: 1 },
|
|
537
|
+
{ system: PIDE_EL_IDIOMA, user: parrafoDeInstrucciones(brief) },
|
|
538
|
+
);
|
|
539
|
+
const codigo = r.trim().toLowerCase().replace(/[^a-z]/g, '');
|
|
540
|
+
return /^[a-z]{2}$/.test(codigo) ? codigo : null;
|
|
541
|
+
} catch {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export async function nombreDelTema(brief: string, deReserva: string, idioma?: string | null): Promise<string> {
|
|
547
|
+
// El idioma se decide con el párrafo de las instrucciones, no con el encargo
|
|
548
|
+
// entero: ver `idiomaDeLasInstrucciones`.
|
|
549
|
+
idioma = idioma ?? (await idiomaDeLasInstrucciones(brief));
|
|
465
550
|
try {
|
|
466
551
|
const cfg = resolverLlm(process.env);
|
|
467
552
|
const respuesta = await llmChat(
|
|
@@ -489,9 +574,19 @@ export async function nombreDelTema(brief: string, deReserva: string): Promise<s
|
|
|
489
574
|
// Sin el manifiesto, por lo mismo que en `formatoPedido`: son 1.500
|
|
490
575
|
// caracteres de presupuesto y un hash de 64 ocupa sitio sin decir nada
|
|
491
576
|
// del tema. Con adjuntos cortos llegaba a colarse entero.
|
|
492
|
-
{
|
|
577
|
+
{
|
|
578
|
+
system: PIDE_UN_NOMBRE,
|
|
579
|
+
// Si quien llama ya sabe el idioma de las instrucciones, se le dice: no
|
|
580
|
+
// hay que volver a adivinarlo, que es donde el modelo se equivoca.
|
|
581
|
+
user:
|
|
582
|
+
stripFilesManifest(brief).trim().slice(0, 1_500) +
|
|
583
|
+
(idioma
|
|
584
|
+
? `\n\n(Write the NAME in ${nombreDeIdioma(idioma)}, even if the data or the quoted field names ` +
|
|
585
|
+
`are in another language. Answer LANG: ${idioma}.)`
|
|
586
|
+
: ''),
|
|
587
|
+
},
|
|
493
588
|
);
|
|
494
|
-
const nombre = comoNombre(respuesta);
|
|
589
|
+
const nombre = comoNombre(lineaDelNombre(respuesta));
|
|
495
590
|
if (nombre) return nombre;
|
|
496
591
|
console.warn(`[salida] el modelo no dio un nombre usable; el archivo va como «${deReserva}»`);
|
|
497
592
|
} catch (err) {
|
package/template/src/server.ts
CHANGED
|
@@ -42,7 +42,6 @@ import {
|
|
|
42
42
|
nivelPara,
|
|
43
43
|
parseAttachmentsManifest,
|
|
44
44
|
parseEnvelope,
|
|
45
|
-
parsePaymentHeader,
|
|
46
45
|
permitNonce,
|
|
47
46
|
readPermitDomain,
|
|
48
47
|
sanitizeFileName,
|
|
@@ -55,10 +54,21 @@ import {
|
|
|
55
54
|
type LlmConfig,
|
|
56
55
|
type Nivel,
|
|
57
56
|
type PermitDomain,
|
|
57
|
+
type X402Currency,
|
|
58
|
+
X402_NATIVE_SCHEME,
|
|
59
|
+
buildNativeQuote,
|
|
60
|
+
formatX402Amount,
|
|
61
|
+
newQuoteSecret,
|
|
62
|
+
parseX402Header,
|
|
63
|
+
parseX402Amount,
|
|
64
|
+
resourceId,
|
|
65
|
+
verifyNativePayment,
|
|
66
|
+
x402Currencies,
|
|
67
|
+
x402CurrencyByName,
|
|
58
68
|
} from '@panal/sdk';
|
|
59
69
|
import { comoAdjunto } from './salida.js';
|
|
60
70
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
61
|
-
import { isAddress, keccak256, parseEther, toBytes, verifyMessage } from 'viem';
|
|
71
|
+
import { isAddress, keccak256, parseEther, parseUnits, toBytes, verifyMessage } from 'viem';
|
|
62
72
|
import type { Address } from 'viem';
|
|
63
73
|
import { handleTask, NIVELES, SUBCONTRATA_SKILLS } from './agent.js';
|
|
64
74
|
import { frasesGuardadas, pedirTraduccion } from './traduccion.js';
|
|
@@ -303,26 +313,57 @@ setInterval(() => void refrescarNiveles(), REFRESCO_NIVELES).unref();
|
|
|
303
313
|
// Es OPCIONAL: sin X402_PRICE en el .env, esta ruta no existe y tu agente
|
|
304
314
|
// funciona igual solo con encargos del escrow.
|
|
305
315
|
//
|
|
306
|
-
//
|
|
307
|
-
//
|
|
316
|
+
// Se cobra en una de las monedas que Panal acepta: $PANAL (por defecto), MON,
|
|
317
|
+
// GHO, USDC o AUSD. Las de token tienen `permit`: el cliente firma y tú
|
|
318
|
+
// ejecutas el cobro. MON no lo tiene: el cliente te MANDA la transferencia
|
|
319
|
+
// (paga él su gas) y tú solo compruebas en la cadena que es la de su
|
|
320
|
+
// cotización. El nombre y los decimales salen de la lista del SDK
|
|
321
|
+
// (`x402Currencies`), no de lo que se escriba aquí: así el precio que ves en
|
|
322
|
+
// el .env es el que ven los clientes.
|
|
323
|
+
//
|
|
324
|
+
// X402_TOKEN=USDC
|
|
325
|
+
// X402_PRICE=0.05 # cinco céntimos: se lee en la moneda elegida
|
|
308
326
|
// ---------------------------------------------------------------------------
|
|
309
327
|
|
|
328
|
+
const X402_MONEDA: X402Currency | null = (() => {
|
|
329
|
+
const raw = process.env.X402_TOKEN?.trim() || MAINNET_ADDRESSES.panalToken;
|
|
330
|
+
const moneda = x402CurrencyByName(raw);
|
|
331
|
+
if (!moneda) {
|
|
332
|
+
const aceptadas = x402Currencies().map((c) => c.symbol).join(', ');
|
|
333
|
+
console.error(`X402_TOKEN="${raw}" no es una moneda que Panal acepte (${aceptadas}): el cobro por llamada queda desactivado.`);
|
|
334
|
+
}
|
|
335
|
+
return moneda;
|
|
336
|
+
})();
|
|
310
337
|
const X402_PRICE = (() => {
|
|
311
338
|
const raw = process.env.X402_PRICE?.trim();
|
|
312
|
-
if (!raw) return null;
|
|
339
|
+
if (!raw || !X402_MONEDA) return null;
|
|
313
340
|
try {
|
|
314
|
-
|
|
315
|
-
|
|
341
|
+
// En los decimales de SU moneda. Con parseEther, «0.05» en USDC eran
|
|
342
|
+
// 5·10¹⁶ unidades: cincuenta mil millones de dólares por pregunta.
|
|
343
|
+
const unidades = parseX402Amount(raw, X402_MONEDA);
|
|
344
|
+
return unidades > 0n ? unidades : null;
|
|
316
345
|
} catch {
|
|
317
346
|
console.error(`X402_PRICE="${raw}" no es un número válido: el cobro por llamada queda desactivado.`);
|
|
318
347
|
return null;
|
|
319
348
|
}
|
|
320
349
|
})();
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const
|
|
350
|
+
// Con la moneda desactivada se quedan los de $PANAL: los usa el log y el
|
|
351
|
+
// presupuesto de subcontratar, que siguen leyéndose aunque no se cobre.
|
|
352
|
+
const X402_TOKEN: Address = X402_MONEDA?.address ?? (MAINNET_ADDRESSES.panalToken as Address);
|
|
353
|
+
const X402_SYMBOL = X402_MONEDA?.symbol ?? '$PANAL';
|
|
354
|
+
const X402_DECIMALES = X402_MONEDA?.decimals ?? 18;
|
|
355
|
+
/** Si se cobra en MON: transferencia del cliente en vez de permit. */
|
|
356
|
+
const X402_EN_MON = X402_MONEDA?.scheme === X402_NATIVE_SCHEME;
|
|
357
|
+
/**
|
|
358
|
+
* Con MON, el secreto con el que se firman las cotizaciones y las
|
|
359
|
+
* transferencias ya cobradas. Basta en memoria: una cotización vale 5 minutos,
|
|
360
|
+
* y al reiniciar se pide otra.
|
|
361
|
+
*/
|
|
362
|
+
const X402_SECRETO = newQuoteSecret();
|
|
363
|
+
const X402_USADOS = new Set<string>();
|
|
364
|
+
if (process.env.X402_SYMBOL?.trim() && process.env.X402_SYMBOL.trim() !== X402_SYMBOL) {
|
|
365
|
+
console.warn(`[panal] X402_SYMBOL ya no se usa: el nombre sale de X402_TOKEN, y es ${X402_SYMBOL}.`);
|
|
366
|
+
}
|
|
326
367
|
// En inglés porque viaja en el 402 y lo lee un desconocido de cualquier parte.
|
|
327
368
|
// Cámbialo por lo tuyo con X402_DESCRIPTION en el .env.
|
|
328
369
|
const X402_DESCRIPTION = process.env.X402_DESCRIPTION?.trim() || 'One question to the agent, answered on the spot.';
|
|
@@ -352,7 +393,7 @@ const SUBCONTRATA_MAX = (() => {
|
|
|
352
393
|
const raw = process.env.SUBCONTRATA_MAX?.trim();
|
|
353
394
|
if (!raw) return 0n;
|
|
354
395
|
try {
|
|
355
|
-
const wei =
|
|
396
|
+
const wei = parseUnits(raw, X402_DECIMALES);
|
|
356
397
|
return wei > 0n ? wei : 0n;
|
|
357
398
|
} catch {
|
|
358
399
|
console.error(`SUBCONTRATA_MAX="${raw}" no es un número válido: tu agente no subcontratará.`);
|
|
@@ -762,6 +803,9 @@ function contexto(
|
|
|
762
803
|
}
|
|
763
804
|
const res = await panal.ask(skill, pregunta, {
|
|
764
805
|
maxSpend: presupuesto,
|
|
806
|
+
// En la moneda del presupuesto, que es la del x402: solo se cotiza con
|
|
807
|
+
// quien cobre en ella.
|
|
808
|
+
asset: X402_TOKEN,
|
|
765
809
|
skillsPermitidas: SUBCONTRATA_SKILLS,
|
|
766
810
|
depth: SUBCONTRATA_SALTOS,
|
|
767
811
|
// El sobre recibido, si lo hay. Sin él se abre una cadena nueva.
|
|
@@ -771,7 +815,7 @@ function contexto(
|
|
|
771
815
|
exclude: [account.address],
|
|
772
816
|
});
|
|
773
817
|
console.log(
|
|
774
|
-
`[panal] consulta a ${res.agent} por ${res.paid} (${skill}) · trace ${sobre?.trace ?? 'nuevo'}`,
|
|
818
|
+
`[panal] consulta a ${res.agent} por ${formatX402Amount(res.paid, res.currency)} ${res.currency.symbol} (${skill}) · trace ${sobre?.trace ?? 'nuevo'}`,
|
|
775
819
|
);
|
|
776
820
|
return res.answer;
|
|
777
821
|
},
|
|
@@ -1313,12 +1357,17 @@ const server = createServer((req, res) => {
|
|
|
1313
1357
|
method: 'POST' as const,
|
|
1314
1358
|
path: '/x402/ask',
|
|
1315
1359
|
...(base ? { url: `${base}/x402/ask` } : {}),
|
|
1316
|
-
scheme: 'eip2612-permit',
|
|
1360
|
+
scheme: X402_EN_MON ? X402_NATIVE_SCHEME : 'eip2612-permit',
|
|
1317
1361
|
asset: X402_TOKEN,
|
|
1318
1362
|
assetSymbol: X402_SYMBOL,
|
|
1363
|
+
// Para quien lea la ficha a mano. Los clientes de Panal no se
|
|
1364
|
+
// fían de esto: sacan los decimales de su propia lista.
|
|
1365
|
+
assetDecimals: X402_DECIMALES,
|
|
1319
1366
|
amount: X402_PRICE.toString(),
|
|
1320
1367
|
payTo: account.address,
|
|
1321
|
-
howTo:
|
|
1368
|
+
howTo: X402_EN_MON
|
|
1369
|
+
? 'POST {"prompt":"…"} and you get a 402 with the quote. Send exactly `amount` MON to `payTo` and repeat with X-Payment.'
|
|
1370
|
+
: 'POST {"prompt":"…"} and you get a 402 with the quote. Sign it and repeat with X-Payment.',
|
|
1322
1371
|
}
|
|
1323
1372
|
: null;
|
|
1324
1373
|
|
|
@@ -1406,7 +1455,9 @@ const server = createServer((req, res) => {
|
|
|
1406
1455
|
throw err;
|
|
1407
1456
|
}
|
|
1408
1457
|
|
|
1409
|
-
|
|
1458
|
+
// El pago se ata a ESTA pregunta: una transferencia en MON presentada con
|
|
1459
|
+
// otra pregunta no cuadra.
|
|
1460
|
+
const recurso = resourceId('POST', '/x402/ask', prompt);
|
|
1410
1461
|
const pagoCrudo = req.headers['x-payment'];
|
|
1411
1462
|
|
|
1412
1463
|
// Sin pago: se responde 402 con el presupuesto. Este es el paso que le da
|
|
@@ -1417,6 +1468,23 @@ const server = createServer((req, res) => {
|
|
|
1417
1468
|
// consulta a la cadena antes de poder firmar.
|
|
1418
1469
|
const quien = req.headers['x-payment-payer'];
|
|
1419
1470
|
const payer = typeof quien === 'string' && isAddress(quien) ? (quien as Address) : null;
|
|
1471
|
+
if (X402_EN_MON) {
|
|
1472
|
+
res.setHeader('www-authenticate', `${X402_NATIVE_SCHEME} realm="panal"`);
|
|
1473
|
+
json(
|
|
1474
|
+
res,
|
|
1475
|
+
402,
|
|
1476
|
+
buildNativeQuote({
|
|
1477
|
+
secret: X402_SECRETO,
|
|
1478
|
+
price: X402_PRICE,
|
|
1479
|
+
payTo: account.address,
|
|
1480
|
+
resource: recurso,
|
|
1481
|
+
description: X402_DESCRIPTION,
|
|
1482
|
+
payer,
|
|
1483
|
+
}),
|
|
1484
|
+
);
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1487
|
+
const domain = await dominioPermit();
|
|
1420
1488
|
const nonce = payer ? await permitNonce(panal.publicClient, X402_TOKEN, payer).catch(() => undefined) : undefined;
|
|
1421
1489
|
|
|
1422
1490
|
res.setHeader('www-authenticate', `eip2612-permit realm="panal", chain="${domain.chainId}"`);
|
|
@@ -1437,19 +1505,38 @@ const server = createServer((req, res) => {
|
|
|
1437
1505
|
return;
|
|
1438
1506
|
}
|
|
1439
1507
|
|
|
1440
|
-
const leido =
|
|
1508
|
+
const leido = parseX402Header(pagoCrudo);
|
|
1441
1509
|
if (!leido.ok) {
|
|
1442
1510
|
json(res, 400, { error: leido.error });
|
|
1443
1511
|
return;
|
|
1444
1512
|
}
|
|
1513
|
+
if ((leido.payment.scheme === X402_NATIVE_SCHEME) !== X402_EN_MON) {
|
|
1514
|
+
json(res, 400, { error: `this agent charges in ${X402_SYMBOL}: ask for a new quote` });
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1445
1517
|
|
|
1446
1518
|
// SE COBRA ANTES DE SERVIR. Si se sirviera primero y el cobro fallara, el
|
|
1447
|
-
// trabajo estaría regalado y no habría forma de recuperarlo.
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1519
|
+
// trabajo estaría regalado y no habría forma de recuperarlo. En MON el
|
|
1520
|
+
// dinero ya lo mandó el cliente: se comprueba en la cadena que es el de
|
|
1521
|
+
// su cotización y que no se ha usado antes.
|
|
1522
|
+
const cobro =
|
|
1523
|
+
leido.payment.scheme === X402_NATIVE_SCHEME
|
|
1524
|
+
? await verifyNativePayment(
|
|
1525
|
+
{ publicClient: panal.publicClient, payee: account.address, secret: X402_SECRETO, used: X402_USADOS },
|
|
1526
|
+
leido.payment,
|
|
1527
|
+
{ price: X402_PRICE, resource: recurso },
|
|
1528
|
+
)
|
|
1529
|
+
: await verifyAndSettle(
|
|
1530
|
+
{
|
|
1531
|
+
publicClient: panal.publicClient,
|
|
1532
|
+
walletClient: panal.walletClient ?? null,
|
|
1533
|
+
token: X402_TOKEN,
|
|
1534
|
+
domain: await dominioPermit(),
|
|
1535
|
+
payee: account.address,
|
|
1536
|
+
},
|
|
1537
|
+
leido.payment,
|
|
1538
|
+
X402_PRICE,
|
|
1539
|
+
);
|
|
1453
1540
|
if (!cobro.ok) {
|
|
1454
1541
|
json(res, cobro.status, { error: cobro.error });
|
|
1455
1542
|
return;
|