@vertesia/client 1.6.0-dev.20261014.015055Z → 1.6.0-dev.20261022.120426Z

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 (82) hide show
  1. package/lib/ApiKeysApi.d.ts +4 -7
  2. package/lib/ApiKeysApi.d.ts.map +1 -1
  3. package/lib/ApiKeysApi.js +5 -4
  4. package/lib/ApiKeysApi.js.map +1 -1
  5. package/lib/AppsApi.d.ts +35 -2
  6. package/lib/AppsApi.d.ts.map +1 -1
  7. package/lib/AppsApi.js +76 -1
  8. package/lib/AppsApi.js.map +1 -1
  9. package/lib/InteractionsApi.d.ts +3 -1
  10. package/lib/InteractionsApi.d.ts.map +1 -1
  11. package/lib/InteractionsApi.js +2 -1
  12. package/lib/InteractionsApi.js.map +1 -1
  13. package/lib/OAuthServerApi.d.ts +7 -1
  14. package/lib/OAuthServerApi.d.ts.map +1 -1
  15. package/lib/OAuthServerApi.js +21 -0
  16. package/lib/OAuthServerApi.js.map +1 -1
  17. package/lib/OidcFederationApi.d.ts +11 -0
  18. package/lib/OidcFederationApi.d.ts.map +1 -0
  19. package/lib/OidcFederationApi.js +22 -0
  20. package/lib/OidcFederationApi.js.map +1 -0
  21. package/lib/RunsApi.d.ts +3 -2
  22. package/lib/RunsApi.d.ts.map +1 -1
  23. package/lib/RunsApi.js +3 -2
  24. package/lib/RunsApi.js.map +1 -1
  25. package/lib/client.d.ts +2 -0
  26. package/lib/client.d.ts.map +1 -1
  27. package/lib/client.js +3 -0
  28. package/lib/client.js.map +1 -1
  29. package/lib/execute.d.ts +16 -2
  30. package/lib/execute.d.ts.map +1 -1
  31. package/lib/execute.js +13 -2
  32. package/lib/execute.js.map +1 -1
  33. package/lib/index.d.ts +2 -0
  34. package/lib/index.d.ts.map +1 -1
  35. package/lib/index.js +1 -0
  36. package/lib/index.js.map +1 -1
  37. package/lib/nodejs/NodeStreamSource.d.ts +1 -0
  38. package/lib/nodejs/NodeStreamSource.d.ts.map +1 -1
  39. package/lib/nodejs/NodeStreamSource.js +17 -15
  40. package/lib/nodejs/NodeStreamSource.js.map +1 -1
  41. package/lib/store/AgentsApi.d.ts +3 -0
  42. package/lib/store/AgentsApi.d.ts.map +1 -1
  43. package/lib/store/AgentsApi.js +7 -1
  44. package/lib/store/AgentsApi.js.map +1 -1
  45. package/lib/store/FilesApi.d.ts.map +1 -1
  46. package/lib/store/FilesApi.js +8 -2
  47. package/lib/store/FilesApi.js.map +1 -1
  48. package/lib/store/IndexingApi.d.ts +0 -6
  49. package/lib/store/IndexingApi.d.ts.map +1 -1
  50. package/lib/store/IndexingApi.js +3 -17
  51. package/lib/store/IndexingApi.js.map +1 -1
  52. package/lib/store/ObjectsApi.d.ts +0 -6
  53. package/lib/store/ObjectsApi.d.ts.map +1 -1
  54. package/lib/store/ObjectsApi.js +2 -16
  55. package/lib/store/ObjectsApi.js.map +1 -1
  56. package/lib/store/ProcessApi.d.ts +4 -0
  57. package/lib/store/ProcessApi.d.ts.map +1 -1
  58. package/lib/store/ProcessApi.js +8 -1
  59. package/lib/store/ProcessApi.js.map +1 -1
  60. package/lib/store/zeno-bulk.d.ts +9 -0
  61. package/lib/store/zeno-bulk.d.ts.map +1 -0
  62. package/lib/store/zeno-bulk.js +26 -0
  63. package/lib/store/zeno-bulk.js.map +1 -0
  64. package/lib/vertesia-client.js +4 -4
  65. package/lib/vertesia-client.js.map +1 -1
  66. package/package.json +13 -8
  67. package/src/ApiKeysApi.ts +4 -12
  68. package/src/AppsApi.ts +106 -0
  69. package/src/InteractionsApi.ts +9 -2
  70. package/src/OAuthServerApi.ts +31 -0
  71. package/src/OidcFederationApi.ts +23 -0
  72. package/src/RunsApi.ts +7 -2
  73. package/src/client.ts +3 -0
  74. package/src/execute.ts +30 -3
  75. package/src/index.ts +2 -0
  76. package/src/nodejs/NodeStreamSource.ts +28 -24
  77. package/src/store/AgentsApi.ts +10 -1
  78. package/src/store/FilesApi.ts +8 -2
  79. package/src/store/IndexingApi.ts +3 -18
  80. package/src/store/ObjectsApi.ts +2 -17
  81. package/src/store/ProcessApi.ts +8 -1
  82. package/src/store/zeno-bulk.ts +30 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/client",
