src/lib/llmClient.ts: browser POSTs to /internal/canvas-llm/chat with
{messages, max_tokens}. Mirrors the normalized request shape used by
earendil-works/pi's @earendil-works/pi-ai. Returns:
- ok:true with content+provider when configured and successful
- ok:false, configured:false when proxy 404/503/unreachable
- ok:false, configured:true when provider returned an error
routeAgentInput now falls back to the LLM with a system prompt that
includes the user identity, tool catalogue, and the top-3 vault recall
hits when no deterministic tool matches. When the proxy is not
configured the deterministic vault-hint reply is used (no regression
in the demo tier).
User explicit ask: 'maybe with a miniature hindsight or a Karpathy
vault behind it as its memory system for each tenant.'
- src/lib/agentMemory.ts: EA2-backed vault per user. Vault root is
flow.kind=value at flow/memory_vault_<email_slug>; each memory is a
flow.kind=value doc linked via defines edge with role=presentation.
- agentMemory.remember/listAll/recall — recall is a simple keyword
overlap score with recency tiebreak.
- agentTools: new 'remember' and 'recall' tools.
matchers: 'remember that ...', 'note ...', 'save ...' /
'recall ...', 'remind me ...', 'what do you know about ...'
- routeAgentInput: every user turn is best-effort persisted as an
episodic memory (fire-and-forget). When no command matches, the
router does a vault recall and surfaces related notes before
showing the help message.
Vault key: deterministic per-user. Owner_email stored on the vault
root + on each memory's config.memory. No raw IDs visible to the user.
The /api/ea2/flow/processes endpoint caps at ~203 items and pr_to_po_def
is not in that window for this tenant despite being status=published.
fetchStartableFlows() does a direct GET per allowlist key and merges
results with the catalogue list (allowlist hits take precedence).
Wired into Hub.loadPublishedFlows + agentTools.list_processes +
agentTools.start_process.
EA2 doesn't expose a generic flow-by-kind list endpoint; flow/processes
is definition-only. Migrated chat to flow.kind=value, so direct listing
needed a new strategy.
- Each user has a deterministic inbox flow.kind=value doc keyed by
email slug. ensureInbox() creates it idempotently on first listThreads.
- createThread links the new thread into BOTH participants' inboxes via
defines edges with role='presentation' (each link is one apply-batch).
- listThreads(userEmail) reads defines edges from the user's inbox key.
No more reliance on source_context regex filtering through the
definition catalogue.
Updated all 3 call sites: Chat.tsx (mount + after-create refresh) and
agentTools.send_chat. 31/31 tests green.
Oracle remediation batch 1 — closing gaps #1 (agent overclaim), #4
(demo-data leak), #5 (raw-ID exposure):
- src/lib/flowCuration.ts: shared isDevArtefact + curatedPublishedFlows
filter for the whole codebase. Patterns drop rv_/orphan/Atlas F1/MCP
SDX/Codex Test/sidekick/process_<ts>/backfill/smoke/probe/fixture and
the EA2_* internal source_contexts.
- src/scenes/Hub.tsx: loadPublishedFlows uses the shared curator
- src/lib/agentTools.ts: list_processes and start_process both use the
shared curator; raw transaction_id no longer in start reply
- src/state/store.ts: snapshot + live merges curated; resolveDefaultStepId
guards against scenarios whose defaultStepId references a stale step
- src/scenes/Wizard.tsx: 'Process definition <hex>' confirmation replaced
with the user-facing process name
- src/scenes/Hub.tsx: start toast no longer shows transaction_id prefix
- src/scenes/Agent.tsx + App.tsx + Landing.tsx: rename Pi -> FlowMaster
Command Assistant. Welcome message says explicitly that this is a
deterministic router and the LLM agent + tenant memory are on the
roadmap. Tab label, hub chip, turn author all rebranded.
- src/scenes/GeoAttendance.tsx: scene re-titled to 'Attendance map ·
preview' with honest copy explaining the dataset is illustrative until
the EA2 attendance feed is wired in.
- src/data/synthetic.ts: orphaned from runtime (kept in tree for tests).
Snapshot now sources only the live-cached EA2 read (scenarios.json),
filtered through the same curator.
29/29 tests green. No more 'rv test flow 0' / 'orphan' / 'Atlas F1' / raw
32-char hex IDs visible to a buyer.
New Pi tab. Talks to a typed tool router that maps natural-language
intent to real EA2 calls:
navigate go to mission / runs / studio / chat / hubs
start_process 'run laptop procurement for store-204'
send_chat 'tell Mariana that the quote is approved'
create_process opens the wizard
list_processes pulls published flow definitions for the tenant
This is the OSS-pluggable shape: today the router is rule-based and
fully wired to FlowMaster; swapping in an LLM provider for intent
parsing is a localised change in routeAgentInput().