All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
- The installer sets up the GUI agent by default (no
--guiflag); pass--no-gui/-NoGuito skip it (host-only, e.g. research/wiki without the ~2 GB torch stack). - PyTorch is auto-selected: the installer detects an NVIDIA GPU (
nvidia-smi) and the driver's CUDA version, then installs the newest published torch CUDA wheel the driver supports (e.g. RTX 3080 + driver 13.1 →cu130); falls back to CPU when there's no GPU. Force with--cpu/-Cpuor a specific--cuda cuXXX/-Cuda cuXXX. - A bare
openprogramfirst run auto-launches the setup wizard when no provider is configured (no separateopenprogram setupstep), then asks which surface to open — terminal UI or web.openprogram tui/openprogram webskip the prompt and launch directly; non-interactive shells default to the terminal UI. Setup is skipped for--print/--resumeand when already configured. - Browser tool + chat channels now install by default (the
[all]extra +playwright install chromium) — no opt-in menu.--minimal/-Minimalskips them; the heavier stealth browsers / agent-browser stay opt-in (--stealth,--agent-browser). - Onboarding docs (README / GETTING_STARTED / README_CN / install.md, EN + 中文) lead with the one-command installer; dropped
pip install openprogramfrom the onboarding paths. README Quick Start trimmed to Install → Run → Add a harness (the GUI/Research/Wiki harnesses presented uniformly viaopenprogram programs install <name>); removed the inline GPU/surfaces flavor text and the "write your own functions" step.
scripts/install.sh(macOS/Linux) +scripts/install.ps1(Windows) — one command sets up the whole stack beyondpip: system-toolchain check (Python 3.11+/Node 20+/git), the Python package (editable), the Next.js web UI (npm install), the Ink TUI (POSIX), and — with--gui/-Gui— the GUI agent installed intoopenprogram/functions/agentics/and auto-registered (PyTorch, the GPA-GUI-Detector YOLO weight, EasyOCR models). Idempotent and re-runnable; optional--browser/--stealth/--agent-browser/--channelsflags run their post-install steps.- Authoritative install docs —
docs/install.mdreworked into the host-model guide (full dependency matrix, troubleshooting); README / GETTING_STARTED (EN + 中文) now lead with the installer instead ofpip(which only covers the Python core, not the web UI build or the GUI weight/OCR)..gitattributespins*.shto LF so the installer never breaks on macOS/Linux from CRLF.
- One way to manage accounts across CLI / web / TUI. Every provider now has the same account surface — list / add / activate / rename / remove multiple accounts (each account is a profile), backed by
/api/providers/{id}/accounts/*. claude-code stays Meridian-backed behind the same routes, so it's just one instance of the generic panel; one<ProviderAccounts>(web) and one Ink picker (TUI) drive every provider./login <provider>in the TUI now completes OAuth / device-code / import-from-CLI / API-key sign-in in the terminal instead of sending you to the web UI. - Per-provider active account (
auth/active.py; CLIopenprogram providers use <provider> [profile]) — run "openai on the work account, anthropic on personal" at the same time. The request path defaults to each provider's active profile; nothing changes until you activate a non-default one (fully backward compatible). - Automatic key rotation + cooldown (
auth/usage.py) — the provider call path now acquires a key from the pool per request and reports the outcome: a 429 cools that key down and the next request rotates to another (the rotation/cooldown/fallback machinery inauth/pool.pywas previously dead — zero callers). Gated: a no-op unless a provider actually has a multi-key pool, so env-key / OAuth / claude-code setups are byte-for-byte unchanged. - One backend model — account = profile — for every provider (forced by the OAuth refresh constraint: at most one OAuth credential per pool, so multi-account must be separate profiles). api-key keys, OAuth sign-ins and claude-code subscriptions are all accounts (profiles) now, behind one surface
/api/providers/{id}/accounts/*and one<AccountManager>panel: rename (hover ✎ on the right), Use to switch the active account, per-account validate + reveal/edit/update of an api-key (eye toggle), Remove, and a per-provider rotation toggle (off by default; on ⇒ a 429 fails over across accounts —auth/usage.acquire_pooled+auth/rotation.py). A key set the old way (env var / config) is migrated into an account automatically. Retires the api-key creds-in-one-pool surface; CLI / web / TUI all run the same model. - Design + status:
docs/design/providers/auth/unified-account-management.md(P-A … P-E).
docs/design/ui/surface-system.md— codified the dark-mode "two surfaces" rule (deep sidebars vs lifted panel) and the borderless Button variants that live on each. Thedefaultvariant is now the brand-coloured ghost pill (bg-background+text-primaryidle,bg-primary+text-primary-foregroundhover); pure-white hover text dropped in favour of near-black--primary-foreground: #1a1a19so the warm orange fill no longer reads as a neon pill.docs/design/ui/indicator-dots.md— unified four parallel CSS classes (.pulse,.pending-pulse,.status-dot,.attach-card-status-dot) under one.indicator-dotprimitive with size / colour / animation modifiers. Outer box always equals the●glyph advance width (~12.8 px) so header glyphs and body dots line up by layout instead of margin tweaks; visual disc painted by::beforeso the scale-breathing animation never jitters surrounding text. Migrated 8 call sites, dropped 4 legacy classes + 3 keyframes blocks.- Two-set sizing tokens —
--ui-list-h: 32px/--ui-list-radius: 6pxfor sidebar / list rows,--ui-button-h: 30px/--ui-button-radius: 8pxfor panel buttons. Each set is locked: no sm / md / lg ladder inside a set. Button height intentionally shorter than list so a panel pill doesn't visually outweigh sidebar rows. Sidebar (nav-classes,favorites-list,sessions-list,sidebar.tsx, popover menu rows),Inputprimitive, and several inline form selects all consume the tokens. - Font-smoothing locked to grayscale antialiasing in both themes — macOS / Safari previously swapped between subpixel and grayscale across dark / light, making bold text "lighter in dark mode" even though no font-weight rule changed.
- fn-form non-agentic tool calls now return a structured 400 instead of failing silently. Picking a non-agentic tool (
bash,edit, …) in the function form used to land in a daemon-threadraiseafter the HTTP response had already returned 200, leaving a phantom[function call] foo()user row that never produced output. The endpoint now validates the tool synchronously before creating a session or spawning the subprocess. The composer surfaces the failure via analert()so users see the reason without opening DevTools. - Mini-DAG pixel-aligned with
Function callglyph —.pending-pulseouter box widened to 12.8 px (●glyph advance width) so the "Running…" disc sits on the same column as the header dot..inline-tree-bodypadding-leftbumped 8→10 px to match.inline-tree-headerso the entire body column lines up with the header label. - CI green on Python 3.11 / 3.12 / 3.13 — fixed a PEP 701 nested-quote f-string in
cli.py; mocked codex CLI presence intest_codex_source_imports_from_file; lower-cased the path check intest_bootstrap_real_user_data_dir_is_platform_specific; skippedtest_dispatcher_dag_attachon bare CI runners (it requires a configured provider in$HOMEto exercise the DAG-attach path, which Linux runners don't have).
- 8 zero-importer half-typed orphan files (
tree-panel.tsx,shiki-code.tsx,ui/dropdown.tsx,branches-panel.tsx,memory-page.tsx,providers-section.tsx,search-providers-section.tsx,use-legacy-globals.ts) — each referenced modules that don't exist, was carried in by an earlier integration commit, and was caught here.tsc --noEmiterror count dropped from 23 to 0, and thenext.config.mjseslint.ignoreDuringBuilds/typescript.ignoreBuildErrorsescape-hatches were removed;npm run buildnow goes through full lint + typecheck on every CI run. web/components/programs/orphan directory +web/lib/programs-*.ts— the upstreamprograms → functionsrename finished inb516787a, but a parallel local copy of the pre-rename tree had survived; cleaned up here.
- Windows:
import fcntlno longer breaks the worker / agent registry / channel bindings / sleep runner / browser bootstrap — six modules did a top-levelimport fcntl, which is POSIX-only and crashed every Windows import withModuleNotFoundError. A newopenprogram._compatshim re-exportsfcntlon POSIX and emulates the sameflock/LOCK_*surface on Windows viamsvcrt.locking, translatingEACCEStoBlockingIOErrorso call sites keep the POSIX exception pattern.openprogram worker <verb>now runs cleanly on Windows. /api/providers/listno longer 500s when an LLM SDK isn't installed —openprogram.providers.anthropicandopenprogram.providers.openai_completionsdid a top-levelimport anthropic/import openai, even though both SDKs are declared as optional extras. Importing the package transitively (e.g. fromwebui/_model_catalog.pyfor the side-effect registry) crashed the catalog endpoint and showed an empty LLM Providers page. Imports are now wrapped in try/except; a runtime guard at the entry of eachstream_simple/_build_clientraises a clearImportErrornaming the pip extra to install if the provider is actually used._init_providersno longer picks a not-actually-usable provider as the global default — the boot-time priority scan only checked thatRuntime.__init__returned cleanly. For HTTP-backed providers likeclaude-codethat's "always yes" because the constructor just stamps a base_url and never touches the network, so a setup where Meridian wasn't running still got selected as the default and persisted into every new conversation'sprovider_name— then exploded at send time with a connection error. The probe now defers to the same_is_configuredpredicate the Settings page uses (API key present / CLI binary on PATH / local proxy answering on its port).- Codex: pre-2026
auth.jsonfiles (noauth_modediscriminator) now import cleanly —import_from_codex_filediscriminated strictly onauth_modeto refuse apikey-shape files, but the field is a recent addition; older ChatGPT-OAuth files were silently rejected. The shape is now inferred when the discriminator is missing: tokens populated + empty/nullOPENAI_API_KEYslot ⇒ treat aschatgptmode. The apikey case still falls through to the existing return-None branch. - Windows: non-ASCII chat content no longer crashes
_log—sys.stdout/sys.stderrdefault tocp1252(orgbkon a CN locale) on Windows, and the chat-execute path's plainprintraisedUnicodeEncodeErroron any CJK or em-dash, bubbling out as a 500.cli.main()now reconfigures both streams to UTF-8 witherrors='replace'so diagnostic logs are lossy-but-non-fatal. POSIX builds are unaffected. @agentic_functiondocstring restored to the rendered context — the tree-Context → DAG refactor dropped the function docstring from the prompt. It is now stored on the function's DAGCallnode (metadata.doc) and rendered into the context of the LLM calls made inside the function, so the model sees what the function does.@agentic_function(system=...)now reaches the model — the decorator's system prompt was stored on the function object but never applied. It is now stamped onto the injected runtime for the duration of the call (saved/restored so a caller's ownsystemsurvives)._retry_choicebuildin module restored —parse_args's retry path imported a module deleted in the DAG refactor, so any failed parse crashed withModuleNotFoundErrorinstead of retrying.- Agent runtime bugs —
wiki_agentpassed a bare string toruntime.execand imported a non-existentlegacy_providers;research_agent's_stage_stepcalledparse_argswith the pre-rewrite API (dict in, tuple out).
- Docstring /
contentsplit applied to wiki / research / gui agents — per-call instructions and output schemas moved out of docstrings intoruntime.exec(content=...), so they reach the model as the operative prompt rather than as background description. - PDF tooling — added
extract_pdf_figures/extract_pdf_tablesagentic functions (LLM-guided figure/table extraction from any PDF).
- Package renamed:
agentic-programming→openprogram(PyPI),agentic/→openprogram/(import path). - Repository renamed:
Agentic-Programming→OpenProgram. - CLI command renamed:
agentic→openprogram. - Internal reorganization:
openprogram/agentic_programming/— core engine (Context / Runtime / @agentic_function), the philosophy's home.openprogram/providers/— LLM provider runtimes (unchanged content).openprogram/functions/tools/—@functionleaf tools (wasopenprogram/tools/).openprogram/functions/agentics/—@agentic_functionmodules, each its own directory with code in__init__.py(replaces the oldprograms/functions/{buildin,third_party}/+programs/applications/split). Harness apps live here as symlinks.openprogram/functions/_registry.py— single unified registry (merges the deleted_app_registry/_agentic_registry).openprogram/webui/— web UI (was standaloneagentic_web/, now a sub-package).
@agentic_functiondecorator name preserved — the paradigm's hallmark symbol stays, OpenProgram is its product realization.- MCP sub-package removed — not part of the product scope.
- Philosophy doc added at
docs/philosophy/agentic-programming.md.
- CLI runner text transforms for provider plugins —
text_transforms.inputrewrites prompts/system prompts before launch, andtext_transforms.outputrewrites streamed assistant text deltas without altering tool payloads - Real-time web UI (
python -m openprogram.webui, also acceptsopenprogram.webui.visualizealias) — interactive Context tree viewer with WebSocket streaming - Built-in agentic functions:
deep_work,ask_user, PDF helpers (extract_pdf_figures,extract_pdf_tables),research,word_count, etc. deep_work— autonomous plan-execute-evaluate loop with quality levels (high_school → professor)- Session continuity for CLI providers (Claude Code, Codex, Gemini CLI)
- Interactive mode for Claude Code CLI with full tool access
- Nested JSON export for Context trees (
.jsonformat) inputparameter for@agentic_function— UI metadata for Visualizer structured input forms- Supports
description,placeholder,multiline,options,hiddenper parameter - Bool params auto-render as Yes/No toggle,
optionsas clickable chips - All meta functions and built-in functions annotated with
inputmetadata - Design principle: free text → selection → structured input (minimize cognitive load)
- Full spec documented in
docs/api/agentic-function.md
- Supports
- Structured function form in Visualizer — replaces text command input for function execution
- Shows function name, description, typed parameter fields with hints
- Integrated into the chat input area (replaces textarea when active)
- Keyboard support: Esc to cancel, Enter/Ctrl+Enter to submit
- Thinking effort selector in Visualizer — per-provider thinking/reasoning level control
- Markdown + LaTeX rendering in Visualizer chat output
- Runtime Block UI — card-style display for function executions with inline context trees
- Retry with branching — attempt navigation (Modify) and error retry (Retry) in Visualizer
- README redesigned: Quick Start with 3 usage paths (Python/Skills/MCP), annotated code hero image, Deep Work feature showcase
- Split meta-function skill into four focused skills
create_skillupdated with "one skill, one entry function" pattern- Context compaction via
/compactinstead of process restart docs/API.mdnow reflects the current public exports forfix,improve, andcreate_runtime- Visualizer welcome page redesigned: examples above input, centered welcome screen
- Provider/model badges lock after conversation starts (session immutability)
- Chat history and execution trees persist across page refreshes
- Context JSON roundtrips now preserve
source_file, so visualizer restores can still locate original function source after reload - Stderr pipe buffer deadlock in CLI providers
- Per-call readline thread replaced with persistent queue-based stdout reader
- Context branch indentation
pytest tests/now works from a fresh checkout without manually exportingPYTHONPATH_loopnot captured when server started viauvicorn.run()(broadcast silently failed)- Detail panel resize/collapse conflict
- Codex default model showing as null
- Built-in providers:
AnthropicRuntime,OpenAIRuntime,GeminiRuntimeinopenprogram/providers/- Each provider is an optional dependency (SDK not required by core)
- Anthropic: text + image, prompt caching (
cache_control) - OpenAI: text + image (base64/URL),
response_format(JSON mode / structured output) - Gemini: text + image, system instructions
fix()meta function: Analyze errors and rewrite broken generated functions- Retry mechanism:
Runtime(max_retries=N)for automatic retry on transient API errors- TypeError/NotImplementedError are never retried (programming errors)
- All other exceptions retried up to
max_retriestimes - Exhausted retries raise
RuntimeErrorwith full error report
- New examples:
examples/code_review.py— code review pipeline (read → analyze → report)examples/data_analysis.py— data analysis with render levels and compressexamples/meta_chain.py— dynamic function chain usingcreate()
- Documentation:
docs/api/providers.md— provider configuration guidedocs/api/meta_function.md— addedfix()documentationdocs/api/runtime.md— added retry mechanism documentation- README — added Built-in Providers section
- Tests: render level tests (summary/detail/result/silent) and summarize parameter combinations (34 new tests, 53 → 87 total)
meta.pyrenamed tometa_function.pyfor clarity
create()meta function: Generate@agentic_functionfrom natural language descriptions- Safety sandbox: Generated code runs with restricted builtins (no imports, no file I/O)
meta_demo.py: Example showingcreate()usage
@agentic_functiondecorator: Auto-records execution into Context tree- Parameters:
render,summarize,compress - Supports sync and async functions
- Parameters:
Runtimeclass: LLM call interface with Context integrationexec()andasync_exec()with automatic context injection- Content blocks: text, image, audio, file
- One exec() per function guard
Contextdataclass: Execution record treesummarize()with depth, siblings, level, include, exclude, branch, max_tokenstree()for human-readable viewtraceback()for error chainssave()to .md or .jsonl
- Render levels: trace, detail, summary (default), result, silent
- Auto-save: Completed trees auto-saved to
agentic/logs/ - Examples:
main.py(Gemini),claude_demo.py(Claude Code CLI)