fix: preserve code agent sessions after turn failures
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
- AgentRun v0.1 接入只使用标准 `threadId` 路径:`POST /v1/agent/chat` 收到的 `conversationId/sessionId/threadId` 必须写入 AgentRun command `payload.threadId` 和 `SessionRef.threadId`;协议字段、trace、result 和 conversation facts 都以该字段为唯一 thread identity。
|
||||
- AgentRun run 级 events 写回 HWLAB trace 时必须按当前 `commandId` 归属过滤;同一 run 的旧 command 尾部事件不能混入后续 command trace。取消、失败或 blocked 轮次如果已有 assistant/tool 可读进展,必须以脱敏、限长的 conversation facts 写入 UI/trace/inspect 证据,供后续 `inspect`/`--from-trace` 可见性使用;这些 facts 不得作为下一轮模型上下文或 prompt 拼接来源。
|
||||
- AgentRun completed 轮次续接必须依赖 Codex stdio 原生 session continuation。Cloud API 只把本轮原始 `message/prompt` 和标准 `conversationId/sessionId/threadId` 写入 AgentRun command payload 与 `SessionRef`;不得从请求、account workspace 或 account conversation 生成 `conversationContext`,不得把历史消息拼入 prompt,也不得把请求体里的 `conversationContext/messages` 当作模型上下文。历史 conversation facts 只用于 UI、inspect、trace 和 `--from-trace` 的可见性证据;收到 synthetic context 字段时只能记录 ignored trace 并剥离。`thread/resume` 失败时按 AgentRun `thread-resume-failed` 终止本轮。
|
||||
- Code Agent 不允许存在 turn/session/conversation 总时长 timeout;只允许无新 app-server 响应、无 notification、无 assistant/tool/event activity 的 idle timeout。AgentRun command 失败、provider 失败或 idle timeout 只终结当前 command,不能把同一 conversation/session/thread 的后续 turn 截断;除显式取消、中断、过期或 thread-resume-failed stale 指针清理外,后续消息必须继续使用原 session/thread。
|
||||
- Cloud API 通过 AgentRun v0.1 `runner-jobs.transientEnv` 传递本次 Code Agent turn 的短期上下文,例如 `HWLAB_RUNTIME_*`、`HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME` 和 `HWLAB_DEVICE_POD_API_KEY`。`transientEnv` 不设固定 8 项上限,新增短期上下文时必须按 name 去重、只传本次 Job 需要的 value;`HWLAB_RUNTIME_API_URL` 必须指向当前 namespace 内的 `hwlab-cloud-api` Service,`HWLAB_RUNTIME_WEB_URL` 才指向 `hwlab-cloud-web`;`HWLAB_DEVICE_POD_API_KEY` 只能作为 assembled runner 内 `hwpod` 访问正式 device-pod 的统一授权,必须标记 sensitive,并继续禁止承载 GitHub token、provider key、长期 SSH key 或其他可复用 credential;文档、日志和 trace 只允许保留脱敏后的 name、来源或摘要,不打印 Secret 值。
|
||||
- 同 Pod sidecar `hwlab-codex-api-forwarder` 监听 `127.0.0.1:49280/responses`,用于 `codex-api` profile 直连 hyueapi,并保持 hyueapi 在 `NO_PROXY` 中。
|
||||
- `hwlab-code-agent-workspace` PVC 挂载到 `/workspace/hwlab`,用于长会话 workspace;它是 cloud-api 运行资源,不是独立用户入口。
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
- 同一 conversation/session 的后续用户消息必须在 AgentRun runner reuse window 有效时复用已存在的 AgentRun run/runner 继续新 command/turn;只有 runner 不可用、已过期或协议明确要求新 runner 时才重新 bundle 和启动 runner。每条消息都重新 bundle/runner 属于 v0.2 AgentRun 接入缺口,不能只靠 trace 显示原因当成已完成。
|
||||
- AgentRun 会话连续性只有一个标准路径:Cloud Web/CLI 提交的 `threadId` 必须经 Cloud API adapter 写入 AgentRun command `payload.threadId` 和 `SessionRef.threadId`。前端、CLI、API 和 AgentRun 的协议字段、trace、result 和 conversation facts 都以该字段为唯一 thread identity。
|
||||
- Cloud Web 提交 Code Agent turn 时只发送当前用户消息、共享 workspace 的 `conversationId/sessionId/threadId`、workspace revision 和必要运行元数据;不得发送 `conversationContext/messages`,也不得把浏览器历史拼入 prompt。历史消息只用于本地 UI 展示和 trace/inspect 可见性,不能替代 AgentRun/Codex stdio 原生 `thread/resume`。
|
||||
- Workbench 不允许把 Code Agent 长任务总耗时当成失败条件;只能在无新 trace/event/activity 的 idle timeout 后显示超时。AgentRun command/provider/backend 失败后,当前消息可显示 failed/blocker,但 conversation/session/thread 仍是后续 turn 的默认 continuation,不能要求用户重新建 session;显式取消、中断、过期和 thread-resume-failed stale 指针清理除外。
|
||||
- 同一 AgentRun run 复用多条 command 时,Web trace 展示只显示当前 command 归属事件和必要 run 级状态;旧 command 的 assistant/tool/terminal 尾部不能堆到新 command 末尾。取消轮次的可读进展必须作为脱敏 conversation facts 进入 UI/trace/inspect 证据,而不是靠旧 trace 尾部串线让后续轮次“碰巧看到”;这些 facts 不得作为下一轮模型上下文或 prompt 拼接来源。
|
||||
|
||||
## 内部架构
|
||||
|
||||
@@ -1854,6 +1854,7 @@ function terminalWorkbenchSessionStatus(result = {}) {
|
||||
const status = textOr(result.status ?? result.agentRun?.terminalStatus, "").toLowerCase();
|
||||
if (status === "completed") return "idle";
|
||||
if (status === "cancelled") return "canceled";
|
||||
if (status === "failed" && result.agentRun && result.reuseEligible !== false && result.agentRun.reuseEligible !== false) return "idle";
|
||||
return status || "idle";
|
||||
}
|
||||
function canActorReadWorkspaceTrace(result = {}, actor = null) {
|
||||
|
||||
@@ -907,13 +907,14 @@ function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId })
|
||||
valuesPrinted: false
|
||||
});
|
||||
const partialContext = partialAgentRunContext(runnerTrace);
|
||||
const recoverableFailure = !canceled;
|
||||
return {
|
||||
...base,
|
||||
status: canceled ? "canceled" : "failed",
|
||||
canceled,
|
||||
updatedAt: now,
|
||||
session: agentRunSessionSummary(base, canceled ? "canceled" : "failed"),
|
||||
sessionReuse: agentRunSessionReuseSummary(base, base.agentRun.reused === true),
|
||||
session: agentRunSessionSummary(base, canceled ? "canceled" : "idle"),
|
||||
sessionReuse: agentRunSessionReuseSummary(base, base.agentRun.reused === true, { status: recoverableFailure ? "available-after-failed-command" : undefined }),
|
||||
runner: agentRunRunnerSummary(base.agentRun),
|
||||
runnerTrace: partialContext ? { ...runnerTrace, partialContext } : runnerTrace,
|
||||
toolCalls: agentRunToolCalls(result, canceled ? "canceled" : "failed"),
|
||||
@@ -940,7 +941,8 @@ function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId })
|
||||
route: "/v1/agent/chat",
|
||||
toolName: "agentrun.manual-dispatch"
|
||||
},
|
||||
agentRun: { ...base.agentRun, terminalStatus, completed: false, providerTrace, valuesPrinted: false },
|
||||
agentRun: { ...base.agentRun, terminalStatus, completed: false, reuseEligible: recoverableFailure, providerTrace, valuesPrinted: false },
|
||||
reuseEligible: recoverableFailure,
|
||||
...(partialContext ? { partialContext } : {}),
|
||||
valuesPrinted: false
|
||||
};
|
||||
@@ -1318,13 +1320,13 @@ function agentRunSessionSummary(base, status) {
|
||||
};
|
||||
}
|
||||
|
||||
function agentRunSessionReuseSummary(base, reused) {
|
||||
function agentRunSessionReuseSummary(base, reused, options = {}) {
|
||||
return {
|
||||
sessionId: base.sessionId ?? base.agentRun?.sessionId ?? null,
|
||||
conversationId: base.conversationId ?? base.agentRun?.conversationId ?? null,
|
||||
threadId: base.threadId ?? base.agentRun?.threadId ?? null,
|
||||
reused: Boolean(reused),
|
||||
status: reused ? "reused" : "new",
|
||||
status: options.status ?? (reused ? "reused" : "new"),
|
||||
runnerReused: Boolean(reused),
|
||||
valuesRedacted: true
|
||||
};
|
||||
|
||||
@@ -1324,9 +1324,9 @@ function errorTaxonomy(code, error = {}) {
|
||||
},
|
||||
session_failed: {
|
||||
layer: "session",
|
||||
category: "session_blocked",
|
||||
category: "runner_blocked",
|
||||
retryable: true,
|
||||
userMessage: "Code Agent session 已失败,可重新发送建立新的 session。"
|
||||
userMessage: "Code Agent 当前 turn 已失败,session/thread 已保留,可继续发送下一条消息。"
|
||||
},
|
||||
session_interrupted: {
|
||||
layer: "session",
|
||||
@@ -1344,13 +1344,13 @@ function errorTaxonomy(code, error = {}) {
|
||||
layer: "session",
|
||||
category: "timeout",
|
||||
retryable: true,
|
||||
userMessage: "Code Agent session 已超时,输入和 trace 已保留,可重试上一条消息。"
|
||||
userMessage: "Code Agent 当前 turn 已超时,输入、trace、session/thread 已保留,可继续发送下一条消息。"
|
||||
},
|
||||
session_error: {
|
||||
layer: "session",
|
||||
category: "session_blocked",
|
||||
category: "runner_blocked",
|
||||
retryable: true,
|
||||
userMessage: "Code Agent session 返回错误,输入和 trace 已保留,可重试上一条消息。"
|
||||
userMessage: "Code Agent 当前 turn 返回错误,输入、trace、session/thread 已保留,可继续发送下一条消息。"
|
||||
},
|
||||
runner_unavailable: {
|
||||
layer: "runner",
|
||||
|
||||
@@ -18,7 +18,7 @@ export const CODE_AGENT_SESSION_STATUS_ALIASES = Object.freeze({
|
||||
});
|
||||
|
||||
const ACTIVE_LIFECYCLE_STATUSES = new Set(["creating", "ready", "busy", "idle"]);
|
||||
const TERMINAL_LIFECYCLE_STATUSES = new Set(["interrupted", "expired", "failed"]);
|
||||
const TERMINAL_LIFECYCLE_STATUSES = new Set(["interrupted", "expired"]);
|
||||
|
||||
export function normalizeCodeAgentSessionLifecycleStatus(value, { fallback = null } = {}) {
|
||||
const text = String(value ?? "").trim().toLowerCase();
|
||||
@@ -75,7 +75,7 @@ export function codeAgentSessionLifecycleSummary(input = {}) {
|
||||
(gateBlocked && !concreteLifecycleBlock);
|
||||
if ((fallbackUsed || degraded) && !hasSession) status = "failed";
|
||||
const requiresNewSession = status ? TERMINAL_LIFECYCLE_STATUSES.has(status) : degraded || fallbackUsed;
|
||||
const reusable = status ? ACTIVE_LIFECYCLE_STATUSES.has(status) && status !== "creating" && degraded !== true : false;
|
||||
const reusable = status ? ((ACTIVE_LIFECYCLE_STATUSES.has(status) && status !== "creating") || status === "failed") && degraded !== true : false;
|
||||
const newSession = reused === false || (hasSession && !reused && !requiresNewSession);
|
||||
const reasonCode = firstText(
|
||||
input.degradationReasonCode,
|
||||
@@ -171,8 +171,8 @@ function lifecyclePresentation({ status, rawStatus, reused, newSession, degraded
|
||||
if (status === "failed") {
|
||||
return {
|
||||
label: "会话失败",
|
||||
action: "new_session",
|
||||
userMessage: "当前 Code Agent session 已失败,需重新发送建立新会话。"
|
||||
action: "continue",
|
||||
userMessage: "当前 Code Agent turn 已失败;session/thread 已保留,可继续发送下一条消息。"
|
||||
};
|
||||
}
|
||||
if (status === "interrupted") {
|
||||
|
||||
@@ -326,7 +326,7 @@ test("code agent session registry reports busy sessions as structured blocker",
|
||||
assert.equal(busy.session.currentTraceId, "trc_busy_1");
|
||||
});
|
||||
|
||||
test("code agent session registry blocks failed and interrupted sessions without reusing them", () => {
|
||||
test("code agent session registry resumes failed sessions and still blocks interrupted sessions", () => {
|
||||
const failedRegistry = createCodeAgentSessionRegistry({
|
||||
idFactory: () => "ses_failed"
|
||||
});
|
||||
@@ -348,12 +348,14 @@ test("code agent session registry blocks failed and interrupted sessions without
|
||||
traceId: "trc_failed_2",
|
||||
now: "2026-05-23T00:00:01.000Z"
|
||||
});
|
||||
assert.equal(failed.ok, false);
|
||||
assert.equal(failed.code, "session_failed");
|
||||
assert.equal(failed.session.status, "failed");
|
||||
assert.equal(failed.session.lifecycleStatus, "failed");
|
||||
assert.match(failed.session.lifecycle.userMessage, /需重新发送建立新会话/u);
|
||||
assert.equal(failed.blocker.sourceIssue, "pikasTech/HWLAB#317");
|
||||
assert.equal(failed.ok, true);
|
||||
assert.equal(failed.reused, true);
|
||||
assert.equal(failed.session.sessionId, "ses_failed");
|
||||
assert.equal(failed.session.status, "busy");
|
||||
assert.equal(failed.session.lifecycleStatus, "busy");
|
||||
assert.equal(failed.session.currentTraceId, "trc_failed_2");
|
||||
assert.equal(failed.session.lastTraceId, "trc_failed_2");
|
||||
assert.equal(failed.session.turn, 2);
|
||||
|
||||
const interruptedRegistry = createCodeAgentSessionRegistry({
|
||||
idFactory: () => "ses_interrupted"
|
||||
@@ -493,7 +495,7 @@ test("stale read-only conversation facts do not mask Codex stdio blockers", asyn
|
||||
assert.equal(payload.provider === "openai-responses", false);
|
||||
});
|
||||
|
||||
test("expired, busy, and failed session states are surfaced as structured blockers", async () => {
|
||||
test("expired and busy sessions are blockers while failed sessions can resume", async () => {
|
||||
const expiredRegistry = createCodeAgentSessionRegistry({
|
||||
idleTimeoutMs: 10,
|
||||
idFactory: () => "ses_expired_chat"
|
||||
@@ -646,25 +648,16 @@ test("expired, busy, and failed session states are surfaced as structured blocke
|
||||
traceId: "trc_failed_request",
|
||||
now: "2026-05-23T00:04:01.000Z"
|
||||
});
|
||||
const failed = codeAgentBlockedPayloadFromAcquire(failedAcquire, {
|
||||
conversationId: "cnv_failed_chat",
|
||||
traceId: "trc_failed_request",
|
||||
conversationFacts: failedRegistry.getConversationFacts("cnv_failed_chat")
|
||||
});
|
||||
assert.equal(failed.status, "failed");
|
||||
assert.equal(failed.error.code, "session_failed");
|
||||
assert.equal(failed.error.layer, "session");
|
||||
assert.equal(failed.error.retryable, true);
|
||||
assert.match(failed.error.userMessage, /session 已失败/u);
|
||||
assert.equal(failed.error.blocker.code, "session_failed");
|
||||
assert.equal(failed.session.status, "failed");
|
||||
assert.equal(failed.sessionLifecycleStatus, "failed");
|
||||
assert.equal(failed.sessionSummary.requiresNewSession, true);
|
||||
assert.match(failed.sessionSummary.userMessage, /需重新发送建立新会话/u);
|
||||
assert.equal(failed.capabilityLevel, "blocked");
|
||||
assert.equal(failed.conversationFacts.conversationId, "cnv_failed_chat");
|
||||
assert.equal(failed.conversationFacts.sessionId, "ses_failed_chat");
|
||||
assert.equal(failed.conversationFacts.recentToolCalls[0].name, "pwd");
|
||||
assert.equal(failedAcquire.ok, true);
|
||||
assert.equal(failedAcquire.reused, true);
|
||||
assert.equal(failedAcquire.session.sessionId, "ses_failed_chat");
|
||||
assert.equal(failedAcquire.session.status, "busy");
|
||||
assert.equal(failedAcquire.session.lifecycleStatus, "busy");
|
||||
assert.equal(failedAcquire.session.currentTraceId, "trc_failed_request");
|
||||
const failedFacts = failedRegistry.getConversationFacts("cnv_failed_chat");
|
||||
assert.equal(failedFacts.conversationId, "cnv_failed_chat");
|
||||
assert.equal(failedFacts.sessionId, "ses_failed_chat");
|
||||
assert.equal(failedFacts.recentToolCalls[0].name, "pwd");
|
||||
});
|
||||
|
||||
test("OpenAI fallback hook is not used when Codex stdio is unavailable", async () => {
|
||||
@@ -1196,7 +1189,7 @@ function codeAgentBlockedPayloadFromAcquire(acquireResult, { conversationId, tra
|
||||
? "Code Agent session 正在处理上一轮请求,请稍后重试。"
|
||||
: code === "session_expired"
|
||||
? "Code Agent session 已过期,可重新发送建立新的 session。"
|
||||
: "Code Agent session 已失败,可重新发送建立新的 session。";
|
||||
: "Code Agent 当前 turn 已失败,session/thread 已保留,可继续发送下一条消息。";
|
||||
const sessionSummary = acquireResult.session.lifecycle;
|
||||
const blocker = {
|
||||
code,
|
||||
|
||||
@@ -84,7 +84,7 @@ export function createCodeAgentSessionRegistry(options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
if (session && ["failed", "interrupted", "timeout", "error", "canceled"].includes(session.status)) {
|
||||
if (session && ["interrupted", "canceled"].includes(session.status)) {
|
||||
session.updatedAt = timestamp;
|
||||
session.lastTraceId = optionalId(params.traceId) ?? session.lastTraceId;
|
||||
return blockedAcquire({
|
||||
|
||||
@@ -644,6 +644,11 @@ test("cloud api AgentRun adapter exposes invalid tool-call attribution in result
|
||||
assert.match(payload.error.userMessage, /不是 HWLAB device-pod/u);
|
||||
assert.equal(payload.blocker.category, "provider_invalid_tool_call");
|
||||
assert.match(payload.blocker.summary, /invalid function arguments json string/u);
|
||||
assert.equal(payload.session.status, "idle");
|
||||
assert.equal(payload.sessionReuse.status, "available-after-failed-command");
|
||||
assert.equal(payload.sessionReuse.threadId, "thread_invalid_tool");
|
||||
assert.equal(payload.agentRun.reuseEligible, true);
|
||||
assert.equal(payload.reuseEligible, true);
|
||||
} finally {
|
||||
await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
|
||||
await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve())));
|
||||
|
||||
@@ -502,7 +502,7 @@ export async function handleCodeAgentChatResultHttp(request, response, url, opti
|
||||
}
|
||||
if (synced.result && synced.result.status !== "running") {
|
||||
recordCodeAgentConversationFact(synced.result, options);
|
||||
await recordCodeAgentSessionOwner({ payload: synced.result, params: synced.result, options, status: synced.result.status === "completed" ? "active" : synced.result.status });
|
||||
await recordCodeAgentSessionOwner({ payload: synced.result, params: synced.result, options, status: codeAgentOwnerStatusForResult(synced.result) });
|
||||
sendJson(response, 200, compactCodeAgentChatResultPayload(synced.result, options));
|
||||
return;
|
||||
}
|
||||
@@ -1057,6 +1057,13 @@ async function recordCodeAgentSessionOwner({ payload = {}, params = {}, options
|
||||
}
|
||||
}
|
||||
|
||||
function codeAgentOwnerStatusForResult(result = {}) {
|
||||
if (result?.status === "completed") return "active";
|
||||
if (result?.status === "canceled" || result?.status === "cancelled") return "canceled";
|
||||
if (result?.agentRun && result?.reuseEligible !== false && result?.agentRun?.reuseEligible !== false) return "active";
|
||||
return result?.status ?? "active";
|
||||
}
|
||||
|
||||
async function claimWorkbenchWorkspaceTurn({ params = {}, options = {}, traceId, response } = {}) {
|
||||
const workspaceId = safeWorkspaceId(params.workspaceId);
|
||||
if (!workspaceId || !options.accessController?.store || !options.actor) return null;
|
||||
|
||||
@@ -764,11 +764,19 @@ function agentFailurePresentation(error, { result = null, traceId = null } = {})
|
||||
};
|
||||
}
|
||||
|
||||
if (["session_reuse_conflict", "session_failed", "session_interrupted", "session_canceled", "session_timeout", "session_error"].includes(code)) {
|
||||
if (["session_failed", "session_timeout", "session_error"].includes(code)) {
|
||||
return {
|
||||
category: "runner_blocked",
|
||||
title: "Code Agent 当前轮次失败",
|
||||
text: `当前 Code Agent turn 失败、超时或返回错误;输入、sessionId 和 traceId 已保留,可继续发送下一条消息。${traceSuffix}`
|
||||
};
|
||||
}
|
||||
|
||||
if (["session_reuse_conflict", "session_interrupted", "session_canceled"].includes(code)) {
|
||||
return {
|
||||
category: "session_blocked",
|
||||
title: "Code Agent Session 受阻",
|
||||
text: `当前 session 已过期、失败、中断或与 conversation 绑定不一致;输入已保留,可重新发送建立新的 Codex stdio session。${traceSuffix}`
|
||||
text: `当前 session 已中断、取消或与 conversation 绑定不一致;输入已保留,可重新发送建立新的 Codex stdio session。${traceSuffix}`
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -522,12 +522,14 @@ function failedSessionContinuity(pending = null, error = null) {
|
||||
: code === "session_expired"
|
||||
? "会话已过期"
|
||||
: ["session_failed", "session_error", "session_timeout"].includes(code)
|
||||
? "需新建会话"
|
||||
? "可继续会话"
|
||||
: "会话受阻";
|
||||
const summary = code === "session_busy"
|
||||
? "上一轮仍在处理;输入已保留,请等待当前请求完成或取消后重试。"
|
||||
: code === "session_expired"
|
||||
? "当前 session 已过期;输入已保留,可重新发送建立新的 Code Agent session。"
|
||||
: ["session_failed", "session_error", "session_timeout"].includes(code)
|
||||
? "当前 Code Agent turn 失败、超时或返回错误;session/thread 已保留,可继续发送下一条消息。"
|
||||
: `本次请求没有完成可复用会话确认;输入已保留。${error?.code ? ` code=${error.code}` : ""}`;
|
||||
return {
|
||||
...base,
|
||||
|
||||
@@ -413,10 +413,10 @@ test("surfaces expired and failed Code Agent session lifecycle hints", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
assert.equal(failed.kind, "session-failed");
|
||||
assert.equal(failed.label, "会话失败");
|
||||
assert.equal(failed.kind, "turn-failed-session-continuable");
|
||||
assert.equal(failed.label, "当前轮次失败");
|
||||
assert.equal(failed.sessionLifecycleStatus, "failed");
|
||||
assert.match(failed.sessionLifecycleHint, /需重新发送建立新会话/u);
|
||||
assert.match(failed.sessionLifecycleHint, /session\/thread 已保留/u);
|
||||
});
|
||||
|
||||
test("missing Code Agent session evidence is shown as lifecycle degradation", () => {
|
||||
|
||||
@@ -175,7 +175,7 @@ function classifyPayload(payload, blockers) {
|
||||
}
|
||||
|
||||
if (lifecycle.status === "failed" && (payload.session?.status === "failed" || payload.sessionLifecycle?.status === "failed" || payload.sessionSummary?.status === "failed") && !isFallbackTextChatOnly(payload)) {
|
||||
return status("session-failed", "会话失败", "blocked", "×", lifecycle.hint || "当前 Code Agent session 已失败,需重新发送建立新会话。");
|
||||
return status("turn-failed-session-continuable", "当前轮次失败", "warn", "!", lifecycle.hint || "当前 Code Agent turn 已失败;session/thread 已保留,可继续发送下一条消息。");
|
||||
}
|
||||
|
||||
if (lifecycle.status === "interrupted" && !["canceled", "timeout", "error"].includes(payload.status)) {
|
||||
@@ -453,7 +453,7 @@ function sessionLifecycleSummary(payload = {}) {
|
||||
return { status, label: "会话已过期", hint: "会话已过期,可重新发送建立新的 Code Agent session。" };
|
||||
}
|
||||
if (status === "failed") {
|
||||
return { status, label: "会话失败", hint: "当前 Code Agent session 已失败,需重新发送建立新会话。" };
|
||||
return { status, label: "会话失败", hint: "当前 Code Agent turn 已失败;session/thread 已保留,可继续发送下一条消息。" };
|
||||
}
|
||||
if (status === "interrupted") {
|
||||
return { status, label: "会话已中断", hint: "当前 Code Agent session 已中断;输入和 trace 已保留,可重试或重新发送。" };
|
||||
|
||||
Reference in New Issue
Block a user