Files
ai-workflow/dashboard/src/copy/en.ts
T

702 lines
27 KiB
TypeScript

export const en = {
shell: {
kicker: "AI delivery workflow",
title: "Delivery Console",
},
tabs: {
workflow: "Leader Console",
roles: "Agents",
skills: "Skills",
executions: "Runs",
merges: "Merge queue",
},
localeToggle: {
label: "Language",
zh: "中文",
en: "EN",
},
common: {
close: "Close",
cancel: "Cancel",
retry: "Retry",
loading: "Loading...",
loadingPage: "Loading page...",
send: "Send",
sending: "Sending...",
sendUpdate: "Send update",
create: "Create",
creating: "Creating...",
confirm: "Confirm",
edit: "Edit",
active: "Active",
notStarted: "Not started",
backToLatest: "Back to latest",
previous: "Previous",
next: "Next",
showNote: "Show note",
hideNote: "Hide note",
latestReply: "Latest reply",
runNote: "Run note",
openWorkflow: "Open workflow",
copyPath: "Copy path",
pathCopied: "Path copied",
noMessageContent: "No message content.",
emptyMessage: "(empty message)",
emptyBody: "(empty)",
never: "Never",
running: "Running",
fallback: "-",
},
themes: {
"atelier-copper": {
label: "Atelier Copper",
description: "Warm premium console",
},
"graphite-aqua": {
label: "Graphite Aqua",
description: "Cool technical slate",
},
"midnight-plum": {
label: "Midnight Plum",
description: "Moody editorial control room",
},
"ivory-brass": {
label: "Ivory Brass",
description: "Warm paper and brass",
},
"mist-blue": {
label: "Mist Blue",
description: "Crisp blue studio",
},
"sage-paper": {
label: "Sage Paper",
description: "Soft green planning desk",
},
appearance: {
dark: "Dark",
light: "Light",
darkThemes: "Dark themes",
lightThemes: "Light themes",
},
},
themeSelector: {
themeTitle: "Theme",
},
workspaceRequired: {
eyebrow: "Project context",
title: "Select a project",
detail: (subject: string) =>
`Choose a project from the picker in the top-right corner to load ${subject}.`,
},
globalPendingIndicator: {
label: "Pending",
title: (topic: string, count: number) =>
count === 1
? `Open pending topic ${topic}`
: `Open ${topic} and review ${count} pending topics`,
ariaLabel: (count: number, topic: string) =>
count === 1
? `1 pending item. Open topic ${topic}`
: `${count} pending items. Open topic ${topic}`,
modalTitle: (count: number) =>
count === 1 ? "1 pending item" : `${count} pending items`,
modalDetail: "These topics are waiting for plan confirmation. Choose one to open its detail view.",
openAction: "Open",
},
metadata: {
workflow: {
label: "Leader Console",
description:
"Operate the leader console: user dialogue, lane orchestration, task graph, and execution state.",
},
roles: {
label: "Agents",
description:
"Monitor leader and worker runtime state, attention, and recent activity.",
},
skills: {
label: "Skills",
description:
"Maintain the shared skill catalog separately from per-role skill assignment.",
},
executions: {
label: "Runs",
description:
"Inspect agent runs, outputs, and completion state across the current workspace.",
},
merges: {
label: "Merge queue",
description:
"Review queued merges, changed files, and merge readiness before promoting work.",
},
workspaceSuffix: (workspace: string) => `Active workspace: ${workspace}.`,
},
workspaceSelector: {
unavailable: "Workspaces unavailable",
loadingProjects: "Loading workspaces...",
missingProject: (value: string) => `${value} (missing)`,
noProjects: "No workspaces",
chooseProject: "Choose workspace",
projectsTitle: "Workspaces",
noProjectsYet: "No workspaces yet",
ensureWorkspaceError: "Couldn't prepare the workspace runtime.",
statusReadyRunning: "Ready",
statusReadyStopped: "Ready",
statusFailed: "Setup failed",
statusMissing: "Not ready",
loadProjectsError: "Couldn't load workspaces",
noProjectPath: "No workspace path available",
},
runtimeStatus: {
eyebrow: "Runtime monitor",
title: "Container runtime",
detail:
"Monitor the selected workspace container and recover it here instead of coupling runtime startup to project selection.",
openDetails: (workspace: string) => `Open runtime details for ${workspace}`,
summaryFallback: "Open details",
loadingTitle: "Loading runtime status...",
errorTitle: "Couldn't load runtime status",
fields: {
workspace: "Workspace",
container: "Container",
containerState: "Container state",
provisionState: "Provision state",
endpoint: "Runner endpoint",
lastProvisioned: "Last provisioned",
error: "Provision error",
},
states: {
running: "Running",
stopped: "Stopped",
missing: "Missing",
failed: "Failed",
},
endpointMissing: "Endpoint missing",
refresh: "Refresh status",
refreshing: "Refreshing...",
start: "Start runtime",
starting: "Starting runtime...",
retry: "Retry runtime",
retrying: "Retrying runtime...",
reconcile: "Reconcile runtime",
reconciling: "Reconciling runtime...",
actionError: "Runtime action failed",
},
roleEditor: {
title: (roleName: string) => `Edit: ${roleName}`,
roleTab: "Role",
roleSkillsTab: "Role Skills",
loading: "Loading...",
name: "Name",
sortOrder: "Sort Order",
description: "Description",
descriptionPlaceholder: "Short description of what this role does",
systemPrompt: "System Prompt",
promptPlaceholder: "The full system prompt for this agent role...",
codexConfig: "Codex Config",
codexConfigPlaceholder:
"model = \"gpt-5.4\"\napproval_policy = \"never\"\nsandbox_mode = \"workspace-write\"",
codexAuth: "Codex Auth",
codexAuthPlaceholder:
"{\n \"OPENAI_API_KEY\": \"...\"\n}",
skillsEmpty: "No skills are available for this role yet.",
skillMissing: "Missing",
skillsGroupingScope:
"Skill groups are shared across roles. Edit shared skill content from the Skills page.",
skillGroupLabel: "Group",
skillGroupPlaceholder: "group-name",
skillCategories: {
adaptation: "Adaptation",
visual_design: "Visual Design",
consistency: "Consistency",
quality: "Quality & Resilience",
ux_copy: "UX & Copy",
systemization: "Systemization",
capability: "Capability",
other: "Other",
},
saveRole: "Save Role",
saveSkills: "Save Skills",
saving: "Saving...",
},
skillsCatalog: {
workspaceSubject: "skill catalog",
eyebrow: "Shared skill library",
heroTitle: "Maintain shared skills as a first-class surface, separate from role detail editing.",
heroDetail:
"This page owns the global skill catalog. Role detail panels stay focused on assignment and grouping, while shared skill instructions live here.",
summary: {
totalSkills: "Skills in catalog",
totalSkillsDetail: "Shared entries available for role assignment.",
categories: "Categories",
categoriesDetail: "Unique buckets used to organize the library.",
assignmentSurface: "Assignments live in",
assignmentSurfaceValue: "Roles",
assignmentSurfaceDetail: "Enable and group skills from each role's detail panel.",
},
loadingTitle: "Loading skills...",
errorTitle: "Couldn't load skills",
catalogScope:
"Skill content is global. Changes here affect every role that enables the skill.",
catalogListLabel: "Skill catalog",
catalogEmpty: "No skills are stored in the database yet.",
newSkill: "New Skill",
deleteSkill: "Delete Skill",
skillGroupLabel: "Group",
skillGroupPlaceholder: "group-name",
skillMarkdownSummaryLabel: "Parsed from Markdown",
skillNameLabel: "Skill Name",
skillNameMissing: "Add `name:` to the markdown frontmatter.",
skillDescriptionLabel: "Description",
skillDescriptionMissing: "Optional. Add `description:` if you want a short summary.",
skillMarkdownHint:
"The internal skill ID is generated and maintained automatically from the markdown frontmatter.",
skillContentLabel: "Skill Content",
skillContentPlaceholder:
"---\nname: my-skill\ndescription: Brief summary shown in role assignment.\n---\n\nWrite the skill instructions here.",
skillCategories: {
adaptation: "Adaptation",
visual_design: "Visual Design",
consistency: "Consistency",
quality: "Quality & Resilience",
ux_copy: "UX & Copy",
systemization: "Systemization",
capability: "Capability",
other: "Other",
},
sortOrder: "Sort Order",
saveSkill: "Save Skill",
saving: "Saving...",
},
roleStatus: {
workspaceSubject: "agent status",
listTitle: "Agents",
listDetail: "Leader and workers currently active in this workspace.",
conciseDescription: {
leader: "Talks to the user, plans task graphs and derived lanes, and supervises execution.",
worker: "Executes lane tasks inside an isolated runtime.",
},
states: {
attention: "Needs attention",
active: "Active",
idle: "Not started",
waiting: (count: number) => `${count} waiting`,
},
loadingTitle: "Loading agents...",
errorTitle: "Couldn't load agent status",
emptyTitle: "No agents yet",
emptyDetail:
"Agent status appears here after this workspace starts running leader and worker threads.",
eyebrow: "Operational roster",
heroTitle:
"A live read on which agents are active, blocked, or still waiting to start.",
heroDetail:
"This surface keeps the orchestration layer legible: who is running, who has inbox work, and which roles have not entered the thread yet.",
summary: {
activeAgents: "Active agents",
activeAgentsDetail: "Roles with a live session right now.",
needsAttention: "Needs attention",
needsAttentionDetail: "Roles carrying pending inbox work.",
waitingItems: "Waiting items",
waitingItemsDetail: "Total queued messages across the roster.",
notStarted: "Not started",
notStartedDetail: "Roles that have not entered the workspace yet.",
},
lastActive: "Last active",
},
messageWorkflow: {
workspaceSubject: "message history",
eyebrow: "Activity feed",
loadingTitle: "Loading activity...",
errorTitle: "Couldn't load activity",
emptyTitle: "No activity yet",
emptyDetail:
"Messages appear here after the first handoff, clarification, or decision lands in Workflow.",
heroTitle: "Every handoff, decision, and clarification in one readable stream.",
heroDetail:
"This view turns message routing into a working narrative, so you can skim what moved, who moved it, and where the thread is headed next.",
summary: {
messages: "Messages",
messagesDetail: "Captured in the current workspace timeline.",
expanded: "Expanded",
expandedDetail: "Open cards for deeper reading right now.",
},
expandMessage: "Expand full message",
collapseMessage: "Collapse full message",
},
executions: {
workspaceSubject: "run history",
eyebrow: "Run ledger",
loadingTitle: "Loading runs...",
errorTitle: "Couldn't load runs",
emptyTitle: "No runs yet",
emptyDetail:
"Agent runs appear here after work starts on this project, including new threads, resumed sessions, and verification passes.",
heroTitle: "A readable history of every agent execution across the workspace.",
heroDetail:
"Track who started work, which threads were resumed, and where failures or long-running executions still need attention.",
summary: {
running: "Running",
runningDetail: "Live executions in progress.",
resumed: "Resumed",
resumedDetail: "Runs that continued an existing thread.",
failed: "Failed",
failedDetail: "Completed with a non-zero exit code.",
},
newRunsAvailable: "New runs available",
newRunsDetail:
"New executions arrived while you were reviewing older history.",
topic: "Topic",
phase: "Phase",
thread: "Thread",
duration: "Duration",
started: "Started",
recentRuns: "Recent runs",
recentRunsTitle: "Ordered newest first for quick triage.",
role: "Role",
exitCode: "Exit code",
details: "Details",
noDetail: "-",
mobileShowing: (visible: number, total: number) =>
`Showing ${visible} of ${total} runs.`,
desktopShowing: (start: number, end: number, total: number) =>
`Showing ${start}-${end} of ${total} runs.`,
loadOlderRuns: "Load older runs",
pageLabel: (page: number, totalPages: number) => `Page ${page} of ${totalPages}`,
},
mergeQueue: {
workspaceSubject: "merge requests",
eyebrow: "Merge queue",
loadingTitle: "Loading merge queue...",
errorTitle: "Couldn't load merge queue",
emptyTitle: "Nothing waiting to merge",
emptyDetail:
"Changes show up here after a build finishes and is ready for review, approval, and merge back to the main project branch.",
heroTitle: "Review-ready changes collected in one calm, traceable queue.",
heroDetail:
"This surface is for final judgment: what is pending, what merged cleanly, and which requests still need human intervention before they can land.",
summary: {
pending: "Pending",
pendingDetail: "Waiting for approval or merge.",
merged: "Merged",
mergedDetail: "Already landed back on target.",
failed: "Failed",
failedDetail: "Requests blocked by errors or conflicts.",
},
status: {
pending: "Pending",
merged: "Merged",
failed: "Failed",
},
filesChanged: (count: number) => `${count} files changed`,
loadDiffError: "Couldn't load file diffs.",
failedToMerge: "Failed to merge",
files: "Files",
created: "Created",
merged: "Merged",
mergeAction: "Approve & Merge",
merging: "Merging...",
},
workflow: {
topicExamplesLabel: "Example topics",
starterTopicsLabel: "Starter topics",
kickoffChecklist: [
{
label: "Start with the owner",
detail:
"Send the first message to the leader so the leader can decide whether to clarify, derive lanes, or start execution.",
},
{
label: "Anchor the goal",
detail:
"Summarize the user outcome, constraints, and done criteria the receiving role should optimize for.",
},
{
label: "Keep the first handoff executable",
detail:
"Use a topic name and opening message that can survive multiple rounds of review, build, and verification.",
},
],
topicCount: (count: number) => `${count} msg${count !== 1 ? "s" : ""}`,
noMessageContent: "No message content.",
recipient: "Recipient",
phase: "Phase",
message: "Message",
messageHint: (shortcut: string) => `${shortcut}+Enter to send`,
disabledPlaceholder: "Name the topic first...",
enabledPlaceholder: "Outline the next decision, handoff, or build request...",
sendFailed: "Send failed",
createTopicFailed: "Couldn't create the topic.",
deleteTopic: "Delete topic",
confirmDeleteTopic: "Confirm delete",
deletingTopic: "Deleting topic",
deleteTopicFailed: "Couldn't delete the topic.",
deleteTopicDialogTitle: "Delete this topic?",
deleteTopicDialogDetail:
"This will permanently remove the topic and its workflow history from the leader console.",
stopTopic: "Stop topic",
confirmStopTopic: "Confirm stop",
stoppingTopic: "Stopping topic",
stopTopicFailed: "Couldn't stop the topic.",
stopTopicDialogTitle: "Stop this topic?",
stopTopicDialogDetail:
"This will halt further execution for the topic. Use this only when the topic should not continue.",
confirmPlan: "Confirm plan",
confirmingPlan: "Confirming plan",
confirmPlanFailed: "Couldn't confirm the plan.",
topicAwaitingApproval: "Awaiting approval",
topicStopped: "Stopped",
topicStatus: (status: string) => `Status: ${status}`,
pendingPlanBanner: {
title: (count: number) => count === 1 ? "1 topic is waiting for plan approval" : `${count} topics are waiting for plan approval`,
detail: "Execution stays paused until the plan is confirmed. Open the topic details to review and approve it.",
openTopic: (topic: string) => `Open ${topic}`,
},
planReview: {
eyebrow: "Plan review",
title: "Review the frozen plan before execution starts",
detail: "This topic already has a proposed task graph. The execution map stays hidden until the plan is confirmed.",
summaryTitle: "Plan summary",
summaryEmpty: "The leader froze a plan, but no summary was saved with it.",
proposedLanes: "Proposed lanes",
proposedTasks: "Planned tasks",
lanesEmpty: "No lanes have been proposed yet.",
tasksEmpty: "No tasks have been proposed yet.",
versionLabel: (version: number) => `Plan v${version}`,
createdByLabel: (role: string) => `Prepared by ${role}`,
},
untitledTopic: "(no topic)",
workspaceSubject: "workflow",
errorTitle: "Couldn't load workflow",
topics: "Topics",
newTopic: "+ New",
hideTopics: "Hide",
showTopics: "Show topics",
topicName: "Topic name",
topicPlaceholder: "topic-name",
topicHint: "Enter to create, Esc to cancel",
emptyTopicRailTitle: "No topics yet",
emptyTopicRailDetail:
"Start the first leader thread here. The leader will turn it into a task graph, derived lanes, and execution runs.",
srHeading: (topic: string | null) =>
topic ? `Topic ${topic}` : "Workflow topic details",
emptyState: {
eyebrow: "Workflow",
namingTitle: "Name this topic",
initialTitle: "Launch a topic and move it through the team",
namingDetail:
"Give the workstream a durable topic name on the left, then send the opening message to the leader.",
initialDetail:
"Each topic becomes a leader-owned thread. Start with a clear goal, then let the leader define the task graph and derived lanes from there.",
startFirstTopic: "+ Start first topic",
whatHappensNext: "What happens next",
strongFirstHandoff: "A strong first handoff",
exampleOpeningTitle: "Example opening",
creatingSideNote:
"Keep one topic per outcome. That makes the timeline easier to read when planning, building, and verification overlap.",
nonCreatingSideNote:
"Keep one topic per outcome. That makes the timeline easier to read when planning, building, and verification overlap.",
},
runningSummary: {
single: (role: string) => `${role} running`,
double: (left: string, right: string) => `${left} + ${right} running`,
multiple: (count: number) => `${count} agents running`,
},
topicSignals: {
running: (count: number) => `${count} running`,
waiting: (count: number) => `${count} queued`,
},
summary: {
eyebrow: "Active topic",
detail:
"See who owns the next move, which handoffs are still queued, and how this topic is moving through build and verification.",
runningLabel: "Running now",
waitingLabel: "Queued",
activeLabel: "In motion",
running: (count: number) => `${count} agents are still executing.`,
waiting: (count: number) => `${count} agents are waiting on the next handoff.`,
active: (count: number) => `${count} roles are part of the current flow.`,
},
board: {
detail:
"Roles stay pinned to fixed swimlanes while direct handoffs stack by time, so the handoff sequence stays readable even when traffic gets dense.",
hint:
"Select a lane header to inspect a role, or select a handoff row to inspect the direct exchange behind it.",
laneCount: (count: number) => `${count} msg${count !== 1 ? "s" : ""}`,
},
selection: {
eyebrow: "Board context",
emptyTitle: "Select a role or lane",
emptyDetail:
"Use the swimlane to choose a role header or a handoff row, then inspect the details here.",
messageEyebrow: "Message detail",
messageTitle: "Selected handoff",
messageDetail:
"Read the full message first, then use the recipient buttons to inspect each role's handling message without opening the whole lane.",
messageBody: "Full message",
sourceEyebrow: "Sender",
responseEyebrow: "Recipients",
responseTitle: (role: string) => `${role} handling message`,
responsePending: (role: string) => `${role} has not sent a handling message yet.`,
viewResponse: (role: string) => `View ${role} handling message`,
laneEyebrow: "Message lane",
laneTitle: (from: string, to: string) => `${from} to ${to}`,
laneDetail:
"This lane groups direct messages passed from the source role to the target role on the active topic.",
laneMessages: "Messages on this lane",
noLaneMessages: "No direct messages on this lane yet.",
openLane: (role: string) => `Open ${role} lane`,
openRole: (role: string) => `Open ${role}`,
threadTitle: "Latest thread updates",
threadDetail:
"Keep the newest cross-role updates visible while you inspect a single lane.",
showThread: "Show latest topic updates",
hideThread: "Hide latest topic updates",
},
focusTabs: {
label: "Workflow panels",
overview: "Overview",
roles: "Roles",
timeline: "Timeline",
},
nextStep: {
eyebrow: "Next move",
queuedTitle: (role: string) => `${role} has the baton`,
queuedDetail:
"That role already has the latest handoff. Open Roles to inspect the context, or send a correction only if the plan changed.",
runningTitle: (role: string) => `${role} is already working`,
runningDetail:
"Execution is in flight. Use Roles to inspect live output, or redirect the work only if priorities changed.",
recentTitle: (role: string) => `Route the next handoff to ${role}`,
recentDetail:
"Based on the latest activity, this is the clearest next owner for the topic.",
idleTitle: "Start with Product",
idleDetail:
"If ownership still feels fuzzy, send the next update to Product to freeze scope and route execution.",
inspectRole: (role: string) => `Inspect ${role}`,
},
composer: {
eyebrow: "Next handoff",
title: "Send the next update first",
detail:
"Messages from this page go straight to the leader. The leader decides how to decompose and dispatch the work.",
inspectRole: (role: string) => `Focus ${role}`,
},
humanTask: {
eyebrow: "Waiting On You",
title: "A role needs a human answer",
detail:
"This prompt was routed to the host-side user inbox instead of the Codex runtime. Answer here to push the workflow forward.",
fromLabel: (role: string) => `${role} is waiting for your reply`,
promptLabel: "Prompt",
responseLabel: "Your answer",
responseHint: (shortcut: string) => `${shortcut}+Enter to answer`,
placeholder: "Answer the question, clarify scope, or unblock the next handoff...",
send: "Send answer",
failed: "Couldn't send your answer.",
remaining: (count: number) => `${count} more pending question${count === 1 ? "" : "s"} after this one.`,
},
overview: {
recentEyebrow: "Recent activity",
recentTitle: "Latest events",
recentDetail:
"Scan the latest handoffs and runs here before diving into the full timeline.",
noRecentTitle: "No events yet",
noRecentDetail:
"Send the opening PRD and the first role runs will show up here.",
snapshotEyebrow: "Flow snapshot",
snapshotTitle: "Who is moving now",
snapshotDetail:
"Keep the current state readable without forcing the full role graph into the default view.",
runningTitle: "Running now",
waitingTitle: "Waiting next",
recentTitleShort: "Recently involved",
emptyRunning: "No one is running right now.",
emptyWaiting: "No queued handoff right now.",
emptyRecentRoles: "No recent role activity yet.",
openRoles: "Open role map",
openTimeline: "Open timeline",
},
sandbox: {
eyebrow: "Realtime swimlane",
title: "Handoff timeline",
detail:
"Each role keeps a fixed lane while direct handoffs stack by time. Execution state stays in the lane headers and role details.",
linkCount: (count: number) => `${count} lane${count !== 1 ? "s" : ""}`,
emptyTitle: "No handoffs yet",
emptyDetail:
"Messages will appear here once one role hands work to another. Running and failed executions stay visible in role details.",
},
roleState: {
running: "Running",
queued: "Queued",
recent: "Recent",
idle: "Idle",
},
inspector: {
eyebrow: "Role inspector",
title: "Role detail",
noSelection: "Select a role to inspect its handoffs, queue pressure, and current execution state.",
globalInbox: "Global inbox",
globalInboxValue: (count: number) => `${count} queued globally`,
lastSession: (value: string) => `Last session ${value}`,
sessionMissing: "No session recorded yet.",
latestInbound: "Latest inbound",
latestOutbound: "Latest outbound",
noInbound: "No inbound handoff for this topic yet.",
noOutbound: "No outbound handoff for this topic yet.",
currentRun: "Current run",
noCurrentRun: "No execution recorded for this topic yet.",
startedAt: (value: string) => `Started ${value}`,
completedAt: (value: string) => `Completed ${value}`,
inFlight: "Still running…",
exitCode: (value: number) => `Exit ${value}`,
liveOutput: "Live output",
waitingLiveOutput: "Waiting for live output...",
noLiveOutput: "No live output captured yet.",
},
timeline: {
eyebrow: "Event timeline",
title: "Topic events",
detail:
"Messages and runs are merged into one sequence so the operational story stays readable.",
count: (count: number) => `${count} events`,
emptyTitle: "No events yet",
emptyDetail:
"Send the opening PRD to start the topic. Role runs and replies will appear here as the work begins.",
dispatchRunning: "Run in flight",
dispatchSettled: (exitCode: number) => (exitCode === 0 ? "Run complete" : "Run failed"),
},
openingHandoff: {
eyebrow: "Opening Handoff",
creatingTitle: "Send the opening PRD",
regularTitle: "Route the next step",
creatingDetail:
"Your first message should name the outcome, who should act first, and what done looks like.",
regularDetail:
"Use the composer below to push this topic into the next phase with a concrete ask for the receiving role.",
coverPoints: "Cover these points",
exampleOpeningTitle: "Example opening",
exampleOpeningBody:
"Ship password reset. The leader should clarify scope, derive execution lanes, and start worker containers only after the task graph is ready.",
},
},
};
export type DeepWiden<T> = T extends (...args: infer A) => infer R
? (...args: A) => DeepWiden<R>
: T extends readonly (infer U)[]
? DeepWiden<U>[]
: T extends object
? { [K in keyof T]: DeepWiden<T[K]> }
: T extends string
? string
: T extends number
? number
: T extends boolean
? boolean
: T;
export type AppCopy = DeepWiden<typeof en>;