@zuora/agent-toolkit 0.0.1-alpha.0 → 0.1.0-alpha.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.
@@ -1,7 +1,7 @@
1
1
  import { tool } from '@openai/agents';
2
2
  import { ToolkitCore } from '../shared/toolkit-core.js';
3
- import { Z as ZuoraToolDefinition, a as ZuoraAgentToolkitConfigInput, T as ToolSelection } from '../async-jobs-Dd8Jafv0.js';
4
- import { Z as ZuoraOpenAIAgentToolkit } from '../toolkit-CafRZuAV.js';
3
+ import { b as ZuoraToolExecutionResult, Z as ZuoraToolDefinition, a as ZuoraAgentToolkitConfigInput, T as ToolSelection } from '../async-jobs-DJ_GygII.js';
4
+ import { Z as ZuoraOpenAIAgentToolkit } from '../toolkit-Bjw46goJ.js';
5
5
  import 'openai/resources/chat/completions';
6
6
 
7
7
  /** Native tool type returned by `tool()` from `@openai/agents`. */
@@ -22,6 +22,35 @@ type AgentsFunctionTool = ReturnType<typeof tool>;
22
22
  * automatically inside `convertTools()` — callers do not need to know about it.
23
23
  */
24
24
  declare class ZuoraOpenAIAgentsToolkit extends ToolkitCore<AgentsFunctionTool> {
25
+ /** Maps sanitized adapter tool names back to their original ZuoraToolDefinition. */
26
+ private sanitizedToolMap;
27
+ /**
28
+ * Look up a ZuoraToolDefinition by its sanitized adapter tool name.
29
+ *
30
+ * The OpenAI Agents SDK registers tools with sanitized names (see convertTools),
31
+ * but external code may reference tools by their original MCP names. This method
32
+ * provides resolution in both directions.
33
+ */
34
+ private resolveToolName;
35
+ /**
36
+ * Resolve a tool name that may be either sanitized or original MCP format.
37
+ *
38
+ * This provides additional name normalization to handle cases where:
39
+ * - A tool name contains unsanitized characters (like dots)
40
+ * - A tool name was learned from descriptions or logs
41
+ *
42
+ * We attempt to find a match with relaxed sanitization rules before giving up.
43
+ */
44
+ private normalizeToolName;
45
+ /**
46
+ * Override callTool to provide flexible name resolution.
47
+ *
48
+ * The OpenAI Agents SDK may receive tool calls with names that were learned
49
+ * from descriptions or other sources (not directly from tool registration).
50
+ * This override allows us to handle both sanitized and original tool names
51
+ * by falling back to name normalization when the direct lookup fails.
52
+ */
53
+ callTool(operationName: string, input: Record<string, unknown>): Promise<ZuoraToolExecutionResult>;
25
54
  /**
26
55
  * Serialises the agent tool response, emits an `agent.tool.response`
27
56
  * observability event (with the response in `metadata` when
@@ -42,7 +71,7 @@ declare class ZuoraOpenAIAgentsToolkit extends ToolkitCore<AgentsFunctionTool> {
42
71
  * import { createZuoraOpenAIAgentsToolkit } from "@zuora/agent-toolkit/openai-agents";
43
72
  *
44
73
  * const toolkit = await createZuoraOpenAIAgentsToolkit({ env: true });
45
- * const tools = await toolkit.getTools({ bundles: ["query"], mode: "readOnly" });
74
+ * const tools = await toolkit.getTools({ bundles: ["query"], mode: SafetyMode.ReadOnly });
46
75
  * const agent = new Agent({ name: "Cli Agent", tools });
47
76
  * const result = await run(agent, "How many active accounts?");
48
77
  * ```
@@ -22,6 +22,10 @@ var ZuoraAgentToolkitError = class extends Error {
22
22
  };
23
23
 
24
24
  // src/shared/types.ts
25
+ var SafetyMode = {
26
+ ReadOnly: "readOnly",
27
+ WriteEnabled: "writeEnabled"
28
+ };
25
29
  var ApprovalPolicyPreset = {
26
30
  Off: "off",
27
31
  AllWrites: "all_writes",
@@ -117,15 +121,15 @@ var evaluatePreset = (preset, request) => {
117
121
  const isProduction = environment === "production";
118
122
  switch (preset) {
119
123
  case ApprovalPolicyPreset.Off:
120
- return "not_required";
124
+ return "allow";
121
125
  case ApprovalPolicyPreset.AllWrites:
122
- return isWrite ? "deny" : "not_required";
126
+ return isWrite ? "deny" : "allow";
123
127
  case ApprovalPolicyPreset.HighRisk:
124
- return isHighRisk ? "deny" : "not_required";
128
+ return isHighRisk ? "deny" : "allow";
125
129
  case ApprovalPolicyPreset.ProductionWrites:
126
- return isProduction && isWrite ? "deny" : "not_required";
130
+ return isProduction && isWrite ? "deny" : "allow";
127
131
  case ApprovalPolicyPreset.ProductionHighRisk:
128
- return isProduction && isHighRisk ? "deny" : "not_required";
132
+ return isProduction && isHighRisk ? "deny" : "allow";
129
133
  }
130
134
  };
131
135
  var throwApprovalRejected = (tool2) => {
@@ -2600,26 +2604,11 @@ var readEnvAllowedHosts = () => {
2600
2604
  const hosts = raw.split(",").map((host) => host.trim()).filter(Boolean);
2601
2605
  return hosts.length > 0 ? hosts : void 0;
2602
2606
  };
2603
- var isQueryBundleConfigured = (configuration) => {
2604
- const bundles = configuration?.bundles;
2605
- if (!bundles) {
2606
- return false;
2607
- }
2608
- const query = bundles.query;
2609
- if (query === true) {
2610
- return true;
2611
- }
2612
- return typeof query === "object" && query !== null;
2613
- };
2614
2607
  var resolveNativeFileToolsInput = (config) => {
2615
2608
  const explicit = config.nativeFileTools;
2616
2609
  if (explicit?.enabled === false) {
2617
2610
  return { enabled: false };
2618
2611
  }
2619
- const autoEnable = isQueryBundleConfigured(config.configuration);
2620
- if (!autoEnable && explicit?.enabled !== true) {
2621
- return explicit;
2622
- }
2623
2612
  const rootDir = nonEmptyString(explicit?.rootDir) ?? nonEmptyString(process.env["ZUORA_NATIVE_FILE_ROOT"]) ?? DEFAULT_NATIVE_ARTIFACT_ROOT;
2624
2613
  const allowedHosts = explicit?.allowedHosts ?? readEnvAllowedHosts() ?? DEFAULT_NATIVE_ALLOWED_HOSTS;
2625
2614
  return {
@@ -3490,21 +3479,10 @@ var OAuthTokenProvider = class {
3490
3479
  category: "auth"
3491
3480
  });
3492
3481
  }
3493
- if (this.config.auth.type === "accessToken" && this.config.auth.accessToken) {
3494
- this.accessToken = this.config.auth.accessToken;
3495
- return this.config.auth.accessToken;
3496
- }
3497
3482
  const now = Date.now();
3498
3483
  if (this.accessToken && this.isAccessTokenFresh(now)) {
3499
3484
  return this.accessToken;
3500
3485
  }
3501
- if (this.config.auth.type !== "oauthClientCredentials") {
3502
- throw new ZuoraAgentToolkitError({
3503
- code: "UNSUPPORTED_AUTH",
3504
- message: "Unsupported auth configuration for Zuora HTTP MCP client.",
3505
- category: "auth"
3506
- });
3507
- }
3508
3486
  const token = await this.fetchOAuthToken();
3509
3487
  this.accessToken = token.access_token;
3510
3488
  this.tokenIssuedAt = now;
@@ -3523,10 +3501,8 @@ var OAuthTokenProvider = class {
3523
3501
  sensitiveValues() {
3524
3502
  const values = [this.accessToken];
3525
3503
  const auth = this.config.auth;
3526
- if (auth?.type === "oauthClientCredentials") {
3504
+ if (auth?.clientSecret) {
3527
3505
  values.push(auth.clientSecret);
3528
- } else if (auth?.type === "accessToken") {
3529
- values.push(auth.accessToken);
3530
3506
  }
3531
3507
  return values.filter(
3532
3508
  (value) => typeof value === "string" && value.length > 0
@@ -3590,11 +3566,8 @@ var OAuthTokenProvider = class {
3590
3566
  });
3591
3567
  }
3592
3568
  async resolveTokenEndpoints() {
3593
- if (this.config.tokenEndpoint) {
3594
- return [this.config.tokenEndpoint];
3595
- }
3596
3569
  const restEndpoint = `${this.restBaseUrl}/oauth/token`;
3597
- const oneIdEndpoint = await this.resolveOneIdTokenEndpoint();
3570
+ const oneIdEndpoint = isStagingZuoraBaseUrl(this.restBaseUrl) ? "https://one.stg.na.zuora.com/oauth2/token" : "https://one.zuora.com/oauth2/token";
3598
3571
  const endpoints = [restEndpoint, oneIdEndpoint];
3599
3572
  const uniqueEndpoints = [...new Set(endpoints)];
3600
3573
  if (!this.preferredTokenEndpoint) {
@@ -3607,29 +3580,8 @@ var OAuthTokenProvider = class {
3607
3580
  )
3608
3581
  ];
3609
3582
  }
3610
- async resolveOneIdTokenEndpoint() {
3611
- const fallback = isStagingZuoraBaseUrl(this.restBaseUrl) ? "https://one.stg.na.zuora.com/oauth2/token" : "https://one.zuora.com/oauth2/token";
3612
- try {
3613
- return await this.discoverTokenEndpoint() ?? fallback;
3614
- } catch {
3615
- return fallback;
3616
- }
3617
- }
3618
- async discoverTokenEndpoint() {
3619
- const metadataUrl = new URL(this.restBaseUrl);
3620
- metadataUrl.pathname = "/.well-known/oauth-authorization-server";
3621
- metadataUrl.search = "";
3622
- const response = await fetch(metadataUrl, {
3623
- headers: { accept: "application/json" }
3624
- });
3625
- if (!response.ok) {
3626
- return void 0;
3627
- }
3628
- const metadata = await response.json().catch(() => ({}));
3629
- return metadata.token_endpoint;
3630
- }
3631
3583
  async requestOAuthToken(tokenEndpoint) {
3632
- if (!this.config.auth || this.config.auth.type !== "oauthClientCredentials") {
3584
+ if (!this.config.auth) {
3633
3585
  throw new ZuoraAgentToolkitError({
3634
3586
  code: "UNSUPPORTED_AUTH",
3635
3587
  message: "Unsupported auth configuration for Zuora HTTP MCP client.",
@@ -3642,17 +3594,8 @@ var OAuthTokenProvider = class {
3642
3594
  accept: "application/json"
3643
3595
  };
3644
3596
  const auth = this.config.auth;
3645
- if (this.config.tokenAuthMethod === "client_secret_basic") {
3646
- headers.authorization = `Basic ${base64Encode(
3647
- `${auth.clientId}:${auth.clientSecret}`
3648
- )}`;
3649
- } else {
3650
- body.set("client_id", auth.clientId);
3651
- body.set("client_secret", auth.clientSecret);
3652
- }
3653
- if (auth.scope) {
3654
- body.set("scope", auth.scope);
3655
- }
3597
+ body.set("client_id", auth.clientId);
3598
+ body.set("client_secret", auth.clientSecret);
3656
3599
  const response = await fetch(tokenEndpoint, {
3657
3600
  method: "POST",
3658
3601
  headers,
@@ -3694,7 +3637,6 @@ var extractTenantId = (scope) => {
3694
3637
  const tenantMatch = scope?.match(/tenant\.(\d+)/);
3695
3638
  return tenantMatch?.[1];
3696
3639
  };
3697
- var base64Encode = (value) => Buffer.from(value).toString("base64");
3698
3640
 
3699
3641
  // src/shared/transport/request-headers.ts
3700
3642
  var RESERVED_HEADER_NAMES = /* @__PURE__ */ new Set([
@@ -3766,10 +3708,8 @@ var resolveZuoraConnectionConfig = (config, mcpUrl) => ({
3766
3708
  clientName: config.clientName ?? "zuora-agent-toolkit",
3767
3709
  clientVersion: config.clientVersion ?? "0.1.0",
3768
3710
  protocolVersion: config.protocolVersion ?? "2025-03-26",
3769
- tokenAuthMethod: config.tokenAuthMethod ?? "client_secret_post",
3770
3711
  url: mcpUrl,
3771
3712
  ...config.auth ? { auth: config.auth } : {},
3772
- ...config.tokenEndpoint ? { tokenEndpoint: config.tokenEndpoint } : {},
3773
3713
  ...config.headers ? { headers: config.headers } : {},
3774
3714
  ...config.tenantAuth ? { tenantAuth: config.tenantAuth } : {},
3775
3715
  ...config.entityIds ? { entityIds: config.entityIds } : {},
@@ -3950,7 +3890,7 @@ var resolveToolSelectionPolicy = (configuration, selection) => ({
3950
3890
  ...selection?.exclude ?? []
3951
3891
  ])
3952
3892
  });
3953
- var resolveToolMode = (configuration, selection) => selection?.mode ?? configuration?.mode ?? "readOnly";
3893
+ var resolveToolMode = (configuration, selection) => selection?.mode ?? configuration?.mode ?? SafetyMode.ReadOnly;
3954
3894
  var resolveExcludedToolNames = (configuration) => /* @__PURE__ */ new Set([
3955
3895
  ...resolveConfiguredExcludedToolNames(configuration),
3956
3896
  ...resolveBundleExcludedToolNames(configuration)
@@ -4212,11 +4152,19 @@ var ZuoraHttpMcpClient = class _ZuoraHttpMcpClient extends ZuoraClient {
4212
4152
 
4213
4153
  // src/shared/toolkit-core.ts
4214
4154
  var createHttpMcpClientConfig = (config) => {
4215
- if (isTenantAuthConnection(config.connection)) {
4155
+ const contextEntityIds = config.configuration?.context?.entityIds;
4156
+ const contextOrgIds = config.configuration?.context?.orgIds;
4157
+ const contextOverrides = {
4158
+ ...contextEntityIds ? { entityIds: contextEntityIds } : {},
4159
+ ...contextOrgIds ? { orgIds: contextOrgIds } : {}
4160
+ };
4161
+ const connection = "connection" in config ? config.connection : void 0;
4162
+ if (isTenantAuthConnection(connection)) {
4216
4163
  return {
4217
4164
  url: config.baseUrl,
4218
4165
  ...config.auth ? { auth: config.auth } : {},
4219
- ...config.connection
4166
+ ...connection,
4167
+ ...contextOverrides
4220
4168
  };
4221
4169
  }
4222
4170
  if (!config.auth) {
@@ -4229,13 +4177,23 @@ var createHttpMcpClientConfig = (config) => {
4229
4177
  return {
4230
4178
  url: config.baseUrl,
4231
4179
  auth: config.auth,
4232
- ...config.connection
4180
+ ...connection ?? {},
4181
+ ...contextOverrides
4233
4182
  };
4234
4183
  };
4235
4184
  var isTenantAuthConnection = (connection) => connection?.tenantAuth?.enabled === true;
4236
4185
  var filterCatalogTools = (tools, policy) => tools.filter((tool2) => {
4237
- if (policy.mode === "readOnly" && tool2.meta.access === "write") {
4238
- return false;
4186
+ if (policy.mode === SafetyMode.ReadOnly) {
4187
+ if (tool2.meta.operationAccess) {
4188
+ const hasReadOperation = Object.values(tool2.meta.operationAccess).some(
4189
+ (access) => access === "read"
4190
+ );
4191
+ if (!hasReadOperation) {
4192
+ return false;
4193
+ }
4194
+ } else if (tool2.meta.access === "write") {
4195
+ return false;
4196
+ }
4239
4197
  }
4240
4198
  if (policy.bundles.size > 0 && !policy.bundles.has(tool2.meta.bundle)) {
4241
4199
  return false;
@@ -4396,19 +4354,19 @@ var ToolkitCore = class {
4396
4354
  category: "configuration"
4397
4355
  });
4398
4356
  }
4399
- const mode = this.config.configuration?.mode ?? "readOnly";
4400
- if (mode === "readOnly" && tool2.meta.access === "write") {
4401
- throw new ZuoraAgentToolkitError({
4402
- code: "TOOL_EXECUTION_ERROR",
4403
- message: `Write tool "${operationName}" is disabled in readOnly mode.`,
4404
- category: "tool_execution"
4405
- });
4406
- }
4357
+ const mode = this.config.configuration?.mode ?? SafetyMode.ReadOnly;
4407
4358
  const nativeRuntimeTool = this.nativeToolRegistry?.get(operationName);
4408
4359
  const nativeApprovalMetadata = nativeRuntimeTool?.approvalMetadata(input);
4409
4360
  const invokedOperation = tool2.meta.operationSelector ? String(input[tool2.meta.operationSelector] ?? "") : void 0;
4410
4361
  const effectiveAccess = (invokedOperation ? tool2.meta.operationAccess?.[invokedOperation] : void 0) ?? tool2.meta.access;
4411
4362
  const effectiveRiskLevel = (invokedOperation ? tool2.meta.operationRiskLevel?.[invokedOperation] : void 0) ?? tool2.meta.riskLevel;
4363
+ if (mode === SafetyMode.ReadOnly && effectiveAccess === "write") {
4364
+ throw new ZuoraAgentToolkitError({
4365
+ code: "TOOL_EXECUTION_ERROR",
4366
+ message: `Write operation "${invokedOperation ?? operationName}" is disabled in readOnly mode.`,
4367
+ category: "tool_execution"
4368
+ });
4369
+ }
4412
4370
  const effectiveTool = effectiveAccess === tool2.meta.access && effectiveRiskLevel === tool2.meta.riskLevel ? tool2 : { ...tool2, meta: { ...tool2.meta, access: effectiveAccess, riskLevel: effectiveRiskLevel } };
4413
4371
  await enforceApproval(
4414
4372
  effectiveTool,
@@ -4642,7 +4600,7 @@ var ToolkitCore = class {
4642
4600
  return this.catalogTools ?? [];
4643
4601
  }
4644
4602
  resolveSplitOperations() {
4645
- return this.config.configuration?.splitOperations ?? true;
4603
+ return this.config.configuration?.splitOperations ?? false;
4646
4604
  }
4647
4605
  warnIfNotInitialized() {
4648
4606
  if (!this._initialized) {
@@ -4711,13 +4669,11 @@ var ToolkitCore = class {
4711
4669
  }
4712
4670
  createEventContext() {
4713
4671
  const configurationContext = this.config.configuration?.context ?? {};
4714
- const tenantId = nonEmptyString(configurationContext.tenantId) ?? nonEmptyString(configurationContext.tenant_id);
4715
4672
  const entityIds = normalizeIdList(configurationContext.entityIds) ?? normalizeIdList(configurationContext.entityId);
4716
4673
  const orgIds = normalizeIdList(configurationContext.orgIds) ?? normalizeIdList(configurationContext.orgId);
4717
4674
  const environment = inferZuoraEnvironment(this.config.baseUrl) ?? "sandbox";
4718
4675
  return {
4719
4676
  environment,
4720
- ...tenantId ? { tenantId } : {},
4721
4677
  ...entityIds ? { entityIds } : {},
4722
4678
  ...orgIds ? { orgIds } : {}
4723
4679
  };
@@ -4809,51 +4765,21 @@ var readBooleanEnvValue = (name, rawValue) => {
4809
4765
  var readBooleanEnv = (env, name) => readBooleanEnvValue(name, env[name]);
4810
4766
  var readZuoraEndpoint = (env) => readTrimmedEnv(env, "ZUORA_BASE_URL") ?? readTrimmedEnv(env, "ZUORA_MCP_URL");
4811
4767
  var readToolkitMode = (env) => {
4812
- const mode = env.ZUORA_TOOLKIT_MODE?.trim() ?? "readOnly";
4813
- if (mode === "readOnly" || mode === "writeEnabled") {
4768
+ const mode = env.ZUORA_TOOLKIT_MODE?.trim() ?? SafetyMode.ReadOnly;
4769
+ if (mode === SafetyMode.ReadOnly || mode === SafetyMode.WriteEnabled) {
4814
4770
  return mode;
4815
4771
  }
4816
4772
  throw new Error(
4817
- `Invalid ZUORA_TOOLKIT_MODE: ${mode}. Expected readOnly or writeEnabled.`
4818
- );
4819
- };
4820
- var readOptionalUrlEnv = (env, name) => {
4821
- const value = readTrimmedEnv(env, name);
4822
- if (!value) {
4823
- return void 0;
4824
- }
4825
- try {
4826
- const url = new URL(value);
4827
- if (url.protocol === "http:" || url.protocol === "https:") {
4828
- return url.toString();
4829
- }
4830
- } catch {
4831
- }
4832
- throw new Error(`${name} must be an absolute HTTP(S) URL.`);
4833
- };
4834
- var readTokenAuthMethodEnv = (env) => {
4835
- const value = readTrimmedEnv(env, "ZUORA_TOKEN_AUTH_METHOD");
4836
- if (!value) {
4837
- return void 0;
4838
- }
4839
- if (value === "client_secret_post" || value === "client_secret_basic") {
4840
- return value;
4841
- }
4842
- throw new Error(
4843
- "Invalid ZUORA_TOKEN_AUTH_METHOD: expected client_secret_post or client_secret_basic."
4773
+ `Invalid ZUORA_TOOLKIT_MODE: ${mode}. Expected ${SafetyMode.ReadOnly} or ${SafetyMode.WriteEnabled}.`
4844
4774
  );
4845
4775
  };
4846
4776
  var readConnectionOptions = (env) => {
4847
- const tokenEndpoint = readOptionalUrlEnv(env, "ZUORA_TOKEN_ENDPOINT");
4848
- const tokenAuthMethod = readTokenAuthMethodEnv(env);
4849
4777
  const entityIds = readTrimmedEnv(env, "ZUORA_ENTITY_IDS");
4850
4778
  const orgIds = readTrimmedEnv(env, "ZUORA_ORG_IDS");
4851
4779
  const zuoraVersion = readTrimmedEnv(env, "ZUORA_VERSION");
4852
4780
  return {
4853
4781
  ...entityIds ? { entityIds } : {},
4854
4782
  ...orgIds ? { orgIds } : {},
4855
- ...tokenEndpoint ? { tokenEndpoint } : {},
4856
- ...tokenAuthMethod ? { tokenAuthMethod } : {},
4857
4783
  ...zuoraVersion ? { zuoraVersion } : {}
4858
4784
  };
4859
4785
  };
@@ -4878,28 +4804,19 @@ var zuoraAgentToolkitConfigFromEnv = (env = process.env) => {
4878
4804
  }
4879
4805
  },
4880
4806
  configuration: {
4881
- mode,
4882
- context: {
4883
- tenantId
4884
- }
4807
+ mode
4885
4808
  }
4886
4809
  };
4887
4810
  }
4888
4811
  return {
4889
4812
  baseUrl,
4890
4813
  auth: {
4891
- type: "oauthClientCredentials",
4892
4814
  clientId: requiredEnv(env, "ZUORA_CLIENT_ID"),
4893
4815
  clientSecret: requiredEnv(env, "ZUORA_CLIENT_SECRET")
4894
4816
  },
4895
4817
  ...Object.keys(connection).length > 0 ? { connection } : {},
4896
4818
  configuration: {
4897
- mode,
4898
- ...readTrimmedEnv(env, "ZUORA_TENANT_ID") ? {
4899
- context: {
4900
- tenantId: readTrimmedEnv(env, "ZUORA_TENANT_ID")
4901
- }
4902
- } : {}
4819
+ mode
4903
4820
  }
4904
4821
  };
4905
4822
  };
@@ -5067,6 +4984,79 @@ var strictifySchema = (schema) => {
5067
4984
 
5068
4985
  // src/openai-agents/toolkit.ts
5069
4986
  var ZuoraOpenAIAgentsToolkit = class extends ToolkitCore {
4987
+ /** Maps sanitized adapter tool names back to their original ZuoraToolDefinition. */
4988
+ sanitizedToolMap = /* @__PURE__ */ new Map();
4989
+ /**
4990
+ * Look up a ZuoraToolDefinition by its sanitized adapter tool name.
4991
+ *
4992
+ * The OpenAI Agents SDK registers tools with sanitized names (see convertTools),
4993
+ * but external code may reference tools by their original MCP names. This method
4994
+ * provides resolution in both directions.
4995
+ */
4996
+ resolveToolName(name) {
4997
+ if (this.sanitizedToolMap.has(name)) {
4998
+ return name;
4999
+ }
5000
+ for (const [sanitized, def] of this.sanitizedToolMap) {
5001
+ if (def.name === name) {
5002
+ return sanitized;
5003
+ }
5004
+ }
5005
+ return void 0;
5006
+ }
5007
+ /**
5008
+ * Resolve a tool name that may be either sanitized or original MCP format.
5009
+ *
5010
+ * This provides additional name normalization to handle cases where:
5011
+ * - A tool name contains unsanitized characters (like dots)
5012
+ * - A tool name was learned from descriptions or logs
5013
+ *
5014
+ * We attempt to find a match with relaxed sanitization rules before giving up.
5015
+ */
5016
+ normalizeToolName(name) {
5017
+ const directMatch = this.resolveToolName(name);
5018
+ if (directMatch) {
5019
+ return directMatch;
5020
+ }
5021
+ const normalized = name.replace(/\./g, "_");
5022
+ const normalizedMatch = this.resolveToolName(normalized);
5023
+ if (normalizedMatch) {
5024
+ return normalizedMatch;
5025
+ }
5026
+ for (const [sanitized, def] of this.sanitizedToolMap) {
5027
+ if (def.name === normalized) {
5028
+ return sanitized;
5029
+ }
5030
+ }
5031
+ return void 0;
5032
+ }
5033
+ /**
5034
+ * Override callTool to provide flexible name resolution.
5035
+ *
5036
+ * The OpenAI Agents SDK may receive tool calls with names that were learned
5037
+ * from descriptions or other sources (not directly from tool registration).
5038
+ * This override allows us to handle both sanitized and original tool names
5039
+ * by falling back to name normalization when the direct lookup fails.
5040
+ */
5041
+ async callTool(operationName, input) {
5042
+ try {
5043
+ return await super.callTool(operationName, input);
5044
+ } catch (error) {
5045
+ if (error instanceof Error && (error.message.includes("Unknown Zuora tool") || error.message.includes("TOOL_NOT_FOUND"))) {
5046
+ const sanitized = this.normalizeToolName(operationName);
5047
+ if (sanitized) {
5048
+ const def = this.sanitizedToolMap.get(sanitized);
5049
+ if (def) {
5050
+ console.warn(
5051
+ `[zuora-agent-toolkit] Tool name normalized: "${operationName}" -> "${def.name}" (sanitized: "${sanitized}")`
5052
+ );
5053
+ return await super.callTool(def.name, input);
5054
+ }
5055
+ }
5056
+ }
5057
+ throw error;
5058
+ }
5059
+ }
5070
5060
  /**
5071
5061
  * Serialises the agent tool response, emits an `agent.tool.response`
5072
5062
  * observability event (with the response in `metadata` when
@@ -5090,6 +5080,7 @@ var ZuoraOpenAIAgentsToolkit = class extends ToolkitCore {
5090
5080
  convertTools(tools) {
5091
5081
  return tools.map((def) => {
5092
5082
  const sanitizedName = sanitizeAdapterToolName(def.name);
5083
+ this.sanitizedToolMap.set(sanitizedName, def);
5093
5084
  return tool({
5094
5085
  name: sanitizedName,
5095
5086
  description: def.description,