@dexto/core 1.6.0 → 1.6.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.
Files changed (79) hide show
  1. package/dist/agent/DextoAgent.cjs +25 -5
  2. package/dist/agent/DextoAgent.d.ts +12 -1
  3. package/dist/agent/DextoAgent.d.ts.map +1 -1
  4. package/dist/agent/DextoAgent.js +25 -5
  5. package/dist/agent/schemas.d.ts +18 -18
  6. package/dist/approval/manager.cjs +87 -27
  7. package/dist/approval/manager.d.ts +10 -1
  8. package/dist/approval/manager.d.ts.map +1 -1
  9. package/dist/approval/manager.js +87 -27
  10. package/dist/approval/schemas.cjs +22 -8
  11. package/dist/approval/schemas.d.ts +276 -102
  12. package/dist/approval/schemas.d.ts.map +1 -1
  13. package/dist/approval/schemas.js +22 -8
  14. package/dist/context/manager.cjs +2 -2
  15. package/dist/context/manager.d.ts +2 -1
  16. package/dist/context/manager.d.ts.map +1 -1
  17. package/dist/context/manager.js +2 -2
  18. package/dist/context/types.d.ts +3 -2
  19. package/dist/context/types.d.ts.map +1 -1
  20. package/dist/events/index.d.ts +17 -12
  21. package/dist/events/index.d.ts.map +1 -1
  22. package/dist/hooks/index.d.ts +1 -1
  23. package/dist/hooks/index.d.ts.map +1 -1
  24. package/dist/hooks/types.d.ts +1 -22
  25. package/dist/hooks/types.d.ts.map +1 -1
  26. package/dist/llm/executor/stream-processor.cjs +3 -3
  27. package/dist/llm/executor/stream-processor.d.ts +3 -2
  28. package/dist/llm/executor/stream-processor.d.ts.map +1 -1
  29. package/dist/llm/executor/stream-processor.js +3 -3
  30. package/dist/llm/executor/turn-executor.cjs +3 -3
  31. package/dist/llm/executor/turn-executor.d.ts +1 -1
  32. package/dist/llm/executor/turn-executor.d.ts.map +1 -1
  33. package/dist/llm/executor/turn-executor.js +3 -3
  34. package/dist/llm/providers/local/schemas.d.ts +2 -2
  35. package/dist/llm/schemas.d.ts +4 -4
  36. package/dist/llm/services/vercel.cjs +1 -1
  37. package/dist/llm/services/vercel.js +1 -1
  38. package/dist/logger/default-logger-factory.d.ts +12 -12
  39. package/dist/logger/v2/dexto-logger.cjs +35 -0
  40. package/dist/logger/v2/dexto-logger.d.ts +19 -0
  41. package/dist/logger/v2/dexto-logger.d.ts.map +1 -1
  42. package/dist/logger/v2/dexto-logger.js +35 -0
  43. package/dist/logger/v2/schemas.d.ts +6 -6
  44. package/dist/logger/v2/test-utils.cjs +2 -0
  45. package/dist/logger/v2/test-utils.d.ts.map +1 -1
  46. package/dist/logger/v2/test-utils.js +2 -0
  47. package/dist/logger/v2/types.d.ts +14 -1
  48. package/dist/logger/v2/types.d.ts.map +1 -1
  49. package/dist/mcp/schemas.d.ts +15 -15
  50. package/dist/memory/schemas.d.ts +4 -4
  51. package/dist/prompts/schemas.d.ts +7 -7
  52. package/dist/systemPrompt/in-built-prompts.cjs +5 -5
  53. package/dist/systemPrompt/in-built-prompts.d.ts +1 -1
  54. package/dist/systemPrompt/in-built-prompts.d.ts.map +1 -1
  55. package/dist/systemPrompt/in-built-prompts.js +5 -5
  56. package/dist/systemPrompt/schemas.d.ts +5 -5
  57. package/dist/systemPrompt/types.d.ts +11 -0
  58. package/dist/systemPrompt/types.d.ts.map +1 -1
  59. package/dist/tools/display-types.d.ts +10 -0
  60. package/dist/tools/display-types.d.ts.map +1 -1
  61. package/dist/tools/index.cjs +3 -1
  62. package/dist/tools/index.d.ts +1 -0
  63. package/dist/tools/index.d.ts.map +1 -1
  64. package/dist/tools/index.js +1 -0
  65. package/dist/tools/presentation.cjs +49 -0
  66. package/dist/tools/presentation.d.ts +11 -0
  67. package/dist/tools/presentation.d.ts.map +1 -0
  68. package/dist/tools/presentation.js +24 -0
  69. package/dist/tools/tool-manager.cjs +322 -155
  70. package/dist/tools/tool-manager.d.ts +23 -25
  71. package/dist/tools/tool-manager.d.ts.map +1 -1
  72. package/dist/tools/tool-manager.js +322 -155
  73. package/dist/tools/types.d.ts +134 -55
  74. package/dist/tools/types.d.ts.map +1 -1
  75. package/dist/utils/path.cjs +10 -1
  76. package/dist/utils/path.d.ts +5 -2
  77. package/dist/utils/path.d.ts.map +1 -1
  78. package/dist/utils/path.js +10 -1
  79. package/package.json +2 -2
