Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,27 @@ server that `veadk frontend` launches — no separate backend.
- **Custom-agent workbench**: configure an agent with a rich Markdown
system-prompt editor (including heading and list shortcuts), then debug with
expandable, copyable runner error details, per-result Trace inspection, and
review. In-progress drafts are stored only in the current browser and scoped
review. Debugging is optional: creators can enter comparison debugging or
publish directly after architecture configuration. Comparison results keep
the baseline and candidates side by side, while one focused configuration
panel preserves edits when switching Agent or dimension. The baseline exposes
a read-only model, prompt, and Skills summary; candidate model changes support
Model ID, Provider, API Base, and temporary API Key values. In-progress drafts
are stored only in the current browser and scoped
to the signed-in user. MCP tokens are converted to Runtime environment
variables: generated source retains only the `${ENV_NAME}` reference, while
YAML and browser drafts preserve the corresponding environment value.
Runtime updates reload existing values, and the deployment form keeps all
environment values visible to users who can view the Agent. Entering a
replacement Token overrides the previous value. Long descriptions and prompts
scroll within bounded editors, while the sidebar stays pinned to the
viewport. On narrow desktop windows, the structure, configuration, and debug
viewport. Changing the group set or any candidate model, prompt, credential,
or Skill configuration makes the previous comparison Session read-only.
Studio keeps the old transcript visible until the user confirms a new Session,
then starts all valid groups with one new comparison ID without replaying prior
inputs. If any group fails to start, Studio cleans the newly staged runs and
preserves the previous evidence. On narrow desktop windows, the structure,
configuration, and debug
panels stack vertically instead of squeezing the form. The deployment page
pairs an inspectable Agent topology with a vertically aligned action rail for
YAML export, source download, and the code browser/editor dialog, while keeping
Expand All @@ -145,6 +157,14 @@ server that `veadk frontend` launches — no separate backend.
its generated internal proxy mounts AgentKit A2A center agents dynamically
from the center ID, recall count, region, and OpenAPI endpoint. Remote names,
descriptions, and capabilities come from the returned Agent Cards.

Custom model endpoints used by comparison debugging require an endpoint-specific
temporary API Key and an exact server-side hostname allowlist. Studio operators
configure comma-separated hostnames with
`VEADK_STUDIO_DEBUG_MODEL_HOST_ALLOWLIST`; URLs, wildcards, and inherited
subdomains are not accepted. The endpoint must still use HTTPS and resolve to a
public address.

- **Code-package deployment**: upload a ZIP project from the add-Agent menu,
inspect or edit its files in the existing code browser, then choose the
region and public/VPC network before deploying it to AgentKit. The package
Expand Down Expand Up @@ -432,6 +452,38 @@ Deleting a draft attachment deletes its object. Deleting a session deletes all
media scoped to that session from either backend. Because `/tmp` may be cleared
at any time, use TOS when attachments must survive process or host replacement.

## Multi-scheme debug comparison

The Custom Agent editor can compare its read-only baseline with up to three
candidate schemes in one debug workspace. A candidate may change multiple
Agents and dimensions together. The supported dimensions are system prompt,
model, and skills. Model changes treat Model ID, provider, and API Base as one
atomic configuration.

Model API keys are temporary debug credentials. A key entered while creating a
Custom Agent may be left blank so the Studio server resolves its configured
credential. A candidate with a custom API Base must provide a temporary key.
The browser keeps these keys outside the persisted draft and comparison
history; the server injects them only into the selected debug environment.
Applying a candidate copies configuration and in-memory credential state, but
never writes a key into generated source, the Draft, or local storage.

All running schemes receive the same initial text input. Interactive A2UI
actions are broadcast only when every running scheme exposes the same action;
otherwise the affected runs are marked as input-diverged. Changing any scheme
makes the previous Session read-only. Starting a new Session requires explicit
confirmation, starts all schemes atomically, and never replays prior inputs. If
any environment fails to start, Studio cleans the newly staged runs and keeps
the previous Session evidence available.

The workspace reports first visible text latency, total latency, completed tool
calls, and total tokens. Cost is shown as unavailable until a shared accounting
contract exists. Trace alignment uses exact invocation or tool-call identifiers
and leaves unmatched events explicit. Human verdicts and reasons are stored in
the local comparison record; applying a candidate requires an explicit
`Adopt candidate` verdict. Scenario evaluation remains disabled until its API is
available.

## Skills and sub-agents

Type `/` in the composer to search skills mounted on the selected agent. Type
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/a2ui/Surface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,18 @@ function Fallback({ node }: { node: A2uiComponent }) {
export interface SurfaceViewProps {
surface: SurfaceState;
onAction: (action: A2uiAction | undefined, node: A2uiComponent) => void;
readOnly?: boolean;
}

/** Render a single surface starting at its root component. */
export function SurfaceView({ surface, onAction }: SurfaceViewProps) {
export function SurfaceView({
surface,
onAction,
readOnly = false,
}: SurfaceViewProps) {
const ctx: RenderContext = {
surface,
readOnly,
resolve: (v: DynamicValue) => resolveValue(v, surface.dataModel),
resolveString: (v: DynamicValue) => resolveStr(v, surface.dataModel),
dispatchAction: onAction,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/a2ui/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export function Button({ node, ctx }: ComponentRendererProps) {
className={`a2ui-button a2ui-button--${variant}`}
data-a2ui-id={node.id}
data-a2ui-component={node.component}
disabled={ctx.readOnly}
onClick={() => ctx.dispatchAction(node.action as A2uiAction | undefined, node)}
>
{ctx.render(node.child as string)}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/a2ui/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { A2uiAction, A2uiComponent, DynamicValue, SurfaceState } from "./ty

export interface RenderContext {
surface: SurfaceState;
readOnly: boolean;
/** Render a child component by id. */
render: (id: string | undefined) => ReactNode;
/** Resolve a dynamic value against the surface data model. */
Expand Down
12 changes: 9 additions & 3 deletions frontend/src/adk/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2959,15 +2959,21 @@ export async function generateAgentDraftFromRequirement(

export async function createGeneratedAgentTestRun(
draft: AgentDraft,
runtime?: { runtimeId: string; region: string },
options: {
runtime?: { runtimeId: string; region: string };
modelCredentials?: Array<{ agentPath: number[]; apiKey: string }>;
comparisonId?: string;
} = {},
): Promise<GeneratedAgentTestRun> {
const res = await apiFetch("/web/generated-agent-test-runs", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
draft,
runtimeId: runtime?.runtimeId,
runtimeRegion: runtime?.region,
runtimeId: options.runtime?.runtimeId,
runtimeRegion: options.runtime?.region,
modelCredentials: options.modelCredentials ?? [],
comparisonId: options.comparisonId ?? "",
}),
});
if (!res.ok) {
Expand Down
Loading