Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx agentmods add rules/pauljphilp/effectpatterns/cache-http-responsesgit clone --depth 1 https://github.com/PaulJPhilp/EffectPatternsWhat it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5 | $0.01669 | $0.01669 |
| Opus 5 | $0.00834 | $0.00834 |
| Sonnet 5 | $0.00334 | $0.00334 |
| Haiku 4.5 | $0.00167 | $0.00167 |
Grade A, and why
cache-http-responses scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured yesterday.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 259 lines — stays where its author put it; the contents beside it link to each section on GitHub.
description: Use an in-memory or persistent cache to store HTTP responses. globs: "**/*.ts" alwaysApply: true
Cache HTTP Responses
Rule: Use an in-memory or persistent cache to store HTTP responses.
Example
import { Effect, Ref, HashMap, Option, Duration } from "effect"
import { HttpClient, HttpClientResponse } from "@effect/platform"
// ============================================
// 1. Simple in-memory cache
// ============================================
interface CacheEntry<T> {
readonly data: T
readonly timestamp: number
readonly ttl: number
}
const makeCache = <T>() =>
Effect.gen(function* () {
const store = yield* Ref.make(HashMap.empty<string, CacheEntry<T>>())
const get = (key: string): Effect.Effect<Option.Option<T>> =>
Ref.get(store).pipe(
Effect.map((map) => {
const entry = HashMap.get(map, key)
if (entry._tag === "None") return Option.none()
const now = Date.now()
if (now > entry.value.timestamp + entry.value.ttl) {
return Option.none() // Expired
}
return Option.some(entry.value.data)
})
)
const set = (key: string, data: T, ttl: number): Effect.Effect<void> =>
Ref.update(store, (map) =>
HashMap.set(map, key, {
data,
timestamp: Date.now(),
ttl,
})
)
const invalidate = (key: string): Effect.Effect<void> =>
Ref.update(store, (map) => HashMap.remove(map, key))
const clear = (): Effect.Effect<void> =>
Ref.set(store, HashMap.empty())
return { get, set, invalidate, clear }
})
// ============================================
// 2. Cached HTTP client
// ============================================
interface CachedHttpClient {
readonly get: <T>(
url: string,
options?: { ttl?: Duration.DurationInput }
) => Effect.Effect<T, Error>
readonly invalidate: (url: string) => Effect.Effect<void>
}
const makeCachedHttpClient = Effect.gen(function* () {
const httpClient = yield* HttpClient.HttpClient
const cache = yield* makeCache<unknown>()
const client: CachedHttpClient = {
get: <T>(url: string, options?: { ttl?: Duration.DurationInput }) => {
const ttl = options?.ttl ? Duration.toMillis(Duration.decode(options.ttl)) : 60000
return Effect.gen(function* () {
// Check cache first
const cached = yield* cache.get(url)
if (Option.isSome(cached)) {
yield* Effect.log(`Cache hit: ${url}`)
return cached.value as T
}
yield* Effect.log(`Cache miss: ${url}`)
// Fetch from network
const response = yield* httpClient.get(url)
const data = yield* HttpClientResponse.json(response) as Effect.Effect<T>
// Store in cache
yield* cache.set(url, data, ttl)
return data
})
},
invalidate: (url) => cache.invalidate(url),
}
return client
})
// ============================================
// 3. Stale-while-revalidate pattern
// ============================================
interface SWRCache<T> {
readonly data: T
readonly timestamp: number
readonly staleAfter: number
readonly expireAfter: number
}
const makeSWRClient = Effect.gen(function* () {
const httpClient = yield* HttpClient.HttpClient
const cache = yield* Ref.make(HashMap.empty<string, SWRCache<unknown>>())
return {
get: <T>(
url: string,
options: {
staleAfter: Duration.DurationInput
expireAfter: Duration.DurationInput
}
) =>
Effect.gen(function* () {
const now = Date.now()
const staleMs = Duration.toMillis(Duration.decode(options.staleAfter))
const expireMs = Duration.toMillis(Duration.decode(options.expireAfter))
const cached = yield* Ref.get(cache).pipe(
Effect.map((map) => HashMap.get(map, url))
)
if (cached._tag === "Some") {
const entry = cached.value
const age = now - entry.timestamp
if (age < staleMs) {
// Fresh - return immediately
return entry.data as T
}
if (age < expireMs) {
// Stale - return cached, revalidate in background
yield* Effect.fork(
httpClient.get(url).pipe(
Effect.flatMap((r) => HttpClientResponse.json(r)),
Effect.flatMap((data) =>
Ref.update(cache, (map) =>
HashMap.set(map, url, {
data,
timestamp: Date.now(),
staleAfter: staleMs,
expireAfter: expireMs,
})
)
),
Effect.catchAll(() => Effect.void) // Ignore errors
)
)
return entry.data as T
}
}
// Expired or missing - fetch fresh
const response = yield* httpClient.get(url)
const data = yield* HttpClientResponse.json(response) as Effect.Effect<T>
yield* Ref.update(cache, (map) =>
HashMap.set(map, url, {
data,
timestamp: now,
staleAfter: staleMs,
expireAfter: expireMs,
})
)
return data
}),
}
})
// ============================================
// 4. Cache with request deduplication
// ============================================
const makeDeduplicatedClient = Effect.gen(function* () {
const httpClient = yield* HttpClient.HttpClient
const inFlight = yield* Ref.make(HashMap.empty<string, Effect.Effect<unknown>>())
const cache = yield* makeCache<unknown>()
return {
get: <T>(url: string, ttl: number = 60000) =>
Effect.gen(function* () {
// Check cache
const cached = yield* cache.get(url)
if (Option.isSome(cached)) {
return cached.value as T
}
// Check if request already in flight
const pending = yield* Ref.get(inFlight).pipe(
Effect.map((map) => HashMap.get(map, url))
)
if (pending._tag === "Some") {
yield* Effect.log(`Deduplicating request: ${url}`)
return (yield* pending.value) as T
}
// Make the request
const request = httpClient.get(url).pipe(
Effect.flatMap((r) => HttpClientResponse.json(r)),
Effect.tap((data) => cache.set(url, data, ttl)),
Effect.ensuring(
Ref.update(inFlight, (map) => HashMap.remove(map, url))
)
)
// Store in-flight request
yield* Ref.update(inFlight, (map) => HashMap.set(map, url, request))
return (yield* request) as T
}),
}
})
// ============================================
// 5. Usage
// ============================================
const program = Effect.gen(function* () {
const client = yield* makeCachedHttpClient
// First call - cache miss
yield* client.get("https://api.example.com/users/1", { ttl: "5 minutes" })
// Second call - cache hit
yield* client.get("https://api.example.com/users/1")
// Invalidate when data changes
yield* client.invalidate("https://api.example.com/users/1")
})
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- yesterday First seen · 259 lines · 1,669 tokens per session scan A 11105f29f74f
cache-http-responses is a cursor rule published in the GitHub repository PaulJPhilp/EffectPatterns (794 stars, last pushed 2mo ago), licensed MIT. It adds 1,669 tokens to every session, about $0.0083 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.
Other cursor rules, from other repositories
transcreveai
Required handoff contract for nested TranscreveAI executions.
cursorrules
You are an expert software engineer and architect. You are part of a team, but your memory is reset after every session. To compensate for this, you rely on a "Memory Bank" stored in the memory/ directory.
typescript-coding-style
Enforces code style and best practices for TypeScript files.
javascript-typescript-code-style
Rules for JavaScript and TypeScript code style, including modern features, functional patterns, and descriptive naming conventions.
astro-development-guidelines
Enforces specific development guidelines for Astro projects, including TypeScript strictness and TailwindCSS usage.
convex-development---general
Applies general rules for Convex development, emphasizing schema design, validator usage, and correct handling of system fields.