Skip to content
Merged
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
13 changes: 13 additions & 0 deletions .github/workflows/harness-sidecar-release-gate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ on:
- 'frontend/**'
- 'tests/cli/test_generated_agent_harness_sidecar.py'
- 'tests/cli/test_managed_sidecar_source.py'
- 'tests/cli/test_legacy_runtime_recovery.py'
- 'tests/cli/test_generated_agent_backend_codegen_extended.py'
- 'tests/cli/test_studio_rbac.py'
- 'tests/extensions/harness/**'
- 'tests/integrations/agentkit/test_app.py'
- 'veadk/cli/generated_agent_codegen.py'
- 'veadk/cli/cli_frontend.py'
- 'veadk/cli/managed_sidecar_source.py'
- 'veadk/cli/legacy_runtime_recovery.py'
- 'veadk/extensions/harness/**'
- 'veadk/integrations/agentkit/app.py'
- 'pyproject.toml'
Expand All @@ -24,13 +26,15 @@ on:
- 'frontend/**'
- 'tests/cli/test_generated_agent_harness_sidecar.py'
- 'tests/cli/test_managed_sidecar_source.py'
- 'tests/cli/test_legacy_runtime_recovery.py'
- 'tests/cli/test_generated_agent_backend_codegen_extended.py'
- 'tests/cli/test_studio_rbac.py'
- 'tests/extensions/harness/**'
- 'tests/integrations/agentkit/test_app.py'
- 'veadk/cli/generated_agent_codegen.py'
- 'veadk/cli/cli_frontend.py'
- 'veadk/cli/managed_sidecar_source.py'
- 'veadk/cli/legacy_runtime_recovery.py'
- 'veadk/extensions/harness/**'
- 'veadk/integrations/agentkit/app.py'
- 'pyproject.toml'
Expand Down Expand Up @@ -74,6 +78,15 @@ jobs:
--include='*/veadk/extensions/harness/sidecar.py' \
--fail-under=91 --show-missing

- name: Verify customer Studio Agent-update regressions
run: |
python -m pytest -q \
tests/cli/test_legacy_runtime_recovery.py::test_changed_unnamed_mcp_url_reuses_same_published_tool_slot \
tests/cli/test_legacy_runtime_recovery.py::test_changed_unnamed_mcp_url_rejects_moved_credential_slot
python -m pytest -q \
tests/cli/test_studio_rbac.py::test_sidecar_update_resolves_or_explicitly_reuses_stored_mcp_credentials \
-k changed-unnamed-explicit-reuse

- name: Install frontend dependencies
working-directory: frontend
run: npm ci --ignore-scripts
Expand Down
21 changes: 17 additions & 4 deletions frontend/src/adk/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import {
} from "./runtimeLogs";
import { parseSSE } from "./sse";
import { normalizeRuntimeDescription } from "./runtimeDescription";
import {
DeploymentStatusUnconfirmedError,
isDeploymentAbortError,
isDeploymentStatusUnconfirmedError,
} from "./deploymentStatus";
import {
DEFAULT_REQUEST_TIMEOUT_MS,
requestSignal,
Expand Down Expand Up @@ -3605,7 +3610,8 @@ export async function deployAgentkitProject(
});
} catch (error) {
clearController();
throw error;
if (isDeploymentAbortError(error)) throw error;
throw new DeploymentStatusUnconfirmedError({ taskId, cause: error });
}
if (!res.ok) {
const detail = await httpErrorMessage(res, adkT("client.deploymentFailed"));
Expand All @@ -3625,12 +3631,19 @@ export async function deployAgentkitProject(
}
} catch (error) {
clearController();
throw error;
if (isDeploymentAbortError(error)) throw error;
throw new DeploymentStatusUnconfirmedError({ taskId, cause: error });
}
clearController();

if (!final) throw new Error(adkT("client.deploymentDisconnected"));
if (!final.success) throw new Error(final.error || adkT("client.deploymentFailed"));
if (!final) throw new DeploymentStatusUnconfirmedError({ taskId });
if (!final.success) {
const error = new Error(final.error || adkT("client.deploymentFailed"));
if (isDeploymentStatusUnconfirmedError(error)) {
throw new DeploymentStatusUnconfirmedError({ taskId, cause: error });
}
throw error;
}
if (!final.agentName) {
throw new Error(adkT("client.deploymentMissingAgentName"));
}
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/adk/deploymentStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const AMBIGUOUS_DEPLOYMENT_RESULT =
/RunPipeline result could not be reconciled|Polling build status failed/i;

