@agentforge-io/sdk-server 4.2.0 → 4.4.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.
@@ -227,8 +227,9 @@ export declare class ConnectorsResource {
227
227
  * `listConnected()`, which is the ak_live_ tenant-wide picker). Returns
228
228
  * the SAME rich shape `listForTenant`/`listForUser` produce server-side
229
229
  * (`AppUserConnectorStatus`: name/description/category/iconUrl/configured/
230
- * accountLabel/connectedAt) — NOT the narrow `ConnectorStatus` the
231
- * tenant-picker methods above use.
230
+ * accountLabel/connectedAt), PLUS `browsable` (AGF-074, computed
231
+ * server-side on top of the raw `ConnectorStatus` rows) — NOT the narrow
232
+ * `ConnectorStatus` the tenant-picker methods above use.
232
233
  *
233
234
  * Requires `appSecret` — throws early otherwise, same as
234
235
  * `authorizeForAppUser`.
@@ -263,6 +264,111 @@ export declare class ConnectorsResource {
263
264
  }): Promise<{
264
265
  ok: true;
265
266
  }>;
267
+ /**
268
+ * `GET /app-user/connector-resources/:connectorId?externalUserId=...` —
269
+ * real resource browsing (Slack channels, GitHub repos, ClickUp lists…)
270
+ * scoped to ONE end user's OWN grant. App-user counterpart of
271
+ * `listResources()` above — same return shape, same `unsupported: true`
272
+ * behavior for connectors without a resource selector implemented yet.
273
+ *
274
+ * Requires `appSecret` — throws early otherwise, same as
275
+ * `authorizeForAppUser`.
276
+ *
277
+ * @example
278
+ * const { items } = await af.connectors.listResourcesForAppUser('slack', {
279
+ * externalUserId: 'scrappydoc-user-42',
280
+ * });
281
+ */
282
+ listResourcesForAppUser(connectorId: string, input: {
283
+ externalUserId: string;
284
+ }): Promise<ListConnectorResourcesResult>;
285
+ /**
286
+ * `GET /app-user/connector-resources/:connectorId/defaults?externalUserId=...`
287
+ * — tool permissions for this connector, resolved with the App's own
288
+ * override always live (tier 0, `af_app_connectors`) since `appId` comes
289
+ * from the secret. App-user counterpart of `getDefaults()` above.
290
+ * `externalUserId` doesn't affect resolution (defaults are per-App, not
291
+ * per-end-user) — still required because the platform route always reads
292
+ * it off the scope helper shared by this controller's other routes.
293
+ *
294
+ * Requires `appSecret` — throws early otherwise, same as
295
+ * `authorizeForAppUser`.
296
+ */
297
+ getDefaultsForAppUser(connectorId: string, input: {
298
+ externalUserId: string;
299
+ }): Promise<ConnectorDefaultsResult>;
300
+ /**
301
+ * `PUT /app-user/connector-resources/:connectorId/defaults?externalUserId=...`
302
+ * — overwrites the connector's tool permissions. App-user counterpart of
303
+ * `updateDefaults()` above. NOTE (mirrors the platform controller's own
304
+ * doc comment): this write lands on the tenant-level row
305
+ * (`af_connector_tool_defaults`), same as the ak_live_ route — there's no
306
+ * App-scoped-only override write today. `externalUserId` travels as a
307
+ * query param (matching `getDefaultsForAppUser`); the platform route also
308
+ * accepts it in the body but query is canonical for this SDK's GET/PUT
309
+ * pair.
310
+ *
311
+ * Requires `appSecret` — throws early otherwise, same as
312
+ * `authorizeForAppUser`.
313
+ */
314
+ updateDefaultsForAppUser(connectorId: string, permissions: ToolPermission[], input: {
315
+ externalUserId: string;
316
+ }): Promise<{
317
+ ok: true;
318
+ }>;
319
+ /**
320
+ * `POST /app-user/connector-resources/clickup/tasks` — creates a REAL
321
+ * ClickUp task using the end user's OWN grant. App-user counterpart of
322
+ * `createClickupTask()` above. Unlike the GET/PUT methods above,
323
+ * `externalUserId` travels in the BODY here — the platform route reads it
324
+ * off `body.externalUserId` for this action endpoint (not a query param).
325
+ *
326
+ * Requires `appSecret` — throws early otherwise, same as
327
+ * `authorizeForAppUser`.
328
+ */
329
+ createClickupTaskForAppUser(input: CreateClickupTaskInput & {
330
+ externalUserId: string;
331
+ }): Promise<CreateClickupTaskResult>;
332
+ /**
333
+ * `POST /app-user/connector-resources/slack/messages` — posts a REAL
334
+ * Slack message using the end user's OWN grant. App-user counterpart of
335
+ * `postSlackMessage()` above. `externalUserId` travels in the BODY, same
336
+ * as `createClickupTaskForAppUser` (the platform route reads
337
+ * `body.externalUserId` for both action endpoints).
338
+ *
339
+ * Requires `appSecret` — throws early otherwise, same as
340
+ * `authorizeForAppUser`.
341
+ */
342
+ postSlackMessageForAppUser(input: {
343
+ channelId: string;
344
+ text: string;
345
+ externalUserId: string;
346
+ }): Promise<{
347
+ ok: true;
348
+ ts?: string;
349
+ permalink?: string;
350
+ }>;
351
+ /**
352
+ * `POST /app-user/connector-resources/:connectorId/api-key` — guarda un
353
+ * API key pegado por el end user, para SU PROPIO grant (no el tenant del
354
+ * App). App-user counterpart of `saveApiKeyForTenant()` above, para
355
+ * connectors que NO usan OAuth (Granola, Mapbox). `externalUserId` viaja
356
+ * en el BODY, mismo patrón que `createClickupTaskForAppUser`/
357
+ * `postSlackMessageForAppUser`.
358
+ *
359
+ * Requires `appSecret` — throws early otherwise, same as
360
+ * `authorizeForAppUser`.
361
+ *
362
+ * @example
363
+ * await af.connectors.saveApiKeyForAppUser('granola', 'grn_xxxxx', {
364
+ * externalUserId: 'scrappydoc-user-42',
365
+ * });
366
+ */
367
+ saveApiKeyForAppUser(connectorId: string, apiKey: string, input: {
368
+ externalUserId: string;
369
+ }): Promise<{
370
+ connectorId: string;
371
+ }>;
266
372
  }
