@collective-intelligence/external-apps-server-sdk 0.1.4 → 0.1.6

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/README.md CHANGED
@@ -427,6 +427,54 @@ succeeded before the new turn started count. A turn is
427
427
  subject, never with the application's scopes. Visitors are separate owners:
428
428
  one visitor never reads another visitor's conversation.
429
429
 
430
+ ## Messages
431
+
432
+ `messages.send` sends one message through a messaging connection of the
433
+ Organization, for example a Telegram bot or a WhatsApp Business Account. The
434
+ application needs the `messaging:send` scope, and an administrator must grant
435
+ the connection to the application on its administration page.
436
+
437
+ ```ts
438
+ const sent = await ci.messages.send({
439
+ connectionId,
440
+ content: { kind: "text", text: "Your order is ready." },
441
+ idempotencyKey: `order-${orderId}-ready`,
442
+ organizationId,
443
+ target: { conversation, mode: "continue" }
444
+ })
445
+ // sent.value.result: { conversation, messageId }
446
+ ```
447
+
448
+ - `target` continues a conversation with the Conversation Reference of an
449
+ earlier message (`{ mode: "continue", conversation }`), or starts one with
450
+ the recipient fields of the vendor (`{ mode: "start_new", recipient }`).
451
+ - `content` is a text, or `{ kind: "voice", text, voice }` for a text that CI
452
+ speaks.
453
+ - `sender` holds the sender fields of the vendor, for example
454
+ `{ phoneNumberId }`. It is required when the account has more than one
455
+ sender. `service` names the vendor service when the connection has more
456
+ than one.
457
+ - The result is the Message Receipt: the message id of the vendor and the
458
+ Conversation Reference that continues the conversation. It means that the
459
+ vendor accepted the message, not that a person received it.
460
+ - `idempotencyKey` (at most 256 characters; generated when omitted) makes a
461
+ repeat safe. A repeat with the same key and content returns the first
462
+ outcome and sends nothing. A repeat with the same key and other content
463
+ returns `conflict`. Use your own key when your server can repeat a send
464
+ after a crash.
465
+ - CI sends once and never retries a failed send. A failed send returns
466
+ `message_send_failed` with a `sendErrorCode`, for example `window_closed`,
467
+ `recipient_unreachable`, `template_required` or `transport`. The same key
468
+ returns the same failure: use a new key to send again.
469
+ - `send_outcome_unknown` means that the first send with the key has no
470
+ recorded outcome: it still runs, or CI stopped before it recorded the
471
+ outcome. CI does not send again for the key. Repeat the call later; if the
472
+ answer stays the same, check with the vendor before you send with a new key.
473
+ - `not_found` means that the connection does not exist or is not granted to
474
+ the application. `rate_limited` (HTTP 429) means that the connection
475
+ reached its rate limit; it is retryable, and the client repeats it with the
476
+ same key.
477
+
430
478
  ## Member users
431
479
 
432
480
  A confidential application administers the `member` users of its own
@@ -503,11 +551,18 @@ const links = await ci.users.listCompanyLinks({ organizationId, userId })
503
551
  - A delegated user's `/application/v1/resources` and `knowledge_*` calls, and
504
552
  the Agents, Skills and workflows the user runs, carry the user's links to
505
553
  Knowledge policy as `subject.linkedCompanyIds`, and a policy reads the
506
- entity id as `resource` path `["id"]`. To keep `member`-role users to their
507
- own companies, declare a `deny` `filter` policy on the company schema: subject
508
- match `user.role` equals `member`, rule "entity id not in
509
- `linkedCompanyIds`". Gets and writes of any other company, creates included,
510
- then fail with `forbidden` and lists omit it; other roles and service
554
+ entity id as `resource` path `["id"]`. CI also sets `subject.access`:
555
+ `"read"` for gets, lists, searches, facets, autocomplete, traversal and
556
+ retrieval, `"write"` for creates, updates, deletes and relation writes. The
557
+ operation sets it; a caller cannot. A policy that does not test `access`
558
+ applies to reads and writes alike.
559
+ - To let `member`-role users write only their own companies but read every
560
+ company (for example through member-run Agents), declare a `deny` `filter`
561
+ policy on the company schema: subject match `user.role` equals `member` and
562
+ `access` equals `write`, rule "entity id not in `linkedCompanyIds`". Updates
563
+ and deletes of any other company, and creates, then fail with `forbidden`;
564
+ gets, lists and searches still return it. Without the `access` clause the
565
+ same policy also hides other companies from reads. Other roles and service
511
566
  credentials are unaffected.
