@economic/agents 2.15.0 → 3.0.0-alpha.38232858664.1.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/README.md CHANGED
@@ -223,10 +223,10 @@ What `ChatAgent` adds over `Agent`:
223
223
  - **Compaction** — past 100,000 tokens, older messages are summarised (with `getModel()`) while recent ones are kept verbatim. Storage keeps the full history.
224
224
  - **Message feedback** — thumbs up/down with an optional comment, in the chat's SQLite (`assistant_messages_feedback`, created automatically):
225
225
 
226
- | Method | Description |
227
- | ---------------------------------------------------- | ------------------------------------------------------------ |
228
- | `submitMessageFeedback(messageId, rating, comment?)` | `rating` is `1` (up) or `-1` (down). Upserts on the message. |
229
- | `getMessageFeedback()` | All feedback for the chat, keyed by message id. |
226
+ | Method | Description |
227
+ | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
228
+ | `submitMessageFeedback(messageId, rating, comment?)` | `rating` is `1` (up), `-1` (down) or `0` (clear). Upserts on the message. Only for the chat's assistant messages; comments up to 2,000 characters. |
229
+ | `getMessageFeedback()` | All feedback for the chat, keyed by message id. |
230
230
 
231
231
  Surfaced by the client as `chat.submitMessageFeedback` / `chat.getMessageFeedback`.
232
232
 
