Q173: agents work in the company's or task's language, not the display language

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-09 04:40:52 -04:00
1 parent 538d834647
commit 3f14e3f706
36 files changed
+268 -105

No files matched your search

+5
View File
@@ -695,6 +695,11 @@ export const zh: Record<string, string> = {
"Zero data retention switched background off.": "零数据保留已关闭后台执行。",
"Resets {date} (UTC)": "{date} 重置(UTC)",
"Unsaved": "未保存",
"Working language": "工作语言",
"Company default ({language})": "公司默认({language})",
"Agents' working language": "Agent 工作语言",
"Agents write messages, tasks, documents and deliverables in it. The display language does not change it, and a task can set its own.": "Agent 用它撰写消息、任务、文档和交付物。显示语言不会改变它,单个任务也可以单独设置。",
"Changes only what you see; agents keep the company's working language.": "只改变界面显示;Agent 仍使用公司的工作语言。",
"Disconnect {label}?": "断开 {label}?",
"Checking what this affects…": "正在检查影响范围…",
"Agents on this provider stop working until it is reconnected or they move to another provider: {names}.": "使用该 Provider 的 Agent 将停止工作,直到重新连接或改用其他 Provider:{names}。",
+5 -1
View File
@@ -5,7 +5,9 @@ import type { AppSnapshot, RiskLevel, TaskPriority, WritingMode } from "../../..
import { RISK_LEVEL, TASK_PRIORITY, TASK_STATUS, WRITING_MODE } from "../../../shared/domain";
import type { SnapshotActions } from "../app/useSnapshot";
import { t } from "../i18n/i18n";
import { WRITING_OPTIONS } from "./work/task-helpers";
import { WRITING_OPTIONS, readWorkLanguage } from "./work/task-helpers";
import { WorkLanguageField } from "./work/WorkLanguageField";
import { requireActiveOrganization } from "./types";
const RISK_OPTIONS: ReadonlyArray<{ value: RiskLevel; label: string }> = [
{ value: RISK_LEVEL.LOW, label: t("Low") },
@@ -61,6 +63,7 @@ export function NewTaskDialog({
priority: value(form, "priority") as TaskPriority,
requiresFilesystem: form.get("requiresFilesystem") === "on",
writingMode: value(form, "writingMode") as WritingMode,
workLanguage: readWorkLanguage(form),
})).then((result) => {
if (result) onClose();
});
@@ -109,6 +112,7 @@ export function NewTaskDialog({
{WRITING_OPTIONS.map((option) => <option key={option.value} value={option.value}>{option.label}</option>)}
</select>
</Field>
<WorkLanguageField companyLanguage={requireActiveOrganization(snapshot).workLanguage} defaultValue={null} />
<label className="checkbox-row">
<input type="checkbox" name="requiresFilesystem" /> {t("Requires workspace access")}
</label>
+2 -1
View File
@@ -22,7 +22,7 @@ import { ListView } from "./work/ListView";
import { ProgressView } from "./work/ProgressView";
import { isBusinessTask, latestTaskRun, nextStatus, prettyRunState, providerFor, runBlocker, runTone } from "./work/task-helpers";
import type { PageProps } from "./types";
import { activeOrgId, type Focus } from "./types";
import { activeOrgId, requireActiveOrganization, type Focus } from "./types";
import { t } from "../i18n/i18n";
type View = "board" | "list" | "progress";
@@ -166,6 +166,7 @@ export function WorkPage({ snapshot, actions, navigate, focus }: PageProps) {
<TaskDetailDialog
key={detailTask?.id ?? "none"}
task={detailTask}
companyLanguage={requireActiveOrganization(snapshot).workLanguage}
agents={agents}
projects={projects}
goals={goals}
@@ -4,6 +4,7 @@ import { Field } from "../../shared/ui";
import { Dialog } from "../../shared/Dialog";
import type { SnapshotActions } from "../../app/useSnapshot";
import { t } from "../../i18n/i18n";
import { WORK_LANGUAGE_NAMES } from "../work/task-helpers";
/** Changes the company's profile and how it runs after onboarding; requests already waiting keep their state. */
export function CompanyDialog({ organization, open, actions, onClose }: { organization: OrganizationRecord; open: boolean; actions: SnapshotActions; onClose: () => void }) {
@@ -22,6 +23,7 @@ export function CompanyDialog({ organization, open, actions, onClose }: { organi
executionMode: value("executionMode") === "ai_cofounder" ? "ai_cofounder" : "ai_ceo",
autoApproveHires: form.get("autoApproveHires") === "on",
reviewDeliverables: form.get("reviewDeliverables") === "on",
workLanguage: value("workLanguage") === "zh" ? "zh" : "en",
})).then(onClose, (reason: unknown) => setError(reason instanceof Error ? reason.message : String(reason)));
};
return (
@@ -36,6 +38,13 @@ export function CompanyDialog({ organization, open, actions, onClose }: { organi
<option value="ai_cofounder">{t("AI Co-Founder: asks before risky tasks")}</option>
</select>
</Field>
<Field label={t("Agents' working language")}>
<select className="input" name="workLanguage" defaultValue={organization.workLanguage}>
<option value="en">{WORK_LANGUAGE_NAMES.en}</option>
<option value="zh">{WORK_LANGUAGE_NAMES.zh}</option>
</select>
</Field>
<p style={{ fontSize: 12, color: "var(--text-3)", margin: 0 }}>{t("Agents write messages, tasks, documents and deliverables in it. The display language does not change it, and a task can set its own.")}</p>
<label className="checkbox-row"><input type="checkbox" name="autoApproveHires" defaultChecked={organization.autoApproveHires} /> {t("Auto-approve hires")}</label>
<label className="checkbox-row"><input type="checkbox" name="reviewDeliverables" defaultChecked={organization.reviewDeliverables} /> {t("Review deliverables before they are written")}</label>
<div style={{ fontSize: 12, color: "var(--text-3)" }}>{t("Changes apply to work from now on; requests already waiting keep their state.")}</div>
@@ -114,6 +114,7 @@ export function WorkspacePanel({
<option value="en">English</option>
<option value="zh">中文</option>
</select>
<span style={{ fontSize: 12, color: "var(--text-3)" }}>{t("Changes only what you see; agents keep the company's working language.")}</span>
</Field>
</div>
@@ -12,9 +12,10 @@ import type {
TaskRunRecord,
WorkspaceRecord,
} from "../../../../shared/domain";
import { TASK_STATUS, type TaskDeleteImpact, type TaskStatus, type WritingMode } from "../../../../shared/domain";
import { TASK_STATUS, type TaskDeleteImpact, type TaskStatus, type WorkLanguage, type WritingMode } from "../../../../shared/domain";
import { auditEventLabel, formatRelative } from "../types";
import { WRITING_OPTIONS, descendantIds, nextStatus, prettyRunState, prettyStatus, providerFor, runBlocker, runsForTask, runTone } from "./task-helpers";
import { WorkLanguageField } from "./WorkLanguageField";
import { WRITING_OPTIONS, readWorkLanguage, descendantIds, nextStatus, prettyRunState, prettyStatus, providerFor, runBlocker, runsForTask, runTone } from "./task-helpers";
import type { SnapshotActions } from "../../app/useSnapshot";
import type { Focus, PageProps } from "../types";
import { t } from "../../i18n/i18n";
@@ -28,6 +29,7 @@ const TABS: Array<{ id: Tab; label: string }> = [
export function TaskDetailDialog({
task,
companyLanguage,
agents,
projects,
goals,
@@ -49,6 +51,7 @@ export function TaskDetailDialog({
navigate,
}: {
task: TaskRecord | null;
companyLanguage: WorkLanguage;
agents: AgentRecord[];
projects: ProjectRecord[];
goals: GoalRecord[];
@@ -114,6 +117,7 @@ export function TaskDetailDialog({
workspaceId: val("workspaceId") || null,
requiresFilesystem: form.get("requiresFilesystem") === "on",
writingMode: val("writingMode") as WritingMode,
workLanguage: readWorkLanguage(form),
})).then((r) => { if (r) setEditing(false); }, (error: unknown) => setEditError(error instanceof Error ? error.message : String(error)));
};
@@ -203,6 +207,7 @@ export function TaskDetailDialog({
{editing && (
<EditOverlay
task={task}
companyLanguage={companyLanguage}
agents={agents}
allTasks={allTasks}
projects={projects}
@@ -314,8 +319,8 @@ function ActivityTab({ audit, approvals, onOpenApproval }: { audit: AuditEventRe
);
}
function EditOverlay({ task, agents, allTasks, projects, goals, workspaces, error, onSubmit, onCancel }: {
task: TaskRecord; agents: AgentRecord[]; allTasks: TaskRecord[];
function EditOverlay({ task, companyLanguage, agents, allTasks, projects, goals, workspaces, error, onSubmit, onCancel }: {
task: TaskRecord; companyLanguage: WorkLanguage; agents: AgentRecord[]; allTasks: TaskRecord[];
projects: ProjectRecord[]; goals: GoalRecord[]; workspaces: WorkspaceRecord[]; error: string | null;
onSubmit: (e: FormEvent<HTMLFormElement>) => void; onCancel: () => void;
}) {
@@ -374,6 +379,7 @@ function EditOverlay({ task, agents, allTasks, projects, goals, workspaces, erro
{WRITING_OPTIONS.map((option) => <option key={option.value} value={option.value}>{option.label}</option>)}
</select>
</Field>
<WorkLanguageField companyLanguage={companyLanguage} defaultValue={task.workLanguage} />
<label className="checkbox-row">
<input type="checkbox" name="requiresFilesystem" defaultChecked={task.requiresFilesystem} /> {t("Requires workspace access")}
</label>
@@ -0,0 +1,17 @@
import type { WorkLanguage } from "../../../../shared/domain";
import { Field } from "../../shared/ui";
import { t } from "../../i18n/i18n";
import { WORK_LANGUAGE_NAMES } from "./task-helpers";
/** A task's working language; blank follows the company's, named so the user sees what blank means. */
export function WorkLanguageField({ companyLanguage, defaultValue }: { companyLanguage: WorkLanguage; defaultValue: WorkLanguage | null }) {
return (
<Field label={t("Working language")}>
<select className="input" name="workLanguage" defaultValue={defaultValue ?? ""}>
<option value="">{t("Company default ({language})", { language: WORK_LANGUAGE_NAMES[companyLanguage] })}</option>
<option value="en">{WORK_LANGUAGE_NAMES.en}</option>
<option value="zh">{WORK_LANGUAGE_NAMES.zh}</option>
</select>
</Field>
);
}
@@ -6,10 +6,22 @@ import {
type TaskRecord,
type TaskRunRecord,
type TaskStatus,
type WorkLanguage,
type WritingMode,
} from "../../../../shared/domain";
import { t } from "../../i18n/i18n";
/** Each working language in its own script, so it reads right whatever the display language. */
export const WORK_LANGUAGE_NAMES: Record<WorkLanguage, string> = { en: "English", zh: "简体中文" };
/** The task form's working language: blank follows the company. */
export function readWorkLanguage(form: FormData): WorkLanguage | null {
const value = String(form.get("workLanguage") ?? "");
if (value === "") return null;
if (value === "en" || value === "zh") return value;
throw new Error(`Unknown working language "${value}".`);
}
/** The writing modes a task can ask for, as the create and edit forms offer them. */
export const WRITING_OPTIONS: ReadonlyArray<{ value: WritingMode; label: string }> = [
{ value: WRITING_MODE.CONVERSATIONAL, label: t("Conversational (house style)") },