@kwirthmagnify/kwirth-common-ai 0.5.22 → 0.5.23

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/back.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { IBackChannelObject } from '@kwirthmagnify/kwirth-common';
2
- import { ILlm, ILlmProvider } from './index';
2
+ import { ILlm, ILlmProvider, IAgent } from './index';
3
3
  import { LanguageModel } from 'ai';
4
4
  import { z } from 'zod';
5
5
  export declare const buildModel: (llm: ILlm, providers: ILlmProvider[]) => LanguageModel | null;
@@ -14,9 +14,14 @@ export interface IToolContext {
14
14
  clusterMetrics: any[];
15
15
  trace: (toolName: string, args: Record<string, unknown>) => void;
16
16
  }
17
+ export declare enum EToolEffect {
18
+ READ = "read",
19
+ WRITE = "write"
20
+ }
17
21
  export interface IToolInfo {
18
22
  name: string;
19
23
  description: string;
24
+ effect: EToolEffect;
20
25
  }
21
26
  export declare const runWithToolContext: <T>(context: IToolContext, fn: () => Promise<T>) => Promise<T>;
22
27
  export declare const tools: {
@@ -315,3 +320,12 @@ export declare const tools: {
315
320
  }, unknown>;
316
321
  };
317
322
  export declare const toolInfoList: IToolInfo[];
323
+ export interface IAgentRunResult {
324
+ text: string;
325
+ inputTokens: number;
326
+ outputTokens: number;
327
+ steps: number;
328
+ toolCalls: string[];
329
+ }
330
+ export declare const selectAgentToolNames: (agent: IAgent) => string[];
331
+ export declare const runAgent: (agent: IAgent, prompt: string, llms: ILlm[], providers: ILlmProvider[], context: IToolContext) => Promise<IAgentRunResult>;
package/dist/back.js CHANGED
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.toolInfoList = exports.tools = exports.runWithToolContext = exports.zodFromExample = exports.z = exports.tool = exports.stepCountIs = exports.Output = exports.generateText = exports.loadModels = exports.buildModel = void 0;
36
+ exports.runAgent = exports.selectAgentToolNames = exports.toolInfoList = exports.tools = exports.runWithToolContext = exports.EToolEffect = exports.zodFromExample = exports.z = exports.tool = exports.stepCountIs = exports.Output = exports.generateText = exports.loadModels = exports.buildModel = void 0;
37
37
  const ai_1 = require("ai");
38
38
  const zod_1 = require("zod");
39
39
  const async_hooks_1 = require("async_hooks");
@@ -174,6 +174,13 @@ const zodFromExample = (example) => {
174
174
  return zod_1.z.object(shape);
175
175
  };
176
176
  exports.zodFromExample = zodFromExample;
177
+ // Effect of a tool: READ (safe, informational) or WRITE (has side effects on the cluster).
178
+ // Dual purpose: hints the LLM, and gates authorization (Agora/readOnly filter out WRITE).
179
+ var EToolEffect;
180
+ (function (EToolEffect) {
181
+ EToolEffect["READ"] = "read";
182
+ EToolEffect["WRITE"] = "write";
183
+ })(EToolEffect || (exports.EToolEffect = EToolEffect = {}));
177
184
  const toolContextStorage = new async_hooks_1.AsyncLocalStorage();
178
185
  const runWithToolContext = (context, fn) => toolContextStorage.run(context, fn);
179
186
  exports.runWithToolContext = runWithToolContext;
@@ -689,29 +696,75 @@ exports.tools = {
689
696
  }),
690
697
  };
