@latimer-woods-tech/wordis-bond 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Latimer Woods Tech
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # @latimer-woods-tech/wordis-bond
2
+
3
+ Typed TypeScript client for the **Word Is Bond** voice-agent testing API
4
+ (`https://api.wordis-bond.com`). Point a synthetic caller at a voice agent **you run**
5
+ and get back a scored transcript, a pass/fail verdict, and per-turn latency /
6
+ word-error-rate / barge-in metrics.
7
+
8
+ - Zero runtime dependencies — uses the platform global `fetch` (Node ≥ 18, Workers, Deno, the browser).
9
+ - Authenticates with your `wb_live_…` API key.
10
+ - Unwraps the `{ data, error }` envelope and throws `WordIsBondError` on a non-2xx response.
11
+ - Fully typed against the live [OpenAPI 3.1 spec](https://api.wordis-bond.com/openapi.json).
12
+
13
+ > **You only ever test systems you control.** This client wraps the testing surface —
14
+ > there is no outbound-to-strangers capability.
15
+
16
+ ## Install
17
+
18
+ ```sh
19
+ npm install @latimer-woods-tech/wordis-bond
20
+ ```
21
+
22
+ ## Get a key
23
+
24
+ Sign up at [studio.wordis-bond.com](https://studio.wordis-bond.com) (the free Starter
25
+ plan needs no card), then create a key — the raw token is shown once:
26
+
27
+ ```ts
28
+ // with a signed-in session, or via the Studio UI:
29
+ const { token } = await wb.createApiKey('my agent');
30
+ ```
31
+
32
+ Plans and prices are served live (never hard-coded) — read them with `wb.getPlans()` or
33
+ see [`GET /api/billing/plans`](https://api.wordis-bond.com/api/billing/plans).
34
+
35
+ ## Quickstart
36
+
37
+ ```ts
38
+ import { WordIsBond } from '@latimer-woods-tech/wordis-bond';
39
+
40
+ const wb = new WordIsBond({ apiKey: process.env.WB_API_KEY });
41
+
42
+ // 1. First scored result in ~60s — no target of your own needed:
43
+ const demo = await wb.runDemo();
44
+ console.log(demo.score, demo.passed, demo.reportUrl);
45
+
46
+ // 2. Score a captured transcript against a goal:
47
+ const run = await wb.runTest({
48
+ transcript: 'Caller: I need a cleaning. Agent: Sure — Tuesday at 10 works, $89.',
49
+ scenarioGoal: 'Book a cleaning and quote the price.',
50
+ });
51
+ console.log(run); // completed, scored TestRun
52
+
53
+ // 3. Drive a live synthetic call against an agent you run (direct = SIP/WebRTC):
54
+ const live = await wb.runTest({
55
+ targetAgent: { transport: 'webrtc' },
56
+ goal: 'Ask for opening hours and confirm the address.',
57
+ persona: { description: 'a first-time caller in a hurry' },
58
+ });
59
+ // A direct run hands back a tokenized mediaWebSocketUrl per run to dial from your harness.
60
+
61
+ // 4. Watch for drift:
62
+ const trends = await wb.getTrends({ days: 30 });
63
+ console.log(trends.totals.passRate, trends.totals.regressions);
64
+ ```
65
+
66
+ ### Suites
67
+
68
+ ```ts
69
+ const suite = await wb.createSuite({
70
+ name: 'Front desk — booking',
71
+ targetAgent: { transport: 'webrtc' },
72
+ scenarios: [{ persona: 'a new patient', goal: 'Book a cleaning and get the price.' }],
73
+ schedule: 'daily', // plan-gated
74
+ });
75
+ const series = await wb.getSuiteTrends(suite.id);
76
+ ```
77
+
78
+ ### Error handling
79
+
80
+ ```ts
81
+ import { WordIsBondError } from '@latimer-woods-tech/wordis-bond';
82
+
83
+ try {
84
+ await wb.runTest({ targetAgent: { transport: 'pstn', toNumber: '+15551234567' }, goal: 'x' });
85
+ } catch (err) {
86
+ if (err instanceof WordIsBondError) {
87
+ console.error(err.status, err.code, err.message); // e.g. 402 — PSTN needs a paid plan
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## API surface
93
+
94
+ | Method | Endpoint |
95
+ |---|---|
96
+ | `health()` | `GET /health` |
97
+ | `getPlans()` | `GET /api/billing/plans` (no key) |
98
+ | `getSubscription()` | `GET /api/billing/subscription` |
99
+ | `listApiKeys()` / `createApiKey(name)` / `revokeApiKey(id)` | `…/api/api-keys` |
100
+ | `runDemo(input?)` | `POST /api/tests/demo` |
101
+ | `runTest(input)` | `POST /api/tests/run` |
102
+ | `rescoreRun(id, input?)` | `POST /api/tests/{id}/score` |
103
+ | `runCanary()` | `POST /api/tests/canary` |
104
+ | `getRun(id)` / `listRuns()` | `GET /api/tests/{id}` · `GET /api/tests` |
105
+ | `listSuites()` / `createSuite(input)` / `getSuite(id)` | `…/api/tests/suites` |
106
+ | `getSuiteTrends(id)` / `getTrends({days})` | `…/trends` |
107
+ | `getReportLink(id)` | `GET /api/reports/{id}/link` |
108
+
109
+ ## Also available: Remote MCP server
110
+
111
+ Agents can use the same capability over the Model Context Protocol without this SDK:
112
+ `https://api.wordis-bond.com/mcp` (Streamable HTTP). See
113
+ [`/llms.txt`](https://wordis-bond.com/llms.txt).
114
+
115
+ ## Develop
116
+
117
+ ```sh
118
+ npm install
119
+ npm run typecheck
120
+ npm test
121
+ npm run build # → dist/ (ESM + .d.ts)
122
+ ```
123
+
124
+ ## Release
125
+
126
+ Publication is automated. Push a tag `sdk/v<version>` on `main`; the
127
+ `.github/workflows/publish-sdk.yml` workflow builds, verifies the tag matches
128
+ `packages/sdk/package.json`, and publishes to npmjs.org with a Sigstore provenance
129
+ attestation.
130
+
131
+ ## License
132
+
133
+ MIT — see [`LICENSE`](./LICENSE).
@@ -0,0 +1,330 @@
1
+ /**
2
+ * @latimer-woods-tech/wordis-bond — typed client for the Word Is Bond voice-agent
3
+ * testing API (https://api.wordis-bond.com).
4
+ *
5
+ * A thin, dependency-free wrapper over the live REST surface: it uses the platform
6
+ * global `fetch`, authenticates with your `wb_live_…` API key, unwraps the
7
+ * `{ data, error }` envelope, and throws {@link WordIsBondError} on a non-2xx response.
8
+ *
9
+ * Prices/quotas are never baked in here — read them live with {@link WordIsBond.getPlans}.
10
+ * This client wraps a testing surface: you only ever test voice agents you run.
11
+ */
12
+ /** The public production API origin. */
13
+ declare const DEFAULT_BASE_URL = "https://api.wordis-bond.com";
14
+ /** Client options. */
15
+ interface WordIsBondOptions {
16
+ /** Your API key (`wb_live_…`). Required for every endpoint except {@link WordIsBond.getPlans} and {@link WordIsBond.health}. */
17
+ apiKey?: string;
18
+ /** Override the API origin (e.g. a staging URL). Defaults to {@link DEFAULT_BASE_URL}. */
19
+ baseUrl?: string;
20
+ /** Inject a custom fetch (defaults to the global `fetch`). */
21
+ fetch?: typeof fetch;
22
+ }
23
+ /** Thrown when the API returns a non-2xx response. Carries the status and parsed error. */
24
+ declare class WordIsBondError extends Error {
25
+ readonly status: number;
26
+ readonly code?: string;
27
+ readonly body: unknown;
28
+ constructor(status: number, message: string, opts?: {
29
+ code?: string;
30
+ body?: unknown;
31
+ });
32
+ }
33
+ type Plan = 'starter' | 'pro' | 'enterprise';
34
+ type Transport = 'pstn' | 'webrtc' | 'direct' | 'sip';
35
+ type RunStatus = 'queued' | 'running' | 'completed' | 'failed';
36
+ /** The voice agent under test — a system you run. */
37
+ interface TargetAgent {
38
+ transport: Transport;
39
+ /** E.164 number to dial (required for a `pstn` target). */
40
+ toNumber?: string;
41
+ /** Hosted-agent id, e.g. `wb-demo-dental`. */
42
+ peerId?: string;
43
+ [k: string]: unknown;
44
+ }
45
+ interface Scorecard {
46
+ score: number;
47
+ passed: boolean;
48
+ summary?: string;
49
+ sentiment?: number;
50
+ taskCompletion?: number;
51
+ layers?: Array<{
52
+ name: string;
53
+ score: number;
54
+ passed: boolean;
55
+ rationale: string;
56
+ assertions: unknown[];
57
+ }>;
58
+ }
59
+ interface RunMetrics {
60
+ latencyMs?: {
61
+ p50: number;
62
+ p95: number;
63
+ max: number;
64
+ };
65
+ wer?: number | null;
66
+ bargeInCount?: number;
67
+ turnCount?: number;
68
+ taskCompletion?: number;
69
+ sentiment?: number;
70
+ }
71
+ interface TestRun {
72
+ id: string;
73
+ accountId?: string;
74
+ suiteId?: string | null;
75
+ status: RunStatus;
76
+ transport?: string;
77
+ language?: string;
78
+ persona?: unknown;
79
+ targetAgent?: TargetAgent | null;
80
+ score?: number | null;
81
+ passed?: boolean | null;
82
+ scorecard?: Scorecard | null;
83
+ metrics?: RunMetrics | null;
84
+ transcript?: string | null;
85
+ /** Only on a live `direct` run — the tokenized WS your agent-side harness dials. */
86
+ mediaWebSocketUrl?: string;
87
+ reportUrl?: string;
88
+ peerId?: string;
89
+ startedAt?: string | null;
90
+ endedAt?: string | null;
91
+ createdAt?: string;
92
+ [k: string]: unknown;
93
+ }
94
+ interface Scenario {
95
+ persona?: string | {
96
+ name?: string;
97
+ description?: string;
98
+ };
99
+ goal?: string;
100
+ expected?: string[];
101
+ }
102
+ interface TestSuite {
103
+ id: string;
104
+ accountId?: string;
105
+ name: string;
106
+ targetAgent: TargetAgent;
107
+ language?: string;
108
+ scenarios?: Scenario[];
109
+ schedule?: string | null;
110
+ createdAt?: string;
111
+ updatedAt?: string;
112
+ }
113
+ interface PlanView {
114
+ plan: Plan;
115
+ priceUsdPerMonth: number;
116
+ purchasable?: boolean;
117
+ lookupKey?: string | null;
118
+ callMinutesPerMonth: number;
119
+ allowedTransports: Array<'direct' | 'pstn'>;
120
+ overageUsdPerMinute?: {
121
+ pstn: number | null;
122
+ direct: number | null;
123
+ };
124
+ ttsCharsPerMonth?: number;
125
+ concurrentCalls?: number;
126
+ scheduledRuns?: {
127
+ cadence: 'weekly' | 'daily' | 'hourly';
128
+ maxSuites: number | null;
129
+ };
130
+ apiKeys?: number | null;
131
+ requiresCard?: boolean;
132
+ }
133
+ interface PlansResponse {
134
+ plans: PlanView[];
135
+ currency: string;
136
+ }
137
+ interface Subscription {
138
+ plan: Plan;
139
+ hasBillingAccount: boolean;
140
+ usage: {
141
+ callMinutes: number;
142
+ ttsChars: number;
143
+ };
144
+ entitlements: PlanView;
145
+ }
146
+ interface ApiKeySummary {
147
+ id: string;
148
+ name: string;
149
+ keyPrefix: string;
150
+ lastUsedAt?: string | null;
151
+ createdAt?: string;
152
+ }
153
+ /** A newly-created key — `token` is present exactly once and never retrievable again. */
154
+ interface CreatedApiKey extends ApiKeySummary {
155
+ token: string;
156
+ }
157
+ interface TrendPoint {
158
+ runId: string;
159
+ score: number | null;
160
+ passed: boolean | null;
161
+ at: string | null;
162
+ }
163
+ interface RegressionPoint {
164
+ runId: string;
165
+ at: string | null;
166
+ reason: 'pass_to_fail' | 'score_drop';
167
+ from: number | null;
168
+ to: number | null;
169
+ }
170
+ interface SuiteTrend {
171
+ suiteId: string;
172
+ name: string | null;
173
+ runs: number;
174
+ passed: number;
175
+ failed: number;
176
+ passRate: number | null;
177
+ avgScore: number | null;
178
+ regressions: number;
179
+ }
180
+ interface SuiteTrendSeries {
181
+ suiteId: string;
182
+ name: string | null;
183
+ points: TrendPoint[];
184
+ regressions: RegressionPoint[];
185
+ summary: SuiteTrend;
186
+ }
187
+ interface AccountTrends {
188
+ window: string;
189
+ totals: {
190
+ suites: number;
191
+ runs: number;
192
+ passed: number;
193
+ failed: number;
194
+ passRate: number | null;
195
+ avgScore: number | null;
196
+ regressions: number;
197
+ };
198
+ suites: SuiteTrend[];
199
+ }
200
+ /** Body for the offline transcript-scoring path of {@link WordIsBond.runTest}. */
201
+ interface ScoreTranscriptInput {
202
+ transcript?: string;
203
+ turns?: Array<{
204
+ role: 'agent' | 'caller';
205
+ text: string;
206
+ }>;
207
+ scenarioGoal?: string;
208
+ assertions?: string[];
209
+ suiteId?: string | null;
210
+ language?: string;
211
+ transport?: 'pstn' | 'webrtc';
212
+ }
213
+ /** Body for the live synthetic-call path of {@link WordIsBond.runTest}. */
214
+ interface LiveRunInput {
215
+ targetAgent: TargetAgent;
216
+ goal?: string;
217
+ persona?: {
218
+ name?: string;
219
+ description: string;
220
+ };
221
+ suiteId?: string | null;
222
+ transport?: 'pstn' | 'webrtc';
223
+ language?: string;
224
+ concurrency?: number;
225
+ expected?: string[];
226
+ bargeIn?: boolean;
227
+ }
228
+ /** Result of a live run: either a single completed run or a batch with per-run entries. */
229
+ type RunTestResult = TestRun | {
230
+ runs: TestRun[];
231
+ concurrency?: number;
232
+ transport?: string;
233
+ };
234
+ interface DemoInput {
235
+ persona?: {
236
+ description: string;
237
+ };
238
+ goal?: string;
239
+ language?: string;
240
+ expected?: string[];
241
+ bargeIn?: boolean;
242
+ }
243
+ interface CreateSuiteInput {
244
+ name: string;
245
+ targetAgent: TargetAgent;
246
+ language?: string;
247
+ scenarios?: Scenario[];
248
+ schedule?: string;
249
+ }
250
+ /**
251
+ * Typed client for the Word Is Bond testing API.
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * const wb = new WordIsBond({ apiKey: process.env.WB_API_KEY });
256
+ * const run = await wb.runDemo();
257
+ * console.log(run.score, run.passed, run.reportUrl);
258
+ * ```
259
+ */
260
+ declare class WordIsBond {
261
+ private readonly apiKey?;
262
+ private readonly baseUrl;
263
+ private readonly fetchImpl;
264
+ constructor(options?: WordIsBondOptions);
265
+ /** Service health (no auth). */
266
+ health(): Promise<{
267
+ status: string;
268
+ worker?: string;
269
+ env?: string;
270
+ }>;
271
+ /** The plan + entitlement table (no auth). The single source of truth for pricing. */
272
+ getPlans(): Promise<PlansResponse>;
273
+ /** Your current plan, usage, and entitlements. */
274
+ getSubscription(): Promise<Subscription>;
275
+ /** List your active (non-revoked) API keys. */
276
+ listApiKeys(): Promise<ApiKeySummary[]>;
277
+ /** Create an API key. The returned `token` is shown once — store it securely. */
278
+ createApiKey(name: string): Promise<CreatedApiKey>;
279
+ /** Revoke an API key. */
280
+ revokeApiKey(id: string): Promise<{
281
+ revoked: boolean;
282
+ }>;
283
+ /**
284
+ * Run the hosted demo voice agent end-to-end and get a real, fully-scored result —
285
+ * no target of your own required. Returns the run plus a shareable report URL.
286
+ */
287
+ runDemo(input?: DemoInput): Promise<TestRun>;
288
+ /**
289
+ * Run a test against a voice agent you control. Pass a {@link ScoreTranscriptInput}
290
+ * to score a captured transcript offline (returns a completed {@link TestRun}), or a
291
+ * {@link LiveRunInput} to drive a live synthetic call (returns a run or a batch).
292
+ */
293
+ runTest(input: ScoreTranscriptInput | LiveRunInput): Promise<RunTestResult>;
294
+ /** (Re)score a run's transcript with the judge. */
295
+ rescoreRun(id: string, input?: {
296
+ scenarioGoal?: string;
297
+ assertions?: string[];
298
+ transcript?: string;
299
+ }): Promise<TestRun>;
300
+ /** Trigger the hosted self-test now and publish a public report. */
301
+ runCanary(): Promise<{
302
+ runId: string;
303
+ score: number | null;
304
+ passed: boolean | null;
305
+ reportUrl: string;
306
+ }>;
307
+ /** Fetch one run — poll here for the terminal scored state of a live run. */
308
+ getRun(id: string): Promise<TestRun>;
309
+ /** List your most recent runs (up to 100). */
310
+ listRuns(): Promise<TestRun[]>;
311
+ /** List your reusable test suites. */
312
+ listSuites(): Promise<TestSuite[]>;
313
+ /** Create a reusable test suite. */
314
+ createSuite(input: CreateSuiteInput): Promise<TestSuite>;
315
+ /** Fetch one suite. */
316
+ getSuite(id: string): Promise<TestSuite>;
317
+ /** Per-suite time series + run-to-run regression flags. */
318
+ getSuiteTrends(id: string): Promise<SuiteTrendSeries>;
319
+ /** Account-wide pass-rate, average score, and regressions over a window (default 90 days). */
320
+ getTrends(opts?: {
321
+ days?: number;
322
+ }): Promise<AccountTrends>;
323
+ /** Get the shareable public report URL for one of your runs. */
324
+ getReportLink(id: string): Promise<{
325
+ url: string;
326
+ }>;
327
+ private request;
328
+ }
329
+
330
+ export { type AccountTrends, type ApiKeySummary, type CreateSuiteInput, type CreatedApiKey, DEFAULT_BASE_URL, type DemoInput, type LiveRunInput, type Plan, type PlanView, type PlansResponse, type RegressionPoint, type RunMetrics, type RunStatus, type RunTestResult, type Scenario, type ScoreTranscriptInput, type Scorecard, type Subscription, type SuiteTrend, type SuiteTrendSeries, type TargetAgent, type TestRun, type TestSuite, type Transport, type TrendPoint, WordIsBond, WordIsBondError, type WordIsBondOptions, WordIsBond as default };
package/dist/index.js ADDED
@@ -0,0 +1,162 @@
1
+ // src/index.ts
2
+ var DEFAULT_BASE_URL = "https://api.wordis-bond.com";
3
+ var WordIsBondError = class extends Error {
4
+ status;
5
+ code;
6
+ body;
7
+ constructor(status, message, opts = {}) {
8
+ super(message);
9
+ this.name = "WordIsBondError";
10
+ this.status = status;
11
+ this.code = opts.code;
12
+ this.body = opts.body;
13
+ }
14
+ };
15
+ var WordIsBond = class {
16
+ apiKey;
17
+ baseUrl;
18
+ fetchImpl;
19
+ constructor(options = {}) {
20
+ this.apiKey = options.apiKey;
21
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
22
+ const f = options.fetch ?? globalThis.fetch;
23
+ if (!f) {
24
+ throw new Error("No fetch implementation available \u2014 pass one via the `fetch` option.");
25
+ }
26
+ this.fetchImpl = f;
27
+ }
28
+ // ── Meta ─────────────────────────────────────────────────────────────────
29
+ /** Service health (no auth). */
30
+ health() {
31
+ return this.request("GET", "/health", { auth: false });
32
+ }
33
+ // ── Billing (source of truth for pricing) ─────────────────────────────────
34
+ /** The plan + entitlement table (no auth). The single source of truth for pricing. */
35
+ getPlans() {
36
+ return this.request("GET", "/api/billing/plans", { auth: false });
37
+ }
38
+ /** Your current plan, usage, and entitlements. */
39
+ getSubscription() {
40
+ return this.request("GET", "/api/billing/subscription");
41
+ }
42
+ // ── API keys ──────────────────────────────────────────────────────────────
43
+ /** List your active (non-revoked) API keys. */
44
+ listApiKeys() {
45
+ return this.request("GET", "/api/api-keys");
46
+ }
47
+ /** Create an API key. The returned `token` is shown once — store it securely. */
48
+ createApiKey(name) {
49
+ return this.request("POST", "/api/api-keys", { body: { name } });
50
+ }
51
+ /** Revoke an API key. */
52
+ revokeApiKey(id) {
53
+ return this.request("DELETE", `/api/api-keys/${encodeURIComponent(id)}`);
54
+ }
55
+ // ── Testing ────────────────────────────────────────────────────────────────
56
+ /**
57
+ * Run the hosted demo voice agent end-to-end and get a real, fully-scored result —
58
+ * no target of your own required. Returns the run plus a shareable report URL.
59
+ */
60
+ runDemo(input = {}) {
61
+ return this.request("POST", "/api/tests/demo", { body: input });
62
+ }
63
+ /**
64
+ * Run a test against a voice agent you control. Pass a {@link ScoreTranscriptInput}
65
+ * to score a captured transcript offline (returns a completed {@link TestRun}), or a
66
+ * {@link LiveRunInput} to drive a live synthetic call (returns a run or a batch).
67
+ */
68
+ runTest(input) {
69
+ return this.request("POST", "/api/tests/run", { body: input });
70
+ }
71
+ /** (Re)score a run's transcript with the judge. */
72
+ rescoreRun(id, input = {}) {
73
+ return this.request("POST", `/api/tests/${encodeURIComponent(id)}/score`, { body: input });
74
+ }
75
+ /** Trigger the hosted self-test now and publish a public report. */
76
+ runCanary() {
77
+ return this.request("POST", "/api/tests/canary");
78
+ }
79
+ /** Fetch one run — poll here for the terminal scored state of a live run. */
80
+ getRun(id) {
81
+ return this.request("GET", `/api/tests/${encodeURIComponent(id)}`);
82
+ }
83
+ /** List your most recent runs (up to 100). */
84
+ listRuns() {
85
+ return this.request("GET", "/api/tests");
86
+ }
87
+ // ── Suites ───────────────────────────────────────────────────────────────
88
+ /** List your reusable test suites. */
89
+ listSuites() {
90
+ return this.request("GET", "/api/tests/suites");
91
+ }
92
+ /** Create a reusable test suite. */
93
+ createSuite(input) {
94
+ return this.request("POST", "/api/tests/suites", { body: input });
95
+ }
96
+ /** Fetch one suite. */
97
+ getSuite(id) {
98
+ return this.request("GET", `/api/tests/suites/${encodeURIComponent(id)}`);
99
+ }
100
+ // ── Trends ───────────────────────────────────────────────────────────────
101
+ /** Per-suite time series + run-to-run regression flags. */
102
+ getSuiteTrends(id) {
103
+ return this.request("GET", `/api/tests/suites/${encodeURIComponent(id)}/trends`);
104
+ }
105
+ /** Account-wide pass-rate, average score, and regressions over a window (default 90 days). */
106
+ getTrends(opts = {}) {
107
+ const q = opts.days ? `?days=${Math.trunc(opts.days)}` : "";
108
+ return this.request("GET", `/api/tests/trends${q}`);
109
+ }
110
+ // ── Reports ──────────────────────────────────────────────────────────────
111
+ /** Get the shareable public report URL for one of your runs. */
112
+ getReportLink(id) {
113
+ return this.request("GET", `/api/reports/${encodeURIComponent(id)}/link`);
114
+ }
115
+ // ── Internal ─────────────────────────────────────────────────────────────
116
+ async request(method, path, opts = {}) {
117
+ const auth = opts.auth !== false;
118
+ const headers = { accept: "application/json" };
119
+ if (auth) {
120
+ if (!this.apiKey) {
121
+ throw new WordIsBondError(0, `${method} ${path} requires an API key \u2014 construct the client with { apiKey }.`);
122
+ }
123
+ headers["authorization"] = `Bearer ${this.apiKey}`;
124
+ }
125
+ if (opts.body !== void 0) headers["content-type"] = "application/json";
126
+ let res;
127
+ try {
128
+ res = await this.fetchImpl(`${this.baseUrl}${path}`, {
129
+ method,
130
+ headers,
131
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
132
+ });
133
+ } catch (err) {
134
+ throw new WordIsBondError(0, `Network error calling ${method} ${path}: ${err.message}`);
135
+ }
136
+ const text = await res.text();
137
+ let json;
138
+ try {
139
+ json = text ? JSON.parse(text) : void 0;
140
+ } catch {
141
+ json = void 0;
142
+ }
143
+ if (!res.ok) {
144
+ const err = json?.error;
145
+ const message = typeof err === "string" ? err : err && typeof err === "object" && "message" in err ? String(err.message) : `Request failed with status ${res.status}`;
146
+ const code = err && typeof err === "object" && "code" in err ? String(err.code) : void 0;
147
+ throw new WordIsBondError(res.status, message, { code, body: json ?? text });
148
+ }
149
+ if (json && typeof json === "object" && "data" in json) {
150
+ return json.data;
151
+ }
152
+ return json;
153
+ }
154
+ };
155
+ var index_default = WordIsBond;
156
+ export {
157
+ DEFAULT_BASE_URL,
158
+ WordIsBond,
159
+ WordIsBondError,
160
+ index_default as default
161
+ };
162
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @latimer-woods-tech/wordis-bond — typed client for the Word Is Bond voice-agent\n * testing API (https://api.wordis-bond.com).\n *\n * A thin, dependency-free wrapper over the live REST surface: it uses the platform\n * global `fetch`, authenticates with your `wb_live_…` API key, unwraps the\n * `{ data, error }` envelope, and throws {@link WordIsBondError} on a non-2xx response.\n *\n * Prices/quotas are never baked in here — read them live with {@link WordIsBond.getPlans}.\n * This client wraps a testing surface: you only ever test voice agents you run.\n */\n\n/** The public production API origin. */\nexport const DEFAULT_BASE_URL = 'https://api.wordis-bond.com';\n\n/** Client options. */\nexport interface WordIsBondOptions {\n /** Your API key (`wb_live_…`). Required for every endpoint except {@link WordIsBond.getPlans} and {@link WordIsBond.health}. */\n apiKey?: string;\n /** Override the API origin (e.g. a staging URL). Defaults to {@link DEFAULT_BASE_URL}. */\n baseUrl?: string;\n /** Inject a custom fetch (defaults to the global `fetch`). */\n fetch?: typeof fetch;\n}\n\n/** Thrown when the API returns a non-2xx response. Carries the status and parsed error. */\nexport class WordIsBondError extends Error {\n readonly status: number;\n readonly code?: string;\n readonly body: unknown;\n constructor(status: number, message: string, opts: { code?: string; body?: unknown } = {}) {\n super(message);\n this.name = 'WordIsBondError';\n this.status = status;\n this.code = opts.code;\n this.body = opts.body;\n }\n}\n\n// ── Domain types (mirror docs/openapi.json; see the OpenAPI spec for full detail) ──\n\nexport type Plan = 'starter' | 'pro' | 'enterprise';\nexport type Transport = 'pstn' | 'webrtc' | 'direct' | 'sip';\nexport type RunStatus = 'queued' | 'running' | 'completed' | 'failed';\n\n/** The voice agent under test — a system you run. */\nexport interface TargetAgent {\n transport: Transport;\n /** E.164 number to dial (required for a `pstn` target). */\n toNumber?: string;\n /** Hosted-agent id, e.g. `wb-demo-dental`. */\n peerId?: string;\n [k: string]: unknown;\n}\n\nexport interface Scorecard {\n score: number;\n passed: boolean;\n summary?: string;\n sentiment?: number;\n taskCompletion?: number;\n layers?: Array<{ name: string; score: number; passed: boolean; rationale: string; assertions: unknown[] }>;\n}\n\nexport interface RunMetrics {\n latencyMs?: { p50: number; p95: number; max: number };\n wer?: number | null;\n bargeInCount?: number;\n turnCount?: number;\n taskCompletion?: number;\n sentiment?: number;\n}\n\nexport interface TestRun {\n id: string;\n accountId?: string;\n suiteId?: string | null;\n status: RunStatus;\n transport?: string;\n language?: string;\n persona?: unknown;\n targetAgent?: TargetAgent | null;\n score?: number | null;\n passed?: boolean | null;\n scorecard?: Scorecard | null;\n metrics?: RunMetrics | null;\n transcript?: string | null;\n /** Only on a live `direct` run — the tokenized WS your agent-side harness dials. */\n mediaWebSocketUrl?: string;\n reportUrl?: string;\n peerId?: string;\n startedAt?: string | null;\n endedAt?: string | null;\n createdAt?: string;\n [k: string]: unknown;\n}\n\nexport interface Scenario {\n persona?: string | { name?: string; description?: string };\n goal?: string;\n expected?: string[];\n}\n\nexport interface TestSuite {\n id: string;\n accountId?: string;\n name: string;\n targetAgent: TargetAgent;\n language?: string;\n scenarios?: Scenario[];\n schedule?: string | null;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface PlanView {\n plan: Plan;\n priceUsdPerMonth: number;\n purchasable?: boolean;\n lookupKey?: string | null;\n callMinutesPerMonth: number;\n allowedTransports: Array<'direct' | 'pstn'>;\n overageUsdPerMinute?: { pstn: number | null; direct: number | null };\n ttsCharsPerMonth?: number;\n concurrentCalls?: number;\n scheduledRuns?: { cadence: 'weekly' | 'daily' | 'hourly'; maxSuites: number | null };\n apiKeys?: number | null;\n requiresCard?: boolean;\n}\n\nexport interface PlansResponse {\n plans: PlanView[];\n currency: string;\n}\n\nexport interface Subscription {\n plan: Plan;\n hasBillingAccount: boolean;\n usage: { callMinutes: number; ttsChars: number };\n entitlements: PlanView;\n}\n\nexport interface ApiKeySummary {\n id: string;\n name: string;\n keyPrefix: string;\n lastUsedAt?: string | null;\n createdAt?: string;\n}\n\n/** A newly-created key — `token` is present exactly once and never retrievable again. */\nexport interface CreatedApiKey extends ApiKeySummary {\n token: string;\n}\n\nexport interface TrendPoint {\n runId: string;\n score: number | null;\n passed: boolean | null;\n at: string | null;\n}\nexport interface RegressionPoint {\n runId: string;\n at: string | null;\n reason: 'pass_to_fail' | 'score_drop';\n from: number | null;\n to: number | null;\n}\nexport interface SuiteTrend {\n suiteId: string;\n name: string | null;\n runs: number;\n passed: number;\n failed: number;\n passRate: number | null;\n avgScore: number | null;\n regressions: number;\n}\nexport interface SuiteTrendSeries {\n suiteId: string;\n name: string | null;\n points: TrendPoint[];\n regressions: RegressionPoint[];\n summary: SuiteTrend;\n}\nexport interface AccountTrends {\n window: string;\n totals: {\n suites: number;\n runs: number;\n passed: number;\n failed: number;\n passRate: number | null;\n avgScore: number | null;\n regressions: number;\n };\n suites: SuiteTrend[];\n}\n\n/** Body for the offline transcript-scoring path of {@link WordIsBond.runTest}. */\nexport interface ScoreTranscriptInput {\n transcript?: string;\n turns?: Array<{ role: 'agent' | 'caller'; text: string }>;\n scenarioGoal?: string;\n assertions?: string[];\n suiteId?: string | null;\n language?: string;\n transport?: 'pstn' | 'webrtc';\n}\n\n/** Body for the live synthetic-call path of {@link WordIsBond.runTest}. */\nexport interface LiveRunInput {\n targetAgent: TargetAgent;\n goal?: string;\n persona?: { name?: string; description: string };\n suiteId?: string | null;\n transport?: 'pstn' | 'webrtc';\n language?: string;\n concurrency?: number;\n expected?: string[];\n bargeIn?: boolean;\n}\n\n/** Result of a live run: either a single completed run or a batch with per-run entries. */\nexport type RunTestResult = TestRun | { runs: TestRun[]; concurrency?: number; transport?: string };\n\nexport interface DemoInput {\n persona?: { description: string };\n goal?: string;\n language?: string;\n expected?: string[];\n bargeIn?: boolean;\n}\n\nexport interface CreateSuiteInput {\n name: string;\n targetAgent: TargetAgent;\n language?: string;\n scenarios?: Scenario[];\n schedule?: string;\n}\n\ninterface Envelope<T> {\n data: T | null;\n error: unknown;\n}\n\n/**\n * Typed client for the Word Is Bond testing API.\n *\n * @example\n * ```ts\n * const wb = new WordIsBond({ apiKey: process.env.WB_API_KEY });\n * const run = await wb.runDemo();\n * console.log(run.score, run.passed, run.reportUrl);\n * ```\n */\nexport class WordIsBond {\n private readonly apiKey?: string;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: WordIsBondOptions = {}) {\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const f = options.fetch ?? (globalThis.fetch as typeof fetch | undefined);\n if (!f) {\n throw new Error('No fetch implementation available — pass one via the `fetch` option.');\n }\n this.fetchImpl = f;\n }\n\n // ── Meta ─────────────────────────────────────────────────────────────────\n\n /** Service health (no auth). */\n health(): Promise<{ status: string; worker?: string; env?: string }> {\n return this.request('GET', '/health', { auth: false });\n }\n\n // ── Billing (source of truth for pricing) ─────────────────────────────────\n\n /** The plan + entitlement table (no auth). The single source of truth for pricing. */\n getPlans(): Promise<PlansResponse> {\n return this.request('GET', '/api/billing/plans', { auth: false });\n }\n\n /** Your current plan, usage, and entitlements. */\n getSubscription(): Promise<Subscription> {\n return this.request('GET', '/api/billing/subscription');\n }\n\n // ── API keys ──────────────────────────────────────────────────────────────\n\n /** List your active (non-revoked) API keys. */\n listApiKeys(): Promise<ApiKeySummary[]> {\n return this.request('GET', '/api/api-keys');\n }\n\n /** Create an API key. The returned `token` is shown once — store it securely. */\n createApiKey(name: string): Promise<CreatedApiKey> {\n return this.request('POST', '/api/api-keys', { body: { name } });\n }\n\n /** Revoke an API key. */\n revokeApiKey(id: string): Promise<{ revoked: boolean }> {\n return this.request('DELETE', `/api/api-keys/${encodeURIComponent(id)}`);\n }\n\n // ── Testing ────────────────────────────────────────────────────────────────\n\n /**\n * Run the hosted demo voice agent end-to-end and get a real, fully-scored result —\n * no target of your own required. Returns the run plus a shareable report URL.\n */\n runDemo(input: DemoInput = {}): Promise<TestRun> {\n return this.request('POST', '/api/tests/demo', { body: input as Record<string, unknown> });\n }\n\n /**\n * Run a test against a voice agent you control. Pass a {@link ScoreTranscriptInput}\n * to score a captured transcript offline (returns a completed {@link TestRun}), or a\n * {@link LiveRunInput} to drive a live synthetic call (returns a run or a batch).\n */\n runTest(input: ScoreTranscriptInput | LiveRunInput): Promise<RunTestResult> {\n return this.request('POST', '/api/tests/run', { body: input as Record<string, unknown> });\n }\n\n /** (Re)score a run's transcript with the judge. */\n rescoreRun(\n id: string,\n input: { scenarioGoal?: string; assertions?: string[]; transcript?: string } = {},\n ): Promise<TestRun> {\n return this.request('POST', `/api/tests/${encodeURIComponent(id)}/score`, { body: input });\n }\n\n /** Trigger the hosted self-test now and publish a public report. */\n runCanary(): Promise<{ runId: string; score: number | null; passed: boolean | null; reportUrl: string }> {\n return this.request('POST', '/api/tests/canary');\n }\n\n /** Fetch one run — poll here for the terminal scored state of a live run. */\n getRun(id: string): Promise<TestRun> {\n return this.request('GET', `/api/tests/${encodeURIComponent(id)}`);\n }\n\n /** List your most recent runs (up to 100). */\n listRuns(): Promise<TestRun[]> {\n return this.request('GET', '/api/tests');\n }\n\n // ── Suites ───────────────────────────────────────────────────────────────\n\n /** List your reusable test suites. */\n listSuites(): Promise<TestSuite[]> {\n return this.request('GET', '/api/tests/suites');\n }\n\n /** Create a reusable test suite. */\n createSuite(input: CreateSuiteInput): Promise<TestSuite> {\n return this.request('POST', '/api/tests/suites', { body: input as unknown as Record<string, unknown> });\n }\n\n /** Fetch one suite. */\n getSuite(id: string): Promise<TestSuite> {\n return this.request('GET', `/api/tests/suites/${encodeURIComponent(id)}`);\n }\n\n // ── Trends ───────────────────────────────────────────────────────────────\n\n /** Per-suite time series + run-to-run regression flags. */\n getSuiteTrends(id: string): Promise<SuiteTrendSeries> {\n return this.request('GET', `/api/tests/suites/${encodeURIComponent(id)}/trends`);\n }\n\n /** Account-wide pass-rate, average score, and regressions over a window (default 90 days). */\n getTrends(opts: { days?: number } = {}): Promise<AccountTrends> {\n const q = opts.days ? `?days=${Math.trunc(opts.days)}` : '';\n return this.request('GET', `/api/tests/trends${q}`);\n }\n\n // ── Reports ──────────────────────────────────────────────────────────────\n\n /** Get the shareable public report URL for one of your runs. */\n getReportLink(id: string): Promise<{ url: string }> {\n return this.request('GET', `/api/reports/${encodeURIComponent(id)}/link`);\n }\n\n // ── Internal ─────────────────────────────────────────────────────────────\n\n private async request<T>(\n method: string,\n path: string,\n opts: { body?: Record<string, unknown>; auth?: boolean } = {},\n ): Promise<T> {\n const auth = opts.auth !== false;\n const headers: Record<string, string> = { accept: 'application/json' };\n if (auth) {\n if (!this.apiKey) {\n throw new WordIsBondError(0, `${method} ${path} requires an API key — construct the client with { apiKey }.`);\n }\n headers['authorization'] = `Bearer ${this.apiKey}`;\n }\n if (opts.body !== undefined) headers['content-type'] = 'application/json';\n\n let res: Response;\n try {\n res = await this.fetchImpl(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n });\n } catch (err) {\n throw new WordIsBondError(0, `Network error calling ${method} ${path}: ${(err as Error).message}`);\n }\n\n const text = await res.text();\n let json: Envelope<T> | undefined;\n try {\n json = text ? (JSON.parse(text) as Envelope<T>) : undefined;\n } catch {\n json = undefined;\n }\n\n if (!res.ok) {\n const err = json?.error;\n const message =\n typeof err === 'string'\n ? err\n : err && typeof err === 'object' && 'message' in err\n ? String((err as { message: unknown }).message)\n : `Request failed with status ${res.status}`;\n const code = err && typeof err === 'object' && 'code' in err ? String((err as { code: unknown }).code) : undefined;\n throw new WordIsBondError(res.status, message, { code, body: json ?? text });\n }\n\n // Successful responses are the { data, error } envelope; hand back `data`.\n if (json && typeof json === 'object' && 'data' in json) {\n return json.data as T;\n }\n return json as unknown as T;\n }\n}\n\nexport default WordIsBond;\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@latimer-woods-tech/wordis-bond",
3
+ "version": "0.1.0",
4
+ "description": "Typed TypeScript client for the Word Is Bond voice-agent testing API.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Adrian Perry <aperry@latwoodtech.com>",
8
+ "homepage": "https://github.com/Latimer-Woods-Tech/wordis-bond/tree/main/packages/sdk",
9
+ "bugs": {
10
+ "url": "https://github.com/Latimer-Woods-Tech/wordis-bond/issues"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/Latimer-Woods-Tech/wordis-bond.git",
15
+ "directory": "packages/sdk"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "sideEffects": false,
21
+ "main": "./dist/index.js",
22
+ "module": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ },
29
+ "./package.json": "./package.json"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "engines": {
37
+ "node": ">=18"
38
+ },
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "typecheck": "tsc --noEmit",
42
+ "test": "vitest run"
43
+ },
44
+ "keywords": [
45
+ "voice-agent",
46
+ "voice-agent-testing",
47
+ "testing",
48
+ "wordis-bond",
49
+ "api-client",
50
+ "sdk",
51
+ "mcp",
52
+ "typescript",
53
+ "esm"
54
+ ],
55
+ "devDependencies": {
56
+ "tsup": "^8.3.0",
57
+ "typescript": "^5.7.0",
58
+ "vitest": "^3.2.6"
59
+ }
60
+ }