@matimo/core 0.1.0-alpha.12.1 → 0.1.0-alpha.13
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 +169 -8
- package/dist/approval/approval-handler.d.ts +5 -1
- package/dist/approval/approval-handler.d.ts.map +1 -1
- package/dist/approval/approval-handler.js +6 -0
- package/dist/approval/approval-handler.js.map +1 -1
- package/dist/core/schema.d.ts +29 -8
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +10 -3
- package/dist/core/schema.js.map +1 -1
- package/dist/core/skill-content-parser.d.ts +91 -0
- package/dist/core/skill-content-parser.d.ts.map +1 -0
- package/dist/core/skill-content-parser.js +248 -0
- package/dist/core/skill-content-parser.js.map +1 -0
- package/dist/core/skill-loader.d.ts +46 -0
- package/dist/core/skill-loader.d.ts.map +1 -0
- package/dist/core/skill-loader.js +310 -0
- package/dist/core/skill-loader.js.map +1 -0
- package/dist/core/skill-registry.d.ts +131 -0
- package/dist/core/skill-registry.d.ts.map +1 -0
- package/dist/core/skill-registry.js +316 -0
- package/dist/core/skill-registry.js.map +1 -0
- package/dist/core/tfidf-embedding.d.ts +45 -0
- package/dist/core/tfidf-embedding.d.ts.map +1 -0
- package/dist/core/tfidf-embedding.js +199 -0
- package/dist/core/tfidf-embedding.js.map +1 -0
- package/dist/core/types.d.ts +155 -6
- package/dist/core/types.d.ts.map +1 -1
- package/dist/errors/matimo-error.d.ts +3 -1
- package/dist/errors/matimo-error.d.ts.map +1 -1
- package/dist/errors/matimo-error.js +2 -0
- package/dist/errors/matimo-error.js.map +1 -1
- package/dist/index.d.ts +20 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/langchain.d.ts +55 -0
- package/dist/integrations/langchain.d.ts.map +1 -1
- package/dist/integrations/langchain.js +66 -0
- package/dist/integrations/langchain.js.map +1 -1
- package/dist/logging/winston-logger.d.ts.map +1 -1
- package/dist/logging/winston-logger.js +9 -1
- package/dist/logging/winston-logger.js.map +1 -1
- package/dist/matimo-instance.d.ts +171 -6
- package/dist/matimo-instance.d.ts.map +1 -1
- package/dist/matimo-instance.js +602 -13
- package/dist/matimo-instance.js.map +1 -1
- package/dist/mcp/mcp-server.d.ts +23 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +119 -8
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/tool-converter.d.ts.map +1 -1
- package/dist/mcp/tool-converter.js +10 -1
- package/dist/mcp/tool-converter.js.map +1 -1
- package/dist/policy/approval-manifest.d.ts +74 -0
- package/dist/policy/approval-manifest.d.ts.map +1 -0
- package/dist/policy/approval-manifest.js +178 -0
- package/dist/policy/approval-manifest.js.map +1 -0
- package/dist/policy/content-validator.d.ts +19 -0
- package/dist/policy/content-validator.d.ts.map +1 -0
- package/dist/policy/content-validator.js +196 -0
- package/dist/policy/content-validator.js.map +1 -0
- package/dist/policy/default-policy.d.ts +46 -0
- package/dist/policy/default-policy.d.ts.map +1 -0
- package/dist/policy/default-policy.js +241 -0
- package/dist/policy/default-policy.js.map +1 -0
- package/dist/policy/events.d.ts +71 -0
- package/dist/policy/events.d.ts.map +1 -0
- package/dist/policy/events.js +8 -0
- package/dist/policy/events.js.map +1 -0
- package/dist/policy/index.d.ts +13 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +9 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/policy/integrity-tracker.d.ts +62 -0
- package/dist/policy/integrity-tracker.d.ts.map +1 -0
- package/dist/policy/integrity-tracker.js +79 -0
- package/dist/policy/integrity-tracker.js.map +1 -0
- package/dist/policy/policy-loader.d.ts +58 -0
- package/dist/policy/policy-loader.d.ts.map +1 -0
- package/dist/policy/policy-loader.js +153 -0
- package/dist/policy/policy-loader.js.map +1 -0
- package/dist/policy/risk-classifier.d.ts +18 -0
- package/dist/policy/risk-classifier.d.ts.map +1 -0
- package/dist/policy/risk-classifier.js +43 -0
- package/dist/policy/risk-classifier.js.map +1 -0
- package/dist/policy/types.d.ts +126 -0
- package/dist/policy/types.d.ts.map +1 -0
- package/dist/policy/types.js +8 -0
- package/dist/policy/types.js.map +1 -0
- package/package.json +2 -2
- package/tools/matimo_approve_tool/definition.yaml +36 -0
- package/tools/matimo_approve_tool/matimo_approve_tool.ts +90 -0
- package/tools/matimo_create_skill/definition.yaml +46 -0
- package/tools/matimo_create_skill/matimo_create_skill.ts +75 -0
- package/tools/matimo_create_tool/definition.yaml +48 -0
- package/tools/matimo_create_tool/matimo_create_tool.ts +137 -0
- package/tools/matimo_get_skill/definition.yaml +60 -0
- package/tools/matimo_get_skill/matimo_get_skill.ts +182 -0
- package/tools/matimo_get_tool_status/definition.yaml +42 -0
- package/tools/matimo_get_tool_status/matimo_get_tool_status.ts +101 -0
- package/tools/matimo_list_skills/definition.yaml +52 -0
- package/tools/matimo_list_skills/matimo_list_skills.ts +138 -0
- package/tools/matimo_list_user_tools/definition.yaml +32 -0
- package/tools/matimo_list_user_tools/matimo_list_user_tools.ts +74 -0
- package/tools/matimo_reload_tools/definition.yaml +35 -0
- package/tools/matimo_reload_tools/matimo_reload_tools.ts +29 -0
- package/tools/matimo_validate_skill/definition.yaml +43 -0
- package/tools/matimo_validate_skill/matimo_validate_skill.ts +137 -0
- package/tools/matimo_validate_tool/definition.yaml +34 -0
- package/tools/matimo_validate_tool/matimo_validate_tool.ts +168 -0
- package/tools/shared/skill-validation.ts +335 -0
package/dist/matimo-instance.js
CHANGED
|
@@ -1,28 +1,102 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _MatimoInstance_instances, _MatimoInstance_policy, _MatimoInstance_integrityTracker, _MatimoInstance_approvalManifest, _MatimoInstance_onEvent, _MatimoInstance_hitlCallback, _MatimoInstance_trustedPaths, _MatimoInstance_untrustedPaths, _MatimoInstance_policyFile, _MatimoInstance_emitEvent, _MatimoInstance_resolveHITL;
|
|
13
|
+
import fs from 'fs';
|
|
1
14
|
import path from 'path';
|
|
2
15
|
import { ToolLoader } from './core/tool-loader';
|
|
3
16
|
import { ToolRegistry } from './core/tool-registry';
|
|
17
|
+
import { SkillLoader } from './core/skill-loader';
|
|
18
|
+
import { SkillRegistry } from './core/skill-registry';
|
|
4
19
|
import { CommandExecutor } from './executors/command-executor';
|
|
5
20
|
import { HttpExecutor } from './executors/http-executor';
|
|
6
21
|
import { FunctionExecutor } from './executors/function-executor';
|
|
7
22
|
import { MatimoError, ErrorCode } from './errors/matimo-error';
|
|
8
23
|
import { getLoggerConfig, createLogger, setGlobalMatimoLogger, } from './logging';
|
|
9
24
|
import { getGlobalApprovalHandler } from './approval/approval-handler';
|
|
25
|
+
import { DefaultPolicyEngine } from './policy/default-policy';
|
|
26
|
+
import { loadPolicyFromFile } from './policy/policy-loader';
|
|
27
|
+
import { ToolIntegrityTracker } from './policy/integrity-tracker';
|
|
28
|
+
import { ApprovalManifest } from './policy/approval-manifest';
|
|
29
|
+
/**
|
|
30
|
+
* Find the core skills directory by walking up from process.cwd().
|
|
31
|
+
* Works in CJS (Jest), ESM (tsx), compiled dist, and on all platforms.
|
|
32
|
+
*/
|
|
33
|
+
function findCoreSkillsPath() {
|
|
34
|
+
let dir = process.cwd();
|
|
35
|
+
for (let i = 0; i < 20; i++) {
|
|
36
|
+
const candidate = path.join(dir, 'packages', 'core', 'skills');
|
|
37
|
+
if (fs.existsSync(candidate))
|
|
38
|
+
return candidate;
|
|
39
|
+
// Also check node_modules/@matimo/core/skills
|
|
40
|
+
const nmCandidate = path.join(dir, 'node_modules', '@matimo', 'core', 'skills');
|
|
41
|
+
if (fs.existsSync(nmCandidate))
|
|
42
|
+
return nmCandidate;
|
|
43
|
+
const parent = path.dirname(dir);
|
|
44
|
+
if (parent === dir)
|
|
45
|
+
break;
|
|
46
|
+
dir = parent;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
10
50
|
/**
|
|
11
51
|
* Matimo Instance - Single initialization point for tool execution
|
|
12
52
|
* Combines loader, registry, and executors into one interface
|
|
13
53
|
*/
|
|
14
54
|
export class MatimoInstance {
|
|
15
|
-
constructor(toolPaths, logger) {
|
|
55
|
+
constructor(toolPaths, skillPaths, logger, policyOptions) {
|
|
56
|
+
_MatimoInstance_instances.add(this);
|
|
57
|
+
// Policy engine fields — runtime-enforced encapsulation via ES #private
|
|
58
|
+
_MatimoInstance_policy.set(this, void 0);
|
|
59
|
+
_MatimoInstance_integrityTracker.set(this, void 0);
|
|
60
|
+
_MatimoInstance_approvalManifest.set(this, void 0);
|
|
61
|
+
_MatimoInstance_onEvent.set(this, void 0);
|
|
62
|
+
_MatimoInstance_hitlCallback.set(this, void 0);
|
|
63
|
+
_MatimoInstance_trustedPaths.set(this, void 0);
|
|
64
|
+
_MatimoInstance_untrustedPaths.set(this, void 0);
|
|
65
|
+
_MatimoInstance_policyFile.set(this, void 0);
|
|
16
66
|
this.toolPaths = toolPaths;
|
|
67
|
+
this.skillPaths = skillPaths;
|
|
17
68
|
this.logger = logger;
|
|
18
69
|
this.loader = new ToolLoader();
|
|
19
70
|
this.registry = new ToolRegistry();
|
|
71
|
+
this.skillLoader = new SkillLoader();
|
|
72
|
+
this.skillRegistry = new SkillRegistry();
|
|
20
73
|
// Use the first path (primary) as working directory for command executor
|
|
21
74
|
const workingDir = toolPaths.length > 0 ? path.dirname(toolPaths[0]) : process.cwd();
|
|
22
75
|
this.commandExecutor = new CommandExecutor(workingDir);
|
|
23
76
|
this.httpExecutor = new HttpExecutor();
|
|
24
77
|
this.functionExecutor = new FunctionExecutor(toolPaths[0] || '');
|
|
25
78
|
this.approvalHandler = getGlobalApprovalHandler();
|
|
79
|
+
// Policy engine setup
|
|
80
|
+
__classPrivateFieldSet(this, _MatimoInstance_policy, policyOptions?.policy ?? null, "f");
|
|
81
|
+
__classPrivateFieldSet(this, _MatimoInstance_trustedPaths, policyOptions?.trustedPaths ?? [], "f");
|
|
82
|
+
__classPrivateFieldSet(this, _MatimoInstance_untrustedPaths, policyOptions?.untrustedPaths ?? [], "f");
|
|
83
|
+
__classPrivateFieldSet(this, _MatimoInstance_integrityTracker, new ToolIntegrityTracker(), "f");
|
|
84
|
+
__classPrivateFieldSet(this, _MatimoInstance_onEvent, policyOptions?.onEvent ?? null, "f");
|
|
85
|
+
__classPrivateFieldSet(this, _MatimoInstance_hitlCallback, policyOptions?.onHITL ?? null, "f");
|
|
86
|
+
__classPrivateFieldSet(this, _MatimoInstance_policyFile, policyOptions?.policyFile ?? null, "f");
|
|
87
|
+
// Approval manifest
|
|
88
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_policy, "f")) {
|
|
89
|
+
const approvalDir = policyOptions?.approvalDir ?? process.cwd();
|
|
90
|
+
__classPrivateFieldSet(this, _MatimoInstance_approvalManifest, new ApprovalManifest(approvalDir, policyOptions?.approvalSecret), "f");
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
__classPrivateFieldSet(this, _MatimoInstance_approvalManifest, null, "f");
|
|
94
|
+
}
|
|
95
|
+
// Freeze policy to prevent runtime mutation by agents.
|
|
96
|
+
// (The SDK's own reloadPolicy() bypasses the frozen reference by replacing #policy.)
|
|
97
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_policy, "f")) {
|
|
98
|
+
Object.freeze(__classPrivateFieldGet(this, _MatimoInstance_policy, "f"));
|
|
99
|
+
}
|
|
26
100
|
}
|
|
27
101
|
/**
|
|
28
102
|
* Initialize Matimo with tools from directory or auto-discovery
|
|
@@ -82,27 +156,83 @@ export class MatimoInstance {
|
|
|
82
156
|
autoDiscover: finalOptions.autoDiscover,
|
|
83
157
|
});
|
|
84
158
|
const toolPaths = [];
|
|
159
|
+
const skillPaths = [];
|
|
85
160
|
// Include core tools (calculator, etc.) - currently not used in monorepo
|
|
86
161
|
// Use explicit toolPaths or autoDiscover instead
|
|
87
162
|
// if (finalOptions.includeCore) { ... }
|
|
88
|
-
// Add explicit paths
|
|
163
|
+
// Add explicit tool paths
|
|
89
164
|
if (finalOptions.toolPaths) {
|
|
90
165
|
toolPaths.push(...finalOptions.toolPaths);
|
|
91
166
|
logger.debug(`Adding explicit tool paths`, { count: finalOptions.toolPaths.length });
|
|
92
167
|
}
|
|
168
|
+
// Add explicit skill paths (include core skills by default)
|
|
169
|
+
if (finalOptions.skillPaths) {
|
|
170
|
+
skillPaths.push(...finalOptions.skillPaths);
|
|
171
|
+
logger.debug(`Adding explicit skill paths`, { count: finalOptions.skillPaths.length });
|
|
172
|
+
}
|
|
173
|
+
// Always include core skills bundled with Matimo
|
|
174
|
+
const coreSkillsPath = findCoreSkillsPath();
|
|
175
|
+
if (coreSkillsPath && !skillPaths.includes(coreSkillsPath)) {
|
|
176
|
+
skillPaths.push(coreSkillsPath);
|
|
177
|
+
logger.debug(`Including core skills`, { path: coreSkillsPath });
|
|
178
|
+
}
|
|
93
179
|
// Auto-discover @matimo/* packages
|
|
94
180
|
if (finalOptions.autoDiscover) {
|
|
95
181
|
const discoveredPaths = new ToolLoader().autoDiscoverPackages();
|
|
96
182
|
toolPaths.push(...discoveredPaths);
|
|
97
183
|
logger.debug(`Auto-discovered tool paths`, { count: discoveredPaths.length });
|
|
184
|
+
// Also discover skill paths from the same @matimo/* packages
|
|
185
|
+
// Each discovered tool path is like @matimo/slack/tools/ — sibling skills/ may exist
|
|
186
|
+
for (const toolPath of discoveredPaths) {
|
|
187
|
+
const pkgDir = path.dirname(toolPath); // e.g. @matimo/slack/
|
|
188
|
+
const pkgSkillsPath = path.join(pkgDir, 'skills');
|
|
189
|
+
if (fs.existsSync(pkgSkillsPath) && !skillPaths.includes(pkgSkillsPath)) {
|
|
190
|
+
skillPaths.push(pkgSkillsPath);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
logger.debug(`Auto-discovered skill paths`, { count: skillPaths.length });
|
|
194
|
+
}
|
|
195
|
+
// Build policy engine
|
|
196
|
+
let policy = null;
|
|
197
|
+
if (finalOptions.policy) {
|
|
198
|
+
policy = finalOptions.policy;
|
|
98
199
|
}
|
|
99
|
-
|
|
200
|
+
else if (finalOptions.policyFile) {
|
|
201
|
+
policy = loadPolicyFromFile(finalOptions.policyFile);
|
|
202
|
+
}
|
|
203
|
+
else if (finalOptions.policyConfig) {
|
|
204
|
+
policy = new DefaultPolicyEngine(finalOptions.policyConfig);
|
|
205
|
+
}
|
|
206
|
+
const instance = new MatimoInstance(toolPaths, skillPaths, logger, {
|
|
207
|
+
policy,
|
|
208
|
+
trustedPaths: finalOptions.trustedPaths,
|
|
209
|
+
untrustedPaths: finalOptions.untrustedPaths,
|
|
210
|
+
approvalSecret: finalOptions.approvalSecret,
|
|
211
|
+
approvalDir: finalOptions.approvalDir,
|
|
212
|
+
onEvent: finalOptions.onEvent,
|
|
213
|
+
onHITL: finalOptions.onHITL,
|
|
214
|
+
policyFile: finalOptions.policyFile,
|
|
215
|
+
});
|
|
100
216
|
// Load tools from all paths
|
|
101
217
|
const allTools = instance.loader.loadToolsFromMultiplePaths(toolPaths);
|
|
102
218
|
instance.registry.registerAll(Array.from(allTools.values()));
|
|
219
|
+
// Load skills from all paths
|
|
220
|
+
for (const skillPath of skillPaths) {
|
|
221
|
+
try {
|
|
222
|
+
const skillsFromPath = instance.skillLoader.loadSkillsFromDirectory(skillPath, skillPath.includes('core') ? 'builtin' : 'user');
|
|
223
|
+
instance.skillRegistry.registerAll(skillsFromPath);
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
logger.warn(`Failed to load skills from path: ${skillPath}`, {
|
|
227
|
+
error: err.message,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
103
231
|
logger.info(`Matimo SDK initialized successfully`, {
|
|
104
232
|
toolCount: allTools.size,
|
|
233
|
+
skillCount: instance.skillRegistry.count(),
|
|
105
234
|
paths: toolPaths.length,
|
|
235
|
+
skillPaths: skillPaths.length,
|
|
106
236
|
});
|
|
107
237
|
return instance;
|
|
108
238
|
}
|
|
@@ -151,6 +281,38 @@ export class MatimoInstance {
|
|
|
151
281
|
paramCount: Object.keys(params).length,
|
|
152
282
|
});
|
|
153
283
|
try {
|
|
284
|
+
// Policy check: enforce RBAC and tool status before any execution
|
|
285
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_policy, "f")) {
|
|
286
|
+
const policyContext = options?.context ?? {};
|
|
287
|
+
const decision = __classPrivateFieldGet(this, _MatimoInstance_policy, "f").canExecute(policyContext, tool);
|
|
288
|
+
if (decision.allowed === false) {
|
|
289
|
+
__classPrivateFieldGet(this, _MatimoInstance_instances, "m", _MatimoInstance_emitEvent).call(this, {
|
|
290
|
+
type: 'tool:execution_denied',
|
|
291
|
+
toolName,
|
|
292
|
+
reason: decision.reason,
|
|
293
|
+
agentId: policyContext.agentId,
|
|
294
|
+
timestamp: new Date().toISOString(),
|
|
295
|
+
});
|
|
296
|
+
throw new MatimoError(`Policy denied execution of '${toolName}': ${decision.reason}`, ErrorCode.POLICY_DENIED, { toolName, reason: decision.reason, riskLevel: decision.riskLevel });
|
|
297
|
+
}
|
|
298
|
+
// Handle quarantined tools — check approval manifest or invoke HITL callback
|
|
299
|
+
if (decision.allowed === 'pending_approval') {
|
|
300
|
+
const approved = await __classPrivateFieldGet(this, _MatimoInstance_instances, "m", _MatimoInstance_resolveHITL).call(this, tool, decision, policyContext);
|
|
301
|
+
if (!approved) {
|
|
302
|
+
__classPrivateFieldGet(this, _MatimoInstance_instances, "m", _MatimoInstance_emitEvent).call(this, {
|
|
303
|
+
type: 'tool:quarantine_rejected',
|
|
304
|
+
toolName,
|
|
305
|
+
timestamp: new Date().toISOString(),
|
|
306
|
+
});
|
|
307
|
+
throw new MatimoError(`Tool '${toolName}' is quarantined and was not approved: ${decision.reason}`, ErrorCode.POLICY_DENIED, { toolName, reason: decision.reason, riskLevel: decision.riskLevel });
|
|
308
|
+
}
|
|
309
|
+
__classPrivateFieldGet(this, _MatimoInstance_instances, "m", _MatimoInstance_emitEvent).call(this, {
|
|
310
|
+
type: 'tool:quarantine_approved',
|
|
311
|
+
toolName,
|
|
312
|
+
timestamp: new Date().toISOString(),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
154
316
|
// Simple approval flow:
|
|
155
317
|
// 1. Check if tool requires approval (from YAML or keyword detection)
|
|
156
318
|
// 2. Check if pre-approved via env vars
|
|
@@ -178,7 +340,12 @@ export class MatimoInstance {
|
|
|
178
340
|
scanContent = parts.join(' ');
|
|
179
341
|
}
|
|
180
342
|
const requiresApproval = this.approvalHandler.requiresApproval(tool.requires_approval, scanContent);
|
|
181
|
-
if (
|
|
343
|
+
// Skip approval prompt if options.approved=true (e.g., MCP pre-confirmed approval).
|
|
344
|
+
// This avoids mutating global state in concurrent scenarios.
|
|
345
|
+
const skipApprovalPrompt = options?.approved === true;
|
|
346
|
+
if (requiresApproval &&
|
|
347
|
+
!this.approvalHandler.isPreApproved(toolName) &&
|
|
348
|
+
!skipApprovalPrompt) {
|
|
182
349
|
this.logger.debug(`Approval required for: ${toolName}`, { toolName });
|
|
183
350
|
await this.approvalHandler.requestApproval({
|
|
184
351
|
toolName,
|
|
@@ -192,8 +359,32 @@ export class MatimoInstance {
|
|
|
192
359
|
// Auto-inject authentication parameters. When per-call credentials are
|
|
193
360
|
// supplied they take precedence over process.env (multi-tenant support).
|
|
194
361
|
const finalParams = this.injectAuthParameters(tool, params, credentials);
|
|
362
|
+
// After injection, detect any auth-looking placeholders that are still unfilled.
|
|
363
|
+
// This gives a clear actionable error instead of silently sending a bad header to the API.
|
|
364
|
+
this.assertAuthParamsFilled(tool, finalParams, credentials);
|
|
195
365
|
// Apply per-call timeout override if provided. Create a shallow copy so the
|
|
196
366
|
// registered tool definition is never mutated between calls.
|
|
367
|
+
// Built-in interception: matimo_reload_tools must run on the instance
|
|
368
|
+
// itself because reloadTools() clears/rebuilds the in-memory registry.
|
|
369
|
+
// The function executor has no reference to the MatimoInstance, so we
|
|
370
|
+
// handle it directly here. This works identically for SDK, LangChain,
|
|
371
|
+
// and MCP callers.
|
|
372
|
+
if (toolName === 'matimo_reload_tools') {
|
|
373
|
+
const reloadResult = await this.reloadTools();
|
|
374
|
+
this.logger.info('matimo_reload_tools: reload completed', {
|
|
375
|
+
loaded: reloadResult.loaded,
|
|
376
|
+
removed: reloadResult.removed,
|
|
377
|
+
rejected: reloadResult.rejected.length,
|
|
378
|
+
});
|
|
379
|
+
return {
|
|
380
|
+
success: true,
|
|
381
|
+
loaded: reloadResult.loaded,
|
|
382
|
+
removed: reloadResult.removed,
|
|
383
|
+
revalidated: reloadResult.revalidated,
|
|
384
|
+
rejected: reloadResult.rejected,
|
|
385
|
+
message: `Reload complete. ${reloadResult.loaded} tools loaded, ${reloadResult.removed} removed, ${reloadResult.rejected.length} rejected.`,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
197
388
|
const effectiveTool = timeoutOverride !== undefined
|
|
198
389
|
? { ...tool, execution: { ...tool.execution, timeout: timeoutOverride } }
|
|
199
390
|
: tool;
|
|
@@ -222,34 +413,47 @@ export class MatimoInstance {
|
|
|
222
413
|
return this.registry.get(toolName);
|
|
223
414
|
}
|
|
224
415
|
/**
|
|
225
|
-
* List all available tools
|
|
416
|
+
* List all available tools, optionally filtered by policy.
|
|
417
|
+
* @param context - PolicyContext for filtering. If omitted and policy is active, returns all tools (backward compatible).
|
|
226
418
|
* @returns Array of tool definitions
|
|
227
419
|
*/
|
|
228
|
-
listTools() {
|
|
229
|
-
|
|
420
|
+
listTools(context) {
|
|
421
|
+
const tools = this.registry.getAll();
|
|
422
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_policy, "f") && context) {
|
|
423
|
+
return __classPrivateFieldGet(this, _MatimoInstance_policy, "f").filterForAgent(context, tools);
|
|
424
|
+
}
|
|
425
|
+
return tools;
|
|
230
426
|
}
|
|
231
427
|
/**
|
|
232
428
|
* Get all available tools (alias for listTools)
|
|
233
429
|
* @returns Array of tool definitions
|
|
234
430
|
*/
|
|
235
|
-
getAllTools() {
|
|
236
|
-
return this.
|
|
431
|
+
getAllTools(context) {
|
|
432
|
+
return this.listTools(context);
|
|
237
433
|
}
|
|
238
434
|
/**
|
|
239
435
|
* Search tools by name or description
|
|
240
436
|
* @param query - Search query
|
|
241
437
|
* @returns Matching tools
|
|
242
438
|
*/
|
|
243
|
-
searchTools(query) {
|
|
244
|
-
|
|
439
|
+
searchTools(query, context) {
|
|
440
|
+
const results = this.registry.search(query);
|
|
441
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_policy, "f") && context) {
|
|
442
|
+
return __classPrivateFieldGet(this, _MatimoInstance_policy, "f").filterForAgent(context, results);
|
|
443
|
+
}
|
|
444
|
+
return results;
|
|
245
445
|
}
|
|
246
446
|
/**
|
|
247
447
|
* Get tools by tag
|
|
248
448
|
* @param tag - Tag to search for
|
|
249
449
|
* @returns Tools with the given tag
|
|
250
450
|
*/
|
|
251
|
-
getToolsByTag(tag) {
|
|
252
|
-
|
|
451
|
+
getToolsByTag(tag, context) {
|
|
452
|
+
const results = this.registry.getByTag(tag);
|
|
453
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_policy, "f") && context) {
|
|
454
|
+
return __classPrivateFieldGet(this, _MatimoInstance_policy, "f").filterForAgent(context, results);
|
|
455
|
+
}
|
|
456
|
+
return results;
|
|
253
457
|
}
|
|
254
458
|
/**
|
|
255
459
|
* Return the credential key names that a tool expects.
|
|
@@ -311,6 +515,86 @@ export class MatimoInstance {
|
|
|
311
515
|
}
|
|
312
516
|
return credentialKeys;
|
|
313
517
|
}
|
|
518
|
+
// ─── Skills API ──────────────────────────────────────────────────────────
|
|
519
|
+
/**
|
|
520
|
+
* List all available skills (Level 1 discovery - minimal context)
|
|
521
|
+
* @returns Array of skill summaries
|
|
522
|
+
*/
|
|
523
|
+
listSkills() {
|
|
524
|
+
return this.skillRegistry.list();
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Get a single skill by name (Level 2 activation - full content)
|
|
528
|
+
* @param name - Skill name
|
|
529
|
+
* @returns Skill definition or null
|
|
530
|
+
*/
|
|
531
|
+
getSkill(name) {
|
|
532
|
+
return this.skillRegistry.get(name) || null;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Get selective skill content — only the sections an agent needs.
|
|
536
|
+
* Prevents dumping entire SKILL.md files into the LLM context window.
|
|
537
|
+
*
|
|
538
|
+
* @example
|
|
539
|
+
* // Get only error handling, max 500 tokens
|
|
540
|
+
* matimo.getSkillContent('postgres-query-operations', {
|
|
541
|
+
* sections: ['Error Handling'],
|
|
542
|
+
* maxTokens: 500,
|
|
543
|
+
* })
|
|
544
|
+
*/
|
|
545
|
+
getSkillContent(name, options) {
|
|
546
|
+
return this.skillRegistry.getSkillContent(name, options);
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* List all sections of a skill with their token costs.
|
|
550
|
+
* Agents use this to decide which sections to load before activating.
|
|
551
|
+
*/
|
|
552
|
+
getSkillSections(name) {
|
|
553
|
+
return this.skillRegistry.getSkillSections(name);
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Search skills by keyword, category, difficulty, etc.
|
|
557
|
+
* Set `options.semantic = true` for embedding-based similarity ranking.
|
|
558
|
+
* @param options - Search options
|
|
559
|
+
* @returns Matching skills
|
|
560
|
+
*/
|
|
561
|
+
searchSkills(options = {}) {
|
|
562
|
+
return this.skillRegistry.search(options);
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Semantic search with relevance scores.
|
|
566
|
+
* Uses embeddings to find skills by meaning, not just keywords.
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* const results = await matimo.semanticSearchSkills('How do I handle Postgres locking?');
|
|
570
|
+
* // → [{ skill: { name: 'postgres-query-operations' }, score: 0.82 }]
|
|
571
|
+
*/
|
|
572
|
+
async semanticSearchSkills(query, options) {
|
|
573
|
+
return this.skillRegistry.semanticSearch(query, options);
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Set a custom embedding provider for semantic skill search.
|
|
577
|
+
* If not set, a built-in TF-IDF provider is used.
|
|
578
|
+
*/
|
|
579
|
+
setSkillEmbeddingProvider(provider) {
|
|
580
|
+
this.skillRegistry.setEmbeddingProvider(provider);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Get a bundled resource from a skill (Level 3 resources)
|
|
584
|
+
* @param skillName - Skill name
|
|
585
|
+
* @param resourcePath - Relative path to resource (e.g., "scripts/extract.py")
|
|
586
|
+
* @returns Resource content
|
|
587
|
+
*/
|
|
588
|
+
getSkillResource(skillName, resourcePath) {
|
|
589
|
+
return this.skillLoader.loadSkillResource(skillName, this.skillPaths[0], resourcePath);
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Get all skill paths
|
|
593
|
+
* @returns Array of skill paths
|
|
594
|
+
*/
|
|
595
|
+
getSkillPaths() {
|
|
596
|
+
return [...this.skillPaths];
|
|
597
|
+
}
|
|
314
598
|
/**
|
|
315
599
|
* Automatically inject parameters from environment variables
|
|
316
600
|
* Uses a YAML-native, scale-friendly approach:
|
|
@@ -369,6 +653,60 @@ export class MatimoInstance {
|
|
|
369
653
|
}
|
|
370
654
|
return result;
|
|
371
655
|
}
|
|
656
|
+
/**
|
|
657
|
+
* After injectAuthParameters(), verify no auth-looking placeholders remain unfilled.
|
|
658
|
+
* Only checks HTTP headers (where auth credentials are injected) — not query params or body.
|
|
659
|
+
* Throws AUTH_FAILED with actionable guidance naming the missing env var(s).
|
|
660
|
+
*/
|
|
661
|
+
assertAuthParamsFilled(tool, finalParams, credentials) {
|
|
662
|
+
const execution = tool.execution;
|
|
663
|
+
if (!('headers' in execution) || !execution.headers)
|
|
664
|
+
return;
|
|
665
|
+
const authPatterns = [
|
|
666
|
+
'token',
|
|
667
|
+
'key',
|
|
668
|
+
'secret',
|
|
669
|
+
'password',
|
|
670
|
+
'credential',
|
|
671
|
+
'auth',
|
|
672
|
+
'bearer',
|
|
673
|
+
'api_key',
|
|
674
|
+
];
|
|
675
|
+
const placeholderRegex = /\{([^}]+)\}/g;
|
|
676
|
+
const missing = [];
|
|
677
|
+
// Only inspect headers — auth credentials belong there, not in query_params or body
|
|
678
|
+
for (const [headerName, headerValue] of Object.entries(execution.headers)) {
|
|
679
|
+
if (typeof headerValue !== 'string')
|
|
680
|
+
continue;
|
|
681
|
+
// Only check headers that look auth-related (Authorization, X-API-Key, etc.)
|
|
682
|
+
const lowerHeader = headerName.toLowerCase();
|
|
683
|
+
const isAuthHeader = lowerHeader === 'authorization' ||
|
|
684
|
+
lowerHeader.includes('auth') ||
|
|
685
|
+
lowerHeader.includes('token') ||
|
|
686
|
+
lowerHeader.includes('key') ||
|
|
687
|
+
lowerHeader.includes('secret');
|
|
688
|
+
if (!isAuthHeader)
|
|
689
|
+
continue;
|
|
690
|
+
let match;
|
|
691
|
+
placeholderRegex.lastIndex = 0;
|
|
692
|
+
while ((match = placeholderRegex.exec(headerValue)) !== null) {
|
|
693
|
+
const paramName = match[1];
|
|
694
|
+
if (paramName in finalParams)
|
|
695
|
+
continue;
|
|
696
|
+
const lowerName = paramName.toLowerCase();
|
|
697
|
+
if (authPatterns.some((p) => lowerName.includes(p))) {
|
|
698
|
+
missing.push(paramName);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (missing.length === 0)
|
|
703
|
+
return;
|
|
704
|
+
const hints = missing
|
|
705
|
+
.map((n) => ` • ${n} → MATIMO_${n} (or pass via credentials option)`)
|
|
706
|
+
.join('\n');
|
|
707
|
+
const credentialsHint = credentials ? '' : ' (No per-call credentials were supplied.)';
|
|
708
|
+
throw new MatimoError(`Authentication credentials are missing for tool "${tool.name}".\n${hints}\n${credentialsHint}`.trim(), ErrorCode.AUTH_FAILED, { toolName: tool.name, missingCredentials: missing });
|
|
709
|
+
}
|
|
372
710
|
/**
|
|
373
711
|
* Extract all parameter placeholders from execution config
|
|
374
712
|
* Scans headers, body, URL, and query_params for {paramName} patterns
|
|
@@ -470,7 +808,258 @@ export class MatimoInstance {
|
|
|
470
808
|
throw new MatimoError(`Unsupported execution type: ${executionType}`, ErrorCode.EXECUTION_FAILED, { executionType });
|
|
471
809
|
}
|
|
472
810
|
}
|
|
811
|
+
/**
|
|
812
|
+
* Hot-reload tools from all configured paths.
|
|
813
|
+
* Re-validates untrusted tools via content validator and integrity tracker.
|
|
814
|
+
* Tools that fail validation are rejected and not loaded.
|
|
815
|
+
*
|
|
816
|
+
* Atomic: if loading fails mid-way (e.g. I/O error), the registry is restored
|
|
817
|
+
* to its previous state and `rolledBack: true` is included in the result.
|
|
818
|
+
*/
|
|
819
|
+
async reloadTools() {
|
|
820
|
+
const previousNames = new Set(this.registry.getAll().map((t) => t.name));
|
|
821
|
+
// Snapshot the previous registry state for rollback on partial failure
|
|
822
|
+
const snapshot = this.registry.getAll();
|
|
823
|
+
this.registry.clear();
|
|
824
|
+
const result = {
|
|
825
|
+
loaded: 0,
|
|
826
|
+
removed: 0,
|
|
827
|
+
revalidated: 0,
|
|
828
|
+
rejected: [],
|
|
829
|
+
rolledBack: false,
|
|
830
|
+
};
|
|
831
|
+
let allTools;
|
|
832
|
+
try {
|
|
833
|
+
allTools = this.loader.loadToolsFromMultiplePaths(this.toolPaths);
|
|
834
|
+
}
|
|
835
|
+
catch (err) {
|
|
836
|
+
// I/O failure during load — restore snapshot and signal rollback
|
|
837
|
+
this.registry.registerAll(snapshot);
|
|
838
|
+
result.rolledBack = true;
|
|
839
|
+
this.logger.error('reloadTools: failed to load tools, rolled back to previous state', {
|
|
840
|
+
error: err.message,
|
|
841
|
+
});
|
|
842
|
+
return result;
|
|
843
|
+
}
|
|
844
|
+
const untrustedSet = new Set(__classPrivateFieldGet(this, _MatimoInstance_untrustedPaths, "f"));
|
|
845
|
+
for (const [, tool] of allTools) {
|
|
846
|
+
const defPath = tool._definitionPath ?? '';
|
|
847
|
+
const isUntrusted = untrustedSet.size > 0 && __classPrivateFieldGet(this, _MatimoInstance_untrustedPaths, "f").some((up) => defPath.startsWith(up));
|
|
848
|
+
if (isUntrusted && __classPrivateFieldGet(this, _MatimoInstance_policy, "f")) {
|
|
849
|
+
// Run policy validation on untrusted tools
|
|
850
|
+
const policyDecision = __classPrivateFieldGet(this, _MatimoInstance_policy, "f").canCreate({}, tool);
|
|
851
|
+
if (policyDecision.allowed === false) {
|
|
852
|
+
result.rejected.push(tool.name);
|
|
853
|
+
__classPrivateFieldGet(this, _MatimoInstance_instances, "m", _MatimoInstance_emitEvent).call(this, {
|
|
854
|
+
type: 'tool:rejected',
|
|
855
|
+
toolName: tool.name,
|
|
856
|
+
violations: [
|
|
857
|
+
{ rule: 'policy-denied', severity: 'high', message: policyDecision.reason },
|
|
858
|
+
],
|
|
859
|
+
timestamp: new Date().toISOString(),
|
|
860
|
+
});
|
|
861
|
+
this.logger.warn(`Tool rejected during reload: ${tool.name}`, {
|
|
862
|
+
reason: policyDecision.reason,
|
|
863
|
+
});
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
if (policyDecision.allowed === 'pending_approval') {
|
|
867
|
+
// Quarantine: mark as pending in the approval manifest
|
|
868
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_approvalManifest, "f")) {
|
|
869
|
+
__classPrivateFieldGet(this, _MatimoInstance_approvalManifest, "f").markPending(tool.name);
|
|
870
|
+
}
|
|
871
|
+
__classPrivateFieldGet(this, _MatimoInstance_instances, "m", _MatimoInstance_emitEvent).call(this, {
|
|
872
|
+
type: 'tool:quarantined',
|
|
873
|
+
toolName: tool.name,
|
|
874
|
+
riskLevel: policyDecision.riskLevel,
|
|
875
|
+
reason: policyDecision.reason,
|
|
876
|
+
timestamp: new Date().toISOString(),
|
|
877
|
+
});
|
|
878
|
+
this.logger.info(`Tool quarantined during reload: ${tool.name}`, {
|
|
879
|
+
riskLevel: policyDecision.riskLevel,
|
|
880
|
+
reason: policyDecision.reason,
|
|
881
|
+
});
|
|
882
|
+
// Still register the tool so it exists, but it will be blocked
|
|
883
|
+
// at execution time until approved via the approval manifest
|
|
884
|
+
result.revalidated++;
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
result.revalidated++;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
this.registry.register(tool);
|
|
891
|
+
const source = isUntrusted ? 'untrusted' : 'trusted';
|
|
892
|
+
__classPrivateFieldGet(this, _MatimoInstance_integrityTracker, "f").record(tool.name, JSON.stringify(tool), source);
|
|
893
|
+
result.loaded++;
|
|
894
|
+
}
|
|
895
|
+
// Calculate removed tools
|
|
896
|
+
const currentNames = new Set(this.registry.getAll().map((t) => t.name));
|
|
897
|
+
for (const name of previousNames) {
|
|
898
|
+
if (!currentNames.has(name)) {
|
|
899
|
+
result.removed++;
|
|
900
|
+
__classPrivateFieldGet(this, _MatimoInstance_integrityTracker, "f").removeEntry(name);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
__classPrivateFieldGet(this, _MatimoInstance_instances, "m", _MatimoInstance_emitEvent).call(this, {
|
|
904
|
+
type: 'tools:reloaded',
|
|
905
|
+
loaded: result.loaded,
|
|
906
|
+
removed: result.removed,
|
|
907
|
+
rejected: result.rejected,
|
|
908
|
+
timestamp: new Date().toISOString(),
|
|
909
|
+
});
|
|
910
|
+
this.logger.info('Tools reloaded', {
|
|
911
|
+
loaded: result.loaded,
|
|
912
|
+
removed: result.removed,
|
|
913
|
+
revalidated: result.revalidated,
|
|
914
|
+
rejected: result.rejected.length,
|
|
915
|
+
});
|
|
916
|
+
return result;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Check if a policy engine is active.
|
|
920
|
+
*/
|
|
921
|
+
hasPolicy() {
|
|
922
|
+
return __classPrivateFieldGet(this, _MatimoInstance_policy, "f") !== null;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Get the approval manifest (if policy engine is active).
|
|
926
|
+
*/
|
|
927
|
+
getApprovalManifest() {
|
|
928
|
+
return __classPrivateFieldGet(this, _MatimoInstance_approvalManifest, "f");
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Get the integrity tracker.
|
|
932
|
+
*/
|
|
933
|
+
getIntegrityTracker() {
|
|
934
|
+
return __classPrivateFieldGet(this, _MatimoInstance_integrityTracker, "f");
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Get the tool registry (for advanced use cases).
|
|
938
|
+
*/
|
|
939
|
+
getRegistry() {
|
|
940
|
+
return this.registry;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Set a Human-in-the-Loop callback for quarantined tools.
|
|
944
|
+
* The callback is invoked when a tool with `pending_approval` status is executed.
|
|
945
|
+
* Return `true` to approve, `false` to reject.
|
|
946
|
+
*/
|
|
947
|
+
setHITLCallback(callback) {
|
|
948
|
+
__classPrivateFieldSet(this, _MatimoInstance_hitlCallback, callback, "f");
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Hot-reload the policy engine at runtime.
|
|
952
|
+
*
|
|
953
|
+
* - If `configOrFile` is a `PolicyConfig` object, creates a new `DefaultPolicyEngine`.
|
|
954
|
+
* - If `configOrFile` is a string, re-reads and parses the YAML file.
|
|
955
|
+
* - If omitted and the instance was initialized with `policyFile`, re-reads that file.
|
|
956
|
+
*
|
|
957
|
+
* The new policy is validated before swap — if validation fails, the old policy remains active.
|
|
958
|
+
* After swap, all tools are re-validated against the new policy via `reloadTools()`.
|
|
959
|
+
*
|
|
960
|
+
* @returns The ReloadResult from the subsequent tool re-validation.
|
|
961
|
+
*/
|
|
962
|
+
async reloadPolicy(configOrFile) {
|
|
963
|
+
let newPolicy;
|
|
964
|
+
if (typeof configOrFile === 'string') {
|
|
965
|
+
// Re-read from file path
|
|
966
|
+
newPolicy = loadPolicyFromFile(configOrFile);
|
|
967
|
+
__classPrivateFieldSet(this, _MatimoInstance_policyFile, configOrFile, "f");
|
|
968
|
+
}
|
|
969
|
+
else if (configOrFile) {
|
|
970
|
+
// Build from inline config
|
|
971
|
+
newPolicy = new DefaultPolicyEngine(configOrFile);
|
|
972
|
+
}
|
|
973
|
+
else if (__classPrivateFieldGet(this, _MatimoInstance_policyFile, "f")) {
|
|
974
|
+
// Re-read the original policy file
|
|
975
|
+
newPolicy = loadPolicyFromFile(__classPrivateFieldGet(this, _MatimoInstance_policyFile, "f"));
|
|
976
|
+
}
|
|
977
|
+
else if (__classPrivateFieldGet(this, _MatimoInstance_policy, "f") && 'updateConfig' in __classPrivateFieldGet(this, _MatimoInstance_policy, "f")) {
|
|
978
|
+
// No config provided and no file — nothing to reload
|
|
979
|
+
this.logger.warn('reloadPolicy: no config or file provided, nothing to reload');
|
|
980
|
+
return {
|
|
981
|
+
loaded: 0,
|
|
982
|
+
removed: 0,
|
|
983
|
+
revalidated: 0,
|
|
984
|
+
rejected: [],
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
else {
|
|
988
|
+
this.logger.warn('reloadPolicy: no policy engine to reload');
|
|
989
|
+
return {
|
|
990
|
+
loaded: 0,
|
|
991
|
+
removed: 0,
|
|
992
|
+
revalidated: 0,
|
|
993
|
+
rejected: [],
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
// Atomic swap: replace the policy engine reference
|
|
997
|
+
__classPrivateFieldSet(this, _MatimoInstance_policy, newPolicy, "f");
|
|
998
|
+
Object.freeze(__classPrivateFieldGet(this, _MatimoInstance_policy, "f"));
|
|
999
|
+
__classPrivateFieldGet(this, _MatimoInstance_instances, "m", _MatimoInstance_emitEvent).call(this, {
|
|
1000
|
+
type: 'policy:reloaded',
|
|
1001
|
+
timestamp: new Date().toISOString(),
|
|
1002
|
+
});
|
|
1003
|
+
this.logger.info('Policy engine reloaded');
|
|
1004
|
+
// Re-validate all tools against the new policy
|
|
1005
|
+
return this.reloadTools();
|
|
1006
|
+
}
|
|
473
1007
|
}
|
|
1008
|
+
_MatimoInstance_policy = new WeakMap(), _MatimoInstance_integrityTracker = new WeakMap(), _MatimoInstance_approvalManifest = new WeakMap(), _MatimoInstance_onEvent = new WeakMap(), _MatimoInstance_hitlCallback = new WeakMap(), _MatimoInstance_trustedPaths = new WeakMap(), _MatimoInstance_untrustedPaths = new WeakMap(), _MatimoInstance_policyFile = new WeakMap(), _MatimoInstance_instances = new WeakSet(), _MatimoInstance_emitEvent = function _MatimoInstance_emitEvent(event) {
|
|
1009
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_onEvent, "f")) {
|
|
1010
|
+
try {
|
|
1011
|
+
__classPrivateFieldGet(this, _MatimoInstance_onEvent, "f").call(this, event);
|
|
1012
|
+
}
|
|
1013
|
+
catch {
|
|
1014
|
+
// Never let event handler errors break SDK execution
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}, _MatimoInstance_resolveHITL =
|
|
1018
|
+
/**
|
|
1019
|
+
* Resolve a quarantined tool via HITL callback or approval manifest.
|
|
1020
|
+
*
|
|
1021
|
+
* Resolution order:
|
|
1022
|
+
* 1. Check approval manifest — if already approved, allow.
|
|
1023
|
+
* 2. Invoke HITL callback — if set, ask the human.
|
|
1024
|
+
* 3. If no callback, reject by default (safe fail-closed).
|
|
1025
|
+
*/
|
|
1026
|
+
async function _MatimoInstance_resolveHITL(tool, decision, context) {
|
|
1027
|
+
// 1. Check approval manifest — previously approved tools pass through
|
|
1028
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_approvalManifest, "f")) {
|
|
1029
|
+
const yamlHash = __classPrivateFieldGet(this, _MatimoInstance_integrityTracker, "f").getHash(tool.name);
|
|
1030
|
+
if (yamlHash && __classPrivateFieldGet(this, _MatimoInstance_approvalManifest, "f").isApproved(tool.name, yamlHash)) {
|
|
1031
|
+
return true;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
// 2. Invoke HITL callback
|
|
1035
|
+
if (__classPrivateFieldGet(this, _MatimoInstance_hitlCallback, "f")) {
|
|
1036
|
+
__classPrivateFieldGet(this, _MatimoInstance_instances, "m", _MatimoInstance_emitEvent).call(this, {
|
|
1037
|
+
type: 'tool:quarantined',
|
|
1038
|
+
toolName: tool.name,
|
|
1039
|
+
riskLevel: decision.riskLevel,
|
|
1040
|
+
reason: decision.reason,
|
|
1041
|
+
environment: context.environment,
|
|
1042
|
+
timestamp: new Date().toISOString(),
|
|
1043
|
+
});
|
|
1044
|
+
const approved = await __classPrivateFieldGet(this, _MatimoInstance_hitlCallback, "f").call(this, {
|
|
1045
|
+
toolName: tool.name,
|
|
1046
|
+
riskLevel: decision.riskLevel,
|
|
1047
|
+
reason: decision.reason,
|
|
1048
|
+
environment: context.environment,
|
|
1049
|
+
agentId: context.agentId,
|
|
1050
|
+
toolDefinition: tool,
|
|
1051
|
+
});
|
|
1052
|
+
// If approved, record in manifest for future calls
|
|
1053
|
+
if (approved && __classPrivateFieldGet(this, _MatimoInstance_approvalManifest, "f")) {
|
|
1054
|
+
const yamlHash = __classPrivateFieldGet(this, _MatimoInstance_integrityTracker, "f").getHash(tool.name) ??
|
|
1055
|
+
__classPrivateFieldGet(this, _MatimoInstance_approvalManifest, "f").computeHash(JSON.stringify(tool));
|
|
1056
|
+
__classPrivateFieldGet(this, _MatimoInstance_approvalManifest, "f").approve(tool.name, yamlHash);
|
|
1057
|
+
}
|
|
1058
|
+
return approved;
|
|
1059
|
+
}
|
|
1060
|
+
// 3. No callback — fail closed
|
|
1061
|
+
return false;
|
|
1062
|
+
};
|
|
474
1063
|
/**
|
|
475
1064
|
* Matimo namespace - Entry point for the SDK
|
|
476
1065
|
*/
|