@datalayer/agent-runtimes 1.3.111 → 1.3.113

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 (44) hide show
  1. package/dist-embed/datalayer-app-main.js +4 -4
  2. package/lib/apps/apps/appspec.js +2 -0
  3. package/lib/apps/apps/checks.js +10 -6
  4. package/lib/examples/PapSignInDisplayExample.d.ts +4 -0
  5. package/lib/examples/PapSignInDisplayExample.js +23 -0
  6. package/lib/examples/example-selector.js +1 -0
  7. package/lib/examples/index.d.ts +1 -0
  8. package/lib/examples/index.js +1 -0
  9. package/lib/examples/main.js +2 -0
  10. package/lib/examples/pap/PapSignInDisplayView.d.ts +15 -0
  11. package/lib/examples/pap/PapSignInDisplayView.js +49 -0
  12. package/lib/examples/pap/PapTraceView.d.ts +2 -3
  13. package/lib/examples/pap/PapTraceView.js +3 -4
  14. package/lib/examples/pap/papSignInCases.d.ts +12 -0
  15. package/lib/examples/pap/papSignInCases.js +88 -0
  16. package/lib/protocols/a2a/index.d.ts +52 -0
  17. package/lib/protocols/a2a/index.js +164 -0
  18. package/lib/protocols/agentProtocols.d.ts +15 -0
  19. package/lib/protocols/agentProtocols.js +47 -0
  20. package/lib/protocols/agentic-commerce/index.d.ts +86 -0
  21. package/lib/protocols/agentic-commerce/index.js +267 -0
  22. package/lib/protocols/agentic-commerce/models.d.ts +3292 -0
  23. package/lib/protocols/agentic-commerce/models.js +10 -0
  24. package/lib/protocols/commerce.d.ts +42 -0
  25. package/lib/protocols/commerce.js +47 -0
  26. package/lib/protocols/core.d.ts +44 -0
  27. package/lib/protocols/core.js +62 -0
  28. package/lib/protocols/mcp/index.d.ts +4 -0
  29. package/lib/protocols/mcp/index.js +37 -0
  30. package/lib/protocols/pact/index.d.ts +141 -0
  31. package/lib/protocols/pact/index.js +363 -0
  32. package/lib/protocols/ucp/index.d.ts +79 -0
  33. package/lib/protocols/ucp/index.js +277 -0
  34. package/lib/protocols/web-bot-auth/index.d.ts +161 -0
  35. package/lib/protocols/web-bot-auth/index.js +427 -0
  36. package/lib/specs/actions.js +4 -0
  37. package/lib/specs/apps.d.ts +2 -0
  38. package/lib/specs/apps.js +481 -0
  39. package/lib/specs/appspecSchema.js +200 -2
  40. package/lib/types/agentspecs.d.ts +25 -0
  41. package/package.json +4 -1
  42. package/scripts/codegen/generate_agentic_commerce.sh +74 -0
  43. package/scripts/codegen/generate_apps.py +11 -0
  44. package/scripts/validate_pap_examples.py +27 -2
