You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
SDK v1.29.0 (2026-06-18) — git show v1.29.0:… confirms the full substrate present: profiles/agent_profile.py (model), agent_profile_store.py, profile_refs.py (FK), resolver.py (resolve_agent_profile + resolve_agent_profile_dry_run + AgentProfileDiagnostics), agent_profiles_router.py (active_agent_profile_id + CRUD/activate/seed/materialize), LaunchedAgentProfile, and conv-start agent_profile_id handling.
ts-client v1.26.0 (2026-06-18, latest on npm) — confirms AgentProfile types + AgentProfilesClient + pure deriveSwitchPlan. (Note: npm version is set from the git tag at publish time, so the committed package.json reading an older value is by design, not a bug.)
⚠️ 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.0→1.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.tsxdisabledByAcp, 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 nollm_profile_ref. The AgentProfile itself stores no secrets.
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_registry ← request.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.
materialize endpoint(PR [AgentProfile][agent-server] materialize endpoint (resolve dry-run) #3783, merged) — POST /api/agent-profiles/{name}/materialize → AgentProfileDiagnostics (dry-run; dangling refs reported in body, not 4xx). (Path param is {name}, not {id} — only /activate is {profile_id}-keyed.)
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.
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.
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.git show v1.29.0:…confirms the full substrate present:profiles/agent_profile.py(model),agent_profile_store.py,profile_refs.py(FK),resolver.py(resolve_agent_profile+resolve_agent_profile_dry_run+AgentProfileDiagnostics),agent_profiles_router.py(active_agent_profile_id+ CRUD/activate/seed/materialize),LaunchedAgentProfile, and conv-startagent_profile_idhandling.lateston npm) — confirms AgentProfile types +AgentProfilesClient+ purederiveSwitchPlan. (Note: npm version is set from the git tag at publish time, so the committedpackage.jsonreading an older value is by design, not a bug.)LaunchedProfile→LaunchedAgentProfile, dropped thesnapshotmember) · ts-client Proposal: Spec-only Tools + Explicit RunLoop (Local/Remote) with Remote Execution #213→[AgentProfile][ts-client] AgentProfile types + client + resolve/materialize types + deriveSwitchPlan #3722 · canvas Add page_iterator utility and refactor resend_all functionality #1386→[AgentProfile][canvas] AgentProfile library + kind-aware editor (dissolve ACP nav lockout) #3726 (prereq). ts-client do not try to force ImageContent be subclass of mcp image content #212 (meta-profiles) and SDK Add REST API contract summaries to PR descriptions #3789 (API-contract tooling) surfaced on the "profile" search but belong to other features.LLMProfileStore()/AgentProfileStore()default to~/.openhandsand ignoreOH_PERSISTENCE_DIR, so an isolated agent-server leaks the host's profile state and seedssettings.jsonfrom 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
AgentProfilesClientwiring + thelaunched_agent_profileprovenance consumption the other two need; bump canvas's ts-client pin1.25.0→1.26.0inside 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 ofroutes/agent-settings.tsx(already has both OpenHands + ACP branches — verified), the ACP-nav lockout removal is ~5 files of deletions (settings-nav.tsxdisabledByAcp,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.0on OpenHandsmain) 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'sagent_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:
LLMProfile(exists) ·AgentProfile(new, kind-discriminatedopenhands | acp). OpenHands variant references onellm_profile_ref+ 0+ server names viamcp_server_refs; ACP variant hasacp_*config fields + 0+ server names viamcp_server_refsand nollm_profile_ref. TheAgentProfileitself stores no secrets.mcp_server_refs: list[str] | None— filter, not a separate store. Names servers in the user's existing globalmcp_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-givennamefield 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.llm_profile_refonly. Delete of a referenced LLM profile →409naming referrers; rename → cascade-update refs under lock; dangling ref → hard error. MCP server refs are names in the user's mutable globalmcp_config— checked at resolve-time only.LLMProfileembeds its own secrets encrypted in-file. ACP provider creds are not stored on a profile — derived fromacp_serverviaACP_PROVIDERSregistry and ride the single consolidated channelstate.secret_registry←request.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) → AgentSettingsConfigis the only join point. The existingcreate_agent/apply_agent_settings_diff/validate_agent_settingsexecution path is unchanged.switch_llm/switch_profile; ACP viasession/set_modelwhensupports_runtime_model_switch.active_agent_profile_idis a NEW field — do not reuse/generalizeactive_profile(= active LLM profile).GET /api/agent-profileson empty store) — not one per existing LLM profile.Phase 0 — Foundation (DONE ✅)
_merge_custom_mcp_configACP early-return fix is genuinely still deferred to Phase 5 / [AgentProfile][cloud] OpenHands cloud/SaaS surface (umbrella — needs its own design pass) #3730 — confirmed still present on OpenHandsmainatlive_status_app_conversation_service.py:1247(early returns 1257/1261).)apply_agent_settings_diffeverywhere (SDKPersistedSettings.updatedone — PR feat(settings): delegate PersistedSettings.update to apply_agent_settings_diff #3712, in v1.29.0. CORRECTION (2026-06-21): the OpenHands app-server portion is DONE, not deferred — landed via OpenHands #14677 (settings_models.py:218+enterprise/storage/org_store.py:352delegate toapply_agent_settings_diff). #14677 referenced the predecessor feat(settings): add apply_agent_settings_diff and canonicalize the llm tag on read #3534 rather than Foundation: adopt apply_agent_settings_diff in all settings stores #3706, so the trail wasn't linked. Only the canvas portion remains, and no code change there is currently identified as required — canvas forwards the agent-settings diff verbatim and relies on the backend merge.)Sub-issues
Built bottom-up.
←= depends on.Phase 1 — SDK substrate (
software-agent-sdk) — all merged + in v1.29.0, audit verdictmatch[sdk]AgentProfile union (OpenHands | ACP) — reference-bearing, secret-free (PR [AgentProfile][sdk] AgentProfile kind-discriminated union #3757; secret-free-at-rest enforced via the Skillmcp_toolsmask root-fix fix(skills): mask Skill.mcp_tools credentials at rest #3774, in v1.29.0)[sdk]AgentProfileStore + FK lifecycle (restrict-on-delete / cascade-rename forllm_profile_ref) (PR [AgentProfile][sdk] AgentProfileStore + FK lifecycle #3775) ← [AgentProfile][sdk] AgentProfile kind-discriminated union (OpenHands | ACP) — reference-bearing, secret-free #3715[sdk]resolve_agent_profile()— filter-based MCP composition + resource-specific secret channels + dry-run (PR [AgentProfile][sdk] resolve_agent_profile() #3780; shipsresolve_agent_profile+resolve_agent_profile_dry_run+AgentProfileDiagnosticsinprofiles/resolver.py) ← [AgentProfile][sdk] AgentProfileStore + FK lifecycle (restrict-on-delete, cascade-rename, referrer scan) #3716Phase 2 — SDK agent-server (
software-agent-sdk/openhands-agent-server) — all merged + in v1.29.0[agent-server]active_agent_profile_id+/api/agent-profilesrouter + migration seed (PR [AgentProfile][agent-server] active_agent_profile_id + /api/agent-profiles router + migration seed #3781, merged;POST /{name}/materializedeferred to [AgentProfile][agent-server] materialize endpoint (resolve dry-run) #3783. NB the contract diverged benignly from the issue text — materialize returns dangling refs as diagnostics at HTTP 200, not 422, and readssettings.agent_settings.mcp_config+ cipher rather thandecrypt_mcp_config_secrets.) ← [AgentProfile][sdk] resolve_agent_profile(): collision-checked composition + resource-specific secret channels #3717, [AgentProfile][sdk] AgentProfileStore + FK lifecycle (restrict-on-delete, cascade-rename, referrer scan) #3716[agent-server]Acceptagent_profile_idat conversation start + stampLaunchedAgentProfileprovenance (PR [AgentProfile][agent-server] agent_profile_id at conversation start + LaunchedProfile provenance #3784 + naming follow-up feat: API design: launched agent profile provenance names #3788. Shipped model isLaunchedAgentProfile { agent_profile_id, revision }— renamed fromLaunchedProfileand thesnapshotmember was dropped; snapshot-at-start is provided by persisting the fully-builtrequest.agent.) ← [AgentProfile][agent-server] active_agent_profile_id + /api/agent-profiles router + migration seed #3719, [AgentProfile][sdk] resolve_agent_profile(): collision-checked composition + resource-specific secret channels #3717materializeendpoint (PR [AgentProfile][agent-server] materialize endpoint (resolve dry-run) #3783, merged) —POST /api/agent-profiles/{name}/materialize→AgentProfileDiagnostics(dry-run; dangling refs reported in body, not 4xx). (Path param is{name}, not{id}— only/activateis{profile_id}-keyed.)[agent-server]Cascademcp_server_refson MCP-server rename — closed, no code (decision verified correct): no server-side rename signal exists (settings PATCH is a blob diff; an MCP rename is{new:{…}, old:null}, indistinguishable from delete+create), resolve-timeDanglingMcpServerRefis the backstop, and cascade-on-rename (if wanted) belongs canvas-side ([AgentProfile][canvas] AgentProfile library + kind-aware editor (dissolve ACP nav lockout) #3726)[agent-server]profile-storeOH_PERSISTENCE_DIRisolation (Phase-2 follow-up, open + unreleased). Latent leak shipped in v1.29.0; fix open in PR fix(agent_server): honor OH_PERSISTENCE_DIR in profile stores (#3815) #3816. PR isMERGEABLE/BLOCKEDbut has real CI failures (pre-commit: ruff-format reformat + 2 pyright errors in the newtests/agent_server/test_profile_store_persistence_dir.py) plus theValidate PR descriptiongate — it needs a code push, not just a human merge. Land it, then cut v1.29.1 so canvas's dev/CI/QA rigs get a known-empty agent-server. Eases (does not block) [AgentProfile][canvas] AgentProfile library + kind-aware editor (dissolve ACP nav lockout) #3726.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.0deprecation shims as required by the deprecation-deadline gate.Phase 3 — typescript-client (
typescript-client) — merged + in v1.26.0 (npmlatest)[ts-client]AgentProfile types + client + resolve/materialize types + purederiveSwitchPlan(PR Proposal: Spec-only Tools + Explicit RunLoop (Local/Remote) with Remote Execution #213. ACP credential descriptor lives insrc/models/acp.ts— relocated fromutils/in earlier work feat: visualize notes in italic for task tracker #210; theagent_profile_id/launched_profileConversationInfo types are already present, only their server-side runtime activation was gated on SDK [AgentProfile][agent-server] agent_profile_id at conversation start + LaunchedProfile provenance #3784.) ← [AgentProfile][agent-server] active_agent_profile_id + /api/agent-profiles router + migration seed #3719, [AgentProfile][agent-server] Accept agent_profile_id at conversation start + stamp LaunchedProfile provenance #3720Phase 4 — agent-canvas UX (
agent-canvas) — all OPEN, greenfield, no implementing PRs (verified)[canvas]AgentProfile library + kind-aware editor (dissolves the ACP nav lockout) + surface MCP servernameinput + consumelaunched_agent_profileprovenance ← [AgentProfile][ts-client] AgentProfile types + client + resolve/materialize types + deriveSwitchPlan #3722 ✅, [AgentProfile][agent-server] active_agent_profile_id + /api/agent-profiles router + migration seed #3719 ✅ · ACTIVE NEXT — fully unblocked; bundles the ts-client1.25.0→1.26.0pin bump (SSE/shttp serialization already shipped in canvas Add page_iterator utility and refactor resend_all functionality #1386, merged). All reuse claims verified againstorigin/main.[canvas]Unified capability-gated chat-input model/profile picker ← [AgentProfile][canvas] AgentProfile library + kind-aware editor (dissolve ACP nav lockout) #3726, [AgentProfile][ts-client] AgentProfile types + client + resolve/materialize types + deriveSwitchPlan #3722 ✅ · hard-gated on [AgentProfile][canvas] AgentProfile library + kind-aware editor (dissolve ACP nav lockout) #3726 (deriveSwitchPlanneeds thelaunched_agent_profileprovenance — which the backend already exposes onConversationInfobut canvas does not consume yet; [AgentProfile][canvas] AgentProfile library + kind-aware editor (dissolve ACP nav lockout) #3726 must surface it). The two divergent branches today (SwitchProfileButton+ChatInputModelinchat-input-actions.tsx) are confirmed.[canvas]Provider-driven ACP Authentication section ← [AgentProfile][canvas] AgentProfile library + kind-aware editor (dissolve ACP nav lockout) #3726, [AgentProfile][ts-client] AgentProfile types + client + resolve/materialize types + deriveSwitchPlan #3722 ✅ · scope-corrected body (2026-06-19) verified accurate: creds are already out of the generic panel (AcpCredentialsSectionlives inagent-settings.tsx) — real work = relocate into [AgentProfile][canvas] AgentProfile library + kind-aware editor (dissolve ACP nav lockout) #3726's editor ACP branch + filter the env-keyed cred names out of the secrets list (secrets-settings.tsx:188is currently unfiltered) + repoint onboarding (setup-acp-secrets-step.tsx). Reuse, not new UI.Phase 5 — OpenHands cloud / SaaS (
OpenHands) — later milestone, own design pass needed[cloud]OpenHands cloud/SaaS surface (umbrella) — SDK substrate is pinned (1.29.0) but zero AgentProfile wiring exists and the cloud SaaS app uses its own router stack (no auto-mount). Includes: OpenHands_merge_custom_mcp_configACP fix (Foundation: codify ACP MCP forwarding across SDK and integrations #3705 remainder) · AgentProfiles container+column+migration+router+FK ·resolve_agent_profilewiring ·LaunchedAgentProfileprovenance · the standalone acp_env member-private pre-req (buildable now). (Foundation: adopt apply_agent_settings_diff in all settings stores #3706's app-serverapply_agent_settings_diffadoption is already DONE — #14677 — remove it from this umbrella's scope.) ← local-first milestoneCritical 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):
routes/agent-settings.tsx), bundle the ts-client1.25.0→1.26.0pin bump, dissolve the ACP nav lockout (~5 files), and consume thelaunched_agent_profileprovenance (already onConversationInfoin agent-server v1.29.0 / ts-client v1.26.0) so [AgentProfile][canvas] Unified capability-gated chat-input model/profile picker #3727'sderiveSwitchPlanhas its input. Unblocks [AgentProfile][canvas] Unified capability-gated chat-input model/profile picker #3727 ∥ [AgentProfile][canvas] Provider-driven ACP Authentication section (creds out of the generic Secrets panel) #3728. Mostly reuse/extraction.MetaProfileStorereuses the same home-dir-default pattern and would reintroduce the agent-server: OH_PERSISTENCE_DIR doesn't fully isolate state — LLM/agent profile stores ignore it #3815 leak). Doesn't touch canvas — fully parallelizable.#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 newMetaProfileStore//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)MetaProfileStorereads~/.openhands/meta-profiles/directly and will reintroduce the #3815 isolation leak unless #3744 adopts #3816'sget_*_store()accessor — land #3816 first; (3) meta-profile → LLM-profile references appear to lack the delete-409 / cascade-rename FK guard the epic enforces forllm_profile_ref.Repos
All sub-issues are tracked here in
software-agent-sdkfor 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 onmain2026-06-17.