@vaia-lab/sdk 0.2.0 → 0.4.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/README.md +107 -151
- package/dist/agent-1bVw0eB8.d.cts +222 -0
- package/dist/agent-1bVw0eB8.d.ts +222 -0
- package/dist/cli.js +252 -0
- package/dist/index.cjs +495 -12
- package/dist/index.d.cts +487 -5
- package/dist/index.d.ts +487 -5
- package/dist/index.js +477 -11
- package/dist/react/index.cjs +1240 -0
- package/dist/react/index.d.cts +95 -0
- package/dist/react/index.d.ts +95 -0
- package/dist/react/index.js +1228 -0
- package/package.json +45 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -23,10 +23,12 @@ __export(gandia_exports, {
|
|
|
23
23
|
// src/crypto.ts
|
|
24
24
|
var enc = new TextEncoder();
|
|
25
25
|
function hexToBytes(hex) {
|
|
26
|
-
if (hex.length % 2 !== 0
|
|
27
|
-
|
|
26
|
+
if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) {
|
|
27
|
+
throw new Error("Invalid hex string");
|
|
28
|
+
}
|
|
29
|
+
const bytes = new Uint8Array(new ArrayBuffer(hex.length / 2));
|
|
28
30
|
for (let i = 0; i < hex.length; i += 2) {
|
|
29
|
-
bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
|
|
31
|
+
bytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);
|
|
30
32
|
}
|
|
31
33
|
return bytes;
|
|
32
34
|
}
|
|
@@ -70,9 +72,7 @@ async function verify(request, secret) {
|
|
|
70
72
|
const sigHeader = headers.get("x-gandia-signature") ?? "";
|
|
71
73
|
const tsHeader = headers.get("x-gandia-timestamp") ?? "";
|
|
72
74
|
const callId = headers.get("x-gandia-call-id") ?? "";
|
|
73
|
-
|
|
74
|
-
return { ctx: buildProbeCtx(callId), raw: rawBody };
|
|
75
|
-
}
|
|
75
|
+
const esProbe = headers.get(PROBE_HEADER) === "1";
|
|
76
76
|
if (!sigHeader || !tsHeader) {
|
|
77
77
|
throw new VAIAError(
|
|
78
78
|
"Faltan headers de autenticaci\xF3n (X-Gandia-Signature, X-Gandia-Timestamp)",
|
|
@@ -90,6 +90,9 @@ async function verify(request, secret) {
|
|
|
90
90
|
if (!valid) {
|
|
91
91
|
throw new VAIAError("Firma HMAC inv\xE1lida", "HMAC_INVALID", 401);
|
|
92
92
|
}
|
|
93
|
+
if (esProbe) {
|
|
94
|
+
return { ctx: buildProbeCtx(callId), raw: rawBody };
|
|
95
|
+
}
|
|
93
96
|
let body;
|
|
94
97
|
try {
|
|
95
98
|
body = JSON.parse(rawBody);
|
|
@@ -449,9 +452,7 @@ async function verify3(request, secret) {
|
|
|
449
452
|
const sigHeader = headers.get("x-handeia-signature") ?? "";
|
|
450
453
|
const tsHeader = headers.get("x-handeia-timestamp") ?? "";
|
|
451
454
|
const callId = headers.get("x-handeia-call-id") ?? "";
|
|
452
|
-
|
|
453
|
-
return { ctx: buildProbeCtx2(callId), raw: rawBody };
|
|
454
|
-
}
|
|
455
|
+
const esProbe = headers.get(PROBE_HEADER2) === "1";
|
|
455
456
|
if (!sigHeader || !tsHeader) {
|
|
456
457
|
throw new VAIAError(
|
|
457
458
|
"Faltan headers de autenticaci\xF3n (X-Handeia-Signature, X-Handeia-Timestamp)",
|
|
@@ -469,6 +470,9 @@ async function verify3(request, secret) {
|
|
|
469
470
|
if (!valid) {
|
|
470
471
|
throw new VAIAError("Firma HMAC inv\xE1lida", "HMAC_INVALID", 401);
|
|
471
472
|
}
|
|
473
|
+
if (esProbe) {
|
|
474
|
+
return { ctx: buildProbeCtx2(callId), raw: rawBody };
|
|
475
|
+
}
|
|
472
476
|
let body;
|
|
473
477
|
try {
|
|
474
478
|
body = JSON.parse(rawBody);
|
|
@@ -552,6 +556,154 @@ async function fromUrl2(url, secret) {
|
|
|
552
556
|
return verify4(token, secret);
|
|
553
557
|
}
|
|
554
558
|
|
|
559
|
+
// src/agent.ts
|
|
560
|
+
var AGENT_PROTOCOL_VERSION = 1;
|
|
561
|
+
var CONNECTOR_OF_OPERATION = {
|
|
562
|
+
"github.repos": "github",
|
|
563
|
+
"github.issues": "github",
|
|
564
|
+
"drive.files": "drive",
|
|
565
|
+
"calendar.events": "calendar",
|
|
566
|
+
"email.recent": "email",
|
|
567
|
+
"notion.pages": "notion"
|
|
568
|
+
};
|
|
569
|
+
var NOMBRE_ACCION = /^[a-z][a-z0-9_]{1,48}$/;
|
|
570
|
+
function validateAgentSurface(cfg) {
|
|
571
|
+
const errores = [];
|
|
572
|
+
const vistos = /* @__PURE__ */ new Set();
|
|
573
|
+
for (const accion of cfg.actions ?? []) {
|
|
574
|
+
if (!NOMBRE_ACCION.test(accion.name)) {
|
|
575
|
+
errores.push(`Acci\xF3n "${accion.name}": el nombre debe ser min\xFAsculas, n\xFAmeros o guion bajo.`);
|
|
576
|
+
}
|
|
577
|
+
if (vistos.has(accion.name)) {
|
|
578
|
+
errores.push(`Acci\xF3n "${accion.name}": declarada dos veces.`);
|
|
579
|
+
}
|
|
580
|
+
vistos.add(accion.name);
|
|
581
|
+
if (!accion.description?.trim()) {
|
|
582
|
+
errores.push(`Acci\xF3n "${accion.name}": falta la descripci\xF3n, que es lo que el agente lee para elegirla.`);
|
|
583
|
+
}
|
|
584
|
+
if (accion.writes && !accion.permission) {
|
|
585
|
+
errores.push(`Acci\xF3n "${accion.name}": modifica datos, as\xED que necesita un permiso declarado.`);
|
|
586
|
+
}
|
|
587
|
+
for (const p of accion.params ?? []) {
|
|
588
|
+
if (!p.name?.trim()) errores.push(`Acci\xF3n "${accion.name}": un par\xE1metro no tiene nombre.`);
|
|
589
|
+
if (!p.description?.trim()) {
|
|
590
|
+
errores.push(`Acci\xF3n "${accion.name}", par\xE1metro "${p.name}": falta la descripci\xF3n.`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (cfg.queryEndpoint && !cfg.queryEndpoint.startsWith("/")) {
|
|
595
|
+
errores.push('queryEndpoint debe ser una ruta de tu propio servidor, empezando por "/".');
|
|
596
|
+
}
|
|
597
|
+
return errores;
|
|
598
|
+
}
|
|
599
|
+
function validateActionCall(llamada, declaradas) {
|
|
600
|
+
const action = declaradas.find((a) => a.name === llamada.name);
|
|
601
|
+
if (!action) return { ok: false, reason: `La acci\xF3n "${llamada.name}" no est\xE1 declarada por el espacio.` };
|
|
602
|
+
const args = llamada.args ?? {};
|
|
603
|
+
for (const p of action.params ?? []) {
|
|
604
|
+
const v = args[p.name];
|
|
605
|
+
if (v === void 0 || v === null) {
|
|
606
|
+
if (p.required) return { ok: false, reason: `Falta el par\xE1metro obligatorio "${p.name}".` };
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
if (typeof v !== p.type) {
|
|
610
|
+
return { ok: false, reason: `El par\xE1metro "${p.name}" debe ser ${p.type}.` };
|
|
611
|
+
}
|
|
612
|
+
if (p.enum && !p.enum.includes(String(v))) {
|
|
613
|
+
return { ok: false, reason: `El par\xE1metro "${p.name}" no admite el valor "${String(v)}".` };
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
const permitidos = new Set((action.params ?? []).map((p) => p.name));
|
|
617
|
+
for (const k of Object.keys(args)) {
|
|
618
|
+
if (!permitidos.has(k)) return { ok: false, reason: `El par\xE1metro "${k}" no est\xE1 declarado.` };
|
|
619
|
+
}
|
|
620
|
+
return { ok: true, action };
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/pieces.ts
|
|
624
|
+
var NOMBRE = /^[a-z][a-z0-9_]{1,48}$/;
|
|
625
|
+
var ORDEN = {
|
|
626
|
+
prohibida: 0,
|
|
627
|
+
requiere_aprobacion: 1,
|
|
628
|
+
autonoma: 2
|
|
629
|
+
};
|
|
630
|
+
function validatePieces(cfg) {
|
|
631
|
+
const errores = [];
|
|
632
|
+
const vistos = /* @__PURE__ */ new Set();
|
|
633
|
+
const revisarNombre = (pieza, name) => {
|
|
634
|
+
if (!NOMBRE.test(name)) errores.push(`${pieza} "${name}": el nombre debe ser min\xFAsculas, n\xFAmeros o guion bajo.`);
|
|
635
|
+
if (vistos.has(name)) errores.push(`"${name}": hay dos piezas con el mismo nombre.`);
|
|
636
|
+
vistos.add(name);
|
|
637
|
+
};
|
|
638
|
+
const revisarAutoridad = (pieza, a) => {
|
|
639
|
+
if (a.consequence === "irreversible" && a.level === "autonoma") {
|
|
640
|
+
errores.push(`${pieza}: una acci\xF3n irreversible no puede ser aut\xF3noma \u2014 como m\xEDnimo requiere aprobaci\xF3n.`);
|
|
641
|
+
}
|
|
642
|
+
if (a.maxAmount !== void 0) {
|
|
643
|
+
if (a.maxAmount <= 0) errores.push(`${pieza}: el tope de gasto debe ser mayor que cero.`);
|
|
644
|
+
if (!a.currency) errores.push(`${pieza}: hay tope de gasto pero no se declar\xF3 la moneda.`);
|
|
645
|
+
}
|
|
646
|
+
if (a.consequence === "costosa" && a.level === "autonoma" && a.maxAmount === void 0) {
|
|
647
|
+
errores.push(`${pieza}: es aut\xF3noma y cuesta dinero, as\xED que necesita un tope declarado.`);
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
for (const s2 of cfg.skills ?? []) {
|
|
651
|
+
revisarNombre("Skill", s2.name);
|
|
652
|
+
if (!s2.description?.trim()) errores.push(`Skill "${s2.name}": falta la descripci\xF3n, que es lo que el modelo lee para elegirla.`);
|
|
653
|
+
if (s2.evidence?.required && (s2.evidence.accepts?.length ?? 0) === 0) {
|
|
654
|
+
errores.push(`Skill "${s2.name}": exige evidencia pero no declara qu\xE9 tipos acepta.`);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
for (const t of cfg.tools ?? []) {
|
|
658
|
+
revisarNombre("Herramienta", t.name);
|
|
659
|
+
if (!t.description?.trim()) errores.push(`Herramienta "${t.name}": falta la descripci\xF3n.`);
|
|
660
|
+
if (!t.permission?.trim()) errores.push(`Herramienta "${t.name}": toca el mundo real, as\xED que necesita un permiso declarado.`);
|
|
661
|
+
revisarAutoridad(`Herramienta "${t.name}"`, t.authority);
|
|
662
|
+
}
|
|
663
|
+
for (const w of cfg.workflows ?? []) {
|
|
664
|
+
revisarNombre("Workflow", w.name);
|
|
665
|
+
if ((w.steps?.length ?? 0) === 0) errores.push(`Workflow "${w.name}": no tiene pasos.`);
|
|
666
|
+
revisarAutoridad(`Workflow "${w.name}"`, w.authority);
|
|
667
|
+
}
|
|
668
|
+
const porNombre = new Map((cfg.tools ?? []).map((t) => [t.name, t]));
|
|
669
|
+
for (const a of cfg.agents ?? []) {
|
|
670
|
+
revisarNombre("Agente", a.name);
|
|
671
|
+
if (!a.purpose?.trim()) {
|
|
672
|
+
errores.push(`Agente "${a.name}": falta el prop\xF3sito \u2014 para qu\xE9 existe.`);
|
|
673
|
+
}
|
|
674
|
+
revisarAutoridad(`Agente "${a.name}"`, a.authority);
|
|
675
|
+
for (const nombreTool of a.tools ?? []) {
|
|
676
|
+
const tool = porNombre.get(nombreTool);
|
|
677
|
+
if (!tool) {
|
|
678
|
+
errores.push(`Agente "${a.name}": usa la herramienta "${nombreTool}", que no est\xE1 declarada.`);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
if (ORDEN[tool.authority.level] > ORDEN[a.authority.level]) {
|
|
682
|
+
errores.push(
|
|
683
|
+
`Agente "${a.name}": su herramienta "${nombreTool}" tiene m\xE1s autoridad que \xE9l. Ninguna pieza puede superar el techo de su agente.`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return errores;
|
|
689
|
+
}
|
|
690
|
+
function requiresApproval(a) {
|
|
691
|
+
return a.level !== "autonoma";
|
|
692
|
+
}
|
|
693
|
+
function checkAuthority(a, intento = {}) {
|
|
694
|
+
if (a.level === "prohibida") return { ok: false, reason: "Esta acci\xF3n est\xE1 prohibida para esta pieza." };
|
|
695
|
+
if (intento.amount !== void 0) {
|
|
696
|
+
if (a.maxAmount === void 0) return { ok: false, reason: "Mueve dinero pero no hay tope declarado." };
|
|
697
|
+
if (intento.currency && a.currency && intento.currency !== a.currency) {
|
|
698
|
+
return { ok: false, reason: `Moneda distinta a la declarada (${a.currency}).` };
|
|
699
|
+
}
|
|
700
|
+
if (intento.amount > a.maxAmount) {
|
|
701
|
+
return { ok: false, reason: `Excede el tope declarado (${a.maxAmount} ${a.currency ?? ""}).`.trim() };
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return { ok: true };
|
|
705
|
+
}
|
|
706
|
+
|
|
555
707
|
// src/define.ts
|
|
556
708
|
function defineCapability(config) {
|
|
557
709
|
const required = ["id", "name", "version", "target", "type", "sector", "permissions", "risk"];
|
|
@@ -568,6 +720,20 @@ function defineCapability(config) {
|
|
|
568
720
|
if (Object.keys(config.surfaces).length === 0) {
|
|
569
721
|
throw new Error(`[@vaia/sdk] defineCapability: 'surfaces' no puede estar vac\xEDo. Define al menos un surface con su endpoint.`);
|
|
570
722
|
}
|
|
723
|
+
if (config.pieces) {
|
|
724
|
+
const errores = validatePieces(config.pieces);
|
|
725
|
+
if (errores.length > 0) {
|
|
726
|
+
throw new Error(`[@vaia/sdk] defineCapability: piezas inv\xE1lidas:
|
|
727
|
+
- ${errores.join("\n - ")}`);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (config.agent) {
|
|
731
|
+
const errores = validateAgentSurface(config.agent);
|
|
732
|
+
if (errores.length > 0) {
|
|
733
|
+
throw new Error(`[@vaia/sdk] defineCapability: superficie de agente inv\xE1lida:
|
|
734
|
+
- ${errores.join("\n - ")}`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
571
737
|
return config;
|
|
572
738
|
}
|
|
573
739
|
function toManifest(config) {
|
|
@@ -582,6 +748,16 @@ function toManifest(config) {
|
|
|
582
748
|
level: config.level,
|
|
583
749
|
sector: config.sector,
|
|
584
750
|
surfaces,
|
|
751
|
+
// El agente viaja en el manifest para que el portal y Handeia sepan qué
|
|
752
|
+
// puede hacer este espacio sin abrir su código.
|
|
753
|
+
agent: config.agent ? {
|
|
754
|
+
protocol: AGENT_PROTOCOL_VERSION,
|
|
755
|
+
actions: config.agent.actions ?? [],
|
|
756
|
+
query_endpoint: config.agent.queryEndpoint
|
|
757
|
+
} : void 0,
|
|
758
|
+
// Las piezas viajan al manifest: el portal necesita mostrar qué autoridad
|
|
759
|
+
// pide una capacidad ANTES de que alguien la instale.
|
|
760
|
+
pieces: config.pieces,
|
|
585
761
|
permissions: config.permissions,
|
|
586
762
|
risk: config.risk,
|
|
587
763
|
has_own_auth: config.has_own_auth ?? false,
|
|
@@ -596,14 +772,304 @@ function toManifest(config) {
|
|
|
596
772
|
};
|
|
597
773
|
}
|
|
598
774
|
|
|
775
|
+
// src/capabilities.ts
|
|
776
|
+
async function conAutoridad(tools, call, ejecutar) {
|
|
777
|
+
const tool = tools.find((t) => t.name === call.name);
|
|
778
|
+
if (!tool) {
|
|
779
|
+
return { ok: false, error: `La operaci\xF3n "${call.name}" no est\xE1 declarada en esta capacidad.` };
|
|
780
|
+
}
|
|
781
|
+
const permitido = checkAuthority(tool.authority, {
|
|
782
|
+
amount: call.amount,
|
|
783
|
+
currency: call.currency
|
|
784
|
+
});
|
|
785
|
+
if (!permitido.ok) return { ok: false, error: permitido.reason };
|
|
786
|
+
if (tool.authority.level === "prohibida") {
|
|
787
|
+
return { ok: false, error: `"${call.name}" est\xE1 prohibida.` };
|
|
788
|
+
}
|
|
789
|
+
if (tool.authority.level !== "autonoma" && !call.approved) {
|
|
790
|
+
return { ok: false, needsApproval: true, error: "Requiere aprobaci\xF3n humana antes de ejecutarse." };
|
|
791
|
+
}
|
|
792
|
+
try {
|
|
793
|
+
return await ejecutar();
|
|
794
|
+
} catch (e) {
|
|
795
|
+
return { ok: false, error: e instanceof Error ? e.message : "La capacidad fall\xF3." };
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
function local(opts) {
|
|
799
|
+
return {
|
|
800
|
+
id: opts.id,
|
|
801
|
+
tools: opts.tools,
|
|
802
|
+
run: (call) => conAutoridad(opts.tools, call, async () => ({
|
|
803
|
+
ok: true,
|
|
804
|
+
data: await opts.handler(call.name, call.args ?? {}),
|
|
805
|
+
evidence: { source: "local", label: opts.id }
|
|
806
|
+
}))
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
function http(opts) {
|
|
810
|
+
const base = opts.baseUrl.replace(/\/$/, "");
|
|
811
|
+
return {
|
|
812
|
+
id: opts.id,
|
|
813
|
+
tools: opts.tools,
|
|
814
|
+
run: (call) => conAutoridad(opts.tools, call, async () => {
|
|
815
|
+
const res = await fetch(`${base}/${call.name}`, {
|
|
816
|
+
method: "POST",
|
|
817
|
+
headers: { "Content-Type": "application/json; charset=utf-8", ...opts.headers },
|
|
818
|
+
body: JSON.stringify(call.args ?? {}),
|
|
819
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
|
|
820
|
+
});
|
|
821
|
+
const texto = await res.text();
|
|
822
|
+
if (!res.ok) return { ok: false, error: `${opts.id} respondi\xF3 ${res.status}` };
|
|
823
|
+
let data;
|
|
824
|
+
try {
|
|
825
|
+
data = JSON.parse(texto);
|
|
826
|
+
} catch {
|
|
827
|
+
data = texto;
|
|
828
|
+
}
|
|
829
|
+
return { ok: true, data, evidence: { source: "http", label: `${opts.id}/${call.name}` } };
|
|
830
|
+
})
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
function httpTransport(url, headers) {
|
|
834
|
+
return {
|
|
835
|
+
async send(mensaje) {
|
|
836
|
+
const res = await fetch(url, {
|
|
837
|
+
method: "POST",
|
|
838
|
+
headers: { "Content-Type": "application/json", Accept: "application/json", ...headers },
|
|
839
|
+
body: JSON.stringify(mensaje),
|
|
840
|
+
signal: AbortSignal.timeout(2e4)
|
|
841
|
+
});
|
|
842
|
+
if (!res.ok) throw new VAIAError(`Servidor MCP respondi\xF3 ${res.status}`, "MCP_HTTP_ERROR", 502);
|
|
843
|
+
return res.json();
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
var PROHIBIDA = { level: "prohibida", consequence: "irreversible" };
|
|
848
|
+
async function mcp(opts) {
|
|
849
|
+
let siguienteId = 1;
|
|
850
|
+
const rpc = async (method, params) => {
|
|
851
|
+
const r = await opts.transport.send({
|
|
852
|
+
jsonrpc: "2.0",
|
|
853
|
+
id: siguienteId++,
|
|
854
|
+
method,
|
|
855
|
+
...params ? { params } : {}
|
|
856
|
+
});
|
|
857
|
+
if (r?.["error"]) {
|
|
858
|
+
const e = r["error"];
|
|
859
|
+
throw new VAIAError(e?.message ?? "Error del servidor MCP", "MCP_ERROR", 502);
|
|
860
|
+
}
|
|
861
|
+
return r?.["result"] ?? {};
|
|
862
|
+
};
|
|
863
|
+
await rpc("initialize", {
|
|
864
|
+
protocolVersion: "2024-11-05",
|
|
865
|
+
capabilities: {},
|
|
866
|
+
clientInfo: { name: "@vaia-lab/sdk", version: "0.3.0" }
|
|
867
|
+
});
|
|
868
|
+
const listadas = (await rpc("tools/list"))["tools"] ?? [];
|
|
869
|
+
const tools = listadas.map((t) => ({
|
|
870
|
+
name: normalizar(t.name),
|
|
871
|
+
description: t.description?.trim() || `Herramienta MCP "${t.name}".`,
|
|
872
|
+
authority: opts.authority[t.name] ?? opts.defaultAuthority ?? PROHIBIDA,
|
|
873
|
+
permission: opts.permission ?? `mcp:${opts.id}`
|
|
874
|
+
}));
|
|
875
|
+
const original = new Map(listadas.map((t) => [normalizar(t.name), t.name]));
|
|
876
|
+
return {
|
|
877
|
+
id: opts.id,
|
|
878
|
+
tools,
|
|
879
|
+
run: (call) => conAutoridad(tools, call, async () => {
|
|
880
|
+
const nombreReal = original.get(call.name) ?? call.name;
|
|
881
|
+
const r = await rpc("tools/call", { name: nombreReal, arguments: call.args ?? {} });
|
|
882
|
+
return {
|
|
883
|
+
ok: !r["isError"],
|
|
884
|
+
data: r["content"] ?? r,
|
|
885
|
+
error: r["isError"] ? "La herramienta MCP devolvi\xF3 un error." : void 0,
|
|
886
|
+
evidence: { source: "mcp", label: `${opts.id}/${nombreReal}` }
|
|
887
|
+
};
|
|
888
|
+
}),
|
|
889
|
+
dispose: () => opts.transport.close?.()
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
function normalizar(n) {
|
|
893
|
+
return n.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").slice(0, 48);
|
|
894
|
+
}
|
|
895
|
+
var capabilities = { local, http, mcp, httpTransport };
|
|
896
|
+
|
|
897
|
+
// src/ecosystem.ts
|
|
898
|
+
function coincide(patron, nombre) {
|
|
899
|
+
if (patron === "*") return true;
|
|
900
|
+
if (patron.endsWith("*")) return nombre.startsWith(patron.slice(0, -1));
|
|
901
|
+
return patron === nombre;
|
|
902
|
+
}
|
|
903
|
+
var ORDEN2 = { prohibida: 0, requiere_aprobacion: 1, autonoma: 2 };
|
|
904
|
+
function aplicarReglas(tool, reglas) {
|
|
905
|
+
if (!reglas) return tool;
|
|
906
|
+
let resultado = tool.authority;
|
|
907
|
+
for (const [patron, regla] of Object.entries(reglas)) {
|
|
908
|
+
if (!coincide(patron, tool.name)) continue;
|
|
909
|
+
if (ORDEN2[regla.level] < ORDEN2[resultado.level]) resultado = regla;
|
|
910
|
+
else if (regla.maxAmount !== void 0 && (resultado.maxAmount === void 0 || regla.maxAmount < resultado.maxAmount)) {
|
|
911
|
+
resultado = { ...resultado, maxAmount: regla.maxAmount, currency: regla.currency ?? resultado.currency };
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
return { ...tool, authority: resultado };
|
|
915
|
+
}
|
|
916
|
+
function defineEcosystem(cfg) {
|
|
917
|
+
const caps = new Map(cfg.capabilities.map((c) => [c.id, c]));
|
|
918
|
+
const recopilar = () => [...caps.values()].flatMap(
|
|
919
|
+
(c) => c.tools.map((t) => ({ ...aplicarReglas(t, cfg.authority), capability: c.id }))
|
|
920
|
+
);
|
|
921
|
+
let tools = recopilar();
|
|
922
|
+
return {
|
|
923
|
+
name: cfg.name,
|
|
924
|
+
get tools() {
|
|
925
|
+
return tools;
|
|
926
|
+
},
|
|
927
|
+
async run(call) {
|
|
928
|
+
const due\u00F1o = [...caps.values()].find((c) => c.tools.some((t) => t.name === call.name));
|
|
929
|
+
if (!due\u00F1o) {
|
|
930
|
+
return { ok: false, error: `Ninguna capacidad de "${cfg.name}" declara "${call.name}".` };
|
|
931
|
+
}
|
|
932
|
+
const conReglas = tools.find((t) => t.name === call.name);
|
|
933
|
+
if (conReglas && conReglas.authority.level === "prohibida") {
|
|
934
|
+
return { ok: false, error: `"${call.name}" est\xE1 prohibida por las reglas de ${cfg.name}.` };
|
|
935
|
+
}
|
|
936
|
+
if (conReglas && conReglas.authority.level !== "autonoma" && !call.approved) {
|
|
937
|
+
return { ok: false, needsApproval: true, error: "Requiere aprobaci\xF3n humana antes de ejecutarse." };
|
|
938
|
+
}
|
|
939
|
+
return due\u00F1o.run(call);
|
|
940
|
+
},
|
|
941
|
+
add(capability) {
|
|
942
|
+
caps.set(capability.id, capability);
|
|
943
|
+
tools = recopilar();
|
|
944
|
+
},
|
|
945
|
+
async remove(capabilityId) {
|
|
946
|
+
const c = caps.get(capabilityId);
|
|
947
|
+
if (!c) return;
|
|
948
|
+
await c.dispose?.();
|
|
949
|
+
caps.delete(capabilityId);
|
|
950
|
+
tools = recopilar();
|
|
951
|
+
},
|
|
952
|
+
async dispose() {
|
|
953
|
+
for (const c of caps.values()) await c.dispose?.();
|
|
954
|
+
caps.clear();
|
|
955
|
+
tools = [];
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
async function agentLoop(opts) {
|
|
960
|
+
const maxSteps = opts.maxSteps ?? 6;
|
|
961
|
+
return async function turno(message, history = []) {
|
|
962
|
+
const steps = [];
|
|
963
|
+
let lastResult;
|
|
964
|
+
for (let i = 0; i < maxSteps; i++) {
|
|
965
|
+
const salida = await opts.model({
|
|
966
|
+
message,
|
|
967
|
+
tools: opts.ecosystem.tools.map((t) => ({ name: t.name, description: t.description })),
|
|
968
|
+
history,
|
|
969
|
+
lastResult
|
|
970
|
+
});
|
|
971
|
+
if (!salida.action) return { text: salida.text, steps };
|
|
972
|
+
let resultado = await opts.ecosystem.run(salida.action);
|
|
973
|
+
if (resultado.needsApproval && opts.onApproval) {
|
|
974
|
+
const aprobado = await opts.onApproval(salida.action.name, salida.action.args ?? {});
|
|
975
|
+
if (aprobado) {
|
|
976
|
+
resultado = await ejecutarAprobado(opts.ecosystem, salida.action);
|
|
977
|
+
} else {
|
|
978
|
+
resultado = { ok: false, error: "El usuario no lo autoriz\xF3." };
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
steps.push({ action: salida.action.name, ok: resultado.ok, error: resultado.error });
|
|
982
|
+
lastResult = resultado;
|
|
983
|
+
}
|
|
984
|
+
return { text: "No pude completarlo en los pasos disponibles.", steps };
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
async function ejecutarAprobado(eco, action) {
|
|
988
|
+
const t = eco.tools.find((x) => x.name === action.name);
|
|
989
|
+
if (!t) return { ok: false, error: "La herramienta ya no existe." };
|
|
990
|
+
if (t.authority.level === "prohibida") {
|
|
991
|
+
return { ok: false, error: "Prohibida: ni con aprobaci\xF3n se ejecuta." };
|
|
992
|
+
}
|
|
993
|
+
return eco.run({ ...action, approved: true });
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// src/mcp.ts
|
|
997
|
+
function fromMCPTool(tool, authority, permission) {
|
|
998
|
+
const warnings = [];
|
|
999
|
+
if (tool.annotations?.destructiveHint && authority.consequence === "reversible") {
|
|
1000
|
+
warnings.push(
|
|
1001
|
+
`"${tool.name}": el servidor MCP la marca como destructiva, pero se declar\xF3 como reversible. Rev\xEDsalo.`
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
if (tool.annotations?.readOnlyHint === false && authority.level === "autonoma") {
|
|
1005
|
+
warnings.push(
|
|
1006
|
+
`"${tool.name}": escribe y se le dio autoridad aut\xF3noma. Aseg\xFArate de que sea lo que quieres.`
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
if (!tool.description?.trim()) {
|
|
1010
|
+
warnings.push(`"${tool.name}": el servidor MCP no la describe, as\xED que el agente no sabr\xE1 cu\xE1ndo usarla.`);
|
|
1011
|
+
}
|
|
1012
|
+
return {
|
|
1013
|
+
tool: {
|
|
1014
|
+
// Prefijo para que se vea de dónde viene y no choque con lo propio.
|
|
1015
|
+
name: normalizarNombre(`mcp_${tool.name}`),
|
|
1016
|
+
description: tool.description?.trim() || `Herramienta MCP "${tool.name}" (sin descripci\xF3n del servidor).`,
|
|
1017
|
+
authority,
|
|
1018
|
+
permission
|
|
1019
|
+
},
|
|
1020
|
+
warnings
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
function toMCPTool(tool) {
|
|
1024
|
+
if (tool.authority.level !== "autonoma") return null;
|
|
1025
|
+
return {
|
|
1026
|
+
name: tool.name,
|
|
1027
|
+
description: tool.description,
|
|
1028
|
+
annotations: {
|
|
1029
|
+
readOnlyHint: tool.authority.consequence === "reversible",
|
|
1030
|
+
destructiveHint: tool.authority.consequence === "irreversible",
|
|
1031
|
+
idempotentHint: false
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
function toMCPTools(tools) {
|
|
1036
|
+
const published = [];
|
|
1037
|
+
const withheld = [];
|
|
1038
|
+
for (const t of tools) {
|
|
1039
|
+
const m = toMCPTool(t);
|
|
1040
|
+
if (m) published.push(m);
|
|
1041
|
+
else withheld.push(t.name);
|
|
1042
|
+
}
|
|
1043
|
+
return { published, withheld };
|
|
1044
|
+
}
|
|
1045
|
+
function normalizarNombre(n) {
|
|
1046
|
+
return n.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").slice(0, 48);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
599
1049
|
// src/index.ts
|
|
600
1050
|
var gandia = gandia_exports;
|
|
601
1051
|
var handeia = handeia_exports;
|
|
602
1052
|
export {
|
|
1053
|
+
AGENT_PROTOCOL_VERSION,
|
|
1054
|
+
CONNECTOR_OF_OPERATION,
|
|
603
1055
|
VAIAError,
|
|
1056
|
+
agentLoop,
|
|
1057
|
+
capabilities,
|
|
1058
|
+
checkAuthority,
|
|
604
1059
|
defineCapability,
|
|
1060
|
+
defineEcosystem,
|
|
1061
|
+
fromMCPTool,
|
|
605
1062
|
gandia,
|
|
606
1063
|
handeia,
|
|
607
|
-
|
|
1064
|
+
http,
|
|
1065
|
+
httpTransport,
|
|
1066
|
+
local,
|
|
1067
|
+
mcp,
|
|
1068
|
+
requiresApproval,
|
|
1069
|
+
toMCPTool,
|
|
1070
|
+
toMCPTools,
|
|
1071
|
+
toManifest,
|
|
1072
|
+
validateActionCall,
|
|
1073
|
+
validateAgentSurface,
|
|
1074
|
+
validatePieces
|
|
608
1075
|
};
|
|
609
|
-
//# sourceMappingURL=index.js.map
|