@hypha/workcache is a typed runtime cache that consumes Hypha events and
materializes reusable CacheBlock records. It is not the source of truth:
DomainPack, Session, Run, and Event semantics stay unchanged, and replay,
audit, regression, and projections still derive from events.
Recovery outcomes use the same boundary. RecoveryTree stores revision-safe
strategy knowledge derived from recovery events; it never marks an FSM case
complete or replaces a durable receipt.
WorkCache V1 maps only current FrameworkEventType values by default.
Unknown events are ignored or rejected according to unknownEventPolicy; they
are not accepted as source events unless extension events are explicitly
enabled.
| Source events | Work node | Primary tree |
|---|---|---|
agent.reasoning.completed, thinking.completed, agent.deliberation.completed, reasoning.decision.recorded |
plan |
PlanTree |
inference.completed, model.call.completed |
computation |
ComputationTree |
tool.call.completed, mcp.call.completed |
tool |
ToolTree |
context.build.completed, context.compacted |
observation |
ObservationTree |
message.enqueued, message.delivered, message.acknowledged, message.failed, message.dead_lettered |
observation |
ObservationTree |
eval.completed, regression.completed |
verification |
VerificationTree |
memory.read.completed, memory.write.committed |
memory |
MemoryTree |
recovery.attempt.completed, recovery.case.resolved, recovery.case.escalated |
recovery |
RecoveryTree |
llm.cache.write with prompt prefix metadata |
prompt_prefix |
PromptPrefixTree |
MessageTree and KVPrefixTree are not V1 roots. PromptPrefixTree stores
stable prompt blocks and can materialize a logical prefix string; it does not
manage provider KV cache.
| Export | Purpose |
|---|---|
RuntimeTypeDefinition |
Source event to work node/tree alignment plus materializer. |
NormalizedWorkEvent |
Event payload normalized for a primary tree. |
WorkGraphNode, WorkGraphEdge, WorkGraph, DemandSignal |
Event-derived scheduling graph, typed dependencies, and cache demand signals. |
CacheBlock<T> |
Typed cache artifact with validity, provenance, utility, and source event linkage. |
CacheTree<T>, TypedCacheForest |
Tree lookup/write/invalidation over a shared store. |
WorkCacheManager |
Ingests events, enforces TTL/validity, and emits audit events. |
WorkCachePolicy |
Store mode, prompt budget, unknown-event behavior, and per-tree TTLs. |
WorkCacheRecoveryKnowledgeStore |
RecoveryKnowledgePort backed by revision-safe RecoveryTree blocks. |
WorkCache updates follow three rules:
event-first: source runtime events are recorded before WorkCache ingests them.graph-derived: each normalized source event becomes a typedWorkGraphNode, dependencyWorkGraphEdgerecords are derived from payload references/provenance/cache links, andDemandSignalscores are computed from proximity, recompute cost, fanout, criticality, staleness risk, and validation cost.tree-local: eachCacheTreeonly updates blocks in its own tree. Demand signals update block utility and hot-index priority; they do not mutate source events or agent state.
The graph is a scheduling view over events, computations, and cache blocks. It
is rebuildable by replaying source events through WorkCacheManager.ingest().
It is not a second event log and it does not introduce additional runtime
event names.
MemoryWorkCacheStore is for local runs and tests. SQLiteWorkCacheStore
persists workcache_blocks with id, tree_type, node_type, cache_key,
serialized block JSON, timestamps, expiry, and source event linkage.
HotIndexedWorkCacheStore wraps either store and keeps an in-process index by
block id and tree/cache key. Runtime lookup checks the hot index first, writes
through to the backing store, and evicts low-utility entries by demand score and
last update time. Hot and Redis indexes verify their tree/key binding before a
hit; stale aliases are removed instead of returning a differently keyed block.
The intended runtime layout is:
source events in event log
-> WorkGraphIndex and DemandSignal in CPU memory
-> HotIndexedWorkCacheStore in CPU memory
-> MemoryWorkCacheStore, SQLiteWorkCacheStore, or RedisWorkCacheStore
SQLite and Redis modes give persistent or shared cache trees; the event log
remains the rebuild source of truth. Cache blocks and keys include tenant,
user, workspace, session, agent, and DomainPack scope where available. The
default policy requires userId. Unscoped events bypass caching, scope
mismatches miss, and validity.status=unknown is never reusable.
Configure the server with:
HYPHA_WORKCACHE=off
HYPHA_WORKCACHE=memory # bundled server default
HYPHA_WORKCACHE=sqlite
HYPHA_WORKCACHE=redis
HYPHA_WORKCACHE_SQLITE_PATH=./data/runtime/cache/hypha-workcache.sqlite
HYPHA_WORKCACHE_PROMPT_BUDGET_TOKENS=4096
HYPHA_WORKCACHE_FAILURE_MODE=bypass
HYPHA_WORKCACHE_SCOPE_REQUIREMENT=userMemory and tree stores enforce configured entry and byte limits. WorkGraph history and demand signals are also bounded. Redis mode publishes versioned invalidation messages so peer hot indexes cannot continue serving a deleted block; Redis index replacement and deletion use atomic operations when the client supports them. Store calls are time-bounded and optional cache failures do not change the source event, inference result, or recovery outcome. Thinking Cache also requires the configured WorkCache scope before it can use its in-process singleflight map, so an unscoped request cannot be coalesced with another request even when no persistent write occurs.
Tool result reuse is conservative. Tool blocks are written only when the source event declares read-only side effects, stable args, permission scope, and validity or provenance hashes. Verification blocks require source, test, and environment hashes or an equivalent validity proof. Observation blocks invalidate when provenance or content hashes change.
PromptPrefixTree reuse is block-level. llm.cache.write prefix metadata is
split into stable blocks such as system, tool-schema, prompt-template,
project-context, domain-pack, and memory. Each block is keyed by
prefixHash, block type/id, and block hash, and stores the block content plus
ordering/template metadata. Dynamic suffix hashes and request ids are trace
metadata only; they do not invalidate stable prefix blocks. A template content
or version change should produce a new block hash and therefore a new block.
Recovery knowledge is keyed by tenant/user/workspace/session/agent/DomainPack
scope, failure fingerprint, participant id, and policy/spec/provider revisions.
WorkCacheManager.getRecoveryKnowledgePort() validates scoped knowledge with
the Core runtime schema before persistence and removes malformed legacy blocks.
A new revision removes stale entries only inside the same scope; invalidation
cannot remove another user's hint. The recovery supervisor still revalidates
every hit and uses only a verified strategy for a handler declared by the
current participant. Negative knowledge records a failed strategy but does not
authorize a different side effect. Store outages bypass recovery hints rather
than interrupting the recovery supervisor.
WorkCache may append derived audit events after the source event:
| Event | Meaning |
|---|---|
workcache.lookup |
A tree lookup was attempted for a source event. |
workcache.hit |
A fresh block with matching validity was reused. |
workcache.miss |
No block existed, or the block expired/was invalid. |
workcache.write |
A new block was stored. |
workcache.invalidate |
A stale, expired, or changed-validity block was removed. |
workcache.bypass |
The source event was known but not safe to cache. |
workcache.prefix.materialized |
Stable prefix content was assembled from PromptPrefixTree. |
Each audit payload includes sourceEventId, sourceEventType, treeType,
blockId, and cacheKey.