Skip to content

[AgentProfile] Epic: first-class Agent Profiles (named launch setups) across SDK, agent-server, canvas, cloud #3713

@simonrosenberg

Description

@simonrosenberg

Goal

Ship Agent Profiles: a library of named, reusable agent setups so a user can save everything about how an agent runs — which agent (built-in OpenHands/CodeAct vs. an ACP CLI: Claude Code / Codex / Gemini), the model + credentials, MCP servers, skills, and personality — then pick one to start a conversation. Easy UX is the product goal: Settings → Agents becomes a profile library; you launch with a profile and, where it's safe, switch the model live.

This epic tracks the implementation. It is local-first, cloud-ready: build on the SDK agent-server + agent-canvas first, with the cloud/SaaS surface following the same contract.

Status (2026-06-21 — audit-reconciled)

Phases 1–3 ✅ COMPLETE and RELEASED. Release containment independently re-verified against the published tags (not just main), and every merged PR re-mapped to its sub-issue against the actual code.

⚠️ Phase-2 substrate regression (NEW follow-up, open + unreleased): #3815 / PR #3816. LLMProfileStore() / AgentProfileStore() default to ~/.openhands and ignore OH_PERSISTENCE_DIR, so an isolated agent-server leaks the host's profile state and seeds settings.json from a stray host LLM profile. The latent leak SHIPPED in v1.29.0 (the leaking default-constructed call sites are in the released agent-server); only the fix (PR #3816: get_*_store() accessors resolving via _get_persistence_dir) is post-v1.29.0 and unreleased. It impairs the canvas #3726 dev/CI/QA loop (no guaranteed known-empty agent-server — exactly the friction canvas #1389 hit) but does not hard-block #3726 from compiling/merging. See the Phase-2 list.

Foundation correction: #3706's OpenHands app-server portion is DONE, not deferred — see Phase 0.

Active work = Phase 4 (canvas), greenfield, no PRs yet. Build order is fixed by the code, not preference: #3726 first (it introduces the AgentProfilesClient wiring + the launched_agent_profile provenance consumption the other two need; bump canvas's ts-client pin 1.25.01.26.0 inside it), then #3727 + #3728 in parallel (disjoint subsystems — chat-input vs settings/secrets). Most of canvas is reuse/extraction, not new code: the kind-aware editor is an extraction of routes/agent-settings.tsx (already has both OpenHands + ACP branches — verified), the ACP-nav lockout removal is ~5 files of deletions (settings-nav.tsx disabledByAcp, acp-route-guard.ts, use-settings-nav-items.ts — all still present), and the ACP credential components already exist (AcpCredentialsSection). Phase 5 (cloud #3730) is a separate repo + needs its own design pass first; the SDK substrate it consumes is already pinned (openhands-sdk==1.29.0, openhands-agent-server==1.29.0 on OpenHands main) but no AgentProfile wiring exists there yet and the cloud SaaS app runs its own router stack (it does NOT auto-mount the agent-server's agent_profiles_router).

Canonical design