691
698
  exports.toolInfoList = [
692
- { name: 'list_namespaces', description: 'Lists all namespaces in the cluster with their status and labels.' },
693
- { name: 'get_node_data', description: 'Returns configuration info about all Kubernetes nodes (name, IP). Configuration only — not workload or usage data.' },
694
- { name: 'get_cluster_data', description: 'Returns general cluster info: name, flavour (AKS/EKS/GKE/k3s/k3d), total vCPUs, total memory, node count and readiness status.' },
695
- { name: 'get_workload_data', description: 'Returns all workloads in the cluster: deployments, statefulsets, daemonsets, pods and services. Optionally filter by namespace.' },
696
- { name: 'get_space_data', description: 'Returns all resources in a specific Kubernetes namespace: pods (with restart count), deployments, services, configmap names.' },
697
- { name: 'get_service_yaml', description: 'Returns the full Kubernetes Service manifest (equivalent to kubectl get service -o yaml) for a given namespace and service name.' },
698
- { name: 'list_services', description: 'Lists all Services in the cluster with full details (type, clusterIP, ports, selector). Optionally filter by namespace.' },
699
- { name: 'list_ingresses', description: 'Lists all Ingresses in the cluster (hosts, paths, TLS, backend services). Optionally filter by namespace.' },
700
- { name: 'get_ingress_yaml', description: 'Returns the full Kubernetes Ingress manifest (equivalent to kubectl get ingress -o yaml) for a given namespace and ingress name.' },
701
- { name: 'get_cluster_usage', description: 'Returns current overall cluster resource usage: CPU%, memory%, network Mbps, total vCPUs and total memory GB.' },
702
- { name: 'get_node_usage', description: 'Returns current CPU and memory usage for one node or all nodes from the latest metrics reading.' },
703
- { name: 'get_deployment_usage', description: 'Returns current aggregated CPU and memory usage for all pods belonging to a specific deployment.' },
704
- { name: 'get_prev_cluster_usage', description: 'Returns historical overall cluster usage over the last N metrics readings (CPU%, memory%, network Mbps).' },
705
- { name: 'get_prev_node_usage', description: 'Returns historical CPU and memory usage for one or all nodes over the last N metrics readings.' },
706
- { name: 'get_prev_deployment_usage', description: 'Returns historical aggregated CPU and memory usage for a deployment over the last N metrics readings.' },
707
- { name: 'get_prev_space_data', description: 'Returns historical aggregated CPU and memory usage for all pods in a namespace over the last N metrics readings.' },
708
- { name: 'add_node', description: 'Adds a new agent node to the cluster. For k3d uses `k3d node create`. Cloud providers not yet implemented.' },
709
- { name: 'remove_node', description: 'Removes a node from the cluster (cordon + delete). For k3d uses `k3d node delete`. Cloud providers not yet implemented.' },
710
- { name: 'stop_node', description: 'Stops a running cluster node: cordons it then stops the container. For k3d uses `k3d node stop`.' },
711
- { name: 'start_node', description: 'Starts a previously stopped cluster node and uncordons it. For k3d uses `k3d node start`.' },
712
- { name: 'add_replica', description: 'Scales up a deployment by adding one replica.' },
713
- { name: 'remove_replica', description: 'Scales down a deployment by removing one replica. Minimum of 1 replica is enforced.' },
714
- { name: 'times_two', description: 'Multiplies a number by two.' },
715
- { name: 'father_of', description: 'Returns the name of the father of a person.' },
716
- { name: 'get_certificate_info', description: 'Connects to a hostname via HTTPS and returns TLS certificate details: subject, issuer, validity dates, SANs, fingerprint and whether it is currently valid.' },
699
+ { name: 'list_namespaces', effect: EToolEffect.READ, description: 'Lists all namespaces in the cluster with their status and labels.' },
700
+ { name: 'get_node_data', effect: EToolEffect.READ, description: 'Returns configuration info about all Kubernetes nodes (name, IP). Configuration only — not workload or usage data.' },
701
+ { name: 'get_cluster_data', effect: EToolEffect.READ, description: 'Returns general cluster info: name, flavour (AKS/EKS/GKE/k3s/k3d), total vCPUs, total memory, node count and readiness status.' },
702
+ { name: 'get_workload_data', effect: EToolEffect.READ, description: 'Returns all workloads in the cluster: deployments, statefulsets, daemonsets, pods and services. Optionally filter by namespace.' },
703
+ { name: 'get_space_data', effect: EToolEffect.READ, description: 'Returns all resources in a specific Kubernetes namespace: pods (with restart count), deployments, services, configmap names.' },
704
+ { name: 'get_service_yaml', effect: EToolEffect.READ, description: 'Returns the full Kubernetes Service manifest (equivalent to kubectl get service -o yaml) for a given namespace and service name.' },
705
+ { name: 'list_services', effect: EToolEffect.READ, description: 'Lists all Services in the cluster with full details (type, clusterIP, ports, selector). Optionally filter by namespace.' },
706
+ { name: 'list_ingresses', effect: EToolEffect.READ, description: 'Lists all Ingresses in the cluster (hosts, paths, TLS, backend services). Optionally filter by namespace.' },
707
+ { name: 'get_ingress_yaml', effect: EToolEffect.READ, description: 'Returns the full Kubernetes Ingress manifest (equivalent to kubectl get ingress -o yaml) for a given namespace and ingress name.' },
708
+ { name: 'get_cluster_usage', effect: EToolEffect.READ, description: 'Returns current overall cluster resource usage: CPU%, memory%, network Mbps, total vCPUs and total memory GB.' },
709
+ { name: 'get_node_usage', effect: EToolEffect.READ, description: 'Returns current CPU and memory usage for one node or all nodes from the latest metrics reading.' },
710
+ { name: 'get_deployment_usage', effect: EToolEffect.READ, description: 'Returns current aggregated CPU and memory usage for all pods belonging to a specific deployment.' },
711
+ { name: 'get_prev_cluster_usage', effect: EToolEffect.READ, description: 'Returns historical overall cluster usage over the last N metrics readings (CPU%, memory%, network Mbps).' },
712
+ { name: 'get_prev_node_usage', effect: EToolEffect.READ, description: 'Returns historical CPU and memory usage for one or all nodes over the last N metrics readings.' },
713
+ { name: 'get_prev_deployment_usage', effect: EToolEffect.READ, description: 'Returns historical aggregated CPU and memory usage for a deployment over the last N metrics readings.' },
714
+ { name: 'get_prev_space_data', effect: EToolEffect.READ, description: 'Returns historical aggregated CPU and memory usage for all pods in a namespace over the last N metrics readings.' },
715
+ { name: 'add_node', effect: EToolEffect.WRITE, description: 'Adds a new agent node to the cluster. For k3d uses `k3d node create`. Cloud providers not yet implemented.' },
716
+ { name: 'remove_node', effect: EToolEffect.WRITE, description: 'Removes a node from the cluster (cordon + delete). For k3d uses `k3d node delete`. Cloud providers not yet implemented.' },
717
+ { name: 'stop_node', effect: EToolEffect.WRITE, description: 'Stops a running cluster node: cordons it then stops the container. For k3d uses `k3d node stop`.' },
718
+ { name: 'start_node', effect: EToolEffect.WRITE, description: 'Starts a previously stopped cluster node and uncordons it. For k3d uses `k3d node start`.' },
719
+ { name: 'add_replica', effect: EToolEffect.WRITE, description: 'Scales up a deployment by adding one replica.' },
720
+ { name: 'remove_replica', effect: EToolEffect.WRITE, description: 'Scales down a deployment by removing one replica. Minimum of 1 replica is enforced.' },
721
+ { name: 'times_two', effect: EToolEffect.READ, description: 'Multiplies a number by two.' },
722
+ { name: 'father_of', effect: EToolEffect.READ, description: 'Returns the name of the father of a person.' },
723
+ { name: 'get_certificate_info', effect: EToolEffect.READ, description: 'Connects to a hostname via HTTPS and returns TLS certificate details: subject, issuer, validity dates, SANs, fingerprint and whether it is currently valid.' },
717
724
  ];
