@frontmcp/plugin-codecall 1.7.2 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/codecall.plugin.d.ts.map +1 -1
- package/esm/index.mjs +558 -79
- package/esm/package.json +5 -5
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +582 -101
- package/package.json +5 -5
- package/security/codecall-tool-policy.d.ts +141 -0
- package/security/codecall-tool-policy.d.ts.map +1 -0
- package/security/index.d.ts +1 -1
- package/security/index.d.ts.map +1 -1
- package/services/enclave.service.d.ts +7 -0
- package/services/enclave.service.d.ts.map +1 -1
- package/services/index.d.ts +0 -1
- package/services/index.d.ts.map +1 -1
- package/services/tool-search.service.d.ts +7 -15
- package/services/tool-search.service.d.ts.map +1 -1
- package/tools/describe.tool.d.ts.map +1 -1
- package/tools/execute.tool.d.ts +9 -0
- package/tools/execute.tool.d.ts.map +1 -1
- package/tools/invoke.tool.d.ts +2 -0
- package/tools/invoke.tool.d.ts.map +1 -1
- package/tools/search-knowledge.tool.d.ts.map +1 -1
- package/tools/search-skills.tool.d.ts.map +1 -1
- package/tools/search.tool.d.ts.map +1 -1
- package/security/tool-access-control.service.d.ts +0 -105
- package/security/tool-access-control.service.d.ts.map +0 -1
- package/services/error-enrichment.service.d.ts +0 -95
- package/services/error-enrichment.service.d.ts.map +0 -1
package/esm/index.mjs
CHANGED
|
@@ -372,9 +372,434 @@ function presetDefaults(preset) {
|
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
+
// plugins/plugin-codecall/src/services/audit-logger.service.ts
|
|
376
|
+
import { Provider as Provider2, ProviderScope as ProviderScope2 } from "@frontmcp/sdk";
|
|
377
|
+
var AUDIT_EVENT_TYPES = {
|
|
378
|
+
/** Script execution started */
|
|
379
|
+
EXECUTION_START: "codecall:execution:start",
|
|
380
|
+
/** Script execution completed successfully */
|
|
381
|
+
EXECUTION_SUCCESS: "codecall:execution:success",
|
|
382
|
+
/** Script execution failed */
|
|
383
|
+
EXECUTION_FAILURE: "codecall:execution:failure",
|
|
384
|
+
/** Script execution timed out */
|
|
385
|
+
EXECUTION_TIMEOUT: "codecall:execution:timeout",
|
|
386
|
+
/** Tool call initiated from script */
|
|
387
|
+
TOOL_CALL_START: "codecall:tool:call:start",
|
|
388
|
+
/** Tool call completed successfully */
|
|
389
|
+
TOOL_CALL_SUCCESS: "codecall:tool:call:success",
|
|
390
|
+
/** Tool call failed */
|
|
391
|
+
TOOL_CALL_FAILURE: "codecall:tool:call:failure",
|
|
392
|
+
/** Self-reference attack blocked */
|
|
393
|
+
SECURITY_SELF_REFERENCE: "codecall:security:self-reference",
|
|
394
|
+
/** Tool access denied */
|
|
395
|
+
SECURITY_ACCESS_DENIED: "codecall:security:access-denied",
|
|
396
|
+
/** AST validation failed (blocked code pattern) */
|
|
397
|
+
SECURITY_AST_BLOCKED: "codecall:security:ast-blocked",
|
|
398
|
+
/** Search performed */
|
|
399
|
+
SEARCH_PERFORMED: "codecall:search:performed",
|
|
400
|
+
/** Tool described */
|
|
401
|
+
DESCRIBE_PERFORMED: "codecall:describe:performed",
|
|
402
|
+
/** Direct invoke performed */
|
|
403
|
+
INVOKE_PERFORMED: "codecall:invoke:performed"
|
|
404
|
+
};
|
|
405
|
+
var AuditLoggerService = class {
|
|
406
|
+
listeners = /* @__PURE__ */ new Set();
|
|
407
|
+
executionCounter = 0;
|
|
408
|
+
/**
|
|
409
|
+
* Subscribe to audit events.
|
|
410
|
+
*
|
|
411
|
+
* @param listener - Function to call when events occur
|
|
412
|
+
* @returns Unsubscribe function
|
|
413
|
+
*/
|
|
414
|
+
subscribe(listener) {
|
|
415
|
+
this.listeners.add(listener);
|
|
416
|
+
return () => {
|
|
417
|
+
this.listeners.delete(listener);
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Generate a unique execution ID.
|
|
422
|
+
*/
|
|
423
|
+
generateExecutionId() {
|
|
424
|
+
const timestamp = Date.now().toString(36);
|
|
425
|
+
const counter = (++this.executionCounter).toString(36).padStart(4, "0");
|
|
426
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
427
|
+
return `exec_${timestamp}_${counter}_${random}`;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Log execution start.
|
|
431
|
+
*/
|
|
432
|
+
logExecutionStart(executionId, script) {
|
|
433
|
+
this.emit({
|
|
434
|
+
type: AUDIT_EVENT_TYPES.EXECUTION_START,
|
|
435
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
436
|
+
executionId,
|
|
437
|
+
data: {
|
|
438
|
+
scriptHash: this.hashScript(script),
|
|
439
|
+
scriptLength: script.length
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Log execution success.
|
|
445
|
+
*/
|
|
446
|
+
logExecutionSuccess(executionId, script, durationMs, toolCallCount) {
|
|
447
|
+
this.emit({
|
|
448
|
+
type: AUDIT_EVENT_TYPES.EXECUTION_SUCCESS,
|
|
449
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
450
|
+
executionId,
|
|
451
|
+
durationMs,
|
|
452
|
+
data: {
|
|
453
|
+
scriptHash: this.hashScript(script),
|
|
454
|
+
scriptLength: script.length,
|
|
455
|
+
toolCallCount
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Log execution failure.
|
|
461
|
+
*/
|
|
462
|
+
logExecutionFailure(executionId, script, durationMs, error) {
|
|
463
|
+
this.emit({
|
|
464
|
+
type: AUDIT_EVENT_TYPES.EXECUTION_FAILURE,
|
|
465
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
466
|
+
executionId,
|
|
467
|
+
durationMs,
|
|
468
|
+
data: {
|
|
469
|
+
scriptHash: this.hashScript(script),
|
|
470
|
+
scriptLength: script.length,
|
|
471
|
+
error: this.sanitizeError(error)
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Log execution timeout.
|
|
477
|
+
*/
|
|
478
|
+
logExecutionTimeout(executionId, script, durationMs) {
|
|
479
|
+
this.emit({
|
|
480
|
+
type: AUDIT_EVENT_TYPES.EXECUTION_TIMEOUT,
|
|
481
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
482
|
+
executionId,
|
|
483
|
+
durationMs,
|
|
484
|
+
data: {
|
|
485
|
+
scriptHash: this.hashScript(script),
|
|
486
|
+
scriptLength: script.length
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Log tool call start.
|
|
492
|
+
*/
|
|
493
|
+
logToolCallStart(executionId, toolName, callDepth) {
|
|
494
|
+
this.emit({
|
|
495
|
+
type: AUDIT_EVENT_TYPES.TOOL_CALL_START,
|
|
496
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
497
|
+
executionId,
|
|
498
|
+
data: {
|
|
499
|
+
toolName,
|
|
500
|
+
callDepth
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Log tool call success.
|
|
506
|
+
*/
|
|
507
|
+
logToolCallSuccess(executionId, toolName, callDepth, durationMs) {
|
|
508
|
+
this.emit({
|
|
509
|
+
type: AUDIT_EVENT_TYPES.TOOL_CALL_SUCCESS,
|
|
510
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
511
|
+
executionId,
|
|
512
|
+
durationMs,
|
|
513
|
+
data: {
|
|
514
|
+
toolName,
|
|
515
|
+
callDepth
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Log tool call failure.
|
|
521
|
+
*/
|
|
522
|
+
logToolCallFailure(executionId, toolName, callDepth, durationMs, errorCode) {
|
|
523
|
+
this.emit({
|
|
524
|
+
type: AUDIT_EVENT_TYPES.TOOL_CALL_FAILURE,
|
|
525
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
526
|
+
executionId,
|
|
527
|
+
durationMs,
|
|
528
|
+
data: {
|
|
529
|
+
toolName,
|
|
530
|
+
callDepth,
|
|
531
|
+
errorCode
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Log security event: self-reference blocked.
|
|
537
|
+
*/
|
|
538
|
+
logSecuritySelfReference(executionId, toolName) {
|
|
539
|
+
this.emit({
|
|
540
|
+
type: AUDIT_EVENT_TYPES.SECURITY_SELF_REFERENCE,
|
|
541
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
542
|
+
executionId,
|
|
543
|
+
data: {
|
|
544
|
+
blocked: toolName,
|
|
545
|
+
reason: "Self-reference attack: attempted to call CodeCall tool from within AgentScript"
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Log security event: access denied.
|
|
551
|
+
*/
|
|
552
|
+
logSecurityAccessDenied(executionId, toolName, reason) {
|
|
553
|
+
this.emit({
|
|
554
|
+
type: AUDIT_EVENT_TYPES.SECURITY_ACCESS_DENIED,
|
|
555
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
556
|
+
executionId,
|
|
557
|
+
data: {
|
|
558
|
+
blocked: toolName,
|
|
559
|
+
reason: this.sanitizeError(reason)
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Log security event: AST validation blocked.
|
|
565
|
+
*/
|
|
566
|
+
logSecurityAstBlocked(executionId, pattern, reason) {
|
|
567
|
+
this.emit({
|
|
568
|
+
type: AUDIT_EVENT_TYPES.SECURITY_AST_BLOCKED,
|
|
569
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
570
|
+
executionId,
|
|
571
|
+
data: {
|
|
572
|
+
blocked: pattern,
|
|
573
|
+
reason: this.sanitizeError(reason)
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Log search operation.
|
|
579
|
+
*/
|
|
580
|
+
logSearch(executionId, query, resultCount, durationMs) {
|
|
581
|
+
this.emit({
|
|
582
|
+
type: AUDIT_EVENT_TYPES.SEARCH_PERFORMED,
|
|
583
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
584
|
+
executionId,
|
|
585
|
+
durationMs,
|
|
586
|
+
data: {
|
|
587
|
+
queryLength: query.length,
|
|
588
|
+
resultCount
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Log describe operation.
|
|
594
|
+
*/
|
|
595
|
+
logDescribe(executionId, toolNames, durationMs) {
|
|
596
|
+
this.emit({
|
|
597
|
+
type: AUDIT_EVENT_TYPES.DESCRIBE_PERFORMED,
|
|
598
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
599
|
+
executionId,
|
|
600
|
+
durationMs,
|
|
601
|
+
data: {
|
|
602
|
+
toolCount: toolNames.length,
|
|
603
|
+
toolNames: toolNames.slice(0, 10)
|
|
604
|
+
// Limit to first 10 for audit
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Log invoke operation.
|
|
610
|
+
*/
|
|
611
|
+
logInvoke(executionId, toolName, success, durationMs) {
|
|
612
|
+
this.emit({
|
|
613
|
+
type: AUDIT_EVENT_TYPES.INVOKE_PERFORMED,
|
|
614
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
615
|
+
executionId,
|
|
616
|
+
durationMs,
|
|
617
|
+
data: {
|
|
618
|
+
toolName,
|
|
619
|
+
success
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Emit an audit event to all listeners.
|
|
625
|
+
*/
|
|
626
|
+
emit(event) {
|
|
627
|
+
const frozenEvent = Object.freeze({ ...event, data: Object.freeze({ ...event.data }) });
|
|
628
|
+
for (const listener of this.listeners) {
|
|
629
|
+
try {
|
|
630
|
+
listener(frozenEvent);
|
|
631
|
+
} catch {
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Create a simple hash of the script for identification.
|
|
637
|
+
* Uses a fast, non-cryptographic hash for performance.
|
|
638
|
+
*/
|
|
639
|
+
hashScript(script) {
|
|
640
|
+
let hash = 0;
|
|
641
|
+
for (let i = 0; i < script.length; i++) {
|
|
642
|
+
const char = script.charCodeAt(i);
|
|
643
|
+
hash = (hash << 5) - hash + char;
|
|
644
|
+
hash = hash & hash;
|
|
645
|
+
}
|
|
646
|
+
return `sh_${(hash >>> 0).toString(16).padStart(8, "0")}`;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Sanitize error messages to remove sensitive information.
|
|
650
|
+
*/
|
|
651
|
+
sanitizeError(error) {
|
|
652
|
+
if (!error) return "Unknown error";
|
|
653
|
+
let sanitized = error.replace(/(?:\/[\w.-]+)+|(?:[A-Za-z]:\\[\w\\.-]+)+/g, "[path]");
|
|
654
|
+
sanitized = sanitized.replace(/:\d+:\d+/g, "");
|
|
655
|
+
sanitized = sanitized.replace(/\n\s*at .*/g, "");
|
|
656
|
+
if (sanitized.length > 200) {
|
|
657
|
+
sanitized = sanitized.substring(0, 200) + "...";
|
|
658
|
+
}
|
|
659
|
+
return sanitized.trim();
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
AuditLoggerService = __decorateClass([
|
|
663
|
+
Provider2({
|
|
664
|
+
name: "codecall:audit-logger",
|
|
665
|
+
scope: ProviderScope2.GLOBAL
|
|
666
|
+
})
|
|
667
|
+
], AuditLoggerService);
|
|
668
|
+
|
|
375
669
|
// plugins/plugin-codecall/src/services/tool-search.service.ts
|
|
376
670
|
import { TFIDFVectoria, VectoriaDB } from "vectoriadb";
|
|
377
671
|
|
|
672
|
+
// plugins/plugin-codecall/src/security/codecall-tool-policy.ts
|
|
673
|
+
var BLOCKED_NAMESPACE_PATTERNS = Object.freeze([/^system:/, /^internal:/, /^__/]);
|
|
674
|
+
var ALLOWED = { allowed: true };
|
|
675
|
+
function deny(reason) {
|
|
676
|
+
return { allowed: false, reason };
|
|
677
|
+
}
|
|
678
|
+
function codeCallAppIdOf(scope, entry) {
|
|
679
|
+
const lineage = scope?.tools?.lineageOf?.(entry) ?? [];
|
|
680
|
+
const owners = entry.owner ? [...lineage, entry.owner] : lineage;
|
|
681
|
+
return owners.find((owner) => owner.kind === "app")?.id;
|
|
682
|
+
}
|
|
683
|
+
function toCodeCallPolicyTool(entry, requestedName, scope) {
|
|
684
|
+
const { metadata } = entry;
|
|
685
|
+
return {
|
|
686
|
+
name: entry.name || entry.fullName,
|
|
687
|
+
fullName: entry.fullName,
|
|
688
|
+
aliases: requestedName ? [requestedName] : void 0,
|
|
689
|
+
appId: codeCallAppIdOf(scope, entry),
|
|
690
|
+
description: metadata?.description,
|
|
691
|
+
tags: metadata?.tags,
|
|
692
|
+
// `internal` tools refuse every external tools/call, CodeCall's included, so no surface may offer them.
|
|
693
|
+
hidden: metadata?.hideFromDiscovery === true || metadata?.visibility === "hidden" || metadata?.visibility === "internal",
|
|
694
|
+
codecall: metadata?.codecall
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
function toToolFilterInfo(tool) {
|
|
698
|
+
return {
|
|
699
|
+
name: tool.name,
|
|
700
|
+
appId: tool.appId,
|
|
701
|
+
source: tool.codecall?.source,
|
|
702
|
+
description: tool.description,
|
|
703
|
+
tags: tool.codecall?.tags ?? tool.tags
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function ownNamesOf(tool) {
|
|
707
|
+
return [tool.name, tool.fullName].filter((candidate) => !!candidate);
|
|
708
|
+
}
|
|
709
|
+
function checkCodeCallToolPolicy(tool, config) {
|
|
710
|
+
const { name } = tool;
|
|
711
|
+
const spellings = [...ownNamesOf(tool), ...tool.aliases ?? []];
|
|
712
|
+
if (spellings.some((candidate) => candidate.startsWith("codecall:"))) {
|
|
713
|
+
return deny("CodeCall meta-tools are not callable from CodeCall");
|
|
714
|
+
}
|
|
715
|
+
for (const candidate of spellings) {
|
|
716
|
+
if (BLOCKED_NAMESPACE_PATTERNS.some((pattern) => pattern.test(candidate))) {
|
|
717
|
+
return deny(`Tool "${candidate}" is in a namespace CodeCall never calls`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (tool.hidden === true) {
|
|
721
|
+
return deny(`Tool "${name}" is hidden from discovery`);
|
|
722
|
+
}
|
|
723
|
+
const enabled = tool.codecall?.enabledInCodeCall;
|
|
724
|
+
switch (config.mode) {
|
|
725
|
+
case "codecall_only":
|
|
726
|
+
case "metadata_driven":
|
|
727
|
+
if (enabled === false) {
|
|
728
|
+
return deny(`Tool "${name}" sets enabledInCodeCall: false`);
|
|
729
|
+
}
|
|
730
|
+
break;
|
|
731
|
+
case "codecall_opt_in":
|
|
732
|
+
if (enabled !== true) {
|
|
733
|
+
return deny(`Tool "${name}" has not opted in to CodeCall`);
|
|
734
|
+
}
|
|
735
|
+
break;
|
|
736
|
+
default:
|
|
737
|
+
return deny(`Unknown CodeCall mode: ${String(config.mode)}`);
|
|
738
|
+
}
|
|
739
|
+
if (config.includeTools && !config.includeTools(toToolFilterInfo(tool))) {
|
|
740
|
+
return deny(`Tool "${name}" is excluded by the includeTools filter`);
|
|
741
|
+
}
|
|
742
|
+
return ALLOWED;
|
|
743
|
+
}
|
|
744
|
+
function denyUnknownTool(name) {
|
|
745
|
+
return deny(`Tool "${name}" is not available through CodeCall`);
|
|
746
|
+
}
|
|
747
|
+
function resolveCodeCallTool(scope, name) {
|
|
748
|
+
const typedScope = scope;
|
|
749
|
+
const alias = /[-_]/.test(name) ? name.includes("_") ? name.replace(/_/g, "-") : name.replace(/-/g, "_") : void 0;
|
|
750
|
+
const candidates = alias ? [name, alias] : [name];
|
|
751
|
+
const matches = (entry) => candidates.includes(entry.fullName) || candidates.includes(entry.name);
|
|
752
|
+
const local = typedScope.tools?.getTools(true)?.find(matches);
|
|
753
|
+
if (local) return local;
|
|
754
|
+
const registries = typedScope.providers?.getRegistries?.("AppRegistry") ?? [];
|
|
755
|
+
for (const registry of registries) {
|
|
756
|
+
for (const app of registry.getApps()) {
|
|
757
|
+
if (!app.isRemote) continue;
|
|
758
|
+
const remote = app.tools.getTools(true).find(matches);
|
|
759
|
+
if (remote) return remote;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return void 0;
|
|
763
|
+
}
|
|
764
|
+
function checkDirectCallPolicy(tool, directCalls) {
|
|
765
|
+
if (!directCalls) return ALLOWED;
|
|
766
|
+
if (directCalls.enabled === false) {
|
|
767
|
+
return deny("Direct tool invocation is disabled");
|
|
768
|
+
}
|
|
769
|
+
const { allowedTools } = directCalls;
|
|
770
|
+
if (allowedTools && !ownNamesOf(tool).some((ownName) => allowedTools.includes(ownName))) {
|
|
771
|
+
return deny(`Tool "${tool.name}" is not in the direct-call allowlist`);
|
|
772
|
+
}
|
|
773
|
+
if (directCalls.filter && !directCalls.filter(toToolFilterInfo(tool))) {
|
|
774
|
+
return deny(`Tool "${tool.name}" is excluded by the directCalls filter`);
|
|
775
|
+
}
|
|
776
|
+
return ALLOWED;
|
|
777
|
+
}
|
|
778
|
+
function readCodeCallPolicyConfig(config) {
|
|
779
|
+
return {
|
|
780
|
+
// An absent mode means an unparsed/partial config, not a hostile one: fall back to the
|
|
781
|
+
// schema's own documented default rather than denying every call. An unrecognised mode
|
|
782
|
+
// still fails closed inside the policy's switch.
|
|
783
|
+
mode: config.get("mode") ?? "codecall_only",
|
|
784
|
+
includeTools: config.get("includeTools")
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function checkCodeCallToolAccess(scope, config, name, options = {}) {
|
|
788
|
+
const entry = resolveCodeCallTool(scope, name);
|
|
789
|
+
if (!entry) return denyUnknownTool(name);
|
|
790
|
+
const policyTool = toCodeCallPolicyTool(entry, name, scope);
|
|
791
|
+
const baseDecision = checkCodeCallToolPolicy(policyTool, readCodeCallPolicyConfig(config));
|
|
792
|
+
if (!baseDecision.allowed) return baseDecision;
|
|
793
|
+
if (options.directCall) {
|
|
794
|
+
const directDecision = checkDirectCallPolicy(
|
|
795
|
+
policyTool,
|
|
796
|
+
config.get("directCalls")
|
|
797
|
+
);
|
|
798
|
+
if (!directDecision.allowed) return directDecision;
|
|
799
|
+
}
|
|
800
|
+
return { allowed: true, entry };
|
|
801
|
+
}
|
|
802
|
+
|
|
378
803
|
// plugins/plugin-codecall/src/services/synonym-expansion.service.ts
|
|
379
804
|
var DEFAULT_SYNONYM_GROUPS = [
|
|
380
805
|
// ===========================================================================
|
|
@@ -1043,62 +1468,12 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1043
1468
|
this.initialized = true;
|
|
1044
1469
|
}
|
|
1045
1470
|
/**
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1049
|
-
* - Excludes tools with hideFromDiscovery: true (system tools like sendElicitationResult)
|
|
1050
|
-
* - Mode-based filtering (codecall_only, codecall_opt_in, metadata_driven)
|
|
1051
|
-
* - Per-tool metadata.codecall.enabledInCodeCall
|
|
1052
|
-
* - Custom includeTools filter function
|
|
1471
|
+
* Whether a tool belongs in the search index: exactly when CodeCall may execute it.
|
|
1472
|
+
* The decision is `codecall:execute`'s own, so search cannot show a tool execution refuses
|
|
1473
|
+
* or hide one it runs (GHSA-6w3j-82v5-6qrr).
|
|
1053
1474
|
*/
|
|
1054
1475
|
shouldIndexTool(tool) {
|
|
1055
|
-
|
|
1056
|
-
if (toolName.startsWith("codecall:")) {
|
|
1057
|
-
return false;
|
|
1058
|
-
}
|
|
1059
|
-
if (tool.metadata.hideFromDiscovery === true) {
|
|
1060
|
-
return false;
|
|
1061
|
-
}
|
|
1062
|
-
const codecallMeta = this.getCodeCallMetadata(tool);
|
|
1063
|
-
switch (this.config.mode) {
|
|
1064
|
-
case "codecall_only":
|
|
1065
|
-
if (codecallMeta?.enabledInCodeCall === false) {
|
|
1066
|
-
return false;
|
|
1067
|
-
}
|
|
1068
|
-
break;
|
|
1069
|
-
case "codecall_opt_in":
|
|
1070
|
-
if (codecallMeta?.enabledInCodeCall !== true) {
|
|
1071
|
-
return false;
|
|
1072
|
-
}
|
|
1073
|
-
break;
|
|
1074
|
-
case "metadata_driven":
|
|
1075
|
-
if (codecallMeta?.enabledInCodeCall === false) {
|
|
1076
|
-
return false;
|
|
1077
|
-
}
|
|
1078
|
-
break;
|
|
1079
|
-
default:
|
|
1080
|
-
throw new Error(`Unknown CodeCall mode: ${this.config.mode}`);
|
|
1081
|
-
}
|
|
1082
|
-
if (this.config.includeTools) {
|
|
1083
|
-
const appId = this.extractAppId(tool);
|
|
1084
|
-
const filterInfo = {
|
|
1085
|
-
name: toolName,
|
|
1086
|
-
appId,
|
|
1087
|
-
source: codecallMeta?.source,
|
|
1088
|
-
description: tool.metadata.description,
|
|
1089
|
-
tags: codecallMeta?.tags || tool.metadata.tags
|
|
1090
|
-
};
|
|
1091
|
-
if (!this.config.includeTools(filterInfo)) {
|
|
1092
|
-
return false;
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
return true;
|
|
1096
|
-
}
|
|
1097
|
-
/**
|
|
1098
|
-
* Extract CodeCall-specific metadata from a tool.
|
|
1099
|
-
*/
|
|
1100
|
-
getCodeCallMetadata(tool) {
|
|
1101
|
-
return tool.metadata?.codecall;
|
|
1476
|
+
return checkCodeCallToolPolicy(toCodeCallPolicyTool(tool, void 0, this.scope), this.config).allowed;
|
|
1102
1477
|
}
|
|
1103
1478
|
/**
|
|
1104
1479
|
* Initializes the search service by indexing all tools from the registry.
|
|
@@ -1188,11 +1563,7 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1188
1563
|
* Extracts app ID from tool's owner lineage
|
|
1189
1564
|
*/
|
|
1190
1565
|
extractAppId(tool) {
|
|
1191
|
-
|
|
1192
|
-
if (tool.owner.kind === "app") {
|
|
1193
|
-
return tool.owner.id;
|
|
1194
|
-
}
|
|
1195
|
-
return void 0;
|
|
1566
|
+
return codeCallAppIdOf(this.scope, tool);
|
|
1196
1567
|
}
|
|
1197
1568
|
/**
|
|
1198
1569
|
* Searches for tools matching the query
|
|
@@ -1279,7 +1650,7 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1279
1650
|
|
|
1280
1651
|
// plugins/plugin-codecall/src/services/enclave.service.ts
|
|
1281
1652
|
import { Enclave } from "@enclave-vm/core";
|
|
1282
|
-
import { Provider as
|
|
1653
|
+
import { Provider as Provider3, ProviderScope as ProviderScope3 } from "@frontmcp/sdk";
|
|
1283
1654
|
var ScriptTooLargeError = class extends Error {
|
|
1284
1655
|
code = "SCRIPT_TOO_LARGE";
|
|
1285
1656
|
scriptLength;
|
|
@@ -1398,15 +1769,23 @@ var EnclaveService = class {
|
|
|
1398
1769
|
const error = result.error;
|
|
1399
1770
|
const timedOut = error.message?.includes("timed out") || error.code === "TIMEOUT";
|
|
1400
1771
|
if (error.code === "VALIDATION_ERROR") {
|
|
1772
|
+
const issues = error.data?.issues ?? [];
|
|
1773
|
+
const blockedPatterns = [...new Set(issues.map((issue) => issue.code).filter((code) => !!code))];
|
|
1401
1774
|
return {
|
|
1402
1775
|
success: false,
|
|
1403
1776
|
error: {
|
|
1404
1777
|
message: error.message,
|
|
1405
1778
|
name: "ValidationError",
|
|
1406
|
-
code: error.code
|
|
1779
|
+
code: error.code,
|
|
1780
|
+
...blockedPatterns.length > 0 ? { blockedPatterns } : {}
|
|
1407
1781
|
},
|
|
1408
1782
|
logs,
|
|
1409
|
-
timedOut: false
|
|
1783
|
+
timedOut: false,
|
|
1784
|
+
stats: {
|
|
1785
|
+
duration: result.stats.duration,
|
|
1786
|
+
toolCallCount: result.stats.toolCallCount,
|
|
1787
|
+
iterationCount: result.stats.iterationCount
|
|
1788
|
+
}
|
|
1410
1789
|
};
|
|
1411
1790
|
}
|
|
1412
1791
|
const errorData = error.data;
|
|
@@ -1446,10 +1825,10 @@ var EnclaveService = class {
|
|
|
1446
1825
|
}
|
|
1447
1826
|
};
|
|
1448
1827
|
EnclaveService = __decorateClass([
|
|
1449
|
-
|
|
1828
|
+
Provider3({
|
|
1450
1829
|
name: "codecall:enclave",
|
|
1451
1830
|
description: "Executes AgentScript code in a secure enclave",
|
|
1452
|
-
scope:
|
|
1831
|
+
scope: ProviderScope3.GLOBAL
|
|
1453
1832
|
})
|
|
1454
1833
|
], EnclaveService);
|
|
1455
1834
|
|
|
@@ -1509,6 +1888,9 @@ var SearchTool = class extends ToolContext {
|
|
|
1509
1888
|
const { queries, appIds, excludeToolNames = [], topK = 5, minRelevanceScore = 0.3 } = input;
|
|
1510
1889
|
const searchService = this.get(ToolSearchService);
|
|
1511
1890
|
const warnings = [];
|
|
1891
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
1892
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
1893
|
+
const startedAt = Date.now();
|
|
1512
1894
|
const nonExistentExcludedTools = excludeToolNames.filter((toolName) => !searchService.hasTool(toolName));
|
|
1513
1895
|
if (nonExistentExcludedTools.length > 0) {
|
|
1514
1896
|
warnings.push({
|
|
@@ -1564,6 +1946,7 @@ var SearchTool = class extends ToolContext {
|
|
|
1564
1946
|
message: `${lowRelevanceCount} result(s) filtered due to relevance below ${minRelevanceScore}`
|
|
1565
1947
|
});
|
|
1566
1948
|
}
|
|
1949
|
+
audit?.logSearch(executionId, queries.join(" "), tools.length, Date.now() - startedAt);
|
|
1567
1950
|
return {
|
|
1568
1951
|
tools,
|
|
1569
1952
|
warnings,
|
|
@@ -1594,7 +1977,7 @@ SearchTool = __decorateClass([
|
|
|
1594
1977
|
], SearchTool);
|
|
1595
1978
|
|
|
1596
1979
|
// plugins/plugin-codecall/src/tools/search-skills.tool.ts
|
|
1597
|
-
import { Tool as Tool2, ToolContext as ToolContext2 } from "@frontmcp/sdk";
|
|
1980
|
+
import { filterServableSkills, Tool as Tool2, ToolContext as ToolContext2 } from "@frontmcp/sdk";
|
|
1598
1981
|
|
|
1599
1982
|
// plugins/plugin-codecall/src/tools/search-skills.schema.ts
|
|
1600
1983
|
import { z as z3 } from "@frontmcp/lazy-zod";
|
|
@@ -1655,7 +2038,7 @@ var SearchSkillsTool = class extends ToolContext2 {
|
|
|
1655
2038
|
async execute(input) {
|
|
1656
2039
|
const { queries, tags, excludeSkillNames = [], topK = 5, minRelevanceScore = 0.1 } = input;
|
|
1657
2040
|
const skillRegistry = this.scope.skills;
|
|
1658
|
-
const executableSkills = skillRegistry.getExecutableSkills();
|
|
2041
|
+
const executableSkills = await filterServableSkills(this.scope, skillRegistry.getExecutableSkills());
|
|
1659
2042
|
const executableSet = new Set(executableSkills.map((s) => s.name));
|
|
1660
2043
|
const totalExecutableSkills = executableSkills.length;
|
|
1661
2044
|
const warnings = [];
|
|
@@ -1746,7 +2129,7 @@ SearchSkillsTool = __decorateClass([
|
|
|
1746
2129
|
], SearchSkillsTool);
|
|
1747
2130
|
|
|
1748
2131
|
// plugins/plugin-codecall/src/tools/search-knowledge.tool.ts
|
|
1749
|
-
import { Tool as Tool3, ToolContext as ToolContext3 } from "@frontmcp/sdk";
|
|
2132
|
+
import { filterServableSkills as filterServableSkills2, Tool as Tool3, ToolContext as ToolContext3 } from "@frontmcp/sdk";
|
|
1750
2133
|
|
|
1751
2134
|
// plugins/plugin-codecall/src/tools/search-knowledge.schema.ts
|
|
1752
2135
|
import { z as z4 } from "@frontmcp/lazy-zod";
|
|
@@ -1796,7 +2179,7 @@ var SearchKnowledgeTool = class extends ToolContext3 {
|
|
|
1796
2179
|
async execute(input) {
|
|
1797
2180
|
const { queries, tags, excludeSkillNames = [], topK = 5, minRelevanceScore = 0.1 } = input;
|
|
1798
2181
|
const skillRegistry = this.scope.skills;
|
|
1799
|
-
const knowledgeSkills = skillRegistry.getKnowledgeOnlySkills();
|
|
2182
|
+
const knowledgeSkills = await filterServableSkills2(this.scope, skillRegistry.getKnowledgeOnlySkills());
|
|
1800
2183
|
const knowledgeSet = new Set(knowledgeSkills.map((s) => s.name));
|
|
1801
2184
|
const totalKnowledgeSkills = knowledgeSkills.length;
|
|
1802
2185
|
const warnings = [];
|
|
@@ -2384,21 +2767,24 @@ var describeToolOutputSchema = z5.object({
|
|
|
2384
2767
|
var DescribeTool = class extends ToolContext4 {
|
|
2385
2768
|
async execute(input) {
|
|
2386
2769
|
const { toolNames } = input;
|
|
2770
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
2771
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
2772
|
+
const startedAt = Date.now();
|
|
2387
2773
|
const tools = [];
|
|
2388
2774
|
const notFound = [];
|
|
2389
|
-
const
|
|
2390
|
-
const toolMap = new Map(allTools.map((t) => [t.name, t]));
|
|
2391
|
-
const fullNameMap = new Map(allTools.map((t) => [t.fullName, t]));
|
|
2775
|
+
const config = this.get(CodeCallConfig);
|
|
2392
2776
|
for (const toolName of toolNames) {
|
|
2393
2777
|
if (isBlockedSelfReference(toolName)) {
|
|
2394
2778
|
notFound.push(toolName);
|
|
2395
2779
|
continue;
|
|
2396
2780
|
}
|
|
2397
|
-
const
|
|
2398
|
-
if (!
|
|
2781
|
+
const access = checkCodeCallToolAccess(this.scope, config, toolName);
|
|
2782
|
+
if (!access.allowed) {
|
|
2783
|
+
audit?.logSecurityAccessDenied(executionId, toolName, access.reason);
|
|
2399
2784
|
notFound.push(toolName);
|
|
2400
2785
|
continue;
|
|
2401
2786
|
}
|
|
2787
|
+
const tool = access.entry;
|
|
2402
2788
|
const appId = this.extractAppId(tool);
|
|
2403
2789
|
const inputSchema = this.getInputSchema(tool);
|
|
2404
2790
|
const outputSchema = this.toJsonSchema(tool.rawOutputSchema ?? tool.outputSchema);
|
|
@@ -2413,6 +2799,7 @@ var DescribeTool = class extends ToolContext4 {
|
|
|
2413
2799
|
usageExamples
|
|
2414
2800
|
});
|
|
2415
2801
|
}
|
|
2802
|
+
audit?.logDescribe(executionId, toolNames, Date.now() - startedAt);
|
|
2416
2803
|
return {
|
|
2417
2804
|
tools,
|
|
2418
2805
|
notFound: notFound.length > 0 ? notFound : void 0
|
|
@@ -2667,20 +3054,51 @@ function getErrorCode(error) {
|
|
|
2667
3054
|
return TOOL_CALL_ERROR_CODES.EXECUTION;
|
|
2668
3055
|
}
|
|
2669
3056
|
var ExecuteTool = class extends ToolContext5 {
|
|
3057
|
+
/**
|
|
3058
|
+
* Resolve the server-side CodeCall policy for a tool name (GHSA-6w3j-82v5-6qrr).
|
|
3059
|
+
*
|
|
3060
|
+
* Shares its decision with discovery and with `codecall:invoke`, so the three cannot
|
|
3061
|
+
* diverge. A name matching no registered tool is denied rather than passed through —
|
|
3062
|
+
* otherwise it would skip every metadata-driven check and reach `tools:call-tool`
|
|
3063
|
+
* unexamined.
|
|
3064
|
+
*/
|
|
3065
|
+
checkToolPolicy(name) {
|
|
3066
|
+
return checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), name);
|
|
3067
|
+
}
|
|
2670
3068
|
async execute(input) {
|
|
2671
3069
|
const { script, allowedTools } = input;
|
|
3070
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
3071
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
3072
|
+
const callDepth = 1;
|
|
3073
|
+
let toolCallCount = 0;
|
|
2672
3074
|
const allowedToolSet = allowedTools ? new Set(allowedTools) : null;
|
|
2673
3075
|
const environment = {
|
|
2674
3076
|
callTool: async (name, toolInput, options) => {
|
|
2675
3077
|
const throwOnError = options?.throwOnError !== false;
|
|
3078
|
+
if (isBlockedSelfReference(name)) {
|
|
3079
|
+
audit?.logSecuritySelfReference(executionId, name);
|
|
3080
|
+
}
|
|
2676
3081
|
assertNotSelfReference(name);
|
|
3082
|
+
const decision = this.checkToolPolicy(name);
|
|
3083
|
+
if (!decision.allowed) {
|
|
3084
|
+
audit?.logSecurityAccessDenied(executionId, name, decision.reason);
|
|
3085
|
+
const error = createToolCallError(TOOL_CALL_ERROR_CODES.ACCESS_DENIED, name, decision.reason);
|
|
3086
|
+
if (throwOnError) {
|
|
3087
|
+
throw error;
|
|
3088
|
+
}
|
|
3089
|
+
return { success: false, error };
|
|
3090
|
+
}
|
|
2677
3091
|
if (allowedToolSet && !allowedToolSet.has(name)) {
|
|
3092
|
+
audit?.logSecurityAccessDenied(executionId, name, "not in the caller-supplied allowedTools list");
|
|
2678
3093
|
const error = createToolCallError(TOOL_CALL_ERROR_CODES.ACCESS_DENIED, name);
|
|
2679
3094
|
if (throwOnError) {
|
|
2680
3095
|
throw error;
|
|
2681
3096
|
}
|
|
2682
3097
|
return { success: false, error };
|
|
2683
3098
|
}
|
|
3099
|
+
toolCallCount += 1;
|
|
3100
|
+
audit?.logToolCallStart(executionId, name, callDepth);
|
|
3101
|
+
const toolCallStartedAt = Date.now();
|
|
2684
3102
|
try {
|
|
2685
3103
|
const request = {
|
|
2686
3104
|
method: "tools/call",
|
|
@@ -2694,6 +3112,13 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2694
3112
|
};
|
|
2695
3113
|
const mcpResult = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
2696
3114
|
if (!mcpResult) {
|
|
3115
|
+
audit?.logToolCallFailure(
|
|
3116
|
+
executionId,
|
|
3117
|
+
name,
|
|
3118
|
+
callDepth,
|
|
3119
|
+
Date.now() - toolCallStartedAt,
|
|
3120
|
+
TOOL_CALL_ERROR_CODES.EXECUTION
|
|
3121
|
+
);
|
|
2697
3122
|
const error = createToolCallError(TOOL_CALL_ERROR_CODES.EXECUTION, name, "Flow returned no result");
|
|
2698
3123
|
if (throwOnError) {
|
|
2699
3124
|
throw error;
|
|
@@ -2701,6 +3126,7 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2701
3126
|
return { success: false, error };
|
|
2702
3127
|
}
|
|
2703
3128
|
const result = extractResultFromCallToolResult(mcpResult);
|
|
3129
|
+
audit?.logToolCallSuccess(executionId, name, callDepth, Date.now() - toolCallStartedAt);
|
|
2704
3130
|
if (throwOnError) {
|
|
2705
3131
|
return result;
|
|
2706
3132
|
}
|
|
@@ -2708,6 +3134,7 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2708
3134
|
} catch (error) {
|
|
2709
3135
|
const errorCode = getErrorCode(error);
|
|
2710
3136
|
const rawMessage = error instanceof Error ? error.message : void 0;
|
|
3137
|
+
audit?.logToolCallFailure(executionId, name, callDepth, Date.now() - toolCallStartedAt, errorCode);
|
|
2711
3138
|
const sanitizedError = createToolCallError(errorCode, name, rawMessage);
|
|
2712
3139
|
if (throwOnError) {
|
|
2713
3140
|
throw sanitizedError;
|
|
@@ -2717,11 +3144,17 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2717
3144
|
},
|
|
2718
3145
|
getTool: (name) => {
|
|
2719
3146
|
try {
|
|
2720
|
-
if (isBlockedSelfReference(name))
|
|
3147
|
+
if (isBlockedSelfReference(name)) {
|
|
3148
|
+
audit?.logSecuritySelfReference(executionId, name);
|
|
3149
|
+
return void 0;
|
|
3150
|
+
}
|
|
3151
|
+
const introspectionAccess = this.checkToolPolicy(name);
|
|
3152
|
+
if (!introspectionAccess.allowed) {
|
|
3153
|
+
audit?.logSecurityAccessDenied(executionId, name, introspectionAccess.reason);
|
|
3154
|
+
return void 0;
|
|
3155
|
+
}
|
|
2721
3156
|
if (allowedToolSet && !allowedToolSet.has(name)) return void 0;
|
|
2722
|
-
const
|
|
2723
|
-
const tool = tools.find((t) => t.name === name || t.fullName === name);
|
|
2724
|
-
if (!tool) return void 0;
|
|
3157
|
+
const tool = introspectionAccess.entry;
|
|
2725
3158
|
return toPlainJson({
|
|
2726
3159
|
name: tool.name,
|
|
2727
3160
|
description: tool.metadata?.description,
|
|
@@ -2741,8 +3174,11 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2741
3174
|
}
|
|
2742
3175
|
};
|
|
2743
3176
|
try {
|
|
2744
|
-
const
|
|
2745
|
-
const
|
|
3177
|
+
const policyConfig = readCodeCallPolicyConfig(this.get(CodeCallConfig));
|
|
3178
|
+
const callableTools = this.scope.tools.getTools(true).filter(
|
|
3179
|
+
(tool) => checkCodeCallToolPolicy(toCodeCallPolicyTool(tool, void 0, this.scope), policyConfig).allowed
|
|
3180
|
+
);
|
|
3181
|
+
const { namespaces, skipped } = buildToolNamespaces(callableTools, environment.callTool);
|
|
2746
3182
|
environment.namespaces = namespaces;
|
|
2747
3183
|
if (skipped.length > 0) {
|
|
2748
3184
|
this.logger?.debug?.("codecall: tools skipped during namespace generation", {
|
|
@@ -2758,9 +3194,14 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2758
3194
|
}
|
|
2759
3195
|
const enclaveService = this.get(EnclaveService);
|
|
2760
3196
|
const config = this.get(CodeCallConfig);
|
|
3197
|
+
const startedAt = Date.now();
|
|
3198
|
+
const elapsed = (reported) => reported ?? Date.now() - startedAt;
|
|
3199
|
+
audit?.logExecutionStart(executionId, script);
|
|
2761
3200
|
try {
|
|
2762
3201
|
const executionResult = await enclaveService.execute(script, environment);
|
|
3202
|
+
const durationMs = elapsed(executionResult.stats?.duration);
|
|
2763
3203
|
if (executionResult.timedOut) {
|
|
3204
|
+
audit?.logExecutionTimeout(executionId, script, durationMs);
|
|
2764
3205
|
return {
|
|
2765
3206
|
status: "timeout",
|
|
2766
3207
|
error: {
|
|
@@ -2771,6 +3212,8 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2771
3212
|
if (!executionResult.success) {
|
|
2772
3213
|
const error = executionResult.error;
|
|
2773
3214
|
if (error.code === "VALIDATION_ERROR" || error.name === "ValidationError") {
|
|
3215
|
+
audit?.logSecurityAstBlocked(executionId, error.blockedPatterns?.join(", ") ?? "UNKNOWN", error.message);
|
|
3216
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
2774
3217
|
return {
|
|
2775
3218
|
status: "illegal_access",
|
|
2776
3219
|
error: {
|
|
@@ -2780,6 +3223,7 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2780
3223
|
};
|
|
2781
3224
|
}
|
|
2782
3225
|
if (error.toolName) {
|
|
3226
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
2783
3227
|
return {
|
|
2784
3228
|
status: "tool_error",
|
|
2785
3229
|
error: {
|
|
@@ -2792,6 +3236,7 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2792
3236
|
}
|
|
2793
3237
|
};
|
|
2794
3238
|
}
|
|
3239
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
2795
3240
|
return {
|
|
2796
3241
|
status: "runtime_error",
|
|
2797
3242
|
error: {
|
|
@@ -2802,6 +3247,7 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2802
3247
|
}
|
|
2803
3248
|
};
|
|
2804
3249
|
}
|
|
3250
|
+
audit?.logExecutionSuccess(executionId, script, durationMs, toolCallCount);
|
|
2805
3251
|
return {
|
|
2806
3252
|
status: "ok",
|
|
2807
3253
|
result: executionResult.result,
|
|
@@ -2810,6 +3256,7 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
2810
3256
|
} catch (error) {
|
|
2811
3257
|
const errorName = error instanceof Error ? error.name : "Error";
|
|
2812
3258
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3259
|
+
audit?.logExecutionFailure(executionId, script, elapsed(), errorMessage);
|
|
2813
3260
|
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
2814
3261
|
const errorLoc = error.loc;
|
|
2815
3262
|
if (errorName === "SyntaxError" || errorMessage?.includes("syntax")) {
|
|
@@ -2887,11 +3334,20 @@ function buildErrorResult(message) {
|
|
|
2887
3334
|
var InvokeTool = class extends ToolContext6 {
|
|
2888
3335
|
async execute(input) {
|
|
2889
3336
|
const { tool: toolName, input: toolInput } = input;
|
|
3337
|
+
const audit = this.tryGet(AuditLoggerService);
|
|
3338
|
+
const executionId = audit ? audit.generateExecutionId() : "";
|
|
3339
|
+
const startedAt = Date.now();
|
|
2890
3340
|
if (isBlockedSelfReference(toolName)) {
|
|
3341
|
+
audit?.logSecuritySelfReference(executionId, toolName);
|
|
2891
3342
|
return buildErrorResult(
|
|
2892
3343
|
`Tool "${toolName}" cannot be invoked directly. CodeCall tools are internal and not accessible via codecall:invoke.`
|
|
2893
3344
|
);
|
|
2894
3345
|
}
|
|
3346
|
+
const decision = checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), toolName, { directCall: true });
|
|
3347
|
+
if (!decision.allowed) {
|
|
3348
|
+
audit?.logSecurityAccessDenied(executionId, toolName, decision.reason);
|
|
3349
|
+
return buildErrorResult(`Tool "${toolName}" is not available. Use codecall:search to discover available tools.`);
|
|
3350
|
+
}
|
|
2895
3351
|
const request = {
|
|
2896
3352
|
method: "tools/call",
|
|
2897
3353
|
params: {
|
|
@@ -2904,8 +3360,10 @@ var InvokeTool = class extends ToolContext6 {
|
|
|
2904
3360
|
};
|
|
2905
3361
|
const result = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
2906
3362
|
if (!result) {
|
|
3363
|
+
audit?.logInvoke(executionId, toolName, false, Date.now() - startedAt);
|
|
2907
3364
|
return buildErrorResult(`Tool "${toolName}" not found. Use codecall:search to discover available tools.`);
|
|
2908
3365
|
}
|
|
3366
|
+
audit?.logInvoke(executionId, toolName, true, Date.now() - startedAt);
|
|
2909
3367
|
return result;
|
|
2910
3368
|
}
|
|
2911
3369
|
};
|
|
@@ -2928,6 +3386,10 @@ InvokeTool = __decorateClass([
|
|
|
2928
3386
|
], InvokeTool);
|
|
2929
3387
|
|
|
2930
3388
|
// plugins/plugin-codecall/src/codecall.plugin.ts
|
|
3389
|
+
function auditLogFields(event) {
|
|
3390
|
+
const { executionId, durationMs, data } = event;
|
|
3391
|
+
return durationMs === void 0 ? { executionId, ...data } : { executionId, durationMs, ...data };
|
|
3392
|
+
}
|
|
2931
3393
|
var CodeCallPlugin = class extends DynamicPlugin {
|
|
2932
3394
|
options;
|
|
2933
3395
|
cachedLogger;
|
|
@@ -2962,6 +3424,21 @@ var CodeCallPlugin = class extends DynamicPlugin {
|
|
|
2962
3424
|
return new EnclaveService(cfg);
|
|
2963
3425
|
}
|
|
2964
3426
|
},
|
|
3427
|
+
{
|
|
3428
|
+
// GHSA-adjacent: production.mdx promised operators these events and nothing emitted them,
|
|
3429
|
+
// because the service was never registered. The subscription lives here, in the factory,
|
|
3430
|
+
// for a reason: there is no provider teardown hook in the SDK, so subscribing anywhere
|
|
3431
|
+
// per-request would grow the listener Set for the life of the process.
|
|
3432
|
+
name: "codecall:audit-logger",
|
|
3433
|
+
provide: AuditLoggerService,
|
|
3434
|
+
inject: () => [ScopeEntry],
|
|
3435
|
+
useFactory: (scope) => {
|
|
3436
|
+
const audit = new AuditLoggerService();
|
|
3437
|
+
const logger = scope.logger.child("codecall:audit");
|
|
3438
|
+
audit.subscribe((event) => logger.info(event.type, auditLogFields(event)));
|
|
3439
|
+
return audit;
|
|
3440
|
+
}
|
|
3441
|
+
},
|
|
2965
3442
|
{
|
|
2966
3443
|
name: "codecall:tool-search",
|
|
2967
3444
|
provide: ToolSearchService,
|
|
@@ -3059,6 +3536,8 @@ CodeCallPlugin = __decorateClass([
|
|
|
3059
3536
|
})
|
|
3060
3537
|
], CodeCallPlugin);
|
|
3061
3538
|
export {
|
|
3539
|
+
AUDIT_EVENT_TYPES,
|
|
3540
|
+
AuditLoggerService,
|
|
3062
3541
|
CodeCallPlugin,
|
|
3063
3542
|
codeCallDirectCallsOptionsSchema,
|
|
3064
3543
|
codeCallEmbeddingOptionsSchema,
|