@roarkanalytics/sdk-mcp 4.42.1 → 4.43.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/CHANGELOG.md +7 -0
- package/README.md +44 -20
- package/client-headers.d.mts +2 -2
- package/client-headers.d.ts +2 -2
- package/client-headers.js +1 -1
- package/client-headers.mjs +1 -1
- package/code-tool-types.d.mts +0 -1
- package/code-tool-types.d.mts.map +1 -1
- package/code-tool-types.d.ts +0 -1
- package/code-tool-types.d.ts.map +1 -1
- package/code-tool.d.mts.map +1 -1
- package/code-tool.d.ts.map +1 -1
- package/code-tool.js +12 -34
- package/code-tool.js.map +1 -1
- package/code-tool.mjs +12 -34
- package/code-tool.mjs.map +1 -1
- package/directory-tools.d.mts +16 -0
- package/directory-tools.d.mts.map +1 -0
- package/directory-tools.d.ts +16 -0
- package/directory-tools.d.ts.map +1 -0
- package/directory-tools.js +704 -0
- package/directory-tools.js.map +1 -0
- package/directory-tools.mjs +700 -0
- package/directory-tools.mjs.map +1 -0
- package/docs-search-tool.d.mts.map +1 -1
- package/docs-search-tool.d.ts.map +1 -1
- package/docs-search-tool.js +4 -1
- package/docs-search-tool.js.map +1 -1
- package/docs-search-tool.mjs +4 -1
- package/docs-search-tool.mjs.map +1 -1
- package/http.d.mts.map +1 -1
- package/http.d.ts.map +1 -1
- package/http.js +9 -4
- package/http.js.map +1 -1
- package/http.mjs +9 -4
- package/http.mjs.map +1 -1
- package/options.d.mts +17 -0
- package/options.d.mts.map +1 -1
- package/options.d.ts +17 -0
- package/options.d.ts.map +1 -1
- package/options.js +22 -0
- package/options.js.map +1 -1
- package/options.mjs +21 -0
- package/options.mjs.map +1 -1
- package/package.json +11 -1
- package/server.d.mts +2 -1
- package/server.d.mts.map +1 -1
- package/server.d.ts +2 -1
- package/server.d.ts.map +1 -1
- package/server.js +18 -4
- package/server.js.map +1 -1
- package/server.mjs +18 -4
- package/server.mjs.map +1 -1
- package/src/client-headers.ts +1 -1
- package/src/code-tool-types.ts +0 -1
- package/src/code-tool.ts +14 -34
- package/src/directory-tools.ts +987 -0
- package/src/docs-search-tool.ts +4 -1
- package/src/http.ts +9 -5
- package/src/options.ts +33 -0
- package/src/server.ts +19 -4
- package/src/stdio.ts +2 -1
- package/stdio.d.mts.map +1 -1
- package/stdio.d.ts.map +1 -1
- package/stdio.js +2 -0
- package/stdio.js.map +1 -1
- package/stdio.mjs +2 -0
- package/stdio.mjs.map +1 -1
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
// Named tools for the remote OAuth server that Claude's connector directory reviews.
|
|
2
|
+
//
|
|
3
|
+
// The directory rejects one tool that both reads and writes. `execute` is that tool:
|
|
4
|
+
// a snippet can call any SDK method. This surface replaces it with one tool per
|
|
5
|
+
// action, each a single HTTP method, for the operations Roark users actually do:
|
|
6
|
+
// look up a project, read agents, calls, transcripts, and simulation results, and
|
|
7
|
+
// separately create a persona or customer flow and start a simulation.
|
|
8
|
+
//
|
|
9
|
+
// The local code-execution server stays on `execute`. This file is not generated.
|
|
10
|
+
import { APIError } from '@roarkanalytics/sdk';
|
|
11
|
+
import { asErrorResult, asTextContentResult, } from "./types.mjs";
|
|
12
|
+
/**
|
|
13
|
+
* Server instructions for the directory surface.
|
|
14
|
+
*
|
|
15
|
+
* Kept in-process on purpose. The code-execution server fetches its instructions
|
|
16
|
+
* from Stainless, and that document tells the model how to write snippets. The
|
|
17
|
+
* directory reviews tool text for instructions that are not about the tool, so
|
|
18
|
+
* this surface describes what the server does and stops there.
|
|
19
|
+
*/
|
|
20
|
+
export const DIRECTORY_SERVER_INSTRUCTIONS = 'Roark is a voice and chat agent observability and simulation platform. ' +
|
|
21
|
+
'This server reads agents, calls, transcripts, simulation results, and metrics, ' +
|
|
22
|
+
'and it can create agents, phone endpoints, personas, and customer flows, and start simulations, in the signed-in account. ' +
|
|
23
|
+
'Each tool does one of those things. ' +
|
|
24
|
+
'Tools that act inside a project take a project id when the connection is not already pinned to one. ' +
|
|
25
|
+
'list_projects returns the projects this credential can use.';
|
|
26
|
+
const ACCENTS = [
|
|
27
|
+
'US',
|
|
28
|
+
'US_X_SOUTH',
|
|
29
|
+
'GB',
|
|
30
|
+
'ES',
|
|
31
|
+
'DE',
|
|
32
|
+
'IN',
|
|
33
|
+
'FR',
|
|
34
|
+
'NL',
|
|
35
|
+
'SA',
|
|
36
|
+
'GR',
|
|
37
|
+
'AU',
|
|
38
|
+
'IT',
|
|
39
|
+
'ID',
|
|
40
|
+
'TH',
|
|
41
|
+
'JP',
|
|
42
|
+
'NZ',
|
|
43
|
+
'PH',
|
|
44
|
+
'SG',
|
|
45
|
+
'MY',
|
|
46
|
+
'HK',
|
|
47
|
+
'TR',
|
|
48
|
+
'PT',
|
|
49
|
+
'IL',
|
|
50
|
+
];
|
|
51
|
+
const LANGUAGES = [
|
|
52
|
+
'EN',
|
|
53
|
+
'ES',
|
|
54
|
+
'DE',
|
|
55
|
+
'HI',
|
|
56
|
+
'FR',
|
|
57
|
+
'NL',
|
|
58
|
+
'AR',
|
|
59
|
+
'EL',
|
|
60
|
+
'IT',
|
|
61
|
+
'ID',
|
|
62
|
+
'TH',
|
|
63
|
+
'JA',
|
|
64
|
+
'TL',
|
|
65
|
+
'MS',
|
|
66
|
+
'ZH',
|
|
67
|
+
'TR',
|
|
68
|
+
'PT',
|
|
69
|
+
'HE',
|
|
70
|
+
];
|
|
71
|
+
const GENDERS = ['MALE', 'FEMALE'];
|
|
72
|
+
const AGES = ['CHILD', 'TEENAGER', 'ADULT', 'ELDERLY'];
|
|
73
|
+
const EMOTIONS = [
|
|
74
|
+
'NEUTRAL',
|
|
75
|
+
'CHEERFUL',
|
|
76
|
+
'CONFUSED',
|
|
77
|
+
'FRUSTRATED',
|
|
78
|
+
'SKEPTICAL',
|
|
79
|
+
'RUSHED',
|
|
80
|
+
'DISTRACTED',
|
|
81
|
+
'ANGRY',
|
|
82
|
+
'ANXIOUS',
|
|
83
|
+
'SAD',
|
|
84
|
+
];
|
|
85
|
+
const DIRECTIONS = ['INBOUND', 'OUTBOUND'];
|
|
86
|
+
const ENDPOINT_DIRECTIONS = ['INCOMING', 'OUTGOING', 'INCOMING_AND_OUTGOING'];
|
|
87
|
+
const CALL_STATUSES = ['RINGING', 'IN_PROGRESS', 'ENDED'];
|
|
88
|
+
const FLOW_TYPES = ['SCRIPTED', 'IMPROV', 'VOICEMAIL'];
|
|
89
|
+
const TRANSCRIPT_SOURCES = [
|
|
90
|
+
'ROARK_POST_CALL',
|
|
91
|
+
'SIMULATION_AGENT_REALTIME',
|
|
92
|
+
'CUSTOMER_AGENT_REALTIME',
|
|
93
|
+
];
|
|
94
|
+
class ToolInputError extends Error {
|
|
95
|
+
constructor(message) {
|
|
96
|
+
super(message);
|
|
97
|
+
this.name = 'ToolInputError';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const readAnnotations = (title) => ({
|
|
101
|
+
title,
|
|
102
|
+
readOnlyHint: true,
|
|
103
|
+
destructiveHint: false,
|
|
104
|
+
});
|
|
105
|
+
const writeAnnotations = (title) => ({
|
|
106
|
+
title,
|
|
107
|
+
readOnlyHint: false,
|
|
108
|
+
destructiveHint: true,
|
|
109
|
+
});
|
|
110
|
+
const projectProperty = {
|
|
111
|
+
type: 'string',
|
|
112
|
+
description: 'Roark project id. Required when this connection is not already pinned to one project. list_projects returns the ids this credential can use.',
|
|
113
|
+
};
|
|
114
|
+
const pagingProperties = {
|
|
115
|
+
after: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
description: 'Cursor from a previous response. Omit for the first page.',
|
|
118
|
+
},
|
|
119
|
+
limit: {
|
|
120
|
+
type: 'number',
|
|
121
|
+
description: 'Maximum number of rows to return.',
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
const objectSchema = (properties, required) => ({
|
|
125
|
+
type: 'object',
|
|
126
|
+
properties,
|
|
127
|
+
...(required && required.length > 0 ? { required } : {}),
|
|
128
|
+
});
|
|
129
|
+
const readMeta = (resource, httpPath) => ({
|
|
130
|
+
resource,
|
|
131
|
+
operation: 'read',
|
|
132
|
+
tags: [],
|
|
133
|
+
httpMethod: 'get',
|
|
134
|
+
httpPath,
|
|
135
|
+
});
|
|
136
|
+
const writeMeta = (resource, httpPath) => ({
|
|
137
|
+
resource,
|
|
138
|
+
operation: 'write',
|
|
139
|
+
tags: [],
|
|
140
|
+
httpMethod: 'post',
|
|
141
|
+
httpPath,
|
|
142
|
+
});
|
|
143
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
144
|
+
const requireString = (args, key) => {
|
|
145
|
+
const value = args[key];
|
|
146
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
147
|
+
throw new ToolInputError(`${key} is required and must be a non-empty string.`);
|
|
148
|
+
}
|
|
149
|
+
return value;
|
|
150
|
+
};
|
|
151
|
+
const optionalString = (args, key) => {
|
|
152
|
+
if (args[key] === undefined)
|
|
153
|
+
return undefined;
|
|
154
|
+
return requireString(args, key);
|
|
155
|
+
};
|
|
156
|
+
const optionalNumber = (args, key) => {
|
|
157
|
+
if (args[key] === undefined)
|
|
158
|
+
return undefined;
|
|
159
|
+
const value = args[key];
|
|
160
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
161
|
+
throw new ToolInputError(`${key} must be a number.`);
|
|
162
|
+
}
|
|
163
|
+
return value;
|
|
164
|
+
};
|
|
165
|
+
const optionalLimit = (args) => {
|
|
166
|
+
const value = optionalNumber(args, 'limit');
|
|
167
|
+
if (value === undefined)
|
|
168
|
+
return undefined;
|
|
169
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
170
|
+
throw new ToolInputError('limit must be an integer of at least 1.');
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
};
|
|
174
|
+
const requireEnum = (args, key, allowed) => {
|
|
175
|
+
const value = requireString(args, key);
|
|
176
|
+
if (!allowed.includes(value)) {
|
|
177
|
+
throw new ToolInputError(`${key} must be one of: ${allowed.join(', ')}.`);
|
|
178
|
+
}
|
|
179
|
+
return value;
|
|
180
|
+
};
|
|
181
|
+
const optionalEnum = (args, key, allowed) => {
|
|
182
|
+
if (args[key] === undefined)
|
|
183
|
+
return undefined;
|
|
184
|
+
return requireEnum(args, key, allowed);
|
|
185
|
+
};
|
|
186
|
+
const requireStringArray = (args, key) => {
|
|
187
|
+
const value = args[key];
|
|
188
|
+
if (!Array.isArray(value) ||
|
|
189
|
+
value.length === 0 ||
|
|
190
|
+
value.some((entry) => typeof entry !== 'string' || entry.length === 0)) {
|
|
191
|
+
throw new ToolInputError(`${key} is required and must be a non-empty array of strings.`);
|
|
192
|
+
}
|
|
193
|
+
return value;
|
|
194
|
+
};
|
|
195
|
+
const requireRecord = (args, key) => {
|
|
196
|
+
const value = args[key];
|
|
197
|
+
if (!isRecord(value)) {
|
|
198
|
+
throw new ToolInputError(`${key} is required and must be an object.`);
|
|
199
|
+
}
|
|
200
|
+
return value;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* The unpinned connector (`/mcp`) has no project, and customer-api rejects a call
|
|
204
|
+
* that names none. A pinned connector already set `reqContext.project` on the SDK
|
|
205
|
+
* client, so passing the same id again would be a second, conflicting scope.
|
|
206
|
+
*/
|
|
207
|
+
const scopedClient = (reqContext, args) => {
|
|
208
|
+
if (args['project'] !== undefined &&
|
|
209
|
+
(typeof args['project'] !== 'string' || args['project'].length === 0)) {
|
|
210
|
+
throw new ToolInputError('project must be a non-empty string.');
|
|
211
|
+
}
|
|
212
|
+
const project = typeof args['project'] === 'string' ? args['project'] : reqContext.project;
|
|
213
|
+
if (!project) {
|
|
214
|
+
throw new ToolInputError('This connection is not pinned to a project. Pass a project id. list_projects returns the projects this credential can use.');
|
|
215
|
+
}
|
|
216
|
+
if (project === reqContext.project)
|
|
217
|
+
return reqContext.client;
|
|
218
|
+
return reqContext.client.withOptions({ project });
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Drops keys whose value is undefined.
|
|
222
|
+
*
|
|
223
|
+
* `exactOptionalPropertyTypes` rejects `{ after: string | undefined }` for an
|
|
224
|
+
* SDK query type whose `after` is optional, so the returned type has to say
|
|
225
|
+
* the key is absent rather than present-and-undefined.
|
|
226
|
+
*/
|
|
227
|
+
const omitUndefined = (value) => {
|
|
228
|
+
const out = {};
|
|
229
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
230
|
+
if (entry !== undefined)
|
|
231
|
+
out[key] = entry;
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
};
|
|
235
|
+
const callApi = async (fn) => {
|
|
236
|
+
try {
|
|
237
|
+
return asTextContentResult(await fn());
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
if (error instanceof ToolInputError || error instanceof APIError || error instanceof Error) {
|
|
241
|
+
return asErrorResult(error.message);
|
|
242
|
+
}
|
|
243
|
+
return asErrorResult(String(error));
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const handler = (fn) => async ({ reqContext, args, }) => callApi(() => fn(reqContext, args ?? {}));
|
|
247
|
+
const defineTool = (name, title, description, inputSchema, annotations, metadata, fn) => ({
|
|
248
|
+
metadata,
|
|
249
|
+
tool: {
|
|
250
|
+
name,
|
|
251
|
+
title,
|
|
252
|
+
description,
|
|
253
|
+
inputSchema,
|
|
254
|
+
annotations,
|
|
255
|
+
},
|
|
256
|
+
handler: handler(fn),
|
|
257
|
+
});
|
|
258
|
+
const endpointSchema = {
|
|
259
|
+
type: 'object',
|
|
260
|
+
description: 'An agent endpoint Roark dials or connects to.',
|
|
261
|
+
properties: {
|
|
262
|
+
id: { type: 'string', description: 'Agent endpoint id from list_agent_endpoints.' },
|
|
263
|
+
},
|
|
264
|
+
required: ['id'],
|
|
265
|
+
};
|
|
266
|
+
const metricRefSchema = {
|
|
267
|
+
type: 'object',
|
|
268
|
+
description: 'A metric to score, by id or by slug.',
|
|
269
|
+
properties: {
|
|
270
|
+
id: { type: 'string', description: 'Metric definition id.' },
|
|
271
|
+
slug: { type: 'string', description: 'Metric slug, such as call_outcome.' },
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
const flowAttachmentSchema = {
|
|
275
|
+
type: 'object',
|
|
276
|
+
description: 'A customer flow to include. Set happyPath, edgeCases, or both. id comes from list_customer_flows.',
|
|
277
|
+
properties: {
|
|
278
|
+
id: { type: 'string', description: 'Customer flow id.' },
|
|
279
|
+
slug: { type: 'string', description: 'Roark-curated flow slug, used instead of id.' },
|
|
280
|
+
happyPath: { type: 'boolean', description: 'Whether to run the flow happy path.' },
|
|
281
|
+
edgeCases: {
|
|
282
|
+
description: 'ALL runs every edge case. An array runs only the named edge case ids.',
|
|
283
|
+
},
|
|
284
|
+
personaOverrideId: { type: 'string', description: 'Persona id to use for this attachment.' },
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
const buildRunBody = (args) => {
|
|
288
|
+
const hasPlanId = args['planId'] !== undefined;
|
|
289
|
+
const hasPlan = args['plan'] !== undefined;
|
|
290
|
+
const hasTemplate = args['template'] !== undefined;
|
|
291
|
+
const selected = [hasPlanId, hasPlan, hasTemplate].filter(Boolean).length;
|
|
292
|
+
if (selected !== 1) {
|
|
293
|
+
throw new ToolInputError('Pass exactly one of planId, plan, or template.');
|
|
294
|
+
}
|
|
295
|
+
if (args['saveAsPlan'] !== undefined && typeof args['saveAsPlan'] !== 'boolean') {
|
|
296
|
+
throw new ToolInputError('saveAsPlan must be a boolean.');
|
|
297
|
+
}
|
|
298
|
+
if (args['saveAsPlan'] !== undefined && !hasPlan) {
|
|
299
|
+
throw new ToolInputError('saveAsPlan applies only when plan is set.');
|
|
300
|
+
}
|
|
301
|
+
if (args['variables'] !== undefined && !isRecord(args['variables']) && !Array.isArray(args['variables'])) {
|
|
302
|
+
throw new ToolInputError('variables must be an object or an array.');
|
|
303
|
+
}
|
|
304
|
+
if (hasPlanId) {
|
|
305
|
+
const planId = requireString(args, 'planId');
|
|
306
|
+
const body = { planId };
|
|
307
|
+
if (args['variables'] !== undefined) {
|
|
308
|
+
body.variables = args['variables'];
|
|
309
|
+
}
|
|
310
|
+
return body;
|
|
311
|
+
}
|
|
312
|
+
if (hasTemplate) {
|
|
313
|
+
const template = requireString(args, 'template');
|
|
314
|
+
if (!Array.isArray(args['agentEndpoints']) || args['agentEndpoints'].length === 0) {
|
|
315
|
+
throw new ToolInputError('A template run requires agentEndpoints, a non-empty array of { id }.');
|
|
316
|
+
}
|
|
317
|
+
const direction = requireEnum(args, 'direction', DIRECTIONS);
|
|
318
|
+
const body = {
|
|
319
|
+
template,
|
|
320
|
+
direction,
|
|
321
|
+
agentEndpoints: args['agentEndpoints'],
|
|
322
|
+
};
|
|
323
|
+
if (args['flows'] !== undefined) {
|
|
324
|
+
if (!Array.isArray(args['flows']))
|
|
325
|
+
throw new ToolInputError('flows must be an array.');
|
|
326
|
+
body.flows = args['flows'];
|
|
327
|
+
}
|
|
328
|
+
const iterationCount = optionalNumber(args, 'iterationCount');
|
|
329
|
+
if (iterationCount !== undefined)
|
|
330
|
+
body.iterationCount = iterationCount;
|
|
331
|
+
const maxSimulationDurationSeconds = optionalNumber(args, 'maxSimulationDurationSeconds');
|
|
332
|
+
if (maxSimulationDurationSeconds !== undefined)
|
|
333
|
+
body.maxSimulationDurationSeconds = maxSimulationDurationSeconds;
|
|
334
|
+
return body;
|
|
335
|
+
}
|
|
336
|
+
const plan = requireRecord(args, 'plan');
|
|
337
|
+
if (!Array.isArray(plan['agentEndpoints']) || plan['agentEndpoints'].length === 0) {
|
|
338
|
+
throw new ToolInputError('plan.agentEndpoints must be a non-empty array of { id }.');
|
|
339
|
+
}
|
|
340
|
+
if (plan['direction'] !== 'INBOUND' && plan['direction'] !== 'OUTBOUND') {
|
|
341
|
+
throw new ToolInputError('plan.direction must be INBOUND or OUTBOUND.');
|
|
342
|
+
}
|
|
343
|
+
if (typeof plan['maxSimulationDurationSeconds'] !== 'number' ||
|
|
344
|
+
!Number.isFinite(plan['maxSimulationDurationSeconds'])) {
|
|
345
|
+
throw new ToolInputError('plan.maxSimulationDurationSeconds must be a number of seconds.');
|
|
346
|
+
}
|
|
347
|
+
const body = {
|
|
348
|
+
plan: plan,
|
|
349
|
+
};
|
|
350
|
+
if (typeof args['saveAsPlan'] === 'boolean')
|
|
351
|
+
body.saveAsPlan = args['saveAsPlan'];
|
|
352
|
+
if (args['variables'] !== undefined) {
|
|
353
|
+
body.variables = args['variables'];
|
|
354
|
+
}
|
|
355
|
+
return body;
|
|
356
|
+
};
|
|
357
|
+
const buildPersonaBody = (args) => {
|
|
358
|
+
const body = {
|
|
359
|
+
name: requireString(args, 'name'),
|
|
360
|
+
language: requireEnum(args, 'language', LANGUAGES),
|
|
361
|
+
accent: requireEnum(args, 'accent', ACCENTS),
|
|
362
|
+
gender: requireEnum(args, 'gender', GENDERS),
|
|
363
|
+
};
|
|
364
|
+
const age = optionalEnum(args, 'age', AGES);
|
|
365
|
+
if (age)
|
|
366
|
+
body.age = age;
|
|
367
|
+
const baseEmotion = optionalEnum(args, 'baseEmotion', EMOTIONS);
|
|
368
|
+
if (baseEmotion)
|
|
369
|
+
body.baseEmotion = baseEmotion;
|
|
370
|
+
const backstoryPrompt = optionalString(args, 'backstoryPrompt');
|
|
371
|
+
if (backstoryPrompt)
|
|
372
|
+
body.backstoryPrompt = backstoryPrompt;
|
|
373
|
+
const description = optionalString(args, 'description');
|
|
374
|
+
if (description)
|
|
375
|
+
body.description = description;
|
|
376
|
+
return body;
|
|
377
|
+
};
|
|
378
|
+
const buildFlowBody = (args) => {
|
|
379
|
+
const happyPath = requireRecord(args, 'happyPath');
|
|
380
|
+
const prompt = optionalString(happyPath, 'prompt');
|
|
381
|
+
const description = optionalString(args, 'description');
|
|
382
|
+
const body = {
|
|
383
|
+
type: 'IMPROV',
|
|
384
|
+
title: requireString(args, 'title'),
|
|
385
|
+
agentIds: requireStringArray(args, 'agentIds'),
|
|
386
|
+
happyPath: {
|
|
387
|
+
title: requireString(happyPath, 'title'),
|
|
388
|
+
personaOverrideId: requireString(happyPath, 'personaOverrideId'),
|
|
389
|
+
environmentId: requireString(happyPath, 'environmentId'),
|
|
390
|
+
...(prompt ? { prompt } : {}),
|
|
391
|
+
},
|
|
392
|
+
...(description ? { description } : {}),
|
|
393
|
+
};
|
|
394
|
+
if (args['edgeCases'] !== undefined) {
|
|
395
|
+
if (!Array.isArray(args['edgeCases'])) {
|
|
396
|
+
throw new ToolInputError('edgeCases must be an array.');
|
|
397
|
+
}
|
|
398
|
+
body.edgeCases = args['edgeCases'].map((entry, index) => {
|
|
399
|
+
if (!isRecord(entry))
|
|
400
|
+
throw new ToolInputError(`edgeCases[${index}] must be an object.`);
|
|
401
|
+
const edgePrompt = optionalString(entry, 'prompt');
|
|
402
|
+
const personaOverrideId = optionalString(entry, 'personaOverrideId');
|
|
403
|
+
const environmentId = optionalString(entry, 'environmentId');
|
|
404
|
+
return {
|
|
405
|
+
title: requireString(entry, 'title'),
|
|
406
|
+
...(edgePrompt ? { prompt: edgePrompt } : {}),
|
|
407
|
+
...(personaOverrideId ? { personaOverrideId } : {}),
|
|
408
|
+
...(environmentId ? { environmentId } : {}),
|
|
409
|
+
};
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
return body;
|
|
413
|
+
};
|
|
414
|
+
const buildEndpointBody = (args) => {
|
|
415
|
+
const outboundDialType = optionalEnum(args, 'outboundDialType', ['NONE', 'HTTP_REQUEST']);
|
|
416
|
+
const outboundDialHttpRequestDefinitionId = optionalString(args, 'outboundDialHttpRequestDefinitionId');
|
|
417
|
+
if (outboundDialType === 'HTTP_REQUEST' && !outboundDialHttpRequestDefinitionId) {
|
|
418
|
+
throw new ToolInputError('outboundDialHttpRequestDefinitionId is required when outboundDialType is HTTP_REQUEST.');
|
|
419
|
+
}
|
|
420
|
+
const value = requireString(args, 'value');
|
|
421
|
+
if (!value.startsWith('+')) {
|
|
422
|
+
throw new ToolInputError('value must be a phone number in E.164 format, starting with +.');
|
|
423
|
+
}
|
|
424
|
+
const environment = optionalString(args, 'environment');
|
|
425
|
+
return {
|
|
426
|
+
agentId: requireString(args, 'agentId'),
|
|
427
|
+
direction: requireEnum(args, 'direction', ENDPOINT_DIRECTIONS),
|
|
428
|
+
value,
|
|
429
|
+
...(environment ? { environment } : {}),
|
|
430
|
+
...(outboundDialType ? { outboundDialType } : {}),
|
|
431
|
+
...(outboundDialHttpRequestDefinitionId ? { outboundDialHttpRequestDefinitionId } : {}),
|
|
432
|
+
};
|
|
433
|
+
};
|
|
434
|
+
/**
|
|
435
|
+
* Tools exposed on the directory surface. Order is the core loop: find the
|
|
436
|
+
* project, read what is already there, then the writes that change it.
|
|
437
|
+
*/
|
|
438
|
+
export function directoryTools() {
|
|
439
|
+
return [
|
|
440
|
+
defineTool('list_projects', 'List projects', 'Lists the Roark projects this credential can use, with the permissions it holds in each. Returns each project id, name, slug, and organization.', objectSchema({}), readAnnotations('List projects'), readMeta('project', '/v1/projects'), async (reqContext) => reqContext.client.project.list()),
|
|
441
|
+
defineTool('list_agents', 'List agents', 'Lists voice and chat agents in a Roark project. Returns each agent id, name, custom id, and description.', objectSchema({
|
|
442
|
+
project: projectProperty,
|
|
443
|
+
searchText: { type: 'string', description: 'Match against agent name or description.' },
|
|
444
|
+
...pagingProperties,
|
|
445
|
+
}), readAnnotations('List agents'), readMeta('agent', '/v1/agent'), async (reqContext, args) => scopedClient(reqContext, args).agent.list(omitUndefined({
|
|
446
|
+
searchText: optionalString(args, 'searchText'),
|
|
447
|
+
after: optionalString(args, 'after'),
|
|
448
|
+
limit: optionalLimit(args),
|
|
449
|
+
}))),
|
|
450
|
+
defineTool('list_agent_endpoints', 'List agent endpoints', 'Lists the endpoints Roark uses to reach agents in a project. Returns each endpoint id, agent id, direction, type, and address. A simulation runs against an endpoint id.', objectSchema({
|
|
451
|
+
project: projectProperty,
|
|
452
|
+
agentId: { type: 'string', description: 'Return endpoints for this agent only.' },
|
|
453
|
+
searchText: { type: 'string', description: 'Match against endpoint address or environment name.' },
|
|
454
|
+
...pagingProperties,
|
|
455
|
+
}), readAnnotations('List agent endpoints'), readMeta('agentEndpoint', '/v1/agent/endpoint'), async (reqContext, args) => scopedClient(reqContext, args).agentEndpoint.list(omitUndefined({
|
|
456
|
+
agentId: optionalString(args, 'agentId'),
|
|
457
|
+
searchText: optionalString(args, 'searchText'),
|
|
458
|
+
after: optionalString(args, 'after'),
|
|
459
|
+
limit: optionalLimit(args),
|
|
460
|
+
}))),
|
|
461
|
+
defineTool('list_calls', 'List calls', 'Lists calls in a Roark project, including simulated and live calls. Returns call ids, status, direction, and timestamps. Does not include transcripts.', objectSchema({
|
|
462
|
+
project: projectProperty,
|
|
463
|
+
searchText: { type: 'string', description: 'Match against call title, summary, or transcript text.' },
|
|
464
|
+
status: { type: 'string', enum: [...CALL_STATUSES], description: 'Call status filter.' },
|
|
465
|
+
simulationRunPlanJobId: {
|
|
466
|
+
type: 'string',
|
|
467
|
+
description: 'Return calls from this simulation run only.',
|
|
468
|
+
},
|
|
469
|
+
...pagingProperties,
|
|
470
|
+
}), readAnnotations('List calls'), readMeta('call', '/v1/call'), async (reqContext, args) => scopedClient(reqContext, args).call.list(omitUndefined({
|
|
471
|
+
searchText: optionalString(args, 'searchText'),
|
|
472
|
+
status: optionalEnum(args, 'status', CALL_STATUSES),
|
|
473
|
+
simulationRunPlanJobId: optionalString(args, 'simulationRunPlanJobId'),
|
|
474
|
+
after: optionalString(args, 'after'),
|
|
475
|
+
limit: optionalLimit(args),
|
|
476
|
+
}))),
|
|
477
|
+
defineTool('get_call', 'Get call', 'Returns one call in a Roark project by its id, including status, direction, timestamps, and summary. Does not include the transcript.', objectSchema({
|
|
478
|
+
project: projectProperty,
|
|
479
|
+
callId: { type: 'string', description: 'Call id.' },
|
|
480
|
+
}, ['callId']), readAnnotations('Get call'), readMeta('call', '/v1/call/{callId}'), async (reqContext, args) => scopedClient(reqContext, args).call.getByID(requireString(args, 'callId'))),
|
|
481
|
+
defineTool('get_call_transcript', 'Get call transcript', 'Returns the transcript of one call in a Roark project: participants and the text of each turn, with timing.', objectSchema({
|
|
482
|
+
project: projectProperty,
|
|
483
|
+
callId: { type: 'string', description: 'Call id.' },
|
|
484
|
+
source: {
|
|
485
|
+
type: 'string',
|
|
486
|
+
enum: [...TRANSCRIPT_SOURCES],
|
|
487
|
+
description: 'Transcription source. When omitted, the API uses the best source it has for the call.',
|
|
488
|
+
},
|
|
489
|
+
}, ['callId']), readAnnotations('Get call transcript'), readMeta('call', '/v1/call/{callId}/transcript'), async (reqContext, args) => scopedClient(reqContext, args).call.getTranscript(requireString(args, 'callId'), omitUndefined({ source: optionalEnum(args, 'source', TRANSCRIPT_SOURCES) }))),
|
|
490
|
+
defineTool('list_call_metrics', 'List call metrics', 'Returns the metric scores recorded for one call in a Roark project. status success returns computed scores only. status all also returns rows that have no value. flatten true returns one row per score; when omitted, scores are grouped by metric.', objectSchema({
|
|
491
|
+
project: projectProperty,
|
|
492
|
+
callId: { type: 'string', description: 'Call id.' },
|
|
493
|
+
flatten: {
|
|
494
|
+
type: 'string',
|
|
495
|
+
description: 'true returns one row per score. Any other value, or omitting it, groups scores by metric.',
|
|
496
|
+
},
|
|
497
|
+
status: {
|
|
498
|
+
type: 'string',
|
|
499
|
+
enum: ['success', 'all'],
|
|
500
|
+
description: 'success returns computed scores. all also returns rows with no value.',
|
|
501
|
+
},
|
|
502
|
+
}, ['callId']), readAnnotations('List call metrics'), readMeta('call', '/v1/call/{callId}/metrics'), async (reqContext, args) => scopedClient(reqContext, args).call.listMetrics(requireString(args, 'callId'), omitUndefined({
|
|
503
|
+
flatten: optionalString(args, 'flatten'),
|
|
504
|
+
status: optionalEnum(args, 'status', ['success', 'all']),
|
|
505
|
+
}))),
|
|
506
|
+
defineTool('list_metric_definitions', 'List metric definitions', 'Lists the metrics a Roark project can score, including built-in and custom metrics. Returns each metric id, slug, name, and type.', objectSchema({ project: projectProperty, ...pagingProperties }), readAnnotations('List metric definitions'), readMeta('metric', '/v1/metric/definitions'), async (reqContext, args) => scopedClient(reqContext, args).metric.listDefinitions(omitUndefined({ after: optionalString(args, 'after'), limit: optionalLimit(args) }))),
|
|
507
|
+
defineTool('get_simulation_results', 'Get simulation results', 'Returns the results of one simulation run in a Roark project: run status, score, pass or fail verdict, per-metric aggregates, and the checks on each simulated call. Counts include calls that have finished so far. The verdict is absent until the run ends.', objectSchema({
|
|
508
|
+
project: projectProperty,
|
|
509
|
+
jobId: {
|
|
510
|
+
type: 'string',
|
|
511
|
+
description: 'Simulation run id (simulationRunPlanJobId), returned by run_simulation.',
|
|
512
|
+
},
|
|
513
|
+
compareToJobId: {
|
|
514
|
+
type: 'string',
|
|
515
|
+
description: 'Another simulation run id to compare against. When omitted, the API compares with the previous settled run of the same plan.',
|
|
516
|
+
},
|
|
517
|
+
}, ['jobId']), readAnnotations('Get simulation results'), readMeta('simulationRunPlanJob', '/v1/simulation/plan/job/{jobId}/results'), async (reqContext, args) => scopedClient(reqContext, args).simulationRunPlanJob.getResults(requireString(args, 'jobId'), omitUndefined({ compareToJobId: optionalString(args, 'compareToJobId') }))),
|
|
518
|
+
defineTool('list_personas', 'List personas', 'Lists simulated caller personas in a Roark project. Returns each persona id, name, language, accent, and gender.', objectSchema({
|
|
519
|
+
project: projectProperty,
|
|
520
|
+
searchText: { type: 'string', description: 'Match against persona name or description.' },
|
|
521
|
+
...pagingProperties,
|
|
522
|
+
}), readAnnotations('List personas'), readMeta('simulationPersona', '/v1/persona'), async (reqContext, args) => scopedClient(reqContext, args).simulationPersona.list(omitUndefined({
|
|
523
|
+
searchText: optionalString(args, 'searchText'),
|
|
524
|
+
after: optionalString(args, 'after'),
|
|
525
|
+
limit: optionalLimit(args),
|
|
526
|
+
}))),
|
|
527
|
+
defineTool('list_customer_flows', 'List customer flows', 'Lists customer flows in a Roark project. A customer flow is the conversation scenario a simulated caller follows. Returns each flow id, title, and type. The step graph of a scripted flow is not included.', objectSchema({
|
|
528
|
+
project: projectProperty,
|
|
529
|
+
searchText: { type: 'string', description: 'Match against flow title.' },
|
|
530
|
+
type: { type: 'string', enum: [...FLOW_TYPES], description: 'Flow type filter.' },
|
|
531
|
+
includeSystem: {
|
|
532
|
+
type: 'string',
|
|
533
|
+
enum: ['true', 'false'],
|
|
534
|
+
description: 'true includes flows Roark provides. false returns only flows in the project.',
|
|
535
|
+
},
|
|
536
|
+
...pagingProperties,
|
|
537
|
+
}), readAnnotations('List customer flows'), readMeta('customerFlow', '/v1/customer-flow'), async (reqContext, args) => scopedClient(reqContext, args).customerFlow.list(omitUndefined({
|
|
538
|
+
searchText: optionalString(args, 'searchText'),
|
|
539
|
+
type: optionalEnum(args, 'type', FLOW_TYPES),
|
|
540
|
+
includeSystem: optionalEnum(args, 'includeSystem', ['true', 'false']),
|
|
541
|
+
after: optionalString(args, 'after'),
|
|
542
|
+
limit: optionalLimit(args),
|
|
543
|
+
}))),
|
|
544
|
+
defineTool('list_simulation_environments', 'List simulation environments', 'Lists call environments in a Roark project, including environments Roark provides. Returns each environment id, name, and background noise. Creating a customer flow requires an environment id.', objectSchema({ project: projectProperty, ...pagingProperties }), readAnnotations('List simulation environments'), readMeta('simulationEnvironment', '/v1/simulation/environment'), async (reqContext, args) => scopedClient(reqContext, args).simulationEnvironment.list(omitUndefined({ after: optionalString(args, 'after'), limit: optionalLimit(args) }))),
|
|
545
|
+
defineTool('create_agent', 'Create agent', 'Creates an agent in a Roark project. Requires a name. Returns the created agent, including its id. Does not create an endpoint.', objectSchema({
|
|
546
|
+
project: projectProperty,
|
|
547
|
+
name: { type: 'string', description: 'Agent name.' },
|
|
548
|
+
description: { type: 'string', description: 'What the agent does.' },
|
|
549
|
+
customId: { type: 'string', description: 'Caller-chosen id, unique in the project.' },
|
|
550
|
+
}, ['name']), writeAnnotations('Create agent'), writeMeta('agent', '/v1/agent'), async (reqContext, args) => scopedClient(reqContext, args).agent.create({
|
|
551
|
+
name: requireString(args, 'name'),
|
|
552
|
+
...omitUndefined({
|
|
553
|
+
description: optionalString(args, 'description'),
|
|
554
|
+
customId: optionalString(args, 'customId'),
|
|
555
|
+
}),
|
|
556
|
+
})),
|
|
557
|
+
defineTool('create_agent_endpoint', 'Create agent endpoint', 'Creates a phone endpoint for an agent in a Roark project. Roark places simulated calls to this number. Requires an agent id, an E.164 phone number, and a direction of INCOMING, OUTGOING, or INCOMING_AND_OUTGOING. Phone is the only endpoint type this tool creates.', objectSchema({
|
|
558
|
+
project: projectProperty,
|
|
559
|
+
agentId: { type: 'string', description: 'Agent id from list_agents or create_agent.' },
|
|
560
|
+
value: { type: 'string', description: 'Phone number in E.164 format, starting with +.' },
|
|
561
|
+
direction: {
|
|
562
|
+
type: 'string',
|
|
563
|
+
enum: [...ENDPOINT_DIRECTIONS],
|
|
564
|
+
description: 'Which way calls can travel on this number.',
|
|
565
|
+
},
|
|
566
|
+
environment: { type: 'string', description: 'Environment name. The API default is production.' },
|
|
567
|
+
outboundDialType: {
|
|
568
|
+
type: 'string',
|
|
569
|
+
enum: ['NONE', 'HTTP_REQUEST'],
|
|
570
|
+
description: 'How Roark places an outbound call. The API default is NONE.',
|
|
571
|
+
},
|
|
572
|
+
outboundDialHttpRequestDefinitionId: {
|
|
573
|
+
type: 'string',
|
|
574
|
+
description: 'HTTP request definition id. Required when outboundDialType is HTTP_REQUEST.',
|
|
575
|
+
},
|
|
576
|
+
}, ['agentId', 'value', 'direction']), writeAnnotations('Create agent endpoint'), writeMeta('agentEndpoint', '/v1/agent/endpoint'), async (reqContext, args) => scopedClient(reqContext, args).agentEndpoint.create(buildEndpointBody(args))),
|
|
577
|
+
defineTool('create_persona', 'Create persona', 'Creates a simulated caller persona in a Roark project. Requires a name, language, accent, and gender. Returns the created persona, including its id.', objectSchema({
|
|
578
|
+
project: projectProperty,
|
|
579
|
+
name: { type: 'string', description: 'Name the caller uses in conversation.' },
|
|
580
|
+
language: { type: 'string', enum: [...LANGUAGES], description: 'Primary spoken language.' },
|
|
581
|
+
accent: { type: 'string', enum: [...ACCENTS], description: 'Accent the persona is voiced with.' },
|
|
582
|
+
gender: { type: 'string', enum: [...GENDERS], description: 'Gender the persona is voiced with.' },
|
|
583
|
+
age: {
|
|
584
|
+
type: 'string',
|
|
585
|
+
enum: [...AGES],
|
|
586
|
+
description: 'How old the caller sounds. The API default is ADULT.',
|
|
587
|
+
},
|
|
588
|
+
baseEmotion: {
|
|
589
|
+
type: 'string',
|
|
590
|
+
enum: [...EMOTIONS],
|
|
591
|
+
description: 'Emotional baseline. The API default is NEUTRAL.',
|
|
592
|
+
},
|
|
593
|
+
backstoryPrompt: {
|
|
594
|
+
type: 'string',
|
|
595
|
+
description: 'Background and behavior the simulated caller plays.',
|
|
596
|
+
},
|
|
597
|
+
description: { type: 'string', description: 'Short label for the persona.' },
|
|
598
|
+
}, ['name', 'language', 'accent', 'gender']), writeAnnotations('Create persona'), writeMeta('simulationPersona', '/v1/persona'), async (reqContext, args) => scopedClient(reqContext, args).simulationPersona.create(buildPersonaBody(args))),
|
|
599
|
+
defineTool('create_customer_flow', 'Create customer flow', 'Creates an improv customer flow in a Roark project. A customer flow is the conversation scenario a simulated caller follows. Requires a title, at least one agent id, and a happy path with a title, a persona id, and an environment id. Returns the created flow, including its id. Scripted step graphs are not created by this tool.', objectSchema({
|
|
600
|
+
project: projectProperty,
|
|
601
|
+
title: { type: 'string', description: 'Flow title.' },
|
|
602
|
+
description: { type: 'string', description: 'What the conversation covers.' },
|
|
603
|
+
agentIds: {
|
|
604
|
+
type: 'array',
|
|
605
|
+
items: { type: 'string' },
|
|
606
|
+
description: 'Agents this flow exercises. At least one.',
|
|
607
|
+
},
|
|
608
|
+
happyPath: {
|
|
609
|
+
type: 'object',
|
|
610
|
+
description: 'The way the conversation is meant to go.',
|
|
611
|
+
properties: {
|
|
612
|
+
title: { type: 'string', description: 'Happy path title.' },
|
|
613
|
+
prompt: { type: 'string', description: 'What the simulated caller is trying to do.' },
|
|
614
|
+
personaOverrideId: {
|
|
615
|
+
type: 'string',
|
|
616
|
+
description: 'Persona id from list_personas or create_persona.',
|
|
617
|
+
},
|
|
618
|
+
environmentId: {
|
|
619
|
+
type: 'string',
|
|
620
|
+
description: 'Environment id from list_simulation_environments.',
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
required: ['title', 'personaOverrideId', 'environmentId'],
|
|
624
|
+
},
|
|
625
|
+
edgeCases: {
|
|
626
|
+
type: 'array',
|
|
627
|
+
description: 'Other ways of running the flow. Each inherits the happy path persona and environment when those are omitted.',
|
|
628
|
+
items: {
|
|
629
|
+
type: 'object',
|
|
630
|
+
properties: {
|
|
631
|
+
title: { type: 'string' },
|
|
632
|
+
prompt: { type: 'string' },
|
|
633
|
+
personaOverrideId: { type: 'string' },
|
|
634
|
+
environmentId: { type: 'string' },
|
|
635
|
+
},
|
|
636
|
+
required: ['title'],
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
}, ['title', 'agentIds', 'happyPath']), writeAnnotations('Create customer flow'), writeMeta('customerFlow', '/v1/customer-flow'), async (reqContext, args) => scopedClient(reqContext, args).customerFlow.create(buildFlowBody(args))),
|
|
640
|
+
defineTool('run_simulation', 'Run simulation', 'Starts a simulation run in a Roark project and places simulated calls. Pass exactly one of planId (an existing plan), plan (an inline plan), or template (a built-in template name, with agentEndpoints and direction). Returns the run id and how many calls the run places. This spends simulation usage on the account.', objectSchema({
|
|
641
|
+
project: projectProperty,
|
|
642
|
+
planId: { type: 'string', description: 'Id of a saved simulation plan to run.' },
|
|
643
|
+
plan: {
|
|
644
|
+
type: 'object',
|
|
645
|
+
description: 'Inline plan to run once. Requires agentEndpoints, direction, and maxSimulationDurationSeconds.',
|
|
646
|
+
properties: {
|
|
647
|
+
name: { type: 'string', description: 'Plan name. Required when saveAsPlan is true.' },
|
|
648
|
+
description: { type: 'string' },
|
|
649
|
+
direction: { type: 'string', enum: [...DIRECTIONS] },
|
|
650
|
+
agentEndpoints: { type: 'array', items: endpointSchema },
|
|
651
|
+
metrics: {
|
|
652
|
+
type: 'array',
|
|
653
|
+
items: metricRefSchema,
|
|
654
|
+
description: 'Metrics to score, by id or slug.',
|
|
655
|
+
},
|
|
656
|
+
flows: { type: 'array', items: flowAttachmentSchema },
|
|
657
|
+
maxSimulationDurationSeconds: { type: 'number', description: 'Per-call cap, in seconds.' },
|
|
658
|
+
iterationCount: {
|
|
659
|
+
type: 'number',
|
|
660
|
+
description: 'Repeats of each test case. The API default is 1.',
|
|
661
|
+
},
|
|
662
|
+
maxConcurrentJobs: {
|
|
663
|
+
type: 'number',
|
|
664
|
+
description: 'How many calls run at once. The API default is 5.',
|
|
665
|
+
},
|
|
666
|
+
executionMode: {
|
|
667
|
+
type: 'string',
|
|
668
|
+
enum: ['PARALLEL', 'SEQUENTIAL_SAME_RUN_PLAN', 'SEQUENTIAL_PROJECT'],
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
required: ['agentEndpoints', 'direction', 'maxSimulationDurationSeconds'],
|
|
672
|
+
},
|
|
673
|
+
template: {
|
|
674
|
+
type: 'string',
|
|
675
|
+
description: 'Built-in template name, as listed by the simulation template API.',
|
|
676
|
+
},
|
|
677
|
+
agentEndpoints: {
|
|
678
|
+
type: 'array',
|
|
679
|
+
items: endpointSchema,
|
|
680
|
+
description: 'Required with template. The endpoints the template calls.',
|
|
681
|
+
},
|
|
682
|
+
direction: { type: 'string', enum: [...DIRECTIONS], description: 'Required with template.' },
|
|
683
|
+
flows: {
|
|
684
|
+
type: 'array',
|
|
685
|
+
items: flowAttachmentSchema,
|
|
686
|
+
description: 'Flows for a template that does not supply its own.',
|
|
687
|
+
},
|
|
688
|
+
iterationCount: { type: 'number' },
|
|
689
|
+
maxSimulationDurationSeconds: { type: 'number' },
|
|
690
|
+
saveAsPlan: {
|
|
691
|
+
type: 'boolean',
|
|
692
|
+
description: 'When plan is set, true keeps the plan so it can be run again by planId. Requires plan.name.',
|
|
693
|
+
},
|
|
694
|
+
variables: {
|
|
695
|
+
description: 'Placeholder values for the run. An object applies to the whole run. An array applies per flow.',
|
|
696
|
+
},
|
|
697
|
+
}), writeAnnotations('Run simulation'), writeMeta('simulation', '/v1/simulation/run'), async (reqContext, args) => scopedClient(reqContext, args).simulation.run(buildRunBody(args))),
|
|
698
|
+
];
|
|
699
|
+
}
|
|
700
|
+
//# sourceMappingURL=directory-tools.mjs.map
|