@@ -0,0 +1,427 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * Web Bot Auth: an agent's requests signed, and verified
7
+ * (AGENT_PROTOCOLS PR-20 to PR-23).
8
+ *
9
+ * Implements `draft-ietf-webbotauth-httpsig-protocol-00` (2026-09-01) by
10
+ * wrapping Cloudflare's `web-bot-auth` 0.2.0, which reproduces the draft's
11
+ * Ed25519 vector E.2.1 byte for byte. What is added here is what an agent
12
+ * runtime needs around it:
13
+ *
14
+ * - a signer that is a callable the host owns (`protocols.httpSigner`; Vault
15
+ * transit in Datalayer): the key never reaches the runtime;
16
+ * - `signedFetch`, a `fetch` that signs every request it sends;
17
+ * - the key directory and its signed response (Appendix B);
18
+ * - verification with keys found by (agent URL, keyid), freshness and an
19
+ * atomic nonce check, answering `verified`, `invalid` or `unverified`.
20
+ *
21
+ * The Python package `agent_runtimes.protocols.web_bot_auth` is the same
22
+ * protocol, and both read `fixtures/vectors.json` (PR-51).
23
+ *
24
+ * @module protocols/web-bot-auth
25
+ */
26
+ import { HTTP_MESSAGE_SIGNATURES_DIRECTORY, HTTP_MESSAGE_SIGNATURE_TAG, SIGNATURE_AGENT_HEADER, generateNonce, jwkToKeyID, parseSignatureAgentHeader, sign, verify, } from 'web-bot-auth';
27
+ import { verifierFromJWK } from 'web-bot-auth/crypto';
28
+ import { contribution, defineContributionPoint, definePlugin, } from '@datalayer/reactor';
29
+ import { DEFAULT_ORDER, ProtocolDescriptions, firstContribution, optionalContribution, } from '../core';
30
+ /** The specification implemented. */
31
+ export const WEB_BOT_AUTH_SPECIFICATION = 'draft-ietf-webbotauth-httpsig-protocol-00';
32
+ /** Where an agent's key directory is served. */
33
+ export const DIRECTORY_PATH = HTTP_MESSAGE_SIGNATURES_DIRECTORY;
34
+ /** The directory's media type. */
35
+ export const DIRECTORY_MEDIA_TYPE = 'application/http-message-signatures-directory+json';
36
+ /** The tag of a directory response's signature. */
37
+ export const DIRECTORY_TAG = 'http-message-signatures-directory';
38
+ export const TAG = HTTP_MESSAGE_SIGNATURE_TAG;
39
+ export { generateNonce };
40
+ function base64url(bytes) {
41
+ return base64(bytes)
42
+ .replace(/\+/g, '-')
43
+ .replace(/\//g, '_')
44
+ .replace(/=+$/, '');
45
+ }
46
+ /** The base64url SHA-256 thumbprint of a public JWK (RFC 7638, RFC 8037): the draft's `keyid`. */
47
+ export function jwkThumbprint(jwk) {
48
+ return jwkToKeyID(jwk, (data) => crypto.subtle.digest('SHA-256', data), (hash) => base64url(new Uint8Array(hash)));
49
+ }
50
+ function toWebBotSigner(signer) {
51
+ return {
52
+ keyid: signer.keyid,
53
+ algorithm: signer.algorithm,
54
+ sign: (data) => signer.sign(data),
55
+ };
56
+ }
57
+ /** A signer whose key is in this page or process: tests and examples only. */
58
+ export async function localSigner(privateJwk) {
59
+ if (privateJwk.kty !== 'OKP' ||
60
+ privateJwk.crv !== 'Ed25519' ||
61
+ !privateJwk.d) {
62
+ throw new Error('a local signer takes an Ed25519 private JWK');
63
+ }
64
+ const key = await crypto.subtle.importKey('jwk', privateJwk, { name: 'Ed25519' }, false, ['sign']);
65
+ const keyid = await jwkThumbprint(publicJwkOf(privateJwk));
66
+ return {
67
+ keyid,
68
+ algorithm: 'ed25519',
69
+ async sign(data) {
70
+ return new Uint8Array(await crypto.subtle.sign({ name: 'Ed25519' }, key, data));
71
+ },
72
+ };
73
+ }
74
+ export async function generateLocalSigner() {
75
+ const pair = (await crypto.subtle.generateKey({ name: 'Ed25519' }, true, [
76
+ 'sign',
77
+ 'verify',
78
+ ]));
79
+ const privateJwk = await crypto.subtle.exportKey('jwk', pair.privateKey);
80
+ const signer = await localSigner(privateJwk);
81
+ return {
82
+ signer,
83
+ publicJwk: { ...publicJwkOf(privateJwk), kid: signer.keyid },
84
+ };
85
+ }
86
+ /** The public members of a JWK. */
87
+ export function publicJwkOf(jwk) {
88
+ if (jwk.kty === 'OKP') {
89
+ return { kty: 'OKP', crv: jwk.crv, x: jwk.x };
90
+ }
91
+ if (jwk.kty === 'EC') {
92
+ return { kty: 'EC', crv: jwk.crv, x: jwk.x, y: jwk.y };
93
+ }
94
+ throw new Error(`key type ${jwk.kty} is not supported`);
95
+ }
96
+ /**
97
+ * Sign one request: the `Signature-Agent` member, `@authority`, and —
98
+ * narrower than the draft's minimum, as its §5.2 advises — `@method` and
99
+ * `@path`.
100
+ */
101
+ export async function signRequest(request, signer, options) {
102
+ if (!options.agent.startsWith('https://')) {
103
+ throw new Error('a Signature-Agent is an https URI');
104
+ }
105
+ const label = options.label ?? 'sig1';
106
+ const discovery = options.discovery ?? 'directory';
107
+ const agentHeader = `${label}="${options.agent}"` +
108
+ (discovery === 'directory' ? '' : `;type=${discovery}`);
109
+ const created = options.created ?? new Date();
110
+ const lifetime = options.lifetime ?? 60;
111
+ if (lifetime > 24 * 60 * 60) {
112
+ throw new Error('a Web Bot Auth signature lives at most 24 hours');
113
+ }
114
+ const expires = options.expires ?? new Date(created.getTime() + lifetime * 1000);
115
+ const headers = new Headers(request.headers);
116
+ headers.set(SIGNATURE_AGENT_HEADER, agentHeader);
117
+ const withAgent = new Request(request, { headers });
118
+ const fields = await sign(withAgent, {
119
+ signer: toWebBotSigner(signer),
120
+ created,
121
+ expires,
122
+ label,
123
+ nonce: options.nonce ?? generateNonce(),
124
+ signatureAgentKey: label,
125
+ additionalComponents: (options.components ?? ['@method', '@path', '@query']).filter(name => name !== '@authority'),
126
+ });
127
+ return {
128
+ 'Signature-Agent': agentHeader,
129
+ 'Signature-Input': fields.signatureInput,
130
+ Signature: fields.signature,
131
+ };
132
+ }
133
+ /** A `fetch` that signs every request it sends. */
134
+ export function signedFetch(signer, options, fetchImpl = fetch) {
135
+ return async (input, init) => {
136
+ const request = new Request(input, init);
137
+ const signed = await signRequest(request, signer, options);
138
+ const headers = new Headers(request.headers);
139
+ for (const [name, value] of Object.entries(signed)) {
140
+ headers.set(name, value);
141
+ }
142
+ return fetchImpl(new Request(request, { headers }));
143
+ };
144
+ }
145
+ /** The directory's body: public keys only, each `kid` its thumbprint. */
146
+ export async function directoryBody(keys) {
147
+ const out = [];
148
+ for (const jwk of keys) {
149
+ if ('d' in jwk && jwk.d) {
150
+ throw new Error('a key directory publishes public keys only');
151
+ }
152
+ const kid = await jwkThumbprint(publicJwkOf(jwk));
153
+ if (jwk.kid &&
154
+ jwk.kid !== kid) {
155
+ throw new Error("a directory key's kid is its thumbprint");
156
+ }
157
+ out.push({ ...jwk, kid });
158
+ }
159
+ return JSON.stringify({ keys: out });
160
+ }
161
+ function base64(bytes) {
162
+ let text = '';
163
+ for (const byte of bytes) {
164
+ text += String.fromCharCode(byte);
165
+ }
166
+ return btoa(text);
167
+ }
168
+ /** The `Content-Digest` of a body, SHA-256 (RFC 9530). */
169
+ export async function contentDigest(body) {
170
+ const bytes = typeof body === 'string' ? new TextEncoder().encode(body) : body;
171
+ const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));
172
+ return `sha-256=:${base64(digest)}:`;
173
+ }
174
+ function authorityOf(url) {
175
+ return new URL(url).host.toLowerCase();
176
+ }
177
+ /**
178
+ * Sign a directory response with every key it lists (Appendix B): the
179
+ * signature covers `"@authority";req` and `content-digest`.
180
+ */
181
+ export async function signDirectoryResponse(body, authorityUrl, signers, options = {}) {
182
+ if (!signers.length) {
183
+ throw new Error('a directory is signed by the keys it publishes');
184
+ }
185
+ const listed = new Set(JSON.parse(body).keys.map(key => key.kid));
186
+ const created = options.created ?? Math.floor(Date.now() / 1000);
187
+ const expires = options.expires ?? created + (options.lifetime ?? 300);
188
+ const digest = await contentDigest(body);
189
+ const inputs = [];
190
+ const signatures = [];
191
+ for (const [index, signer] of signers.entries()) {
192
+ if (!listed.has(signer.keyid)) {
193
+ throw new Error(`key ${signer.keyid} signs a directory that does not list it`);
194
+ }
195
+ const label = index === 0 ? 'binding' : `binding${index}`;
196
+ const params = `("@authority";req "content-digest");created=${created};expires=${expires}` +
197
+ `;keyid="${signer.keyid}";tag="${DIRECTORY_TAG}"`;
198
+ const base = `"@authority";req: ${authorityOf(authorityUrl)}\n` +
199
+ `"content-digest": ${digest}\n` +
200
+ `"@signature-params": ${params}`;
201
+ const signature = await signer.sign(new TextEncoder().encode(base));
202
+ inputs.push(`${label}=${params}`);
203
+ signatures.push(`${label}=:${base64(signature)}:`);
204
+ }
205
+ return {
206
+ 'Content-Type': DIRECTORY_MEDIA_TYPE,
207
+ 'Content-Digest': digest,
208
+ 'Signature-Input': inputs.join(', '),
209
+ Signature: signatures.join(', '),
210
+ };
211
+ }
212
+ /** A nonce store for one page or process. */
213
+ export class InMemoryNonceStore {
214
+ seen = new Map();
215
+ async checkAndRecord(scope, nonce, until) {
216
+ const now = Date.now();
217
+ for (const [key, end] of this.seen) {
218
+ if (end < now) {
219
+ this.seen.delete(key);
220
+ }
221
+ }
222
+ const key = `${scope} ${nonce}`;
223
+ if (this.seen.has(key)) {
224
+ return false;
225
+ }
226
+ this.seen.set(key, until.getTime());
227
+ return true;
228
+ }
229
+ }
230
+ async function fetchChecked(fetchImpl, url, insecureHosts) {
231
+ const parsed = new URL(url);
232
+ if (parsed.protocol !== 'https:' &&
233
+ !insecureHosts.includes(parsed.hostname)) {
234
+ throw new Error('a key directory is fetched over https');
235
+ }
236
+ const response = await fetchImpl(url, {
237
+ headers: { Accept: DIRECTORY_MEDIA_TYPE },
238
+ redirect: 'manual',
239
+ });
240
+ if (response.status !== 200) {
241
+ throw new Error(`the key directory answered ${response.status}`);
242
+ }
243
+ return response;
244
+ }
245
+ /** `type=directory`: the origin's well-known directory, its Content-Digest checked. */
246
+ export function directoryDiscovery(fetchImpl = (input, init) => fetch(input, init), insecureHosts = []) {
247
+ return {
248
+ async keys(agent) {
249
+ const origin = new URL(agent);
250
+ const url = `${origin.protocol}//${origin.host}${DIRECTORY_PATH}`;
251
+ const response = await fetchChecked(fetchImpl, url, insecureHosts);
252
+ const body = await response.text();
253
+ if (response.headers.get('content-digest') !== (await contentDigest(body))) {
254
+ throw new Error("the directory's Content-Digest does not match its body");
255
+ }
256
+ return JSON.parse(body).keys.map(publicJwkOf);
257
+ },
258
+ };
259
+ }
260
+ /** `type=jwks_uri`: a JWK Set at the URL itself. */
261
+ export function jwksUriDiscovery(fetchImpl = (input, init) => fetch(input, init), insecureHosts = []) {
262
+ return {
263
+ async keys(agent) {
264
+ const response = await fetchChecked(fetchImpl, agent, insecureHosts);
265
+ return (await response.json()).keys.map(publicJwkOf);
266
+ },
267
+ };
268
+ }
269
+ /** A resolver over one discovery per `Signature-Agent` type; a type with none answers nothing. */
270
+ export function keyResolver(discoveries) {
271
+ return async (agent, discovery) => {
272
+ const found = discoveries[discovery];
273
+ if (!found) {
274
+ return undefined;
275
+ }
276
+ try {
277
+ const keys = new Map();
278
+ for (const jwk of await found.keys(agent)) {
279
+ keys.set(await jwkThumbprint(jwk), jwk);
280
+ }
281
+ return keys;
282
+ }
283
+ catch {
284
+ return undefined;
285
+ }
286
+ };
287
+ }
288
+ /** The two discoveries this package implements, over one fetch. */
289
+ export function directoryResolver(fetchImpl = (input, init) => fetch(input, init), insecureHosts = []) {
290
+ return keyResolver({
291
+ directory: directoryDiscovery(fetchImpl, insecureHosts),
292
+ jwks_uri: jwksUriDiscovery(fetchImpl, insecureHosts),
293
+ });
294
+ }
295
+ export const WEB_BOT_AUTH_PLUGIN = 'agent-runtimes.protocols.web-bot-auth';
296
+ /** The host's signer: no default, the key is the host's. */
297
+ export const WebBotAuthSigners = defineContributionPoint('web_bot_auth.signer');
298
+ export const WebBotAuthNonceStores = defineContributionPoint('web_bot_auth.nonceStore');
299
+ export const WebBotAuthDirectoryDiscoveries = defineContributionPoint('web_bot_auth.extension.directory');
300
+ export const WebBotAuthJwksUriDiscoveries = defineContributionPoint('web_bot_auth.extension.jwks_uri');
301
+ export const WebBotAuthCimdDiscoveries = defineContributionPoint('web_bot_auth.extension.cimd');
302
+ const DISCOVERY_POINTS = {
303
+ directory: WebBotAuthDirectoryDiscoveries,
304
+ jwks_uri: WebBotAuthJwksUriDiscoveries,
305
+ cimd: WebBotAuthCimdDiscoveries,
306
+ };
307
+ const WEB_BOT_AUTH_POINTS = [
308
+ WebBotAuthSigners,
309
+ WebBotAuthNonceStores,
310
+ ...Object.values(DISCOVERY_POINTS),
311
+ ];
312
+ export const WebBotAuthPlugin = definePlugin({
313
+ name: WEB_BOT_AUTH_PLUGIN,
314
+ version: '1.0.0',
315
+ description: `Web Bot Auth (${WEB_BOT_AUTH_SPECIFICATION}): sign an agent's requests, verify an agent's.`,
316
+ contributionPoints: WEB_BOT_AUTH_POINTS,
317
+ contributes: [
318
+ contribution(ProtocolDescriptions, {
319
+ id: 'web_bot_auth',
320
+ name: 'Web Bot Auth (HTTP Message Signatures for automated traffic)',
321
+ layer: 'identity',
322
+ version: WEB_BOT_AUTH_SPECIFICATION,
323
+ roles: ['signer', 'verifier', 'directory'],
324
+ status: 'implemented',
325
+ specification: 'https://datatracker.ietf.org/doc/draft-ietf-webbotauth-httpsig-protocol/',
326
+ plugin: WEB_BOT_AUTH_PLUGIN,
327
+ points: WEB_BOT_AUTH_POINTS.map(point => point.id),
328
+ }, { id: 'web_bot_auth', order: DEFAULT_ORDER }),
329
+ contribution(WebBotAuthDirectoryDiscoveries, directoryDiscovery(), {
330
+ id: 'fetch',
331
+ order: DEFAULT_ORDER,
332
+ }),
333
+ contribution(WebBotAuthJwksUriDiscoveries, jwksUriDiscovery(), {
334
+ id: 'fetch',
335
+ order: DEFAULT_ORDER,
336
+ }),
337
+ ],
338
+ // One nonce store per reactor, not one per module: a store is state.
339
+ register: ctx => {
340
+ ctx.contribute(WebBotAuthNonceStores, new InMemoryNonceStore(), {
341
+ id: 'memory',
342
+ order: DEFAULT_ORDER,
343
+ });
344
+ },
345
+ });
346
+ /** The host's signer; throws when the host contributed none. */
347
+ export function signerOf(reactor) {
348
+ return firstContribution(reactor, WebBotAuthSigners, 'Web Bot Auth signer');
349
+ }
350
+ /** A resolver over the discoveries contributed: one per type that has one. */
351
+ export function resolverOf(reactor) {
352
+ const discoveries = {};
353
+ for (const [type, point] of Object.entries(DISCOVERY_POINTS)) {
354
+ const found = optionalContribution(reactor, point, `Web Bot Auth ${type} discovery`);
355
+ if (found) {
356
+ discoveries[type] = found;
357
+ }
358
+ }
359
+ return keyResolver(discoveries);
360
+ }
361
+ /**
362
+ * Verify the Web Bot Auth signature of one request. Keys are found by the
363
+ * pair (the covered `Signature-Agent` URL, `keyid`), never by `keyid` alone.
364
+ */
365
+ export async function verifyRequest(request, resolveKeys, options = {}) {
366
+ if (!request.headers.get('signature') ||
367
+ !request.headers.get('signature-input')) {
368
+ return { outcome: 'unverified', reason: 'the request is not signed' };
369
+ }
370
+ const agentHeader = request.headers.get(SIGNATURE_AGENT_HEADER);
371
+ if (!agentHeader) {
372
+ return {
373
+ outcome: 'invalid',
374
+ reason: 'the request names no Signature-Agent',
375
+ };
376
+ }
377
+ let unresolved;
378
+ try {
379
+ const verified = await verify(request, {
380
+ now: options.now,
381
+ clockSkew: options.clockSkew,
382
+ resolver: async (candidate) => {
383
+ const entry = candidate.signatureAgent;
384
+ if (!entry) {
385
+ throw new Error('the signature does not cover its Signature-Agent member');
386
+ }
387
+ const keys = await resolveKeys(entry.uri, entry.type);
388
+ const jwk = keys?.get(candidate.keyid);
389
+ if (!jwk) {
390
+ unresolved = entry.uri;
391
+ throw new Error('the key is not in the agent’s directory');
392
+ }
393
+ return (await verifierFromJWK(jwk));
394
+ },
395
+ });
396
+ const agent = verified.signatureAgent?.uri;
397
+ if (verified.nonce && options.nonceStore) {
398
+ const fresh = await options.nonceStore.checkAndRecord(`${agent} ${verified.keyid}`, verified.nonce, verified.expires);
399
+ if (!fresh) {
400
+ return {
401
+ outcome: 'invalid',
402
+ reason: 'the nonce was seen before',
403
+ agent,
404
+ };
405
+ }
406
+ }
407
+ return {
408
+ outcome: 'verified',
409
+ agent,
410
+ keyid: verified.keyid,
411
+ created: verified.created,
412
+ expires: verified.expires,
413
+ nonce: verified.nonce,
414
+ };
415
+ }
416
+ catch (error) {
417
+ if (unresolved) {
418
+ return {
419
+ outcome: 'unverified',
420
+ reason: 'the key is not in the agent’s directory',
421
+ agent: unresolved,
422
+ };
423
+ }
424
+ return { outcome: 'invalid', reason: error.message };
425
+ }
426
+ }
427
+ export { parseSignatureAgentHeader };
@@ -739,6 +739,7 @@ export const APP_BEHAVIOURS = {
739
739
  'earthdata.search_earth_datasets': 'do_it',
740
740
  },
