Skip to content

Commit 9da7b8f

Browse files
authored
Add Durable Streams fetch caching to agents runner (#4478)
## Summary - install an Undici cache interceptor for the built-in agents local Node runner - use a 100 MiB in-memory HTTP cache for Durable Streams fetches - add undici as an agents package dependency ## Testing - pnpm --filter @electric-ax/agents typecheck - pnpm --filter @electric-ax/agents stylecheck
1 parent 7c62024 commit 9da7b8f

6 files changed

Lines changed: 64 additions & 0 deletions

File tree

.changeset/quiet-streams-cache.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@electric-ax/agents': patch
3+
'@electric-ax/agents-desktop': patch
4+
---
5+
6+
Install an Undici HTTP cache dispatcher for the built-in agents local Node runner so Durable Streams catch-up reads can use server cache headers. Electric Agents Desktop uses an on-disk SQLite cache so runtime restarts can reuse cached catch-up responses.

packages/agents-desktop/src/runtime/lifecycle.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from 'node:path'
12
import { app, powerSaveBlocker } from 'electron'
23
import { AGENT_SKILLS_DIR } from '../shared/paths'
34
import {
@@ -290,6 +291,14 @@ export async function startRuntime(
290291
const nextRuntime = new BuiltinAgentsServer({
291292
agentServerUrl: activeServer.url,
292293
workingDirectory: deps.settings.workingDirectory ?? app.getPath(`home`),
294+
durableStreamsFetchCache: {
295+
store: `sqlite`,
296+
sqliteLocation: path.join(
297+
app.getPath(`userData`),
298+
`durable-streams-fetch-cache.sqlite`
299+
),
300+
maxCount: 10_000,
301+
},
293302
extraMcpServers: deps.settings.mcp?.servers,
294303
enabledModelValues: resolveEnabledModelValues(
295304
deps.settings.enabledModelValues

packages/agents/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"pino": "^10.3.1",
6363
"pino-pretty": "^13.0.0",
6464
"sqlite-vec": "^0.1.9",
65+
"undici": "^7.24.7",
6566
"zod": "^4.3.6"
6667
},
6768
"devDependencies": {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Agent, cacheStores, interceptors, setGlobalDispatcher } from 'undici'
2+
3+
const MEMORY_CACHE_SIZE_BYTES = 100 * 1024 * 1024
4+
5+
export type DurableStreamsFetchCacheOptions =
6+
| false
7+
| {
8+
store?: `memory` | `sqlite`
9+
sqliteLocation?: string
10+
maxCount?: number
11+
}
12+
13+
export function installDurableStreamsFetchCache(
14+
options: DurableStreamsFetchCacheOptions = {}
15+
): void {
16+
if (options === false) return
17+
18+
const store =
19+
options.store === `sqlite` || options.sqliteLocation
20+
? new cacheStores.SqliteCacheStore({
21+
location: options.sqliteLocation,
22+
maxCount: options.maxCount,
23+
})
24+
: new cacheStores.MemoryCacheStore({
25+
maxSize: MEMORY_CACHE_SIZE_BYTES,
26+
})
27+
28+
setGlobalDispatcher(
29+
new Agent().compose(
30+
interceptors.cache({
31+
store,
32+
})
33+
)
34+
)
35+
}

packages/agents/src/server.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import path from 'node:path'
2+
import { installDurableStreamsFetchCache } from './durable-streams-cache.js'
23
import { serverLog } from './log.js'
34
import {
45
createBuiltinAgentHandler,
@@ -29,12 +30,19 @@ import type {
2930
PullWakeRunner,
3031
PullWakeRunnerConfig,
3132
} from '@electric-ax/agents-runtime'
33+
import type { DurableStreamsFetchCacheOptions } from './durable-streams-cache.js'
3234
import type { StreamFn } from '@mariozechner/pi-agent-core'
3335

3436
export interface BuiltinAgentsServerOptions {
3537
agentServerUrl: string
3638
workingDirectory?: string
3739
mockStreamFn?: StreamFn
40+
/**
41+
* Configure the process-wide HTTP cache used by Undici-backed fetch calls.
42+
* Defaults to a 100 MiB in-memory cache. Pass `false` to leave the global
43+
* dispatcher unchanged.
44+
*/
45+
durableStreamsFetchCache?: DurableStreamsFetchCacheOptions
3846
/** Pull-wake runner configuration for built-in agents. */
3947
pullWake: {
4048
runnerId: string
@@ -177,6 +185,8 @@ export class BuiltinAgentsServer {
177185
throw new Error(`Builtin agents runtime already started`)
178186
}
179187

188+
installDurableStreamsFetchCache(this.options.durableStreamsFetchCache)
189+
180190
const pullWake = this.options.pullWake
181191
if (!pullWake?.runnerId) {
182192
throw new Error(`Builtin agents require a pull-wake runner id`)

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)