@@ -270,14 +270,16 @@ The `Assistant` keeps the chat list in its own SQLite (a `chats` table, created
270
270
 
271
271
  ## Bindings
272
272
 
273
- Checked in `Agent.onStart` when a connection opens:
273
+ `AGENTS_DB` is checked in `Agent.onStart` when a connection opens; the rest are needed only for what
274
+ they're noted for:
274
275
 
275
- | Binding | Type | Required | Notes |
276
- | ------------------- | ---------------- | -------- | ------------------------------------------------------------------------- |
277
- | `AGENTS_AUDIT_LOGS` | R2 | Yes | Connection rejected if missing. One [audit log](#observability) per turn. |
278
- | `AGENTS_ANALYTICS` | Analytics Engine | No | Per-turn/per-tool [analytics](#observability). Warns if missing. |
279
- | `SKILLS_BUCKET` | R2 | No | Source for [remote skills](#remote-skills). |
280
- | `LOADER` | Worker Loader | No | Enables [native skill scripts](#skill-scripts) when bound. |
276
+ | Binding | Type | Required | Notes |
277
+ | ------------------- | ---------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
278
+ | `AGENTS_AUDIT_LOGS` | R2 | With `AuditLogHandler` | Where the [audit log](#observability) handler writes one log per turn. Any name; you pass it in. |
279
+ | `AGENTS_ANALYTICS` | Analytics Engine | With `AnalyticsHandler` | Where the [analytics](#observability) handler writes per-turn and per-tool events. Any name; you pass it in. |
280
+ | `SKILLS_BUCKET` | R2 | No | Source for [remote skills](#remote-skills). |
281
+ | `LOADER` | Worker Loader | No | Enables [native skill scripts](#skill-scripts) when bound. |
282
+ | `AGENTS_USERS` | Durable Object (`UserStore`) | With `toolCallLimits` or `memory` | Keeps each user's state across their chats: their tool call counts and their memory. Export `UserStore` from your worker and bind it as a SQLite Durable Object. |
281
283
 
282
284
  ## Tools
283
285
 
@@ -314,27 +316,24 @@ const export_report = tool({
314
316
 
315
317
  ### Tool context
316
318
 
317
- The tool context is the second argument to `execute` — `experimental_context`:
319
+ The tool context is `context` in the second argument to `execute`, typed by the tool's first type parameter:
318
320
 
319
321
  ```typescript
320
322
  import { tool, type ToolContext } from "@economic/agents";
321
323
  import { z } from "zod";
322
324
 
323
- interface RequestBody {
324
- tokens: Record<string, string>;
325
- }
326
-
327
- const call_api = tool({
325
+ const call_api = tool<ToolContext>({
328
326
  description: "Call the API for the user",
329
327
  inputSchema: z.object({ path: z.string() }),
330
- execute: async ({ path }, { experimental_context }) => {
331
- const ctx = experimental_context as ToolContext<RequestBody>;
332
- return fetchWithAuth(ctx.tokens, path);
328
+ execute: async ({ path }, { context }) => {
329
+ const token = await context._auth?.credential("api");
330
+
331
+ return fetchWithToken(token, path);
333
332
  },
334
333
  });
335
334
  ```
336
335
 
337
- `ToolContext<RequestContext, UserContext>` is `RequestContext & { _userContext?: UserContext }`. Request context comes from the client (`toolContext` in the hooks); `_userContext` comes from [`getUserContext`](#user-context) when using JWT Authentication (via `getJwtAuthConfig`).
336
+ `ToolContext<RequestContext, UserContext>` is `RequestContext & { _userContext?: UserContext; _auth?: Auth }`. Request context comes from the client (`toolContext` in the hooks). `_userContext` and `_auth` come from the agent's [authenticator](#authentication): `_auth.credential(name)` gives a token for another service, such as an exchanged one.
338
337
 
339
338
  ## Actions
340
339
 
@@ -352,7 +351,7 @@ interface RequestContext extends Record<string, unknown> {
352
351
 
353
352
  const deleteInput = z.object({ invoiceId: z.string() });
354
353
 
355
- const deleteInvoice = action<ToolContext<RequestContext>, typeof deleteInput>({
354
+ const deleteInvoice = action<ToolContext<RequestContext>, z.infer<typeof deleteInput>>({
356
355
  description: "Delete a draft invoice",
357
356
  inputSchema: deleteInput,
358
357
 
@@ -367,7 +366,7 @@ const deleteInvoice = action<ToolContext<RequestContext>, typeof deleteInput>({
367
366
  const response = await fetch(`/api/invoices/${input.invoiceId}`, {
368
367
  method: "DELETE",
369
368
  headers: {
370
- Authorization: ctx.experimental_context.tokens.Authorization ?? "",
369
+ Authorization: ctx.context.tokens.Authorization ?? "",
371
370
  // Think uses this key for its ledger. Forward it when the downstream
372
371
  // API also supports idempotency.
373
372
  "Idempotency-Key": ctx.idempotencyKey,
@@ -391,7 +390,7 @@ export const invoiceSkill = skill({
391
390
 
392
391
  An action receives the same tool execution data plus Cloudflare's native action fields:
393
392
 
394
- - `experimental_context` — the typed `ToolContext` supplied to ordinary tools.
393
+ - `context` — the typed `ToolContext` supplied to ordinary tools.
395
394
  - `toolCallId`, `messages`, and `abortSignal` — the ordinary tool execution fields.
396
395
  - `idempotencyKey` — the resolved domain key, or the tool-call fallback when none was declared.
397
396
  - `requestId`, `signal`, `agent`, `env`, and `attachReply` — native Think action fields.
@@ -451,9 +450,15 @@ Set `command` to make a code-defined skill directly invocable from chat. For exa
451
450
  first model step; the model still interprets `in Copenhagen` and chooses the appropriate
452
451
  tool. Commands use lower-case letters, numbers, and hyphens, without the leading `/`.
453
452
 
454
- `ChatAgent` also exposes an authenticated `GET getInvocableSkills` action for command
455
- pickers. It returns the commands available to the current user as
456
- `{ command, name, description }[]`. The existing skill `description` is used for both the
453
+ `activation` sets when a skill is active. Every skill is still returned from `getSkills()`.
454
+
455
+ - `"auto"` (the default): once the model starts it, when it's relevant, or the user runs its `command`.
456
+ - `"always"`: on every turn, without being started. Its instructions are a block of the system prompt, named after the skill, after the agent's own prompt, and its tools are always available.
457
+ - `"manual"`: only once the user runs its `command`. It's hidden from the model's catalog, and the model can't start it itself. It needs a `command`, which the type requires.
458
+
459
+ `ChatAgent` also has a `getInvocableSkills()` callable for command pickers. It returns the
460
+ commands available to the caller as `{ command, name, description }[]`; `useChat` from
461
+ `@economic/agents-react` exposes it as `getInvocableSkills()`. The existing skill `description` is used for both the
457
462
  model catalog and the human-facing picker.
458
463
 
459
464
  ### Native Agent Skills
@@ -572,118 +577,115 @@ Source order matters: if two sources define the same skill name, the first sourc
572
577
 
573
578
  ## Authentication
574
579
 
575
- ### JWT
576
-
577
- Override the static `getJwtAuthConfig(env)` to verify a JWT on connect (static so an `Assistant` can check before routing). Return `undefined` to skip.
580
+ Define `static getAuthenticator(env)` on the agent class to check callers on every WebSocket connect and HTTP request. An `Assistant` checks callers for its chats with its chat agent's authenticator, so it's defined once. Without one, the agent is open.
578
581
 
579
582
  ```typescript
580
- export class SupportAgent extends Agent {
581
- static getJwtAuthConfig(env: Cloudflare.Env) {
582
- return {
583
- allowedIssuers: [env.IDENTITY_ENDPOINT], // strings or RegExp
584
- audience: "my-api",
585
- requiredScopes: ["support.read"],
586
- getClaims: (payload) => ({
587
- userGuid: payload.user_guid as string,
588
- agreementNumber: payload.agreement_number as number,
589
- }),
590
- getActorId: (claims) => `${claims.agreementNumber}_${claims.userGuid}`,
591
- };
592
- }
583
+ import { Agent, jwtAuthenticator, TokenExchange } from "@economic/agents";
584
+
585
+ const tokenExchange = new TokenExchange({
586
+ tokenUrl: `${env.IDENTITY_ENDPOINT}/connect/token`,
587
+ clientId: env.OAUTH_CLIENT_ID,
588
+ clientSecret: env.OAUTH_CLIENT_SECRET, // a string, or a Secrets Store binding
589
+ scopes: ["api.read", "api.write"],
590
+ actor: { scopes: ["agent.read"] }, // optional: the agent's own token as the actor
591
+ });
593
592
 
594
- // ...
593
+ const authenticator = jwtAuthenticator<UserContext>({
594
+ issuers: [env.IDENTITY_ENDPOINT], // strings or RegExp
595
+ audience: "my-api",
596
+ scopes: ["support.read"],
597
+ actorId: (claims) => `${claims.agreement_number}_${claims.user_guid}`,
598
+ credentials: {
599
+ api: ({ token }) => tokenExchange.exchange(token),
600
+ },
601
+ userContext: async (auth) => fetchProfile(await auth.credential("api")),
602
+ });
603
+
604
+ export class SupportAgent extends Agent<RequestContext, UserContext> {
605
+ static getAuthenticator() {
606
+ return authenticator;
607
+ }
595
608
  }
596
609
  ```
597
610
 
598
- `getActorId` derives the authenticated actor from verified claims. It must exactly match the
599
- actor that owns the requested Durable Object: the Assistant name, the root parent of a chat
600
- facet, or the prefix in a standalone `${actorId}:${chatId}` name. A valid token belonging to
601
- another actor is rejected with `4003` before the connection is established.
602
-
603
- 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.
604
-
605
- ### User context
611
+ - **The token** is read from `Authorization: Bearer …`, then from the `Sec-WebSocket-Protocol: bearer, <token>` header. Its signature, issuer, audience, expiry and scopes are checked, with the issuer's keys found through its OpenID configuration. `allow(claims)` can refuse a verified token, such as for its tenant.
612
+ - **The owner:** `actorId` turns the claims into the actor the token is for. It must match the actor that owns the requested Durable Object: the Assistant's name, the root parent of a chat facet, or the prefix in a standalone `${actorId}:${chatId}` name. Another actor's token is refused with `4003`.
613
+ - **Credentials** are fetched by name the first time a tool or the user context asks for one, with `auth.credential(name)`, and stored until they expire.
614
+ - **The user context** is loaded once per token, when the caller is authenticated, and stored. Tools see it as `_userContext`.
615
+ - **Refusals:** the socket closes with `4001` (no token, or an expired or refused one) or `4003` (forbidden), with status `"unauthorized"`. Throw an `AuthenticationError` from a credential provider or `userContext` to refuse a caller the same way. Any other error closes it with `1011`.
616
+ - **Expiry:** a turn still runs after the caller's token has expired, such as a scheduled one. Tools then get no `_auth` or `_userContext`.
617
+ - **What's kept:** a WebSocket handshake keeps the caller's identity, user context and credentials in the agent until the token expires, so turns after hibernation or eviction, and scheduled turns, fetch nothing again. Other requests are only checked.
618
+ - **Requests without the caller's token,** such as an OAuth callback or a signed webhook, are refused unless `acceptsWithoutToken(request)` returns `true` for them. Check them another way.
619
+ - **Any authenticator works:** `getAuthenticator(env)` can return your own `Authenticator`, such as one for a different identity provider.
606
620
 
607
- Implement `getUserContext(jwtToken)` to load per-user data after auth. It's exposed as `ctx._userContext` in `getModel`, `getSystemPrompt`, tools, and skill `authorize`. Runs only when JWT auth is configured.
621
+ ## Memory
608
622
 
609
- ```typescript
610
- protected async getUserContext(jwtToken: string) {
611
- const profile = await fetchProfile(jwtToken);
612
- return { role: profile.role, tokens: profile.tokens };
613
- }
614
- ```
623
+ Return `memory: true` from `getAgentConfig()` to give each user a private memory with the agent, kept across all their chats in their `UserStore` (`AGENTS_USERS`). It's a writable block of the system prompt, which the model updates itself with `set_context`.
615
624
 
616
- If `getUserContext` throws, the socket closes with `1011` — or, when the thrown `Error` carries a numeric `closeCode` in the `4000`–`4999` range, with that code and an `"unauthorized"` status frame:
625
+ It only works once the user opts in. Until then there's no memory block, and the model can't write to it. `ChatAgent` has callables for the client: `getMemory()` returns `{ optedIn, content }`, `setMemory(content)` replaces it, `deleteMemory()` empties it, and `setMemoryOptIn(optedIn)` turns it on or off. Turning it off keeps what's saved.
617
626
 
618
- ```typescript
619
- const error = new Error("No Eva access");
620
- (error as Error & { closeCode: number }).closeCode = 4003;
621
- throw error;
622
- ```
627
+ ## Observability
623
628
 
624
- ### Token exchange
629
+ An agent's tracing events go to the handlers it returns from `getTracingHandlers()`. It has none
630
+ by default. The SDK provides two, each taking the binding it writes to:
625
631
 
626
- For agents whose clients connect with a _down-scoped_ token — one only valid for the client ↔ agent hop — override the static `getTokenExchangeConfig(env)` to exchange it (RFC 8693) at the identity server for a token issued to the agent's own OAuth client. Requires `getJwtAuthConfig`.
632
+ - **`AuditLogHandler`** → one JSON object per turn in an R2 bucket (model, actor, IP, prompt,
633
+ response, tool calls). Each is kept in the agent's storage until the bucket has it, and one that
634
+ couldn't be written is sent again with the next turn's.
635
+ - **`AnalyticsHandler`** → typed per-turn and per-tool events in an Analytics Engine dataset, using
636
+ the existing schema.
627
637
 
628
638
  ```typescript
629
- export class SupportAgent extends Agent {
630
- static getTokenExchangeConfig(env: Cloudflare.Env) {
631
- return {
632
- tokenUrl: `${env.IDENTITY_ENDPOINT}/connect/token`,
633
- clientId: env.OAUTH_CLIENT_ID,
634
- clientSecret: env.OAUTH_CLIENT_SECRET, // string or Secrets Store binding
635
- scopes: "api.read api.write", // always sent explicitly
636
- tokenHeader: "X-EconomicToken", // default
637
- };
638
- }
639
+ import { AnalyticsHandler, AuditLogHandler } from "@economic/agents";
640
+
641
+ override getTracingHandlers() {
642
+ return [
643
+ new AuditLogHandler(this.env.AGENTS_AUDIT_LOGS, this.ctx.storage),
644
+ new AnalyticsHandler(this.env.AGENTS_ANALYTICS),
645
+ ];
639
646
  }
640
647
  ```
641
648
 
642
- When configured:
649
+ `getTracingHandlers()` is called once, when the agent starts, so a handler can keep state across a
650
+ turn's events, as both of these do. A handler that throws, as these do without their binding, stops
651
+ the agent starting. Leave one out where it doesn't apply, such as analytics for
652
+ test traffic.
643
653
 
644
- - The verified connect token is exchanged on every connect; the exchanged token is what `getUserContext` receives and what tools see as `ctx.tokens[tokenHeader]` each turn. **Client-sent request-body tokens for that key are ignored** — tool credentials always derive from the verified connection identity, so clients should stop sending them.
645
- - Renewal is connection-based. The client token rides in the WebSocket protocols, so presenting a renewed token always means a reconnect, which re-verifies and re-exchanges. Keep the `authToken` passed to the hooks current (refresh it before expiry) and the socket rotates silently — the `unauthorized` close (`4001`) then only happens when the session is genuinely dead (logout, revocation).
646
- - The exchanged token expires on the identity server's clock (with a 30s margin). A turn arriving after expiry closes the connection `4001` to force a reconnect; a client that refreshes its token on time never hits this.
647
- - Exchange failures at connect: `invalid_grant` (dead session token) → status `"unauthorized"` + close `4001`; anything else (identity server down, misconfigured client) → status `"error"` + close `1011`. Partial configuration is the app's concern — throw from `getTokenExchangeConfig` if you consider it a deployment fault, or return `undefined` to disable the exchange.
648
-
649
- ## Observability
654
+ Each handler's `handle` receives every `TracingEvent`, told apart by `type`:
650
655
 
651
- AI telemetry is emitted per turn from the `ai` SDK's OpenTelemetry spans:
656
+ - `span`: each OpenTelemetry span as soon as it ends, a model call, a tool call or the turn.
657
+ - `turn.started`: the model messages the turn starts with.
658
+ - `turn.ended`: its status and the assistant message.
659
+ - `skill.invoked`: a skill run by its command.
660
+ - `turn`: the completed turn. Who it was for, the request's origin (`clientIp`, `forwardedFor`), its
661
+ request `context` (as tools see it) and `spans`, the `ai` SDK's spans under the turn's
662
+ `ai.streamText` span. A turn continuing an earlier one, e.g. after a tool approval, sits under
663
+ that turn in the same trace.
664
+ - `feedback`: a user's rating of an assistant message. `messageId`, `rating`, `comment` and
665
+ `spans`, the rating as a `gen_ai.evaluation.*` span under the turn that first answered the
666
+ message (none when the rating was removed).
652
667
 
653
- - **Audit logs** → one JSON object per turn in `AGENTS_AUDIT_LOGS` (model, actor, IP, prompt, response, tool calls).
654
- - **Analytics** → typed per-turn and per-tool events, written to `AGENTS_ANALYTICS` using the
655
- existing Analytics Engine schema.
656
- - **Tracing** → completed turns and message feedback, handed to the handlers you return from
657
- `getTracingHandlers()`.
658
-
659
- Override `getTracingHandlers()` to send turns and message feedback to a tracing or analytics
660
- service. Each handler's `handle` receives a `TracingEvent`, a `turn` or a `feedback` event told
661
- apart by `type`. Both carry their OpenTelemetry `spans`, so a handler that only forwards spans
662
- doesn't need to check:
668
+ A handler ignores the types it doesn't need. One that forwards whole traces to a tracing service
669
+ only needs `turn` and `feedback`:
663
670
 
664
671
  ```typescript
665
672
  import type { TracingHandler } from "@economic/agents";
666
673
 
667
674
  const tracing: TracingHandler = {
668
675
  name: "tracing-service",
669
- handle: (event) => sendToTracingService(event.spans),
676
+ handle: (event) => {
677
+ if (event.type === "turn" || event.type === "feedback") {
678
+ return sendToTracingService(event.spans);
679
+ }
680
+ },
670
681
  };
671
-
672
- getTracingHandlers() {
673
- return [tracing];
674
- }
675
682
  ```
676
683
 
677
- A `turn` event comes when a turn ends: who it was for, its request `context` (as tools see it) and
678
- `spans`, the `ai` SDK's OpenTelemetry spans under the turn's `ai.streamText` span. A turn
679
- continuing an earlier one, e.g. after a tool approval, sits under that turn in the same trace. A
680
- `feedback` event comes when a user rates an assistant message: `messageId`, `rating`, `comment` and
681
- `spans`, the rating as a `gen_ai.evaluation.*` span under the turn that first answered the message
682
- (none when the rating was removed).
683
684
  Hand the spans to any OpenTelemetry exporter or span processor as they are; `examples/assistant`
684
685
  has a complete handler.
685
686
 
686
- Spans and `context` can hold prompts, tool results and credentials, so pick what you send. Handlers
687
+ Spans, `context`, the turn's messages and the request's origin can hold prompts, tool results,
688
+ credentials and IP addresses, so pick what you send. Handlers
687
689
  run in the background; a failure is logged as `agent.tracing.handler.failed` and never affects the
688
690
  chat.
689
691
 
@@ -697,11 +699,13 @@ Imported from `@economic/agents` unless noted.
697
699
 
698
700
  ### Classes
699
701
 
700
- | Export | Description |
701
- | ----------- | -------------------------------------------------------- |
702
- | `Agent` | Core agent base. Implement `getModel`/`getSystemPrompt`. |
703
- | `ChatAgent` | `Agent` + model-only compaction and message feedback. |
704
- | `Assistant` | Per-user manager of `ChatAgent` chats. |
702
+ | Export | Description |
703
+ | ------------------ | ------------------------------------------------------------- |
704
+ | `Agent` | Core agent base. Implement `getModel`/`getSystemPrompt`. |
705
+ | `ChatAgent` | `Agent` + model-only compaction and message feedback. |
706
+ | `Assistant` | Per-user manager of `ChatAgent` chats. |
707
+ | `AuditLogHandler` | Tracing handler writing one audit log per turn to R2. |
708
+ | `AnalyticsHandler` | Tracing handler writing analytics events to Analytics Engine. |
705
709
 
706
710
  ### Helpers and types
707
711
 
@@ -718,20 +722,20 @@ Imported from `@economic/agents` unless noted.
718
722
 
719
723
  ### Members
720
724
 
721
- | Member | On | Description |
722
- | ---------------------------------------------- | ----------- | ---------------------------------------------------------------------- |
723
- | `getModel(ctx?)` | `Agent` | Required. Model for inference. |
724
- | `getSystemPrompt(ctx?)` | `Agent` | Required. System prompt. |
725
- | `getTools()` | `Agent` | Always-on tools (default `{}`). |
726
- | `getSkills()` | `Agent` | Local skills (default `[]`). |
727
- | `getTracingHandlers()` | `Agent` | Destinations for completed turns and message feedback (default: `[]`). |
728
- | `getAnalyticsAttributes()` | `Agent` | Additional dimensions included on every analytics event. |
729
- | `static getJwtAuthConfig(env)` | `Agent` | Optional JWT verification and actor binding. |
730
- | `getUserContext(jwtToken)` | `Agent` | Optional per-user context after auth. |
731
- | `submitMessageFeedback` / `getMessageFeedback` | `ChatAgent` | Callable message feedback. |
732
- | `agent` | `Assistant` | Required. Your `ChatAgent` subclass. |
733
- | `fastModel` | `Assistant` | Required. Cheap model for titles/summaries. |
734
- | `createChat` / `getChats` / `deleteChat` | `Assistant` | Callable chat management. |
725
+ | Member | On | Description |
726
+ | ---------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
727
+ | `getModel(ctx?)` | `Agent` | Required. Model for inference. |
728
+ | `getSystemPrompt(ctx?)` | `Agent` | Required. System prompt. |
729
+ | `getTools()` | `Agent` | Always-on tools (default `{}`). |
730
+ | `getSkills()` | `Agent` | Local skills (default `[]`). |
731
+ | `configureContext()` | `Agent` | Think's system prompt blocks. An override must include `...(await super.configureContext())`, or the agent's own prompt and rules are lost. |
732
+ | `getTracingHandlers()` | `Agent` | Destinations for completed turns and message feedback (default: `[]`). |
733
+ | `static getAuthenticator(env)` | `Agent` | Optional caller authentication, credentials and user context. |
734
+ | `submitMessageFeedback` / `getMessageFeedback` | `ChatAgent` | Callable message feedback. |
735
+ | `getAgentConfig(ctx?)` | `Agent` | Settings for a turn (default `{}`): `maxSteps`, `temperature`, `maxOutputTokens`, `topP`, `toolChoice`, `maxRetries`, `timeout`, `sendReasoning`, `providerOptions`; `toolCallLimits`, how often each user may call each tool by name, per day (`dailyCalls`) and per minute (`burst`), which needs `AGENTS_USERS`; for a chat, `memory`, a private memory per user, and `deleteMessagesAfterMs`, after which its messages are deleted. |
736
+ | `agent` | `Assistant` | Required. Your `ChatAgent` subclass. |
737
+ | `fastModel` | `Assistant` | Required. Cheap model for titles/summaries. |
738
+ | `createChat` / `getChats` / `deleteChat` | `Assistant` | Callable chat management. |
735
739
 
736
740
  ### Package root (`@economic/agents`)
737
741