Skip to content

feat(agents): "save as persona template" on agent card + sidebar#1301

Draft
tellaho wants to merge 1 commit into
kennylopez-agents-page-cardsfrom
tho/persona-template-ux
Draft

feat(agents): "save as persona template" on agent card + sidebar#1301
tellaho wants to merge 1 commit into
kennylopez-agents-page-cardsfrom
tho/persona-template-ux

Conversation

@tellaho

@tellaho tellaho commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

What & why

Adds an opt-in "Save as persona template" action so a configured managed agent can be saved as a reusable persona template — wired consistently across the surfaces where a user manages an agent. Part of the #1201 line of UX work.

Based on kennylopez-agents-page-cards (#1199), not main — the action lives on exactly the surface #1199 rewrites (agent action menus → card ). Basing here lands it on the UI that actually ships and avoids a double-merge into dead UI. Targets that branch as a draft so it merges into Kenny's work.

Surfaces

  • Card menu (AgentActionItems) — item gated !agent.personaId, so it only appears on agents that aren't already persona-backed.
  • Sidebar agent profile (UserProfilePanelProfileQuickActions overflow ) — gated canEditAgent (owner + managed agent). Tucked in an overflow to keep the quick-action row lean.
  • Persona actions menu — intentionally untouched. The thing is already a persona, so "save as persona" there would be nonsense — absence is correct.

How

  • Mirrors the existing duplicatePersonaDialogState prefilled-dialog pattern: saveAsPersonaTemplateDialogState(agent) prefills the persona editor (name, system prompt, model, provider, env vars; reverse-maps agentCommand → runtime), leaves namePool empty for the user to fill, and opens the existing PersonaDialog so the user reviews before anything is created.
  • No new backend / IPC — produces a CreatePersonaInput through the existing create mutation.
  • New focused hook useSaveAsPersonaTemplate for surfaces outside the Agents page (the sidebar), with toast-based error handling.
  • Naming-boundary comment in personaLibraryCopy.ts: UI says "persona template", backend stays persona (kind:30175); symbols deliberately not renamed.
  • Provider carried across (lossless, on ManagedAgent) — a databricks agent saves as a databricks template. (Reversed an earlier "leave provider unset" call once we confirmed provider is on the row type.)

Guardrails held

  • Did not touch buildUnifiedGroups or the card-grid structure — pure wiring.
  • The UnifiedAgentsSection.tsx change tipped the 1000-line file-size gate; resolved by extracting the shared AgentActionItems into its own file (the sanctioned "split, don't override" path) — a pure lift, no logic change. File now 869 lines.

Part 1 (regression guard)

Verified CreateAgentDialog.handleSubmit never sets personaId (persona-less by construction) and added 6 regression tests, incl. an explicit "default create stays persona-less" guard.

Checks

pnpm typecheck ✅ · pnpm check ✅ (biome + file-sizes + px-text) · pnpm test1138/1138 · pre-push hooks green.


Draft for review. Targets kennylopez-agents-page-cards.

Opt-in promote of an existing managed agent into a reusable persona
template. Lands on two surfaces — the agent card overflow menu (standalone
agents only) and the sidebar agent profile's overflow menu — sharing one
`saveAsPersonaTemplateDialogState` + the existing PersonaDialog and
create-persona mutation. No new backend or IPC.

The promote is near-lossless: name, system prompt, model, provider, and env
vars copy across; the resolved harness command reverse-maps to a runtime id;
namePool starts empty for the user to fill in the dialog.

UI says "persona template" while the backend stays `persona` (kind:30175,
builtin:*, .persona.md) — a boundary comment in personaLibraryCopy.ts records
the mapping so the names are not conflated.

Pure refactors to stay under the 1000-line file limit: extract AgentActionItems
(+ AgentMenuProps) and the profile quick-action buttons into sibling files.

Adds regression tests for the dialog-state mapping and a guard that default
agent create stays persona-less.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho

tellaho commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator Author

Reviewer note — runtime reverse-map on first open.

The sidebar "Save as persona template" lazily loads ACP runtimes only when the dialog opens (useSaveAsPersonaTemplate flips the query's enabled flag in the same tick as open()). On the very first open, runtime discovery may not have resolved yet, so the command→runtime reverse-map can't find a match and the dialog falls back to its default-runtime behavior.

This is intentional and degrades gracefully — the user just picks the runtime themselves, same as a fresh persona create. A naive "re-seed once runtimes load" fix was prototyped and rejected: PersonaDialog seeds its local fields on [initialValues, open], so re-deriving initialValues after load would clobber any in-progress edits. The snapshot-on-open behavior is the correct tradeoff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant