@loops-adk/core 0.1.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/LICENSE +21 -0
- package/README.md +486 -0
- package/bin/loops.mjs +16 -0
- package/dist/App-3YQS6DXA.js +461 -0
- package/dist/App-3YQS6DXA.js.map +1 -0
- package/dist/agent-sdk-RF5VJZAT.js +95 -0
- package/dist/agent-sdk-RF5VJZAT.js.map +1 -0
- package/dist/anthropic-api-XJY6Y4T2.js +131 -0
- package/dist/anthropic-api-XJY6Y4T2.js.map +1 -0
- package/dist/api.d.ts +949 -0
- package/dist/api.js +898 -0
- package/dist/api.js.map +1 -0
- package/dist/chunk-33YIGWNU.js +63 -0
- package/dist/chunk-33YIGWNU.js.map +1 -0
- package/dist/chunk-3BPU34DE.js +2163 -0
- package/dist/chunk-3BPU34DE.js.map +1 -0
- package/dist/chunk-CXEPZHSR.js +86 -0
- package/dist/chunk-CXEPZHSR.js.map +1 -0
- package/dist/chunk-I3STY7U6.js +61 -0
- package/dist/chunk-I3STY7U6.js.map +1 -0
- package/dist/chunk-JFTXJ7I2.js +18 -0
- package/dist/chunk-JFTXJ7I2.js.map +1 -0
- package/dist/chunk-XC46B4FD.js +9 -0
- package/dist/chunk-XC46B4FD.js.map +1 -0
- package/dist/chunk-Y2SD7GBL.js +30 -0
- package/dist/chunk-Y2SD7GBL.js.map +1 -0
- package/dist/claude-cli-U7WEVAOL.js +124 -0
- package/dist/claude-cli-U7WEVAOL.js.map +1 -0
- package/dist/codex-6I5UZ2HM.js +60 -0
- package/dist/codex-6I5UZ2HM.js.map +1 -0
- package/dist/env/command.d.ts +53 -0
- package/dist/env/command.js +3 -0
- package/dist/env/command.js.map +1 -0
- package/dist/env/docker.d.ts +38 -0
- package/dist/env/docker.js +33 -0
- package/dist/env/docker.js.map +1 -0
- package/dist/env/sst.d.ts +39 -0
- package/dist/env/sst.js +20 -0
- package/dist/env/sst.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +620 -0
- package/dist/index.js.map +1 -0
- package/dist/types-B4wGVpqo.d.ts +898 -0
- package/package.json +100 -0
- package/skills/author-loop/SKILL.md +121 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { retryAfterHeaderToMs } from './chunk-Y2SD7GBL.js';
|
|
2
|
+
import { LoopError } from './chunk-I3STY7U6.js';
|
|
3
|
+
import pRetry, { AbortError } from 'p-retry';
|
|
4
|
+
|
|
5
|
+
function isTransient(error) {
|
|
6
|
+
const status = error?.status;
|
|
7
|
+
if (typeof status === "number") return status >= 500;
|
|
8
|
+
const name = error?.name ?? "";
|
|
9
|
+
return /connection|timeout|socket|network/i.test(name);
|
|
10
|
+
}
|
|
11
|
+
function headerValue(error, name) {
|
|
12
|
+
const headers = error.headers;
|
|
13
|
+
if (headers && typeof headers.get === "function") {
|
|
14
|
+
return headers.get(name) ?? void 0;
|
|
15
|
+
}
|
|
16
|
+
if (headers && typeof headers === "object") {
|
|
17
|
+
const v = headers[name];
|
|
18
|
+
if (typeof v === "string") return v;
|
|
19
|
+
}
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
function classifyLimit(error) {
|
|
23
|
+
const status = error?.status;
|
|
24
|
+
const type = error?.type;
|
|
25
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
26
|
+
if (status === 429 || type === "rate_limit_error") {
|
|
27
|
+
return new LoopError({
|
|
28
|
+
code: "RATE_LIMIT",
|
|
29
|
+
phase: "engine",
|
|
30
|
+
message: `anthropic-api rate limited: ${message}`,
|
|
31
|
+
cause: error,
|
|
32
|
+
retryAfterMs: retryAfterHeaderToMs(headerValue(error, "retry-after"))
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (type === "billing_error") {
|
|
36
|
+
return new LoopError({
|
|
37
|
+
code: "QUOTA",
|
|
38
|
+
phase: "engine",
|
|
39
|
+
message: `anthropic-api usage/billing limit: ${message}`,
|
|
40
|
+
cause: error
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
var AnthropicApiEngine = class {
|
|
46
|
+
constructor(opts = {}) {
|
|
47
|
+
this.opts = opts;
|
|
48
|
+
}
|
|
49
|
+
opts;
|
|
50
|
+
name = "anthropic-api";
|
|
51
|
+
clientPromise;
|
|
52
|
+
async client() {
|
|
53
|
+
if (!this.clientPromise) {
|
|
54
|
+
const apiKey = this.opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
55
|
+
if (!apiKey) {
|
|
56
|
+
throw new LoopError({
|
|
57
|
+
code: "CONFIG",
|
|
58
|
+
phase: "engine",
|
|
59
|
+
retryable: false,
|
|
60
|
+
message: "the anthropic-api engine needs an API key \u2014 set ANTHROPIC_API_KEY or pass --api-key (or use the agent-sdk / claude-cli engine, which use host Claude auth)"
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
this.clientPromise = import('@anthropic-ai/sdk').then(
|
|
64
|
+
// One honest cast at the boundary to the structural shape we consume.
|
|
65
|
+
(m) => new m.default({ apiKey })
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return this.clientPromise;
|
|
69
|
+
}
|
|
70
|
+
async run(req, onEvent, signal) {
|
|
71
|
+
const client = await this.client();
|
|
72
|
+
const model = req.model ?? this.opts.defaultModel ?? "claude-haiku-4-5-20251001";
|
|
73
|
+
const maxTokens = req.maxTokens ?? 1024;
|
|
74
|
+
const attempt = async () => {
|
|
75
|
+
try {
|
|
76
|
+
const stream = client.messages.stream(
|
|
77
|
+
{
|
|
78
|
+
model,
|
|
79
|
+
max_tokens: maxTokens,
|
|
80
|
+
system: req.system,
|
|
81
|
+
messages: [{ role: "user", content: req.prompt }]
|
|
82
|
+
},
|
|
83
|
+
{ signal }
|
|
84
|
+
);
|
|
85
|
+
stream.on("text", (delta) => onEvent({ type: "text", delta }));
|
|
86
|
+
return await stream.finalMessage();
|
|
87
|
+
} catch (e) {
|
|
88
|
+
const limit = classifyLimit(e);
|
|
89
|
+
if (limit) throw new AbortError(limit);
|
|
90
|
+
if (signal.aborted || !isTransient(e)) {
|
|
91
|
+
throw new AbortError(e instanceof Error ? e : new Error(String(e)));
|
|
92
|
+
}
|
|
93
|
+
throw e;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
let message;
|
|
97
|
+
try {
|
|
98
|
+
message = await pRetry(attempt, {
|
|
99
|
+
retries: 2,
|
|
100
|
+
minTimeout: 500,
|
|
101
|
+
factor: 2
|
|
102
|
+
});
|
|
103
|
+
} catch (e) {
|
|
104
|
+
if (signal.aborted)
|
|
105
|
+
throw new LoopError({
|
|
106
|
+
code: "ABORTED",
|
|
107
|
+
phase: "engine",
|
|
108
|
+
message: "anthropic-api run aborted"
|
|
109
|
+
});
|
|
110
|
+
if (e instanceof LoopError) throw e;
|
|
111
|
+
throw LoopError.from(e, { code: "ENGINE", phase: "engine" });
|
|
112
|
+
}
|
|
113
|
+
const text = message.content.filter((b) => b.type === "text").map((b) => b.text ?? "").join("");
|
|
114
|
+
const usage = {
|
|
115
|
+
inputTokens: message.usage.input_tokens,
|
|
116
|
+
outputTokens: message.usage.output_tokens
|
|
117
|
+
};
|
|
118
|
+
onEvent({ type: "usage", usage, model });
|
|
119
|
+
return {
|
|
120
|
+
text,
|
|
121
|
+
usage,
|
|
122
|
+
model,
|
|
123
|
+
stopReason: message.stop_reason ?? void 0,
|
|
124
|
+
raw: message
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export { AnthropicApiEngine };
|
|
130
|
+
//# sourceMappingURL=anthropic-api-XJY6Y4T2.js.map
|
|
131
|
+
//# sourceMappingURL=anthropic-api-XJY6Y4T2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/engines/anthropic-api.ts"],"names":[],"mappings":";;;;AA4BA,SAAS,YAAY,KAAA,EAAyB;AAC5C,EAAA,MAAM,SAAU,KAAA,EAA+B,MAAA;AAC/C,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,EAAU,OAAO,MAAA,IAAU,GAAA;AACjD,EAAA,MAAM,IAAA,GAAQ,OAA6B,IAAA,IAAQ,EAAA;AACnD,EAAA,OAAO,oCAAA,CAAqC,KAAK,IAAI,CAAA;AACvD;AAOA,SAAS,WAAA,CAAY,OAAgB,IAAA,EAAkC;AACrE,EAAA,MAAM,UAAW,KAAA,CAAgC,OAAA;AACjD,EAAA,IAAI,OAAA,IAAW,OAAQ,OAAA,CAAoB,GAAA,KAAQ,UAAA,EAAY;AAC7D,IAAA,OAAQ,OAAA,CAAoB,GAAA,CAAI,IAAI,CAAA,IAAK,MAAA;AAAA,EAC3C;AACA,EAAA,IAAI,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AAC1C,IAAA,MAAM,CAAA,GAAK,QAAoC,IAAI,CAAA;AACnD,IAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,CAAA;AAAA,EACpC;AACA,EAAA,OAAO,MAAA;AACT;AAYA,SAAS,cAAc,KAAA,EAAuC;AAC5D,EAAA,MAAM,SAAU,KAAA,EAA+B,MAAA;AAC/C,EAAA,MAAM,OAAQ,KAAA,EAA6B,IAAA;AAC3C,EAAA,MAAM,UAAU,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AAErE,EAAA,IAAI,MAAA,KAAW,GAAA,IAAO,IAAA,KAAS,kBAAA,EAAoB;AACjD,IAAA,OAAO,IAAI,SAAA,CAAU;AAAA,MACnB,IAAA,EAAM,YAAA;AAAA,MACN,KAAA,EAAO,QAAA;AAAA,MACP,OAAA,EAAS,+BAA+B,OAAO,CAAA,CAAA;AAAA,MAC/C,KAAA,EAAO,KAAA;AAAA,MACP,YAAA,EAAc,oBAAA,CAAqB,WAAA,CAAY,KAAA,EAAO,aAAa,CAAC;AAAA,KACrE,CAAA;AAAA,EACH;AACA,EAAA,IAAI,SAAS,eAAA,EAAiB;AAC5B,IAAA,OAAO,IAAI,SAAA,CAAU;AAAA,MACnB,IAAA,EAAM,OAAA;AAAA,MACN,KAAA,EAAO,QAAA;AAAA,MACP,OAAA,EAAS,sCAAsC,OAAO,CAAA,CAAA;AAAA,MACtD,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACA,EAAA,OAAO,MAAA;AACT;AAkBO,IAAM,qBAAN,MAA2C;AAAA,EAIhD,WAAA,CAA6B,IAAA,GAAsB,EAAC,EAAG;AAA1B,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAA2B;AAAA,EAA3B,IAAA;AAAA,EAHpB,IAAA,GAAO,eAAA;AAAA,EACR,aAAA;AAAA,EAIR,MAAc,MAAA,GAAsC;AAClD,IAAA,IAAI,CAAC,KAAK,aAAA,EAAe;AACvB,MAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,MAAA,IAAU,QAAQ,GAAA,CAAI,iBAAA;AAC/C,MAAA,IAAI,CAAC,MAAA,EAAQ;AAGX,QAAA,MAAM,IAAI,SAAA,CAAU;AAAA,UAClB,IAAA,EAAM,QAAA;AAAA,UACN,KAAA,EAAO,QAAA;AAAA,UACP,SAAA,EAAW,KAAA;AAAA,UACX,OAAA,EACE;AAAA,SACH,CAAA;AAAA,MACH;AACA,MAAA,IAAA,CAAK,aAAA,GAAgB,OAAO,mBAAmB,CAAA,CAAE,IAAA;AAAA;AAAA,QAE/C,CAAC,CAAA,KAAM,IAAI,EAAE,OAAA,CAAQ,EAAE,QAAQ;AAAA,OACjC;AAAA,IACF;AACA,IAAA,OAAO,IAAA,CAAK,aAAA;AAAA,EACd;AAAA,EAEA,MAAM,GAAA,CACJ,GAAA,EACA,OAAA,EACA,MAAA,EACsB;AACtB,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,EAAO;AACjC,IAAA,MAAM,KAAA,GACJ,GAAA,CAAI,KAAA,IAAS,IAAA,CAAK,KAAK,YAAA,IAAgB,2BAAA;AACzC,IAAA,MAAM,SAAA,GAAY,IAAI,SAAA,IAAa,IAAA;AAEnC,IAAA,MAAM,UAAU,YAAmC;AACjD,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,OAAO,QAAA,CAAS,MAAA;AAAA,UAC7B;AAAA,YACE,KAAA;AAAA,YACA,UAAA,EAAY,SAAA;AAAA,YACZ,QAAQ,GAAA,CAAI,MAAA;AAAA,YACZ,QAAA,EAAU,CAAC,EAAE,IAAA,EAAM,QAAQ,OAAA,EAAS,GAAA,CAAI,QAAQ;AAAA,WAClD;AAAA,UACA,EAAE,MAAA;AAAO,SACX;AACA,QAAA,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,CAAC,KAAA,KAAU,OAAA,CAAQ,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,CAAC,CAAA;AAC7D,QAAA,OAAO,MAAM,OAAO,YAAA,EAAa;AAAA,MACnC,SAAS,CAAA,EAAG;AAIV,QAAA,MAAM,KAAA,GAAQ,cAAc,CAAC,CAAA;AAC7B,QAAA,IAAI,KAAA,EAAO,MAAM,IAAI,UAAA,CAAW,KAAK,CAAA;AACrC,QAAA,IAAI,MAAA,CAAO,OAAA,IAAW,CAAC,WAAA,CAAY,CAAC,CAAA,EAAG;AACrC,UAAA,MAAM,IAAI,UAAA,CAAW,CAAA,YAAa,KAAA,GAAQ,CAAA,GAAI,IAAI,KAAA,CAAM,MAAA,CAAO,CAAC,CAAC,CAAC,CAAA;AAAA,QACpE;AACA,QAAA,MAAM,CAAA;AAAA,MACR;AAAA,IACF,CAAA;AAEA,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,MAAM,OAAO,OAAA,EAAS;AAAA,QAC9B,OAAA,EAAS,CAAA;AAAA,QACT,UAAA,EAAY,GAAA;AAAA,QACZ,MAAA,EAAQ;AAAA,OACT,CAAA;AAAA,IACH,SAAS,CAAA,EAAG;AACV,MAAA,IAAI,MAAA,CAAO,OAAA;AACT,QAAA,MAAM,IAAI,SAAA,CAAU;AAAA,UAClB,IAAA,EAAM,SAAA;AAAA,UACN,KAAA,EAAO,QAAA;AAAA,UACP,OAAA,EAAS;AAAA,SACV,CAAA;AAEH,MAAA,IAAI,CAAA,YAAa,WAAW,MAAM,CAAA;AAClC,MAAA,MAAM,SAAA,CAAU,KAAK,CAAA,EAAG,EAAE,MAAM,QAAA,EAAU,KAAA,EAAO,UAAU,CAAA;AAAA,IAC7D;AAEA,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA,CAClB,OAAO,CAAC,CAAA,KAAM,EAAE,IAAA,KAAS,MAAM,CAAA,CAC/B,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,EAAE,CAAA,CACvB,KAAK,EAAE,CAAA;AACV,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,WAAA,EAAa,QAAQ,KAAA,CAAM,YAAA;AAAA,MAC3B,YAAA,EAAc,QAAQ,KAAA,CAAM;AAAA,KAC9B;AACA,IAAA,OAAA,CAAQ,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,OAAO,CAAA;AACvC,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,KAAA;AAAA,MACA,KAAA;AAAA,MACA,UAAA,EAAY,QAAQ,WAAA,IAAe,MAAA;AAAA,MACnC,GAAA,EAAK;AAAA,KACP;AAAA,EACF;AACF","file":"anthropic-api-XJY6Y4T2.js","sourcesContent":["/**\n * Engine adapter: the raw Anthropic Messages API (`@anthropic-ai/sdk`). Lowest\n * level, token-level streaming, and the cheapest path for small validator\n * models. Transient 429/5xx/connection errors are retried with backoff via\n * `p-retry`; non-retryable errors abort immediately.\n *\n * Needs `ANTHROPIC_API_KEY` (or `EngineOptions.apiKey`). Constructed lazily by\n * the registry, so other engines work without a key present.\n */\n\nimport pRetry, { AbortError } from 'p-retry';\n\nimport type {\n AgentRequest,\n AgentResult,\n Engine,\n EngineEventSink,\n EngineOptions,\n} from './engine.ts';\nimport { LoopError } from '../core/errors.ts';\nimport { retryAfterHeaderToMs } from '../core/limits.ts';\n\n/**\n * Transient backend errors that warrant p-retry's blind backoff: 5xx (incl.\n * 529 overloaded) and connection/timeout. A 429 is NOT here — a rate limit is\n * classified to a typed `RATE_LIMIT` and handed to the loop's `onLimit` policy,\n * which waits the provider's actual reset rather than a generic backoff.\n */\nfunction isTransient(error: unknown): boolean {\n const status = (error as { status?: number })?.status;\n if (typeof status === 'number') return status >= 500;\n const name = (error as { name?: string })?.name ?? '';\n return /connection|timeout|socket|network/i.test(name);\n}\n\n/**\n * Best-effort `.get()` off the SDK error's `headers` (a web `Headers`). The SDK\n * attaches the response headers on `APIError`; read defensively in case a\n * version exposes them as a plain object instead.\n */\nfunction headerValue(error: unknown, name: string): string | undefined {\n const headers = (error as { headers?: unknown }).headers;\n if (headers && typeof (headers as Headers).get === 'function') {\n return (headers as Headers).get(name) ?? undefined;\n }\n if (headers && typeof headers === 'object') {\n const v = (headers as Record<string, unknown>)[name];\n if (typeof v === 'string') return v;\n }\n return undefined;\n}\n\n/**\n * Classify an Anthropic SDK error into a provider-limit `LoopError`, or return\n * `undefined` to let the generic handling take over. The SDK throws an\n * `APIError` with `.status` (HTTP), `.headers` (web `Headers`), and `.type`\n * (the body `error.type`, e.g. `rate_limit_error` / `billing_error`).\n * - 429 → RATE_LIMIT, reading `retry-after` (seconds) into `retryAfterMs`.\n * - a billing/quota error → QUOTA with no reset (not auto-waitable).\n * 529 / overloaded is deliberately NOT a limit — it is a transient ENGINE error\n * and stays on p-retry's backoff path.\n */\nfunction classifyLimit(error: unknown): LoopError | undefined {\n const status = (error as { status?: number })?.status;\n const type = (error as { type?: string })?.type;\n const message = error instanceof Error ? error.message : String(error);\n\n if (status === 429 || type === 'rate_limit_error') {\n return new LoopError({\n code: 'RATE_LIMIT',\n phase: 'engine',\n message: `anthropic-api rate limited: ${message}`,\n cause: error,\n retryAfterMs: retryAfterHeaderToMs(headerValue(error, 'retry-after')),\n });\n }\n if (type === 'billing_error') {\n return new LoopError({\n code: 'QUOTA',\n phase: 'engine',\n message: `anthropic-api usage/billing limit: ${message}`,\n cause: error,\n });\n }\n return undefined;\n}\n\n/** The slice of the Anthropic SDK this adapter actually consumes. */\ninterface FinalMessage {\n content: { type: string; text?: string }[];\n usage: { input_tokens: number; output_tokens: number };\n stop_reason: string | null;\n}\ninterface MessageStreamLike {\n on(event: 'text', cb: (delta: string) => void): void;\n finalMessage(): Promise<FinalMessage>;\n}\ninterface MessagesClientLike {\n messages: {\n stream(body: unknown, opts?: { signal?: AbortSignal }): MessageStreamLike;\n };\n}\n\nexport class AnthropicApiEngine implements Engine {\n readonly name = 'anthropic-api';\n private clientPromise?: Promise<MessagesClientLike>;\n\n constructor(private readonly opts: EngineOptions = {}) {}\n\n private async client(): Promise<MessagesClientLike> {\n if (!this.clientPromise) {\n const apiKey = this.opts.apiKey ?? process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n // Fail fast with an actionable, non-retryable error instead of leaking\n // the SDK's internal \"could not resolve authentication method\" message.\n throw new LoopError({\n code: 'CONFIG',\n phase: 'engine',\n retryable: false,\n message:\n 'the anthropic-api engine needs an API key — set ANTHROPIC_API_KEY or pass --api-key (or use the agent-sdk / claude-cli engine, which use host Claude auth)',\n });\n }\n this.clientPromise = import('@anthropic-ai/sdk').then(\n // One honest cast at the boundary to the structural shape we consume.\n (m) => new m.default({ apiKey }) as unknown as MessagesClientLike,\n );\n }\n return this.clientPromise;\n }\n\n async run(\n req: AgentRequest,\n onEvent: EngineEventSink,\n signal: AbortSignal,\n ): Promise<AgentResult> {\n const client = await this.client();\n const model =\n req.model ?? this.opts.defaultModel ?? 'claude-haiku-4-5-20251001';\n const maxTokens = req.maxTokens ?? 1024;\n\n const attempt = async (): Promise<FinalMessage> => {\n try {\n const stream = client.messages.stream(\n {\n model,\n max_tokens: maxTokens,\n system: req.system,\n messages: [{ role: 'user', content: req.prompt }],\n },\n { signal },\n );\n stream.on('text', (delta) => onEvent({ type: 'text', delta }));\n return await stream.finalMessage();\n } catch (e) {\n // A provider limit is not for p-retry's blind backoff — stop retrying\n // and let the loop's onLimit policy wait the actual reset. Wrap the\n // typed LoopError as the AbortError cause so it survives to the catch.\n const limit = classifyLimit(e);\n if (limit) throw new AbortError(limit);\n if (signal.aborted || !isTransient(e)) {\n throw new AbortError(e instanceof Error ? e : new Error(String(e)));\n }\n throw e;\n }\n };\n\n let message;\n try {\n message = await pRetry(attempt, {\n retries: 2,\n minTimeout: 500,\n factor: 2,\n });\n } catch (e) {\n if (signal.aborted)\n throw new LoopError({\n code: 'ABORTED',\n phase: 'engine',\n message: 'anthropic-api run aborted',\n });\n // p-retry unwraps AbortError to its cause; surface a typed limit as-is.\n if (e instanceof LoopError) throw e;\n throw LoopError.from(e, { code: 'ENGINE', phase: 'engine' });\n }\n\n const text = message.content\n .filter((b) => b.type === 'text')\n .map((b) => b.text ?? '')\n .join('');\n const usage = {\n inputTokens: message.usage.input_tokens,\n outputTokens: message.usage.output_tokens,\n };\n onEvent({ type: 'usage', usage, model });\n return {\n text,\n usage,\n model,\n stopReason: message.stop_reason ?? undefined,\n raw: message,\n };\n }\n}\n"]}
|