@loopingai/core 0.7.1 → 0.8.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 +3 -4
- package/dist/a2a/self-origin.d.ts +7 -7
- package/dist/a2a/self-origin.js +7 -7
- package/dist/agent/errors.d.ts +16 -16
- package/dist/agent/errors.js +1 -1
- package/dist/agent/inference.d.ts +7 -5
- package/dist/agent/inference.js +0 -2
- package/dist/agent/model.d.ts +11 -5
- package/dist/agent/workers-ai/index.d.ts +13 -13
- package/dist/agent/workers-ai/index.js +13 -13
- package/dist/config.d.ts +3 -18
- package/dist/config.js +0 -4
- package/dist/host/agent.d.ts +5 -6
- package/dist/host/agent.js +5 -6
- package/dist/round/subagent.d.ts +7 -4
- package/dist/round/subagent.js +7 -4
- package/dist/subtasks/delegate.d.ts +4 -3
- package/dist/subtasks/delegate.js +4 -3
- package/dist/testing/mock-model.js +1 -1
- package/package.json +1 -15
- package/dist/agent/anthropic/index.d.ts +0 -15
- package/dist/agent/anthropic/index.js +0 -19
- package/dist/agent/anthropic/language-model.d.ts +0 -59
- package/dist/agent/anthropic/language-model.js +0 -442
- package/dist/agent/anthropic/prompt.d.ts +0 -84
- package/dist/agent/anthropic/prompt.js +0 -541
- package/dist/agent/anthropic/runtime.d.ts +0 -79
- package/dist/agent/anthropic/runtime.js +0 -130
package/README.md
CHANGED
|
@@ -229,7 +229,6 @@ not drag in the A2A adapter, and the test harness cannot reach a production bund
|
|
|
229
229
|
| `@loopingai/core/agent` | session, history, models + Workers AI, inference, budget, control tools |
|
|
230
230
|
| `@loopingai/core/host` | `LoopingAgent` — the Durable Object body — and `PluginHost` |
|
|
231
231
|
| `@loopingai/core/round` | the delegating round loop: `RoundAgentBase`, `runHandleTask`, `runTurn` |
|
|
232
|
-
| `@loopingai/core/anthropic` | Claude as a second provider — _optional peer on `@anthropic-ai/sdk`_ |
|
|
233
232
|
| `@loopingai/core/subtasks` | delegation types, decomposition, the `delegate` tool |
|
|
234
233
|
| `@loopingai/core/subagent` | `RecipeSubagentBase`, resumable runs, fingerprinting, workspace |
|
|
235
234
|
| `@loopingai/core/db` | `AgentDB`, `notify_tasks` + `subtasks` schema, migrations, `PluginStore` |
|
|
@@ -271,8 +270,9 @@ performs. A gateway pins the card's `kid` + `jku` on first registration
|
|
|
271
270
|
|
|
272
271
|
### Calling out, and knowing your own origin
|
|
273
272
|
|
|
274
|
-
The same key proves this agent to services that are not the gateway —
|
|
275
|
-
|
|
273
|
+
The same key proves this agent to services that are not the gateway — another agent,
|
|
274
|
+
or any service that verifies against the published JWKS. `signCallerToken` mints the
|
|
275
|
+
short-lived token for that: `iss` is
|
|
276
276
|
this deployment's origin, `jku` is derived from it, and the audience is normalized to a
|
|
277
277
|
bare origin because the far side compares it byte-for-byte.
|
|
278
278
|
|
|
@@ -499,7 +499,6 @@ await withDb("accepts a turn once", async (db) => {
|
|
|
499
499
|
- **Bindings:** `AI`, one Durable Object, one Workflow
|
|
500
500
|
- **Secrets:** `A2A_SIGNING_KEY`, `GATEWAY_ORIGINS`
|
|
501
501
|
- **Peers, never bundled:** `agents`, `ai`, `workers-ai-provider`
|
|
502
|
-
- **Optional peer:** `@anthropic-ai/sdk`, needed only by `@loopingai/core/anthropic`
|
|
503
502
|
|
|
504
503
|
That last point is not stylistic: two copies of `agents` in one Worker breaks the
|
|
505
504
|
`Session` / `SessionMessage` types and every `instanceof`. For local development
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
* The obvious answer is a `SELF_ORIGIN` secret, and it is the wrong one. It
|
|
16
16
|
* restates a value the request already carries, and it has to be kept
|
|
17
17
|
* byte-identical by hand with the origin allowlist on the far side, in every
|
|
18
|
-
* environment, forever.
|
|
19
|
-
* `looping-
|
|
20
|
-
* and
|
|
21
|
-
*
|
|
18
|
+
* environment, forever. Every sibling that tried it took it back out —
|
|
19
|
+
* `looping-gateway` discovers its own origin from the first signature-verified
|
|
20
|
+
* request rather than being told, and a verifying Worker that once carried a
|
|
21
|
+
* configured audience replaced it with `url.origin`.
|
|
22
22
|
*
|
|
23
23
|
* ## Where the value comes from
|
|
24
24
|
*
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
* The cost of pinning is what an agent does not have: several identities. An
|
|
52
52
|
* agent has one endpoint — the one its card advertises, the one a gateway calls
|
|
53
53
|
* and a verifier allowlists — so there is nothing to follow. Note the asymmetry
|
|
54
|
-
* with
|
|
55
|
-
*
|
|
56
|
-
*
|
|
54
|
+
* with a *verifier*, which derives the audience it expects per request and must
|
|
55
|
+
* not cache it: a verifier has to accept every hostname it answers on, while a
|
|
56
|
+
* signer needs one stable identity.
|
|
57
57
|
*
|
|
58
58
|
* Nothing is persisted, which is what keeps a pin from outliving its truth. An
|
|
59
59
|
* isolate is fresh on every `wrangler deploy` and recycles on its own, so a moved
|
package/dist/a2a/self-origin.js
CHANGED
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
* The obvious answer is a `SELF_ORIGIN` secret, and it is the wrong one. It
|
|
16
16
|
* restates a value the request already carries, and it has to be kept
|
|
17
17
|
* byte-identical by hand with the origin allowlist on the far side, in every
|
|
18
|
-
* environment, forever.
|
|
19
|
-
* `looping-
|
|
20
|
-
* and
|
|
21
|
-
*
|
|
18
|
+
* environment, forever. Every sibling that tried it took it back out —
|
|
19
|
+
* `looping-gateway` discovers its own origin from the first signature-verified
|
|
20
|
+
* request rather than being told, and a verifying Worker that once carried a
|
|
21
|
+
* configured audience replaced it with `url.origin`.
|
|
22
22
|
*
|
|
23
23
|
* ## Where the value comes from
|
|
24
24
|
*
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
* The cost of pinning is what an agent does not have: several identities. An
|
|
52
52
|
* agent has one endpoint — the one its card advertises, the one a gateway calls
|
|
53
53
|
* and a verifier allowlists — so there is nothing to follow. Note the asymmetry
|
|
54
|
-
* with
|
|
55
|
-
*
|
|
56
|
-
*
|
|
54
|
+
* with a *verifier*, which derives the audience it expects per request and must
|
|
55
|
+
* not cache it: a verifier has to accept every hostname it answers on, while a
|
|
56
|
+
* signer needs one stable identity.
|
|
57
57
|
*
|
|
58
58
|
* Nothing is persisted, which is what keeps a pin from outliving its truth. An
|
|
59
59
|
* isolate is fresh on every `wrangler deploy` and recycles on its own, so a moved
|
package/dist/agent/errors.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* any particular one exists. `nonRecoverableKind` in
|
|
7
7
|
* {@link file://./inference.ts inference.ts} keys on this file, so a provider
|
|
8
8
|
* written outside core — the thing `ModelRuntimeFactory` exists to make cheap —
|
|
9
|
-
* gets the same handling as
|
|
9
|
+
* gets the same handling as core's own, with no change to core.
|
|
10
10
|
*
|
|
11
11
|
* ## Why a third classification was needed at all
|
|
12
12
|
*
|
|
@@ -24,26 +24,26 @@
|
|
|
24
24
|
/**
|
|
25
25
|
* Who refused the request, when a `401` came back.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* it was sends an operator to rotate the wrong secret — which is exactly what
|
|
33
|
-
* happened before this existed.
|
|
34
|
-
*
|
|
35
|
-
* `"proxy"` is the odd one out: it is generally *not* a secret to rotate. An
|
|
36
|
-
* intermediary that mints its caller credential per request fails for reasons
|
|
37
|
-
* upstream of any stored secret — configuration drift, a rotated signing key,
|
|
38
|
-
* clock skew — so the remedy is to look, not to rotate. Core recognises no
|
|
39
|
-
* particular intermediary; a deployment that has one supplies its own classifier
|
|
40
|
-
* (see `AnthropicModelDeps.classifyAuthFailure`).
|
|
27
|
+
* Two authorities sit on the path, each with its own credential: the AI Gateway
|
|
28
|
+
* (`cf-aig-authorization`) and the model provider itself (`Authorization`). They
|
|
29
|
+
* fail with the same status code and have completely different remedies, so a
|
|
30
|
+
* rejection that does not say which one it was sends an operator to rotate the
|
|
31
|
+
* wrong secret — which is exactly what happened before this existed.
|
|
41
32
|
*
|
|
42
33
|
* `"unknown"` is a real answer and the default. Guessing `"provider"` for an
|
|
43
34
|
* unrecognised body is how the misdiagnosis happens; saying "one of these, here
|
|
44
35
|
* is how to check each" is worse copy and better information.
|
|
36
|
+
*
|
|
37
|
+
* A third arm, `"proxy"`, named an optional intermediary between the two — the
|
|
38
|
+
* shape where a deployment terminates the gateway's request at its own Worker to
|
|
39
|
+
* attach a credential. It was removed in 0.8.0 with the deployment that had one.
|
|
40
|
+
* If you build that topology again, the honest classification for its refusals
|
|
41
|
+
* is `"unknown"` until you widen this union, because the remedy genuinely
|
|
42
|
+
* differs: an intermediary minting its caller credential per request fails for
|
|
43
|
+
* reasons upstream of any stored secret, so the fix is to look rather than to
|
|
44
|
+
* rotate.
|
|
45
45
|
*/
|
|
46
|
-
export type CredentialRejectedBy = "provider" | "gateway" | "
|
|
46
|
+
export type CredentialRejectedBy = "provider" | "gateway" | "unknown";
|
|
47
47
|
/**
|
|
48
48
|
* A credential on the path to the model was rejected (HTTP 401 / 403).
|
|
49
49
|
*
|
package/dist/agent/errors.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* any particular one exists. `nonRecoverableKind` in
|
|
7
7
|
* {@link file://./inference.ts inference.ts} keys on this file, so a provider
|
|
8
8
|
* written outside core — the thing `ModelRuntimeFactory` exists to make cheap —
|
|
9
|
-
* gets the same handling as
|
|
9
|
+
* gets the same handling as core's own, with no change to core.
|
|
10
10
|
*
|
|
11
11
|
* ## Why a third classification was needed at all
|
|
12
12
|
*
|
|
@@ -49,14 +49,16 @@ export declare function isTransientAiError(err: unknown): boolean;
|
|
|
49
49
|
* - `gateway-credential` — the gateway *in front of* the provider rejected the
|
|
50
50
|
* request, which the provider therefore never saw. Rotate the gateway's token
|
|
51
51
|
* instead; the model credential is very likely fine.
|
|
52
|
-
* - `proxy-credential` — an intermediary between the gateway and the provider
|
|
53
|
-
* rejected the caller. Notably **not** a token to rotate: the credential it
|
|
54
|
-
* refused is minted per request, so the fault is upstream of the secret —
|
|
55
|
-
* configuration drift, a rotated signing key, or clock skew.
|
|
56
52
|
* - `unknown-credential` — a `401`/`403` matching none of the shapes. Says so,
|
|
57
53
|
* rather than picking one and sending an operator to rotate a working secret.
|
|
54
|
+
*
|
|
55
|
+
* A fourth, `proxy-credential`, was removed in 0.8.0 along with
|
|
56
|
+
* {@link file://./errors.ts CredentialRejectedBy}'s `"proxy"` arm. Adding a kind
|
|
57
|
+
* back is a breaking change for every consumer, because the `Record` they map it
|
|
58
|
+
* with is total — which is the property that makes a new kind impossible to
|
|
59
|
+
* ignore, and the reason to remove one rather than leave it unreachable.
|
|
58
60
|
*/
|
|
59
|
-
export type NonRecoverableKind = "credential" | "gateway-credential" | "
|
|
61
|
+
export type NonRecoverableKind = "credential" | "gateway-credential" | "unknown-credential";
|
|
60
62
|
/**
|
|
61
63
|
* Why a round ended with no answer — one terminal status, two situations.
|
|
62
64
|
*
|
package/dist/agent/inference.js
CHANGED
|
@@ -80,8 +80,6 @@ export function nonRecoverableKind(err) {
|
|
|
80
80
|
return "credential";
|
|
81
81
|
case "gateway":
|
|
82
82
|
return "gateway-credential";
|
|
83
|
-
case "proxy":
|
|
84
|
-
return "proxy-credential";
|
|
85
83
|
// Includes an error that crossed a realm boundary carrying no `source` at
|
|
86
84
|
// all: `isInstance` is structural, so that is reachable, and "unknown" is
|
|
87
85
|
// the honest reading of it.
|
package/dist/agent/model.d.ts
CHANGED
|
@@ -10,11 +10,17 @@ import type { ModelConfig } from "../config.js";
|
|
|
10
10
|
* more than a `LanguageModel` from `ai`. It never learns which provider produced
|
|
11
11
|
* it.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
* {@link file://./workers-ai/index.ts `./workers-ai`}
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* Core ships exactly one implementation —
|
|
14
|
+
* {@link file://./workers-ai/index.ts `./workers-ai`} — as a sibling directory
|
|
15
|
+
* under `agent/`. A second provider is a second directory exporting one
|
|
16
|
+
* {@link ModelRuntimeFactory}, and nothing here has to change to admit it;
|
|
17
|
+
* a consumer can equally supply one from outside the package, which is the
|
|
18
|
+
* point of the seam.
|
|
19
|
+
*
|
|
20
|
+
* There was a `./anthropic` sibling until 0.8.0 — a hand-written Messages API
|
|
21
|
+
* adapter behind an optional peer dependency. It was removed with the only
|
|
22
|
+
* deployment that used it. Nothing about this contract changed when it went,
|
|
23
|
+
* which is the strongest thing that can be said for the contract.
|
|
18
24
|
*
|
|
19
25
|
* Which is why this file has no runtime imports at all. The Workers AI factory
|
|
20
26
|
* used to live in it, and a contract that ships one implementation inline reads
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workers AI — core's default model provider.
|
|
3
3
|
*
|
|
4
|
-
* A
|
|
5
|
-
* provider rather than a capability: it ships no tools, no prompt copy and no
|
|
4
|
+
* A provider rather than a capability: it ships no tools, no prompt copy and no
|
|
6
5
|
* policy; it satisfies {@link file://../model.ts ModelRuntime} and stops.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
7
|
+
* Two things about its shape are worth stating, because both look like
|
|
8
|
+
* omissions:
|
|
9
9
|
*
|
|
10
10
|
* **No `LanguageModel` adapter.** `workers-ai-provider` ships one, so this
|
|
11
|
-
* directory is a factory and nothing else
|
|
12
|
-
* adapter
|
|
13
|
-
*
|
|
11
|
+
* directory is a factory and nothing else — one file rather than the five a
|
|
12
|
+
* hand-written adapter costs (a prompt mapping, a cache-control policy, an
|
|
13
|
+
* error taxonomy). That is what makes it the cheap default.
|
|
14
14
|
*
|
|
15
|
-
* **No package subpath.**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
15
|
+
* **No package subpath.** `workers-ai-provider` is a *required* peer and this is
|
|
16
|
+
* the default every un-overridden seam reaches through `LoopingAgent` itself, so
|
|
17
|
+
* it is in every consumer's module graph already and a subpath would buy
|
|
18
|
+
* nothing. It is exported from `@loopingai/core/agent` — one symbol, one import
|
|
19
|
+
* path. A provider behind an *optional* peer would want its own subpath instead,
|
|
20
|
+
* so that an agent never calling it does not pay for it; `./anthropic` was one
|
|
21
|
+
* until 0.8.0.
|
|
22
22
|
*/
|
|
23
23
|
export { createWorkersAIModelRuntime, workersAIModels, type WorkersAIRuntimeDeps } from "./runtime.js";
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workers AI — core's default model provider.
|
|
3
3
|
*
|
|
4
|
-
* A
|
|
5
|
-
* provider rather than a capability: it ships no tools, no prompt copy and no
|
|
4
|
+
* A provider rather than a capability: it ships no tools, no prompt copy and no
|
|
6
5
|
* policy; it satisfies {@link file://../model.ts ModelRuntime} and stops.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
7
|
+
* Two things about its shape are worth stating, because both look like
|
|
8
|
+
* omissions:
|
|
9
9
|
*
|
|
10
10
|
* **No `LanguageModel` adapter.** `workers-ai-provider` ships one, so this
|
|
11
|
-
* directory is a factory and nothing else
|
|
12
|
-
* adapter
|
|
13
|
-
*
|
|
11
|
+
* directory is a factory and nothing else — one file rather than the five a
|
|
12
|
+
* hand-written adapter costs (a prompt mapping, a cache-control policy, an
|
|
13
|
+
* error taxonomy). That is what makes it the cheap default.
|
|
14
14
|
*
|
|
15
|
-
* **No package subpath.**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
15
|
+
* **No package subpath.** `workers-ai-provider` is a *required* peer and this is
|
|
16
|
+
* the default every un-overridden seam reaches through `LoopingAgent` itself, so
|
|
17
|
+
* it is in every consumer's module graph already and a subpath would buy
|
|
18
|
+
* nothing. It is exported from `@loopingai/core/agent` — one symbol, one import
|
|
19
|
+
* path. A provider behind an *optional* peer would want its own subpath instead,
|
|
20
|
+
* so that an agent never calling it does not pay for it; `./anthropic` was one
|
|
21
|
+
* until 0.8.0.
|
|
22
22
|
*/
|
|
23
23
|
export { createWorkersAIModelRuntime, workersAIModels } from "./runtime.js";
|
package/dist/config.d.ts
CHANGED
|
@@ -27,21 +27,6 @@ export interface ModelConfig {
|
|
|
27
27
|
fallbackChatModelId: string;
|
|
28
28
|
/** AI Gateway slug; `"default"` auto-provisions on first request. */
|
|
29
29
|
aiGatewayId: string;
|
|
30
|
-
/**
|
|
31
|
-
* The segment after the gateway slug in `.../{gateway}/{provider}` — either a
|
|
32
|
-
* provider-native path like `"anthropic"` or a
|
|
33
|
-
* [custom provider](https://developers.cloudflare.com/ai-gateway/configuration/custom-providers/)
|
|
34
|
-
* slug. Core never reads it; a `ModelRuntime` that builds its own base URL
|
|
35
|
-
* does.
|
|
36
|
-
*
|
|
37
|
-
* Config rather than a literal at that call site because the parent agent and
|
|
38
|
-
* its subagent facet resolve their config independently, and a value that
|
|
39
|
-
* drifts between them routes delegated subtasks through a different provider
|
|
40
|
-
* than the round that delegated them — silently, since both satisfy
|
|
41
|
-
* `ModelRuntime`. Which path to pick, and why, belongs with the agent that
|
|
42
|
-
* picks it.
|
|
43
|
-
*/
|
|
44
|
-
aiGatewayProvider: string;
|
|
45
30
|
/**
|
|
46
31
|
* Output-token ceiling for every chat call. Left unset, the binding applies a
|
|
47
32
|
* per-model default which a reasoning model spends on `reasoning_content`
|
|
@@ -71,9 +56,9 @@ export interface ModelConfig {
|
|
|
71
56
|
* The AI SDK does the waiting, and it does it properly:
|
|
72
57
|
* `retryWithExponentialBackoffRespectingRetryHeaders` honours `retry-after-ms`
|
|
73
58
|
* and `retry-after` and falls back to exponential backoff. It only fires for
|
|
74
|
-
* an `APICallError` carrying `isRetryable`,
|
|
75
|
-
*
|
|
76
|
-
*
|
|
59
|
+
* an `APICallError` carrying `isRetryable`, so a provider written outside core
|
|
60
|
+
* has to map its errors into that shape rather than rethrowing them raw, or
|
|
61
|
+
* none of this fires for it.
|
|
77
62
|
*
|
|
78
63
|
* Bounded at 4 by `resolveConfig`. The retries happen *inside*
|
|
79
64
|
* `step.do("turn:<round>")`, and the SDK caps a single honoured `retry-after`
|
package/dist/config.js
CHANGED
|
@@ -32,10 +32,6 @@ export const DEFAULT_CORE_CONFIG = {
|
|
|
32
32
|
// Not a model: an AI Gateway slug, and `"default"` is Cloudflare's own
|
|
33
33
|
// auto-provision behaviour rather than a choice core is making for anyone.
|
|
34
34
|
aiGatewayId: "default",
|
|
35
|
-
// The provider-native Anthropic endpoint, which is what a deployment with
|
|
36
|
-
// no custom provider registered has. Agents that must own the credential
|
|
37
|
-
// themselves override this with their `custom-*` slug.
|
|
38
|
-
aiGatewayProvider: "anthropic",
|
|
39
35
|
maxOutputTokens: 16_384,
|
|
40
36
|
reasoningEffort: "medium",
|
|
41
37
|
// Two, which is the AI SDK's own default and enough to ride out the kind of
|
package/dist/host/agent.d.ts
CHANGED
|
@@ -118,12 +118,11 @@ export declare abstract class LoopingAgent<TEnv extends Cloudflare.Env & AiEnv &
|
|
|
118
118
|
* a trap that only shows up as a performance bug. This is called once.
|
|
119
119
|
*
|
|
120
120
|
* `ModelRuntime` is the whole contract: return anything satisfying it and
|
|
121
|
-
* every loop in core keeps working unchanged. Core ships
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* anything on this path.
|
|
121
|
+
* every loop in core keeps working unchanged. Core ships one implementation,
|
|
122
|
+
* {@link file://../agent/workers-ai/index.ts `agent/workers-ai`} (the default
|
|
123
|
+
* below); a second provider is one more
|
|
124
|
+
* {@link file://../agent/model.ts ModelRuntimeFactory}, defined here or in the
|
|
125
|
+
* consumer, not a change to anything on this path.
|
|
127
126
|
*
|
|
128
127
|
* Takes the resolved {@link ModelConfig} rather than reading `this.config`, so
|
|
129
128
|
* that this signature matches
|
package/dist/host/agent.js
CHANGED
|
@@ -125,12 +125,11 @@ export class LoopingAgent extends Agent {
|
|
|
125
125
|
* a trap that only shows up as a performance bug. This is called once.
|
|
126
126
|
*
|
|
127
127
|
* `ModelRuntime` is the whole contract: return anything satisfying it and
|
|
128
|
-
* every loop in core keeps working unchanged. Core ships
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* anything on this path.
|
|
128
|
+
* every loop in core keeps working unchanged. Core ships one implementation,
|
|
129
|
+
* {@link file://../agent/workers-ai/index.ts `agent/workers-ai`} (the default
|
|
130
|
+
* below); a second provider is one more
|
|
131
|
+
* {@link file://../agent/model.ts ModelRuntimeFactory}, defined here or in the
|
|
132
|
+
* consumer, not a change to anything on this path.
|
|
134
133
|
*
|
|
135
134
|
* Takes the resolved {@link ModelConfig} rather than reading `this.config`, so
|
|
136
135
|
* that this signature matches
|
package/dist/round/subagent.d.ts
CHANGED
|
@@ -59,14 +59,17 @@ export declare abstract class RecipeSubagentHost<TEnv extends Cloudflare.Env & A
|
|
|
59
59
|
* Which provider this facet's chunks run on. Mirrors
|
|
60
60
|
* {@link file://../host/agent.ts LoopingAgent.modelRuntime}, and **must be
|
|
61
61
|
* overridden to match it** — a facet that keeps the Workers AI default while
|
|
62
|
-
* its parent runs on
|
|
63
|
-
* different model than the round that delegated it.
|
|
62
|
+
* its parent runs on another provider would silently execute every subtask on
|
|
63
|
+
* a different model than the round that delegated it.
|
|
64
64
|
*
|
|
65
65
|
* The two seams take the same arguments precisely so that keeping them in step
|
|
66
66
|
* needs no discipline: write the provider once as a
|
|
67
67
|
* {@link file://../agent/model.ts ModelRuntimeFactory} and have both return
|
|
68
|
-
* it. Two hand-copied
|
|
69
|
-
*
|
|
68
|
+
* it. Two hand-copied runtime-construction bodies is what this shape exists to
|
|
69
|
+
* stop, because nothing type-checks their agreement.
|
|
70
|
+
*
|
|
71
|
+
* Note the cheapest way to satisfy this is to override *neither* seam, which
|
|
72
|
+
* is what an agent on core's default does.
|
|
70
73
|
*
|
|
71
74
|
* Takes the model config rather than reading `this.config`, because the facet
|
|
72
75
|
* resolves its config inside `buildRuntime` and this is called from there.
|
package/dist/round/subagent.js
CHANGED
|
@@ -54,14 +54,17 @@ export class RecipeSubagentHost extends RecipeSubagentBase {
|
|
|
54
54
|
* Which provider this facet's chunks run on. Mirrors
|
|
55
55
|
* {@link file://../host/agent.ts LoopingAgent.modelRuntime}, and **must be
|
|
56
56
|
* overridden to match it** — a facet that keeps the Workers AI default while
|
|
57
|
-
* its parent runs on
|
|
58
|
-
* different model than the round that delegated it.
|
|
57
|
+
* its parent runs on another provider would silently execute every subtask on
|
|
58
|
+
* a different model than the round that delegated it.
|
|
59
59
|
*
|
|
60
60
|
* The two seams take the same arguments precisely so that keeping them in step
|
|
61
61
|
* needs no discipline: write the provider once as a
|
|
62
62
|
* {@link file://../agent/model.ts ModelRuntimeFactory} and have both return
|
|
63
|
-
* it. Two hand-copied
|
|
64
|
-
*
|
|
63
|
+
* it. Two hand-copied runtime-construction bodies is what this shape exists to
|
|
64
|
+
* stop, because nothing type-checks their agreement.
|
|
65
|
+
*
|
|
66
|
+
* Note the cheapest way to satisfy this is to override *neither* seam, which
|
|
67
|
+
* is what an agent on core's default does.
|
|
65
68
|
*
|
|
66
69
|
* Takes the model config rather than reading `this.config`, because the facet
|
|
67
70
|
* resolves its config inside `buildRuntime` and this is called from there.
|
|
@@ -54,9 +54,10 @@ export declare function makeDelegateTool(types: SubtaskTypeRegistry, maxSubtasks
|
|
|
54
54
|
* why it took a Claude-backed agent to surface it.
|
|
55
55
|
*
|
|
56
56
|
* Nothing persists this: both halves of the pair are rebuilt together on every
|
|
57
|
-
* request, so changing the shape needs no migration.
|
|
58
|
-
*
|
|
59
|
-
*
|
|
57
|
+
* request, so changing the shape needs no migration. There is no longer a
|
|
58
|
+
* provider-side backstop in core — the adapter that carried one went with
|
|
59
|
+
* `./anthropic` in 0.8.0 — so a provider added here that validates tool-call ids
|
|
60
|
+
* needs to sanitize them on its own way out.
|
|
60
61
|
*/
|
|
61
62
|
export declare function delegateToolCallId(taskId: string, round: number): string;
|
|
62
63
|
/**
|
|
@@ -60,9 +60,10 @@ export function makeDelegateTool(types, maxSubtasks) {
|
|
|
60
60
|
* why it took a Claude-backed agent to surface it.
|
|
61
61
|
*
|
|
62
62
|
* Nothing persists this: both halves of the pair are rebuilt together on every
|
|
63
|
-
* request, so changing the shape needs no migration.
|
|
64
|
-
*
|
|
65
|
-
*
|
|
63
|
+
* request, so changing the shape needs no migration. There is no longer a
|
|
64
|
+
* provider-side backstop in core — the adapter that carried one went with
|
|
65
|
+
* `./anthropic` in 0.8.0 — so a provider added here that validates tool-call ids
|
|
66
|
+
* needs to sanitize them on its own way out.
|
|
66
67
|
*/
|
|
67
68
|
export function delegateToolCallId(taskId, round) {
|
|
68
69
|
return `task_${taskId}_round_${round}_delegate`;
|
|
@@ -122,7 +122,7 @@ export function rateLimitedModel(failures, ...steps) {
|
|
|
122
122
|
if (calls <= failures) {
|
|
123
123
|
throw new APICallError({
|
|
124
124
|
message: "429 Wholesale Rate limited",
|
|
125
|
-
url: "
|
|
125
|
+
url: "mock:chat:test",
|
|
126
126
|
requestBodyValues: {},
|
|
127
127
|
statusCode: 429,
|
|
128
128
|
responseHeaders: { "retry-after": "0" }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loopingai/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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",
|
|
@@ -94,10 +94,6 @@
|
|
|
94
94
|
"import": "./dist/testing/vcr-global-setup.js"
|
|
95
95
|
},
|
|
96
96
|
"./eslint": "./eslint-rules/index.js",
|
|
97
|
-
"./anthropic": {
|
|
98
|
-
"types": "./dist/agent/anthropic/index.d.ts",
|
|
99
|
-
"import": "./dist/agent/anthropic/index.js"
|
|
100
|
-
},
|
|
101
97
|
"./package.json": "./package.json"
|
|
102
98
|
},
|
|
103
99
|
"scripts": {
|
|
@@ -123,8 +119,6 @@
|
|
|
123
119
|
"zod": "^4.4.3"
|
|
124
120
|
},
|
|
125
121
|
"peerDependencies": {
|
|
126
|
-
"@ai-sdk/provider": "^4.0.0",
|
|
127
|
-
"@anthropic-ai/sdk": "^0.116.0",
|
|
128
122
|
"@cloudflare/vitest-pool-workers": ">=0.18",
|
|
129
123
|
"@typescript-eslint/utils": ">=8",
|
|
130
124
|
"agents": "^0.20.0",
|
|
@@ -133,12 +127,6 @@
|
|
|
133
127
|
"workers-ai-provider": "^4.0.0"
|
|
134
128
|
},
|
|
135
129
|
"peerDependenciesMeta": {
|
|
136
|
-
"@ai-sdk/provider": {
|
|
137
|
-
"optional": true
|
|
138
|
-
},
|
|
139
|
-
"@anthropic-ai/sdk": {
|
|
140
|
-
"optional": true
|
|
141
|
-
},
|
|
142
130
|
"@cloudflare/vitest-pool-workers": {
|
|
143
131
|
"optional": true
|
|
144
132
|
},
|
|
@@ -150,8 +138,6 @@
|
|
|
150
138
|
}
|
|
151
139
|
},
|
|
152
140
|
"devDependencies": {
|
|
153
|
-
"@ai-sdk/provider": "^4.0.0",
|
|
154
|
-
"@anthropic-ai/sdk": "^0.116.0",
|
|
155
141
|
"@cloudflare/vitest-pool-workers": "^0.20.1",
|
|
156
142
|
"@types/node": "^26.1.1",
|
|
157
143
|
"agents": "^0.20.0",
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@loopingai/core/anthropic` — Claude as a second model provider.
|
|
3
|
-
*
|
|
4
|
-
* Its own subpath, and an **optional** peer dependency on `@anthropic-ai/sdk`,
|
|
5
|
-
* for the same reason `/round` is not re-exported from the root barrel: an agent
|
|
6
|
-
* that runs on Workers AI should not pay — in install size, in bundle bytes, or
|
|
7
|
-
* in a dependency it must keep current — for a provider it never calls.
|
|
8
|
-
*
|
|
9
|
-
* What lives here is a provider, not a capability. It ships no tools, no prompt
|
|
10
|
-
* copy and no policy; it satisfies {@link ModelRuntime} and stops.
|
|
11
|
-
*/
|
|
12
|
-
export { CredentialRejectedError, type CredentialRejectedBy } from "../errors.js";
|
|
13
|
-
export { createAnthropicLanguageModel, type AnthropicModelDeps } from "./language-model.js";
|
|
14
|
-
export { createAnthropicModelRuntime, type AnthropicRuntimeDeps } from "./runtime.js";
|
|
15
|
-
export { ANTHROPIC_PROVIDER, type CacheTtl } from "./prompt.js";
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@loopingai/core/anthropic` — Claude as a second model provider.
|
|
3
|
-
*
|
|
4
|
-
* Its own subpath, and an **optional** peer dependency on `@anthropic-ai/sdk`,
|
|
5
|
-
* for the same reason `/round` is not re-exported from the root barrel: an agent
|
|
6
|
-
* that runs on Workers AI should not pay — in install size, in bundle bytes, or
|
|
7
|
-
* in a dependency it must keep current — for a provider it never calls.
|
|
8
|
-
*
|
|
9
|
-
* What lives here is a provider, not a capability. It ships no tools, no prompt
|
|
10
|
-
* copy and no policy; it satisfies {@link ModelRuntime} and stops.
|
|
11
|
-
*/
|
|
12
|
-
// Re-exported, not owned: a rejected credential is a fact about the path to a
|
|
13
|
-
// model, not about Anthropic, so the error lives with the rest of the provider
|
|
14
|
-
// contract in {@link file://../errors.ts}. An agent that only imports this
|
|
15
|
-
// subpath still gets it from one place.
|
|
16
|
-
export { CredentialRejectedError } from "../errors.js";
|
|
17
|
-
export { createAnthropicLanguageModel } from "./language-model.js";
|
|
18
|
-
export { createAnthropicModelRuntime } from "./runtime.js";
|
|
19
|
-
export { ANTHROPIC_PROVIDER } from "./prompt.js";
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type Anthropic from "@anthropic-ai/sdk";
|
|
2
|
-
import type { LanguageModelV4 } from "@ai-sdk/provider";
|
|
3
|
-
import { type CredentialRejectedBy } from "../errors.js";
|
|
4
|
-
import { type CacheTtl } from "./prompt.js";
|
|
5
|
-
/**
|
|
6
|
-
* A `LanguageModelV4` over `@anthropic-ai/sdk`, so core's loops can call Claude
|
|
7
|
-
* without knowing they are.
|
|
8
|
-
*
|
|
9
|
-
* Every loop in core is written against `generateText` from `ai` and a
|
|
10
|
-
* `LanguageModel` — {@link file://../../round/turn.ts turn.ts},
|
|
11
|
-
* {@link file://../../subagent/run.ts run.ts} and
|
|
12
|
-
* {@link file://../session.ts session.ts}. Satisfying that interface is what
|
|
13
|
-
* keeps the round loop, the control-tool repair ladder, subtask execution and
|
|
14
|
-
* the Workflow untouched by a second provider. The alternative — a bespoke
|
|
15
|
-
* Messages-API loop for one agent — would have given all of that up.
|
|
16
|
-
*
|
|
17
|
-
* `ai@7` accepts `LanguageModelV2 | V3 | V4`; this targets **v4**, the newest
|
|
18
|
-
* the installed `@ai-sdk/provider` defines.
|
|
19
|
-
*/
|
|
20
|
-
/** How the adapter reaches the API. Everything is injected so nothing reads env. */
|
|
21
|
-
export interface AnthropicModelDeps {
|
|
22
|
-
/**
|
|
23
|
-
* Constructed lazily by the runtime — see the note in
|
|
24
|
-
* {@link file://./runtime.ts}. Awaited, because building it has to resolve
|
|
25
|
-
* `env.AI.gateway(id).getUrl()`, which is async.
|
|
26
|
-
*/
|
|
27
|
-
client: () => Anthropic | Promise<Anthropic>;
|
|
28
|
-
/** Anthropic model id, e.g. `claude-opus-5`. */
|
|
29
|
-
modelId: string;
|
|
30
|
-
/** `max_tokens` when a caller supplies none. Anthropic requires the field. */
|
|
31
|
-
defaultMaxTokens: number;
|
|
32
|
-
/** Prompt-cache TTL, or `false` to place no breakpoints. Defaults to `"5m"`. */
|
|
33
|
-
cache?: CacheTtl | false;
|
|
34
|
-
/**
|
|
35
|
-
* Reasoning effort, when the caller does not set one per-call.
|
|
36
|
-
*
|
|
37
|
-
* Maps to `output_config.effort`. Coding and agentic work wants `"xhigh"`;
|
|
38
|
-
* `"high"` is the API default. Core never picks this — an agent does.
|
|
39
|
-
*/
|
|
40
|
-
effort?: "low" | "medium" | "high" | "xhigh" | "max";
|
|
41
|
-
/** Extra headers merged into every request (AI Gateway metadata lives here). */
|
|
42
|
-
headers?: Record<string, string>;
|
|
43
|
-
/**
|
|
44
|
-
* Recognise a deployment-specific authority in a `401`/`403` body.
|
|
45
|
-
*
|
|
46
|
-
* Consulted before the built-in shapes; return `undefined` to fall through to
|
|
47
|
-
* them. It exists because a deployment may put an authenticated intermediary
|
|
48
|
-
* between the gateway and Anthropic, and only that deployment knows what its
|
|
49
|
-
* refusal looks like — core recognising one particular proxy's error body
|
|
50
|
-
* would be exactly the deployment policy this package does not ship.
|
|
51
|
-
*
|
|
52
|
-
* The remedy is what makes it worth distinguishing at all: a proxy that mints
|
|
53
|
-
* its caller credential per request has no secret to rotate, so reporting its
|
|
54
|
-
* `401` as `credential` sends an operator to replace a working token. See
|
|
55
|
-
* {@link file://../errors.ts CredentialRejectedBy}.
|
|
56
|
-
*/
|
|
57
|
-
classifyAuthFailure?: (body: unknown) => CredentialRejectedBy | undefined;
|
|
58
|
-
}
|
|
59
|
-
export declare function createAnthropicLanguageModel(deps: AnthropicModelDeps): LanguageModelV4;
|