@frontmcp/plugin-codecall 1.7.2 → 1.8.1
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/codecall.plugin.d.ts.map +1 -1
- package/esm/index.mjs +558 -79
- package/esm/package.json +5 -5
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +582 -101
- package/package.json +5 -5
- package/security/codecall-tool-policy.d.ts +141 -0
- package/security/codecall-tool-policy.d.ts.map +1 -0
- package/security/index.d.ts +1 -1
- package/security/index.d.ts.map +1 -1
- package/services/enclave.service.d.ts +7 -0
- package/services/enclave.service.d.ts.map +1 -1
- package/services/index.d.ts +0 -1
- package/services/index.d.ts.map +1 -1
- package/services/tool-search.service.d.ts +7 -15
- package/services/tool-search.service.d.ts.map +1 -1
- package/tools/describe.tool.d.ts.map +1 -1
- package/tools/execute.tool.d.ts +9 -0
- package/tools/execute.tool.d.ts.map +1 -1
- package/tools/invoke.tool.d.ts +2 -0
- package/tools/invoke.tool.d.ts.map +1 -1
- package/tools/search-knowledge.tool.d.ts.map +1 -1
- package/tools/search-skills.tool.d.ts.map +1 -1
- package/tools/search.tool.d.ts.map +1 -1
- package/security/tool-access-control.service.d.ts +0 -105
- package/security/tool-access-control.service.d.ts.map +0 -1
- package/services/error-enrichment.service.d.ts +0 -95
- package/services/error-enrichment.service.d.ts.map +0 -1
package/index.js
CHANGED
|
@@ -38,6 +38,8 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
38
38
|
// plugins/plugin-codecall/src/index.ts
|
|
39
39
|
var index_exports = {};
|
|
40
40
|
__export(index_exports, {
|
|
41
|
+
AUDIT_EVENT_TYPES: () => AUDIT_EVENT_TYPES,
|
|
42
|
+
AuditLoggerService: () => AuditLoggerService,
|
|
41
43
|
CodeCallPlugin: () => CodeCallPlugin,
|
|
42
44
|
codeCallDirectCallsOptionsSchema: () => codeCallDirectCallsOptionsSchema,
|
|
43
45
|
codeCallEmbeddingOptionsSchema: () => codeCallEmbeddingOptionsSchema,
|
|
@@ -54,7 +56,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
54
56
|
|
|
55
57
|
// plugins/plugin-codecall/src/codecall.plugin.ts
|
|
56
58
|
var import_plugin_cache = __toESM(require("@frontmcp/plugin-cache"));
|
|
57
|
-
var
|
|
59
|
+
var import_sdk10 = require("@frontmcp/sdk");
|
|
58
60
|
|
|
59
61
|
// plugins/plugin-codecall/src/codecall.types.ts
|
|
60
62
|
var import_lazy_zod = require("@frontmcp/lazy-zod");
|
|
@@ -409,9 +411,434 @@ function presetDefaults(preset) {
|
|
|
409
411
|
}
|
|
410
412
|
}
|
|
411
413
|
|
|
414
|
+
// plugins/plugin-codecall/src/services/audit-logger.service.ts
|
|
415
|
+
var import_sdk2 = require("@frontmcp/sdk");
|
|
416
|
+
var AUDIT_EVENT_TYPES = {
|
|
417
|
+
/** Script execution started */
|
|
418
|
+
EXECUTION_START: "codecall:execution:start",
|
|
419
|
+
/** Script execution completed successfully */
|
|
420
|
+
EXECUTION_SUCCESS: "codecall:execution:success",
|
|
421
|
+
/** Script execution failed */
|
|
422
|
+
EXECUTION_FAILURE: "codecall:execution:failure",
|
|
423
|
+
/** Script execution timed out */
|
|
424
|
+
EXECUTION_TIMEOUT: "codecall:execution:timeout",
|
|
425
|
+
/** Tool call initiated from script */
|
|
426
|
+
TOOL_CALL_START: "codecall:tool:call:start",
|
|
427
|
+
/** Tool call completed successfully */
|
|
428
|
+
TOOL_CALL_SUCCESS: "codecall:tool:call:success",
|
|
429
|
+
/** Tool call failed */
|
|
430
|
+
TOOL_CALL_FAILURE: "codecall:tool:call:failure",
|
|
431
|
+
/** Self-reference attack blocked */
|
|
432
|
+
SECURITY_SELF_REFERENCE: "codecall:security:self-reference",
|
|
433
|
+
/** Tool access denied */
|
|
434
|
+
SECURITY_ACCESS_DENIED: "codecall:security:access-denied",
|
|
435
|
+
/** AST validation failed (blocked code pattern) */
|
|
436
|
+
SECURITY_AST_BLOCKED: "codecall:security:ast-blocked",
|
|
437
|
+
/** Search performed */
|
|
438
|
+
SEARCH_PERFORMED: "codecall:search:performed",
|
|
439
|
+
/** Tool described */
|
|
440
|
+
DESCRIBE_PERFORMED: "codecall:describe:performed",
|
|
441
|
+
/** Direct invoke performed */
|
|
442
|
+
INVOKE_PERFORMED: "codecall:invoke:performed"
|
|
443
|
+
};
|
|
444
|
+
var AuditLoggerService = class {
|
|
445
|
+
listeners = /* @__PURE__ */ new Set();
|
|
446
|
+
executionCounter = 0;
|
|
447
|
+
/**
|
|
448
|
+
* Subscribe to audit events.
|
|
449
|
+
*
|
|
450
|
+
* @param listener - Function to call when events occur
|
|
451
|
+
* @returns Unsubscribe function
|
|
452
|
+
*/
|
|
453
|
+
subscribe(listener) {
|
|
454
|
+
this.listeners.add(listener);
|
|
455
|
+
return () => {
|
|
456
|
+
this.listeners.delete(listener);
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Generate a unique execution ID.
|
|
461
|
+
*/
|
|
462
|
+
generateExecutionId() {
|
|
463
|
+
const timestamp = Date.now().toString(36);
|
|
464
|
+
const counter = (++this.executionCounter).toString(36).padStart(4, "0");
|
|
465
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
466
|
+
return `exec_${timestamp}_${counter}_${random}`;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Log execution start.
|
|
470
|
+
*/
|
|
471
|
+
logExecutionStart(executionId, script) {
|
|
472
|
+
this.emit({
|
|
473
|
+
type: AUDIT_EVENT_TYPES.EXECUTION_START,
|
|
474
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
475
|
+
executionId,
|
|
476
|
+
data: {
|
|
477
|
+
scriptHash: this.hashScript(script),
|
|
478
|
+
scriptLength: script.length
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Log execution success.
|
|
484
|
+
*/
|
|
485
|
+
logExecutionSuccess(executionId, script, durationMs, toolCallCount) {
|
|
486
|
+
this.emit({
|
|
487
|
+
type: AUDIT_EVENT_TYPES.EXECUTION_SUCCESS,
|
|
488
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
489
|
+
executionId,
|
|
490
|
+
durationMs,
|
|
491
|
+
data: {
|
|
492
|
+
scriptHash: this.hashScript(script),
|
|
493
|
+
scriptLength: script.length,
|
|
494
|
+
toolCallCount
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Log execution failure.
|
|
500
|
+
*/
|
|
501
|
+
logExecutionFailure(executionId, script, durationMs, error) {
|
|
502
|
+
this.emit({
|
|
503
|
+
type: AUDIT_EVENT_TYPES.EXECUTION_FAILURE,
|
|
504
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
505
|
+
executionId,
|
|
506
|
+
durationMs,
|
|
507
|
+
data: {
|
|
508
|
+
scriptHash: this.hashScript(script),
|
|
509
|
+
scriptLength: script.length,
|
|
510
|
+
error: this.sanitizeError(error)
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Log execution timeout.
|
|
516
|
+
*/
|
|
517
|
+
logExecutionTimeout(executionId, script, durationMs) {
|
|
518
|
+
this.emit({
|
|
519
|
+
type: AUDIT_EVENT_TYPES.EXECUTION_TIMEOUT,
|
|
520
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
521
|
+
executionId,
|
|
522
|
+
durationMs,
|
|
523
|
+
data: {
|
|
524
|
+
scriptHash: this.hashScript(script),
|
|
525
|
+
scriptLength: script.length
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Log tool call start.
|
|
531
|
+
*/
|
|
532
|
+
logToolCallStart(executionId, toolName, callDepth) {
|
|
533
|
+
this.emit({
|
|
534
|
+
type: AUDIT_EVENT_TYPES.TOOL_CALL_START,
|
|
535
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
536
|
+
executionId,
|
|
537
|
+
data: {
|
|
538
|
+
toolName,
|
|
539
|
+
callDepth
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Log tool call success.
|
|
545
|
+
*/
|
|
546
|
+
logToolCallSuccess(executionId, toolName, callDepth, durationMs) {
|
|
547
|
+
this.emit({
|
|
548
|
+
type: AUDIT_EVENT_TYPES.TOOL_CALL_SUCCESS,
|
|
549
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
550
|
+
executionId,
|
|
551
|
+
durationMs,
|
|
552
|
+
data: {
|
|
553
|
+
toolName,
|
|
554
|
+
callDepth
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Log tool call failure.
|
|
560
|
+
*/
|
|
561
|
+
logToolCallFailure(executionId, toolName, callDepth, durationMs, errorCode) {
|
|
562
|
+
this.emit({
|
|
563
|
+
type: AUDIT_EVENT_TYPES.TOOL_CALL_FAILURE,
|
|
564
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
565
|
+
executionId,
|
|
566
|
+
durationMs,
|
|
567
|
+
data: {
|
|
568
|
+
toolName,
|
|
569
|
+
callDepth,
|
|
570
|
+
errorCode
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Log security event: self-reference blocked.
|
|
576
|
+
*/
|
|
577
|
+
logSecuritySelfReference(executionId, toolName) {
|
|
578
|
+
this.emit({
|
|
579
|
+
type: AUDIT_EVENT_TYPES.SECURITY_SELF_REFERENCE,
|
|
580
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
581
|
+
executionId,
|
|
582
|
+
data: {
|
|
583
|
+
blocked: toolName,
|
|
584
|
+
reason: "Self-reference attack: attempted to call CodeCall tool from within AgentScript"
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Log security event: access denied.
|
|
590
|
+
*/
|
|
591
|
+
logSecurityAccessDenied(executionId, toolName, reason) {
|
|
592
|
+
this.emit({
|
|
593
|
+
type: AUDIT_EVENT_TYPES.SECURITY_ACCESS_DENIED,
|
|
594
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
595
|
+
executionId,
|
|
596
|
+
data: {
|
|
597
|
+
blocked: toolName,
|
|
598
|
+
reason: this.sanitizeError(reason)
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Log security event: AST validation blocked.
|
|
604
|
+
*/
|
|
605
|
+
logSecurityAstBlocked(executionId, pattern, reason) {
|
|
606
|
+
this.emit({
|
|
607
|
+
type: AUDIT_EVENT_TYPES.SECURITY_AST_BLOCKED,
|
|
608
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
609
|
+
executionId,
|
|
610
|
+
data: {
|
|
611
|
+
blocked: pattern,
|
|
612
|
+
reason: this.sanitizeError(reason)
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Log search operation.
|
|
618
|
+
*/
|
|
619
|
+
logSearch(executionId, query, resultCount, durationMs) {
|
|
620
|
+
this.emit({
|
|
621
|
+
type: AUDIT_EVENT_TYPES.SEARCH_PERFORMED,
|
|
622
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
623
|
+
executionId,
|
|
624
|
+
durationMs,
|
|
625
|
+
data: {
|
|
626
|
+
queryLength: query.length,
|
|
627
|
+
resultCount
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Log describe operation.
|
|
633
|
+
*/
|
|
634
|
+
logDescribe(executionId, toolNames, durationMs) {
|
|
635
|
+
this.emit({
|
|
636
|
+
type: AUDIT_EVENT_TYPES.DESCRIBE_PERFORMED,
|
|
637
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
638
|
+
executionId,
|
|
639
|
+
durationMs,
|
|
640
|
+
data: {
|
|
641
|
+
toolCount: toolNames.length,
|
|
642
|
+
toolNames: toolNames.slice(0, 10)
|
|
643
|
+
// Limit to first 10 for audit
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Log invoke operation.
|
|
649
|
+
*/
|
|
650
|
+
logInvoke(executionId, toolName, success, durationMs) {
|
|
651
|
+
this.emit({
|
|
652
|
+
type: AUDIT_EVENT_TYPES.INVOKE_PERFORMED,
|
|
653
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
654
|
+
executionId,
|
|
655
|
+
durationMs,
|
|
656
|
+
data: {
|
|
657
|
+
toolName,
|
|
658
|
+
success
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Emit an audit event to all listeners.
|
|
664
|
+
*/
|
|
665
|
+
emit(event) {
|
|
666
|
+
const frozenEvent = Object.freeze({ ...event, data: Object.freeze({ ...event.data }) });
|
|
667
|
+
for (const listener of this.listeners) {
|
|
668
|
+
try {
|
|
669
|
+
listener(frozenEvent);
|
|
670
|
+
} catch {
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Create a simple hash of the script for identification.
|
|
676
|
+
* Uses a fast, non-cryptographic hash for performance.
|
|
677
|
+
*/
|
|
678
|
+
hashScript(script) {
|
|
679
|
+
let hash = 0;
|
|
680
|
+
for (let i = 0; i < script.length; i++) {
|
|
681
|
+
const char = script.charCodeAt(i);
|
|
682
|
+
hash = (hash << 5) - hash + char;
|
|
683
|
+
hash = hash & hash;
|
|
684
|
+
}
|
|
685
|
+
return `sh_${(hash >>> 0).toString(16).padStart(8, "0")}`;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Sanitize error messages to remove sensitive information.
|
|
689
|
+
*/
|
|
690
|
+
sanitizeError(error) {
|
|
691
|
+
if (!error) return "Unknown error";
|
|
692
|
+
let sanitized = error.replace(/(?:\/[\w.-]+)+|(?:[A-Za-z]:\\[\w\\.-]+)+/g, "[path]");
|
|
693
|
+
sanitized = sanitized.replace(/:\d+:\d+/g, "");
|
|
694
|
+
sanitized = sanitized.replace(/\n\s*at .*/g, "");
|
|
695
|
+
if (sanitized.length > 200) {
|
|
696
|
+
sanitized = sanitized.substring(0, 200) + "...";
|
|
697
|
+
}
|
|
698
|
+
return sanitized.trim();
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
AuditLoggerService = __decorateClass([
|
|
702
|
+
(0, import_sdk2.Provider)({
|
|
703
|
+
name: "codecall:audit-logger",
|
|
704
|
+
scope: import_sdk2.ProviderScope.GLOBAL
|
|
705
|
+
})
|
|
706
|
+
], AuditLoggerService);
|
|
707
|
+
|
|
412
708
|
// plugins/plugin-codecall/src/services/tool-search.service.ts
|
|
413
709
|
var import_vectoriadb = require("vectoriadb");
|
|
414
710
|
|
|
711
|
+
// plugins/plugin-codecall/src/security/codecall-tool-policy.ts
|
|
712
|
+
var BLOCKED_NAMESPACE_PATTERNS = Object.freeze([/^system:/, /^internal:/, /^__/]);
|
|
713
|
+
var ALLOWED = { allowed: true };
|
|
714
|
+
function deny(reason) {
|
|
715
|
+
return { allowed: false, reason };
|
|
716
|
+
}
|
|
717
|
+
function codeCallAppIdOf(scope, entry) {
|
|
718
|
+
const lineage = scope?.tools?.lineageOf?.(entry) ?? [];
|
|
719
|
+
const owners = entry.owner ? [...lineage, entry.owner] : lineage;
|
|
720
|
+
return owners.find((owner) => owner.kind === "app")?.id;
|
|
721
|
+
}
|
|
722
|
+
function toCodeCallPolicyTool(entry, requestedName, scope) {
|
|
723
|
+
const { metadata } = entry;
|
|
724
|
+
return {
|
|
725
|
+
name: entry.name || entry.fullName,
|
|
726
|
+
fullName: entry.fullName,
|
|
727
|
+
aliases: requestedName ? [requestedName] : void 0,
|
|
728
|
+
appId: codeCallAppIdOf(scope, entry),
|
|
729
|
+
description: metadata?.description,
|
|
730
|
+
tags: metadata?.tags,
|
|
731
|
+
// `internal` tools refuse every external tools/call, CodeCall's included, so no surface may offer them.
|
|
732
|
+
hidden: metadata?.hideFromDiscovery === true || metadata?.visibility === "hidden" || metadata?.visibility === "internal",
|
|
733
|
+
codecall: metadata?.codecall
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
function toToolFilterInfo(tool) {
|
|
737
|
+
return {
|
|
738
|
+
name: tool.name,
|
|
739
|
+
appId: tool.appId,
|
|
740
|
+
source: tool.codecall?.source,
|
|
741
|
+
description: tool.description,
|
|
742
|
+
tags: tool.codecall?.tags ?? tool.tags
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function ownNamesOf(tool) {
|
|
746
|
+
return [tool.name, tool.fullName].filter((candidate) => !!candidate);
|
|
747
|
+
}
|
|
748
|
+
function checkCodeCallToolPolicy(tool, config) {
|
|
749
|
+
const { name } = tool;
|
|
750
|
+
const spellings = [...ownNamesOf(tool), ...tool.aliases ?? []];
|
|
751
|
+
if (spellings.some((candidate) => candidate.startsWith("codecall:"))) {
|
|
752
|
+
return deny("CodeCall meta-tools are not callable from CodeCall");
|
|
753
|
+
}
|
|
754
|
+
for (const candidate of spellings) {
|
|
755
|
+
if (BLOCKED_NAMESPACE_PATTERNS.some((pattern) => pattern.test(candidate))) {
|
|
756
|
+
return deny(`Tool "${candidate}" is in a namespace CodeCall never calls`);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
if (tool.hidden === true) {
|
|
760
|
+
return deny(`Tool "${name}" is hidden from discovery`);
|
|
761
|
+
}
|
|
762
|
+
const enabled = tool.codecall?.enabledInCodeCall;
|
|
763
|
+
switch (config.mode) {
|
|
764
|
+
case "codecall_only":
|
|
765
|
+
case "metadata_driven":
|
|
766
|
+
if (enabled === false) {
|
|
767
|
+
return deny(`Tool "${name}" sets enabledInCodeCall: false`);
|
|
768
|
+
}
|
|
769
|
+
break;
|
|
770
|
+
case "codecall_opt_in":
|
|
771
|
+
if (enabled !== true) {
|
|
772
|
+
return deny(`Tool "${name}" has not opted in to CodeCall`);
|
|
773
|
+
}
|
|
774
|
+
break;
|
|
775
|
+
default:
|
|
776
|
+
return deny(`Unknown CodeCall mode: ${String(config.mode)}`);
|
|
777
|
+
}
|
|
778
|
+
if (config.includeTools && !config.includeTools(toToolFilterInfo(tool))) {
|
|
779
|
+
return deny(`Tool "${name}" is excluded by the includeTools filter`);
|
|
780
|
+
}
|
|
781
|
+
return ALLOWED;
|
|
782
|
+
}
|
|
783
|
+
function denyUnknownTool(name) {
|
|
784
|
+
return deny(`Tool "${name}" is not available through CodeCall`);
|
|
785
|
+
}
|
|
786
|
+
function resolveCodeCallTool(scope, name) {
|
|
787
|
+
const typedScope = scope;
|
|
788
|
+
const alias = /[-_]/.test(name) ? name.includes("_") ? name.replace(/_/g, "-") : name.replace(/-/g, "_") : void 0;
|
|
789
|
+
const candidates = alias ? [name, alias] : [name];
|
|
790
|
+
const matches = (entry) => candidates.includes(entry.fullName) || candidates.includes(entry.name);
|
|
791
|
+
const local = typedScope.tools?.getTools(true)?.find(matches);
|
|
792
|
+
if (local) return local;
|
|
793
|
+
const registries = typedScope.providers?.getRegistries?.("AppRegistry") ?? [];
|
|
794
|
+
for (const registry of registries) {
|
|
795
|
+
for (const app of registry.getApps()) {
|
|
796
|
+
if (!app.isRemote) continue;
|
|
797
|
+
const remote = app.tools.getTools(true).find(matches);
|
|
798
|
+
if (remote) return remote;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
return void 0;
|
|
802
|
+
}
|
|
803
|
+
function checkDirectCallPolicy(tool, directCalls) {
|
|
804
|
+
if (!directCalls) return ALLOWED;
|
|
805
|
+
if (directCalls.enabled === false) {
|
|
806
|
+
return deny("Direct tool invocation is disabled");
|
|
807
|
+
}
|
|
808
|
+
const { allowedTools } = directCalls;
|
|
809
|
+
if (allowedTools && !ownNamesOf(tool).some((ownName) => allowedTools.includes(ownName))) {
|
|
810
|
+
return deny(`Tool "${tool.name}" is not in the direct-call allowlist`);
|
|
811
|
+
}
|
|
812
|
+
if (directCalls.filter && !directCalls.filter(toToolFilterInfo(tool))) {
|
|
813
|
+
return deny(`Tool "${tool.name}" is excluded by the directCalls filter`);
|
|
814
|
+
}
|
|
815
|
+
return ALLOWED;
|
|
816
|
+
}
|
|
817
|
+
function readCodeCallPolicyConfig(config) {
|
|
818
|
+
return {
|
|
819
|
+
// An absent mode means an unparsed/partial config, not a hostile one: fall back to the
|
|
820
|
+
// schema's own documented default rather than denying every call. An unrecognised mode
|
|
821
|
+
// still fails closed inside the policy's switch.
|
|
822
|
+
mode: config.get("mode") ?? "codecall_only",
|
|
823
|
+
includeTools: config.get("includeTools")
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
function checkCodeCallToolAccess(scope, config, name, options = {}) {
|
|
827
|
+
const entry = resolveCodeCallTool(scope, name);
|
|
828
|
+
if (!entry) return denyUnknownTool(name);
|
|
829
|
+
const policyTool = toCodeCallPolicyTool(entry, name, scope);
|
|
830
|
+
const baseDecision = checkCodeCallToolPolicy(policyTool, readCodeCallPolicyConfig(config));
|
|
831
|
+
if (!baseDecision.allowed) return baseDecision;
|
|
832
|
+
if (options.directCall) {
|
|
833
|
+
const directDecision = checkDirectCallPolicy(
|
|
834
|
+
policyTool,
|
|
835
|
+
config.get("directCalls")
|
|
836
|
+
);
|
|
837
|
+
if (!directDecision.allowed) return directDecision;
|
|
838
|
+
}
|
|
839
|
+
return { allowed: true, entry };
|
|
840
|
+
}
|
|
841
|
+
|
|
415
842
|
// plugins/plugin-codecall/src/services/synonym-expansion.service.ts
|
|
416
843
|
var DEFAULT_SYNONYM_GROUPS = [
|
|
417
844
|
// ===========================================================================
|
|
@@ -1080,62 +1507,12 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1080
1507
|
this.initialized = true;
|
|
1081
1508
|
}
|
|
1082
1509
|
/**
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1085
|
-
*
|
|
1086
|
-
* - Excludes tools with hideFromDiscovery: true (system tools like sendElicitationResult)
|
|
1087
|
-
* - Mode-based filtering (codecall_only, codecall_opt_in, metadata_driven)
|
|
1088
|
-
* - Per-tool metadata.codecall.enabledInCodeCall
|
|
1089
|
-
* - Custom includeTools filter function
|
|
1510
|
+
* Whether a tool belongs in the search index: exactly when CodeCall may execute it.
|
|
1511
|
+
* The decision is `codecall:execute`'s own, so search cannot show a tool execution refuses
|
|
1512
|
+
* or hide one it runs (GHSA-6w3j-82v5-6qrr).
|
|
1090
1513
|
*/
|
|
1091
1514
|
shouldIndexTool(tool) {
|
|
1092
|
-
|
|
1093
|
-
if (toolName.startsWith("codecall:")) {
|
|
1094
|
-
return false;
|
|
1095
|
-
}
|
|
1096
|
-
if (tool.metadata.hideFromDiscovery === true) {
|
|
1097
|
-
return false;
|
|
1098
|
-
}
|
|
1099
|
-
const codecallMeta = this.getCodeCallMetadata(tool);
|
|
1100
|
-
switch (this.config.mode) {
|
|
1101
|
-
case "codecall_only":
|
|
1102
|
-
if (codecallMeta?.enabledInCodeCall === false) {
|
|
1103
|
-
return false;
|
|
1104
|
-
}
|
|
1105
|
-
break;
|
|
1106
|
-
case "codecall_opt_in":
|
|
1107
|
-
if (codecallMeta?.enabledInCodeCall !== true) {
|
|
1108
|
-
return false;
|
|
1109
|
-
}
|
|
1110
|
-
break;
|
|
1111
|
-
case "metadata_driven":
|
|
1112
|
-
if (codecallMeta?.enabledInCodeCall === false) {
|
|
1113
|
-
return false;
|
|
1114
|
-
}
|
|
1115
|
-
break;
|
|
1116
|
-
default:
|
|
1117
|
-
throw new Error(`Unknown CodeCall mode: ${this.config.mode}`);
|
|
1118
|
-
}
|
|
1119
|
-
if (this.config.includeTools) {
|
|
1120
|
-
const appId = this.extractAppId(tool);
|
|
1121
|
-
const filterInfo = {
|
|
1122
|
-
name: toolName,
|
|
1123
|
-
appId,
|
|
1124
|
-
source: codecallMeta?.source,
|
|
1125
|
-
description: tool.metadata.description,
|
|
1126
|
-
tags: codecallMeta?.tags || tool.metadata.tags
|
|
1127
|
-
};
|
|
1128
|
-
if (!this.config.includeTools(filterInfo)) {
|
|
1129
|
-
return false;
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
return true;
|
|
1133
|
-
}
|
|
1134
|
-
/**
|
|
1135
|
-
* Extract CodeCall-specific metadata from a tool.
|
|
1136
|
-
*/
|
|
1137
|
-
getCodeCallMetadata(tool) {
|
|
1138
|
-
return tool.metadata?.codecall;
|
|
1515
|
+
return checkCodeCallToolPolicy(toCodeCallPolicyTool(tool, void 0, this.scope), this.config).allowed;
|
|
1139
1516
|
}
|
|
1140
1517
|
/**
|
|
1141
1518
|
* Initializes the search service by indexing all tools from the registry.
|
|
@@ -1225,11 +1602,7 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1225
1602
|
* Extracts app ID from tool's owner lineage
|
|
1226
1603
|
*/
|
|
1227
1604
|
extractAppId(tool) {
|
|
1228
|
-
|
|
1229
|
-
if (tool.owner.kind === "app") {
|
|
1230
|
-
return tool.owner.id;
|
|
1231
|
-
}
|
|
1232
|
-
return void 0;
|
|
1605
|
+
return codeCallAppIdOf(this.scope, tool);
|
|
1233
1606
|
}
|
|
1234
1607
|
/**
|
|
1235
1608
|
* Searches for tools matching the query
|
|
@@ -1316,7 +1689,7 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1316
1689
|
|
|
1317
1690
|
// plugins/plugin-codecall/src/services/enclave.service.ts
|
|
1318
1691
|
var import_core = require("@enclave-vm/core");
|
|
1319
|
-
var
|
|
1692
|
+
var import_sdk3 = require("@frontmcp/sdk");
|
|
1320
1693
|
var ScriptTooLargeError = class extends Error {
|
|
1321
1694
|
code = "SCRIPT_TOO_LARGE";
|
|
1322
1695
|
scriptLength;
|
|
@@ -1435,15 +1808,23 @@ var EnclaveService = class {
|
|
|
1435
1808
|
const error = result.error;
|
|
1436
1809
|
const timedOut = error.message?.includes("timed out") || error.code === "TIMEOUT";
|
|
1437
1810
|
if (error.code === "VALIDATION_ERROR") {
|
|
1811
|
+
const issues = error.data?.issues ?? [];
|
|
1812
|
+
const blockedPatterns = [...new Set(issues.map((issue) => issue.code).filter((code) => !!code))];
|
|
1438
1813
|
return {
|
|
1439
1814
|
success: false,
|
|
1440
1815
|
error: {
|
|
1441
1816
|
message: error.message,
|
|
1442
1817
|
name: "ValidationError",
|
|
1443
|
-
code: error.code
|
|
1818
|
+
code: error.code,
|
|
1819
|
+
...blockedPatterns.length > 0 ? { blockedPatterns } : {}
|
|
1444
1820
|
},
|
|
1445
1821
|
logs,
|
|
1446
|
-
timedOut: false
|
|
1822
|
+
timedOut: false,
|
|
1823
|
+
stats: {
|
|
1824
|
+
duration: result.stats.duration,
|
|
1825
|
+
toolCallCount: result.stats.toolCallCount,
|
|
1826
|
+
iterationCount: result.stats.iterationCount
|
|
1827
|
+
}
|
|
1447
1828
|
};
|
|
1448
1829
|
}
|
|
1449
1830
|
const errorData = error.data;
|
|
@@ -1483,15 +1864,15 @@ var EnclaveService = class {
|
|
|
1483
1864
|
}
|
|
1484
1865
|
};
|
|
1485
1866
|
EnclaveService = __decorateClass([
|
|
1486
|
-
(0,
|
|
1867
|
+
(0, import_sdk3.Provider)({
|
|
1487
1868
|
name: "codecall:enclave",
|
|
1488
1869
|
description: "Executes AgentScript code in a secure enclave",
|
|
1489
|
-
scope:
|
|
1870
|
+
scope: import_sdk3.ProviderScope.GLOBAL
|
|
1490
1871
|
})
|
|
1491
1872
|
], EnclaveService);
|
|
1492
1873
|
|
|
1493
1874
|
// plugins/plugin-codecall/src/tools/search.tool.ts
|
|
1494
|
-
var
|
|
1875
|
+
var import_sdk4 = require("@frontmcp/sdk");
|
|
1495
1876
|
|
|
1496
1877
|
// plugins/plugin-codecall/src/tools/search.schema.ts
|
|
1497
1878
|
var import_lazy_zod2 = require("@frontmcp/lazy-zod");
|
|
@@ -1541,11 +1922,14 @@ var searchToolOutputSchema = import_lazy_zod2.z.object({
|
|
|
1541
1922
|
});
|
|
1542
1923
|
|
|
1543
1924
|
// plugins/plugin-codecall/src/tools/search.tool.ts
|
|
1544
|
-
var SearchTool = class extends
|
|
1925
|
+
var SearchTool = class extends import_sdk4.ToolContext {
|
|
1545
1926
|
async execute(input) {
|
|
1546
1927
|
const { queries, appIds, excludeToolNames = [], topK = 5, minRelevanceScore = 0.3 } = input;
|
|
1547
1928
|
const searchService = this.get(ToolSearchService);
|
|
1548
1929
|
const warnings = [];
|
|
1930
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
1931
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
1932
|
+
const startedAt = Date.now();
|
|
1549
1933
|
const nonExistentExcludedTools = excludeToolNames.filter((toolName) => !searchService.hasTool(toolName));
|
|
1550
1934
|
if (nonExistentExcludedTools.length > 0) {
|
|
1551
1935
|
warnings.push({
|
|
@@ -1601,6 +1985,7 @@ var SearchTool = class extends import_sdk3.ToolContext {
|
|
|
1601
1985
|
message: `${lowRelevanceCount} result(s) filtered due to relevance below ${minRelevanceScore}`
|
|
1602
1986
|
});
|
|
1603
1987
|
}
|
|
1988
|
+
audit?.logSearch(executionId, queries.join(" "), tools.length, Date.now() - startedAt);
|
|
1604
1989
|
return {
|
|
1605
1990
|
tools,
|
|
1606
1991
|
warnings,
|
|
@@ -1609,7 +1994,7 @@ var SearchTool = class extends import_sdk3.ToolContext {
|
|
|
1609
1994
|
}
|
|
1610
1995
|
};
|
|
1611
1996
|
SearchTool = __decorateClass([
|
|
1612
|
-
(0,
|
|
1997
|
+
(0, import_sdk4.Tool)({
|
|
1613
1998
|
name: "codecall:search",
|
|
1614
1999
|
cache: {
|
|
1615
2000
|
ttl: 60,
|
|
@@ -1631,7 +2016,7 @@ SearchTool = __decorateClass([
|
|
|
1631
2016
|
], SearchTool);
|
|
1632
2017
|
|
|
1633
2018
|
// plugins/plugin-codecall/src/tools/search-skills.tool.ts
|
|
1634
|
-
var
|
|
2019
|
+
var import_sdk5 = require("@frontmcp/sdk");
|
|
1635
2020
|
|
|
1636
2021
|
// plugins/plugin-codecall/src/tools/search-skills.schema.ts
|
|
1637
2022
|
var import_lazy_zod3 = require("@frontmcp/lazy-zod");
|
|
@@ -1688,11 +2073,11 @@ var searchSkillsToolOutputSchema = import_lazy_zod3.z.object({
|
|
|
1688
2073
|
});
|
|
1689
2074
|
|
|
1690
2075
|
// plugins/plugin-codecall/src/tools/search-skills.tool.ts
|
|
1691
|
-
var SearchSkillsTool = class extends
|
|
2076
|
+
var SearchSkillsTool = class extends import_sdk5.ToolContext {
|
|
1692
2077
|
async execute(input) {
|
|
1693
2078
|
const { queries, tags, excludeSkillNames = [], topK = 5, minRelevanceScore = 0.1 } = input;
|
|
1694
2079
|
const skillRegistry = this.scope.skills;
|
|
1695
|
-
const executableSkills = skillRegistry.getExecutableSkills();
|
|
2080
|
+
const executableSkills = await (0, import_sdk5.filterServableSkills)(this.scope, skillRegistry.getExecutableSkills());
|
|
1696
2081
|
const executableSet = new Set(executableSkills.map((s) => s.name));
|
|
1697
2082
|
const totalExecutableSkills = executableSkills.length;
|
|
1698
2083
|
const warnings = [];
|
|
@@ -1762,7 +2147,7 @@ var SearchSkillsTool = class extends import_sdk4.ToolContext {
|
|
|
1762
2147
|
}
|
|
1763
2148
|
};
|
|
1764
2149
|
SearchSkillsTool = __decorateClass([
|
|
1765
|
-
(0,
|
|
2150
|
+
(0, import_sdk5.Tool)({
|
|
1766
2151
|
name: "codecall:searchSkills",
|
|
1767
2152
|
cache: {
|
|
1768
2153
|
ttl: 60,
|
|
@@ -1783,7 +2168,7 @@ SearchSkillsTool = __decorateClass([
|
|
|
1783
2168
|
], SearchSkillsTool);
|
|
1784
2169
|
|
|
1785
2170
|
// plugins/plugin-codecall/src/tools/search-knowledge.tool.ts
|
|
1786
|
-
var
|
|
2171
|
+
var import_sdk6 = require("@frontmcp/sdk");
|
|
1787
2172
|
|
|
1788
2173
|
// plugins/plugin-codecall/src/tools/search-knowledge.schema.ts
|
|
1789
2174
|
var import_lazy_zod4 = require("@frontmcp/lazy-zod");
|
|
@@ -1829,11 +2214,11 @@ var searchKnowledgeToolOutputSchema = import_lazy_zod4.z.object({
|
|
|
1829
2214
|
});
|
|
1830
2215
|
|
|
1831
2216
|
// plugins/plugin-codecall/src/tools/search-knowledge.tool.ts
|
|
1832
|
-
var SearchKnowledgeTool = class extends
|
|
2217
|
+
var SearchKnowledgeTool = class extends import_sdk6.ToolContext {
|
|
1833
2218
|
async execute(input) {
|
|
1834
2219
|
const { queries, tags, excludeSkillNames = [], topK = 5, minRelevanceScore = 0.1 } = input;
|
|
1835
2220
|
const skillRegistry = this.scope.skills;
|
|
1836
|
-
const knowledgeSkills = skillRegistry.getKnowledgeOnlySkills();
|
|
2221
|
+
const knowledgeSkills = await (0, import_sdk6.filterServableSkills)(this.scope, skillRegistry.getKnowledgeOnlySkills());
|
|
1837
2222
|
const knowledgeSet = new Set(knowledgeSkills.map((s) => s.name));
|
|
1838
2223
|
const totalKnowledgeSkills = knowledgeSkills.length;
|
|
1839
2224
|
const warnings = [];
|
|
@@ -1895,7 +2280,7 @@ var SearchKnowledgeTool = class extends import_sdk5.ToolContext {
|
|
|
1895
2280
|
}
|
|
1896
2281
|
};
|
|
1897
2282
|
SearchKnowledgeTool = __decorateClass([
|
|
1898
|
-
(0,
|
|
2283
|
+
(0, import_sdk6.Tool)({
|
|
1899
2284
|
name: "codecall:searchKnowledge",
|
|
1900
2285
|
cache: {
|
|
1901
2286
|
ttl: 60,
|
|
@@ -1917,7 +2302,7 @@ SearchKnowledgeTool = __decorateClass([
|
|
|
1917
2302
|
|
|
1918
2303
|
// plugins/plugin-codecall/src/tools/describe.tool.ts
|
|
1919
2304
|
var import_lazy_zod6 = require("@frontmcp/lazy-zod");
|
|
1920
|
-
var
|
|
2305
|
+
var import_sdk7 = require("@frontmcp/sdk");
|
|
1921
2306
|
|
|
1922
2307
|
// plugins/plugin-codecall/src/errors/tool-call.errors.ts
|
|
1923
2308
|
var TOOL_CALL_ERROR_CODES = {
|
|
@@ -2418,24 +2803,27 @@ var describeToolOutputSchema = import_lazy_zod5.z.object({
|
|
|
2418
2803
|
});
|
|
2419
2804
|
|
|
2420
2805
|
// plugins/plugin-codecall/src/tools/describe.tool.ts
|
|
2421
|
-
var DescribeTool = class extends
|
|
2806
|
+
var DescribeTool = class extends import_sdk7.ToolContext {
|
|
2422
2807
|
async execute(input) {
|
|
2423
2808
|
const { toolNames } = input;
|
|
2809
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
2810
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
2811
|
+
const startedAt = Date.now();
|
|
2424
2812
|
const tools = [];
|
|
2425
2813
|
const notFound = [];
|
|
2426
|
-
const
|
|
2427
|
-
const toolMap = new Map(allTools.map((t) => [t.name, t]));
|
|
2428
|
-
const fullNameMap = new Map(allTools.map((t) => [t.fullName, t]));
|
|
2814
|
+
const config = this.get(CodeCallConfig);
|
|
2429
2815
|
for (const toolName of toolNames) {
|
|
2430
2816
|
if (isBlockedSelfReference(toolName)) {
|
|
2431
2817
|
notFound.push(toolName);
|
|
2432
2818
|
continue;
|
|
2433
2819
|
}
|
|
2434
|
-
const
|
|
2435
|
-
if (!
|
|
2820
|
+
const access = checkCodeCallToolAccess(this.scope, config, toolName);
|
|
2821
|
+
if (!access.allowed) {
|
|
2822
|
+
audit?.logSecurityAccessDenied(executionId, toolName, access.reason);
|
|
2436
2823
|
notFound.push(toolName);
|
|
2437
2824
|
continue;
|
|
2438
2825
|
}
|
|
2826
|
+
const tool = access.entry;
|
|
2439
2827
|
const appId = this.extractAppId(tool);
|
|
2440
2828
|
const inputSchema = this.getInputSchema(tool);
|
|
2441
2829
|
const outputSchema = this.toJsonSchema(tool.rawOutputSchema ?? tool.outputSchema);
|
|
@@ -2450,6 +2838,7 @@ var DescribeTool = class extends import_sdk6.ToolContext {
|
|
|
2450
2838
|
usageExamples
|
|
2451
2839
|
});
|
|
2452
2840
|
}
|
|
2841
|
+
audit?.logDescribe(executionId, toolNames, Date.now() - startedAt);
|
|
2453
2842
|
return {
|
|
2454
2843
|
tools,
|
|
2455
2844
|
notFound: notFound.length > 0 ? notFound : void 0
|
|
@@ -2564,7 +2953,7 @@ return result;`
|
|
|
2564
2953
|
}
|
|
2565
2954
|
};
|
|
2566
2955
|
DescribeTool = __decorateClass([
|
|
2567
|
-
(0,
|
|
2956
|
+
(0, import_sdk7.Tool)({
|
|
2568
2957
|
name: "codecall:describe",
|
|
2569
2958
|
cache: {
|
|
2570
2959
|
ttl: 60,
|
|
@@ -2586,7 +2975,7 @@ DescribeTool = __decorateClass([
|
|
|
2586
2975
|
], DescribeTool);
|
|
2587
2976
|
|
|
2588
2977
|
// plugins/plugin-codecall/src/tools/execute.tool.ts
|
|
2589
|
-
var
|
|
2978
|
+
var import_sdk8 = require("@frontmcp/sdk");
|
|
2590
2979
|
|
|
2591
2980
|
// plugins/plugin-codecall/src/tools/execute.schema.ts
|
|
2592
2981
|
var import_lazy_zod7 = require("@frontmcp/lazy-zod");
|
|
@@ -2703,21 +3092,52 @@ function getErrorCode(error) {
|
|
|
2703
3092
|
}
|
|
2704
3093
|
return TOOL_CALL_ERROR_CODES.EXECUTION;
|
|
2705
3094
|
}
|
|
2706
|
-
var ExecuteTool = class extends
|
|
3095
|
+
var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
3096
|
+
/**
|
|
3097
|
+
* Resolve the server-side CodeCall policy for a tool name (GHSA-6w3j-82v5-6qrr).
|
|
3098
|
+
*
|
|
3099
|
+
* Shares its decision with discovery and with `codecall:invoke`, so the three cannot
|
|
3100
|
+
* diverge. A name matching no registered tool is denied rather than passed through —
|
|
3101
|
+
* otherwise it would skip every metadata-driven check and reach `tools:call-tool`
|
|
3102
|
+
* unexamined.
|
|
3103
|
+
*/
|
|
3104
|
+
checkToolPolicy(name) {
|
|
3105
|
+
return checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), name);
|
|
3106
|
+
}
|
|
2707
3107
|
async execute(input) {
|
|
2708
3108
|
const { script, allowedTools } = input;
|
|
3109
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
3110
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
3111
|
+
const callDepth = 1;
|
|
3112
|
+
let toolCallCount = 0;
|
|
2709
3113
|
const allowedToolSet = allowedTools ? new Set(allowedTools) : null;
|
|
2710
3114
|
const environment = {
|
|
2711
3115
|
callTool: async (name, toolInput, options) => {
|
|
2712
3116
|
const throwOnError = options?.throwOnError !== false;
|
|
3117
|
+
if (isBlockedSelfReference(name)) {
|
|
3118
|
+
audit?.logSecuritySelfReference(executionId, name);
|
|
3119
|
+
}
|
|
2713
3120
|
assertNotSelfReference(name);
|
|
3121
|
+
const decision = this.checkToolPolicy(name);
|
|
3122
|
+
if (!decision.allowed) {
|
|
3123
|
+
audit?.logSecurityAccessDenied(executionId, name, decision.reason);
|
|
3124
|
+
const error = createToolCallError(TOOL_CALL_ERROR_CODES.ACCESS_DENIED, name, decision.reason);
|
|
3125
|
+
if (throwOnError) {
|
|
3126
|
+
throw error;
|
|
3127
|
+
}
|
|
3128
|
+
return { success: false, error };
|
|
3129
|
+
}
|
|
2714
3130
|
if (allowedToolSet && !allowedToolSet.has(name)) {
|
|
3131
|
+
audit?.logSecurityAccessDenied(executionId, name, "not in the caller-supplied allowedTools list");
|
|
2715
3132
|
const error = createToolCallError(TOOL_CALL_ERROR_CODES.ACCESS_DENIED, name);
|
|
2716
3133
|
if (throwOnError) {
|
|
2717
3134
|
throw error;
|
|
2718
3135
|
}
|
|
2719
3136
|
return { success: false, error };
|
|
2720
3137
|
}
|
|
3138
|
+
toolCallCount += 1;
|
|
3139
|
+
audit?.logToolCallStart(executionId, name, callDepth);
|
|
3140
|
+
const toolCallStartedAt = Date.now();
|
|
2721
3141
|
try {
|
|
2722
3142
|
const request = {
|
|
2723
3143
|
method: "tools/call",
|
|
@@ -2731,6 +3151,13 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2731
3151
|
};
|
|
2732
3152
|
const mcpResult = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
2733
3153
|
if (!mcpResult) {
|
|
3154
|
+
audit?.logToolCallFailure(
|
|
3155
|
+
executionId,
|
|
3156
|
+
name,
|
|
3157
|
+
callDepth,
|
|
3158
|
+
Date.now() - toolCallStartedAt,
|
|
3159
|
+
TOOL_CALL_ERROR_CODES.EXECUTION
|
|
3160
|
+
);
|
|
2734
3161
|
const error = createToolCallError(TOOL_CALL_ERROR_CODES.EXECUTION, name, "Flow returned no result");
|
|
2735
3162
|
if (throwOnError) {
|
|
2736
3163
|
throw error;
|
|
@@ -2738,6 +3165,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2738
3165
|
return { success: false, error };
|
|
2739
3166
|
}
|
|
2740
3167
|
const result = extractResultFromCallToolResult(mcpResult);
|
|
3168
|
+
audit?.logToolCallSuccess(executionId, name, callDepth, Date.now() - toolCallStartedAt);
|
|
2741
3169
|
if (throwOnError) {
|
|
2742
3170
|
return result;
|
|
2743
3171
|
}
|
|
@@ -2745,6 +3173,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2745
3173
|
} catch (error) {
|
|
2746
3174
|
const errorCode = getErrorCode(error);
|
|
2747
3175
|
const rawMessage = error instanceof Error ? error.message : void 0;
|
|
3176
|
+
audit?.logToolCallFailure(executionId, name, callDepth, Date.now() - toolCallStartedAt, errorCode);
|
|
2748
3177
|
const sanitizedError = createToolCallError(errorCode, name, rawMessage);
|
|
2749
3178
|
if (throwOnError) {
|
|
2750
3179
|
throw sanitizedError;
|
|
@@ -2754,11 +3183,17 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2754
3183
|
},
|
|
2755
3184
|
getTool: (name) => {
|
|
2756
3185
|
try {
|
|
2757
|
-
if (isBlockedSelfReference(name))
|
|
3186
|
+
if (isBlockedSelfReference(name)) {
|
|
3187
|
+
audit?.logSecuritySelfReference(executionId, name);
|
|
3188
|
+
return void 0;
|
|
3189
|
+
}
|
|
3190
|
+
const introspectionAccess = this.checkToolPolicy(name);
|
|
3191
|
+
if (!introspectionAccess.allowed) {
|
|
3192
|
+
audit?.logSecurityAccessDenied(executionId, name, introspectionAccess.reason);
|
|
3193
|
+
return void 0;
|
|
3194
|
+
}
|
|
2758
3195
|
if (allowedToolSet && !allowedToolSet.has(name)) return void 0;
|
|
2759
|
-
const
|
|
2760
|
-
const tool = tools.find((t) => t.name === name || t.fullName === name);
|
|
2761
|
-
if (!tool) return void 0;
|
|
3196
|
+
const tool = introspectionAccess.entry;
|
|
2762
3197
|
return toPlainJson({
|
|
2763
3198
|
name: tool.name,
|
|
2764
3199
|
description: tool.metadata?.description,
|
|
@@ -2778,8 +3213,11 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2778
3213
|
}
|
|
2779
3214
|
};
|
|
2780
3215
|
try {
|
|
2781
|
-
const
|
|
2782
|
-
const
|
|
3216
|
+
const policyConfig = readCodeCallPolicyConfig(this.get(CodeCallConfig));
|
|
3217
|
+
const callableTools = this.scope.tools.getTools(true).filter(
|
|
3218
|
+
(tool) => checkCodeCallToolPolicy(toCodeCallPolicyTool(tool, void 0, this.scope), policyConfig).allowed
|
|
3219
|
+
);
|
|
3220
|
+
const { namespaces, skipped } = buildToolNamespaces(callableTools, environment.callTool);
|
|
2783
3221
|
environment.namespaces = namespaces;
|
|
2784
3222
|
if (skipped.length > 0) {
|
|
2785
3223
|
this.logger?.debug?.("codecall: tools skipped during namespace generation", {
|
|
@@ -2795,9 +3233,14 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2795
3233
|
}
|
|
2796
3234
|
const enclaveService = this.get(EnclaveService);
|
|
2797
3235
|
const config = this.get(CodeCallConfig);
|
|
3236
|
+
const startedAt = Date.now();
|
|
3237
|
+
const elapsed = (reported) => reported ?? Date.now() - startedAt;
|
|
3238
|
+
audit?.logExecutionStart(executionId, script);
|
|
2798
3239
|
try {
|
|
2799
3240
|
const executionResult = await enclaveService.execute(script, environment);
|
|
3241
|
+
const durationMs = elapsed(executionResult.stats?.duration);
|
|
2800
3242
|
if (executionResult.timedOut) {
|
|
3243
|
+
audit?.logExecutionTimeout(executionId, script, durationMs);
|
|
2801
3244
|
return {
|
|
2802
3245
|
status: "timeout",
|
|
2803
3246
|
error: {
|
|
@@ -2808,6 +3251,8 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2808
3251
|
if (!executionResult.success) {
|
|
2809
3252
|
const error = executionResult.error;
|
|
2810
3253
|
if (error.code === "VALIDATION_ERROR" || error.name === "ValidationError") {
|
|
3254
|
+
audit?.logSecurityAstBlocked(executionId, error.blockedPatterns?.join(", ") ?? "UNKNOWN", error.message);
|
|
3255
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
2811
3256
|
return {
|
|
2812
3257
|
status: "illegal_access",
|
|
2813
3258
|
error: {
|
|
@@ -2817,6 +3262,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2817
3262
|
};
|
|
2818
3263
|
}
|
|
2819
3264
|
if (error.toolName) {
|
|
3265
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
2820
3266
|
return {
|
|
2821
3267
|
status: "tool_error",
|
|
2822
3268
|
error: {
|
|
@@ -2829,6 +3275,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2829
3275
|
}
|
|
2830
3276
|
};
|
|
2831
3277
|
}
|
|
3278
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
2832
3279
|
return {
|
|
2833
3280
|
status: "runtime_error",
|
|
2834
3281
|
error: {
|
|
@@ -2839,6 +3286,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2839
3286
|
}
|
|
2840
3287
|
};
|
|
2841
3288
|
}
|
|
3289
|
+
audit?.logExecutionSuccess(executionId, script, durationMs, toolCallCount);
|
|
2842
3290
|
return {
|
|
2843
3291
|
status: "ok",
|
|
2844
3292
|
result: executionResult.result,
|
|
@@ -2847,6 +3295,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2847
3295
|
} catch (error) {
|
|
2848
3296
|
const errorName = error instanceof Error ? error.name : "Error";
|
|
2849
3297
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3298
|
+
audit?.logExecutionFailure(executionId, script, elapsed(), errorMessage);
|
|
2850
3299
|
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
2851
3300
|
const errorLoc = error.loc;
|
|
2852
3301
|
if (errorName === "SyntaxError" || errorMessage?.includes("syntax")) {
|
|
@@ -2871,7 +3320,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2871
3320
|
}
|
|
2872
3321
|
};
|
|
2873
3322
|
ExecuteTool = __decorateClass([
|
|
2874
|
-
(0,
|
|
3323
|
+
(0, import_sdk8.Tool)({
|
|
2875
3324
|
name: "codecall:execute",
|
|
2876
3325
|
cache: {
|
|
2877
3326
|
ttl: 0,
|
|
@@ -2889,7 +3338,7 @@ ExecuteTool = __decorateClass([
|
|
|
2889
3338
|
], ExecuteTool);
|
|
2890
3339
|
|
|
2891
3340
|
// plugins/plugin-codecall/src/tools/invoke.tool.ts
|
|
2892
|
-
var
|
|
3341
|
+
var import_sdk9 = require("@frontmcp/sdk");
|
|
2893
3342
|
|
|
2894
3343
|
// plugins/plugin-codecall/src/tools/invoke.schema.ts
|
|
2895
3344
|
var import_lazy_zod8 = require("@frontmcp/lazy-zod");
|
|
@@ -2921,14 +3370,23 @@ function buildErrorResult(message) {
|
|
|
2921
3370
|
isError: true
|
|
2922
3371
|
};
|
|
2923
3372
|
}
|
|
2924
|
-
var InvokeTool = class extends
|
|
3373
|
+
var InvokeTool = class extends import_sdk9.ToolContext {
|
|
2925
3374
|
async execute(input) {
|
|
2926
3375
|
const { tool: toolName, input: toolInput } = input;
|
|
3376
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
3377
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
3378
|
+
const startedAt = Date.now();
|
|
2927
3379
|
if (isBlockedSelfReference(toolName)) {
|
|
3380
|
+
audit?.logSecuritySelfReference(executionId, toolName);
|
|
2928
3381
|
return buildErrorResult(
|
|
2929
3382
|
`Tool "${toolName}" cannot be invoked directly. CodeCall tools are internal and not accessible via codecall:invoke.`
|
|
2930
3383
|
);
|
|
2931
3384
|
}
|
|
3385
|
+
const decision = checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), toolName, { directCall: true });
|
|
3386
|
+
if (!decision.allowed) {
|
|
3387
|
+
audit?.logSecurityAccessDenied(executionId, toolName, decision.reason);
|
|
3388
|
+
return buildErrorResult(`Tool "${toolName}" is not available. Use codecall:search to discover available tools.`);
|
|
3389
|
+
}
|
|
2932
3390
|
const request = {
|
|
2933
3391
|
method: "tools/call",
|
|
2934
3392
|
params: {
|
|
@@ -2941,13 +3399,15 @@ var InvokeTool = class extends import_sdk8.ToolContext {
|
|
|
2941
3399
|
};
|
|
2942
3400
|
const result = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
2943
3401
|
if (!result) {
|
|
3402
|
+
audit?.logInvoke(executionId, toolName, false, Date.now() - startedAt);
|
|
2944
3403
|
return buildErrorResult(`Tool "${toolName}" not found. Use codecall:search to discover available tools.`);
|
|
2945
3404
|
}
|
|
3405
|
+
audit?.logInvoke(executionId, toolName, true, Date.now() - startedAt);
|
|
2946
3406
|
return result;
|
|
2947
3407
|
}
|
|
2948
3408
|
};
|
|
2949
3409
|
InvokeTool = __decorateClass([
|
|
2950
|
-
(0,
|
|
3410
|
+
(0, import_sdk9.Tool)({
|
|
2951
3411
|
name: "codecall:invoke",
|
|
2952
3412
|
cache: {
|
|
2953
3413
|
ttl: 0,
|
|
@@ -2965,12 +3425,16 @@ InvokeTool = __decorateClass([
|
|
|
2965
3425
|
], InvokeTool);
|
|
2966
3426
|
|
|
2967
3427
|
// plugins/plugin-codecall/src/codecall.plugin.ts
|
|
2968
|
-
|
|
3428
|
+
function auditLogFields(event) {
|
|
3429
|
+
const { executionId, durationMs, data } = event;
|
|
3430
|
+
return durationMs === void 0 ? { executionId, ...data } : { executionId, durationMs, ...data };
|
|
3431
|
+
}
|
|
3432
|
+
var CodeCallPlugin = class extends import_sdk10.DynamicPlugin {
|
|
2969
3433
|
options;
|
|
2970
3434
|
cachedLogger;
|
|
2971
3435
|
getLogger() {
|
|
2972
3436
|
if (!this.cachedLogger) {
|
|
2973
|
-
this.cachedLogger = this.get(
|
|
3437
|
+
this.cachedLogger = this.get(import_sdk10.FrontMcpLogger).child("CodeCall");
|
|
2974
3438
|
}
|
|
2975
3439
|
return this.cachedLogger;
|
|
2976
3440
|
}
|
|
@@ -2999,10 +3463,25 @@ var CodeCallPlugin = class extends import_sdk9.DynamicPlugin {
|
|
|
2999
3463
|
return new EnclaveService(cfg);
|
|
3000
3464
|
}
|
|
3001
3465
|
},
|
|
3466
|
+
{
|
|
3467
|
+
// GHSA-adjacent: production.mdx promised operators these events and nothing emitted them,
|
|
3468
|
+
// because the service was never registered. The subscription lives here, in the factory,
|
|
3469
|
+
// for a reason: there is no provider teardown hook in the SDK, so subscribing anywhere
|
|
3470
|
+
// per-request would grow the listener Set for the life of the process.
|
|
3471
|
+
name: "codecall:audit-logger",
|
|
3472
|
+
provide: AuditLoggerService,
|
|
3473
|
+
inject: () => [import_sdk10.ScopeEntry],
|
|
3474
|
+
useFactory: (scope) => {
|
|
3475
|
+
const audit = new AuditLoggerService();
|
|
3476
|
+
const logger = scope.logger.child("codecall:audit");
|
|
3477
|
+
audit.subscribe((event) => logger.info(event.type, auditLogFields(event)));
|
|
3478
|
+
return audit;
|
|
3479
|
+
}
|
|
3480
|
+
},
|
|
3002
3481
|
{
|
|
3003
3482
|
name: "codecall:tool-search",
|
|
3004
3483
|
provide: ToolSearchService,
|
|
3005
|
-
inject: () => [
|
|
3484
|
+
inject: () => [import_sdk10.ScopeEntry],
|
|
3006
3485
|
useFactory: async (scope) => {
|
|
3007
3486
|
return new ToolSearchService(
|
|
3008
3487
|
{
|
|
@@ -3084,10 +3563,10 @@ var CodeCallPlugin = class extends import_sdk9.DynamicPlugin {
|
|
|
3084
3563
|
}
|
|
3085
3564
|
};
|
|
3086
3565
|
__decorateClass([
|
|
3087
|
-
|
|
3566
|
+
import_sdk10.ListToolsHook.Did("resolveConflicts", { priority: 1e3 })
|
|
3088
3567
|
], CodeCallPlugin.prototype, "adjustListTools", 1);
|
|
3089
3568
|
CodeCallPlugin = __decorateClass([
|
|
3090
|
-
(0,
|
|
3569
|
+
(0, import_sdk10.Plugin)({
|
|
3091
3570
|
name: "codecall",
|
|
3092
3571
|
description: "CodeCall plugin: AgentScript-based meta-tools for orchestrating MCP tools",
|
|
3093
3572
|
providers: [],
|
|
@@ -3097,6 +3576,8 @@ CodeCallPlugin = __decorateClass([
|
|
|
3097
3576
|
], CodeCallPlugin);
|
|
3098
3577
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3099
3578
|
0 && (module.exports = {
|
|
3579
|
+
AUDIT_EVENT_TYPES,
|
|
3580
|
+
AuditLoggerService,
|
|
3100
3581
|
CodeCallPlugin,
|
|
3101
3582
|
codeCallDirectCallsOptionsSchema,
|
|
3102
3583
|
codeCallEmbeddingOptionsSchema,
|