@economic/agents 2.13.0 → 2.14.1
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 +11 -0
- package/dist/index.d.mts +77 -44
- package/dist/index.mjs +169 -21
- 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:
|
package/dist/index.d.mts
CHANGED
|
@@ -153,6 +153,11 @@ type AnalyticsEventPayload = {
|
|
|
153
153
|
durationMs: number;
|
|
154
154
|
inputSize: number;
|
|
155
155
|
outputSize: number;
|
|
156
|
+
} | {
|
|
157
|
+
type: "skill.invoked";
|
|
158
|
+
skillName: string;
|
|
159
|
+
command: string;
|
|
160
|
+
source: "slash-command";
|
|
156
161
|
} | {
|
|
157
162
|
type: "feedback.submitted";
|
|
158
163
|
messageId: string;
|
|
@@ -207,6 +212,7 @@ declare class Analytics {
|
|
|
207
212
|
promptReceived(messages: readonly ModelMessage[], continuation: boolean): void;
|
|
208
213
|
turnCompleted(requestId: string, status: string, continuation: boolean, response: unknown): void;
|
|
209
214
|
feedbackSubmitted(messageId: string, rating: 1 | -1 | 0): void;
|
|
215
|
+
skillInvoked(skillName: string, command: string): void;
|
|
210
216
|
event(payload: AnalyticsEventPayload | (() => AnalyticsEventPayload)): void;
|
|
211
217
|
}
|
|
212
218
|
//#endregion
|
|
@@ -220,6 +226,16 @@ interface TurnSnapshot {
|
|
|
220
226
|
frameworkToolNames: string[];
|
|
221
227
|
messages: ModelMessage[];
|
|
222
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
|
+
};
|
|
223
239
|
type Authentication<UserContext> = {
|
|
224
240
|
success: true;
|
|
225
241
|
userContext?: UserContext;
|
|
@@ -243,8 +259,9 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
243
259
|
onStart(): Promise<void>;
|
|
244
260
|
configureSession(session: Session): Session;
|
|
245
261
|
onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
|
|
246
|
-
|
|
247
|
-
|
|
262
|
+
protected _turn: CurrentTurnState<RequestContext, UserContext>;
|
|
263
|
+
protected createToolContextForCurrentTurn(requestContext?: RequestContext): ToolContext<RequestContext, UserContext>;
|
|
264
|
+
protected getToolContextForCurrentTurn(): ToolContext<RequestContext, UserContext> | undefined;
|
|
248
265
|
/**
|
|
249
266
|
* Merges the client request `body` into `experimental_context` for tools
|
|
250
267
|
* returned from {@link getTools} only (Think-internal tools are unchanged).
|
|
@@ -432,12 +449,69 @@ type MessageFeedback = {
|
|
|
432
449
|
updated_at: number;
|
|
433
450
|
};
|
|
434
451
|
//#endregion
|
|
452
|
+
//#region src/server/util/actions.d.ts
|
|
453
|
+
/** Tool execution options plus the additional context supplied by native Think actions. */
|
|
454
|
+
type ActionContext<Context extends Record<string, unknown> = Record<string, unknown>> = Omit<ToolExecutionOptions, "experimental_context"> & ActionContext$1 & {
|
|
455
|
+
experimental_context: Context; /** The action's resolved domain key, or its tool-call fallback when none was declared. */
|
|
456
|
+
idempotencyKey: string;
|
|
457
|
+
};
|
|
458
|
+
type Action<Context extends Record<string, unknown> = Record<string, unknown>, Input = any, Output = any> = Omit<ActionConfig<FlexibleSchema<Input>, Output>, "approval" | "execute" | "idempotencyKey" | "inputSchema" | "permissions"> & {
|
|
459
|
+
description: string;
|
|
460
|
+
inputSchema: FlexibleSchema<Input>;
|
|
461
|
+
approval?: ActionApprovalPolicy<Input>;
|
|
462
|
+
idempotencyKey?: ActionIdempotencyKey<Input>;
|
|
463
|
+
permissions?: ActionPermissionSpec<Input>;
|
|
464
|
+
authorize?: (ctx?: Context) => boolean;
|
|
465
|
+
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
466
|
+
execute(input: Input, ctx: ActionContext<Context>): Promise<Output> | Output;
|
|
467
|
+
};
|
|
468
|
+
type RegisteredAction<Context extends Record<string, unknown> = Record<string, unknown>, InputSchema extends FlexibleSchema = FlexibleSchema, Output = unknown> = Action$1<InputSchema, Output> & {
|
|
469
|
+
authorize?: (ctx?: Context) => boolean;
|
|
470
|
+
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
471
|
+
};
|
|
472
|
+
/** A native Think action with the SDK's standard authorization policy. */
|
|
473
|
+
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>;
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/server/util/skills.d.ts
|
|
476
|
+
interface Skill<Context extends Record<string, unknown> = Record<string, unknown>> {
|
|
477
|
+
name: string;
|
|
478
|
+
description: string;
|
|
479
|
+
instructions: string;
|
|
480
|
+
/** Enables direct invocation from chat with `/command`. */
|
|
481
|
+
command?: string;
|
|
482
|
+
tools?: Record<string, Tool<Context>>;
|
|
483
|
+
actions?: Record<string, RegisteredAction<Context, any, any>>;
|
|
484
|
+
authorize?: (ctx?: Context) => boolean;
|
|
485
|
+
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
486
|
+
}
|
|
487
|
+
interface SkillSource<Context extends Record<string, unknown> = Record<string, unknown>> extends SkillSource$1 {
|
|
488
|
+
command?: string;
|
|
489
|
+
tools?: Record<string, Tool<Context>>;
|
|
490
|
+
actions?: Record<string, RegisteredAction<Context, any, any>>;
|
|
491
|
+
authorize?: (ctx?: Context | undefined) => boolean;
|
|
492
|
+
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
493
|
+
}
|
|
494
|
+
declare function skill<Context extends Record<string, unknown> = Record<string, unknown>>(definition: Skill<Context>): SkillSource<Context>;
|
|
495
|
+
//#endregion
|
|
496
|
+
//#region src/server/util/skillCommands.d.ts
|
|
497
|
+
interface InvocableSkill {
|
|
498
|
+
/** Slash command without the leading `/`. */
|
|
499
|
+
command: string;
|
|
500
|
+
/** Name of the skill the command activates. */
|
|
501
|
+
name: string;
|
|
502
|
+
/** Human- and model-readable summary from the skill definition. */
|
|
503
|
+
description: string;
|
|
504
|
+
}
|
|
505
|
+
//#endregion
|
|
435
506
|
//#region src/server/agents/ChatAgent.d.ts
|
|
436
507
|
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> {
|
|
437
508
|
protected agentType: AgentConnectionType;
|
|
438
509
|
onStart(): Promise<void>;
|
|
439
510
|
configureSession(session: Session): Session;
|
|
440
511
|
beforeTurn(ctx: TurnContext): Promise<void | TurnConfig>;
|
|
512
|
+
private getSlashCommandSkill;
|
|
513
|
+
private invokeSkill;
|
|
514
|
+
protected getInvocableSkills(userContext?: UserContext | undefined, requestContext?: RequestContext): Promise<InvocableSkill[]>;
|
|
441
515
|
onChatResponse(result: ChatResponseResult): Promise<void>;
|
|
442
516
|
onRequest(request: Request): Promise<Response>;
|
|
443
517
|
/**
|
|
@@ -512,51 +586,10 @@ declare abstract class Assistant<State = unknown> extends Agent$1<Cloudflare.Env
|
|
|
512
586
|
observability?: Observability;
|
|
513
587
|
}
|
|
514
588
|
//#endregion
|
|
515
|
-
//#region src/server/util/actions.d.ts
|
|
516
|
-
/** Tool execution options plus the additional context supplied by native Think actions. */
|
|
517
|
-
type ActionContext<Context extends Record<string, unknown> = Record<string, unknown>> = Omit<ToolExecutionOptions, "experimental_context"> & ActionContext$1 & {
|
|
518
|
-
experimental_context: Context; /** The action's resolved domain key, or its tool-call fallback when none was declared. */
|
|
519
|
-
idempotencyKey: string;
|
|
520
|
-
};
|
|
521
|
-
type Action<Context extends Record<string, unknown> = Record<string, unknown>, Input = any, Output = any> = Omit<ActionConfig<FlexibleSchema<Input>, Output>, "approval" | "execute" | "idempotencyKey" | "inputSchema" | "permissions"> & {
|
|
522
|
-
description: string;
|
|
523
|
-
inputSchema: FlexibleSchema<Input>;
|
|
524
|
-
approval?: ActionApprovalPolicy<Input>;
|
|
525
|
-
idempotencyKey?: ActionIdempotencyKey<Input>;
|
|
526
|
-
permissions?: ActionPermissionSpec<Input>;
|
|
527
|
-
authorize?: (ctx?: Context) => boolean;
|
|
528
|
-
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
529
|
-
execute(input: Input, ctx: ActionContext<Context>): Promise<Output> | Output;
|
|
530
|
-
};
|
|
531
|
-
type RegisteredAction<Context extends Record<string, unknown> = Record<string, unknown>, InputSchema extends FlexibleSchema = FlexibleSchema, Output = unknown> = Action$1<InputSchema, Output> & {
|
|
532
|
-
authorize?: (ctx?: Context) => boolean;
|
|
533
|
-
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
534
|
-
};
|
|
535
|
-
/** A native Think action with the SDK's standard authorization policy. */
|
|
536
|
-
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>;
|
|
537
|
-
//#endregion
|
|
538
|
-
//#region src/server/util/skills.d.ts
|
|
539
|
-
interface Skill<Context extends Record<string, unknown> = Record<string, unknown>> {
|
|
540
|
-
name: string;
|
|
541
|
-
description: string;
|
|
542
|
-
instructions: string;
|
|
543
|
-
tools?: Record<string, Tool<Context>>;
|
|
544
|
-
actions?: Record<string, RegisteredAction<Context, any, any>>;
|
|
545
|
-
authorize?: (ctx?: Context) => boolean;
|
|
546
|
-
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
547
|
-
}
|
|
548
|
-
interface SkillSource<Context extends Record<string, unknown> = Record<string, unknown>> extends SkillSource$1 {
|
|
549
|
-
tools?: Record<string, Tool<Context>>;
|
|
550
|
-
actions?: Record<string, RegisteredAction<Context, any, any>>;
|
|
551
|
-
authorize?: (ctx?: Context | undefined) => boolean;
|
|
552
|
-
onUnauthorized?: UnauthorizedPolicy<Context>;
|
|
553
|
-
}
|
|
554
|
-
declare function skill<Context extends Record<string, unknown> = Record<string, unknown>>(definition: Skill<Context>): SkillSource<Context>;
|
|
555
|
-
//#endregion
|
|
556
589
|
//#region src/server/route-agent-request.d.ts
|
|
557
590
|
declare function routeAgentRequest<Env>(request: Request, env: Env, options?: AgentOptions<Env>): Promise<Response | null>;
|
|
558
591
|
//#endregion
|
|
559
592
|
//#region src/server/index.d.ts
|
|
560
593
|
declare function getCurrentToolContext(): any;
|
|
561
594
|
//#endregion
|
|
562
|
-
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 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 };
|
|
595
|
+
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
|
@@ -905,6 +905,19 @@ function toAnalyticsEnginePoints(event) {
|
|
|
905
905
|
0
|
|
906
906
|
]
|
|
907
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
|
+
}];
|
|
908
921
|
case "feedback.submitted": return [{
|
|
909
922
|
indexes,
|
|
910
923
|
blobs: [
|
|
@@ -998,6 +1011,14 @@ var Analytics = class {
|
|
|
998
1011
|
rating
|
|
999
1012
|
});
|
|
1000
1013
|
}
|
|
1014
|
+
skillInvoked(skillName, command) {
|
|
1015
|
+
this.emit({
|
|
1016
|
+
type: "skill.invoked",
|
|
1017
|
+
skillName,
|
|
1018
|
+
command,
|
|
1019
|
+
source: "slash-command"
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1001
1022
|
event(payload) {
|
|
1002
1023
|
this.emit(payload);
|
|
1003
1024
|
}
|
|
@@ -1137,13 +1158,17 @@ var Agent = class extends Think {
|
|
|
1137
1158
|
authorizedSkillNames: /* @__PURE__ */ new Set(),
|
|
1138
1159
|
baseSystem: ""
|
|
1139
1160
|
};
|
|
1140
|
-
|
|
1161
|
+
createToolContextForCurrentTurn(requestContext = {}) {
|
|
1141
1162
|
const connectionState = getCurrentAgent().connection?.state;
|
|
1142
1163
|
return {
|
|
1164
|
+
...this._turn.context,
|
|
1143
1165
|
...requestContext,
|
|
1144
1166
|
_userContext: connectionState?.userContext ?? this.userContext
|
|
1145
1167
|
};
|
|
1146
1168
|
}
|
|
1169
|
+
getToolContextForCurrentTurn() {
|
|
1170
|
+
return this._turn.context;
|
|
1171
|
+
}
|
|
1147
1172
|
/**
|
|
1148
1173
|
* Merges the client request `body` into `experimental_context` for tools
|
|
1149
1174
|
* returned from {@link getTools} only (Think-internal tools are unchanged).
|
|
@@ -1154,14 +1179,16 @@ var Agent = class extends Think {
|
|
|
1154
1179
|
async beforeTurn(ctx) {
|
|
1155
1180
|
this.analytics.promptReceived(ctx.messages, ctx.continuation);
|
|
1156
1181
|
this.registerInstanceActivity();
|
|
1157
|
-
const body = await this._applyTokenExchange(
|
|
1158
|
-
|
|
1182
|
+
const body = await this._applyTokenExchange({
|
|
1183
|
+
...this._turn.context,
|
|
1184
|
+
...ctx.body
|
|
1185
|
+
});
|
|
1186
|
+
this._turn.context = this.createToolContextForCurrentTurn(body);
|
|
1159
1187
|
this._turn.inputTools = ctx.tools;
|
|
1160
1188
|
const activeSkillName = this._getLastActivatedSkillName(ctx.messages);
|
|
1161
1189
|
const { tools, activeTools } = await this._prepareTools(ctx.tools, activeSkillName, true);
|
|
1162
1190
|
const baseSystem = await this.session.refreshSystemPrompt();
|
|
1163
1191
|
this._turn.baseSystem = [baseSystem, this._turn.skillsSystemBlock].filter(Boolean).join("\n\n");
|
|
1164
|
-
console.log(this._turn.baseSystem, "\n\n// end system prompt\n\n");
|
|
1165
1192
|
return {
|
|
1166
1193
|
system: this._turn.baseSystem,
|
|
1167
1194
|
model: this.getModel(this._turn.context),
|
|
@@ -2338,6 +2365,38 @@ var Assistant = class extends Agent$1 {
|
|
|
2338
2365
|
agentClass: this.constructor.name
|
|
2339
2366
|
}) });
|
|
2340
2367
|
};
|
|
2368
|
+
//#endregion
|
|
2369
|
+
//#region src/server/util/skillCommands.ts
|
|
2370
|
+
async function listInvocableSkills(sources) {
|
|
2371
|
+
const commands = /* @__PURE__ */ new Set();
|
|
2372
|
+
const skills = [];
|
|
2373
|
+
for (const source of sources) {
|
|
2374
|
+
if (!source.command) continue;
|
|
2375
|
+
if (commands.has(source.command)) throw new Error(`Duplicate skill command "/${source.command}"`);
|
|
2376
|
+
const descriptor = (await source.list()).find((skill) => skill.name === source.id);
|
|
2377
|
+
if (!descriptor) continue;
|
|
2378
|
+
commands.add(source.command);
|
|
2379
|
+
skills.push({
|
|
2380
|
+
command: source.command,
|
|
2381
|
+
name: descriptor.name,
|
|
2382
|
+
description: descriptor.description
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
return skills;
|
|
2386
|
+
}
|
|
2387
|
+
function parseSkillCommand(messages, skills) {
|
|
2388
|
+
const message = messages.findLast((candidate) => candidate.role === "user");
|
|
2389
|
+
if (!message) return;
|
|
2390
|
+
const text = getMessageText(message);
|
|
2391
|
+
if (!text?.startsWith("/")) return;
|
|
2392
|
+
const match = /^\/([^\s]+)/.exec(text);
|
|
2393
|
+
if (!match) return;
|
|
2394
|
+
return skills.find((skill) => skill.command === match[1]);
|
|
2395
|
+
}
|
|
2396
|
+
function getMessageText(message) {
|
|
2397
|
+
if (typeof message.content === "string") return message.content;
|
|
2398
|
+
return message.content.filter((part) => part.type === "text").map((part) => part.text).join("") || void 0;
|
|
2399
|
+
}
|
|
2341
2400
|
const COMPACTION_MAX_OUTPUT_TOKENS = 2e3;
|
|
2342
2401
|
const createCompactFn = (model) => createCompactFunction({
|
|
2343
2402
|
protectHead: 2,
|
|
@@ -2675,7 +2734,22 @@ var ChatAgent = class extends Agent {
|
|
|
2675
2734
|
async beforeTurn(ctx) {
|
|
2676
2735
|
const parent = await this.getParentAgent();
|
|
2677
2736
|
if (parent?.registerChat) await parent.registerChat(this.name);
|
|
2678
|
-
const
|
|
2737
|
+
const slashCommandSkill = await this.getSlashCommandSkill(ctx);
|
|
2738
|
+
let skillInvocation;
|
|
2739
|
+
if (slashCommandSkill) skillInvocation = await this.invokeSkill(ctx, slashCommandSkill);
|
|
2740
|
+
const turnContext = skillInvocation ? {
|
|
2741
|
+
...ctx,
|
|
2742
|
+
messages: [...ctx.messages, ...skillInvocation.messages]
|
|
2743
|
+
} : ctx;
|
|
2744
|
+
let config = await super.beforeTurn(turnContext) ?? {};
|
|
2745
|
+
if (skillInvocation) {
|
|
2746
|
+
const transforms = config.experimental_transform ? Array.isArray(config.experimental_transform) ? config.experimental_transform : [config.experimental_transform] : [];
|
|
2747
|
+
config = {
|
|
2748
|
+
...config,
|
|
2749
|
+
activeTools: config.activeTools?.filter((name) => name !== "activate_skill"),
|
|
2750
|
+
experimental_transform: [skillInvocation.transform, ...transforms]
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2679
2753
|
try {
|
|
2680
2754
|
const prepared = await prepareModelHistory(this.messages, getModelCompaction(this.sql.bind(this)), createCompactFn(config.model ?? ctx.model), config.system ?? ctx.system);
|
|
2681
2755
|
if (prepared.stateChanged) if (prepared.state) setModelCompaction(this.sql.bind(this), prepared.state);
|
|
@@ -2691,7 +2765,7 @@ var ChatAgent = class extends Agent {
|
|
|
2691
2765
|
});
|
|
2692
2766
|
return {
|
|
2693
2767
|
...config,
|
|
2694
|
-
messages: this.pruneTurnMessages(modelMessages)
|
|
2768
|
+
messages: this.pruneTurnMessages([...modelMessages, ...skillInvocation?.messages ?? []])
|
|
2695
2769
|
};
|
|
2696
2770
|
}
|
|
2697
2771
|
} catch (error) {
|
|
@@ -2699,6 +2773,67 @@ var ChatAgent = class extends Agent {
|
|
|
2699
2773
|
}
|
|
2700
2774
|
return config;
|
|
2701
2775
|
}
|
|
2776
|
+
async getSlashCommandSkill(context) {
|
|
2777
|
+
if (context.continuation) return;
|
|
2778
|
+
const connectionState = getCurrentAgent()?.connection?.state;
|
|
2779
|
+
const invocableSkills = await this.getInvocableSkills(connectionState?.userContext ?? this.userContext, context.body);
|
|
2780
|
+
return parseSkillCommand(context.messages, invocableSkills);
|
|
2781
|
+
}
|
|
2782
|
+
async invokeSkill(context, skill) {
|
|
2783
|
+
const activateSkill = context.tools.activate_skill;
|
|
2784
|
+
if (!activateSkill?.execute) return;
|
|
2785
|
+
const input = { name: skill.name };
|
|
2786
|
+
const toolCallId = crypto.randomUUID();
|
|
2787
|
+
const output = await activateSkill.execute(input, {
|
|
2788
|
+
toolCallId,
|
|
2789
|
+
messages: context.messages
|
|
2790
|
+
});
|
|
2791
|
+
if (typeof output !== "string") throw new Error("activate_skill returned an invalid result");
|
|
2792
|
+
this.analytics.skillInvoked(skill.name, skill.command);
|
|
2793
|
+
const messages = await convertToModelMessages([{
|
|
2794
|
+
id: crypto.randomUUID(),
|
|
2795
|
+
role: "assistant",
|
|
2796
|
+
parts: [{
|
|
2797
|
+
type: "tool-activate_skill",
|
|
2798
|
+
toolCallId,
|
|
2799
|
+
state: "output-available",
|
|
2800
|
+
input,
|
|
2801
|
+
output
|
|
2802
|
+
}]
|
|
2803
|
+
}], { tools: context.tools });
|
|
2804
|
+
const transform = () => {
|
|
2805
|
+
let emitted = false;
|
|
2806
|
+
return new TransformStream({ transform(chunk, controller) {
|
|
2807
|
+
controller.enqueue(chunk);
|
|
2808
|
+
if (emitted || chunk.type !== "start-step") return;
|
|
2809
|
+
emitted = true;
|
|
2810
|
+
controller.enqueue({
|
|
2811
|
+
type: "tool-call",
|
|
2812
|
+
toolCallId,
|
|
2813
|
+
toolName: "activate_skill",
|
|
2814
|
+
input
|
|
2815
|
+
});
|
|
2816
|
+
controller.enqueue({
|
|
2817
|
+
type: "tool-result",
|
|
2818
|
+
toolCallId,
|
|
2819
|
+
toolName: "activate_skill",
|
|
2820
|
+
input,
|
|
2821
|
+
output
|
|
2822
|
+
});
|
|
2823
|
+
} });
|
|
2824
|
+
};
|
|
2825
|
+
return {
|
|
2826
|
+
messages,
|
|
2827
|
+
transform
|
|
2828
|
+
};
|
|
2829
|
+
}
|
|
2830
|
+
async getInvocableSkills(userContext = this.userContext, requestContext = {}) {
|
|
2831
|
+
const context = {
|
|
2832
|
+
...requestContext,
|
|
2833
|
+
_userContext: userContext
|
|
2834
|
+
};
|
|
2835
|
+
return listInvocableSkills((await this.getSkills()).filter((source) => source.authorize?.(context) !== false));
|
|
2836
|
+
}
|
|
2702
2837
|
async onChatResponse(result) {
|
|
2703
2838
|
this.analytics.turnCompleted(result.requestId, result.status, result.continuation, result.message.parts);
|
|
2704
2839
|
const parent = await this.getParentAgent();
|
|
@@ -2709,6 +2844,7 @@ var ChatAgent = class extends Agent {
|
|
|
2709
2844
|
if (!auth.success) return Response.json({ error: auth.message }, { status: auth.status });
|
|
2710
2845
|
const action = new URL(request.url).pathname.split("/").pop();
|
|
2711
2846
|
if (action === "getMessages" && request.method === "GET") return Response.json(await this.getMessages());
|
|
2847
|
+
if (action === "getInvocableSkills" && request.method === "GET") return Response.json(await this.getInvocableSkills(auth.userContext));
|
|
2712
2848
|
if (action === "sendMessage" && request.method === "POST") return this.streamMessage(request);
|
|
2713
2849
|
if (action === "cancelChat" && request.method === "POST") {
|
|
2714
2850
|
const { requestId } = await request.json();
|
|
@@ -2722,38 +2858,48 @@ var ChatAgent = class extends Agent {
|
|
|
2722
2858
|
* returned immediately and each event reaches the client as it is produced.
|
|
2723
2859
|
*/
|
|
2724
2860
|
async streamMessage(request) {
|
|
2725
|
-
let
|
|
2861
|
+
let payload;
|
|
2726
2862
|
try {
|
|
2727
|
-
|
|
2863
|
+
payload = await request.json();
|
|
2728
2864
|
} catch {
|
|
2729
|
-
return Response.json({ error: "
|
|
2865
|
+
return Response.json({ error: "Request body must be valid JSON" }, { status: 400 });
|
|
2730
2866
|
}
|
|
2731
|
-
if (typeof
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2867
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return Response.json({ error: "Request body must be an object" }, { status: 400 });
|
|
2868
|
+
let { message, context } = payload;
|
|
2869
|
+
if (context !== void 0 && (context === null || typeof context !== "object" || Array.isArray(context))) return Response.json({ error: "Context must be an object" }, { status: 400 });
|
|
2870
|
+
if (typeof message === "string") {
|
|
2871
|
+
if (!message.trim()) return Response.json({ error: "Message must not be empty" }, { status: 400 });
|
|
2872
|
+
message = {
|
|
2873
|
+
id: crypto.randomUUID(),
|
|
2874
|
+
role: "user",
|
|
2875
|
+
parts: [{
|
|
2876
|
+
type: "text",
|
|
2877
|
+
text: message
|
|
2878
|
+
}]
|
|
2879
|
+
};
|
|
2880
|
+
} 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 });
|
|
2881
|
+
const input = context ? () => {
|
|
2882
|
+
this._turn.context = this.createToolContextForCurrentTurn(context);
|
|
2883
|
+
return [message];
|
|
2884
|
+
} : message;
|
|
2740
2885
|
const encoder = new TextEncoder();
|
|
2741
2886
|
const readable = new ReadableStream({ start: async (controller) => {
|
|
2742
2887
|
const send = (event, data) => controller.enqueue(encoder.encode(`event: ${event}\ndata: ${data}\n\n`));
|
|
2888
|
+
const sendError = (error) => send("error", JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
2743
2889
|
try {
|
|
2744
2890
|
await this.runTurn({
|
|
2745
2891
|
mode: "stream",
|
|
2746
|
-
input
|
|
2892
|
+
input,
|
|
2747
2893
|
callback: {
|
|
2748
2894
|
onStart: (event) => send("start", JSON.stringify(event)),
|
|
2749
2895
|
onEvent: (json) => send("chunk", json),
|
|
2750
2896
|
onDone: () => send("done", "{}"),
|
|
2751
|
-
onError:
|
|
2897
|
+
onError: sendError,
|
|
2752
2898
|
onInterrupted: () => send("interrupted", "{}")
|
|
2753
2899
|
}
|
|
2754
2900
|
});
|
|
2755
2901
|
} catch (error) {
|
|
2756
|
-
|
|
2902
|
+
sendError(error);
|
|
2757
2903
|
} finally {
|
|
2758
2904
|
controller.close();
|
|
2759
2905
|
}
|
|
@@ -2828,6 +2974,7 @@ function skill(definition) {
|
|
|
2828
2974
|
if (!definition.name) throw new Error("Skill name is required");
|
|
2829
2975
|
if (!definition.description) throw new Error("Skill description is required");
|
|
2830
2976
|
if (!definition.instructions) throw new Error("Skill content is required");
|
|
2977
|
+
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");
|
|
2831
2978
|
return {
|
|
2832
2979
|
id: definition.name,
|
|
2833
2980
|
fingerprint: definition.name,
|
|
@@ -2845,6 +2992,7 @@ function skill(definition) {
|
|
|
2845
2992
|
body: definition.instructions
|
|
2846
2993
|
};
|
|
2847
2994
|
},
|
|
2995
|
+
command: definition.command,
|
|
2848
2996
|
tools: definition.tools,
|
|
2849
2997
|
actions: definition.actions,
|
|
2850
2998
|
authorize: definition.authorize,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@economic/agents",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.1",
|
|
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"
|