3
- "version": "1.6.0-dev.20261014.015055Z",
3
+ "version": "1.6.0-dev.20261022.120426Z",
4
4
  "type": "module",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -12,16 +12,16 @@
12
12
  ],
13
13
  "license": "Apache-2.0",
14
14
  "devDependencies": {
15
- "@types/node": "^24.19.1",
15
+ "@types/node": "^24.19.2",
16
16
  "@vertesia/tsconfig": "0.1.0",
17
- "rolldown": "1.2.12",
17
+ "rolldown": "1.2.14",
18
18
  "typescript": "7.0.2",
19
- "vitest": "5.0.3"
19
+ "vitest": "5.0.4"
20
20
  },
21
21
  "dependencies": {
22
- "@llumiverse/common": "1.6.0-dev.20261014.012028Z",
23
- "@vertesia/api-fetch-client": "1.6.0-dev.20261014.015055Z",
24
- "@vertesia/common": "1.6.0-dev.20261014.015055Z",
22
+ "@llumiverse/common": "1.6.0-dev.20261022.120426Z",
23
+ "@vertesia/api-fetch-client": "1.6.0-dev.20261022.120426Z",
24
+ "@vertesia/common": "1.6.0-dev.20261022.120426Z",
25
25
  "eventsource": "^5.1.2"
26
26
  },
27
27
  "exports": {
@@ -49,7 +49,12 @@
49
49
  "ai",
50
50
  "typescript"
51
51
  ],
