@noodleseed/one 0.170.0 → 0.171.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.
Files changed (105) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +3 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/catalog.d.ts.map +1 -1
  5. package/dist/commands/catalog.js +2 -1
  6. package/dist/commands/catalog.js.map +1 -1
  7. package/dist/commands/whatsapp-ops.d.ts +13 -0
  8. package/dist/commands/whatsapp-ops.d.ts.map +1 -0
  9. package/dist/commands/whatsapp-ops.js +244 -0
  10. package/dist/commands/whatsapp-ops.js.map +1 -0
  11. package/dist/commands/whatsapp-output.d.ts +3 -0
  12. package/dist/commands/whatsapp-output.d.ts.map +1 -0
  13. package/dist/commands/whatsapp-output.js +70 -0
  14. package/dist/commands/whatsapp-output.js.map +1 -0
  15. package/dist/dev.js +2 -0
  16. package/dist/dev.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/plugin-mode/plugin-operation-contract.d.ts +1 -1
  22. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +4 -0
  23. package/node_modules/@noodle-borg/agent-kit/dist/curated/error-fixes.js +1 -0
  24. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
  25. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +3 -0
  26. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +6 -0
  27. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  28. package/node_modules/@noodle-borg/app-audit/dist/audit-embedded-assistant.js +2 -0
  29. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-configuration.js +2 -1
  30. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-model-context.js +9 -4
  31. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-turn-context.d.ts +16 -0
  32. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-turn-context.js +19 -0
  33. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +145 -0
  34. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +378 -0
  35. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-inbox.js +160 -0
  36. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-provider-blocks.d.ts +18 -0
  37. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-provider-blocks.js +69 -0
  38. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-spend.js +82 -0
  39. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.d.ts +42 -0
  40. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.js +76 -0
  41. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +134 -0
  42. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.js +72 -0
  43. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.d.ts +20 -0
  44. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.js +221 -0
  45. package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.d.ts +15 -0
  46. package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.js +34 -0
  47. package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.d.ts +19 -1
  48. package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.js +22 -6
  49. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +7 -0
  50. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +7 -0
  51. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-appearance-store.js +83 -0
  52. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-elevation.js +151 -0
  53. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-interaction-writes.js +45 -0
  54. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-interactions.js +340 -0
  55. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant.js +439 -0
  56. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-channel-store.js +102 -0
  57. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-continuity-store.js +159 -0
  58. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-elevation-store.js +110 -0
  59. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-embed-store.js +134 -0
  60. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres.js +11 -0
  61. package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.d.ts +8 -0
  62. package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.js +23 -0
  63. package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +17 -2
  64. package/node_modules/@noodle-borg/authoring/dist/assistant.js +48 -19
  65. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -1
  66. package/node_modules/@noodle-borg/authoring/dist/index.js +1 -1
  67. package/node_modules/@noodle-borg/authoring/dist/load-authored.js +1 -1
  68. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +121 -0
  69. package/node_modules/@noodle-borg/cli-catalog/dist/index.js +1 -0
  70. package/node_modules/@noodle-borg/compiler/dist/artifact/config.js +1 -1
  71. package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
  72. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +72 -12
  73. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +51 -27
  74. package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.d.ts +1 -0
  75. package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.js +19 -0
  76. package/node_modules/@noodle-borg/module-audit/dist/index.js +37 -0
  77. package/node_modules/@noodle-borg/module-audit/dist/postgres-audit.js +126 -0
  78. package/node_modules/@noodle-borg/module-audit/dist/route.js +66 -0
  79. package/node_modules/@noodle-borg/runtime/dist/managed-origins.js +8 -4
  80. package/node_modules/@noodle-borg/runtime/dist/postgres-state-handles.js +311 -0
  81. package/node_modules/@noodle-borg/runtime/dist/postgres.js +4 -0
  82. package/node_modules/@noodle-borg/service/dist/assistant-composition.js +18 -0
  83. package/node_modules/@noodle-borg/service/dist/build-info.js +5 -2
  84. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-coordinator.js +7 -0
  85. package/node_modules/@noodle-borg/service/dist/channels/360dialog.js +233 -0
  86. package/node_modules/@noodle-borg/service/dist/channels/cipher.js +37 -0
  87. package/node_modules/@noodle-borg/service/dist/channels/composition.js +37 -0
  88. package/node_modules/@noodle-borg/service/dist/channels/inference-guard.js +53 -0
  89. package/node_modules/@noodle-borg/service/dist/channels/runtime.js +337 -0
  90. package/node_modules/@noodle-borg/service/dist/channels/worker-loop.js +50 -0
  91. package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +62 -34
  92. package/node_modules/@noodle-borg/service/dist/routes/assistant-model-binding.js +6 -2
  93. package/node_modules/@noodle-borg/service/dist/routes/business-information-channels.js +14 -1
  94. package/node_modules/@noodle-borg/service/dist/routes/whatsapp.js +238 -0
  95. package/node_modules/@noodle-borg/service/dist/service.js +30 -32
  96. package/node_modules/@noodle-borg/service/package.json +1 -1
  97. package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.d.ts +33 -0
  98. package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.js +9 -0
  99. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +1 -0
  100. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +1 -0
  101. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.d.ts +2 -0
  102. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.js +10 -2
  103. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.d.ts +426 -0
  104. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.js +213 -0
  105. package/package.json +2 -2
