The context engine. Where the ideas are.
A block fetches live state and renders it. A wall is a program that spawns blocks and assembles them every turn. A strategy is an ordering with cache breakpoints, because what's stable goes first and the provider bills you for the difference. The conversation is one block among a dozen. It isn't the state.
one per user scope A wall
class ManagerWall(Wall):
async def start(self):
self.system = await self.spawn(SystemPrompt)
self.runtime = await self.spawn(RuntimeContext) # who is this, what tier
self.workspace= await self.spawn(UserWorkspace) # their files, live
self.knowledge= await self.spawn(WorkingKnowledge) # what the gate kept
self.recent = await self.spawn(RecentConversation) # one block, not the state
self.now = await self.spawn(CurrentTime)
self.strategy("gate",
GatePrompt, pr.CacheControl(ttl="1h"),
self.runtime, self.workspace, pr.CacheControl(ttl="5m"),
self.recent, self.knowledge, self.now)
self.strategy("main",
self.system, pr.CacheWarm(name="main", ttl="1h"),
self.runtime, self.workspace, pr.CacheControl(ttl="1h"),
self.knowledge, pr.CacheControl(ttl="5m"),
self.recent, self.now)
self.user_input(self._ear)
async def _ear(self, event):
await self.runtime.refresh() # fail closed
gate = await self._loop(model=CHEAP, toolset=gate_tools, stream=True,
scope=CURRENT_SCOPE.get(), context=self.outer)
main = await self._loop(model=BIG, toolset=main_tools,
allowed_tools={"sources": self.runtime.allowed},
stream=True, scope=CURRENT_SCOPE.get(), context=self.outer)
Two strategies over the same blocks. The gate runs on a cheap model with a narrow toolset and decides what persists. The main turn runs on the big model with the user's real tools. Both are _loop calls the Hub executes, scoped to the user, rendered from self.outer.
The wall is the ear. User input lands here, not in a UI, not in a router.
fetch, render, tools A block
class WorkingKnowledge(Block):
"""Whole documents the gate decided to keep. Topic-embedded, not chunked."""
async def fetch(self):
self.docs = await kb.kept_for(CURRENT_SCOPE.get())
def outer(self):
if not self.docs: return None
return SystemContent("\n\n".join(f"<doc id={d.id}>{d.text}</doc>" for d in self.docs))
@tewl(source="pro_tools", category="knowledge")
async def keep_knowledge_doc(self, args: KeepArgs) -> dict:
"""Persist a matched doc into working knowledge for the main turn."""
return await kb.keep(CURRENT_SCOPE.get(), args.doc_ids)
fetch() pulls state. outer() renders it, or nothing. Tools declared on a block belong to that block's source, and the wall decides which sources a given turn can see.
Knowledge here is topic-embedded, not chunk-embedded: each doc carries generated topics, user input matches on those, whole documents come back, and the gate keeps or drops them. Retrieval finds candidates. The gate is the editor.