fix: reconcile agent profile on startup when relay publish was missed#921
Merged
Conversation
added 4 commits
June 9, 2026 14:23
When a managed agent starts, the initial profile sync at creation time may have failed silently, leaving the agent with no kind:0 event on the relay (no name/avatar in channel views). Add a fire-and-forget background task to start_managed_agent that: - Queries the relay for the agent's existing kind:0 profile - Re-publishes if missing or stale (display_name/picture mismatch) - Derives avatar from persona config or command-based fallback - Never blocks agent startup — logs warning on failure Also adds query_agent_profile() helper to relay.rs for querying kind:0 events by pubkey. Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
… mismatch Two issues from code review: 1. Avatar resolution divergence: reconciliation re-derived the avatar URL from persona config, which could differ from what was originally published (the create path uses input.avatar_url, not persona.avatar_url). Fix: persist the resolved avatar_url on ManagedAgentRecord at creation time and compare against the stored value during reconciliation. 2. Relay endpoint mismatch: query_agent_profile used the workspace relay override while sync_managed_agent_profile used the agent's stored relay_url. Under an active override, the query hit relay A while the publish hit relay B. Fix: extract query_relay_at() helper that accepts an explicit base URL, and have query_agent_profile target the agent's stored relay_url — same endpoint the publish uses. Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Records created before avatar_url was persisted deserialize with None. Comparing None against a published picture would re-publish an empty avatar, regressing it on every start. Resolve the expected avatar from the stored value first, falling back to the legacy persona/command derivation only when absent, so old records self-heal without losing their avatar. Bumps the agents.rs line-size override to cover the fallback helper; the file is already tracked for splitting. Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
resolve_avatar_for_reconcile preferred persona.avatar_url for pre-existing records (avatar_url: None), but the original create path never consulted persona — it used input.avatar_url with managed_agent_avatar_url fallback. This caused reconciliation to compute a different expected avatar than what was published, silently overwriting user intent on every restart. Replace with reconcile_avatar that derives solely from managed_agent_avatar_url(agent_command) for legacy records, matching what was actually published. Stored avatar_url (new records) still wins. Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
6223421 to
f1af79e
Compare
Rename path now uses stored avatar_url with command fallback, matching the create and reconcile paths. Malformed kind:0 content is treated as missing (triggers re-publish) instead of bailing reconciliation.
wpfleger96
pushed a commit
that referenced
this pull request
Jun 9, 2026
Legacy ManagedAgentRecords (created before PR #921) have avatar_url: None because the field was added with #[serde(default)]. Previously, reconciliation fell back to the generic runtime icon, overwriting custom persona avatars. Now, on first start after update, reconcile_agent_profile queries the relay for the agent's existing kind:0 profile and backfills avatar_url from the picture field. The backfilled value is persisted so migration runs only once. If no profile exists on the relay, falls back to the command-derived icon. After backfill, normal reconciliation proceeds uniformly for all records — no special-casing needed for legacy vs post-PR-921 agents. Removes the reconcile_avatar helper (migration handles legacy directly). Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
wpfleger96
pushed a commit
that referenced
this pull request
Jun 9, 2026
The startup avatar backfill read the relay's kind:0 picture as the source of truth, but PR #921's old reconciliation had already overwritten that picture with the command default — so the backfill locked in the wrong avatar. Persona-backed agents now resolve their backfill avatar from PersonaRecord.avatar_url first, falling back to the relay picture and command icon only when no persona avatar exists.
wpfleger96
pushed a commit
that referenced
this pull request
Jun 9, 2026
Legacy ManagedAgentRecords (created before PR #921) have avatar_url: None because the field was added with #[serde(default)]. Previously, reconciliation fell back to the generic runtime icon, overwriting custom persona avatars. Now, on first start after update, reconcile_agent_profile queries the relay for the agent's existing kind:0 profile and backfills avatar_url from the picture field. The backfilled value is persisted so migration runs only once. If no profile exists on the relay, falls back to the command-derived icon. After backfill, normal reconciliation proceeds uniformly for all records — no special-casing needed for legacy vs post-PR-921 agents. Removes the reconcile_avatar helper (migration handles legacy directly). Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1fgdl5qqnh3k3f2xkqrvt7cujalhm623x4s7fdjdj5yrtp5fzjl9qrjpucw <4a1bfa0013bc6d14a8d600d8bf6392efefbd2a26ac3c96c9b2a106b0d12297ca@sprout-oss.stage.blox.sqprod.co>
wpfleger96
pushed a commit
that referenced
this pull request
Jun 9, 2026
The startup avatar backfill read the relay's kind:0 picture as the source of truth, but PR #921's old reconciliation had already overwritten that picture with the command default — so the backfill locked in the wrong avatar. Persona-backed agents now resolve their backfill avatar from PersonaRecord.avatar_url first, falling back to the relay picture and command icon only when no persona avatar exists. Signed-off-by: npub1fgdl5qqnh3k3f2xkqrvt7cujalhm623x4s7fdjdj5yrtp5fzjl9qrjpucw <4a1bfa0013bc6d14a8d600d8bf6392efefbd2a26ac3c96c9b2a106b0d12297ca@sprout-oss.stage.blox.sqprod.co>
tlongwell-block
pushed a commit
that referenced
this pull request
Jun 10, 2026
* origin/main: Fix post-compact handoff context for OpenAI providers (#931) chore(release): release version 0.3.15 (#936) fix: persona is source of truth at spawn + thread-depth conventions (#930) fix: skip avatar reconciliation for legacy agent records (#933) feat(desktop): add nest commit identity guidance with human sign-off (#929) feat: provider/model selection for personas and runtime-aware env injection (#794) fix: reconcile agent profile on startup when relay publish was missed (#921) Revamp first-run onboarding (#924) Update setup loading screen (#926) fix(dm): keep hidden DMs hidden across refetch via relay-signed visibility snapshot (NIP-DV) (#857) Maximize desktop window on launch (#925) feat: preview features (experiments settings UI) (#888) fix(updater): send no-cache header on update check to avoid stale manifest (#922) fix(desktop): refresh channel state after unarchive (#923) Add channel visibility & ephemeral TTL controls to manage sidebar (#911) ci(release): add Intel macOS (x86_64) DMG as a release target (#748) Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> # Conflicts: # desktop/src/features/sidebar/ui/AppSidebar.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a managed agent is created,
sync_managed_agent_profile()publishes the agent's kind:0 profile event to the relay. This can fail silently — the error is captured inprofileSyncErrorbut the frontend ignores it. The result: agents run with no kind:0 on the relay, so channel views show no name or avatar for the agent.Additionally, the avatar resolution path diverged between creation and reconciliation — creation used
input.avatar_urlwith a fallback, while reconciliation re-derived from persona config, potentially overwriting user intent on every restart.Solution
Add a fire-and-forget profile reconciliation step to
start_managed_agent. Every time an agent starts successfully, a background task:query_agent_profile()(using the agent's stored relay URL for endpoint consistency)display_nameandpictureagainst the stored canonical values onManagedAgentRecordsync_managed_agent_profile()eprintln!, never blocks agent startupThe avatar URL is resolved once at creation time and persisted on the record (
avatar_urlfield). Reconciliation compares against this stored value — no re-derivation from persona config, no divergence between create and reconcile paths. Pre-existing records without the field deserialize cleanly via#[serde(default)]; for those, the expected avatar falls back to the legacy persona/command derivation so they self-heal without regressing a previously published avatar to nothing.Changes
desktop/src-tauri/src/relay.rs— addedquery_relay_at()(parameterized relay URL),query_agent_profile(),AgentProfileInfostructdesktop/src-tauri/src/commands/agents.rs— addedavatar_url: Option<String>toManagedAgentRecord,ProfileReconcileData,reconcile_agent_profile(),profile_needs_sync(), and the spawn call instart_managed_agent.resolve_avatar_for_reconcile()is retained only as the fallback for records that predate theavatar_urlfield.desktop/scripts/check-file-sizes.mjs— file size override for agents.rsavatar_urlfield inmanaged_agents/{nest,runtime,relay_mesh}.rs