@@ -89,6 +89,7 @@ let _ToolManager = class _ToolManager {
89
89
  agentEventBus;
90
90
  toolPolicies;
91
91
  toolExecutionContextFactory;
92
+ contributorContextFactory;
92
93
  // Hook support - set after construction to avoid circular dependencies
93
94
  hookManager;
94
95
  sessionManager;
@@ -447,16 +448,175 @@ let _ToolManager = class _ToolManager {
447
448
  });
448
449
  }
449
450
  // ==================== Pattern Approval Helpers ====================
451
+ getToolApprovalPatternKeyFn(toolName) {
452
+ const tool = this.agentTools.get(toolName);
453
+ return tool?.approval?.patternKey;
454
+ }
455
+ getToolSuggestApprovalPatternsFn(toolName) {
456
+ const tool = this.agentTools.get(toolName);
457
+ return tool?.approval?.suggestPatterns;
458
+ }
459
+ getToolApprovalOverrideFn(toolName) {
460
+ const tool = this.agentTools.get(toolName);
461
+ return tool?.approval?.override;
462
+ }
463
+ getToolApprovalOnGrantedFn(toolName) {
464
+ const tool = this.agentTools.get(toolName);
465
+ return tool?.approval?.onGranted;
466
+ }
467
+ getToolPreviewFn(toolName) {
468
+ const tool = this.agentTools.get(toolName);
469
+ return tool?.presentation?.preview;
470
+ }
471
+ getToolDescribeHeaderFn(toolName) {
472
+ const tool = this.agentTools.get(toolName);
473
+ return tool?.presentation?.describeHeader;
474
+ }
475
+ getToolDescribeArgsFn(toolName) {
476
+ const tool = this.agentTools.get(toolName);
477
+ return tool?.presentation?.describeArgs;
478
+ }
479
+ getToolDescribeResultFn(toolName) {
480
+ const tool = this.agentTools.get(toolName);
481
+ return tool?.presentation?.describeResult;
482
+ }
483
+ buildGenericToolPresentationSnapshot(toolName) {
484
+ const toTitleCase = (name) => name.replace(/[_-]+/g, " ").split(" ").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
485
+ const isMcp = toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX);
486
+ const fallbackTitle = (() => {
487
+ if (!isMcp) {
488
+ return toTitleCase(toolName);
489
+ }
490
+ const actualToolName = toolName.substring(_ToolManager.MCP_TOOL_PREFIX.length);
491
+ const parts = actualToolName.split("--");
492
+ const toolPart = parts.length >= 2 ? parts.slice(1).join("--") : actualToolName;
493
+ return toTitleCase(toolPart);
494
+ })();
495
+ const snapshot = {
496
+ version: 1,
497
+ source: {
498
+ type: toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX) ? "mcp" : "local"
499
+ },
500
+ header: {
501
+ title: fallbackTitle
502
+ }
503
+ };
504
+ if (snapshot.source?.type === "mcp") {
505
+ const actualToolName = toolName.substring(_ToolManager.MCP_TOOL_PREFIX.length);
506
+ const parts = actualToolName.split("--");
507
+ if (parts.length >= 2 && parts[0]) {
508
+ snapshot.source.mcpServerName = parts[0];
509
+ }
510
+ }
511
+ return snapshot;
512
+ }
513
+ getToolPresentationSnapshotForToolCallEvent(toolName, args, toolCallId, sessionId) {
514
+ const fallback = this.buildGenericToolPresentationSnapshot(toolName);
515
+ if (toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX)) {
516
+ return fallback;
517
+ }
518
+ const describeHeader = this.getToolDescribeHeaderFn(toolName);
519
+ const describeArgs = this.getToolDescribeArgsFn(toolName);
520
+ if (!describeHeader && !describeArgs) {
521
+ return fallback;
522
+ }
523
+ try {
524
+ const validatedArgs = this.validateLocalToolArgs(toolName, args);
525
+ const context = this.buildToolExecutionContext({ sessionId, toolCallId });
526
+ const isPromiseLike = (value) => {
527
+ if (typeof value !== "object" || value === null) {
528
+ return false;
529
+ }
530
+ return typeof value.then === "function";
531
+ };
532
+ let nextSnapshot = fallback;
533
+ if (describeHeader) {
534
+ const header = describeHeader(validatedArgs, context);
535
+ if (!isPromiseLike(header) && header) {
536
+ nextSnapshot = {
537
+ ...nextSnapshot,
538
+ header: { ...nextSnapshot.header, ...header }
539
+ };
540
+ }
541
+ }
542
+ if (describeArgs) {
543
+ const argsPresentation = describeArgs(validatedArgs, context);
544
+ if (!isPromiseLike(argsPresentation) && argsPresentation) {
545
+ nextSnapshot = {
546
+ ...nextSnapshot,
547
+ args: argsPresentation
548
+ };
549
+ }
550
+ }
551
+ return nextSnapshot;
552
+ } catch (error) {
553
+ this.logger.debug(
554
+ `Tool presentation snapshot generation failed for '${toolName}': ${error instanceof Error ? error.message : String(error)}`
555
+ );
556
+ return fallback;
557
+ }
558
+ }
559
+ async getToolPresentationSnapshotForCall(toolName, args, toolCallId, sessionId) {
560
+ const fallback = this.buildGenericToolPresentationSnapshot(toolName);
561
+ if (toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX)) {
562
+ return fallback;
563
+ }
564
+ const describeHeader = this.getToolDescribeHeaderFn(toolName);
565
+ const describeArgs = this.getToolDescribeArgsFn(toolName);
566
+ if (!describeHeader && !describeArgs) {
567
+ return fallback;
568
+ }
569
+ try {
570
+ const context = this.buildToolExecutionContext({ sessionId, toolCallId });
571
+ const describedHeader = describeHeader ? await Promise.resolve(describeHeader(args, context)) : null;
572
+ const describedArgs = describeArgs ? await Promise.resolve(describeArgs(args, context)) : null;
573
+ return {
574
+ ...fallback,
575
+ ...describedHeader ? { header: { ...fallback.header, ...describedHeader } } : {},
576
+ ...describedArgs ? { args: describedArgs } : {}
577
+ };
578
+ } catch (error) {
579
+ this.logger.debug(
580
+ `Tool presentation snapshot generation failed for '${toolName}': ${error instanceof Error ? error.message : String(error)}`
581
+ );
582
+ return fallback;
583
+ }
584
+ }
585
+ async augmentSnapshotWithResult(toolName, snapshot, result, args, toolCallId, sessionId) {
586
+ if (toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX)) {
587
+ return snapshot;
588
+ }
589
+ const describeResult = this.getToolDescribeResultFn(toolName);
590
+ if (!describeResult) {
591
+ return snapshot;
592
+ }
593
+ try {
594
+ const context = this.buildToolExecutionContext({ sessionId, toolCallId });
595
+ const resultPresentation = await Promise.resolve(describeResult(result, args, context));
596
+ if (!resultPresentation) {
597
+ return snapshot;
598
+ }
599
+ return {
600
+ ...snapshot,
601
+ result: resultPresentation
602
+ };
603
+ } catch (error) {
604
+ this.logger.debug(
605
+ `Tool result presentation snapshot generation failed for '${toolName}': ${error instanceof Error ? error.message : String(error)}`
606
+ );
607
+ return snapshot;
608
+ }
609
+ }
450
610
  getToolPatternKey(toolName, args) {
451
611
  if (toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX)) {
452
612
  return null;
453
613
  }