725
+ // Resolves the tool names an agent may use: autoTools = full catalog, otherwise its own list
726
+ // (intersected with the catalog); readOnly filters out WRITE-effect tools. Pure (no LLM) → unit-testable.
727
+ const selectAgentToolNames = (agent) => {
728
+ const writeNames = new Set(exports.toolInfoList.filter(t => t.effect === EToolEffect.WRITE).map(t => t.name));
729
+ const catalogNames = exports.toolInfoList.map(t => t.name);
730
+ const wanted = agent.autoTools ? catalogNames : agent.tools.filter(n => catalogNames.includes(n));
731
+ return wanted.filter(n => !(agent.readOnly && writeNames.has(n)));
732
+ };
733
+ exports.selectAgentToolNames = selectAgentToolNames;
734
+ // Runs an IAgent: resolves its LLM, selects/filters its tools (readOnly drops WRITE-effect tools) and
735
+ // invokes the model within the k8s tool context. Wraps the existing engine (buildModel + generateText +
736
+ // runWithToolContext) — factors the pattern pinocchio does by hand, reusable by Agora/pinocchio/defender.
737
+ // No providerOptions here: pinocchio's are for structured output; a chat/tool agent returns free text.
738
+ const runAgent = async (agent, prompt, llms, providers, context) => {
739
+ var _a, _b, _c, _d, _e, _f, _g, _h;
740
+ const llm = llms.find(l => l.id === agent.llm);
741
+ if (!llm)
742
+ throw new Error(`[common-ai] runAgent: llm '${agent.llm}' not found`);
743
+ const model = (0, exports.buildModel)(llm, providers);
744
+ if (!model)
745
+ throw new Error(`[common-ai] runAgent: could not build model for llm '${agent.llm}'`);
746
+ const allowed = new Set((0, exports.selectAgentToolNames)(agent));
747
+ const selectedTools = Object.fromEntries(Object.entries(exports.tools).filter(([n]) => allowed.has(n)));
748
+ const result = await (0, exports.runWithToolContext)(context, () => (0, ai_1.generateText)({
749
+ model,
750
+ temperature: llm.temperature,
751
+ stopWhen: (0, ai_1.stepCountIs)(agent.steps || 15),
752
+ tools: selectedTools,
753
+ system: agent.system,
754
+ prompt
755
+ }));
756
+ const toolCalls = [];
757
+ for (const step of ((_a = result.steps) !== null && _a !== void 0 ? _a : [])) {
758
+ const s = step;
759
+ for (const call of ((_b = s.toolCalls) !== null && _b !== void 0 ? _b : []))
760
+ toolCalls.push(call.toolName);
761
+ }
762
+ return {
763
+ text: result.text,
764
+ inputTokens: (_d = (_c = result.usage) === null || _c === void 0 ? void 0 : _c.inputTokens) !== null && _d !== void 0 ? _d : 0,
765
+ outputTokens: (_f = (_e = result.usage) === null || _e === void 0 ? void 0 : _e.outputTokens) !== null && _f !== void 0 ? _f : 0,
766
+ steps: (_h = (_g = result.steps) === null || _g === void 0 ? void 0 : _g.length) !== null && _h !== void 0 ? _h : 0,
767
+ toolCalls
768
+ };
769
+ };
770
+ exports.runAgent = runAgent;
package/dist/index.d.ts CHANGED
@@ -23,3 +23,16 @@ export interface ILlm {
23
23
  outputCostPerMillion?: number;
24
24
  data?: unknown;
25
25
  }
