@economic/agents 1.6.2 → 1.6.4

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.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { LanguageModel, StreamTextOnFinishCallback, ToolSet, UIMessage, generateText, streamText } from "ai";
2
2
  import { Agent as Agent$1, AgentOptions, Connection, ConnectionContext } from "agents";
3
- import { JWTPayload } from "jose";
4
3
  import { AIChatAgent, ChatResponseResult, OnChatMessageOptions } from "@cloudflare/ai-chat";
4
+ import { JWTPayload } from "jose";
5
5
 
6
6
  //#region src/server/shared/features/skills/index.d.ts
7
7
  /**
@@ -57,9 +57,9 @@ declare function buildLLMParams(config: BuildLLMParamsConfig): LLMParams;
57
57
  * type MyContext = AgentToolContext<MyBody>;
58
58
  * ```
59
59
  */
60
- type AgentToolContext<TBody = Record<string, unknown>, TSession = Record<string, unknown> | undefined> = TBody & {
61
- logEvent: (message: string, payload?: Record<string, unknown>) => void | Promise<void>;
62
- session?: TSession;
60
+ type AgentToolContext<TBody = Record<string, unknown>, TJwtIdentity = Record<string, unknown> | undefined> = TBody & {
61
+ _logEvent: (message: string, payload?: Record<string, unknown>) => void | Promise<void>;
62
+ _jwtIdentity?: TJwtIdentity;
63
63
  };
64
64
  interface AgentEnv {
65
65
  AGENT_DB: D1Database;
@@ -83,7 +83,6 @@ interface JwtAuthConfig<TClaims extends Record<string, unknown> = Record<string,
83
83
  */
84
84
  getClaims: (payload: JWTPayload) => TClaims;
85
85
  }
86
- declare function extractTokenFromConnectRequest(request: Request): string | null;
87
86
  //#endregion
88
87
  //#region src/server/agent/Agent.d.ts
89
88
  /**
@@ -96,12 +95,7 @@ declare function extractTokenFromConnectRequest(request: Request): string | null
96
95
  * For chat agents with message history, compaction, and conversation recording,
97
96
  * extend {@link ChatAgent} instead.
98
97
  */
99
- declare abstract class Agent<Env extends Cloudflare.Env = Cloudflare.Env> extends Agent$1<Env & AgentEnv> {
100
- /**
101
- * Verified session claims from JWT authentication.
102
- * Set in `onConnect` when `getJwtAuthConfig` is implemented.
103
- */
104
- private session?;
98
+ declare abstract class Agent<Env extends Cloudflare.Env = Cloudflare.Env, TJwtIdentity extends Record<string, unknown> = Record<string, unknown>> extends Agent$1<Env & AgentEnv> {
105
99
  /**
106
100
  * Override to enable JWT authentication on WebSocket connections.
107
101
  * Return the auth config based on the incoming request, or undefined to skip auth.
@@ -110,6 +104,17 @@ declare abstract class Agent<Env extends Cloudflare.Env = Cloudflare.Env> extend
110
104
  * @returns JWT auth config or undefined to skip authentication
111
105
  */
112
106
  protected getJwtAuthConfig?(request: Request): JwtAuthConfig<Record<string, unknown>> | undefined;
107
+ /**
108
+ * The identity associated with the session.
109
+ * Define getIdentity to return the identity from the request.
110
+ */
111
+ protected jwtIdentity: TJwtIdentity;
112
+ /**
113
+ * Returns the identity following verification of the JWT token - getJwtAuthConfig is required.
114
+ * @returns The identity from the request.
115
+ * @param token - The token from the request.
116
+ */
117
+ protected getJwtIdentity?(token: string): Promise<TJwtIdentity>;
113
118
  /**
114
119
  * Returns the user ID from the durable object name.
115
120
  */
@@ -153,7 +158,7 @@ interface MessageRating {
153
158
  * Skill loading, compaction, and LLM calls use `buildLLMParams` from
154
159
  * `@economic/agents` inside `onChatMessage`.
155
160
  */
156
- declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env> extends AIChatAgent<Env & ChatAgentEnv> {
161
+ declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env, TJwtIdentity extends Record<string, unknown> = Record<string, unknown>> extends AIChatAgent<Env & ChatAgentEnv> {
157
162
  /**
158
163
  * The binding of the Durable Object instance for this agent.
159
164
  */
@@ -189,11 +194,6 @@ declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env> ex
189
194
  * Default is 15.
190
195
  */
191
196
  protected maxMessagesBeforeCompaction?: number | undefined;
192
- /**
193
- * Verified session claims from JWT authentication.
194
- * Set in `onConnect` when `getJwtAuthConfig` is implemented.
195
- */
196
- private session?;
197
197
  /**
198
198
  * Override to enable JWT authentication on WebSocket connections.
199
199
  * Return the auth config based on the incoming request, or undefined to skip auth.
@@ -202,6 +202,17 @@ declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env> ex
202
202
  * @returns JWT auth config or undefined to skip authentication
203
203
  */
204
204
  protected getJwtAuthConfig?(request: Request): JwtAuthConfig<Record<string, unknown>> | undefined;
205
+ /**
206
+ * The identity associated with the session.
207
+ * Define getIdentity to return the identity from the request.
208
+ */
209
+ protected jwtIdentity: TJwtIdentity;
210
+ /**
211
+ * Returns the identity following verification of the JWT token - getJwtAuthConfig is required.
212
+ * @returns The identity from the request.
213
+ * @param token - The token from the request.
214
+ */
215
+ protected getJwtIdentity?(token: string): Promise<TJwtIdentity>;
205
216
  /**
206
217
  * Returns the user ID from the durable object name.
207
218
  */
@@ -244,7 +255,7 @@ declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env> ex
244
255
  }
245
256
  //#endregion
246
257
  //#region src/server/agent-chat/ChatAgentHarness.d.ts
247
- declare abstract class ChatAgentHarness<Env extends Cloudflare.Env, RequestBody extends Record<string, unknown> = Record<string, unknown>> extends ChatAgent<Env> {
258
+ declare abstract class ChatAgentHarness<Env extends Cloudflare.Env, RequestBody extends Record<string, unknown> = Record<string, unknown>, SessionIdentity extends Record<string, unknown> = Record<string, unknown>> extends ChatAgent<Env, SessionIdentity> {
248
259
  get binding(): {
249
260
  getByName(name: string): {
250
261
  destroy(): Promise<void>;
@@ -278,4 +289,4 @@ declare abstract class ChatAgentHarness<Env extends Cloudflare.Env, RequestBody
278
289
  onChatMessage(onFinish: StreamTextOnFinishCallback<ToolSet>, options?: OnChatMessageOptions): Promise<Response>;
279
290
  }
280
291
  //#endregion
281
- export { Agent, type AgentToolContext, type BuildLLMParamsConfig, ChatAgent, ChatAgentHarness, type Skill, buildLLMParams, extractTokenFromConnectRequest, routeAgentRequest };
292
+ export { Agent, type AgentToolContext, type BuildLLMParamsConfig, ChatAgent, ChatAgentHarness, type Skill, buildLLMParams, routeAgentRequest };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Output, convertToModelMessages, generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
2
2
  import { Agent as Agent$1, callable, routeAgentRequest as routeAgentRequest$1 } from "agents";
3
- import { createRemoteJWKSet, decodeJwt, errors, jwtVerify } from "jose";
4
3
  import { AIChatAgent } from "@cloudflare/ai-chat";
4
+ import { createRemoteJWKSet, decodeJwt, errors, jwtVerify } from "jose";
5
5
  //#region src/server/shared/features/skills/index.ts
6
6
  const TOOL_NAME_ACTIVATE_SKILL = "activate_skill";
7
7
  const TOOL_NAME_LIST_CAPABILITIES = "list_capabilities";
@@ -269,6 +269,98 @@ async function routeAgentRequest(request, env, options) {
269
269
  return response;
270
270
  }
271
271
  //#endregion
272
+ //#region src/server/shared/features/audit/audit.ts
273
+ /**
274
+ * Inserts a single audit event row into the shared `audit_events` D1 table.
275
+ *
276
+ * Called by `ChatAgentHarness.logEvent()` (and `AgentHarness.logEvent()`). Not intended for direct use.
277
+ */
278
+ const SENSITIVE_KEYS = /^(password|token|secret|api_key|apikey|authorization|credentials)$/i;
279
+ const REDACTED = "[REDACTED]";
280
+ /** Deep-clone and redact values for keys that look like secrets (for audit logging). */
281
+ function sanitizePayload(value) {
282
+ if (value === null || value === void 0) return value;
283
+ if (Array.isArray(value)) return value.map(sanitizePayload);
284
+ if (typeof value === "object") {
285
+ const result = {};
286
+ for (const [key, val] of Object.entries(value)) result[key] = SENSITIVE_KEYS.test(key) ? REDACTED : sanitizePayload(val);
287
+ return result;
288
+ }
289
+ return value;
290
+ }
291
+ async function insertAuditEvent(db, durableObjectName, message, payload) {
292
+ await db.prepare(`INSERT INTO audit_events (id, durable_object_name, message, payload, created_at)
293
+ VALUES (?, ?, ?, ?, ?)`).bind(crypto.randomUUID(), durableObjectName, message, payload ? JSON.stringify(sanitizePayload(payload)) : null, (/* @__PURE__ */ new Date()).toISOString()).run();
294
+ }
295
+ function stringifyForSkillScan(output) {
296
+ if (typeof output === "string") return output;
297
+ if (output === null || output === void 0) return "";
298
+ try {
299
+ return JSON.stringify(output);
300
+ } catch {
301
+ return "";
302
+ }
303
+ }
304
+ function forEachStep(event, fn) {
305
+ if (event.steps.length > 0) for (const step of event.steps) fn(step);
306
+ else fn(event);
307
+ }
308
+ /**
309
+ * User text from provider request body (`contents` is e.g. Gemini format).
310
+ */
311
+ function extractUserInputFromRequestBody(body) {
312
+ const firstContent = body?.contents?.[0];
313
+ if (firstContent?.role !== "user" || !firstContent.parts?.length) return "";
314
+ return firstContent.parts.map((p) => p.text).filter((t) => typeof t === "string").join(" ").trim();
315
+ }
316
+ /**
317
+ * Builds the payload for a "Turn completed" audit event from the AI SDK
318
+ * `OnFinishEvent`.
319
+ *
320
+ * Returns:
321
+ * - `input`: user message text from `request.body.contents[0]` (Gemini-style)
322
+ * - `output`: assistant response text (truncated to 200 chars)
323
+ * - `toolCalls`: array of { toolName, toolInput, toolOutput }
324
+ * - `loadedSkills`: skill names extracted from activate_skill results
325
+ */
326
+ function buildTurnLogPayload(event) {
327
+ const toolCalls = [];
328
+ let latestSkills;
329
+ const toolOutputs = /* @__PURE__ */ new Map();
330
+ forEachStep(event, (step) => {
331
+ for (const tr of step.toolResults) toolOutputs.set(tr.toolCallId, tr.output);
332
+ });
333
+ forEachStep(event, (step) => {
334
+ for (const tc of step.toolCalls) toolCalls.push({
335
+ name: tc.toolName,
336
+ input: tc.input,
337
+ output: toolOutputs.get(tc.toolCallId)
338
+ });
339
+ const considerToolResultForSkills = (toolName, output) => {
340
+ if (toolName !== "activate_skill") return;
341
+ const s = stringifyForSkillScan(output);
342
+ const sentinelIdx = s.indexOf(SKILL_STATE_SENTINEL);
343
+ if (sentinelIdx === -1) return;
344
+ try {
345
+ const stateJson = s.slice(sentinelIdx + 18);
346
+ latestSkills = JSON.parse(stateJson);
347
+ } catch {}
348
+ };
349
+ for (const tr of step.toolResults) considerToolResultForSkills(tr.toolName, tr.output);
350
+ });
351
+ const input = extractUserInputFromRequestBody(event.request?.body);
352
+ return {
353
+ detail: {
354
+ model: event.model.modelId,
355
+ tokens: event.usage?.totalTokens
356
+ },
357
+ loadedSkills: latestSkills ?? [],
358
+ toolCalls,
359
+ input: input.slice(0, 200),
360
+ output: (event.text ?? "").slice(0, 200)
361
+ };
362
+ }
363
+ //#endregion
272
364
  //#region src/server/shared/features/auth/index.ts
273
365
  const jwksByIssuer = /* @__PURE__ */ new Map();
274
366
  function getJwksForIssuer(issuer) {
@@ -369,98 +461,6 @@ async function verifyJwt(request, config) {
369
461
  };
370
462
  }
371
463
  //#endregion
372
- //#region src/server/shared/features/audit/audit.ts
373
- /**
374
- * Inserts a single audit event row into the shared `audit_events` D1 table.
375
- *
376
- * Called by `ChatAgentHarness.logEvent()` (and `AgentHarness.logEvent()`). Not intended for direct use.
377
- */
378
- const SENSITIVE_KEYS = /^(password|token|secret|api_key|apikey|authorization|credentials)$/i;
379
- const REDACTED = "[REDACTED]";
380
- /** Deep-clone and redact values for keys that look like secrets (for audit logging). */
381
- function sanitizePayload(value) {
382
- if (value === null || value === void 0) return value;
383
- if (Array.isArray(value)) return value.map(sanitizePayload);
384
- if (typeof value === "object") {
385
- const result = {};
386
- for (const [key, val] of Object.entries(value)) result[key] = SENSITIVE_KEYS.test(key) ? REDACTED : sanitizePayload(val);
387
- return result;
388
- }
389
- return value;
390
- }
391
- async function insertAuditEvent(db, durableObjectName, message, payload) {
392
- await db.prepare(`INSERT INTO audit_events (id, durable_object_name, message, payload, created_at)
393
- VALUES (?, ?, ?, ?, ?)`).bind(crypto.randomUUID(), durableObjectName, message, payload ? JSON.stringify(sanitizePayload(payload)) : null, (/* @__PURE__ */ new Date()).toISOString()).run();
394
- }
395
- function stringifyForSkillScan(output) {
396
- if (typeof output === "string") return output;
397
- if (output === null || output === void 0) return "";
398
- try {
399
- return JSON.stringify(output);
400
- } catch {
401
- return "";
402
- }
403
- }
404
- function forEachStep(event, fn) {
405
- if (event.steps.length > 0) for (const step of event.steps) fn(step);
406
- else fn(event);
407
- }
408
- /**
409
- * User text from provider request body (`contents` is e.g. Gemini format).
410
- */
411
- function extractUserInputFromRequestBody(body) {
412
- const firstContent = body?.contents?.[0];
413
- if (firstContent?.role !== "user" || !firstContent.parts?.length) return "";
414
- return firstContent.parts.map((p) => p.text).filter((t) => typeof t === "string").join(" ").trim();
415
- }
416
- /**
417
- * Builds the payload for a "Turn completed" audit event from the AI SDK
418
- * `OnFinishEvent`.
419
- *
420
- * Returns:
421
- * - `input`: user message text from `request.body.contents[0]` (Gemini-style)
422
- * - `output`: assistant response text (truncated to 200 chars)
423
- * - `toolCalls`: array of { toolName, toolInput, toolOutput }
424
- * - `loadedSkills`: skill names extracted from activate_skill results
425
- */
426
- function buildTurnLogPayload(event) {
427
- const toolCalls = [];
428
- let latestSkills;
429
- const toolOutputs = /* @__PURE__ */ new Map();
430
- forEachStep(event, (step) => {
431
- for (const tr of step.toolResults) toolOutputs.set(tr.toolCallId, tr.output);
432
- });
433
- forEachStep(event, (step) => {
434
- for (const tc of step.toolCalls) toolCalls.push({
435
- name: tc.toolName,
436
- input: tc.input,
437
- output: toolOutputs.get(tc.toolCallId)
438
- });
439
- const considerToolResultForSkills = (toolName, output) => {
440
- if (toolName !== "activate_skill") return;
441
- const s = stringifyForSkillScan(output);
442
- const sentinelIdx = s.indexOf(SKILL_STATE_SENTINEL);
443
- if (sentinelIdx === -1) return;
444
- try {
445
- const stateJson = s.slice(sentinelIdx + 18);
446
- latestSkills = JSON.parse(stateJson);
447
- } catch {}
448
- };
449
- for (const tr of step.toolResults) considerToolResultForSkills(tr.toolName, tr.output);
450
- });
451
- const input = extractUserInputFromRequestBody(event.request?.body);
452
- return {
453
- detail: {
454
- model: event.model.modelId,
455
- tokens: event.usage?.totalTokens
456
- },
457
- loadedSkills: latestSkills ?? [],
458
- toolCalls,
459
- input: input.slice(0, 200),
460
- output: (event.text ?? "").slice(0, 200)
461
- };
462
- }
463
- //#endregion
464
464
  //#region src/server/agent/Agent.ts
465
465
  /**
466
466
  * Base agent for Cloudflare Agents SDK Durable Objects with lazy skill loading,
@@ -474,10 +474,10 @@ function buildTurnLogPayload(event) {
474
474
  */
475
475
  var Agent = class extends Agent$1 {
476
476
  /**
477
- * Verified session claims from JWT authentication.
478
- * Set in `onConnect` when `getJwtAuthConfig` is implemented.
477
+ * The identity associated with the session.
478
+ * Define getIdentity to return the identity from the request.
479
479
  */
480
- session;
480
+ jwtIdentity = {};
481
481
  /**
482
482
  * Returns the user ID from the durable object name.
483
483
  */
@@ -510,7 +510,8 @@ var Agent = class extends Agent$1 {
510
510
  connection.close(result.status === 401 ? 4001 : 4003, result.message);
511
511
  return;
512
512
  }
513
- this.session = result.claims;
513
+ const token = extractTokenFromConnectRequest(ctx.request);
514
+ if (token) this.jwtIdentity = await this.getJwtIdentity?.(token) ?? {};
514
515
  }
515
516
  }
516
517
  return super.onConnect(connection, ctx);
@@ -541,8 +542,8 @@ var Agent = class extends Agent$1 {
541
542
  const experimental_context = {
542
543
  ...config.experimental_context,
543
544
  ...config.options?.body,
544
- session: this.session,
545
- logEvent: this.logEvent.bind(this)
545
+ _jwtIdentity: this.jwtIdentity,
546
+ _logEvent: this.logEvent.bind(this)
546
547
  };
547
548
  const onFinish = async (event) => {
548
549
  this.logEvent("Turn completed", buildTurnLogPayload(event));
@@ -844,10 +845,10 @@ var ChatAgent = class extends AIChatAgent {
844
845
  */
845
846
  maxMessagesBeforeCompaction = 15;
846
847
  /**
847
- * Verified session claims from JWT authentication.
848
- * Set in `onConnect` when `getJwtAuthConfig` is implemented.
848
+ * The identity associated with the session.
849
+ * Define getIdentity to return the identity from the request.
849
850
  */
850
- session;
851
+ jwtIdentity = {};
851
852
  /**
852
853
  * Returns the user ID from the durable object name.
853
854
  */
@@ -880,8 +881,9 @@ var ChatAgent = class extends AIChatAgent {
880
881
  connection.close(result.status === 401 ? 4001 : 4003, result.message);
881
882
  return;
882
883
  }
883
- this.session = result.claims;
884
884
  }
885
+ const token = extractTokenFromConnectRequest(ctx.request);
886
+ if (token) this.jwtIdentity = await this.getJwtIdentity?.(token) ?? {};
885
887
  }
886
888
  return super.onConnect(connection, ctx);
887
889
  }
@@ -916,8 +918,8 @@ var ChatAgent = class extends AIChatAgent {
916
918
  const experimental_context = {
917
919
  ...config.experimental_context,
918
920
  ...config.options?.body,
919
- session: this.session,
920
- logEvent: this.logEvent.bind(this)
921
+ _jwtIdentity: this.jwtIdentity,
922
+ _logEvent: this.logEvent.bind(this)
921
923
  };
922
924
  const messages = await convertToModelMessages(this.messages);
923
925
  const fastModel = this.getFastModel();
@@ -1040,4 +1042,4 @@ var ChatAgentHarness = class extends ChatAgent {
1040
1042
  }
1041
1043
  };
1042
1044
  //#endregion
1043
- export { Agent, ChatAgent, ChatAgentHarness, buildLLMParams, extractTokenFromConnectRequest, routeAgentRequest };
1045
+ export { Agent, ChatAgent, ChatAgentHarness, buildLLMParams, routeAgentRequest };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@economic/agents",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "description": "A starter for creating a TypeScript package.",
5
5
  "license": "MIT",
6
6
  "bin": {