512
567
  - The policy binds an execution only while it acts as the member. An Agent or
513
568
  Skill tool bound to a fixed user, or a workflow that runs as another user or
@@ -1149,7 +1204,100 @@ The store persists only hashed opaque-cookie state, token grants and revisions;
1149
1204
  the browser receives a `Secure`, `HttpOnly`, `SameSite=Lax` host cookie. Every
1150
1205
  session lookup revalidates the short-lived CI identity. Revoked or expired CI
1151
1206
  access ends application authorization on the next validation. A confidential
1152
- OAuth client may supply `clientSecret`; a public PKCE client omits it.
1207
+ OAuth client may supply `clientSecret`; a public PKCE client omits it. The
1208
+ client type decides how long the User session lasts; see
1209
+ [Session lifetime and renewal](#session-lifetime-and-renewal).
1210
+
1211
+ ### Session lifetime and renewal
1212
+
1213
+ `sessionTtlSeconds` sets the Session lifetime: the time from sign-in to the
1214
+ end of the User session. The default is `86400` (one day). The SDK accepts an
1215
+ integer from `300` to `604800` (seven days).
1216
+
1217
+ A User session lasts its Session lifetime only with renewal. Without renewal,
1218
+ the User session ends when its Access token expires, 300 seconds after
1219
+ sign-in, whatever the value of `sessionTtlSeconds`. The Access token lifetime
1220
+ is always 300 seconds. There is no setting for it.
1221
+
1222
+ Renewal needs these three conditions:
1223
+
1224
+ - The external Application is confidential at CI.
1225
+ - `createFederatedAuth` gets the `clientSecret` of that Application.
1226
+ - The user signed in after the upgrade to an SDK version that renews. An
1227
+ earlier cookie or store record holds no Renewal credential. That User
1228
+ session ends at its Access token expiry.
1229
+
1230
+ When the three conditions are true, CI returns a Renewal credential at
1231
+ sign-in. On the wire it is the OAuth `refresh_token`. The SDK keeps it in the
1232
+ session record and uses it to get a new Access token with no browser redirect.
1233
+ The application code does not change.
1234
+
1235
+ A public client has no client secret, so it gets no Renewal credential. Its
1236
+ User session lasts 300 seconds. To continue, the application sends the browser
1237
+ to `<basePath>/login` again, as before. A `fetch` call or a form post cannot
1238
+ follow that redirect. For a public client, `createFederatedAuth` still accepts
1239
+ each `sessionTtlSeconds` value in the range and gives no warning.
1240
+
1241
+ `getSession`, `getAccessToken`, `getEntityContext` and
1242
+ `GET <basePath>/session` are the session lookups. With renewal, each lookup
1243
+ first gets a new Access token when the current one is expired or expires in 30
1244
+ seconds or less. Then it checks the current identity at `/oauth/identity`. A
1245
+ lookup never returns an expired Access token. Lookups at the same time share
1246
+ one renewal request.
1247
+
1248
+ The SDK writes the cookie or the store record one time, at sign-in. A renewal
1249
+ writes no cookie and no store record. A renewed Access token stays only in
1250
+ process memory. Each `createFederatedAuth` instance keeps at most 5000 of
1251
+ them, separate from each other instance. A restart loses them, and the next
1252
+ lookup renews again.
1253
+
1254
+ A Federated authorization is the record that CI keeps of one sign-in to one
1255
+ external Application. It lasts at most 604800 seconds from sign-in, and never
1256
+ longer than the CI session of the user. The User session ends at the earlier
1257
+ of these two times:
1258
+
1259
+ - sign-in plus `sessionTtlSeconds`;
1260
+ - the end of the Federated authorization.
1261
+
1262
+ `expiresAt` of the session reports that end. It is the same at each lookup,
1263
+ because a lookup does not extend the User session. Without a Renewal
1264
+ credential, `expiresAt` stays the Access token expiry.
1265
+
1266
+ A lookup has one of three results:
1267
+
1268
+ - **A session.** CI accepts the identity, after a renewal when one is
1269
+ necessary.
1270
+ - **`null`**, and `401` on `GET <basePath>/session`. CI refuses the user or
1271
+ the Federated authorization: it answers `invalid_grant` to the renewal, or
1272
+ it refuses the identity. The result is also `null` after the end of the User
1273
+ session. Send the user to the login route.
1274
+ - **`ApplicationUserAuthUnavailableError`**, and `503` on
1275
+ `GET <basePath>/session`. The client secret is wrong or rotated
1276
+ (`invalid_client`), CI gives another OAuth error, or CI does not answer. The
1277
+ User session stays. It returns when the fault is corrected, with no
1278
+ sign-in.
1279
+
1280
+ A renewal applies the same admission rule as `/oauth/identity`:
1281
+
1282
+ - the external Application is enabled;
1283
+ - the CI session is live;
1284
+ - the user is not banned and is still admitted to the Organization, which
1285
+ today means the Organization membership of the user and its role;
1286
+ - the scopes of the sign-in are still allowed.
1287
+
1288
+ A refusal ends nothing. When the condition is corrected, the same Renewal
1289
+ credential renews again. A Federated authorization ends for good only by one
1290
+ of these events: logout, a new sign-in in the same User session, its limit of
1291
+ 604800 seconds, or the end of the CI session.
1292
+
1293
+ `POST <basePath>/logout` presents the Renewal credential to CI, and CI ends
1294
+ the Federated authorization. If CI does not answer, the route answers `503`
1295
+ and still clears the User session. That Federated authorization then ends at
1296
+ its limit or with its CI session.
1297
+
1298
+ An application that does not use this SDK finds the CI token and logout
1299
+ contract in
1300
+ [Access token renewal](../../../docs/architecture/external-app-gateway.md#access-token-renewal).
1153
1301
 
1154
1302
  ### Configure from the CLI enrollment
1155
1303
 
@@ -1159,6 +1307,9 @@ OAuth client may supply `clientSecret`; a public PKCE client omits it.
1159
1307
  `CI_CLIENT_SECRET`. `readApplicationEnrollment` reads those variables, so the
1160
1308
  application names none of them. It throws a `TypeError` that lists every
1161
1309
  missing variable. Pass the same prefix as `--prefix` when it is not `CI_`.
1310
+ `ci apps env <application>` prints the same variables for an application that
1311
+ already exists, for example to copy them into a deployment pipeline; it leaves
1312
+ out `CI_CLIENT_SECRET`, which CI shows only once.
1162
1313
 
1163
1314
  ```ts
1164
1315
  import {
package/dist/admin.d.ts CHANGED
@@ -4965,6 +4965,7 @@ declare const createAdministrationApi: (client: Client$1) => {
4965
4965
  description: string | null;
4966
4966
  directChildCount: number;
4967
4967
  directMemberCount: number;
4968
+ fqn: string | null;
4968
4969
  id: string;
4969
4970
  inheritedMembershipImpact: {
4970
4971
  descendantGroupCount: number;
@@ -4973,6 +4974,11 @@ declare const createAdministrationApi: (client: Client$1) => {
4973
4974
  };
4974
4975
  name: string;
4975
4976
  organizationId: string;
4977
+ package: {
4978
+ fqn: string;
4979
+ managed: boolean;
4980
+ readonly: boolean;
4981
+ } | null;
4976
4982
  parentGroupId: string | null;
4977
4983
  parentPath: Array<{
4978
4984
  id: string;
@@ -5001,6 +5007,7 @@ declare const createAdministrationApi: (client: Client$1) => {
5001
5007
  description: string | null;
5002
5008
  directChildCount: number;
5003
5009
  directMemberCount: number;
5010
+ fqn: string | null;
5004
5011
  id: string;
5005
5012
  inheritedMembershipImpact: {
5006
5013
  descendantGroupCount: number;
@@ -5009,6 +5016,11 @@ declare const createAdministrationApi: (client: Client$1) => {
5009
5016
  };
5010
5017
  name: string;
5011
5018
  organizationId: string;
5019
+ package: {
5020
+ fqn: string;
5021
+ managed: boolean;
5022
+ readonly: boolean;
5023
+ } | null;
5012
5024
  parentGroupId: string | null;
5013
5025
  parentPath: Array<{
5014
5026
  id: string;
@@ -125,6 +125,10 @@ declare class FederatedAuthRejectedError extends Error {
125
125
  export type FederatedGrant = Readonly<{
126
126
  accessToken: string;
127
127
  expiresAt: number;
128
+ /** Renewal credential of the Federated authorization; set together with `refreshExpiresAt`. */
129
+ refreshToken?: string;
130
+ /** Epoch milliseconds at which the Renewal credential stops; set together with `refreshToken`. */
131
+ refreshExpiresAt?: number;
128
132
  }>;
129
133
  export type FederatedLogin = Readonly<{
130
134
  status: "idle";