@agentforge-io/sdk-server 4.1.0 → 4.3.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/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { AgentForgeClient } from './client';
2
2
  export type { AgentForgeClientOptions, RequestOptions } from './client';
3
3
  export { AgentForgeError, AgentForgeAuthError, AgentForgeNotFoundError, AgentForgeNetworkError, } from './errors';
4
4
  export { TOOL_CALLBACK_HEADERS, ToolCallbackSignatureError, signToolCallbackPayload, verifyToolCallbackSignature, } from './webhooks';
5
- export type { AgentMe, AgentSummary, ApprovalCopyBundle, ChatStreamChunk, ChatStreamEvent, ChatTokenRecord, ConnectorDefaultsResult, ConnectorDefaultsSource, ConnectorResourceItem, ConnectorStatus, ConnectorToolInfo, ConversationHistory, ConversationStatus, CreateAgentInput, CreateClickupTaskInput, CreateClickupTaskResult, ExternalToolSummary, IssueChatTokenInput, ListAgentsResult, ListConnectorResourcesResult, McpServerConfig, MessageRecord, MessageRole, PublicAppBranding, AppSelf, AppSelfConnector, AppSelfConnectors, RegisterToolInput, RegisterToolsInput, RegisterToolsResult, ToolCallbackPayload, ToolCallRecord, ToolPermission, ToolPermissionMode, TokenUsage, TenantSecretEntry, UpdateAgentInput, } from './types';
5
+ export type { AgentMe, AgentSummary, AppUserConnectorStatus, ApprovalCopyBundle, ChatStreamChunk, ChatStreamEvent, ChatTokenRecord, ConnectorDefaultsResult, ConnectorDefaultsSource, ConnectorResourceItem, ConnectorStatus, ConnectorToolInfo, ConversationHistory, ConversationStatus, CreateAgentInput, CreateClickupTaskInput, CreateClickupTaskResult, ExternalToolSummary, IssueChatTokenInput, ListAgentsResult, ListConnectorResourcesResult, McpServerConfig, MessageRecord, MessageRole, PublicAppBranding, AppSelf, AppSelfConnector, AppSelfConnectors, RegisterToolInput, RegisterToolsInput, RegisterToolsResult, ToolCallbackPayload, ToolCallRecord, ToolPermission, ToolPermissionMode, TokenUsage, TenantSecretEntry, UpdateAgentInput, } from './types';
6
6
  export { AgentsResource } from './resources/agents';
7
7
  export { AppsResource } from './resources/apps';
8
8
  export { ChatResource } from './resources/chat';
@@ -1,5 +1,5 @@
1
1
  import type { AgentForgeClient } from '../client';
