@panal/sdk 0.12.0 → 0.14.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/dist/agent-card.d.ts +78 -0
- package/dist/agent-card.js +89 -0
- package/dist/client.d.ts +26 -0
- package/dist/client.js +31 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/llm.js +12 -2
- package/dist/types.d.ts +29 -1
- package/dist/types.js +82 -0
- package/package.json +1 -1
package/dist/agent-card.d.ts
CHANGED
|
@@ -68,6 +68,36 @@ export interface FichaGetResult {
|
|
|
68
68
|
* el del bot sí— y un esquema que obligue a rellenar lo que no se sabe termina
|
|
69
69
|
* rellenándose con mentiras.
|
|
70
70
|
*/
|
|
71
|
+
/**
|
|
72
|
+
* Un nivel de servicio: cuánto cobra y cuánto acepta a cambio.
|
|
73
|
+
*
|
|
74
|
+
* El registro guarda UN `pricePerTask` por agente y no va a guardar más, así
|
|
75
|
+
* que este es el único sitio donde un agente puede anunciar que hace el mismo
|
|
76
|
+
* trabajo en varios tamaños. Lo publica quien lo va a cumplir, que es la
|
|
77
|
+
* diferencia entre un nivel y un multiplicador inventado por el escaparate.
|
|
78
|
+
*
|
|
79
|
+
* Los topes se declaran EN CARACTERES a propósito. Son lo que el cliente puede
|
|
80
|
+
* contar antes de pagar y lo que cualquiera puede recontar después, porque el
|
|
81
|
+
* encargo se ancla en la cadena y el tamaño de cada adjunto viaja dentro de su
|
|
82
|
+
* manifiesto. Un nivel que prometiera «más esfuerzo» no se podría comprobar.
|
|
83
|
+
*/
|
|
84
|
+
export interface FichaNivel {
|
|
85
|
+
/** Nombre corto para enseñar. El cliente elige por aquí. */
|
|
86
|
+
name?: string;
|
|
87
|
+
/** Una línea de qué compra. */
|
|
88
|
+
description?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Lo que hay que bloquear para tener este nivel, en unidades mínimas de la
|
|
91
|
+
* moneda del agente y como cadena decimal, igual que `price.amountWei`.
|
|
92
|
+
*/
|
|
93
|
+
amountWei?: string;
|
|
94
|
+
/** Tope de caracteres del encargo en este nivel. */
|
|
95
|
+
maxBriefChars?: number;
|
|
96
|
+
/** Tope de caracteres que aporta CADA adjunto. */
|
|
97
|
+
maxAttachChars?: number;
|
|
98
|
+
/** Y el de todos los adjuntos juntos. */
|
|
99
|
+
maxAttachCharsTotal?: number;
|
|
100
|
+
}
|
|
71
101
|
export interface AgentCard {
|
|
72
102
|
/** La dirección on-chain que este dominio declara suya. Es lo que verifica. */
|
|
73
103
|
agent?: Address;
|
|
@@ -84,6 +114,16 @@ export interface AgentCard {
|
|
|
84
114
|
currency?: Address;
|
|
85
115
|
symbol?: string;
|
|
86
116
|
} | null;
|
|
117
|
+
/**
|
|
118
|
+
* Los niveles que ofrece, de menor a mayor. Opcional y casi siempre ausente.
|
|
119
|
+
*
|
|
120
|
+
* AUSENTE NO ES «tiene un nivel»: es que este agente no los ofrece, y hay
|
|
121
|
+
* que tratarlo exactamente como se le trataba antes de que esto existiera.
|
|
122
|
+
* Quien lee no debe inventarle niveles a partir de `price`, que es
|
|
123
|
+
* justamente lo que hacía el escaparate y por lo que enseñaba precios que
|
|
124
|
+
* luego no se cobraban.
|
|
125
|
+
*/
|
|
126
|
+
tiers?: FichaNivel[];
|
|
87
127
|
active?: boolean | null;
|
|
88
128
|
contracts?: {
|
|
89
129
|
escrow?: Address;
|
|
@@ -119,3 +159,41 @@ export declare function leerX402(card: unknown): FichaX402 | null;
|
|
|
119
159
|
* basura en un campo.
|
|
120
160
|
*/
|
|
121
161
|
export declare function leerMaxBriefChars(card: unknown): number | null;
|
|
162
|
+
/** Un nivel ya validado: precio en bigint y `null` en todo lo que la ficha no diga. */
|
|
163
|
+
export interface Nivel {
|
|
164
|
+
name: string | null;
|
|
165
|
+
description: string | null;
|
|
166
|
+
/** Lo que hay que bloquear, en unidades mínimas de la moneda del agente. */
|
|
167
|
+
wei: bigint;
|
|
168
|
+
maxBriefChars: number | null;
|
|
169
|
+
maxAttachChars: number | null;
|
|
170
|
+
maxAttachCharsTotal: number | null;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Los niveles que ofrece un agente, de menor a mayor precio.
|
|
174
|
+
*
|
|
175
|
+
* Devuelve `[]` cuando la ficha no los declara, y eso significa que el agente
|
|
176
|
+
* NO ofrece niveles: quien llama tiene que seguir tratándolo como siempre, no
|
|
177
|
+
* fabricarle uno a partir de `price`.
|
|
178
|
+
*
|
|
179
|
+
* La ficha la sirve un desconocido, así que un nivel sin precio legible se cae
|
|
180
|
+
* de la lista en vez de tumbarla entera: un campo mal escrito no puede dejar
|
|
181
|
+
* incontratable a un agente que sí tiene otros niveles buenos.
|
|
182
|
+
*/
|
|
183
|
+
export declare function leerNiveles(card: unknown): Nivel[];
|
|
184
|
+
/**
|
|
185
|
+
* Qué nivel compró quien bloqueó `pagado`.
|
|
186
|
+
*
|
|
187
|
+
* EL NIVEL LO DECIDE LA CADENA, NO EL ENCARGO. El brief lo escribe el cliente
|
|
188
|
+
* y podría afirmar que compró el más caro; el importe bloqueado no se puede
|
|
189
|
+
* discutir. Por eso esta función toma un `bigint` del escrow y nada más.
|
|
190
|
+
*
|
|
191
|
+
* Se queda con el nivel más alto que quepa en lo pagado. Pagar de más da el
|
|
192
|
+
* nivel pagado, no el siguiente: quien bloquea 10 veces el precio del mayor
|
|
193
|
+
* sigue comprando el mayor, y el resto es cosa del agente y su cliente.
|
|
194
|
+
*
|
|
195
|
+
* `null` significa que lo bloqueado no llega ni al nivel más barato. Qué hacer
|
|
196
|
+
* entonces —trabajar igual, devolver, no empezar— lo decide el agente; el SDK
|
|
197
|
+
* no lo va a decidir por él.
|
|
198
|
+
*/
|
|
199
|
+
export declare function nivelPara(niveles: Nivel[], pagado: bigint): Nivel | null;
|
package/dist/agent-card.js
CHANGED
|
@@ -52,3 +52,92 @@ export function leerMaxBriefChars(card) {
|
|
|
52
52
|
const max = card?.endpoints?.postBrief?.maxBriefChars;
|
|
53
53
|
return typeof max === 'number' && Number.isInteger(max) && max > 0 ? max : null;
|
|
54
54
|
}
|
|
55
|
+
/** Tope de niveles que se leen de una ficha ajena. Ocho ya son demasiados para elegir. */
|
|
56
|
+
const MAX_NIVELES = 8;
|
|
57
|
+
/**
|
|
58
|
+
* Cuántas entradas se miran para sacar esos ocho.
|
|
59
|
+
*
|
|
60
|
+
* El recorte va DESPUÉS de filtrar, no antes: recortando primero, un nivel
|
|
61
|
+
* bueno colocado detrás de ocho mal escritos desaparecía sin que nadie lo
|
|
62
|
+
* notara. Y aun así se mira un número fijo, porque la lista la escribe un
|
|
63
|
+
* desconocido y nadie tiene ocho niveles buenos detrás de doscientos malos.
|
|
64
|
+
*/
|
|
65
|
+
const MAX_MIRADOS = 200;
|
|
66
|
+
/** Un entero positivo, o `null`. Misma regla que `leerMaxBriefChars`. */
|
|
67
|
+
function tope(v) {
|
|
68
|
+
return typeof v === 'number' && Number.isInteger(v) && v > 0 ? v : null;
|
|
69
|
+
}
|
|
70
|
+
/** Texto de una ficha ajena: recortado, porque va a un escaparate. */
|
|
71
|
+
function letrero(v, max) {
|
|
72
|
+
if (typeof v !== 'string')
|
|
73
|
+
return null;
|
|
74
|
+
const limpio = v.replace(/\s+/g, ' ').trim().slice(0, max);
|
|
75
|
+
return limpio || null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Los niveles que ofrece un agente, de menor a mayor precio.
|
|
79
|
+
*
|
|
80
|
+
* Devuelve `[]` cuando la ficha no los declara, y eso significa que el agente
|
|
81
|
+
* NO ofrece niveles: quien llama tiene que seguir tratándolo como siempre, no
|
|
82
|
+
* fabricarle uno a partir de `price`.
|
|
83
|
+
*
|
|
84
|
+
* La ficha la sirve un desconocido, así que un nivel sin precio legible se cae
|
|
85
|
+
* de la lista en vez de tumbarla entera: un campo mal escrito no puede dejar
|
|
86
|
+
* incontratable a un agente que sí tiene otros niveles buenos.
|
|
87
|
+
*/
|
|
88
|
+
export function leerNiveles(card) {
|
|
89
|
+
const crudos = card?.tiers;
|
|
90
|
+
if (!Array.isArray(crudos))
|
|
91
|
+
return [];
|
|
92
|
+
const out = [];
|
|
93
|
+
for (const n of crudos.slice(0, MAX_MIRADOS)) {
|
|
94
|
+
if (out.length >= MAX_NIVELES)
|
|
95
|
+
break;
|
|
96
|
+
if (!n || typeof n !== 'object')
|
|
97
|
+
continue;
|
|
98
|
+
const wei = enteroWei(n.amountWei);
|
|
99
|
+
if (wei === null)
|
|
100
|
+
continue;
|
|
101
|
+
out.push({
|
|
102
|
+
name: letrero(n.name, 60),
|
|
103
|
+
description: letrero(n.description, 200),
|
|
104
|
+
wei,
|
|
105
|
+
maxBriefChars: tope(n.maxBriefChars),
|
|
106
|
+
maxAttachChars: tope(n.maxAttachChars),
|
|
107
|
+
maxAttachCharsTotal: tope(n.maxAttachCharsTotal),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// De menor a mayor: es el orden en que se enseñan y el que hace que
|
|
111
|
+
// `nivelPara` pueda quedarse con el último que entra en lo pagado.
|
|
112
|
+
return out.sort((a, b) => (a.wei < b.wei ? -1 : a.wei > b.wei ? 1 : 0));
|
|
113
|
+
}
|
|
114
|
+
/** `amountWei` como bigint. Solo dígitos: `BigInt('0x10')` valdría 16 y no es eso. */
|
|
115
|
+
function enteroWei(v) {
|
|
116
|
+
if (typeof v !== 'string' || !/^\d+$/.test(v.trim()))
|
|
117
|
+
return null;
|
|
118
|
+
const n = BigInt(v.trim());
|
|
119
|
+
return n > 0n ? n : null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Qué nivel compró quien bloqueó `pagado`.
|
|
123
|
+
*
|
|
124
|
+
* EL NIVEL LO DECIDE LA CADENA, NO EL ENCARGO. El brief lo escribe el cliente
|
|
125
|
+
* y podría afirmar que compró el más caro; el importe bloqueado no se puede
|
|
126
|
+
* discutir. Por eso esta función toma un `bigint` del escrow y nada más.
|
|
127
|
+
*
|
|
128
|
+
* Se queda con el nivel más alto que quepa en lo pagado. Pagar de más da el
|
|
129
|
+
* nivel pagado, no el siguiente: quien bloquea 10 veces el precio del mayor
|
|
130
|
+
* sigue comprando el mayor, y el resto es cosa del agente y su cliente.
|
|
131
|
+
*
|
|
132
|
+
* `null` significa que lo bloqueado no llega ni al nivel más barato. Qué hacer
|
|
133
|
+
* entonces —trabajar igual, devolver, no empezar— lo decide el agente; el SDK
|
|
134
|
+
* no lo va a decidir por él.
|
|
135
|
+
*/
|
|
136
|
+
export function nivelPara(niveles, pagado) {
|
|
137
|
+
let elegido = null;
|
|
138
|
+
for (const n of niveles) {
|
|
139
|
+
if (n.wei <= pagado)
|
|
140
|
+
elegido = n;
|
|
141
|
+
}
|
|
142
|
+
return elegido;
|
|
143
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -90,6 +90,18 @@ export interface HireResult {
|
|
|
90
90
|
* búsqueda y no cambia nada.
|
|
91
91
|
*/
|
|
92
92
|
export declare function variantesDeSkill(skill: string): string[];
|
|
93
|
+
/**
|
|
94
|
+
* Las variantes que se pueden buscar de verdad, dada la lista de permitidas.
|
|
95
|
+
*
|
|
96
|
+
* Va aparte y exportada porque es LA regla que impide que un agente de código
|
|
97
|
+
* acabe pagando a uno de vídeo. Sin lista devuelve todas, que es como se
|
|
98
|
+
* comportaba esto antes de que la lista existiera.
|
|
99
|
+
*
|
|
100
|
+
* La comparación normaliza espacios y mayúsculas: la lista la escribe una
|
|
101
|
+
* persona en su `agent.ts` y la skill la escribe un modelo, y no van a coincidir
|
|
102
|
+
* en el formato.
|
|
103
|
+
*/
|
|
104
|
+
export declare function variantesPermitidas(skill: string, permitidas?: string[]): string[];
|
|
93
105
|
export declare class PanalClient {
|
|
94
106
|
readonly network: PanalNetwork;
|
|
95
107
|
readonly addresses: PanalAddresses;
|
|
@@ -313,6 +325,20 @@ export declare class PanalClient {
|
|
|
313
325
|
envelope?: CallEnvelope | null;
|
|
314
326
|
/** Saltos permitidos al abrir una cadena nueva. */
|
|
315
327
|
depth?: number;
|
|
328
|
+
/**
|
|
329
|
+
* LAS ÚNICAS SKILLS QUE SE PUEDEN COMPRAR. Sin esto, cualquiera.
|
|
330
|
+
*
|
|
331
|
+
* Existe por la degradación de aquí abajo. Quien pide la skill suele ser
|
|
332
|
+
* un modelo, y al no encontrar a nadie se recorta por la izquierda:
|
|
333
|
+
* "python video encoding" acaba buscando "video". Para un agente de
|
|
334
|
+
* código eso es contratar a un agente de vídeo con su dinero, y lo peor
|
|
335
|
+
* es que el resultado parece razonable — pagó, le contestaron, entregó.
|
|
336
|
+
*
|
|
337
|
+
* Con esta lista, una variante que no esté en ella NI SE BUSCA. La
|
|
338
|
+
* comprobación va antes de cotizar, así que un intento prohibido no
|
|
339
|
+
* cuesta ni una petición.
|
|
340
|
+
*/
|
|
341
|
+
skillsPermitidas?: string[];
|
|
316
342
|
}): Promise<AskResult & {
|
|
317
343
|
agent: Address;
|
|
318
344
|
skill: string;
|
package/dist/client.js
CHANGED
|
@@ -63,6 +63,21 @@ export function variantesDeSkill(skill) {
|
|
|
63
63
|
}
|
|
64
64
|
return out;
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Las variantes que se pueden buscar de verdad, dada la lista de permitidas.
|
|
68
|
+
*
|
|
69
|
+
* Va aparte y exportada porque es LA regla que impide que un agente de código
|
|
70
|
+
* acabe pagando a uno de vídeo. Sin lista devuelve todas, que es como se
|
|
71
|
+
* comportaba esto antes de que la lista existiera.
|
|
72
|
+
*
|
|
73
|
+
* La comparación normaliza espacios y mayúsculas: la lista la escribe una
|
|
74
|
+
* persona en su `agent.ts` y la skill la escribe un modelo, y no van a coincidir
|
|
75
|
+
* en el formato.
|
|
76
|
+
*/
|
|
77
|
+
export function variantesPermitidas(skill, permitidas) {
|
|
78
|
+
const blancas = permitidas?.map((p) => p.trim().replace(/\s+/g, ' ').toLowerCase()).filter(Boolean);
|
|
79
|
+
return variantesDeSkill(skill).filter((v) => !blancas || blancas.includes(v.replace(/\s+/g, ' ').toLowerCase()));
|
|
80
|
+
}
|
|
66
81
|
function esNombre(v) {
|
|
67
82
|
if (v === null || typeof v !== 'object')
|
|
68
83
|
return false;
|
|
@@ -188,11 +203,16 @@ export class PanalClient {
|
|
|
188
203
|
catch {
|
|
189
204
|
continue;
|
|
190
205
|
}
|
|
206
|
+
// La ficha en crudo, si el indexador la manda. Los enlaces del creador
|
|
207
|
+
// salen de ahí, y quedarse con ella entera es además más fiel que
|
|
208
|
+
// recomponerla: lo que este SDK no entienda se conserva tal cual.
|
|
209
|
+
const uri = typeof raw.metadataURI === 'string' ? raw.metadataURI : '';
|
|
191
210
|
const metadata = {
|
|
192
211
|
name: typeof raw.name === 'string' ? raw.name : '',
|
|
193
212
|
description: typeof raw.description === 'string' ? raw.description : '',
|
|
194
213
|
skills,
|
|
195
214
|
botUrl: typeof raw.botUrl === 'string' ? raw.botUrl : null,
|
|
215
|
+
links: parseAgentMetadata(uri).links,
|
|
196
216
|
};
|
|
197
217
|
out.push({
|
|
198
218
|
address: getAddress(address),
|
|
@@ -201,7 +221,7 @@ export class PanalClient {
|
|
|
201
221
|
currency: getAddress(typeof raw.currency === 'string' && /^0x[0-9a-fA-F]{40}$/.test(raw.currency) ? raw.currency : NATIVE_CURRENCY),
|
|
202
222
|
active: raw.active !== false,
|
|
203
223
|
registeredAt,
|
|
204
|
-
metadataURI: formatAgentMetadata(metadata),
|
|
224
|
+
metadataURI: uri || formatAgentMetadata(metadata),
|
|
205
225
|
metadata,
|
|
206
226
|
// Solo `true` cuenta como verificado. Un indexador viejo no manda el
|
|
207
227
|
// campo, y tratar «no lo sé» como «sí» es justo al revés de lo que
|
|
@@ -681,9 +701,18 @@ export class PanalClient {
|
|
|
681
701
|
// núcleo va al final: "Spanish tax law" → "tax law" → "law". Así se
|
|
682
702
|
// generaliza sin perder de qué se estaba hablando; recortar por la derecha
|
|
683
703
|
// dejaría "Spanish", que casaría con cualquier cosa española.
|
|
704
|
+
// Las variantes que este agente tiene permitido comprar. Sin lista, todas.
|
|
705
|
+
const permitidas = options.skillsPermitidas;
|
|
706
|
+
const variantes = variantesPermitidas(skill, permitidas);
|
|
707
|
+
if (!variantes.length) {
|
|
708
|
+
throw new X402Error(`Este agente no tiene permitido subcontratar "${skill}". ` +
|
|
709
|
+
(permitidas?.length
|
|
710
|
+
? `Sólo puede comprar: ${permitidas.join(', ')}.`
|
|
711
|
+
: 'No tiene ninguna skill permitida.'));
|
|
712
|
+
}
|
|
684
713
|
let candidates = [];
|
|
685
714
|
let usada = skill;
|
|
686
|
-
for (const intento of
|
|
715
|
+
for (const intento of variantes) {
|
|
687
716
|
candidates = (await this.searchAgents(intento, { skill: intento }))
|
|
688
717
|
.filter((a) => !excluded.has(a.address.toLowerCase()) && a.metadata.botUrl)
|
|
689
718
|
.slice(0, options.maxCandidates ?? 5);
|
package/dist/index.d.ts
CHANGED
|
@@ -23,8 +23,8 @@ export { PanalClient, createPanalClient } from './client.js';
|
|
|
23
23
|
export type { PanalClientOptions, HireParams, HireResult } from './client.js';
|
|
24
24
|
export { monad, monadTestnet, addressesFor, chainFor, MAINNET_ADDRESSES, TESTNET_ADDRESSES, NATIVE_CURRENCY, FEE_BPS, AUTO_RELEASE_SECONDS, DISPUTE_TIMEOUT_SECONDS, } from './chains.js';
|
|
25
25
|
export type { PanalNetwork, PanalAddresses } from './chains.js';
|
|
26
|
-
export { TaskStatus, TASK_STATUS_LABEL, formatAgentMetadata, parseAgentMetadata, } from './types.js';
|
|
27
|
-
export type { Agent, AgentMetadata, Task } from './types.js';
|
|
26
|
+
export { TaskStatus, TASK_STATUS_LABEL, formatAgentMetadata, normalizeAgentLink, parseAgentMetadata, AGENT_LINK_KEYS, } from './types.js';
|
|
27
|
+
export type { Agent, AgentLinkKey, AgentLinks, AgentMetadata, Task } from './types.js';
|
|
28
28
|
export { erc20Abi, escrowAbi, registryAbi } from './abis.js';
|
|
29
29
|
export { X402_SCHEME, X402Error, payAndAsk, quoteAsk } from './x402.js';
|
|
30
30
|
export type { AskResult, PayAndAskOptions, PermitDomain, X402Accept, X402Quote } from './x402.js';
|
|
@@ -38,5 +38,5 @@ export type { SettleDeps, SettleResult, X402Payment, X402ServerAccept, X402Serve
|
|
|
38
38
|
export { ENVELOPE_HEADERS, DEFAULT_DEPTH, MAX_DEPTH, BudgetExhausted, DepthExhausted, LoopDetected, assertCanServe, descend, envelopeHeaders, newEnvelope, parseEnvelope, remainingBudget, } from './envelope.js';
|
|
39
39
|
export type { CallEnvelope } from './envelope.js';
|
|
40
40
|
export type { UrlGuardOptions } from './net.js';
|
|
41
|
-
export { leerDireccion, leerMaxBriefChars, leerX402 } from './agent-card.js';
|
|
42
|
-
export type { AgentCard, FichaGetResult, FichaPostBrief, FichaX402 } from './agent-card.js';
|
|
41
|
+
export { leerDireccion, leerMaxBriefChars, leerNiveles, leerX402, nivelPara } from './agent-card.js';
|
|
42
|
+
export type { AgentCard, FichaGetResult, FichaNivel, FichaPostBrief, FichaX402, Nivel } from './agent-card.js';
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
export { PanalClient, createPanalClient } from './client.js';
|
|
23
23
|
export { monad, monadTestnet, addressesFor, chainFor, MAINNET_ADDRESSES, TESTNET_ADDRESSES, NATIVE_CURRENCY, FEE_BPS, AUTO_RELEASE_SECONDS, DISPUTE_TIMEOUT_SECONDS, } from './chains.js';
|
|
24
|
-
export { TaskStatus, TASK_STATUS_LABEL, formatAgentMetadata, parseAgentMetadata, } from './types.js';
|
|
24
|
+
export { TaskStatus, TASK_STATUS_LABEL, formatAgentMetadata, normalizeAgentLink, parseAgentMetadata, AGENT_LINK_KEYS, } from './types.js';
|
|
25
25
|
export { erc20Abi, escrowAbi, registryAbi } from './abis.js';
|
|
26
26
|
// x402: pagar a otro agente por una consulta, sin escrow y sin humano.
|
|
27
27
|
export { X402_SCHEME, X402Error, payAndAsk, quoteAsk } from './x402.js';
|
|
@@ -39,4 +39,4 @@ export { X402_VERSION, X402_SERVER_SCHEME, buildQuote, enqueueByPayer, parsePaym
|
|
|
39
39
|
export { ENVELOPE_HEADERS, DEFAULT_DEPTH, MAX_DEPTH, BudgetExhausted, DepthExhausted, LoopDetected, assertCanServe, descend, envelopeHeaders, newEnvelope, parseEnvelope, remainingBudget, } from './envelope.js';
|
|
40
40
|
// La ficha de GET /agent.json: un solo formato, y lectores que perdonan el
|
|
41
41
|
// antiguo. En agent-card.ts está por qué llegó a haber dos.
|
|
42
|
-
export { leerDireccion, leerMaxBriefChars, leerX402 } from './agent-card.js';
|
|
42
|
+
export { leerDireccion, leerMaxBriefChars, leerNiveles, leerX402, nivelPara } from './agent-card.js';
|
package/dist/llm.js
CHANGED
|
@@ -42,8 +42,18 @@ export const PROVEEDORES = {
|
|
|
42
42
|
dialecto: 'gemini',
|
|
43
43
|
modeloSugerido: 'gemini-2.0-flash',
|
|
44
44
|
},
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// `moonshot-v1-8k` era la sugerencia y es de la generación anterior: medido
|
|
46
|
+
// contra la API en agosto de 2026, es además el único de la lista que
|
|
47
|
+
// devolvía 429 por límite de cuota. Quien escribía `LLM_PROVIDER=kimi` sin
|
|
48
|
+
// más se llevaba el peor modelo disponible.
|
|
49
|
+
//
|
|
50
|
+
// `kimi-k2.6` es de propósito general y cabe de sobra en el timeout por
|
|
51
|
+
// defecto. `kimi-k3` responde mejor y tarda bastante más —medido: entre 40 s
|
|
52
|
+
// y 114 s en encargos normales, contra un timeout de 120 s—, así que se
|
|
53
|
+
// elige a mano con LLM_MODEL y no por defecto. Los `kimi-k2.7-code` son los
|
|
54
|
+
// rápidos, y para código.
|
|
55
|
+
kimi: { baseUrl: 'https://api.moonshot.ai/v1', dialecto: 'openai', modeloSugerido: 'kimi-k2.6' },
|
|
56
|
+
moonshot: { baseUrl: 'https://api.moonshot.ai/v1', dialecto: 'openai', modeloSugerido: 'kimi-k2.6' },
|
|
47
57
|
grok: { baseUrl: 'https://api.x.ai/v1', dialecto: 'openai', modeloSugerido: 'grok-2-vision-1212' },
|
|
48
58
|
xai: { baseUrl: 'https://api.x.ai/v1', dialecto: 'openai', modeloSugerido: 'grok-2-vision-1212' },
|
|
49
59
|
glm: { baseUrl: 'https://open.bigmodel.cn/api/paas/v4', dialecto: 'openai', modeloSugerido: 'glm-4v' },
|
package/dist/types.d.ts
CHANGED
|
@@ -39,7 +39,33 @@ export interface AgentMetadata {
|
|
|
39
39
|
* se entrega por otros medios y en la cadena solo queda su hash.
|
|
40
40
|
*/
|
|
41
41
|
botUrl: string | null;
|
|
42
|
+
/**
|
|
43
|
+
* El logo y los enlaces del creador, si los publicó. Todo opcional.
|
|
44
|
+
*
|
|
45
|
+
* Van en la misma cadena, como tokens `clave:valor`:
|
|
46
|
+
*
|
|
47
|
+
* "Lint · Revisa contratos · solidity · logo:https://lint.dev/l.png · github:lintlabs"
|
|
48
|
+
*
|
|
49
|
+
* Se leen aparte por lo mismo que `bot:`: sin apartarlos, el `logo:` de un
|
|
50
|
+
* agente saldría de skill suya. Un token solo cuenta si su valor vale —una
|
|
51
|
+
* `https://` de verdad, o un usuario con forma de usuario—, porque la
|
|
52
|
+
* descripción es texto libre y alguien va a escribir «web: la mejor» dentro.
|
|
53
|
+
*/
|
|
54
|
+
links: AgentLinks;
|
|
42
55
|
}
|
|
56
|
+
/** Las claves de marca que entiende el marketplace, en el orden en que se pintan. */
|
|
57
|
+
export declare const AGENT_LINK_KEYS: readonly ["logo", "web", "github", "x", "telegram"];
|
|
58
|
+
export type AgentLinkKey = (typeof AGENT_LINK_KEYS)[number];
|
|
59
|
+
/** Cadena vacía = el agente no lo publicó. */
|
|
60
|
+
export type AgentLinks = Record<AgentLinkKey, string>;
|
|
61
|
+
/**
|
|
62
|
+
* Deja un valor de marca como se guarda, o vacío si no sirve.
|
|
63
|
+
*
|
|
64
|
+
* Acepta las tres formas que la gente usa de verdad —`@panal`, `panal` y
|
|
65
|
+
* `https://x.com/panal`— y guarda siempre la misma. La referencia es
|
|
66
|
+
* `src/lib/marca.ts` del marketplace: si cambias una, cambia la otra.
|
|
67
|
+
*/
|
|
68
|
+
export declare function normalizeAgentLink(key: AgentLinkKey, raw: string): string;
|
|
43
69
|
/** Un agente del registry, con su metadata ya interpretada. */
|
|
44
70
|
export interface Agent {
|
|
45
71
|
/** La dirección del agente: la que ejecuta el trabajo y cobra. */
|
|
@@ -119,7 +145,9 @@ export interface Task {
|
|
|
119
145
|
* descripción desplazaría las skills a otro segmento y dejaría la ficha del
|
|
120
146
|
* agente descuadrada sin ningún error visible.
|
|
121
147
|
*/
|
|
122
|
-
export declare function formatAgentMetadata(meta: AgentMetadata
|
|
148
|
+
export declare function formatAgentMetadata(meta: Omit<AgentMetadata, 'links'> & {
|
|
149
|
+
links?: Partial<AgentLinks>;
|
|
150
|
+
}): string;
|
|
123
151
|
/**
|
|
124
152
|
* Lee el metadata de un agente. Nunca lanza: un agente puede haberse registrado
|
|
125
153
|
* con cualquier cadena, y un marketplace que se rompe por una ficha mal escrita
|
package/dist/types.js
CHANGED
|
@@ -23,6 +23,71 @@ export const TASK_STATUS_LABEL = {
|
|
|
23
23
|
[TaskStatus.Disputed]: 'disputed',
|
|
24
24
|
[TaskStatus.Cancelled]: 'cancelled',
|
|
25
25
|
};
|
|
26
|
+
/** Las claves de marca que entiende el marketplace, en el orden en que se pintan. */
|
|
27
|
+
export const AGENT_LINK_KEYS = ['logo', 'web', 'github', 'x', 'telegram'];
|
|
28
|
+
const NO_LINKS = { logo: '', web: '', github: '', x: '', telegram: '' };
|
|
29
|
+
/**
|
|
30
|
+
* Deja un valor de marca como se guarda, o vacío si no sirve.
|
|
31
|
+
*
|
|
32
|
+
* Acepta las tres formas que la gente usa de verdad —`@panal`, `panal` y
|
|
33
|
+
* `https://x.com/panal`— y guarda siempre la misma. La referencia es
|
|
34
|
+
* `src/lib/marca.ts` del marketplace: si cambias una, cambia la otra.
|
|
35
|
+
*/
|
|
36
|
+
export function normalizeAgentLink(key, raw) {
|
|
37
|
+
const value = raw.trim().slice(0, 120);
|
|
38
|
+
if (!value)
|
|
39
|
+
return '';
|
|
40
|
+
// Un espacio o un `·` por dentro invalida en vez de borrarse: borrarlos
|
|
41
|
+
// convertiría «dos palabras» en el usuario `dospalabras`, que es de otro.
|
|
42
|
+
if (/[\u00b7\s]/.test(value))
|
|
43
|
+
return '';
|
|
44
|
+
if (key === 'logo' || key === 'web') {
|
|
45
|
+
try {
|
|
46
|
+
const u = new URL(value);
|
|
47
|
+
return u.protocol === 'https:' && u.hostname.includes('.') ? value : '';
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const hosts = {
|
|
54
|
+
github: /^(www\.)?github\.com$/i,
|
|
55
|
+
x: /^(www\.)?(x|twitter)\.com$/i,
|
|
56
|
+
telegram: /^(www\.)?(t\.me|telegram\.me)$/i,
|
|
57
|
+
};
|
|
58
|
+
let user = value.replace(/^@/, '');
|
|
59
|
+
if (/^https?:\/\//i.test(user)) {
|
|
60
|
+
try {
|
|
61
|
+
const u = new URL(user);
|
|
62
|
+
if (!hosts[key].test(u.hostname))
|
|
63
|
+
return '';
|
|
64
|
+
user = u.pathname.replace(/^\/+|\/+$/g, '');
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
user = user.replace(/^@/, '');
|
|
71
|
+
if (!user)
|
|
72
|
+
return '';
|
|
73
|
+
const simple = /^[A-Za-z0-9][A-Za-z0-9._-]{0,38}$/;
|
|
74
|
+
if (key === 'github')
|
|
75
|
+
return simple.test(user) || /^[A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9._-]{1,100}$/.test(user) ? user : '';
|
|
76
|
+
if (key === 'telegram')
|
|
77
|
+
return simple.test(user) || /^\+[A-Za-z0-9_-]{5,64}$/.test(user) ? user : '';
|
|
78
|
+
return simple.test(user) ? user : '';
|
|
79
|
+
}
|
|
80
|
+
/** `github:panal/lint` → la pareja, o null si no es un token de marca válido. */
|
|
81
|
+
function parseLinkToken(segment) {
|
|
82
|
+
const i = segment.indexOf(':');
|
|
83
|
+
if (i <= 0)
|
|
84
|
+
return null;
|
|
85
|
+
const key = segment.slice(0, i).trim().toLowerCase();
|
|
86
|
+
if (!AGENT_LINK_KEYS.includes(key))
|
|
87
|
+
return null;
|
|
88
|
+
const value = normalizeAgentLink(key, segment.slice(i + 1));
|
|
89
|
+
return value ? [key, value] : null;
|
|
90
|
+
}
|
|
26
91
|
/**
|
|
27
92
|
* Compone el metadata en el formato que espera el marketplace.
|
|
28
93
|
*
|
|
@@ -35,6 +100,13 @@ export function formatAgentMetadata(meta) {
|
|
|
35
100
|
const parts = [clean(meta.name), clean(meta.description), meta.skills.map(clean).join(', ')];
|
|
36
101
|
if (meta.botUrl)
|
|
37
102
|
parts.push(`bot:${meta.botUrl.trim()}`);
|
|
103
|
+
// La marca al final: primero lo que el protocolo necesita, luego lo que el
|
|
104
|
+
// creador quiere enseñar. Sin enlaces sale exactamente la ficha de siempre.
|
|
105
|
+
for (const key of AGENT_LINK_KEYS) {
|
|
106
|
+
const value = normalizeAgentLink(key, meta.links?.[key] ?? '');
|
|
107
|
+
if (value)
|
|
108
|
+
parts.push(`${key}:${value}`);
|
|
109
|
+
}
|
|
38
110
|
return parts.join(' · ');
|
|
39
111
|
}
|
|
40
112
|
/**
|
|
@@ -48,6 +120,7 @@ export function parseAgentMetadata(metadataURI) {
|
|
|
48
120
|
.map((s) => s.trim())
|
|
49
121
|
.filter(Boolean);
|
|
50
122
|
let botUrl = null;
|
|
123
|
+
const links = { ...NO_LINKS };
|
|
51
124
|
const rest = [];
|
|
52
125
|
for (const seg of segments) {
|
|
53
126
|
const candidate = seg.toLowerCase().startsWith('bot:') ? seg.slice(4).trim() : null;
|
|
@@ -55,6 +128,14 @@ export function parseAgentMetadata(metadataURI) {
|
|
|
55
128
|
botUrl = candidate;
|
|
56
129
|
continue;
|
|
57
130
|
}
|
|
131
|
+
const link = parseLinkToken(seg);
|
|
132
|
+
// El primero manda: una ficha con dos `logo:` no puede quedar a merced del
|
|
133
|
+
// orden en que se lean.
|
|
134
|
+
if (link) {
|
|
135
|
+
if (!links[link[0]])
|
|
136
|
+
links[link[0]] = link[1];
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
58
139
|
rest.push(seg);
|
|
59
140
|
}
|
|
60
141
|
// Los campos que falten quedan vacíos en vez de desplazar a los siguientes:
|
|
@@ -68,5 +149,6 @@ export function parseAgentMetadata(metadataURI) {
|
|
|
68
149
|
.map((s) => s.trim())
|
|
69
150
|
.filter(Boolean),
|
|
70
151
|
botUrl,
|
|
152
|
+
links,
|
|
71
153
|
};
|
|
72
154
|
}
|