454
- const tool = this.agentTools.get(toolName);
455
- if (!tool?.getApprovalPatternKey) {
614
+ const getPatternKey = this.getToolApprovalPatternKeyFn(toolName);
615
+ if (!getPatternKey) {
456
616
  return null;
457
617
  }
458
618
  try {
459
- return tool.getApprovalPatternKey(args);
619
+ return getPatternKey(args);
460
620
  } catch (error) {
461
621
  this.logger.debug(
462
622
  `Pattern key generation failed for '${toolName}': ${error instanceof Error ? error.message : String(error)}`
@@ -468,12 +628,12 @@ let _ToolManager = class _ToolManager {
468
628
  if (toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX)) {
469
629
  return void 0;
470
630
  }
471
- const tool = this.agentTools.get(toolName);
472
- if (!tool?.suggestApprovalPatterns) {
631
+ const suggestPatterns = this.getToolSuggestApprovalPatternsFn(toolName);
632
+ if (!suggestPatterns) {
473
633
  return void 0;
474
634
  }
475
635
  try {
476
- const patterns = tool.suggestApprovalPatterns(args);
636
+ const patterns = suggestPatterns(args);
477
637
  return patterns.length > 0 ? patterns : void 0;
478
638
  } catch (error) {
479
639
  this.logger.debug(
@@ -499,8 +659,7 @@ let _ToolManager = class _ToolManager {
499
659
  if (request.sessionId !== sessionId) {
500
660
  return false;
501
661
  }
502
- const metadata = request.metadata;
503
- return metadata.toolName === toolName;
662
+ return request.metadata.toolName === toolName;
504
663
  },
505
664
  { rememberChoice: false }
506
665
  // Don't propagate remember choice to auto-approved requests
@@ -519,8 +678,7 @@ let _ToolManager = class _ToolManager {
519
678
  if (toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX)) {
520
679
  return;
521
680
  }
522
- const tool = this.agentTools.get(toolName);
523
- const getPatternKey = tool?.getApprovalPatternKey;
681
+ const getPatternKey = this.getToolApprovalPatternKeyFn(toolName);
524
682
  if (!getPatternKey) {
525
683
  return;
526
684
  }
@@ -532,17 +690,17 @@ let _ToolManager = class _ToolManager {
532
690
  if (request.sessionId !== sessionId) {
533
691
  return false;
534
692
  }
535
- const metadata = request.metadata;
536
- if (metadata.toolName !== toolName) {
693
+ if (request.metadata.toolName !== toolName) {
537
694
  return false;
538
695
  }
539
- const args = metadata.args;
696
+ const args = request.metadata.args;
540
697
  if (typeof args !== "object" || args === null) {
541
698
  return false;
542
699
  }
700
+ const argsRecord = args;
543
701
  let patternKey;
544
702
  try {
545
- patternKey = getPatternKey(args);
703
+ patternKey = getPatternKey(argsRecord);
546
704
  } catch (error) {
547
705
  this.logger.debug(
548
706
  `Pattern key generation failed for '${toolName}': ${error instanceof Error ? error.message : String(error)}`
@@ -561,43 +719,38 @@ let _ToolManager = class _ToolManager {
561
719
  );
562
720
  }
563
721
  }
564
- /**
565
- * Auto-approve pending directory access requests that match a newly approved directory access request.
566
- *
567
- * This handles the case where parallel file operations request directory approval concurrently.
568
- */
569
- autoApprovePendingDirectoryAccessRequests(options) {
570
- const count = this.approvalManager.autoApprovePendingRequests(
571
- (request) => {
572
- if (request.type !== import_types3.ApprovalType.DIRECTORY_ACCESS) {
573
- return false;
574
- }
575
- if (request.sessionId !== options.sessionId) {
576
- return false;
577
- }
578
- const metadata = request.metadata;
579
- if (typeof metadata?.parentDir !== "string" || metadata.parentDir !== options.parentDir) {
580
- return false;
581
- }
582
- if (options.rememberDirectory) {
583
- return true;
584
- }
585
- if (typeof metadata?.operation !== "string" || typeof metadata?.toolName !== "string") {
586
- return false;
587
- }
588
- return metadata.operation === options.operation && metadata.toolName === options.toolName;
589
- },
590
- { rememberDirectory: false }
591
- );
592
- if (count > 0) {
593
- this.logger.info(
594
- `Auto-approved ${count} parallel request(s) for directory '${options.parentDir}' after directory access was approved`
595
- );
596
- }
597
- }
598
722
  getMcpManager() {
599
723
  return this.mcpManager;
600
724
  }
725
+ setContributorContextFactory(factory) {
726
+ this.contributorContextFactory = factory ?? void 0;
727
+ }
728
+ async buildContributorContext() {
729
+ const baseWorkspace = this.currentWorkspace ?? null;
730
+ const baseContext = {
731
+ mcpManager: this.mcpManager,
732
+ workspace: baseWorkspace
733
+ };
734
+ if (!this.contributorContextFactory) {
735
+ return baseContext;
736
+ }
737
+ try {
738
+ const overrides = await this.contributorContextFactory() ?? {};
739
+ const workspace = overrides.workspace !== void 0 ? overrides.workspace : baseWorkspace;
740
+ const environment = overrides.environment !== void 0 ? overrides.environment : baseContext.environment;
741
+ const mcpManager = overrides.mcpManager ?? baseContext.mcpManager;
742
+ return {
743
+ mcpManager,
744
+ workspace,
745
+ ...environment !== void 0 ? { environment } : {}
746
+ };
747
+ } catch (error) {
748
+ this.logger.warn(
749
+ `Failed to build contributor context: ${error instanceof Error ? error.message : String(error)}`
750
+ );
751
+ return baseContext;
752
+ }
753
+ }
601
754
  /**
602
755
  * Get all MCP tools (delegates to mcpManager.getAllTools())
603
756
  * This provides access to MCP tools while maintaining separation of concerns
@@ -617,7 +770,7 @@ let _ToolManager = class _ToolManager {
617
770
  };
618
771
  return this.toolExecutionContextFactory(baseContext);
619
772
  }
620
- validateLocalToolArgsOrThrow(toolName, args) {
773
+ validateLocalToolArgs(toolName, args) {
621
774
  if (toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX)) {
622
775
  return args;
623
776
  }
@@ -738,15 +891,22 @@ let _ToolManager = class _ToolManager {
738
891
  async executeTool(toolName, args, toolCallId, sessionId, abortSignal) {
739
892
  const { toolArgs: rawToolArgs, meta } = (0, import_tool_call_metadata.extractToolCallMeta)(args);
740
893
  let toolArgs = rawToolArgs;
894
+ const callDescription = typeof meta.callDescription === "string" ? meta.callDescription : typeof rawToolArgs.description === "string" ? rawToolArgs.description : void 0;
741
895
  const backgroundTasksEnabled = (0, import_env.isBackgroundTasksEnabled)();
742
- const toolDisplayName = this.agentTools.get(toolName)?.displayName;
743
896
  this.logger.debug(`\u{1F527} Tool execution requested: '${toolName}' (toolCallId: ${toolCallId})`);
744
897
  this.logger.debug(`Tool args: ${JSON.stringify(toolArgs, null, 2)}`);
745
898
  if (sessionId) {
899
+ const presentationSnapshot = this.getToolPresentationSnapshotForToolCallEvent(
900
+ toolName,
901
+ toolArgs,
902
+ toolCallId,
903
+ sessionId
904
+ );
746
905
  this.agentEventBus.emit("llm:tool-call", {
747
906
  toolName,
748
- ...toolDisplayName !== void 0 && { toolDisplayName },
907
+ presentationSnapshot,
749
908
  args: toolArgs,
909
+ ...callDescription !== void 0 && { callDescription },
750
910
  callId: toolCallId,
751
911
  sessionId
752
912
  });
@@ -754,13 +914,14 @@ let _ToolManager = class _ToolManager {
754
914
  const {
755
915
  requireApproval,
756
916
  approvalStatus,
757
- args: validatedToolArgs
917
+ args: validatedToolArgs,
918
+ presentationSnapshot: callSnapshot
758
919
  } = await this.handleToolApproval(
759
920
  toolName,
760
921
  toolArgs,
761
922
  toolCallId,
762
923
  sessionId,
763
- meta.callDescription
924
+ callDescription
764
925
  );
765
926
  toolArgs = validatedToolArgs;
766
927
  this.logger.debug(`\u2705 Tool execution approved: ${toolName}`);
@@ -794,7 +955,7 @@ let _ToolManager = class _ToolManager {
794
955
  );
795
956
  toolArgs = modifiedPayload.args;
796
957
  try {
797
- toolArgs = this.validateLocalToolArgsOrThrow(toolName, toolArgs);
958
+ toolArgs = this.validateLocalToolArgs(toolName, toolArgs);
798
959
  } catch (error) {
799
960
  this.logger.error(
800
961
  `Post-hook validation failed for tool '${toolName}': a beforeToolCall hook may have set invalid args`
@@ -917,9 +1078,17 @@ let _ToolManager = class _ToolManager {
917
1078
  );
918
1079
  result = modifiedPayload.result;
919
1080
  }
1081
+ const presentationSnapshot = await this.augmentSnapshotWithResult(
1082
+ toolName,
1083
+ callSnapshot,
1084
+ result,
1085
+ toolArgs,
1086
+ toolCallId,
1087
+ sessionId
1088
+ );
920
1089
  return {
921
1090
  result,
922
- ...toolDisplayName !== void 0 && { toolDisplayName },
1091
+ ...presentationSnapshot !== void 0 && { presentationSnapshot },
923
1092
  ...requireApproval && { requireApproval, approvalStatus }
924
1093
  };
925
1094
  } catch (error) {
@@ -1044,124 +1213,113 @@ let _ToolManager = class _ToolManager {
1044
1213
  );
1045
1214
  }
1046
1215
  /**
1047
- * Check if a tool has a custom approval override and handle it.
1048
- * Tools can implement getApprovalOverride() to request specialized approval flows
1049
- * (e.g., directory access approval for file tools) instead of default tool confirmation.
1050
- *
1051
- * @param toolName Tool name
1052
- * @param args The tool arguments
1053
- * @param sessionId Optional session ID
1054
- * @returns { handled: true } if custom approval was processed, { handled: false } to continue normal flow
1216
+ * Handle tool approval flow. Checks various precedence levels to determine
1217
+ * if a tool should be auto-approved, denied, or requires manual approval.
1055
1218
  */
1056
- async checkCustomApprovalOverride(toolName, args, toolCallId, sessionId) {
1057
- if (toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX)) {
1058
- return { handled: false };
1059
- }
1060
- const tool = this.agentTools.get(toolName);
1061
- if (!tool?.getApprovalOverride) {
1062
- return { handled: false };
1063
- }
1064
- const context = this.buildToolExecutionContext({ sessionId, toolCallId });
1065
- const approvalRequest = await tool.getApprovalOverride(args, context);
1066
- if (!approvalRequest) {
1067
- return { handled: false };
1068
- }
1069
- this.logger.debug(
1070
- `Tool '${toolName}' requested custom approval: type=${approvalRequest.type}`
1071
- );
1072
- if (sessionId && !approvalRequest.sessionId) {
1073
- approvalRequest.sessionId = sessionId;
1074
- }
1075
- const response = await this.approvalManager.requestApproval(approvalRequest);
1076
- if (response.status === import_types3.ApprovalStatus.APPROVED) {
1077
- if (tool.onApprovalGranted) {
1078
- tool.onApprovalGranted(response, context, approvalRequest);
1079
- }
1080
- if (approvalRequest.type === import_types3.ApprovalType.DIRECTORY_ACCESS) {
1081
- const metadata = approvalRequest.metadata;
1082
- const parentDir = typeof metadata?.parentDir === "string" ? metadata.parentDir : null;
1083
- const operation = typeof metadata?.operation === "string" ? metadata.operation : null;
1084
- if (parentDir && operation) {
1085
- const data = response.data;
1086
- const rememberDirectory = data?.rememberDirectory ?? false;
1087
- this.autoApprovePendingDirectoryAccessRequests({
1088
- parentDir,
1089
- operation,
1090
- sessionId,
1091
- toolName,
1092
- rememberDirectory
1093
- });
1094
- }
1095
- }
1219
+ async handleToolApproval(toolName, args, toolCallId, sessionId, callDescription) {
1220
+ if (this.isInAlwaysDenyList(toolName)) {
1096
1221
  this.logger.info(
1097
- `Custom approval granted for '${toolName}', type=${approvalRequest.type}, session=${sessionId ?? "global"}`
1222
+ `Tool '${toolName}' is in static deny list \u2013 blocking execution (session: ${sessionId ?? "global"})`
1098
1223
  );
1099
- return { handled: true };
1224
+ throw import_errors.ToolError.executionDenied(toolName, sessionId);
1100
1225
  }
1101
- this.logger.info(
1102
- `Custom approval denied for '${toolName}', type=${approvalRequest.type}, reason=${response.reason ?? "unknown"}`
1226
+ const validatedArgs = this.validateLocalToolArgs(toolName, args);
1227
+ const presentationSnapshot = await this.getToolPresentationSnapshotForCall(
1228
+ toolName,
1229
+ validatedArgs,
1230
+ toolCallId,
1231
+ sessionId
1103
1232
  );
1104
- if (approvalRequest.type === "directory_access") {
1105
- const metadata = approvalRequest.metadata;
1106
- throw import_errors.ToolError.directoryAccessDenied(
1107
- metadata?.parentDir ?? "unknown directory",
1108
- sessionId
1109
- );
1233
+ let directoryAccess;
1234
+ let directoryAccessApprovalRequest;
1235
+ if (!toolName.startsWith(_ToolManager.MCP_TOOL_PREFIX)) {
1236
+ const getApprovalOverride = this.getToolApprovalOverrideFn(toolName);
1237
+ if (getApprovalOverride) {
1238
+ const context = this.buildToolExecutionContext({ sessionId, toolCallId });
1239
+ const approvalRequest = await getApprovalOverride(validatedArgs, context);
1240
+ if (approvalRequest) {
1241
+ if (approvalRequest.type === import_types3.ApprovalType.DIRECTORY_ACCESS) {
1242
+ const metadata = approvalRequest.metadata;
1243
+ if (typeof metadata !== "object" || metadata === null || typeof metadata.path !== "string" || typeof metadata.parentDir !== "string" || typeof metadata.operation !== "string" || typeof metadata.toolName !== "string") {
1244
+ throw import_errors.ToolError.configInvalid(
1245
+ `Tool '${toolName}' returned invalid directory access metadata`
1246
+ );
1247
+ }
1248
+ directoryAccess = metadata;
1249
+ directoryAccessApprovalRequest = approvalRequest;
1250
+ } else {
1251
+ this.logger.debug(
1252
+ `Tool '${toolName}' requested custom approval: type=${approvalRequest.type}`
1253
+ );
1254
+ if (sessionId && !approvalRequest.sessionId) {
1255
+ approvalRequest.sessionId = sessionId;
1256
+ }
1257
+ const response = await this.approvalManager.requestApproval(approvalRequest);
1258
+ if (response.status === import_types3.ApprovalStatus.APPROVED) {
1259
+ const onGranted = this.getToolApprovalOnGrantedFn(toolName);
1260
+ if (onGranted) {
1261
+ await Promise.resolve(
1262
+ onGranted(response, context, approvalRequest)
1263
+ );
1264
+ }
1265
+ this.logger.info(
1266
+ `Custom approval granted for '${toolName}', type=${approvalRequest.type}, session=${sessionId ?? "global"}`
1267
+ );
1268
+ return {
1269
+ requireApproval: true,
1270
+ approvalStatus: "approved",
1271
+ args: validatedArgs,
1272
+ presentationSnapshot
1273
+ };
1274
+ }
1275
+ this.logger.info(
1276
+ `Custom approval denied for '${toolName}', type=${approvalRequest.type}, reason=${response.reason ?? "unknown"}`
1277
+ );
1278
+ throw import_errors.ToolError.executionDenied(toolName, sessionId);
1279
+ }
1280
+ }
1281
+ }
1110
1282
  }
1111
- throw import_errors.ToolError.executionDenied(toolName, sessionId);
1112
- }
1113
- /**
1114
- * Handle tool approval flow. Checks various precedence levels to determine
1115
- * if a tool should be auto-approved, denied, or requires manual approval.
1116
- */
1117
- async handleToolApproval(toolName, args, toolCallId, sessionId, callDescription) {
1118
- const validatedArgs = this.validateLocalToolArgsOrThrow(toolName, args);
1119
1283
  const quickResult = await this.tryQuickApprovalResolution(
1120
1284
  toolName,
1121
1285
  validatedArgs,
1122
- toolCallId,
1123
- sessionId
1286
+ sessionId,
1287
+ directoryAccess
1124
1288
  );
1125
1289
  if (quickResult !== null) {
1126
- return { ...quickResult, args: validatedArgs };
1290
+ return { ...quickResult, args: validatedArgs, presentationSnapshot };
1127
1291
  }
1128
1292
  const manualResult = await this.requestManualApproval(
1129
1293
  toolName,
1130
1294
  validatedArgs,
1131
1295
  toolCallId,
1132
1296
  sessionId,
1133
- callDescription
1297
+ directoryAccess,
1298
+ directoryAccessApprovalRequest,
1299
+ callDescription,
1300
+ presentationSnapshot
1134
1301
  );
1135
- return { ...manualResult, args: validatedArgs };
1302
+ return { ...manualResult, args: validatedArgs, presentationSnapshot };
1136
1303
  }
1137
1304
  /**
1138
1305
  * Try to resolve tool approval quickly based on policies and cached permissions.
1139
1306
  * Returns null if manual approval is needed.
1140
1307
  *
1141
1308
  * Precedence order (highest to lowest):
1142
- * 1. Static deny list (security - always blocks)
1143
- * 2. Custom approval override (tool-specific approval flows)
1144
- * 3. Session auto-approve (skill allowed-tools)
1145
- * 4. Static allow list
1146
- * 5. Dynamic "remembered" allowed list
1147
- * 6. Tool approval patterns
1148
- * 7. Approval mode (auto-approve/auto-deny)
1309
+ * 1. Directory access requirement (outside-root paths)
1310
+ * 2. Session auto-approve (skill allowed-tools)
1311
+ * 3. Static allow list
1312
+ * 4. Dynamic "remembered" allowed list
1313
+ * 5. Tool approval patterns
1314
+ * 6. Approval mode (auto-approve/auto-deny)
1149
1315
  */
1150
- async tryQuickApprovalResolution(toolName, args, toolCallId, sessionId) {
1151
- if (this.isInAlwaysDenyList(toolName)) {
1152
- this.logger.info(
1153
- `Tool '${toolName}' is in static deny list \u2013 blocking execution (session: ${sessionId ?? "global"})`
1154
- );
1155
- throw import_errors.ToolError.executionDenied(toolName, sessionId);
1156
- }
1157
- const customApprovalResult = await this.checkCustomApprovalOverride(
1158
- toolName,
1159
- args,
1160
- toolCallId,
1161
- sessionId
1162
- );
1163
- if (customApprovalResult.handled) {
1164
- return { requireApproval: true, approvalStatus: "approved" };
1316
+ async tryQuickApprovalResolution(toolName, args, sessionId, directoryAccess) {
1317
+ if (directoryAccess) {
1318
+ if (this.approvalMode === "auto-approve") {
1319
+ this.approvalManager.addApprovedDirectory(directoryAccess.parentDir, "once");
1320
+ return { requireApproval: false };
1321
+ }
1322
+ return null;
1165
1323
  }
1166
1324
  if (sessionId && this.isToolAutoApprovedForSession(sessionId, toolName)) {
1167
1325
  this.logger.info(
@@ -1202,12 +1360,11 @@ let _ToolManager = class _ToolManager {
1202
1360
  * Request manual approval from the user for a tool execution.
1203
1361
  * Generates preview, sends approval request, and handles the response.
1204
1362
  */
1205
- async requestManualApproval(toolName, args, toolCallId, sessionId, callDescription) {
1363
+ async requestManualApproval(toolName, args, toolCallId, sessionId, directoryAccess, directoryAccessApprovalRequest, callDescription, presentationSnapshot) {
1206
1364
  this.logger.info(
1207
1365
  `Tool approval requested for ${toolName}, sessionId: ${sessionId ?? "global"}`
1208
1366
  );
1209
1367
  try {
1210
- const toolDisplayName = this.agentTools.get(toolName)?.displayName;
1211
1368
  const displayPreview = await this.generateToolPreview(
1212
1369
  toolName,
1213
1370
  args,
@@ -1217,14 +1374,24 @@ let _ToolManager = class _ToolManager {
1217
1374
  const suggestedPatterns = this.getToolSuggestedPatterns(toolName, args);
1218
1375
  const response = await this.approvalManager.requestToolApproval({
1219
1376
  toolName,
1220
- ...toolDisplayName !== void 0 && { toolDisplayName },
1377
+ ...presentationSnapshot !== void 0 && { presentationSnapshot },
1221
1378
  toolCallId,
1222
1379
  args,
1223
1380
  ...callDescription !== void 0 && { description: callDescription },
1224
1381
  ...sessionId !== void 0 && { sessionId },
1225
1382
  ...displayPreview !== void 0 && { displayPreview },
1383
+ ...directoryAccess !== void 0 && { directoryAccess },
1226
1384
  ...suggestedPatterns !== void 0 && { suggestedPatterns }
1227
1385
  });
1386
+ if (response.status === import_types3.ApprovalStatus.APPROVED && directoryAccessApprovalRequest !== void 0) {
1387
+ const onGranted = this.getToolApprovalOnGrantedFn(toolName);
1388
+ if (onGranted) {
1389
+ const context = this.buildToolExecutionContext({ sessionId, toolCallId });
1390
+ await Promise.resolve(
1391
+ onGranted(response, context, directoryAccessApprovalRequest)
1392
+ );
1393
+ }
1394
+ }
1228
1395
  if (response.status === import_types3.ApprovalStatus.APPROVED && response.data) {
1229
1396
  await this.handleRememberChoice(toolName, response, sessionId);
1230
1397
  }
@@ -1246,13 +1413,13 @@ let _ToolManager = class _ToolManager {
1246
1413
  * Generate a preview for the tool approval UI if the tool supports it.
1247
1414
  */
1248
1415
  async generateToolPreview(toolName, args, toolCallId, sessionId) {
1249
- const tool = this.agentTools.get(toolName);
1250
- if (!tool?.generatePreview) {
1416
+ const previewFn = this.getToolPreviewFn(toolName);
1417
+ if (!previewFn) {
1251
1418
  return void 0;
1252
1419
  }
1253
1420
  try {
1254
1421
  const context = this.buildToolExecutionContext({ sessionId, toolCallId });
1255
- const preview = await tool.generatePreview(args, context);
1422
+ const preview = await previewFn(args, context);
1256
1423
  this.logger.debug(`Generated preview for ${toolName}`);
1257
1424
  return preview ?? void 0;
1258
1425
  } catch (previewError) {
@@ -1281,7 +1448,7 @@ let _ToolManager = class _ToolManager {
1281
1448
  `Tool '${toolName}' added to allowed tools for session '${allowSessionId ?? "global"}' (remember choice selected)`
1282
1449
  );
1283
1450
  this.autoApprovePendingToolRequests(toolName, allowSessionId);
1284
- } else if (rememberPattern && this.agentTools.get(toolName)?.getApprovalPatternKey) {
1451
+ } else if (rememberPattern && this.getToolApprovalPatternKeyFn(toolName)) {
1285
1452
  this.approvalManager.addPattern(toolName, rememberPattern);
1286
1453
  this.logger.info(`Pattern '${rememberPattern}' added for tool '${toolName}' approval`);
1287
1454
  this.autoApprovePendingPatternRequests(toolName, sessionId);