2
- import type { ConnectorDefaultsResult, ConnectorStatus, CreateClickupTaskInput, CreateClickupTaskResult, ListConnectorResourcesResult, ToolPermission } from '../types';
2
+ import type { AppUserConnectorStatus, ConnectorDefaultsResult, ConnectorStatus, CreateClickupTaskInput, CreateClickupTaskResult, ListConnectorResourcesResult, ToolPermission } from '../types';
3
3
  export declare class ConnectorsResource {
4
4
  private readonly client;
5
5
  constructor(client: AgentForgeClient);
@@ -196,6 +196,178 @@ export declare class ConnectorsResource {
196
196
  * desconecta un canal del inbox omnicanal.
197
197
  */
198
198
  destroyInboxChannel(agentId: string, channelId: string): Promise<void>;
199
+ /**
200
+ * `POST /connectors/oauth/:id/authorize-for-app-user` — starts the
201
+ * connector OAuth flow on behalf of ONE end user of the `sk_live_`/
202
+ * `sk_test_` App (`appId` resolved from the secret, `externalUserId`
203
+ * explicit). App-user counterpart of `authorizeForTenant()` above — same
204
+ * response shape (`{ url }`, open it e.g. via `window.open` in the
205
+ * consumer app), same callback flow end-to-end on the platform.
206
+ *
207
+ * Requires the `AgentForgeClient` to have been constructed with
208
+ * `appSecret` — throws early otherwise (this surface authenticates AS the
209
+ * App, not via ak_live_/ChatToken).
210
+ *
211
+ * @example
212
+ * const af = new AgentForgeClient({ baseURL, appSecret: 'sk_live_...' });
213
+ * const { url } = await af.connectors.authorizeForAppUser('slack', {
214
+ * externalUserId: 'scrappydoc-user-42',
215
+ * postAuthRedirect: 'https://myapp.com/settings',
216
+ * });
217
+ */
218
+ authorizeForAppUser(connectorId: string, input: {
219
+ externalUserId: string;
220
+ postAuthRedirect?: string;
221
+ }): Promise<{
222
+ url: string;
223
+ }>;
224
+ /**
225
+ * `GET /connectors/oauth/app-user/directory` — the given end user's
226
+ * connectors + connection status (App-user counterpart of
227
+ * `listConnected()`, which is the ak_live_ tenant-wide picker). Returns
228
+ * the SAME rich shape `listForTenant`/`listForUser` produce server-side
229
+ * (`AppUserConnectorStatus`: name/description/category/iconUrl/configured/
230
+ * accountLabel/connectedAt) — NOT the narrow `ConnectorStatus` the
231
+ * tenant-picker methods above use.
232
+ *
233
+ * Requires `appSecret` — throws early otherwise, same as
234
+ * `authorizeForAppUser`.
235
+ *
236
+ * @example
237
+ * const { items } = await af.connectors.listConnectorsForAppUser({
238
+ * externalUserId: 'scrappydoc-user-42',
239
+ * });
240
+ * const unconnected = items.filter((c) => c.configured && !c.connected);
241
+ */
242
+ listConnectorsForAppUser(input: {
243
+ externalUserId: string;
244
+ }): Promise<{
245
+ items: AppUserConnectorStatus[];
246
+ }>;
247
+ /**
248
+ * `DELETE /connectors/oauth/:id/app-user` — disconnects `connectorId` for
249
+ * the given end user. App-user counterpart of `disconnect()` above.
250
+ * Mirrors the platform's `disconnectForAppUser` 404-on-missing behavior
251
+ * (`ConnectorError('not_connected', ...)` → `AgentForgeNotFoundError`).
252
+ *
253
+ * Requires `appSecret` — throws early otherwise, same as
254
+ * `authorizeForAppUser`.
255
+ *
256
+ * @example
257
+ * await af.connectors.disconnectForAppUser('google-drive', {
258
+ * externalUserId: 'scrappydoc-user-42',
259
+ * });
260
+ */
261
+ disconnectForAppUser(connectorId: string, input: {
262
+ externalUserId: string;
263
+ }): Promise<{
264
+ ok: true;
265
+ }>;
266
+ /**
267
+ * `GET /app-user/connector-resources/:connectorId?externalUserId=...` —
268
+ * real resource browsing (Slack channels, GitHub repos, ClickUp lists…)
269
+ * scoped to ONE end user's OWN grant. App-user counterpart of
270
+ * `listResources()` above — same return shape, same `unsupported: true`
271
+ * behavior for connectors without a resource selector implemented yet.
272
+ *
273
+ * Requires `appSecret` — throws early otherwise, same as
274
+ * `authorizeForAppUser`.
275
+ *
276
+ * @example
277
+ * const { items } = await af.connectors.listResourcesForAppUser('slack', {
278
+ * externalUserId: 'scrappydoc-user-42',
279
+ * });
280
+ */
281
+ listResourcesForAppUser(connectorId: string, input: {
282
+ externalUserId: string;
283
+ }): Promise<ListConnectorResourcesResult>;
284
+ /**
285
+ * `GET /app-user/connector-resources/:connectorId/defaults?externalUserId=...`
286
+ * — tool permissions for this connector, resolved with the App's own
287
+ * override always live (tier 0, `af_app_connectors`) since `appId` comes
288
+ * from the secret. App-user counterpart of `getDefaults()` above.
289
+ * `externalUserId` doesn't affect resolution (defaults are per-App, not
290
+ * per-end-user) — still required because the platform route always reads
291
+ * it off the scope helper shared by this controller's other routes.
292
+ *
293
+ * Requires `appSecret` — throws early otherwise, same as
294
+ * `authorizeForAppUser`.
295
+ */
296
+ getDefaultsForAppUser(connectorId: string, input: {
297
+ externalUserId: string;
298
+ }): Promise<ConnectorDefaultsResult>;
299
+ /**
300
+ * `PUT /app-user/connector-resources/:connectorId/defaults?externalUserId=...`
301
+ * — overwrites the connector's tool permissions. App-user counterpart of
302
+ * `updateDefaults()` above. NOTE (mirrors the platform controller's own
303
+ * doc comment): this write lands on the tenant-level row
304
+ * (`af_connector_tool_defaults`), same as the ak_live_ route — there's no
305
+ * App-scoped-only override write today. `externalUserId` travels as a
306
+ * query param (matching `getDefaultsForAppUser`); the platform route also
307
+ * accepts it in the body but query is canonical for this SDK's GET/PUT
308
+ * pair.
309
+ *
310
+ * Requires `appSecret` — throws early otherwise, same as
311
+ * `authorizeForAppUser`.
312
+ */
313
+ updateDefaultsForAppUser(connectorId: string, permissions: ToolPermission[], input: {
314
+ externalUserId: string;
315
+ }): Promise<{
316
+ ok: true;
317
+ }>;
318
+ /**
319
+ * `POST /app-user/connector-resources/clickup/tasks` — creates a REAL
320
+ * ClickUp task using the end user's OWN grant. App-user counterpart of
321
+ * `createClickupTask()` above. Unlike the GET/PUT methods above,
322
+ * `externalUserId` travels in the BODY here — the platform route reads it
323
+ * off `body.externalUserId` for this action endpoint (not a query param).
324
+ *
325
+ * Requires `appSecret` — throws early otherwise, same as
326
+ * `authorizeForAppUser`.
327
+ */
328
+ createClickupTaskForAppUser(input: CreateClickupTaskInput & {
329
+ externalUserId: string;
330
+ }): Promise<CreateClickupTaskResult>;
331
+ /**
332
+ * `POST /app-user/connector-resources/slack/messages` — posts a REAL
333
+ * Slack message using the end user's OWN grant. App-user counterpart of
334
+ * `postSlackMessage()` above. `externalUserId` travels in the BODY, same
335
+ * as `createClickupTaskForAppUser` (the platform route reads
336
+ * `body.externalUserId` for both action endpoints).
337
+ *
338
+ * Requires `appSecret` — throws early otherwise, same as
339
+ * `authorizeForAppUser`.
340
+ */
341
+ postSlackMessageForAppUser(input: {
342
+ channelId: string;
343
+ text: string;
344
+ externalUserId: string;
345
+ }): Promise<{
346
+ ok: true;
347
+ ts?: string;
348
+ permalink?: string;
349
+ }>;
350
+ /**
351
+ * `POST /app-user/connector-resources/:connectorId/api-key` — guarda un
352
+ * API key pegado por el end user, para SU PROPIO grant (no el tenant del
353
+ * App). App-user counterpart of `saveApiKeyForTenant()` above, para
354
+ * connectors que NO usan OAuth (Granola, Mapbox). `externalUserId` viaja
355
+ * en el BODY, mismo patrón que `createClickupTaskForAppUser`/
356
+ * `postSlackMessageForAppUser`.
357
+ *
358
+ * Requires `appSecret` — throws early otherwise, same as
359
+ * `authorizeForAppUser`.
360
+ *
361
+ * @example
362
+ * await af.connectors.saveApiKeyForAppUser('granola', 'grn_xxxxx', {
363
+ * externalUserId: 'scrappydoc-user-42',
364
+ * });
365
+ */
366
+ saveApiKeyForAppUser(connectorId: string, apiKey: string, input: {
367
+ externalUserId: string;
368
+ }): Promise<{
369
+ connectorId: string;
370
+ }>;
199
371
  }
200
372
  export interface InboxChannelView {
201
373
  id: string;
@@ -5,9 +5,13 @@
5
5
  // reales de uno conectado (canales de Slack, repos de GitHub, etc), y arranca
6
6
  // el OAuth authorize para uno que falte conectar.
7
7
  //
8
- // Auth: SIEMPRE ak_live_ tenant-wide — estos 3 endpoints usan `ApiKeyGuard`
9
- // en el server y rechazan ChatToken de plano, aunque el `AgentForgeClient`
10
- // también tenga uno configurado (ver `requestWithApiKey`).
8
+ // Auth: la mayoría de los métodos son ak_live_ tenant-wide — usan
9
+ // `ApiKeyGuard` en el server y rechazan ChatToken de plano, aunque el
10
+ // `AgentForgeClient` también tenga uno configurado (ver `requestWithApiKey`).
11
+ // EXCEPCIÓN (AGF-072, sección "App-user scope" al final del archivo): los
12
+ // métodos `*ForAppUser` (Plane B — un end user del App conectando SU PROPIA
13
+ // cuenta) usan `appSecret` (sk_live_/sk_test_) en vez de ak_live_, y por eso
14
+ // llaman `this.client.request()` directo en lugar de `requestWithApiKey()`.
11
15
  Object.defineProperty(exports, "__esModule", { value: true });
12
16
  exports.ConnectorsResource = void 0;
13
17
  class ConnectorsResource {
@@ -203,5 +207,243 @@ class ConnectorsResource {
203
207
  async destroyInboxChannel(agentId, channelId) {
204
208
  await this.client.requestWithApiKey('DELETE', `/connectors/inbox/agents/${encodeURIComponent(agentId)}/inbox/channels/${encodeURIComponent(channelId)}`);
205
209
  }
210
+ // ─── App-user scope (AGF-072, Option A — APP_END_USER_CONNECTIONS_SDD.md §4.4) ──
211
+ //
212
+ // The 3 methods below are Plane B: a specific END USER of an `sk_live_`/
213
+ // `sk_test_` App connecting THEIR OWN account (their Slack, their Gmail) —
214
+ // distinct from `*ForTenant` above (Plane A, the App's own tenant-wide
215
+ // credentials) and from the ak_live_-only picker methods at the top of this
216
+ // file. Auth is `appSecret` (sk_live_/sk_test_), NOT ak_live_ — these calls
217
+ // use `this.client.request()` directly (plain `authHeaders()`, which picks
218
+ // `appSecret` when set) instead of `requestWithApiKey()`, because that
219
+ // helper hardcodes `forceApiKey: true` and would reject an appSecret-only
220
+ // client. Same pattern as `ChatResource.streamMessageForAppUser`.
221
+ //
222
+ // `appId` is NEVER a param on any of these — the platform resolves it
223
+ // exclusively from the `sk_live_`/`sk_test_` secret server-side (SDD §4.4),
224
+ // so App Y's secret can never touch App X's grants no matter what
225
+ // `externalUserId` is passed. Only `externalUserId` (the App's own,
226
+ // unsigned identifier for its end user) travels as a param.
227
+ /**
228
+ * `POST /connectors/oauth/:id/authorize-for-app-user` — starts the
229
+ * connector OAuth flow on behalf of ONE end user of the `sk_live_`/
230
+ * `sk_test_` App (`appId` resolved from the secret, `externalUserId`
231
+ * explicit). App-user counterpart of `authorizeForTenant()` above — same
232
+ * response shape (`{ url }`, open it e.g. via `window.open` in the
233
+ * consumer app), same callback flow end-to-end on the platform.
234
+ *
235
+ * Requires the `AgentForgeClient` to have been constructed with
236
+ * `appSecret` — throws early otherwise (this surface authenticates AS the
237
+ * App, not via ak_live_/ChatToken).
238
+ *
239
+ * @example
240
+ * const af = new AgentForgeClient({ baseURL, appSecret: 'sk_live_...' });
241
+ * const { url } = await af.connectors.authorizeForAppUser('slack', {
242
+ * externalUserId: 'scrappydoc-user-42',
243
+ * postAuthRedirect: 'https://myapp.com/settings',
244
+ * });
245
+ */
246
+ async authorizeForAppUser(connectorId, input) {
247
+ if (!this.client.getAppSecret()) {
248
+ throw new Error('connectors.authorizeForAppUser requires appSecret (sk_live_/sk_test_) in ' +
249
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
250
+ }
251
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
252
+ if (input.postAuthRedirect)
253
+ params.set('postAuthRedirect', input.postAuthRedirect);
254
+ return this.client.request('POST', `/connectors/oauth/${encodeURIComponent(connectorId)}/authorize-for-app-user?${params.toString()}`);
255
+ }
256
+ /**
257
+ * `GET /connectors/oauth/app-user/directory` — the given end user's
258
+ * connectors + connection status (App-user counterpart of
259
+ * `listConnected()`, which is the ak_live_ tenant-wide picker). Returns
260
+ * the SAME rich shape `listForTenant`/`listForUser` produce server-side
261
+ * (`AppUserConnectorStatus`: name/description/category/iconUrl/configured/
262
+ * accountLabel/connectedAt) — NOT the narrow `ConnectorStatus` the
263
+ * tenant-picker methods above use.
264
+ *
265
+ * Requires `appSecret` — throws early otherwise, same as
266
+ * `authorizeForAppUser`.
267
+ *
268
+ * @example
269
+ * const { items } = await af.connectors.listConnectorsForAppUser({
270
+ * externalUserId: 'scrappydoc-user-42',
271
+ * });
272
+ * const unconnected = items.filter((c) => c.configured && !c.connected);
273
+ */
274
+ async listConnectorsForAppUser(input) {
275
+ if (!this.client.getAppSecret()) {
276
+ throw new Error('connectors.listConnectorsForAppUser requires appSecret (sk_live_/sk_test_) in ' +
277
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
278
+ }
279
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
280
+ return this.client.request('GET', `/connectors/oauth/app-user/directory?${params.toString()}`);
281
+ }
282
+ /**
283
+ * `DELETE /connectors/oauth/:id/app-user` — disconnects `connectorId` for
284
+ * the given end user. App-user counterpart of `disconnect()` above.
285
+ * Mirrors the platform's `disconnectForAppUser` 404-on-missing behavior
286
+ * (`ConnectorError('not_connected', ...)` → `AgentForgeNotFoundError`).
287
+ *
288
+ * Requires `appSecret` — throws early otherwise, same as
289
+ * `authorizeForAppUser`.
290
+ *
291
+ * @example
292
+ * await af.connectors.disconnectForAppUser('google-drive', {
293
+ * externalUserId: 'scrappydoc-user-42',
294
+ * });
295
+ */
296
+ async disconnectForAppUser(connectorId, input) {
297
+ if (!this.client.getAppSecret()) {
298
+ throw new Error('connectors.disconnectForAppUser requires appSecret (sk_live_/sk_test_) in ' +
299
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
300
+ }
301
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
302
+ return this.client.request('DELETE', `/connectors/oauth/${encodeURIComponent(connectorId)}/app-user?${params.toString()}`);
303
+ }
304
+ // ─── App-user resource picker (AGF-072d — app-user twin of the picker
305
+ // methods at the top of this file) ──────────────────────────────────────
306
+ //
307
+ // Mirrors listResources/getDefaults/updateDefaults/createClickupTask/
308
+ // postSlackMessage above, scoped to (appId-from-secret, externalUserId)
309
+ // instead of the ak_live_ tenant. Hits `AppUserResourcePickerController`
310
+ // (`/app-user/connector-resources/*`) — a DISTINCT base path from the
311
+ // tenant picker's `/connector-resources/*`, not just a query-param
312
+ // variant. Same auth rule as the 3 methods above: requires `appSecret`,
313
+ // uses `this.client.request()` directly (not `requestWithApiKey()`).
314
+ //
315
+ // NOT mirrored here:
316
+ // - list-connected (`GET /app-user/connector-resources`): its
317
+ // `{connectorId,connected,browsable}[]` shape is a narrower duplicate
318
+ // of what `listConnectorsForAppUser()` above already returns (the
319
+ // richer `AppUserConnectorStatus[]` from
320
+ // `/connectors/oauth/app-user/directory`). Adding a second, thinner
321
+ // "list connected" would just invite callers to pick the wrong one.
322
+ // - disconnect (`DELETE /app-user/connector-resources/:connectorId`):
323
+ // redundant with `disconnectForAppUser()` above
324
+ // (`/connectors/oauth/:id/app-user`) — same ConnectorAuth row, same
325
+ // method name would collide. Use the existing one.
326
+ /**
327
+ * `GET /app-user/connector-resources/:connectorId?externalUserId=...` —
328
+ * real resource browsing (Slack channels, GitHub repos, ClickUp lists…)
329
+ * scoped to ONE end user's OWN grant. App-user counterpart of
330
+ * `listResources()` above — same return shape, same `unsupported: true`
331
+ * behavior for connectors without a resource selector implemented yet.
332
+ *
333
+ * Requires `appSecret` — throws early otherwise, same as
334
+ * `authorizeForAppUser`.
335
+ *
336
+ * @example
337
+ * const { items } = await af.connectors.listResourcesForAppUser('slack', {
338
+ * externalUserId: 'scrappydoc-user-42',
339
+ * });
340
+ */
341
+ async listResourcesForAppUser(connectorId, input) {
342
+ if (!this.client.getAppSecret()) {
343
+ throw new Error('connectors.listResourcesForAppUser requires appSecret (sk_live_/sk_test_) in ' +
344
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
345
+ }
346
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
347
+ return this.client.request('GET', `/app-user/connector-resources/${encodeURIComponent(connectorId)}?${params.toString()}`);
348
+ }
349
+ /**
350
+ * `GET /app-user/connector-resources/:connectorId/defaults?externalUserId=...`
351
+ * — tool permissions for this connector, resolved with the App's own
352
+ * override always live (tier 0, `af_app_connectors`) since `appId` comes
353
+ * from the secret. App-user counterpart of `getDefaults()` above.
354
+ * `externalUserId` doesn't affect resolution (defaults are per-App, not
355
+ * per-end-user) — still required because the platform route always reads
356
+ * it off the scope helper shared by this controller's other routes.
357
+ *
358
+ * Requires `appSecret` — throws early otherwise, same as
359
+ * `authorizeForAppUser`.
360
+ */
361
+ async getDefaultsForAppUser(connectorId, input) {
362
+ if (!this.client.getAppSecret()) {
363
+ throw new Error('connectors.getDefaultsForAppUser requires appSecret (sk_live_/sk_test_) in ' +
364
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
365
+ }
366
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
367
+ return this.client.request('GET', `/app-user/connector-resources/${encodeURIComponent(connectorId)}/defaults?${params.toString()}`);
368
+ }
369
+ /**
370
+ * `PUT /app-user/connector-resources/:connectorId/defaults?externalUserId=...`
371
+ * — overwrites the connector's tool permissions. App-user counterpart of
372
+ * `updateDefaults()` above. NOTE (mirrors the platform controller's own
373
+ * doc comment): this write lands on the tenant-level row
374
+ * (`af_connector_tool_defaults`), same as the ak_live_ route — there's no
375
+ * App-scoped-only override write today. `externalUserId` travels as a
376
+ * query param (matching `getDefaultsForAppUser`); the platform route also
377
+ * accepts it in the body but query is canonical for this SDK's GET/PUT
378
+ * pair.
379
+ *
380
+ * Requires `appSecret` — throws early otherwise, same as
381
+ * `authorizeForAppUser`.
382
+ */
383
+ async updateDefaultsForAppUser(connectorId, permissions, input) {
384
+ if (!this.client.getAppSecret()) {
385
+ throw new Error('connectors.updateDefaultsForAppUser requires appSecret (sk_live_/sk_test_) in ' +
386
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
387
+ }
388
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
389
+ return this.client.request('PUT', `/app-user/connector-resources/${encodeURIComponent(connectorId)}/defaults?${params.toString()}`, { permissions });
390
+ }
391
+ /**
392
+ * `POST /app-user/connector-resources/clickup/tasks` — creates a REAL
393
+ * ClickUp task using the end user's OWN grant. App-user counterpart of
394
+ * `createClickupTask()` above. Unlike the GET/PUT methods above,
395
+ * `externalUserId` travels in the BODY here — the platform route reads it
396
+ * off `body.externalUserId` for this action endpoint (not a query param).
397
+ *
398
+ * Requires `appSecret` — throws early otherwise, same as
399
+ * `authorizeForAppUser`.
400
+ */
401
+ async createClickupTaskForAppUser(input) {
402
+ if (!this.client.getAppSecret()) {
403
+ throw new Error('connectors.createClickupTaskForAppUser requires appSecret (sk_live_/sk_test_) in ' +
404
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
405
+ }
406
+ return this.client.request('POST', '/app-user/connector-resources/clickup/tasks', input);
407
+ }
408
+ /**
409
+ * `POST /app-user/connector-resources/slack/messages` — posts a REAL
410
+ * Slack message using the end user's OWN grant. App-user counterpart of
411
+ * `postSlackMessage()` above. `externalUserId` travels in the BODY, same
412
+ * as `createClickupTaskForAppUser` (the platform route reads
413
+ * `body.externalUserId` for both action endpoints).
414
+ *
415
+ * Requires `appSecret` — throws early otherwise, same as
416
+ * `authorizeForAppUser`.
417
+ */
418
+ async postSlackMessageForAppUser(input) {
419
+ if (!this.client.getAppSecret()) {
420
+ throw new Error('connectors.postSlackMessageForAppUser requires appSecret (sk_live_/sk_test_) in ' +
421
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
422
+ }
423
+ return this.client.request('POST', '/app-user/connector-resources/slack/messages', input);
424
+ }
425
+ /**
426
+ * `POST /app-user/connector-resources/:connectorId/api-key` — guarda un
427
+ * API key pegado por el end user, para SU PROPIO grant (no el tenant del
428
+ * App). App-user counterpart of `saveApiKeyForTenant()` above, para
429
+ * connectors que NO usan OAuth (Granola, Mapbox). `externalUserId` viaja
430
+ * en el BODY, mismo patrón que `createClickupTaskForAppUser`/
431
+ * `postSlackMessageForAppUser`.
432
+ *
433
+ * Requires `appSecret` — throws early otherwise, same as
434
+ * `authorizeForAppUser`.
435
+ *
436
+ * @example
437
+ * await af.connectors.saveApiKeyForAppUser('granola', 'grn_xxxxx', {
438
+ * externalUserId: 'scrappydoc-user-42',
439
+ * });
440
+ */
441
+ async saveApiKeyForAppUser(connectorId, apiKey, input) {
442
+ if (!this.client.getAppSecret()) {
443
+ throw new Error('connectors.saveApiKeyForAppUser requires appSecret (sk_live_/sk_test_) in ' +
444
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
445
+ }
446
+ return this.client.request('POST', `/app-user/connector-resources/${encodeURIComponent(connectorId)}/api-key`, { apiKey, externalUserId: input.externalUserId });
447
+ }
206
448
  }
207
449
  exports.ConnectorsResource = ConnectorsResource;
package/dist/types.d.ts CHANGED
@@ -319,6 +319,21 @@ export interface ConnectorStatus {
319
319
  * false = se puede conectar pero `listResources()` devuelve `unsupported`. */
320
320
  browsable: boolean;
321
321
  }
322
+ export interface AppUserConnectorStatus {
323
+ connectorId: string;
324
+ name: string;
325
+ description: string;
326
+ category?: string;
327
+ iconUrl?: string;
328
+ /** El operador del tenant ya trajo credenciales para este provider. Si es
329
+ * `false`, este end user no puede conectar todavía (falta setup admin). */
330
+ configured: boolean;
331
+ /** Este externalUserId particular ya completó el OAuth/API-key setup. */
332
+ connected: boolean;
333
+ accountLabel?: string;
334
+ /** ISO 8601 — `Date` en el server, serializado a string sobre JSON. */
335
+ connectedAt?: string;
336
+ }
322
337
  export interface ConnectorResourceItem {
323
338
  id: string;
324
339
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge-io/sdk-server",
3
- "version": "4.1.0",
3
+ "version": "4.3.0",
4
4
  "description": "Server-side SDK for backend apps integrating with AgentForge platform. Auth via ChatToken or ak_live_ API key.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",