@iflytekopensource/memflywheel
File-native long-term memory for Pi, Hermes, OpenCode, and OpenClaw.
Package details
Install @iflytekopensource/memflywheel from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@iflytekopensource/memflywheel- Package
@iflytekopensource/memflywheel- Version
0.1.1- Published
- Jul 28, 2026
- Downloads
- 83/mo · 83/wk
- Author
- oldduckde
- License
- Apache-2.0
- Types
- extension
- Size
- 366.2 KB
- Dependencies
- 3 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./pi-extension/index.mjs"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@iflytekopensource/memflywheel
The single public MemFlywheel package for Pi, Hermes, OpenCode, and OpenClaw. It contains the host lifecycle mappings, native model bindings, direct package entrypoints, and the Hermes MemoryProvider installer. Core memory semantics remain in the bundled MemFlywheel Core and SDK layers.
The package installs Pi Agent Core, pi-ai, and proper-lockfile as runtime
dependencies.
Built-in adapters
| id | host | prompt recall | turn end | session end | integration |
|---|---|---|---|---|---|
pi |
Pi | context |
agent_end |
session_shutdown |
real |
hermes |
Hermes | prefetch |
sync_turn |
on_session_end |
real |
openclaw |
OpenClaw | before_prompt_build |
agent_end |
session_end |
real |
opencode |
OpenCode | experimental.chat.system.transform |
experimental.text.complete / session.idle |
session.deleted |
real |
Host-specific installation still differs: Pi, OpenCode, and OpenClaw load
package entrypoints directly, while Hermes runs the included
memflywheel-hermes-install command to install its Python MemoryProvider,
config wiring, and skill mirror.
pi— real:@iflytekopensource/memflywheelis a Pi package. Itspackage.jsondeclarespi.extensions, and Pi installs it withpi install npm:@iflytekopensource/memflywheel.context→onPromptBuild;agent_end→onTurnEnd; andsession_shutdown→onSessionEnd.hermes— real:memflywheel-hermes-installinstalls a HermesMemoryProvider, whose bridge imports this package's shared runtime.prefetchbuilds recall context,sync_turnruns the write-side lifecycle, and session end coordinates idle consolidation.
npm install -g @iflytekopensource/memflywheel
memflywheel-hermes-install
hermes config set memory.provider memflywheel
Each adapter declares a defaultConfigRelPath (the host config under $HOME) and
an integrationNote describing how the host actually consumes the scribe.
The HostAdapter contract
interface HostAdapter {
readonly id: string;
readonly name: string;
readonly lifecycle: LifecycleMap; // host event → scribe hook, per hook
attach(scribe: MemFlywheel, host: HostRuntime): () => void; // wire events, returns disposer
install(target: InstallTarget, opts?: { apply?: boolean }): Promise<InstallPlan | InstallResult>;
verify(target: InstallTarget): Promise<VerifyResult>; // real round-trip from disk
doctor(target: InstallTarget): Promise<DoctorFinding[]>;
}
attach — pure event translation
attach binds each host event to the matching scribe hook and returns a disposer
that removes every listener. The MemFlywheel interface is structural: any
object with the lifecycle hooks satisfies it, including the runtime assembled by
createMemFlywheelHarnessRuntime(...).
import { piAdapter } from "@iflytekopensource/memflywheel";
const dispose = piAdapter.attach(scribe, host);
// ... later
dispose();
onTurnEndis fire-and-forget: a rejecting extractor never blocks or throws into the host's stream.onPromptBuildreturns the two recall segments (systemPrompt,preludePrompt). Hosts that need the result attach arespondcallback to the emitted payload; the adapter delivers thePromise<MemFlywheelContext>to it.
install — plan / apply (never "write and hope")
Install always plans first. The plan is a pure read that reports the steps it
would take and whether the on-disk wiring is already current (satisfied).
Passing { apply: true } then merges a versioned wiring marker into the host
config and writes it atomically (temp file + rename), preserving all other keys.
const plan = await piAdapter.install({ configPath }); // no writes
if (!plan.satisfied) {
await piAdapter.install({ configPath }, { apply: true });
}
Apply is idempotent: re-applying current wiring writes nothing. Stale (older version) or corrupt configs are detected and rewritten.
verify — real round-trip
verify re-reads the host config from disk and confirms the wiring marker is
present, belongs to this adapter, matches the current version, and has the exact
expected bindings. It never reports success from an in-memory write — a
post-install tamper is caught.
const v = await piAdapter.verify({ configPath });
if (!v.ok) console.error(v.problems);
doctor — diagnose installed state
for (const f of await piAdapter.doctor({ configPath })) {
console.log(f.code, f.message); // not-installed | stale-wiring | corrupt-config | ok
}
Custom adapters
Build one from a lifecycle map + payload translators with makeAdapter:
import { makeAdapter, normalizeMessages, readString } from "@iflytekopensource/memflywheel";
export const myAdapter = makeAdapter({
id: "my-host",
name: "My Host",
lifecycle: {
onSessionStart: { hook: "onSessionStart", hostEvent: "start", note: "..." },
onPromptBuild: { hook: "onPromptBuild", hostEvent: "build", note: "..." },
onTurnEnd: { hook: "onTurnEnd", hostEvent: "done", note: "..." },
onIdle: { hook: "onIdle", hostEvent: "idle", note: "..." },
},
translators: {
sessionId: (p) => readString(p, "sessionId"),
turnEnd: (p) => ({
sessionId: readString(p, "sessionId"),
messages: normalizeMessages((p as { messages?: unknown }).messages),
}),
},
});
Install/verify/doctor come for free.
Direct integration: createMemFlywheelHarnessRuntime
An adapter contains no memory loop. It resolves the host's active model into a
pi-ai Model + StreamFn and exposes lifecycle events through HostHarnessPort.
createMemFlywheelHarnessRuntime then builds Extraction, Dream, and Skill
Evolution on the single Pi Agent Core runner.
import { createMemFlywheelHarnessRuntime } from "@iflytekopensource/memflywheel";
const { scribe, sdk } = createMemFlywheelHarnessRuntime({ port });
Pi phase-1 native integration uses a host port:
import {
createMemFlywheelHarnessRuntime,
createPiHarnessPort,
} from "@iflytekopensource/memflywheel";
import { streamSimple } from "@earendil-works/pi-ai/compat";
export default function memFlywheelExtension(pi) {
const port = createPiHarnessPort(pi, { streamSimple });
const runtime = createMemFlywheelHarnessRuntime({ port });
return runtime.dispose;
}
The packaged Pi extension enables learned skills by default. It stores
MemFlywheel state under $MEMFLYWHEEL_HOME when set, otherwise
~/.pi/agent/memflywheel, and mirrors finalized learned skills into Pi's native
~/.pi/agent/skills/memflywheel/ tree. Pi then lists them through its ordinary
skills loader and renders them in the host-native <available_skills> prompt
surface.
Large memory stores need embedding pre-recall after the generated MEMORY.md
index grows beyond the direct prompt budget (200 lines / 25 000 bytes). When
memoryIndexRetrieval is not supplied explicitly, the runtime auto-enables
index-layer retrieval from OpenAI-compatible embedding env:
export MEMFLYWHEEL_EMBEDDING_ENDPOINT="https://embedding-gateway.example.com/v1"
export MEMFLYWHEEL_EMBEDDING_API_KEY="..."
export MEMFLYWHEEL_EMBEDDING_MODEL="text-embedding-3-small"
export MEMFLYWHEEL_MEMORY_INDEX_RETRIEVAL="auto"
MEMFLYWHEEL_EMBEDDING_API_KEY is sent as a Bearer token. For proxy or gateway
deployments, set MEMFLYWHEEL_EMBEDDING_ENDPOINT to the OpenAI-compatible
gateway URL; provider-specific auth and routing stay in that gateway or in a
custom memoryIndexRetrieval.embeddingProvider.
Use MEMFLYWHEEL_MEMORY_INDEX_RETRIEVAL=required while testing if a missing or
broken embedding provider should fail prompt build instead of using direct index
injection.
Custom hosts can either pass custom lifecycle hooks or ask
createMemFlywheelHarnessRuntime to assemble the bundled file-native
learned-skill store:
const { scribe } = createMemFlywheelHarnessRuntime({
port,
learnedSkills: {
skillsRoot: "/path/to/skills",
checkpointRoot: "/path/to/.skill-checkpoints",
},
learningLoop: {
gate: { minDoneTurns: 3, cooldownTurns: 2, minToolCalls: 6 },
},
});
- With
resolveModelorport: real semantic extraction AND dream consolidation run as tool-calling subagents on the host's own model, writing memory files directly. - With
learnedSkills: the bridge creates a learned-skill store, recall provider, andrunSkillEvolutionAgent; turn-end can run extraction -> skill evolution -> dream, and the next prompt sees the learned-skill route. - With
skillRecall/skillPreludeBuilder: prompt build appends learned-skill routes through the same SDK prompt context. - With custom
learningLoop.skillEvolution: hosts may replace the default learned-skill runner while keeping SDK gate/dream coordination. - Without
resolveModel/portand without an explicitagent: construction fails unlessmode: "recall-only"is set explicitly. Recall-only injects memory on prompt build, turns never extract, and dream runs only its deterministic structural pre-pass. - The adapter-facing
onSessionEndruns a final agent-end sweep (extracting any not-yet-processed messages) before dropping the session.
Hosts with no in-process model-call API must either run recall-only or expose a real pi-ai stream through a sidecar/upstream host API. MemFlywheel does not parse text as fake tool calls.
const { scribe } = createMemFlywheelHarnessRuntime({ mode: "recall-only" });
Connect: install + round-trip verify in one call
connect resolves the target (an explicit path or the adapter's
defaultConfigRelPath under $HOME), plans the wiring, and — with apply —
applies it and immediately re-reads from disk to verify the marker round-trips:
import { connect, piAdapter } from "@iflytekopensource/memflywheel";
const plan = await connect(piAdapter); // plan only, no writes
const res = await connect(piAdapter, { apply: true }); // write + verify
if (!res.verify!.ok) console.error(res.verify!.problems);
Runnable integration examples live under examples/.
Pi, Hermes, OpenCode, and OpenClaw are the public first-class host paths.
Host setup, embedding pre-recall, verification, and troubleshooting live in
docs/integrations.md.