@frontmcp/plugin-codecall 1.8.0 → 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 +535 -199
- package/esm/package.json +5 -5
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +559 -221
- package/package.json +5 -5
- package/security/codecall-tool-policy.d.ts +67 -15
- package/security/codecall-tool-policy.d.ts.map +1 -1
- package/security/index.d.ts +0 -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.map +1 -1
- 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 -115
- 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,126 +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
|
|
1921
|
-
|
|
1922
|
-
// plugins/plugin-codecall/src/security/codecall-tool-policy.ts
|
|
1923
|
-
var BLOCKED_NAMESPACE_PATTERNS = Object.freeze([/^system:/, /^internal:/, /^__/]);
|
|
1924
|
-
var ALLOWED = { allowed: true };
|
|
1925
|
-
function deny(reason) {
|
|
1926
|
-
return { allowed: false, reason };
|
|
1927
|
-
}
|
|
1928
|
-
function checkCodeCallToolPolicy(tool, config) {
|
|
1929
|
-
const { name } = tool;
|
|
1930
|
-
const names = [name, ...tool.aliases ?? []].filter((candidate) => !!candidate);
|
|
1931
|
-
if (names.some((candidate) => candidate.startsWith("codecall:"))) {
|
|
1932
|
-
return deny("CodeCall meta-tools are not callable from CodeCall");
|
|
1933
|
-
}
|
|
1934
|
-
for (const candidate of names) {
|
|
1935
|
-
if (BLOCKED_NAMESPACE_PATTERNS.some((pattern) => pattern.test(candidate))) {
|
|
1936
|
-
return deny(`Tool "${candidate}" is in a namespace CodeCall never calls`);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
if (tool.hideFromDiscovery === true) {
|
|
1940
|
-
return deny(`Tool "${name}" is hidden from discovery`);
|
|
1941
|
-
}
|
|
1942
|
-
const enabled = tool.codecall?.enabledInCodeCall;
|
|
1943
|
-
switch (config.mode) {
|
|
1944
|
-
case "codecall_only":
|
|
1945
|
-
case "metadata_driven":
|
|
1946
|
-
if (enabled === false) {
|
|
1947
|
-
return deny(`Tool "${name}" sets enabledInCodeCall: false`);
|
|
1948
|
-
}
|
|
1949
|
-
break;
|
|
1950
|
-
case "codecall_opt_in":
|
|
1951
|
-
if (enabled !== true) {
|
|
1952
|
-
return deny(`Tool "${name}" has not opted in to CodeCall`);
|
|
1953
|
-
}
|
|
1954
|
-
break;
|
|
1955
|
-
default:
|
|
1956
|
-
return deny(`Unknown CodeCall mode: ${String(config.mode)}`);
|
|
1957
|
-
}
|
|
1958
|
-
if (config.includeTools) {
|
|
1959
|
-
const included = config.includeTools({
|
|
1960
|
-
name,
|
|
1961
|
-
appId: tool.appId,
|
|
1962
|
-
source: tool.codecall?.source,
|
|
1963
|
-
description: tool.description,
|
|
1964
|
-
tags: tool.codecall?.tags ?? tool.tags
|
|
1965
|
-
});
|
|
1966
|
-
if (!included) {
|
|
1967
|
-
return deny(`Tool "${name}" is excluded by the includeTools filter`);
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
return ALLOWED;
|
|
1971
|
-
}
|
|
1972
|
-
function denyUnknownTool(name) {
|
|
1973
|
-
return deny(`Tool "${name}" is not available through CodeCall`);
|
|
1974
|
-
}
|
|
1975
|
-
function resolveCodeCallTool(scope, name) {
|
|
1976
|
-
const typedScope = scope;
|
|
1977
|
-
const alias = /[-_]/.test(name) ? name.includes("_") ? name.replace(/_/g, "-") : name.replace(/-/g, "_") : void 0;
|
|
1978
|
-
const candidates = alias ? [name, alias] : [name];
|
|
1979
|
-
const matches = (entry) => candidates.includes(entry.fullName) || candidates.includes(entry.name);
|
|
1980
|
-
const local = typedScope.tools?.getTools(true)?.find(matches);
|
|
1981
|
-
if (local) return local;
|
|
1982
|
-
const registries = typedScope.providers?.getRegistries?.("AppRegistry") ?? [];
|
|
1983
|
-
for (const registry of registries) {
|
|
1984
|
-
for (const app of registry.getApps()) {
|
|
1985
|
-
if (!app.isRemote) continue;
|
|
1986
|
-
const remote = app.tools.getTools(true).find(matches);
|
|
1987
|
-
if (remote) return remote;
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
return void 0;
|
|
1991
|
-
}
|
|
1992
|
-
function checkDirectCallPolicy(tool, directCalls) {
|
|
1993
|
-
if (!directCalls) return ALLOWED;
|
|
1994
|
-
if (directCalls.enabled === false) {
|
|
1995
|
-
return deny("Direct tool invocation is disabled");
|
|
1996
|
-
}
|
|
1997
|
-
if (directCalls.allowedTools && !directCalls.allowedTools.includes(tool.name)) {
|
|
1998
|
-
return deny(`Tool "${tool.name}" is not in the direct-call allowlist`);
|
|
1999
|
-
}
|
|
2000
|
-
if (directCalls.filter) {
|
|
2001
|
-
const permitted = directCalls.filter({
|
|
2002
|
-
name: tool.name,
|
|
2003
|
-
appId: tool.appId,
|
|
2004
|
-
source: tool.codecall?.source,
|
|
2005
|
-
tags: tool.codecall?.tags ?? tool.tags
|
|
2006
|
-
});
|
|
2007
|
-
if (!permitted) {
|
|
2008
|
-
return deny(`Tool "${tool.name}" is excluded by the directCalls filter`);
|
|
2009
|
-
}
|
|
2010
|
-
}
|
|
2011
|
-
return ALLOWED;
|
|
2012
|
-
}
|
|
2013
|
-
function checkCodeCallToolAccess(scope, config, name, options = {}) {
|
|
2014
|
-
const entry = resolveCodeCallTool(scope, name);
|
|
2015
|
-
if (!entry) return denyUnknownTool(name);
|
|
2016
|
-
const metadata = entry.metadata;
|
|
2017
|
-
const owner = entry.owner;
|
|
2018
|
-
const policyTool = {
|
|
2019
|
-
// The qualified name is the subject: `call-tool.flow.ts` dispatches `fullName`, so the
|
|
2020
|
-
// policy has to judge the same string the flow will run.
|
|
2021
|
-
name: entry.fullName || entry.name,
|
|
2022
|
-
aliases: [entry.name, name],
|
|
2023
|
-
appId: owner?.kind === "app" ? owner.id : void 0,
|
|
2024
|
-
description: metadata?.description,
|
|
2025
|
-
tags: metadata?.tags,
|
|
2026
|
-
hideFromDiscovery: metadata?.hideFromDiscovery,
|
|
2027
|
-
codecall: metadata?.codecall
|
|
2028
|
-
};
|
|
2029
|
-
const baseDecision = checkCodeCallToolPolicy(policyTool, {
|
|
2030
|
-
// An absent mode means an unparsed/partial config, not a hostile one: fall back to the
|
|
2031
|
-
// schema's own documented default rather than denying every call. An unrecognised mode
|
|
2032
|
-
// still fails closed inside the policy's switch.
|
|
2033
|
-
mode: config.get("mode") ?? "codecall_only",
|
|
2034
|
-
includeTools: config.get("includeTools")
|
|
2035
|
-
});
|
|
2036
|
-
if (!baseDecision.allowed) return baseDecision;
|
|
2037
|
-
if (!options.directCall) return ALLOWED;
|
|
2038
|
-
return checkDirectCallPolicy(policyTool, config.get("directCalls"));
|
|
2039
|
-
}
|
|
2305
|
+
var import_sdk7 = require("@frontmcp/sdk");
|
|
2040
2306
|
|
|
2041
2307
|
// plugins/plugin-codecall/src/errors/tool-call.errors.ts
|
|
2042
2308
|
var TOOL_CALL_ERROR_CODES = {
|
|
@@ -2537,24 +2803,27 @@ var describeToolOutputSchema = import_lazy_zod5.z.object({
|
|
|
2537
2803
|
});
|
|
2538
2804
|
|
|
2539
2805
|
// plugins/plugin-codecall/src/tools/describe.tool.ts
|
|
2540
|
-
var DescribeTool = class extends
|
|
2806
|
+
var DescribeTool = class extends import_sdk7.ToolContext {
|
|
2541
2807
|
async execute(input) {
|
|
2542
2808
|
const { toolNames } = input;
|
|
2809
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
2810
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
2811
|
+
const startedAt = Date.now();
|
|
2543
2812
|
const tools = [];
|
|
2544
2813
|
const notFound = [];
|
|
2545
|
-
const
|
|
2546
|
-
const toolMap = new Map(allTools.map((t) => [t.name, t]));
|
|
2547
|
-
const fullNameMap = new Map(allTools.map((t) => [t.fullName, t]));
|
|
2814
|
+
const config = this.get(CodeCallConfig);
|
|
2548
2815
|
for (const toolName of toolNames) {
|
|
2549
2816
|
if (isBlockedSelfReference(toolName)) {
|
|
2550
2817
|
notFound.push(toolName);
|
|
2551
2818
|
continue;
|
|
2552
2819
|
}
|
|
2553
|
-
const
|
|
2554
|
-
if (!
|
|
2820
|
+
const access = checkCodeCallToolAccess(this.scope, config, toolName);
|
|
2821
|
+
if (!access.allowed) {
|
|
2822
|
+
audit?.logSecurityAccessDenied(executionId, toolName, access.reason);
|
|
2555
2823
|
notFound.push(toolName);
|
|
2556
2824
|
continue;
|
|
2557
2825
|
}
|
|
2826
|
+
const tool = access.entry;
|
|
2558
2827
|
const appId = this.extractAppId(tool);
|
|
2559
2828
|
const inputSchema = this.getInputSchema(tool);
|
|
2560
2829
|
const outputSchema = this.toJsonSchema(tool.rawOutputSchema ?? tool.outputSchema);
|
|
@@ -2569,6 +2838,7 @@ var DescribeTool = class extends import_sdk6.ToolContext {
|
|
|
2569
2838
|
usageExamples
|
|
2570
2839
|
});
|
|
2571
2840
|
}
|
|
2841
|
+
audit?.logDescribe(executionId, toolNames, Date.now() - startedAt);
|
|
2572
2842
|
return {
|
|
2573
2843
|
tools,
|
|
2574
2844
|
notFound: notFound.length > 0 ? notFound : void 0
|
|
@@ -2683,7 +2953,7 @@ return result;`
|
|
|
2683
2953
|
}
|
|
2684
2954
|
};
|
|
2685
2955
|
DescribeTool = __decorateClass([
|
|
2686
|
-
(0,
|
|
2956
|
+
(0, import_sdk7.Tool)({
|
|
2687
2957
|
name: "codecall:describe",
|
|
2688
2958
|
cache: {
|
|
2689
2959
|
ttl: 60,
|
|
@@ -2705,7 +2975,7 @@ DescribeTool = __decorateClass([
|
|
|
2705
2975
|
], DescribeTool);
|
|
2706
2976
|
|
|
2707
2977
|
// plugins/plugin-codecall/src/tools/execute.tool.ts
|
|
2708
|
-
var
|
|
2978
|
+
var import_sdk8 = require("@frontmcp/sdk");
|
|
2709
2979
|
|
|
2710
2980
|
// plugins/plugin-codecall/src/tools/execute.schema.ts
|
|
2711
2981
|
var import_lazy_zod7 = require("@frontmcp/lazy-zod");
|
|
@@ -2822,7 +3092,7 @@ function getErrorCode(error) {
|
|
|
2822
3092
|
}
|
|
2823
3093
|
return TOOL_CALL_ERROR_CODES.EXECUTION;
|
|
2824
3094
|
}
|
|
2825
|
-
var ExecuteTool = class extends
|
|
3095
|
+
var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
2826
3096
|
/**
|
|
2827
3097
|
* Resolve the server-side CodeCall policy for a tool name (GHSA-6w3j-82v5-6qrr).
|
|
2828
3098
|
*
|
|
@@ -2836,13 +3106,21 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2836
3106
|
}
|
|
2837
3107
|
async execute(input) {
|
|
2838
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;
|
|
2839
3113
|
const allowedToolSet = allowedTools ? new Set(allowedTools) : null;
|
|
2840
3114
|
const environment = {
|
|
2841
3115
|
callTool: async (name, toolInput, options) => {
|
|
2842
3116
|
const throwOnError = options?.throwOnError !== false;
|
|
3117
|
+
if (isBlockedSelfReference(name)) {
|
|
3118
|
+
audit?.logSecuritySelfReference(executionId, name);
|
|
3119
|
+
}
|
|
2843
3120
|
assertNotSelfReference(name);
|
|
2844
3121
|
const decision = this.checkToolPolicy(name);
|
|
2845
3122
|
if (!decision.allowed) {
|
|
3123
|
+
audit?.logSecurityAccessDenied(executionId, name, decision.reason);
|
|
2846
3124
|
const error = createToolCallError(TOOL_CALL_ERROR_CODES.ACCESS_DENIED, name, decision.reason);
|
|
2847
3125
|
if (throwOnError) {
|
|
2848
3126
|
throw error;
|
|
@@ -2850,12 +3128,16 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2850
3128
|
return { success: false, error };
|
|
2851
3129
|
}
|
|
2852
3130
|
if (allowedToolSet && !allowedToolSet.has(name)) {
|
|
3131
|
+
audit?.logSecurityAccessDenied(executionId, name, "not in the caller-supplied allowedTools list");
|
|
2853
3132
|
const error = createToolCallError(TOOL_CALL_ERROR_CODES.ACCESS_DENIED, name);
|
|
2854
3133
|
if (throwOnError) {
|
|
2855
3134
|
throw error;
|
|
2856
3135
|
}
|
|
2857
3136
|
return { success: false, error };
|
|
2858
3137
|
}
|
|
3138
|
+
toolCallCount += 1;
|
|
3139
|
+
audit?.logToolCallStart(executionId, name, callDepth);
|
|
3140
|
+
const toolCallStartedAt = Date.now();
|
|
2859
3141
|
try {
|
|
2860
3142
|
const request = {
|
|
2861
3143
|
method: "tools/call",
|
|
@@ -2869,6 +3151,13 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2869
3151
|
};
|
|
2870
3152
|
const mcpResult = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
2871
3153
|
if (!mcpResult) {
|
|
3154
|
+
audit?.logToolCallFailure(
|
|
3155
|
+
executionId,
|
|
3156
|
+
name,
|
|
3157
|
+
callDepth,
|
|
3158
|
+
Date.now() - toolCallStartedAt,
|
|
3159
|
+
TOOL_CALL_ERROR_CODES.EXECUTION
|
|
3160
|
+
);
|
|
2872
3161
|
const error = createToolCallError(TOOL_CALL_ERROR_CODES.EXECUTION, name, "Flow returned no result");
|
|
2873
3162
|
if (throwOnError) {
|
|
2874
3163
|
throw error;
|
|
@@ -2876,6 +3165,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2876
3165
|
return { success: false, error };
|
|
2877
3166
|
}
|
|
2878
3167
|
const result = extractResultFromCallToolResult(mcpResult);
|
|
3168
|
+
audit?.logToolCallSuccess(executionId, name, callDepth, Date.now() - toolCallStartedAt);
|
|
2879
3169
|
if (throwOnError) {
|
|
2880
3170
|
return result;
|
|
2881
3171
|
}
|
|
@@ -2883,6 +3173,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2883
3173
|
} catch (error) {
|
|
2884
3174
|
const errorCode = getErrorCode(error);
|
|
2885
3175
|
const rawMessage = error instanceof Error ? error.message : void 0;
|
|
3176
|
+
audit?.logToolCallFailure(executionId, name, callDepth, Date.now() - toolCallStartedAt, errorCode);
|
|
2886
3177
|
const sanitizedError = createToolCallError(errorCode, name, rawMessage);
|
|
2887
3178
|
if (throwOnError) {
|
|
2888
3179
|
throw sanitizedError;
|
|
@@ -2892,12 +3183,17 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2892
3183
|
},
|
|
2893
3184
|
getTool: (name) => {
|
|
2894
3185
|
try {
|
|
2895
|
-
if (isBlockedSelfReference(name))
|
|
2896
|
-
|
|
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
|
+
}
|
|
2897
3195
|
if (allowedToolSet && !allowedToolSet.has(name)) return void 0;
|
|
2898
|
-
const
|
|
2899
|
-
const tool = tools.find((t) => t.name === name || t.fullName === name);
|
|
2900
|
-
if (!tool) return void 0;
|
|
3196
|
+
const tool = introspectionAccess.entry;
|
|
2901
3197
|
return toPlainJson({
|
|
2902
3198
|
name: tool.name,
|
|
2903
3199
|
description: tool.metadata?.description,
|
|
@@ -2917,8 +3213,11 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2917
3213
|
}
|
|
2918
3214
|
};
|
|
2919
3215
|
try {
|
|
2920
|
-
const
|
|
2921
|
-
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);
|
|
2922
3221
|
environment.namespaces = namespaces;
|
|
2923
3222
|
if (skipped.length > 0) {
|
|
2924
3223
|
this.logger?.debug?.("codecall: tools skipped during namespace generation", {
|
|
@@ -2934,9 +3233,14 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2934
3233
|
}
|
|
2935
3234
|
const enclaveService = this.get(EnclaveService);
|
|
2936
3235
|
const config = this.get(CodeCallConfig);
|
|
3236
|
+
const startedAt = Date.now();
|
|
3237
|
+
const elapsed = (reported) => reported ?? Date.now() - startedAt;
|
|
3238
|
+
audit?.logExecutionStart(executionId, script);
|
|
2937
3239
|
try {
|
|
2938
3240
|
const executionResult = await enclaveService.execute(script, environment);
|
|
3241
|
+
const durationMs = elapsed(executionResult.stats?.duration);
|
|
2939
3242
|
if (executionResult.timedOut) {
|
|
3243
|
+
audit?.logExecutionTimeout(executionId, script, durationMs);
|
|
2940
3244
|
return {
|
|
2941
3245
|
status: "timeout",
|
|
2942
3246
|
error: {
|
|
@@ -2947,6 +3251,8 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2947
3251
|
if (!executionResult.success) {
|
|
2948
3252
|
const error = executionResult.error;
|
|
2949
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);
|
|
2950
3256
|
return {
|
|
2951
3257
|
status: "illegal_access",
|
|
2952
3258
|
error: {
|
|
@@ -2956,6 +3262,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2956
3262
|
};
|
|
2957
3263
|
}
|
|
2958
3264
|
if (error.toolName) {
|
|
3265
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
2959
3266
|
return {
|
|
2960
3267
|
status: "tool_error",
|
|
2961
3268
|
error: {
|
|
@@ -2968,6 +3275,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2968
3275
|
}
|
|
2969
3276
|
};
|
|
2970
3277
|
}
|
|
3278
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
2971
3279
|
return {
|
|
2972
3280
|
status: "runtime_error",
|
|
2973
3281
|
error: {
|
|
@@ -2978,6 +3286,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2978
3286
|
}
|
|
2979
3287
|
};
|
|
2980
3288
|
}
|
|
3289
|
+
audit?.logExecutionSuccess(executionId, script, durationMs, toolCallCount);
|
|
2981
3290
|
return {
|
|
2982
3291
|
status: "ok",
|
|
2983
3292
|
result: executionResult.result,
|
|
@@ -2986,6 +3295,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
2986
3295
|
} catch (error) {
|
|
2987
3296
|
const errorName = error instanceof Error ? error.name : "Error";
|
|
2988
3297
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3298
|
+
audit?.logExecutionFailure(executionId, script, elapsed(), errorMessage);
|
|
2989
3299
|
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
2990
3300
|
const errorLoc = error.loc;
|
|
2991
3301
|
if (errorName === "SyntaxError" || errorMessage?.includes("syntax")) {
|
|
@@ -3010,7 +3320,7 @@ var ExecuteTool = class extends import_sdk7.ToolContext {
|
|
|
3010
3320
|
}
|
|
3011
3321
|
};
|
|
3012
3322
|
ExecuteTool = __decorateClass([
|
|
3013
|
-
(0,
|
|
3323
|
+
(0, import_sdk8.Tool)({
|
|
3014
3324
|
name: "codecall:execute",
|
|
3015
3325
|
cache: {
|
|
3016
3326
|
ttl: 0,
|
|
@@ -3028,7 +3338,7 @@ ExecuteTool = __decorateClass([
|
|
|
3028
3338
|
], ExecuteTool);
|
|
3029
3339
|
|
|
3030
3340
|
// plugins/plugin-codecall/src/tools/invoke.tool.ts
|
|
3031
|
-
var
|
|
3341
|
+
var import_sdk9 = require("@frontmcp/sdk");
|
|
3032
3342
|
|
|
3033
3343
|
// plugins/plugin-codecall/src/tools/invoke.schema.ts
|
|
3034
3344
|
var import_lazy_zod8 = require("@frontmcp/lazy-zod");
|
|
@@ -3060,16 +3370,21 @@ function buildErrorResult(message) {
|
|
|
3060
3370
|
isError: true
|
|
3061
3371
|
};
|
|
3062
3372
|
}
|
|
3063
|
-
var InvokeTool = class extends
|
|
3373
|
+
var InvokeTool = class extends import_sdk9.ToolContext {
|
|
3064
3374
|
async execute(input) {
|
|
3065
3375
|
const { tool: toolName, input: toolInput } = input;
|
|
3376
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
3377
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
3378
|
+
const startedAt = Date.now();
|
|
3066
3379
|
if (isBlockedSelfReference(toolName)) {
|
|
3380
|
+
audit?.logSecuritySelfReference(executionId, toolName);
|
|
3067
3381
|
return buildErrorResult(
|
|
3068
3382
|
`Tool "${toolName}" cannot be invoked directly. CodeCall tools are internal and not accessible via codecall:invoke.`
|
|
3069
3383
|
);
|
|
3070
3384
|
}
|
|
3071
3385
|
const decision = checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), toolName, { directCall: true });
|
|
3072
3386
|
if (!decision.allowed) {
|
|
3387
|
+
audit?.logSecurityAccessDenied(executionId, toolName, decision.reason);
|
|
3073
3388
|
return buildErrorResult(`Tool "${toolName}" is not available. Use codecall:search to discover available tools.`);
|
|
3074
3389
|
}
|
|
3075
3390
|
const request = {
|
|
@@ -3084,13 +3399,15 @@ var InvokeTool = class extends import_sdk8.ToolContext {
|
|
|
3084
3399
|
};
|
|
3085
3400
|
const result = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
3086
3401
|
if (!result) {
|
|
3402
|
+
audit?.logInvoke(executionId, toolName, false, Date.now() - startedAt);
|
|
3087
3403
|
return buildErrorResult(`Tool "${toolName}" not found. Use codecall:search to discover available tools.`);
|
|
3088
3404
|
}
|
|
3405
|
+
audit?.logInvoke(executionId, toolName, true, Date.now() - startedAt);
|
|
3089
3406
|
return result;
|
|
3090
3407
|
}
|
|
3091
3408
|
};
|
|
3092
3409
|
InvokeTool = __decorateClass([
|
|
3093
|
-
(0,
|
|
3410
|
+
(0, import_sdk9.Tool)({
|
|
3094
3411
|
name: "codecall:invoke",
|
|
3095
3412
|
cache: {
|
|
3096
3413
|
ttl: 0,
|
|
@@ -3108,12 +3425,16 @@ InvokeTool = __decorateClass([
|
|
|
3108
3425
|
], InvokeTool);
|
|
3109
3426
|
|
|
3110
3427
|
// plugins/plugin-codecall/src/codecall.plugin.ts
|
|
3111
|
-
|
|
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 {
|
|
3112
3433
|
options;
|
|
3113
3434
|
cachedLogger;
|
|
3114
3435
|
getLogger() {
|
|
3115
3436
|
if (!this.cachedLogger) {
|
|
3116
|
-
this.cachedLogger = this.get(
|
|
3437
|
+
this.cachedLogger = this.get(import_sdk10.FrontMcpLogger).child("CodeCall");
|
|
3117
3438
|
}
|
|
3118
3439
|
return this.cachedLogger;
|
|
3119
3440
|
}
|
|
@@ -3142,10 +3463,25 @@ var CodeCallPlugin = class extends import_sdk9.DynamicPlugin {
|
|
|
3142
3463
|
return new EnclaveService(cfg);
|
|
3143
3464
|
}
|
|
3144
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
|
+
},
|
|
3145
3481
|
{
|
|
3146
3482
|
name: "codecall:tool-search",
|
|
3147
3483
|
provide: ToolSearchService,
|
|
3148
|
-
inject: () => [
|
|
3484
|
+
inject: () => [import_sdk10.ScopeEntry],
|
|
3149
3485
|
useFactory: async (scope) => {
|
|
3150
3486
|
return new ToolSearchService(
|
|
3151
3487
|
{
|
|
@@ -3227,10 +3563,10 @@ var CodeCallPlugin = class extends import_sdk9.DynamicPlugin {
|
|
|
3227
3563
|
}
|
|
3228
3564
|
};
|
|
3229
3565
|
__decorateClass([
|
|
3230
|
-
|
|
3566
|
+
import_sdk10.ListToolsHook.Did("resolveConflicts", { priority: 1e3 })
|
|
3231
3567
|
], CodeCallPlugin.prototype, "adjustListTools", 1);
|
|
3232
3568
|
CodeCallPlugin = __decorateClass([
|
|
3233
|
-
(0,
|
|
3569
|
+
(0, import_sdk10.Plugin)({
|
|
3234
3570
|
name: "codecall",
|
|
3235
3571
|
description: "CodeCall plugin: AgentScript-based meta-tools for orchestrating MCP tools",
|
|
3236
3572
|
providers: [],
|
|
@@ -3240,6 +3576,8 @@ CodeCallPlugin = __decorateClass([
|
|
|
3240
3576
|
], CodeCallPlugin);
|
|
3241
3577
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3242
3578
|
0 && (module.exports = {
|
|
3579
|
+
AUDIT_EVENT_TYPES,
|
|
3580
|
+
AuditLoggerService,
|
|
3243
3581
|
CodeCallPlugin,
|
|
3244
3582
|
codeCallDirectCallsOptionsSchema,
|
|
3245
3583
|
codeCallEmbeddingOptionsSchema,
|