@economic/agents 2.12.0 → 2.14.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 +51 -13
- package/dist/index.d.mts +168 -42
- package/dist/index.mjs +569 -188
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -421,6 +421,7 @@ import { z } from "zod";
|
|
|
421
421
|
|
|
422
422
|
export const weatherSkill = skill({
|
|
423
423
|
name: "weather",
|
|
424
|
+
command: "weather",
|
|
424
425
|
description: "Look up current weather and forecasts. Use for any weather question.",
|
|
425
426
|
instructions: `
|
|
426
427
|
# Weather
|
|
@@ -445,6 +446,16 @@ Use this skill whenever the user asks about current conditions or a forecast.
|
|
|
445
446
|
|
|
446
447
|
Return skills from `getSkills()`. Add `authorize(ctx)` to gate a skill (and its tools). Denied skills are hidden from the catalog and `activate_skill` by default. Set `onUnauthorized` to a reason string, or a function that returns one, to expose a denied skill with user-facing guidance. A function can return `undefined` to keep it hidden. `skill()` throws if `description` or `instructions` is missing.
|
|
447
448
|
|
|
449
|
+
Set `command` to make a code-defined skill directly invocable from chat. For example,
|
|
450
|
+
`/weather in Copenhagen` loads the weather instructions and enables its tools before the
|
|
451
|
+
first model step; the model still interprets `in Copenhagen` and chooses the appropriate
|
|
452
|
+
tool. Commands use lower-case letters, numbers, and hyphens, without the leading `/`.
|
|
453
|
+
|
|
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
|
|
457
|
+
model catalog and the human-facing picker.
|
|
458
|
+
|
|
448
459
|
### Native Agent Skills
|
|
449
460
|
|
|
450
461
|
Native Agent Skills are folders containing a `SKILL.md` file, with optional references and scripts:
|
|
@@ -640,7 +651,30 @@ When configured:
|
|
|
640
651
|
AI telemetry is emitted per turn from the `ai` SDK's OpenTelemetry spans:
|
|
641
652
|
|
|
642
653
|
- **Audit logs** → one JSON object per turn in `AGENTS_AUDIT_LOGS` (model, actor, IP, prompt, response, tool calls).
|
|
643
|
-
- **Analytics** → per-turn and per-tool
|
|
654
|
+
- **Analytics** → typed per-turn and per-tool events. By default these are written to
|
|
655
|
+
`AGENTS_ANALYTICS` using the existing Analytics Engine schema.
|
|
656
|
+
|
|
657
|
+
Additional analytics destinations can receive the same events by overriding
|
|
658
|
+
`getAnalyticsHandlers()`. Analytics Engine remains enabled whenever `AGENTS_ANALYTICS` is bound:
|
|
659
|
+
|
|
660
|
+
```typescript
|
|
661
|
+
import type { AnalyticsHandler } from "@economic/agents";
|
|
662
|
+
|
|
663
|
+
const warehouse: AnalyticsHandler = {
|
|
664
|
+
name: "warehouse",
|
|
665
|
+
async handle(event) {
|
|
666
|
+
await sendToWarehouse(event);
|
|
667
|
+
},
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
getAnalyticsHandlers() {
|
|
671
|
+
return [warehouse];
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
Handler failures are isolated and recorded as operational errors. Delivery runs through the
|
|
676
|
+
agent's lifetime management without blocking the response. `prompt.received` includes the raw
|
|
677
|
+
prompt for destinations such as conversation classification; handlers must treat it as sensitive.
|
|
644
678
|
|
|
645
679
|
Separately, the SDK writes structured operational logs for completed/failed LLM and tool calls, turn duration, framework failures, and selected recovery warnings. Prompt, response, tool argument, and tool result content is excluded. These records use the stable fields `source`, `event`, `level`, `timestamp`, `agentName`, `agentClass`, `durableObjectName`, and `attributes`, while preserving Cloudflare's diagnostic events.
|
|
646
680
|
|
|
@@ -669,21 +703,25 @@ Imported from `@economic/agents` unless noted.
|
|
|
669
703
|
| `ToolContext` | `RequestContext & { _userContext?: UserContext }`. |
|
|
670
704
|
| `AgentEnv` | The bindings the runtime expects. |
|
|
671
705
|
| `AgentConnectionState` / `AgentConnectionStatus` / `AgentConnectionType` | Connection state shared with the client. |
|
|
706
|
+
| `AnalyticsHandler` / `AnalyticsEvent` | Typed, consumer-configured product-analytics destinations. |
|
|
707
|
+
| `createAnalyticsEngineHandler(dataset)` | Analytics Engine destination using the existing v2 positional schema. |
|
|
672
708
|
|
|
673
709
|
### Members
|
|
674
710
|
|
|
675
|
-
| Member | On | Description
|
|
676
|
-
| ---------------------------------------------- | ----------- |
|
|
677
|
-
| `getModel(ctx?)` | `Agent` | Required. Model for inference.
|
|
678
|
-
| `getSystemPrompt(ctx?)` | `Agent` | Required. System prompt.
|
|
679
|
-
| `getTools()` | `Agent` | Always-on tools (default `{}`).
|
|
680
|
-
| `getSkills()` | `Agent` | Local skills (default `[]`).
|
|
681
|
-
| `
|
|
682
|
-
| `
|
|
683
|
-
| `
|
|
684
|
-
| `
|
|
685
|
-
| `
|
|
686
|
-
| `
|
|
711
|
+
| Member | On | Description |
|
|
712
|
+
| ---------------------------------------------- | ----------- | ---------------------------------------------------------------------- |
|
|
713
|
+
| `getModel(ctx?)` | `Agent` | Required. Model for inference. |
|
|
714
|
+
| `getSystemPrompt(ctx?)` | `Agent` | Required. System prompt. |
|
|
715
|
+
| `getTools()` | `Agent` | Always-on tools (default `{}`). |
|
|
716
|
+
| `getSkills()` | `Agent` | Local skills (default `[]`). |
|
|
717
|
+
| `getAnalyticsHandlers()` | `Agent` | Additional analytics destinations; Analytics Engine remains automatic. |
|
|
718
|
+
| `getAnalyticsAttributes()` | `Agent` | Additional dimensions included on every analytics event. |
|
|
719
|
+
| `static getJwtAuthConfig(env)` | `Agent` | Optional JWT verification and actor binding. |
|
|
720
|
+
| `getUserContext(jwtToken)` | `Agent` | Optional per-user context after auth. |
|
|
721
|
+
| `submitMessageFeedback` / `getMessageFeedback` | `ChatAgent` | Callable message feedback. |
|
|
722
|
+
| `agent` | `Assistant` | Required. Your `ChatAgent` subclass. |
|
|
723
|
+
| `fastModel` | `Assistant` | Required. Cheap model for titles/summaries. |
|
|
724
|
+
| `createChat` / `getChats` / `deleteChat` | `Assistant` | Callable chat management. |
|
|
687
725
|
|
|
688
726
|
### Package root (`@economic/agents`)
|
|
689
727
|
|
package/dist/index.d.mts
CHANGED
|
@@ -114,6 +114,108 @@ type Tool<Context extends Record<string, unknown> = Record<string, unknown>, INP
|
|
|
114
114
|
};
|
|
115
115
|
declare function tool<Context extends Record<string, unknown> = Record<string, unknown>, INPUT extends JSONValue | unknown | never = any, OUTPUT extends JSONValue | unknown | never = any>(definition: Tool<Context, INPUT, OUTPUT>): Tool$1<INPUT, OUTPUT>;
|
|
116
116
|
//#endregion
|
|
117
|
+
//#region src/server/features/analytics/types.d.ts
|
|
118
|
+
type AnalyticsAttribute = string | number | boolean;
|
|
119
|
+
type AnalyticsAttributes = Readonly<Record<string, AnalyticsAttribute>>;
|
|
120
|
+
interface AnalyticsUsage {
|
|
121
|
+
inputTokens: number;
|
|
122
|
+
outputTokens: number;
|
|
123
|
+
totalTokens: number;
|
|
124
|
+
cachedInputTokens?: number;
|
|
125
|
+
cacheWriteTokens?: number;
|
|
126
|
+
reasoningTokens?: number;
|
|
127
|
+
}
|
|
128
|
+
type AnalyticsEventPayload = {
|
|
129
|
+
type: "prompt.received"; /** Raw model messages. Handlers must treat this field as sensitive data. */
|
|
130
|
+
messages: readonly ModelMessage[];
|
|
131
|
+
continuation: boolean;
|
|
132
|
+
} | {
|
|
133
|
+
type: "turn.completed";
|
|
134
|
+
requestId: string;
|
|
135
|
+
status: string;
|
|
136
|
+
continuation: boolean;
|
|
137
|
+
responseSize: number;
|
|
138
|
+
} | {
|
|
139
|
+
type: "llm.completed";
|
|
140
|
+
model: string;
|
|
141
|
+
provider: string;
|
|
142
|
+
finishReason: string;
|
|
143
|
+
usage: AnalyticsUsage;
|
|
144
|
+
durationMs: number;
|
|
145
|
+
inputSize: number;
|
|
146
|
+
responseSize: number;
|
|
147
|
+
} | {
|
|
148
|
+
type: "tool.completed";
|
|
149
|
+
toolCallId?: string;
|
|
150
|
+
toolName: string;
|
|
151
|
+
skillNames: readonly string[];
|
|
152
|
+
success: boolean;
|
|
153
|
+
durationMs: number;
|
|
154
|
+
inputSize: number;
|
|
155
|
+
outputSize: number;
|
|
156
|
+
} | {
|
|
157
|
+
type: "skill.invoked";
|
|
158
|
+
skillName: string;
|
|
159
|
+
command: string;
|
|
160
|
+
source: "slash-command";
|
|
161
|
+
} | {
|
|
162
|
+
type: "feedback.submitted";
|
|
163
|
+
messageId: string;
|
|
164
|
+
rating: 1 | -1 | 0;
|
|
165
|
+
};
|
|
166
|
+
type AnalyticsEvent = AnalyticsEventPayload & {
|
|
167
|
+
schemaVersion: 1;
|
|
168
|
+
eventId: string;
|
|
169
|
+
timestamp: number;
|
|
170
|
+
agentName: string;
|
|
171
|
+
durableObjectName: string;
|
|
172
|
+
actorId: string;
|
|
173
|
+
attributes: AnalyticsAttributes;
|
|
174
|
+
};
|
|
175
|
+
/** Receives typed product-analytics events emitted by the SDK. */
|
|
176
|
+
interface AnalyticsHandler {
|
|
177
|
+
readonly name?: string;
|
|
178
|
+
handle(event: AnalyticsEvent): void | Promise<void>;
|
|
179
|
+
}
|
|
180
|
+
interface AnalyticsEventContext {
|
|
181
|
+
agentName: string;
|
|
182
|
+
durableObjectName: string;
|
|
183
|
+
actorId: string;
|
|
184
|
+
attributes?: AnalyticsAttributes;
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/server/features/analytics/analytics-engine.d.ts
|
|
188
|
+
/**
|
|
189
|
+
* Creates a Cloudflare Analytics Engine destination for SDK analytics events.
|
|
190
|
+
* It preserves the SDK v2 positional schema and never writes prompt content.
|
|
191
|
+
*/
|
|
192
|
+
declare function createAnalyticsEngineHandler(dataset: AnalyticsEngineDataset): AnalyticsHandler;
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/server/features/analytics/dispatcher.d.ts
|
|
195
|
+
declare function createAnalyticsEvent(context: AnalyticsEventContext, payload: AnalyticsEventPayload): AnalyticsEvent;
|
|
196
|
+
declare function dispatchAnalyticsEvent(handlers: readonly AnalyticsHandler[], event: AnalyticsEvent): Promise<void>;
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/server/features/analytics/emitter.d.ts
|
|
199
|
+
interface AnalyticsOptions {
|
|
200
|
+
getContext(): AnalyticsEventContext;
|
|
201
|
+
getHandlers(): readonly AnalyticsHandler[];
|
|
202
|
+
keepAlive<T>(callback: () => Promise<T>): Promise<T>;
|
|
203
|
+
}
|
|
204
|
+
declare class Analytics {
|
|
205
|
+
private readonly options;
|
|
206
|
+
private handlers?;
|
|
207
|
+
constructor(options: AnalyticsOptions);
|
|
208
|
+
private getHandlers;
|
|
209
|
+
private emit;
|
|
210
|
+
get enabled(): boolean;
|
|
211
|
+
get context(): AnalyticsEventContext;
|
|
212
|
+
promptReceived(messages: readonly ModelMessage[], continuation: boolean): void;
|
|
213
|
+
turnCompleted(requestId: string, status: string, continuation: boolean, response: unknown): void;
|
|
214
|
+
feedbackSubmitted(messageId: string, rating: 1 | -1 | 0): void;
|
|
215
|
+
skillInvoked(skillName: string, command: string): void;
|
|
216
|
+
event(payload: AnalyticsEventPayload | (() => AnalyticsEventPayload)): void;
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
117
219
|
//#region src/server/agents/Agent.d.ts
|
|
118
220
|
/** What the model would see on its next step — returned by {@link Agent.inspectTurn}. */
|
|
119
221
|
interface TurnSnapshot {
|
|
@@ -305,6 +407,14 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
305
407
|
* Best-effort and non-blocking — never allowed to interfere with a turn.
|
|
306
408
|
*/
|
|
307
409
|
protected registerInstanceActivity(): Promise<void>;
|
|
410
|
+
protected readonly analytics: Analytics;
|
|
411
|
+
/**
|
|
412
|
+
* Additional product-analytics destinations for this agent. Analytics Engine
|
|
413
|
+
* remains enabled whenever `AGENTS_ANALYTICS` is bound.
|
|
414
|
+
*/
|
|
415
|
+
getAnalyticsHandlers(): readonly AnalyticsHandler[];
|
|
416
|
+
/** Business dimensions added to every analytics event. */
|
|
417
|
+
getAnalyticsAttributes(): AnalyticsAttributes;
|
|
308
418
|
observability?: Observability;
|
|
309
419
|
/**
|
|
310
420
|
* Resolves the analytics/telemetry agent name to the entry-point agent: the
|
|
@@ -328,12 +438,69 @@ type MessageFeedback = {
|
|
|
328
438
|
updated_at: number;
|
|
329
439
|
};
|
|
330
440
|
//#endregion
|
|
441
|
+
//#region src/server/util/actions.d.ts
|
|
442
|
+
/** Tool execution options plus the additional context supplied by native Think actions. */
|
|
443
|
+
type ActionContext<Context extends Record<string, unknown> = Record<string, unknown>> = Omit<ToolExecutionOptions, "experimental_context"> & ActionContext$1 & {
|
|
444
|
+
experimental_context: Context; /** The action's resolved domain key, or its tool-call fallback when none was declared. */
|
|
445
|
+
idempotencyKey: string;
|
|
446
|
+
};
|
|
447
|
+
type Action<Context extends Record<string, unknown> = Record<string, unknown>, Input = any, Output = any> = Omit<ActionConfig<FlexibleSchema<Input>, Output>, "approval" | "execute" | "idempotencyKey" | "inputSchema" | "permissions"> & {
|
|
448
|
+
description: string;
|
|
449
|
+
inputSchema: FlexibleSchema<Input>;
|
|
450
|
+
approval?: ActionApprovalPolicy<Input>;
|
|
451
|
+
idempotencyKey?: ActionIdempotencyKey<Input>;
|
|
452
|
+
permissions?: ActionPermissionSpec<Input>;
|
|
453
|
+
authorize?: (ctx?: Context) => boolean;
|
|
454
|
+
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
455
|
+
execute(input: Input, ctx: ActionContext<Context>): Promise<Output> | Output;
|
|
456
|
+
};
|
|
457
|
+
type RegisteredAction<Context extends Record<string, unknown> = Record<string, unknown>, InputSchema extends FlexibleSchema = FlexibleSchema, Output = unknown> = Action$1<InputSchema, Output> & {
|
|
458
|
+
authorize?: (ctx?: Context) => boolean;
|
|
459
|
+
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
460
|
+
};
|
|
461
|
+
/** A native Think action with the SDK's standard authorization policy. */
|
|
462
|
+
declare function action<Context extends Record<string, unknown> = Record<string, unknown>, Input = any, Output = any>(definition: Action<Context, Input, Output>): RegisteredAction<Context, FlexibleSchema<Input>, Output>;
|
|
463
|
+
//#endregion
|
|
464
|
+
//#region src/server/util/skills.d.ts
|
|
465
|
+
interface Skill<Context extends Record<string, unknown> = Record<string, unknown>> {
|
|
466
|
+
name: string;
|
|
467
|
+
description: string;
|
|
468
|
+
instructions: string;
|
|
469
|
+
/** Enables direct invocation from chat with `/command`. */
|
|
470
|
+
command?: string;
|
|
471
|
+
tools?: Record<string, Tool<Context>>;
|
|
472
|
+
actions?: Record<string, RegisteredAction<Context, any, any>>;
|
|
473
|
+
authorize?: (ctx?: Context) => boolean;
|
|
474
|
+
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
475
|
+
}
|
|
476
|
+
interface SkillSource<Context extends Record<string, unknown> = Record<string, unknown>> extends SkillSource$1 {
|
|
477
|
+
command?: string;
|
|
478
|
+
tools?: Record<string, Tool<Context>>;
|
|
479
|
+
actions?: Record<string, RegisteredAction<Context, any, any>>;
|
|
480
|
+
authorize?: (ctx?: Context | undefined) => boolean;
|
|
481
|
+
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
482
|
+
}
|
|
483
|
+
declare function skill<Context extends Record<string, unknown> = Record<string, unknown>>(definition: Skill<Context>): SkillSource<Context>;
|
|
484
|
+
//#endregion
|
|
485
|
+
//#region src/server/util/skillCommands.d.ts
|
|
486
|
+
interface InvocableSkill {
|
|
487
|
+
/** Slash command without the leading `/`. */
|
|
488
|
+
command: string;
|
|
489
|
+
/** Name of the skill the command activates. */
|
|
490
|
+
name: string;
|
|
491
|
+
/** Human- and model-readable summary from the skill definition. */
|
|
492
|
+
description: string;
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
331
495
|
//#region src/server/agents/ChatAgent.d.ts
|
|
332
496
|
declare abstract class ChatAgent<RequestContext extends Record<string, unknown> = Record<string, unknown>, UserContext extends Record<string, unknown> = Record<string, unknown>, State = unknown> extends Agent<RequestContext, UserContext, State> {
|
|
333
497
|
protected agentType: AgentConnectionType;
|
|
334
498
|
onStart(): Promise<void>;
|
|
335
499
|
configureSession(session: Session): Session;
|
|
336
500
|
beforeTurn(ctx: TurnContext): Promise<void | TurnConfig>;
|
|
501
|
+
private getSlashCommandSkill;
|
|
502
|
+
private invokeSkill;
|
|
503
|
+
protected getInvocableSkills(userContext?: UserContext | undefined, requestContext?: RequestContext): Promise<InvocableSkill[]>;
|
|
337
504
|
onChatResponse(result: ChatResponseResult): Promise<void>;
|
|
338
505
|
onRequest(request: Request): Promise<Response>;
|
|
339
506
|
/**
|
|
@@ -408,51 +575,10 @@ declare abstract class Assistant<State = unknown> extends Agent$1<Cloudflare.Env
|
|
|
408
575
|
observability?: Observability;
|
|
409
576
|
}
|
|
410
577
|
//#endregion
|
|
411
|
-
//#region src/server/util/actions.d.ts
|
|
412
|
-
/** Tool execution options plus the additional context supplied by native Think actions. */
|
|
413
|
-
type ActionContext<Context extends Record<string, unknown> = Record<string, unknown>> = Omit<ToolExecutionOptions, "experimental_context"> & ActionContext$1 & {
|
|
414
|
-
experimental_context: Context; /** The action's resolved domain key, or its tool-call fallback when none was declared. */
|
|
415
|
-
idempotencyKey: string;
|
|
416
|
-
};
|
|
417
|
-
type Action<Context extends Record<string, unknown> = Record<string, unknown>, Input = any, Output = any> = Omit<ActionConfig<FlexibleSchema<Input>, Output>, "approval" | "execute" | "idempotencyKey" | "inputSchema" | "permissions"> & {
|
|
418
|
-
description: string;
|
|
419
|
-
inputSchema: FlexibleSchema<Input>;
|
|
420
|
-
approval?: ActionApprovalPolicy<Input>;
|
|
421
|
-
idempotencyKey?: ActionIdempotencyKey<Input>;
|
|
422
|
-
permissions?: ActionPermissionSpec<Input>;
|
|
423
|
-
authorize?: (ctx?: Context) => boolean;
|
|
424
|
-
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
425
|
-
execute(input: Input, ctx: ActionContext<Context>): Promise<Output> | Output;
|
|
426
|
-
};
|
|
427
|
-
type RegisteredAction<Context extends Record<string, unknown> = Record<string, unknown>, InputSchema extends FlexibleSchema = FlexibleSchema, Output = unknown> = Action$1<InputSchema, Output> & {
|
|
428
|
-
authorize?: (ctx?: Context) => boolean;
|
|
429
|
-
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
430
|
-
};
|
|
431
|
-
/** A native Think action with the SDK's standard authorization policy. */
|
|
432
|
-
declare function action<Context extends Record<string, unknown> = Record<string, unknown>, Input = any, Output = any>(definition: Action<Context, Input, Output>): RegisteredAction<Context, FlexibleSchema<Input>, Output>;
|
|
433
|
-
//#endregion
|
|
434
|
-
//#region src/server/util/skills.d.ts
|
|
435
|
-
interface Skill<Context extends Record<string, unknown> = Record<string, unknown>> {
|
|
436
|
-
name: string;
|
|
437
|
-
description: string;
|
|
438
|
-
instructions: string;
|
|
439
|
-
tools?: Record<string, Tool<Context>>;
|
|
440
|
-
actions?: Record<string, RegisteredAction<Context, any, any>>;
|
|
441
|
-
authorize?: (ctx?: Context) => boolean;
|
|
442
|
-
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
443
|
-
}
|
|
444
|
-
interface SkillSource<Context extends Record<string, unknown> = Record<string, unknown>> extends SkillSource$1 {
|
|
445
|
-
tools?: Record<string, Tool<Context>>;
|
|
446
|
-
actions?: Record<string, RegisteredAction<Context, any, any>>;
|
|
447
|
-
authorize?: (ctx?: Context | undefined) => boolean;
|
|
448
|
-
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
449
|
-
}
|
|
450
|
-
declare function skill<Context extends Record<string, unknown> = Record<string, unknown>>(definition: Skill<Context>): SkillSource<Context>;
|
|
451
|
-
//#endregion
|
|
452
578
|
//#region src/server/route-agent-request.d.ts
|
|
453
579
|
declare function routeAgentRequest<Env>(request: Request, env: Env, options?: AgentOptions<Env>): Promise<Response | null>;
|
|
454
580
|
//#endregion
|
|
455
581
|
//#region src/server/index.d.ts
|
|
456
582
|
declare function getCurrentToolContext(): any;
|
|
457
583
|
//#endregion
|
|
458
|
-
export { type Action, type ActionContext, Agent, type AgentConnectionStatus, type AgentConnectionType, type AgentEnv, Assistant, ChatAgent, type ExchangedToken, type JwtAuthConfig, type RegisteredAction, type Skill, type SkillSource, type TokenExchangeConfig, type TokenExchangeResult, type Tool, type ToolContext, type ToolSet, type TurnSnapshot, type UnauthorizedPolicy, type UnauthorizedToolPolicy, action, createJwtAuthConfig, exchangeToken, getCurrentToolContext, routeAgentRequest, skill, tool };
|
|
584
|
+
export { type Action, type ActionContext, Agent, type AgentConnectionStatus, type AgentConnectionType, type AgentEnv, type AnalyticsAttribute, type AnalyticsAttributes, type AnalyticsEvent, type AnalyticsEventPayload, type AnalyticsHandler, type AnalyticsUsage, Assistant, ChatAgent, type ExchangedToken, type InvocableSkill, type JwtAuthConfig, type RegisteredAction, type Skill, type SkillSource, type TokenExchangeConfig, type TokenExchangeResult, type Tool, type ToolContext, type ToolSet, type TurnSnapshot, type UnauthorizedPolicy, type UnauthorizedToolPolicy, action, createAnalyticsEngineHandler, createAnalyticsEvent, createJwtAuthConfig, dispatchAnalyticsEvent, exchangeToken, getCurrentToolContext, routeAgentRequest, skill, tool };
|
package/dist/index.mjs
CHANGED
|
@@ -510,94 +510,35 @@ async function handleAuditSpan(span, auditLogs, context) {
|
|
|
510
510
|
//#endregion
|
|
511
511
|
//#region src/server/features/telemetry/analytics.ts
|
|
512
512
|
const emittedToolCallIds = /* @__PURE__ */ new Set();
|
|
513
|
-
function
|
|
514
|
-
if (value === void 0) return void 0;
|
|
513
|
+
function serializedSize$1(value) {
|
|
515
514
|
try {
|
|
516
|
-
return JSON.stringify(value);
|
|
515
|
+
return JSON.stringify(value)?.length ?? 0;
|
|
517
516
|
} catch {
|
|
518
|
-
return;
|
|
517
|
+
return 0;
|
|
519
518
|
}
|
|
520
519
|
}
|
|
521
|
-
function
|
|
522
|
-
if (!analytics) return;
|
|
523
|
-
try {
|
|
524
|
-
analytics.writeDataPoint(dataPoint);
|
|
525
|
-
} catch (error) {
|
|
526
|
-
console.error("[Agent] Failed to write analytics datapoint", error);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Writes a message-feedback rating to Analytics Engine so sentiment can be
|
|
531
|
-
* aggregated across all chats without fanning out to every chat DO.
|
|
532
|
-
*
|
|
533
|
-
* Emitted on every rating change; the dashboard takes the latest rating per
|
|
534
|
-
* `messageId` (blob4) so re-rates and clears are idempotent.
|
|
535
|
-
*/
|
|
536
|
-
function recordFeedbackAnalytics(analytics, context, messageId, rating) {
|
|
537
|
-
writeAnalyticsDatapoint(analytics, {
|
|
538
|
-
indexes: [context.actorId],
|
|
539
|
-
blobs: [
|
|
540
|
-
"feedback",
|
|
541
|
-
context.agentName,
|
|
542
|
-
context.durableObjectName,
|
|
543
|
-
messageId
|
|
544
|
-
],
|
|
545
|
-
doubles: [rating]
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
function recordPromptReceivedAnalytics(analytics, context, messages, continuation) {
|
|
549
|
-
if (!analytics) return;
|
|
550
|
-
writeAnalyticsDatapoint(analytics, {
|
|
551
|
-
indexes: [context.actorId],
|
|
552
|
-
blobs: [
|
|
553
|
-
"prompt_received",
|
|
554
|
-
context.agentName,
|
|
555
|
-
context.durableObjectName,
|
|
556
|
-
String(continuation)
|
|
557
|
-
],
|
|
558
|
-
doubles: [safeJsonString(messages)?.length ?? 0]
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
function recordTurnCompletedAnalytics(analytics, context, requestId, status, continuation, response) {
|
|
562
|
-
if (!analytics) return;
|
|
563
|
-
writeAnalyticsDatapoint(analytics, {
|
|
564
|
-
indexes: [context.actorId],
|
|
565
|
-
blobs: [
|
|
566
|
-
"turn_completed",
|
|
567
|
-
context.agentName,
|
|
568
|
-
context.durableObjectName,
|
|
569
|
-
requestId,
|
|
570
|
-
status,
|
|
571
|
-
String(continuation)
|
|
572
|
-
],
|
|
573
|
-
doubles: [safeJsonString(response)?.length ?? 0]
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
function handleAnalyticsSpan(span, analytics, context) {
|
|
520
|
+
function handleAnalyticsSpan(span, analytics) {
|
|
521
|
+
if (!analytics.enabled) return;
|
|
577
522
|
if (span.name === "ai.streamText.doStream") {
|
|
578
523
|
const promptMessages = parseJson(stringAttribute(span, "ai.prompt.messages"));
|
|
579
524
|
const responseText = stringAttribute(span, "ai.response.text") ?? "";
|
|
580
525
|
const responseToolCalls = parseJson(stringAttribute(span, "ai.response.toolCalls"));
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
numberAttribute(span, "ai.usage.
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
durationMs(span.duration),
|
|
598
|
-
promptCharCount(promptMessages ?? []),
|
|
599
|
-
responseText.length
|
|
600
|
-
]
|
|
526
|
+
analytics.event({
|
|
527
|
+
type: "llm.completed",
|
|
528
|
+
model: stringAttribute(span, "ai.model.id") ?? "",
|
|
529
|
+
provider: stringAttribute(span, "ai.model.provider") ?? "",
|
|
530
|
+
finishReason: stringAttribute(span, "ai.response.finishReason") ?? "",
|
|
531
|
+
usage: {
|
|
532
|
+
inputTokens: numberAttribute(span, "ai.usage.inputTokens") ?? 0,
|
|
533
|
+
outputTokens: numberAttribute(span, "ai.usage.outputTokens") ?? 0,
|
|
534
|
+
totalTokens: numberAttribute(span, "ai.usage.totalTokens") ?? 0,
|
|
535
|
+
cachedInputTokens: numberAttribute(span, "ai.usage.inputTokenDetails.cacheReadTokens") ?? void 0,
|
|
536
|
+
cacheWriteTokens: numberAttribute(span, "ai.usage.inputTokenDetails.cacheWriteTokens") ?? void 0,
|
|
537
|
+
reasoningTokens: numberAttribute(span, "ai.usage.reasoningTokens") ?? void 0
|
|
538
|
+
},
|
|
539
|
+
durationMs: durationMs(span.duration),
|
|
540
|
+
inputSize: promptCharCount(promptMessages ?? []),
|
|
541
|
+
responseSize: responseText.length
|
|
601
542
|
});
|
|
602
543
|
if (Array.isArray(responseToolCalls)) for (const value of responseToolCalls) {
|
|
603
544
|
if (!value || typeof value !== "object") continue;
|
|
@@ -607,70 +548,39 @@ function handleAnalyticsSpan(span, analytics, context) {
|
|
|
607
548
|
const toolName = typeof toolCall.toolName === "string" ? toolCall.toolName : void 0;
|
|
608
549
|
if (!toolName) continue;
|
|
609
550
|
const input = toolCall.input;
|
|
610
|
-
const argsRaw = safeJsonString(input);
|
|
611
|
-
const success = span.status.code === 0;
|
|
612
551
|
const activatedSkills = extractSkillNames(toolName, input);
|
|
613
|
-
const skillNames = activatedSkills.length ? activatedSkills : [currentSkillByChat.get(context.durableObjectName) ?? ""];
|
|
552
|
+
const skillNames = activatedSkills.length ? activatedSkills : [currentSkillByChat.get(analytics.context.durableObjectName) ?? ""];
|
|
614
553
|
if (toolCallId) emittedToolCallIds.add(toolCallId);
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
],
|
|
625
|
-
doubles: [
|
|
626
|
-
0,
|
|
627
|
-
success ? 1 : 0,
|
|
628
|
-
argsRaw?.length ?? 0,
|
|
629
|
-
0,
|
|
630
|
-
0,
|
|
631
|
-
0,
|
|
632
|
-
0,
|
|
633
|
-
0
|
|
634
|
-
]
|
|
554
|
+
analytics.event({
|
|
555
|
+
type: "tool.completed",
|
|
556
|
+
toolCallId,
|
|
557
|
+
toolName,
|
|
558
|
+
skillNames,
|
|
559
|
+
success: span.status.code === 0,
|
|
560
|
+
durationMs: 0,
|
|
561
|
+
inputSize: serializedSize$1(input),
|
|
562
|
+
outputSize: 0
|
|
635
563
|
});
|
|
636
564
|
}
|
|
637
565
|
return;
|
|
638
566
|
}
|
|
639
|
-
if (span.name
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
context.agentName,
|
|
657
|
-
context.durableObjectName,
|
|
658
|
-
toolName ?? "",
|
|
659
|
-
skillName,
|
|
660
|
-
success ? "success" : "error"
|
|
661
|
-
],
|
|
662
|
-
doubles: [
|
|
663
|
-
duration,
|
|
664
|
-
success ? 1 : 0,
|
|
665
|
-
argsSize,
|
|
666
|
-
resultSize,
|
|
667
|
-
0,
|
|
668
|
-
0,
|
|
669
|
-
0,
|
|
670
|
-
0
|
|
671
|
-
]
|
|
672
|
-
});
|
|
673
|
-
}
|
|
567
|
+
if (span.name !== "ai.toolCall") return;
|
|
568
|
+
const toolCallId = stringAttribute(span, "ai.toolCall.id");
|
|
569
|
+
if (toolCallId && emittedToolCallIds.has(toolCallId)) return;
|
|
570
|
+
if (toolCallId) emittedToolCallIds.add(toolCallId);
|
|
571
|
+
const toolName = stringAttribute(span, "ai.toolCall.name") ?? "";
|
|
572
|
+
const argsRaw = stringAttribute(span, "ai.toolCall.args");
|
|
573
|
+
const activatedSkills = extractSkillNames(toolName, parseJson(argsRaw));
|
|
574
|
+
analytics.event({
|
|
575
|
+
type: "tool.completed",
|
|
576
|
+
toolCallId,
|
|
577
|
+
toolName,
|
|
578
|
+
skillNames: activatedSkills.length ? activatedSkills : [currentSkillByChat.get(analytics.context.durableObjectName) ?? ""],
|
|
579
|
+
success: span.status.code === 0,
|
|
580
|
+
durationMs: durationMs(span.duration),
|
|
581
|
+
inputSize: argsRaw?.length ?? 0,
|
|
582
|
+
outputSize: stringAttribute(span, "ai.toolCall.result")?.length ?? 0
|
|
583
|
+
});
|
|
674
584
|
}
|
|
675
585
|
//#endregion
|
|
676
586
|
//#region src/server/features/observability/index.ts
|
|
@@ -692,6 +602,36 @@ function writeOperationalLog(record, logger = console) {
|
|
|
692
602
|
else logger.log(message);
|
|
693
603
|
} catch {}
|
|
694
604
|
}
|
|
605
|
+
function errorAttributes(error) {
|
|
606
|
+
if (!(error instanceof Error)) return { error: String(error) };
|
|
607
|
+
const causes = [];
|
|
608
|
+
let cause = error.cause;
|
|
609
|
+
while (cause !== void 0 && causes.length < 5) {
|
|
610
|
+
causes.push(cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause));
|
|
611
|
+
cause = cause instanceof Error ? cause.cause : void 0;
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
error: error.message,
|
|
615
|
+
errorType: error.name,
|
|
616
|
+
stack: error.stack,
|
|
617
|
+
...causes.length ? { causes } : {}
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
function logOperationalError(event, error, context = {}) {
|
|
621
|
+
writeOperationalLog({
|
|
622
|
+
source: "@economic/agents",
|
|
623
|
+
event,
|
|
624
|
+
level: "error",
|
|
625
|
+
timestamp: Date.now(),
|
|
626
|
+
agentName: context.agentName,
|
|
627
|
+
agentClass: context.agentClass,
|
|
628
|
+
durableObjectName: context.durableObjectName,
|
|
629
|
+
attributes: {
|
|
630
|
+
...context.attributes,
|
|
631
|
+
...errorAttributes(error)
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
}
|
|
695
635
|
function isFailure(event) {
|
|
696
636
|
return event.type.endsWith("error") || event.type.endsWith("failed") || event.type.endsWith("exhausted") || "error" in event.payload && Boolean(event.payload.error);
|
|
697
637
|
}
|
|
@@ -793,11 +733,11 @@ var AgentSpanExporter = class {
|
|
|
793
733
|
if (span.name === "ai.streamText.doStream") {
|
|
794
734
|
rememberDoStream(span, this.context.durableObjectName);
|
|
795
735
|
attachToolCallsToChat(span, this.context.durableObjectName);
|
|
796
|
-
handleAnalyticsSpan(span, this.analytics
|
|
736
|
+
handleAnalyticsSpan(span, this.analytics);
|
|
797
737
|
} else if (span.name === "ai.streamText") await handleAuditSpan(span, this.auditLogs, this.context);
|
|
798
738
|
else if (span.name === "ai.toolCall") {
|
|
799
739
|
rememberToolCall(span);
|
|
800
|
-
handleAnalyticsSpan(span, this.analytics
|
|
740
|
+
handleAnalyticsSpan(span, this.analytics);
|
|
801
741
|
}
|
|
802
742
|
}
|
|
803
743
|
resultCallback({ code: 0 });
|
|
@@ -815,6 +755,275 @@ function createAgentTracer(auditLogs, analytics, context) {
|
|
|
815
755
|
return new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(new AgentSpanExporter(auditLogs, analytics, context))] }).getTracer("@economic/agents");
|
|
816
756
|
}
|
|
817
757
|
//#endregion
|
|
758
|
+
//#region src/server/features/analytics/utils.ts
|
|
759
|
+
function serializedSize(value) {
|
|
760
|
+
try {
|
|
761
|
+
return JSON.stringify(value)?.length ?? 0;
|
|
762
|
+
} catch {
|
|
763
|
+
return 0;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
//#endregion
|
|
767
|
+
//#region src/server/features/analytics/analytics-engine.ts
|
|
768
|
+
const MAX_INDEX_BYTES = 96;
|
|
769
|
+
const MAX_BLOB_BYTES = 16 * 1024;
|
|
770
|
+
const encoder = new TextEncoder();
|
|
771
|
+
/**
|
|
772
|
+
* Creates a Cloudflare Analytics Engine destination for SDK analytics events.
|
|
773
|
+
* It preserves the SDK v2 positional schema and never writes prompt content.
|
|
774
|
+
*/
|
|
775
|
+
function createAnalyticsEngineHandler(dataset) {
|
|
776
|
+
return {
|
|
777
|
+
name: "Cloudflare Analytics Engine",
|
|
778
|
+
async handle(event) {
|
|
779
|
+
for (const point of toAnalyticsEnginePoints(event)) dataset.writeDataPoint(await boundAnalyticsPoint(point));
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
async function boundAnalyticsPoint(point) {
|
|
784
|
+
return {
|
|
785
|
+
...point,
|
|
786
|
+
indexes: await Promise.all(point.indexes.map(boundIndex)),
|
|
787
|
+
blobs: boundBlobs(point.blobs)
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
async function boundIndex(value) {
|
|
791
|
+
if (byteLength(value) <= MAX_INDEX_BYTES) return value;
|
|
792
|
+
const digest = await crypto.subtle.digest("SHA-256", encoder.encode(value));
|
|
793
|
+
const suffix = `:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
794
|
+
return `${truncateUtf8(value, MAX_INDEX_BYTES - byteLength(suffix))}${suffix}`;
|
|
795
|
+
}
|
|
796
|
+
function boundBlobs(values) {
|
|
797
|
+
const blobs = [...values];
|
|
798
|
+
let sizes = blobs.map(byteLength);
|
|
799
|
+
let total = sizes.reduce((sum, size) => sum + size, 0);
|
|
800
|
+
if (total <= MAX_BLOB_BYTES) return blobs;
|
|
801
|
+
const attributesIndex = blobs.length - 2;
|
|
802
|
+
blobs[attributesIndex] = "{}";
|
|
803
|
+
sizes = blobs.map(byteLength);
|
|
804
|
+
total = sizes.reduce((sum, size) => sum + size, 0);
|
|
805
|
+
while (total > MAX_BLOB_BYTES) {
|
|
806
|
+
const largestSize = Math.max(...sizes);
|
|
807
|
+
const largestIndex = sizes.indexOf(largestSize);
|
|
808
|
+
const value = truncateUtf8(blobs[largestIndex] ?? "", largestSize - (total - MAX_BLOB_BYTES));
|
|
809
|
+
const nextSize = byteLength(value);
|
|
810
|
+
blobs[largestIndex] = value;
|
|
811
|
+
sizes[largestIndex] = nextSize;
|
|
812
|
+
total -= largestSize - nextSize;
|
|
813
|
+
}
|
|
814
|
+
return blobs;
|
|
815
|
+
}
|
|
816
|
+
function truncateUtf8(value, maxBytes) {
|
|
817
|
+
let bytes = 0;
|
|
818
|
+
let end = 0;
|
|
819
|
+
for (const character of value) {
|
|
820
|
+
const characterBytes = byteLength(character);
|
|
821
|
+
if (bytes + characterBytes > maxBytes) break;
|
|
822
|
+
bytes += characterBytes;
|
|
823
|
+
end += character.length;
|
|
824
|
+
}
|
|
825
|
+
return value.slice(0, end);
|
|
826
|
+
}
|
|
827
|
+
function byteLength(value) {
|
|
828
|
+
return encoder.encode(value).byteLength;
|
|
829
|
+
}
|
|
830
|
+
function toAnalyticsEnginePoints(event) {
|
|
831
|
+
const common = [
|
|
832
|
+
event.eventId,
|
|
833
|
+
JSON.stringify(event.attributes),
|
|
834
|
+
String(event.schemaVersion)
|
|
835
|
+
];
|
|
836
|
+
const indexes = [event.actorId];
|
|
837
|
+
switch (event.type) {
|
|
838
|
+
case "prompt.received": return [{
|
|
839
|
+
indexes,
|
|
840
|
+
blobs: [
|
|
841
|
+
"prompt_received",
|
|
842
|
+
event.agentName,
|
|
843
|
+
event.durableObjectName,
|
|
844
|
+
String(event.continuation),
|
|
845
|
+
...common
|
|
846
|
+
],
|
|
847
|
+
doubles: [serializedSize(event.messages)]
|
|
848
|
+
}];
|
|
849
|
+
case "turn.completed": return [{
|
|
850
|
+
indexes,
|
|
851
|
+
blobs: [
|
|
852
|
+
"turn_completed",
|
|
853
|
+
event.agentName,
|
|
854
|
+
event.durableObjectName,
|
|
855
|
+
event.requestId,
|
|
856
|
+
event.status,
|
|
857
|
+
String(event.continuation),
|
|
858
|
+
...common
|
|
859
|
+
],
|
|
860
|
+
doubles: [event.responseSize]
|
|
861
|
+
}];
|
|
862
|
+
case "llm.completed": return [{
|
|
863
|
+
indexes,
|
|
864
|
+
blobs: [
|
|
865
|
+
"llm_call",
|
|
866
|
+
event.agentName,
|
|
867
|
+
event.durableObjectName,
|
|
868
|
+
event.model,
|
|
869
|
+
event.provider,
|
|
870
|
+
event.finishReason,
|
|
871
|
+
...common
|
|
872
|
+
],
|
|
873
|
+
doubles: [
|
|
874
|
+
event.usage.inputTokens,
|
|
875
|
+
event.usage.outputTokens,
|
|
876
|
+
event.usage.totalTokens,
|
|
877
|
+
event.usage.cachedInputTokens ?? 0,
|
|
878
|
+
event.usage.cacheWriteTokens ?? 0,
|
|
879
|
+
event.durationMs,
|
|
880
|
+
event.inputSize,
|
|
881
|
+
event.responseSize,
|
|
882
|
+
event.usage.reasoningTokens ?? 0
|
|
883
|
+
]
|
|
884
|
+
}];
|
|
885
|
+
case "tool.completed": return (event.skillNames.length ? event.skillNames : [""]).map((skillName) => ({
|
|
886
|
+
indexes,
|
|
887
|
+
blobs: [
|
|
888
|
+
"tool_call",
|
|
889
|
+
event.agentName,
|
|
890
|
+
event.durableObjectName,
|
|
891
|
+
event.toolName,
|
|
892
|
+
skillName,
|
|
893
|
+
event.success ? "success" : "error",
|
|
894
|
+
event.toolCallId ?? "",
|
|
895
|
+
...common
|
|
896
|
+
],
|
|
897
|
+
doubles: [
|
|
898
|
+
event.durationMs,
|
|
899
|
+
event.success ? 1 : 0,
|
|
900
|
+
event.inputSize,
|
|
901
|
+
event.outputSize,
|
|
902
|
+
0,
|
|
903
|
+
0,
|
|
904
|
+
0,
|
|
905
|
+
0
|
|
906
|
+
]
|
|
907
|
+
}));
|
|
908
|
+
case "skill.invoked": return [{
|
|
909
|
+
indexes,
|
|
910
|
+
blobs: [
|
|
911
|
+
"skill_invoked",
|
|
912
|
+
event.agentName,
|
|
913
|
+
event.durableObjectName,
|
|
914
|
+
event.skillName,
|
|
915
|
+
event.command,
|
|
916
|
+
event.source,
|
|
917
|
+
...common
|
|
918
|
+
],
|
|
919
|
+
doubles: [1]
|
|
920
|
+
}];
|
|
921
|
+
case "feedback.submitted": return [{
|
|
922
|
+
indexes,
|
|
923
|
+
blobs: [
|
|
924
|
+
"feedback",
|
|
925
|
+
event.agentName,
|
|
926
|
+
event.durableObjectName,
|
|
927
|
+
event.messageId,
|
|
928
|
+
...common
|
|
929
|
+
],
|
|
930
|
+
doubles: [event.rating]
|
|
931
|
+
}];
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
//#endregion
|
|
935
|
+
//#region src/server/features/analytics/dispatcher.ts
|
|
936
|
+
function createAnalyticsEvent(context, payload) {
|
|
937
|
+
return {
|
|
938
|
+
...payload,
|
|
939
|
+
schemaVersion: 1,
|
|
940
|
+
eventId: crypto.randomUUID(),
|
|
941
|
+
timestamp: Date.now(),
|
|
942
|
+
agentName: context.agentName,
|
|
943
|
+
durableObjectName: context.durableObjectName,
|
|
944
|
+
actorId: context.actorId,
|
|
945
|
+
attributes: context.attributes ?? {}
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
async function dispatchAnalyticsEvent(handlers, event) {
|
|
949
|
+
await Promise.all(handlers.map(async (handler) => {
|
|
950
|
+
try {
|
|
951
|
+
await handler.handle(event);
|
|
952
|
+
} catch (error) {
|
|
953
|
+
writeOperationalLog({
|
|
954
|
+
source: "@economic/agents",
|
|
955
|
+
event: "agent.analytics.handler.failed",
|
|
956
|
+
level: "error",
|
|
957
|
+
timestamp: Date.now(),
|
|
958
|
+
agentName: event.agentName,
|
|
959
|
+
durableObjectName: event.durableObjectName,
|
|
960
|
+
attributes: {
|
|
961
|
+
handler: handler.name ?? "handler",
|
|
962
|
+
error: error instanceof Error ? error.message : String(error)
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
}));
|
|
967
|
+
}
|
|
968
|
+
//#endregion
|
|
969
|
+
//#region src/server/features/analytics/emitter.ts
|
|
970
|
+
var Analytics = class {
|
|
971
|
+
options;
|
|
972
|
+
handlers;
|
|
973
|
+
constructor(options) {
|
|
974
|
+
this.options = options;
|
|
975
|
+
}
|
|
976
|
+
getHandlers() {
|
|
977
|
+
return this.handlers ??= this.options.getHandlers();
|
|
978
|
+
}
|
|
979
|
+
emit(payload) {
|
|
980
|
+
const handlers = this.getHandlers();
|
|
981
|
+
if (handlers.length === 0) return;
|
|
982
|
+
const eventPayload = typeof payload === "function" ? payload() : payload;
|
|
983
|
+
this.options.keepAlive(() => dispatchAnalyticsEvent(handlers, createAnalyticsEvent(this.context, eventPayload)));
|
|
984
|
+
}
|
|
985
|
+
get enabled() {
|
|
986
|
+
return this.getHandlers().length > 0;
|
|
987
|
+
}
|
|
988
|
+
get context() {
|
|
989
|
+
return this.options.getContext();
|
|
990
|
+
}
|
|
991
|
+
promptReceived(messages, continuation) {
|
|
992
|
+
this.emit({
|
|
993
|
+
type: "prompt.received",
|
|
994
|
+
messages,
|
|
995
|
+
continuation
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
turnCompleted(requestId, status, continuation, response) {
|
|
999
|
+
this.emit(() => ({
|
|
1000
|
+
type: "turn.completed",
|
|
1001
|
+
requestId,
|
|
1002
|
+
status,
|
|
1003
|
+
continuation,
|
|
1004
|
+
responseSize: serializedSize(response)
|
|
1005
|
+
}));
|
|
1006
|
+
}
|
|
1007
|
+
feedbackSubmitted(messageId, rating) {
|
|
1008
|
+
this.emit({
|
|
1009
|
+
type: "feedback.submitted",
|
|
1010
|
+
messageId,
|
|
1011
|
+
rating
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
skillInvoked(skillName, command) {
|
|
1015
|
+
this.emit({
|
|
1016
|
+
type: "skill.invoked",
|
|
1017
|
+
skillName,
|
|
1018
|
+
command,
|
|
1019
|
+
source: "slash-command"
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
event(payload) {
|
|
1023
|
+
this.emit(payload);
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
//#endregion
|
|
818
1027
|
//#region src/server/features/registry.ts
|
|
819
1028
|
/**
|
|
820
1029
|
* Registers a top-level DO in the global registry.
|
|
@@ -964,11 +1173,7 @@ var Agent = class extends Think {
|
|
|
964
1173
|
* `returned.tools` into your own `TurnConfig.tools` if you return tools.
|
|
965
1174
|
*/
|
|
966
1175
|
async beforeTurn(ctx) {
|
|
967
|
-
|
|
968
|
-
agentName: this.getTelemetryAgentName(),
|
|
969
|
-
durableObjectName: this.name,
|
|
970
|
-
actorId: this.getTelemetryActorId()
|
|
971
|
-
}, ctx.messages, ctx.continuation);
|
|
1176
|
+
this.analytics.promptReceived(ctx.messages, ctx.continuation);
|
|
972
1177
|
this.registerInstanceActivity();
|
|
973
1178
|
const body = await this._applyTokenExchange(ctx.body);
|
|
974
1179
|
this._turn.context = this.getToolContextForCurrentTurn(body);
|
|
@@ -977,7 +1182,6 @@ var Agent = class extends Think {
|
|
|
977
1182
|
const { tools, activeTools } = await this._prepareTools(ctx.tools, activeSkillName, true);
|
|
978
1183
|
const baseSystem = await this.session.refreshSystemPrompt();
|
|
979
1184
|
this._turn.baseSystem = [baseSystem, this._turn.skillsSystemBlock].filter(Boolean).join("\n\n");
|
|
980
|
-
console.log(this._turn.baseSystem, "\n\n// end system prompt\n\n");
|
|
981
1185
|
return {
|
|
982
1186
|
system: this._turn.baseSystem,
|
|
983
1187
|
model: this.getModel(this._turn.context),
|
|
@@ -987,7 +1191,7 @@ var Agent = class extends Think {
|
|
|
987
1191
|
experimental_transform: buildSourcesTransform(),
|
|
988
1192
|
experimental_telemetry: {
|
|
989
1193
|
isEnabled: true,
|
|
990
|
-
tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.
|
|
1194
|
+
tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.analytics, {
|
|
991
1195
|
agentName: this.getTelemetryAgentName(),
|
|
992
1196
|
agentClass: this.constructor.name,
|
|
993
1197
|
durableObjectName: this.name,
|
|
@@ -1346,7 +1550,12 @@ var Agent = class extends Think {
|
|
|
1346
1550
|
try {
|
|
1347
1551
|
result = await verifyJwt(request, config);
|
|
1348
1552
|
} catch (error) {
|
|
1349
|
-
|
|
1553
|
+
logOperationalError("agent.authentication.failed", error, {
|
|
1554
|
+
agentName: this.getTelemetryAgentName(),
|
|
1555
|
+
agentClass: this.constructor.name,
|
|
1556
|
+
durableObjectName: this.name,
|
|
1557
|
+
attributes: { stage: "jwt-verification" }
|
|
1558
|
+
});
|
|
1350
1559
|
return {
|
|
1351
1560
|
success: false,
|
|
1352
1561
|
status: 401,
|
|
@@ -1369,7 +1578,12 @@ var Agent = class extends Think {
|
|
|
1369
1578
|
try {
|
|
1370
1579
|
exchanged = await exchangeToken(token, exchangeConfig);
|
|
1371
1580
|
} catch (error) {
|
|
1372
|
-
|
|
1581
|
+
logOperationalError("agent.authentication.failed", error, {
|
|
1582
|
+
agentName: this.getTelemetryAgentName(),
|
|
1583
|
+
agentClass: this.constructor.name,
|
|
1584
|
+
durableObjectName: this.name,
|
|
1585
|
+
attributes: { stage: "token-exchange" }
|
|
1586
|
+
});
|
|
1373
1587
|
return {
|
|
1374
1588
|
success: false,
|
|
1375
1589
|
status: 500,
|
|
@@ -1377,7 +1591,12 @@ var Agent = class extends Think {
|
|
|
1377
1591
|
};
|
|
1378
1592
|
}
|
|
1379
1593
|
if (!exchanged.success) {
|
|
1380
|
-
|
|
1594
|
+
logOperationalError("agent.authentication.failed", new Error(exchanged.message), {
|
|
1595
|
+
agentName: this.getTelemetryAgentName(),
|
|
1596
|
+
agentClass: this.constructor.name,
|
|
1597
|
+
durableObjectName: this.name,
|
|
1598
|
+
attributes: { stage: "token-exchange" }
|
|
1599
|
+
});
|
|
1381
1600
|
return {
|
|
1382
1601
|
success: false,
|
|
1383
1602
|
status: exchanged.subjectTokenRejected ? 401 : 500,
|
|
@@ -1395,7 +1614,12 @@ var Agent = class extends Think {
|
|
|
1395
1614
|
userContext: this.userContext
|
|
1396
1615
|
};
|
|
1397
1616
|
} catch (error) {
|
|
1398
|
-
|
|
1617
|
+
logOperationalError("agent.authentication.failed", error, {
|
|
1618
|
+
agentName: this.getTelemetryAgentName(),
|
|
1619
|
+
agentClass: this.constructor.name,
|
|
1620
|
+
durableObjectName: this.name,
|
|
1621
|
+
attributes: { stage: "user-context" }
|
|
1622
|
+
});
|
|
1399
1623
|
const closeCode = getConnectCloseCode(error);
|
|
1400
1624
|
return {
|
|
1401
1625
|
success: false,
|
|
@@ -1459,7 +1683,11 @@ var Agent = class extends Think {
|
|
|
1459
1683
|
createdAt: Date.now()
|
|
1460
1684
|
});
|
|
1461
1685
|
} catch (error) {
|
|
1462
|
-
|
|
1686
|
+
logOperationalError("agent.registry.registration.failed", error, {
|
|
1687
|
+
agentName: this.getTelemetryAgentName(),
|
|
1688
|
+
agentClass: this.constructor.name,
|
|
1689
|
+
durableObjectName: this.name
|
|
1690
|
+
});
|
|
1463
1691
|
}
|
|
1464
1692
|
}
|
|
1465
1693
|
/**
|
|
@@ -1485,9 +1713,34 @@ var Agent = class extends Think {
|
|
|
1485
1713
|
updatedAt: Date.now()
|
|
1486
1714
|
});
|
|
1487
1715
|
} catch (error) {
|
|
1488
|
-
|
|
1716
|
+
logOperationalError("agent.registry.activity.failed", error, {
|
|
1717
|
+
agentName: this.getTelemetryAgentName(),
|
|
1718
|
+
agentClass: this.constructor.name,
|
|
1719
|
+
durableObjectName: this.name
|
|
1720
|
+
});
|
|
1489
1721
|
}
|
|
1490
1722
|
}
|
|
1723
|
+
analytics = new Analytics({
|
|
1724
|
+
getContext: () => ({
|
|
1725
|
+
agentName: this.getTelemetryAgentName(),
|
|
1726
|
+
durableObjectName: this.name,
|
|
1727
|
+
actorId: this.getTelemetryActorId(),
|
|
1728
|
+
attributes: this.getAnalyticsAttributes()
|
|
1729
|
+
}),
|
|
1730
|
+
getHandlers: () => [...this.env.AGENTS_ANALYTICS ? [createAnalyticsEngineHandler(this.env.AGENTS_ANALYTICS)] : [], ...this.getAnalyticsHandlers()],
|
|
1731
|
+
keepAlive: (callback) => this.keepAliveWhile(callback)
|
|
1732
|
+
});
|
|
1733
|
+
/**
|
|
1734
|
+
* Additional product-analytics destinations for this agent. Analytics Engine
|
|
1735
|
+
* remains enabled whenever `AGENTS_ANALYTICS` is bound.
|
|
1736
|
+
*/
|
|
1737
|
+
getAnalyticsHandlers() {
|
|
1738
|
+
return [];
|
|
1739
|
+
}
|
|
1740
|
+
/** Business dimensions added to every analytics event. */
|
|
1741
|
+
getAnalyticsAttributes() {
|
|
1742
|
+
return {};
|
|
1743
|
+
}
|
|
1491
1744
|
observability = createWorkerObservability({ getIdentity: () => ({
|
|
1492
1745
|
agentName: this.getTelemetryAgentName(),
|
|
1493
1746
|
agentClass: this.constructor.name
|
|
@@ -1884,7 +2137,12 @@ var Assistant = class extends Agent$1 {
|
|
|
1884
2137
|
try {
|
|
1885
2138
|
result = await verifyJwt(request, config);
|
|
1886
2139
|
} catch (error) {
|
|
1887
|
-
|
|
2140
|
+
logOperationalError("agent.authentication.failed", error, {
|
|
2141
|
+
agentName: this.constructor.name,
|
|
2142
|
+
agentClass: this.constructor.name,
|
|
2143
|
+
durableObjectName: this.name,
|
|
2144
|
+
attributes: { stage: "jwt-verification" }
|
|
2145
|
+
});
|
|
1888
2146
|
return {
|
|
1889
2147
|
success: false,
|
|
1890
2148
|
status: 401,
|
|
@@ -1896,13 +2154,18 @@ var Assistant = class extends Agent$1 {
|
|
|
1896
2154
|
durableObjectName: this.name,
|
|
1897
2155
|
parentPath: this.parentPath
|
|
1898
2156
|
})) {
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
2157
|
+
logOperationalError("agent.authentication.failed", /* @__PURE__ */ new Error("Authenticated actor does not own the requested assistant"), {
|
|
2158
|
+
agentName: this.constructor.name,
|
|
2159
|
+
agentClass: this.constructor.name,
|
|
2160
|
+
durableObjectName: this.name,
|
|
2161
|
+
attributes: {
|
|
2162
|
+
stage: "actor-authorization",
|
|
2163
|
+
authenticatedActorId: config.getActorId(result.claims),
|
|
2164
|
+
requiredActorId: getTargetActorId({
|
|
2165
|
+
durableObjectName: this.name,
|
|
2166
|
+
parentPath: this.parentPath
|
|
2167
|
+
})
|
|
2168
|
+
}
|
|
1906
2169
|
});
|
|
1907
2170
|
return {
|
|
1908
2171
|
success: false,
|
|
@@ -1940,9 +2203,11 @@ var Assistant = class extends Agent$1 {
|
|
|
1940
2203
|
try {
|
|
1941
2204
|
await this.deleteSubAgent(this.agent, id);
|
|
1942
2205
|
} catch (cleanupError) {
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
2206
|
+
logOperationalError("assistant.chat.cleanup.failed", cleanupError, {
|
|
2207
|
+
agentName: this.constructor.name,
|
|
2208
|
+
agentClass: this.constructor.name,
|
|
2209
|
+
durableObjectName: this.name,
|
|
2210
|
+
attributes: { chatName: id }
|
|
1946
2211
|
});
|
|
1947
2212
|
}
|
|
1948
2213
|
throw error;
|
|
@@ -1973,16 +2238,20 @@ var Assistant = class extends Agent$1 {
|
|
|
1973
2238
|
touchChat(this.sql.bind(this), durableObjectName);
|
|
1974
2239
|
this.keepAliveWhile(async () => {
|
|
1975
2240
|
await Promise.all([summariseChatWithAI(this.sql.bind(this), durableObjectName, messages, this.fastModel), this.scheduleChatForAutoDeletion(durableObjectName).catch((error) => {
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
2241
|
+
logOperationalError("assistant.chat.reschedule.failed", error, {
|
|
2242
|
+
agentName: this.constructor.name,
|
|
2243
|
+
agentClass: this.constructor.name,
|
|
2244
|
+
durableObjectName: this.name,
|
|
2245
|
+
attributes: { chatName: durableObjectName }
|
|
1979
2246
|
});
|
|
1980
2247
|
})]);
|
|
1981
2248
|
await this.syncChatToGlobalList(durableObjectName);
|
|
1982
2249
|
}).catch((error) => {
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
2250
|
+
logOperationalError("assistant.chat.metadata.failed", error, {
|
|
2251
|
+
agentName: this.constructor.name,
|
|
2252
|
+
agentClass: this.constructor.name,
|
|
2253
|
+
durableObjectName: this.name,
|
|
2254
|
+
attributes: { chatName: durableObjectName }
|
|
1986
2255
|
});
|
|
1987
2256
|
});
|
|
1988
2257
|
}
|
|
@@ -2024,7 +2293,11 @@ var Assistant = class extends Agent$1 {
|
|
|
2024
2293
|
createdAt: Date.now()
|
|
2025
2294
|
});
|
|
2026
2295
|
} catch (error) {
|
|
2027
|
-
|
|
2296
|
+
logOperationalError("agent.registry.registration.failed", error, {
|
|
2297
|
+
agentName: this.constructor.name,
|
|
2298
|
+
agentClass: this.constructor.name,
|
|
2299
|
+
durableObjectName: this.name
|
|
2300
|
+
});
|
|
2028
2301
|
}
|
|
2029
2302
|
}
|
|
2030
2303
|
/**
|
|
@@ -2041,7 +2314,11 @@ var Assistant = class extends Agent$1 {
|
|
|
2041
2314
|
updatedAt
|
|
2042
2315
|
});
|
|
2043
2316
|
} catch (error) {
|
|
2044
|
-
|
|
2317
|
+
logOperationalError("agent.registry.activity.failed", error, {
|
|
2318
|
+
agentName: this.constructor.name,
|
|
2319
|
+
agentClass: this.constructor.name,
|
|
2320
|
+
durableObjectName: this.name
|
|
2321
|
+
});
|
|
2045
2322
|
}
|
|
2046
2323
|
}
|
|
2047
2324
|
async syncChatToGlobalList(durableObjectName) {
|
|
@@ -2081,6 +2358,38 @@ var Assistant = class extends Agent$1 {
|
|
|
2081
2358
|
agentClass: this.constructor.name
|
|
2082
2359
|
}) });
|
|
2083
2360
|
};
|
|
2361
|
+
//#endregion
|
|
2362
|
+
//#region src/server/util/skillCommands.ts
|
|
2363
|
+
async function listInvocableSkills(sources) {
|
|
2364
|
+
const commands = /* @__PURE__ */ new Set();
|
|
2365
|
+
const skills = [];
|
|
2366
|
+
for (const source of sources) {
|
|
2367
|
+
if (!source.command) continue;
|
|
2368
|
+
if (commands.has(source.command)) throw new Error(`Duplicate skill command "/${source.command}"`);
|
|
2369
|
+
const descriptor = (await source.list()).find((skill) => skill.name === source.id);
|
|
2370
|
+
if (!descriptor) continue;
|
|
2371
|
+
commands.add(source.command);
|
|
2372
|
+
skills.push({
|
|
2373
|
+
command: source.command,
|
|
2374
|
+
name: descriptor.name,
|
|
2375
|
+
description: descriptor.description
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2378
|
+
return skills;
|
|
2379
|
+
}
|
|
2380
|
+
function parseSkillCommand(messages, skills) {
|
|
2381
|
+
const message = messages.findLast((candidate) => candidate.role === "user");
|
|
2382
|
+
if (!message) return;
|
|
2383
|
+
const text = getMessageText(message);
|
|
2384
|
+
if (!text?.startsWith("/")) return;
|
|
2385
|
+
const match = /^\/([^\s]+)/.exec(text);
|
|
2386
|
+
if (!match) return;
|
|
2387
|
+
return skills.find((skill) => skill.command === match[1]);
|
|
2388
|
+
}
|
|
2389
|
+
function getMessageText(message) {
|
|
2390
|
+
if (typeof message.content === "string") return message.content;
|
|
2391
|
+
return message.content.filter((part) => part.type === "text").map((part) => part.text).join("") || void 0;
|
|
2392
|
+
}
|
|
2084
2393
|
const COMPACTION_MAX_OUTPUT_TOKENS = 2e3;
|
|
2085
2394
|
const createCompactFn = (model) => createCompactFunction({
|
|
2086
2395
|
protectHead: 2,
|
|
@@ -2418,7 +2727,22 @@ var ChatAgent = class extends Agent {
|
|
|
2418
2727
|
async beforeTurn(ctx) {
|
|
2419
2728
|
const parent = await this.getParentAgent();
|
|
2420
2729
|
if (parent?.registerChat) await parent.registerChat(this.name);
|
|
2421
|
-
const
|
|
2730
|
+
const slashCommandSkill = await this.getSlashCommandSkill(ctx);
|
|
2731
|
+
let skillInvocation;
|
|
2732
|
+
if (slashCommandSkill) skillInvocation = await this.invokeSkill(ctx, slashCommandSkill);
|
|
2733
|
+
const turnContext = skillInvocation ? {
|
|
2734
|
+
...ctx,
|
|
2735
|
+
messages: [...ctx.messages, ...skillInvocation.messages]
|
|
2736
|
+
} : ctx;
|
|
2737
|
+
let config = await super.beforeTurn(turnContext) ?? {};
|
|
2738
|
+
if (skillInvocation) {
|
|
2739
|
+
const transforms = config.experimental_transform ? Array.isArray(config.experimental_transform) ? config.experimental_transform : [config.experimental_transform] : [];
|
|
2740
|
+
config = {
|
|
2741
|
+
...config,
|
|
2742
|
+
activeTools: config.activeTools?.filter((name) => name !== "activate_skill"),
|
|
2743
|
+
experimental_transform: [skillInvocation.transform, ...transforms]
|
|
2744
|
+
};
|
|
2745
|
+
}
|
|
2422
2746
|
try {
|
|
2423
2747
|
const prepared = await prepareModelHistory(this.messages, getModelCompaction(this.sql.bind(this)), createCompactFn(config.model ?? ctx.model), config.system ?? ctx.system);
|
|
2424
2748
|
if (prepared.stateChanged) if (prepared.state) setModelCompaction(this.sql.bind(this), prepared.state);
|
|
@@ -2434,7 +2758,7 @@ var ChatAgent = class extends Agent {
|
|
|
2434
2758
|
});
|
|
2435
2759
|
return {
|
|
2436
2760
|
...config,
|
|
2437
|
-
messages: this.pruneTurnMessages(modelMessages)
|
|
2761
|
+
messages: this.pruneTurnMessages([...modelMessages, ...skillInvocation?.messages ?? []])
|
|
2438
2762
|
};
|
|
2439
2763
|
}
|
|
2440
2764
|
} catch (error) {
|
|
@@ -2442,12 +2766,69 @@ var ChatAgent = class extends Agent {
|
|
|
2442
2766
|
}
|
|
2443
2767
|
return config;
|
|
2444
2768
|
}
|
|
2769
|
+
async getSlashCommandSkill(context) {
|
|
2770
|
+
if (context.continuation) return;
|
|
2771
|
+
const connectionState = getCurrentAgent()?.connection?.state;
|
|
2772
|
+
const invocableSkills = await this.getInvocableSkills(connectionState?.userContext ?? this.userContext, context.body);
|
|
2773
|
+
return parseSkillCommand(context.messages, invocableSkills);
|
|
2774
|
+
}
|
|
2775
|
+
async invokeSkill(context, skill) {
|
|
2776
|
+
const activateSkill = context.tools.activate_skill;
|
|
2777
|
+
if (!activateSkill?.execute) return;
|
|
2778
|
+
const input = { name: skill.name };
|
|
2779
|
+
const toolCallId = crypto.randomUUID();
|
|
2780
|
+
const output = await activateSkill.execute(input, {
|
|
2781
|
+
toolCallId,
|
|
2782
|
+
messages: context.messages
|
|
2783
|
+
});
|
|
2784
|
+
if (typeof output !== "string") throw new Error("activate_skill returned an invalid result");
|
|
2785
|
+
this.analytics.skillInvoked(skill.name, skill.command);
|
|
2786
|
+
const messages = await convertToModelMessages([{
|
|
2787
|
+
id: crypto.randomUUID(),
|
|
2788
|
+
role: "assistant",
|
|
2789
|
+
parts: [{
|
|
2790
|
+
type: "tool-activate_skill",
|
|
2791
|
+
toolCallId,
|
|
2792
|
+
state: "output-available",
|
|
2793
|
+
input,
|
|
2794
|
+
output
|
|
2795
|
+
}]
|
|
2796
|
+
}], { tools: context.tools });
|
|
2797
|
+
const transform = () => {
|
|
2798
|
+
let emitted = false;
|
|
2799
|
+
return new TransformStream({ transform(chunk, controller) {
|
|
2800
|
+
controller.enqueue(chunk);
|
|
2801
|
+
if (emitted || chunk.type !== "start-step") return;
|
|
2802
|
+
emitted = true;
|
|
2803
|
+
controller.enqueue({
|
|
2804
|
+
type: "tool-call",
|
|
2805
|
+
toolCallId,
|
|
2806
|
+
toolName: "activate_skill",
|
|
2807
|
+
input
|
|
2808
|
+
});
|
|
2809
|
+
controller.enqueue({
|
|
2810
|
+
type: "tool-result",
|
|
2811
|
+
toolCallId,
|
|
2812
|
+
toolName: "activate_skill",
|
|
2813
|
+
input,
|
|
2814
|
+
output
|
|
2815
|
+
});
|
|
2816
|
+
} });
|
|
2817
|
+
};
|
|
2818
|
+
return {
|
|
2819
|
+
messages,
|
|
2820
|
+
transform
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2823
|
+
async getInvocableSkills(userContext = this.userContext, requestContext = {}) {
|
|
2824
|
+
const context = {
|
|
2825
|
+
...requestContext,
|
|
2826
|
+
_userContext: userContext
|
|
2827
|
+
};
|
|
2828
|
+
return listInvocableSkills((await this.getSkills()).filter((source) => source.authorize?.(context) !== false));
|
|
2829
|
+
}
|
|
2445
2830
|
async onChatResponse(result) {
|
|
2446
|
-
|
|
2447
|
-
agentName: this.getTelemetryAgentName(),
|
|
2448
|
-
durableObjectName: this.name,
|
|
2449
|
-
actorId: this.getTelemetryActorId()
|
|
2450
|
-
}, result.requestId, result.status, result.continuation, result.message.parts);
|
|
2831
|
+
this.analytics.turnCompleted(result.requestId, result.status, result.continuation, result.message.parts);
|
|
2451
2832
|
const parent = await this.getParentAgent();
|
|
2452
2833
|
if (parent?.recordChatTurn) await parent.recordChatTurn(this.name, this.messages);
|
|
2453
2834
|
}
|
|
@@ -2456,6 +2837,7 @@ var ChatAgent = class extends Agent {
|
|
|
2456
2837
|
if (!auth.success) return Response.json({ error: auth.message }, { status: auth.status });
|
|
2457
2838
|
const action = new URL(request.url).pathname.split("/").pop();
|
|
2458
2839
|
if (action === "getMessages" && request.method === "GET") return Response.json(await this.getMessages());
|
|
2840
|
+
if (action === "getInvocableSkills" && request.method === "GET") return Response.json(await this.getInvocableSkills(auth.userContext));
|
|
2459
2841
|
if (action === "sendMessage" && request.method === "POST") return this.streamMessage(request);
|
|
2460
2842
|
if (action === "cancelChat" && request.method === "POST") {
|
|
2461
2843
|
const { requestId } = await request.json();
|
|
@@ -2519,18 +2901,15 @@ var ChatAgent = class extends Agent {
|
|
|
2519
2901
|
async submitMessageFeedback(messageId, rating, comment) {
|
|
2520
2902
|
const result = submitMessageFeedback(this.sql.bind(this), messageId, rating, comment);
|
|
2521
2903
|
const parent = await this.getParentAgent();
|
|
2522
|
-
|
|
2523
|
-
agentName: this.getTelemetryAgentName(),
|
|
2524
|
-
durableObjectName: this.name,
|
|
2525
|
-
actorId: this.getTelemetryActorId()
|
|
2526
|
-
}, messageId, rating);
|
|
2904
|
+
if (rating === 1 || rating === -1 || rating === 0) this.analytics.feedbackSubmitted(messageId, rating);
|
|
2527
2905
|
if (parent?.recordChatFeedback) try {
|
|
2528
2906
|
await parent.recordChatFeedback(this.name, messageId, rating);
|
|
2529
2907
|
} catch (error) {
|
|
2530
|
-
|
|
2908
|
+
logOperationalError("agent.feedback.summary.failed", error, {
|
|
2909
|
+
agentName: this.getTelemetryAgentName(),
|
|
2910
|
+
agentClass: this.constructor.name,
|
|
2531
2911
|
durableObjectName: this.name,
|
|
2532
|
-
messageId
|
|
2533
|
-
error
|
|
2912
|
+
attributes: { messageId }
|
|
2534
2913
|
});
|
|
2535
2914
|
}
|
|
2536
2915
|
return result;
|
|
@@ -2578,6 +2957,7 @@ function skill(definition) {
|
|
|
2578
2957
|
if (!definition.name) throw new Error("Skill name is required");
|
|
2579
2958
|
if (!definition.description) throw new Error("Skill description is required");
|
|
2580
2959
|
if (!definition.instructions) throw new Error("Skill content is required");
|
|
2960
|
+
if (definition.command !== void 0 && !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(definition.command)) throw new Error("Skill command must use lower-case letters, numbers, and hyphens");
|
|
2581
2961
|
return {
|
|
2582
2962
|
id: definition.name,
|
|
2583
2963
|
fingerprint: definition.name,
|
|
@@ -2595,6 +2975,7 @@ function skill(definition) {
|
|
|
2595
2975
|
body: definition.instructions
|
|
2596
2976
|
};
|
|
2597
2977
|
},
|
|
2978
|
+
command: definition.command,
|
|
2598
2979
|
tools: definition.tools,
|
|
2599
2980
|
actions: definition.actions,
|
|
2600
2981
|
authorize: definition.authorize,
|
|
@@ -2620,4 +3001,4 @@ function getCurrentToolContext() {
|
|
|
2620
3001
|
return getCurrentAgent().agent._lastBody;
|
|
2621
3002
|
}
|
|
2622
3003
|
//#endregion
|
|
2623
|
-
export { Agent, Assistant, ChatAgent, action, createJwtAuthConfig, exchangeToken, getCurrentToolContext, routeAgentRequest, skill, tool };
|
|
3004
|
+
export { Agent, Assistant, ChatAgent, action, createAnalyticsEngineHandler, createAnalyticsEvent, createJwtAuthConfig, dispatchAnalyticsEvent, exchangeToken, getCurrentToolContext, routeAgentRequest, skill, tool };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@economic/agents",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "A starter for creating a TypeScript package.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@cloudflare/think": "^0.17.0",
|
|
48
|
-
"@cloudflare/vite-plugin": "^1.
|
|
48
|
+
"@cloudflare/vite-plugin": "^1.45.1",
|
|
49
49
|
"@cloudflare/worker-bundler": "^0.2.1",
|
|
50
50
|
"agents": "^0.22.0",
|
|
51
51
|
"vite": ">=8.0.0"
|