#3710 — "Agent Profiles: reference composition with profile-local secrets" is the design of record (it supersedes #3708 value-composition and the closed #3709), with author amendments folded in. Non-negotiable invariants every sub-issue must honor:

  • Two name-addressed profile types. LLMProfile (exists) · AgentProfile (new, kind-discriminated openhands | acp). OpenHands variant references one llm_profile_ref + 0+ server names via mcp_server_refs; ACP variant has acp_* config fields + 0+ server names via mcp_server_refs and no llm_profile_ref. The AgentProfile itself stores no secrets.
  • mcp_server_refs: list[str] | None — filter, not a separate store. Names servers in the user's existing global mcp_config.mcpServers (already a named dict). null = use all configured servers; non-null = filter to exactly those named keys. Checked at resolve-time; dangling name → hard error. SSE/shttp servers get a user-given name field in the canvas editor ([AgentProfile][canvas] AgentProfile library + kind-aware editor (dissolve ACP nav lockout) #3726; shipped early in canvas Add page_iterator utility and refactor resend_all functionality #1386) so they become referenceable.
  • Single FK lifecycle: llm_profile_ref only. Delete of a referenced LLM profile → 409 naming referrers; rename → cascade-update refs under lock; dangling ref → hard error. MCP server refs are names in the user's mutable global mcp_config — checked at resolve-time only.
  • Secrets stay out of the generic global panel. LLMProfile embeds its own secrets encrypted in-file. ACP provider creds are not stored on a profile — derived from acp_server via ACP_PROVIDERS registry and ride the single consolidated channel state.secret_registryrequest.secrets (Fix error in 422 logs due to blank bearer token #1022/Fix Laminar span stack warning in LocalConversation #1039). ACP profiles are secret-free / portable at rest.
  • resolve_agent_profile(profile, *, llm_store, mcp_config, cipher) → AgentSettingsConfig is the only join point. The existing create_agent / apply_agent_settings_diff / validate_agent_settings execution path is unchanged.
  • Creation-time only. No whole-profile mid-conversation switch. Runtime mutation stays narrow: OpenHands via switch_llm/switch_profile; ACP via session/set_model when supports_runtime_model_switch.
  • active_agent_profile_id is a NEW field — do not reuse/generalize active_profile (= active LLM profile).
  • Conservative migration. Seed exactly one default AgentProfile lazily (first GET /api/agent-profiles on empty store) — not one per existing LLM profile.

Phase 0 — Foundation (DONE ✅)

Sub-issues

Built bottom-up. = depends on.

Phase 1 — SDK substrate (software-agent-sdk) — all merged + in v1.29.0, audit verdict match

Phase 2 — SDK agent-server (software-agent-sdk / openhands-agent-server) — all merged + in v1.29.0

Release gate (DONE): SDK v1.29.0 + ts-client v1.26.0 are cut, published, and contain the full substrate (containment re-verified against the tags). The rel-1.29.0 PR also removed the 7 expired removed_in=1.29.0 deprecation shims as required by the deprecation-deadline gate.

Phase 3 — typescript-client (typescript-client) — merged + in v1.26.0 (npm latest)

Phase 4 — agent-canvas UX (agent-canvas) — all OPEN, greenfield, no implementing PRs (verified)

Phase 5 — OpenHands cloud / SaaS (OpenHands) — later milestone, own design pass needed

Critical path: #3715#3720#3722 (ts-client)SDK v1.29.0 + ts-client v1.26.0 released#3726 (canvas)#3727#3728#3730 (cloud, separate repo + design pass)

What's next (2026-06-21)

The single critical-path action is implement #3726 (canvas) — the only item gating the rest of Phase 4. Three tracks can proceed in parallel (different repos/owners, no cross-blocking):

#3727 + #3728 stay HARD-gated on #3726 — they cannot start until it lands.

Out of epic (adjacent — do not fold in)

#3744 (PR) "Intelligent model router: classify_and_switch_llm + meta-profiles" and its canvas companion #1395 ("Model Router (meta-profiles) settings tab", types in ts-client #212) are a separate feature: runtime per-task LLM routing that consumes LLM profiles by name via a new MetaProfileStore / /api/meta-profiles. They are not epic sub-issues. Coordination risks to watch (not dependencies): (1) the meta-profile "Model Router" Settings tab (#1395) and the AgentProfile library (#3726) become two adjacent "profile-of-profiles" Settings surfaces — share the IA; (2) MetaProfileStore reads ~/.openhands/meta-profiles/ directly and will reintroduce the #3815 isolation leak unless #3744 adopts #3816's get_*_store() accessor — land #3816 first; (3) meta-profile → LLM-profile references appear to lack the delete-409 / cascade-rename FK guard the epic enforces for llm_profile_ref.

Repos

All sub-issues are tracked here in software-agent-sdk for unified epic tracking; each title is tagged with its target repo ([sdk], [agent-server], [ts-client], [canvas], [cloud] → OpenHands). Phase 1 substrate (#3715, #3716) landed on main 2026-06-17.

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions