@economic/agents 2.14.0 → 2.14.2

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
@@ -226,6 +226,16 @@ interface TurnSnapshot {
226
226
  frameworkToolNames: string[];
227
227
  messages: ModelMessage[];
228
228
  }
229
+ type CurrentTurnState<RequestContext extends Record<string, unknown>, UserContext extends Record<string, unknown>> = {
230
+ context?: ToolContext<RequestContext, UserContext>;
231
+ inputTools: ToolSet;
232
+ preparedTools: ToolSet;
233
+ unavailableSkillIds: Set<string>;
234
+ skillsSystemBlock: string;
235
+ visibleSkillNames: string[];
236
+ authorizedSkillNames: Set<string>;
237
+ baseSystem: string;
238
+ };
229
239
  type Authentication<UserContext> = {
230
240
  success: true;
231
241
  userContext?: UserContext;
@@ -249,8 +259,9 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
249
259
  onStart(): Promise<void>;
250
260
  configureSession(session: Session): Session;
251
261
  onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
252
- private _turn;
253
- private getToolContextForCurrentTurn;
262
+ protected _turn: CurrentTurnState<RequestContext, UserContext>;
263
+ protected createToolContextForCurrentTurn(requestContext?: RequestContext): ToolContext<RequestContext, UserContext>;
264
+ protected getToolContextForCurrentTurn(): ToolContext<RequestContext, UserContext> | undefined;
254
265
  /**
255
266
  * Merges the client request `body` into `experimental_context` for tools
256
267
  * returned from {@link getTools} only (Think-internal tools are unchanged).
@@ -508,6 +519,8 @@ declare abstract class ChatAgent<RequestContext extends Record<string, unknown>
508
519
  * returned immediately and each event reaches the client as it is produced.
509
520
  */
510
521
  private streamMessage;
522
+ private streamToolApproval;
523
+ private streamTurn;
511
524
  /**
512
525
  * Submit feedback for a message by its id.
513
526
  * @param messageId - The id of the message to give feedback on.
package/dist/index.mjs CHANGED
@@ -4,6 +4,7 @@ import { Output, convertToModelMessages, generateText, jsonSchema, pruneMessages
4
4
  import { createRemoteJWKSet, decodeJwt, errors, jwtVerify } from "jose";
5
5
  import { BasicTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
6
6
  import { genericObservability } from "agents/observability";
7
+ import { applyToolUpdate, toolApprovalUpdate } from "agents/chat";
7
8
  import { createCompactFunction, estimateMessageTokens, estimateStringTokens, sanitizeToolPairs, truncateOlderMessages } from "agents/experimental/memory/utils";
8
9
  import { nanoid } from "nanoid";
9
10
  //#region src/server/features/auth/index.ts
@@ -1158,13 +1159,17 @@ var Agent = class extends Think {
1158
1159
  authorizedSkillNames: /* @__PURE__ */ new Set(),
1159
1160
  baseSystem: ""
1160
1161
  };