@@ -0,0 +1,233 @@
1
+ import { timingSafeEqual } from 'node:crypto';
2
+ import { ChannelError, } from '@noodle-borg/assistant-gateway/portable';
3
+ import { guardedFetch } from '@noodle-borg/connector-http';
4
+ import { z } from 'zod';
5
+ export const WHATSAPP_CALLBACK_HEADER = 'x-noodle-webhook-secret';
6
+ const identifier = z.string().min(1).max(512);
7
+ const timestamp = z
8
+ .string()
9
+ .regex(/^\d{1,13}$/)
10
+ .transform((value) => Number(value) * 1000)
11
+ .refine(Number.isSafeInteger);
12
+ const valueSchema = z.object({
13
+ messaging_product: z.literal('whatsapp'),
14
+ metadata: z.object({ phone_number_id: identifier }),
15
+ messages: z
16
+ .array(z.object({
17
+ id: identifier,
18
+ from: z.string().max(128).optional(),
19
+ user_id: z.string().max(128).optional(),
20
+ timestamp,
21
+ type: z.string().max(64),
22
+ text: z.object({ body: z.string().max(1 << 20) }).optional(),
23
+ }))
24
+ .max(100)
25
+ .optional(),
26
+ statuses: z
27
+ .array(z.object({
28
+ id: identifier,
29
+ status: z.enum(['sent', 'delivered', 'read', 'failed']),
30
+ timestamp,
31
+ }))
32
+ .max(100)
33
+ .optional(),
34
+ });
35
+ const webhookSchema = z.object({
36
+ object: z.literal('whatsapp_business_account'),
37
+ entry: z
38
+ .array(z.object({
39
+ changes: z.array(z.object({ field: z.string().max(128), value: z.unknown() })).max(100),
40
+ }))
41
+ .max(100),
42
+ });
43
+ export function verifyWhatsAppWebhookSecret(input, secret) {
44
+ if (typeof input !== 'string' || secret.length < 32 || secret.length > 512)
45
+ return false;
46
+ const actual = Buffer.from(input), expected = Buffer.from(secret);
47
+ return actual.length === expected.length && timingSafeEqual(actual, expected);
48
+ }
49
+ export function parseWhatsAppWebhook(input, phoneNumberId) {
50
+ const parsed = webhookSchema.safeParse(input);
51
+ if (!parsed.success)
52
+ throw new ChannelError('webhook_invalid');
53
+ const messages = [];
54
+ const statuses = [];
55
+ let count = 0;
56
+ for (const entry of parsed.data.entry)
57
+ for (const change of entry.changes) {
58
+ if (++count > 100)
59
+ throw new ChannelError('batch_too_large');
60
+ // System notifications do not participate in conversation processing.
61
+ if (change.field !== 'messages')
62
+ continue;
63
+ const validated = valueSchema.safeParse(change.value);
64
+ if (!validated.success)
65
+ throw new ChannelError('webhook_invalid');
66
+ const value = validated.data;
67
+ if (value.metadata.phone_number_id !== phoneNumberId)
68
+ throw new ChannelError('asset_mismatch');
69
+ for (const message of value.messages ?? []) {
70
+ let address;
71
+ if (message.from && /^[1-9]\d{5,14}$/.test(message.from))
72
+ address = { kind: 'phone', value: message.from };
73
+ else if (message.user_id && /^[A-Z]{2}\.[A-Za-z0-9]{1,124}$/.test(message.user_id))
74
+ address = { kind: 'opaque', value: message.user_id };
75
+ else
76
+ throw new ChannelError('recipient_invalid');
77
+ messages.push({
78
+ providerId: message.id,
79
+ address,
80
+ eventAt: message.timestamp,
81
+ ...(message.type === 'text' && message.text ? { text: message.text.body } : {}),
82
+ });
83
+ }
84
+ for (const status of value.statuses ?? [])
85
+ statuses.push({ providerMessageId: status.id, state: status.status });
86
+ if (messages.length + statuses.length > 100)
87
+ throw new ChannelError('batch_too_large');
88
+ }
89
+ return { messages, statuses };
90
+ }
91
+ async function boundedJson(response) {
92
+ const reader = response.body?.getReader();
93
+ if (!reader)
94
+ throw new ChannelError('provider_response_invalid');
95
+ const chunks = [];
96
+ let size = 0;
97
+ try {
98
+ for (;;) {
99
+ const result = await reader.read();
100
+ if (result.done)
101
+ break;
102
+ size += result.value.byteLength;
103
+ if (size > 65_536)
104
+ throw new ChannelError('provider_response_invalid');
105
+ chunks.push(result.value);
106
+ }
107
+ return JSON.parse(Buffer.concat(chunks).toString('utf8'));
108
+ }
109
+ finally {
110
+ await reader.cancel().catch(() => undefined);
111
+ }
112
+ }
113
+ export class Dialog360 {
114
+ apiKey;
115
+ fetcher;
116
+ constructor(apiKey, fetcher = (url, init) => guardedFetch(new URL(url), init)) {
117
+ this.apiKey = apiKey;
118
+ this.fetcher = fetcher;
119
+ }
120
+ async request(path, method = 'GET', body) {
121
+ return this.fetcher(`https://waba-v2.360dialog.io${path}`, {
122
+ method,
123
+ headers: { 'D360-API-KEY': this.apiKey, 'content-type': 'application/json' },
124
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
125
+ redirect: 'manual',
126
+ signal: AbortSignal.timeout(15_000),
127
+ });
128
+ }
129
+ async health() {
130
+ const response = await this.request('/health_status?fields=id');
131
+ if (!response.ok)
132
+ throw new ChannelError('provider_unavailable');
133
+ const parsed = z
134
+ .object({ id: identifier, health_status: z.object({ can_send_message: z.string() }) })
135
+ .safeParse(await boundedJson(response));
136
+ if (!parsed.success)
137
+ throw new ChannelError('provider_response_invalid');
138
+ return {
139
+ phoneNumberId: parsed.data.id,
140
+ canSend: parsed.data.health_status.can_send_message === 'AVAILABLE',
141
+ };
142
+ }
143
+ async webhook() {
144
+ const response = await this.request('/v1/configs/webhook');
145
+ if (!response.ok)
146
+ throw new ChannelError('provider_unavailable');
147
+ const parsed = z
148
+ .object({ url: z.string().max(2048), headers: z.record(z.string(), z.string()).optional() })
149
+ .safeParse(await boundedJson(response));
150
+ if (!parsed.success)
151
+ throw new ChannelError('provider_response_invalid');
152
+ return parsed.data;
153
+ }
154
+ async configureWebhook(url, secret) {
155
+ const target = new URL(url);
156
+ if (target.protocol !== 'https:' ||
157
+ target.username ||
158
+ target.password ||
159
+ target.port ||
160
+ target.hash ||
161
+ target.search)
162
+ throw new ChannelError('webhook_url_invalid');
163
+ if (!verifyWhatsAppWebhookSecret(secret, secret))
164
+ throw new ChannelError('webhook_secret_invalid');
165
+ const response = await this.request('/v1/configs/webhook', 'POST', {
166
+ url,
167
+ headers: { [WHATSAPP_CALLBACK_HEADER]: secret },
168
+ });
169
+ if (!response.ok)
170
+ throw new ChannelError('webhook_configuration_failed');
171
+ await response.body?.cancel();
172
+ }
173
+ async setBlocked(phone, blocked) {
174
+ try {
175
+ const response = await this.request('/block_users', blocked ? 'POST' : 'DELETE', {
176
+ messaging_product: 'whatsapp',
177
+ block_users: [{ user: phone }],
178
+ });
179
+ if (!response.ok) {
180
+ await response.body?.cancel();
181
+ return {
182
+ state: response.status >= 500 ? 'unknown' : 'error',
183
+ code: `provider_http_${response.status}`,
184
+ };
185
+ }
186
+ const field = blocked ? 'added_users' : 'removed_users';
187
+ const parsed = z
188
+ .object({
189
+ block_users: z.object({
190
+ [field]: z.array(z.object({ input: z.string(), wa_id: z.string() })),
191
+ }),
192
+ })
193
+ .safeParse(await boundedJson(response));
194
+ return parsed.success &&
195
+ parsed.data.block_users[field]?.some((user) => user.input === phone || user.wa_id === phone)
196
+ ? { state: 'confirmed' }
197
+ : { state: 'unknown', code: 'provider_receipt_invalid' };
198
+ }
199
+ catch {
200
+ return { state: 'unknown', code: 'provider_outcome_unknown' };
201
+ }
202
+ }
203
+ async send(input) {
204
+ if (!input.text || input.text.length > 4096)
205
+ return { state: 'failed', code: 'reply_invalid' };
206
+ try {
207
+ const response = await this.request('/messages', 'POST', {
208
+ messaging_product: 'whatsapp',
209
+ recipient_type: 'individual',
210
+ type: 'text',
211
+ ...(input.to.kind === 'phone' ? { to: input.to.value } : { recipient: input.to.value }),
212
+ text: { body: input.text, preview_url: false },
213
+ });
214
+ if (!response.ok) {
215
+ await response.body?.cancel();
216
+ return {
217
+ state: response.status >= 500 || response.status === 408 ? 'unknown' : 'failed',
218
+ code: `provider_http_${response.status}`,
219
+ };
220
+ }
221
+ const parsed = z
222
+ .object({ messages: z.array(z.object({ id: identifier })).length(1) })
223
+ .safeParse(await boundedJson(response));
224
+ return parsed.success
225
+ ? { state: 'accepted', providerMessageId: parsed.data.messages[0].id }
226
+ : { state: 'unknown', code: 'provider_receipt_invalid' };
227
+ }
228
+ catch {
229
+ return { state: 'unknown', code: 'send_outcome_unknown' };
230
+ }
231
+ }
232
+ }
233
+ //# sourceMappingURL=360dialog.js.map
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ const envelope = z
3
+ .object({
4
+ v: z.number(),
5
+ algo: z.string(),
6
+ keyId: z.string(),
7
+ iv: z.string(),
8
+ tag: z.string(),
9
+ ct: z.string(),
10
+ })
11
+ .passthrough();
12
+ export class ChannelSecretBoxCipher {
13
+ box;
14
+ constructor(box) {
15
+ this.box = box;
16
+ }
17
+ seal(scope, id, value) {
18
+ return this.box.seal(JSON.stringify({ purpose: 'assistant-channel-v1', scope, id, value }));
19
+ }
20
+ async open(scope, id, sealed) {
21
+ const parsed = envelope.parse(sealed);
22
+ const value = JSON.parse(await this.box.open(parsed));
23
+ const opened = z
24
+ .object({
25
+ purpose: z.literal('assistant-channel-v1'),
26
+ scope: z.string(),
27
+ id: z.string(),
28
+ value: z.unknown(),
29
+ })
30
+ .strict()
31
+ .parse(value);
32
+ if (opened.scope !== scope || opened.id !== id)
33
+ throw new Error('channel payload context mismatch');
34
+ return opened.value;
35
+ }
36
+ }
37
+ //# sourceMappingURL=cipher.js.map
@@ -0,0 +1,37 @@
1
+ import { PostgresChannelStore } from '@noodle-borg/assistant-gateway/postgres';
2
+ import { noopLogger } from '@noodle-borg/transport-http';
3
+ import { ChannelSecretBoxCipher } from './cipher.js';
4
+ import { WhatsAppRuntime } from './runtime.js';
5
+ import { ChannelWorkerLoop } from './worker-loop.js';
6
+ export async function createPostgresWhatsApp(pool, secretBox, options) {
7
+ if (!secretBox)
8
+ throw new Error('WhatsApp storage requires the service key custodian');
9
+ const store = new PostgresChannelStore(pool, new ChannelSecretBoxCipher(secretBox));
10
+ await store.ensureSchema();
11
+ return {
12
+ store,
13
+ worker: new ChannelWorkerLoop((code) => (options.logger ?? noopLogger).warn('assistant.channel.worker', { code })),
14
+ };
15
+ }
16
+ export function createWhatsAppRuntime(options, deps, history) {
17
+ if (!options)
18
+ return undefined;
19
+ return new WhatsAppRuntime({
20
+ ...options,
21
+ ...(history.allowance
22
+ ? {
23
+ historyDays: async (tenant) => {
24
+ const allowance = await history.allowance?.(tenant.org);
25
+ if (!allowance)
26
+ throw new Error('history policy unavailable');
27
+ const installation = (await history.installations?.listInstallations(tenant.org))?.find((item) => item.scope.app === tenant.app && item.scope.env === tenant.env);
28
+ return installation && history.activity
29
+ ? Math.min(allowance.maximumDays, (await history.activity.settings(installation.scope, false)).projection
30
+ .retentionDays)
31
+ : allowance.maximumDays;
32
+ },
33
+ }
34
+ : {}),
35
+ }, deps);
36
+ }
37
+ //# sourceMappingURL=composition.js.map
@@ -0,0 +1,53 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { billedOutputTokens, ChannelError, } from '@noodle-borg/assistant-gateway/portable';
3
+ export function verifiedInferenceBound(binding, now = Date.now()) {
4
+ const bound = binding.inferenceCost;
5
+ if (!bound ||
6
+ !bound.version ||
7
+ !Number.isFinite(Date.parse(bound.validUntil)) ||
8
+ Date.parse(bound.validUntil) <= now)
9
+ throw new ChannelError('spend_bound_unverified');
10
+ for (const number of [
11
+ bound.maxInputTokens,
12
+ bound.maxBilledOutputTokens,
13
+ bound.inputMicroUsdPerMillionTokens,
14
+ bound.outputMicroUsdPerMillionTokens,
15
+ ])
16
+ if (!Number.isSafeInteger(number) || number <= 0)
17
+ throw new ChannelError('spend_bound_unverified');
18
+ if (!Number.isSafeInteger(quote(bound, bound.maxInputTokens, bound.maxBilledOutputTokens)))
19
+ throw new ChannelError('spend_bound_unverified');
20
+ return bound;
21
+ }
22
+ function quote(bound, input, output) {
23
+ // Integer arithmetic retains fractional micro-dollar prices without floating-point under-reservation.
24
+ return Number((BigInt(input) * BigInt(bound.inputMicroUsdPerMillionTokens) +
25
+ BigInt(output) * BigInt(bound.outputMicroUsdPerMillionTokens) +
26
+ 999999n) /
27
+ 1000000n);
28
+ }
29
+ export function withChannelInferenceGuard(binding, channels, id, turn, beforeRequest) {
30
+ verifiedInferenceBound(binding, channels.now());
31
+ return {
32
+ ...binding,
33
+ inferenceGuard: {
34
+ async reserve() {
35
+ await beforeRequest();
36
+ const bound = verifiedInferenceBound(binding, channels.now());
37
+ const attempt = `${turn}:${randomUUID()}`;
38
+ await channels.reserveSpend(id, attempt, quote(bound, bound.maxInputTokens, bound.maxBilledOutputTokens));
39
+ return {
40
+ async cancelBeforeDispatch() {
41
+ await channels.settleSpend(id, attempt, 0);
42
+ },
43
+ async settle(usage) {
44
+ const output = billedOutputTokens(usage);
45
+ const actual = quote(bound, usage.promptTokens, output);
46
+ await channels.settleSpend(id, attempt, actual);
47
+ },
48
+ };
49
+ },
50
+ },
51
+ };
52
+ }
53
+ //# sourceMappingURL=inference-guard.js.map