@loopingai/core 0.1.1 → 0.2.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 +131 -15
- package/dist/a2a/card.d.ts +35 -0
- package/dist/a2a/card.d.ts.map +1 -1
- package/dist/a2a/card.js +41 -3
- package/dist/a2a/card.js.map +1 -1
- package/dist/a2a/context.d.ts +9 -3
- package/dist/a2a/context.d.ts.map +1 -1
- package/dist/a2a/context.js +11 -4
- package/dist/a2a/context.js.map +1 -1
- package/dist/a2a/index.d.ts +1 -1
- package/dist/a2a/index.d.ts.map +1 -1
- package/dist/a2a/index.js +1 -1
- package/dist/a2a/index.js.map +1 -1
- package/dist/a2a/notify.d.ts +11 -2
- package/dist/a2a/notify.d.ts.map +1 -1
- package/dist/a2a/notify.js +12 -3
- package/dist/a2a/notify.js.map +1 -1
- package/dist/a2a/verify.d.ts +23 -2
- package/dist/a2a/verify.d.ts.map +1 -1
- package/dist/a2a/verify.js +26 -3
- package/dist/a2a/verify.js.map +1 -1
- package/dist/agent/history.d.ts +15 -0
- package/dist/agent/history.d.ts.map +1 -1
- package/dist/agent/history.js +17 -0
- package/dist/agent/history.js.map +1 -1
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js.map +1 -1
- package/dist/contract/plugin.d.ts +86 -3
- package/dist/contract/plugin.d.ts.map +1 -1
- package/dist/contract/plugin.js.map +1 -1
- package/dist/db/db.d.ts +32 -14
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js.map +1 -1
- package/dist/env.d.ts +10 -2
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +10 -2
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/index.d.ts +28 -3
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +39 -3
- package/dist/runtime/index.js.map +1 -1
- package/dist/subagent/workspace.d.ts +21 -0
- package/dist/subagent/workspace.d.ts.map +1 -1
- package/dist/subagent/workspace.js +80 -5
- package/dist/subagent/workspace.js.map +1 -1
- package/dist/testing/auth.d.ts +23 -0
- package/dist/testing/auth.d.ts.map +1 -1
- package/dist/testing/auth.js +11 -3
- package/dist/testing/auth.js.map +1 -1
- package/dist/testing/fixtures.d.ts +21 -0
- package/dist/testing/fixtures.d.ts.map +1 -1
- package/dist/testing/fixtures.js +21 -0
- package/dist/testing/fixtures.js.map +1 -1
- package/dist/testing/node.d.ts +23 -0
- package/dist/testing/node.d.ts.map +1 -1
- package/dist/testing/node.js +23 -0
- package/dist/testing/node.js.map +1 -1
- package/dist/testing/vcr-spec.d.ts +4 -4
- package/dist/testing/vcr-spec.d.ts.map +1 -1
- package/dist/testing/vcr-spec.js +32 -11
- package/dist/testing/vcr-spec.js.map +1 -1
- package/dist/worker/index.d.ts +142 -3
- package/dist/worker/index.d.ts.map +1 -1
- package/dist/worker/index.js +152 -29
- package/dist/worker/index.js.map +1 -1
- package/package.json +12 -4
package/dist/worker/index.d.ts
CHANGED
|
@@ -18,20 +18,134 @@ import { type A2ASecretsEnv } from "../env.js";
|
|
|
18
18
|
*
|
|
19
19
|
* No secret is ever shared in either direction: trust flows entirely on domains
|
|
20
20
|
* and asymmetric (Ed25519) signatures over public JWKS.
|
|
21
|
+
*
|
|
22
|
+
* **Several agents share one Worker, addressed by `tenant`.** They share one
|
|
23
|
+
* origin, one endpoint, one signing key and one card at the well-known path;
|
|
24
|
+
* what differs is the tenant id on every request. That is the A2A mechanism for
|
|
25
|
+
* exactly this — `AgentInterface.tenant` is "an opaque string used for routing
|
|
26
|
+
* requests to a specific agent or tenant when multiple agents are served behind
|
|
27
|
+
* a single A2A endpoint", and §8.3.2 requires a client to send the value the
|
|
28
|
+
* selected interface declared.
|
|
29
|
+
*
|
|
30
|
+
* A tenant is **required on every request**. There is no default and no
|
|
31
|
+
* implicit routing: an absent tenant is an error, because guessing one would
|
|
32
|
+
* mean picking an agent the caller never named.
|
|
33
|
+
*
|
|
34
|
+
* This replaces mounting a handler per path prefix. That could not work: the
|
|
35
|
+
* card is a **well-known URI**, which RFC 8615 defines per-authority, so only
|
|
36
|
+
* one card per origin is discoverable at the registered path — a gateway
|
|
37
|
+
* resolving `…/.well-known/agent-card.json` against the origin found whichever
|
|
38
|
+
* agent owned the bare path and pinned *its* key for all of them.
|
|
39
|
+
*
|
|
40
|
+
* Three routes:
|
|
41
|
+
*
|
|
42
|
+
* 1. `GET jwksPath` — the card-signing public JWKS, resolving every card's
|
|
43
|
+
* `jku`.
|
|
44
|
+
* 2. `GET …/.well-known/agent-card.json` — the signed **stub** card for the
|
|
45
|
+
* origin. Matched by suffix, since that path is fixed by the spec.
|
|
46
|
+
* 3. `POST rpcPath` — gateway-authenticated JSON-RPC, routed to the named
|
|
47
|
+
* tenant. `GetExtendedAgentCard` returns that tenant's own signed card;
|
|
48
|
+
* everything else runs a turn against its Durable Object.
|
|
49
|
+
*
|
|
50
|
+
* Two independent checks keep one tenant's traffic out of another's:
|
|
51
|
+
* `audience` proves the token was minted for this deployment, and
|
|
52
|
+
* {@link file://../a2a/verify.ts TENANT_CLAIM} proves it was minted for *this
|
|
53
|
+
* agent on it*. The second is what makes `tenant` more than an unauthenticated
|
|
54
|
+
* routing hint in the request body.
|
|
21
55
|
*/
|
|
22
56
|
/** Default path serving the card-signing public JWKS (the card's `jku`). */
|
|
23
57
|
export declare const JWKS_PATH = "/.well-known/jwks.json";
|
|
24
|
-
|
|
58
|
+
/** The two secrets a mount signs and verifies with, already read off `env`. */
|
|
59
|
+
export interface A2ASecrets {
|
|
60
|
+
/** Ed25519 private JWK, as JSON. See {@link A2ASecretsEnv.A2A_SIGNING_KEY}. */
|
|
61
|
+
signingKey: string;
|
|
62
|
+
/** Origin allowlist. See {@link A2ASecretsEnv.GATEWAY_ORIGINS}. */
|
|
63
|
+
gatewayOrigins: string;
|
|
64
|
+
}
|
|
65
|
+
/** One agent served on this origin — everything that differs between tenants. */
|
|
66
|
+
export interface TenantAgent {
|
|
25
67
|
/** The transport-independent half of this agent's card. */
|
|
26
68
|
manifest: AgentManifest;
|
|
27
69
|
/** Resolve the agent DO stub for a verified caller. */
|
|
28
70
|
resolveAgent: AgentResolver;
|
|
29
71
|
/** Start the durable turn. Must be idempotent — see {@link TurnStarter}. */
|
|
30
72
|
startTurn: TurnStarter;
|
|
73
|
+
}
|
|
74
|
+
export interface A2AWorkerOptions<TEnv = A2ASecretsEnv> {
|
|
75
|
+
/**
|
|
76
|
+
* The **stub** card served at the well-known path.
|
|
77
|
+
*
|
|
78
|
+
* It describes the origin, not an agent. Every agent here is a tenant and its
|
|
79
|
+
* card is reached through `GetExtendedAgentCard`, so this card exists to be
|
|
80
|
+
* the one conformant, signed AgentCard at the URI RFC 8615 and the A2A IANA
|
|
81
|
+
* registration reserve — advertising the endpoint, the protocol binding and
|
|
82
|
+
* `extendedAgentCard`.
|
|
83
|
+
*
|
|
84
|
+
* It cannot enumerate the tenants: a card carries one interface entry and
|
|
85
|
+
* spec §8.3.2 has clients take the first, so listing siblings there would
|
|
86
|
+
* just make every client address the same one. Put their names in
|
|
87
|
+
* `description` for a human, and register them out of band.
|
|
88
|
+
*/
|
|
89
|
+
manifest: AgentManifest;
|
|
90
|
+
/**
|
|
91
|
+
* The agents on this origin, keyed by tenant id. At least one.
|
|
92
|
+
*
|
|
93
|
+
* The id is opaque to the protocol — "an opaque string used for routing
|
|
94
|
+
* requests to a specific agent or tenant when multiple agents are served
|
|
95
|
+
* behind a single A2A endpoint". A caller names one on every request and this
|
|
96
|
+
* Worker refuses a request that does not.
|
|
97
|
+
*
|
|
98
|
+
* The tenant chooses *which agent*; the verified `identity.key` still chooses
|
|
99
|
+
* *which instance of it*, so two callers of one tenant stay in separate
|
|
100
|
+
* Durable Objects exactly as before.
|
|
101
|
+
*/
|
|
102
|
+
tenants: Record<string, TenantAgent>;
|
|
31
103
|
/** Path serving the public JWKS. Defaults to {@link JWKS_PATH}. */
|
|
32
104
|
jwksPath?: string;
|
|
33
105
|
/** Path this agent answers JSON-RPC on. Defaults to `/a2a`. */
|
|
34
106
|
rpcPath?: string;
|
|
107
|
+
/**
|
|
108
|
+
* Where to read this deployment's two secrets. Defaults to the documented
|
|
109
|
+
* names, `env.A2A_SIGNING_KEY` and `env.GATEWAY_ORIGINS`.
|
|
110
|
+
*
|
|
111
|
+
* There is **one signing key per origin**, not one per tenant: the card is
|
|
112
|
+
* per-origin now, so the key the gateway pins is too. Nothing was lost — the
|
|
113
|
+
* tenants share a Worker and an `env`, so they could always read each other's
|
|
114
|
+
* secrets, and separate keys never expressed a boundary that existed.
|
|
115
|
+
*
|
|
116
|
+
* ```ts
|
|
117
|
+
* secrets: (env) => ({
|
|
118
|
+
* signingKey: env.AGENT_KEY,
|
|
119
|
+
* gatewayOrigins: env.ALLOWED_GATEWAYS
|
|
120
|
+
* })
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* Renaming does not weaken anything: the same key is still Ed25519, still
|
|
124
|
+
* signs the cards and every callback JWT, and its public half is still what
|
|
125
|
+
* the gateway pins. Only where it is read from changes.
|
|
126
|
+
*/
|
|
127
|
+
secrets?: (env: TEnv) => A2ASecrets;
|
|
128
|
+
/**
|
|
129
|
+
* The audience a gateway JWT must carry.
|
|
130
|
+
*
|
|
131
|
+
* Defaults to this deployment's **own endpoint** — `${origin}${rpcPath}`, the
|
|
132
|
+
* same URL its cards advertise as their interface — and that default is
|
|
133
|
+
* almost certainly what you want. Setting this is for a gateway that mints
|
|
134
|
+
* something else.
|
|
135
|
+
*
|
|
136
|
+
* The audience is one half of a two-sided contract: whatever is required here
|
|
137
|
+
* has to be exactly what the calling gateway *mints*, and a mismatch is a 401
|
|
138
|
+
* on every request. looping-gateway mints
|
|
139
|
+
* `new URL(agent.a2aEndpoint).origin + pathname`, which is what this default
|
|
140
|
+
* matches.
|
|
141
|
+
*
|
|
142
|
+
* It proves the token was minted for **this deployment**, and deliberately
|
|
143
|
+
* says nothing about which agent on it — every tenant shares one endpoint and
|
|
144
|
+
* therefore one audience. {@link file://../a2a/verify.ts TENANT_CLAIM} is what
|
|
145
|
+
* separates them, and is the check to look at when reasoning about one agent
|
|
146
|
+
* spending another's token.
|
|
147
|
+
*/
|
|
148
|
+
audience?: string | ((url: URL) => string);
|
|
35
149
|
/**
|
|
36
150
|
* Advertise `securitySchemes` on the card. **Defaults to `false`** — see
|
|
37
151
|
* `BuildCardOptions.advertiseSecuritySchemes` for why, and read that note
|
|
@@ -40,6 +154,17 @@ export interface A2AWorkerOptions {
|
|
|
40
154
|
advertiseSecuritySchemes?: boolean;
|
|
41
155
|
/** Claim carrying the caller identity. Defaults to the Looping namespace. */
|
|
42
156
|
identityClaim?: string;
|
|
157
|
+
/**
|
|
158
|
+
* Claim carrying the authorized tenant. Defaults to the Looping namespace.
|
|
159
|
+
*
|
|
160
|
+
* Both claim names are one side of the same contract as `audience`: whatever
|
|
161
|
+
* is named here has to be exactly what the calling gateway *mints*. Override
|
|
162
|
+
* them together, or not at all — a deployment fronted by something other than
|
|
163
|
+
* looping-gateway that renames only the identity claim keeps reading the
|
|
164
|
+
* tenant from a key its gateway never sets, and every request 401s on the
|
|
165
|
+
* empty-tenant comparison below.
|
|
166
|
+
*/
|
|
167
|
+
tenantClaim?: string;
|
|
43
168
|
/**
|
|
44
169
|
* Require a `taskPushNotificationConfig` on every `SendMessage`.
|
|
45
170
|
*
|
|
@@ -54,9 +179,23 @@ export interface A2AWorkerOptions {
|
|
|
54
179
|
* Build the Worker `fetch` handler.
|
|
55
180
|
*
|
|
56
181
|
* ```ts
|
|
57
|
-
* const handler = createA2AWorker({
|
|
182
|
+
* const handler = createA2AWorker({
|
|
183
|
+
* manifest: hostManifest,
|
|
184
|
+
* tenants: {
|
|
185
|
+
* reactive: { manifest, resolveAgent: getAgent, startTurn }
|
|
186
|
+
* }
|
|
187
|
+
* });
|
|
58
188
|
* export default { fetch: handler } satisfies ExportedHandler<Env>;
|
|
59
189
|
* ```
|
|
190
|
+
*
|
|
191
|
+
* Two overloads, because `secrets` is optional for exactly one shape of `env`.
|
|
192
|
+
* An `env` carrying the two documented names needs no reader; anything else has
|
|
193
|
+
* to say where its keys live, and the type system is where that gets enforced —
|
|
194
|
+
* the alternative is a `parsePrivateJwk` failure on the first request, which is
|
|
195
|
+
* both later and much harder to read.
|
|
60
196
|
*/
|
|
61
|
-
export declare function createA2AWorker<TEnv extends A2ASecretsEnv>(options: A2AWorkerOptions): (request: Request, env: TEnv) => Promise<Response>;
|
|
197
|
+
export declare function createA2AWorker<TEnv extends A2ASecretsEnv>(options: A2AWorkerOptions<TEnv>): (request: Request, env: TEnv) => Promise<Response>;
|
|
198
|
+
export declare function createA2AWorker<TEnv extends object>(options: A2AWorkerOptions<TEnv> & {
|
|
199
|
+
secrets: (env: TEnv) => A2ASecrets;
|
|
200
|
+
}): (request: Request, env: TEnv) => Promise<Response>;
|
|
62
201
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAOL,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC;AAQxB,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH,4EAA4E;AAC5E,eAAO,MAAM,SAAS,2BAA2B,CAAC;AAKlD,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACzB,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,QAAQ,EAAE,aAAa,CAAC;IACxB,uDAAuD;IACvD,YAAY,EAAE,aAAa,CAAC;IAC5B,4EAA4E;IAC5E,SAAS,EAAE,WAAW,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB,CAAC,IAAI,GAAG,aAAa;IACpD;;;;;;;;;;;;;OAaG;IACH,QAAQ,EAAE,aAAa,CAAC;IACxB;;;;;;;;;;;OAWG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,UAAU,CAAC;IACpC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC,CAAC;IAC3C;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AA2ED;;;;;;;;;;;;;;;;;;GAkBG;AAEH,wBAAgB,eAAe,CAAC,IAAI,SAAS,aAAa,EACxD,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,GAC9B,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEtD,wBAAgB,eAAe,CAAC,IAAI,SAAS,MAAM,EACjD,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,GAAG;IAChC,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,UAAU,CAAC;CACpC,GACA,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC"}
|
package/dist/worker/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AGENT_CARD_PATH, SendMessageRequest } from "@a2a-js/sdk";
|
|
2
2
|
import { DefaultRequestHandler, JsonRpcTransportHandler, validateVersion } from "@a2a-js/sdk/server";
|
|
3
3
|
import { RequestMalformedError, toJsonRpcError } from "@a2a-js/sdk/errors";
|
|
4
|
-
import { buildBaseCard, parsePrivateJwk, publicCardJwks, signCard } from "../a2a/card.js";
|
|
4
|
+
import { A2A_RPC_PATH, buildBaseCard, parsePrivateJwk, publicCardJwks, signCard, signCardInPlace } from "../a2a/card.js";
|
|
5
5
|
import { buildCallContext, extensionHeaders } from "../a2a/context.js";
|
|
6
6
|
import { GatewayAuthError, bearerToken, verifyGatewayToken } from "../a2a/verify.js";
|
|
7
7
|
import { A2AExecutor } from "../a2a/executor.js";
|
|
@@ -23,6 +23,40 @@ import { parseGatewayOrigins } from "../env.js";
|
|
|
23
23
|
*
|
|
24
24
|
* No secret is ever shared in either direction: trust flows entirely on domains
|
|
25
25
|
* and asymmetric (Ed25519) signatures over public JWKS.
|
|
26
|
+
*
|
|
27
|
+
* **Several agents share one Worker, addressed by `tenant`.** They share one
|
|
28
|
+
* origin, one endpoint, one signing key and one card at the well-known path;
|
|
29
|
+
* what differs is the tenant id on every request. That is the A2A mechanism for
|
|
30
|
+
* exactly this — `AgentInterface.tenant` is "an opaque string used for routing
|
|
31
|
+
* requests to a specific agent or tenant when multiple agents are served behind
|
|
32
|
+
* a single A2A endpoint", and §8.3.2 requires a client to send the value the
|
|
33
|
+
* selected interface declared.
|
|
34
|
+
*
|
|
35
|
+
* A tenant is **required on every request**. There is no default and no
|
|
36
|
+
* implicit routing: an absent tenant is an error, because guessing one would
|
|
37
|
+
* mean picking an agent the caller never named.
|
|
38
|
+
*
|
|
39
|
+
* This replaces mounting a handler per path prefix. That could not work: the
|
|
40
|
+
* card is a **well-known URI**, which RFC 8615 defines per-authority, so only
|
|
41
|
+
* one card per origin is discoverable at the registered path — a gateway
|
|
42
|
+
* resolving `…/.well-known/agent-card.json` against the origin found whichever
|
|
43
|
+
* agent owned the bare path and pinned *its* key for all of them.
|
|
44
|
+
*
|
|
45
|
+
* Three routes:
|
|
46
|
+
*
|
|
47
|
+
* 1. `GET jwksPath` — the card-signing public JWKS, resolving every card's
|
|
48
|
+
* `jku`.
|
|
49
|
+
* 2. `GET …/.well-known/agent-card.json` — the signed **stub** card for the
|
|
50
|
+
* origin. Matched by suffix, since that path is fixed by the spec.
|
|
51
|
+
* 3. `POST rpcPath` — gateway-authenticated JSON-RPC, routed to the named
|
|
52
|
+
* tenant. `GetExtendedAgentCard` returns that tenant's own signed card;
|
|
53
|
+
* everything else runs a turn against its Durable Object.
|
|
54
|
+
*
|
|
55
|
+
* Two independent checks keep one tenant's traffic out of another's:
|
|
56
|
+
* `audience` proves the token was minted for this deployment, and
|
|
57
|
+
* {@link file://../a2a/verify.ts TENANT_CLAIM} proves it was minted for *this
|
|
58
|
+
* agent on it*. The second is what makes `tenant` more than an unauthenticated
|
|
59
|
+
* routing hint in the request body.
|
|
26
60
|
*/
|
|
27
61
|
/** Default path serving the card-signing public JWKS (the card's `jku`). */
|
|
28
62
|
export const JWKS_PATH = "/.well-known/jwks.json";
|
|
@@ -89,49 +123,90 @@ function pushConfigError(rpcBody) {
|
|
|
89
123
|
}
|
|
90
124
|
return undefined;
|
|
91
125
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Build the Worker `fetch` handler.
|
|
94
|
-
*
|
|
95
|
-
* ```ts
|
|
96
|
-
* const handler = createA2AWorker({ manifest, resolveAgent: getAgent, startTurn });
|
|
97
|
-
* export default { fetch: handler } satisfies ExportedHandler<Env>;
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
126
|
export function createA2AWorker(options) {
|
|
101
127
|
const jwksPath = options.jwksPath ?? JWKS_PATH;
|
|
128
|
+
const rpcPath = options.rpcPath ?? A2A_RPC_PATH;
|
|
102
129
|
const requirePushConfig = options.requirePushConfig ?? true;
|
|
130
|
+
// Nothing can be served without at least one agent, and a deployment that
|
|
131
|
+
// configured none is a startup mistake rather than a per-request one.
|
|
132
|
+
const tenantIds = Object.keys(options.tenants);
|
|
133
|
+
if (tenantIds.length === 0) {
|
|
134
|
+
throw new Error("createA2AWorker requires at least one tenant");
|
|
135
|
+
}
|
|
136
|
+
// …and one registered under `""` is the same mistake wearing a disguise. A
|
|
137
|
+
// request must name a tenant, and the empty string is how "named none" is
|
|
138
|
+
// spelled, so that agent is registered and permanently unreachable — every
|
|
139
|
+
// call to it is refused as a missing tenant before the lookup ever runs. The
|
|
140
|
+
// check above would otherwise report a deployment with no routable agent as
|
|
141
|
+
// satisfying "at least one tenant".
|
|
142
|
+
if (tenantIds.some((id) => !id)) {
|
|
143
|
+
throw new Error("createA2AWorker requires every tenant id to be non-empty: a request " +
|
|
144
|
+
"names its tenant, so an agent registered under '' can never be reached");
|
|
145
|
+
}
|
|
146
|
+
// The tenant id is caller-controlled, so the lookup is by *own* property.
|
|
147
|
+
// Plain indexing reaches `Object.prototype`, and every inherited name —
|
|
148
|
+
// `toString`, `constructor`, `__proto__` — comes back truthy, so a tenant
|
|
149
|
+
// called one of those slips past an `if (!agent)` guard and only fails later
|
|
150
|
+
// when a function is read off it. That is a 500 where the whole point of the
|
|
151
|
+
// guard is a 400 naming the unknown tenant.
|
|
152
|
+
const tenantAgent = (id) => Object.hasOwn(options.tenants, id) ? options.tenants[id] : undefined;
|
|
153
|
+
// Only reachable through the first overload, which has already established
|
|
154
|
+
// that `TEnv` carries the two documented names.
|
|
155
|
+
const readSecrets = options.secrets ??
|
|
156
|
+
((env) => ({
|
|
157
|
+
signingKey: env.A2A_SIGNING_KEY,
|
|
158
|
+
gatewayOrigins: env.GATEWAY_ORIGINS
|
|
159
|
+
}));
|
|
103
160
|
return async function fetch(request, env) {
|
|
104
161
|
const url = new URL(request.url);
|
|
105
162
|
const origin = url.origin;
|
|
106
|
-
const
|
|
107
|
-
|
|
163
|
+
const secrets = readSecrets(env);
|
|
164
|
+
// This agent's own endpoint, which is also exactly what its card advertises
|
|
165
|
+
// as its interface — so the value the gateway was registered with and the
|
|
166
|
+
// value checked here are the same string by construction.
|
|
167
|
+
const audience = typeof options.audience === "function"
|
|
168
|
+
? options.audience(url)
|
|
169
|
+
: (options.audience ?? `${origin}${rpcPath}`);
|
|
170
|
+
const privateJwk = parsePrivateJwk(secrets.signingKey);
|
|
171
|
+
const signing = { privateJwk, jku: `${origin}${jwksPath}` };
|
|
172
|
+
const cardFor = (manifest, tenant) => buildBaseCard(manifest, {
|
|
108
173
|
origin,
|
|
109
174
|
rpcPath: options.rpcPath,
|
|
110
|
-
advertiseSecuritySchemes: options.advertiseSecuritySchemes
|
|
111
|
-
|
|
112
|
-
|
|
175
|
+
advertiseSecuritySchemes: options.advertiseSecuritySchemes,
|
|
176
|
+
tenant
|
|
177
|
+
});
|
|
178
|
+
// (1) Card-signing public JWKS — resolves every card's `jku` for the
|
|
179
|
+
// gateway. One key per origin, so one JWKS for every tenant.
|
|
113
180
|
if (request.method === "GET" && url.pathname === jwksPath) {
|
|
114
181
|
return Response.json(publicCardJwks(privateJwk), {
|
|
115
182
|
headers: { "cache-control": "public, max-age=3600" }
|
|
116
183
|
});
|
|
117
184
|
}
|
|
118
|
-
// (2)
|
|
119
|
-
// encoding — the exact document the signature is computed
|
|
185
|
+
// (2) The stub card, at the well-known path. `signCard` returns the
|
|
186
|
+
// protobuf-JSON encoding — the exact document the signature is computed
|
|
187
|
+
// over — so it is serialized here without re-encoding.
|
|
120
188
|
if (request.method === "GET" && url.pathname.endsWith(AGENT_CARD_PATH)) {
|
|
121
|
-
|
|
122
|
-
return Response.json(card);
|
|
189
|
+
return Response.json(await signCard(cardFor(options.manifest), signing));
|
|
123
190
|
}
|
|
124
191
|
// (3) A2A JSON-RPC — gateway-authenticated, dispatched into the caller's DO.
|
|
125
|
-
|
|
192
|
+
//
|
|
193
|
+
// Matched on `rpcPath`, not on the method alone. Accepting every POST made
|
|
194
|
+
// the path this agent advertises purely decorative: a call to any URL on the
|
|
195
|
+
// origin was served as JSON-RPC, so a mounted agent's isolation rested
|
|
196
|
+
// entirely on an outer router matching first, and a typo'd endpoint quietly
|
|
197
|
+
// worked instead of 404ing.
|
|
198
|
+
if (request.method === "POST" && url.pathname === rpcPath) {
|
|
126
199
|
const token = bearerToken(request);
|
|
127
200
|
if (!token)
|
|
128
201
|
return unauthorized("missing gateway bearer token");
|
|
129
202
|
let identity;
|
|
203
|
+
let authorizedTenant;
|
|
130
204
|
try {
|
|
131
|
-
({ identity } = await verifyGatewayToken(token, {
|
|
132
|
-
allowedOrigins: parseGatewayOrigins(
|
|
133
|
-
audience
|
|
134
|
-
identityClaim: options.identityClaim
|
|
205
|
+
({ identity, tenant: authorizedTenant } = await verifyGatewayToken(token, {
|
|
206
|
+
allowedOrigins: parseGatewayOrigins(secrets.gatewayOrigins),
|
|
207
|
+
audience,
|
|
208
|
+
identityClaim: options.identityClaim,
|
|
209
|
+
tenantClaim: options.tenantClaim
|
|
135
210
|
}));
|
|
136
211
|
}
|
|
137
212
|
catch (err) {
|
|
@@ -152,8 +227,37 @@ export function createA2AWorker(options) {
|
|
|
152
227
|
// throwing on the property reads below instead of reaching the SDK
|
|
153
228
|
// handler, which maps it to the same -32602 that `[]` or a string gets.
|
|
154
229
|
const rpcBody = (typeof body === "object" && body !== null ? body : {});
|
|
155
|
-
|
|
156
|
-
|
|
230
|
+
// Which agent this call is for. Read straight off the body rather than
|
|
231
|
+
// through a codec: every request message carries `tenant` in the same
|
|
232
|
+
// place, so decoding as one specific request type would mean knowing the
|
|
233
|
+
// method first, and the tenant is needed to pick the handler that would
|
|
234
|
+
// decide that.
|
|
235
|
+
const requestedTenant = rpcBody.params?.tenant ?? "";
|
|
236
|
+
if (typeof requestedTenant !== "string" || !requestedTenant) {
|
|
237
|
+
return jsonRpcErrorResponse(body, toJsonRpcError(new RequestMalformedError("params.tenant is required: this endpoint serves several agents " +
|
|
238
|
+
"and none is the default. Use the tenant declared on the " +
|
|
239
|
+
"interface of the agent's card (A2A §8.3.2), or call " +
|
|
240
|
+
"GetExtendedAgentCard with it to fetch that card.")));
|
|
241
|
+
}
|
|
242
|
+
// The token names the tenant the gateway authorized; the body names the
|
|
243
|
+
// one the call addressed. They must agree.
|
|
244
|
+
//
|
|
245
|
+
// This is the check that makes `tenant` more than a routing hint. Every
|
|
246
|
+
// tenant here shares one endpoint and so one `aud`, which means the
|
|
247
|
+
// audience cannot tell them apart — without this, a token legitimately
|
|
248
|
+
// minted for one agent could be replayed against any of its siblings by
|
|
249
|
+
// editing a field in the request body. An absent claim is a rejection
|
|
250
|
+
// rather than a wildcard for the same reason.
|
|
251
|
+
if (authorizedTenant !== requestedTenant) {
|
|
252
|
+
return unauthorized(`gateway token authorizes tenant '${authorizedTenant || "<none>"}', ` +
|
|
253
|
+
`but the request addressed '${requestedTenant}'`);
|
|
254
|
+
}
|
|
255
|
+
const agent = tenantAgent(requestedTenant);
|
|
256
|
+
if (!agent) {
|
|
257
|
+
return jsonRpcErrorResponse(body, toJsonRpcError(new RequestMalformedError(`unknown tenant '${requestedTenant}'`)));
|
|
258
|
+
}
|
|
259
|
+
const card = cardFor(agent.manifest, requestedTenant);
|
|
260
|
+
const context = buildCallContext(request, identity, requestedTenant);
|
|
157
261
|
// v1.0 negotiates the protocol version per request via the `A2A-Version`
|
|
158
262
|
// header, and the SDK leaves enforcement to the transport binding (its
|
|
159
263
|
// Express handlers do it; this Worker is the equivalent seam). An absent
|
|
@@ -174,12 +278,31 @@ export function createA2AWorker(options) {
|
|
|
174
278
|
return jsonRpcErrorResponse(body, toJsonRpcError(new RequestMalformedError(contractError)));
|
|
175
279
|
}
|
|
176
280
|
}
|
|
177
|
-
const handler = new DefaultRequestHandler(card, new DurableTaskStore(identity,
|
|
281
|
+
const handler = new DefaultRequestHandler(card, new DurableTaskStore(identity, agent.resolveAgent), new A2AExecutor({
|
|
178
282
|
identity,
|
|
179
283
|
jku: `${origin}${jwksPath}`,
|
|
180
|
-
resolveAgent:
|
|
181
|
-
startTurn:
|
|
182
|
-
})
|
|
284
|
+
resolveAgent: agent.resolveAgent,
|
|
285
|
+
startTurn: agent.startTurn
|
|
286
|
+
}), undefined, undefined, undefined,
|
|
287
|
+
// `GetExtendedAgentCard` — how a caller gets a *tenant's* card, since
|
|
288
|
+
// only the stub is discoverable at the well-known path.
|
|
289
|
+
//
|
|
290
|
+
// Returns the in-memory card and lets the SDK encode it: its JSON-RPC
|
|
291
|
+
// transport runs `AgentCard.toJSON()` over whatever comes back, so
|
|
292
|
+
// handing it `signCard`'s already-encoded wire card would encode twice
|
|
293
|
+
// and silently break the signature. See {@link signCardInPlace}.
|
|
294
|
+
//
|
|
295
|
+
// The tenant is read off the context rather than the request params
|
|
296
|
+
// because that is all the SDK passes; it is the value this Worker
|
|
297
|
+
// already authorized and routed on.
|
|
298
|
+
async (ctx) => {
|
|
299
|
+
const requested = ctx.tenant ?? "";
|
|
300
|
+
const target = tenantAgent(requested);
|
|
301
|
+
if (!target) {
|
|
302
|
+
throw new RequestMalformedError(`unknown tenant '${requested}'`);
|
|
303
|
+
}
|
|
304
|
+
return signCardInPlace(cardFor(target.manifest, requested), signing);
|
|
305
|
+
});
|
|
183
306
|
const rpc = new JsonRpcTransportHandler(handler);
|
|
184
307
|
const result = await rpc.handle(body, context);
|
|
185
308
|
// Streaming is not advertised; reject async generators outright.
|
package/dist/worker/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loopingai/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Shared, mandatory foundation for Looping agents on Cloudflare Workers: zero-trust A2A, durable task lifecycle, delegation and subagent runtime, test harness.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"a2a",
|
|
@@ -69,6 +69,10 @@
|
|
|
69
69
|
"types": "./dist/testing/index.d.ts",
|
|
70
70
|
"import": "./dist/testing/index.js"
|
|
71
71
|
},
|
|
72
|
+
"./testing/fixtures": {
|
|
73
|
+
"types": "./dist/testing/fixtures.d.ts",
|
|
74
|
+
"import": "./dist/testing/fixtures.js"
|
|
75
|
+
},
|
|
72
76
|
"./testing/node": {
|
|
73
77
|
"types": "./dist/testing/node.d.ts",
|
|
74
78
|
"import": "./dist/testing/node.js"
|
|
@@ -102,13 +106,17 @@
|
|
|
102
106
|
"zod": "^4.4.3"
|
|
103
107
|
},
|
|
104
108
|
"peerDependencies": {
|
|
109
|
+
"@cloudflare/vitest-pool-workers": "^0.18.8",
|
|
105
110
|
"agents": "^0.20.0",
|
|
106
111
|
"ai": "^7.0.40",
|
|
107
|
-
"undici": "
|
|
112
|
+
"undici": "^7.28.0",
|
|
108
113
|
"vitest": ">=4",
|
|
109
114
|
"workers-ai-provider": "^4.0.0"
|
|
110
115
|
},
|
|
111
116
|
"peerDependenciesMeta": {
|
|
117
|
+
"@cloudflare/vitest-pool-workers": {
|
|
118
|
+
"optional": true
|
|
119
|
+
},
|
|
112
120
|
"undici": {
|
|
113
121
|
"optional": true
|
|
114
122
|
},
|
|
@@ -117,7 +125,7 @@
|
|
|
117
125
|
}
|
|
118
126
|
},
|
|
119
127
|
"devDependencies": {
|
|
120
|
-
"@cloudflare/vitest-pool-workers": "^0.
|
|
128
|
+
"@cloudflare/vitest-pool-workers": "^0.18.8",
|
|
121
129
|
"@types/node": "^26.1.1",
|
|
122
130
|
"agents": "^0.20.0",
|
|
123
131
|
"ai": "^7.0.40",
|
|
@@ -126,7 +134,7 @@
|
|
|
126
134
|
"prettier": "^3.9.6",
|
|
127
135
|
"typescript": "^6.0.3",
|
|
128
136
|
"typescript-eslint": "^8.65.0",
|
|
129
|
-
"undici": "
|
|
137
|
+
"undici": "7.28.0",
|
|
130
138
|
"vitest": "^4.1.10",
|
|
131
139
|
"workers-ai-provider": "^4.0.0",
|
|
132
140
|
"wrangler": "^4.114.0"
|