267
373
  export interface InboxChannelView {
268
374
  id: string;
@@ -259,8 +259,9 @@ class ConnectorsResource {
259
259
  * `listConnected()`, which is the ak_live_ tenant-wide picker). Returns
260
260
  * the SAME rich shape `listForTenant`/`listForUser` produce server-side
261
261
  * (`AppUserConnectorStatus`: name/description/category/iconUrl/configured/
262
- * accountLabel/connectedAt) — NOT the narrow `ConnectorStatus` the
263
- * tenant-picker methods above use.
262
+ * accountLabel/connectedAt), PLUS `browsable` (AGF-074, computed
263
+ * server-side on top of the raw `ConnectorStatus` rows) — NOT the narrow
264
+ * `ConnectorStatus` the tenant-picker methods above use.
264
265
  *
265
266
  * Requires `appSecret` — throws early otherwise, same as
266
267
  * `authorizeForAppUser`.
@@ -301,5 +302,149 @@ class ConnectorsResource {
301
302
  const params = new URLSearchParams({ externalUserId: input.externalUserId });
302
303
  return this.client.request('DELETE', `/connectors/oauth/${encodeURIComponent(connectorId)}/app-user?${params.toString()}`);
303
304
  }
305
+ // ─── App-user resource picker (AGF-072d — app-user twin of the picker
306
+ // methods at the top of this file) ──────────────────────────────────────
307
+ //
308
+ // Mirrors listResources/getDefaults/updateDefaults/createClickupTask/
309
+ // postSlackMessage above, scoped to (appId-from-secret, externalUserId)
310
+ // instead of the ak_live_ tenant. Hits `AppUserResourcePickerController`
311
+ // (`/app-user/connector-resources/*`) — a DISTINCT base path from the
312
+ // tenant picker's `/connector-resources/*`, not just a query-param
313
+ // variant. Same auth rule as the 3 methods above: requires `appSecret`,
314
+ // uses `this.client.request()` directly (not `requestWithApiKey()`).
315
+ //
316
+ // NOT mirrored here:
317
+ // - list-connected (`GET /app-user/connector-resources`): its
318
+ // `{connectorId,connected,browsable}[]` shape is a narrower duplicate
319
+ // of what `listConnectorsForAppUser()` above already returns (the
320
+ // richer `AppUserConnectorStatus[]` from
321
+ // `/connectors/oauth/app-user/directory`). Adding a second, thinner
322
+ // "list connected" would just invite callers to pick the wrong one.
323
+ // - disconnect (`DELETE /app-user/connector-resources/:connectorId`):
324
+ // redundant with `disconnectForAppUser()` above
325
+ // (`/connectors/oauth/:id/app-user`) — same ConnectorAuth row, same
326
+ // method name would collide. Use the existing one.
327
+ /**
328
+ * `GET /app-user/connector-resources/:connectorId?externalUserId=...` —
329
+ * real resource browsing (Slack channels, GitHub repos, ClickUp lists…)
330
+ * scoped to ONE end user's OWN grant. App-user counterpart of
331
+ * `listResources()` above — same return shape, same `unsupported: true`
332
+ * behavior for connectors without a resource selector implemented yet.
333
+ *
334
+ * Requires `appSecret` — throws early otherwise, same as
335
+ * `authorizeForAppUser`.
336
+ *
337
+ * @example
338
+ * const { items } = await af.connectors.listResourcesForAppUser('slack', {
339
+ * externalUserId: 'scrappydoc-user-42',
340
+ * });
341
+ */
342
+ async listResourcesForAppUser(connectorId, input) {
343
+ if (!this.client.getAppSecret()) {
344
+ throw new Error('connectors.listResourcesForAppUser requires appSecret (sk_live_/sk_test_) in ' +
345
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
346
+ }
347
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
348
+ return this.client.request('GET', `/app-user/connector-resources/${encodeURIComponent(connectorId)}?${params.toString()}`);
349
+ }
350
+ /**
351
+ * `GET /app-user/connector-resources/:connectorId/defaults?externalUserId=...`
352
+ * — tool permissions for this connector, resolved with the App's own
353
+ * override always live (tier 0, `af_app_connectors`) since `appId` comes
354
+ * from the secret. App-user counterpart of `getDefaults()` above.
355
+ * `externalUserId` doesn't affect resolution (defaults are per-App, not
356
+ * per-end-user) — still required because the platform route always reads
357
+ * it off the scope helper shared by this controller's other routes.
358
+ *
359
+ * Requires `appSecret` — throws early otherwise, same as
360
+ * `authorizeForAppUser`.
361
+ */
362
+ async getDefaultsForAppUser(connectorId, input) {
363
+ if (!this.client.getAppSecret()) {
364
+ throw new Error('connectors.getDefaultsForAppUser requires appSecret (sk_live_/sk_test_) in ' +
365
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
366
+ }
367
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
368
+ return this.client.request('GET', `/app-user/connector-resources/${encodeURIComponent(connectorId)}/defaults?${params.toString()}`);
369
+ }
370
+ /**
371
+ * `PUT /app-user/connector-resources/:connectorId/defaults?externalUserId=...`
372
+ * — overwrites the connector's tool permissions. App-user counterpart of
373
+ * `updateDefaults()` above. NOTE (mirrors the platform controller's own
374
+ * doc comment): this write lands on the tenant-level row
375
+ * (`af_connector_tool_defaults`), same as the ak_live_ route — there's no
376
+ * App-scoped-only override write today. `externalUserId` travels as a
377
+ * query param (matching `getDefaultsForAppUser`); the platform route also
378
+ * accepts it in the body but query is canonical for this SDK's GET/PUT
379
+ * pair.
380
+ *
381
+ * Requires `appSecret` — throws early otherwise, same as
382
+ * `authorizeForAppUser`.
383
+ */
384
+ async updateDefaultsForAppUser(connectorId, permissions, input) {
385
+ if (!this.client.getAppSecret()) {
386
+ throw new Error('connectors.updateDefaultsForAppUser requires appSecret (sk_live_/sk_test_) in ' +
387
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
388
+ }
389
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
390
+ return this.client.request('PUT', `/app-user/connector-resources/${encodeURIComponent(connectorId)}/defaults?${params.toString()}`, { permissions });
391
+ }
392
+ /**
393
+ * `POST /app-user/connector-resources/clickup/tasks` — creates a REAL
394
+ * ClickUp task using the end user's OWN grant. App-user counterpart of
395
+ * `createClickupTask()` above. Unlike the GET/PUT methods above,
396
+ * `externalUserId` travels in the BODY here — the platform route reads it
397
+ * off `body.externalUserId` for this action endpoint (not a query param).
398
+ *
399
+ * Requires `appSecret` — throws early otherwise, same as
400
+ * `authorizeForAppUser`.
401
+ */
402
+ async createClickupTaskForAppUser(input) {
403
+ if (!this.client.getAppSecret()) {
404
+ throw new Error('connectors.createClickupTaskForAppUser requires appSecret (sk_live_/sk_test_) in ' +
405
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
406
+ }
407
+ return this.client.request('POST', '/app-user/connector-resources/clickup/tasks', input);
408
+ }
409
+ /**
410
+ * `POST /app-user/connector-resources/slack/messages` — posts a REAL
411
+ * Slack message using the end user's OWN grant. App-user counterpart of
412
+ * `postSlackMessage()` above. `externalUserId` travels in the BODY, same
413
+ * as `createClickupTaskForAppUser` (the platform route reads
414
+ * `body.externalUserId` for both action endpoints).
415
+ *
416
+ * Requires `appSecret` — throws early otherwise, same as
417
+ * `authorizeForAppUser`.
418
+ */
419
+ async postSlackMessageForAppUser(input) {
420
+ if (!this.client.getAppSecret()) {
421
+ throw new Error('connectors.postSlackMessageForAppUser requires appSecret (sk_live_/sk_test_) in ' +
422
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
423
+ }
424
+ return this.client.request('POST', '/app-user/connector-resources/slack/messages', input);
425
+ }
426
+ /**
427
+ * `POST /app-user/connector-resources/:connectorId/api-key` — guarda un
428
+ * API key pegado por el end user, para SU PROPIO grant (no el tenant del
429
+ * App). App-user counterpart of `saveApiKeyForTenant()` above, para
430
+ * connectors que NO usan OAuth (Granola, Mapbox). `externalUserId` viaja
431
+ * en el BODY, mismo patrón que `createClickupTaskForAppUser`/
432
+ * `postSlackMessageForAppUser`.
433
+ *
434
+ * Requires `appSecret` — throws early otherwise, same as
435
+ * `authorizeForAppUser`.
436
+ *
437
+ * @example
438
+ * await af.connectors.saveApiKeyForAppUser('granola', 'grn_xxxxx', {
439
+ * externalUserId: 'scrappydoc-user-42',
440
+ * });
441
+ */
442
+ async saveApiKeyForAppUser(connectorId, apiKey, input) {
443
+ if (!this.client.getAppSecret()) {
444
+ throw new Error('connectors.saveApiKeyForAppUser requires appSecret (sk_live_/sk_test_) in ' +
445
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
446
+ }
447
+ return this.client.request('POST', `/app-user/connector-resources/${encodeURIComponent(connectorId)}/api-key`, { apiKey, externalUserId: input.externalUserId });
448
+ }
304
449
  }
305
450
  exports.ConnectorsResource = ConnectorsResource;
package/dist/types.d.ts CHANGED
@@ -330,6 +330,11 @@ export interface AppUserConnectorStatus {
330
330
  configured: boolean;
331
331
  /** Este externalUserId particular ya completó el OAuth/API-key setup. */
332
332
  connected: boolean;
333
+ /** ¿Tiene selector de recursos real implementado (nivel 2 del picker)?
334
+ * `false` = se puede conectar pero `listResourcesForAppUser()` devuelve
335
+ * `unsupported: true`. Mismo criterio (`SUPPORTED_CONNECTORS`) que
336
+ * `ConnectorStatus.browsable` usa para el picker tenant-wide. */
337
+ browsable: boolean;
333
338
  accountLabel?: string;
334
339
  /** ISO 8601 — `Date` en el server, serializado a string sobre JSON. */
335
340
  connectedAt?: string;
@@ -338,12 +343,24 @@ export interface ConnectorResourceItem {
338
343
  id: string;
339
344
  name: string;
340
345
  description?: string;
346
+ /** Avatar/icon URL for the resource — populated for Slack DMs/people
347
+ * (AGF-074 bug #2: the interlocutor's profile photo, from
348
+ * `users.info`/`users.list`). Channels and other resource kinds leave
349
+ * this `undefined`; render a generic icon (`#` for Slack channels)
350
+ * as a fallback. */
351
+ imageUrl?: string;
341
352
  }
342
353
  export interface ListConnectorResourcesResult {
343
354
  items: ConnectorResourceItem[];
344
355
  /** true cuando el connector está conectado pero `browsable` es false —
345
356
  * no es un error, la UI debe mostrar un estado "sin selector todavía". */
346
357
  unsupported?: true;
358
+ /** AGF-074 bug #3: true cuando el provider devolvió `missing_scope` para
359
+ * un sub-recurso opcional (ej. Slack DMs vía `im`/`mpim`/`users.list`,
360
+ * en un grant OAuth previo a que el connector declarara esos scopes).
361
+ * `items` igual trae lo que SÍ se pudo listar (ej. los canales); la UI
362
+ * debe ofrecer un CTA de reconectar en vez de tratarlo como error. */
363
+ needsReconnectForDms?: true;
347
364
  }
348
365
  export interface CreateClickupTaskInput {
349
366
  /** Id de la lista de ClickUp (`ConnectorResourceItem.id` del picker). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge-io/sdk-server",
3
- "version": "4.2.0",
3
+ "version": "4.4.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",