@economic/agents 1.1.0 → 1.2.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/dist/hono.d.mts +10 -3
- package/dist/hono.mjs +19 -4
- package/dist/index.d.mts +5 -13
- package/dist/index.mjs +33 -32
- package/package.json +2 -2
package/dist/hono.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MiddlewareHandler } from "hono";
|
|
1
|
+
import { Context, MiddlewareHandler } from "hono";
|
|
2
2
|
|
|
3
3
|
//#region src/hono/jwt-auth.d.ts
|
|
4
4
|
interface JwtAuthConfig {
|
|
@@ -8,13 +8,20 @@ interface JwtAuthConfig {
|
|
|
8
8
|
audience: string;
|
|
9
9
|
/** Required OAuth scopes; token `scope` must include all (empty = no scope check). */
|
|
10
10
|
requiredScopes?: readonly string[];
|
|
11
|
+
/**
|
|
12
|
+
* Custom token extraction function.
|
|
13
|
+
* If provided, replaces the default extraction entirely.
|
|
14
|
+
* Default checks Authorization header, then Sec-WebSocket-Protocol.
|
|
15
|
+
*/
|
|
16
|
+
getToken?: (c: Context) => string | null | Promise<string | null>;
|
|
11
17
|
}
|
|
12
18
|
/**
|
|
13
19
|
* Hono middleware: verify JWT via JWKS derived from the token `iss` claim,
|
|
14
20
|
* after `iss` passes `allowedIssuers`.
|
|
15
21
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
22
|
+
* By default, reads the token from `Authorization: Bearer` header first,
|
|
23
|
+
* then falls back to `Sec-WebSocket-Protocol: bearer, <token>` for WebSocket connections.
|
|
24
|
+
* Provide a custom `getToken` function to replace this behavior entirely.
|
|
18
25
|
*/
|
|
19
26
|
declare function jwtAuth(config: JwtAuthConfig): MiddlewareHandler;
|
|
20
27
|
//#endregion
|
package/dist/hono.mjs
CHANGED
|
@@ -5,7 +5,9 @@ function getJwksForIssuer(issuer) {
|
|
|
5
5
|
const normalized = issuer.replace(/\/$/, "");
|
|
6
6
|
let jwks = jwksByIssuer.get(normalized);
|
|
7
7
|
if (!jwks) {
|
|
8
|
-
|
|
8
|
+
const jwksUrl = new URL(`${normalized}/.well-known/openid-configuration/jwks`);
|
|
9
|
+
console.log("jwksUrl", jwksUrl);
|
|
10
|
+
jwks = createRemoteJWKSet(jwksUrl);
|
|
9
11
|
jwksByIssuer.set(normalized, jwks);
|
|
10
12
|
}
|
|
11
13
|
return jwks;
|
|
@@ -17,6 +19,17 @@ function bearerTokenFromAuthorizationHeader(authorization) {
|
|
|
17
19
|
if (!authorization) return null;
|
|
18
20
|
return authorization.match(/^Bearer\s+(.+)$/i)?.[1]?.trim() ?? null;
|
|
19
21
|
}
|
|
22
|
+
function tokenFromWebSocketProtocol(header) {
|
|
23
|
+
if (!header) return null;
|
|
24
|
+
const parts = header.split(",").map((p) => p.trim());
|
|
25
|
+
const idx = parts.indexOf("bearer");
|
|
26
|
+
return idx !== -1 && parts[idx + 1] ? parts[idx + 1] : null;
|
|
27
|
+
}
|
|
28
|
+
function defaultGetToken(c) {
|
|
29
|
+
const authToken = bearerTokenFromAuthorizationHeader(c.req.header("Authorization"));
|
|
30
|
+
if (authToken) return authToken;
|
|
31
|
+
return tokenFromWebSocketProtocol(c.req.header("Sec-WebSocket-Protocol"));
|
|
32
|
+
}
|
|
20
33
|
function hasRequiredScopes(tokenScope, required) {
|
|
21
34
|
if (required.length === 0) return true;
|
|
22
35
|
if (tokenScope === void 0) return false;
|
|
@@ -31,13 +44,15 @@ function authErrorResponse(status, message) {
|
|
|
31
44
|
* Hono middleware: verify JWT via JWKS derived from the token `iss` claim,
|
|
32
45
|
* after `iss` passes `allowedIssuers`.
|
|
33
46
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
47
|
+
* By default, reads the token from `Authorization: Bearer` header first,
|
|
48
|
+
* then falls back to `Sec-WebSocket-Protocol: bearer, <token>` for WebSocket connections.
|
|
49
|
+
* Provide a custom `getToken` function to replace this behavior entirely.
|
|
36
50
|
*/
|
|
37
51
|
function jwtAuth(config) {
|
|
38
52
|
const requiredScopes = config.requiredScopes ?? [];
|
|
53
|
+
const getToken = config.getToken ?? defaultGetToken;
|
|
39
54
|
return async (c, next) => {
|
|
40
|
-
const token =
|
|
55
|
+
const token = await getToken(c);
|
|
41
56
|
if (!token) return authErrorResponse(401, "Unauthorized: Missing authentication token");
|
|
42
57
|
let unverifiedPayload;
|
|
43
58
|
try {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Agent as Agent$1, Connection, ConnectionContext } from "agents";
|
|
1
|
+
import { Agent as Agent$1, AgentOptions, Connection, ConnectionContext } from "agents";
|
|
2
2
|
import { AIChatAgent, OnChatMessageOptions } from "@cloudflare/ai-chat";
|
|
3
3
|
import { LanguageModel, StreamTextOnFinishCallback, ToolSet, UIMessage, generateText, streamText } from "ai";
|
|
4
4
|
|
|
@@ -176,17 +176,6 @@ declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env> ex
|
|
|
176
176
|
protected buildLLMParams<TBody = Record<string, unknown>>(config: BuildLLMParamsConfig & {
|
|
177
177
|
options?: OnChatMessageOptions;
|
|
178
178
|
}): Promise<LLMParams>;
|
|
179
|
-
/**
|
|
180
|
-
* Extracts skill state from activate_skill results, persists to DO SQLite,
|
|
181
|
-
* then strips all skill meta-tool messages before delegating to super.
|
|
182
|
-
*
|
|
183
|
-
* 1. Scans activate_skill tool results for SKILL_STATE_SENTINEL. When found,
|
|
184
|
-
* the embedded JSON array of loaded skill names is written to DO SQLite.
|
|
185
|
-
*
|
|
186
|
-
* 2. Strips all activate_skill and list_capabilities messages from history.
|
|
187
|
-
*
|
|
188
|
-
* 3. Delegates to super.persistMessages for message storage and WS broadcast.
|
|
189
|
-
*/
|
|
190
179
|
persistMessages(messages: UIMessage[], excludeBroadcastIds?: string[], options?: {
|
|
191
180
|
_deleteStaleRows?: boolean;
|
|
192
181
|
}): Promise<void>;
|
|
@@ -244,4 +233,7 @@ declare abstract class ChatAgentHarness<Env extends Cloudflare.Env, RequestBody
|
|
|
244
233
|
onChatMessage(onFinish: StreamTextOnFinishCallback<ToolSet>, options?: OnChatMessageOptions): Promise<Response>;
|
|
245
234
|
}
|
|
246
235
|
//#endregion
|
|
247
|
-
|
|
236
|
+
//#region src/server/route-agent-request.d.ts
|
|
237
|
+
declare function routeAgentRequest<Env>(request: Request, env: Env, options?: AgentOptions<Env>): Promise<Response | null>;
|
|
238
|
+
//#endregion
|
|
239
|
+
export { Agent, type AgentToolContext, type BuildLLMParamsConfig, ChatAgent, ChatAgentHarness, type Skill, buildLLMParams, routeAgentRequest };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Agent as Agent$1, callable } from "agents";
|
|
1
|
+
import { Agent as Agent$1, callable, routeAgentRequest as routeAgentRequest$1 } from "agents";
|
|
2
2
|
import { AIChatAgent } from "@cloudflare/ai-chat";
|
|
3
3
|
import { Output, convertToModelMessages, generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
|
|
4
4
|
//#region src/server/features/skills/index.ts
|
|
@@ -162,9 +162,23 @@ function getStoredSkills(sql) {
|
|
|
162
162
|
* Persists the current list of loaded skill names to DO SQLite.
|
|
163
163
|
* Upserts the single `skill_state` row (id = 1).
|
|
164
164
|
*/
|
|
165
|
-
function
|
|
165
|
+
function saveSkillStateFromMessages(sql, messages) {
|
|
166
|
+
let latestSkillState;
|
|
167
|
+
for (const msg of messages) {
|
|
168
|
+
if (msg.role !== "assistant" || !msg.parts) continue;
|
|
169
|
+
for (const part of msg.parts) {
|
|
170
|
+
if (!("toolCallId" in part)) continue;
|
|
171
|
+
if (part.type !== `tool-activate_skill` || typeof part.output !== "string") continue;
|
|
172
|
+
const sentinelIndex = part.output.indexOf(SKILL_STATE_SENTINEL);
|
|
173
|
+
if (sentinelIndex !== -1) try {
|
|
174
|
+
const stateJson = part.output.slice(sentinelIndex + 18);
|
|
175
|
+
latestSkillState = JSON.parse(stateJson);
|
|
176
|
+
} catch {}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (latestSkillState == void 0) return;
|
|
166
180
|
ensureSkillTable(sql);
|
|
167
|
-
sql`INSERT OR REPLACE INTO skill_state(id, active_skills) VALUES(1, ${JSON.stringify(
|
|
181
|
+
sql`INSERT OR REPLACE INTO skill_state(id, active_skills) VALUES(1, ${JSON.stringify(latestSkillState)})`;
|
|
168
182
|
}
|
|
169
183
|
//#endregion
|
|
170
184
|
//#region src/server/llm.ts
|
|
@@ -732,37 +746,11 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
732
746
|
onFinish
|
|
733
747
|
});
|
|
734
748
|
}
|
|
735
|
-
/**
|
|
736
|
-
* Extracts skill state from activate_skill results, persists to DO SQLite,
|
|
737
|
-
* then strips all skill meta-tool messages before delegating to super.
|
|
738
|
-
*
|
|
739
|
-
* 1. Scans activate_skill tool results for SKILL_STATE_SENTINEL. When found,
|
|
740
|
-
* the embedded JSON array of loaded skill names is written to DO SQLite.
|
|
741
|
-
*
|
|
742
|
-
* 2. Strips all activate_skill and list_capabilities messages from history.
|
|
743
|
-
*
|
|
744
|
-
* 3. Delegates to super.persistMessages for message storage and WS broadcast.
|
|
745
|
-
*/
|
|
746
749
|
async persistMessages(messages, excludeBroadcastIds = [], options) {
|
|
747
|
-
let latestSkillState;
|
|
748
|
-
for (const msg of messages) {
|
|
749
|
-
if (msg.role !== "assistant" || !msg.parts) continue;
|
|
750
|
-
for (const part of msg.parts) {
|
|
751
|
-
if (!("toolCallId" in part)) continue;
|
|
752
|
-
const { type, output } = part;
|
|
753
|
-
if (type !== `tool-activate_skill` || typeof output !== "string") continue;
|
|
754
|
-
const sentinelIdx = output.indexOf(SKILL_STATE_SENTINEL);
|
|
755
|
-
if (sentinelIdx !== -1) try {
|
|
756
|
-
const stateJson = output.slice(sentinelIdx + 18);
|
|
757
|
-
latestSkillState = JSON.parse(stateJson);
|
|
758
|
-
} catch {}
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
if (latestSkillState !== void 0) saveStoredSkills(this.sql.bind(this), latestSkillState);
|
|
762
750
|
const filtered = filterEphemeralMessages(messages);
|
|
763
|
-
|
|
751
|
+
await super.persistMessages(filtered, excludeBroadcastIds, options);
|
|
752
|
+
saveSkillStateFromMessages(this.sql.bind(this), messages);
|
|
764
753
|
this.scheduleConversationForDeletion();
|
|
765
|
-
return result;
|
|
766
754
|
}
|
|
767
755
|
@callable({ description: "Returns all conversations for the current user" }) async getConversations() {
|
|
768
756
|
return getConversations(this.env.AGENT_DB, this.getUserId());
|
|
@@ -880,4 +868,17 @@ var ChatAgentHarness = class extends ChatAgent {
|
|
|
880
868
|
}
|
|
881
869
|
};
|
|
882
870
|
//#endregion
|
|
883
|
-
|
|
871
|
+
//#region src/server/route-agent-request.ts
|
|
872
|
+
async function routeAgentRequest(request, env, options) {
|
|
873
|
+
const response = await routeAgentRequest$1(request, env, options);
|
|
874
|
+
if (!response) return null;
|
|
875
|
+
const protocol = request.headers.get("sec-websocket-protocol");
|
|
876
|
+
if (response.status === 101 && protocol) {
|
|
877
|
+
const newResponse = new Response(null, response);
|
|
878
|
+
newResponse.headers.set("Sec-WebSocket-Protocol", protocol.split(",")[0].trim());
|
|
879
|
+
return newResponse;
|
|
880
|
+
}
|
|
881
|
+
return response;
|
|
882
|
+
}
|
|
883
|
+
//#endregion
|
|
884
|
+
export { Agent, ChatAgent, ChatAgentHarness, buildLLMParams, routeAgentRequest };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@economic/agents",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "A starter for creating a TypeScript package.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@cloudflare/ai-chat": ">=0.1.0 <1.0.0",
|
|
46
|
-
"agents": "^0.10.
|
|
46
|
+
"agents": "^0.10.2",
|
|
47
47
|
"ai": "^6.0.0",
|
|
48
48
|
"hono": "^4.0.0",
|
|
49
49
|
"jose": "^6.0.0"
|