741
741
  'event-response': {},
742
+ 'exchange-desk': {},
742
743
  'inbox-triage': {
743
744
  'google-workspace.append_table_rows': 'leave_to_me',
744
745
  'google-workspace.batch_modify_gmail_message_labels': 'do_it',
@@ -913,6 +914,7 @@ export const APP_BEHAVIOURS = {
913
914
  'odoo-accounting.odoo_accounting_trial_balance': 'do_it',
914
915
  'odoo-accounting.odoo_accounting_unreconcile_lines': 'leave_to_me',
915
916
  },
917
+ 'order-desk': {},
916
918
  'pipeline-report': {},
917
919
  'quote-calculator': {},
918
920
  'report-from-a-file': {},
@@ -941,6 +943,7 @@ export const APP_ESCALATIONS = {
941
943
  decide: {},
942
944
  'disaster-assessment': {},
943
945
  'event-response': {},
946
+ 'exchange-desk': {},
944
947
  'inbox-triage': {
945
948
  'google-workspace.batch_modify_gmail_message_labels': [
946
949
  {
@@ -977,6 +980,7 @@ export const APP_ESCALATIONS = {
977
980
  },
978
981
  'model-choice': {},
979
982
  'month-end-close': {},
983
+ 'order-desk': {},
980
984
  'pipeline-report': {},
981
985
  'quote-calculator': {},
982
986
  'report-from-a-file': {},
@@ -16,9 +16,11 @@ export declare const DATA_QUALITY_APP_0_0_1: AppSpec;
16
16
  export declare const DECIDE_APP_0_0_1: AppSpec;
17
17
  export declare const DISASTER_ASSESSMENT_APP_0_0_1: AppSpec;
18
18
  export declare const EVENT_RESPONSE_APP_0_0_1: AppSpec;
19
+ export declare const EXCHANGE_DESK_APP_0_0_1: AppSpec;
19
20
  export declare const INBOX_TRIAGE_APP_0_0_1: AppSpec;
20
21
  export declare const MODEL_CHOICE_APP_0_0_1: AppSpec;
21
22
  export declare const MONTH_END_CLOSE_APP_0_0_1: AppSpec;
23
+ export declare const ORDER_DESK_APP_0_0_1: AppSpec;
22
24
  export declare const PIPELINE_REPORT_APP_0_0_1: AppSpec;
23
25
  export declare const QUOTE_CALCULATOR_APP_0_0_1: AppSpec;
24
26
  export declare const REPORT_FROM_A_FILE_APP_0_0_1: AppSpec;