@economic/agents 2.10.6 → 2.11.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 +86 -8
- package/dist/index.d.mts +48 -7
- package/dist/index.mjs +271 -135
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -301,6 +301,17 @@ getTools(): ToolSet {
|
|
|
301
301
|
|
|
302
302
|
`authorize` returning `false` hides the tool for that request.
|
|
303
303
|
|
|
304
|
+
Use `approval` when an ordinary tool needs the AI SDK's basic approval gate. The SDK translates it to the upstream `needsApproval` option; `needsApproval` remains supported for backwards compatibility, but do not supply both.
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
const export_report = tool({
|
|
308
|
+
description: "Export a report",
|
|
309
|
+
inputSchema: z.object({ reportId: z.string() }),
|
|
310
|
+
approval: true,
|
|
311
|
+
execute: async ({ reportId }) => exportReport(reportId),
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
304
315
|
### Tool context
|
|
305
316
|
|
|
306
317
|
The tool context is the second argument to `execute` — `experimental_context`:
|
|
@@ -325,6 +336,72 @@ const call_api = tool({
|
|
|
325
336
|
|
|
326
337
|
`ToolContext<RequestContext, UserContext>` is `RequestContext & { _userContext?: UserContext }`. Request context comes from the client (`toolContext` in the hooks); `_userContext` comes from [`getUserContext`](#user-context) when using JWT Authentication (via `getJwtAuthConfig`).
|
|
327
338
|
|
|
339
|
+
## Actions
|
|
340
|
+
|
|
341
|
+
Use `action()` instead of `tool()` for operations with side effects. Actions use Cloudflare Think's durable action ledger for idempotent execution, settled-result replay, pending-action handling, recovery, timeouts, cancellation, and structured errors.
|
|
342
|
+
|
|
343
|
+
The SDK enables approval by default. `approval` may be `true`, `false`, or a predicate; `approvalSummary` and `approvalRisk` provide the metadata shown by an approval UI.
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { action, skill, type ToolContext } from "@economic/agents";
|
|
347
|
+
import { z } from "zod";
|
|
348
|
+
|
|
349
|
+
interface RequestContext extends Record<string, unknown> {
|
|
350
|
+
tokens: Record<string, string>;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const deleteInput = z.object({ invoiceId: z.string() });
|
|
354
|
+
|
|
355
|
+
const deleteInvoice = action<ToolContext<RequestContext>, typeof deleteInput>({
|
|
356
|
+
description: "Delete a draft invoice",
|
|
357
|
+
inputSchema: deleteInput,
|
|
358
|
+
|
|
359
|
+
// This identifies the business operation across retries. Do not use a
|
|
360
|
+
// timestamp, random value, or request id.
|
|
361
|
+
idempotencyKey: ({ input }) => `invoice:${input.invoiceId}:delete`,
|
|
362
|
+
|
|
363
|
+
approvalSummary: "Delete draft invoice",
|
|
364
|
+
approvalRisk: "high",
|
|
365
|
+
|
|
366
|
+
execute: async (input, ctx) => {
|
|
367
|
+
const response = await fetch(`/api/invoices/${input.invoiceId}`, {
|
|
368
|
+
method: "DELETE",
|
|
369
|
+
headers: {
|
|
370
|
+
Authorization: ctx.experimental_context.tokens.Authorization ?? "",
|
|
371
|
+
// Think uses this key for its ledger. Forward it when the downstream
|
|
372
|
+
// API also supports idempotency.
|
|
373
|
+
"Idempotency-Key": ctx.idempotencyKey,
|
|
374
|
+
},
|
|
375
|
+
signal: ctx.signal,
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
return { deleted: response.ok };
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
export const invoiceSkill = skill({
|
|
383
|
+
name: "invoices",
|
|
384
|
+
description: "View and manage invoices.",
|
|
385
|
+
instructions: "Use delete_draft_invoice only for draft invoices.",
|
|
386
|
+
actions: {
|
|
387
|
+
delete_draft_invoice: deleteInvoice,
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
An action receives the same tool execution data plus Cloudflare's native action fields:
|
|
393
|
+
|
|
394
|
+
- `experimental_context` — the typed `ToolContext` supplied to ordinary tools.
|
|
395
|
+
- `toolCallId`, `messages`, and `abortSignal` — the ordinary tool execution fields.
|
|
396
|
+
- `idempotencyKey` — the resolved domain key, or the tool-call fallback when none was declared.
|
|
397
|
+
- `requestId`, `signal`, `agent`, `env`, and `attachReply` — native Think action fields.
|
|
398
|
+
|
|
399
|
+
`authorize` and `onUnauthorized` behave exactly as they do on `tool()`. Native action options such as `permissions`, `kind`, `timeoutMs`, `approval`, `approvalSummary`, and `approvalRisk` pass through unchanged.
|
|
400
|
+
|
|
401
|
+
Define an explicit `idempotencyKey` for actions that must deduplicate across recovery retries. Without one, Think falls back to `toolCallId`, which only deduplicates that particular tool call. The action ledger does not make an external API idempotent: forward `ctx.idempotencyKey` to the API, adapting its format if required. If one action performs several writes, derive a stable child key for each write rather than reusing one downstream key.
|
|
402
|
+
|
|
403
|
+
Cloudflare currently marks Think Actions as experimental, so this SDK surface may evolve with the upstream API.
|
|
404
|
+
|
|
328
405
|
## Skills
|
|
329
406
|
|
|
330
407
|
The SDK supports two skill styles:
|
|
@@ -336,7 +413,7 @@ Use skills when a capability should be loaded on demand instead of living in the
|
|
|
336
413
|
|
|
337
414
|
### Code-defined skills
|
|
338
415
|
|
|
339
|
-
A code-defined skill bundles markdown `instructions` with optional tools. Only `description` sits in the system prompt; the model loads the instructions and
|
|
416
|
+
A code-defined skill bundles markdown `instructions` with optional tools and actions. Only `description` sits in the system prompt; the model loads the instructions, tools, and actions on demand.
|
|
340
417
|
|
|
341
418
|
```typescript
|
|
342
419
|
import { skill, tool } from "@economic/agents";
|
|
@@ -583,13 +660,14 @@ Imported from `@economic/agents` unless noted.
|
|
|
583
660
|
|
|
584
661
|
### Helpers and types
|
|
585
662
|
|
|
586
|
-
| Export | Description
|
|
587
|
-
| ------------------------------------------------------------------------ |
|
|
588
|
-
| `tool(def)` / `Tool` / `ToolSet` | Define a tool with
|
|
589
|
-
| `
|
|
590
|
-
| `
|
|
591
|
-
| `
|
|
592
|
-
| `
|
|
663
|
+
| Export | Description |
|
|
664
|
+
| ------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
|
|
665
|
+
| `tool(def)` / `Tool` / `ToolSet` | Define a tool with optional `approval`, `authorize`, and `onUnauthorized`. |
|
|
666
|
+
| `action(def)` / `Action` / `ActionConfig` / `ActionContext` | Define a ledger-backed action with approval and idempotency support. |
|
|
667
|
+
| `skill(def)` / `Skill` | Define a skill with optional `tools`, `actions`, and authorization. |
|
|
668
|
+
| `ToolContext` | `RequestContext & { _userContext?: UserContext }`. |
|
|
669
|
+
| `AgentEnv` | The bindings the runtime expects. |
|
|
670
|
+
| `AgentConnectionState` / `AgentConnectionStatus` / `AgentConnectionType` | Connection state shared with the client. |
|
|
593
671
|
|
|
594
672
|
### Members
|
|
595
673
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Agent as Agent$1, AgentOptions, Connection, ConnectionContext, SubAgentClass } from "agents";
|
|
2
|
-
import { ChatResponseResult, PrepareStepContext, Session, Think, ToolCallContext, ToolCallDecision, TurnConfig, TurnContext, skills } from "@cloudflare/think";
|
|
3
|
-
import { JSONValue, LanguageModel, ModelMessage, Tool as Tool$1, UIMessage } from "ai";
|
|
2
|
+
import { Action as Action$1, ActionConfig as ActionConfig$1, ActionContext as ActionContext$1, ChatResponseResult, PrepareStepContext, Session, Think, ToolCallContext, ToolCallDecision, TurnConfig, TurnContext, skills } from "@cloudflare/think";
|
|
3
|
+
import { FlexibleSchema, InferSchema, JSONValue, LanguageModel, ModelMessage, Tool as Tool$1, ToolExecutionOptions, UIMessage } from "ai";
|
|
4
4
|
import { Observability } from "agents/observability";
|
|
5
5
|
import { createCompactFunction } from "agents/experimental/memory/utils";
|
|
6
6
|
import { SkillSource as SkillSource$1 } from "agents/skills";
|
|
@@ -110,10 +110,11 @@ interface UnauthorizedToolPolicy<Context extends Record<string, unknown> = Recor
|
|
|
110
110
|
reason: string | ((ctx?: Context) => string);
|
|
111
111
|
}
|
|
112
112
|
type Tool<Context extends Record<string, unknown> = Record<string, unknown>, INPUT extends JSONValue | unknown | never = any, OUTPUT extends JSONValue | unknown | never = any> = Tool$1<INPUT, OUTPUT> & {
|
|
113
|
+
/** SDK-consistent alias for the AI SDK's `needsApproval` option. */approval?: Tool$1<INPUT, OUTPUT>["needsApproval"];
|
|
113
114
|
authorize?: (ctx?: Context) => boolean;
|
|
114
115
|
onUnauthorized?: UnauthorizedToolPolicy<Context>;
|
|
115
116
|
};
|
|
116
|
-
declare function tool<Context extends Record<string, unknown> = Record<string, unknown>, INPUT extends JSONValue | unknown | never = any, OUTPUT extends JSONValue | unknown | never = any>(
|
|
117
|
+
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>;
|
|
117
118
|
//#endregion
|
|
118
119
|
//#region src/server/agents/Agent.d.ts
|
|
119
120
|
/** What the model would see on its next step — returned by {@link Agent.inspectTurn}. */
|
|
@@ -125,6 +126,14 @@ interface TurnSnapshot {
|
|
|
125
126
|
frameworkToolNames: string[];
|
|
126
127
|
messages: ModelMessage[];
|
|
127
128
|
}
|
|
129
|
+
type Authentication<UserContext> = {
|
|
130
|
+
success: true;
|
|
131
|
+
userContext?: UserContext;
|
|
132
|
+
} | {
|
|
133
|
+
success: false;
|
|
134
|
+
status: 401 | 403 | 500;
|
|
135
|
+
message: string;
|
|
136
|
+
};
|
|
128
137
|
declare abstract class Agent<RequestContext extends Record<string, unknown> = Record<string, unknown>, UserContext extends Record<string, unknown> = Record<string, unknown>, State = unknown> extends Think<Cloudflare.Env & AgentEnv, State> {
|
|
129
138
|
protected agentType: AgentConnectionType;
|
|
130
139
|
protected clientIp?: string;
|
|
@@ -166,6 +175,8 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
166
175
|
}>;
|
|
167
176
|
beforeToolCall(ctx: ToolCallContext): Promise<ToolCallDecision | void>;
|
|
168
177
|
getTools(): ToolSet;
|
|
178
|
+
/** Collects the actions declared by this agent's skills for Think to compile. */
|
|
179
|
+
getActions(): Promise<Record<string, Action$1>>;
|
|
169
180
|
/**
|
|
170
181
|
* Read-only: what the model would see on its next step for the given history
|
|
171
182
|
* — the assembled system prompt, the skill-gated toolset, and pruned
|
|
@@ -215,8 +226,10 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
215
226
|
*/
|
|
216
227
|
private _withTurnContextFallback;
|
|
217
228
|
private _getLastActivatedSkillName;
|
|
229
|
+
/** Verifies the request and prepares the same user context for WebSocket and HTTP requests. */
|
|
230
|
+
protected authenticate(request: Request): Promise<Authentication<UserContext>>;
|
|
218
231
|
/**
|
|
219
|
-
* Override to enable JWT authentication on WebSocket
|
|
232
|
+
* Override to enable JWT authentication on WebSocket and HTTP requests.
|
|
220
233
|
* Return the auth config based on the incoming request, or undefined to skip auth.
|
|
221
234
|
* Do not add side effects to this method - return a single JwtAuthConfig from it.
|
|
222
235
|
*
|
|
@@ -231,8 +244,8 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
231
244
|
* When configured, the exchanged token is passed to {@link getUserContext}
|
|
232
245
|
* and injected into the tool context every turn under
|
|
233
246
|
* `tokens[config.tokenHeader]` — client-sent request-body tokens for that
|
|
234
|
-
* key are ignored.
|
|
235
|
-
*
|
|
247
|
+
* key are ignored. Each authenticated WebSocket connection or HTTP request
|
|
248
|
+
* re-verifies and re-exchanges the client token.
|
|
236
249
|
*
|
|
237
250
|
* Do not add side effects to this method - return a single config from it,
|
|
238
251
|
* or undefined to disable the exchange.
|
|
@@ -323,6 +336,12 @@ declare abstract class ChatAgent<RequestContext extends Record<string, unknown>
|
|
|
323
336
|
configureSession(session: Session): Session;
|
|
324
337
|
beforeTurn(ctx: TurnContext): Promise<void | TurnConfig>;
|
|
325
338
|
onChatResponse(result: ChatResponseResult): Promise<void>;
|
|
339
|
+
onRequest(request: Request): Promise<Response>;
|
|
340
|
+
/**
|
|
341
|
+
* Runs the turn from the stream's start callback so the response can be
|
|
342
|
+
* returned immediately and each event reaches the client as it is produced.
|
|
343
|
+
*/
|
|
344
|
+
private streamMessage;
|
|
326
345
|
/**
|
|
327
346
|
* Submit feedback for a message by its id.
|
|
328
347
|
* @param messageId - The id of the message to give feedback on.
|
|
@@ -357,6 +376,8 @@ declare abstract class Assistant<State = unknown> extends Agent$1<Cloudflare.Env
|
|
|
357
376
|
protected abstract agent: SubAgentClass<ChatAgent>;
|
|
358
377
|
onStart(): void;
|
|
359
378
|
onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
|
|
379
|
+
private authenticate;
|
|
380
|
+
onRequest(request: Request): Promise<Response>;
|
|
360
381
|
createChat(): Promise<string>;
|
|
361
382
|
deleteChat(id: string): Promise<void>;
|
|
362
383
|
renameChat(id: string, title: string): Promise<void>;
|
|
@@ -388,16 +409,36 @@ declare abstract class Assistant<State = unknown> extends Agent$1<Cloudflare.Env
|
|
|
388
409
|
observability?: Observability;
|
|
389
410
|
}
|
|
390
411
|
//#endregion
|
|
412
|
+
//#region src/server/util/actions.d.ts
|
|
413
|
+
/** Tool execution options plus the additional context supplied by native Think actions. */
|
|
414
|
+
type ActionContext<Context extends Record<string, unknown> = Record<string, unknown>> = Omit<ToolExecutionOptions, "experimental_context"> & ActionContext$1 & {
|
|
415
|
+
experimental_context: Context; /** The action's resolved domain key, or its tool-call fallback when none was declared. */
|
|
416
|
+
idempotencyKey: string;
|
|
417
|
+
};
|
|
418
|
+
type ActionConfig<Context extends Record<string, unknown>, InputSchema extends FlexibleSchema, Output = unknown> = Omit<ActionConfig$1<InputSchema, Output>, "execute"> & {
|
|
419
|
+
authorize?: (ctx?: Context) => boolean;
|
|
420
|
+
onUnauthorized?: UnauthorizedToolPolicy<Context>;
|
|
421
|
+
execute(input: InferSchema<InputSchema>, ctx: ActionContext<Context>): Promise<Output> | Output;
|
|
422
|
+
};
|
|
423
|
+
type Action<Context extends Record<string, unknown> = Record<string, unknown>, InputSchema extends FlexibleSchema = FlexibleSchema, Output = unknown> = Action$1<InputSchema, Output> & {
|
|
424
|
+
authorize?: (ctx?: Context) => boolean;
|
|
425
|
+
onUnauthorized?: UnauthorizedToolPolicy<Context>;
|
|
426
|
+
};
|
|
427
|
+
/** A native Think action with the SDK's standard authorization policy. */
|
|
428
|
+
declare function action<Context extends Record<string, unknown> = Record<string, unknown>, const InputSchema extends FlexibleSchema = FlexibleSchema, Output = unknown>(definition: ActionConfig<Context, InputSchema, Output>): Action<Context, InputSchema, Output>;
|
|
429
|
+
//#endregion
|
|
391
430
|
//#region src/server/util/skills.d.ts
|
|
392
431
|
interface Skill<Context extends Record<string, unknown> = Record<string, unknown>> {
|
|
393
432
|
name: string;
|
|
394
433
|
description: string;
|
|
395
434
|
instructions: string;
|
|
396
435
|
tools?: Record<string, Tool<Context>>;
|
|
436
|
+
actions?: Record<string, Action<Context, any, any>>;
|
|
397
437
|
authorize?: (ctx?: Context) => boolean;
|
|
398
438
|
}
|
|
399
439
|
interface SkillSource<Context extends Record<string, unknown> = Record<string, unknown>> extends SkillSource$1 {
|
|
400
440
|
tools?: Record<string, Tool<Context>>;
|
|
441
|
+
actions?: Record<string, Action<Context, any, any>>;
|
|
401
442
|
authorize?: (ctx?: Context | undefined) => boolean;
|
|
402
443
|
}
|
|
403
444
|
declare function skill<Context extends Record<string, unknown> = Record<string, unknown>>(definition: Skill<Context>): SkillSource<Context>;
|
|
@@ -408,4 +449,4 @@ declare function routeAgentRequest<Env>(request: Request, env: Env, options?: Ag
|
|
|
408
449
|
//#region src/server/index.d.ts
|
|
409
450
|
declare function getCurrentToolContext(): any;
|
|
410
451
|
//#endregion
|
|
411
|
-
export { Agent, type AgentConnectionStatus, type AgentConnectionType, type AgentEnv, Assistant, ChatAgent, type ExchangedToken, type JwtAuthConfig, type Skill, type SkillSource, type TokenExchangeConfig, type TokenExchangeResult, type Tool, type ToolContext, type ToolSet, type TurnSnapshot, type UnauthorizedToolPolicy, createJwtAuthConfig, exchangeToken, getCurrentToolContext, routeAgentRequest, skill, tool };
|
|
452
|
+
export { type Action, type ActionConfig, type ActionContext, Agent, type AgentConnectionStatus, type AgentConnectionType, type AgentEnv, Assistant, ChatAgent, type ExchangedToken, type JwtAuthConfig, type Skill, type SkillSource, type TokenExchangeConfig, type TokenExchangeResult, type Tool, type ToolContext, type ToolSet, type TurnSnapshot, type UnauthorizedToolPolicy, action, createJwtAuthConfig, exchangeToken, getCurrentToolContext, routeAgentRequest, skill, tool };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Agent as Agent$1, callable, getCurrentAgent, routeAgentRequest as routeAgentRequest$1 } from "agents";
|
|
2
|
-
import { Think, skills } from "@cloudflare/think";
|
|
2
|
+
import { Think, action as action$1, skills } from "@cloudflare/think";
|
|
3
3
|
import { Output, convertToModelMessages, generateText, jsonSchema, pruneMessages, tool as tool$1 } from "ai";
|
|
4
4
|
import { createRemoteJWKSet, decodeJwt, errors, jwtVerify } from "jose";
|
|
5
5
|
import { BasicTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
@@ -243,39 +243,6 @@ function withExchangedToken(body, tokenHeader, accessToken) {
|
|
|
243
243
|
};
|
|
244
244
|
}
|
|
245
245
|
/**
|
|
246
|
-
* Connect-time exchange. On failure, signals the client and closes the
|
|
247
|
-
* connection — `unauthorized`/4001 when the session token itself was rejected
|
|
248
|
-
* (only a re-login fixes that), `error`/1011 for identity-server or
|
|
249
|
-
* configuration faults — and returns undefined so the caller abandons the
|
|
250
|
-
* connect.
|
|
251
|
-
*/
|
|
252
|
-
async function exchangeConnectToken(subjectToken, config, connection) {
|
|
253
|
-
let result;
|
|
254
|
-
try {
|
|
255
|
-
result = await exchangeToken(subjectToken, config);
|
|
256
|
-
} catch (error) {
|
|
257
|
-
sendConnectionStatus(connection, "error");
|
|
258
|
-
console.error(`[Agent] Token exchange error - ${error}`);
|
|
259
|
-
connection.close(1011, "Token exchange failed");
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
if (!result.success) {
|
|
263
|
-
console.error(`[Agent] Token exchange failed - ${result.message}`);
|
|
264
|
-
if (result.subjectTokenRejected) {
|
|
265
|
-
sendConnectionStatus(connection, "unauthorized");
|
|
266
|
-
connection.close(4001, "Unauthorized");
|
|
267
|
-
} else {
|
|
268
|
-
sendConnectionStatus(connection, "error");
|
|
269
|
-
connection.close(1011, "Token exchange failed");
|
|
270
|
-
}
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
return {
|
|
274
|
-
accessToken: result.accessToken,
|
|
275
|
-
expiresAt: result.expiresAt
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
246
|
* Turn-time injection: returns a body copy with the held token under
|
|
280
247
|
* `tokens[config.tokenHeader]`. When the held token is missing or expired the
|
|
281
248
|
* turn cannot run — only a re-authenticating reconnect delivers a fresh
|
|
@@ -294,8 +261,13 @@ function injectExchangedToken(body, config, token, connection) {
|
|
|
294
261
|
}
|
|
295
262
|
//#endregion
|
|
296
263
|
//#region src/server/util/tools.ts
|
|
297
|
-
function tool(
|
|
298
|
-
|
|
264
|
+
function tool(definition) {
|
|
265
|
+
const { approval, ...tool } = definition;
|
|
266
|
+
if (approval !== void 0 && tool.needsApproval !== void 0) throw new Error("tool() accepts either approval or needsApproval, not both");
|
|
267
|
+
return tool$1({
|
|
268
|
+
...tool,
|
|
269
|
+
...approval !== void 0 ? { needsApproval: approval } : {}
|
|
270
|
+
});
|
|
299
271
|
}
|
|
300
272
|
/**
|
|
301
273
|
* Wrap each tool's `execute` so it falls back to `getFallbackContext()` for
|
|
@@ -962,61 +934,16 @@ var Agent = class extends Think {
|
|
|
962
934
|
sendConnectionStatus(connection, "connecting");
|
|
963
935
|
this.clientIp = ctx.request.headers.get("CF-Connecting-IP") ?? ctx.request.headers.get("X-Forwarded-For")?.split(",")[0]?.trim();
|
|
964
936
|
this.forwardedFor = ctx.request.headers.get("X-Forwarded-For") ?? void 0;
|
|
965
|
-
const
|
|
966
|
-
if (
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
connection.close(4001, "Unauthorized");
|
|
976
|
-
return;
|
|
977
|
-
}
|
|
978
|
-
if (!result.success) {
|
|
979
|
-
sendConnectionStatus(connection, "unauthorized");
|
|
980
|
-
console.error(`[Agent] JWT verification error - ${result.message}`);
|
|
981
|
-
connection.close(result.status === 401 ? 4001 : 4003, result.message);
|
|
982
|
-
return;
|
|
983
|
-
}
|
|
984
|
-
if (!isJwtActorAuthorized(config, result.claims, {
|
|
985
|
-
durableObjectName: this.name,
|
|
986
|
-
parentPath: this.parentPath
|
|
987
|
-
})) {
|
|
988
|
-
sendConnectionStatus(connection, "unauthorized");
|
|
989
|
-
console.error("[Agent] Authenticated actor does not own the requested agent", {
|
|
990
|
-
authenticatedActorId: config.getActorId(result.claims),
|
|
991
|
-
requiredActorId: getTargetActorId({
|
|
992
|
-
durableObjectName: this.name,
|
|
993
|
-
parentPath: this.parentPath
|
|
994
|
-
}),
|
|
995
|
-
durableObjectName: this.name
|
|
996
|
-
});
|
|
997
|
-
connection.close(4003, "Forbidden");
|
|
998
|
-
return;
|
|
999
|
-
}
|
|
1000
|
-
const token = extractTokenFromConnectRequest(ctx.request);
|
|
1001
|
-
let effectiveToken = token;
|
|
1002
|
-
const getTokenExchangeConfig = this.constructor.getTokenExchangeConfig;
|
|
1003
|
-
const exchangeConfig = getTokenExchangeConfig?.(this.env);
|
|
1004
|
-
if (token && exchangeConfig) {
|
|
1005
|
-
const exchanged = await exchangeConnectToken(token, exchangeConfig, connection);
|
|
1006
|
-
if (!exchanged) return;
|
|
1007
|
-
await this._storeExchangedToken(exchanged);
|
|
1008
|
-
effectiveToken = exchanged.accessToken;
|
|
1009
|
-
}
|
|
1010
|
-
if (effectiveToken && this.getUserContext) try {
|
|
1011
|
-
connection.setState({ userContext: await this.getUserContext(effectiveToken) });
|
|
1012
|
-
} catch (error) {
|
|
1013
|
-
const closeCode = getConnectCloseCode(error);
|
|
1014
|
-
sendConnectionStatus(connection, closeCode === 1011 ? "error" : "unauthorized");
|
|
1015
|
-
console.error(`[Agent] getUserContext error - ${error}`);
|
|
1016
|
-
connection.close(closeCode, "Could not establish user context");
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
937
|
+
const auth = await this.authenticate(ctx.request);
|
|
938
|
+
if (!auth.success) {
|
|
939
|
+
const closeCode = auth.status === 401 ? 4001 : auth.status === 403 ? 4003 : 1011;
|
|
940
|
+
sendConnectionStatus(connection, closeCode === 1011 ? "error" : "unauthorized");
|
|
941
|
+
connection.close(closeCode, auth.message);
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
if (auth.userContext) {
|
|
945
|
+
this.userContext = auth.userContext;
|
|
946
|
+
connection.setState({ userContext: auth.userContext });
|
|
1020
947
|
}
|
|
1021
948
|
sendConnectionStatus(connection, "connected");
|
|
1022
949
|
}
|
|
@@ -1031,7 +958,7 @@ var Agent = class extends Think {
|
|
|
1031
958
|
const connectionState = getCurrentAgent().connection?.state;
|
|
1032
959
|
return {
|
|
1033
960
|
...requestContext,
|
|
1034
|
-
_userContext: connectionState?.userContext
|
|
961
|
+
_userContext: connectionState?.userContext ?? this.userContext
|
|
1035
962
|
};
|
|
1036
963
|
}
|
|
1037
964
|
/**
|
|
@@ -1121,6 +1048,12 @@ var Agent = class extends Think {
|
|
|
1121
1048
|
getTools() {
|
|
1122
1049
|
return {};
|
|
1123
1050
|
}
|
|
1051
|
+
/** Collects the actions declared by this agent's skills for Think to compile. */
|
|
1052
|
+
async getActions() {
|
|
1053
|
+
const actions = {};
|
|
1054
|
+
for (const skill of await this.getSkills()) Object.assign(actions, skill.actions);
|
|
1055
|
+
return actions;
|
|
1056
|
+
}
|
|
1124
1057
|
/**
|
|
1125
1058
|
* Read-only: what the model would see on its next step for the given history
|
|
1126
1059
|
* — the assembled system prompt, the skill-gated toolset, and pruned
|
|
@@ -1151,8 +1084,10 @@ var Agent = class extends Think {
|
|
|
1151
1084
|
...think._skillRegistry?.tools(),
|
|
1152
1085
|
...think.mcp?.getAITools?.()
|
|
1153
1086
|
};
|
|
1087
|
+
const actionTools = await think._compileActionTools?.() ?? {};
|
|
1154
1088
|
const inputTools = {
|
|
1155
1089
|
...this.getTools(),
|
|
1090
|
+
...actionTools,
|
|
1156
1091
|
...frameworkTools
|
|
1157
1092
|
};
|
|
1158
1093
|
this._turn.inputTools = inputTools;
|
|
@@ -1207,7 +1142,18 @@ var Agent = class extends Think {
|
|
|
1207
1142
|
const unavailableSkillIds = /* @__PURE__ */ new Set();
|
|
1208
1143
|
let hasActionTool = false;
|
|
1209
1144
|
let hasAuthorizedActionTool = false;
|
|
1210
|
-
const
|
|
1145
|
+
const skillSources = await this.getSkills();
|
|
1146
|
+
const registeredActions = await this.getActions();
|
|
1147
|
+
const actionsByToolName = /* @__PURE__ */ new Map();
|
|
1148
|
+
const skillActionNames = /* @__PURE__ */ new Set();
|
|
1149
|
+
for (const [registrationName, definition] of Object.entries(registeredActions)) actionsByToolName.set(definition.config.name ?? registrationName, definition);
|
|
1150
|
+
for (const skill of skillSources) for (const [registrationName, definition] of Object.entries(skill.actions ?? {})) skillActionNames.add(definition.config.name ?? registrationName);
|
|
1151
|
+
const configureTool = (name, tool, trackRestriction = true, authorization = tool) => {
|
|
1152
|
+
tool = {
|
|
1153
|
+
...tool,
|
|
1154
|
+
authorize: authorization.authorize,
|
|
1155
|
+
onUnauthorized: authorization.onUnauthorized
|
|
1156
|
+
};
|
|
1211
1157
|
const authorized = tool.authorize?.(ctx) !== false;
|
|
1212
1158
|
if (tool.needsApproval) {
|
|
1213
1159
|
hasActionTool = true;
|
|
@@ -1234,11 +1180,18 @@ var Agent = class extends Think {
|
|
|
1234
1180
|
tools[name] = tool;
|
|
1235
1181
|
return true;
|
|
1236
1182
|
};
|
|
1237
|
-
for (const [name, tool] of Object.entries(inputTools))
|
|
1238
|
-
|
|
1183
|
+
for (const [name, tool] of Object.entries(inputTools)) {
|
|
1184
|
+
if (skillActionNames.has(name)) continue;
|
|
1185
|
+
if (configureTool(name, tool, true, actionsByToolName.get(name))) activeTools.push(name);
|
|
1186
|
+
}
|
|
1239
1187
|
for (const skill of skillSources) {
|
|
1240
1188
|
const skillAuthorized = skill.authorize?.(ctx) !== false;
|
|
1241
|
-
const skillTools = Object.entries(skill.tools ?? {});
|
|
1189
|
+
const skillTools = [...Object.entries(skill.tools ?? {})];
|
|
1190
|
+
for (const [registrationName, definition] of Object.entries(skill.actions ?? {})) {
|
|
1191
|
+
const name = definition.config.name ?? registrationName;
|
|
1192
|
+
const compiled = inputTools[name];
|
|
1193
|
+
if (compiled) skillTools.push([name, compiled]);
|
|
1194
|
+
}
|
|
1242
1195
|
let hasVisibleTool = false;
|
|
1243
1196
|
for (const [name, tool] of skillTools) {
|
|
1244
1197
|
if (!skillAuthorized) {
|
|
@@ -1247,13 +1200,14 @@ var Agent = class extends Think {
|
|
|
1247
1200
|
continue;
|
|
1248
1201
|
}
|
|
1249
1202
|
const needsApproval = tool.needsApproval;
|
|
1250
|
-
const
|
|
1203
|
+
const preparedTool = tool.authorize && needsApproval ? {
|
|
1251
1204
|
...tool,
|
|
1252
1205
|
needsApproval: async (input, options) => {
|
|
1253
1206
|
if (tool.authorize?.(ctx) === false) return false;
|
|
1254
1207
|
return typeof needsApproval === "function" ? needsApproval(input, options) : needsApproval;
|
|
1255
1208
|
}
|
|
1256
|
-
} : tool
|
|
1209
|
+
} : tool;
|
|
1210
|
+
const visible = configureTool(name, preparedTool, false, actionsByToolName.get(name) ?? preparedTool);
|
|
1257
1211
|
hasVisibleTool ||= visible;
|
|
1258
1212
|
}
|
|
1259
1213
|
if (skillTools.length > 0 && !hasVisibleTool) unavailableSkillIds.add(skill.id);
|
|
@@ -1261,7 +1215,13 @@ var Agent = class extends Think {
|
|
|
1261
1215
|
this._turn.unavailableSkillIds = unavailableSkillIds;
|
|
1262
1216
|
const activeSkill = skillSources.find((skill) => skill.id === activeSkillName);
|
|
1263
1217
|
if (activeSkill && activeSkill.authorize?.(ctx) !== false) {
|
|
1264
|
-
|
|
1218
|
+
const activeSkillTools = [...Object.entries(activeSkill.tools ?? {})];
|
|
1219
|
+
for (const [registrationName, definition] of Object.entries(activeSkill.actions ?? {})) {
|
|
1220
|
+
const name = definition.config.name ?? registrationName;
|
|
1221
|
+
const compiled = inputTools[name];
|
|
1222
|
+
if (compiled) activeSkillTools.push([name, compiled]);
|
|
1223
|
+
}
|
|
1224
|
+
for (const [name, tool] of activeSkillTools) if (configureTool(name, tool, true, actionsByToolName.get(name) ?? tool)) activeTools.push(name);
|
|
1265
1225
|
}
|
|
1266
1226
|
this._turn.preparedTools = tools;
|
|
1267
1227
|
this._turn.unavailableTools = Array.from(unavailableTools, ([name, reason]) => ({
|
|
@@ -1324,6 +1284,72 @@ var Agent = class extends Think {
|
|
|
1324
1284
|
}
|
|
1325
1285
|
}
|
|
1326
1286
|
}
|
|
1287
|
+
/** Verifies the request and prepares the same user context for WebSocket and HTTP requests. */
|
|
1288
|
+
async authenticate(request) {
|
|
1289
|
+
const config = this.constructor.getJwtAuthConfig?.(this.env);
|
|
1290
|
+
if (!config) return { success: true };
|
|
1291
|
+
let result;
|
|
1292
|
+
try {
|
|
1293
|
+
result = await verifyJwt(request, config);
|
|
1294
|
+
} catch (error) {
|
|
1295
|
+
console.error(`[Agent] JWT verification error - ${error}`);
|
|
1296
|
+
return {
|
|
1297
|
+
success: false,
|
|
1298
|
+
status: 401,
|
|
1299
|
+
message: "Unauthorized"
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
if (!result.success) return result;
|
|
1303
|
+
if (!isJwtActorAuthorized(config, result.claims, {
|
|
1304
|
+
durableObjectName: this.name,
|
|
1305
|
+
parentPath: this.parentPath
|
|
1306
|
+
})) return {
|
|
1307
|
+
success: false,
|
|
1308
|
+
status: 403,
|
|
1309
|
+
message: "Forbidden"
|
|
1310
|
+
};
|
|
1311
|
+
let token = extractTokenFromConnectRequest(request);
|
|
1312
|
+
const exchangeConfig = this.constructor.getTokenExchangeConfig?.(this.env);
|
|
1313
|
+
if (exchangeConfig) {
|
|
1314
|
+
let exchanged;
|
|
1315
|
+
try {
|
|
1316
|
+
exchanged = await exchangeToken(token, exchangeConfig);
|
|
1317
|
+
} catch (error) {
|
|
1318
|
+
console.error(`[Agent] Token exchange error - ${error}`);
|
|
1319
|
+
return {
|
|
1320
|
+
success: false,
|
|
1321
|
+
status: 500,
|
|
1322
|
+
message: "Token exchange failed"
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
if (!exchanged.success) {
|
|
1326
|
+
console.error(`[Agent] Token exchange failed - ${exchanged.message}`);
|
|
1327
|
+
return {
|
|
1328
|
+
success: false,
|
|
1329
|
+
status: exchanged.subjectTokenRejected ? 401 : 500,
|
|
1330
|
+
message: exchanged.subjectTokenRejected ? "Unauthorized" : "Token exchange failed"
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
await this._storeExchangedToken(exchanged);
|
|
1334
|
+
token = exchanged.accessToken;
|
|
1335
|
+
}
|
|
1336
|
+
if (!this.getUserContext) return { success: true };
|
|
1337
|
+
try {
|
|
1338
|
+
this.userContext = await this.getUserContext(token);
|
|
1339
|
+
return {
|
|
1340
|
+
success: true,
|
|
1341
|
+
userContext: this.userContext
|
|
1342
|
+
};
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
console.error(`[Agent] getUserContext error - ${error}`);
|
|
1345
|
+
const closeCode = getConnectCloseCode(error);
|
|
1346
|
+
return {
|
|
1347
|
+
success: false,
|
|
1348
|
+
status: closeCode === 4003 ? 403 : closeCode === 4001 ? 401 : 500,
|
|
1349
|
+
message: "Could not establish user context"
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1327
1353
|
/**
|
|
1328
1354
|
* The exchanged token for this agent, refreshed on every authenticated
|
|
1329
1355
|
* connect. Kept in memory for the hot path and mirrored to DO storage so
|
|
@@ -1788,44 +1814,66 @@ var Assistant = class extends Agent$1 {
|
|
|
1788
1814
|
sendAgentType(connection, "assistant");
|
|
1789
1815
|
sendSubAgentName(connection, this.agent.name);
|
|
1790
1816
|
sendConnectionStatus(connection, "connecting");
|
|
1791
|
-
const
|
|
1792
|
-
if (
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1817
|
+
const auth = await this.authenticate(ctx.request);
|
|
1818
|
+
if (!auth.success) {
|
|
1819
|
+
sendConnectionStatus(connection, "unauthorized");
|
|
1820
|
+
connection.close(auth.status === 401 ? 4001 : 4003, auth.message);
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
sendConnectionStatus(connection, "connected");
|
|
1824
|
+
}
|
|
1825
|
+
async authenticate(request) {
|
|
1826
|
+
const config = this.agent.getJwtAuthConfig?.(this.env);
|
|
1827
|
+
if (!config) return { success: true };
|
|
1828
|
+
let result;
|
|
1829
|
+
try {
|
|
1830
|
+
result = await verifyJwt(request, config);
|
|
1831
|
+
} catch (error) {
|
|
1832
|
+
console.error(`[Assistant] JWT verification error - ${error}`);
|
|
1833
|
+
return {
|
|
1834
|
+
success: false,
|
|
1835
|
+
status: 401,
|
|
1836
|
+
message: "Unauthorized"
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
if (!result.success) return result;
|
|
1840
|
+
if (!isJwtActorAuthorized(config, result.claims, {
|
|
1841
|
+
durableObjectName: this.name,
|
|
1842
|
+
parentPath: this.parentPath
|
|
1843
|
+
})) {
|
|
1844
|
+
console.error("[Assistant] Authenticated actor does not own the requested assistant", {
|
|
1845
|
+
authenticatedActorId: config.getActorId(result.claims),
|
|
1846
|
+
requiredActorId: getTargetActorId({
|
|
1811
1847
|
durableObjectName: this.name,
|
|
1812
1848
|
parentPath: this.parentPath
|
|
1813
|
-
})
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
durableObjectName: this.name
|
|
1822
|
-
});
|
|
1823
|
-
connection.close(4003, "Forbidden");
|
|
1824
|
-
return;
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1849
|
+
}),
|
|
1850
|
+
durableObjectName: this.name
|
|
1851
|
+
});
|
|
1852
|
+
return {
|
|
1853
|
+
success: false,
|
|
1854
|
+
status: 403,
|
|
1855
|
+
message: "Forbidden"
|
|
1856
|
+
};
|
|
1827
1857
|
}
|
|
1828
|
-
|
|
1858
|
+
return { success: true };
|
|
1859
|
+
}
|
|
1860
|
+
async onRequest(request) {
|
|
1861
|
+
const auth = await this.authenticate(request);
|
|
1862
|
+
if (!auth.success) return Response.json({ error: auth.message }, { status: auth.status });
|
|
1863
|
+
const action = new URL(request.url).pathname.split("/").pop();
|
|
1864
|
+
if (action === "getChats" && request.method === "GET") return Response.json(await this.getChats());
|
|
1865
|
+
if (action === "createChat" && request.method === "POST") return Response.json({ id: await this.createChat() }, { status: 201 });
|
|
1866
|
+
if (action === "deleteChat" && request.method === "POST") {
|
|
1867
|
+
const { id } = await request.json();
|
|
1868
|
+
await this.deleteChat(id);
|
|
1869
|
+
return new Response(null, { status: 204 });
|
|
1870
|
+
}
|
|
1871
|
+
if (action === "renameChat" && request.method === "POST") {
|
|
1872
|
+
const { id, title } = await request.json();
|
|
1873
|
+
await this.renameChat(id, title);
|
|
1874
|
+
return new Response(null, { status: 204 });
|
|
1875
|
+
}
|
|
1876
|
+
return super.onRequest(request);
|
|
1829
1877
|
}
|
|
1830
1878
|
async createChat() {
|
|
1831
1879
|
const id = nanoid();
|
|
@@ -2348,6 +2396,65 @@ var ChatAgent = class extends Agent {
|
|
|
2348
2396
|
const parent = await this.getParentAgent();
|
|
2349
2397
|
if (parent?.recordChatTurn) await parent.recordChatTurn(this.name, this.messages);
|
|
2350
2398
|
}
|
|
2399
|
+
async onRequest(request) {
|
|
2400
|
+
const auth = await this.authenticate(request);
|
|
2401
|
+
if (!auth.success) return Response.json({ error: auth.message }, { status: auth.status });
|
|
2402
|
+
const action = new URL(request.url).pathname.split("/").pop();
|
|
2403
|
+
if (action === "getMessages" && request.method === "GET") return Response.json(await this.getMessages());
|
|
2404
|
+
if (action === "sendMessage" && request.method === "POST") return this.streamMessage(request);
|
|
2405
|
+
if (action === "cancelChat" && request.method === "POST") {
|
|
2406
|
+
const { requestId } = await request.json();
|
|
2407
|
+
this.cancelChat(requestId);
|
|
2408
|
+
return new Response(null, { status: 204 });
|
|
2409
|
+
}
|
|
2410
|
+
return super.onRequest(request);
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Runs the turn from the stream's start callback so the response can be
|
|
2414
|
+
* returned immediately and each event reaches the client as it is produced.
|
|
2415
|
+
*/
|
|
2416
|
+
async streamMessage(request) {
|
|
2417
|
+
let message;
|
|
2418
|
+
try {
|
|
2419
|
+
({message} = await request.json());
|
|
2420
|
+
} catch {
|
|
2421
|
+
return Response.json({ error: "Invalid request body" }, { status: 400 });
|
|
2422
|
+
}
|
|
2423
|
+
if (typeof message === "string") message = {
|
|
2424
|
+
id: crypto.randomUUID(),
|
|
2425
|
+
role: "user",
|
|
2426
|
+
parts: [{
|
|
2427
|
+
type: "text",
|
|
2428
|
+
text: message
|
|
2429
|
+
}]
|
|
2430
|
+
};
|
|
2431
|
+
else if (!message || message.role !== "user" || !Array.isArray(message.parts)) return Response.json({ error: "message must be a user message" }, { status: 400 });
|
|
2432
|
+
const encoder = new TextEncoder();
|
|
2433
|
+
const readable = new ReadableStream({ start: async (controller) => {
|
|
2434
|
+
const send = (event, data) => controller.enqueue(encoder.encode(`event: ${event}\ndata: ${data}\n\n`));
|
|
2435
|
+
try {
|
|
2436
|
+
await this.runTurn({
|
|
2437
|
+
mode: "stream",
|
|
2438
|
+
input: message,
|
|
2439
|
+
callback: {
|
|
2440
|
+
onStart: (event) => send("start", JSON.stringify(event)),
|
|
2441
|
+
onEvent: (json) => send("chunk", json),
|
|
2442
|
+
onDone: () => send("done", "{}"),
|
|
2443
|
+
onError: (error) => send("error", JSON.stringify({ error })),
|
|
2444
|
+
onInterrupted: () => send("interrupted", "{}")
|
|
2445
|
+
}
|
|
2446
|
+
});
|
|
2447
|
+
} catch (error) {
|
|
2448
|
+
send("error", JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
2449
|
+
} finally {
|
|
2450
|
+
controller.close();
|
|
2451
|
+
}
|
|
2452
|
+
} });
|
|
2453
|
+
return new Response(readable, { headers: {
|
|
2454
|
+
"Content-Type": "text/event-stream",
|
|
2455
|
+
"Cache-Control": "no-cache"
|
|
2456
|
+
} });
|
|
2457
|
+
}
|
|
2351
2458
|
/**
|
|
2352
2459
|
* Submit feedback for a message by its id.
|
|
2353
2460
|
* @param messageId - The id of the message to give feedback on.
|
|
@@ -2382,6 +2489,34 @@ var ChatAgent = class extends Agent {
|
|
|
2382
2489
|
}
|
|
2383
2490
|
};
|
|
2384
2491
|
//#endregion
|
|
2492
|
+
//#region src/server/util/actions.ts
|
|
2493
|
+
/** A native Think action with the SDK's standard authorization policy. */
|
|
2494
|
+
function action(definition) {
|
|
2495
|
+
const { authorize, onUnauthorized, execute, ...config } = definition;
|
|
2496
|
+
const descriptor = action$1({
|
|
2497
|
+
...config,
|
|
2498
|
+
approval: config.approval ?? true,
|
|
2499
|
+
execute: async (input, ctx) => {
|
|
2500
|
+
const idempotencyKey = typeof config.idempotencyKey === "function" ? await config.idempotencyKey({
|
|
2501
|
+
input,
|
|
2502
|
+
ctx
|
|
2503
|
+
}) : config.idempotencyKey ?? ctx.toolCallId;
|
|
2504
|
+
return execute(input, {
|
|
2505
|
+
...ctx,
|
|
2506
|
+
messages: [...ctx.messages],
|
|
2507
|
+
abortSignal: ctx.signal,
|
|
2508
|
+
experimental_context: getCurrentAgent().agent._lastBody,
|
|
2509
|
+
idempotencyKey
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2512
|
+
});
|
|
2513
|
+
return Object.freeze({
|
|
2514
|
+
...descriptor,
|
|
2515
|
+
authorize,
|
|
2516
|
+
onUnauthorized
|
|
2517
|
+
});
|
|
2518
|
+
}
|
|
2519
|
+
//#endregion
|
|
2385
2520
|
//#region src/server/util/skills.ts
|
|
2386
2521
|
function skill(definition) {
|
|
2387
2522
|
if (!definition.name) throw new Error("Skill name is required");
|
|
@@ -2405,6 +2540,7 @@ function skill(definition) {
|
|
|
2405
2540
|
};
|
|
2406
2541
|
},
|
|
2407
2542
|
tools: definition.tools,
|
|
2543
|
+
actions: definition.actions,
|
|
2408
2544
|
authorize: definition.authorize
|
|
2409
2545
|
};
|
|
2410
2546
|
}
|
|
@@ -2427,4 +2563,4 @@ function getCurrentToolContext() {
|
|
|
2427
2563
|
return getCurrentAgent().agent._lastBody;
|
|
2428
2564
|
}
|
|
2429
2565
|
//#endregion
|
|
2430
|
-
export { Agent, Assistant, ChatAgent, createJwtAuthConfig, exchangeToken, getCurrentToolContext, routeAgentRequest, skill, tool };
|
|
2566
|
+
export { Agent, Assistant, ChatAgent, action, createJwtAuthConfig, exchangeToken, getCurrentToolContext, routeAgentRequest, skill, tool };
|