@animalabs/membrane 0.5.81 → 0.5.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache-wire-receipt.d.ts +13 -0
- package/dist/cache-wire-receipt.d.ts.map +1 -0
- package/dist/cache-wire-receipt.js +108 -0
- package/dist/cache-wire-receipt.js.map +1 -0
- package/dist/formatters/anthropic-xml.d.ts.map +1 -1
- package/dist/formatters/anthropic-xml.js +9 -6
- package/dist/formatters/anthropic-xml.js.map +1 -1
- package/dist/formatters/native.d.ts.map +1 -1
- package/dist/formatters/native.js +16 -3
- package/dist/formatters/native.js.map +1 -1
- package/dist/formatters/types.d.ts +2 -0
- package/dist/formatters/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/membrane.d.ts.map +1 -1
- package/dist/membrane.js +15 -4
- package/dist/membrane.js.map +1 -1
- package/dist/providers/anthropic.d.ts +12 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +4 -4
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/types/request.d.ts +6 -0
- package/dist/types/request.d.ts.map +1 -1
- package/dist/utils/cache-marker-budget.d.ts +9 -0
- package/dist/utils/cache-marker-budget.d.ts.map +1 -1
- package/dist/utils/cache-marker-budget.js +19 -0
- package/dist/utils/cache-marker-budget.js.map +1 -1
- package/package.json +1 -1
- package/src/cache-wire-receipt.ts +125 -0
- package/src/formatters/anthropic-xml.ts +9 -8
- package/src/formatters/native.ts +23 -2
- package/src/formatters/types.ts +3 -0
- package/src/index.ts +6 -0
- package/src/membrane.ts +14 -3
- package/src/providers/anthropic.ts +18 -6
- package/src/providers/index.ts +1 -0
- package/src/types/request.ts +7 -0
- package/src/utils/cache-marker-budget.ts +29 -0
|
@@ -130,6 +130,18 @@ export function thinkingEnabled(request: ProviderRequest): boolean {
|
|
|
130
130
|
// Adapter Configuration
|
|
131
131
|
// ============================================================================
|
|
132
132
|
|
|
133
|
+
/**
|
|
134
|
+
* What the dynamicHeaders callback is told about the request it stamps.
|
|
135
|
+
* `lane` names the transport shape: 'stream' is the conversational turn loop,
|
|
136
|
+
* 'complete' the non-streamed lane (compression, side-calls, keepalive
|
|
137
|
+
* touches). A stamp that describes WHY the agent's turn fired belongs on the
|
|
138
|
+
* stream lane only — a compression call running in the background is not the
|
|
139
|
+
* turn, and stamping it with the turn's cause would lie to the ledger.
|
|
140
|
+
*/
|
|
141
|
+
export interface DynamicHeadersContext {
|
|
142
|
+
lane: 'stream' | 'complete';
|
|
143
|
+
}
|
|
144
|
+
|
|
133
145
|
export interface AnthropicAdapterConfig {
|
|
134
146
|
/** API key (defaults to ANTHROPIC_API_KEY env var) */
|
|
135
147
|
apiKey?: string | null;
|
|
@@ -157,7 +169,7 @@ export interface AnthropicAdapterConfig {
|
|
|
157
169
|
* never replayed stale — an unstamped touch is honest, a stale stamp lies.
|
|
158
170
|
* null/undefined/'' values are dropped.
|
|
159
171
|
*/
|
|
160
|
-
dynamicHeaders?: () => Record<string, string | number | null | undefined>;
|
|
172
|
+
dynamicHeaders?: (ctx?: DynamicHeadersContext) => Record<string, string | number | null | undefined>;
|
|
161
173
|
|
|
162
174
|
/** Default max tokens */
|
|
163
175
|
defaultMaxTokens?: number;
|
|
@@ -196,7 +208,7 @@ export class AnthropicAdapter implements ProviderAdapter {
|
|
|
196
208
|
/** Holds idle agents' cached prefixes warm; undefined when disabled. */
|
|
197
209
|
readonly cacheKeepalive: CacheKeepalive | undefined;
|
|
198
210
|
/** Live per-request header source (see AnthropicAdapterConfig.dynamicHeaders). */
|
|
199
|
-
private readonly dynamicHeaders?: () => Record<string, string | number | null | undefined>;
|
|
211
|
+
private readonly dynamicHeaders?: (ctx?: DynamicHeadersContext) => Record<string, string | number | null | undefined>;
|
|
200
212
|
|
|
201
213
|
constructor(config: AnthropicAdapterConfig = {}) {
|
|
202
214
|
const clientOptions: ClientOptions = {
|
|
@@ -250,7 +262,7 @@ export class AnthropicAdapter implements ProviderAdapter {
|
|
|
250
262
|
try {
|
|
251
263
|
const response = await this.client.messages.create(fullRequest, {
|
|
252
264
|
signal: options?.signal,
|
|
253
|
-
headers: this.liveHeaders(headers),
|
|
265
|
+
headers: this.liveHeaders(headers, 'complete'),
|
|
254
266
|
});
|
|
255
267
|
|
|
256
268
|
return this.parseResponse(response, fullRequest);
|
|
@@ -323,7 +335,7 @@ export class AnthropicAdapter implements ProviderAdapter {
|
|
|
323
335
|
try {
|
|
324
336
|
const stream = await this.client.messages.stream(anthropicRequest, {
|
|
325
337
|
signal: idleAbort.signal,
|
|
326
|
-
headers: this.liveHeaders(this.betaHeaders(request)),
|
|
338
|
+
headers: this.liveHeaders(this.betaHeaders(request), 'stream'),
|
|
327
339
|
});
|
|
328
340
|
|
|
329
341
|
// Accumulate response metadata from SSE events directly, so we can
|
|
@@ -573,8 +585,8 @@ export class AnthropicAdapter implements ProviderAdapter {
|
|
|
573
585
|
/** Base headers + the live dynamicHeaders stamp. Request time only: the
|
|
574
586
|
* keepalive recorder receives the base headers BEFORE this merge, so
|
|
575
587
|
* replayed touches never carry a stale telemetry value. */
|
|
576
|
-
private liveHeaders(base: Record<string, string> | undefined): Record<string, string> | undefined {
|
|
577
|
-
const dyn = this.dynamicHeaders?.();
|
|
588
|
+
private liveHeaders(base: Record<string, string> | undefined, lane: DynamicHeadersContext['lane']): Record<string, string> | undefined {
|
|
589
|
+
const dyn = this.dynamicHeaders?.({ lane });
|
|
578
590
|
if (!dyn) return base;
|
|
579
591
|
const out: Record<string, string> = { ...(base ?? {}) };
|
|
580
592
|
for (const [k, v] of Object.entries(dyn)) {
|
package/src/providers/index.ts
CHANGED
package/src/types/request.ts
CHANGED
|
@@ -112,6 +112,9 @@ export type ToolMode =
|
|
|
112
112
|
// ============================================================================
|
|
113
113
|
|
|
114
114
|
export interface NormalizedRequest {
|
|
115
|
+
/** Receives the exact post-format/post-hook cache receipt immediately before
|
|
116
|
+
* network submission. Observability only; never forwarded to providers. */
|
|
117
|
+
onCacheWireReceipt?: (receipt: import('../cache-wire-receipt.js').CacheWireReceipt) => void;
|
|
115
118
|
/**
|
|
116
119
|
* Explicitly own the loss of old inline images when the serialized request
|
|
117
120
|
* exceeds the API byte cap: oldest images are replaced with loud
|
|
@@ -152,6 +155,10 @@ export interface NormalizedRequest {
|
|
|
152
155
|
*/
|
|
153
156
|
promptCaching?: boolean;
|
|
154
157
|
|
|
158
|
+
/** Marker ownership policy. `cm-owned` disables every formatter-generated
|
|
159
|
+
* system/context-prefix marker; only normalized message breakpoints survive. */
|
|
160
|
+
cacheMarkers?: 'membrane-system' | 'cm-owned';
|
|
161
|
+
|
|
155
162
|
/**
|
|
156
163
|
* Cache TTL for Anthropic prompt caching.
|
|
157
164
|
* '5m' (default) = 5 minute TTL
|
|
@@ -149,6 +149,35 @@ export function countWireCacheMarkers(surfaces: WireCacheSurfaces): number {
|
|
|
149
149
|
return collectMarkedBlocks(surfaces).length;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Validate a caller-owned marker layout without changing it.
|
|
154
|
+
*
|
|
155
|
+
* In `cm-owned` mode the caller has deliberately allocated the complete
|
|
156
|
+
* breakpoint set. Silently stripping or reordering those markers would make
|
|
157
|
+
* the cache receipt describe a request that was never sent, so invalid
|
|
158
|
+
* layouts fail before submission instead of using the legacy repair clamp.
|
|
159
|
+
*/
|
|
160
|
+
export function assertCacheMarkersWithinLimit(
|
|
161
|
+
surfaces: WireCacheSurfaces,
|
|
162
|
+
site: string
|
|
163
|
+
): number {
|
|
164
|
+
const marked = collectMarkedBlocks(surfaces);
|
|
165
|
+
const invalidThinking = marked.filter(
|
|
166
|
+
(block) => block.type === 'thinking' || block.type === 'redacted_thinking'
|
|
167
|
+
).length;
|
|
168
|
+
if (invalidThinking > 0) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`${site}: caller-owned cache_control cannot be attached to thinking/redacted_thinking blocks`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
if (marked.length > MAX_CACHE_BREAKPOINTS) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`${site}: cache_control limit exceeded: ${marked.length} markers (maximum ${MAX_CACHE_BREAKPOINTS})`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return marked.length;
|
|
179
|
+
}
|
|
180
|
+
|
|
152
181
|
/**
|
|
153
182
|
* Bring a request inside the breakpoint budget, in place, at the last exit
|
|
154
183
|
* before the adapter call. Two repairs, both loud:
|