@economic/agents 1.6.2 → 1.6.3
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 +15 -5
- package/dist/index.mjs +109 -94
- package/package.json +1 -1
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
|
/**
|
|
@@ -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
|
/**
|
|
@@ -153,7 +152,7 @@ interface MessageRating {
|
|
|
153
152
|
* Skill loading, compaction, and LLM calls use `buildLLMParams` from
|
|
154
153
|
* `@economic/agents` inside `onChatMessage`.
|
|
155
154
|
*/
|
|
156
|
-
declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env> extends AIChatAgent<Env & ChatAgentEnv> {
|
|
155
|
+
declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env, TSessionIdentity extends Record<string, unknown> = Record<string, unknown>> extends AIChatAgent<Env & ChatAgentEnv> {
|
|
157
156
|
/**
|
|
158
157
|
* The binding of the Durable Object instance for this agent.
|
|
159
158
|
*/
|
|
@@ -202,6 +201,17 @@ declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env> ex
|
|
|
202
201
|
* @returns JWT auth config or undefined to skip authentication
|
|
203
202
|
*/
|
|
204
203
|
protected getJwtAuthConfig?(request: Request): JwtAuthConfig<Record<string, unknown>> | undefined;
|
|
204
|
+
/**
|
|
205
|
+
* The identity associated with the session.
|
|
206
|
+
* Define getIdentity to return the identity from the request.
|
|
207
|
+
*/
|
|
208
|
+
protected identity: TSessionIdentity;
|
|
209
|
+
/**
|
|
210
|
+
* Returns the identity from the request.
|
|
211
|
+
* @returns The identity from the request.
|
|
212
|
+
* @param token - The token from the request.
|
|
213
|
+
*/
|
|
214
|
+
protected getIdentity(_token: string): Promise<TSessionIdentity>;
|
|
205
215
|
/**
|
|
206
216
|
* Returns the user ID from the durable object name.
|
|
207
217
|
*/
|
|
@@ -244,7 +254,7 @@ declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env> ex
|
|
|
244
254
|
}
|
|
245
255
|
//#endregion
|
|
246
256
|
//#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> {
|
|
257
|
+
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
258
|
get binding(): {
|
|
249
259
|
getByName(name: string): {
|
|
250
260
|
destroy(): Promise<void>;
|
|
@@ -278,4 +288,4 @@ declare abstract class ChatAgentHarness<Env extends Cloudflare.Env, RequestBody
|
|
|
278
288
|
onChatMessage(onFinish: StreamTextOnFinishCallback<ToolSet>, options?: OnChatMessageOptions): Promise<Response>;
|
|
279
289
|
}
|
|
280
290
|
//#endregion
|
|
281
|
-
export { Agent, type AgentToolContext, type BuildLLMParamsConfig, ChatAgent, ChatAgentHarness, type Skill, buildLLMParams,
|
|
291
|
+
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,
|
|
@@ -849,6 +849,19 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
849
849
|
*/
|
|
850
850
|
session;
|
|
851
851
|
/**
|
|
852
|
+
* The identity associated with the session.
|
|
853
|
+
* Define getIdentity to return the identity from the request.
|
|
854
|
+
*/
|
|
855
|
+
identity = {};
|
|
856
|
+
/**
|
|
857
|
+
* Returns the identity from the request.
|
|
858
|
+
* @returns The identity from the request.
|
|
859
|
+
* @param token - The token from the request.
|
|
860
|
+
*/
|
|
861
|
+
async getIdentity(_token) {
|
|
862
|
+
return Promise.resolve({});
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
852
865
|
* Returns the user ID from the durable object name.
|
|
853
866
|
*/
|
|
854
867
|
getUserId() {
|
|
@@ -883,6 +896,8 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
883
896
|
this.session = result.claims;
|
|
884
897
|
}
|
|
885
898
|
}
|
|
899
|
+
const token = extractTokenFromConnectRequest(ctx.request);
|
|
900
|
+
if (token) this.identity = await this.getIdentity(token);
|
|
886
901
|
return super.onConnect(connection, ctx);
|
|
887
902
|
}
|
|
888
903
|
/**
|
|
@@ -1040,4 +1055,4 @@ var ChatAgentHarness = class extends ChatAgent {
|
|
|
1040
1055
|
}
|
|
1041
1056
|
};
|
|
1042
1057
|
//#endregion
|
|
1043
|
-
export { Agent, ChatAgent, ChatAgentHarness, buildLLMParams,
|
|
1058
|
+
export { Agent, ChatAgent, ChatAgentHarness, buildLLMParams, routeAgentRequest };
|