mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
Polish chat replies and managed agent creation (#81)
This commit is contained in:
@@ -13,7 +13,7 @@ use crate::{
|
||||
DiscoverManagedAgentPrereqsRequest, ManagedAgentLogResponse, ManagedAgentPrereqsInfo,
|
||||
ManagedAgentSummary, MintManagedAgentTokenRequest, MintManagedAgentTokenResponse,
|
||||
RelayAgentInfo, DEFAULT_ACP_COMMAND, DEFAULT_AGENT_ARG, DEFAULT_AGENT_COMMAND,
|
||||
DEFAULT_AGENT_TURN_TIMEOUT_SECONDS, DEFAULT_MCP_COMMAND,
|
||||
DEFAULT_AGENT_PARALLELISM, DEFAULT_AGENT_TURN_TIMEOUT_SECONDS, DEFAULT_MCP_COMMAND,
|
||||
},
|
||||
relay::{
|
||||
build_authed_request, managed_agent_owner_pubkey, relay_ws_url, send_json_request,
|
||||
@@ -94,6 +94,11 @@ pub async fn create_managed_agent(
|
||||
if name.is_empty() {
|
||||
return Err("agent name is required".to_string());
|
||||
}
|
||||
if let Some(parallelism) = input.parallelism {
|
||||
if !(1..=32).contains(¶llelism) {
|
||||
return Err("parallelism must be between 1 and 32".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let owner_pubkey = if input.mint_token {
|
||||
Some(managed_agent_owner_pubkey(&state).await?)
|
||||
@@ -209,6 +214,16 @@ pub async fn create_managed_agent(
|
||||
.turn_timeout_seconds
|
||||
.filter(|seconds| *seconds > 0)
|
||||
.unwrap_or(DEFAULT_AGENT_TURN_TIMEOUT_SECONDS),
|
||||
parallelism: input
|
||||
.parallelism
|
||||
.filter(|count| (1..=32).contains(count))
|
||||
.unwrap_or(DEFAULT_AGENT_PARALLELISM),
|
||||
system_prompt: input
|
||||
.system_prompt
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string),
|
||||
created_at: now_iso(),
|
||||
updated_at: now_iso(),
|
||||
last_started_at: None,
|
||||
|
||||
@@ -88,6 +88,8 @@ pub fn build_managed_agent_summary(
|
||||
agent_args: record.agent_args.clone(),
|
||||
mcp_command: record.mcp_command.clone(),
|
||||
turn_timeout_seconds: record.turn_timeout_seconds,
|
||||
parallelism: record.parallelism,
|
||||
system_prompt: record.system_prompt.clone(),
|
||||
has_api_token: record.api_token.is_some(),
|
||||
status,
|
||||
pid,
|
||||
@@ -167,10 +169,16 @@ pub fn start_managed_agent_process(
|
||||
"SPROUT_ACP_TURN_TIMEOUT",
|
||||
record.turn_timeout_seconds.to_string(),
|
||||
);
|
||||
command.env("SPROUT_ACP_AGENTS", record.parallelism.to_string());
|
||||
command.env(
|
||||
"GOOSE_MODE",
|
||||
std::env::var("GOOSE_MODE").unwrap_or_else(|_| "auto".to_string()),
|
||||
);
|
||||
if let Some(system_prompt) = &record.system_prompt {
|
||||
command.env("SPROUT_ACP_SYSTEM_PROMPT", system_prompt);
|
||||
} else {
|
||||
command.env_remove("SPROUT_ACP_SYSTEM_PROMPT");
|
||||
}
|
||||
command.env_remove("SPROUT_ACP_PRIVATE_KEY");
|
||||
command.env_remove("SPROUT_ACP_API_TOKEN");
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ pub struct ManagedAgentRecord {
|
||||
pub agent_args: Vec<String>,
|
||||
pub mcp_command: String,
|
||||
pub turn_timeout_seconds: u64,
|
||||
#[serde(default = "default_agent_parallelism")]
|
||||
pub parallelism: u32,
|
||||
pub system_prompt: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub last_started_at: Option<String>,
|
||||
@@ -48,6 +51,8 @@ pub struct ManagedAgentSummary {
|
||||
pub agent_args: Vec<String>,
|
||||
pub mcp_command: String,
|
||||
pub turn_timeout_seconds: u64,
|
||||
pub parallelism: u32,
|
||||
pub system_prompt: Option<String>,
|
||||
pub has_api_token: bool,
|
||||
pub status: String,
|
||||
pub pid: Option<u32>,
|
||||
@@ -71,6 +76,8 @@ pub struct CreateManagedAgentRequest {
|
||||
pub agent_args: Vec<String>,
|
||||
pub mcp_command: Option<String>,
|
||||
pub turn_timeout_seconds: Option<u64>,
|
||||
pub parallelism: Option<u32>,
|
||||
pub system_prompt: Option<String>,
|
||||
#[serde(default)]
|
||||
pub mint_token: bool,
|
||||
#[serde(default)]
|
||||
@@ -155,3 +162,8 @@ pub const DEFAULT_AGENT_COMMAND: &str = "goose";
|
||||
pub const DEFAULT_MCP_COMMAND: &str = "sprout-mcp-server";
|
||||
pub const DEFAULT_AGENT_ARG: &str = "acp";
|
||||
pub const DEFAULT_AGENT_TURN_TIMEOUT_SECONDS: u64 = 300;
|
||||
pub const DEFAULT_AGENT_PARALLELISM: u32 = 1;
|
||||
|
||||
fn default_agent_parallelism() -> u32 {
|
||||
DEFAULT_AGENT_PARALLELISM
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ export function CreateAgentDialog({
|
||||
() => new Set<TokenScope>(DEFAULT_MANAGED_AGENT_SCOPES),
|
||||
);
|
||||
const [turnTimeoutSeconds, setTurnTimeoutSeconds] = React.useState("300");
|
||||
const [parallelism, setParallelism] = React.useState("1");
|
||||
const [systemPrompt, setSystemPrompt] = React.useState("");
|
||||
const [selectedProviderId, setSelectedProviderId] =
|
||||
React.useState<string>("custom");
|
||||
const [hasSyncedProviderSelection, setHasSyncedProviderSelection] =
|
||||
@@ -133,6 +135,8 @@ export function CreateAgentDialog({
|
||||
setAgentArgs("acp");
|
||||
setMcpCommand("sprout-mcp-server");
|
||||
setTurnTimeoutSeconds("300");
|
||||
setParallelism("1");
|
||||
setSystemPrompt("");
|
||||
setSelectedProviderId("custom");
|
||||
setHasSyncedProviderSelection(false);
|
||||
setShowAdvanced(false);
|
||||
@@ -202,6 +206,11 @@ export function CreateAgentDialog({
|
||||
Number.parseInt(turnTimeoutSeconds, 10) > 0
|
||||
? Number.parseInt(turnTimeoutSeconds, 10)
|
||||
: undefined,
|
||||
parallelism:
|
||||
Number.parseInt(parallelism, 10) > 0
|
||||
? Number.parseInt(parallelism, 10)
|
||||
: undefined,
|
||||
systemPrompt: systemPrompt.trim() || undefined,
|
||||
mintToken,
|
||||
tokenName: tokenName.trim() || undefined,
|
||||
tokenScopes: [...selectedScopes],
|
||||
@@ -280,7 +289,8 @@ export function CreateAgentDialog({
|
||||
Advanced setup
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Relay overrides, raw commands, timeout, and doctor guidance.
|
||||
Relay overrides, raw commands, timeout, parallelism, prompt
|
||||
override, and doctor guidance.
|
||||
</p>
|
||||
</div>
|
||||
<span className="shrink-0 self-center text-muted-foreground">
|
||||
@@ -298,14 +308,18 @@ export function CreateAgentDialog({
|
||||
agentArgs={agentArgs}
|
||||
agentCommand={agentCommand}
|
||||
mcpCommand={mcpCommand}
|
||||
onParallelismChange={setParallelism}
|
||||
onAcpCommandChange={setAcpCommand}
|
||||
onAgentArgsChange={setAgentArgs}
|
||||
onAgentCommandChange={setAgentCommand}
|
||||
onMcpCommandChange={setMcpCommand}
|
||||
onRelayUrlChange={setRelayUrl}
|
||||
onSystemPromptChange={setSystemPrompt}
|
||||
onTurnTimeoutChange={setTurnTimeoutSeconds}
|
||||
parallelism={parallelism}
|
||||
relayUrl={relayUrl}
|
||||
selectedProviderId={selectedProviderId}
|
||||
systemPrompt={systemPrompt}
|
||||
turnTimeoutSeconds={turnTimeoutSeconds}
|
||||
/>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
import { MANAGED_AGENT_SCOPE_OPTIONS } from "@/features/tokens/lib/scopeOptions";
|
||||
import { cn } from "@/shared/lib/cn";
|
||||
import { Input } from "@/shared/ui/input";
|
||||
import { Textarea } from "@/shared/ui/textarea";
|
||||
import { describeResolvedCommand } from "./agentUi";
|
||||
|
||||
export function CreateAgentBasicsFields({
|
||||
@@ -98,28 +99,36 @@ export function CreateAgentRuntimeFields({
|
||||
agentArgs,
|
||||
agentCommand,
|
||||
mcpCommand,
|
||||
parallelism,
|
||||
relayUrl,
|
||||
selectedProviderId,
|
||||
systemPrompt,
|
||||
turnTimeoutSeconds,
|
||||
onAcpCommandChange,
|
||||
onAgentArgsChange,
|
||||
onAgentCommandChange,
|
||||
onMcpCommandChange,
|
||||
onParallelismChange,
|
||||
onRelayUrlChange,
|
||||
onSystemPromptChange,
|
||||
onTurnTimeoutChange,
|
||||
}: {
|
||||
acpCommand: string;
|
||||
agentArgs: string;
|
||||
agentCommand: string;
|
||||
mcpCommand: string;
|
||||
parallelism: string;
|
||||
relayUrl: string;
|
||||
selectedProviderId: string;
|
||||
systemPrompt: string;
|
||||
turnTimeoutSeconds: string;
|
||||
onAcpCommandChange: (value: string) => void;
|
||||
onAgentArgsChange: (value: string) => void;
|
||||
onAgentCommandChange: (value: string) => void;
|
||||
onMcpCommandChange: (value: string) => void;
|
||||
onParallelismChange: (value: string) => void;
|
||||
onRelayUrlChange: (value: string) => void;
|
||||
onSystemPromptChange: (value: string) => void;
|
||||
onTurnTimeoutChange: (value: string) => void;
|
||||
}) {
|
||||
return (
|
||||
@@ -165,7 +174,7 @@ export function CreateAgentRuntimeFields({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-[1.5fr,1.2fr,0.8fr]">
|
||||
<div className="grid gap-4 md:grid-cols-[1.5fr,1.2fr,0.8fr,0.8fr]">
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium" htmlFor="agent-runtime-args">
|
||||
Agent runtime args
|
||||
@@ -199,9 +208,48 @@ export function CreateAgentRuntimeFields({
|
||||
<Input
|
||||
id="agent-timeout"
|
||||
onChange={(event) => onTurnTimeoutChange(event.target.value)}
|
||||
placeholder="300"
|
||||
value={turnTimeoutSeconds}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium" htmlFor="agent-parallelism">
|
||||
Parallelism
|
||||
</label>
|
||||
<Input
|
||||
data-testid="agent-parallelism-input"
|
||||
id="agent-parallelism"
|
||||
inputMode="numeric"
|
||||
max="32"
|
||||
min="1"
|
||||
onChange={(event) => onParallelismChange(event.target.value)}
|
||||
placeholder="1"
|
||||
step="1"
|
||||
type="number"
|
||||
value={parallelism}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Number of ACP worker subprocesses. `sprout-acp` allows 1-32.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium" htmlFor="agent-system-prompt">
|
||||
System prompt override
|
||||
</label>
|
||||
<Textarea
|
||||
data-testid="agent-system-prompt-input"
|
||||
id="agent-system-prompt"
|
||||
onChange={(event) => onSystemPromptChange(event.target.value)}
|
||||
placeholder="Leave blank to send no ACP system prompt"
|
||||
value={systemPrompt}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Blank means no override. `sprout-acp` will not add a `[System]`
|
||||
prompt.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -54,7 +54,10 @@ export function ManagedAgentLogPanel({
|
||||
<span>{selectedAgent.name}</span>
|
||||
<span>{selectedAgent.status}</span>
|
||||
</div>
|
||||
<pre className="max-h-[22rem] overflow-auto whitespace-pre-wrap px-4 py-4">
|
||||
<pre
|
||||
className="max-h-[22rem] overflow-auto whitespace-pre-wrap px-4 py-4"
|
||||
data-testid="managed-agent-log-content"
|
||||
>
|
||||
{logContent?.trim() ? logContent : "No log output yet."}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
@@ -239,10 +239,6 @@ export function formatTimelineMessages(
|
||||
const authorPubkey =
|
||||
authorPubkeyByEventId.get(event.id) ?? getEffectiveAuthorPubkey(event);
|
||||
const thread = getThreadReference(event.tags);
|
||||
const parentEvent = thread.parentId
|
||||
? eventsById.get(thread.parentId)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
id: event.id,
|
||||
createdAt: event.created_at,
|
||||
@@ -262,8 +258,6 @@ export function formatTimelineMessages(
|
||||
parentId: thread.parentId,
|
||||
rootId: thread.rootId,
|
||||
depth: getDepth(event),
|
||||
replyToAuthor: parentEvent ? getAuthorLabel(parentEvent) : null,
|
||||
replyToSnippet: parentEvent?.content ?? null,
|
||||
accent: currentPubkey === authorPubkey,
|
||||
pending: event.pending,
|
||||
kind: event.kind,
|
||||
|
||||
@@ -16,8 +16,6 @@ export type TimelineMessage = {
|
||||
parentId?: string | null;
|
||||
rootId?: string | null;
|
||||
depth: number;
|
||||
replyToAuthor?: string | null;
|
||||
replyToSnippet?: string | null;
|
||||
accent?: boolean;
|
||||
pending?: boolean;
|
||||
highlighted?: boolean;
|
||||
|
||||
@@ -37,7 +37,6 @@ export function MessageRow({
|
||||
>(null);
|
||||
const [reactionPending, setReactionPending] = React.useState(false);
|
||||
const visibleDepth = Math.min(message.depth, 6);
|
||||
const compressedDepth = Math.max(message.depth - visibleDepth, 0);
|
||||
const indentPx = visibleDepth * 28;
|
||||
const initials = message.author
|
||||
.split(" ")
|
||||
@@ -190,25 +189,6 @@ export function MessageRow({
|
||||
)}
|
||||
|
||||
<div className="min-w-0 flex-1 space-y-0">
|
||||
{message.parentId ? (
|
||||
<div className="mb-1 flex min-w-0 flex-wrap items-center gap-2 text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground">
|
||||
<span className="rounded-full bg-muted px-2 py-0.5 text-[10px]">
|
||||
Reply
|
||||
</span>
|
||||
<span className="truncate normal-case tracking-normal">
|
||||
{message.replyToAuthor
|
||||
? `to ${message.replyToAuthor}`
|
||||
: "to an earlier message"}
|
||||
</span>
|
||||
{compressedDepth > 0 ? (
|
||||
<span className="rounded-full bg-muted px-2 py-0.5 text-[10px]">
|
||||
+{compressedDepth} more level
|
||||
{compressedDepth === 1 ? "" : "s"}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
{message.pubkey ? (
|
||||
<UserProfilePopover pubkey={message.pubkey}>
|
||||
@@ -291,13 +271,6 @@ export function MessageRow({
|
||||
{reactionErrorMessage}
|
||||
</p>
|
||||
) : null}
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
{message.replyToSnippet ? (
|
||||
<p className="truncate text-xs text-muted-foreground">
|
||||
{message.replyToSnippet}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
{expandedDiffId === message.id ? (
|
||||
<React.Suspense
|
||||
fallback={
|
||||
|
||||
@@ -443,6 +443,7 @@ export function AppSidebar({
|
||||
</SidebarMenuItem>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
data-testid="open-agents-view"
|
||||
isActive={selectedView === "agents"}
|
||||
onClick={onSelectAgents}
|
||||
tooltip="Agents"
|
||||
|
||||
@@ -214,6 +214,8 @@ type RawManagedAgent = {
|
||||
agent_args: string[];
|
||||
mcp_command: string;
|
||||
turn_timeout_seconds: number;
|
||||
parallelism: number;
|
||||
system_prompt: string | null;
|
||||
has_api_token: boolean;
|
||||
status: ManagedAgent["status"];
|
||||
pid: number | null;
|
||||
@@ -711,6 +713,8 @@ function fromRawManagedAgent(agent: RawManagedAgent): ManagedAgent {
|
||||
agentArgs: agent.agent_args,
|
||||
mcpCommand: agent.mcp_command,
|
||||
turnTimeoutSeconds: agent.turn_timeout_seconds,
|
||||
parallelism: agent.parallelism,
|
||||
systemPrompt: agent.system_prompt,
|
||||
hasApiToken: agent.has_api_token,
|
||||
status: agent.status,
|
||||
pid: agent.pid,
|
||||
@@ -804,6 +808,8 @@ export async function createManagedAgent(
|
||||
agentArgs: input.agentArgs,
|
||||
mcpCommand: input.mcpCommand,
|
||||
turnTimeoutSeconds: input.turnTimeoutSeconds,
|
||||
parallelism: input.parallelism,
|
||||
systemPrompt: input.systemPrompt,
|
||||
mintToken: input.mintToken,
|
||||
tokenScopes: input.tokenScopes,
|
||||
tokenName: input.tokenName,
|
||||
|
||||
@@ -253,6 +253,8 @@ export type ManagedAgent = {
|
||||
agentArgs: string[];
|
||||
mcpCommand: string;
|
||||
turnTimeoutSeconds: number;
|
||||
parallelism: number;
|
||||
systemPrompt: string | null;
|
||||
hasApiToken: boolean;
|
||||
status: "running" | "stopped";
|
||||
pid: number | null;
|
||||
@@ -273,6 +275,8 @@ export type CreateManagedAgentInput = {
|
||||
agentArgs?: string[];
|
||||
mcpCommand?: string;
|
||||
turnTimeoutSeconds?: number;
|
||||
parallelism?: number;
|
||||
systemPrompt?: string;
|
||||
mintToken?: boolean;
|
||||
tokenScopes?: TokenScope[];
|
||||
tokenName?: string;
|
||||
|
||||
@@ -208,6 +208,8 @@ type RawManagedAgent = {
|
||||
agent_args: string[];
|
||||
mcp_command: string;
|
||||
turn_timeout_seconds: number;
|
||||
parallelism: number;
|
||||
system_prompt: string | null;
|
||||
has_api_token: boolean;
|
||||
status: "running" | "stopped";
|
||||
pid: number | null;
|
||||
@@ -224,6 +226,7 @@ type RawCreateManagedAgentResponse = {
|
||||
agent: RawManagedAgent;
|
||||
private_key_nsec: string;
|
||||
api_token: string | null;
|
||||
profile_sync_error: string | null;
|
||||
spawn_error: string | null;
|
||||
};
|
||||
|
||||
@@ -469,6 +472,8 @@ function cloneManagedAgent(agent: MockManagedAgent): RawManagedAgent {
|
||||
agent_args: [...agent.agent_args],
|
||||
mcp_command: agent.mcp_command,
|
||||
turn_timeout_seconds: agent.turn_timeout_seconds,
|
||||
parallelism: agent.parallelism,
|
||||
system_prompt: agent.system_prompt,
|
||||
has_api_token: agent.has_api_token,
|
||||
status: agent.status,
|
||||
pid: agent.pid,
|
||||
@@ -2030,6 +2035,8 @@ async function handleCreateManagedAgent(args: {
|
||||
agentArgs?: string[];
|
||||
mcpCommand?: string;
|
||||
turnTimeoutSeconds?: number;
|
||||
parallelism?: number;
|
||||
systemPrompt?: string;
|
||||
mintToken?: boolean;
|
||||
tokenScopes?: string[];
|
||||
tokenName?: string;
|
||||
@@ -2059,6 +2066,8 @@ async function handleCreateManagedAgent(args: {
|
||||
: ["acp"],
|
||||
mcp_command: args.input.mcpCommand ?? "sprout-mcp-server",
|
||||
turn_timeout_seconds: args.input.turnTimeoutSeconds ?? 300,
|
||||
parallelism: args.input.parallelism ?? 1,
|
||||
system_prompt: args.input.systemPrompt?.trim() || null,
|
||||
has_api_token: token !== null,
|
||||
status: args.input.spawnAfterCreate ? "running" : "stopped",
|
||||
pid: args.input.spawnAfterCreate ? 42000 + mockManagedAgents.length : null,
|
||||
@@ -2072,7 +2081,10 @@ async function handleCreateManagedAgent(args: {
|
||||
private_key_nsec: `nsec1mock${pubkey.slice(0, 20)}`,
|
||||
api_token: token,
|
||||
log_lines: [
|
||||
`sprout-acp starting: relay=${args.input.relayUrl ?? DEFAULT_RELAY_WS_URL} agent_pubkey=${pubkey}`,
|
||||
`sprout-acp starting: relay=${args.input.relayUrl ?? DEFAULT_RELAY_WS_URL} agent_pubkey=${pubkey} parallelism=${args.input.parallelism ?? 1}`,
|
||||
args.input.systemPrompt?.trim()
|
||||
? `system prompt override configured (${args.input.systemPrompt.trim().length} chars)`
|
||||
: "system prompt override not set",
|
||||
args.input.spawnAfterCreate
|
||||
? "connected to relay at ws://localhost:3000"
|
||||
: "profile created; harness not started",
|
||||
@@ -2086,6 +2098,7 @@ async function handleCreateManagedAgent(args: {
|
||||
agent: cloneManagedAgent(managedAgent),
|
||||
private_key_nsec: managedAgent.private_key_nsec,
|
||||
api_token: managedAgent.api_token,
|
||||
profile_sync_error: null,
|
||||
spawn_error: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -214,6 +214,7 @@ test("supports nested replies with visible indentation", async ({ page }) => {
|
||||
await page.getByTestId("message-input").fill(firstReply);
|
||||
await page.getByTestId("send-message").click();
|
||||
await expect(rows.last()).toContainText(firstReply);
|
||||
await expect(rows.last()).not.toContainText("Welcome to #general");
|
||||
|
||||
await rows.last().hover();
|
||||
await replyButtons.last().click();
|
||||
@@ -221,6 +222,7 @@ test("supports nested replies with visible indentation", async ({ page }) => {
|
||||
await page.getByTestId("message-input").fill(nestedReply);
|
||||
await page.getByTestId("send-message").click();
|
||||
await expect(rows.last()).toContainText(nestedReply);
|
||||
await expect(rows.last()).not.toContainText(firstReply);
|
||||
|
||||
const rootBox = await rows.nth(0).boundingBox();
|
||||
const firstReplyBox = await rows.nth(1).boundingBox();
|
||||
|
||||
@@ -68,6 +68,36 @@ test("creates a new mocked stream", async ({ page }) => {
|
||||
await expect(page.getByTestId("chat-title")).toHaveText(channelName);
|
||||
});
|
||||
|
||||
test("create agent supports parallelism and system prompt overrides", async ({
|
||||
page,
|
||||
}) => {
|
||||
const agentName = `Parallel agent ${Date.now()}`;
|
||||
|
||||
await page.goto("/");
|
||||
await page.getByTestId("open-agents-view").click();
|
||||
await page.getByRole("button", { name: "Create agent" }).click();
|
||||
|
||||
await page.getByTestId("agent-name-input").fill(agentName);
|
||||
await page.getByRole("button", { name: "Advanced setup" }).click();
|
||||
await page.getByTestId("agent-parallelism-input").fill("3");
|
||||
await page
|
||||
.getByTestId("agent-system-prompt-input")
|
||||
.fill("You are concise and parallelize independent work.");
|
||||
await page.getByTestId("create-agent-submit").click();
|
||||
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Agent created" }),
|
||||
).toBeVisible();
|
||||
await page.getByRole("button", { name: "Done" }).click();
|
||||
|
||||
await expect(page.getByTestId("managed-agent-log-content")).toContainText(
|
||||
"parallelism=3",
|
||||
);
|
||||
await expect(page.getByTestId("managed-agent-log-content")).toContainText(
|
||||
"system prompt override configured",
|
||||
);
|
||||
});
|
||||
|
||||
test("opens a mocked channel from the home feed", async ({ page }) => {
|
||||
const mentionsSection = page.locator("section").filter({
|
||||
has: page.getByRole("heading", { name: "@Mentions" }),
|
||||
|
||||
Reference in New Issue
Block a user