@easynet/agent-tool 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -0
- package/dist/chunk-3YLVPZRJ.cjs +32 -0
- package/dist/chunk-3YLVPZRJ.cjs.map +1 -0
- package/dist/chunk-AXUNV4MK.js +28 -0
- package/dist/chunk-AXUNV4MK.js.map +1 -0
- package/dist/chunk-BM4EVYI5.js +1069 -0
- package/dist/chunk-BM4EVYI5.js.map +1 -0
- package/dist/chunk-P3UEAZHK.cjs +171 -0
- package/dist/chunk-P3UEAZHK.cjs.map +1 -0
- package/dist/chunk-RPAMQCFH.js +167 -0
- package/dist/chunk-RPAMQCFH.js.map +1 -0
- package/dist/chunk-Z7TGIG77.cjs +1108 -0
- package/dist/chunk-Z7TGIG77.cjs.map +1 -0
- package/dist/cli.cjs +154 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +147 -0
- package/dist/cli.js.map +1 -0
- package/dist/codegen/build.d.ts +23 -0
- package/dist/codegen/build.d.ts.map +1 -0
- package/dist/codegen/generator.d.ts +15 -0
- package/dist/codegen/generator.d.ts.map +1 -0
- package/dist/codegen/index.d.ts +21 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/init.d.ts +17 -0
- package/dist/codegen/init.d.ts.map +1 -0
- package/dist/codegen/run.d.ts +19 -0
- package/dist/codegen/run.d.ts.map +1 -0
- package/dist/codegen/scan/scanN8n.d.ts +17 -0
- package/dist/codegen/scan/scanN8n.d.ts.map +1 -0
- package/dist/codegen/scan/scanSkill.d.ts +17 -0
- package/dist/codegen/scan/scanSkill.d.ts.map +1 -0
- package/dist/codegen/scan/scanTools.d.ts +31 -0
- package/dist/codegen/scan/scanTools.d.ts.map +1 -0
- package/dist/codegen/scan/scanner.d.ts +26 -0
- package/dist/codegen/scan/scanner.d.ts.map +1 -0
- package/dist/codegen/scan/schemaFromTs.d.ts +16 -0
- package/dist/codegen/scan/schemaFromTs.d.ts.map +1 -0
- package/dist/codegen/types.d.ts +81 -0
- package/dist/codegen/types.d.ts.map +1 -0
- package/dist/core.cjs +20 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.ts +8 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +3 -0
- package/dist/core.js.map +1 -0
- package/dist/discovery/MCPProcessManager.d.ts +57 -0
- package/dist/discovery/MCPProcessManager.d.ts.map +1 -0
- package/dist/discovery/errors.d.ts +13 -0
- package/dist/discovery/errors.d.ts.map +1 -0
- package/dist/discovery/load/LangChainLoader.d.ts +7 -0
- package/dist/discovery/load/LangChainLoader.d.ts.map +1 -0
- package/dist/discovery/load/MCPLoader.d.ts +8 -0
- package/dist/discovery/load/MCPLoader.d.ts.map +1 -0
- package/dist/discovery/load/N8nLoader.d.ts +25 -0
- package/dist/discovery/load/N8nLoader.d.ts.map +1 -0
- package/dist/discovery/load/SkillLoader.d.ts +20 -0
- package/dist/discovery/load/SkillLoader.d.ts.map +1 -0
- package/dist/discovery/load/SkillManifest.d.ts +79 -0
- package/dist/discovery/load/SkillManifest.d.ts.map +1 -0
- package/dist/discovery/load/SkillMdParser.d.ts +31 -0
- package/dist/discovery/load/SkillMdParser.d.ts.map +1 -0
- package/dist/discovery/load/index.d.ts +6 -0
- package/dist/discovery/load/index.d.ts.map +1 -0
- package/dist/discovery/load/resolveEntry.d.ts +7 -0
- package/dist/discovery/load/resolveEntry.d.ts.map +1 -0
- package/dist/discovery/scan/DirectoryScanner.d.ts +37 -0
- package/dist/discovery/scan/DirectoryScanner.d.ts.map +1 -0
- package/dist/discovery/scan/scanUtil.d.ts +16 -0
- package/dist/discovery/scan/scanUtil.d.ts.map +1 -0
- package/dist/discovery/types.d.ts +99 -0
- package/dist/discovery/types.d.ts.map +1 -0
- package/dist/index.cjs +3014 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2778 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/AgentLLMAdapter.d.ts +27 -0
- package/dist/llm/AgentLLMAdapter.d.ts.map +1 -0
- package/dist/llm/LangChainToolsHub.d.ts +31 -0
- package/dist/llm/LangChainToolsHub.d.ts.map +1 -0
- package/dist/llm/OpenAICompatibleClient.d.ts +64 -0
- package/dist/llm/OpenAICompatibleClient.d.ts.map +1 -0
- package/dist/llm/ReActAgent.d.ts +35 -0
- package/dist/llm/ReActAgent.d.ts.map +1 -0
- package/dist/llm-export.cjs +20 -0
- package/dist/llm-export.cjs.map +1 -0
- package/dist/llm-export.d.ts +9 -0
- package/dist/llm-export.d.ts.map +1 -0
- package/dist/llm-export.js +3 -0
- package/dist/llm-export.js.map +1 -0
- package/dist/mcp/MCPClientAdapter.d.ts +34 -0
- package/dist/mcp/MCPClientAdapter.d.ts.map +1 -0
- package/dist/mcp/connectMCP.d.ts +47 -0
- package/dist/mcp/connectMCP.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +10 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/registerMCPTools.d.ts +24 -0
- package/dist/mcp/registerMCPTools.d.ts.map +1 -0
- package/dist/observability/EventLog.d.ts +60 -0
- package/dist/observability/EventLog.d.ts.map +1 -0
- package/dist/observability/Logger.d.ts +33 -0
- package/dist/observability/Logger.d.ts.map +1 -0
- package/dist/observability/Metrics.d.ts +70 -0
- package/dist/observability/Metrics.d.ts.map +1 -0
- package/dist/observability/Tracing.d.ts +69 -0
- package/dist/observability/Tracing.d.ts.map +1 -0
- package/dist/registry/ToolRegistry.d.ts +68 -0
- package/dist/registry/ToolRegistry.d.ts.map +1 -0
- package/dist/report/AgentReportGenerator.d.ts +53 -0
- package/dist/report/AgentReportGenerator.d.ts.map +1 -0
- package/dist/report/agent-report-template.html +362 -0
- package/dist/report/index.d.ts +3 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/types.d.ts +101 -0
- package/dist/report/types.d.ts.map +1 -0
- package/dist/runAgent.d.ts +37 -0
- package/dist/runAgent.d.ts.map +1 -0
- package/dist/runtime/Budget.d.ts +63 -0
- package/dist/runtime/Budget.d.ts.map +1 -0
- package/dist/runtime/Evidence.d.ts +19 -0
- package/dist/runtime/Evidence.d.ts.map +1 -0
- package/dist/runtime/PTCRuntime.d.ts +115 -0
- package/dist/runtime/PTCRuntime.d.ts.map +1 -0
- package/dist/runtime/PTCRuntimeObservability.d.ts +26 -0
- package/dist/runtime/PTCRuntimeObservability.d.ts.map +1 -0
- package/dist/runtime/PTCRuntimePipeline.d.ts +62 -0
- package/dist/runtime/PTCRuntimePipeline.d.ts.map +1 -0
- package/dist/runtime/PolicyEngine.d.ts +67 -0
- package/dist/runtime/PolicyEngine.d.ts.map +1 -0
- package/dist/runtime/Retry.d.ts +33 -0
- package/dist/runtime/Retry.d.ts.map +1 -0
- package/dist/runtime/SchemaValidator.d.ts +42 -0
- package/dist/runtime/SchemaValidator.d.ts.map +1 -0
- package/dist/templates/mcp-server.js +48 -0
- package/dist/templates/n8n-invoker.js +11 -0
- package/dist/templates/skill-invoker.js +11 -0
- package/dist/templates/tool-index.js +9 -0
- package/dist/toolDescriptor.d.ts +38 -0
- package/dist/toolDescriptor.d.ts.map +1 -0
- package/dist/types/Events.d.ts +99 -0
- package/dist/types/Events.d.ts.map +1 -0
- package/dist/types/ToolIntent.d.ts +40 -0
- package/dist/types/ToolIntent.d.ts.map +1 -0
- package/dist/types/ToolResult.d.ts +30 -0
- package/dist/types/ToolResult.d.ts.map +1 -0
- package/dist/types/ToolSpec.d.ts +99 -0
- package/dist/types/ToolSpec.d.ts.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/extensions/examples/README.md +40 -0
- package/extensions/examples/scripts/agent-tool-react-stock.mjs +30 -0
- package/extensions/examples/tools/instruction-only/skill/SKILL.md +26 -0
- package/extensions/examples/tools/web-search/mcp/mcp.json +8 -0
- package/package.json +122 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3014 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkZ7TGIG77_cjs = require('./chunk-Z7TGIG77.cjs');
|
|
4
|
+
var chunk3YLVPZRJ_cjs = require('./chunk-3YLVPZRJ.cjs');
|
|
5
|
+
var chunkP3UEAZHK_cjs = require('./chunk-P3UEAZHK.cjs');
|
|
6
|
+
var Ajv = require('ajv');
|
|
7
|
+
var addFormats = require('ajv-formats');
|
|
8
|
+
var cockatiel = require('cockatiel');
|
|
9
|
+
var eventemitter3 = require('eventemitter3');
|
|
10
|
+
var uuid = require('uuid');
|
|
11
|
+
var pRetry = require('p-retry');
|
|
12
|
+
var pTimeout = require('p-timeout');
|
|
13
|
+
var agentToolBuiltinTools = require('@easynet/agent-tool-builtin-tools');
|
|
14
|
+
var agentToolExampleTools = require('@easynet/agent-tool-example-tools');
|
|
15
|
+
var promises = require('fs/promises');
|
|
16
|
+
var path = require('path');
|
|
17
|
+
var url = require('url');
|
|
18
|
+
var tools = require('@langchain/core/tools');
|
|
19
|
+
var fs = require('fs');
|
|
20
|
+
var yaml = require('js-yaml');
|
|
21
|
+
var agentLlm = require('@easynet/agent-llm');
|
|
22
|
+
|
|
23
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
24
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
25
|
+
|
|
26
|
+
var Ajv__default = /*#__PURE__*/_interopDefault(Ajv);
|
|
27
|
+
var addFormats__default = /*#__PURE__*/_interopDefault(addFormats);
|
|
28
|
+
var pRetry__default = /*#__PURE__*/_interopDefault(pRetry);
|
|
29
|
+
var pTimeout__default = /*#__PURE__*/_interopDefault(pTimeout);
|
|
30
|
+
var yaml__default = /*#__PURE__*/_interopDefault(yaml);
|
|
31
|
+
|
|
32
|
+
// src/registry/ToolRegistry.ts
|
|
33
|
+
var ToolRegistry = class {
|
|
34
|
+
tools = /* @__PURE__ */ new Map();
|
|
35
|
+
tagIndex = /* @__PURE__ */ new Map();
|
|
36
|
+
// tag → tool names
|
|
37
|
+
kindIndex = /* @__PURE__ */ new Map();
|
|
38
|
+
// kind → tool names
|
|
39
|
+
/**
|
|
40
|
+
* Register a single tool spec.
|
|
41
|
+
* Overwrites if same name already exists.
|
|
42
|
+
*/
|
|
43
|
+
register(spec) {
|
|
44
|
+
this.validateSpec(spec);
|
|
45
|
+
this.tools.set(spec.name, spec);
|
|
46
|
+
this.indexTool(spec);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Register multiple tool specs at once.
|
|
50
|
+
*/
|
|
51
|
+
bulkRegister(specs) {
|
|
52
|
+
for (const spec of specs) {
|
|
53
|
+
this.register(spec);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Unregister a tool by name.
|
|
58
|
+
*/
|
|
59
|
+
unregister(name) {
|
|
60
|
+
const spec = this.tools.get(name);
|
|
61
|
+
if (!spec) return false;
|
|
62
|
+
this.tools.delete(name);
|
|
63
|
+
this.deindexTool(spec);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get a tool spec by name.
|
|
68
|
+
*/
|
|
69
|
+
get(name) {
|
|
70
|
+
return this.tools.get(name);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Check if a tool exists.
|
|
74
|
+
*/
|
|
75
|
+
has(name) {
|
|
76
|
+
return this.tools.has(name);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Search tools by query.
|
|
80
|
+
*/
|
|
81
|
+
search(query) {
|
|
82
|
+
let candidates;
|
|
83
|
+
if (query.kind) {
|
|
84
|
+
const names = this.kindIndex.get(query.kind);
|
|
85
|
+
if (!names || names.size === 0) return [];
|
|
86
|
+
candidates = [...names].map((n) => this.tools.get(n)).filter((s) => s !== void 0);
|
|
87
|
+
} else {
|
|
88
|
+
candidates = [...this.tools.values()];
|
|
89
|
+
}
|
|
90
|
+
if (query.tags && query.tags.length > 0) {
|
|
91
|
+
candidates = candidates.filter(
|
|
92
|
+
(spec) => query.tags.some((tag) => spec.tags?.includes(tag))
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
if (query.capabilities && query.capabilities.length > 0) {
|
|
96
|
+
candidates = candidates.filter(
|
|
97
|
+
(spec) => query.capabilities.every((cap) => spec.capabilities.includes(cap))
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (query.text) {
|
|
101
|
+
const lower = query.text.toLowerCase();
|
|
102
|
+
candidates = candidates.filter(
|
|
103
|
+
(spec) => spec.name.toLowerCase().includes(lower) || spec.description?.toLowerCase().includes(lower) || spec.tags?.some((t) => t.toLowerCase().includes(lower))
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return candidates;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* List all registered tool names.
|
|
110
|
+
*/
|
|
111
|
+
list() {
|
|
112
|
+
return [...this.tools.keys()];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get count of registered tools.
|
|
116
|
+
*/
|
|
117
|
+
get size() {
|
|
118
|
+
return this.tools.size;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Export a snapshot of all registered tools (for debugging/routing).
|
|
122
|
+
*/
|
|
123
|
+
snapshot() {
|
|
124
|
+
return [...this.tools.values()];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Clear all registered tools.
|
|
128
|
+
*/
|
|
129
|
+
clear() {
|
|
130
|
+
this.tools.clear();
|
|
131
|
+
this.tagIndex.clear();
|
|
132
|
+
this.kindIndex.clear();
|
|
133
|
+
}
|
|
134
|
+
validateSpec(spec) {
|
|
135
|
+
if (!spec.name) throw new Error("ToolSpec.name is required");
|
|
136
|
+
if (!spec.version) throw new Error("ToolSpec.version is required");
|
|
137
|
+
if (!spec.kind) throw new Error("ToolSpec.kind is required");
|
|
138
|
+
if (!spec.inputSchema) throw new Error("ToolSpec.inputSchema is required");
|
|
139
|
+
if (!spec.outputSchema) throw new Error("ToolSpec.outputSchema is required");
|
|
140
|
+
if (!spec.capabilities) throw new Error("ToolSpec.capabilities is required");
|
|
141
|
+
}
|
|
142
|
+
indexTool(spec) {
|
|
143
|
+
let kindSet = this.kindIndex.get(spec.kind);
|
|
144
|
+
if (!kindSet) {
|
|
145
|
+
kindSet = /* @__PURE__ */ new Set();
|
|
146
|
+
this.kindIndex.set(spec.kind, kindSet);
|
|
147
|
+
}
|
|
148
|
+
kindSet.add(spec.name);
|
|
149
|
+
if (spec.tags) {
|
|
150
|
+
for (const tag of spec.tags) {
|
|
151
|
+
let tagSet = this.tagIndex.get(tag);
|
|
152
|
+
if (!tagSet) {
|
|
153
|
+
tagSet = /* @__PURE__ */ new Set();
|
|
154
|
+
this.tagIndex.set(tag, tagSet);
|
|
155
|
+
}
|
|
156
|
+
tagSet.add(spec.name);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
deindexTool(spec) {
|
|
161
|
+
this.kindIndex.get(spec.kind)?.delete(spec.name);
|
|
162
|
+
if (spec.tags) {
|
|
163
|
+
for (const tag of spec.tags) {
|
|
164
|
+
this.tagIndex.get(tag)?.delete(spec.name);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
var SchemaValidator = class {
|
|
170
|
+
ajv;
|
|
171
|
+
cache = /* @__PURE__ */ new Map();
|
|
172
|
+
constructor() {
|
|
173
|
+
this.ajv = new Ajv__default.default({
|
|
174
|
+
allErrors: true,
|
|
175
|
+
coerceTypes: true,
|
|
176
|
+
useDefaults: true,
|
|
177
|
+
removeAdditional: "failing",
|
|
178
|
+
strict: false
|
|
179
|
+
});
|
|
180
|
+
addFormats__default.default(this.ajv);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Validate data against a JSON Schema.
|
|
184
|
+
* Coerces types and applies defaults in-place.
|
|
185
|
+
*/
|
|
186
|
+
validate(schema, data) {
|
|
187
|
+
const validate = this.getOrCompile(schema);
|
|
188
|
+
const cloned = structuredClone(data);
|
|
189
|
+
const valid = validate(cloned);
|
|
190
|
+
if (valid) {
|
|
191
|
+
return { valid: true, data: cloned };
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
valid: false,
|
|
195
|
+
errors: validate.errors ?? void 0
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Validate and return coerced data, or throw a descriptive error.
|
|
200
|
+
*/
|
|
201
|
+
validateOrThrow(schema, data, context) {
|
|
202
|
+
const result = this.validate(schema, data);
|
|
203
|
+
if (!result.valid) {
|
|
204
|
+
const messages = (result.errors ?? []).map((e) => `${e.instancePath || "/"} ${e.message}`).join("; ");
|
|
205
|
+
throw new SchemaValidationError(
|
|
206
|
+
`${context}: ${messages}`,
|
|
207
|
+
result.errors ?? []
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return result.data;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Apply default values from schema to data without full validation.
|
|
214
|
+
*/
|
|
215
|
+
enrichDefaults(schema, data) {
|
|
216
|
+
const validate = this.getOrCompile(schema);
|
|
217
|
+
const cloned = structuredClone(data);
|
|
218
|
+
validate(cloned);
|
|
219
|
+
return cloned;
|
|
220
|
+
}
|
|
221
|
+
getOrCompile(schema) {
|
|
222
|
+
const normalized = this.normalizeSchema(schema);
|
|
223
|
+
const key = JSON.stringify(normalized);
|
|
224
|
+
let cached = this.cache.get(key);
|
|
225
|
+
if (!cached) {
|
|
226
|
+
cached = this.ajv.compile(normalized);
|
|
227
|
+
this.cache.set(key, cached);
|
|
228
|
+
}
|
|
229
|
+
return cached;
|
|
230
|
+
}
|
|
231
|
+
/** Ensure schema is AJV-compatible (required = string[], nullable handled via type). */
|
|
232
|
+
normalizeSchema(schema) {
|
|
233
|
+
return this.normalizeSchemaRec(schema);
|
|
234
|
+
}
|
|
235
|
+
normalizeSchemaRec(s) {
|
|
236
|
+
const out = {};
|
|
237
|
+
for (const [key, value] of Object.entries(s)) {
|
|
238
|
+
if (key === "required") {
|
|
239
|
+
const raw = value;
|
|
240
|
+
out.required = Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : typeof raw === "string" ? [raw] : [];
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (key === "nullable") {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (key === "properties" && value !== null && typeof value === "object") {
|
|
247
|
+
const props = {};
|
|
248
|
+
for (const [pk, pv] of Object.entries(value)) {
|
|
249
|
+
if (pv !== null && typeof pv === "object" && !Array.isArray(pv)) {
|
|
250
|
+
props[pk] = this.normalizeSchemaRec(pv);
|
|
251
|
+
} else {
|
|
252
|
+
props[pk] = pv;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
out.properties = props;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if ((key === "items" || key === "additionalProperties") && value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
259
|
+
out[key] = this.normalizeSchemaRec(value);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if ((key === "oneOf" || key === "anyOf" || key === "allOf") && Array.isArray(value)) {
|
|
263
|
+
out[key] = value.map(
|
|
264
|
+
(item) => item !== null && typeof item === "object" && !Array.isArray(item) ? this.normalizeSchemaRec(item) : item
|
|
265
|
+
);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
out[key] = value;
|
|
269
|
+
}
|
|
270
|
+
if (s.nullable === true) {
|
|
271
|
+
const existingType = out.type;
|
|
272
|
+
if (existingType === void 0) {
|
|
273
|
+
out.type = "object";
|
|
274
|
+
} else if (Array.isArray(existingType)) {
|
|
275
|
+
if (!existingType.includes("null")) out.type = [...existingType, "null"];
|
|
276
|
+
} else {
|
|
277
|
+
out.type = [existingType, "null"];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
var SchemaValidationError = class extends Error {
|
|
284
|
+
constructor(message, errors) {
|
|
285
|
+
super(message);
|
|
286
|
+
this.errors = errors;
|
|
287
|
+
this.name = "SchemaValidationError";
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
// src/runtime/PolicyEngine.ts
|
|
292
|
+
var PolicyEngine = class {
|
|
293
|
+
config;
|
|
294
|
+
constructor(config = {}) {
|
|
295
|
+
this.config = {
|
|
296
|
+
requireExplicitDangerPermission: true,
|
|
297
|
+
deniedSqlPatterns: ["DROP\\s", "TRUNCATE\\s", "DELETE\\s+FROM\\s+\\w+\\s*$"],
|
|
298
|
+
...config
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Enforce all policy checks. Throws PolicyDeniedError if denied.
|
|
303
|
+
*/
|
|
304
|
+
enforce(spec, args, ctx) {
|
|
305
|
+
const result = this.check(spec, args, ctx);
|
|
306
|
+
if (!result.allowed) {
|
|
307
|
+
throw new PolicyDeniedError(
|
|
308
|
+
result.reason ?? "Policy denied",
|
|
309
|
+
result.missingCapabilities
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Check all policies without throwing.
|
|
315
|
+
*/
|
|
316
|
+
check(spec, args, ctx) {
|
|
317
|
+
const capResult = this.checkCapabilities(spec, ctx);
|
|
318
|
+
if (!capResult.allowed) return capResult;
|
|
319
|
+
const paramResult = this.checkParameters(spec, args);
|
|
320
|
+
if (!paramResult.allowed) return paramResult;
|
|
321
|
+
return { allowed: true };
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Check that context permissions cover tool capabilities.
|
|
325
|
+
*/
|
|
326
|
+
checkCapabilities(spec, ctx) {
|
|
327
|
+
const missing = [];
|
|
328
|
+
for (const cap of spec.capabilities) {
|
|
329
|
+
if (cap === "danger:destructive") {
|
|
330
|
+
if (this.config.requireExplicitDangerPermission && !ctx.permissions.includes("danger:destructive")) {
|
|
331
|
+
missing.push(cap);
|
|
332
|
+
}
|
|
333
|
+
} else if (!ctx.permissions.includes(cap)) {
|
|
334
|
+
missing.push(cap);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (missing.length > 0) {
|
|
338
|
+
return {
|
|
339
|
+
allowed: false,
|
|
340
|
+
reason: `Missing capabilities: ${missing.join(", ")}`,
|
|
341
|
+
missingCapabilities: missing
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
return { allowed: true };
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Check parameter-level security constraints.
|
|
348
|
+
*/
|
|
349
|
+
checkParameters(spec, args) {
|
|
350
|
+
if (!args || typeof args !== "object") return { allowed: true };
|
|
351
|
+
const argsObj = args;
|
|
352
|
+
if (spec.capabilities.includes("write:fs") && this.config.sandboxPaths) {
|
|
353
|
+
const pathResult = this.checkFilePaths(argsObj);
|
|
354
|
+
if (!pathResult.allowed) return pathResult;
|
|
355
|
+
}
|
|
356
|
+
if (spec.capabilities.includes("network") || spec.capabilities.includes("read:web")) {
|
|
357
|
+
const urlResult = this.checkUrls(argsObj);
|
|
358
|
+
if (!urlResult.allowed) return urlResult;
|
|
359
|
+
}
|
|
360
|
+
if (spec.capabilities.includes("write:db") || spec.capabilities.includes("read:db")) {
|
|
361
|
+
const sqlResult = this.checkSql(argsObj);
|
|
362
|
+
if (!sqlResult.allowed) return sqlResult;
|
|
363
|
+
}
|
|
364
|
+
if (spec.capabilities.includes("network") && this.config.allowedDomains) {
|
|
365
|
+
const domainResult = this.checkDomains(argsObj);
|
|
366
|
+
if (!domainResult.allowed) return domainResult;
|
|
367
|
+
}
|
|
368
|
+
return { allowed: true };
|
|
369
|
+
}
|
|
370
|
+
checkFilePaths(args) {
|
|
371
|
+
const paths = this.extractStringValues(args, ["path", "file", "filepath", "filename", "dir", "directory"]);
|
|
372
|
+
for (const p of paths) {
|
|
373
|
+
const normalized = p.replace(/\.\./g, "");
|
|
374
|
+
if (p.includes("..")) {
|
|
375
|
+
return {
|
|
376
|
+
allowed: false,
|
|
377
|
+
reason: `Path traversal detected: ${p}`
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
if (this.config.sandboxPaths && this.config.sandboxPaths.length > 0) {
|
|
381
|
+
const inSandbox = this.config.sandboxPaths.some(
|
|
382
|
+
(sp) => normalized.startsWith(sp)
|
|
383
|
+
);
|
|
384
|
+
if (!inSandbox) {
|
|
385
|
+
return {
|
|
386
|
+
allowed: false,
|
|
387
|
+
reason: `Path outside sandbox: ${p}. Allowed: ${this.config.sandboxPaths.join(", ")}`
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return { allowed: true };
|
|
393
|
+
}
|
|
394
|
+
checkUrls(args) {
|
|
395
|
+
const urls = this.extractStringValues(args, ["url", "endpoint", "href", "uri"]);
|
|
396
|
+
for (const url of urls) {
|
|
397
|
+
if (this.config.urlDenylist) {
|
|
398
|
+
for (const pattern of this.config.urlDenylist) {
|
|
399
|
+
if (new RegExp(pattern, "i").test(url)) {
|
|
400
|
+
return {
|
|
401
|
+
allowed: false,
|
|
402
|
+
reason: `URL denied by policy: ${url}`
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (this.config.urlAllowlist && this.config.urlAllowlist.length > 0) {
|
|
408
|
+
const allowed = this.config.urlAllowlist.some(
|
|
409
|
+
(pattern) => new RegExp(pattern, "i").test(url)
|
|
410
|
+
);
|
|
411
|
+
if (!allowed) {
|
|
412
|
+
return {
|
|
413
|
+
allowed: false,
|
|
414
|
+
reason: `URL not in allowlist: ${url}`
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return { allowed: true };
|
|
420
|
+
}
|
|
421
|
+
checkSql(args) {
|
|
422
|
+
const sqls = this.extractStringValues(args, ["sql", "query", "statement"]);
|
|
423
|
+
for (const sql of sqls) {
|
|
424
|
+
if (this.config.deniedSqlPatterns) {
|
|
425
|
+
for (const pattern of this.config.deniedSqlPatterns) {
|
|
426
|
+
if (new RegExp(pattern, "i").test(sql)) {
|
|
427
|
+
return {
|
|
428
|
+
allowed: false,
|
|
429
|
+
reason: `SQL pattern denied: ${sql.slice(0, 50)}...`
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return { allowed: true };
|
|
436
|
+
}
|
|
437
|
+
checkDomains(args) {
|
|
438
|
+
const urls = this.extractStringValues(args, ["url", "endpoint", "href", "host", "domain"]);
|
|
439
|
+
for (const url of urls) {
|
|
440
|
+
try {
|
|
441
|
+
const hostname = url.includes("://") ? new URL(url).hostname : url;
|
|
442
|
+
if (this.config.allowedDomains && !this.config.allowedDomains.some(
|
|
443
|
+
(d) => hostname === d || hostname.endsWith(`.${d}`)
|
|
444
|
+
)) {
|
|
445
|
+
return {
|
|
446
|
+
allowed: false,
|
|
447
|
+
reason: `Domain not allowed: ${hostname}`
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
} catch {
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return { allowed: true };
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Extract string values from args matching given key patterns.
|
|
457
|
+
*/
|
|
458
|
+
extractStringValues(args, keyPatterns) {
|
|
459
|
+
const results = [];
|
|
460
|
+
const walk = (obj) => {
|
|
461
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
462
|
+
const lowerKey = key.toLowerCase();
|
|
463
|
+
if (typeof val === "string" && keyPatterns.some((p) => lowerKey.includes(p))) {
|
|
464
|
+
results.push(val);
|
|
465
|
+
} else if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
466
|
+
walk(val);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
walk(args);
|
|
471
|
+
return results;
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
var PolicyDeniedError = class extends Error {
|
|
475
|
+
constructor(message, missingCapabilities) {
|
|
476
|
+
super(message);
|
|
477
|
+
this.missingCapabilities = missingCapabilities;
|
|
478
|
+
this.name = "PolicyDeniedError";
|
|
479
|
+
}
|
|
480
|
+
kind = "POLICY_DENIED";
|
|
481
|
+
};
|
|
482
|
+
var RateLimiter = class {
|
|
483
|
+
constructor(maxCalls, windowMs) {
|
|
484
|
+
this.maxCalls = maxCalls;
|
|
485
|
+
this.windowMs = windowMs;
|
|
486
|
+
}
|
|
487
|
+
timestamps = [];
|
|
488
|
+
tryAcquire() {
|
|
489
|
+
const now = Date.now();
|
|
490
|
+
while (this.timestamps.length > 0 && this.timestamps[0] <= now - this.windowMs) {
|
|
491
|
+
this.timestamps.shift();
|
|
492
|
+
}
|
|
493
|
+
if (this.timestamps.length >= this.maxCalls) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
this.timestamps.push(now);
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
get remaining() {
|
|
500
|
+
const now = Date.now();
|
|
501
|
+
const active = this.timestamps.filter((t) => t > now - this.windowMs);
|
|
502
|
+
return Math.max(0, this.maxCalls - active.length);
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
var BudgetManager = class {
|
|
506
|
+
defaultTimeoutMs;
|
|
507
|
+
bulkheads = /* @__PURE__ */ new Map();
|
|
508
|
+
circuitBreakers = /* @__PURE__ */ new Map();
|
|
509
|
+
rateLimiters = /* @__PURE__ */ new Map();
|
|
510
|
+
options;
|
|
511
|
+
constructor(options = {}) {
|
|
512
|
+
this.options = options;
|
|
513
|
+
this.defaultTimeoutMs = options.defaultTimeoutMs ?? 3e4;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Get effective timeout for a tool invocation.
|
|
517
|
+
*/
|
|
518
|
+
getTimeout(_toolName, contextTimeoutMs) {
|
|
519
|
+
return contextTimeoutMs ?? this.defaultTimeoutMs;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Check rate limit for a tool. Returns true if allowed.
|
|
523
|
+
*/
|
|
524
|
+
checkRateLimit(toolName) {
|
|
525
|
+
if (!this.options.rateLimit) return true;
|
|
526
|
+
let limiter = this.rateLimiters.get(toolName);
|
|
527
|
+
if (!limiter) {
|
|
528
|
+
limiter = new RateLimiter(
|
|
529
|
+
this.options.rateLimit.maxCalls,
|
|
530
|
+
this.options.rateLimit.windowMs
|
|
531
|
+
);
|
|
532
|
+
this.rateLimiters.set(toolName, limiter);
|
|
533
|
+
}
|
|
534
|
+
return limiter.tryAcquire();
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Get or create a bulkhead (concurrency limiter) for a tool.
|
|
538
|
+
*/
|
|
539
|
+
getBulkhead(toolName) {
|
|
540
|
+
if (!this.options.maxConcurrency) return void 0;
|
|
541
|
+
let bh = this.bulkheads.get(toolName);
|
|
542
|
+
if (!bh) {
|
|
543
|
+
bh = cockatiel.bulkhead(this.options.maxConcurrency, 0);
|
|
544
|
+
this.bulkheads.set(toolName, bh);
|
|
545
|
+
}
|
|
546
|
+
return bh;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Get or create a circuit breaker for a tool.
|
|
550
|
+
*/
|
|
551
|
+
getCircuitBreaker(toolName) {
|
|
552
|
+
if (!this.options.circuitBreaker) return void 0;
|
|
553
|
+
let breaker = this.circuitBreakers.get(toolName);
|
|
554
|
+
if (!breaker) {
|
|
555
|
+
breaker = cockatiel.circuitBreaker(cockatiel.handleAll, {
|
|
556
|
+
breaker: new cockatiel.ConsecutiveBreaker(this.options.circuitBreaker.threshold),
|
|
557
|
+
halfOpenAfter: this.options.circuitBreaker.halfOpenAfterMs
|
|
558
|
+
});
|
|
559
|
+
this.circuitBreakers.set(toolName, breaker);
|
|
560
|
+
}
|
|
561
|
+
return breaker;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Execute a function within budget constraints (bulkhead + circuit breaker).
|
|
565
|
+
*/
|
|
566
|
+
async execute(toolName, fn) {
|
|
567
|
+
const bh = this.getBulkhead(toolName);
|
|
568
|
+
const breaker = this.getCircuitBreaker(toolName);
|
|
569
|
+
let wrapped = fn;
|
|
570
|
+
if (breaker) {
|
|
571
|
+
const prevWrapped = wrapped;
|
|
572
|
+
wrapped = () => breaker.execute(() => prevWrapped());
|
|
573
|
+
}
|
|
574
|
+
if (bh) {
|
|
575
|
+
const prevWrapped = wrapped;
|
|
576
|
+
wrapped = () => bh.execute(() => prevWrapped());
|
|
577
|
+
}
|
|
578
|
+
return wrapped();
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Reset all policies for a tool (useful for testing).
|
|
582
|
+
*/
|
|
583
|
+
reset(toolName) {
|
|
584
|
+
this.bulkheads.delete(toolName);
|
|
585
|
+
this.circuitBreakers.delete(toolName);
|
|
586
|
+
this.rateLimiters.delete(toolName);
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Reset all policies globally.
|
|
590
|
+
*/
|
|
591
|
+
resetAll() {
|
|
592
|
+
this.bulkheads.clear();
|
|
593
|
+
this.circuitBreakers.clear();
|
|
594
|
+
this.rateLimiters.clear();
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
// src/runtime/Evidence.ts
|
|
599
|
+
function buildEvidence(options) {
|
|
600
|
+
const { spec, args, result, ctx, durationMs } = options;
|
|
601
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
602
|
+
const evidence = [];
|
|
603
|
+
evidence.push({
|
|
604
|
+
type: "tool",
|
|
605
|
+
ref: `${spec.name}@${spec.version}`,
|
|
606
|
+
summary: summarizeToolCall(spec, args, result, durationMs),
|
|
607
|
+
createdAt: now
|
|
608
|
+
});
|
|
609
|
+
if (result && typeof result === "object") {
|
|
610
|
+
const urls = extractUrls(result);
|
|
611
|
+
for (const url of urls) {
|
|
612
|
+
evidence.push({
|
|
613
|
+
type: "url",
|
|
614
|
+
ref: url,
|
|
615
|
+
summary: `Output URL from ${spec.name}`,
|
|
616
|
+
createdAt: now
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
const files = extractFilePaths(result);
|
|
620
|
+
for (const file of files) {
|
|
621
|
+
evidence.push({
|
|
622
|
+
type: "file",
|
|
623
|
+
ref: file,
|
|
624
|
+
summary: `Output file from ${spec.name}`,
|
|
625
|
+
createdAt: now
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (durationMs !== void 0 && durationMs > 0) {
|
|
630
|
+
evidence.push({
|
|
631
|
+
type: "metric",
|
|
632
|
+
ref: `latency:${spec.name}`,
|
|
633
|
+
summary: `Completed in ${durationMs}ms (request: ${ctx.requestId})`,
|
|
634
|
+
createdAt: now
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
return evidence;
|
|
638
|
+
}
|
|
639
|
+
function summarizeToolCall(spec, args, result, durationMs) {
|
|
640
|
+
const argKeys = args && typeof args === "object" ? Object.keys(args).join(", ") : "none";
|
|
641
|
+
const duration = durationMs ? ` in ${durationMs}ms` : "";
|
|
642
|
+
const resultPreview = summarizeValue(result, 100);
|
|
643
|
+
return `${spec.kind}:${spec.name} called with [${argKeys}]${duration} \u2192 ${resultPreview}`;
|
|
644
|
+
}
|
|
645
|
+
function summarizeValue(value, maxLen) {
|
|
646
|
+
if (value === null || value === void 0) return "null";
|
|
647
|
+
if (typeof value === "string") {
|
|
648
|
+
return value.length > maxLen ? value.slice(0, maxLen) + "..." : value;
|
|
649
|
+
}
|
|
650
|
+
const str = JSON.stringify(value);
|
|
651
|
+
return str.length > maxLen ? str.slice(0, maxLen) + "..." : str;
|
|
652
|
+
}
|
|
653
|
+
function extractUrls(obj) {
|
|
654
|
+
const urls = [];
|
|
655
|
+
const walk = (val) => {
|
|
656
|
+
if (typeof val === "string" && /^https?:\/\//i.test(val)) {
|
|
657
|
+
urls.push(val);
|
|
658
|
+
} else if (val && typeof val === "object") {
|
|
659
|
+
for (const v of Object.values(val)) {
|
|
660
|
+
walk(v);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
walk(obj);
|
|
665
|
+
return urls.slice(0, 10);
|
|
666
|
+
}
|
|
667
|
+
function extractFilePaths(obj) {
|
|
668
|
+
const paths = [];
|
|
669
|
+
const walk = (val) => {
|
|
670
|
+
if (typeof val === "string" && (val.startsWith("/") || val.startsWith("./")) && val.includes(".")) {
|
|
671
|
+
paths.push(val);
|
|
672
|
+
} else if (val && typeof val === "object") {
|
|
673
|
+
for (const v of Object.values(val)) {
|
|
674
|
+
walk(v);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
walk(obj);
|
|
679
|
+
return paths.slice(0, 10);
|
|
680
|
+
}
|
|
681
|
+
var EventLog = class {
|
|
682
|
+
entries = [];
|
|
683
|
+
seq = 0;
|
|
684
|
+
maxEntries;
|
|
685
|
+
emitter = new eventemitter3.EventEmitter();
|
|
686
|
+
constructor(options = {}) {
|
|
687
|
+
this.maxEntries = options.maxEntries ?? 1e4;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Append an event to the log.
|
|
691
|
+
*/
|
|
692
|
+
append(event) {
|
|
693
|
+
const entry = { seq: ++this.seq, event };
|
|
694
|
+
this.entries.push(entry);
|
|
695
|
+
if (this.entries.length > this.maxEntries) {
|
|
696
|
+
this.entries.shift();
|
|
697
|
+
}
|
|
698
|
+
this.emitter.emit("event", entry);
|
|
699
|
+
this.emitter.emit(event.type, entry);
|
|
700
|
+
return entry;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Subscribe to all events.
|
|
704
|
+
*/
|
|
705
|
+
on(listener) {
|
|
706
|
+
this.emitter.on("event", listener);
|
|
707
|
+
return () => this.emitter.off("event", listener);
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Subscribe to events of a specific type.
|
|
711
|
+
*/
|
|
712
|
+
onType(type, listener) {
|
|
713
|
+
this.emitter.on(type, listener);
|
|
714
|
+
return () => this.emitter.off(type, listener);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Query events by filter.
|
|
718
|
+
*/
|
|
719
|
+
query(filter) {
|
|
720
|
+
let results = this.entries;
|
|
721
|
+
if (filter.since !== void 0) {
|
|
722
|
+
results = results.filter((e) => e.seq > filter.since);
|
|
723
|
+
}
|
|
724
|
+
if (filter.type) {
|
|
725
|
+
results = results.filter((e) => e.event.type === filter.type);
|
|
726
|
+
}
|
|
727
|
+
if (filter.toolName) {
|
|
728
|
+
results = results.filter((e) => e.event.toolName === filter.toolName);
|
|
729
|
+
}
|
|
730
|
+
if (filter.requestId) {
|
|
731
|
+
results = results.filter((e) => e.event.requestId === filter.requestId);
|
|
732
|
+
}
|
|
733
|
+
if (filter.limit) {
|
|
734
|
+
results = results.slice(-filter.limit);
|
|
735
|
+
}
|
|
736
|
+
return results;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Get all entries.
|
|
740
|
+
*/
|
|
741
|
+
getAll() {
|
|
742
|
+
return this.entries;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Get entry count.
|
|
746
|
+
*/
|
|
747
|
+
get size() {
|
|
748
|
+
return this.entries.length;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Clear all entries (for testing).
|
|
752
|
+
*/
|
|
753
|
+
clear() {
|
|
754
|
+
this.entries.length = 0;
|
|
755
|
+
this.seq = 0;
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
// src/observability/Logger.ts
|
|
760
|
+
var LEVEL_ORDER = {
|
|
761
|
+
silent: 0,
|
|
762
|
+
error: 1,
|
|
763
|
+
warn: 2,
|
|
764
|
+
info: 3,
|
|
765
|
+
debug: 4,
|
|
766
|
+
trace: 5
|
|
767
|
+
};
|
|
768
|
+
function createLogger(options = {}) {
|
|
769
|
+
const resolved = resolveDebugOptions(options);
|
|
770
|
+
const log = (level, message, meta) => {
|
|
771
|
+
if (!resolved.enabled) return;
|
|
772
|
+
if (LEVEL_ORDER[level] > LEVEL_ORDER[resolved.level]) return;
|
|
773
|
+
const prefix = `[${resolved.prefix}]`;
|
|
774
|
+
const levelTag = `[${level.toUpperCase()}]`;
|
|
775
|
+
const metaText = meta ? ` ${safeStringify(meta, 1e3)}` : "";
|
|
776
|
+
switch (level) {
|
|
777
|
+
case "error":
|
|
778
|
+
console.error(`${prefix} ${levelTag} ${message}${metaText}`);
|
|
779
|
+
break;
|
|
780
|
+
case "warn":
|
|
781
|
+
console.warn(`${prefix} ${levelTag} ${message}${metaText}`);
|
|
782
|
+
break;
|
|
783
|
+
case "info":
|
|
784
|
+
console.info(`${prefix} ${levelTag} ${message}${metaText}`);
|
|
785
|
+
break;
|
|
786
|
+
default:
|
|
787
|
+
console.log(`${prefix} ${levelTag} ${message}${metaText}`);
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
return {
|
|
792
|
+
options: resolved,
|
|
793
|
+
isEnabled: (level) => resolved.enabled && LEVEL_ORDER[level] <= LEVEL_ORDER[resolved.level],
|
|
794
|
+
error: (message, meta) => log("error", message, meta),
|
|
795
|
+
warn: (message, meta) => log("warn", message, meta),
|
|
796
|
+
info: (message, meta) => log("info", message, meta),
|
|
797
|
+
debug: (message, meta) => log("debug", message, meta),
|
|
798
|
+
trace: (message, meta) => log("trace", message, meta)
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
function resolveDebugOptions(options = {}) {
|
|
802
|
+
const envLevel = parseEnvLogLevel();
|
|
803
|
+
const enabledFromEnv = envLevel !== void 0 && envLevel !== "silent";
|
|
804
|
+
const enabled = options.enabled ?? enabledFromEnv ?? false;
|
|
805
|
+
const level = options.level ?? envLevel ?? (enabled ? "debug" : "silent");
|
|
806
|
+
return {
|
|
807
|
+
enabled,
|
|
808
|
+
level,
|
|
809
|
+
includeArgs: options.includeArgs ?? false,
|
|
810
|
+
includeResults: options.includeResults ?? false,
|
|
811
|
+
includeRaw: options.includeRaw ?? false,
|
|
812
|
+
logEvents: options.logEvents ?? false,
|
|
813
|
+
prefix: options.prefix ?? "agent-tool"
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
function sanitizeForLog(value, maxLen = 500) {
|
|
817
|
+
const str = safeStringify(value, maxLen);
|
|
818
|
+
return str.replace(
|
|
819
|
+
/"(password|token|secret|key|auth)":\s*"[^"]*"/gi,
|
|
820
|
+
'"$1":"[REDACTED]"'
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
function summarizeForLog(value, maxLen = 200) {
|
|
824
|
+
if (value === null) return "null";
|
|
825
|
+
if (value === void 0) return "undefined";
|
|
826
|
+
if (typeof value === "string") {
|
|
827
|
+
return value.length > maxLen ? `${value.slice(0, maxLen)}...` : value;
|
|
828
|
+
}
|
|
829
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
830
|
+
return String(value);
|
|
831
|
+
}
|
|
832
|
+
if (Array.isArray(value)) {
|
|
833
|
+
return `Array(${value.length})`;
|
|
834
|
+
}
|
|
835
|
+
if (typeof value === "object") {
|
|
836
|
+
const keys = Object.keys(value);
|
|
837
|
+
const shown = keys.slice(0, 5).join(", ");
|
|
838
|
+
return `Object(keys: ${shown}${keys.length > 5 ? ", ..." : ""})`;
|
|
839
|
+
}
|
|
840
|
+
return String(value);
|
|
841
|
+
}
|
|
842
|
+
function safeStringify(value, maxLen) {
|
|
843
|
+
try {
|
|
844
|
+
const json = JSON.stringify(value);
|
|
845
|
+
if (!json) return String(value);
|
|
846
|
+
return json.length > maxLen ? `${json.slice(0, maxLen)}...` : json;
|
|
847
|
+
} catch {
|
|
848
|
+
const fallback = String(value);
|
|
849
|
+
return fallback.length > maxLen ? `${fallback.slice(0, maxLen)}...` : fallback;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
function parseEnvLogLevel() {
|
|
853
|
+
const raw = process.env.TOOLHUB_LOG_LEVEL ?? process.env.TOOLHUB_DEBUG ?? process.env.DEBUG;
|
|
854
|
+
if (!raw) return void 0;
|
|
855
|
+
const value = raw.trim().toLowerCase();
|
|
856
|
+
if (!value || value === "0" || value === "false" || value === "off") {
|
|
857
|
+
return "silent";
|
|
858
|
+
}
|
|
859
|
+
if (value.includes("trace")) return "trace";
|
|
860
|
+
if (value.includes("debug") || value === "1" || value === "true" || value === "yes") {
|
|
861
|
+
return "debug";
|
|
862
|
+
}
|
|
863
|
+
if (value.includes("info")) return "info";
|
|
864
|
+
if (value.includes("warn")) return "warn";
|
|
865
|
+
if (value.includes("error")) return "error";
|
|
866
|
+
if (value.includes("silent")) return "silent";
|
|
867
|
+
return "debug";
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// src/observability/Metrics.ts
|
|
871
|
+
var Metrics = class {
|
|
872
|
+
counters = /* @__PURE__ */ new Map();
|
|
873
|
+
histograms = /* @__PURE__ */ new Map();
|
|
874
|
+
defaultBuckets = [
|
|
875
|
+
5,
|
|
876
|
+
10,
|
|
877
|
+
25,
|
|
878
|
+
50,
|
|
879
|
+
100,
|
|
880
|
+
250,
|
|
881
|
+
500,
|
|
882
|
+
1e3,
|
|
883
|
+
2500,
|
|
884
|
+
5e3,
|
|
885
|
+
1e4
|
|
886
|
+
];
|
|
887
|
+
/**
|
|
888
|
+
* Increment a counter.
|
|
889
|
+
*/
|
|
890
|
+
increment(name, labels = {}, value = 1) {
|
|
891
|
+
const key = this.makeKey(name, labels);
|
|
892
|
+
this.counters.set(key, (this.counters.get(key) ?? 0) + value);
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Record a value in a histogram.
|
|
896
|
+
*/
|
|
897
|
+
observe(name, labels, value) {
|
|
898
|
+
const key = this.makeKey(name, labels);
|
|
899
|
+
let hist = this.histograms.get(key);
|
|
900
|
+
if (!hist) {
|
|
901
|
+
hist = { count: 0, sum: 0, values: [] };
|
|
902
|
+
this.histograms.set(key, hist);
|
|
903
|
+
}
|
|
904
|
+
hist.count++;
|
|
905
|
+
hist.sum += value;
|
|
906
|
+
hist.values.push(value);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Get a counter value.
|
|
910
|
+
*/
|
|
911
|
+
getCounter(name, labels = {}) {
|
|
912
|
+
return this.counters.get(this.makeKey(name, labels)) ?? 0;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Get histogram stats.
|
|
916
|
+
*/
|
|
917
|
+
getHistogram(name, labels) {
|
|
918
|
+
const key = this.makeKey(name, labels);
|
|
919
|
+
const hist = this.histograms.get(key);
|
|
920
|
+
if (!hist) return void 0;
|
|
921
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
922
|
+
for (const bound of this.defaultBuckets) {
|
|
923
|
+
buckets.set(bound, hist.values.filter((v) => v <= bound).length);
|
|
924
|
+
}
|
|
925
|
+
return { name, labels, count: hist.count, sum: hist.sum, buckets };
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Get all counter values.
|
|
929
|
+
*/
|
|
930
|
+
getAllCounters() {
|
|
931
|
+
const results = [];
|
|
932
|
+
for (const [key, value] of this.counters) {
|
|
933
|
+
const { name, labels } = this.parseKey(key);
|
|
934
|
+
results.push({ name, labels, value });
|
|
935
|
+
}
|
|
936
|
+
return results;
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Get all histogram values.
|
|
940
|
+
*/
|
|
941
|
+
getAllHistograms() {
|
|
942
|
+
const results = [];
|
|
943
|
+
for (const [key, hist] of this.histograms) {
|
|
944
|
+
const { name, labels } = this.parseKey(key);
|
|
945
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
946
|
+
for (const bound of this.defaultBuckets) {
|
|
947
|
+
buckets.set(bound, hist.values.filter((v) => v <= bound).length);
|
|
948
|
+
}
|
|
949
|
+
results.push({ name, labels, count: hist.count, sum: hist.sum, buckets });
|
|
950
|
+
}
|
|
951
|
+
return results;
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Record standard tool invocation metrics.
|
|
955
|
+
*/
|
|
956
|
+
recordInvocation(toolName, ok, durationMs) {
|
|
957
|
+
this.increment("tool_invocations_total", {
|
|
958
|
+
toolName,
|
|
959
|
+
ok: String(ok)
|
|
960
|
+
});
|
|
961
|
+
this.observe("tool_latency_ms", { toolName }, durationMs);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Record a retry event.
|
|
965
|
+
*/
|
|
966
|
+
recordRetry(toolName) {
|
|
967
|
+
this.increment("tool_retries_total", { toolName });
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Record a policy denial.
|
|
971
|
+
*/
|
|
972
|
+
recordPolicyDenied(toolName, reason) {
|
|
973
|
+
this.increment("policy_denied_total", { toolName, reason });
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Reset all metrics (for testing).
|
|
977
|
+
*/
|
|
978
|
+
reset() {
|
|
979
|
+
this.counters.clear();
|
|
980
|
+
this.histograms.clear();
|
|
981
|
+
}
|
|
982
|
+
makeKey(name, labels) {
|
|
983
|
+
const sortedLabels = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
|
|
984
|
+
return `${name}{${sortedLabels}}`;
|
|
985
|
+
}
|
|
986
|
+
parseKey(key) {
|
|
987
|
+
const match = key.match(/^(.+?)\{(.*)\}$/);
|
|
988
|
+
if (!match) return { name: key, labels: {} };
|
|
989
|
+
const labels = {};
|
|
990
|
+
if (match[2]) {
|
|
991
|
+
for (const part of match[2].split(",")) {
|
|
992
|
+
const [k, v] = part.split("=");
|
|
993
|
+
if (k && v !== void 0) labels[k] = v;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return { name: match[1], labels };
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
var Tracing = class {
|
|
1000
|
+
spans = /* @__PURE__ */ new Map();
|
|
1001
|
+
traceSpans = /* @__PURE__ */ new Map();
|
|
1002
|
+
// traceId → spanIds
|
|
1003
|
+
/**
|
|
1004
|
+
* Start a new span.
|
|
1005
|
+
*/
|
|
1006
|
+
startSpan(options) {
|
|
1007
|
+
const span = {
|
|
1008
|
+
spanId: uuid.v4(),
|
|
1009
|
+
traceId: options.traceId ?? uuid.v4(),
|
|
1010
|
+
parentSpanId: options.parentSpanId,
|
|
1011
|
+
name: options.name,
|
|
1012
|
+
startTime: Date.now(),
|
|
1013
|
+
status: "in_progress",
|
|
1014
|
+
attributes: options.attributes ?? {},
|
|
1015
|
+
events: []
|
|
1016
|
+
};
|
|
1017
|
+
this.spans.set(span.spanId, span);
|
|
1018
|
+
const traceList = this.traceSpans.get(span.traceId) ?? [];
|
|
1019
|
+
traceList.push(span.spanId);
|
|
1020
|
+
this.traceSpans.set(span.traceId, traceList);
|
|
1021
|
+
return span;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* End a span and calculate duration.
|
|
1025
|
+
*/
|
|
1026
|
+
endSpan(spanId, status = "ok") {
|
|
1027
|
+
const span = this.spans.get(spanId);
|
|
1028
|
+
if (!span) return void 0;
|
|
1029
|
+
span.endTime = Date.now();
|
|
1030
|
+
span.durationMs = span.endTime - span.startTime;
|
|
1031
|
+
span.status = status;
|
|
1032
|
+
return span;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Add an event to a span.
|
|
1036
|
+
*/
|
|
1037
|
+
addEvent(spanId, name, attributes) {
|
|
1038
|
+
const span = this.spans.get(spanId);
|
|
1039
|
+
if (!span) return;
|
|
1040
|
+
span.events.push({ name, timestamp: Date.now(), attributes });
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Set attributes on a span.
|
|
1044
|
+
*/
|
|
1045
|
+
setAttributes(spanId, attributes) {
|
|
1046
|
+
const span = this.spans.get(spanId);
|
|
1047
|
+
if (!span) return;
|
|
1048
|
+
Object.assign(span.attributes, attributes);
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Get a span by ID.
|
|
1052
|
+
*/
|
|
1053
|
+
getSpan(spanId) {
|
|
1054
|
+
return this.spans.get(spanId);
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Get all spans for a trace.
|
|
1058
|
+
*/
|
|
1059
|
+
getTrace(traceId) {
|
|
1060
|
+
const spanIds = this.traceSpans.get(traceId) ?? [];
|
|
1061
|
+
return spanIds.map((id) => this.spans.get(id)).filter((s) => s !== void 0);
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Create a child span from a parent.
|
|
1065
|
+
*/
|
|
1066
|
+
createChildSpan(parentSpanId, name, attributes) {
|
|
1067
|
+
const parent = this.spans.get(parentSpanId);
|
|
1068
|
+
if (!parent) return void 0;
|
|
1069
|
+
return this.startSpan({
|
|
1070
|
+
name,
|
|
1071
|
+
traceId: parent.traceId,
|
|
1072
|
+
parentSpanId: parent.spanId,
|
|
1073
|
+
attributes
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Clear all traces (for testing).
|
|
1078
|
+
*/
|
|
1079
|
+
clear() {
|
|
1080
|
+
this.spans.clear();
|
|
1081
|
+
this.traceSpans.clear();
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
var NON_RETRYABLE_ERRORS = /* @__PURE__ */ new Set([
|
|
1085
|
+
"TOOL_NOT_FOUND",
|
|
1086
|
+
"INPUT_SCHEMA_INVALID",
|
|
1087
|
+
"POLICY_DENIED",
|
|
1088
|
+
"HITL_DENIED",
|
|
1089
|
+
"OUTPUT_SCHEMA_INVALID",
|
|
1090
|
+
"PATH_OUTSIDE_SANDBOX",
|
|
1091
|
+
"FILE_TOO_LARGE",
|
|
1092
|
+
"HTTP_DISALLOWED_HOST",
|
|
1093
|
+
"HTTP_TOO_LARGE"
|
|
1094
|
+
]);
|
|
1095
|
+
function isRetryable(error) {
|
|
1096
|
+
if (error instanceof Error) {
|
|
1097
|
+
const kind = error.kind;
|
|
1098
|
+
if (kind && NON_RETRYABLE_ERRORS.has(kind)) return false;
|
|
1099
|
+
}
|
|
1100
|
+
return true;
|
|
1101
|
+
}
|
|
1102
|
+
async function withRetry(fn, options = {}) {
|
|
1103
|
+
const {
|
|
1104
|
+
maxRetries = 2,
|
|
1105
|
+
baseDelayMs = 1e3,
|
|
1106
|
+
maxDelayMs = 1e4,
|
|
1107
|
+
jitter = 0.1,
|
|
1108
|
+
shouldRetry,
|
|
1109
|
+
onRetry
|
|
1110
|
+
} = options;
|
|
1111
|
+
if (maxRetries <= 0) {
|
|
1112
|
+
return fn();
|
|
1113
|
+
}
|
|
1114
|
+
const pRetryOptions = {
|
|
1115
|
+
retries: maxRetries,
|
|
1116
|
+
minTimeout: baseDelayMs,
|
|
1117
|
+
maxTimeout: maxDelayMs,
|
|
1118
|
+
randomize: true,
|
|
1119
|
+
factor: 2,
|
|
1120
|
+
onFailedAttempt: (error) => {
|
|
1121
|
+
if (jitter > 0 && error.retriesLeft > 0) ;
|
|
1122
|
+
if (shouldRetry && !shouldRetry(error)) {
|
|
1123
|
+
throw error;
|
|
1124
|
+
}
|
|
1125
|
+
if (!isRetryable(error)) {
|
|
1126
|
+
throw error;
|
|
1127
|
+
}
|
|
1128
|
+
onRetry?.(error, maxRetries - error.retriesLeft);
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
return pRetry__default.default(fn, pRetryOptions);
|
|
1132
|
+
}
|
|
1133
|
+
function createTaggedError(kind, message, details) {
|
|
1134
|
+
const error = new Error(message);
|
|
1135
|
+
error.kind = kind;
|
|
1136
|
+
error.details = details;
|
|
1137
|
+
return error;
|
|
1138
|
+
}
|
|
1139
|
+
function resolveTool(toolName, registry) {
|
|
1140
|
+
const spec = registry.get(toolName);
|
|
1141
|
+
if (!spec) {
|
|
1142
|
+
throw createTaggedError(
|
|
1143
|
+
"TOOL_NOT_FOUND",
|
|
1144
|
+
`Tool not found: ${toolName}`,
|
|
1145
|
+
{ availableTools: registry.snapshot().slice(0, 20).map((s) => s.name) }
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
return spec;
|
|
1149
|
+
}
|
|
1150
|
+
function validateInput(spec, args, validator) {
|
|
1151
|
+
try {
|
|
1152
|
+
return validator.validateOrThrow(
|
|
1153
|
+
spec.inputSchema,
|
|
1154
|
+
args,
|
|
1155
|
+
`Input validation failed for ${spec.name}`
|
|
1156
|
+
);
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
if (error instanceof SchemaValidationError) {
|
|
1159
|
+
throw createTaggedError("INPUT_SCHEMA_INVALID", error.message, {
|
|
1160
|
+
errors: error.errors,
|
|
1161
|
+
schema: spec.inputSchema
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
throw error;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
function enrichDefaults(spec, args, validator) {
|
|
1168
|
+
return validator.enrichDefaults(spec.inputSchema, args);
|
|
1169
|
+
}
|
|
1170
|
+
function enforcePolicy(spec, args, ctx, deps) {
|
|
1171
|
+
try {
|
|
1172
|
+
deps.policy.enforce(spec, args, ctx);
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
if (error instanceof PolicyDeniedError) {
|
|
1175
|
+
const event = {
|
|
1176
|
+
type: "POLICY_DENIED",
|
|
1177
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1178
|
+
requestId: ctx.requestId,
|
|
1179
|
+
taskId: ctx.taskId,
|
|
1180
|
+
toolName: spec.name,
|
|
1181
|
+
traceId: ctx.traceId,
|
|
1182
|
+
userId: ctx.userId,
|
|
1183
|
+
reason: error.message,
|
|
1184
|
+
missingCapabilities: error.missingCapabilities?.map(String)
|
|
1185
|
+
};
|
|
1186
|
+
deps.eventLog.append(event);
|
|
1187
|
+
deps.metrics.recordPolicyDenied(spec.name, error.message);
|
|
1188
|
+
}
|
|
1189
|
+
throw error;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
var HITL_GATED_SIDE_EFFECTS = ["external_write", "destructive"];
|
|
1193
|
+
async function requireHumanApproval(spec, args, ctx, deps) {
|
|
1194
|
+
const sideEffect = spec._meta?.hitl?.sideEffect ?? "none";
|
|
1195
|
+
if (!HITL_GATED_SIDE_EFFECTS.includes(sideEffect)) return;
|
|
1196
|
+
const onApproval = deps.onApprovalRequired;
|
|
1197
|
+
if (!onApproval) return;
|
|
1198
|
+
const baseEvent = {
|
|
1199
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1200
|
+
requestId: ctx.requestId,
|
|
1201
|
+
taskId: ctx.taskId,
|
|
1202
|
+
toolName: spec.name,
|
|
1203
|
+
traceId: ctx.traceId,
|
|
1204
|
+
userId: ctx.userId
|
|
1205
|
+
};
|
|
1206
|
+
const requested = {
|
|
1207
|
+
...baseEvent,
|
|
1208
|
+
type: "HITL_APPROVAL_REQUESTED",
|
|
1209
|
+
sideEffect
|
|
1210
|
+
};
|
|
1211
|
+
deps.eventLog.append(requested);
|
|
1212
|
+
deps.logger.trace("hitl.requested", { tool: spec.name, sideEffect, requestId: ctx.requestId });
|
|
1213
|
+
let approved;
|
|
1214
|
+
try {
|
|
1215
|
+
approved = await onApproval(spec, args, ctx);
|
|
1216
|
+
} catch (err) {
|
|
1217
|
+
deps.eventLog.append({
|
|
1218
|
+
...baseEvent,
|
|
1219
|
+
type: "HITL_APPROVAL_DENIED",
|
|
1220
|
+
sideEffect,
|
|
1221
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
1222
|
+
});
|
|
1223
|
+
throw createTaggedError(
|
|
1224
|
+
"HITL_DENIED",
|
|
1225
|
+
`Human denied approval for ${spec.name} (${sideEffect})`,
|
|
1226
|
+
{ reason: err instanceof Error ? err.message : String(err) }
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
if (approved === false) {
|
|
1230
|
+
deps.eventLog.append({
|
|
1231
|
+
...baseEvent,
|
|
1232
|
+
type: "HITL_APPROVAL_DENIED",
|
|
1233
|
+
sideEffect,
|
|
1234
|
+
reason: "User rejected"
|
|
1235
|
+
});
|
|
1236
|
+
throw createTaggedError("HITL_DENIED", `Human denied approval for ${spec.name} (${sideEffect})`);
|
|
1237
|
+
}
|
|
1238
|
+
deps.eventLog.append({
|
|
1239
|
+
...baseEvent,
|
|
1240
|
+
type: "HITL_APPROVAL_GRANTED",
|
|
1241
|
+
sideEffect
|
|
1242
|
+
});
|
|
1243
|
+
deps.logger.trace("hitl.granted", { tool: spec.name, sideEffect, requestId: ctx.requestId });
|
|
1244
|
+
}
|
|
1245
|
+
async function executeWithBudget(spec, args, ctx, spanId, deps) {
|
|
1246
|
+
const adapter = deps.adapters.get(spec.kind);
|
|
1247
|
+
if (!adapter) {
|
|
1248
|
+
throw createTaggedError(
|
|
1249
|
+
"TOOL_NOT_FOUND",
|
|
1250
|
+
`No adapter registered for kind: ${spec.kind}`
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
const timeoutMs = deps.budget.getTimeout(
|
|
1254
|
+
spec.name,
|
|
1255
|
+
ctx.budget?.timeoutMs
|
|
1256
|
+
);
|
|
1257
|
+
const maxRetries = ctx.budget?.maxRetries ?? deps.defaultMaxRetries ?? 2;
|
|
1258
|
+
const executeFn = async () => {
|
|
1259
|
+
return deps.budget.execute(spec.name, async () => {
|
|
1260
|
+
deps.tracing.addEvent(spanId, "execute_start");
|
|
1261
|
+
deps.logger.trace("execute.start", {
|
|
1262
|
+
tool: spec.name,
|
|
1263
|
+
requestId: ctx.requestId,
|
|
1264
|
+
timeoutMs,
|
|
1265
|
+
maxRetries
|
|
1266
|
+
});
|
|
1267
|
+
const result = await adapter.invoke(spec, args, ctx);
|
|
1268
|
+
deps.tracing.addEvent(spanId, "execute_end");
|
|
1269
|
+
deps.logger.trace("execute.end", {
|
|
1270
|
+
tool: spec.name,
|
|
1271
|
+
requestId: ctx.requestId
|
|
1272
|
+
});
|
|
1273
|
+
return result;
|
|
1274
|
+
});
|
|
1275
|
+
};
|
|
1276
|
+
const retryFn = () => withRetry(executeFn, {
|
|
1277
|
+
maxRetries,
|
|
1278
|
+
onRetry: (error, attempt) => {
|
|
1279
|
+
deps.metrics.recordRetry(spec.name);
|
|
1280
|
+
const event = {
|
|
1281
|
+
type: "RETRY",
|
|
1282
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1283
|
+
requestId: ctx.requestId,
|
|
1284
|
+
taskId: ctx.taskId,
|
|
1285
|
+
toolName: spec.name,
|
|
1286
|
+
traceId: ctx.traceId,
|
|
1287
|
+
userId: ctx.userId,
|
|
1288
|
+
attempt,
|
|
1289
|
+
maxRetries,
|
|
1290
|
+
reason: error.message
|
|
1291
|
+
};
|
|
1292
|
+
deps.eventLog.append(event);
|
|
1293
|
+
deps.tracing.addEvent(spanId, "retry", { attempt, reason: error.message });
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
try {
|
|
1297
|
+
return await pTimeout__default.default(retryFn(), {
|
|
1298
|
+
milliseconds: timeoutMs,
|
|
1299
|
+
message: `Tool ${spec.name} timed out after ${timeoutMs}ms`
|
|
1300
|
+
});
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
if (error instanceof Error && error.message.includes("timed out")) {
|
|
1303
|
+
throw createTaggedError("TIMEOUT", error.message);
|
|
1304
|
+
}
|
|
1305
|
+
throw error;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
function validateOutput(spec, result, validator) {
|
|
1309
|
+
try {
|
|
1310
|
+
return validator.validateOrThrow(
|
|
1311
|
+
spec.outputSchema,
|
|
1312
|
+
result,
|
|
1313
|
+
`Output validation failed for ${spec.name}`
|
|
1314
|
+
);
|
|
1315
|
+
} catch (error) {
|
|
1316
|
+
if (error instanceof SchemaValidationError) {
|
|
1317
|
+
throw createTaggedError("OUTPUT_SCHEMA_INVALID", error.message, {
|
|
1318
|
+
errors: error.errors
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
throw error;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// src/runtime/PTCRuntimeObservability.ts
|
|
1326
|
+
function emitToolCalled(intent, ctx, deps) {
|
|
1327
|
+
const event = {
|
|
1328
|
+
type: "TOOL_CALLED",
|
|
1329
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1330
|
+
requestId: ctx.requestId,
|
|
1331
|
+
taskId: ctx.taskId,
|
|
1332
|
+
toolName: intent.tool,
|
|
1333
|
+
traceId: ctx.traceId,
|
|
1334
|
+
userId: ctx.userId,
|
|
1335
|
+
argsSummary: sanitizeArgs(intent.args),
|
|
1336
|
+
purpose: intent.purpose,
|
|
1337
|
+
idempotencyKey: intent.idempotencyKey
|
|
1338
|
+
};
|
|
1339
|
+
deps.eventLog.append(event);
|
|
1340
|
+
}
|
|
1341
|
+
function recordSuccess(spec, durationMs, _evidence, spanId, deps) {
|
|
1342
|
+
deps.metrics.recordInvocation(spec.name, true, durationMs);
|
|
1343
|
+
deps.tracing.setAttributes(spanId, {
|
|
1344
|
+
"tool.duration_ms": durationMs,
|
|
1345
|
+
"tool.ok": true
|
|
1346
|
+
});
|
|
1347
|
+
deps.tracing.endSpan(spanId, "ok");
|
|
1348
|
+
}
|
|
1349
|
+
function handleError(error, intent, ctx, durationMs, spanId, deps) {
|
|
1350
|
+
const kind = error?.kind ?? "UPSTREAM_ERROR";
|
|
1351
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1352
|
+
const details = error?.details;
|
|
1353
|
+
deps.metrics.recordInvocation(intent.tool, false, durationMs);
|
|
1354
|
+
deps.tracing.setAttributes(spanId, {
|
|
1355
|
+
"tool.duration_ms": durationMs,
|
|
1356
|
+
"tool.ok": false,
|
|
1357
|
+
"tool.error_kind": kind
|
|
1358
|
+
});
|
|
1359
|
+
deps.tracing.endSpan(spanId, "error");
|
|
1360
|
+
const event = {
|
|
1361
|
+
type: "TOOL_RESULT",
|
|
1362
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1363
|
+
requestId: ctx.requestId,
|
|
1364
|
+
taskId: ctx.taskId,
|
|
1365
|
+
toolName: intent.tool,
|
|
1366
|
+
traceId: ctx.traceId,
|
|
1367
|
+
userId: ctx.userId,
|
|
1368
|
+
ok: false,
|
|
1369
|
+
durationMs,
|
|
1370
|
+
resultSummary: message,
|
|
1371
|
+
evidence: [],
|
|
1372
|
+
error: { kind, message, details }
|
|
1373
|
+
};
|
|
1374
|
+
deps.eventLog.append(event);
|
|
1375
|
+
deps.logger.warn("invoke.error", {
|
|
1376
|
+
tool: intent.tool,
|
|
1377
|
+
requestId: ctx.requestId,
|
|
1378
|
+
taskId: ctx.taskId,
|
|
1379
|
+
traceId: ctx.traceId,
|
|
1380
|
+
kind,
|
|
1381
|
+
message,
|
|
1382
|
+
durationMs,
|
|
1383
|
+
details: deps.logger.options.includeResults ? summarizeForLog(details) : void 0
|
|
1384
|
+
});
|
|
1385
|
+
return {
|
|
1386
|
+
ok: false,
|
|
1387
|
+
evidence: [],
|
|
1388
|
+
error: { kind, message, details }
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
function sanitizeArgs(args) {
|
|
1392
|
+
if (!args) return "{}";
|
|
1393
|
+
return sanitizeForLog(args);
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// src/runtime/PTCRuntime.ts
|
|
1397
|
+
var PTCRuntime = class {
|
|
1398
|
+
registry;
|
|
1399
|
+
adapters = /* @__PURE__ */ new Map();
|
|
1400
|
+
validator;
|
|
1401
|
+
policy;
|
|
1402
|
+
budget;
|
|
1403
|
+
eventLog;
|
|
1404
|
+
metrics;
|
|
1405
|
+
tracing;
|
|
1406
|
+
config;
|
|
1407
|
+
logger;
|
|
1408
|
+
constructor(options = {}) {
|
|
1409
|
+
this.config = options.config ?? {};
|
|
1410
|
+
this.registry = options.registry ?? new ToolRegistry();
|
|
1411
|
+
this.validator = options.validator ?? new SchemaValidator();
|
|
1412
|
+
this.policy = options.policy ?? new PolicyEngine(this.config.policy);
|
|
1413
|
+
this.budget = options.budget ?? new BudgetManager(this.config.budget);
|
|
1414
|
+
this.eventLog = options.eventLog ?? new EventLog();
|
|
1415
|
+
this.metrics = options.metrics ?? new Metrics();
|
|
1416
|
+
this.tracing = options.tracing ?? new Tracing();
|
|
1417
|
+
this.logger = createLogger({ ...this.config.debug, prefix: "PTCRuntime" });
|
|
1418
|
+
if (this.logger.options.logEvents) {
|
|
1419
|
+
this.eventLog.on((entry) => {
|
|
1420
|
+
const event = entry.event;
|
|
1421
|
+
this.logger.debug("event", {
|
|
1422
|
+
seq: entry.seq,
|
|
1423
|
+
type: event.type,
|
|
1424
|
+
toolName: event.toolName,
|
|
1425
|
+
requestId: event.requestId,
|
|
1426
|
+
taskId: event.taskId,
|
|
1427
|
+
ok: "ok" in event ? event.ok : void 0
|
|
1428
|
+
});
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Register an adapter for a tool kind.
|
|
1434
|
+
*/
|
|
1435
|
+
registerAdapter(adapter) {
|
|
1436
|
+
this.adapters.set(adapter.kind, adapter);
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Get an adapter by kind (e.g. "mcp"). Use to set MCP client via adapter.setClient().
|
|
1440
|
+
*/
|
|
1441
|
+
getAdapter(kind) {
|
|
1442
|
+
return this.adapters.get(kind);
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Get the tool registry.
|
|
1446
|
+
*/
|
|
1447
|
+
getRegistry() {
|
|
1448
|
+
return this.registry;
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Get the event log.
|
|
1452
|
+
*/
|
|
1453
|
+
getEventLog() {
|
|
1454
|
+
return this.eventLog;
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Get the metrics collector.
|
|
1458
|
+
*/
|
|
1459
|
+
getMetrics() {
|
|
1460
|
+
return this.metrics;
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Get the tracing system.
|
|
1464
|
+
*/
|
|
1465
|
+
getTracing() {
|
|
1466
|
+
return this.tracing;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Invoke a tool through the PTC pipeline.
|
|
1470
|
+
* Never throws - always returns a structured ToolResult.
|
|
1471
|
+
*/
|
|
1472
|
+
async invoke(intent, ctx) {
|
|
1473
|
+
const startTime = Date.now();
|
|
1474
|
+
if (this.logger.isEnabled("debug")) {
|
|
1475
|
+
this.logger.debug("invoke.start", {
|
|
1476
|
+
tool: intent.tool,
|
|
1477
|
+
requestId: ctx.requestId,
|
|
1478
|
+
taskId: ctx.taskId,
|
|
1479
|
+
traceId: ctx.traceId,
|
|
1480
|
+
purpose: intent.purpose,
|
|
1481
|
+
args: this.logger.options.includeArgs ? sanitizeForLog(intent.args) : void 0
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
const span = this.tracing.startSpan({
|
|
1485
|
+
name: `tool:${intent.tool}`,
|
|
1486
|
+
traceId: ctx.traceId,
|
|
1487
|
+
attributes: {
|
|
1488
|
+
"tool.name": intent.tool,
|
|
1489
|
+
"tool.purpose": intent.purpose,
|
|
1490
|
+
requestId: ctx.requestId,
|
|
1491
|
+
taskId: ctx.taskId
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
emitToolCalled(intent, ctx, this.getObservabilityDeps());
|
|
1495
|
+
try {
|
|
1496
|
+
const spec = resolveTool(intent.tool, this.registry);
|
|
1497
|
+
this.tracing.addEvent(span.spanId, "resolved", {
|
|
1498
|
+
kind: spec.kind,
|
|
1499
|
+
version: spec.version
|
|
1500
|
+
});
|
|
1501
|
+
const validatedArgs = validateInput(spec, intent.args, this.validator);
|
|
1502
|
+
const enrichedArgs = enrichDefaults(spec, validatedArgs, this.validator);
|
|
1503
|
+
enforcePolicy(spec, enrichedArgs, ctx, {
|
|
1504
|
+
policy: this.policy,
|
|
1505
|
+
eventLog: this.eventLog,
|
|
1506
|
+
metrics: this.metrics,
|
|
1507
|
+
tracing: this.tracing
|
|
1508
|
+
});
|
|
1509
|
+
if (!this.budget.checkRateLimit(spec.name)) {
|
|
1510
|
+
throw createTaggedError(
|
|
1511
|
+
"BUDGET_EXCEEDED",
|
|
1512
|
+
`Rate limit exceeded for tool: ${spec.name}`
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
await requireHumanApproval(spec, enrichedArgs, ctx, {
|
|
1516
|
+
onApprovalRequired: this.config.onApprovalRequired,
|
|
1517
|
+
eventLog: this.eventLog,
|
|
1518
|
+
logger: this.logger
|
|
1519
|
+
});
|
|
1520
|
+
if (ctx.dryRun) {
|
|
1521
|
+
return this.buildDryRunResult(spec, enrichedArgs, ctx, startTime, span.spanId);
|
|
1522
|
+
}
|
|
1523
|
+
const { result, raw } = await executeWithBudget(
|
|
1524
|
+
spec,
|
|
1525
|
+
enrichedArgs,
|
|
1526
|
+
ctx,
|
|
1527
|
+
span.spanId,
|
|
1528
|
+
this.getPipelineDeps()
|
|
1529
|
+
);
|
|
1530
|
+
const validatedOutput = validateOutput(spec, result, this.validator);
|
|
1531
|
+
const durationMs = Date.now() - startTime;
|
|
1532
|
+
const builtEvidence = buildEvidence({
|
|
1533
|
+
spec,
|
|
1534
|
+
args: enrichedArgs,
|
|
1535
|
+
result: validatedOutput,
|
|
1536
|
+
raw,
|
|
1537
|
+
ctx,
|
|
1538
|
+
durationMs
|
|
1539
|
+
});
|
|
1540
|
+
const adapterEvidence = raw && typeof raw === "object" && Array.isArray(raw.evidence) ? raw.evidence : [];
|
|
1541
|
+
const evidence = [...adapterEvidence, ...builtEvidence];
|
|
1542
|
+
recordSuccess(spec, durationMs, evidence, span.spanId, this.getObservabilityDeps());
|
|
1543
|
+
if (this.logger.isEnabled("debug")) {
|
|
1544
|
+
this.logger.debug("invoke.ok", {
|
|
1545
|
+
tool: spec.name,
|
|
1546
|
+
durationMs,
|
|
1547
|
+
result: this.logger.options.includeResults ? summarizeForLog(validatedOutput) : void 0,
|
|
1548
|
+
raw: this.logger.options.includeRaw ? summarizeForLog(raw) : void 0
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
return {
|
|
1552
|
+
ok: true,
|
|
1553
|
+
result: validatedOutput,
|
|
1554
|
+
evidence,
|
|
1555
|
+
raw: this.config.includeRaw !== false ? raw : void 0
|
|
1556
|
+
};
|
|
1557
|
+
} catch (error) {
|
|
1558
|
+
const durationMs = Date.now() - startTime;
|
|
1559
|
+
return handleError(error, intent, ctx, durationMs, span.spanId, this.getObservabilityDeps());
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Search for tools in the registry.
|
|
1564
|
+
*/
|
|
1565
|
+
searchTools(query, filters) {
|
|
1566
|
+
return this.registry.search({
|
|
1567
|
+
text: query,
|
|
1568
|
+
kind: filters?.kind,
|
|
1569
|
+
capabilities: filters?.capabilities,
|
|
1570
|
+
tags: filters?.tags
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* Get the schema for a tool.
|
|
1575
|
+
*/
|
|
1576
|
+
getToolSchema(toolName) {
|
|
1577
|
+
const spec = this.registry.get(toolName);
|
|
1578
|
+
if (!spec) return void 0;
|
|
1579
|
+
return { input: spec.inputSchema, output: spec.outputSchema };
|
|
1580
|
+
}
|
|
1581
|
+
// --- Helper Methods ---
|
|
1582
|
+
getPipelineDeps() {
|
|
1583
|
+
return {
|
|
1584
|
+
registry: this.registry,
|
|
1585
|
+
adapters: this.adapters,
|
|
1586
|
+
validator: this.validator,
|
|
1587
|
+
policy: this.policy,
|
|
1588
|
+
budget: this.budget,
|
|
1589
|
+
eventLog: this.eventLog,
|
|
1590
|
+
metrics: this.metrics,
|
|
1591
|
+
tracing: this.tracing,
|
|
1592
|
+
logger: this.logger,
|
|
1593
|
+
defaultMaxRetries: this.config.defaultMaxRetries,
|
|
1594
|
+
onApprovalRequired: this.config.onApprovalRequired
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
getObservabilityDeps() {
|
|
1598
|
+
return {
|
|
1599
|
+
eventLog: this.eventLog,
|
|
1600
|
+
metrics: this.metrics,
|
|
1601
|
+
tracing: this.tracing,
|
|
1602
|
+
logger: this.logger
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
buildDryRunResult(spec, args, _ctx, startTime, spanId) {
|
|
1606
|
+
this.tracing.endSpan(spanId, "ok");
|
|
1607
|
+
return {
|
|
1608
|
+
ok: true,
|
|
1609
|
+
result: {
|
|
1610
|
+
dryRun: true,
|
|
1611
|
+
tool: spec.name,
|
|
1612
|
+
kind: spec.kind,
|
|
1613
|
+
args,
|
|
1614
|
+
capabilities: spec.capabilities
|
|
1615
|
+
},
|
|
1616
|
+
evidence: [
|
|
1617
|
+
{
|
|
1618
|
+
type: "tool",
|
|
1619
|
+
ref: `${spec.name}@${spec.version}`,
|
|
1620
|
+
summary: `Dry-run: would execute ${spec.kind}:${spec.name}`,
|
|
1621
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1622
|
+
}
|
|
1623
|
+
]
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1627
|
+
|
|
1628
|
+
// src/toolDescriptor.ts
|
|
1629
|
+
var NPM_TOOL_DESCRIPTOR_REGEX = /^npm:(@[\w.-]+\/[\w.-]+)#(.+)$/;
|
|
1630
|
+
function isNpmToolDescriptor(descriptor) {
|
|
1631
|
+
return NPM_TOOL_DESCRIPTOR_REGEX.test(descriptor.trim());
|
|
1632
|
+
}
|
|
1633
|
+
function parseNpmToolDescriptor(descriptor) {
|
|
1634
|
+
const s = descriptor.trim();
|
|
1635
|
+
const m = s.match(NPM_TOOL_DESCRIPTOR_REGEX);
|
|
1636
|
+
if (!m) return null;
|
|
1637
|
+
return { fullPackage: m[1], toolPath: m[2] };
|
|
1638
|
+
}
|
|
1639
|
+
var KNOWN_PACKAGE_PREFIX = {
|
|
1640
|
+
"@easynet/agent-tool-builtin-tools": "core/",
|
|
1641
|
+
"@easynet/agent-tool-buildin-tools": "core/",
|
|
1642
|
+
// alias (e.g. npm:@easynet/agent-tool-buildin#fs.readText)
|
|
1643
|
+
"@easynet/agent-tool-buildin": "core/",
|
|
1644
|
+
// shorthand
|
|
1645
|
+
"@easynet/agent-tool-example-tools": "example/"
|
|
1646
|
+
};
|
|
1647
|
+
function resolveNpmToolDescriptor(descriptor) {
|
|
1648
|
+
const parsed = parseNpmToolDescriptor(descriptor);
|
|
1649
|
+
if (!parsed) return null;
|
|
1650
|
+
const prefix = KNOWN_PACKAGE_PREFIX[parsed.fullPackage];
|
|
1651
|
+
if (!prefix) return null;
|
|
1652
|
+
const path = parsed.toolPath;
|
|
1653
|
+
if (path.startsWith(prefix)) return path;
|
|
1654
|
+
return prefix + path;
|
|
1655
|
+
}
|
|
1656
|
+
function resolveToolDescriptor(descriptor) {
|
|
1657
|
+
const s = descriptor.trim();
|
|
1658
|
+
const resolved = resolveNpmToolDescriptor(s);
|
|
1659
|
+
if (resolved !== null) return resolved;
|
|
1660
|
+
return s;
|
|
1661
|
+
}
|
|
1662
|
+
function normalizeToolList(descriptors) {
|
|
1663
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1664
|
+
const out = [];
|
|
1665
|
+
for (const d of descriptors) {
|
|
1666
|
+
if (typeof d !== "string" || !d.trim()) continue;
|
|
1667
|
+
const name = resolveToolDescriptor(d);
|
|
1668
|
+
if (!seen.has(name)) {
|
|
1669
|
+
seen.add(name);
|
|
1670
|
+
out.push(name);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
return out;
|
|
1674
|
+
}
|
|
1675
|
+
function isCursorFormat(obj) {
|
|
1676
|
+
return typeof obj === "object" && obj !== null && "mcpServers" in obj && typeof obj.mcpServers === "object" && obj.mcpServers !== null;
|
|
1677
|
+
}
|
|
1678
|
+
function extractMCPConfig(parsed, toolName) {
|
|
1679
|
+
if (isCursorFormat(parsed)) {
|
|
1680
|
+
const servers = parsed.mcpServers;
|
|
1681
|
+
const keys = Object.keys(servers);
|
|
1682
|
+
if (keys.length === 0) {
|
|
1683
|
+
return {};
|
|
1684
|
+
}
|
|
1685
|
+
const name = toolName && keys.includes(toolName) ? toolName : keys[0];
|
|
1686
|
+
return servers[name];
|
|
1687
|
+
}
|
|
1688
|
+
return parsed;
|
|
1689
|
+
}
|
|
1690
|
+
async function loadMCPTool(dirPath, manifest) {
|
|
1691
|
+
const mcpPath = path.join(dirPath, manifest.entryPoint ?? "mcp.json");
|
|
1692
|
+
let raw;
|
|
1693
|
+
try {
|
|
1694
|
+
raw = await promises.readFile(mcpPath, "utf-8");
|
|
1695
|
+
} catch (err) {
|
|
1696
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
1697
|
+
dirPath,
|
|
1698
|
+
"load",
|
|
1699
|
+
`Failed to read MCP config: ${mcpPath}`,
|
|
1700
|
+
err
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
let parsed;
|
|
1704
|
+
try {
|
|
1705
|
+
parsed = JSON.parse(raw);
|
|
1706
|
+
} catch (err) {
|
|
1707
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
1708
|
+
dirPath,
|
|
1709
|
+
"load",
|
|
1710
|
+
`Invalid JSON in ${mcpPath}`,
|
|
1711
|
+
err
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
const baseName = manifest.name?.split("/").pop();
|
|
1715
|
+
const config = extractMCPConfig(parsed, baseName);
|
|
1716
|
+
if (!config.command && !config.url) {
|
|
1717
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
1718
|
+
dirPath,
|
|
1719
|
+
"validate",
|
|
1720
|
+
`mcp.json must have either "command" or "url" field`
|
|
1721
|
+
);
|
|
1722
|
+
}
|
|
1723
|
+
return { manifest, dirPath, mcpConfig: config };
|
|
1724
|
+
}
|
|
1725
|
+
var DEFAULT_EXTENSIONS = [".js", ".mjs"];
|
|
1726
|
+
async function resolveEntryPoint(dirPath, baseName, extensions = DEFAULT_EXTENSIONS) {
|
|
1727
|
+
if (extensions.some((ext) => baseName.endsWith(ext))) {
|
|
1728
|
+
const fullPath = path.join(dirPath, baseName);
|
|
1729
|
+
await promises.stat(fullPath);
|
|
1730
|
+
return fullPath;
|
|
1731
|
+
}
|
|
1732
|
+
for (const ext of extensions) {
|
|
1733
|
+
const fullPath = path.join(dirPath, `${baseName}${ext}`);
|
|
1734
|
+
try {
|
|
1735
|
+
await promises.stat(fullPath);
|
|
1736
|
+
return fullPath;
|
|
1737
|
+
} catch {
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
throw new Error(
|
|
1741
|
+
`Could not find entry point in ${dirPath}. Tried: ${extensions.map((e) => baseName + e).join(", ")}`
|
|
1742
|
+
);
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// src/discovery/load/LangChainLoader.ts
|
|
1746
|
+
async function loadLangChainTool(dirPath, manifest, extensions) {
|
|
1747
|
+
let entryFile;
|
|
1748
|
+
try {
|
|
1749
|
+
entryFile = await resolveEntryPoint(
|
|
1750
|
+
dirPath,
|
|
1751
|
+
manifest.entryPoint ?? "index",
|
|
1752
|
+
extensions
|
|
1753
|
+
);
|
|
1754
|
+
} catch (err) {
|
|
1755
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
1756
|
+
dirPath,
|
|
1757
|
+
"load",
|
|
1758
|
+
`Cannot find LangChain entry point`,
|
|
1759
|
+
err
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1762
|
+
let mod;
|
|
1763
|
+
try {
|
|
1764
|
+
mod = await import(url.pathToFileURL(entryFile).href);
|
|
1765
|
+
} catch (err) {
|
|
1766
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
1767
|
+
dirPath,
|
|
1768
|
+
"load",
|
|
1769
|
+
`Failed to import ${entryFile}`,
|
|
1770
|
+
err
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
const tool = mod.default ?? mod.tool ?? mod;
|
|
1774
|
+
if (!tool || typeof tool.invoke !== "function") {
|
|
1775
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
1776
|
+
dirPath,
|
|
1777
|
+
"validate",
|
|
1778
|
+
`Entry point must export an object with invoke() method (LangChainToolLike)`
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
return { manifest, dirPath, impl: tool };
|
|
1782
|
+
}
|
|
1783
|
+
var DEFAULT_EXTENSIONS2 = [".js", ".mjs"];
|
|
1784
|
+
async function listSkillProgramFiles(dirPath, extensions = DEFAULT_EXTENSIONS2) {
|
|
1785
|
+
let entries;
|
|
1786
|
+
try {
|
|
1787
|
+
const dirEntries = await promises.readdir(dirPath, { withFileTypes: true });
|
|
1788
|
+
entries = dirEntries.map((entry) => ({
|
|
1789
|
+
name: entry.name,
|
|
1790
|
+
isFile: entry.isFile()
|
|
1791
|
+
}));
|
|
1792
|
+
} catch {
|
|
1793
|
+
return [];
|
|
1794
|
+
}
|
|
1795
|
+
return entries.filter((e) => e.isFile).map((e) => e.name).filter((name) => {
|
|
1796
|
+
if (name.startsWith(".") || name.startsWith("_")) return false;
|
|
1797
|
+
if (name.includes(".test.") || name.includes(".spec.")) return false;
|
|
1798
|
+
return extensions.some((ext) => name.endsWith(ext));
|
|
1799
|
+
}).sort((a, b) => {
|
|
1800
|
+
if (a === "handler.js" || a === "index.js") return -1;
|
|
1801
|
+
if (b === "handler.js" || b === "index.js") return 1;
|
|
1802
|
+
return a.localeCompare(b);
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
function isLangChainLikeTool(val) {
|
|
1806
|
+
return val != null && typeof val === "object" && "invoke" in val && typeof val.invoke === "function";
|
|
1807
|
+
}
|
|
1808
|
+
function isConstructable(val) {
|
|
1809
|
+
return typeof val === "function" && typeof val.prototype === "object";
|
|
1810
|
+
}
|
|
1811
|
+
async function loadOneSkillProgram(dirPath, manifest, entryFile, skillDef, programKey, extensions) {
|
|
1812
|
+
let impl;
|
|
1813
|
+
try {
|
|
1814
|
+
const fullPath = await resolveEntryPoint(dirPath, entryFile, extensions ?? [".js", ".mjs"]);
|
|
1815
|
+
const mod = await import(url.pathToFileURL(fullPath).href);
|
|
1816
|
+
const fn = mod.default ?? mod.handler ?? mod.Tool;
|
|
1817
|
+
if (isLangChainLikeTool(fn)) {
|
|
1818
|
+
impl = fn;
|
|
1819
|
+
} else if (isConstructable(fn)) {
|
|
1820
|
+
const instance = new fn();
|
|
1821
|
+
if (isLangChainLikeTool(instance)) impl = instance;
|
|
1822
|
+
} else if (typeof fn === "function") {
|
|
1823
|
+
impl = fn;
|
|
1824
|
+
}
|
|
1825
|
+
} catch {
|
|
1826
|
+
}
|
|
1827
|
+
return {
|
|
1828
|
+
manifest,
|
|
1829
|
+
dirPath,
|
|
1830
|
+
impl,
|
|
1831
|
+
skillDefinition: skillDef,
|
|
1832
|
+
programKey
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
async function loadSkillTools(dirPath, manifest, extensions) {
|
|
1836
|
+
let skillDef;
|
|
1837
|
+
try {
|
|
1838
|
+
skillDef = await chunkZ7TGIG77_cjs.loadSkillDefinition(dirPath);
|
|
1839
|
+
} catch (err) {
|
|
1840
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
1841
|
+
dirPath,
|
|
1842
|
+
"load",
|
|
1843
|
+
`Failed to parse SKILL.md: ${err.message}`,
|
|
1844
|
+
err
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
const programs = manifest.programs;
|
|
1848
|
+
if (programs && typeof programs === "object" && Object.keys(programs).length > 0) {
|
|
1849
|
+
const result = [];
|
|
1850
|
+
for (const [programKey, entryFile2] of Object.entries(programs)) {
|
|
1851
|
+
const loaded2 = await loadOneSkillProgram(
|
|
1852
|
+
dirPath,
|
|
1853
|
+
manifest,
|
|
1854
|
+
entryFile2,
|
|
1855
|
+
skillDef,
|
|
1856
|
+
programKey,
|
|
1857
|
+
extensions
|
|
1858
|
+
);
|
|
1859
|
+
result.push(loaded2);
|
|
1860
|
+
}
|
|
1861
|
+
return result;
|
|
1862
|
+
}
|
|
1863
|
+
const exts = extensions ?? DEFAULT_EXTENSIONS2;
|
|
1864
|
+
const files = await listSkillProgramFiles(dirPath, exts);
|
|
1865
|
+
if (files.length >= 2) {
|
|
1866
|
+
const result = [];
|
|
1867
|
+
for (let i = 0; i < files.length; i++) {
|
|
1868
|
+
const file = files[i];
|
|
1869
|
+
const programKey = i === 0 ? "default" : file.replace(/\.[^.]+$/, "");
|
|
1870
|
+
const loaded2 = await loadOneSkillProgram(
|
|
1871
|
+
dirPath,
|
|
1872
|
+
manifest,
|
|
1873
|
+
file,
|
|
1874
|
+
skillDef,
|
|
1875
|
+
programKey,
|
|
1876
|
+
extensions
|
|
1877
|
+
);
|
|
1878
|
+
result.push(loaded2);
|
|
1879
|
+
}
|
|
1880
|
+
return result;
|
|
1881
|
+
}
|
|
1882
|
+
const entryFile = manifest.entryPoint ?? files[0] ?? "handler";
|
|
1883
|
+
const loaded = await loadOneSkillProgram(
|
|
1884
|
+
dirPath,
|
|
1885
|
+
manifest,
|
|
1886
|
+
entryFile,
|
|
1887
|
+
skillDef,
|
|
1888
|
+
void 0,
|
|
1889
|
+
extensions
|
|
1890
|
+
);
|
|
1891
|
+
return [loaded];
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
// src/discovery/scan/DirectoryScanner.ts
|
|
1895
|
+
var DEFAULT_EXTENSIONS3 = [".js", ".mjs"];
|
|
1896
|
+
var DirectoryScanner = class {
|
|
1897
|
+
roots;
|
|
1898
|
+
extensions;
|
|
1899
|
+
onError;
|
|
1900
|
+
constructor(options) {
|
|
1901
|
+
const defaultNamespace = options.namespace ?? "dir";
|
|
1902
|
+
this.roots = options.roots.map((root) => {
|
|
1903
|
+
if (typeof root === "string") {
|
|
1904
|
+
return { path: root, namespace: defaultNamespace };
|
|
1905
|
+
}
|
|
1906
|
+
return {
|
|
1907
|
+
path: root.path,
|
|
1908
|
+
namespace: root.namespace ?? defaultNamespace
|
|
1909
|
+
};
|
|
1910
|
+
});
|
|
1911
|
+
this.extensions = options.extensions ?? DEFAULT_EXTENSIONS3;
|
|
1912
|
+
this.onError = options.onError;
|
|
1913
|
+
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Scan all root directories and return discovered ToolSpecs.
|
|
1916
|
+
* Errors in individual tool directories are reported via onError
|
|
1917
|
+
* and do not prevent other tools from loading.
|
|
1918
|
+
*/
|
|
1919
|
+
async scan() {
|
|
1920
|
+
const specs = [];
|
|
1921
|
+
for (const root of this.roots) {
|
|
1922
|
+
const rootSpecs = await this.scanRoot(root.path, root.namespace);
|
|
1923
|
+
specs.push(...rootSpecs);
|
|
1924
|
+
}
|
|
1925
|
+
return specs;
|
|
1926
|
+
}
|
|
1927
|
+
async scanRoot(rootPath, namespace) {
|
|
1928
|
+
return this.scanRecursive(rootPath, namespace);
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Recursively scan directories for tool definitions.
|
|
1932
|
+
* Directories can be detected via tool.json or inferred markers.
|
|
1933
|
+
*/
|
|
1934
|
+
async scanRecursive(dirPath, namespace) {
|
|
1935
|
+
const specs = [];
|
|
1936
|
+
let dirEntries;
|
|
1937
|
+
try {
|
|
1938
|
+
const entries = await promises.readdir(dirPath, { withFileTypes: true });
|
|
1939
|
+
dirEntries = entries.map((entry) => ({
|
|
1940
|
+
name: entry.name,
|
|
1941
|
+
isDirectory: entry.isDirectory()
|
|
1942
|
+
}));
|
|
1943
|
+
} catch (error) {
|
|
1944
|
+
this.onError?.(dirPath, error);
|
|
1945
|
+
return specs;
|
|
1946
|
+
}
|
|
1947
|
+
const dirName = path.basename(dirPath);
|
|
1948
|
+
try {
|
|
1949
|
+
const loadedSpecs = await this.loadToolDir(dirPath, dirName, namespace);
|
|
1950
|
+
if (loadedSpecs.length > 0) {
|
|
1951
|
+
specs.push(...loadedSpecs);
|
|
1952
|
+
}
|
|
1953
|
+
} catch (error) {
|
|
1954
|
+
this.onError?.(dirPath, error);
|
|
1955
|
+
}
|
|
1956
|
+
for (const entry of dirEntries) {
|
|
1957
|
+
if (!entry.isDirectory) {
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
const childPath = path.join(dirPath, entry.name);
|
|
1961
|
+
try {
|
|
1962
|
+
const childSpecs = await this.scanRecursive(childPath, namespace);
|
|
1963
|
+
specs.push(...childSpecs);
|
|
1964
|
+
} catch (error) {
|
|
1965
|
+
this.onError?.(childPath, error);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
return specs;
|
|
1969
|
+
}
|
|
1970
|
+
async loadToolDir(dirPath, dirName, namespace) {
|
|
1971
|
+
const manifestPath = path.join(dirPath, "tool.json");
|
|
1972
|
+
let manifestRaw;
|
|
1973
|
+
try {
|
|
1974
|
+
manifestRaw = await promises.readFile(manifestPath, "utf-8");
|
|
1975
|
+
} catch {
|
|
1976
|
+
const inferred = await this.inferManifest(dirPath, dirName);
|
|
1977
|
+
if (!inferred) {
|
|
1978
|
+
return [];
|
|
1979
|
+
}
|
|
1980
|
+
if (inferred.kind === "langchain") {
|
|
1981
|
+
if (inferred.entryPoint) {
|
|
1982
|
+
const loaded3 = await loadLangChainTool(dirPath, inferred, this.extensions);
|
|
1983
|
+
return [this.toToolSpec(loaded3, dirName, dirPath, namespace)];
|
|
1984
|
+
}
|
|
1985
|
+
return this.loadLangChainTools(dirPath, dirName, inferred, false, namespace);
|
|
1986
|
+
}
|
|
1987
|
+
if (inferred.kind === "skill") {
|
|
1988
|
+
const loadedList = await loadSkillTools(dirPath, inferred, this.extensions);
|
|
1989
|
+
return loadedList.map(
|
|
1990
|
+
(loaded3) => this.toToolSpec(loaded3, dirName, dirPath, namespace)
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
const loaded2 = await this.loadByKind(dirPath, inferred);
|
|
1994
|
+
return [this.toToolSpec(loaded2, dirName, dirPath, namespace)];
|
|
1995
|
+
}
|
|
1996
|
+
let manifest;
|
|
1997
|
+
try {
|
|
1998
|
+
manifest = JSON.parse(manifestRaw);
|
|
1999
|
+
} catch (err) {
|
|
2000
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
2001
|
+
dirPath,
|
|
2002
|
+
"manifest",
|
|
2003
|
+
"Invalid JSON in tool.json",
|
|
2004
|
+
err
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
if (!manifest.kind) {
|
|
2008
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
2009
|
+
dirPath,
|
|
2010
|
+
"manifest",
|
|
2011
|
+
`tool.json must have a "kind" field`
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
if (manifest.enabled === false) {
|
|
2015
|
+
return [];
|
|
2016
|
+
}
|
|
2017
|
+
if (manifest.kind === "langchain") {
|
|
2018
|
+
if (manifest.entryPoint) {
|
|
2019
|
+
const loaded2 = await loadLangChainTool(dirPath, manifest, this.extensions);
|
|
2020
|
+
return [this.toToolSpec(loaded2, dirName, dirPath, namespace)];
|
|
2021
|
+
}
|
|
2022
|
+
return this.loadLangChainTools(dirPath, dirName, manifest, true, namespace);
|
|
2023
|
+
}
|
|
2024
|
+
if (manifest.kind === "skill") {
|
|
2025
|
+
const loadedList = await loadSkillTools(dirPath, manifest, this.extensions);
|
|
2026
|
+
return loadedList.map(
|
|
2027
|
+
(loaded2) => this.toToolSpec(loaded2, dirName, dirPath, namespace)
|
|
2028
|
+
);
|
|
2029
|
+
}
|
|
2030
|
+
const loaded = await this.loadByKind(dirPath, manifest);
|
|
2031
|
+
return [this.toToolSpec(loaded, dirName, dirPath, namespace)];
|
|
2032
|
+
}
|
|
2033
|
+
async inferManifest(dirPath, dirName) {
|
|
2034
|
+
const hasSkill = await this.fileExists(path.join(dirPath, "SKILL.md"));
|
|
2035
|
+
const hasN8n = await this.fileExists(path.join(dirPath, "workflow.json"));
|
|
2036
|
+
const hasMcp = await this.fileExists(path.join(dirPath, "mcp.json"));
|
|
2037
|
+
const isLangchainDir = dirName === "langchain";
|
|
2038
|
+
const hasLangchain = isLangchainDir ? await this.hasLangchainFiles(dirPath) : dirName !== "skill" && await this.hasEntryPoint(dirPath, "index");
|
|
2039
|
+
const kinds = [
|
|
2040
|
+
hasSkill ? "skill" : null,
|
|
2041
|
+
hasN8n ? "n8n" : null,
|
|
2042
|
+
hasMcp ? "mcp" : null,
|
|
2043
|
+
hasLangchain ? "langchain" : null
|
|
2044
|
+
].filter(Boolean);
|
|
2045
|
+
if (kinds.length === 0) return null;
|
|
2046
|
+
if (kinds.length > 1) {
|
|
2047
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
2048
|
+
dirPath,
|
|
2049
|
+
"manifest",
|
|
2050
|
+
`Ambiguous tool kind (found ${kinds.join(", ")}). Add tool.json to disambiguate.`
|
|
2051
|
+
);
|
|
2052
|
+
}
|
|
2053
|
+
const kind = kinds[0];
|
|
2054
|
+
const manifest = { kind };
|
|
2055
|
+
if (kind === "n8n") manifest.entryPoint = "workflow.json";
|
|
2056
|
+
if (kind === "mcp") manifest.entryPoint = "mcp.json";
|
|
2057
|
+
if (kind === "langchain" && !isLangchainDir) manifest.entryPoint = "index";
|
|
2058
|
+
if (kind === "skill") manifest.entryPoint = "handler";
|
|
2059
|
+
return manifest;
|
|
2060
|
+
}
|
|
2061
|
+
async fileExists(path) {
|
|
2062
|
+
try {
|
|
2063
|
+
await promises.access(path);
|
|
2064
|
+
return true;
|
|
2065
|
+
} catch {
|
|
2066
|
+
return false;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
async hasEntryPoint(dirPath, baseName) {
|
|
2070
|
+
try {
|
|
2071
|
+
await resolveEntryPoint(dirPath, baseName, this.extensions);
|
|
2072
|
+
return true;
|
|
2073
|
+
} catch {
|
|
2074
|
+
return false;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
async hasLangchainFiles(dirPath) {
|
|
2078
|
+
const entryFiles = await this.listLangchainEntryFiles(dirPath);
|
|
2079
|
+
return entryFiles.length > 0;
|
|
2080
|
+
}
|
|
2081
|
+
async listLangchainEntryFiles(dirPath) {
|
|
2082
|
+
let entries;
|
|
2083
|
+
try {
|
|
2084
|
+
const dirEntries = await promises.readdir(dirPath, { withFileTypes: true });
|
|
2085
|
+
entries = dirEntries.map((entry) => ({
|
|
2086
|
+
name: entry.name,
|
|
2087
|
+
isFile: entry.isFile()
|
|
2088
|
+
}));
|
|
2089
|
+
} catch {
|
|
2090
|
+
return [];
|
|
2091
|
+
}
|
|
2092
|
+
return entries.filter((entry) => entry.isFile).map((entry) => entry.name).filter((name) => {
|
|
2093
|
+
if (name.startsWith(".") || name.startsWith("_")) return false;
|
|
2094
|
+
if (name.endsWith(".d.ts")) return false;
|
|
2095
|
+
if (name.includes(".test.") || name.includes(".spec.")) return false;
|
|
2096
|
+
return this.extensions.some((ext) => name.endsWith(ext));
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2099
|
+
async loadByKind(dirPath, manifest) {
|
|
2100
|
+
switch (manifest.kind) {
|
|
2101
|
+
case "mcp":
|
|
2102
|
+
return loadMCPTool(dirPath, manifest);
|
|
2103
|
+
case "langchain":
|
|
2104
|
+
return loadLangChainTool(dirPath, manifest, this.extensions);
|
|
2105
|
+
case "skill": {
|
|
2106
|
+
const list = await loadSkillTools(dirPath, manifest, this.extensions);
|
|
2107
|
+
if (list.length === 0) {
|
|
2108
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(dirPath, "load", "No skill programs loaded", new Error("empty"));
|
|
2109
|
+
}
|
|
2110
|
+
return list[0];
|
|
2111
|
+
}
|
|
2112
|
+
case "n8n":
|
|
2113
|
+
return chunkZ7TGIG77_cjs.loadN8nTool(dirPath, manifest);
|
|
2114
|
+
default:
|
|
2115
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
2116
|
+
dirPath,
|
|
2117
|
+
"manifest",
|
|
2118
|
+
`Unknown tool kind: "${manifest.kind}"`
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
async loadLangChainTools(dirPath, dirName, manifest, strict, namespace) {
|
|
2123
|
+
const entryFiles = await this.listLangchainEntryFiles(dirPath);
|
|
2124
|
+
if (entryFiles.length === 0) {
|
|
2125
|
+
if (strict) {
|
|
2126
|
+
throw new chunkZ7TGIG77_cjs.DiscoveryError(
|
|
2127
|
+
dirPath,
|
|
2128
|
+
"load",
|
|
2129
|
+
"No LangChain entry files found"
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
2132
|
+
return [];
|
|
2133
|
+
}
|
|
2134
|
+
const specs = [];
|
|
2135
|
+
const useDirNameForSingle = dirName !== "langchain";
|
|
2136
|
+
for (const entryFile of entryFiles) {
|
|
2137
|
+
const fileManifest = {
|
|
2138
|
+
...manifest,
|
|
2139
|
+
entryPoint: entryFile
|
|
2140
|
+
};
|
|
2141
|
+
try {
|
|
2142
|
+
const loaded = await loadLangChainTool(dirPath, fileManifest, this.extensions);
|
|
2143
|
+
const fileBase = path.basename(entryFile).replace(/\.[^.]+$/, "");
|
|
2144
|
+
const nameHint = entryFiles.length === 1 && useDirNameForSingle ? dirName : fileBase;
|
|
2145
|
+
specs.push(this.toToolSpec(loaded, nameHint, dirPath, namespace));
|
|
2146
|
+
} catch (error) {
|
|
2147
|
+
const err = error;
|
|
2148
|
+
if (err instanceof chunkZ7TGIG77_cjs.DiscoveryError && err.phase === "validate") {
|
|
2149
|
+
if (strict) {
|
|
2150
|
+
throw err;
|
|
2151
|
+
}
|
|
2152
|
+
continue;
|
|
2153
|
+
}
|
|
2154
|
+
this.onError?.(path.join(dirPath, entryFile), err);
|
|
2155
|
+
if (strict) {
|
|
2156
|
+
throw err;
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
return specs;
|
|
2161
|
+
}
|
|
2162
|
+
toToolSpec(loaded, dirName, dirPath, namespace) {
|
|
2163
|
+
const { manifest } = loaded;
|
|
2164
|
+
const kindDirNames = /* @__PURE__ */ new Set(["mcp", "langchain", "skill", "n8n"]);
|
|
2165
|
+
const parentName = path.basename(path.join(dirPath, ".."));
|
|
2166
|
+
const isKindDir = kindDirNames.has(dirName);
|
|
2167
|
+
const defaultDirName = isKindDir ? parentName : dirName;
|
|
2168
|
+
const inferredName = isKindDir ? `${namespace}/${defaultDirName}-${dirName}` : `${namespace}/${defaultDirName}`;
|
|
2169
|
+
const name = manifest.name ?? inferredName;
|
|
2170
|
+
const spec = this.buildBaseSpec(manifest, name, dirName);
|
|
2171
|
+
this.applyKindSpecificFields(spec, loaded, manifest, defaultDirName, namespace);
|
|
2172
|
+
return spec;
|
|
2173
|
+
}
|
|
2174
|
+
buildBaseSpec(manifest, name, dirName) {
|
|
2175
|
+
return {
|
|
2176
|
+
name,
|
|
2177
|
+
version: manifest.version ?? "1.0.0",
|
|
2178
|
+
kind: manifest.kind,
|
|
2179
|
+
description: manifest.description ?? `${manifest.kind} tool: ${dirName}`,
|
|
2180
|
+
tags: manifest.tags,
|
|
2181
|
+
inputSchema: manifest.inputSchema ?? {
|
|
2182
|
+
type: "object",
|
|
2183
|
+
additionalProperties: true
|
|
2184
|
+
},
|
|
2185
|
+
outputSchema: manifest.outputSchema ?? {
|
|
2186
|
+
type: "object",
|
|
2187
|
+
additionalProperties: true
|
|
2188
|
+
},
|
|
2189
|
+
capabilities: manifest.capabilities ?? [],
|
|
2190
|
+
costHints: manifest.costHints
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
applyKindSpecificFields(spec, loaded, manifest, defaultDirName, namespace) {
|
|
2194
|
+
switch (manifest.kind) {
|
|
2195
|
+
case "mcp":
|
|
2196
|
+
if (loaded.mcpConfig?.url) {
|
|
2197
|
+
spec.endpoint = loaded.mcpConfig.url;
|
|
2198
|
+
}
|
|
2199
|
+
spec.impl = loaded.mcpConfig;
|
|
2200
|
+
break;
|
|
2201
|
+
case "langchain":
|
|
2202
|
+
spec.impl = loaded.impl;
|
|
2203
|
+
if (!manifest.name) {
|
|
2204
|
+
const toolName = loaded.impl?.name;
|
|
2205
|
+
if (toolName) {
|
|
2206
|
+
spec.name = `${namespace}/${toolName}`;
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
if (!manifest.description) {
|
|
2210
|
+
const toolDescription = loaded.impl?.description;
|
|
2211
|
+
if (toolDescription) {
|
|
2212
|
+
spec.description = toolDescription;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
if (!manifest.inputSchema && loaded.impl) {
|
|
2216
|
+
const tool = loaded.impl;
|
|
2217
|
+
if (tool.schema) {
|
|
2218
|
+
spec.inputSchema = tool.schema;
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
break;
|
|
2222
|
+
case "skill": {
|
|
2223
|
+
if (loaded.skillDefinition) {
|
|
2224
|
+
spec.name = manifest.name ?? loaded.skillDefinition.frontmatter.name;
|
|
2225
|
+
spec.description = loaded.skillDefinition.frontmatter.description;
|
|
2226
|
+
if (loaded.programKey === "default") {
|
|
2227
|
+
spec.name = `${namespace}/${defaultDirName}`;
|
|
2228
|
+
} else if (loaded.programKey) {
|
|
2229
|
+
spec.name = `${namespace}/${defaultDirName}/${loaded.programKey}`;
|
|
2230
|
+
}
|
|
2231
|
+
const impl = loaded.impl;
|
|
2232
|
+
if (impl && typeof impl === "object" && typeof impl.invoke === "function") {
|
|
2233
|
+
if (impl.description != null && impl.description !== "") spec.description = impl.description;
|
|
2234
|
+
if (impl.schema != null && typeof impl.schema === "object") spec.inputSchema = impl.schema;
|
|
2235
|
+
}
|
|
2236
|
+
spec.impl = {
|
|
2237
|
+
...loaded.skillDefinition,
|
|
2238
|
+
handler: loaded.impl
|
|
2239
|
+
};
|
|
2240
|
+
} else {
|
|
2241
|
+
spec.impl = loaded.impl;
|
|
2242
|
+
}
|
|
2243
|
+
break;
|
|
2244
|
+
}
|
|
2245
|
+
case "n8n": {
|
|
2246
|
+
const workflow = loaded.workflowDef;
|
|
2247
|
+
if (workflow?.id) {
|
|
2248
|
+
spec.resourceId = String(workflow.id);
|
|
2249
|
+
}
|
|
2250
|
+
if (!manifest.description && workflow) {
|
|
2251
|
+
const workflowDesc = workflow.description ?? workflow.meta?.description ?? (typeof workflow.name === "string" ? workflow.name : void 0);
|
|
2252
|
+
if (workflowDesc) {
|
|
2253
|
+
spec.description = workflowDesc;
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
spec.impl = loaded.workflowDef;
|
|
2257
|
+
break;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
// src/discovery/MCPProcessManager.ts
|
|
2264
|
+
var MCPProcessManager = class {
|
|
2265
|
+
connections = /* @__PURE__ */ new Map();
|
|
2266
|
+
/**
|
|
2267
|
+
* Get connection info for an MCP tool based on its config.
|
|
2268
|
+
* Caches the result by tool name.
|
|
2269
|
+
*/
|
|
2270
|
+
getConnectionInfo(toolName, config) {
|
|
2271
|
+
const cached = this.connections.get(toolName);
|
|
2272
|
+
if (cached) return cached;
|
|
2273
|
+
const info = config.url ? { type: "url", url: config.url } : {
|
|
2274
|
+
type: "stdio",
|
|
2275
|
+
command: config.command,
|
|
2276
|
+
args: config.args,
|
|
2277
|
+
env: config.env,
|
|
2278
|
+
cwd: config.cwd
|
|
2279
|
+
};
|
|
2280
|
+
this.connections.set(toolName, info);
|
|
2281
|
+
return info;
|
|
2282
|
+
}
|
|
2283
|
+
/**
|
|
2284
|
+
* Remove cached connection info for a tool.
|
|
2285
|
+
*/
|
|
2286
|
+
remove(toolName) {
|
|
2287
|
+
return this.connections.delete(toolName);
|
|
2288
|
+
}
|
|
2289
|
+
/**
|
|
2290
|
+
* Get all registered tool names.
|
|
2291
|
+
*/
|
|
2292
|
+
getToolNames() {
|
|
2293
|
+
return [...this.connections.keys()];
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* Clear all cached connection info.
|
|
2297
|
+
*/
|
|
2298
|
+
dispose() {
|
|
2299
|
+
this.connections.clear();
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
|
|
2303
|
+
// src/mcp/MCPClientAdapter.ts
|
|
2304
|
+
function textFromCallToolResult(result) {
|
|
2305
|
+
if (result.content?.length) {
|
|
2306
|
+
const parts = result.content.filter((c) => c.type === "text" && c.text != null).map((c) => c.text);
|
|
2307
|
+
if (parts.length) return parts.join("\n");
|
|
2308
|
+
}
|
|
2309
|
+
if (result.toolResult !== void 0) return JSON.stringify(result.toolResult);
|
|
2310
|
+
return JSON.stringify(result);
|
|
2311
|
+
}
|
|
2312
|
+
var MCPClientAdapter = class {
|
|
2313
|
+
constructor(client) {
|
|
2314
|
+
this.client = client;
|
|
2315
|
+
}
|
|
2316
|
+
kind = "mcp";
|
|
2317
|
+
async invoke(spec, args, _ctx) {
|
|
2318
|
+
const params = args != null && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
2319
|
+
const result = await this.client.callTool({ name: spec.name, arguments: params });
|
|
2320
|
+
const text = textFromCallToolResult(result);
|
|
2321
|
+
let resultValue = text;
|
|
2322
|
+
try {
|
|
2323
|
+
resultValue = JSON.parse(text);
|
|
2324
|
+
} catch {
|
|
2325
|
+
}
|
|
2326
|
+
return { result: resultValue, raw: result };
|
|
2327
|
+
}
|
|
2328
|
+
};
|
|
2329
|
+
|
|
2330
|
+
// src/mcp/connectMCP.ts
|
|
2331
|
+
async function connectMCP(connectionInfo) {
|
|
2332
|
+
if (connectionInfo.type !== "stdio" || !connectionInfo.command) {
|
|
2333
|
+
throw new Error(
|
|
2334
|
+
`MCP stdio requires connectionInfo.type "stdio" and command; got type=${connectionInfo.type}`
|
|
2335
|
+
);
|
|
2336
|
+
}
|
|
2337
|
+
const { Client } = await import('@modelcontextprotocol/sdk/client');
|
|
2338
|
+
const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');
|
|
2339
|
+
const serverParams = {
|
|
2340
|
+
command: connectionInfo.command,
|
|
2341
|
+
args: connectionInfo.args ?? [],
|
|
2342
|
+
env: connectionInfo.env,
|
|
2343
|
+
cwd: connectionInfo.cwd
|
|
2344
|
+
};
|
|
2345
|
+
const transport = new StdioClientTransport(serverParams);
|
|
2346
|
+
const client = new Client({ name: "agent-tool", version: "1.0.0" });
|
|
2347
|
+
await client.connect(transport);
|
|
2348
|
+
return {
|
|
2349
|
+
client,
|
|
2350
|
+
transport
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
function mcpToolsToSpecs(tools) {
|
|
2354
|
+
return tools.map((t) => ({
|
|
2355
|
+
name: t.name,
|
|
2356
|
+
version: "1.0.0",
|
|
2357
|
+
kind: "mcp",
|
|
2358
|
+
description: t.description ?? `MCP tool: ${t.name}`,
|
|
2359
|
+
inputSchema: t.inputSchema ?? chunk3YLVPZRJ_cjs.DEFAULT_INPUT_SCHEMA,
|
|
2360
|
+
outputSchema: chunk3YLVPZRJ_cjs.DEFAULT_OUTPUT_SCHEMA,
|
|
2361
|
+
capabilities: []
|
|
2362
|
+
}));
|
|
2363
|
+
}
|
|
2364
|
+
async function registerMCPToolsFromConfig(runtime, registry, options = {}) {
|
|
2365
|
+
const configPath = options.configPath ?? process.env.MCP_CONFIG_PATH ?? path.join(process.cwd(), "mcp.json");
|
|
2366
|
+
const dirPath = path.dirname(configPath);
|
|
2367
|
+
const entryPoint = path.basename(configPath);
|
|
2368
|
+
const toolName = options.toolName ?? "mcp";
|
|
2369
|
+
const loaded = await loadMCPTool(dirPath, { kind: "mcp", name: toolName, entryPoint });
|
|
2370
|
+
if (!loaded.mcpConfig) {
|
|
2371
|
+
throw new Error("mcp.json must have command or url");
|
|
2372
|
+
}
|
|
2373
|
+
const manager = new MCPProcessManager();
|
|
2374
|
+
const connectionInfo = manager.getConnectionInfo(toolName, loaded.mcpConfig);
|
|
2375
|
+
const { client, transport } = await connectMCP(connectionInfo);
|
|
2376
|
+
const { tools } = await client.listTools();
|
|
2377
|
+
const specs = mcpToolsToSpecs(tools);
|
|
2378
|
+
registry.bulkRegister(specs);
|
|
2379
|
+
runtime.registerAdapter(new MCPClientAdapter(client));
|
|
2380
|
+
return { transport };
|
|
2381
|
+
}
|
|
2382
|
+
var DEFAULT_CTX_FACTORY = () => ({
|
|
2383
|
+
requestId: `lc-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
2384
|
+
taskId: `task-${Date.now()}`,
|
|
2385
|
+
permissions: [
|
|
2386
|
+
"read:web",
|
|
2387
|
+
"read:fs",
|
|
2388
|
+
"write:fs",
|
|
2389
|
+
"read:db",
|
|
2390
|
+
"write:db",
|
|
2391
|
+
"network",
|
|
2392
|
+
"workflow",
|
|
2393
|
+
"danger:destructive"
|
|
2394
|
+
]
|
|
2395
|
+
});
|
|
2396
|
+
var LangChainToolsHub = class {
|
|
2397
|
+
constructor(runtime, ctxFactory = DEFAULT_CTX_FACTORY) {
|
|
2398
|
+
this.runtime = runtime;
|
|
2399
|
+
this.ctxFactory = ctxFactory;
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* Returns all registered tools as LangChain tools (DynamicTool[]).
|
|
2403
|
+
* Each tool invokes the runtime through the full pipeline (policy, HITL, etc.).
|
|
2404
|
+
*/
|
|
2405
|
+
getLangChainTools() {
|
|
2406
|
+
const registry = this.runtime.getRegistry();
|
|
2407
|
+
const specs = registry.snapshot();
|
|
2408
|
+
return specs.map((spec) => this.specToLangChainTool(spec));
|
|
2409
|
+
}
|
|
2410
|
+
/**
|
|
2411
|
+
* Returns LangChain tools for a subset of tools (by name or query).
|
|
2412
|
+
*/
|
|
2413
|
+
getLangChainToolsForNames(toolNames) {
|
|
2414
|
+
const registry = this.runtime.getRegistry();
|
|
2415
|
+
const set = new Set(toolNames);
|
|
2416
|
+
const specs = registry.snapshot().filter((s) => set.has(s.name));
|
|
2417
|
+
return specs.map((spec) => this.specToLangChainTool(spec));
|
|
2418
|
+
}
|
|
2419
|
+
specToLangChainTool(spec) {
|
|
2420
|
+
const runtime = this.runtime;
|
|
2421
|
+
const ctxFactory = this.ctxFactory;
|
|
2422
|
+
return new tools.DynamicTool({
|
|
2423
|
+
name: spec.name,
|
|
2424
|
+
description: spec.description ?? `Tool: ${spec.name}`,
|
|
2425
|
+
func: async (input) => {
|
|
2426
|
+
const args = parseToolInput(input);
|
|
2427
|
+
const intent = {
|
|
2428
|
+
tool: spec.name,
|
|
2429
|
+
args,
|
|
2430
|
+
purpose: "langchain"
|
|
2431
|
+
};
|
|
2432
|
+
const ctx = ctxFactory();
|
|
2433
|
+
const result = await runtime.invoke(intent, ctx);
|
|
2434
|
+
if (result.ok) {
|
|
2435
|
+
return typeof result.result === "string" ? result.result : JSON.stringify(result.result);
|
|
2436
|
+
}
|
|
2437
|
+
const err = result.error;
|
|
2438
|
+
const message = err?.message ?? "Tool failed";
|
|
2439
|
+
const details = err?.details;
|
|
2440
|
+
return JSON.stringify(
|
|
2441
|
+
details != null ? { error: message, details } : { error: message }
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
});
|
|
2445
|
+
}
|
|
2446
|
+
};
|
|
2447
|
+
function parseToolInput(input) {
|
|
2448
|
+
const s = input.trim();
|
|
2449
|
+
if (!s) return {};
|
|
2450
|
+
try {
|
|
2451
|
+
const parsed = JSON.parse(s);
|
|
2452
|
+
if (parsed != null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2453
|
+
return parsed;
|
|
2454
|
+
}
|
|
2455
|
+
} catch {
|
|
2456
|
+
}
|
|
2457
|
+
const lastBrace = s.lastIndexOf("{");
|
|
2458
|
+
if (lastBrace !== -1) {
|
|
2459
|
+
try {
|
|
2460
|
+
const parsed = JSON.parse(s.slice(lastBrace));
|
|
2461
|
+
if (parsed != null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2462
|
+
return parsed;
|
|
2463
|
+
}
|
|
2464
|
+
} catch {
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
return {};
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
// src/llm/AgentLLMAdapter.ts
|
|
2471
|
+
function createAgentLLMAdapter(client) {
|
|
2472
|
+
if (!client.chatWithTools) {
|
|
2473
|
+
throw new Error(
|
|
2474
|
+
`agent-llm client "${client.id}" does not support chatWithTools; ReAct agent requires tool-calling.`
|
|
2475
|
+
);
|
|
2476
|
+
}
|
|
2477
|
+
return new AgentLLMAdapter(client);
|
|
2478
|
+
}
|
|
2479
|
+
var AgentLLMAdapter = class {
|
|
2480
|
+
constructor(client) {
|
|
2481
|
+
this.client = client;
|
|
2482
|
+
}
|
|
2483
|
+
async chat(messages, options) {
|
|
2484
|
+
const result = await this.client.chat(messages);
|
|
2485
|
+
return { content: result.content, raw: result.usage ?? {} };
|
|
2486
|
+
}
|
|
2487
|
+
async chatWithTools(messages, tools, options) {
|
|
2488
|
+
const agentLlmTools = tools.map((t) => ({
|
|
2489
|
+
type: "function",
|
|
2490
|
+
function: {
|
|
2491
|
+
name: t.function.name,
|
|
2492
|
+
description: t.function.description,
|
|
2493
|
+
parameters: t.function.parameters
|
|
2494
|
+
}
|
|
2495
|
+
}));
|
|
2496
|
+
const result = await this.client.chatWithTools(messages, agentLlmTools, {
|
|
2497
|
+
timeoutMs: options?.timeoutMs
|
|
2498
|
+
});
|
|
2499
|
+
return {
|
|
2500
|
+
message: result.message,
|
|
2501
|
+
raw: result.usage ?? {}
|
|
2502
|
+
};
|
|
2503
|
+
}
|
|
2504
|
+
};
|
|
2505
|
+
var DEFAULT_ALLOWED_HOSTS = ["*"];
|
|
2506
|
+
var DEFAULT_SANDBOX_ROOT = process.cwd();
|
|
2507
|
+
function loadAgentConfig(configPath) {
|
|
2508
|
+
const abs = path.resolve(configPath);
|
|
2509
|
+
const raw = fs.readFileSync(abs, "utf8");
|
|
2510
|
+
const parsed = yaml__default.default.load(raw);
|
|
2511
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
2512
|
+
return parsed;
|
|
2513
|
+
}
|
|
2514
|
+
function createRuntimeWithTools(options) {
|
|
2515
|
+
const sandboxRoot = options.sandboxRoot ?? DEFAULT_SANDBOX_ROOT;
|
|
2516
|
+
const allowedHosts = options.allowedHosts ?? DEFAULT_ALLOWED_HOSTS;
|
|
2517
|
+
const registry = new ToolRegistry();
|
|
2518
|
+
const coreConfig = { sandboxRoot, allowedHosts };
|
|
2519
|
+
const coreAdapter = agentToolBuiltinTools.registerCoreTools(registry, coreConfig);
|
|
2520
|
+
const exampleConfig = { sandboxRoot, allowedHosts };
|
|
2521
|
+
const exampleAdapter = agentToolExampleTools.registerExampleTools(registry, exampleConfig);
|
|
2522
|
+
const runtime = new PTCRuntime({ registry });
|
|
2523
|
+
runtime.registerAdapter(coreAdapter);
|
|
2524
|
+
runtime.registerAdapter(exampleAdapter);
|
|
2525
|
+
return { runtime, coreAdapter, exampleAdapter };
|
|
2526
|
+
}
|
|
2527
|
+
async function runAgent(configPath, task, options) {
|
|
2528
|
+
const config = loadAgentConfig(configPath);
|
|
2529
|
+
const llmSection = config.llm ?? null;
|
|
2530
|
+
const registry = agentLlm.createLLMRegistry({ llmSection });
|
|
2531
|
+
const defaultId = registry.defaultId();
|
|
2532
|
+
if (!defaultId) {
|
|
2533
|
+
throw new Error(
|
|
2534
|
+
"agent.yaml has no llm section or no LLM instances. Add an llm block (see agent.yaml.example)."
|
|
2535
|
+
);
|
|
2536
|
+
}
|
|
2537
|
+
const client = registry.get(defaultId);
|
|
2538
|
+
if (!client) {
|
|
2539
|
+
throw new Error(`LLM "${defaultId}" not found in registry.`);
|
|
2540
|
+
}
|
|
2541
|
+
const adapter = createAgentLLMAdapter(client);
|
|
2542
|
+
const sandboxRoot = options?.sandboxRoot ?? DEFAULT_SANDBOX_ROOT;
|
|
2543
|
+
const allowedHosts = options?.allowedHosts ?? DEFAULT_ALLOWED_HOSTS;
|
|
2544
|
+
const { runtime } = createRuntimeWithTools({ sandboxRoot, allowedHosts });
|
|
2545
|
+
const ctxFactory = () => ({
|
|
2546
|
+
requestId: `run-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
2547
|
+
taskId: `task-${Date.now()}`,
|
|
2548
|
+
permissions: [
|
|
2549
|
+
"read:web",
|
|
2550
|
+
"read:fs",
|
|
2551
|
+
"write:fs",
|
|
2552
|
+
"read:db",
|
|
2553
|
+
"write:db",
|
|
2554
|
+
"network",
|
|
2555
|
+
"workflow",
|
|
2556
|
+
"danger:destructive",
|
|
2557
|
+
"exec"
|
|
2558
|
+
]
|
|
2559
|
+
});
|
|
2560
|
+
const hub = {
|
|
2561
|
+
getRegistry: () => runtime.getRegistry(),
|
|
2562
|
+
invokeTool: async (name, args) => {
|
|
2563
|
+
const ctx = ctxFactory();
|
|
2564
|
+
const result = await runtime.invoke(
|
|
2565
|
+
{ tool: name, args, purpose: "langchain" },
|
|
2566
|
+
ctx
|
|
2567
|
+
);
|
|
2568
|
+
return result;
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
2571
|
+
const agent = new chunkP3UEAZHK_cjs.ReActAgent(adapter, hub);
|
|
2572
|
+
const toolNames = Array.isArray(config.tools) && config.tools.length > 0 ? normalizeToolList(config.tools) : void 0;
|
|
2573
|
+
return agent.run(task, {
|
|
2574
|
+
systemPrompt: options?.systemPrompt,
|
|
2575
|
+
toolNames,
|
|
2576
|
+
...options?.runOptions
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
var PLACEHOLDER = "__REPORT_DATA__";
|
|
2580
|
+
function getReportTemplateDir() {
|
|
2581
|
+
if (typeof __dirname !== "undefined") return __dirname;
|
|
2582
|
+
return path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
2583
|
+
}
|
|
2584
|
+
var DEFAULT_REPORT_TEMPLATE_PATH = (() => {
|
|
2585
|
+
const dir = getReportTemplateDir();
|
|
2586
|
+
const nextToModule = path.join(dir, "agent-report-template.html");
|
|
2587
|
+
if (fs.existsSync(nextToModule)) return nextToModule;
|
|
2588
|
+
return path.join(dir, "report", "agent-report-template.html");
|
|
2589
|
+
})();
|
|
2590
|
+
function generateAgentReport(data, options) {
|
|
2591
|
+
const templatePath = options.templatePath ?? DEFAULT_REPORT_TEMPLATE_PATH;
|
|
2592
|
+
const template = fs.readFileSync(templatePath, "utf8");
|
|
2593
|
+
let json = JSON.stringify(data);
|
|
2594
|
+
json = json.replace(/<\/script/gi, "<\\/script");
|
|
2595
|
+
const html = template.replace(PLACEHOLDER, json);
|
|
2596
|
+
const outPath = path.resolve(options.outputPath);
|
|
2597
|
+
fs.writeFileSync(outPath, html, "utf8");
|
|
2598
|
+
return outPath;
|
|
2599
|
+
}
|
|
2600
|
+
function serializeStepOutput(value) {
|
|
2601
|
+
if (value === null || value === void 0) return value;
|
|
2602
|
+
if (typeof value !== "object") return value;
|
|
2603
|
+
const v = value;
|
|
2604
|
+
const out = {};
|
|
2605
|
+
if (Array.isArray(v.messages)) {
|
|
2606
|
+
out.messages = v.messages.map((m) => serializeMessage(m));
|
|
2607
|
+
}
|
|
2608
|
+
if (v.__state__ && typeof v.__state__ === "object") {
|
|
2609
|
+
const state = v.__state__;
|
|
2610
|
+
if (Array.isArray(state.messages)) {
|
|
2611
|
+
out.messages = state.messages.map((m) => serializeMessage(m));
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
if (Object.keys(out).length === 0) {
|
|
2615
|
+
try {
|
|
2616
|
+
return JSON.parse(JSON.stringify(value));
|
|
2617
|
+
} catch {
|
|
2618
|
+
return { _raw: String(value) };
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
return out;
|
|
2622
|
+
}
|
|
2623
|
+
function serializeMessage(msg) {
|
|
2624
|
+
if (msg === null || typeof msg !== "object") return { content: String(msg) };
|
|
2625
|
+
const m = msg;
|
|
2626
|
+
const out = {
|
|
2627
|
+
type: m.type ?? "message",
|
|
2628
|
+
content: m.content ?? ""
|
|
2629
|
+
};
|
|
2630
|
+
if (m.tool_calls) out.tool_calls = m.tool_calls;
|
|
2631
|
+
if (m.usage_metadata) out.usage_metadata = m.usage_metadata;
|
|
2632
|
+
if (m.name) out.name = m.name;
|
|
2633
|
+
return out;
|
|
2634
|
+
}
|
|
2635
|
+
function formatStepProgress(step) {
|
|
2636
|
+
const parts = [` [${step.stepIndex}] ${step.node}`];
|
|
2637
|
+
if (step.toolCalls?.length) {
|
|
2638
|
+
const toolParts = step.toolCalls.map((tc) => {
|
|
2639
|
+
const argStr = tc.args && Object.keys(tc.args).length ? " " + JSON.stringify(tc.args) : "";
|
|
2640
|
+
return `${tc.name}${argStr}`;
|
|
2641
|
+
});
|
|
2642
|
+
parts.push(`tools \u2192 ${toolParts.join("; ")}`);
|
|
2643
|
+
}
|
|
2644
|
+
if (step.usage) {
|
|
2645
|
+
const inN = step.usage.input_tokens ?? 0;
|
|
2646
|
+
const outN = step.usage.output_tokens ?? 0;
|
|
2647
|
+
const total = step.usage.total_tokens ?? inN + outN;
|
|
2648
|
+
parts.push(`tokens: in=${inN} out=${outN} total=${total}`);
|
|
2649
|
+
}
|
|
2650
|
+
return parts.join(" | ");
|
|
2651
|
+
}
|
|
2652
|
+
function buildStep(stepIndex, node, value) {
|
|
2653
|
+
const v = value;
|
|
2654
|
+
const messages = v?.messages ?? v?.__state__?.messages;
|
|
2655
|
+
const lastMsg = Array.isArray(messages) ? messages.slice(-1)[0] : void 0;
|
|
2656
|
+
const last = lastMsg;
|
|
2657
|
+
const toolCalls = last?.tool_calls;
|
|
2658
|
+
const usage = last?.usage_metadata;
|
|
2659
|
+
return {
|
|
2660
|
+
stepIndex,
|
|
2661
|
+
node,
|
|
2662
|
+
input: void 0,
|
|
2663
|
+
// filled later from previous step's output
|
|
2664
|
+
output: serializeStepOutput(value),
|
|
2665
|
+
toolCalls: toolCalls?.map((tc) => ({ name: tc.name, args: tc.args ?? {} })),
|
|
2666
|
+
usage: usage ? {
|
|
2667
|
+
input_tokens: usage.input_tokens ?? usage.prompt_tokens,
|
|
2668
|
+
output_tokens: usage.output_tokens ?? usage.completion_tokens,
|
|
2669
|
+
total_tokens: usage.total_tokens
|
|
2670
|
+
} : void 0
|
|
2671
|
+
};
|
|
2672
|
+
}
|
|
2673
|
+
function isAiMessage(msg) {
|
|
2674
|
+
if (!msg || typeof msg !== "object") return false;
|
|
2675
|
+
if (msg.type === "ai" || msg.type === "assistant") return true;
|
|
2676
|
+
const getType = msg._getType;
|
|
2677
|
+
if (typeof getType === "function" && getType.call(msg) === "ai") return true;
|
|
2678
|
+
const lcId = msg.lc_id;
|
|
2679
|
+
if (Array.isArray(lcId) && lcId[lcId.length - 1] === "AIMessage") return true;
|
|
2680
|
+
return false;
|
|
2681
|
+
}
|
|
2682
|
+
function getMessageContent(msg) {
|
|
2683
|
+
if (!msg || typeof msg !== "object") return "";
|
|
2684
|
+
let c = msg.content ?? msg.kwargs?.content;
|
|
2685
|
+
if (typeof c === "string") return c;
|
|
2686
|
+
if (Array.isArray(c)) {
|
|
2687
|
+
return c.map((p) => p && typeof p === "object" && "text" in p ? p.text : String(p)).filter(Boolean).join("");
|
|
2688
|
+
}
|
|
2689
|
+
return "";
|
|
2690
|
+
}
|
|
2691
|
+
function extractAiContent(msg) {
|
|
2692
|
+
if (!msg || !isAiMessage(msg)) return "";
|
|
2693
|
+
return getMessageContent(msg);
|
|
2694
|
+
}
|
|
2695
|
+
async function collectStreamSteps(stream, options) {
|
|
2696
|
+
const steps = [];
|
|
2697
|
+
let stepIndex = 0;
|
|
2698
|
+
let lastAiContent = "";
|
|
2699
|
+
for await (const chunk of stream) {
|
|
2700
|
+
const nodeMap = Array.isArray(chunk) && chunk.length >= 2 && typeof chunk[chunk.length - 1] === "object" && chunk[chunk.length - 1] !== null && !Array.isArray(chunk[chunk.length - 1]) ? chunk[chunk.length - 1] : chunk;
|
|
2701
|
+
for (const [node, value] of Object.entries(nodeMap)) {
|
|
2702
|
+
if (value === void 0 || value === null) continue;
|
|
2703
|
+
stepIndex += 1;
|
|
2704
|
+
const step = buildStep(stepIndex, node, value);
|
|
2705
|
+
steps.push(step);
|
|
2706
|
+
options?.onStep?.(step);
|
|
2707
|
+
const v = value;
|
|
2708
|
+
const messages = v?.messages ?? v?.__state__?.messages;
|
|
2709
|
+
const lastMsg = Array.isArray(messages) ? messages.slice(-1)[0] : void 0;
|
|
2710
|
+
let content = extractAiContent(lastMsg);
|
|
2711
|
+
if (!content && node === "agent" && lastMsg && typeof lastMsg === "object" && !("tool_call_id" in lastMsg && lastMsg.tool_call_id)) {
|
|
2712
|
+
content = getMessageContent(lastMsg);
|
|
2713
|
+
}
|
|
2714
|
+
if (content) lastAiContent = content;
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
for (let i = 1; i < steps.length; i++) {
|
|
2718
|
+
const prev = steps[i - 1];
|
|
2719
|
+
const curr = steps[i];
|
|
2720
|
+
if (prev && curr) curr.input = prev.output;
|
|
2721
|
+
}
|
|
2722
|
+
const first = steps[0];
|
|
2723
|
+
if (first && first.input === void 0) {
|
|
2724
|
+
first.input = { __info: "Initial state (see System and User Prompt above)" };
|
|
2725
|
+
}
|
|
2726
|
+
return { steps, lastAiContent };
|
|
2727
|
+
}
|
|
2728
|
+
async function runAgentWithReport(agent, input, options) {
|
|
2729
|
+
const stream = await agent.stream(input, options.streamConfig ?? {});
|
|
2730
|
+
const { steps, lastAiContent } = await collectStreamSteps(stream, { onStep: options.onStep });
|
|
2731
|
+
let reportMarkdown = lastAiContent;
|
|
2732
|
+
if (options.reportPath) {
|
|
2733
|
+
const reportPath = path.resolve(options.reportPath);
|
|
2734
|
+
if (fs.existsSync(reportPath)) {
|
|
2735
|
+
reportMarkdown = fs.readFileSync(reportPath, "utf8");
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
let htmlPath;
|
|
2739
|
+
if (options.htmlReportPath) {
|
|
2740
|
+
htmlPath = generateAgentReport(
|
|
2741
|
+
{
|
|
2742
|
+
systemPrompt: options.systemPrompt,
|
|
2743
|
+
userPrompt: options.userPrompt,
|
|
2744
|
+
reportMarkdown,
|
|
2745
|
+
steps
|
|
2746
|
+
},
|
|
2747
|
+
{
|
|
2748
|
+
outputPath: options.htmlReportPath,
|
|
2749
|
+
templatePath: options.templatePath
|
|
2750
|
+
}
|
|
2751
|
+
);
|
|
2752
|
+
}
|
|
2753
|
+
return { steps, reportMarkdown, htmlPath };
|
|
2754
|
+
}
|
|
2755
|
+
async function writeReportFromStream(stream, options) {
|
|
2756
|
+
const { steps, lastAiContent } = await collectStreamSteps(stream, { onStep: options.onStep });
|
|
2757
|
+
let reportMarkdown = lastAiContent;
|
|
2758
|
+
if (options.reportPath) {
|
|
2759
|
+
const reportPath = path.resolve(options.reportPath);
|
|
2760
|
+
if (fs.existsSync(reportPath)) {
|
|
2761
|
+
reportMarkdown = fs.readFileSync(reportPath, "utf8");
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
let htmlPath;
|
|
2765
|
+
if (options.htmlReportPath) {
|
|
2766
|
+
htmlPath = generateAgentReport(
|
|
2767
|
+
{
|
|
2768
|
+
systemPrompt: options.systemPrompt,
|
|
2769
|
+
userPrompt: options.userPrompt,
|
|
2770
|
+
reportMarkdown,
|
|
2771
|
+
steps
|
|
2772
|
+
},
|
|
2773
|
+
{
|
|
2774
|
+
outputPath: options.htmlReportPath,
|
|
2775
|
+
templatePath: options.templatePath
|
|
2776
|
+
}
|
|
2777
|
+
);
|
|
2778
|
+
}
|
|
2779
|
+
return { steps, reportMarkdown, htmlPath };
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
Object.defineProperty(exports, "DiscoveryError", {
|
|
2783
|
+
enumerable: true,
|
|
2784
|
+
get: function () { return chunkZ7TGIG77_cjs.DiscoveryError; }
|
|
2785
|
+
});
|
|
2786
|
+
Object.defineProperty(exports, "SkillManifestError", {
|
|
2787
|
+
enumerable: true,
|
|
2788
|
+
get: function () { return chunkZ7TGIG77_cjs.SkillManifestError; }
|
|
2789
|
+
});
|
|
2790
|
+
Object.defineProperty(exports, "buildFunctionToTool", {
|
|
2791
|
+
enumerable: true,
|
|
2792
|
+
get: function () { return chunkZ7TGIG77_cjs.buildFunctionToTool; }
|
|
2793
|
+
});
|
|
2794
|
+
Object.defineProperty(exports, "buildMcpPackage", {
|
|
2795
|
+
enumerable: true,
|
|
2796
|
+
get: function () { return chunkZ7TGIG77_cjs.buildMcpPackage; }
|
|
2797
|
+
});
|
|
2798
|
+
Object.defineProperty(exports, "initProject", {
|
|
2799
|
+
enumerable: true,
|
|
2800
|
+
get: function () { return chunkZ7TGIG77_cjs.initProject; }
|
|
2801
|
+
});
|
|
2802
|
+
Object.defineProperty(exports, "loadSkillDefinition", {
|
|
2803
|
+
enumerable: true,
|
|
2804
|
+
get: function () { return chunkZ7TGIG77_cjs.loadSkillDefinition; }
|
|
2805
|
+
});
|
|
2806
|
+
Object.defineProperty(exports, "parseSkillMd", {
|
|
2807
|
+
enumerable: true,
|
|
2808
|
+
get: function () { return chunkZ7TGIG77_cjs.parseSkillMd; }
|
|
2809
|
+
});
|
|
2810
|
+
Object.defineProperty(exports, "runGeneratedMCP", {
|
|
2811
|
+
enumerable: true,
|
|
2812
|
+
get: function () { return chunkZ7TGIG77_cjs.runGeneratedMCP; }
|
|
2813
|
+
});
|
|
2814
|
+
Object.defineProperty(exports, "runMcpServer", {
|
|
2815
|
+
enumerable: true,
|
|
2816
|
+
get: function () { return chunkZ7TGIG77_cjs.runMcpServer; }
|
|
2817
|
+
});
|
|
2818
|
+
Object.defineProperty(exports, "scanForTools", {
|
|
2819
|
+
enumerable: true,
|
|
2820
|
+
get: function () { return chunkZ7TGIG77_cjs.scanForTools; }
|
|
2821
|
+
});
|
|
2822
|
+
Object.defineProperty(exports, "scanSkillResources", {
|
|
2823
|
+
enumerable: true,
|
|
2824
|
+
get: function () { return chunkZ7TGIG77_cjs.scanSkillResources; }
|
|
2825
|
+
});
|
|
2826
|
+
Object.defineProperty(exports, "validateFrontmatter", {
|
|
2827
|
+
enumerable: true,
|
|
2828
|
+
get: function () { return chunkZ7TGIG77_cjs.validateFrontmatter; }
|
|
2829
|
+
});
|
|
2830
|
+
Object.defineProperty(exports, "DEFAULT_INPUT_SCHEMA", {
|
|
2831
|
+
enumerable: true,
|
|
2832
|
+
get: function () { return chunk3YLVPZRJ_cjs.DEFAULT_INPUT_SCHEMA; }
|
|
2833
|
+
});
|
|
2834
|
+
Object.defineProperty(exports, "DEFAULT_OUTPUT_SCHEMA", {
|
|
2835
|
+
enumerable: true,
|
|
2836
|
+
get: function () { return chunk3YLVPZRJ_cjs.DEFAULT_OUTPUT_SCHEMA; }
|
|
2837
|
+
});
|
|
2838
|
+
Object.defineProperty(exports, "createToolSpec", {
|
|
2839
|
+
enumerable: true,
|
|
2840
|
+
get: function () { return chunk3YLVPZRJ_cjs.createToolSpec; }
|
|
2841
|
+
});
|
|
2842
|
+
Object.defineProperty(exports, "LangChainAgent", {
|
|
2843
|
+
enumerable: true,
|
|
2844
|
+
get: function () { return chunkP3UEAZHK_cjs.ReActAgent; }
|
|
2845
|
+
});
|
|
2846
|
+
Object.defineProperty(exports, "OpenAICompatibleClient", {
|
|
2847
|
+
enumerable: true,
|
|
2848
|
+
get: function () { return chunkP3UEAZHK_cjs.OpenAICompatibleClient; }
|
|
2849
|
+
});
|
|
2850
|
+
Object.defineProperty(exports, "ReActAgent", {
|
|
2851
|
+
enumerable: true,
|
|
2852
|
+
get: function () { return chunkP3UEAZHK_cjs.ReActAgent; }
|
|
2853
|
+
});
|
|
2854
|
+
Object.defineProperty(exports, "createOpenAICompatibleClient", {
|
|
2855
|
+
enumerable: true,
|
|
2856
|
+
get: function () { return chunkP3UEAZHK_cjs.createOpenAICompatibleClient; }
|
|
2857
|
+
});
|
|
2858
|
+
Object.defineProperty(exports, "CoreAdapter", {
|
|
2859
|
+
enumerable: true,
|
|
2860
|
+
get: function () { return agentToolBuiltinTools.CoreAdapter; }
|
|
2861
|
+
});
|
|
2862
|
+
Object.defineProperty(exports, "DEFAULT_CORE_TOOLS_CONFIG", {
|
|
2863
|
+
enumerable: true,
|
|
2864
|
+
get: function () { return agentToolBuiltinTools.DEFAULT_CORE_TOOLS_CONFIG; }
|
|
2865
|
+
});
|
|
2866
|
+
Object.defineProperty(exports, "deletePathSpec", {
|
|
2867
|
+
enumerable: true,
|
|
2868
|
+
get: function () { return agentToolBuiltinTools.deletePathSpec; }
|
|
2869
|
+
});
|
|
2870
|
+
Object.defineProperty(exports, "downloadFileSpec", {
|
|
2871
|
+
enumerable: true,
|
|
2872
|
+
get: function () { return agentToolBuiltinTools.downloadFileSpec; }
|
|
2873
|
+
});
|
|
2874
|
+
Object.defineProperty(exports, "duckduckgoSearchSpec", {
|
|
2875
|
+
enumerable: true,
|
|
2876
|
+
get: function () { return agentToolBuiltinTools.duckduckgoSearchSpec; }
|
|
2877
|
+
});
|
|
2878
|
+
Object.defineProperty(exports, "fetchJsonSpec", {
|
|
2879
|
+
enumerable: true,
|
|
2880
|
+
get: function () { return agentToolBuiltinTools.fetchJsonSpec; }
|
|
2881
|
+
});
|
|
2882
|
+
Object.defineProperty(exports, "fetchPageMainContentSpec", {
|
|
2883
|
+
enumerable: true,
|
|
2884
|
+
get: function () { return agentToolBuiltinTools.fetchPageMainContentSpec; }
|
|
2885
|
+
});
|
|
2886
|
+
Object.defineProperty(exports, "fetchTextSpec", {
|
|
2887
|
+
enumerable: true,
|
|
2888
|
+
get: function () { return agentToolBuiltinTools.fetchTextSpec; }
|
|
2889
|
+
});
|
|
2890
|
+
Object.defineProperty(exports, "hashTextSpec", {
|
|
2891
|
+
enumerable: true,
|
|
2892
|
+
get: function () { return agentToolBuiltinTools.hashTextSpec; }
|
|
2893
|
+
});
|
|
2894
|
+
Object.defineProperty(exports, "headSpec", {
|
|
2895
|
+
enumerable: true,
|
|
2896
|
+
get: function () { return agentToolBuiltinTools.headSpec; }
|
|
2897
|
+
});
|
|
2898
|
+
Object.defineProperty(exports, "isIpInBlockedCidrs", {
|
|
2899
|
+
enumerable: true,
|
|
2900
|
+
get: function () { return agentToolBuiltinTools.isIpInBlockedCidrs; }
|
|
2901
|
+
});
|
|
2902
|
+
Object.defineProperty(exports, "jsonSelectSpec", {
|
|
2903
|
+
enumerable: true,
|
|
2904
|
+
get: function () { return agentToolBuiltinTools.jsonSelectSpec; }
|
|
2905
|
+
});
|
|
2906
|
+
Object.defineProperty(exports, "listDirSpec", {
|
|
2907
|
+
enumerable: true,
|
|
2908
|
+
get: function () { return agentToolBuiltinTools.listDirSpec; }
|
|
2909
|
+
});
|
|
2910
|
+
Object.defineProperty(exports, "nowSpec", {
|
|
2911
|
+
enumerable: true,
|
|
2912
|
+
get: function () { return agentToolBuiltinTools.nowSpec; }
|
|
2913
|
+
});
|
|
2914
|
+
Object.defineProperty(exports, "readTextSpec", {
|
|
2915
|
+
enumerable: true,
|
|
2916
|
+
get: function () { return agentToolBuiltinTools.readTextSpec; }
|
|
2917
|
+
});
|
|
2918
|
+
Object.defineProperty(exports, "registerCoreTools", {
|
|
2919
|
+
enumerable: true,
|
|
2920
|
+
get: function () { return agentToolBuiltinTools.registerCoreTools; }
|
|
2921
|
+
});
|
|
2922
|
+
Object.defineProperty(exports, "resolveSandboxedPath", {
|
|
2923
|
+
enumerable: true,
|
|
2924
|
+
get: function () { return agentToolBuiltinTools.resolveSandboxedPath; }
|
|
2925
|
+
});
|
|
2926
|
+
Object.defineProperty(exports, "runCommandSpec", {
|
|
2927
|
+
enumerable: true,
|
|
2928
|
+
get: function () { return agentToolBuiltinTools.runCommandSpec; }
|
|
2929
|
+
});
|
|
2930
|
+
Object.defineProperty(exports, "searchTextSpec", {
|
|
2931
|
+
enumerable: true,
|
|
2932
|
+
get: function () { return agentToolBuiltinTools.searchTextSpec; }
|
|
2933
|
+
});
|
|
2934
|
+
Object.defineProperty(exports, "sha256Spec", {
|
|
2935
|
+
enumerable: true,
|
|
2936
|
+
get: function () { return agentToolBuiltinTools.sha256Spec; }
|
|
2937
|
+
});
|
|
2938
|
+
Object.defineProperty(exports, "templateRenderSpec", {
|
|
2939
|
+
enumerable: true,
|
|
2940
|
+
get: function () { return agentToolBuiltinTools.templateRenderSpec; }
|
|
2941
|
+
});
|
|
2942
|
+
Object.defineProperty(exports, "truncateSpec", {
|
|
2943
|
+
enumerable: true,
|
|
2944
|
+
get: function () { return agentToolBuiltinTools.truncateSpec; }
|
|
2945
|
+
});
|
|
2946
|
+
Object.defineProperty(exports, "validateUrl", {
|
|
2947
|
+
enumerable: true,
|
|
2948
|
+
get: function () { return agentToolBuiltinTools.validateUrl; }
|
|
2949
|
+
});
|
|
2950
|
+
Object.defineProperty(exports, "writeTextSpec", {
|
|
2951
|
+
enumerable: true,
|
|
2952
|
+
get: function () { return agentToolBuiltinTools.writeTextSpec; }
|
|
2953
|
+
});
|
|
2954
|
+
Object.defineProperty(exports, "ExampleAdapter", {
|
|
2955
|
+
enumerable: true,
|
|
2956
|
+
get: function () { return agentToolExampleTools.ExampleAdapter; }
|
|
2957
|
+
});
|
|
2958
|
+
Object.defineProperty(exports, "isHostAllowed", {
|
|
2959
|
+
enumerable: true,
|
|
2960
|
+
get: function () { return agentToolExampleTools.isHostAllowed; }
|
|
2961
|
+
});
|
|
2962
|
+
Object.defineProperty(exports, "registerExampleTools", {
|
|
2963
|
+
enumerable: true,
|
|
2964
|
+
get: function () { return agentToolExampleTools.registerExampleTools; }
|
|
2965
|
+
});
|
|
2966
|
+
Object.defineProperty(exports, "yahooChartSpec", {
|
|
2967
|
+
enumerable: true,
|
|
2968
|
+
get: function () { return agentToolExampleTools.yahooChartSpec; }
|
|
2969
|
+
});
|
|
2970
|
+
Object.defineProperty(exports, "yahooQuoteSpec", {
|
|
2971
|
+
enumerable: true,
|
|
2972
|
+
get: function () { return agentToolExampleTools.yahooQuoteSpec; }
|
|
2973
|
+
});
|
|
2974
|
+
exports.BudgetManager = BudgetManager;
|
|
2975
|
+
exports.DirectoryScanner = DirectoryScanner;
|
|
2976
|
+
exports.EventLog = EventLog;
|
|
2977
|
+
exports.LangChainToolsHub = LangChainToolsHub;
|
|
2978
|
+
exports.MCPClientAdapter = MCPClientAdapter;
|
|
2979
|
+
exports.MCPProcessManager = MCPProcessManager;
|
|
2980
|
+
exports.Metrics = Metrics;
|
|
2981
|
+
exports.PTCRuntime = PTCRuntime;
|
|
2982
|
+
exports.PolicyDeniedError = PolicyDeniedError;
|
|
2983
|
+
exports.PolicyEngine = PolicyEngine;
|
|
2984
|
+
exports.SchemaValidationError = SchemaValidationError;
|
|
2985
|
+
exports.SchemaValidator = SchemaValidator;
|
|
2986
|
+
exports.ToolRegistry = ToolRegistry;
|
|
2987
|
+
exports.Tracing = Tracing;
|
|
2988
|
+
exports.buildEvidence = buildEvidence;
|
|
2989
|
+
exports.collectStreamSteps = collectStreamSteps;
|
|
2990
|
+
exports.connectMCP = connectMCP;
|
|
2991
|
+
exports.createAgentLLMAdapter = createAgentLLMAdapter;
|
|
2992
|
+
exports.createLogger = createLogger;
|
|
2993
|
+
exports.createTaggedError = createTaggedError;
|
|
2994
|
+
exports.formatStepProgress = formatStepProgress;
|
|
2995
|
+
exports.generateAgentReport = generateAgentReport;
|
|
2996
|
+
exports.isNpmToolDescriptor = isNpmToolDescriptor;
|
|
2997
|
+
exports.isRetryable = isRetryable;
|
|
2998
|
+
exports.loadAgentConfig = loadAgentConfig;
|
|
2999
|
+
exports.loadMCPTool = loadMCPTool;
|
|
3000
|
+
exports.mcpToolsToSpecs = mcpToolsToSpecs;
|
|
3001
|
+
exports.normalizeToolList = normalizeToolList;
|
|
3002
|
+
exports.parseNpmToolDescriptor = parseNpmToolDescriptor;
|
|
3003
|
+
exports.registerMCPToolsFromConfig = registerMCPToolsFromConfig;
|
|
3004
|
+
exports.resolveNpmToolDescriptor = resolveNpmToolDescriptor;
|
|
3005
|
+
exports.resolveToolDescriptor = resolveToolDescriptor;
|
|
3006
|
+
exports.runAgent = runAgent;
|
|
3007
|
+
exports.runAgentWithReport = runAgentWithReport;
|
|
3008
|
+
exports.sanitizeForLog = sanitizeForLog;
|
|
3009
|
+
exports.serializeStepOutput = serializeStepOutput;
|
|
3010
|
+
exports.summarizeForLog = summarizeForLog;
|
|
3011
|
+
exports.withRetry = withRetry;
|
|
3012
|
+
exports.writeReportFromStream = writeReportFromStream;
|
|
3013
|
+
//# sourceMappingURL=index.cjs.map
|
|
3014
|
+
//# sourceMappingURL=index.cjs.map
|