26
+ export declare const STORAGE_KEY_AGENTS = "kwirth-ai-agents";
27
+ export interface IAgent {
28
+ id: string;
29
+ name: string;
30
+ description: string;
31
+ cluster: string;
32
+ llm: string;
33
+ system: string;
34
+ tools: string[];
35
+ autoTools: boolean;
36
+ steps: number;
37
+ readOnly: boolean;
38
+ }
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PROVIDERS_AVAILABLE = exports.STORAGE_KEY_LLMS = exports.STORAGE_KEY_PROVIDERS = void 0;
3
+ exports.STORAGE_KEY_AGENTS = exports.PROVIDERS_AVAILABLE = exports.STORAGE_KEY_LLMS = exports.STORAGE_KEY_PROVIDERS = void 0;
4
4
  exports.STORAGE_KEY_PROVIDERS = 'kwirth-ai-providers';
5
5
  exports.STORAGE_KEY_LLMS = 'kwirth-ai-llms';
6
6
  exports.PROVIDERS_AVAILABLE = ['google', 'openai', 'openrouter', 'mistral', 'groq', 'deepseek'];
7
+ exports.STORAGE_KEY_AGENTS = 'kwirth-ai-agents';
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@kwirthmagnify/kwirth-common-ai",
3
- "version": "0.5.22",
3
+ "version": "0.5.23",
4
4
  "description": "Shared AI/LLM types and utilities for Kwirth AI plugins",
5
5
  "scripts": {
6
- "build": "del .\\dist\\* /s /q 2>nul & tsc"
6
+ "build": "del .\\dist\\* /s /q 2>nul & tsc",
7
+ "test": "node --test tests/agent.test.mjs"
7
8
  },
8
9
  "publishConfig": {
9
10
  "access": "public"