@zuora/agent-toolkit 0.0.1-alpha.0 → 0.1.0-alpha.0

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/README.md CHANGED
@@ -6,18 +6,38 @@ Zuora billing operations as callable tools for AI agent frameworks.
6
6
 
7
7
  Built on the [Zuora MCP server](https://developer.zuora.com). Tool inventory is discovered at runtime from your tenant's MCP endpoint — it is not hardcoded in this package.
8
8
 
9
+ ## Quick start
10
+
11
+ ```ts
12
+ import { Agent, run } from "@openai/agents";
13
+ import { createZuoraOpenAIAgentsToolkit } from "@zuora/agent-toolkit/openai-agents";
14
+
15
+ const toolkit = await createZuoraOpenAIAgentsToolkit({ env: true });
16
+ try {
17
+ const agent = new Agent({ name: "Billing Agent", tools: await toolkit.getTools() });
18
+ const result = await run(agent, "How many active accounts do we have?");
19
+ console.log(result.finalOutput);
20
+ } finally {
21
+ await toolkit.close();
22
+ }
23
+ ```
24
+
25
+ Set `ZUORA_BASE_URL`, `ZUORA_CLIENT_ID`, and `ZUORA_CLIENT_SECRET` in your environment. See [Credentials](#credentials) for other auth options.
26
+
9
27
  ## Adapter status
10
28
 
11
29
  | Subpath | Factory | Status |
12
30
  |---|---|---|
13
31
  | `@zuora/agent-toolkit/openai` | `createZuoraOpenAIAgentToolkit` | ✅ Implemented |
14
32
  | `@zuora/agent-toolkit/openai-agents` | `createZuoraOpenAIAgentsToolkit` | ✅ Implemented |
15
- | `@zuora/agent-toolkit/langchain` | — | 🚧 Coming soon |
16
- | `@zuora/agent-toolkit/vercel-ai` | — | 🚧 Coming soon |
33
+ | `@zuora/agent-toolkit/langchain` | `createZuoraLangChainToolkit` | ✅ Implemented |
34
+ | `@zuora/agent-toolkit/vercel-ai` | `createZuoraVercelAIToolkit` | ✅ Implemented |
17
35
  | `@zuora/agent-toolkit/mcp` | — | 🚧 Coming soon |
18
36
 
19
37
  ## Installation
20
38
 
39
+ Requires **Node.js 18** or later.
40
+
21
41
  ```bash
22
42
  npm install @zuora/agent-toolkit
23
43
  ```
@@ -25,52 +45,165 @@ npm install @zuora/agent-toolkit
25
45
  Framework packages are peer dependencies — install only what you use:
26
46
 
27
47
  ```bash
28
- # OpenAI Chat Completions
48
+ # OpenAI Chat Completions (requires openai >=4)
29
49
  npm install openai
30
50
 
31
- # OpenAI Agents SDK
51
+ # OpenAI Agents SDK (requires @openai/agents >=0.13)
32
52
  npm install @openai/agents
53
+
54
+ # LangChain (requires @langchain/core >=1.2.1)
55
+ npm install @langchain/core @langchain/langgraph @langchain/openai
56
+
57
+ # Vercel AI SDK (requires ai >=7 — v7 is a breaking rewrite from v6)
58
+ npm install ai @ai-sdk/openai
33
59
  ```
34
60
 
61
+ > **Version notes**
62
+ > - **Vercel AI SDK** — `ai >=7` is required. v7 introduced `ToolSet`, `isStepCount`, and `jsonSchema()`, which this adapter depends on. v6 is **not** compatible.
63
+ > - **LangChain** — `@langchain/core >=1.2.1` is required for `DynamicStructuredTool` and JSON Schema parameter support.
64
+ > - `@langchain/langgraph` and `@langchain/openai` are not direct peer dependencies but are needed to run the agent (see examples).
65
+
35
66
  ## Credentials
36
67
 
37
- The toolkit authenticates against Zuora using OAuth 2.0 client credentials. You need:
68
+ Pass `env: true` to read credentials from the environment automatically:
69
+
70
+ | Variable | Purpose |
71
+ |---|---|
72
+ | `ZUORA_BASE_URL` | Tenant REST API endpoint (required) |
73
+ | `ZUORA_CLIENT_ID` / `ZUORA_CLIENT_SECRET` | OAuth credentials (required) |
74
+ | `ZUORA_TOOLKIT_MODE` | `readOnly` (default) or `writeEnabled` |
75
+
76
+ `ZUORA_TOOLKIT_MODE` is only read when `env: true` is set, and is overridden by an explicit `configuration.mode`.
77
+
78
+ You can also pass credentials explicitly:
38
79
 
39
- - `ZUORA_BASE_URL` — your Zuora tenant REST API base URL (e.g. `https://rest.apisandbox.zuora.com`)
40
- - `ZUORA_CLIENT_ID` — OAuth client ID
41
- - `ZUORA_CLIENT_SECRET` — OAuth client secret
80
+ ```ts
81
+ import { SafetyMode } from "@zuora/agent-toolkit";
82
+
83
+ const toolkit = await createZuoraOpenAIAgentToolkit({
84
+ baseUrl: process.env.ZUORA_BASE_URL!,
85
+ auth: {
86
+ clientId: process.env.ZUORA_CLIENT_ID!,
87
+ clientSecret: process.env.ZUORA_CLIENT_SECRET!,
88
+ },
89
+ configuration: { mode: SafetyMode.ReadOnly },
90
+ });
91
+ ```
92
+
93
+ `env: true` reads only credentials and base URL from the environment — all other config fields (`configuration`, `approvalPolicy`, `observability`, etc.) can still be set explicitly alongside it.
42
94
 
43
95
  **Use a client scoped to the minimum permissions your agent needs.** Never use admin credentials.
44
96
 
45
97
  ## Usage
46
98
 
99
+ > **Which OpenAI adapter should I use?**
100
+ > - Use `@zuora/agent-toolkit/openai-agents` + `createZuoraOpenAIAgentsToolkit` if you are building with the **`@openai/agents` library** (the higher-level agent runtime).
101
+ > - Use `@zuora/agent-toolkit/openai` + `createZuoraOpenAIAgentToolkit` if you are using the **raw `openai` Chat Completions API** and managing the tool-call loop yourself.
102
+
47
103
  ### OpenAI Agents SDK
48
104
 
49
105
  ```ts
50
106
  import { Agent, run } from "@openai/agents";
107
+ import { SafetyMode } from "@zuora/agent-toolkit";
51
108
  import { createZuoraOpenAIAgentsToolkit } from "@zuora/agent-toolkit/openai-agents";
52
109
 
53
110
  const toolkit = await createZuoraOpenAIAgentsToolkit({
54
111
  env: true,
55
- configuration: { mode: "readOnly", bundles: { query: true } },
112
+ configuration: { mode: SafetyMode.ReadOnly },
56
113
  });
57
114
 
58
- const tools = await toolkit.getTools({ bundles: ["query"] });
115
+ // Scope to specific bundles and cap tool count — improves model accuracy
116
+ const tools = (await toolkit.getTools({ bundles: ["accounts", "query"] })).slice(0, 50);
59
117
  const agent = new Agent({ name: "Billing Agent", tools });
60
118
  const result = await run(agent, "How many active billing accounts do we have?");
61
119
 
62
120
  await toolkit.close();
63
121
  ```
64
122
 
65
- If you already have a Chat Completions toolkit, you can bridge it with `buildAgentsTools`:
123
+ If you already have a `ZuoraOpenAIAgentToolkit` instance (for example, shared across multiple agents or reused from an existing integration), you can convert its tools to Agents SDK format without opening a second MCP connection:
66
124
 
67
125
  ```ts
68
126
  import { buildAgentsTools } from "@zuora/agent-toolkit/openai-agents";
69
- import { createZuoraOpenAIAgentToolkit } from "@zuora/agent-toolkit/openai";
70
127
  import { tool } from "@openai/agents";
71
128
 
72
- const chatToolkit = await createZuoraOpenAIAgentToolkit({ env: true });
129
+ // `tool` is passed in rather than imported directly so @openai/agents stays
130
+ // an optional peer dependency and the module loads without it installed.
73
131
  const tools = await buildAgentsTools(tool, chatToolkit, { bundles: ["query"] });
132
+ const agent = new Agent({ name: "Billing Agent", tools });
133
+ await run(agent, "...");
134
+ // chatToolkit must stay open while tools are in use — close it when done
135
+ ```
136
+
137
+ > If you are starting fresh (no existing toolkit), prefer `createZuoraOpenAIAgentsToolkit` directly — it handles the connection internally.
138
+
139
+ ### LangChain
140
+
141
+ ```ts
142
+ import { ChatOpenAI } from "@langchain/openai";
143
+ import { createReactAgent } from "@langchain/langgraph/prebuilt";
144
+ import { SafetyMode } from "@zuora/agent-toolkit";
145
+ import { createZuoraLangChainToolkit } from "@zuora/agent-toolkit/langchain";
146
+
147
+ const toolkit = await createZuoraLangChainToolkit({
148
+ env: true,
149
+ configuration: { mode: SafetyMode.ReadOnly },
150
+ });
151
+
152
+ // Scope to specific bundles and cap tool count — improves model accuracy
153
+ const tools = (await toolkit.getTools({ bundles: ["accounts", "query"] })).slice(0, 50);
154
+
155
+ const agent = createReactAgent({
156
+ llm: new ChatOpenAI({ model: "gpt-4o" }),
157
+ tools,
158
+ });
159
+
160
+ // LangChain owns the tool-call loop — no manual dispatch needed
161
+ const result = await agent.invoke({
162
+ messages: [{ role: "user", content: "List 3 active accounts" }],
163
+ });
164
+
165
+ const answer = result.messages.at(-1)?.content;
166
+ console.log(typeof answer === "string" ? answer : JSON.stringify(answer));
167
+
168
+ await toolkit.close();
169
+ ```
170
+
171
+ ### Vercel AI SDK
172
+
173
+ > `getToolSet()` returns a `ToolSet` (`Record<string, Tool>`) — the format `streamText` and `generateText` require. All other adapters use `getTools()` which returns an array.
174
+
175
+ ```ts
176
+ import { openai } from "@ai-sdk/openai";
177
+ import { streamText, isStepCount } from "ai";
178
+ import { SafetyMode } from "@zuora/agent-toolkit";
179
+ import { createZuoraVercelAIToolkit } from "@zuora/agent-toolkit/vercel-ai";
180
+
181
+ const toolkit = await createZuoraVercelAIToolkit({
182
+ env: true,
183
+ configuration: { mode: SafetyMode.ReadOnly },
184
+ });
185
+
186
+ // `getToolSet()` returns a ToolSet (`Record<string, Tool>`) rather than an array,
187
+ // so `.slice()` is not available — the second argument is the only way to cap tool count.
188
+ // Omit it to return all matching tools.
189
+ const tools = await toolkit.getToolSet(
190
+ { bundles: ["accounts", "query"] },
191
+ 50,
192
+ );
193
+
194
+ const { textStream } = streamText({
195
+ model: openai("gpt-4o"),
196
+ tools,
197
+ stopWhen: isStepCount(20), // SDK drives the tool-call loop — no manual dispatch needed
198
+ messages: [{ role: "user", content: "List 3 active accounts" }],
199
+ });
200
+
201
+ for await (const chunk of textStream) {
202
+ process.stdout.write(chunk);
203
+ }
204
+ console.log();
205
+
206
+ await toolkit.close();
74
207
  ```
75
208
 
76
209
  ### OpenAI Chat Completions
@@ -81,10 +214,11 @@ import {
81
214
  createZuoraOpenAIAgentToolkit,
82
215
  handleToolCalls,
83
216
  } from "@zuora/agent-toolkit/openai";
217
+ import { SafetyMode } from "@zuora/agent-toolkit";
84
218
 
85
219
  const toolkit = await createZuoraOpenAIAgentToolkit({
86
220
  env: true,
87
- configuration: { mode: "readOnly" },
221
+ configuration: { mode: SafetyMode.ReadOnly },
88
222
  });
89
223
 
90
224
  const client = new OpenAI();
@@ -105,6 +239,8 @@ for (let step = 0; step < 20; step++) {
105
239
  messages.push(choice.message);
106
240
 
107
241
  if (choice.finish_reason === "tool_calls") {
242
+ // handleToolCalls returns { messages, abortTurn, requireToolOnNextStep, followUpToolNames }.
243
+ // For basic loops, only `messages` is needed. See examples/openai/hitl.ts for advanced usage.
108
244
  const { messages: toolMessages } = await handleToolCalls({ toolkit, response });
109
245
  messages.push(...toolMessages);
110
246
  continue;
@@ -119,13 +255,33 @@ await toolkit.close();
119
255
 
120
256
  ## Configuration
121
257
 
258
+ Top-level config shape — all fields except credentials are optional:
259
+
260
+ ```ts
261
+ {
262
+ env: true, // reads ZUORA_BASE_URL, ZUORA_CLIENT_ID, ZUORA_CLIENT_SECRET
263
+ configuration: { // tool filtering, safety, and routing
264
+ mode: SafetyMode.ReadOnly,
265
+ bundles: { accounts: true },
266
+ context: { // scopes operations to the specified entity / org
267
+ entityIds: "entity-1", // → Zuora-Entity-Ids header
268
+ orgIds: "org-1", // → Zuora-Org-Ids header
269
+ },
270
+ },
271
+ // approvalPolicy: … // see "Approval policy" section below
272
+ observability: { // optional: events and logging
273
+ onEvent: (event) => { … },
274
+ },
275
+ }
276
+ ```
277
+
122
278
  ### Safety modes
123
279
 
124
280
  ```ts
125
281
  configuration: {
126
- mode: "readOnly", // only read operations (default)
282
+ mode: SafetyMode.ReadOnly, // only read operations (default)
127
283
  // or
128
- mode: "writeEnabled", // read + write operations
284
+ mode: SafetyMode.WriteEnabled, // read + write operations
129
285
  }
130
286
  ```
131
287
 
@@ -143,96 +299,125 @@ configuration: {
143
299
  }
144
300
  ```
145
301
 
146
- Available bundles: `accounts`, `catalog`, `subscriptions`, `orders`, `billing`, `payments`, `usage`, `revenue`, `finance`, `workflows`, `query`, `admin`, `files`.
302
+ Available bundles: `accounts`, `catalog`, `subscriptions`, `orders`, `billing`, `payments`, `usage`, `revenue`, `finance`, `workflows`, `query`, `admin`, `files`. Which bundles are available depends on your Zuora tenant's MCP endpoint.
303
+
304
+ You can also filter at call time via `getTools()`. Note that `configuration.bundles` uses an object (`{ accounts: true }`) and is fixed at toolkit creation time; `getTools({ bundles: [...] })` uses an array and filters the already-loaded catalog at call time.
305
+
306
+ ```ts
307
+ // Filter by bundle
308
+ toolkit.getTools({ bundles: ["accounts", "query"] })
147
309
 
148
- You can also filter at call time via `getTools({ bundles, include, exclude, mode })`.
310
+ // Include or exclude specific tools by exact name
311
+ toolkit.getTools({ include: ["get_account", "list_accounts"] })
312
+ toolkit.getTools({ exclude: ["delete_account"] })
313
+
314
+ // Narrow to write-enabled tools for this call only (per-call mode overrides global config)
315
+ toolkit.getTools({ mode: SafetyMode.WriteEnabled })
316
+ ```
149
317
 
150
318
  ### Exclude / include specific tools
151
319
 
320
+ `configuration.bundles.billing.exclude` is evaluated once at toolkit creation and applies permanently to that bundle. `getTools({ exclude })` filters the already-loaded catalog on each call and works across all bundles.
321
+
322
+ `include` and `exclude` can be combined: `include` acts as an allowlist first, then `exclude` removes from that set. A tool passes if it matches `include` (when set) **and** is not in `exclude`.
323
+
152
324
  ```ts
153
325
  configuration: {
154
326
  bundles: {
155
327
  billing: {
156
328
  exclude: ["create_invoice", "delete_invoice"],
157
- // or include: ["list_invoices"]
329
+ // or use include to allowlist specific tools: include: ["list_invoices"]
330
+ // or combine both: include: ["run_reports", "list_invoices"], exclude: ["delete_billing_run"]
158
331
  },
159
332
  },
160
333
  }
161
334
  ```
162
335
 
163
- ### Split operations
336
+ ### Enabling or disabling individual tools
337
+
338
+ To enable or disable specific tools without affecting their entire bundle:
164
339
 
165
- Multi-operation MCP tools are split into per-operation tools by default (`splitOperations: true`), producing names like `manage_data_queries__submit_query`. Set `splitOperations: false` to keep merged tools.
340
+ ```ts
341
+ configuration: {
342
+ tools: {
343
+ "delete_invoice": false, // disable one tool
344
+ "run_reports": true, // explicitly enable (useful when a bundle is partially restricted)
345
+ },
346
+ }
347
+ ```
166
348
 
167
349
  ### Approval policy
168
350
 
169
351
  Gate write / high-risk operations before they reach Zuora. `approvalPolicy` is a **top-level** config field (not inside `configuration`):
170
352
 
171
353
  ```ts
172
- import { ApprovalPolicyPreset } from "@zuora/agent-toolkit";
354
+ import { ApprovalPolicyPreset, SafetyMode } from "@zuora/agent-toolkit";
173
355
 
174
356
  const toolkit = await createZuoraOpenAIAgentToolkit({
175
357
  env: true,
176
- configuration: { mode: "writeEnabled" },
358
+ configuration: { mode: SafetyMode.WriteEnabled },
177
359
  approvalPolicy: ApprovalPolicyPreset.HighRisk,
178
360
  // or a custom handler:
179
361
  // approvalPolicy: async (request) => {
180
362
  // if (request.tool.meta.riskLevel === "high") return "deny";
181
- // return "not_required";
363
+ // return "allow";
182
364
  // },
183
365
  });
184
366
  ```
185
367
 
186
368
  Presets: `Off`, `AllWrites`, `HighRisk`, `ProductionWrites`, `ProductionHighRisk`.
187
369
 
188
- ### Context defaults
370
+ Custom handler return values: `"allow"` (proceed) · `"deny"` (block) · `{ decision: "allow" | "deny", reason?: string }` (with an explanation added to the rejection error).
371
+
372
+ ### Context
189
373
 
190
- Pass optional string context keys (for example tenant or entity identifiers) via `configuration.context`. Connection-level `entityIds` / `orgIds` arrays live on `connection` instead:
374
+ Pass routing and observability identifiers via `configuration.context`. All values are strings — use comma-separation for multiple IDs:
375
+
376
+ | Key | Effect |
377
+ |---|---|
378
+ | `entityIds` | Forwarded as `Zuora-Entity-Ids` to scope operations to the specified entity |
379
+ | `orgIds` | Forwarded as `Zuora-Org-Ids` to scope operations to the specified org |
191
380
 
192
381
  ```ts
193
- configuration: {
194
- context: {
195
- tenantId: "…",
382
+ const toolkit = await createZuoraOpenAIAgentToolkit({
383
+ env: true,
384
+ configuration: {
385
+ mode: SafetyMode.WriteEnabled,
386
+ context: {
387
+ entityIds: "entity-1,entity-2", // comma-separated for multiple
388
+ orgIds: "org-1",
389
+ },
196
390
  },
197
- },
198
- connection: {
199
- entityIds: ["entity-1"],
200
- orgIds: ["org-1"],
201
- },
391
+ });
202
392
  ```
203
393
 
204
394
  ### Observability
205
395
 
206
396
  ```ts
207
397
  observability: {
208
- onEvent: (event) => { /* tool.called, tool.completed, approval.*, … */ },
398
+ onEvent: (event) => { /* synchronous observer */ },
399
+ emit: async (event) => { /* async sink — await is respected */ },
209
400
  logger: { info: console.info, warn: console.warn, error: console.error },
210
- includePayloads: false, // default; set true to include sanitized I/O
401
+ includePayloads: false, // default; set true to include tool input keys and
402
+ // truncated values in events — secrets are redacted
211
403
  }
212
404
  ```
213
405
 
214
- ## Environment variable config
215
-
216
- Pass `env: true` to read credentials from the environment automatically:
406
+ Emitted event names:
217
407
 
218
- ```ts
219
- const toolkit = await createZuoraOpenAIAgentToolkit({
220
- env: true,
221
- configuration: { mode: "readOnly" },
222
- });
223
- ```
224
-
225
- | Variable | Purpose |
408
+ | Event | When |
226
409
  |---|---|
227
- | `ZUORA_BASE_URL` or `ZUORA_MCP_URL` | Endpoint (required) |
228
- | `ZUORA_CLIENT_ID` / `ZUORA_CLIENT_SECRET` | OAuth credentials |
229
- | `ZUORA_TOOLKIT_MODE` | `readOnly` (default) or `writeEnabled` |
230
-
231
- You can still pass `baseUrl` + `auth` explicitly instead of `env: true`.
410
+ | `tool.called` | Tool invocation starts |
411
+ | `tool.completed` | Tool returns successfully |
412
+ | `tool.failed` | Tool throws or returns an error |
413
+ | `approval.requested` | Approval policy triggers before execution |
414
+ | `approval.decided` | Approval decision recorded (approved / denied) |
415
+ | `metadata.loaded` | Tool catalog fetched from MCP on `initialize()` |
416
+ | `async.poll.checked` | A polling iteration completed for an async job |
232
417
 
233
418
  ## Lifecycle
234
419
 
235
- Always call `close()` when done:
420
+ Always call `close()` when done — it closes the underlying MCP connection and allows the process to exit cleanly:
236
421
 
237
422
  ```ts
238
423
  const toolkit = await createZuoraOpenAIAgentToolkit({ env: true });
@@ -245,19 +430,19 @@ try {
245
430
 
246
431
  ## Discovery and execution
247
432
 
433
+ > `listTools()` returns `ZuoraToolDefinition[]` — framework-agnostic metadata for inspection and logging.
434
+ > `getTools()` returns framework-native tool objects (e.g. `ChatCompletionTool[]`) ready to pass directly to the LLM.
435
+
248
436
  ```ts
249
437
  const tools = await toolkit.listTools(); // ZuoraToolDefinition[]
250
- const def = await toolkit.describeTool("get_account");
251
- const bundles = await toolkit.listBundles();
252
- const bundle = await toolkit.describeBundle("subscriptions");
438
+ const def = await toolkit.describeTool("get_account"); // ZuoraToolDefinition
439
+ const bundles = await toolkit.listBundles(); // ZuoraBundleName[]
440
+ const bundle = await toolkit.describeBundle("subscriptions"); // { name, description, toolCount, tools: ZuoraToolDefinition[] }
253
441
 
254
442
  const result = await toolkit.callTool("get_account", { accountKey: "…" });
255
- // For async tools: await toolkit.waitForJob(toolName, asyncResult, options?)
256
443
  ```
257
444
 
258
- ## Native query / file tools
259
-
260
- When the `query` bundle is enabled, the SDK may expose local helpers such as `download_query_result` and `read_downloaded_file` so agents can fetch and preview Data Query artifacts. Artifacts default to `~/.zuora-agent-artifacts`.
445
+ For tools that start async jobs (e.g. Data Query), the tool result automatically includes a follow-up instruction telling the model which tool to call next — the model drives polling on subsequent steps without any extra code.
261
446
 
262
447
  ## Errors
263
448
 
@@ -277,7 +462,7 @@ All SDK errors are `ZuoraAgentToolkitError` with a typed `code`:
277
462
  import { ZuoraAgentToolkitError } from "@zuora/agent-toolkit";
278
463
 
279
464
  try {
280
- await handleToolCalls({ toolkit, response });
465
+ await toolkit.callTool("create_invoice", { accountId: "A-1" });
281
466
  } catch (err) {
282
467
  if (err instanceof ZuoraAgentToolkitError && err.code === "APPROVAL_REJECTED") {
283
468
  // handle rejection
@@ -285,6 +470,21 @@ try {
285
470
  }
286
471
  ```
287
472
 
473
+ When using framework adapters, `ZuoraAgentToolkitError` surfaces from `handleToolCalls()` (OpenAI Chat Completions), `run()` (OpenAI Agents SDK), `agent.invoke()` (LangChain), or during stream consumption with `generateText()` / `streamText()` (Vercel AI SDK).
474
+
475
+ ## Key exported types
476
+
477
+ ```ts
478
+ import type {
479
+ ZuoraToolDefinition, // full tool descriptor: name, description, inputSchema, meta
480
+ ZuoraBundleName, // union of all valid bundle name strings
481
+ ToolSelection, // filter shape passed to getTools() / listTools() — { bundles?, include?, exclude?, mode? }
482
+ ZuoraToolkitEvent, // observability event payload
483
+ ApprovalPolicy, // type for the approvalPolicy config field
484
+ } from "@zuora/agent-toolkit";
485
+ import { ZuoraAgentToolkitError, ApprovalPolicyPreset, SafetyMode } from "@zuora/agent-toolkit";
486
+ ```
487
+
288
488
  ## Examples
289
489
 
290
490
  Working examples are available in the source repository:
@@ -293,6 +493,8 @@ Working examples are available in the source repository:
293
493
  |---|---|
294
494
  | `examples/openai/index.ts` | Minimal Chat Completions agent loop |
295
495
  | `examples/openai/advanced.ts` | Observability, hooks, async jobs, discovery |
296
- | `examples/openai/hitl.ts` | Human-in-the-loop via `shouldExecute` |
496
+ | `examples/openai/hitl.ts` | Human-in-the-loop (HITL) approval |
297
497
  | `examples/openai-agents/index.ts` | OpenAI Agents SDK agent |
298
498
  | `examples/openai-agents-cli/` | Interactive CLI agent + planner |
499
+ | `examples/langchain/index.ts` | LangChain ReactAgent with Zuora tools |
500
+ | `examples/vercel-ai/index.ts` | Vercel AI SDK `streamText` agent |
@@ -30,7 +30,11 @@ type EventSanitizerLimits = {
30
30
  };
31
31
 
32
32
  type ZuoraEnvironment = "sandbox" | "production";
33
- type ZuoraSafetyMode = "readOnly" | "writeEnabled";
33
+ declare const SafetyMode: {
34
+ readonly ReadOnly: "readOnly";
35
+ readonly WriteEnabled: "writeEnabled";
36
+ };
37
+ type ZuoraSafetyMode = (typeof SafetyMode)[keyof typeof SafetyMode];
34
38
  type ZuoraToolOperation = "read" | "write";
35
39
  type ZuoraRiskLevel = "low" | "medium" | "high";
36
40
  /**
@@ -164,13 +168,8 @@ type JsonSchema = {
164
168
  [key: string]: unknown;
165
169
  };
166
170
  type ZuoraAuthConfig = {
167
- type: "oauthClientCredentials";
168
171
  clientId: string;
169
172
  clientSecret: string;
170
- scope?: string;
171
- } | {
172
- type: "accessToken";
173
- accessToken: string;
174
173
  };
175
174
  type ZuoraTenantAuthConfig = {
176
175
  enabled: true;
@@ -226,8 +225,6 @@ type ZuoraMcpClient = {
226
225
  close?: () => Promise<void>;
227
226
  };
228
227
  type ZuoraConnectionConfigBase = {
229
- tokenEndpoint?: string;
230
- tokenAuthMethod?: "client_secret_post" | "client_secret_basic";
231
228
  headers?: Record<string, string | undefined>;
232
229
  entityIds?: string | string[];
233
230
  orgIds?: string | string[];
@@ -237,10 +234,6 @@ type ZuoraConnectionConfigBase = {
237
234
  type ZuoraTenantAuthConnectionConfig = ZuoraConnectionConfigBase & {
238
235
  tenantAuth: EnabledZuoraTenantAuthConfig;
239
236
  };
240
- type ZuoraStandardConnectionConfig = ZuoraConnectionConfigBase & {
241
- tenantAuth?: Exclude<ZuoraTenantAuthConfig, EnabledZuoraTenantAuthConfig>;
242
- };
243
- type ZuoraConnectionConfig = ZuoraStandardConnectionConfig | ZuoraTenantAuthConnectionConfig;
244
237
  declare const ApprovalPolicyPreset: {
245
238
  readonly Off: "off";
246
239
  readonly AllWrites: "all_writes";
@@ -249,8 +242,8 @@ declare const ApprovalPolicyPreset: {
249
242
  readonly ProductionHighRisk: "production_high_risk";
250
243
  };
251
244
  type ApprovalPolicyPresetValue = (typeof ApprovalPolicyPreset)[keyof typeof ApprovalPolicyPreset];
252
- type ApprovalPolicyResult = "not_required" | "deny" | {
253
- decision: "not_required" | "deny";
245
+ type ApprovalPolicyResult = "allow" | "deny" | {
246
+ decision: "allow" | "deny";
254
247
  reason?: string;
255
248
  };
256
249
  type ApprovalPolicyHandler = (request: ApprovalRequest) => ApprovalPolicyResult | Promise<ApprovalPolicyResult>;
@@ -362,7 +355,6 @@ type ZuoraAgentToolkitEnvConfig = Omit<ZuoraAgentToolkitConfigBase, "baseUrl"> &
362
355
  };
363
356
  type ZuoraAgentToolkitConfig = (ZuoraAgentToolkitConfigBase & {
364
357
  auth: ZuoraAuthConfig;
365
- connection?: ZuoraConnectionConfig;
366
358
  }) | (ZuoraAgentToolkitConfigBase & {
367
359
  auth?: ZuoraAuthConfig;
368
360
  connection: ZuoraTenantAuthConnectionConfig;
@@ -529,4 +521,4 @@ declare function resolveAsyncJobPollingToolName(result: ZuoraAsyncJobResult | Zu
529
521
  */
530
522
  declare function resolveAsyncJobStatusDetails(result: ZuoraToolExecutionResult, statusPath: string, terminalStatuses: ZuoraAsyncJobTerminalStatuses): ZuoraAsyncJobStatusResolution;
531
523
 
532
- export { type ApprovalPolicy as A, resolveAsyncJobId as B, resolveAsyncJobPollingToolName as C, DEFAULT_ASYNC_JOB_TERMINAL_STATUSES as D, resolveAsyncJobStatus as E, resolveAsyncJobStatusDetails as F, resolveAsyncJobTerminalState as G, type NativeFileApprovalMetadata as N, type ToolSelection as T, type ZuoraToolDefinition as Z, type ZuoraAgentToolkitConfigInput as a, type ZuoraToolExecutionResult as b, type ZuoraAsyncJobResult as c, type ZuoraToolkitEvent as d, type ZuoraAgentToolkitConfig as e, type ZuoraMcpClient as f, type ZuoraBundleDefinition as g, type ZuoraBundleName as h, type ZuoraWaitForJobOptions as i, ApprovalPolicyPreset as j, type ApprovalPolicyPresetValue as k, type ApprovalRequest as l, type NativeFileToolsConfig as m, type NativeToolApprovalMetadata as n, type NativeToolMetadataConfig as o, type ToolkitBundleName as p, type ZuoraAsyncJobStatusResolution as q, type ZuoraAsyncJobTerminalState as r, type ZuoraAsyncJobTerminalStatuses as s, type ZuoraSafetyMode as t, type ZuoraToolkitConfiguration as u, type ZuoraToolkitLogger as v, buildAsyncJobResult as w, isAsyncJobResult as x, isAsyncJobTerminal as y, matchesAsyncJobTerminalStatus as z };
524
+ export { type ApprovalPolicy as A, resolveAsyncJobId as B, resolveAsyncJobPollingToolName as C, DEFAULT_ASYNC_JOB_TERMINAL_STATUSES as D, resolveAsyncJobStatus as E, resolveAsyncJobStatusDetails as F, resolveAsyncJobTerminalState as G, type NativeFileApprovalMetadata as N, SafetyMode as S, type ToolSelection as T, type ZuoraToolDefinition as Z, type ZuoraAgentToolkitConfigInput as a, type ZuoraToolExecutionResult as b, type ZuoraAsyncJobResult as c, type ZuoraToolkitEvent as d, type ZuoraAgentToolkitConfig as e, type ZuoraMcpClient as f, type ZuoraBundleDefinition as g, type ZuoraBundleName as h, type ZuoraWaitForJobOptions as i, ApprovalPolicyPreset as j, type ApprovalPolicyPresetValue as k, type ApprovalRequest as l, type NativeFileToolsConfig as m, type NativeToolApprovalMetadata as n, type NativeToolMetadataConfig as o, type ToolkitBundleName as p, type ZuoraAsyncJobStatusResolution as q, type ZuoraAsyncJobTerminalState as r, type ZuoraAsyncJobTerminalStatuses as s, type ZuoraSafetyMode as t, type ZuoraToolkitConfiguration as u, type ZuoraToolkitLogger as v, buildAsyncJobResult as w, isAsyncJobResult as x, isAsyncJobTerminal as y, matchesAsyncJobTerminalStatus as z };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { b as ZuoraToolExecutionResult, h as ZuoraBundleName } from './async-jobs-Dd8Jafv0.js';
2
- export { A as ApprovalPolicy, j as ApprovalPolicyPreset, k as ApprovalPolicyPresetValue, l as ApprovalRequest, D as DEFAULT_ASYNC_JOB_TERMINAL_STATUSES, N as NativeFileApprovalMetadata, m as NativeFileToolsConfig, n as NativeToolApprovalMetadata, o as NativeToolMetadataConfig, T as ToolSelection, p as ToolkitBundleName, e as ZuoraAgentToolkitConfig, a as ZuoraAgentToolkitConfigInput, c as ZuoraAsyncJobResult, q as ZuoraAsyncJobStatusResolution, r as ZuoraAsyncJobTerminalState, s as ZuoraAsyncJobTerminalStatuses, g as ZuoraBundleDefinition, t as ZuoraSafetyMode, Z as ZuoraToolDefinition, u as ZuoraToolkitConfiguration, d as ZuoraToolkitEvent, v as ZuoraToolkitLogger, i as ZuoraWaitForJobOptions, w as buildAsyncJobResult, x as isAsyncJobResult, y as isAsyncJobTerminal, z as matchesAsyncJobTerminalStatus, B as resolveAsyncJobId, C as resolveAsyncJobPollingToolName, E as resolveAsyncJobStatus, F as resolveAsyncJobStatusDetails, G as resolveAsyncJobTerminalState } from './async-jobs-Dd8Jafv0.js';
1
+ import { b as ZuoraToolExecutionResult, h as ZuoraBundleName } from './async-jobs-DJ_GygII.js';
2
+ export { A as ApprovalPolicy, j as ApprovalPolicyPreset, k as ApprovalPolicyPresetValue, l as ApprovalRequest, D as DEFAULT_ASYNC_JOB_TERMINAL_STATUSES, N as NativeFileApprovalMetadata, m as NativeFileToolsConfig, n as NativeToolApprovalMetadata, o as NativeToolMetadataConfig, S as SafetyMode, T as ToolSelection, p as ToolkitBundleName, e as ZuoraAgentToolkitConfig, a as ZuoraAgentToolkitConfigInput, c as ZuoraAsyncJobResult, q as ZuoraAsyncJobStatusResolution, r as ZuoraAsyncJobTerminalState, s as ZuoraAsyncJobTerminalStatuses, g as ZuoraBundleDefinition, t as ZuoraSafetyMode, Z as ZuoraToolDefinition, u as ZuoraToolkitConfiguration, d as ZuoraToolkitEvent, v as ZuoraToolkitLogger, i as ZuoraWaitForJobOptions, w as buildAsyncJobResult, x as isAsyncJobResult, y as isAsyncJobTerminal, z as matchesAsyncJobTerminalStatus, B as resolveAsyncJobId, C as resolveAsyncJobPollingToolName, E as resolveAsyncJobStatus, F as resolveAsyncJobStatusDetails, G as resolveAsyncJobTerminalState } from './async-jobs-DJ_GygII.js';
3
3
 
4
4
  type ZuoraAgentToolkitP0ErrorCode = "TOOL_EXECUTION_ERROR" | "TOOL_NOT_FOUND" | "BUNDLE_NOT_FOUND" | "APPROVAL_REJECTED" | "MALFORMED_METADATA" | "OAUTH_TOKEN_ERROR" | "ASYNC_JOB_TIMEOUT";
5
5
  type ZuoraAgentToolkitErrorCode = ZuoraAgentToolkitP0ErrorCode | (string & {});
package/dist/index.js CHANGED
@@ -236,6 +236,10 @@ var readNonEmptyString = (value) => {
236
236
  };
237
237
 
238
238
  // src/shared/types.ts
239
+ var SafetyMode = {
240
+ ReadOnly: "readOnly",
241
+ WriteEnabled: "writeEnabled"
242
+ };
239
243
  var ApprovalPolicyPreset = {
240
244
  Off: "off",
241
245
  AllWrites: "all_writes",
@@ -479,6 +483,7 @@ var ZUORA_BUNDLE_NAME_SET = new Set(ZUORA_BUNDLE_NAMES);
479
483
  export {
480
484
  ApprovalPolicyPreset,
481
485
  DEFAULT_ASYNC_JOB_TERMINAL_STATUSES,
486
+ SafetyMode,
482
487
  ZUORA_BUNDLE_NAME_SET,
483
488
  ZuoraAgentToolkitError,
484
489
  buildAsyncJobResult,