feat(app): add cancellable turn lifecycle

This commit is contained in:
Codex
2026-07-28 08:01:56 -04:00
parent 1f935a3206
commit 4831db1763
8 changed files with 786 additions and 55 deletions
+56
View File
@@ -0,0 +1,56 @@
import { describe, expect, it } from "vitest";
import {
describeConnectionDiagnostic,
describeConnectionFailure,
describeOperationFailure,
describeTurnFailure
} from "./errors";
describe("player-safe failure copy", () => {
it("offers an immediate retry only for retryable turn failures", () => {
const retryable = describeTurnFailure({
code: "provider_unavailable",
message: "upstream secret diagnostic",
retryable: true
});
const invalid = describeTurnFailure({
code: "invalid_input",
message: "private validation detail",
retryable: false
});
expect(retryable.retryable).toBe(true);
expect(retryable.noCommitMessage).toContain("没有写入故事");
expect(invalid.retryable).toBe(false);
});
it("does not echo provider or hidden-check diagnostics", () => {
const privateDetail = "roll=97 target=42 api_key=do-not-show";
const turn = describeTurnFailure({
code: "invalid_model_output",
message: privateDetail,
retryable: true
});
expect(JSON.stringify(turn)).not.toContain(privateDetail);
expect(describeOperationFailure({ message: privateDetail })).not.toContain(privateDetail);
expect(describeConnectionFailure({ message: privateDetail })).not.toContain(privateDetail);
expect(describeConnectionDiagnostic("VAULT_CREDENTIAL_NOT_FOUND")).not.toContain(privateDetail);
});
it("does not suggest blindly retrying a stale branch", () => {
const failure = describeTurnFailure({
code: "stale_node",
retryable: true
});
expect(failure.retryable).toBe(false);
expect(failure.message).toContain("重新打开");
});
it("uses connection diagnostic codes without displaying provider text", () => {
expect(describeConnectionDiagnostic("WAIT_TIMEOUT")).toContain("超时");
expect(describeConnectionDiagnostic("VAULT_CREDENTIAL_NOT_FOUND")).toContain("LAPP Vault");
});
});