Milady is a local-first AI assistant built on elizaOS. It wraps the elizaOS runtime with a CLI, desktop app (Electrobun), web dashboard, and platform connectors (Telegram, Discord, WeChat, etc.). Inherits ~/.codex/AGENTS.md.
Any change in eliza/packages/cloud-frontend/ MUST go through the manual-review
loop before being declared done. Automated tests catch what they assert; hover
regressions, palette drift, broken empty states, and mobile layout breaks slip
through. The harness produces evidence — you produce the verdicts.
bun run --cwd eliza/packages/cloud-frontend audit:cloudThis runs tests/e2e/aesthetic-audit.spec.ts headlessly across every route in
src/App.tsx (53 routes × desktop + mobile × rest + hover ≈ 200 screenshots).
The injected-ethereum login flow is wired into the spec so dashboard pages
render their authed content; admin pages are dev-open via import.meta.env.DEV.
Output lands in:
eliza/packages/cloud-frontend/aesthetic-audit-output/
desktop/<slug>.png rest screenshot
desktop/<slug>--hover.png hover state (primary button focused)
mobile/<slug>.png
mobile/<slug>--hover.png
manual-review/<slug>.md REQUIRED per-page verdict markdown
contact-sheet.html grid view of every shot + flagged issues
report.json machine-readable audit data
- Run
audit:cloud. - Open
contact-sheet.html. Walk every page touched by your change (and every page reachable through transitive deps — header, footer, theme, buttons). - For each page, open
manual-review/<slug>.mdand fill in:- Visual issues (anything that looks wrong or off-brand)
- Color / hover violations (orange→black or any blue is banned)
- Layout breaks (viewport + element)
- Interaction targets the e2e suite should cover
- Verdict:
good·needs-work·needs-eyeball·broken
- Fix the issues.
- Commit screenshots + reviews + code together.
- Re-run the audit. Repeat. Do not declare done until every page is
good.
- Every page must have a screenshot AND a
manual-review/<slug>.mdchecked in. - Verdicts of
needs-workorbrokenblock "done." Fix or document why. - Never overwrite an existing
manual-review/<slug>.mdfrom automation — the human notes are load-bearing. The spec only auto-stubs new routes. - The contact sheet is the index, NOT the review. The review is the per-page markdown.
Full protocol: eliza/packages/cloud-frontend/AGENTS.md.
- Write the framework name as elizaOS in prose, comments, user-facing strings, and docs — never
ElizaOS. The npm scope remains@elizaos/*(lowercase). - Say Eliza agents (not "elizaOS agents") in plain language.
- The Eliza Classic plugin name is the one exception (Eliza = the 1966 chatbot).
bun install # postinstall hooks run automatically
bun run dev # API on :31337, UI on :2138 with hot reload (busy ports → next free + env sync)
bun run dev:desktop # Electrobun; skips vite build when apps/app/dist is up to date
bun run dev:desktop:watch # Vite dev server + Electrobun (HMR). Set MILADY_DESKTOP_VITE_BUILD_WATCH=1 for rollup watch.Optional — link a local elizaOS source checkout:
bun run setup:upstreams # initialize repo-local ./eliza and link local @elizaos/* packagesDesktop dev observability (Codex cannot see the native window):
GET /api/dev/stack(orbun run desktop:stack-status -- --json) — running window/process state.GET /api/dev/console-log— loopback tail of.milady/desktop-dev-console.log(default-on aggregated log).GET /api/dev/cursor-screenshot— loopback full-screen OS capture (default-on).
See eliza/docs/apps/desktop-local-development.md.
bun run build # tsdown + vite
bun run verify # typecheck + lint (alias: bun run check)
bun run test # parallel test suite
bun run test:e2e # end-to-end
bun run db:check # database security + readonly testsFirst-party packages, apps, and orchestrator scripts live under the eliza/ submodule. The top-level repo holds Milady-specific glue (apps/app/, packages/{vault,confidant}/, top-level scripts/).
eliza/ Submodule (milady-ai/eliza) — source of truth for runtime
packages/
app-core/ Main runtime package
src/
entry.ts CLI bootstrap (env, log level)
cli/ Commander CLI (milady command)
runtime/eliza.ts Agent loader — sets NODE_PATH, loads plugins dynamically
runtime/dev-server.ts Dev mode entry point (started by dev-ui.mjs)
api/ Dashboard API (port 31337 in dev, 2138 in prod)
config/ Plugin auto-enable, config schemas
services/ Business logic
scripts/
dev-ui.mjs Dev orchestrator (API + Vite) — driven by `bun run dev`
run-repo-setup.mjs Postinstall sequencer
setup-upstreams.mjs Initialize repo-local upstreams and link @elizaos packages
patch-deps.mjs Post-install patches for broken upstream exports
agent/ Upstream elizaOS agent (core plugins, auto-enable maps)
ui/ Shared UI component library
shared/ Shared utilities
skills/ Bundled @elizaos/skills (source of truth for default knowledge)
plugins/
plugin-wechat/ WeChat connector plugin (@elizaos/plugin-wechat)
plugin-agent-orchestrator/ Sub-agent dispatch (workspace:* in dev)
plugin-app-control/ APP action surface (modes: launch / relaunch / load_from_directory / list / create)
plugin-plugin-manager/ PLUGIN action surface (modes: install / eject / sync / reinject / list / search / core_status / create)
plugin-agent-skills/ USE_SKILL action + enabled_skills provider
apps/
app-companion/ Companion app
app-training/ Training pipeline (privacy filter lives here)
app-form/, app-knowledge/, ... (see eliza/apps/)
templates/
min-app/ Minimal Eliza app scaffold (used by APP create)
min-plugin/ Minimal Eliza plugin scaffold (used by PLUGIN create)
docs/apps/desktop-local-development.md
apps/ Top-level Milady-specific apps
app/ Main web + desktop UI (Vite + React)
electrobun/ Electrobun desktop shell
browser-bridge/, home/, homepage/
packages/ Top-level Milady-specific packages
vault/ Secrets / wallet vault
confidant/ Confidant integration
scripts/ Top-level Milady scripts
milady-postinstall-repo-setup.mjs Top-level postinstall entry
sync-workspace-default-skills.mjs Mirrors @elizaos/skills into skills/.defaults/
... (audit, drift checks, fixtures)
Two distinct skill systems live in this repo. Don't conflate them.
Bundled @elizaos/skills are the default knowledge base for the running Eliza agent and for any code agent working in this repo. Repo setup mirrors them into skills/.defaults/ for workspace access.
- Source of truth:
eliza/packages/skills/skills/(31 bundled skills). - Workspace mirror:
skills/.defaults/— refreshed byscripts/sync-workspace-default-skills.mjsduring setup; do not hand-edit. - Managed store seed:
eliza/packages/app-core/scripts/ensure-skills.mjsseeds the bundled skills on first run. - Runtime knowledge seed:
eliza/packages/agent/src/runtime/default-knowledge.tsseeds baseline runtime knowledge (including Eliza Cloud guidance). - Repo-local custom skills: put workspace-specific skills in visible subdirectories under
skills/(e.g.skills/plan-my-day/).
Open the SKILL.md of any of these from the workspace mirror when relevant:
Core eliza/cloud (read first when touching app, runtime, or Cloud work):
eliza-app-development— this repo as an elizaOS app; layout; local/remote/cloud routing.elizaos— runtime concepts, plugin abstractions, AgentRuntime, actions/providers/evaluators/services.eliza-cloud— Cloud as managed backend, app registration, hosted APIs, billing, monetization, container deploys.build-monetized-app— building a Cloud app that earns via inference markup; pairs witheliza-cloud.
Agent-orchestration / authoring:
coding-agent— spawning Codex / Claude Code / OpenCode / Pi via PTY-backed bash for sub-agent work.claude-subagent-milady-bridge— loopback endpoints exposing parent runtime context to a spawned coding sub-agent.skill-creator— authoring new SKILL.md packages (frontmatter, scripts, references, progressive disclosure).
Connectors / OS / SaaS integrations (use when the task touches that surface):
- iMessage / macOS:
imsg,bluebubbles,apple-notes,apple-reminders,things-mac,camsnap - Productivity:
obsidian,notion,slack,discord,github,trello,canvas,spotify-player - CLI tools:
blucli,wacli,ordercli,tmux,1password - Media / generation:
nano-banana-pro,nano-pdf - Misc:
weather,healthcheck,yara-authoring
When Eliza Cloud is enabled or requested, prefer it as the managed backend (app registration, auth, hosted APIs, usage tracking, billing, app domains, creator monetization, Docker container deployments) before inventing custom infrastructure. Cloud monetization (inference markups, purchase-share, redeemable earnings) is a first-class product constraint. If docs disagree with the current schema/UI/API in this repo, the repo wins.
.claude/skills/<name>/SKILL.md files exist in this repo (currently phase-review) but they are Claude Code slash commands — they only fire when the user is running Claude Code, not Codex. Codex agents can ignore them. Documented here so you don't mistake them for elizaOS runtime skills if you encounter them in a directory listing.
- All
@elizaos/*packages use thealphadist-tag.bun run setup:upstreamslinks repo-local./elizaand./pluginspackages so changes are picked up immediately.MILADY_SKIP_LOCAL_UPSTREAMS=1falls back to npm. @elizaos/plugin-agent-orchestratorresolves fromeliza/plugins/plugin-agent-orchestratorviaworkspace:*. Updating the submodule updates the orchestrator.- All official elizaOS plugin repos live under https://github.com/elizaOS-plugins. For plugin work, prefer adding the plugin repo as a git submodule under
eliza/plugins/(tracked ineliza/.gitmodules) and depending viaworkspace:*. Publish to npm when ready. - The eliza submodule is hosted at milady-ai/eliza, not the personal
Dexploarerfork. Pushes and PRs always go tomilady-ai/eliza.
MILADY_STATE_DIR/ELIZA_STATE_DIR— per-user state root (default~/.milady).MILADY_CONFIG_PATH/ELIZA_CONFIG_PATH— config file resolution.ELIZA_DISABLE_TRAJECTORY_LOGGING=1(orNODE_ENV=test) — opt out of trajectory persistence.MILADY_DISABLE_AUTO_BOOTSTRAP=1— skip the runtime native-optimization bootstrap.MILADY_ENABLE_CHILD_SKILL_CALLBACK=0— disable the child→parentUSE_SKILLbridge for spawned coding agents.EXECUTECODE_TIMEOUT_MS(default30000),EXECUTECODE_DISABLE—EXECUTE_CODEaction knobs.MILADY_APP_VERIFICATION_MAX_RETRIES(default3) —APP create/PLUGIN createverification retry cap.MILADY_PROTECTED_APPS— comma-separated names that cannot be overridden viaAPP load_from_directory. First-party apps undereliza/apps/are always implicitly included.MILADY_BROWSER_VERIFY_OPTIONAL=1— explicit opt-out for the headless-browser check whenpuppeteer-coreis absent.
Model defaults (sub-agents inherit unless overridden):
- Anthropic large:
claude-opus-4-7(override:ANTHROPIC_LARGE_MODEL). Small:claude-haiku-4-5-20251001. - OpenAI large/small:
gpt-5.5/gpt-5.5-mini(override:OPENAI_LARGE_MODEL/OPENAI_SMALL_MODEL).
Port env vars (never hardcoded — the dev orchestrator auto-shifts to the next free port and syncs env): MILADY_API_PORT (31337), MILADY_PORT (2138), ELIZA_GATEWAY_PORT (18789), ELIZA_HOME_PORT (2142), MILADY_WECHAT_WEBHOOK_PORT (18790).
USE_SKILLis the only canonical entry point for invoking an enabled skill. LegacyRUN_SKILL_SCRIPT/GET_SKILL_GUIDANCEare removed;RUN_SKILL/INVOKE_SKILLremain as similes.- The
enabled_skillsprovider runs at position-10and surfaces eligible skills to the planner each turn. - Trajectory persistence is on by default. Every turn lands in the
trajectoriestable unlessELIZA_DISABLE_TRAJECTORY_LOGGING=1. - Native optimization (
--backend native) is the default training backend (MIPRO / GEPA / bootstrap-fewshot). Outputs land under~/.milady/optimized-prompts/<task>/andOptimizedPromptServiceauto-loads at boot. - Auto-training defaults: 100 trajectories per task, 12h cooldown. Adjust via
/api/training/auto/configor Settings → Auto-Training. - The privacy filter at
eliza/apps/app-training/src/core/privacy-filter.tsis mandatory on every write path that touches real user trajectories — both the nightly export cron and the on-demand training orchestrator run it before any JSONL is written.
The runtime exposes two unified action surfaces — APP and PLUGIN — that replace the older single-purpose actions. New code MUST call APP / PLUGIN. Legacy actions (LAUNCH_APP, RELAUNCH_APP, LIST_APPS, INSTALL_PLUGIN, UNINSTALL_PLUGIN, EJECT_PLUGIN, SYNC_PLUGINS, REINJECT_PLUGINS, LIST_PLUGINS, SEARCH_PLUGINS, CORE_STATUS, etc.) remain as similes but are no longer canonical.
APP modes: launch, relaunch, load_from_directory, list, create.
PLUGIN modes: install, eject, sync, reinject, list, search, core_status, create.
Coding sub-agents spawned by the orchestrator (Claude Code, Codex, etc.) live in sealed PTY workspaces with no direct access to the parent Milady runtime. They have two channels back to the parent:
1. PTY stdout — USE_SKILL (write channel). Emit USE_SKILL <slug> <json> lines on stdout; the orchestrator parses them and dispatches the matching skill in the parent runtime. This is how a sub-agent invokes APP, PLUGIN, or any other parent skill. Enabled by default; disable with MILADY_ENABLE_CHILD_SKILL_CALLBACK=0.
2. Loopback HTTP bridge (read channel). Three endpoints on 127.0.0.1:31337 (or whatever MILADY_API_PORT resolves to). Loopback-only, agent-authed via the :sessionId path segment (must match an active task in the orchestrator's tasks map; unknown sessions 404, completed/stopped sessions 410):
GET /api/coding-agents/:sessionId/parent-context— parent character, world/room context, the human user's identity. Use this to resolve pronouns the task brief left unresolved.GET /api/coding-agents/:sessionId/memory?q=<query>&limit=<N>— query the parent agent's memory.GET /api/coding-agents/:sessionId/active-workspaces— list the parent's currently-active workspaces.
All bridge responses are read-only. Mutations stay with the orchestrator — sub-agents cannot write parent state through the bridge. The claude-subagent-milady-bridge skill (in skills/.defaults/) documents the calling pattern in detail.
Any sub-agent writing an app or plugin MUST run, in order, before signaling completion:
bun run typecheckbun run lintbun run test
Then emit exactly one structured completion line on stdout:
APP_CREATE_DONE {"appName":"...","files":[...],"tests":{"passed":N,"failed":0},"lint":"ok","typecheck":"ok"}
(or PLUGIN_CREATE_DONE {...}). The parent does not trust the line — it cross-checks every claim against disk and the verification log:
- Every file in
filesmust exist and be non-empty. tests.passedmust match the parsed vitest report.lintandtypecheckclaims are verified against recorded exit codes.- No remaining
__APP_NAME__/__PLUGIN_NAME__/__APP_DISPLAY_NAME__/__PLUGIN_DISPLAY_NAME__placeholders are allowed.
If any claim fails verification, the parent issues a structured failure prompt and the sub-agent fixes and re-emits. Retry cap: MILADY_APP_VERIFICATION_MAX_RETRIES (default 3). After the cap, the failure surfaces verbatim — no silent fallback.
eliza/templates/min-app/— minimal Eliza app (Vite + React entry,Pluginwith one action,package.jsonwithelizaos.appmetadata, vitest smoke test, hero image placeholder,SCAFFOLD.mdagent contract).eliza/templates/min-plugin/— minimal Eliza runtime plugin (one action, one provider,package.jsonwithelizaos.pluginmetadata, vitest smoke test,SCAFFOLD.mdagent contract).
Both use placeholders (__APP_NAME__, __APP_DISPLAY_NAME__, __PLUGIN_NAME__, __PLUGIN_DISPLAY_NAME__) replaced by the scaffold copy step. Read each template's SCAFFOLD.md before customizing.
These govern every change. If existing code conflicts, fix the code.
- Dependencies point inward only. Presentation → Application → Domain → Infrastructure. Never import from an outer layer.
- Computation lives in domain code, not presentation. Derived values are computed in services / actions / use cases and returned as named fields. Clients format DTO fields for display; they do not aggregate, compute, or branch on raw data.
- API routes are auth + dispatch. Route handlers validate input, resolve the caller, and dispatch to a service / action. No business logic, no transformations, no calculations in route bodies.
- No runtime type-tag branching where separate flows belong. Distinct content kinds (apps, plugins, providers, actions) get separate handlers, not
if (kind === ...)chains. - CQRS. Readers read and return domain objects. Writers return
voidor an ID. Mappers handle DB↔domain translation. - Single source of truth for validation. Route-layer schemas validate and transform input. Services / actions trust pre-validated input and perform presence/invariant checks only. No duplicate inline regex.
- DTO fields are required by default. Optional only when genuinely nullable. No
ascasts to skip missing fields. No?? 0to hide broken pipelines. If TypeScript says a field is missing, fix the pipeline. - Logger only, never console. Server logging uses the structured logger only (
Logger.info/warn/error/debug). Prefix[ClassName]. Include structured context on errors. - Every action and endpoint needs a real caller. Every POST/PUT/DELETE has a UI invocation path. Every GET has a consuming component/hook. Every action has a planner trigger or programmatic caller. Anything else is dead code.
What good changes look like: fewer codepaths, fewer special cases, fewer fallback branches, stronger types, cleaner layer boundaries, easier traceability from input → use case → DTO → UI, no dead abstractions, no defensive code that obscures failures.
Remove on sight: unused code, near-duplicate types, legacy / migration leftovers, AI slop and fake TODO implementations, comments describing churn, "temporary" fallbacks that became permanent, broad try/catch that just swallows or replaces errors with defaults, any / unknown / unsafe casts used to avoid thinking.
Constraints: do not preserve bad patterns "for compat" without a documented, verified live caller. Do not add abstractions unless they reduce total complexity. Do not DRY code that should remain separate because the domains differ. Do not centralize unlike concepts. Do not hide uncertainty with fallback values. Do not keep both old and new paths unless a live migration explicitly requires it.
Motto: move fast, but never lose work to dangling branches or stashes.
- Never
git stash. Commit instead — WIP commits are fine and can be amended or squashed later. - Always commit to the current branch in the current worktree. Do not
git checkout/git switchto another branch as part of "cleanup." If a task seems to need a different branch, ask first. - Prefer many small commits over uncommitted changes. Push proactively when work is meaningful.
- Never
--no-verify,--no-gpg-sign, force-push to main/master, or amend a published commit unless explicitly asked. After a hook failure, fix the issue and create a NEW commit (--amendmodifies the wrong commit). - Never blanket-discard uncommitted edits as "dev-env churn."
git checkout -- <file>on uncommitted edits is destructive — confirm per-file, especially for binaries. - Stage by name. Avoid
git add -A/git add ..
The principle: every change ends up as a commit on the current branch in the current worktree, and ideally pushed.