@easynet/agent-tool 1.0.0 → 1.0.2
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/README.md +90 -73
- package/dist/api/adapters/LangChainToolsHub.d.ts +34 -0
- package/dist/api/adapters/LangChainToolsHub.d.ts.map +1 -0
- package/dist/api/createAgentTools.d.ts +24 -0
- package/dist/api/createAgentTools.d.ts.map +1 -0
- package/dist/api/expose/index.d.ts +16 -0
- package/dist/api/expose/index.d.ts.map +1 -0
- package/dist/api/expose/mcp-build/build.d.ts.map +1 -0
- package/dist/{codegen → api/expose/mcp-build}/generator.d.ts +3 -3
- package/dist/api/expose/mcp-build/generator.d.ts.map +1 -0
- package/dist/api/expose/mcp-build/index.d.ts +8 -0
- package/dist/api/expose/mcp-build/index.d.ts.map +1 -0
- package/dist/api/expose/mcp-build/init.d.ts.map +1 -0
- package/dist/api/expose/mcp-build/run.d.ts.map +1 -0
- package/dist/api/expose/mcp-build/types.d.ts +25 -0
- package/dist/api/expose/mcp-build/types.d.ts.map +1 -0
- package/dist/api/expose/mcpServer.d.ts +75 -0
- package/dist/api/expose/mcpServer.d.ts.map +1 -0
- package/dist/api/expose/openapi.d.ts +23 -0
- package/dist/api/expose/openapi.d.ts.map +1 -0
- package/dist/api/expose/openapiHttp.d.ts +67 -0
- package/dist/api/expose/openapiHttp.d.ts.map +1 -0
- package/dist/api/main.cjs +56 -0
- package/dist/api/main.cjs.map +1 -0
- package/dist/api/main.d.ts +23 -0
- package/dist/api/main.d.ts.map +1 -0
- package/dist/api/main.js +7 -0
- package/dist/api/main.js.map +1 -0
- package/dist/api/runtimeFromConfig.d.ts +34 -0
- package/dist/api/runtimeFromConfig.d.ts.map +1 -0
- package/dist/canonicalCoreSchemas-CTW6CCFY.cjs +20 -0
- package/dist/canonicalCoreSchemas-CTW6CCFY.cjs.map +1 -0
- package/dist/canonicalCoreSchemas-YLHVHYJZ.js +3 -0
- package/dist/canonicalCoreSchemas-YLHVHYJZ.js.map +1 -0
- package/dist/{chunk-AXUNV4MK.js → chunk-5SWSNVMI.js} +3 -3
- package/dist/chunk-5SWSNVMI.js.map +1 -0
- package/dist/chunk-6F5JHLZ7.cjs +243 -0
- package/dist/chunk-6F5JHLZ7.cjs.map +1 -0
- package/dist/chunk-AE6FSNGY.js +201 -0
- package/dist/chunk-AE6FSNGY.js.map +1 -0
- package/dist/chunk-BZOKPJMP.cjs +120 -0
- package/dist/chunk-BZOKPJMP.cjs.map +1 -0
- package/dist/chunk-FA2ZEICE.cjs +1620 -0
- package/dist/chunk-FA2ZEICE.cjs.map +1 -0
- package/dist/chunk-FR2CXERF.js +239 -0
- package/dist/chunk-FR2CXERF.js.map +1 -0
- package/dist/chunk-MGEQPAHV.cjs +475 -0
- package/dist/chunk-MGEQPAHV.cjs.map +1 -0
- package/dist/{chunk-BM4EVYI5.js → chunk-PJ4RUBZL.js} +836 -122
- package/dist/chunk-PJ4RUBZL.js.map +1 -0
- package/dist/chunk-Q7KPGWC6.js +1584 -0
- package/dist/chunk-Q7KPGWC6.js.map +1 -0
- package/dist/chunk-QVH6IQKQ.js +469 -0
- package/dist/chunk-QVH6IQKQ.js.map +1 -0
- package/dist/{chunk-3YLVPZRJ.cjs → chunk-SOFUWEZ6.cjs} +3 -3
- package/dist/chunk-SOFUWEZ6.cjs.map +1 -0
- package/dist/chunk-TBMWJWQ2.js +116 -0
- package/dist/chunk-TBMWJWQ2.js.map +1 -0
- package/dist/{chunk-Z7TGIG77.cjs → chunk-ZBNRHRGM.cjs} +843 -127
- package/dist/chunk-ZBNRHRGM.cjs.map +1 -0
- package/dist/chunk-ZNJBRLKN.cjs +210 -0
- package/dist/chunk-ZNJBRLKN.cjs.map +1 -0
- package/dist/core/index.cjs +20 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/{core.d.ts → core/index.d.ts} +2 -3
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/registry/ToolRegistry.d.ts.map +1 -0
- package/dist/core/runtime/Budget.d.ts.map +1 -0
- package/dist/core/runtime/Evidence.d.ts.map +1 -0
- package/dist/{runtime → core/runtime}/PTCRuntime.d.ts +4 -4
- package/dist/core/runtime/PTCRuntime.d.ts.map +1 -0
- package/dist/{runtime → core/runtime}/PTCRuntimeObservability.d.ts +4 -4
- package/dist/core/runtime/PTCRuntimeObservability.d.ts.map +1 -0
- package/dist/{runtime → core/runtime}/PTCRuntimePipeline.d.ts +4 -4
- package/dist/core/runtime/PTCRuntimePipeline.d.ts.map +1 -0
- package/dist/core/runtime/PolicyEngine.d.ts.map +1 -0
- package/dist/core/runtime/Retry.d.ts.map +1 -0
- package/dist/core/runtime/SchemaValidator.d.ts.map +1 -0
- package/dist/core/runtime.cjs +24 -0
- package/dist/core/runtime.cjs.map +1 -0
- package/dist/core/runtime.d.ts +12 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +3 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/types/Events.d.ts.map +1 -0
- package/dist/core/types/ToolIntent.d.ts.map +1 -0
- package/dist/{types → core/types}/ToolResult.d.ts +6 -1
- package/dist/core/types/ToolResult.d.ts.map +1 -0
- package/dist/{types → core/types}/ToolSpec.d.ts +15 -5
- package/dist/core/types/ToolSpec.d.ts.map +1 -0
- package/dist/core/types/ToolTypeHandler.d.ts +88 -0
- package/dist/core/types/ToolTypeHandler.d.ts.map +1 -0
- package/dist/{types → core/types}/index.d.ts +2 -1
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/index.cjs +249 -2749
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +61 -55
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +126 -2688
- package/dist/index.js.map +1 -1
- package/dist/observability/EventLog.d.ts +1 -1
- package/dist/observability/EventLog.d.ts.map +1 -1
- package/dist/tools/discoveryFactory.d.ts +117 -0
- package/dist/tools/discoveryFactory.d.ts.map +1 -0
- package/dist/tools/function/index.d.ts +10 -0
- package/dist/tools/function/index.d.ts.map +1 -0
- package/dist/{codegen/scan → tools/function}/scanner.d.ts +5 -2
- package/dist/{codegen/scan → tools/function}/scanner.d.ts.map +1 -1
- package/dist/{codegen/scan → tools/function}/schemaFromTs.d.ts +1 -1
- package/dist/tools/function/schemaFromTs.d.ts.map +1 -0
- package/dist/tools/function/types.d.ts +20 -0
- package/dist/tools/function/types.d.ts.map +1 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/{discovery/load → tools/langchain}/LangChainLoader.d.ts +1 -1
- package/dist/tools/langchain/LangChainLoader.d.ts.map +1 -0
- package/dist/tools/langchain/directoryApply.d.ts +5 -0
- package/dist/tools/langchain/directoryApply.d.ts.map +1 -0
- package/dist/tools/langchain/directoryLoad.d.ts +13 -0
- package/dist/tools/langchain/directoryLoad.d.ts.map +1 -0
- package/dist/tools/langchain/index.d.ts +3 -0
- package/dist/tools/langchain/index.d.ts.map +1 -0
- package/dist/tools/langchain/scanner.d.ts +8 -0
- package/dist/tools/langchain/scanner.d.ts.map +1 -0
- package/dist/tools/langchain/types.d.ts +5 -0
- package/dist/tools/langchain/types.d.ts.map +1 -0
- package/dist/{mcp → tools/mcp}/MCPClientAdapter.d.ts +3 -3
- package/dist/tools/mcp/MCPClientAdapter.d.ts.map +1 -0
- package/dist/{discovery/load → tools/mcp}/MCPLoader.d.ts +1 -1
- package/dist/tools/mcp/MCPLoader.d.ts.map +1 -0
- package/dist/tools/mcp/MCPProcessManager.d.ts +29 -0
- package/dist/tools/mcp/MCPProcessManager.d.ts.map +1 -0
- package/dist/{mcp → tools/mcp}/connectMCP.d.ts +3 -3
- package/dist/tools/mcp/connectMCP.d.ts.map +1 -0
- package/dist/tools/mcp/directoryApply.d.ts +10 -0
- package/dist/tools/mcp/directoryApply.d.ts.map +1 -0
- package/dist/{mcp → tools/mcp}/index.d.ts +6 -1
- package/dist/tools/mcp/index.d.ts.map +1 -0
- package/dist/tools/mcp/mcpSpecToToolSpec.d.ts +8 -0
- package/dist/tools/mcp/mcpSpecToToolSpec.d.ts.map +1 -0
- package/dist/{mcp → tools/mcp}/registerMCPTools.d.ts +2 -2
- package/dist/tools/mcp/registerMCPTools.d.ts.map +1 -0
- package/dist/tools/mcp/scanner.d.ts +8 -0
- package/dist/tools/mcp/scanner.d.ts.map +1 -0
- package/dist/tools/mcp/types.d.ts +3 -0
- package/dist/tools/mcp/types.d.ts.map +1 -0
- package/dist/{discovery/load → tools/n8n}/N8nLoader.d.ts +3 -3
- package/dist/tools/n8n/N8nLoader.d.ts.map +1 -0
- package/dist/tools/n8n/directoryApply.d.ts +10 -0
- package/dist/tools/n8n/directoryApply.d.ts.map +1 -0
- package/dist/tools/n8n/index.d.ts +6 -0
- package/dist/tools/n8n/index.d.ts.map +1 -0
- package/dist/tools/n8n/scanN8n.d.ts +20 -0
- package/dist/tools/n8n/scanN8n.d.ts.map +1 -0
- package/dist/tools/n8n/types.d.ts +18 -0
- package/dist/tools/n8n/types.d.ts.map +1 -0
- package/dist/tools/scanPackage.d.ts +42 -0
- package/dist/tools/scanPackage.d.ts.map +1 -0
- package/dist/{discovery/load → tools/skill}/SkillLoader.d.ts +1 -1
- package/dist/tools/skill/SkillLoader.d.ts.map +1 -0
- package/dist/tools/skill/SkillManifest.d.ts.map +1 -0
- package/dist/tools/skill/SkillMdParser.d.ts.map +1 -0
- package/dist/tools/skill/directoryApply.d.ts +10 -0
- package/dist/tools/skill/directoryApply.d.ts.map +1 -0
- package/dist/tools/skill/index.d.ts +8 -0
- package/dist/tools/skill/index.d.ts.map +1 -0
- package/dist/tools/skill/scanSkill.d.ts +20 -0
- package/dist/tools/skill/scanSkill.d.ts.map +1 -0
- package/dist/tools/skill/types.d.ts +19 -0
- package/dist/tools/skill/types.d.ts.map +1 -0
- package/dist/tools/util/canonicalCoreSchemas.d.ts +19 -0
- package/dist/tools/util/canonicalCoreSchemas.d.ts.map +1 -0
- package/dist/tools/util/index.d.ts +13 -0
- package/dist/tools/util/index.d.ts.map +1 -0
- package/dist/tools/util/resolveEntry.d.ts +6 -0
- package/dist/tools/util/resolveEntry.d.ts.map +1 -0
- package/dist/tools/util/scanUtil.d.ts +9 -0
- package/dist/tools/util/scanUtil.d.ts.map +1 -0
- package/dist/tools/util/toolConfig.d.ts +32 -0
- package/dist/tools/util/toolConfig.d.ts.map +1 -0
- package/dist/tools/util/toolDescriptor.d.ts +92 -0
- package/dist/tools/util/toolDescriptor.d.ts.map +1 -0
- package/dist/utils/cli/index.cjs +419 -0
- package/dist/utils/cli/index.cjs.map +1 -0
- package/dist/utils/cli/index.d.ts +9 -0
- package/dist/utils/cli/index.d.ts.map +1 -0
- package/dist/utils/cli/index.js +412 -0
- package/dist/utils/cli/index.js.map +1 -0
- package/dist/utils/cli/toolRuntime.d.ts +19 -0
- package/dist/utils/cli/toolRuntime.d.ts.map +1 -0
- package/dist/utils/npmCache.d.ts +28 -0
- package/dist/utils/npmCache.d.ts.map +1 -0
- package/package.json +20 -11
- package/dist/chunk-3YLVPZRJ.cjs.map +0 -1
- package/dist/chunk-AXUNV4MK.js.map +0 -1
- package/dist/chunk-BM4EVYI5.js.map +0 -1
- package/dist/chunk-P3UEAZHK.cjs +0 -171
- package/dist/chunk-P3UEAZHK.cjs.map +0 -1
- package/dist/chunk-RPAMQCFH.js +0 -167
- package/dist/chunk-RPAMQCFH.js.map +0 -1
- package/dist/chunk-Z7TGIG77.cjs.map +0 -1
- package/dist/cli.cjs +0 -154
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.ts +0 -10
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -147
- package/dist/cli.js.map +0 -1
- package/dist/codegen/build.d.ts.map +0 -1
- package/dist/codegen/generator.d.ts.map +0 -1
- package/dist/codegen/index.d.ts +0 -21
- package/dist/codegen/index.d.ts.map +0 -1
- package/dist/codegen/init.d.ts.map +0 -1
- package/dist/codegen/run.d.ts.map +0 -1
- package/dist/codegen/scan/scanN8n.d.ts +0 -17
- package/dist/codegen/scan/scanN8n.d.ts.map +0 -1
- package/dist/codegen/scan/scanSkill.d.ts +0 -17
- package/dist/codegen/scan/scanSkill.d.ts.map +0 -1
- package/dist/codegen/scan/scanTools.d.ts +0 -31
- package/dist/codegen/scan/scanTools.d.ts.map +0 -1
- package/dist/codegen/scan/schemaFromTs.d.ts.map +0 -1
- package/dist/codegen/types.d.ts +0 -81
- package/dist/codegen/types.d.ts.map +0 -1
- package/dist/core.cjs +0 -20
- package/dist/core.cjs.map +0 -1
- package/dist/core.d.ts.map +0 -1
- package/dist/core.js +0 -3
- package/dist/core.js.map +0 -1
- package/dist/discovery/MCPProcessManager.d.ts +0 -57
- package/dist/discovery/MCPProcessManager.d.ts.map +0 -1
- package/dist/discovery/errors.d.ts +0 -13
- package/dist/discovery/errors.d.ts.map +0 -1
- package/dist/discovery/load/LangChainLoader.d.ts.map +0 -1
- package/dist/discovery/load/MCPLoader.d.ts.map +0 -1
- package/dist/discovery/load/N8nLoader.d.ts.map +0 -1
- package/dist/discovery/load/SkillLoader.d.ts.map +0 -1
- package/dist/discovery/load/SkillManifest.d.ts.map +0 -1
- package/dist/discovery/load/SkillMdParser.d.ts.map +0 -1
- package/dist/discovery/load/index.d.ts +0 -6
- package/dist/discovery/load/index.d.ts.map +0 -1
- package/dist/discovery/load/resolveEntry.d.ts +0 -7
- package/dist/discovery/load/resolveEntry.d.ts.map +0 -1
- package/dist/discovery/scan/DirectoryScanner.d.ts +0 -37
- package/dist/discovery/scan/DirectoryScanner.d.ts.map +0 -1
- package/dist/discovery/scan/scanUtil.d.ts +0 -16
- package/dist/discovery/scan/scanUtil.d.ts.map +0 -1
- package/dist/discovery/types.d.ts +0 -99
- package/dist/discovery/types.d.ts.map +0 -1
- package/dist/llm/AgentLLMAdapter.d.ts +0 -27
- package/dist/llm/AgentLLMAdapter.d.ts.map +0 -1
- package/dist/llm/LangChainToolsHub.d.ts +0 -31
- package/dist/llm/LangChainToolsHub.d.ts.map +0 -1
- package/dist/llm/OpenAICompatibleClient.d.ts +0 -64
- package/dist/llm/OpenAICompatibleClient.d.ts.map +0 -1
- package/dist/llm/ReActAgent.d.ts +0 -35
- package/dist/llm/ReActAgent.d.ts.map +0 -1
- package/dist/llm-export.cjs +0 -20
- package/dist/llm-export.cjs.map +0 -1
- package/dist/llm-export.d.ts +0 -9
- package/dist/llm-export.d.ts.map +0 -1
- package/dist/llm-export.js +0 -3
- package/dist/llm-export.js.map +0 -1
- package/dist/mcp/MCPClientAdapter.d.ts.map +0 -1
- package/dist/mcp/connectMCP.d.ts.map +0 -1
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/registerMCPTools.d.ts.map +0 -1
- package/dist/registry/ToolRegistry.d.ts.map +0 -1
- package/dist/report/AgentReportGenerator.d.ts +0 -53
- package/dist/report/AgentReportGenerator.d.ts.map +0 -1
- package/dist/report/agent-report-template.html +0 -362
- package/dist/report/index.d.ts +0 -3
- package/dist/report/index.d.ts.map +0 -1
- package/dist/report/types.d.ts +0 -101
- package/dist/report/types.d.ts.map +0 -1
- package/dist/runAgent.d.ts +0 -37
- package/dist/runAgent.d.ts.map +0 -1
- package/dist/runtime/Budget.d.ts.map +0 -1
- package/dist/runtime/Evidence.d.ts.map +0 -1
- package/dist/runtime/PTCRuntime.d.ts.map +0 -1
- package/dist/runtime/PTCRuntimeObservability.d.ts.map +0 -1
- package/dist/runtime/PTCRuntimePipeline.d.ts.map +0 -1
- package/dist/runtime/PolicyEngine.d.ts.map +0 -1
- package/dist/runtime/Retry.d.ts.map +0 -1
- package/dist/runtime/SchemaValidator.d.ts.map +0 -1
- package/dist/toolDescriptor.d.ts +0 -38
- package/dist/toolDescriptor.d.ts.map +0 -1
- package/dist/types/Events.d.ts.map +0 -1
- package/dist/types/ToolIntent.d.ts.map +0 -1
- package/dist/types/ToolResult.d.ts.map +0 -1
- package/dist/types/ToolSpec.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/extensions/examples/README.md +0 -40
- package/extensions/examples/scripts/agent-tool-react-stock.mjs +0 -30
- package/extensions/examples/tools/instruction-only/skill/SKILL.md +0 -26
- package/extensions/examples/tools/web-search/mcp/mcp.json +0 -8
- /package/dist/{codegen → api/expose/mcp-build}/build.d.ts +0 -0
- /package/dist/{codegen → api/expose/mcp-build}/init.d.ts +0 -0
- /package/dist/{codegen → api/expose/mcp-build}/run.d.ts +0 -0
- /package/dist/{registry → core/registry}/ToolRegistry.d.ts +0 -0
- /package/dist/{runtime → core/runtime}/Budget.d.ts +0 -0
- /package/dist/{runtime → core/runtime}/Evidence.d.ts +0 -0
- /package/dist/{runtime → core/runtime}/PolicyEngine.d.ts +0 -0
- /package/dist/{runtime → core/runtime}/Retry.d.ts +0 -0
- /package/dist/{runtime → core/runtime}/SchemaValidator.d.ts +0 -0
- /package/dist/{types → core/types}/Events.d.ts +0 -0
- /package/dist/{types → core/types}/ToolIntent.d.ts +0 -0
- /package/dist/{discovery/load → tools/skill}/SkillManifest.d.ts +0 -0
- /package/dist/{discovery/load → tools/skill}/SkillMdParser.d.ts +0 -0
|
@@ -0,0 +1,1584 @@
|
|
|
1
|
+
import { ToolRegistry, createTaggedError, withRetry } from './chunk-AE6FSNGY.js';
|
|
2
|
+
import Ajv from 'ajv';
|
|
3
|
+
import addFormats from 'ajv-formats';
|
|
4
|
+
import { bulkhead, circuitBreaker, handleAll, ConsecutiveBreaker } from 'cockatiel';
|
|
5
|
+
import { EventEmitter } from 'eventemitter3';
|
|
6
|
+
import { v4 } from 'uuid';
|
|
7
|
+
import pTimeout from 'p-timeout';
|
|
8
|
+
import { readFileSync, existsSync } from 'fs';
|
|
9
|
+
import { resolve, dirname, join } from 'path';
|
|
10
|
+
import yaml from 'js-yaml';
|
|
11
|
+
import { registerCoreTools } from '@easynet/agent-tool-builtin-tools';
|
|
12
|
+
import { registerExampleTools } from '@easynet/agent-tool-example-tools';
|
|
13
|
+
|
|
14
|
+
var SchemaValidator = class {
|
|
15
|
+
ajv;
|
|
16
|
+
cache = /* @__PURE__ */ new Map();
|
|
17
|
+
constructor() {
|
|
18
|
+
this.ajv = new Ajv({
|
|
19
|
+
allErrors: true,
|
|
20
|
+
coerceTypes: true,
|
|
21
|
+
useDefaults: true,
|
|
22
|
+
removeAdditional: "failing",
|
|
23
|
+
strict: false
|
|
24
|
+
});
|
|
25
|
+
addFormats(this.ajv);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validate data against a JSON Schema.
|
|
29
|
+
* Coerces types and applies defaults in-place.
|
|
30
|
+
*/
|
|
31
|
+
validate(schema, data) {
|
|
32
|
+
const validate = this.getOrCompile(schema);
|
|
33
|
+
const cloned = structuredClone(data);
|
|
34
|
+
const valid = validate(cloned);
|
|
35
|
+
if (valid) {
|
|
36
|
+
return { valid: true, data: cloned };
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
valid: false,
|
|
40
|
+
errors: validate.errors ?? void 0
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate and return coerced data, or throw a descriptive error.
|
|
45
|
+
*/
|
|
46
|
+
validateOrThrow(schema, data, context) {
|
|
47
|
+
const result = this.validate(schema, data);
|
|
48
|
+
if (!result.valid) {
|
|
49
|
+
const messages = (result.errors ?? []).map((e) => `${e.instancePath || "/"} ${e.message}`).join("; ");
|
|
50
|
+
throw new SchemaValidationError(
|
|
51
|
+
`${context}: ${messages}`,
|
|
52
|
+
result.errors ?? []
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return result.data;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Apply default values from schema to data without full validation.
|
|
59
|
+
*/
|
|
60
|
+
enrichDefaults(schema, data) {
|
|
61
|
+
const validate = this.getOrCompile(schema);
|
|
62
|
+
const cloned = structuredClone(data);
|
|
63
|
+
validate(cloned);
|
|
64
|
+
return cloned;
|
|
65
|
+
}
|
|
66
|
+
getOrCompile(schema) {
|
|
67
|
+
const normalized = this.normalizeSchema(schema);
|
|
68
|
+
const key = JSON.stringify(normalized);
|
|
69
|
+
let cached = this.cache.get(key);
|
|
70
|
+
if (!cached) {
|
|
71
|
+
cached = this.ajv.compile(normalized);
|
|
72
|
+
this.cache.set(key, cached);
|
|
73
|
+
}
|
|
74
|
+
return cached;
|
|
75
|
+
}
|
|
76
|
+
/** Ensure schema is AJV-compatible (required = string[], nullable handled via type). */
|
|
77
|
+
normalizeSchema(schema) {
|
|
78
|
+
return this.normalizeSchemaRec(schema);
|
|
79
|
+
}
|
|
80
|
+
normalizeSchemaRec(s) {
|
|
81
|
+
const out = {};
|
|
82
|
+
for (const [key, value] of Object.entries(s)) {
|
|
83
|
+
if (key === "required") {
|
|
84
|
+
const raw = value;
|
|
85
|
+
out.required = Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : typeof raw === "string" ? [raw] : [];
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (key === "nullable") {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (key === "properties" && value !== null && typeof value === "object") {
|
|
92
|
+
const props = {};
|
|
93
|
+
for (const [pk, pv] of Object.entries(value)) {
|
|
94
|
+
if (pv !== null && typeof pv === "object" && !Array.isArray(pv)) {
|
|
95
|
+
props[pk] = this.normalizeSchemaRec(pv);
|
|
96
|
+
} else {
|
|
97
|
+
props[pk] = pv;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
out.properties = props;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if ((key === "items" || key === "additionalProperties") && value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
104
|
+
out[key] = this.normalizeSchemaRec(value);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if ((key === "oneOf" || key === "anyOf" || key === "allOf") && Array.isArray(value)) {
|
|
108
|
+
out[key] = value.map(
|
|
109
|
+
(item) => item !== null && typeof item === "object" && !Array.isArray(item) ? this.normalizeSchemaRec(item) : item
|
|
110
|
+
);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
out[key] = value;
|
|
114
|
+
}
|
|
115
|
+
if (s.nullable === true) {
|
|
116
|
+
const existingType = out.type;
|
|
117
|
+
if (existingType === void 0) {
|
|
118
|
+
out.type = "object";
|
|
119
|
+
} else if (Array.isArray(existingType)) {
|
|
120
|
+
if (!existingType.includes("null")) out.type = [...existingType, "null"];
|
|
121
|
+
} else {
|
|
122
|
+
out.type = [existingType, "null"];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
var SchemaValidationError = class extends Error {
|
|
129
|
+
constructor(message, errors) {
|
|
130
|
+
super(message);
|
|
131
|
+
this.errors = errors;
|
|
132
|
+
this.name = "SchemaValidationError";
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/core/runtime/PolicyEngine.ts
|
|
137
|
+
var PolicyEngine = class {
|
|
138
|
+
config;
|
|
139
|
+
constructor(config = {}) {
|
|
140
|
+
this.config = {
|
|
141
|
+
requireExplicitDangerPermission: true,
|
|
142
|
+
deniedSqlPatterns: ["DROP\\s", "TRUNCATE\\s", "DELETE\\s+FROM\\s+\\w+\\s*$"],
|
|
143
|
+
...config
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Enforce all policy checks. Throws PolicyDeniedError if denied.
|
|
148
|
+
*/
|
|
149
|
+
enforce(spec, args, ctx) {
|
|
150
|
+
const result = this.check(spec, args, ctx);
|
|
151
|
+
if (!result.allowed) {
|
|
152
|
+
throw new PolicyDeniedError(
|
|
153
|
+
result.reason ?? "Policy denied",
|
|
154
|
+
result.missingCapabilities
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check all policies without throwing.
|
|
160
|
+
*/
|
|
161
|
+
check(spec, args, ctx) {
|
|
162
|
+
const capResult = this.checkCapabilities(spec, ctx);
|
|
163
|
+
if (!capResult.allowed) return capResult;
|
|
164
|
+
const paramResult = this.checkParameters(spec, args);
|
|
165
|
+
if (!paramResult.allowed) return paramResult;
|
|
166
|
+
return { allowed: true };
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Check that context permissions cover tool capabilities.
|
|
170
|
+
*/
|
|
171
|
+
checkCapabilities(spec, ctx) {
|
|
172
|
+
const missing = [];
|
|
173
|
+
for (const cap of spec.capabilities) {
|
|
174
|
+
if (cap === "danger:destructive") {
|
|
175
|
+
if (this.config.requireExplicitDangerPermission && !ctx.permissions.includes("danger:destructive")) {
|
|
176
|
+
missing.push(cap);
|
|
177
|
+
}
|
|
178
|
+
} else if (!ctx.permissions.includes(cap)) {
|
|
179
|
+
missing.push(cap);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (missing.length > 0) {
|
|
183
|
+
return {
|
|
184
|
+
allowed: false,
|
|
185
|
+
reason: `Missing capabilities: ${missing.join(", ")}`,
|
|
186
|
+
missingCapabilities: missing
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return { allowed: true };
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Check parameter-level security constraints.
|
|
193
|
+
*/
|
|
194
|
+
checkParameters(spec, args) {
|
|
195
|
+
if (!args || typeof args !== "object") return { allowed: true };
|
|
196
|
+
const argsObj = args;
|
|
197
|
+
if (spec.capabilities.includes("write:fs") && this.config.sandboxPaths) {
|
|
198
|
+
const pathResult = this.checkFilePaths(argsObj);
|
|
199
|
+
if (!pathResult.allowed) return pathResult;
|
|
200
|
+
}
|
|
201
|
+
if (spec.capabilities.includes("network") || spec.capabilities.includes("read:web")) {
|
|
202
|
+
const urlResult = this.checkUrls(argsObj);
|
|
203
|
+
if (!urlResult.allowed) return urlResult;
|
|
204
|
+
}
|
|
205
|
+
if (spec.capabilities.includes("write:db") || spec.capabilities.includes("read:db")) {
|
|
206
|
+
const sqlResult = this.checkSql(argsObj);
|
|
207
|
+
if (!sqlResult.allowed) return sqlResult;
|
|
208
|
+
}
|
|
209
|
+
if (spec.capabilities.includes("network") && this.config.allowedDomains) {
|
|
210
|
+
const domainResult = this.checkDomains(argsObj);
|
|
211
|
+
if (!domainResult.allowed) return domainResult;
|
|
212
|
+
}
|
|
213
|
+
return { allowed: true };
|
|
214
|
+
}
|
|
215
|
+
checkFilePaths(args) {
|
|
216
|
+
const paths = this.extractStringValues(args, ["path", "file", "filepath", "filename", "dir", "directory"]);
|
|
217
|
+
for (const p of paths) {
|
|
218
|
+
const normalized = p.replace(/\.\./g, "");
|
|
219
|
+
if (p.includes("..")) {
|
|
220
|
+
return {
|
|
221
|
+
allowed: false,
|
|
222
|
+
reason: `Path traversal detected: ${p}`
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (this.config.sandboxPaths && this.config.sandboxPaths.length > 0) {
|
|
226
|
+
const inSandbox = this.config.sandboxPaths.some(
|
|
227
|
+
(sp) => normalized.startsWith(sp)
|
|
228
|
+
);
|
|
229
|
+
if (!inSandbox) {
|
|
230
|
+
return {
|
|
231
|
+
allowed: false,
|
|
232
|
+
reason: `Path outside sandbox: ${p}. Allowed: ${this.config.sandboxPaths.join(", ")}`
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { allowed: true };
|
|
238
|
+
}
|
|
239
|
+
checkUrls(args) {
|
|
240
|
+
const urls = this.extractStringValues(args, ["url", "endpoint", "href", "uri"]);
|
|
241
|
+
for (const url of urls) {
|
|
242
|
+
if (this.config.urlDenylist) {
|
|
243
|
+
for (const pattern of this.config.urlDenylist) {
|
|
244
|
+
if (new RegExp(pattern, "i").test(url)) {
|
|
245
|
+
return {
|
|
246
|
+
allowed: false,
|
|
247
|
+
reason: `URL denied by policy: ${url}`
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (this.config.urlAllowlist && this.config.urlAllowlist.length > 0) {
|
|
253
|
+
const allowed = this.config.urlAllowlist.some(
|
|
254
|
+
(pattern) => new RegExp(pattern, "i").test(url)
|
|
255
|
+
);
|
|
256
|
+
if (!allowed) {
|
|
257
|
+
return {
|
|
258
|
+
allowed: false,
|
|
259
|
+
reason: `URL not in allowlist: ${url}`
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return { allowed: true };
|
|
265
|
+
}
|
|
266
|
+
checkSql(args) {
|
|
267
|
+
const sqls = this.extractStringValues(args, ["sql", "query", "statement"]);
|
|
268
|
+
for (const sql of sqls) {
|
|
269
|
+
if (this.config.deniedSqlPatterns) {
|
|
270
|
+
for (const pattern of this.config.deniedSqlPatterns) {
|
|
271
|
+
if (new RegExp(pattern, "i").test(sql)) {
|
|
272
|
+
return {
|
|
273
|
+
allowed: false,
|
|
274
|
+
reason: `SQL pattern denied: ${sql.slice(0, 50)}...`
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return { allowed: true };
|
|
281
|
+
}
|
|
282
|
+
checkDomains(args) {
|
|
283
|
+
const urls = this.extractStringValues(args, ["url", "endpoint", "href", "host", "domain"]);
|
|
284
|
+
for (const url of urls) {
|
|
285
|
+
try {
|
|
286
|
+
const hostname = url.includes("://") ? new URL(url).hostname : url;
|
|
287
|
+
if (this.config.allowedDomains && !this.config.allowedDomains.some(
|
|
288
|
+
(d) => hostname === d || hostname.endsWith(`.${d}`)
|
|
289
|
+
)) {
|
|
290
|
+
return {
|
|
291
|
+
allowed: false,
|
|
292
|
+
reason: `Domain not allowed: ${hostname}`
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
} catch {
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return { allowed: true };
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Extract string values from args matching given key patterns.
|
|
302
|
+
*/
|
|
303
|
+
extractStringValues(args, keyPatterns) {
|
|
304
|
+
const results = [];
|
|
305
|
+
const walk = (obj) => {
|
|
306
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
307
|
+
const lowerKey = key.toLowerCase();
|
|
308
|
+
if (typeof val === "string" && keyPatterns.some((p) => lowerKey.includes(p))) {
|
|
309
|
+
results.push(val);
|
|
310
|
+
} else if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
311
|
+
walk(val);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
walk(args);
|
|
316
|
+
return results;
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
var PolicyDeniedError = class extends Error {
|
|
320
|
+
constructor(message, missingCapabilities) {
|
|
321
|
+
super(message);
|
|
322
|
+
this.missingCapabilities = missingCapabilities;
|
|
323
|
+
this.name = "PolicyDeniedError";
|
|
324
|
+
}
|
|
325
|
+
kind = "POLICY_DENIED";
|
|
326
|
+
};
|
|
327
|
+
var RateLimiter = class {
|
|
328
|
+
constructor(maxCalls, windowMs) {
|
|
329
|
+
this.maxCalls = maxCalls;
|
|
330
|
+
this.windowMs = windowMs;
|
|
331
|
+
}
|
|
332
|
+
timestamps = [];
|
|
333
|
+
tryAcquire() {
|
|
334
|
+
const now = Date.now();
|
|
335
|
+
while (this.timestamps.length > 0 && this.timestamps[0] <= now - this.windowMs) {
|
|
336
|
+
this.timestamps.shift();
|
|
337
|
+
}
|
|
338
|
+
if (this.timestamps.length >= this.maxCalls) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
this.timestamps.push(now);
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
get remaining() {
|
|
345
|
+
const now = Date.now();
|
|
346
|
+
const active = this.timestamps.filter((t) => t > now - this.windowMs);
|
|
347
|
+
return Math.max(0, this.maxCalls - active.length);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
var BudgetManager = class {
|
|
351
|
+
defaultTimeoutMs;
|
|
352
|
+
bulkheads = /* @__PURE__ */ new Map();
|
|
353
|
+
circuitBreakers = /* @__PURE__ */ new Map();
|
|
354
|
+
rateLimiters = /* @__PURE__ */ new Map();
|
|
355
|
+
options;
|
|
356
|
+
constructor(options = {}) {
|
|
357
|
+
this.options = options;
|
|
358
|
+
this.defaultTimeoutMs = options.defaultTimeoutMs ?? 3e4;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Get effective timeout for a tool invocation.
|
|
362
|
+
*/
|
|
363
|
+
getTimeout(_toolName, contextTimeoutMs) {
|
|
364
|
+
return contextTimeoutMs ?? this.defaultTimeoutMs;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Check rate limit for a tool. Returns true if allowed.
|
|
368
|
+
*/
|
|
369
|
+
checkRateLimit(toolName) {
|
|
370
|
+
if (!this.options.rateLimit) return true;
|
|
371
|
+
let limiter = this.rateLimiters.get(toolName);
|
|
372
|
+
if (!limiter) {
|
|
373
|
+
limiter = new RateLimiter(
|
|
374
|
+
this.options.rateLimit.maxCalls,
|
|
375
|
+
this.options.rateLimit.windowMs
|
|
376
|
+
);
|
|
377
|
+
this.rateLimiters.set(toolName, limiter);
|
|
378
|
+
}
|
|
379
|
+
return limiter.tryAcquire();
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Get or create a bulkhead (concurrency limiter) for a tool.
|
|
383
|
+
*/
|
|
384
|
+
getBulkhead(toolName) {
|
|
385
|
+
if (!this.options.maxConcurrency) return void 0;
|
|
386
|
+
let bh = this.bulkheads.get(toolName);
|
|
387
|
+
if (!bh) {
|
|
388
|
+
bh = bulkhead(this.options.maxConcurrency, 0);
|
|
389
|
+
this.bulkheads.set(toolName, bh);
|
|
390
|
+
}
|
|
391
|
+
return bh;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Get or create a circuit breaker for a tool.
|
|
395
|
+
*/
|
|
396
|
+
getCircuitBreaker(toolName) {
|
|
397
|
+
if (!this.options.circuitBreaker) return void 0;
|
|
398
|
+
let breaker = this.circuitBreakers.get(toolName);
|
|
399
|
+
if (!breaker) {
|
|
400
|
+
breaker = circuitBreaker(handleAll, {
|
|
401
|
+
breaker: new ConsecutiveBreaker(this.options.circuitBreaker.threshold),
|
|
402
|
+
halfOpenAfter: this.options.circuitBreaker.halfOpenAfterMs
|
|
403
|
+
});
|
|
404
|
+
this.circuitBreakers.set(toolName, breaker);
|
|
405
|
+
}
|
|
406
|
+
return breaker;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Execute a function within budget constraints (bulkhead + circuit breaker).
|
|
410
|
+
*/
|
|
411
|
+
async execute(toolName, fn) {
|
|
412
|
+
const bh = this.getBulkhead(toolName);
|
|
413
|
+
const breaker = this.getCircuitBreaker(toolName);
|
|
414
|
+
let wrapped = fn;
|
|
415
|
+
if (breaker) {
|
|
416
|
+
const prevWrapped = wrapped;
|
|
417
|
+
wrapped = () => breaker.execute(() => prevWrapped());
|
|
418
|
+
}
|
|
419
|
+
if (bh) {
|
|
420
|
+
const prevWrapped = wrapped;
|
|
421
|
+
wrapped = () => bh.execute(() => prevWrapped());
|
|
422
|
+
}
|
|
423
|
+
return wrapped();
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Reset all policies for a tool (useful for testing).
|
|
427
|
+
*/
|
|
428
|
+
reset(toolName) {
|
|
429
|
+
this.bulkheads.delete(toolName);
|
|
430
|
+
this.circuitBreakers.delete(toolName);
|
|
431
|
+
this.rateLimiters.delete(toolName);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Reset all policies globally.
|
|
435
|
+
*/
|
|
436
|
+
resetAll() {
|
|
437
|
+
this.bulkheads.clear();
|
|
438
|
+
this.circuitBreakers.clear();
|
|
439
|
+
this.rateLimiters.clear();
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// src/core/runtime/Evidence.ts
|
|
444
|
+
function buildEvidence(options) {
|
|
445
|
+
const { spec, args, result, ctx, durationMs } = options;
|
|
446
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
447
|
+
const evidence = [];
|
|
448
|
+
evidence.push({
|
|
449
|
+
type: "tool",
|
|
450
|
+
ref: `${spec.name}@${spec.version}`,
|
|
451
|
+
summary: summarizeToolCall(spec, args, result, durationMs),
|
|
452
|
+
createdAt: now
|
|
453
|
+
});
|
|
454
|
+
if (result && typeof result === "object") {
|
|
455
|
+
const urls = extractUrls(result);
|
|
456
|
+
for (const url of urls) {
|
|
457
|
+
evidence.push({
|
|
458
|
+
type: "url",
|
|
459
|
+
ref: url,
|
|
460
|
+
summary: `Output URL from ${spec.name}`,
|
|
461
|
+
createdAt: now
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
const files = extractFilePaths(result);
|
|
465
|
+
for (const file of files) {
|
|
466
|
+
evidence.push({
|
|
467
|
+
type: "file",
|
|
468
|
+
ref: file,
|
|
469
|
+
summary: `Output file from ${spec.name}`,
|
|
470
|
+
createdAt: now
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (durationMs !== void 0 && durationMs > 0) {
|
|
475
|
+
evidence.push({
|
|
476
|
+
type: "metric",
|
|
477
|
+
ref: `latency:${spec.name}`,
|
|
478
|
+
summary: `Completed in ${durationMs}ms (request: ${ctx.requestId})`,
|
|
479
|
+
createdAt: now
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
return evidence;
|
|
483
|
+
}
|
|
484
|
+
function summarizeToolCall(spec, args, result, durationMs) {
|
|
485
|
+
const argKeys = args && typeof args === "object" ? Object.keys(args).join(", ") : "none";
|
|
486
|
+
const duration = durationMs ? ` in ${durationMs}ms` : "";
|
|
487
|
+
const resultPreview = summarizeValue(result, 100);
|
|
488
|
+
return `${spec.kind}:${spec.name} called with [${argKeys}]${duration} \u2192 ${resultPreview}`;
|
|
489
|
+
}
|
|
490
|
+
function summarizeValue(value, maxLen) {
|
|
491
|
+
if (value === null || value === void 0) return "null";
|
|
492
|
+
if (typeof value === "string") {
|
|
493
|
+
return value.length > maxLen ? value.slice(0, maxLen) + "..." : value;
|
|
494
|
+
}
|
|
495
|
+
const str = JSON.stringify(value);
|
|
496
|
+
return str.length > maxLen ? str.slice(0, maxLen) + "..." : str;
|
|
497
|
+
}
|
|
498
|
+
function extractUrls(obj) {
|
|
499
|
+
const urls = [];
|
|
500
|
+
const walk = (val) => {
|
|
501
|
+
if (typeof val === "string" && /^https?:\/\//i.test(val)) {
|
|
502
|
+
urls.push(val);
|
|
503
|
+
} else if (val && typeof val === "object") {
|
|
504
|
+
for (const v of Object.values(val)) {
|
|
505
|
+
walk(v);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
walk(obj);
|
|
510
|
+
return urls.slice(0, 10);
|
|
511
|
+
}
|
|
512
|
+
function extractFilePaths(obj) {
|
|
513
|
+
const paths = [];
|
|
514
|
+
const walk = (val) => {
|
|
515
|
+
if (typeof val === "string" && (val.startsWith("/") || val.startsWith("./")) && val.includes(".")) {
|
|
516
|
+
paths.push(val);
|
|
517
|
+
} else if (val && typeof val === "object") {
|
|
518
|
+
for (const v of Object.values(val)) {
|
|
519
|
+
walk(v);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
walk(obj);
|
|
524
|
+
return paths.slice(0, 10);
|
|
525
|
+
}
|
|
526
|
+
var EventLog = class {
|
|
527
|
+
entries = [];
|
|
528
|
+
seq = 0;
|
|
529
|
+
maxEntries;
|
|
530
|
+
emitter = new EventEmitter();
|
|
531
|
+
constructor(options = {}) {
|
|
532
|
+
this.maxEntries = options.maxEntries ?? 1e4;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Append an event to the log.
|
|
536
|
+
*/
|
|
537
|
+
append(event) {
|
|
538
|
+
const entry = { seq: ++this.seq, event };
|
|
539
|
+
this.entries.push(entry);
|
|
540
|
+
if (this.entries.length > this.maxEntries) {
|
|
541
|
+
this.entries.shift();
|
|
542
|
+
}
|
|
543
|
+
this.emitter.emit("event", entry);
|
|
544
|
+
this.emitter.emit(event.type, entry);
|
|
545
|
+
return entry;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Subscribe to all events.
|
|
549
|
+
*/
|
|
550
|
+
on(listener) {
|
|
551
|
+
this.emitter.on("event", listener);
|
|
552
|
+
return () => this.emitter.off("event", listener);
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Subscribe to events of a specific type.
|
|
556
|
+
*/
|
|
557
|
+
onType(type, listener) {
|
|
558
|
+
this.emitter.on(type, listener);
|
|
559
|
+
return () => this.emitter.off(type, listener);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Query events by filter.
|
|
563
|
+
*/
|
|
564
|
+
query(filter) {
|
|
565
|
+
let results = this.entries;
|
|
566
|
+
if (filter.since !== void 0) {
|
|
567
|
+
results = results.filter((e) => e.seq > filter.since);
|
|
568
|
+
}
|
|
569
|
+
if (filter.type) {
|
|
570
|
+
results = results.filter((e) => e.event.type === filter.type);
|
|
571
|
+
}
|
|
572
|
+
if (filter.toolName) {
|
|
573
|
+
results = results.filter((e) => e.event.toolName === filter.toolName);
|
|
574
|
+
}
|
|
575
|
+
if (filter.requestId) {
|
|
576
|
+
results = results.filter((e) => e.event.requestId === filter.requestId);
|
|
577
|
+
}
|
|
578
|
+
if (filter.limit) {
|
|
579
|
+
results = results.slice(-filter.limit);
|
|
580
|
+
}
|
|
581
|
+
return results;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Get all entries.
|
|
585
|
+
*/
|
|
586
|
+
getAll() {
|
|
587
|
+
return this.entries;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Get entry count.
|
|
591
|
+
*/
|
|
592
|
+
get size() {
|
|
593
|
+
return this.entries.length;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Clear all entries (for testing).
|
|
597
|
+
*/
|
|
598
|
+
clear() {
|
|
599
|
+
this.entries.length = 0;
|
|
600
|
+
this.seq = 0;
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// src/observability/Logger.ts
|
|
605
|
+
var LEVEL_ORDER = {
|
|
606
|
+
silent: 0,
|
|
607
|
+
error: 1,
|
|
608
|
+
warn: 2,
|
|
609
|
+
info: 3,
|
|
610
|
+
debug: 4,
|
|
611
|
+
trace: 5
|
|
612
|
+
};
|
|
613
|
+
function createLogger(options = {}) {
|
|
614
|
+
const resolved = resolveDebugOptions(options);
|
|
615
|
+
const log = (level, message, meta) => {
|
|
616
|
+
if (!resolved.enabled) return;
|
|
617
|
+
if (LEVEL_ORDER[level] > LEVEL_ORDER[resolved.level]) return;
|
|
618
|
+
const prefix = `[${resolved.prefix}]`;
|
|
619
|
+
const levelTag = `[${level.toUpperCase()}]`;
|
|
620
|
+
const metaText = meta ? ` ${safeStringify(meta, 1e3)}` : "";
|
|
621
|
+
switch (level) {
|
|
622
|
+
case "error":
|
|
623
|
+
console.error(`${prefix} ${levelTag} ${message}${metaText}`);
|
|
624
|
+
break;
|
|
625
|
+
case "warn":
|
|
626
|
+
console.warn(`${prefix} ${levelTag} ${message}${metaText}`);
|
|
627
|
+
break;
|
|
628
|
+
case "info":
|
|
629
|
+
console.info(`${prefix} ${levelTag} ${message}${metaText}`);
|
|
630
|
+
break;
|
|
631
|
+
default:
|
|
632
|
+
console.log(`${prefix} ${levelTag} ${message}${metaText}`);
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
return {
|
|
637
|
+
options: resolved,
|
|
638
|
+
isEnabled: (level) => resolved.enabled && LEVEL_ORDER[level] <= LEVEL_ORDER[resolved.level],
|
|
639
|
+
error: (message, meta) => log("error", message, meta),
|
|
640
|
+
warn: (message, meta) => log("warn", message, meta),
|
|
641
|
+
info: (message, meta) => log("info", message, meta),
|
|
642
|
+
debug: (message, meta) => log("debug", message, meta),
|
|
643
|
+
trace: (message, meta) => log("trace", message, meta)
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function resolveDebugOptions(options = {}) {
|
|
647
|
+
const envLevel = parseEnvLogLevel();
|
|
648
|
+
const enabledFromEnv = envLevel !== void 0 && envLevel !== "silent";
|
|
649
|
+
const enabled = options.enabled ?? enabledFromEnv ?? false;
|
|
650
|
+
const level = options.level ?? envLevel ?? (enabled ? "debug" : "silent");
|
|
651
|
+
return {
|
|
652
|
+
enabled,
|
|
653
|
+
level,
|
|
654
|
+
includeArgs: options.includeArgs ?? false,
|
|
655
|
+
includeResults: options.includeResults ?? false,
|
|
656
|
+
includeRaw: options.includeRaw ?? false,
|
|
657
|
+
logEvents: options.logEvents ?? false,
|
|
658
|
+
prefix: options.prefix ?? "agent-tool"
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function sanitizeForLog(value, maxLen = 500) {
|
|
662
|
+
const str = safeStringify(value, maxLen);
|
|
663
|
+
return str.replace(
|
|
664
|
+
/"(password|token|secret|key|auth)":\s*"[^"]*"/gi,
|
|
665
|
+
'"$1":"[REDACTED]"'
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
function summarizeForLog(value, maxLen = 200) {
|
|
669
|
+
if (value === null) return "null";
|
|
670
|
+
if (value === void 0) return "undefined";
|
|
671
|
+
if (typeof value === "string") {
|
|
672
|
+
return value.length > maxLen ? `${value.slice(0, maxLen)}...` : value;
|
|
673
|
+
}
|
|
674
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
675
|
+
return String(value);
|
|
676
|
+
}
|
|
677
|
+
if (Array.isArray(value)) {
|
|
678
|
+
return `Array(${value.length})`;
|
|
679
|
+
}
|
|
680
|
+
if (typeof value === "object") {
|
|
681
|
+
const keys = Object.keys(value);
|
|
682
|
+
const shown = keys.slice(0, 5).join(", ");
|
|
683
|
+
return `Object(keys: ${shown}${keys.length > 5 ? ", ..." : ""})`;
|
|
684
|
+
}
|
|
685
|
+
return String(value);
|
|
686
|
+
}
|
|
687
|
+
function safeStringify(value, maxLen) {
|
|
688
|
+
try {
|
|
689
|
+
const json = JSON.stringify(value);
|
|
690
|
+
if (!json) return String(value);
|
|
691
|
+
return json.length > maxLen ? `${json.slice(0, maxLen)}...` : json;
|
|
692
|
+
} catch {
|
|
693
|
+
const fallback = String(value);
|
|
694
|
+
return fallback.length > maxLen ? `${fallback.slice(0, maxLen)}...` : fallback;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
function parseEnvLogLevel() {
|
|
698
|
+
const raw = process.env.TOOLHUB_LOG_LEVEL ?? process.env.TOOLHUB_DEBUG ?? process.env.DEBUG;
|
|
699
|
+
if (!raw) return void 0;
|
|
700
|
+
const value = raw.trim().toLowerCase();
|
|
701
|
+
if (!value || value === "0" || value === "false" || value === "off") {
|
|
702
|
+
return "silent";
|
|
703
|
+
}
|
|
704
|
+
if (value.includes("trace")) return "trace";
|
|
705
|
+
if (value.includes("debug") || value === "1" || value === "true" || value === "yes") {
|
|
706
|
+
return "debug";
|
|
707
|
+
}
|
|
708
|
+
if (value.includes("info")) return "info";
|
|
709
|
+
if (value.includes("warn")) return "warn";
|
|
710
|
+
if (value.includes("error")) return "error";
|
|
711
|
+
if (value.includes("silent")) return "silent";
|
|
712
|
+
return "debug";
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// src/observability/Metrics.ts
|
|
716
|
+
var Metrics = class {
|
|
717
|
+
counters = /* @__PURE__ */ new Map();
|
|
718
|
+
histograms = /* @__PURE__ */ new Map();
|
|
719
|
+
defaultBuckets = [
|
|
720
|
+
5,
|
|
721
|
+
10,
|
|
722
|
+
25,
|
|
723
|
+
50,
|
|
724
|
+
100,
|
|
725
|
+
250,
|
|
726
|
+
500,
|
|
727
|
+
1e3,
|
|
728
|
+
2500,
|
|
729
|
+
5e3,
|
|
730
|
+
1e4
|
|
731
|
+
];
|
|
732
|
+
/**
|
|
733
|
+
* Increment a counter.
|
|
734
|
+
*/
|
|
735
|
+
increment(name, labels = {}, value = 1) {
|
|
736
|
+
const key = this.makeKey(name, labels);
|
|
737
|
+
this.counters.set(key, (this.counters.get(key) ?? 0) + value);
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Record a value in a histogram.
|
|
741
|
+
*/
|
|
742
|
+
observe(name, labels, value) {
|
|
743
|
+
const key = this.makeKey(name, labels);
|
|
744
|
+
let hist = this.histograms.get(key);
|
|
745
|
+
if (!hist) {
|
|
746
|
+
hist = { count: 0, sum: 0, values: [] };
|
|
747
|
+
this.histograms.set(key, hist);
|
|
748
|
+
}
|
|
749
|
+
hist.count++;
|
|
750
|
+
hist.sum += value;
|
|
751
|
+
hist.values.push(value);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Get a counter value.
|
|
755
|
+
*/
|
|
756
|
+
getCounter(name, labels = {}) {
|
|
757
|
+
return this.counters.get(this.makeKey(name, labels)) ?? 0;
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Get histogram stats.
|
|
761
|
+
*/
|
|
762
|
+
getHistogram(name, labels) {
|
|
763
|
+
const key = this.makeKey(name, labels);
|
|
764
|
+
const hist = this.histograms.get(key);
|
|
765
|
+
if (!hist) return void 0;
|
|
766
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
767
|
+
for (const bound of this.defaultBuckets) {
|
|
768
|
+
buckets.set(bound, hist.values.filter((v) => v <= bound).length);
|
|
769
|
+
}
|
|
770
|
+
return { name, labels, count: hist.count, sum: hist.sum, buckets };
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Get all counter values.
|
|
774
|
+
*/
|
|
775
|
+
getAllCounters() {
|
|
776
|
+
const results = [];
|
|
777
|
+
for (const [key, value] of this.counters) {
|
|
778
|
+
const { name, labels } = this.parseKey(key);
|
|
779
|
+
results.push({ name, labels, value });
|
|
780
|
+
}
|
|
781
|
+
return results;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Get all histogram values.
|
|
785
|
+
*/
|
|
786
|
+
getAllHistograms() {
|
|
787
|
+
const results = [];
|
|
788
|
+
for (const [key, hist] of this.histograms) {
|
|
789
|
+
const { name, labels } = this.parseKey(key);
|
|
790
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
791
|
+
for (const bound of this.defaultBuckets) {
|
|
792
|
+
buckets.set(bound, hist.values.filter((v) => v <= bound).length);
|
|
793
|
+
}
|
|
794
|
+
results.push({ name, labels, count: hist.count, sum: hist.sum, buckets });
|
|
795
|
+
}
|
|
796
|
+
return results;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Record standard tool invocation metrics.
|
|
800
|
+
*/
|
|
801
|
+
recordInvocation(toolName, ok, durationMs) {
|
|
802
|
+
this.increment("tool_invocations_total", {
|
|
803
|
+
toolName,
|
|
804
|
+
ok: String(ok)
|
|
805
|
+
});
|
|
806
|
+
this.observe("tool_latency_ms", { toolName }, durationMs);
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Record a retry event.
|
|
810
|
+
*/
|
|
811
|
+
recordRetry(toolName) {
|
|
812
|
+
this.increment("tool_retries_total", { toolName });
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Record a policy denial.
|
|
816
|
+
*/
|
|
817
|
+
recordPolicyDenied(toolName, reason) {
|
|
818
|
+
this.increment("policy_denied_total", { toolName, reason });
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Reset all metrics (for testing).
|
|
822
|
+
*/
|
|
823
|
+
reset() {
|
|
824
|
+
this.counters.clear();
|
|
825
|
+
this.histograms.clear();
|
|
826
|
+
}
|
|
827
|
+
makeKey(name, labels) {
|
|
828
|
+
const sortedLabels = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
|
|
829
|
+
return `${name}{${sortedLabels}}`;
|
|
830
|
+
}
|
|
831
|
+
parseKey(key) {
|
|
832
|
+
const match = key.match(/^(.+?)\{(.*)\}$/);
|
|
833
|
+
if (!match) return { name: key, labels: {} };
|
|
834
|
+
const labels = {};
|
|
835
|
+
if (match[2]) {
|
|
836
|
+
for (const part of match[2].split(",")) {
|
|
837
|
+
const [k, v] = part.split("=");
|
|
838
|
+
if (k && v !== void 0) labels[k] = v;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return { name: match[1], labels };
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
var Tracing = class {
|
|
845
|
+
spans = /* @__PURE__ */ new Map();
|
|
846
|
+
traceSpans = /* @__PURE__ */ new Map();
|
|
847
|
+
// traceId → spanIds
|
|
848
|
+
/**
|
|
849
|
+
* Start a new span.
|
|
850
|
+
*/
|
|
851
|
+
startSpan(options) {
|
|
852
|
+
const span = {
|
|
853
|
+
spanId: v4(),
|
|
854
|
+
traceId: options.traceId ?? v4(),
|
|
855
|
+
parentSpanId: options.parentSpanId,
|
|
856
|
+
name: options.name,
|
|
857
|
+
startTime: Date.now(),
|
|
858
|
+
status: "in_progress",
|
|
859
|
+
attributes: options.attributes ?? {},
|
|
860
|
+
events: []
|
|
861
|
+
};
|
|
862
|
+
this.spans.set(span.spanId, span);
|
|
863
|
+
const traceList = this.traceSpans.get(span.traceId) ?? [];
|
|
864
|
+
traceList.push(span.spanId);
|
|
865
|
+
this.traceSpans.set(span.traceId, traceList);
|
|
866
|
+
return span;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* End a span and calculate duration.
|
|
870
|
+
*/
|
|
871
|
+
endSpan(spanId, status = "ok") {
|
|
872
|
+
const span = this.spans.get(spanId);
|
|
873
|
+
if (!span) return void 0;
|
|
874
|
+
span.endTime = Date.now();
|
|
875
|
+
span.durationMs = span.endTime - span.startTime;
|
|
876
|
+
span.status = status;
|
|
877
|
+
return span;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Add an event to a span.
|
|
881
|
+
*/
|
|
882
|
+
addEvent(spanId, name, attributes) {
|
|
883
|
+
const span = this.spans.get(spanId);
|
|
884
|
+
if (!span) return;
|
|
885
|
+
span.events.push({ name, timestamp: Date.now(), attributes });
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Set attributes on a span.
|
|
889
|
+
*/
|
|
890
|
+
setAttributes(spanId, attributes) {
|
|
891
|
+
const span = this.spans.get(spanId);
|
|
892
|
+
if (!span) return;
|
|
893
|
+
Object.assign(span.attributes, attributes);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Get a span by ID.
|
|
897
|
+
*/
|
|
898
|
+
getSpan(spanId) {
|
|
899
|
+
return this.spans.get(spanId);
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Get all spans for a trace.
|
|
903
|
+
*/
|
|
904
|
+
getTrace(traceId) {
|
|
905
|
+
const spanIds = this.traceSpans.get(traceId) ?? [];
|
|
906
|
+
return spanIds.map((id) => this.spans.get(id)).filter((s) => s !== void 0);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Create a child span from a parent.
|
|
910
|
+
*/
|
|
911
|
+
createChildSpan(parentSpanId, name, attributes) {
|
|
912
|
+
const parent = this.spans.get(parentSpanId);
|
|
913
|
+
if (!parent) return void 0;
|
|
914
|
+
return this.startSpan({
|
|
915
|
+
name,
|
|
916
|
+
traceId: parent.traceId,
|
|
917
|
+
parentSpanId: parent.spanId,
|
|
918
|
+
attributes
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Clear all traces (for testing).
|
|
923
|
+
*/
|
|
924
|
+
clear() {
|
|
925
|
+
this.spans.clear();
|
|
926
|
+
this.traceSpans.clear();
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
function resolveTool(toolName, registry) {
|
|
930
|
+
const spec = registry.get(toolName);
|
|
931
|
+
if (!spec) {
|
|
932
|
+
throw createTaggedError(
|
|
933
|
+
"TOOL_NOT_FOUND",
|
|
934
|
+
`Tool not found: ${toolName}`,
|
|
935
|
+
{ availableTools: registry.snapshot().slice(0, 20).map((s) => s.name) }
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
return spec;
|
|
939
|
+
}
|
|
940
|
+
function validateInput(spec, args, validator) {
|
|
941
|
+
try {
|
|
942
|
+
return validator.validateOrThrow(
|
|
943
|
+
spec.inputSchema,
|
|
944
|
+
args,
|
|
945
|
+
`Input validation failed for ${spec.name}`
|
|
946
|
+
);
|
|
947
|
+
} catch (error) {
|
|
948
|
+
if (error instanceof SchemaValidationError) {
|
|
949
|
+
throw createTaggedError("INPUT_SCHEMA_INVALID", error.message, {
|
|
950
|
+
errors: error.errors,
|
|
951
|
+
schema: spec.inputSchema
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
throw error;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
function enrichDefaults(spec, args, validator) {
|
|
958
|
+
return validator.enrichDefaults(spec.inputSchema, args);
|
|
959
|
+
}
|
|
960
|
+
function enforcePolicy(spec, args, ctx, deps) {
|
|
961
|
+
try {
|
|
962
|
+
deps.policy.enforce(spec, args, ctx);
|
|
963
|
+
} catch (error) {
|
|
964
|
+
if (error instanceof PolicyDeniedError) {
|
|
965
|
+
const event = {
|
|
966
|
+
type: "POLICY_DENIED",
|
|
967
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
968
|
+
requestId: ctx.requestId,
|
|
969
|
+
taskId: ctx.taskId,
|
|
970
|
+
toolName: spec.name,
|
|
971
|
+
traceId: ctx.traceId,
|
|
972
|
+
userId: ctx.userId,
|
|
973
|
+
reason: error.message,
|
|
974
|
+
missingCapabilities: error.missingCapabilities?.map(String)
|
|
975
|
+
};
|
|
976
|
+
deps.eventLog.append(event);
|
|
977
|
+
deps.metrics.recordPolicyDenied(spec.name, error.message);
|
|
978
|
+
}
|
|
979
|
+
throw error;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
var HITL_GATED_SIDE_EFFECTS = ["external_write", "destructive"];
|
|
983
|
+
async function requireHumanApproval(spec, args, ctx, deps) {
|
|
984
|
+
const sideEffect = spec._meta?.hitl?.sideEffect ?? "none";
|
|
985
|
+
if (!HITL_GATED_SIDE_EFFECTS.includes(sideEffect)) return;
|
|
986
|
+
const onApproval = deps.onApprovalRequired;
|
|
987
|
+
if (!onApproval) return;
|
|
988
|
+
const baseEvent = {
|
|
989
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
990
|
+
requestId: ctx.requestId,
|
|
991
|
+
taskId: ctx.taskId,
|
|
992
|
+
toolName: spec.name,
|
|
993
|
+
traceId: ctx.traceId,
|
|
994
|
+
userId: ctx.userId
|
|
995
|
+
};
|
|
996
|
+
const requested = {
|
|
997
|
+
...baseEvent,
|
|
998
|
+
type: "HITL_APPROVAL_REQUESTED",
|
|
999
|
+
sideEffect
|
|
1000
|
+
};
|
|
1001
|
+
deps.eventLog.append(requested);
|
|
1002
|
+
deps.logger.trace("hitl.requested", { tool: spec.name, sideEffect, requestId: ctx.requestId });
|
|
1003
|
+
let approved;
|
|
1004
|
+
try {
|
|
1005
|
+
approved = await onApproval(spec, args, ctx);
|
|
1006
|
+
} catch (err) {
|
|
1007
|
+
deps.eventLog.append({
|
|
1008
|
+
...baseEvent,
|
|
1009
|
+
type: "HITL_APPROVAL_DENIED",
|
|
1010
|
+
sideEffect,
|
|
1011
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
1012
|
+
});
|
|
1013
|
+
throw createTaggedError(
|
|
1014
|
+
"HITL_DENIED",
|
|
1015
|
+
`Human denied approval for ${spec.name} (${sideEffect})`,
|
|
1016
|
+
{ reason: err instanceof Error ? err.message : String(err) }
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
if (approved === false) {
|
|
1020
|
+
deps.eventLog.append({
|
|
1021
|
+
...baseEvent,
|
|
1022
|
+
type: "HITL_APPROVAL_DENIED",
|
|
1023
|
+
sideEffect,
|
|
1024
|
+
reason: "User rejected"
|
|
1025
|
+
});
|
|
1026
|
+
throw createTaggedError("HITL_DENIED", `Human denied approval for ${spec.name} (${sideEffect})`);
|
|
1027
|
+
}
|
|
1028
|
+
deps.eventLog.append({
|
|
1029
|
+
...baseEvent,
|
|
1030
|
+
type: "HITL_APPROVAL_GRANTED",
|
|
1031
|
+
sideEffect
|
|
1032
|
+
});
|
|
1033
|
+
deps.logger.trace("hitl.granted", { tool: spec.name, sideEffect, requestId: ctx.requestId });
|
|
1034
|
+
}
|
|
1035
|
+
async function executeWithBudget(spec, args, ctx, spanId, deps) {
|
|
1036
|
+
const adapter = deps.adapters.get(spec.kind);
|
|
1037
|
+
if (!adapter) {
|
|
1038
|
+
throw createTaggedError(
|
|
1039
|
+
"TOOL_NOT_FOUND",
|
|
1040
|
+
`No adapter registered for kind: ${spec.kind}`
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
const timeoutMs = deps.budget.getTimeout(
|
|
1044
|
+
spec.name,
|
|
1045
|
+
ctx.budget?.timeoutMs
|
|
1046
|
+
);
|
|
1047
|
+
const maxRetries = ctx.budget?.maxRetries ?? deps.defaultMaxRetries ?? 2;
|
|
1048
|
+
const executeFn = async () => {
|
|
1049
|
+
return deps.budget.execute(spec.name, async () => {
|
|
1050
|
+
deps.tracing.addEvent(spanId, "execute_start");
|
|
1051
|
+
deps.logger.trace("execute.start", {
|
|
1052
|
+
tool: spec.name,
|
|
1053
|
+
requestId: ctx.requestId,
|
|
1054
|
+
timeoutMs,
|
|
1055
|
+
maxRetries
|
|
1056
|
+
});
|
|
1057
|
+
const result = await adapter.invoke(spec, args, ctx);
|
|
1058
|
+
deps.tracing.addEvent(spanId, "execute_end");
|
|
1059
|
+
deps.logger.trace("execute.end", {
|
|
1060
|
+
tool: spec.name,
|
|
1061
|
+
requestId: ctx.requestId
|
|
1062
|
+
});
|
|
1063
|
+
return result;
|
|
1064
|
+
});
|
|
1065
|
+
};
|
|
1066
|
+
const retryFn = () => withRetry(executeFn, {
|
|
1067
|
+
maxRetries,
|
|
1068
|
+
onRetry: (error, attempt) => {
|
|
1069
|
+
deps.metrics.recordRetry(spec.name);
|
|
1070
|
+
const event = {
|
|
1071
|
+
type: "RETRY",
|
|
1072
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1073
|
+
requestId: ctx.requestId,
|
|
1074
|
+
taskId: ctx.taskId,
|
|
1075
|
+
toolName: spec.name,
|
|
1076
|
+
traceId: ctx.traceId,
|
|
1077
|
+
userId: ctx.userId,
|
|
1078
|
+
attempt,
|
|
1079
|
+
maxRetries,
|
|
1080
|
+
reason: error.message
|
|
1081
|
+
};
|
|
1082
|
+
deps.eventLog.append(event);
|
|
1083
|
+
deps.tracing.addEvent(spanId, "retry", { attempt, reason: error.message });
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
try {
|
|
1087
|
+
return await pTimeout(retryFn(), {
|
|
1088
|
+
milliseconds: timeoutMs,
|
|
1089
|
+
message: `Tool ${spec.name} timed out after ${timeoutMs}ms`
|
|
1090
|
+
});
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
if (error instanceof Error && error.message.includes("timed out")) {
|
|
1093
|
+
throw createTaggedError("TIMEOUT", error.message);
|
|
1094
|
+
}
|
|
1095
|
+
throw error;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
function validateOutput(spec, result, validator) {
|
|
1099
|
+
try {
|
|
1100
|
+
return validator.validateOrThrow(
|
|
1101
|
+
spec.outputSchema,
|
|
1102
|
+
result,
|
|
1103
|
+
`Output validation failed for ${spec.name}`
|
|
1104
|
+
);
|
|
1105
|
+
} catch (error) {
|
|
1106
|
+
if (error instanceof SchemaValidationError) {
|
|
1107
|
+
throw createTaggedError("OUTPUT_SCHEMA_INVALID", error.message, {
|
|
1108
|
+
errors: error.errors
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
throw error;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// src/core/runtime/PTCRuntimeObservability.ts
|
|
1116
|
+
function emitToolCalled(intent, ctx, deps) {
|
|
1117
|
+
const event = {
|
|
1118
|
+
type: "TOOL_CALLED",
|
|
1119
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1120
|
+
requestId: ctx.requestId,
|
|
1121
|
+
taskId: ctx.taskId,
|
|
1122
|
+
toolName: intent.tool,
|
|
1123
|
+
traceId: ctx.traceId,
|
|
1124
|
+
userId: ctx.userId,
|
|
1125
|
+
argsSummary: sanitizeArgs(intent.args),
|
|
1126
|
+
purpose: intent.purpose,
|
|
1127
|
+
idempotencyKey: intent.idempotencyKey
|
|
1128
|
+
};
|
|
1129
|
+
deps.eventLog.append(event);
|
|
1130
|
+
}
|
|
1131
|
+
function recordSuccess(spec, durationMs, _evidence, spanId, deps) {
|
|
1132
|
+
deps.metrics.recordInvocation(spec.name, true, durationMs);
|
|
1133
|
+
deps.tracing.setAttributes(spanId, {
|
|
1134
|
+
"tool.duration_ms": durationMs,
|
|
1135
|
+
"tool.ok": true
|
|
1136
|
+
});
|
|
1137
|
+
deps.tracing.endSpan(spanId, "ok");
|
|
1138
|
+
}
|
|
1139
|
+
function handleError(error, intent, ctx, durationMs, spanId, deps) {
|
|
1140
|
+
const kind = error?.kind ?? "UPSTREAM_ERROR";
|
|
1141
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1142
|
+
const details = error?.details;
|
|
1143
|
+
deps.metrics.recordInvocation(intent.tool, false, durationMs);
|
|
1144
|
+
deps.tracing.setAttributes(spanId, {
|
|
1145
|
+
"tool.duration_ms": durationMs,
|
|
1146
|
+
"tool.ok": false,
|
|
1147
|
+
"tool.error_kind": kind
|
|
1148
|
+
});
|
|
1149
|
+
deps.tracing.endSpan(spanId, "error");
|
|
1150
|
+
const event = {
|
|
1151
|
+
type: "TOOL_RESULT",
|
|
1152
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1153
|
+
requestId: ctx.requestId,
|
|
1154
|
+
taskId: ctx.taskId,
|
|
1155
|
+
toolName: intent.tool,
|
|
1156
|
+
traceId: ctx.traceId,
|
|
1157
|
+
userId: ctx.userId,
|
|
1158
|
+
ok: false,
|
|
1159
|
+
durationMs,
|
|
1160
|
+
resultSummary: message,
|
|
1161
|
+
evidence: [],
|
|
1162
|
+
error: { kind, message, details }
|
|
1163
|
+
};
|
|
1164
|
+
deps.eventLog.append(event);
|
|
1165
|
+
deps.logger.warn("invoke.error", {
|
|
1166
|
+
tool: intent.tool,
|
|
1167
|
+
requestId: ctx.requestId,
|
|
1168
|
+
taskId: ctx.taskId,
|
|
1169
|
+
traceId: ctx.traceId,
|
|
1170
|
+
kind,
|
|
1171
|
+
message,
|
|
1172
|
+
durationMs,
|
|
1173
|
+
details: deps.logger.options.includeResults ? summarizeForLog(details) : void 0
|
|
1174
|
+
});
|
|
1175
|
+
return {
|
|
1176
|
+
ok: false,
|
|
1177
|
+
evidence: [],
|
|
1178
|
+
error: { kind, message, details }
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
function sanitizeArgs(args) {
|
|
1182
|
+
if (!args) return "{}";
|
|
1183
|
+
return sanitizeForLog(args);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// src/core/runtime/PTCRuntime.ts
|
|
1187
|
+
var PTCRuntime = class {
|
|
1188
|
+
registry;
|
|
1189
|
+
adapters = /* @__PURE__ */ new Map();
|
|
1190
|
+
validator;
|
|
1191
|
+
policy;
|
|
1192
|
+
budget;
|
|
1193
|
+
eventLog;
|
|
1194
|
+
metrics;
|
|
1195
|
+
tracing;
|
|
1196
|
+
config;
|
|
1197
|
+
logger;
|
|
1198
|
+
constructor(options = {}) {
|
|
1199
|
+
this.config = options.config ?? {};
|
|
1200
|
+
this.registry = options.registry ?? new ToolRegistry();
|
|
1201
|
+
this.validator = options.validator ?? new SchemaValidator();
|
|
1202
|
+
this.policy = options.policy ?? new PolicyEngine(this.config.policy);
|
|
1203
|
+
this.budget = options.budget ?? new BudgetManager(this.config.budget);
|
|
1204
|
+
this.eventLog = options.eventLog ?? new EventLog();
|
|
1205
|
+
this.metrics = options.metrics ?? new Metrics();
|
|
1206
|
+
this.tracing = options.tracing ?? new Tracing();
|
|
1207
|
+
this.logger = createLogger({ ...this.config.debug, prefix: "PTCRuntime" });
|
|
1208
|
+
if (this.logger.options.logEvents) {
|
|
1209
|
+
this.eventLog.on((entry) => {
|
|
1210
|
+
const event = entry.event;
|
|
1211
|
+
this.logger.debug("event", {
|
|
1212
|
+
seq: entry.seq,
|
|
1213
|
+
type: event.type,
|
|
1214
|
+
toolName: event.toolName,
|
|
1215
|
+
requestId: event.requestId,
|
|
1216
|
+
taskId: event.taskId,
|
|
1217
|
+
ok: "ok" in event ? event.ok : void 0
|
|
1218
|
+
});
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Register an adapter for a tool kind.
|
|
1224
|
+
*/
|
|
1225
|
+
registerAdapter(adapter) {
|
|
1226
|
+
this.adapters.set(adapter.kind, adapter);
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Get an adapter by kind (e.g. "mcp"). Use to set MCP client via adapter.setClient().
|
|
1230
|
+
*/
|
|
1231
|
+
getAdapter(kind) {
|
|
1232
|
+
return this.adapters.get(kind);
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Get the tool registry.
|
|
1236
|
+
*/
|
|
1237
|
+
getRegistry() {
|
|
1238
|
+
return this.registry;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Get the event log.
|
|
1242
|
+
*/
|
|
1243
|
+
getEventLog() {
|
|
1244
|
+
return this.eventLog;
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Get the metrics collector.
|
|
1248
|
+
*/
|
|
1249
|
+
getMetrics() {
|
|
1250
|
+
return this.metrics;
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Get the tracing system.
|
|
1254
|
+
*/
|
|
1255
|
+
getTracing() {
|
|
1256
|
+
return this.tracing;
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Invoke a tool through the PTC pipeline.
|
|
1260
|
+
* Never throws - always returns a structured ToolResult.
|
|
1261
|
+
*/
|
|
1262
|
+
async invoke(intent, ctx) {
|
|
1263
|
+
const startTime = Date.now();
|
|
1264
|
+
if (this.logger.isEnabled("debug")) {
|
|
1265
|
+
this.logger.debug("invoke.start", {
|
|
1266
|
+
tool: intent.tool,
|
|
1267
|
+
requestId: ctx.requestId,
|
|
1268
|
+
taskId: ctx.taskId,
|
|
1269
|
+
traceId: ctx.traceId,
|
|
1270
|
+
purpose: intent.purpose,
|
|
1271
|
+
args: this.logger.options.includeArgs ? sanitizeForLog(intent.args) : void 0
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
const span = this.tracing.startSpan({
|
|
1275
|
+
name: `tool:${intent.tool}`,
|
|
1276
|
+
traceId: ctx.traceId,
|
|
1277
|
+
attributes: {
|
|
1278
|
+
"tool.name": intent.tool,
|
|
1279
|
+
"tool.purpose": intent.purpose,
|
|
1280
|
+
requestId: ctx.requestId,
|
|
1281
|
+
taskId: ctx.taskId
|
|
1282
|
+
}
|
|
1283
|
+
});
|
|
1284
|
+
emitToolCalled(intent, ctx, this.getObservabilityDeps());
|
|
1285
|
+
try {
|
|
1286
|
+
const spec = resolveTool(intent.tool, this.registry);
|
|
1287
|
+
this.tracing.addEvent(span.spanId, "resolved", {
|
|
1288
|
+
kind: spec.kind,
|
|
1289
|
+
version: spec.version
|
|
1290
|
+
});
|
|
1291
|
+
const validatedArgs = validateInput(spec, intent.args, this.validator);
|
|
1292
|
+
const enrichedArgs = enrichDefaults(spec, validatedArgs, this.validator);
|
|
1293
|
+
enforcePolicy(spec, enrichedArgs, ctx, {
|
|
1294
|
+
policy: this.policy,
|
|
1295
|
+
eventLog: this.eventLog,
|
|
1296
|
+
metrics: this.metrics,
|
|
1297
|
+
tracing: this.tracing
|
|
1298
|
+
});
|
|
1299
|
+
if (!this.budget.checkRateLimit(spec.name)) {
|
|
1300
|
+
throw createTaggedError(
|
|
1301
|
+
"BUDGET_EXCEEDED",
|
|
1302
|
+
`Rate limit exceeded for tool: ${spec.name}`
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
await requireHumanApproval(spec, enrichedArgs, ctx, {
|
|
1306
|
+
onApprovalRequired: this.config.onApprovalRequired,
|
|
1307
|
+
eventLog: this.eventLog,
|
|
1308
|
+
logger: this.logger
|
|
1309
|
+
});
|
|
1310
|
+
if (ctx.dryRun) {
|
|
1311
|
+
return this.buildDryRunResult(spec, enrichedArgs, ctx, startTime, span.spanId);
|
|
1312
|
+
}
|
|
1313
|
+
const { result, raw } = await executeWithBudget(
|
|
1314
|
+
spec,
|
|
1315
|
+
enrichedArgs,
|
|
1316
|
+
ctx,
|
|
1317
|
+
span.spanId,
|
|
1318
|
+
this.getPipelineDeps()
|
|
1319
|
+
);
|
|
1320
|
+
const validatedOutput = validateOutput(spec, result, this.validator);
|
|
1321
|
+
const durationMs = Date.now() - startTime;
|
|
1322
|
+
const builtEvidence = buildEvidence({
|
|
1323
|
+
spec,
|
|
1324
|
+
args: enrichedArgs,
|
|
1325
|
+
result: validatedOutput,
|
|
1326
|
+
raw,
|
|
1327
|
+
ctx,
|
|
1328
|
+
durationMs
|
|
1329
|
+
});
|
|
1330
|
+
const adapterEvidence = raw && typeof raw === "object" && Array.isArray(raw.evidence) ? raw.evidence : [];
|
|
1331
|
+
const evidence = [...adapterEvidence, ...builtEvidence];
|
|
1332
|
+
recordSuccess(spec, durationMs, evidence, span.spanId, this.getObservabilityDeps());
|
|
1333
|
+
if (this.logger.isEnabled("debug")) {
|
|
1334
|
+
this.logger.debug("invoke.ok", {
|
|
1335
|
+
tool: spec.name,
|
|
1336
|
+
durationMs,
|
|
1337
|
+
result: this.logger.options.includeResults ? summarizeForLog(validatedOutput) : void 0,
|
|
1338
|
+
raw: this.logger.options.includeRaw ? summarizeForLog(raw) : void 0
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
return {
|
|
1342
|
+
ok: true,
|
|
1343
|
+
result: validatedOutput,
|
|
1344
|
+
evidence,
|
|
1345
|
+
raw: this.config.includeRaw !== false ? raw : void 0
|
|
1346
|
+
};
|
|
1347
|
+
} catch (error) {
|
|
1348
|
+
const durationMs = Date.now() - startTime;
|
|
1349
|
+
return handleError(error, intent, ctx, durationMs, span.spanId, this.getObservabilityDeps());
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Search for tools in the registry.
|
|
1354
|
+
*/
|
|
1355
|
+
searchTools(query, filters) {
|
|
1356
|
+
return this.registry.search({
|
|
1357
|
+
text: query,
|
|
1358
|
+
kind: filters?.kind,
|
|
1359
|
+
capabilities: filters?.capabilities,
|
|
1360
|
+
tags: filters?.tags
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Get the schema for a tool.
|
|
1365
|
+
*/
|
|
1366
|
+
getToolSchema(toolName) {
|
|
1367
|
+
const spec = this.registry.get(toolName);
|
|
1368
|
+
if (!spec) return void 0;
|
|
1369
|
+
return { input: spec.inputSchema, output: spec.outputSchema };
|
|
1370
|
+
}
|
|
1371
|
+
// --- Helper Methods ---
|
|
1372
|
+
getPipelineDeps() {
|
|
1373
|
+
return {
|
|
1374
|
+
registry: this.registry,
|
|
1375
|
+
adapters: this.adapters,
|
|
1376
|
+
validator: this.validator,
|
|
1377
|
+
policy: this.policy,
|
|
1378
|
+
budget: this.budget,
|
|
1379
|
+
eventLog: this.eventLog,
|
|
1380
|
+
metrics: this.metrics,
|
|
1381
|
+
tracing: this.tracing,
|
|
1382
|
+
logger: this.logger,
|
|
1383
|
+
defaultMaxRetries: this.config.defaultMaxRetries,
|
|
1384
|
+
onApprovalRequired: this.config.onApprovalRequired
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
getObservabilityDeps() {
|
|
1388
|
+
return {
|
|
1389
|
+
eventLog: this.eventLog,
|
|
1390
|
+
metrics: this.metrics,
|
|
1391
|
+
tracing: this.tracing,
|
|
1392
|
+
logger: this.logger
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
buildDryRunResult(spec, args, _ctx, startTime, spanId) {
|
|
1396
|
+
this.tracing.endSpan(spanId, "ok");
|
|
1397
|
+
return {
|
|
1398
|
+
ok: true,
|
|
1399
|
+
result: {
|
|
1400
|
+
dryRun: true,
|
|
1401
|
+
tool: spec.name,
|
|
1402
|
+
kind: spec.kind,
|
|
1403
|
+
args,
|
|
1404
|
+
capabilities: spec.capabilities
|
|
1405
|
+
},
|
|
1406
|
+
evidence: [
|
|
1407
|
+
{
|
|
1408
|
+
type: "tool",
|
|
1409
|
+
ref: `${spec.name}@${spec.version}`,
|
|
1410
|
+
summary: `Dry-run: would execute ${spec.kind}:${spec.name}`,
|
|
1411
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1412
|
+
}
|
|
1413
|
+
]
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1417
|
+
|
|
1418
|
+
// src/tools/util/toolDescriptor.ts
|
|
1419
|
+
var TOOL_PATH_REGEX = /^([a-z][a-z0-9-]*):([^/]+)\/([^#]+)(#(.+))?$/;
|
|
1420
|
+
function isToolPath(descriptor) {
|
|
1421
|
+
return TOOL_PATH_REGEX.test(descriptor.trim());
|
|
1422
|
+
}
|
|
1423
|
+
function parseToolPath(descriptor) {
|
|
1424
|
+
const s = descriptor.trim();
|
|
1425
|
+
const m = s.match(TOOL_PATH_REGEX);
|
|
1426
|
+
if (!m || m[1] === void 0 || m[2] === void 0 || m[3] === void 0) return null;
|
|
1427
|
+
return {
|
|
1428
|
+
protocol: m[1],
|
|
1429
|
+
scope: m[2],
|
|
1430
|
+
packageWithVersion: m[3],
|
|
1431
|
+
toolName: m[5] ?? ""
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
function parsePackageVersion(packageWithVersion) {
|
|
1435
|
+
const at = packageWithVersion.lastIndexOf("@");
|
|
1436
|
+
if (at <= 0) return { package: packageWithVersion };
|
|
1437
|
+
const pkg = packageWithVersion.slice(0, at);
|
|
1438
|
+
const version = packageWithVersion.slice(at + 1);
|
|
1439
|
+
if (!version || /^\d/.test(version)) return { package: pkg, version };
|
|
1440
|
+
return { package: packageWithVersion };
|
|
1441
|
+
}
|
|
1442
|
+
function isNpmToolDescriptor(descriptor) {
|
|
1443
|
+
return isToolPath(descriptor) && parseToolPath(descriptor)?.protocol === "npm";
|
|
1444
|
+
}
|
|
1445
|
+
function parseNpmToolDescriptor(descriptor) {
|
|
1446
|
+
const parsed = parseToolPath(descriptor);
|
|
1447
|
+
if (!parsed || parsed.protocol !== "npm") return null;
|
|
1448
|
+
return {
|
|
1449
|
+
fullPackage: `${parsed.scope}/${parsed.packageWithVersion}`,
|
|
1450
|
+
toolPath: parsed.toolName
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
var BUILTIN_NPM_PACKAGE = "@easynet/agent-tool-builtin";
|
|
1454
|
+
var BUILTIN_REGISTRY_PREFIX = "core/";
|
|
1455
|
+
var BUILTIN_DISPLAY_SCOPE_PREFIX = "npm:@easynet/agent-tool-builtin";
|
|
1456
|
+
function getDisplayScope(registryName, kind, toolVersion) {
|
|
1457
|
+
if (kind === "core" || registryName.startsWith(BUILTIN_REGISTRY_PREFIX)) {
|
|
1458
|
+
return toolVersion ? `${BUILTIN_DISPLAY_SCOPE_PREFIX}@${toolVersion}` : BUILTIN_DISPLAY_SCOPE_PREFIX;
|
|
1459
|
+
}
|
|
1460
|
+
const i = registryName.indexOf("/");
|
|
1461
|
+
return i < 0 ? registryName : registryName.slice(0, i);
|
|
1462
|
+
}
|
|
1463
|
+
function resolveNpmToolDescriptor(descriptor) {
|
|
1464
|
+
const parsed = parseToolPath(descriptor);
|
|
1465
|
+
if (!parsed || parsed.protocol !== "npm") return null;
|
|
1466
|
+
const pv = parsePackageVersion(parsed.packageWithVersion);
|
|
1467
|
+
const pkgId = `${parsed.scope}/${pv.package}`;
|
|
1468
|
+
if (pkgId !== BUILTIN_NPM_PACKAGE) return null;
|
|
1469
|
+
if (!parsed.toolName) return null;
|
|
1470
|
+
if (parsed.toolName.includes("*")) return null;
|
|
1471
|
+
return BUILTIN_REGISTRY_PREFIX + parsed.toolName;
|
|
1472
|
+
}
|
|
1473
|
+
function expandToolDescriptorsToRegistryNames(descriptors, registryNames) {
|
|
1474
|
+
const out = [];
|
|
1475
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1476
|
+
function add(name) {
|
|
1477
|
+
if (registryNames.includes(name) && !seen.has(name)) {
|
|
1478
|
+
seen.add(name);
|
|
1479
|
+
out.push(name);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
function globMatch(pattern, name) {
|
|
1483
|
+
const re = new RegExp("^" + pattern.replace(/\*/g, "[^.]*").replace(/\./g, "\\.") + "$");
|
|
1484
|
+
return re.test(name);
|
|
1485
|
+
}
|
|
1486
|
+
for (const d of descriptors) {
|
|
1487
|
+
const s = d.trim();
|
|
1488
|
+
if (!s) continue;
|
|
1489
|
+
const parsed = parseToolPath(s);
|
|
1490
|
+
if (parsed && parsed.protocol === "npm") {
|
|
1491
|
+
const pv = parsePackageVersion(parsed.packageWithVersion);
|
|
1492
|
+
const pkgId = `${parsed.scope}/${pv.package}`;
|
|
1493
|
+
if (pkgId === BUILTIN_NPM_PACKAGE) {
|
|
1494
|
+
if (!parsed.toolName) {
|
|
1495
|
+
registryNames.filter((n) => n.startsWith(BUILTIN_REGISTRY_PREFIX)).forEach(add);
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1498
|
+
if (parsed.toolName.includes("*")) {
|
|
1499
|
+
registryNames.filter((n) => n.startsWith(BUILTIN_REGISTRY_PREFIX) && globMatch(parsed.toolName, n.slice(BUILTIN_REGISTRY_PREFIX.length))).forEach(add);
|
|
1500
|
+
continue;
|
|
1501
|
+
}
|
|
1502
|
+
add(BUILTIN_REGISTRY_PREFIX + parsed.toolName);
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
add(s);
|
|
1507
|
+
}
|
|
1508
|
+
return out;
|
|
1509
|
+
}
|
|
1510
|
+
function resolveToolDescriptor(descriptor) {
|
|
1511
|
+
const s = descriptor.trim();
|
|
1512
|
+
const resolved = resolveNpmToolDescriptor(s);
|
|
1513
|
+
if (resolved !== null) return resolved;
|
|
1514
|
+
return s;
|
|
1515
|
+
}
|
|
1516
|
+
function normalizeToolList(descriptors) {
|
|
1517
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1518
|
+
const out = [];
|
|
1519
|
+
for (const d of descriptors) {
|
|
1520
|
+
if (typeof d !== "string" || !d.trim()) continue;
|
|
1521
|
+
const name = resolveToolDescriptor(d);
|
|
1522
|
+
if (!seen.has(name)) {
|
|
1523
|
+
seen.add(name);
|
|
1524
|
+
out.push(name);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
return out;
|
|
1528
|
+
}
|
|
1529
|
+
function loadToolConfig(toolYamlPath) {
|
|
1530
|
+
const abs = resolve(toolYamlPath);
|
|
1531
|
+
const raw = readFileSync(abs, "utf8");
|
|
1532
|
+
const parsed = yaml.load(raw);
|
|
1533
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
1534
|
+
return parsed;
|
|
1535
|
+
}
|
|
1536
|
+
function resolveSandboxedPath(toolYamlPath, sandboxedPath) {
|
|
1537
|
+
const configDir = dirname(resolve(toolYamlPath));
|
|
1538
|
+
return resolve(configDir, sandboxedPath);
|
|
1539
|
+
}
|
|
1540
|
+
function findAndLoadToolConfig(dir) {
|
|
1541
|
+
const candidates = [join(dir, "tool.yaml"), join(dir, ".tool.yaml")];
|
|
1542
|
+
for (const p of candidates) {
|
|
1543
|
+
if (existsSync(p)) {
|
|
1544
|
+
const config = loadToolConfig(p);
|
|
1545
|
+
return { ...config, configPath: p };
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
return {};
|
|
1549
|
+
}
|
|
1550
|
+
function createRuntimeFromConfigSync(options = {}) {
|
|
1551
|
+
const registry = new ToolRegistry();
|
|
1552
|
+
if (options.coreTools !== void 0) {
|
|
1553
|
+
const coreAdapter = registerCoreTools(registry, options.coreTools);
|
|
1554
|
+
const runtime2 = new PTCRuntime({ registry });
|
|
1555
|
+
runtime2.registerAdapter(coreAdapter);
|
|
1556
|
+
if (options.exampleTools !== void 0) {
|
|
1557
|
+
const exampleAdapter = registerExampleTools(registry, options.exampleTools);
|
|
1558
|
+
runtime2.registerAdapter(exampleAdapter);
|
|
1559
|
+
}
|
|
1560
|
+
return { runtime: runtime2, registry };
|
|
1561
|
+
}
|
|
1562
|
+
if (options.exampleTools !== void 0) {
|
|
1563
|
+
const exampleAdapter = registerExampleTools(registry, options.exampleTools);
|
|
1564
|
+
const runtime2 = new PTCRuntime({ registry });
|
|
1565
|
+
runtime2.registerAdapter(exampleAdapter);
|
|
1566
|
+
return { runtime: runtime2, registry };
|
|
1567
|
+
}
|
|
1568
|
+
const runtime = new PTCRuntime({ registry });
|
|
1569
|
+
return { runtime, registry };
|
|
1570
|
+
}
|
|
1571
|
+
async function createRuntimeFromConfig(options = {}) {
|
|
1572
|
+
return createRuntimeFromConfigSync(options);
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
// src/tools/mcp/types.ts
|
|
1576
|
+
var MCP_KIND = "mcp";
|
|
1577
|
+
|
|
1578
|
+
// src/tools/langchain/types.ts
|
|
1579
|
+
var LANGCHAIN_KIND = "langchain";
|
|
1580
|
+
var LANGCHAIN_DIR_NAME = "langchain";
|
|
1581
|
+
|
|
1582
|
+
export { BudgetManager, EventLog, LANGCHAIN_DIR_NAME, LANGCHAIN_KIND, MCP_KIND, Metrics, PTCRuntime, PolicyDeniedError, PolicyEngine, SchemaValidationError, SchemaValidator, Tracing, buildEvidence, createLogger, createRuntimeFromConfig, createRuntimeFromConfigSync, expandToolDescriptorsToRegistryNames, findAndLoadToolConfig, getDisplayScope, isNpmToolDescriptor, loadToolConfig, normalizeToolList, parseNpmToolDescriptor, resolveNpmToolDescriptor, resolveSandboxedPath, resolveToolDescriptor, sanitizeForLog, summarizeForLog };
|
|
1583
|
+
//# sourceMappingURL=chunk-Q7KPGWC6.js.map
|
|
1584
|
+
//# sourceMappingURL=chunk-Q7KPGWC6.js.map
|