export class DeploymentStatusUnconfirmedError extends Error {
readonly taskId?: string;

constructor({ taskId, cause }: { taskId?: string; cause?: unknown } = {}) {
super("The deployment request may still be running, but its status could not be confirmed.");
// The raw transport detail may contain upstream internals. Classification
// happens before wrapping, so deliberately do not expose or retain it.
void cause;
this.name = "DeploymentStatusUnconfirmedError";
this.taskId = taskId;
}
}

export function isDeploymentStatusUnconfirmedError(
error: unknown,
): error is DeploymentStatusUnconfirmedError {
if (error instanceof DeploymentStatusUnconfirmedError) return true;
const message = error instanceof Error ? error.message : String(error ?? "");
return AMBIGUOUS_DEPLOYMENT_RESULT.test(message);
}

export function isDeploymentAbortError(error: unknown): boolean {
return Boolean(
error &&
typeof error === "object" &&
"name" in error &&
error.name === "AbortError",
);
}
2 changes: 1 addition & 1 deletion frontend/src/i18n/resources/en-US/create.json
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@
"removeTool": "Remove MCP tool",
"namePlaceholder": "Name (optional)",
"urlPlaceholder": "MCP service URL",
"pathWarning": "This address has no path. Confirm that it is the complete MCP service URL.",
"pathWarning": "This address does not end with /mcp. Confirm that it is the complete MCP service URL.",
"configuredPlaceholder": "Authentication is stored securely",
"tokenPlaceholder": "Bearer token (optional)",
"changedUrlWarning": "The MCP address changed. Choose how to handle the stored authentication.",
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/i18n/resources/en-US/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@
},
"deployStatus": {
"running": "Deploying",
"unconfirmed": "Deployment status unconfirmed",
"success": "Deployment complete",
"error": "Deployment failed",
"cancelled": "Deployment cancelled"
Expand Down Expand Up @@ -1035,7 +1036,7 @@
"deployedNotConnected": "Deployed, not connected yet",
"cancelled": "Cancelled",
"cancelledHint": "Deployment was cancelled and the Runtime resources were requested for deletion.",
"buildStatusUnconfirmed": "Build status unconfirmed",
"deploymentStatusUnconfirmed": "Deployment status unconfirmed",
"deploymentFailed": "Deployment failed",
"buildFailedHint": "Image build failed. See the build log for details."
},
Expand Down Expand Up @@ -1093,8 +1094,7 @@
"runtimeNameExists": "This Runtime name already exists. Change it and try again.",
"deployedButGithubMountFailed": "Deployment succeeded, but enabling GitHub continuous delivery failed: {{message}}",
"deployedButGithubBindFailed": "Deployment succeeded, but binding GitHub failed: {{message}}",
"buildStatusUnconfirmed": "The build was submitted, but its final status could not be confirmed. Check the result in CodePipeline later to avoid a duplicate deployment.",
"buildStatusUnconfirmedWithDetail": "Build status unconfirmed: {{message}}",
"deploymentStatusUnconfirmed": "The connection was interrupted, so the final deployment status cannot be confirmed. The task may still be running in the cloud. Check the same task in AgentKit or CodePipeline and avoid a duplicate deployment.",
"failedAtStage": "{{action}} failed during {{stage}}: {{message}}",
"noAgentAtEndpoint": "Connected successfully, but no Agents were found at this endpoint (/list-apps is empty).",
"addAgent": "Failed to add Agent: {{message}}",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/i18n/resources/zh-CN/create.json
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@
"removeTool": "删除 MCP 工具",
"namePlaceholder": "名称(可选)",
"urlPlaceholder": "MCP 服务地址",
"pathWarning": "此地址没有路径,请确认它是完整的 MCP 服务地址。",
"pathWarning": "此地址未以 /mcp 结尾,请确认它是完整的 MCP 服务地址。",
"configuredPlaceholder": "已安全保存认证信息",
"tokenPlaceholder": "Bearer Token(可选)",
"changedUrlWarning": "MCP 地址已变化,请确认如何处理已保存的认证信息。",
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/i18n/resources/zh-CN/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@
},
"deployStatus": {
"running": "正在部署",
"unconfirmed": "部署状态待确认",
"success": "部署完成",
"error": "部署失败",
"cancelled": "部署已取消"
Expand Down Expand Up @@ -1035,7 +1036,7 @@
"deployedNotConnected": "部署完成,暂未连接",
"cancelled": "已取消",
"cancelledHint": "部署已取消,相关 Runtime 资源已请求销毁。",
"buildStatusUnconfirmed": "构建状态待确认",
"deploymentStatusUnconfirmed": "部署状态待确认",
"deploymentFailed": "部署失败",
"buildFailedHint": "构建镜像失败,详见构建日志。"
},
Expand Down Expand Up @@ -1093,8 +1094,7 @@
"runtimeNameExists": "Runtime 名称已存在,请修改后重试。",
"deployedButGithubMountFailed": "部署成功,但挂载 GitHub 持续交付失败:{{message}}",
"deployedButGithubBindFailed": "部署成功,但绑定 GitHub 失败:{{message}}",
"buildStatusUnconfirmed": "构建任务已经提交,但暂时无法确认最终状态。请稍后在 Code Pipeline 查看构建结果,避免重复部署。",
"buildStatusUnconfirmedWithDetail": "构建状态待确认:{{message}}",
"deploymentStatusUnconfirmed": "连接已中断,当前无法确认部署最终状态。任务可能仍在云端运行,请到 AgentKit 或 Code Pipeline 查看同一任务,避免重复部署。",
"failedAtStage": "{{action}}失败({{stage}}阶段):{{message}}",
"noAgentAtEndpoint": "连接成功,但该地址未发现任何 Agent(/list-apps 为空)。",
"addAgent": "添加 Agent 失败:{{message}}",
Expand Down
28 changes: 18 additions & 10 deletions frontend/src/ui/AgentWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -928,13 +928,15 @@ function DeploymentProgressCard({
const progress = task.status === "success"
? 100
: Math.max(6, Math.min(100, task.pct ?? 6));
const title = task.status === "running"
? t("agentWorkspace.deployStatus.running")
: task.status === "success"
? t("agentWorkspace.deployStatus.success")
: task.status === "error"
? t("agentWorkspace.deployStatus.error")
: t("agentWorkspace.deployStatus.cancelled");
const title = task.status === "running" && task.statusUnconfirmed
? t("agentWorkspace.deployStatus.unconfirmed")
: task.status === "running"
? t("agentWorkspace.deployStatus.running")
: task.status === "success"
? t("agentWorkspace.deployStatus.success")
: task.status === "error"
? t("agentWorkspace.deployStatus.error")
: t("agentWorkspace.deployStatus.cancelled");

return (
<section
Expand All @@ -944,7 +946,9 @@ function DeploymentProgressCard({
<div className="aw-deploy-progress-head">
<div>
<span className="aw-deploy-progress-icon" aria-hidden>
{task.status === "running" ? (
{task.status === "running" && task.statusUnconfirmed ? (
<CircleAlert />
) : task.status === "running" ? (
<Loader2 className="spin" />
) : task.status === "success" ? (
<CircleCheck />
Expand All @@ -959,7 +963,11 @@ function DeploymentProgressCard({
<p>{task.runtimeName}</p>
</div>
</div>
<strong>{task.status === "running" ? `${Math.round(progress)}%` : task.label}</strong>
<strong>
{task.status === "running" && !task.statusUnconfirmed
? `${Math.round(progress)}%`
: task.label}
</strong>
</div>

<div
Expand Down Expand Up @@ -989,7 +997,7 @@ function DeploymentProgressCard({
{status === "done" ? (
<Check />
) : status === "active" ? (
<Loader2 className="spin" />
task.statusUnconfirmed ? <CircleAlert /> : <Loader2 className="spin" />
) : status === "failed" ? (
<CircleX />
) : (
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/ui/ProjectPreview.css
Original file line number Diff line number Diff line change
Expand Up @@ -2178,6 +2178,20 @@
line-height: 1.5;
}

.pp-status-unconfirmed {
display: flex;
flex-direction: column;
gap: 4px;
margin: 14px 18px;
padding: 10px 11px;
border: 1px solid hsl(42 90% 45% / 0.25);
border-radius: 5px;
background: hsl(42 90% 50% / 0.07);
color: hsl(35 82% 34%);
font-size: 12.5px;
line-height: 1.5;
}

.pp-deploy-result {
margin: 14px 18px 18px;
padding: 14px;
Expand Down
Loading
Loading