Make English the repository working language (#5)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Yaowei Zheng
2026-07-20 17:45:02 +08:00
committed by GitHub
parent e80cde0ea6
commit 0eaaa54e25
136 changed files with 1515 additions and 1352 deletions
+32 -32
View File
@@ -121,23 +121,23 @@ describe("session-manager", () => {
db.close();
});
it("未知 Session → 404", async () => {
it("unknown Session → 404", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
const err = await manager.startTask("session-ghost", [userText("x")]).catch((e: unknown) => e);
expect((err as { status: number }).status).toBe(404);
});
it("startTask:先 publish 输入,驱动结束置回 idle 并推送 task_state", async () => {
it("startTask: publishes the input first; when driving ends it returns to idle and pushes task_state", async () => {
sessions.updateApprovalMode("session-1", "allow-all");
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
const events = capture("session-1");
const { sessionId } = await manager.startTask("session-1", [userText("你好")]);
const { sessionId } = await manager.startTask("session-1", [userText("hello")]);
expect(sessionId).toBe("session-1");
await waitFor(() => manager.statusOf("session-1") === "idle" && recorded.length >= 3);
// The first entry is the input message (visible to other subscribers), followed by task_state: running.
const first = JSON.parse(events[0]!.data) as { payload: { text: string } };
expect(first.payload.text).toBe("你好");
expect(first.payload.text).toBe("hello");
const states = serverEvents(events).filter((e) => e.type === "task_state");
expect(states.map((s) => s.state)).toEqual(["running", "idle"]);
// Outputs and events are forwarded one by one and handed to the recorder.
@@ -150,7 +150,7 @@ describe("session-manager", () => {
});
});
it("消息流里的 LLM / 工具失败经 drive 落库(source=llm / environment,带当前 Session 上下文)", async () => {
it("LLM / tool failures in the message stream are persisted via drive (source=llm / environment, with the current Session context)", async () => {
sessions.updateApprovalMode("session-1", "allow-all");
const captured: ErrorRecordArgs[] = [];
// core folds LLM / tool failures into the message stream (no throw): a tool
@@ -175,7 +175,7 @@ describe("session-manager", () => {
async *compact(): AsyncGenerator<OmniMessage> {},
};
const manager = makeManager(loaderOf(failing), { record: (args) => captured.push(args) });
await manager.startTask("session-1", [userText("跑")]);
await manager.startTask("session-1", [userText("run")]);
await waitFor(() => manager.statusOf("session-1") === "idle" && captured.length >= 2);
expect(captured.map((a) => [a.source, a.code, a.kind])).toEqual([
@@ -187,7 +187,7 @@ describe("session-manager", () => {
expect(String(captured[1]!.err)).toBe("llm request error: 500 upstream"); // the abort's real reason
});
it("互斥:运行中再次 startTask → 409 task_in_progress;compact → 409", async () => {
it("mutual exclusion: startTask again while running → 409 task_in_progress; compact → 409", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
await manager.startTask("session-1", [userText("go")]);
await waitFor(() => manager.pendingApprovalCount("session-1") === 1);
@@ -202,7 +202,7 @@ describe("session-manager", () => {
await waitFor(() => manager.statusOf("session-1") === "idle");
});
it("always-ask:登记未决审批并推送 approval_request;决定后继续", async () => {
it("always-ask: registers a pending approval and pushes approval_request; continues after the decision", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
const events = capture("session-1");
await manager.startTask("session-1", [userText("go")]);
@@ -225,7 +225,7 @@ describe("session-manager", () => {
expect(texts).toContain("decision=allow");
});
it("deny-all / read-only 自动判定(不转人工)", async () => {
it("deny-all / read-only decide automatically (no human handoff)", async () => {
sessions.updateApprovalMode("session-1", "deny-all");
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
await manager.startTask("session-1", [userText("go")]);
@@ -249,7 +249,7 @@ describe("session-manager", () => {
);
});
it("子会话(origin)登记:session_meta 落库,标题交模型据子会话自己的对话生成", async () => {
it("sub-session (origin) registration: session_meta persists; the title is left to the model using the sub-session's own conversation", async () => {
const fake: RuntimeSession = {
sessionId: "session-1",
toolPermission: () => "rw",
@@ -259,7 +259,7 @@ describe("session-manager", () => {
// The parent-level run_subagent call (no origin): its prompt becomes the sub-session title.
yield toolCall({
name: "run_subagent",
arguments: JSON.stringify({ prompt: "研究一下这个问题的背景资料" }),
arguments: JSON.stringify({ prompt: "Research the background of this question" }),
toolCallId: "sub-1",
});
const hop = "child-1";
@@ -308,17 +308,17 @@ describe("session-manager", () => {
expect(childTitle).toBeTruthy();
// Explicit material override for the sub-session: user material = the prompt that spawned it; assistant material = the sub-session's **own** model output.
expect(childTitle!.req.material).toEqual({
userText: "研究一下这个问题的背景资料",
userText: "Research the background of this question",
assistantText: "child done",
});
expect(childTitle!.req.fallbackText).toBe("研究一下这个问题的背景资料");
expect(childTitle!.req.fallbackText).toBe("Research the background of this question");
// Session/Agent record the sub-session, but modelId records the parent — the request runs on the parent's bare LLM.
expect(childTitle!.ctx).toMatchObject({ agentId: "child_agent", modelId: "m1" });
// The sub-session has no SSE channel of its own: title events are delivered over the parent session's channel.
expect(childTitle!.req.notifyOn).toBe("session-1");
});
it("审批模式即改即生效:运行中 PATCH 后下一次决策用新模式", async () => {
it("approval mode takes effect immediately: after a mid-run PATCH, the next decision uses the new mode", async () => {
// A fake Session that requests approval twice.
const fake: RuntimeSession = {
sessionId: "session-1",
@@ -349,7 +349,7 @@ describe("session-manager", () => {
expect(manager.pendingApprovalCount("session-1")).toBe(0);
});
it("abort:未决审批收敛为 deny 再触发 AbortSignal", async () => {
it("abort: pending approvals collapse to deny before the AbortSignal fires", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
expect(manager.abortTask("session-1")).toBe(false); // no Task in progress → no-op
await manager.startTask("session-1", [userText("go")]);
@@ -363,7 +363,7 @@ describe("session-manager", () => {
expect(payloads.some((p) => p.type === "abort")).toBe(true);
});
it("beginSessionDeletion:中断活跃运行、清出活跃表并标记删除中(新任务 409),end 后恢复", async () => {
it("beginSessionDeletion: interrupts active runs, clears the active table and marks deleting (new Tasks 409); recovers after end", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
expect(manager.beginSessionDeletion("session-1")).toEqual([]); // no active entry
// While deletion is in progress: a new Task is rejected with 409 (prevents resurrection).
@@ -380,7 +380,7 @@ describe("session-manager", () => {
manager.endSessionDeletion("session-1");
});
it("自愈:loader 返回新 session_id 时更新索引主键并返回当前实际 id", async () => {
it("self-heal: when the loader returns a new session_id, the index primary key is updated and the actual current id returned", async () => {
sessions.updateApprovalMode("session-1", "allow-all");
const manager = makeManager(loaderOf(approvalFakeSession("session-2-healed")));
const { sessionId } = await manager.startTask("session-1", [userText("go")]);
@@ -392,7 +392,7 @@ describe("session-manager", () => {
expect(recordedCtx[0]!.sessionId).toBe("session-2-healed");
});
it("通道被回收重建后 drive 仍发到当前通道(每次 publish 前重新 get)", async () => {
it("after the channel is swept and rebuilt, drive still sends to the current channel (re-get before every publish)", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
await manager.startTask("session-1", [userText("go")]);
await waitFor(() => manager.pendingApprovalCount("session-1") === 1);
@@ -415,7 +415,7 @@ describe("session-manager", () => {
expect(states.map((s) => s.state)).toContain("idle");
});
it("abortProject:返回进行中的驱动 Promise,等待后收尾完成", async () => {
it("abortProject: returns the in-flight drive Promises; wrap-up completes after awaiting them", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
expect(manager.abortProject("p1")).toEqual([]); // no active runs → empty array
await manager.startTask("session-1", [userText("go")]);
@@ -429,10 +429,10 @@ describe("session-manager", () => {
expect(manager.statusOf("session-1")).toBe("idle");
});
it("loader 抛 HttpError(如 workspace_missing 409)→ 原样透传,不被重复包装", async () => {
it("loader throwing HttpError (e.g. workspace_missing 409) → passed through as-is, not re-wrapped", async () => {
const loader: SessionLoader = {
load: async () => {
throw new HttpError(409, "workspace_missing", "Workspace 已不存在。");
throw new HttpError(409, "workspace_missing", "Workspace no longer exists.");
},
};
const manager = makeManager(loader);
@@ -441,7 +441,7 @@ describe("session-manager", () => {
expect((err as { code: string }).code).toBe("workspace_missing");
});
it("shutdown 置位后拒收新任务(503 shutting_down)", async () => {
it("rejects new Tasks once shutdown is set (503 shutting_down)", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
await manager.shutdown();
const err = await manager.startTask("session-1", [userText("x")]).catch((e: unknown) => e);
@@ -451,7 +451,7 @@ describe("session-manager", () => {
expect((compactErr as { status: number }).status).toBe(503);
});
it("sweepIdle:空闲超时的 entry 被淘汰(下次访问经 loader 重新装载)", async () => {
it("sweepIdle: entries idle past the timeout are evicted (reloaded via the loader on next access)", async () => {
let loads = 0;
const loader: SessionLoader = {
load: async () => {
@@ -475,7 +475,7 @@ describe("session-manager", () => {
expect(loads).toBe(1);
});
it("sweepIdle:运行中 / 有未决审批的 entry 不淘汰", async () => {
it("sweepIdle: entries that are running / have pending approvals are not evicted", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
await manager.startTask("session-1", [userText("go")]);
await waitFor(() => manager.pendingApprovalCount("session-1") === 1);
@@ -486,7 +486,7 @@ describe("session-manager", () => {
await waitFor(() => manager.statusOf("session-1") === "idle");
});
it("compact:置 compacting、输出进通道、结束回 idle", async () => {
it("compact: sets compacting, output goes to the channel, returns to idle at the end", async () => {
const manager = makeManager(loaderOf(approvalFakeSession("session-1")));
const events = capture("session-1");
await manager.startCompact("session-1");
@@ -499,7 +499,7 @@ describe("session-manager", () => {
]);
});
it("Task 完成后通知标题生成:兜底素材取用户 text、生成素材由 Session 自采;压缩不通知", async () => {
it("notifies title generation after a Task completes: fallback material is the user text, generation material is self-collected by the Session; compaction doesn't notify", async () => {
const notified: { ctx: UsageContext; session: unknown; req: TitleRequest }[] = [];
const plainSession: RuntimeSession = {
sessionId: "session-1",
@@ -507,10 +507,10 @@ describe("session-manager", () => {
generateTitle: async () => ({ title: null, usage: null }),
compactability: () => "ok" as const,
async *run() {
yield thinkingMessage("思考中");
yield assistantText("答案A");
yield withOrigin(assistantText("子会话文本"), "session-sub");
yield assistantText("答案B");
yield thinkingMessage("thinking");
yield assistantText("answer A");
yield withOrigin(assistantText("sub-session text"), "session-sub");
yield assistantText("answer B");
},
async *compact() {
yield compactionBegin({ reason: "manual", mode: "summarize", context: 1, turns: 1 });
@@ -528,9 +528,9 @@ describe("session-manager", () => {
log: () => {},
});
await manager.startTask("session-1", [userText("问题1"), userText("问题2")]);
await manager.startTask("session-1", [userText("question 1"), userText("question 2")]);
await waitFor(() => notified.length === 1);
expect(notified[0]!.req.fallbackText).toBe("问题1\n问题2");
expect(notified[0]!.req.fallbackText).toBe("question 1\nquestion 2");
// No material override for the main session: material is gathered by the core Session during run.
expect(notified[0]!.req.material).toBeUndefined();
expect(notified[0]!.session).toBe(plainSession);