@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/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 import_sdk9 = require("@frontmcp/sdk");
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
- * Determines if a tool should be indexed in the search database.
1084
- * Filters based on:
1085
- * - Excludes codecall:* meta-tools (they should not be searchable)
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
- const toolName = tool.name || tool.fullName;
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
- if (!tool.owner) return void 0;
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 import_sdk2 = require("@frontmcp/sdk");
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, import_sdk2.Provider)({
1867
+ (0, import_sdk3.Provider)({
1487
1868
  name: "codecall:enclave",
1488
1869
  description: "Executes AgentScript code in a secure enclave",
1489
- scope: import_sdk2.ProviderScope.GLOBAL
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 import_sdk3 = require("@frontmcp/sdk");
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 import_sdk3.ToolContext {
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, import_sdk3.Tool)({
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 import_sdk4 = require("@frontmcp/sdk");
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 import_sdk4.ToolContext {
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, import_sdk4.Tool)({
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 import_sdk5 = require("@frontmcp/sdk");
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 import_sdk5.ToolContext {
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, import_sdk5.Tool)({
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 import_sdk6 = require("@frontmcp/sdk");
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 import_sdk6.ToolContext {
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 allTools = this.scope.tools.getTools(true);
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 tool = toolMap.get(toolName) || fullNameMap.get(toolName);
2554
- if (!tool) {
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, import_sdk6.Tool)({
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 import_sdk7 = require("@frontmcp/sdk");
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 import_sdk7.ToolContext {
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)) return void 0;
2896
- if (!this.checkToolPolicy(name).allowed) return void 0;
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 tools = this.scope.tools.getTools(true);
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 registeredTools = this.scope.tools.getTools(true);
2921
- const { namespaces, skipped } = buildToolNamespaces(registeredTools, environment.callTool);
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, import_sdk7.Tool)({
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 import_sdk8 = require("@frontmcp/sdk");
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 import_sdk8.ToolContext {
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, import_sdk8.Tool)({
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
- var CodeCallPlugin = class extends import_sdk9.DynamicPlugin {
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(import_sdk9.FrontMcpLogger).child("CodeCall");
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: () => [import_sdk9.ScopeEntry],
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
- import_sdk9.ListToolsHook.Did("resolveConflicts", { priority: 1e3 })
3566
+ import_sdk10.ListToolsHook.Did("resolveConflicts", { priority: 1e3 })
3231
3567
  ], CodeCallPlugin.prototype, "adjustListTools", 1);
3232
3568
  CodeCallPlugin = __decorateClass([
3233
- (0, import_sdk9.Plugin)({
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,