Summary
Add f.memory to the flow surface, backed by ai-hist (relayhistory) v0.4.1+. This closes the memory bullet of SURFACE.md §2 rule 3 (line 67).
The kernel vocabulary is closed; f.memory is a helper, not a primitive. Its writes compile to journaled effect steps. Its reads inform the run but are not journaled (they don't mutate).
Verbatim spec quote — SURFACE.md §2 rule 3
f.memory — relayhistory: f.memory.recall(query), f.memory.why(task), f.memory.learn(finding).
And the harness example on line 34 declares memory scopes on the flow header:
memory: { script: true, agent: true }, // gate 5 — relayhistory-backed
The full memory scope contract lives at docs/GATE5-MEMORY-CONTRACT.md.
Verified state of relayhistory / ai-hist (2026-09-11)
- Package:
ai-hist v0.4.1 (npm; TS SDK) — also ships as a Rust CLI and stdio MCP server.
- Path (source):
/Users/khaliqgant/Projects/AgentWorkforce/relayhistory
- TS SDK surface (
sdk-ts/src/index.ts):
openAiHist({...}): Promise<AiHist>
class AiHist with methods for search, list, stats, handoff, trajectory
- Types:
HistoryEntry, SessionMeta, HandoffCandidate, Tag, TaggedSession, SessionSummary, ListOptions, SearchOptions, Stats, TrajectoryEntry, TrajectorySearchOptions, GetHandoffOptions, OpenOptions, OpenSourceInfo
- Additional export:
defaultDbPath(), resumeCommand(...)
- MCP server exposes (per README):
search_history, get_context, search_trajectories, why_for_task.
- Trajectory shape:
decisions (question → chosen → reasoning → alternatives) + retrospectives (learnings, confidence).
- Local-first: SQLite + FTS5, no keys/network required. Cloud sync opt-in via
ai-hist/cloud export.
This is enough to back the spec's recall / why / learn triad directly. No new store to build.
Scope of this slice
Surface changes (packages/surface)
Add memory: MemoryHelper to Ctx in packages/surface/src/context.ts.
New file packages/surface/src/memory.ts exporting the helper contract:
export interface MemoryRecallOptions {
limit?: number; // default 20
project?: string; // scope to a project
source?: 'claude' | 'codex' | 'cursor' | 'grok' | 'relay' | 'trajectory' | 'opencode';
}
export interface MemoryFinding {
question: string; // what was being decided
chosen: string; // the decision
reasoning: string; // why
alternatives?: string[];
confidence?: number; // 0..1
}
export interface MemoryHelper {
recall(query: string, options?: MemoryRecallOptions): Promise<HistoryEntry[]>;
why(task: string): Promise<TrajectoryEntry[]>;
learn(finding: MemoryFinding): Promise<void>;
}
HistoryEntry and TrajectoryEntry are re-exported from ai-hist types so authors get the same shapes.
Header contract
The flow header accepts a memory: field:
memory?: { script?: boolean; agent?: boolean }
per SURFACE line 34 and GATE5-MEMORY-CONTRACT.md. Semantics of script vs agent scope are already governed by that contract; this slice does not renegotiate them, it wires the surface method through to the SDK.
Preflight (packages/sdk/src/preflight.ts)
- If the flow header declares
memory: {...} OR the compiler detects f.memory.* usage in an authored TS body, verify that:
ai-hist binary is reachable on $PATH or the JS SDK ai-hist is importable from node_modules, and
defaultDbPath() resolves to a readable location (create-if-missing is fine).
- If either check fails, refuse with
memory_unreachable and name the specific probe that failed.
- Add
memory_unreachable to failure-kinds.ts.
Executor (packages/sdk/src/authored-flow-executor.ts)
- Attach
f.memory to the injected context.
recall / why are read operations — call the SDK directly, do not journal a step. They return their SDK result.
learn is a write operation — journal one effect step with type: memory.learn and the finding as the effect payload. Use the existing effect record/confirm path so exactly-once dedup applies. Idempotency key = flow-run-id + step-id (same convention as slice B's slack write).
Dependencies (packages/sdk/package.json, packages/surface/package.json)
Add ai-hist at ^0.4.1 (or the current v0.x compatible range) as a runtime dep in packages/sdk and a type dep (re-exported types) in packages/surface. Do not add a transitive better-sqlite3 unless the SDK requires it — prefer letting ai-hist own its native binding.
Acceptance evidence
The PR body must include verbatim command output demonstrating each of the following:
- Basic recall works. An authored TS smoke flow calls
const bugs = await f.memory.recall('auth bug'); console.log(bugs.length); against a seeded fixture ai-hist DB in testdata/memory/ and prints a nonzero count.
- learn round-trips through why. The same flow calls
await f.memory.learn({...}) with a distinctive question, then await f.memory.why(<that-question>) and asserts the finding appears with chosen/reasoning intact.
- Preflight refuses when unreachable.
flows check on a flow that declares memory: (or uses f.memory.*) against a PATH with no ai-hist and no node_modules/ai-hist fails with memory_unreachable, exit 2.
- Fresh DB, cloud disabled. A test that creates a temp
defaultDbPath(), opens it via openAiHist, uses only recall/learn, and never touches ai-hist/cloud.
- Idempotency.
learn called twice with the same idempotency key produces one journal effect record.
- New vitest file
packages/sdk/tests/f-memory.test.ts (or similar) with the four above cases, plus a preflight-refuses-on-missing case.
Files to touch
Add:
packages/surface/src/memory.ts
packages/sdk/tests/f-memory.test.ts
testdata/memory/ (small fixture ai-hist DB or a seed script that builds one deterministically)
Modify:
packages/surface/src/context.ts — add memory: MemoryHelper
packages/surface/src/index.ts — re-export memory types
packages/surface/package.json — add ai-hist types-only dep
packages/sdk/src/authored-flow-executor.ts — wire f.memory on ctx
packages/sdk/src/preflight.ts — memory_unreachable probe
packages/sdk/src/failure-kinds.ts — new kind
packages/sdk/package.json — add ai-hist runtime dep
docs/SURFACE.md — a small clarifying paragraph under §2 rule 3 memory bullet linking to GATE5-MEMORY-CONTRACT.md and stating that learn journals as effect; do not restate the spec
Not in scope
- Cloud push. The
ai-hist/cloud export exists but this slice ships local SQLite only. Cloud sync is a follow-up once f.memory write shape is stable.
- Pair mode ("cited warnings drawn from your team's own prior work" per ai-hist README §Why). That's a separate surface, not this triad.
- Server-side scrubbing / PII redaction. Cloud-only concern.
- Renegotiating GATE5-MEMORY-CONTRACT.md. Consume it as-is; don't touch script vs agent scope semantics.
- Cross-flow memory sharing. Each flow's memory scope is defined by its header; sharing is a GATE5 concern.
Interaction with adjacent slices
- Slice B (
f.slack) establishes the "helper compiled to effect step" pattern. f.memory.learn follows that same pattern; read the B PR for the exact effect-record/confirm shape before duplicating a scheme.
- Slice A (
flows build) — f.memory usage should be included in the preflight declaration written into the immutable bundle, so at deploy-time we can still verify memory reachability.
- Slice C (
f.mcp) — ai-hist's stdio MCP server is a real MCP server. If slice C ships f.mcp, the ai-hist MCP could in principle be reached via tools: { mcp: ['ai-hist'] } instead of via f.memory. That is not this slice. Keep f.memory as the ergonomic path per SURFACE §2 rule 3.
References
- SURFACE.md §2 rule 3 (line 65-69) and line 34 (memory header example)
docs/GATE5-MEMORY-CONTRACT.md — memory scope contract
/Users/khaliqgant/Projects/AgentWorkforce/relayhistory/sdk-ts/src/index.ts — ai-hist TS SDK surface
/Users/khaliqgant/Projects/AgentWorkforce/relayhistory/README.md — ai-hist capabilities
- Slice B PR — helper-namespace-to-effect-step pattern
Summary
Add
f.memoryto the flow surface, backed byai-hist(relayhistory) v0.4.1+. This closes the memory bullet of SURFACE.md §2 rule 3 (line 67).The kernel vocabulary is closed;
f.memoryis a helper, not a primitive. Its writes compile to journaled effect steps. Its reads inform the run but are not journaled (they don't mutate).Verbatim spec quote — SURFACE.md §2 rule 3
And the harness example on line 34 declares memory scopes on the flow header:
The full memory scope contract lives at
docs/GATE5-MEMORY-CONTRACT.md.Verified state of relayhistory / ai-hist (2026-09-11)
ai-histv0.4.1 (npm; TS SDK) — also ships as a Rust CLI and stdio MCP server./Users/khaliqgant/Projects/AgentWorkforce/relayhistorysdk-ts/src/index.ts):openAiHist({...}): Promise<AiHist>class AiHistwith methods for search, list, stats, handoff, trajectoryHistoryEntry,SessionMeta,HandoffCandidate,Tag,TaggedSession,SessionSummary,ListOptions,SearchOptions,Stats,TrajectoryEntry,TrajectorySearchOptions,GetHandoffOptions,OpenOptions,OpenSourceInfodefaultDbPath(),resumeCommand(...)search_history,get_context,search_trajectories,why_for_task.decisions(question → chosen → reasoning → alternatives) +retrospectives(learnings, confidence).ai-hist/cloudexport.This is enough to back the spec's
recall/why/learntriad directly. No new store to build.Scope of this slice
Surface changes (
packages/surface)Add
memory: MemoryHelpertoCtxinpackages/surface/src/context.ts.New file
packages/surface/src/memory.tsexporting the helper contract:HistoryEntryandTrajectoryEntryare re-exported fromai-histtypes so authors get the same shapes.Header contract
The flow header accepts a
memory:field:per SURFACE line 34 and GATE5-MEMORY-CONTRACT.md. Semantics of
scriptvsagentscope are already governed by that contract; this slice does not renegotiate them, it wires the surface method through to the SDK.Preflight (
packages/sdk/src/preflight.ts)memory: {...}OR the compiler detectsf.memory.*usage in an authored TS body, verify that:ai-histbinary is reachable on$PATHor the JS SDKai-histis importable fromnode_modules, anddefaultDbPath()resolves to a readable location (create-if-missing is fine).memory_unreachableand name the specific probe that failed.memory_unreachabletofailure-kinds.ts.Executor (
packages/sdk/src/authored-flow-executor.ts)f.memoryto the injected context.recall/whyare read operations — call the SDK directly, do not journal a step. They return their SDK result.learnis a write operation — journal oneeffectstep withtype: memory.learnand the finding as the effect payload. Use the existing effect record/confirm path so exactly-once dedup applies. Idempotency key = flow-run-id + step-id (same convention as slice B's slack write).Dependencies (
packages/sdk/package.json,packages/surface/package.json)Add
ai-histat^0.4.1(or the current v0.x compatible range) as a runtime dep inpackages/sdkand a type dep (re-exported types) inpackages/surface. Do not add a transitivebetter-sqlite3unless the SDK requires it — prefer lettingai-histown its native binding.Acceptance evidence
The PR body must include verbatim command output demonstrating each of the following:
const bugs = await f.memory.recall('auth bug'); console.log(bugs.length);against a seeded fixture ai-hist DB intestdata/memory/and prints a nonzero count.await f.memory.learn({...})with a distinctive question, thenawait f.memory.why(<that-question>)and asserts the finding appears withchosen/reasoningintact.flows checkon a flow that declaresmemory:(or usesf.memory.*) against aPATHwith noai-histand nonode_modules/ai-histfails withmemory_unreachable, exit 2.defaultDbPath(), opens it viaopenAiHist, uses onlyrecall/learn, and never touchesai-hist/cloud.learncalled twice with the same idempotency key produces one journal effect record.packages/sdk/tests/f-memory.test.ts(or similar) with the four above cases, plus a preflight-refuses-on-missing case.Files to touch
Add:
packages/surface/src/memory.tspackages/sdk/tests/f-memory.test.tstestdata/memory/(small fixture ai-hist DB or a seed script that builds one deterministically)Modify:
packages/surface/src/context.ts— addmemory: MemoryHelperpackages/surface/src/index.ts— re-export memory typespackages/surface/package.json— addai-histtypes-only deppackages/sdk/src/authored-flow-executor.ts— wiref.memoryon ctxpackages/sdk/src/preflight.ts—memory_unreachableprobepackages/sdk/src/failure-kinds.ts— new kindpackages/sdk/package.json— addai-histruntime depdocs/SURFACE.md— a small clarifying paragraph under §2 rule 3 memory bullet linking to GATE5-MEMORY-CONTRACT.md and stating thatlearnjournals aseffect; do not restate the specNot in scope
ai-hist/cloudexport exists but this slice ships local SQLite only. Cloud sync is a follow-up oncef.memorywrite shape is stable.Interaction with adjacent slices
f.slack) establishes the "helper compiled to effect step" pattern.f.memory.learnfollows that same pattern; read the B PR for the exact effect-record/confirm shape before duplicating a scheme.flows build) —f.memoryusage should be included in the preflight declaration written into the immutable bundle, so at deploy-time we can still verify memory reachability.f.mcp) — ai-hist's stdio MCP server is a real MCP server. If slice C shipsf.mcp, the ai-hist MCP could in principle be reached viatools: { mcp: ['ai-hist'] }instead of viaf.memory. That is not this slice. Keepf.memoryas the ergonomic path per SURFACE §2 rule 3.References
docs/GATE5-MEMORY-CONTRACT.md— memory scope contract/Users/khaliqgant/Projects/AgentWorkforce/relayhistory/sdk-ts/src/index.ts— ai-hist TS SDK surface/Users/khaliqgant/Projects/AgentWorkforce/relayhistory/README.md— ai-hist capabilities