mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Sync persona profile edits to agents
This commit is contained in:
@@ -17,6 +17,12 @@ use crate::{
|
||||
util::now_iso,
|
||||
};
|
||||
|
||||
fn trim_optional(value: Option<String>) -> Option<String> {
|
||||
value
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
}
|
||||
|
||||
/// Query available models from an agent via `buzz-acp models --json`.
|
||||
///
|
||||
/// Spawns a short-lived subprocess (no relay connection needed). The subprocess
|
||||
@@ -139,7 +145,8 @@ pub async fn get_agent_models(
|
||||
///
|
||||
/// Does NOT auto-restart the agent. Runtime config changes (system prompt,
|
||||
/// parallelism, commands, toolsets) take effect on the next agent spawn.
|
||||
/// Name changes are synced to the relay immediately via a kind:0 re-publish.
|
||||
/// Name and avatar changes are synced to the relay immediately via a kind:0
|
||||
/// re-publish.
|
||||
#[tauri::command]
|
||||
pub async fn update_managed_agent(
|
||||
input: UpdateManagedAgentRequest,
|
||||
@@ -162,6 +169,7 @@ pub async fn update_managed_agent(
|
||||
let record = find_managed_agent_mut(&mut records, &input.pubkey)?;
|
||||
|
||||
let mut name_changed = false;
|
||||
let mut avatar_changed = false;
|
||||
if let Some(name_update) = input.name {
|
||||
let trimmed = name_update.trim().to_string();
|
||||
if !trimmed.is_empty() && trimmed != record.name {
|
||||
@@ -169,6 +177,13 @@ pub async fn update_managed_agent(
|
||||
name_changed = true;
|
||||
}
|
||||
}
|
||||
if let Some(avatar_update) = input.avatar_url {
|
||||
let normalized = trim_optional(avatar_update);
|
||||
if normalized != record.avatar_url {
|
||||
record.avatar_url = normalized;
|
||||
avatar_changed = true;
|
||||
}
|
||||
}
|
||||
if let Some(model_update) = input.model {
|
||||
record.model = model_update;
|
||||
}
|
||||
@@ -245,15 +260,19 @@ pub async fn update_managed_agent(
|
||||
.find(|r| r.pubkey == input.pubkey)
|
||||
.ok_or_else(|| format!("agent {} not found", input.pubkey))?;
|
||||
|
||||
let sync_params = if name_changed {
|
||||
let sync_params = if name_changed || avatar_changed {
|
||||
let agent_keys = Keys::parse(&record.private_key_nsec)
|
||||
.map_err(|e| format!("failed to parse agent keys: {e}"))?;
|
||||
let relay_url = record.relay_url.clone();
|
||||
let display_name = record.name.clone();
|
||||
let avatar_url = record
|
||||
.avatar_url
|
||||
.clone()
|
||||
.or_else(|| managed_agent_avatar_url(&record.agent_command));
|
||||
let avatar_url = if avatar_changed {
|
||||
record.avatar_url.clone()
|
||||
} else {
|
||||
record
|
||||
.avatar_url
|
||||
.clone()
|
||||
.or_else(|| managed_agent_avatar_url(&record.agent_command))
|
||||
};
|
||||
let auth_tag = record.auth_tag.clone();
|
||||
Some((agent_keys, relay_url, display_name, avatar_url, auth_tag))
|
||||
} else {
|
||||
|
||||
@@ -934,7 +934,9 @@ pub(crate) async fn reconcile_agent_profile(
|
||||
let existing = query_agent_profile(state, &data.relay_url, agent_pubkey).await?;
|
||||
|
||||
// Resolve the expected avatar — backfilling for legacy records that have no
|
||||
// stored avatar_url yet.
|
||||
// stored avatar_url yet. `None` is meaningful here: it means publish a
|
||||
// profile with no picture, which clears retired built-in Fizz data URLs from
|
||||
// the relay.
|
||||
let stored_avatar =
|
||||
filter_retired_fizz_avatar(data.persona_id.as_deref(), data.avatar_url.clone());
|
||||
let stored_avatar_was_retired_fizz = data
|
||||
@@ -942,7 +944,7 @@ pub(crate) async fn reconcile_agent_profile(
|
||||
.as_deref()
|
||||
.is_some_and(|url| is_retired_fizz_data_url(data.persona_id.as_deref(), url));
|
||||
let expected_avatar = match stored_avatar {
|
||||
Some(url) => url.to_string(),
|
||||
Some(url) => Some(url.to_string()),
|
||||
None => {
|
||||
// Legacy record: the relay profile may have been corrupted by the
|
||||
// old reconciliation code (it overwrote the persona avatar with the
|
||||
@@ -993,15 +995,15 @@ pub(crate) async fn reconcile_agent_profile(
|
||||
}
|
||||
}
|
||||
|
||||
backfilled
|
||||
if backfilled.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(backfilled)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if expected_avatar.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !profile_needs_sync(existing.as_ref(), &data.name, Some(&expected_avatar)) {
|
||||
if !profile_needs_sync(existing.as_ref(), &data.name, expected_avatar.as_deref()) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -1013,7 +1015,7 @@ pub(crate) async fn reconcile_agent_profile(
|
||||
&data.relay_url,
|
||||
&agent_keys,
|
||||
&data.name,
|
||||
Some(&expected_avatar),
|
||||
expected_avatar.as_deref(),
|
||||
data.auth_tag.as_deref(),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -1364,6 +1364,7 @@ pub fn build_managed_agent_summary(
|
||||
max_turn_duration_seconds: record.max_turn_duration_seconds,
|
||||
parallelism: record.parallelism,
|
||||
system_prompt: effective_prompt,
|
||||
avatar_url: record.avatar_url.clone(),
|
||||
model: effective_model,
|
||||
mcp_toolsets: record.mcp_toolsets.clone(),
|
||||
env_vars: record.env_vars.clone(),
|
||||
|
||||
@@ -230,6 +230,7 @@ pub struct ManagedAgentSummary {
|
||||
pub max_turn_duration_seconds: Option<u64>,
|
||||
pub parallelism: u32,
|
||||
pub system_prompt: Option<String>,
|
||||
pub avatar_url: Option<String>,
|
||||
pub model: Option<String>,
|
||||
pub mcp_toolsets: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
@@ -424,6 +425,9 @@ pub struct UpdateManagedAgentRequest {
|
||||
/// Absent = don't touch. Present = rename the agent.
|
||||
#[serde(default)]
|
||||
pub name: Option<String>,
|
||||
/// Absent = don't touch. null = clear. "url" = set.
|
||||
#[serde(default)]
|
||||
pub avatar_url: Option<Option<String>>,
|
||||
/// Absent = don't touch. null = clear to agent default. "id" = set.
|
||||
#[serde(default)]
|
||||
pub model: Option<Option<String>>,
|
||||
|
||||
@@ -264,11 +264,11 @@ export function useUpdateManagedAgentMutation() {
|
||||
);
|
||||
},
|
||||
onSettled: async (_data, _error, variables) => {
|
||||
// Backend republishes kind:0 on a name change (sync_managed_agent_profile),
|
||||
// so the relay has fresh profile data — but the desktop's React Query cache
|
||||
// for ["user-profile", pubkey] has a 60s staleTime and will not refetch on
|
||||
// Backend republishes kind:0 on name/avatar changes, so the relay has
|
||||
// fresh profile data — but the desktop's React Query cache for
|
||||
// ["user-profile", pubkey] has a 60s staleTime and will not refetch on
|
||||
// its own. Invalidate explicitly so the profile pane re-renders against
|
||||
// the new display name / about / NIP-05 immediately. Also poke any
|
||||
// the new display name/avatar immediately. Also poke any
|
||||
// ["users-batch", ...] entries that include this pubkey so sidebar member
|
||||
// rows, channel header chips, and message author labels refresh too.
|
||||
const lowerPubkey = variables.pubkey.toLowerCase();
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
useSetPersonaActiveMutation,
|
||||
useStartManagedAgentMutation,
|
||||
useStopManagedAgentMutation,
|
||||
useUpdateManagedAgentMutation,
|
||||
useUpdatePersonaMutation,
|
||||
} from "@/features/agents/hooks";
|
||||
import { AddAgentToChannelDialog } from "@/features/agents/ui/AddAgentToChannelDialog";
|
||||
@@ -64,6 +65,7 @@ import {
|
||||
} from "@/features/profile/ui/UserProfilePanelSections";
|
||||
import { useProfileAgentDeletion } from "@/features/profile/ui/UserProfilePanelDeletion";
|
||||
import { useProfileFieldBuckets } from "@/features/profile/ui/UserProfilePanelFields";
|
||||
import { submitProfilePersonaDialog } from "@/features/profile/ui/UserProfilePanelPersonaSubmit";
|
||||
import { UserProfilePersonaDialogs } from "@/features/profile/ui/UserProfilePersonaDialogs";
|
||||
import {
|
||||
buildPersonaDraftProfile,
|
||||
@@ -200,6 +202,7 @@ export function UserProfilePanel({
|
||||
const availableRuntimesQuery = useAvailableAcpRuntimes();
|
||||
const acpRuntimesQuery = useAcpRuntimesQuery();
|
||||
const createAgentMutation = useCreateManagedAgentMutation();
|
||||
const updateManagedAgentMutation = useUpdateManagedAgentMutation();
|
||||
const startAgentMutation = useStartManagedAgentMutation();
|
||||
const stopAgentMutation = useStopManagedAgentMutation();
|
||||
const deleteAgentMutation = useDeleteManagedAgentMutation();
|
||||
@@ -276,6 +279,7 @@ export function UserProfilePanel({
|
||||
managedAgent === undefined;
|
||||
const isAgentActionPending =
|
||||
createAgentMutation.isPending ||
|
||||
updateManagedAgentMutation.isPending ||
|
||||
startAgentMutation.isPending ||
|
||||
stopAgentMutation.isPending ||
|
||||
deleteAgentMutation.isPending ||
|
||||
@@ -488,46 +492,25 @@ export function UserProfilePanel({
|
||||
|
||||
const handleSubmitPersona = React.useCallback(
|
||||
async (input: CreatePersonaInput | UpdatePersonaInput) => {
|
||||
try {
|
||||
if ("id" in input) {
|
||||
await updatePersonaMutation.mutateAsync(input);
|
||||
toast.success(`Updated ${input.displayName}.`);
|
||||
} else {
|
||||
const persona = await createPersonaMutation.mutateAsync(input);
|
||||
try {
|
||||
const created = await createManagedAgentForPersona(persona);
|
||||
if (created.spawnError) {
|
||||
toast.error(
|
||||
`${persona.displayName} was created, but it did not start: ${created.spawnError}`,
|
||||
);
|
||||
} else {
|
||||
toast.success(`Created and started ${created.agent.name}.`);
|
||||
}
|
||||
if (created.profileSyncError) {
|
||||
toast.warning(
|
||||
`${created.agent.name} was created, but profile sync failed: ${created.profileSyncError}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? `${persona.displayName} was created, but the agent instance could not be created: ${error.message}`
|
||||
: `${persona.displayName} was created, but the agent instance could not be created.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
setPersonaDialogState(null);
|
||||
void personasQuery.refetch();
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Failed to save agent.",
|
||||
);
|
||||
}
|
||||
await submitProfilePersonaDialog({
|
||||
createManagedAgentForPersona,
|
||||
createPersona: createPersonaMutation.mutateAsync,
|
||||
input,
|
||||
managedAgent,
|
||||
onDone: () => {
|
||||
setPersonaDialogState(null);
|
||||
void personasQuery.refetch();
|
||||
},
|
||||
updateManagedAgent: updateManagedAgentMutation.mutateAsync,
|
||||
updatePersona: updatePersonaMutation.mutateAsync,
|
||||
});
|
||||
},
|
||||
[
|
||||
createPersonaMutation.mutateAsync,
|
||||
createManagedAgentForPersona,
|
||||
managedAgent,
|
||||
personasQuery.refetch,
|
||||
updateManagedAgentMutation.mutateAsync,
|
||||
updatePersonaMutation.mutateAsync,
|
||||
],
|
||||
);
|
||||
@@ -893,6 +876,7 @@ export function UserProfilePanel({
|
||||
isPending={
|
||||
createPersonaMutation.isPending ||
|
||||
updatePersonaMutation.isPending ||
|
||||
updateManagedAgentMutation.isPending ||
|
||||
createAgentMutation.isPending
|
||||
}
|
||||
personaDialogState={personaDialogState}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { personaManagedAgentUpdate } from "@/features/profile/ui/UserProfilePanelUtils";
|
||||
import type {
|
||||
AgentPersona,
|
||||
CreateManagedAgentResponse,
|
||||
CreatePersonaInput,
|
||||
ManagedAgent,
|
||||
UpdateManagedAgentInput,
|
||||
UpdatePersonaInput,
|
||||
} from "@/shared/api/types";
|
||||
|
||||
type SubmitProfilePersonaDialogOptions = {
|
||||
createManagedAgentForPersona: (
|
||||
persona: AgentPersona,
|
||||
) => Promise<CreateManagedAgentResponse>;
|
||||
createPersona: (input: CreatePersonaInput) => Promise<AgentPersona>;
|
||||
input: CreatePersonaInput | UpdatePersonaInput;
|
||||
managedAgent: ManagedAgent | undefined;
|
||||
onDone: () => void;
|
||||
updateManagedAgent: (
|
||||
input: UpdateManagedAgentInput,
|
||||
) => Promise<{ agent: ManagedAgent; profileSyncError: string | null }>;
|
||||
updatePersona: (input: UpdatePersonaInput) => Promise<AgentPersona>;
|
||||
};
|
||||
|
||||
export async function submitProfilePersonaDialog({
|
||||
createManagedAgentForPersona,
|
||||
createPersona,
|
||||
input,
|
||||
managedAgent,
|
||||
onDone,
|
||||
updateManagedAgent,
|
||||
updatePersona,
|
||||
}: SubmitProfilePersonaDialogOptions) {
|
||||
try {
|
||||
if ("id" in input) {
|
||||
const persona = await updatePersona(input);
|
||||
const agentUpdate = managedAgent
|
||||
? personaManagedAgentUpdate(managedAgent, persona)
|
||||
: null;
|
||||
const result = agentUpdate ? await updateManagedAgent(agentUpdate) : null;
|
||||
if (result?.profileSyncError) {
|
||||
toast.warning(
|
||||
`${result.agent.name} was updated, but profile sync failed: ${result.profileSyncError}`,
|
||||
);
|
||||
}
|
||||
toast.success(`Updated ${input.displayName}.`);
|
||||
} else {
|
||||
const persona = await createPersona(input);
|
||||
try {
|
||||
const created = await createManagedAgentForPersona(persona);
|
||||
if (created.spawnError) {
|
||||
toast.error(
|
||||
`${persona.displayName} was created, but it did not start: ${created.spawnError}`,
|
||||
);
|
||||
} else {
|
||||
toast.success(`Created and started ${created.agent.name}.`);
|
||||
}
|
||||
if (created.profileSyncError) {
|
||||
toast.warning(
|
||||
`${created.agent.name} was created, but profile sync failed: ${created.profileSyncError}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? `${persona.displayName} was created, but the agent instance could not be created: ${error.message}`
|
||||
: `${persona.displayName} was created, but the agent instance could not be created.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onDone();
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Failed to save agent.",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { personaManagedAgentUpdate } from "./UserProfilePanelUtils.ts";
|
||||
|
||||
function agent(overrides = {}) {
|
||||
return {
|
||||
pubkey: "deadbeef".repeat(8),
|
||||
name: "Fizz",
|
||||
personaId: "persona-1",
|
||||
relayUrl: "ws://localhost:3000",
|
||||
acpCommand: "buzz-acp",
|
||||
agentCommand: "goose",
|
||||
agentArgs: [],
|
||||
mcpCommand: "",
|
||||
turnTimeoutSeconds: 320,
|
||||
idleTimeoutSeconds: null,
|
||||
maxTurnDurationSeconds: null,
|
||||
parallelism: 1,
|
||||
systemPrompt: "Old prompt",
|
||||
avatarUrl: "app-avatar://old",
|
||||
model: "old-model",
|
||||
mcpToolsets: null,
|
||||
envVars: { OLD_KEY: "1" },
|
||||
status: "stopped",
|
||||
pid: null,
|
||||
createdAt: "2026-01-01T00:00:00Z",
|
||||
updatedAt: "2026-01-01T00:00:00Z",
|
||||
lastStartedAt: null,
|
||||
lastStoppedAt: null,
|
||||
lastExitCode: null,
|
||||
lastError: null,
|
||||
logPath: null,
|
||||
startOnAppLaunch: true,
|
||||
backend: { type: "local" },
|
||||
backendAgentId: null,
|
||||
respondTo: "owner-only",
|
||||
respondToAllowlist: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function persona(overrides = {}) {
|
||||
return {
|
||||
id: "persona-1",
|
||||
displayName: "Fizz Prime",
|
||||
avatarUrl: null,
|
||||
systemPrompt: "New prompt",
|
||||
runtime: "goose",
|
||||
model: "new-model",
|
||||
provider: null,
|
||||
namePool: [],
|
||||
isBuiltIn: false,
|
||||
isActive: true,
|
||||
envVars: { NEW_KEY: "2" },
|
||||
createdAt: "2026-01-01T00:00:00Z",
|
||||
updatedAt: "2026-01-01T00:00:00Z",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test("personaManagedAgentUpdate syncs edited persona identity to linked agent", () => {
|
||||
assert.deepEqual(personaManagedAgentUpdate(agent(), persona()), {
|
||||
pubkey: "deadbeef".repeat(8),
|
||||
name: "Fizz Prime",
|
||||
avatarUrl: null,
|
||||
systemPrompt: "New prompt",
|
||||
model: "new-model",
|
||||
envVars: { NEW_KEY: "2" },
|
||||
});
|
||||
});
|
||||
|
||||
test("personaManagedAgentUpdate skips unrelated or unchanged agents", () => {
|
||||
assert.equal(
|
||||
personaManagedAgentUpdate(agent({ personaId: "persona-2" }), persona()),
|
||||
null,
|
||||
);
|
||||
assert.equal(
|
||||
personaManagedAgentUpdate(
|
||||
agent({
|
||||
name: "Fizz Prime",
|
||||
avatarUrl: null,
|
||||
systemPrompt: "New prompt",
|
||||
model: "new-model",
|
||||
envVars: { NEW_KEY: "2" },
|
||||
}),
|
||||
persona(),
|
||||
),
|
||||
null,
|
||||
);
|
||||
});
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
ManagedAgent,
|
||||
Profile,
|
||||
RelayAgent,
|
||||
UpdateManagedAgentInput,
|
||||
} from "@/shared/api/types";
|
||||
import { normalizePubkey } from "@/shared/lib/pubkey";
|
||||
|
||||
@@ -158,6 +159,54 @@ export function resolveAgentInstruction(
|
||||
);
|
||||
}
|
||||
|
||||
export function personaManagedAgentUpdate(
|
||||
agent: ManagedAgent,
|
||||
persona: AgentPersona,
|
||||
): UpdateManagedAgentInput | null {
|
||||
if (agent.personaId !== persona.id) return null;
|
||||
|
||||
const input: UpdateManagedAgentInput = { pubkey: agent.pubkey };
|
||||
let hasChanges = false;
|
||||
|
||||
if (persona.displayName !== agent.name) {
|
||||
input.name = persona.displayName;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if ((persona.avatarUrl ?? null) !== (agent.avatarUrl ?? null)) {
|
||||
input.avatarUrl = persona.avatarUrl;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if (persona.systemPrompt !== (agent.systemPrompt ?? "")) {
|
||||
input.systemPrompt = persona.systemPrompt;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if ((persona.model ?? null) !== (agent.model ?? null)) {
|
||||
input.model = persona.model;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if (!stringRecordEqual(persona.envVars, agent.envVars)) {
|
||||
input.envVars = persona.envVars;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
return hasChanges ? input : null;
|
||||
}
|
||||
|
||||
function stringRecordEqual(
|
||||
left: Record<string, string>,
|
||||
right: Record<string, string>,
|
||||
) {
|
||||
const leftKeys = Object.keys(left);
|
||||
const rightKeys = Object.keys(right);
|
||||
if (leftKeys.length !== rightKeys.length) return false;
|
||||
|
||||
return leftKeys.every((key) => left[key] === right[key]);
|
||||
}
|
||||
|
||||
export function useRetainedPersona(
|
||||
sourcePersona: AgentPersona | undefined,
|
||||
profileIdentityKey: string,
|
||||
|
||||
@@ -213,6 +213,7 @@ export type RawManagedAgent = {
|
||||
max_turn_duration_seconds: number | null;
|
||||
parallelism: number;
|
||||
system_prompt: string | null;
|
||||
avatar_url?: string | null;
|
||||
model: string | null;
|
||||
mcp_toolsets: string | null;
|
||||
env_vars?: Record<string, string>;
|
||||
@@ -228,8 +229,7 @@ export type RawManagedAgent = {
|
||||
start_on_app_launch: boolean;
|
||||
backend: ManagedAgentBackend;
|
||||
backend_agent_id: string | null;
|
||||
// Optional: pre-feature mock fixtures may omit these. Mapped to
|
||||
// `"owner-only"` / `[]` in `fromRawManagedAgent`.
|
||||
// Optional in pre-feature mock fixtures; mapped in fromRawManagedAgent.
|
||||
respond_to?: ManagedAgent["respondTo"];
|
||||
respond_to_allowlist?: string[];
|
||||
};
|
||||
@@ -868,6 +868,7 @@ export function fromRawManagedAgent(agent: RawManagedAgent): ManagedAgent {
|
||||
maxTurnDurationSeconds: agent.max_turn_duration_seconds,
|
||||
parallelism: agent.parallelism,
|
||||
systemPrompt: agent.system_prompt,
|
||||
avatarUrl: agent.avatar_url ?? null,
|
||||
model: agent.model,
|
||||
mcpToolsets: agent.mcp_toolsets,
|
||||
envVars: agent.env_vars ?? {},
|
||||
@@ -883,8 +884,7 @@ export function fromRawManagedAgent(agent: RawManagedAgent): ManagedAgent {
|
||||
startOnAppLaunch: agent.start_on_app_launch,
|
||||
backend: agent.backend,
|
||||
backendAgentId: agent.backend_agent_id,
|
||||
// Fallbacks for pre-feature mocks/fixtures that don't carry these fields.
|
||||
// Real agent records always include them (defaulted server-side).
|
||||
// Fallbacks for pre-feature mocks; real records default server-side.
|
||||
respondTo: agent.respond_to ?? "owner-only",
|
||||
respondToAllowlist: agent.respond_to_allowlist ?? [],
|
||||
};
|
||||
|
||||
@@ -282,6 +282,7 @@ export type ManagedAgent = {
|
||||
maxTurnDurationSeconds: number | null;
|
||||
parallelism: number;
|
||||
systemPrompt: string | null;
|
||||
avatarUrl: string | null;
|
||||
model: string | null;
|
||||
mcpToolsets: string | null;
|
||||
/** Per-agent env vars. Layered on top of persona envVars. */
|
||||
@@ -446,6 +447,7 @@ export type AgentModelInfo = {
|
||||
export type UpdateManagedAgentInput = {
|
||||
pubkey: string;
|
||||
name?: string;
|
||||
avatarUrl?: string | null;
|
||||
model?: string | null;
|
||||
systemPrompt?: string | null;
|
||||
mcpToolsets?: string | null;
|
||||
|
||||
@@ -367,6 +367,7 @@ type RawManagedAgent = {
|
||||
max_turn_duration_seconds: number | null;
|
||||
parallelism: number;
|
||||
system_prompt: string | null;
|
||||
avatar_url: string | null;
|
||||
model: string | null;
|
||||
env_vars?: Record<string, string>;
|
||||
status: "running" | "stopped" | "deployed" | "not_deployed";
|
||||
@@ -907,6 +908,7 @@ function cloneManagedAgent(agent: MockManagedAgent): RawManagedAgent {
|
||||
max_turn_duration_seconds: agent.max_turn_duration_seconds ?? null,
|
||||
parallelism: agent.parallelism,
|
||||
system_prompt: agent.system_prompt,
|
||||
avatar_url: agent.avatar_url,
|
||||
model: agent.model,
|
||||
env_vars: { ...(agent.env_vars ?? {}) },
|
||||
status: agent.status,
|
||||
@@ -996,6 +998,7 @@ function buildSeededManagedAgent(seed: MockManagedAgentSeed): MockManagedAgent {
|
||||
max_turn_duration_seconds: null,
|
||||
parallelism: 1,
|
||||
system_prompt: null,
|
||||
avatar_url: null,
|
||||
model: null,
|
||||
env_vars: {},
|
||||
status,
|
||||
@@ -5230,6 +5233,7 @@ async function handleCreateManagedAgent(
|
||||
max_turn_duration_seconds: args.input.maxTurnDurationSeconds ?? null,
|
||||
parallelism: args.input.parallelism ?? 1,
|
||||
system_prompt: args.input.systemPrompt?.trim() || null,
|
||||
avatar_url: avatarUrl,
|
||||
model: args.input.model?.trim() || null,
|
||||
env_vars: { ...(args.input.envVars ?? {}) },
|
||||
status: args.input.spawnAfterCreate ? "running" : "stopped",
|
||||
@@ -5405,6 +5409,7 @@ async function handleUpdateManagedAgent(args: {
|
||||
input: {
|
||||
pubkey: string;
|
||||
name?: string;
|
||||
avatarUrl?: string | null;
|
||||
model?: string | null;
|
||||
systemPrompt?: string | null;
|
||||
envVars?: Record<string, string>;
|
||||
@@ -5416,6 +5421,9 @@ async function handleUpdateManagedAgent(args: {
|
||||
if (args.input.name !== undefined) {
|
||||
agent.name = args.input.name;
|
||||
}
|
||||
if (args.input.avatarUrl !== undefined) {
|
||||
agent.avatar_url = args.input.avatarUrl?.trim() || null;
|
||||
}
|
||||
if (args.input.model !== undefined) {
|
||||
agent.model = args.input.model;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user