1161
- getToolContextForCurrentTurn(requestContext = {}) {
1162
+ createToolContextForCurrentTurn(requestContext = {}) {
1162
1163
  const connectionState = getCurrentAgent().connection?.state;
1163
1164
  return {
1165
+ ...this._turn.context,
1164
1166
  ...requestContext,
1165
1167
  _userContext: connectionState?.userContext ?? this.userContext
1166
1168
  };
1167
1169
  }
1170
+ getToolContextForCurrentTurn() {
1171
+ return this._turn.context;
1172
+ }
1168
1173
  /**
1169
1174
  * Merges the client request `body` into `experimental_context` for tools
1170
1175
  * returned from {@link getTools} only (Think-internal tools are unchanged).
@@ -1175,8 +1180,11 @@ var Agent = class extends Think {
1175
1180
  async beforeTurn(ctx) {
1176
1181
  this.analytics.promptReceived(ctx.messages, ctx.continuation);
1177
1182
  this.registerInstanceActivity();
1178
- const body = await this._applyTokenExchange(ctx.body);
1179
- this._turn.context = this.getToolContextForCurrentTurn(body);
1183
+ const body = await this._applyTokenExchange({
1184
+ ...this._turn.context,
1185
+ ...ctx.body
1186
+ });
1187
+ this._turn.context = this.createToolContextForCurrentTurn(body);
1180
1188
  this._turn.inputTools = ctx.tools;
1181
1189
  const activeSkillName = this._getLastActivatedSkillName(ctx.messages);
1182
1190
  const { tools, activeTools } = await this._prepareTools(ctx.tools, activeSkillName, true);
@@ -2839,6 +2847,22 @@ var ChatAgent = class extends Agent {
2839
2847
  if (action === "getMessages" && request.method === "GET") return Response.json(await this.getMessages());
2840
2848
  if (action === "getInvocableSkills" && request.method === "GET") return Response.json(await this.getInvocableSkills(auth.userContext));
2841
2849
  if (action === "sendMessage" && request.method === "POST") return this.streamMessage(request);
2850
+ if (action === "submitToolApproval" && request.method === "POST") return this.streamToolApproval(request);
2851
+ if (action === "getMessageFeedback" && request.method === "GET") return Response.json(await this.getMessageFeedback());
2852
+ if (action === "submitMessageFeedback" && request.method === "POST") {
2853
+ let payload;
2854
+ try {
2855
+ payload = await request.json();
2856
+ } catch {
2857
+ return Response.json({ error: "Request body must be valid JSON" }, { status: 400 });
2858
+ }
2859
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return Response.json({ error: "Request body must be an object" }, { status: 400 });
2860
+ const { messageId, rating, comment } = payload;
2861
+ if (!messageId) return Response.json({ error: "Message ID is required" }, { status: 400 });
2862
+ if (rating !== -1 && rating !== 0 && rating !== 1) return Response.json({ error: "Rating must be -1, 0, or 1" }, { status: 400 });
2863
+ if (comment !== void 0 && typeof comment !== "string") return Response.json({ error: "Comment must be a string" }, { status: 400 });
2864
+ return Response.json(await this.submitMessageFeedback(messageId, rating, comment));
2865
+ }
2842
2866
  if (action === "cancelChat" && request.method === "POST") {
2843
2867
  const { requestId } = await request.json();
2844
2868
  this.cancelChat(requestId);
@@ -2851,38 +2875,81 @@ var ChatAgent = class extends Agent {
2851
2875
  * returned immediately and each event reaches the client as it is produced.
2852
2876
  */
2853
2877
  async streamMessage(request) {
2854
- let message;
2878
+ let payload;
2855
2879
  try {
2856
- ({message} = await request.json());
2880
+ payload = await request.json();
2857
2881
  } catch {
2858
- return Response.json({ error: "Invalid request body" }, { status: 400 });
2882
+ return Response.json({ error: "Request body must be valid JSON" }, { status: 400 });
2859
2883
  }
2860
- if (typeof message === "string") message = {
2861
- id: crypto.randomUUID(),
2862
- role: "user",
2863
- parts: [{
2864
- type: "text",
2865
- text: message
2866
- }]
2867
- };
2868
- else if (!message || message.role !== "user" || !Array.isArray(message.parts)) return Response.json({ error: "message must be a user message" }, { status: 400 });
2884
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return Response.json({ error: "Request body must be an object" }, { status: 400 });
2885
+ let { message, context } = payload;
2886
+ if (context !== void 0 && (context === null || typeof context !== "object" || Array.isArray(context))) return Response.json({ error: "Context must be an object" }, { status: 400 });
2887
+ if (typeof message === "string") {
2888
+ if (!message.trim()) return Response.json({ error: "Message must not be empty" }, { status: 400 });
2889
+ message = {
2890
+ id: crypto.randomUUID(),
2891
+ role: "user",
2892
+ parts: [{
2893
+ type: "text",
2894
+ text: message
2895
+ }]
2896
+ };
2897
+ } else if (!message || typeof message !== "object" || Array.isArray(message) || typeof message.id !== "string" || !message.id || message.role !== "user" || !Array.isArray(message.parts)) return Response.json({ error: "Message must be a non-empty string or a user UIMessage" }, { status: 400 });
2898
+ const input = context ? () => {
2899
+ this._turn.context = this.createToolContextForCurrentTurn(context);
2900
+ return [message];
2901
+ } : message;
2902
+ return this.streamTurn(input);
2903
+ }
2904
+ async streamToolApproval(request) {
2905
+ let payload;
2906
+ try {
2907
+ payload = await request.json();
2908
+ } catch {
2909
+ return Response.json({ error: "Request body must be valid JSON" }, { status: 400 });
2910
+ }
2911
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return Response.json({ error: "Request body must be an object" }, { status: 400 });
2912
+ const { toolCallId, approved, context } = payload;
2913
+ if (!toolCallId) return Response.json({ error: "Tool call ID is required" }, { status: 400 });
2914
+ if (typeof approved !== "boolean") return Response.json({ error: "Approved must be a boolean" }, { status: 400 });
2915
+ if (context !== void 0 && (context === null || typeof context !== "object" || Array.isArray(context))) return Response.json({ error: "Context must be an object" }, { status: 400 });
2916
+ return this.streamTurn((messages) => {
2917
+ if (context) this._turn.context = this.createToolContextForCurrentTurn(context);
2918
+ const update = toolApprovalUpdate(toolCallId, approved);
2919
+ let found = false;
2920
+ const updatedMessages = messages.map((message) => {
2921
+ if (found || message.role !== "assistant") return message;
2922
+ const result = applyToolUpdate(message.parts, update);
2923
+ if (!result) return message;
2924
+ found = true;
2925
+ return {
2926
+ ...message,
2927
+ parts: result.parts
2928
+ };
2929
+ });
2930
+ if (!found) throw new Error(`No pending tool approval found for ${toolCallId}`);
2931
+ return updatedMessages;
2932
+ });
2933
+ }
2934
+ streamTurn(input) {
2869
2935
  const encoder = new TextEncoder();
2870
2936
  const readable = new ReadableStream({ start: async (controller) => {
2871
2937
  const send = (event, data) => controller.enqueue(encoder.encode(`event: ${event}\ndata: ${data}\n\n`));
2938
+ const sendError = (error) => send("error", JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
2872
2939
  try {
2873
2940
  await this.runTurn({
2874
2941
  mode: "stream",
2875
- input: message,
2942
+ input,
2876
2943
  callback: {
2877
2944
  onStart: (event) => send("start", JSON.stringify(event)),
2878
2945
  onEvent: (json) => send("chunk", json),
2879
2946
  onDone: () => send("done", "{}"),
2880
- onError: (error) => send("error", JSON.stringify({ error })),
2947
+ onError: sendError,
2881
2948
  onInterrupted: () => send("interrupted", "{}")
2882
2949
  }
2883
2950
  });
2884
2951
  } catch (error) {
2885
- send("error", JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
2952
+ sendError(error);
2886
2953
  } finally {
2887
2954
  controller.close();
2888
2955
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@economic/agents",
3
- "version": "2.14.0",
3
+ "version": "2.14.2",
4
4
  "description": "A starter for creating a TypeScript package.",
5
5
  "license": "MIT",
6
6
  "files": [