@economic/agents 2.7.2 → 2.8.1

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
@@ -501,6 +501,7 @@ export class SupportAgent extends Agent {
501
501
  userGuid: payload.user_guid as string,
502
502
  agreementNumber: payload.agreement_number as number,
503
503
  }),
504
+ getActorId: (claims) => `${claims.agreementNumber}_${claims.userGuid}`,
504
505
  };
505
506
  }
506
507
 
@@ -508,6 +509,11 @@ export class SupportAgent extends Agent {
508
509
  }
509
510
  ```
510
511
 
512
+ `getActorId` derives the authenticated actor from verified claims. It must exactly match the
513
+ actor that owns the requested Durable Object: the Assistant name, the root parent of a chat
514
+ facet, or the prefix in a standalone `${actorId}:${chatId}` name. A valid token belonging to
515
+ another actor is rejected with `4003` before the connection is established.
516
+
511
517
  On failure the socket closes (`4001` unauthorized, `4003` forbidden) and status becomes `"unauthorized"`. The token is read from `Authorization: Bearer …`, then the `Sec-WebSocket-Protocol: bearer, <token>` header.
512
518
 
513
519
  ### User context
@@ -585,18 +591,18 @@ Imported from `@economic/agents` unless noted.
585
591
 
586
592
  ### Members
587
593
 
588
- | Member | On | Description |
589
- | ---------------------------------------------- | ----------- | ------------------------------------------- |
590
- | `getModel(ctx?)` | `Agent` | Required. Model for inference. |
591
- | `getSystemPrompt(ctx?)` | `Agent` | Required. System prompt. |
592
- | `getTools()` | `Agent` | Always-on tools (default `{}`). |
593
- | `getSkills()` | `Agent` | Local skills (default `[]`). |
594
- | `static getJwtAuthConfig(env)` | `Agent` | Optional JWT verification config. |
595
- | `getUserContext(jwtToken)` | `Agent` | Optional per-user context after auth. |
596
- | `submitMessageFeedback` / `getMessageFeedback` | `ChatAgent` | Callable message feedback. |
597
- | `agent` | `Assistant` | Required. Your `ChatAgent` subclass. |
598
- | `fastModel` | `Assistant` | Required. Cheap model for titles/summaries. |
599
- | `createChat` / `getChats` / `deleteChat` | `Assistant` | Callable chat management. |
594
+ | Member | On | Description |
595
+ | ---------------------------------------------- | ----------- | -------------------------------------------- |
596
+ | `getModel(ctx?)` | `Agent` | Required. Model for inference. |
597
+ | `getSystemPrompt(ctx?)` | `Agent` | Required. System prompt. |
598
+ | `getTools()` | `Agent` | Always-on tools (default `{}`). |
599
+ | `getSkills()` | `Agent` | Local skills (default `[]`). |
600
+ | `static getJwtAuthConfig(env)` | `Agent` | Optional JWT verification and actor binding. |
601
+ | `getUserContext(jwtToken)` | `Agent` | Optional per-user context after auth. |
602
+ | `submitMessageFeedback` / `getMessageFeedback` | `ChatAgent` | Callable message feedback. |
603
+ | `agent` | `Assistant` | Required. Your `ChatAgent` subclass. |
604
+ | `fastModel` | `Assistant` | Required. Cheap model for titles/summaries. |
605
+ | `createChat` / `getChats` / `deleteChat` | `Assistant` | Callable chat management. |
600
606
 
601
607
  ### Package root (`@economic/agents`)
602
608
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as JwtAuthConfig, t as routeAgentRequest } from "./route-agent-request-D2ll--nF.mjs";
1
+ import { n as JwtAuthConfig, t as routeAgentRequest } from "./route-agent-request-DLlW8kZo.mjs";
2
2
  import { Agent as Agent$1, Connection, ConnectionContext, SubAgentClass } from "agents";
3
3
  import { ChatResponseResult, PrepareStepContext, Session, Think, ToolCallContext, ToolCallDecision, TurnConfig, TurnContext, skills } from "@cloudflare/think";
4
4
  import { JSONValue, LanguageModel, ModelMessage, Tool as Tool$1, UIMessage } from "ai";
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as verifyJwt, i as extractTokenFromConnectRequest, n as createAgentTracer, r as recordFeedbackAnalytics, t as routeAgentRequest } from "./route-agent-request-DBGSXu2O.mjs";
1
+ import { a as isJwtActorAuthorized, i as extractTokenFromConnectRequest, n as createAgentTracer, o as verifyJwt, r as recordFeedbackAnalytics, t as routeAgentRequest } from "./route-agent-request-CYclS_5Q.mjs";
2
2
  import { Agent as Agent$1, callable, getCurrentAgent } from "agents";
3
3
  import { Think, skills } from "@cloudflare/think";
4
4
  import { Output, convertToModelMessages, generateText, jsonSchema, pruneMessages, tool as tool$1 } from "ai";
@@ -359,6 +359,15 @@ var Agent = class extends Think {
359
359
  connection.close(result.status === 401 ? 4001 : 4003, result.message);
360
360
  return;
361
361
  }
362
+ if (!isJwtActorAuthorized(config, result.claims, {
363
+ durableObjectName: this.name,
364
+ parentPath: this.parentPath
365
+ })) {
366
+ sendConnectionStatus(connection, "unauthorized");
367
+ console.error("[Agent] JWT actor does not match the requested agent");
368
+ connection.close(4003, "Forbidden");
369
+ return;
370
+ }
362
371
  const token = extractTokenFromConnectRequest(ctx.request);
363
372
  let effectiveToken = token;
364
373
  const getTokenExchangeConfig = this.constructor.getTokenExchangeConfig;
@@ -1042,6 +1051,15 @@ var Assistant = class extends Agent$1 {
1042
1051
  connection.close(result.status === 401 ? 4001 : 4003, result.message);
1043
1052
  return;
1044
1053
  }
1054
+ if (!isJwtActorAuthorized(config, result.claims, {
1055
+ durableObjectName: this.name,
1056
+ parentPath: this.parentPath
1057
+ })) {
1058
+ sendConnectionStatus(connection, "unauthorized");
1059
+ console.error("[Assistant] JWT actor does not match the requested assistant");
1060
+ connection.close(4003, "Forbidden");
1061
+ return;
1062
+ }
1045
1063
  }
1046
1064
  }
1047
1065
  sendConnectionStatus(connection, "connected");
@@ -3,6 +3,18 @@ import { createRemoteJWKSet, decodeJwt, errors, jwtVerify } from "jose";
3
3
  import { BasicTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
4
4
  //#region src/server/features/auth/index.ts
5
5
  const jwksByIssuer = /* @__PURE__ */ new Map();
6
+ /** Returns the actor portion of a top-level or parent-scoped agent name. */
7
+ function getTargetActorId(target) {
8
+ const rootName = target.parentPath?.[0]?.name ?? target.durableObjectName;
9
+ const separator = rootName.indexOf(":");
10
+ return separator === -1 ? rootName : rootName.slice(0, separator);
11
+ }
12
+ /** Checks that verified JWT claims belong to the addressed agent instance. */
13
+ function isJwtActorAuthorized(config, claims, target) {
14
+ if (typeof config.getActorId !== "function") return false;
15
+ const authenticatedActorId = config.getActorId(claims);
16
+ return typeof authenticatedActorId === "string" && authenticatedActorId.length > 0 && authenticatedActorId === getTargetActorId(target);
17
+ }
6
18
  function getJwksForIssuer(issuer) {
7
19
  const normalized = issuer.replace(/\/$/, "");
8
20
  let jwks = jwksByIssuer.get(normalized);
@@ -465,4 +477,4 @@ async function routeAgentRequest$1(request, env, options) {
465
477
  return response;
466
478
  }
467
479
  //#endregion
468
- export { verifyJwt as a, extractTokenFromConnectRequest as i, createAgentTracer as n, recordFeedbackAnalytics as r, routeAgentRequest$1 as t };
480
+ export { isJwtActorAuthorized as a, extractTokenFromConnectRequest as i, createAgentTracer as n, verifyJwt as o, recordFeedbackAnalytics as r, routeAgentRequest$1 as t };
@@ -13,6 +13,8 @@ interface JwtAuthConfig<TClaims extends Record<string, unknown> = Record<string,
13
13
  * These will be stored and made available in the agent's tool context.
14
14
  */
15
15
  getClaims: (payload: Record<string, unknown>) => TClaims;
16
+ /** Derive the actor ID represented by the verified claims. */
17
+ getActorId(claims: TClaims): string;
16
18
  }
17
19
  //#endregion
18
20
  //#region src/server/route-agent-request.d.ts
package/dist/v1.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as JwtAuthConfig, t as routeAgentRequest } from "./route-agent-request-D2ll--nF.mjs";
1
+ import { n as JwtAuthConfig, t as routeAgentRequest } from "./route-agent-request-DLlW8kZo.mjs";
2
2
  import { Agent as Agent$1, Connection, ConnectionContext } from "agents";
3
3
  import { LanguageModel, StreamTextOnFinishCallback, ToolSet, UIMessage, generateText, streamText } from "ai";
4
4
  import { AIChatAgent, ChatResponseResult, OnChatMessageOptions } from "@cloudflare/ai-chat";
package/dist/v1.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as verifyJwt, i as extractTokenFromConnectRequest, n as createAgentTracer, t as routeAgentRequest } from "./route-agent-request-DBGSXu2O.mjs";
1
+ import { a as isJwtActorAuthorized, i as extractTokenFromConnectRequest, n as createAgentTracer, o as verifyJwt, t as routeAgentRequest } from "./route-agent-request-CYclS_5Q.mjs";
2
2
  import { Agent as Agent$1, callable, getCurrentAgent } from "agents";
3
3
  import { Output, convertToModelMessages, generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
4
4
  import { AIChatAgent } from "@cloudflare/ai-chat";
@@ -312,6 +312,13 @@ var Agent = class extends Agent$1 {
312
312
  connection.close(result.status === 401 ? 4001 : 4003, result.message);
313
313
  return;
314
314
  }
315
+ if (!isJwtActorAuthorized(config, result.claims, {
316
+ durableObjectName: this.name,
317
+ parentPath: this.parentPath
318
+ })) {
319
+ connection.close(4003, "Forbidden");
320
+ return;
321
+ }
315
322
  const token = extractTokenFromConnectRequest(ctx.request);
316
323
  if (token) {
317
324
  const userContext = await this.getUserContext?.(token);
@@ -895,6 +902,17 @@ var ChatAgent = class extends AIChatAgent {
895
902
  });
896
903
  return;
897
904
  }
905
+ if (!isJwtActorAuthorized(config, result.claims, {
906
+ durableObjectName: this.name,
907
+ parentPath: this.parentPath
908
+ })) {
909
+ connection.close(4003, "Forbidden");
910
+ this.setState({
911
+ type: "chat",
912
+ status: "unauthorized"
913
+ });
914
+ return;
915
+ }
898
916
  }
899
917
  const token = extractTokenFromConnectRequest(ctx.request);
900
918
  if (token) this._pendingUserContextRequest = this.getUserContext?.(token).then((userContext) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@economic/agents",
3
- "version": "2.7.2",
3
+ "version": "2.8.1",
4
4
  "description": "A starter for creating a TypeScript package.",
5
5
  "license": "MIT",
6
6
  "files": [