52
- "gitHead": "468240ecf939f968e1324116be26386cdf5ada49",
52
+ "gitHead": "bcaa7879b601ce9ccc394bcd3257822da13869cd",
53
+ "releaseIdentity": {
54
+ "source_sha": "bcaa7879b601ce9ccc394bcd3257822da13869cd",
55
+ "dependency_sha": "93466e47b06db23ac3c800e248d170afe65e5006",
56
+ "lock_sha256": "117e1c8068aa07d59117323a51ceccfc96cab543e50c03eb7efc19709d6f5e6b"
57
+ },
53
58
  "scripts": {
54
59
  "lint": "biome lint src",
55
60
  "clean:lib": "rimraf ./lib ./tsconfig.tsbuildinfo",
package/src/ApiKeysApi.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ApiTopic, type ClientBase } from '@vertesia/api-fetch-client';
2
2
  import type {
3
3
  ApiKey,
4
- ApiKeyReadResponse,
5
4
  ApiKeyWithValue,
6
5
  AuthTokenResponse,
7
6
  CreateApiKeyPayload,
@@ -42,17 +41,10 @@ export class ApiKeysApi extends ApiTopic {
42
41
  }
43
42
 
44
43
  /**
45
- * Retrieve an ApiKey and its value
46
- * BE VERY CAREFUL USING THIS API AS IT EXPOSE THE API KEY VALUE
47
- * ALL REQUESTS ARE LOGGED IN SECURITY AUDIT LOG
48
- * @returns ApiKeyWithValue
49
- * */
50
- retrieve(id: string, withValue: true): Promise<ApiKeyWithValue>;
51
- retrieve(id: string, withValue?: false): Promise<ApiKey>;
52
- retrieve(id: string, withValue: boolean = false): Promise<ApiKeyReadResponse> {
53
- if (withValue) {
54
- return this.get(`/${id}`, { query: { withValue: true } });
55
- }
44
+ * Retrieve an ApiKey without its value. The raw value is returned only once, by `create`.
45
+ * @returns ApiKey
46
+ */
47
+ retrieve(id: string): Promise<ApiKey> {
56
48
  return this.get(`/${id}`);
57
49
  }
58
50
 
package/src/AppsApi.ts CHANGED
@@ -6,12 +6,16 @@ import type {
6
6
  AppDeleteSummary,
7
7
  AppDevelopmentTaskDetails,
8
8
  AppDevelopmentTaskList,
9
+ AppGithubConfiguration,
10
+ AppGithubRepositoryList,
11
+ AppGithubRepositorySetup,
9
12
  AppInspectionResult,
10
13
  AppInstallation,
11
14
  AppInstallationKind,
12
15
  AppInstallationListEntry,
13
16
  AppInstallationPayload,
14
17
  AppInstallationWithManifest,
18
+ AppInstanceAvailability,
15
19
  AppManifest,
16
20
  AppManifestData,
17
21
  AppPackage,
@@ -29,6 +33,7 @@ import type {
29
33
  CountResult,
30
34
  CreateAppRepoBranchRequest,
31
35
  DeleteAppVersionResponse,
36
+ ExchangeAppGithubAuthorizationResponse,
32
37
  McpApiKeyStatus,
33
38
  ProjectRef,
34
39
  PromoteAppVersionResponse,
@@ -37,6 +42,7 @@ import type {
37
42
  StartAppBuildRequest,
38
43
  StartAppBuildResponse,
39
44
  StartAppDevelopmentTaskRequest,
45
+ StartAppGithubAuthorizationResponse,
40
46
  StartAppScaffoldRequest,
41
47
  StartAppScaffoldResponse,
42
48
  UpdateAppInstallationOAuthApprovalPayload,
@@ -75,6 +81,27 @@ export default class AppsApi extends ApiTopic {
75
81
  * The key is write-only: it is encrypted server-side and never returned by any endpoint,
76
82
  * so the response carries only the configured flag and a masked hint.
77
83
  */
84
+ setInstallationMcpApiKey(installationId: string, collectionId: string, apiKey: string): Promise<McpApiKeyStatus> {
85
+ return this.put(
86
+ `/installations/${encodeURIComponent(installationId)}/collections/${encodeURIComponent(collectionId)}/api-key`,
87
+ {
88
+ payload: { api_key: apiKey },
89
+ },
90
+ );
91
+ }
92
+
93
+ getInstallationMcpApiKeyStatus(installationId: string, collectionId: string): Promise<McpApiKeyStatus> {
94
+ return this.get(
95
+ `/installations/${encodeURIComponent(installationId)}/collections/${encodeURIComponent(collectionId)}/api-key`,
96
+ );
97
+ }
98
+
99
+ deleteInstallationMcpApiKey(installationId: string, collectionId: string): Promise<McpApiKeyStatus> {
100
+ return this.del(
101
+ `/installations/${encodeURIComponent(installationId)}/collections/${encodeURIComponent(collectionId)}/api-key`,
102
+ );
103
+ }
104
+
78
105
  setMcpCollectionApiKey(appId: string, collectionId: string, apiKey: string): Promise<McpApiKeyStatus> {
79
106
  return this.put(`/${encodeURIComponent(appId)}/collections/${encodeURIComponent(collectionId)}/api-key`, {
80
107
  payload: { api_key: apiKey } satisfies SetMcpApiKeyRequest,
@@ -149,6 +176,63 @@ export default class AppsApi extends ApiTopic {
149
176
  return this.get(`/scaffolds/${encodeURIComponent(workflowId)}/${encodeURIComponent(runId)}/progress`);
150
177
  }
151
178
 
179
+ /**
180
+ * Return whether the global App Builder GitHub App is configured, together with its slug and
181
+ * installation URL when available.
182
+ */
183
+ getGithubConfiguration(): Promise<AppGithubConfiguration> {
184
+ return this.get('/source-providers/github');
185
+ }
186
+
187
+ /**
188
+ * Start GitHub user authorization. The server creates a short-lived, single-use OAuth state
189
+ * bound to the current Vertesia user/account/project and returns the GitHub authorization URL.
190
+ */
191
+ startGithubAuthorization(): Promise<StartAppGithubAuthorizationResponse> {
192
+ return this.post('/source-providers/github/authorize', {});
193
+ }
194
+
195
+ /**
196
+ * Complete GitHub authorization with the callback code and state. The server consumes the state,
197
+ * verifies the current user's writable GitHub App installations, discards the GitHub user token,
198
+ * and returns those installations plus an opaque `connection_id`. That id references a
199
+ * short-lived server-side snapshot of the writable repositories verified for this Vertesia
200
+ * user/account/project; selecting a repository consumes it once.
201
+ */
202
+ exchangeGithubAuthorization(code: string, state: string): Promise<ExchangeAppGithubAuthorizationResponse> {
203
+ return this.post('/source-providers/github/exchange', { payload: { code, state } });
204
+ }
205
+
206
+ /**
207
+ * List repositories with push access for one installation verified during authorization. The
208
+ * connection id is scoped to the current Vertesia user/account/project and expires shortly.
209
+ */
210
+ listGithubRepositories(connectionId: string, installationId: string): Promise<AppGithubRepositoryList> {
211
+ return this.get('/source-providers/github/repositories', {
212
+ query: { connection_id: connectionId, installation_id: installationId },
213
+ });
214
+ }
215
+
216
+ /**
217
+ * List branches and verify whether a selected branch can safely receive generated source.
218
+ * The verified connection remains reusable until app creation consumes it.
219
+ */
220
+ getGithubRepositorySetup(
221
+ connectionId: string,
222
+ installationId: string,
223
+ repository: string,
224
+ branch: string,
225
+ ): Promise<AppGithubRepositorySetup> {
226
+ return this.get('/source-providers/github/repository-setup', {
227
+ query: {
228
+ connection_id: connectionId,
229
+ installation_id: installationId,
230
+ repository,
231
+ branch,
232
+ },
233
+ });
234
+ }
235
+
152
236
  /**
153
237
  * Get the list if tools provided by the given app.
154
238
  * @param appId
@@ -328,14 +412,20 @@ export default class AppsApi extends ApiTopic {
328
412
  oauthParams?: Record<string, { client_id?: string; client_secret?: string; scopes?: string[] }>,
329
413
  oauthProviderParams?: Record<string, { client_id?: string; client_secret?: string; scopes?: string[] }>,
330
414
  apiKeyParams?: AppApiKeyCollectionParams,
415
+ mcpParameters?: AppInstallationPayload['mcp_parameters'],
416
+ mcpSharedParameters?: AppInstallationPayload['mcp_shared_parameters'],
417
+ instance?: Pick<AppInstallationPayload, 'instance_key' | 'display_name' | 'namespace_overrides'>,
331
418
  ): Promise<AppInstallation> {
332
419
  return this.post(`/install`, {
333
420
  payload: {
334
421
  app_id: appId,
422
+ ...instance,
335
423
  settings,
336
424
  oauth_params: oauthParams,
337
425
  oauth_provider_params: oauthProviderParams,
338
426
  api_key_params: apiKeyParams,
427
+ mcp_parameters: mcpParameters,
428
+ mcp_shared_parameters: mcpSharedParameters,
339
429
  } satisfies AppInstallationPayload,
340
430
  });
341
431
  }
@@ -353,6 +443,14 @@ export default class AppsApi extends ApiTopic {
353
443
  * get an app unstallation given its name or null if the app is not installed
354
444
  * @returns
355
445
  */
446
+ getAppInstanceAvailability(): Promise<AppInstanceAvailability> {
447
+ return this.get('/instance-availability');
448
+ }
449
+
450
+ getAppInstallation(installationId: string): Promise<AppInstallationWithManifest> {
451
+ return this.get(`/installations/id/${encodeURIComponent(installationId)}`);
452
+ }
453
+
356
454
  getAppInstallationByName(appName: string): Promise<AppInstallationWithManifest | null> {
357
455
  return this.get<AppInstallationWithManifest>(`/installations/name/${appName}`).catch((err: ServerError) => {
358
456
  if (err.status === 404) {
@@ -426,6 +524,14 @@ export default class AppsApi extends ApiTopic {
426
524
  // clear an override). The server uses `'access_control' in payload` to distinguish
427
525
  // "leave unchanged" from "clear", so only spread the key when it was supplied.
428
526
  ...('access_control' in settingsPayload ? { access_control: settingsPayload.access_control } : {}),
527
+ ...('display_name' in settingsPayload ? { display_name: settingsPayload.display_name } : {}),
528
+ ...('namespace_overrides' in settingsPayload
529
+ ? { namespace_overrides: settingsPayload.namespace_overrides }
530
+ : {}),
531
+ ...('mcp_parameters' in settingsPayload ? { mcp_parameters: settingsPayload.mcp_parameters } : {}),
532
+ ...('mcp_shared_parameters' in settingsPayload
533
+ ? { mcp_shared_parameters: settingsPayload.mcp_shared_parameters }
534
+ : {}),
429
535
  } satisfies AppInstallationPayload,
430
536
  });
431
537
  }
@@ -33,7 +33,13 @@ import type {
33
33
  ResolveInteractionQuery,
34
34
  } from '@vertesia/common';
35
35
  import type { VertesiaClient } from './client.js';
36
- import { checkRateLimit, executeInteraction, executeInteractionAsync, executeInteractionByName } from './execute.js';
36
+ import {
37
+ checkRateLimit,
38
+ type ExecuteInteractionRequestOptions,
39
+ executeInteraction,
40
+ executeInteractionAsync,
41
+ executeInteractionByName,
42
+ } from './execute.js';
37
43
  import { InteractionCatalogApi } from './InteractionCatalogApi.js';
38
44
  import { type EnhancedInteractionExecutionResult, enhanceInteractionExecutionResult } from './InteractionOutput.js';
39
45
 
@@ -222,13 +228,14 @@ export default class InteractionsApi extends ApiTopic {
222
228
  * @param nameWithTag
223
229
  * @param payload
224
230
  * @param onChunk
231
+ * @param requestOptions request options, e.g. `{ query: { include_prompt: false } }` to omit the rendered prompt
225
232
  * @returns
226
233
  */
227
234
  async executeByName<ResultT = unknown, ParamsT = unknown>(
228
235
  nameWithTag: string,
229
236
  payload: InteractionExecutionPayload = {},
230
237
  onChunk?: (chunk: string) => void,
231
- requestOptions?: IRequestParams,
238
+ requestOptions?: ExecuteInteractionRequestOptions,
232
239
  ): Promise<EnhancedInteractionExecutionResult<ResultT, ParamsT>> {
233
240
  const r = await executeInteractionByName<ParamsT>(
234
241
  this.client as VertesiaClient,
@@ -9,6 +9,9 @@ import type {
9
9
  OAuthGrantableScopesResponse,
10
10
  OAuthLoginDecisionResponse,
11
11
  OAuthLoginPayload,
12
+ OAuthTokenExchangeRequest,
13
+ OAuthTokenExchangeResponse,
14
+ ProjectRef,
12
15
  } from '@vertesia/common';
13
16
 
14
17
  export default class OAuthServerApi extends ClientBase {
@@ -22,6 +25,30 @@ export default class OAuthServerApi extends ClientBase {
22
25
  this.verboseErrors = parent.verboseErrors;
23
26
  }
24
27
 
28
+ /** Exchange a signed IdP assertion; optional client authentication follows the connection policy. */
29
+ exchangeToken(
30
+ payload: OAuthTokenExchangeRequest,
31
+ basic?: { client_id: string; client_secret: string },
32
+ ): Promise<OAuthTokenExchangeResponse> {
33
+ if (
34
+ basic &&
35
+ (payload.client_secret !== undefined ||
36
+ payload.client_assertion !== undefined ||
37
+ (payload.client_id !== undefined && payload.client_id !== basic.client_id))
38
+ )
39
+ throw new Error('Use exactly one client authentication method');
40
+ return this.post('/token', {
41
+ payload,
42
+ ...(basic
43
+ ? {
44
+ headers: {
45
+ Authorization: `Basic ${btoa(`${encodeURIComponent(basic.client_id)}:${encodeURIComponent(basic.client_secret)}`)}`,
46
+ },
47
+ }
48
+ : {}),
49
+ });
50
+ }
51
+
25
52
  login(requestId: string, payload: OAuthLoginPayload): Promise<OAuthLoginDecisionResponse> {
26
53
  return this.post(`/requests/${encodeURIComponent(requestId)}/login`, { payload });
27
54
  }
@@ -76,6 +103,10 @@ export default class OAuthServerApi extends ClientBase {
76
103
  return this.get(`/requests/${requestId}`);
77
104
  }
78
105
 
106
+ retrieveGrantableProjects(requestId: string): Promise<ProjectRef[]> {
107
+ return this.get(`/requests/${encodeURIComponent(requestId)}/grantable-projects`);
108
+ }
109
+
79
110
  retrieveGrantableScopes(requestId: string, projectId: string): Promise<OAuthGrantableScopesResponse> {
80
111
  return this.get(`/requests/${requestId}/grantable-scopes`, { query: { project_id: projectId } });
81
112
  }
@@ -0,0 +1,23 @@
1
+ import { ApiTopic, type ClientBase } from '@vertesia/api-fetch-client';
2
+ import type { OidcFederationConnection, OidcFederationConnectionPayload, SuccessResponse } from '@vertesia/common';
3
+
4
+ export default class OidcFederationApi extends ApiTopic {
5
+ constructor(parent: ClientBase) {
6
+ super(parent, '/api/v1/oidc-federation');
7
+ }
8
+ retrieve(id: string): Promise<OidcFederationConnection> {
9
+ return this.get(`/${encodeURIComponent(id)}`);
10
+ }
11
+ list(): Promise<OidcFederationConnection[]> {
12
+ return this.get('/');
13
+ }
14
+ create(payload: OidcFederationConnectionPayload): Promise<OidcFederationConnection> {
15
+ return this.post('/', { payload });
16
+ }
17
+ update(id: string, payload: OidcFederationConnectionPayload): Promise<OidcFederationConnection> {
18
+ return this.put(`/${encodeURIComponent(id)}`, { payload });
19
+ }
20
+ remove(id: string): Promise<SuccessResponse> {
21
+ return this.del(`/${encodeURIComponent(id)}`);
22
+ }
23
+ }
package/src/RunsApi.ts CHANGED
@@ -8,6 +8,7 @@ import type {
8
8
  ExecutionRunRef,
9
9
  FindPayload,
10
10
  FindRunResult,
11
+ GetRunQuery,
11
12
  InteractionExecutionResult,
12
13
  PopulatedExecutionRun,
13
14
  RunClonePayload,
@@ -67,10 +68,14 @@ export class RunsApi extends ApiTopic {
67
68
  * Get a run by id
68
69
  *
69
70
  * @param id
71
+ * @param query response options, e.g. `{ include_prompt: false }` to omit the rendered prompt
70
72
  * @returns InteractionResult
71
73
  **/
72
- async retrieve<ResultT = unknown, ParamsT = unknown>(id: string): Promise<EnhancedExecutionRun<ResultT, ParamsT>> {
73
- const r = await this.get<ExecutionRun<ParamsT>>(`/${id}`);
74
+ async retrieve<ResultT = unknown, ParamsT = unknown>(
75
+ id: string,
76
+ query?: GetRunQuery,
77
+ ): Promise<EnhancedExecutionRun<ResultT, ParamsT>> {
78
+ const r = await this.get<ExecutionRun<ParamsT>>(`/${id}`, { query });
74
79
  return enhanceExecutionRun<ResultT, ParamsT>(r);
75
80
  }
76
81
 
package/src/client.ts CHANGED
@@ -18,6 +18,7 @@ import OAuthClientsApi from './OAuthClientsApi.js';
18
18
  import OAuthGrantsApi from './OAuthGrantsApi.js';
19
19
  import OAuthProvidersApi from './OAuthProvidersApi.js';
20
20
  import OAuthServerApi from './OAuthServerApi.js';
21
+ import OidcFederationApi from './OidcFederationApi.js';
21
22
  import ProjectsApi from './ProjectsApi.js';
22
23
  import PromptsApi from './PromptsApi.js';
23
24
  import QuotaApi from './QuotaApi.js';
@@ -120,6 +121,7 @@ export class VertesiaClient extends AbstractFetchClient<VertesiaClient> {
120
121
  */
121
122
  tokenServerUrl: string;
122
123
 
124
+ oidcFederation: OidcFederationApi;
123
125
  oauthClients: OAuthClientsApi;
124
126
  oauthGrants: OAuthGrantsApi;
125
127
  oauthServer: OAuthServerApi;
@@ -251,6 +253,7 @@ export class VertesiaClient extends AbstractFetchClient<VertesiaClient> {
251
253
  this.onRequest = opts.onRequest;
252
254
  this.onResponse = opts.onResponse;
253
255
  this.sessionTags = opts.sessionTags;
256
+ this.oidcFederation = new OidcFederationApi(this);
254
257
  this.oauthClients = new OAuthClientsApi(this);
255
258
  this.oauthGrants = new OAuthGrantsApi(this);
256
259
  this.oauthServer = new OAuthServerApi(this, this.tokenServerUrl);
package/src/execute.ts CHANGED
@@ -2,6 +2,7 @@ import type { IRequestParams } from '@vertesia/api-fetch-client';
2
2
  import {
3
3
  type AsyncExecutionPayload,
4
4
  type AsyncExecutionResult,
5
+ type ExecuteNamedInteractionQuery,
5
6
  ExecutionRunStatus,
6
7
  type InteractionExecutionPayload,
7
8
  type InteractionExecutionResult,
@@ -23,6 +24,20 @@ const INTERACTION_EXECUTION_TIMEOUT_MS = (() => {
23
24
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 30 * 60 * 1000; // 30 minutes
24
25
  })();
25
26
 
27
+ /**
28
+ * Per-request options for a synchronous named-interaction execution: the request options this method
29
+ * can honour, plus the published execute query.
30
+ *
31
+ * `reader` and `jsonPayload` are left out: the method parses the JSON execution result and serializes
32
+ * the payload it builds, so either would break the call. `query` is the typed execute query instead of
33
+ * a free-form record, so a misspelt parameter fails to compile rather than being ignored by the server.
34
+ * With `onChunk`, the options apply to the initial request only; the stream that follows is a separate
35
+ * connection.
36
+ */
37
+ export type ExecuteInteractionRequestOptions = Omit<IRequestParams, 'reader' | 'jsonPayload' | 'query'> & {
38
+ query?: ExecuteNamedInteractionQuery;
39
+ };
40
+
26
41
  export async function EventSourceProvider(): Promise<typeof EventSource> {
27
42
  if (typeof globalThis.EventSource === 'function') {
28
43
  return globalThis.EventSource;
@@ -81,6 +96,7 @@ export async function executeInteraction<P = unknown>(
81
96
  * @param interaction
82
97
  * @param payload
83
98
  * @param onChunk
99
+ * @param requestOptions request options, e.g. `{ query: { include_prompt: false } }` to omit the rendered prompt
84
100
  * @returns
85
101
  */
86
102
  export async function executeInteractionByName<P = unknown>(
@@ -88,7 +104,7 @@ export async function executeInteractionByName<P = unknown>(
88
104
  interaction: string,
89
105
  payload: InteractionExecutionPayload = {},
90
106
  onChunk?: (chunk: string) => void,
91
- requestOptions?: IRequestParams,
107
+ requestOptions?: ExecuteInteractionRequestOptions,
92
108
  ): Promise<InteractionExecutionResult<P>> {
93
109
  const stream = !!onChunk;
94
110
  const response = await client.post<InteractionExecutionResult<P>>('/api/v1/execute', {
@@ -98,8 +114,14 @@ export async function executeInteractionByName<P = unknown>(
98
114
  interaction,
99
115
  stream,
100
116
  } as NamedInteractionExecutionPayload,
101
- // Synchronous interaction execution blocks on the model; keep a long client timeout.
102
- timeoutMs: requestOptions?.timeoutMs ?? (stream ? undefined : INTERACTION_EXECUTION_TIMEOUT_MS),
117
+ // Synchronous interaction execution blocks on the model; keep a long client timeout. An explicit
118
+ // timeout wins, including `null`, `false` or `0`, which disable it.
119
+ timeoutMs:
120
+ requestOptions?.timeoutMs !== undefined
121
+ ? requestOptions.timeoutMs
122
+ : stream
123
+ ? undefined
124
+ : INTERACTION_EXECUTION_TIMEOUT_MS,
103
125
  });
104
126
  if (stream) {
105
127
  if (response.status === ExecutionRunStatus.failed) {
@@ -167,8 +189,13 @@ export async function executeInteractionAsync(
167
189
  client: VertesiaClient,
168
190
  payload: AsyncExecutionPayload,
169
191
  ): Promise<AsyncExecutionResult> {
192
+ // A conversation starts an agent run: without `idempotency_key` a replay could start a second one,
193
+ // so opt out of any client-level retry policy (see `AgentsApi.start`). With a key, a replay
194
+ // returns the run the first request started.
195
+ const unkeyedConversation = payload.type === 'conversation' && !payload.idempotency_key;
170
196
  return await client.post('/api/v1/execute/async', {
171
197
  payload,
198
+ retryPolicy: unkeyedConversation ? false : undefined,
172
199
  });
173
200
  }
174
201
 
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export type { EnableEnvironmentModelPayload, OrphanedAppInstallation } from '@ve
4
4
  export { getOAuthPermissionScopes, Permission } from '@vertesia/common';
5
5
  export * from './client.js';
6
6
  export { default as DelegationGrantsApi } from './DelegationGrantsApi.js';
7
+ export type { ExecuteInteractionRequestOptions } from './execute.js';
7
8
  export type { GroupsQueryOptions } from './GroupsApi.js';
8
9
  export * from './InteractionBase.js';
9
10
  export * from './InteractionOutput.js';
@@ -12,6 +13,7 @@ export { default as OAuthClientsApi } from './OAuthClientsApi.js';
12
13
  export { default as OAuthGrantsApi } from './OAuthGrantsApi.js';
13
14
  export { default as OAuthProvidersApi } from './OAuthProvidersApi.js';
14
15
  export { default as OAuthServerApi } from './OAuthServerApi.js';
16
+ export { default as OidcFederationApi } from './OidcFederationApi.js';
15
17
  export type { ComputePromptFacetsResponse, ListInteractionsResponse } from './PromptsApi.js';
16
18
  export { default as RemoteMcpConnectionsApi } from './RemoteMcpConnectionsApi.js';
17
19
  export type { ComputeRunFacetsResponse, FilterOption } from './RunsApi.js';
@@ -1,10 +1,11 @@
1
1
  import type { Readable } from 'node:stream';
2
- import { ReadableStream as NodeReadableStream } from 'node:stream/web';
2
+ import { finished } from 'node:stream/promises';
3
3
  import { StreamSource } from '../StreamSource.js';
4
4
 
5
5
  /**
6
6
  * A stream source that wraps a Node.js Readable stream.
7
7
  * This class is only works in Node.js environments.
8
+ * Cancelling the web stream destroys the Node.js stream, releasing its underlying resource (e.g. a file descriptor).
8
9
  */
9
10
  export class NodeStreamSource extends StreamSource {
10
11
  constructor(stream: Readable, name: string, type?: string, id?: string) {
@@ -12,31 +13,34 @@ export class NodeStreamSource extends StreamSource {
12
13
  }
13
14
  }
14
15
 
15
- function readableToWebStream<T = string | Buffer>(stream: Readable): ReadableStream<T> {
16
- if (NodeReadableStream.from) {
17
- return NodeReadableStream.from(stream) as ReadableStream<T>;
18
- } else {
19
- return readableToWebStream_BUN(stream);
20
- }
21
- }
22
-
23
16
  /**
24
- * Bun implementation since bun is not yet supporting NodeReadableStream.from
25
- * @returns
17
+ * Not ReadableStream.from: it cancels by calling return() on the Node.js async iterator, which is a no-op
18
+ * when nothing was read yet, so the Node.js stream would stay open. Also covers Bun, which lacks ReadableStream.from.
26
19
  */
27
- function readableToWebStream_BUN<T = string | Buffer>(stream: Readable): ReadableStream<T> {
20
+ function readableToWebStream<T = string | Buffer>(stream: Readable): ReadableStream<T> {
21
+ // Nothing listens until the first read, so an error before it (e.g. a failed lazy file open) would otherwise
22
+ // crash the process. The iterator still reports the error on the next read.
23
+ stream.on('error', () => undefined);
28
24
  const it = stream[Symbol.asyncIterator]();
29
- return new ReadableStream<T>({
30
- async pull(controller) {
31
- const { value, done } = await it.next();
32
- if (done) {
33
- controller.close();
34
- } else {
35
- controller.enqueue(value);
36
- }
37
- },
38
- cancel() {
39
- it.return?.();
25
+ return new ReadableStream<T>(
26
+ {
27
+ async pull(controller) {
28
+ const { value, done } = await it.next();
29
+ if (done) {
30
+ controller.close();
31
+ } else {
32
+ controller.enqueue(value);
33
+ }
34
+ },
35
+ async cancel() {
36
+ // no error argument: it would emit 'error', which the caller may not listen for
37
+ stream.destroy();
38
+ // resolve once the underlying resource is released, so callers can remove the file afterwards;
39
+ // bounded because some streams never report their close
40
+ await finished(stream, { signal: AbortSignal.timeout(5_000) }).catch(() => undefined);
41
+ },
40
42
  },
41
- });
43
+ // read on demand, like ReadableStream.from
44
+ { highWaterMark: 0 },
45
+ );
42
46
  }
@@ -87,13 +87,22 @@ export class AgentsApi extends ApiTopic {
87
87
  /**
88
88
  * Create and start a new agent run.
89
89
  * Returns the created AgentRun with its stable id.
90
+ *
91
+ * Set `idempotency_key` on an agent run to make the call safe to retry: the client's retry policy
92
+ * then applies, and a replayed request returns the run the first one started.
90
93
  */
91
94
  start<TData = Record<string, unknown>>(payload: CreateAgentRunPayload<TData>): Promise<AgentRun<TData>>;
92
95
  start<TData = Record<string, unknown>>(payload: CreateProcessRunPayload<TData>): Promise<ProcessRun>;
93
96
  start<TData = Record<string, unknown>>(
94
97
  payload: CreateAgentRunPayload<TData> | CreateProcessRunPayload<TData>,
95
98
  ): Promise<AgentRun<TData> | ProcessRun> {
96
- return this.post('/', { payload });
99
+ // Without a key, starting an agent run is not idempotent: a request abandoned client-side can
100
+ // still complete server-side, so a replay would start a second run. Opt out of any client-level
101
+ // retry policy explicitly rather than relying on POST being absent from its method list.
102
+ // Rate-limiter pacing retries are unaffected: those happen before the request reaches the
103
+ // handler. Process starts take no key and keep the client's policy.
104
+ const unkeyedAgentStart = 'interaction' in payload && !payload.idempotency_key;
105
+ return this.post('/', { payload, retryPolicy: unkeyedAgentStart ? false : undefined });
97
106
  }
98
107
 
99
108
  /**
@@ -258,11 +258,17 @@ export class FilesApi extends ApiTopic {
258
258
  'Content-Type': sourceMimeType,
259
259
  },
260
260
  }).catch((err) => {
261
- console.error('Failed to upload file', { err, url, id, path });
261
+ console.error('Failed to upload file', { err, location: fileLocationForError(url), id, path });
262
262
  throw err;
263
263
  });
264
264
  if (!res.ok) {
265
- console.log(res);
265
+ console.error('Failed to upload file', {
266
+ status: res.status,
267
+ statusText: res.statusText,
268
+ location: fileLocationForError(url),
269
+ id,
270
+ path,
271
+ });
266
272
  throw new Error(`Failed to upload file: ${res.statusText}`);
267
273
  }
268
274
 
@@ -27,6 +27,7 @@ import type {
27
27
  SwapAliasResult,
28
28
  TriggerReindexResult,
29
29
  } from '@vertesia/common';
30
+ import { zenoBulkBaseUrl } from './zeno-bulk.js';
30
31
 
31
32
  /**
32
33
  * API for indexing operations on content objects.
@@ -274,30 +275,14 @@ export class IndexingApi extends ApiTopic {
274
275
 
275
276
  // ========================================================================
276
277
  // Zeno Bulk endpoints (Go migration service)
277
- // Routes via LB path rules in prod, or derived URL in dev.
278
+ // Routes via LB path rules in prod, or derived URL in dev (see zenoBulkBaseUrl).
278
279
  // ========================================================================
279
280
 
280
- /**
281
- * Get the zeno-bulk base URL.
282
- * Dev branches: store URL contains "zeno-server" -> replace with "zeno-bulk".
283
- * Production/preview: same domain, LB routes /reindex/* to zeno-bulk.
284
- */
285
- private get zenoBulkBaseUrl(): string {
286
- const storeBaseUrl = this.client.baseUrl;
287
- if (storeBaseUrl.includes('localhost:') || storeBaseUrl.includes('127.0.0.1:')) {
288
- return 'https://zeno-bulk-dev-main.api.dev1.vertesia.io';
289
- }
290
- if (storeBaseUrl.includes('zeno-server')) {
291
- return storeBaseUrl.replace(/zeno-server/, 'zeno-bulk');
292
- }
293
- return storeBaseUrl;
294
- }
295
-
296
281
  /**
297
282
  * POST to a zeno-bulk endpoint. Resolves the path against the zeno-bulk base URL.
298
283
  */
299
284
  private zenoBulkPost<T>(path: string, body: object): Promise<T> {
300
- return this.client.post(this.zenoBulkBaseUrl + path, { payload: body });
285
+ return this.client.post(zenoBulkBaseUrl(this.client.baseUrl) + path, { payload: body });
301
286
  }
302
287
 
303
288
  /**