@intentface/latch-core 0.9.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/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/agent.d.ts +200 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +9 -0
- package/dist/agent.js.map +1 -0
- package/dist/compaction.d.ts +33 -0
- package/dist/compaction.d.ts.map +1 -0
- package/dist/compaction.js +104 -0
- package/dist/compaction.js.map +1 -0
- package/dist/connections.d.ts +16 -0
- package/dist/connections.d.ts.map +1 -0
- package/dist/connections.js +41 -0
- package/dist/connections.js.map +1 -0
- package/dist/context.d.ts +41 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +25 -0
- package/dist/context.js.map +1 -0
- package/dist/current-date.d.ts +12 -0
- package/dist/current-date.d.ts.map +1 -0
- package/dist/current-date.js +25 -0
- package/dist/current-date.js.map +1 -0
- package/dist/extensions.d.ts +333 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +569 -0
- package/dist/extensions.js.map +1 -0
- package/dist/harness/index.d.ts +17 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +15 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/tools.d.ts +88 -0
- package/dist/harness/tools.d.ts.map +1 -0
- package/dist/harness/tools.js +296 -0
- package/dist/harness/tools.js.map +1 -0
- package/dist/harness/web-fetch.d.ts +47 -0
- package/dist/harness/web-fetch.d.ts.map +1 -0
- package/dist/harness/web-fetch.js +247 -0
- package/dist/harness/web-fetch.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/limits.d.ts +152 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +97 -0
- package/dist/limits.js.map +1 -0
- package/dist/memory.d.ts +93 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +13 -0
- package/dist/memory.js.map +1 -0
- package/dist/message.d.ts +46 -0
- package/dist/message.d.ts.map +1 -0
- package/dist/message.js +2 -0
- package/dist/message.js.map +1 -0
- package/dist/models/catalog.d.ts +56 -0
- package/dist/models/catalog.d.ts.map +1 -0
- package/dist/models/catalog.js +211 -0
- package/dist/models/catalog.js.map +1 -0
- package/dist/models/defaults.d.ts +23 -0
- package/dist/models/defaults.d.ts.map +1 -0
- package/dist/models/defaults.js +19 -0
- package/dist/models/defaults.js.map +1 -0
- package/dist/models/index.d.ts +23 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +19 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/prompt-caching.d.ts +19 -0
- package/dist/models/prompt-caching.d.ts.map +1 -0
- package/dist/models/prompt-caching.js +18 -0
- package/dist/models/prompt-caching.js.map +1 -0
- package/dist/models/provider.d.ts +19 -0
- package/dist/models/provider.d.ts.map +1 -0
- package/dist/models/provider.js +22 -0
- package/dist/models/provider.js.map +1 -0
- package/dist/models/reasoning.d.ts +21 -0
- package/dist/models/reasoning.d.ts.map +1 -0
- package/dist/models/reasoning.js +59 -0
- package/dist/models/reasoning.js.map +1 -0
- package/dist/pricing.d.ts +52 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +37 -0
- package/dist/pricing.js.map +1 -0
- package/dist/principal.d.ts +37 -0
- package/dist/principal.d.ts.map +1 -0
- package/dist/principal.js +30 -0
- package/dist/principal.js.map +1 -0
- package/dist/projections.d.ts +37 -0
- package/dist/projections.d.ts.map +1 -0
- package/dist/projections.js +128 -0
- package/dist/projections.js.map +1 -0
- package/dist/prompt-caching.d.ts +106 -0
- package/dist/prompt-caching.d.ts.map +1 -0
- package/dist/prompt-caching.js +165 -0
- package/dist/prompt-caching.js.map +1 -0
- package/dist/runtime.d.ts +670 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2425 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scheduler.d.ts +31 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +43 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/storage.d.ts +389 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +38 -0
- package/dist/storage.js.map +1 -0
- package/dist/telemetry.d.ts +155 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +2 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/vault-node.d.ts +20 -0
- package/dist/vault-node.d.ts.map +1 -0
- package/dist/vault-node.js +30 -0
- package/dist/vault-node.js.map +1 -0
- package/dist/vault.d.ts +62 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +88 -0
- package/dist/vault.js.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import TurndownService from "turndown";
|
|
2
|
+
import { isBlockedHost, isBlockedResolved } from "@intentface/latch-net";
|
|
3
|
+
import { pinnedFetch } from "@intentface/latch-net/pinned";
|
|
4
|
+
/**
|
|
5
|
+
* Robust URL fetching for the `web_fetch` tool — mirrors eve's implementation
|
|
6
|
+
* so pages come back as clean markdown/text instead of raw HTML, with a
|
|
7
|
+
* browser User-Agent, content negotiation, a timeout, a Cloudflare-challenge
|
|
8
|
+
* retry, a size cap, and head-truncation. Runs in the app process (not the
|
|
9
|
+
* sandbox); private/loopback hosts are blocked (SSRF).
|
|
10
|
+
*/
|
|
11
|
+
const MAX_RESPONSE_SIZE = 5 * 1024 * 1024; // 5 MB
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
13
|
+
const MAX_TIMEOUT_MS = 120_000;
|
|
14
|
+
const MAX_OUTPUT_LINES = 2000;
|
|
15
|
+
const MAX_OUTPUT_BYTES = 50 * 1024;
|
|
16
|
+
const MAX_REDIRECTS = 4;
|
|
17
|
+
// Browser-like UA so servers return full content, not bot-degraded pages.
|
|
18
|
+
const BROWSER_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36";
|
|
19
|
+
const HONEST_UA = "intentface-latch-agent";
|
|
20
|
+
/**
|
|
21
|
+
* Anthropic caps images at 5 MB of source data; staying under it here beats a
|
|
22
|
+
* provider error after the fetch already succeeded.
|
|
23
|
+
*/
|
|
24
|
+
const MAX_MEDIA_SIZE = 4.5 * 1024 * 1024;
|
|
25
|
+
/**
|
|
26
|
+
* What media the body is, if it's a type worth showing the model. The
|
|
27
|
+
* Content-Type header decides when it's one of ours; magic bytes rescue the
|
|
28
|
+
* common case of a server that lies (application/octet-stream, text/plain) —
|
|
29
|
+
* sniffing is deliberately limited to unambiguous signatures.
|
|
30
|
+
*/
|
|
31
|
+
export function detectMedia(contentType, bytes) {
|
|
32
|
+
const declared = contentType.split(";")[0].trim().toLowerCase();
|
|
33
|
+
if (declared === "image/png" ||
|
|
34
|
+
declared === "image/jpeg" ||
|
|
35
|
+
declared === "image/gif" ||
|
|
36
|
+
declared === "image/webp" ||
|
|
37
|
+
declared === "application/pdf") {
|
|
38
|
+
return declared;
|
|
39
|
+
}
|
|
40
|
+
if (declared === "image/jpg")
|
|
41
|
+
return "image/jpeg";
|
|
42
|
+
// Magic bytes, only when the declared type doesn't commit to something else
|
|
43
|
+
// textual (an HTML page that happens to start with %PDF is not a PDF).
|
|
44
|
+
if (declared.startsWith("text/") || declared.includes("html"))
|
|
45
|
+
return null;
|
|
46
|
+
const ascii = (start, s) => bytes.length >= start + s.length &&
|
|
47
|
+
s.split("").every((ch, i) => bytes[start + i] === ch.charCodeAt(0));
|
|
48
|
+
if (ascii(0, "%PDF-"))
|
|
49
|
+
return "application/pdf";
|
|
50
|
+
if (bytes.length >= 8 && bytes[0] === 0x89 && ascii(1, "PNG"))
|
|
51
|
+
return "image/png";
|
|
52
|
+
if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
|
|
53
|
+
return "image/jpeg";
|
|
54
|
+
if (ascii(0, "GIF87a") || ascii(0, "GIF89a"))
|
|
55
|
+
return "image/gif";
|
|
56
|
+
if (ascii(0, "RIFF") && ascii(8, "WEBP"))
|
|
57
|
+
return "image/webp";
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Full SSRF gate for one hop: protocol, the string-level blocklist, and the DNS
|
|
62
|
+
* backstop (a public NAME that resolves to a private address — the string check
|
|
63
|
+
* alone cannot see that). Applied to the initial URL AND every redirect target.
|
|
64
|
+
*/
|
|
65
|
+
async function blockedReason(t) {
|
|
66
|
+
if (t.protocol !== "http:" && t.protocol !== "https:") {
|
|
67
|
+
return "Only http(s) URLs are allowed.";
|
|
68
|
+
}
|
|
69
|
+
if (isBlockedHost(t.hostname) || (await isBlockedResolved(t.hostname))) {
|
|
70
|
+
return "Host is not allowed.";
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function buildHeaders(format) {
|
|
75
|
+
const accept = format === "markdown"
|
|
76
|
+
? "text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1"
|
|
77
|
+
: format === "text"
|
|
78
|
+
? "text/plain;q=1.0, text/markdown;q=0.9, text/html;q=0.8, */*;q=0.1"
|
|
79
|
+
: "text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, */*;q=0.1";
|
|
80
|
+
return { Accept: accept, "Accept-Language": "en-US,en;q=0.9", "User-Agent": BROWSER_USER_AGENT };
|
|
81
|
+
}
|
|
82
|
+
/** HTML → Markdown (Turndown), dropping script/style/meta/link noise. */
|
|
83
|
+
export function convertHtmlToMarkdown(html) {
|
|
84
|
+
const service = new TurndownService({
|
|
85
|
+
bulletListMarker: "-",
|
|
86
|
+
codeBlockStyle: "fenced",
|
|
87
|
+
emDelimiter: "*",
|
|
88
|
+
headingStyle: "atx",
|
|
89
|
+
hr: "---",
|
|
90
|
+
});
|
|
91
|
+
service.remove(["script", "style", "meta", "link"]);
|
|
92
|
+
return service.turndown(html);
|
|
93
|
+
}
|
|
94
|
+
const ENTITY_MAP = {
|
|
95
|
+
"&": "&",
|
|
96
|
+
">": ">",
|
|
97
|
+
"<": "<",
|
|
98
|
+
" ": " ",
|
|
99
|
+
""": '"',
|
|
100
|
+
"'": "'",
|
|
101
|
+
"'": "'",
|
|
102
|
+
"/": "/",
|
|
103
|
+
};
|
|
104
|
+
/** Strip HTML to readable text (no Turndown) — for `format: "text"`. */
|
|
105
|
+
export function extractTextFromHtml(html) {
|
|
106
|
+
let text = html.replace(/<(script|style|noscript)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
|
|
107
|
+
text = text.replace(/<\/(p|div|br|h[1-6]|li|tr|blockquote|pre|section|article|header|footer|nav|aside|main|figure|figcaption|details|summary)>/gi, "\n");
|
|
108
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
109
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
110
|
+
text = text.replace(/&[a-z#0-9]+;/gi, (m) => ENTITY_MAP[m.toLowerCase()] ?? m);
|
|
111
|
+
return text
|
|
112
|
+
.split("\n")
|
|
113
|
+
.map((line) => line.replace(/[ \t]+/g, " ").trim())
|
|
114
|
+
.join("\n")
|
|
115
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
116
|
+
.trim();
|
|
117
|
+
}
|
|
118
|
+
/** Keep the HEAD of long content (the start = the article, not the footer). */
|
|
119
|
+
function headTruncate(text) {
|
|
120
|
+
let out = text;
|
|
121
|
+
let truncated = false;
|
|
122
|
+
const lines = out.split("\n");
|
|
123
|
+
if (lines.length > MAX_OUTPUT_LINES) {
|
|
124
|
+
out = lines.slice(0, MAX_OUTPUT_LINES).join("\n");
|
|
125
|
+
truncated = true;
|
|
126
|
+
}
|
|
127
|
+
if (Buffer.byteLength(out) > MAX_OUTPUT_BYTES) {
|
|
128
|
+
out = out.slice(0, MAX_OUTPUT_BYTES);
|
|
129
|
+
truncated = true;
|
|
130
|
+
}
|
|
131
|
+
return { text: out, truncated };
|
|
132
|
+
}
|
|
133
|
+
export async function webFetch(args) {
|
|
134
|
+
const format = args.format ?? "markdown";
|
|
135
|
+
let u;
|
|
136
|
+
try {
|
|
137
|
+
u = new URL(args.url);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return { ok: false, error: "Invalid URL." };
|
|
141
|
+
}
|
|
142
|
+
// Non-positive / NaN timeouts fall back to the default rather than reaching
|
|
143
|
+
// AbortSignal.timeout(), which throws a RangeError outside our error result.
|
|
144
|
+
const requested = args.timeout != null && Number.isFinite(args.timeout) && args.timeout > 0 ? args.timeout * 1000 : DEFAULT_TIMEOUT_MS;
|
|
145
|
+
const timeoutMs = Math.min(requested, MAX_TIMEOUT_MS);
|
|
146
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
147
|
+
const headers = buildHeaders(format);
|
|
148
|
+
/**
|
|
149
|
+
* Follow redirects MANUALLY so each hop is re-gated: with `redirect: "follow"`
|
|
150
|
+
* a public URL that 302s to an internal address (e.g. cloud metadata) would be
|
|
151
|
+
* fetched transparently, and only the first host would ever be checked.
|
|
152
|
+
*/
|
|
153
|
+
const fetchGuarded = async (start, reqHeaders) => {
|
|
154
|
+
let current = start;
|
|
155
|
+
for (let hop = 0;; hop++) {
|
|
156
|
+
const bad = await blockedReason(current);
|
|
157
|
+
if (bad)
|
|
158
|
+
return { error: bad };
|
|
159
|
+
// Pinned fetch: the SSRF vetting also runs inside the connection's own
|
|
160
|
+
// DNS lookup, so `blockedReason` above is the well-messaged pre-check,
|
|
161
|
+
// not the last line of defense against rebinding.
|
|
162
|
+
const res = await pinnedFetch(current, { headers: reqHeaders, signal, redirect: "manual" });
|
|
163
|
+
const location = res.status >= 300 && res.status < 400 ? res.headers.get("location") : null;
|
|
164
|
+
if (!location)
|
|
165
|
+
return res;
|
|
166
|
+
if (hop >= MAX_REDIRECTS)
|
|
167
|
+
return { error: "Too many redirects." };
|
|
168
|
+
try {
|
|
169
|
+
current = new URL(location, current);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return { error: "Invalid redirect location." };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
let response;
|
|
177
|
+
try {
|
|
178
|
+
const initial = await fetchGuarded(u, headers);
|
|
179
|
+
if ("error" in initial)
|
|
180
|
+
return { ok: false, error: initial.error };
|
|
181
|
+
// Cloudflare may block browser UAs from non-browser TLS; retry honestly.
|
|
182
|
+
if (initial.status === 403 && initial.headers.get("cf-mitigated") === "challenge") {
|
|
183
|
+
const retry = await fetchGuarded(u, { ...headers, "User-Agent": HONEST_UA });
|
|
184
|
+
if ("error" in retry)
|
|
185
|
+
return { ok: false, error: retry.error };
|
|
186
|
+
response = retry;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
response = initial;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
const msg = e instanceof Error
|
|
194
|
+
? e.name === "TimeoutError" || e.name === "AbortError"
|
|
195
|
+
? `Request timed out after ${timeoutMs / 1000}s`
|
|
196
|
+
: // undici wraps connect-time failures (incl. the pinned fetch's
|
|
197
|
+
// SSRF block) as `TypeError: fetch failed` with the reason in
|
|
198
|
+
// `cause` — surface it or the block looks like a network blip.
|
|
199
|
+
e.cause instanceof Error && e.cause.message
|
|
200
|
+
? `${e.message}: ${e.cause.message}`
|
|
201
|
+
: e.message
|
|
202
|
+
: "Fetch failed";
|
|
203
|
+
return { ok: false, error: msg };
|
|
204
|
+
}
|
|
205
|
+
if (!response.ok)
|
|
206
|
+
return { ok: false, error: `Request failed with status ${response.status}` };
|
|
207
|
+
const declared = response.headers.get("content-length");
|
|
208
|
+
if (declared && Number.parseInt(declared, 10) > MAX_RESPONSE_SIZE) {
|
|
209
|
+
return { ok: false, error: "Response too large (exceeds 5 MB)." };
|
|
210
|
+
}
|
|
211
|
+
const buffer = await response.arrayBuffer();
|
|
212
|
+
if (buffer.byteLength > MAX_RESPONSE_SIZE) {
|
|
213
|
+
return { ok: false, error: "Response too large (exceeds 5 MB)." };
|
|
214
|
+
}
|
|
215
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
216
|
+
// Images and PDFs come back as bytes so the model can SEE them (the tool's
|
|
217
|
+
// toModelOutput turns them into provider image/document blocks). Text-decoding
|
|
218
|
+
// them — the previous behavior — produced mojibake the model couldn't use.
|
|
219
|
+
const mediaType = detectMedia(contentType, new Uint8Array(buffer));
|
|
220
|
+
if (mediaType) {
|
|
221
|
+
if (buffer.byteLength > MAX_MEDIA_SIZE) {
|
|
222
|
+
return {
|
|
223
|
+
ok: false,
|
|
224
|
+
error: `Media too large for model input (${(buffer.byteLength / (1024 * 1024)).toFixed(1)} MB, max ${MAX_MEDIA_SIZE / (1024 * 1024)} MB).`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
ok: true,
|
|
229
|
+
kind: "media",
|
|
230
|
+
status: response.status,
|
|
231
|
+
contentType,
|
|
232
|
+
mediaType,
|
|
233
|
+
data: Buffer.from(buffer).toString("base64"),
|
|
234
|
+
bytes: buffer.byteLength,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const isHtml = contentType.includes("text/html");
|
|
238
|
+
const body = new TextDecoder().decode(buffer);
|
|
239
|
+
const raw = format === "markdown" && isHtml
|
|
240
|
+
? convertHtmlToMarkdown(body)
|
|
241
|
+
: format === "text" && isHtml
|
|
242
|
+
? extractTextFromHtml(body)
|
|
243
|
+
: body;
|
|
244
|
+
const { text: content, truncated } = headTruncate(raw);
|
|
245
|
+
return { ok: true, kind: "text", status: response.status, contentType, content, truncated };
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=web-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-fetch.js","sourceRoot":"","sources":["../../src/harness/web-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE3D;;;;;;GAMG;AAEH,MAAM,iBAAiB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAClD,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,cAAc,GAAG,OAAO,CAAC;AAC/B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,CAAC;AACnC,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB,0EAA0E;AAC1E,MAAM,kBAAkB,GACtB,iHAAiH,CAAC;AACpH,MAAM,SAAS,GAAG,wBAAwB,CAAC;AAiC3C;;;GAGG;AACH,MAAM,cAAc,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,WAAmB,EAAE,KAAiB;IAChE,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChE,IACE,QAAQ,KAAK,WAAW;QACxB,QAAQ,KAAK,YAAY;QACzB,QAAQ,KAAK,WAAW;QACxB,QAAQ,KAAK,YAAY;QACzB,QAAQ,KAAK,iBAAiB,EAC9B,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,QAAQ,KAAK,WAAW;QAAE,OAAO,YAAY,CAAC;IAElD,4EAA4E;IAC5E,uEAAuE;IACvE,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3E,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,CAAS,EAAE,EAAE,CACzC,KAAK,CAAC,MAAM,IAAI,KAAK,GAAG,CAAC,CAAC,MAAM;QAChC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,IAAI,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC;QAAE,OAAO,iBAAiB,CAAC;IAChD,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,WAAW,CAAC;IAClF,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QAClF,OAAO,YAAY,CAAC;IACtB,IAAI,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC;QAAE,OAAO,WAAW,CAAC;IACjE,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC;QAAE,OAAO,YAAY,CAAC;IAC9D,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAAC,CAAM;IACjC,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,gCAAgC,CAAC;IAC1C,CAAC;IACD,IAAI,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,sBAAsB,CAAC;IAChC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,MAAsB;IAC1C,MAAM,MAAM,GACV,MAAM,KAAK,UAAU;QACnB,CAAC,CAAC,0FAA0F;QAC5F,CAAC,CAAC,MAAM,KAAK,MAAM;YACjB,CAAC,CAAC,mEAAmE;YACrE,CAAC,CAAC,2EAA2E,CAAC;IACpF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,YAAY,EAAE,kBAAkB,EAAE,CAAC;AACnG,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC;QAClC,gBAAgB,EAAE,GAAG;QACrB,cAAc,EAAE,QAAQ;QACxB,WAAW,EAAE,GAAG;QAChB,YAAY,EAAE,KAAK;QACnB,EAAE,EAAE,KAAK;KACV,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,OAAO,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,GAA2B;IACzC,OAAO,EAAE,GAAG;IACZ,MAAM,EAAE,GAAG;IACX,MAAM,EAAE,GAAG;IACX,QAAQ,EAAE,GAAG;IACb,QAAQ,EAAE,GAAG;IACb,OAAO,EAAE,GAAG;IACZ,QAAQ,EAAE,GAAG;IACb,QAAQ,EAAE,GAAG;CACd,CAAC;AAEF,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,kDAAkD,EAAE,EAAE,CAAC,CAAC;IAChF,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,6HAA6H,EAC7H,IAAI,CACL,CAAC;IACF,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IAC1C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACpC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/E,OAAO,IAAI;SACR,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;SAClD,IAAI,CAAC,IAAI,CAAC;SACV,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;SAC1B,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,+EAA+E;AAC/E,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QACpC,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,gBAAgB,EAAE,CAAC;QAC9C,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACrC,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAI9B;IACC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC;IACzC,IAAI,CAAM,CAAC;IACX,IAAI,CAAC;QACH,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IAC9C,CAAC;IACD,4EAA4E;IAC5E,6EAA6E;IAC7E,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC;IACvI,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAErC;;;;OAIG;IACH,MAAM,YAAY,GAAG,KAAK,EACxB,KAAU,EACV,UAAkC,EACK,EAAE;QACzC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,IAAI,GAAG,GAAG,CAAC,GAAI,GAAG,EAAE,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;YACzC,IAAI,GAAG;gBAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;YAC/B,uEAAuE;YACvE,uEAAuE;YACvE,kDAAkD;YAClD,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC5F,MAAM,QAAQ,GACZ,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7E,IAAI,CAAC,QAAQ;gBAAE,OAAO,GAAG,CAAC;YAC1B,IAAI,GAAG,IAAI,aAAa;gBAAE,OAAO,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC;YAClE,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC;YACjD,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC/C,IAAI,OAAO,IAAI,OAAO;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QACnE,yEAAyE;QACzE,IAAI,OAAO,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,WAAW,EAAE,CAAC;YAClF,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,CAAC,EAAE,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,CAAC;YAC7E,IAAI,OAAO,IAAI,KAAK;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/D,QAAQ,GAAG,KAAK,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,OAAO,CAAC;QACrB,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GACP,CAAC,YAAY,KAAK;YAChB,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;gBACpD,CAAC,CAAC,2BAA2B,SAAS,GAAG,IAAI,GAAG;gBAChD,CAAC,CAAC,+DAA+D;oBAC/D,8DAA8D;oBAC9D,+DAA+D;oBAC/D,CAAC,CAAC,KAAK,YAAY,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO;wBAC3C,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE;wBACpC,CAAC,CAAC,CAAC,CAAC,OAAO;YACf,CAAC,CAAC,cAAc,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACnC,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,8BAA8B,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;IAE/F,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACxD,IAAI,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,iBAAiB,EAAE,CAAC;QAClE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,oCAAoC,EAAE,CAAC;IACpE,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;IAC5C,IAAI,MAAM,CAAC,UAAU,GAAG,iBAAiB,EAAE,CAAC;QAC1C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,oCAAoC,EAAE,CAAC;IACpE,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IAE/D,2EAA2E;IAC3E,+EAA+E;IAC/E,2EAA2E;IAC3E,MAAM,SAAS,GAAG,WAAW,CAAC,WAAW,EAAE,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,MAAM,CAAC,UAAU,GAAG,cAAc,EAAE,CAAC;YACvC,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,oCAAoC,CAAC,MAAM,CAAC,UAAU,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,cAAc,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO;aAC3I,CAAC;QACJ,CAAC;QACD,OAAO;YACL,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,WAAW;YACX,SAAS;YACT,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC5C,KAAK,EAAE,MAAM,CAAC,UAAU;SACzB,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,GAAG,GACP,MAAM,KAAK,UAAU,IAAI,MAAM;QAC7B,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC;QAC7B,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM;YAC3B,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC;YAC3B,CAAC,CAAC,IAAI,CAAC;IAEb,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACvD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAC9F,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @intentface/latch-core
|
|
3
|
+
*
|
|
4
|
+
* Foundational types & seams. The Runtime (composition root + operations facade)
|
|
5
|
+
* and the executor land in later pieces; this is the vocabulary everything else
|
|
6
|
+
* is built on.
|
|
7
|
+
*/
|
|
8
|
+
export * from "./principal.js";
|
|
9
|
+
export * from "./message.js";
|
|
10
|
+
export * from "./storage.js";
|
|
11
|
+
export * from "./context.js";
|
|
12
|
+
export * from "./current-date.js";
|
|
13
|
+
export * from "./agent.js";
|
|
14
|
+
export * from "./memory.js";
|
|
15
|
+
export * from "./runtime.js";
|
|
16
|
+
export * from "./connections.js";
|
|
17
|
+
export * from "./scheduler.js";
|
|
18
|
+
export * from "./vault.js";
|
|
19
|
+
export * from "./projections.js";
|
|
20
|
+
export * from "./compaction.js";
|
|
21
|
+
export * from "./pricing.js";
|
|
22
|
+
export * from "./prompt-caching.js";
|
|
23
|
+
export * from "./telemetry.js";
|
|
24
|
+
export * from "./limits.js";
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @intentface/latch-core
|
|
3
|
+
*
|
|
4
|
+
* Foundational types & seams. The Runtime (composition root + operations facade)
|
|
5
|
+
* and the executor land in later pieces; this is the vocabulary everything else
|
|
6
|
+
* is built on.
|
|
7
|
+
*/
|
|
8
|
+
export * from "./principal.js";
|
|
9
|
+
export * from "./message.js";
|
|
10
|
+
export * from "./storage.js";
|
|
11
|
+
export * from "./context.js";
|
|
12
|
+
export * from "./current-date.js";
|
|
13
|
+
export * from "./agent.js";
|
|
14
|
+
export * from "./memory.js";
|
|
15
|
+
export * from "./runtime.js";
|
|
16
|
+
export * from "./connections.js";
|
|
17
|
+
export * from "./scheduler.js";
|
|
18
|
+
export * from "./vault.js";
|
|
19
|
+
export * from "./projections.js";
|
|
20
|
+
export * from "./compaction.js";
|
|
21
|
+
export * from "./pricing.js";
|
|
22
|
+
export * from "./prompt-caching.js";
|
|
23
|
+
export * from "./telemetry.js";
|
|
24
|
+
export * from "./limits.js";
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
package/dist/limits.d.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { TurnTrigger } from "./telemetry.js";
|
|
2
|
+
/**
|
|
3
|
+
* Limits — per org / user / agent budgets on turns, tokens and cost.
|
|
4
|
+
*
|
|
5
|
+
* The split is the same as every other seam: the HOST decides which counters a
|
|
6
|
+
* turn counts against and what the caps are (`limitsFor` — derived from the
|
|
7
|
+
* principal, the agent and how the turn started), and where the counters live
|
|
8
|
+
* (`LimitStore`, a table the drizzle package implements). The RUNTIME counts
|
|
9
|
+
* and enforces:
|
|
10
|
+
*
|
|
11
|
+
* • admission — before a turn opens, the turn is RESERVED: one atomic
|
|
12
|
+
* increment of every policy's turn counter that returns the new count. The
|
|
13
|
+
* first policy whose count is now over its cap refuses the turn with
|
|
14
|
+
* {@link LimitExceededError} (the HTTP handler maps it to 429 +
|
|
15
|
+
* Retry-After) and the reservation is released. Increment-then-check means
|
|
16
|
+
* N concurrent requests against a one-turn cap get counts 1..N and exactly
|
|
17
|
+
* one is admitted — there is no read-then-write window to race through.
|
|
18
|
+
* Token and cost caps are checked against usage already settled; an
|
|
19
|
+
* in-flight turn's spend lands at settlement, so those caps can be
|
|
20
|
+
* overshot by at most the concurrent in-flight turns (bounded further by
|
|
21
|
+
* the mid-turn stop).
|
|
22
|
+
* • mid-turn — the tool loop stops once this turn's tokens reach the
|
|
23
|
+
* tightest remaining token budget (the model's last text is kept; no
|
|
24
|
+
* further steps run). Without this a single long agentic turn could blow
|
|
25
|
+
* through a whole day's budget after being admitted at 99%.
|
|
26
|
+
* • settlement — tokens and cost are added to every policy's counter when
|
|
27
|
+
* the turn finishes (resumes and subagent turns included).
|
|
28
|
+
*
|
|
29
|
+
* Windows are fixed, aligned to the epoch: `windowStart = floor(now / windowMs)
|
|
30
|
+
* * windowMs`. Fixed windows are what people expect from "per day" / "per
|
|
31
|
+
* hour", cost one row per (key, window) and need no sliding-log storage. A
|
|
32
|
+
* refusal says when the window turns over.
|
|
33
|
+
*/
|
|
34
|
+
/** One counter a turn counts against, plus its caps in the window. */
|
|
35
|
+
export interface LimitPolicy {
|
|
36
|
+
/** Stable counter key — e.g. `org:o1`, `user:o1:u1`, `agent:o1:support`. Name the scope in it. */
|
|
37
|
+
key: string;
|
|
38
|
+
/** Fixed window length in ms (see `MINUTE_MS` … `DAY_MS`). */
|
|
39
|
+
windowMs: number;
|
|
40
|
+
/** Caps within one window. An omitted cap is unlimited. */
|
|
41
|
+
max: {
|
|
42
|
+
/** Turns admitted (a resume doesn't count; a subagent turn does). */
|
|
43
|
+
turns?: number;
|
|
44
|
+
/** Input + output tokens. */
|
|
45
|
+
tokens?: number;
|
|
46
|
+
/** Cost in the pricing seam's currency (0 when no pricing is configured). */
|
|
47
|
+
cost?: number;
|
|
48
|
+
};
|
|
49
|
+
/** How a refusal names this budget to the user, e.g. "your organization's daily token budget". */
|
|
50
|
+
label?: string;
|
|
51
|
+
}
|
|
52
|
+
/** What has been counted against one policy in the current window. */
|
|
53
|
+
export interface LimitUsage {
|
|
54
|
+
turns: number;
|
|
55
|
+
tokens: number;
|
|
56
|
+
cost: number;
|
|
57
|
+
}
|
|
58
|
+
export interface LimitWindowRef {
|
|
59
|
+
key: string;
|
|
60
|
+
windowStart: number;
|
|
61
|
+
windowMs: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Counter storage — one row per (key, window). Both methods must be atomic per
|
|
65
|
+
* row (the drizzle implementation upserts with `SET n = n + excluded.n`).
|
|
66
|
+
*/
|
|
67
|
+
export interface LimitStore {
|
|
68
|
+
/** Current usage for each window ref; absent rows read as zero. */
|
|
69
|
+
read(refs: LimitWindowRef[]): Promise<Record<string, LimitUsage>>;
|
|
70
|
+
/**
|
|
71
|
+
* Add usage to each window (creating the row) and return each window's usage
|
|
72
|
+
* AFTER the add, keyed by `usageKey`. Atomic per row and the returned value
|
|
73
|
+
* must be the row's post-increment state (`INSERT … ON CONFLICT DO UPDATE …
|
|
74
|
+
* RETURNING`): admission reserves a turn with it. Partial usage: omitted
|
|
75
|
+
* fields add 0; negative values release a reservation.
|
|
76
|
+
*/
|
|
77
|
+
add(entries: (LimitWindowRef & {
|
|
78
|
+
usage: Partial<LimitUsage>;
|
|
79
|
+
})[]): Promise<Record<string, LimitUsage>>;
|
|
80
|
+
/** Drop windows that ended before `before` (retention; called by `runtime.cron()`). */
|
|
81
|
+
prune?(before: number): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
/** The limits seam as `createRuntime({ limits })` takes it. */
|
|
84
|
+
export interface LatchLimits<P> {
|
|
85
|
+
store: LimitStore;
|
|
86
|
+
/**
|
|
87
|
+
* The counters this turn counts against. Empty → unlimited. Called per turn
|
|
88
|
+
* (admission), so it may read a policy table; keep it cheap.
|
|
89
|
+
*/
|
|
90
|
+
limitsFor(input: {
|
|
91
|
+
principal: P;
|
|
92
|
+
/** The agent ref as resolved for the turn. */
|
|
93
|
+
agent: string;
|
|
94
|
+
trigger: TurnTrigger;
|
|
95
|
+
}): LimitPolicy[] | Promise<LimitPolicy[]>;
|
|
96
|
+
/** A turn was refused. For metrics/alerts; must not throw. */
|
|
97
|
+
onRefused?(input: {
|
|
98
|
+
principal: P;
|
|
99
|
+
agent: string;
|
|
100
|
+
trigger: TurnTrigger;
|
|
101
|
+
policy: LimitPolicy;
|
|
102
|
+
usage: LimitUsage;
|
|
103
|
+
retryAfterMs: number;
|
|
104
|
+
}): void | Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
export declare const MINUTE_MS = 60000;
|
|
107
|
+
export declare const HOUR_MS: number;
|
|
108
|
+
export declare const DAY_MS: number;
|
|
109
|
+
/** The start of the fixed window containing `now`. */
|
|
110
|
+
export declare const windowStartOf: (now: number, windowMs: number) => number;
|
|
111
|
+
/** `LimitWindowRef` for a policy at `now`. */
|
|
112
|
+
export declare const windowRef: (policy: LimitPolicy, now: number) => LimitWindowRef;
|
|
113
|
+
/**
|
|
114
|
+
* Map key for one window in `LimitStore.read`'s result. The window LENGTH is
|
|
115
|
+
* part of it: the same counter key may carry a per-hour and a per-day policy,
|
|
116
|
+
* and their aligned windows share a start on the hour.
|
|
117
|
+
*/
|
|
118
|
+
export declare const usageKey: (ref: LimitWindowRef) => string;
|
|
119
|
+
/**
|
|
120
|
+
* Thrown (and mapped to HTTP 429) when a turn is refused at admission.
|
|
121
|
+
* `code` is stable so hosts can match it structurally.
|
|
122
|
+
*/
|
|
123
|
+
export declare class LimitExceededError extends Error {
|
|
124
|
+
readonly policy: LimitPolicy;
|
|
125
|
+
readonly usage: LimitUsage;
|
|
126
|
+
/** Milliseconds until the window turns over and the budget resets. */
|
|
127
|
+
readonly retryAfterMs: number;
|
|
128
|
+
readonly code: "limit_exceeded";
|
|
129
|
+
constructor(policy: LimitPolicy, usage: LimitUsage,
|
|
130
|
+
/** Milliseconds until the window turns over and the budget resets. */
|
|
131
|
+
retryAfterMs: number);
|
|
132
|
+
}
|
|
133
|
+
export declare function isLimitExceededError(e: unknown): e is LimitExceededError;
|
|
134
|
+
/**
|
|
135
|
+
* Admission decision over usage that ALREADY includes this turn's reservation
|
|
136
|
+
* (`nextTurn: false`), or over settled usage when the turn is to be counted on
|
|
137
|
+
* top (`nextTurn: true`). Returns the first exhausted policy, or null when every
|
|
138
|
+
* budget has room.
|
|
139
|
+
*/
|
|
140
|
+
export declare function exceededPolicy(policies: LimitPolicy[], usage: Record<string, LimitUsage>, now: number, opts: {
|
|
141
|
+
nextTurn: boolean;
|
|
142
|
+
}): {
|
|
143
|
+
policy: LimitPolicy;
|
|
144
|
+
usage: LimitUsage;
|
|
145
|
+
retryAfterMs: number;
|
|
146
|
+
} | null;
|
|
147
|
+
/**
|
|
148
|
+
* The tightest remaining token budget across policies (for the mid-turn stop),
|
|
149
|
+
* or undefined when no policy caps tokens.
|
|
150
|
+
*/
|
|
151
|
+
export declare function remainingTokens(policies: LimitPolicy[], usage: Record<string, LimitUsage>, now: number): number | undefined;
|
|
152
|
+
//# sourceMappingURL=limits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"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"}
|
package/dist/limits.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export const MINUTE_MS = 60_000;
|
|
2
|
+
export const HOUR_MS = 60 * MINUTE_MS;
|
|
3
|
+
export const DAY_MS = 24 * HOUR_MS;
|
|
4
|
+
/** The start of the fixed window containing `now`. */
|
|
5
|
+
export const windowStartOf = (now, windowMs) => Math.floor(now / windowMs) * windowMs;
|
|
6
|
+
/** `LimitWindowRef` for a policy at `now`. */
|
|
7
|
+
export const windowRef = (policy, now) => ({
|
|
8
|
+
key: policy.key,
|
|
9
|
+
windowStart: windowStartOf(now, policy.windowMs),
|
|
10
|
+
windowMs: policy.windowMs,
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Map key for one window in `LimitStore.read`'s result. The window LENGTH is
|
|
14
|
+
* part of it: the same counter key may carry a per-hour and a per-day policy,
|
|
15
|
+
* and their aligned windows share a start on the hour.
|
|
16
|
+
*/
|
|
17
|
+
export const usageKey = (ref) => `${ref.key}@${ref.windowStart}/${ref.windowMs}`;
|
|
18
|
+
const ZERO = { turns: 0, tokens: 0, cost: 0 };
|
|
19
|
+
/**
|
|
20
|
+
* Thrown (and mapped to HTTP 429) when a turn is refused at admission.
|
|
21
|
+
* `code` is stable so hosts can match it structurally.
|
|
22
|
+
*/
|
|
23
|
+
export class LimitExceededError extends Error {
|
|
24
|
+
policy;
|
|
25
|
+
usage;
|
|
26
|
+
retryAfterMs;
|
|
27
|
+
code = "limit_exceeded";
|
|
28
|
+
constructor(policy, usage,
|
|
29
|
+
/** Milliseconds until the window turns over and the budget resets. */
|
|
30
|
+
retryAfterMs) {
|
|
31
|
+
const what = policy.label ?? `the ${describeWindow(policy.windowMs)} limit for ${policy.key}`;
|
|
32
|
+
super(`Limit reached: ${what}. Try again in ${describeDuration(retryAfterMs)}.`);
|
|
33
|
+
this.policy = policy;
|
|
34
|
+
this.usage = usage;
|
|
35
|
+
this.retryAfterMs = retryAfterMs;
|
|
36
|
+
this.name = "LimitExceededError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function isLimitExceededError(e) {
|
|
40
|
+
return e?.code === "limit_exceeded";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Admission decision over usage that ALREADY includes this turn's reservation
|
|
44
|
+
* (`nextTurn: false`), or over settled usage when the turn is to be counted on
|
|
45
|
+
* top (`nextTurn: true`). Returns the first exhausted policy, or null when every
|
|
46
|
+
* budget has room.
|
|
47
|
+
*/
|
|
48
|
+
export function exceededPolicy(policies, usage, now, opts) {
|
|
49
|
+
for (const policy of policies) {
|
|
50
|
+
const ref = windowRef(policy, now);
|
|
51
|
+
const u = usage[usageKey(ref)] ?? ZERO;
|
|
52
|
+
const turns = u.turns + (opts.nextTurn ? 1 : 0);
|
|
53
|
+
const over = (policy.max.turns !== undefined && turns > policy.max.turns) ||
|
|
54
|
+
(policy.max.tokens !== undefined && u.tokens >= policy.max.tokens) ||
|
|
55
|
+
(policy.max.cost !== undefined && u.cost >= policy.max.cost);
|
|
56
|
+
if (over)
|
|
57
|
+
return { policy, usage: u, retryAfterMs: ref.windowStart + ref.windowMs - now };
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The tightest remaining token budget across policies (for the mid-turn stop),
|
|
63
|
+
* or undefined when no policy caps tokens.
|
|
64
|
+
*/
|
|
65
|
+
export function remainingTokens(policies, usage, now) {
|
|
66
|
+
let min;
|
|
67
|
+
for (const policy of policies) {
|
|
68
|
+
if (policy.max.tokens === undefined)
|
|
69
|
+
continue;
|
|
70
|
+
const u = usage[usageKey(windowRef(policy, now))] ?? ZERO;
|
|
71
|
+
const left = Math.max(0, policy.max.tokens - u.tokens);
|
|
72
|
+
min = min === undefined ? left : Math.min(min, left);
|
|
73
|
+
}
|
|
74
|
+
return min;
|
|
75
|
+
}
|
|
76
|
+
function describeWindow(ms) {
|
|
77
|
+
if (ms % DAY_MS === 0)
|
|
78
|
+
return ms === DAY_MS ? "daily" : `${ms / DAY_MS}-day`;
|
|
79
|
+
if (ms % HOUR_MS === 0)
|
|
80
|
+
return ms === HOUR_MS ? "hourly" : `${ms / HOUR_MS}-hour`;
|
|
81
|
+
if (ms % MINUTE_MS === 0)
|
|
82
|
+
return ms === MINUTE_MS ? "per-minute" : `${ms / MINUTE_MS}-minute`;
|
|
83
|
+
return `${Math.round(ms / 1000)}s`;
|
|
84
|
+
}
|
|
85
|
+
function describeDuration(ms) {
|
|
86
|
+
const s = Math.ceil(ms / 1000);
|
|
87
|
+
if (s < 90)
|
|
88
|
+
return `${s}s`;
|
|
89
|
+
const m = Math.ceil(s / 60);
|
|
90
|
+
if (m < 90)
|
|
91
|
+
return `${m} min`;
|
|
92
|
+
const h = Math.ceil(m / 60);
|
|
93
|
+
if (h < 36)
|
|
94
|
+
return `${h} h`;
|
|
95
|
+
return `${Math.ceil(h / 24)} d`;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=limits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"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"}
|