mateclaw/mateclaw-ui/src/i18n/locales/en-US.ts

4736 lines
205 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export default {
app: {
title: 'MateClaw - AI Assistant',
},
router: {
chunkLoadFailed: 'Failed to load page resources. Check your network and try again.',
},
teams: {
kicker: 'Multi-Agent Collaboration',
title: 'Agent Teams',
subtitle: 'Multi-agent teams: the lead orchestrates, members execute',
create: 'Create Team',
empty: 'No teams yet — create a lead-orchestrated multi-agent team',
memberCount: '{count} members',
back: 'Back',
runs: 'Runs',
board: 'Task Board',
boardScope: 'Task board run scope',
boardAllRuns: 'All historical tasks',
members: 'Members',
addMember: 'Add Member',
memberName: 'Member',
role: 'Role',
name: 'Name',
description: 'Description',
lead: 'Lead',
membersField: 'Members',
createIncomplete: 'Name, lead and at least one member are required',
pickHint: 'Click to select / deselect; the lead cannot double as a member',
roles: {
lead: 'Lead',
member: 'Member',
reviewer: 'Reviewer',
},
noCandidates: 'No agents left to add (all are already on the team)',
deleteConfirm: 'Delete this team? Historical tasks are kept but no longer dispatched.',
cancelConfirm: 'Cancel this task?',
assignee: 'Assignee',
taskDescription: 'Description',
result: 'Result',
comments: 'Comments',
commentPlaceholder: 'Write a comment…',
approve: 'Approve',
approved: 'Approved',
reject: 'Reject',
rejectReason: 'Rejection reason (the lead will be notified)',
retry: 'Retry',
createTask: 'New Task',
taskSubject: 'Subject',
priority: 'Priority',
requireApprovalField: 'Require human approval on completion',
blockedBy: 'Prerequisite tasks',
blockedByHint: 'Optional; this task dispatches only after all selected tasks finish',
taskCreateIncomplete: 'Subject and assignee are required',
deliverables: 'Deliverables',
viewRun: 'View execution',
timeline: 'Timeline',
loadMore: 'Load more ({loaded}/{total})',
eventType: {
created: 'Created',
dispatched: 'Dispatched',
progress: 'Progress',
comment: 'Comment',
blocker: 'Blocker',
deliverable: 'Deliverable',
completed: 'Completed',
in_review: 'In review',
failed: 'Failed',
cancelled: 'Cancelled',
approved: 'Approved',
rejected: 'Rejected',
retried: 'Retried',
stale: 'Stale',
},
column: {
todo: 'To Do',
in_progress: 'In Progress',
settling: 'Finalizing',
in_review: 'In Review',
completed: 'Completed',
closed: 'Closed',
},
status: {
pending: 'Pending',
blocked: 'Blocked',
in_progress: 'In Progress',
in_review: 'In Review',
completed: 'Completed',
failed: 'Failed',
cancelled: 'Cancelled',
stale: 'Stale',
},
},
teamRuns: {
history: 'Run history',
refresh: 'Refresh runs',
loading: 'Loading runs…',
empty: 'No team runs yet',
loadError: 'Run history could not be refreshed.',
retryLoad: 'Retry',
loadMore: 'Load more',
loadingMore: 'Loading more',
detailLoading: 'Loading run details',
detailUnavailable: 'Run details are not available yet.',
close: 'Close run details',
partialNotice: 'Some tasks did not complete. Available results are shown below.',
cancelConfirm: 'Cancel this run and its active tasks?',
status: {
planning: 'Planning',
running: 'Running',
awaiting_review: 'Awaiting review',
finalizing: 'Finalizing',
completed: 'Completed',
partial: 'Partially completed',
failed: 'Failed',
cancelled: 'Cancelled',
},
duration: { day: 'd', hour: 'h', minute: 'm', second: 's' },
progress: '{done} of {total} complete',
tasks: 'Tasks',
emptyTasks: 'No tasks in this run',
assignee: 'Assignee',
dependencies: 'Dependencies',
noDependencies: 'None',
result: 'Result',
noResult: 'No result yet',
summary: 'Summary',
noSummary: 'No summary yet',
outcome: 'Outcome',
attention: 'Needs attention',
noAttention: 'No action needed',
contributions: 'Member contributions',
runtime: 'Runtime',
quality: { synthesized: 'Synthesized', fallback: 'Fallback', partial: 'Partial', pending: 'Pending' },
liveness: { live: 'Live', quiet: 'Waiting for activity', stalled: 'Stalled', terminal: 'Finished' },
deliverables: 'Deliverables',
noDeliverables: 'No deliverables',
cancel: 'Cancel run',
expand: 'Expand run',
collapse: 'Collapse run',
openTask: 'Open task',
objective: 'Objective',
taskProgress: 'Task progress',
stopReason: 'Stop reason',
workerReadOnly: 'Worker conversation',
workerReadOnlyDescription: 'This delegated task conversation is read-only.',
backToLead: 'Lead chat',
openInTeams: 'Teams',
openInAgents: 'Agents',
},
common: {
save: 'Save',
saving: 'Saving...',
saved: 'Saved',
cancel: 'Cancel',
reset: 'Reset',
edit: 'Edit',
delete: 'Delete',
create: 'Create',
update: 'Update',
loading: 'Loading...',
processing: 'Processing...',
success: 'Done',
failed: 'Operation failed',
revoked: 'Revoked',
enabled: 'Enabled',
disabled: 'Disabled',
default: 'Default',
view: 'View',
copy: 'Copy',
copied: 'Copied',
copyFailed: 'Copy failed',
confirm: 'Confirm',
close: 'Close',
selectRange: 'Select date range',
startDate: 'Start date',
endDate: 'End date',
lastDays: 'Last {n} days',
add: 'Add',
search: 'Search',
noResults: 'No matches',
noOptions: 'No options',
none: 'None',
clear: 'Clear',
expandSidebar: 'Expand sidebar',
collapseSidebar: 'Collapse sidebar',
manageWorkspaces: 'Manage Workspaces',
show: 'Show',
hide: 'Hide',
on: 'On',
off: 'Off',
yes: 'Yes',
no: 'No',
configure: 'Configure',
refresh: 'Refresh',
collapse: 'Collapse',
enable: 'Enable',
disable: 'Disable',
more: 'More',
pager: {
total: '{n} total',
perPage: '{n}/page',
prev: 'Previous',
next: 'Next',
jumpTo: 'Go to',
pageSuffix: '',
},
iconPicker: {
title: 'Pick an icon',
tabPixelart: 'Pixelart',
tabEmoji: 'Emoji',
tabUrl: 'Image URL',
search: 'Search by name…',
empty: 'No icons match',
preview: 'Preview',
none: 'No icon',
apply: 'Apply',
emojiHint: 'Type or paste any emoji / text glyph.',
emojiPlaceholder: '🎬 / 📊 / any symbol',
urlHint: 'Use a hosted image (PNG / SVG / JPG) as the icon.',
urlPlaceholder: 'https://example.com/icon.svg',
pixelartCount: '{n} pixelart icons by pixelarticons (MIT)',
pickerOpen: 'Open icon picker',
reprojectionWarning: 'Heads up: if SKILL.md frontmatter declares `icon`, your override will be overwritten on next resolve — edit the body to make it permanent.',
},
},
auth: {
changePassword: 'Change Password',
oldPassword: 'Current Password',
newPassword: 'New Password',
confirmPassword: 'Confirm New Password',
fieldsRequired: 'Please enter current and new password',
passwordMismatch: 'New passwords do not match',
passwordChanged: 'Password changed successfully',
passwordChangeFailed: 'Failed to change password',
},
chat: {
status: {
idle: 'Ready',
streaming: 'Generating...',
error: 'Disconnected',
},
thinking: 'Thinking',
thinkingInProgress: 'Thinking...',
thinkingDoneFor: 'Thought for {duration}',
teamAnnounce: '{count} team task(s) settled',
teamAnnounceGeneric: 'Team tasks settled',
phaseNames: {
reasoning: 'Reasoning',
action: 'Executing tools',
planning: 'Planning',
summarizing: 'Summarizing',
awaitingApproval: 'Waiting for approval',
executing: 'Executing',
replaying: 'Resuming execution',
resumed: 'Resumed',
processing: 'Processing',
},
stopped: 'Generation stopped',
interrupted: 'Interrupted',
subagentStalled: 'Subagent stalled — no progress',
subagentAsync: 'Running in background — result via task_output',
executionPlan: 'Execution Plan',
planDone: 'done',
runOverview: {
title: 'Run Overview',
plan: 'Plan Progress',
subagents: 'Sub-agents',
files: 'Generated Files',
noPlan: 'No execution plan yet',
noSubagents: 'No sub-agents yet',
collapse: 'Collapse',
expand: 'Expand',
running: 'running',
planning: 'Planning…',
},
detail: {
viewDetail: 'View details',
request: 'Request',
response: 'Response',
copy: 'Copy',
copied: 'Copied',
copyFailed: 'Copy failed',
empty: 'No content',
status: {
running: 'In progress',
completed: 'Completed',
error: 'Failed',
pending: 'Pending',
},
},
expandLines: 'Show more ({hidden} more lines)',
collapse: 'Show less',
failed: 'Generation failed',
compressionSummary: 'Previous conversations summarized',
compressionWithCount: '{count} previous messages summarized',
loadingOlder: 'Loading older messages...',
loadOlderMessages: 'Click to load older messages',
retry: 'Retry',
errorCode: 'Error code',
error: {
rate_limit: {
title: 'Too many requests',
description: 'You\'ve exceeded the rate limit. The server is temporarily unable to process your request.',
action: 'Please wait a moment before retrying, or reduce consecutive submissions.',
},
auth_expired: {
title: 'Session expired',
description: 'Your login session has expired and you need to sign in again.',
action: 'You will be redirected to the login page.',
},
provider_auth_error: {
title: 'Model authentication failed',
description: 'The current model\'s API key is invalid or has expired. This is unrelated to your login session.',
action: 'Open Settings → Models to check and update the API key.',
},
forbidden: {
title: 'Access denied',
description: 'You don\'t have permission to perform this action.',
action: 'Please contact the administrator for access.',
},
bad_request: {
title: 'Invalid request',
description: 'The request was malformed and could not be processed by the server.',
action: 'Please check your input and try again. If the problem persists, contact the administrator.',
},
server_error: {
title: 'Server error',
description: 'The server encountered an error while processing your request.',
action: 'Please try again later. If the problem persists, contact the administrator.',
},
service_unavailable: {
title: 'Service unavailable',
description: 'The service is currently under maintenance or temporarily overloaded.',
action: 'Please try again later.',
},
timeout: {
title: 'Request timed out',
description: 'The server did not respond within the expected time. This may be due to network issues or high server load.',
action: 'Please check your network connection and try again.',
},
network: {
title: 'Network error',
description: 'Unable to connect to the server. Please check your network connection.',
action: 'Verify your network is working and try again.',
},
unknown: {
title: 'Unexpected error',
description: 'An unexpected problem occurred during the request.',
action: 'Please try again. If the problem persists, contact the administrator.',
},
},
copy: 'Copy',
copied: 'Copied',
downloadStarted: 'Downloading: {name}',
downloadExpired: 'This file has expired or is no longer available. Ask the assistant to generate it again, then download.',
downloadFailed: 'Download failed: {reason}',
regenerate: 'Regenerate',
regenerateFailed: 'Regenerate failed',
rewindHere: 'Rewind to here',
rewindConfirm: 'This will delete this message and everything after it ({count} messages). Continue?',
rewindFailed: 'Rewind failed',
replyModel: 'Reply model: {model}',
tokenUsageTooltip: 'This turn used {total} tokens ({input} in · {output} out)',
tokenUsageTooltipDelegated: 'This turn used {total} tokens ({input} in · {output} out), of which {delegated} came from delegated sub-agents',
contextUsage: {
title: 'context used',
chipTooltip: 'Context usage (click for breakdown, estimated)',
system: 'System prompt',
tools: 'Tools & skills',
history: 'History',
current: 'Current input',
willCompact: 'Near the limit — older turns will be compacted',
estimated: 'estimated',
},
usageDetail: {
title: 'Token usage detail',
total: 'Total',
input: 'Input',
cacheHit: 'Cache hit',
cacheMiss: 'Cache miss',
cacheWrite: 'Cache write',
output: 'Output',
reasoning: 'Reasoning',
reply: 'Reply',
delegated: 'Delegated sub-agents',
hitRate: 'Cache hit rate',
legendHit: 'Hit',
legendWrite: 'Write',
legendMiss: 'Miss',
},
routing: {
kind: {
image: 'image',
video: 'video',
media: 'media',
},
tooltip: 'This turn was answered by {primary}; image attachments were captioned via {sidecar} ({sidecarProvider}) before reaching the primary model.',
hint: {
willRoute: 'Detected {kind} attachment. The current model "{primary}" does not support {kind} input — it will be captioned by "{sidecar}" first (sidecar mode, primary chat unchanged).',
notConfigured: 'Detected {kind} attachment. The current model does not support {kind} input and no sidecar model has been configured.',
videoReserved: 'Detected video attachment. Video sidecar is reserved (pre-configured "{sidecar}") but not yet wired in this version. Switch to a video-capable primary model instead.',
action: {
gotoSettings: 'Configure',
},
},
},
ttsPlay: 'Read Aloud',
ttsStop: 'Stop Reading',
conversations: 'Conversations',
newChat: 'New Chat',
loadingAgents: 'Loading employees...',
selectAgent: 'Please select an employee',
noConversations: 'No conversations yet',
startNewChat: 'Start a new chat above',
messages: '{count} messages',
configModel: 'Configure Model',
modelSaveFailed: 'Model switch was not saved — the next message may still use the previous model',
openSessions: 'Session Admin',
clearMessages: 'Clear Messages',
scrollToBottom: 'Back to bottom',
goToModelSettings: 'Go to Model Settings',
configModelFirst: 'Please configure a model first',
modelUnavailable: 'Current model is unavailable',
noActiveModel: 'No active model. Go to Model Management to select an available model before chatting.',
providerNotReady: 'The active model belongs to {name}, but this provider is not fully configured. Please complete the API Key or Base URL in Model Management.',
noAvailableModel: 'No available model.',
loadAgentsFailed: 'Failed to load employees',
loadModelFailed: 'Failed to load model state',
loadConversationsFailed: 'Failed to load conversations',
loadMessagesFailed: 'Failed to load messages',
deleteConversationFailed: 'Failed to delete conversation',
deleteConfirm: 'Delete this conversation? This cannot be undone.',
switchModelFailed: 'Failed to switch model',
searchModel: 'Search models…',
noMatchModel: 'No matching models',
// RFC-073: liveness hints shown in the model dropdown header
modelLivenessUnprobed: 'Checking availability',
modelLivenessCooldown: 'In cooldown ({seconds}s remaining)',
// RFC-074 PR-2: empty-state inside the model dropdown
noProvidersConfigured: 'No models available yet',
noModelsAvailableContactAdmin: 'No models are available. Contact a global administrator to configure one.',
goConfigure: 'Configure',
// Issue #81: liveness-aware popup state machine.
prompt: {
noActive: {
title: 'Pick a model first',
desc: 'No active model. Choose one in Model Management before chatting.',
},
unconfigured: {
title: '{name} is not fully configured',
desc: 'Missing: {fields}. {hint}',
},
removed: {
title: '{name} is unavailable',
descFallback: 'The last probe failed — check whether the service is running.',
},
cooldown: {
title: '{name} is temporarily unavailable',
desc: 'Auto-retrying in about {seconds}s.',
},
unprobed: {
title: 'Checking model availability',
desc: 'Hold on or refresh to retry.',
},
noModels: {
title: '{name} has no usable models',
desc: 'Discover models or add one manually.',
},
},
promptAction: {
fillBaseUrl: 'Fill Base URL',
fillApiKey: 'Fill API Key',
fillRequiredFields: 'Complete required fields',
startOAuth: 'Sign in',
testConnection: 'Test connection',
pullModel: 'Discover models',
waitCooldown: 'Retry now',
reprobe: 'Re-probe',
switchToModel: 'Switch to {name}',
fixThis: 'Fix',
},
recoverableBanner: {
message: '{name} is unavailable; sends will fall back to {fallback} automatically',
switched: 'Primary model unavailable; switched to {fallback}',
},
uploadFailed: 'File upload failed',
dropToUpload: 'Drop files or folders here',
copyFailed: 'Copy failed',
datePinned: 'Pinned',
dateToday: 'Today',
dateYesterday: 'Yesterday',
dateLast7Days: 'Last 7 Days',
dateEarlier: 'Earlier',
hasUnread: 'New activity',
filterByAgent: 'Filter by employee',
allAgents: 'All employees',
pin: 'Pin',
unpin: 'Unpin',
rename: 'Rename',
pinFailed: 'Failed to update pin',
selectMode: 'Select',
exitSelectMode: 'Exit selection',
selectAll: 'Select all',
deselectAll: 'Deselect all',
selectedCount: '{count} selected',
batchDelete: 'Delete selected',
batchDeleteConfirm: 'Delete the {count} selected conversations? This cannot be undone.',
batchDeleteFailed: 'Batch delete failed',
cronRunning: {
executing: 'Executing…',
fallbackName: 'Scheduled task',
},
suggestionIntro: 'Remember my Monday 10am team sync — draft the agenda 10 minutes before',
suggestionPoem: 'Ingest this PRD into my knowledge base — check there first before going to the web',
suggestionCode: 'Break this release into executable steps and pause for approval at key checkpoints',
suggestionWeather: 'What skills, tools, and MCP servers do you have? Show me how to add a new one',
suggestionPlan1: 'Decompose a full release plan into steps with approval gates at critical checkpoints',
suggestionPlan2: 'Break this complex task into trackable subtasks and report progress as you go',
approvalRequired: 'Approval Required',
approve: 'Approve',
deny: 'Deny',
approveAlways: 'Always approve',
approveAlwaysConversation: 'this conversation',
approveAlwaysAgent: 'this agent',
approveAlwaysUser: 'all my agents',
approveAlwaysCreated: 'Auto-approve rule added: {tool}',
approvalHint: 'or type <code>/approve</code> / <code>/deny</code>',
approved: '✅ Approved',
denied: '⛔ Denied',
approvalWaiting: 'Awaiting confirmation below...',
pendingApprovalPlaceholder: 'Type /approve or /deny to respond...',
messagePlaceholder: 'Type a message... (Enter to send, Shift+Enter for new line)',
thinkingOn: 'Deep thinking enabled',
thinkingOff: 'Click to enable deep thinking',
thinkingUnsupported: 'Current model does not support deep thinking',
slashMenuTitle: 'Skills',
slashMenuHint: '↑↓ navigate · Enter select · Esc dismiss',
slashMenuLoading: 'Loading skills…',
slashMenuEmpty: 'No matching skills',
slashMenuSearchPlaceholder: 'Search skills…',
useSkillDirective: 'Use the "{name}" skill: ',
subtitle: 'Memory · Knowledge base · Skills · Automation — your personal AI operating surface',
// Queue related
queuedSending: 'Sending queued message...',
queuedWillSend: 'Queued, will send after current step',
queuedCancel: 'Cancel',
queuedReplace: 'Message queued. Press Enter to replace...',
queuedBadge: '{count} queued',
// Stream status
streamPreparingContext: 'Preparing context...',
streamReadingMemory: 'Reading memory...',
streamReasoning: 'Analyzing...',
streamDraftingAnswer: 'Drafting answer...',
streamSummarizingObservations: 'Organizing information...',
streamThinking: 'Thinking...',
streamGenerating: 'Generating...',
streamExecutingTool: 'Executing tool...',
streamAwaitingApproval: 'Awaiting approval',
streamFinalizing: 'Finalizing...',
streamFailed: 'Interrupted',
streamInterrupting: 'Interrupting...',
streamQueued: 'Queued, waiting...',
streamReconnecting: 'Reconnecting...',
streamStopped: 'Stopped',
streamCompleted: 'Completed',
// Context compaction (compact_status SSE)
compactStart: 'Compacting context…',
compactPairSafe: 'Adjusting boundary to preserve tool_call pairs…',
compactSummarize: 'Summarizing the older turns…',
compactStartWithTokens: 'Compacting context… ({tokens})',
compactSummarizeWithCount: 'Summarizing the older turns… ({count} msgs)',
streamPreparingContextDetail: 'Collecting the current question, conversation history, and required context.',
streamReadingMemoryDetail: 'Looking up the most relevant long-term memory and recent notes for this question.',
streamReasoningDetail: 'Comparing options and organizing the answer structure based on the available information.',
streamDraftingAnswerDetail: 'The main analysis is done. Turning it into a readable answer now.',
streamSummarizingObservationsDetail: 'A larger tool result just came back. Condensing the key points before continuing.',
streamThinkingDetail: 'The model is still reasoning internally. Visible text may not appear immediately.',
streamGeneratingDetail: 'Visible output has started and more content should appear shortly.',
streamExecutingToolDetail: 'Reading files, querying data, or running the necessary operation for a more accurate answer.',
streamAwaitingApprovalDetail: 'Your confirmation is required before continuing.',
streamFinalizingDetail: 'Saving the result and closing out this response.',
streamFailedDetail: 'This run did not finish cleanly, but you can retry or keep asking follow-up questions.',
streamSlowShort: 'This step is taking a bit longer than usual, but it is still running.',
streamSlowSummarizing: 'Organizing a larger tool result. This step often takes longer.',
streamSlowReasoning: 'The model is doing heavier analysis here. This is not a frozen connection.',
streamSlowGeneral: 'Processing is still ongoing. The time is currently being spent in internal analysis.',
// Pre-token lifecycle stages
streamConnecting: 'Connecting to server...',
streamStarted: 'Connection established, preparing context...',
streamContextPrepared: 'Context ready, calling LLM...',
streamLlmRequestSent: 'Waiting for first token...',
streamElapsedSuffix: ' ({sec}s elapsed)',
// Per-iteration grouping
iterationEmpty: 'Iteration {index} interrupted (no output)',
contentRepetitionWarning: 'Repetitive content detected near the end (model artifact)',
supersededPreviewCollapsed: 'Collapsed: content drafted before tool execution (may not match actual results)',
supersededPreviewExpanded: 'Below is content drafted before tool execution (may not match actual results)',
earlierThinkingCollapsed: '{count} earlier reasoning span(s) collapsed — enable "Keep Full Reasoning" in settings to show them by default',
pendingReply: 'Preparing a reply…',
expand: 'Expand',
// INCOMPLETE truncation card (finishReason=incomplete)
incompleteTitle: 'Answer auto-truncated after repeated output was detected',
incompleteDescription: 'After the visible text above, the model started repeating itself (or stalled in thinking with no output) and was cut short to avoid wasted tokens. Click below to regenerate the full answer, or refine your prompt to focus on the missing parts.',
incompleteRetry: 'Regenerate',
// EVIDENCE_INSUFFICIENT info card (finishReason=evidence_insufficient)
evidenceTitle: 'Run completed — some source references could not be verified',
evidenceDescription: 'The full answer is preserved above. The classes/files listed in the trailing "[evidence insufficient] …" line were not actually opened during this run; the model may have inferred them from naming. Ask a follow-up to have each one read before relying on those references.',
// feedback_event card — emitted when a turn ends in a non-transient error.
// Buttons are data-driven from the event's `actions` array; labels live
// under chat.feedback.{action} so a future backend can ship a new action
// (e.g. "switch_model") with a single new translation key.
feedback: {
title: 'This turn failed',
description: 'The model call did not finish normally; the error reason is shown above. Click Retry to replay the same prompt, or Report to copy the error details for a bug report.',
retry: 'Retry',
regenerate: 'Regenerate',
report: 'Report',
reportCopied: 'Error details copied to clipboard',
reportFailed: 'Copy failed — check browser permissions',
},
// Attachment inline preview dialog
preview: {
download: 'Download',
fullscreen: 'Fullscreen',
exitFullscreen: 'Exit fullscreen',
loading: 'Loading preview…',
failed: 'Preview failed to load — please download the file instead',
unsupported: 'This format cannot be previewed inline — please download it',
truncated: 'Showing first {max} rows — download for the full content',
htmlSandboxNotice: 'HTML renders in an isolated sandbox with no access to your session',
},
// Approval bar
approvalAllow: 'Allow',
approvalExecute: 'to execute?',
// Time format
timeJustNow: 'Just now',
timeMinutesAgo: '{n}m ago',
timeHoursAgo: '{n}h ago',
},
docs: {
title: 'Docs',
},
plans: {
title: 'Plan Board',
subtitle: "Lay out an employee's plans and steps as a board by status",
selectAgent: 'Select an employee',
allEmployees: 'All employees',
unknownAgent: 'Unknown employee',
noPlansAll: 'No plans across the team yet. Once a plan-execute employee runs a multi-step task, it shows up here.',
detail: 'Plan detail',
assignee: 'Assignee',
progress: 'Progress',
created: 'Created',
output: 'Output',
runs: 'Ran {n}× for the same goal',
more: 'Show {n} more',
collapse: 'Collapse',
goals: 'Goals',
activeGoals: 'Active Goals',
noGoals: 'No active goals',
plans: 'Plans',
noPlans: 'No plans for this employee yet',
noAgent: 'Select an employee to begin',
selectPlan: 'Pick a plan on the left to view its step board',
untitled: 'Untitled plan',
steps: 'steps',
viewResult: 'Click to view result',
delegatedTo: 'Delegated to',
queuedSpillHint: 'Queued steps of an in-progress plan — the same plan also shows in In Progress',
col: {
pending: 'To Do',
running: 'In Progress',
completed: 'Done',
failed: 'Failed',
},
},
contentCalendar: {
title: 'Content Calendar',
subtitle: 'Ledger of produced 公众号 / 小红书 pieces — what is packaged, published, or pending upload (auto-recorded on delivery).',
total: 'All',
packaged: 'Packaged',
draft: 'Draft',
published: 'Published',
allPlatforms: 'All platforms',
refresh: 'Refresh',
platform: 'Platform',
colTitle: 'Title',
topic: 'Topic',
status: 'Status',
createTime: 'Created',
publishTime: 'Published',
st_draft: 'Draft',
st_packaged: 'Packaged',
st_published: 'Published',
st_failed: 'Failed',
st_unknown: 'Unknown',
},
nav: {
dashboard: 'Dashboard',
chat: 'Chat',
control: 'Control',
teams: 'Teams',
channels: 'Channels',
sessions: 'Sessions',
agent: 'Agent',
workspace: 'Workspace',
agentContext: 'Agent Context',
skills: 'Skills',
contentCalendar: 'Content Calendar',
wiki: 'Wiki',
enterprise: 'Enterprise',
tools: 'Tools',
plugins: 'Plugins',
core: 'Core',
connect: 'Connect',
system: 'System',
dsh: 'DeepSeek Harness',
datasources: 'Datasources',
mcpServers: 'MCP Connections',
mcpConnections: 'MCP Connections',
toolsCatalog: 'Tools (Catalog)',
activity: 'Activity',
acpEndpoints: 'ACP Endpoints',
settingsGroup: 'Settings',
agents: 'Employees',
security: 'Security',
docs: 'Docs',
tokenUsage: 'Token Usage',
cronJobs: 'Cron Jobs',
scheduler: 'Scheduler',
workflows: 'Workflows',
triggers: 'Triggers',
settings: 'Settings',
logout: 'Logout',
memory: 'Memory',
themeLight: 'Light',
themeDark: 'Dark',
themeSystem: 'System',
themeLabel: 'Appearance',
languageLabel: 'Language',
appearance: 'Appearance & Language',
roleUser: 'User',
roleAdmin: 'Admin',
shortcutAgents: 'Employees',
shortcutNew: 'New',
shortcutsHint: 'Ctrl+K {agents} | Ctrl+N {newAction}',
},
notifications: {
pendingApprovals: '{n} tool call(s) pending approval',
failedWikiJobs: '{n} knowledge base material(s) failed or degraded',
},
live: {
kicker: 'Live',
title: 'See what your employees are doing',
attention: 'Someone needs your attention',
unknownAgent: 'Employee',
aTool: 'a tool',
orphan: 'no one watching',
orphanHint: 'The browser tab closed but this employee is still working.',
subagentsBadge: '{n} helping',
teamRuns: {
title: 'Team runs',
lead: 'Lead',
elapsed: 'Elapsed',
active: 'Active',
waiting: 'Waiting for dependencies',
review: 'Awaiting review',
stuck: 'Stuck',
cancelled: 'Cancelled',
completed: 'Completed',
failed: 'Failed',
finalizing: 'Finalizing',
openRun: 'Open run',
noWorkers: 'No worker tasks',
otherSessions: 'Other live sessions',
loadError: 'Team run context could not be refreshed.',
},
headline: {
loading: 'Looking around...',
allQuiet: 'All quiet. No one is working right now.',
working: 'Your {n} employees are at work.',
someoneNeedsAttention: '{n} need a look from you.',
workingAlone: '{running} working — {orphan} with no one listening.',
},
status: {
running: '{n} running',
stuck: '{n} stuck',
orphan: '{n} unwatched',
helpers: '{n} helpers',
},
filters: {
all: 'All',
working: 'Working',
attention: 'Needs attention',
quiet: 'Unwatched',
},
saying: {
thinking: 'Thinking...',
replying: 'Writing the reply...',
planning: 'Planning the next steps...',
summarizing: 'Wrapping up...',
usingToolBare: 'Running',
usingSomething: 'Using a tool...',
awaitingApproval: 'Waiting for your approval.',
toolSilentBare: 'Stuck on a tool, no progress.',
idleSilent: 'Quiet for too long.',
hardCap: 'Has been at this far too long.',
},
callout: {
toolSilent: '{tool} has been silent for {time}. It looks stuck.',
idleSilent: 'No sign of activity for {time}.',
hardCap: 'Running for {time} — well past normal.',
},
dotTitle: {
healthy: 'Working normally',
stuck: 'Looks stuck',
orphan: 'Running with no audience',
},
detail: {
who: 'Who',
doing: 'What it\'s doing',
runningFor: 'Running for',
lastHeard: 'Last heard from',
ago: 'ago',
audience: 'Audience',
noOneListening: 'No one watching the chat right now.',
peopleListening: '{n} listening',
helpers: 'Helpers',
session: 'Session',
},
actions: {
live: 'Live',
paused: 'Paused',
pauseRefresh: 'Pause auto-refresh',
resumeRefresh: 'Resume auto-refresh',
tidyUp: 'Tidy up',
stop: 'Stop',
stopHint: 'Ask it to wind down at the next checkpoint.',
endIt: 'End it',
endHint: 'Force it to stop now.',
close: 'Close',
},
confirm: {
stopTitle: 'Stop this employee?',
stopBody: 'Ask {name} to wind down. The current step will finish first.',
endTitle: 'Force {name} to stop now?',
endBody: 'This will stop the employee immediately. Any work in progress will be lost. The user will see "stopped by admin".',
subTitle: 'Stop this helper?',
subBody: 'Stop {name}. The owning employee will keep running.',
sweepTitle: 'Tidy up stuck employees?',
sweepBody: '{n} employee(s) look stuck. Stop all of them now?',
},
toast: {
stopped: 'Asked it to stop.',
ended: 'Stopped.',
subStopped: 'Helper stopped.',
swept: 'Tidied up {n} employee(s).',
},
empty: {
allQuiet: 'All quiet.',
hint: 'No employees are working right now. This page will light up when one is.',
},
time: {
justNow: 'just now',
seconds: '{n}s',
minutes: '{n}m {s}s',
hours: '{n}h {m}m',
},
errors: {
loadFailed: 'Could not load runtime status.',
},
layout: {
grid: 'Grid',
board: 'Board',
gridHint: 'Grid view',
boardHint: 'Lifecycle board view',
},
board: {
running: 'Running',
attention: 'Needs Attention',
done: 'Done',
failed: 'Unmet',
queuedHint: '{n} queued',
goalLabel: 'Goal',
emptyCol: 'Empty',
noGoal: 'No goal',
},
},
doctor: {
title: 'System Diagnostics',
subtitle: 'Local instance health snapshot',
database: 'Connected database',
checking: 'Checking...',
allGood: 'All systems healthy',
hasWarnings: '{count} warning(s)',
hasErrors: '{count} issue(s)',
refresh: 'Re-check',
lastChecked: 'Checked {time} ago',
diagnose: 'Diagnose',
checksPassed: '{healthy}/{total} checks passed',
checksLoading: 'Sampling system signals',
},
// Issue #81: provider-level hint / status text shared across chat popup and ModelSelector.
provider: {
hint: {
ollamaBaseUrlExample: 'Example: {example}',
lmstudioBaseUrlExample: 'Example: {example} (LM Studio default port)',
llamacppBaseUrlExample: 'Example: {example} (llama-server default port)',
vllmBaseUrlExample: 'Example: {example}',
openaiCompatBaseUrlExample: 'Example: OpenAI-compatible endpoint, e.g. {example}',
},
status: {
unconfigured: 'Not configured',
removed: 'Unavailable',
cooldown: 'Retry in {s}s',
unprobed: 'Probing',
},
},
settings: {
title: 'Settings',
kicker: 'Configuration',
layoutDesc: 'Tune models, system behavior, and media capabilities without letting the product collapse into a pile of controls.',
sections: {
model: 'Model Management',
system: 'System',
image: 'Image Generation',
tts: 'Text-to-Speech',
stt: 'Speech Recognition',
music: 'Music Generation',
video: 'Video Generation',
model3d: '3D Generation',
workspace: 'Workspace',
featureFlags: 'Feature Flags',
about: 'About',
advanced: 'Advanced',
skillCurator: 'Skill Curator',
proxy: 'Network Proxy',
localTools: 'Local Tools',
},
models: {
sidecar: {
title: 'Multimodal sidecar',
hint: "When the primary model can't handle images or video, the sidecar model captions the attachment to text first.",
notConfigured: 'Not configured (skip attachment)',
idle: 'Off',
reserved: 'Reserved',
capable: 'Vision',
vision: {
label: 'Vision sidecar model',
desc: 'Invoked once per uploaded image to produce a structured description. The primary chat model is unchanged. Any enabled model can be selected; the "Vision" tag marks models already recognized as supporting image input.',
empty: 'No chat model is enabled yet. Add and enable one under "Cloud Models" above.',
},
video: {
label: 'Video sidecar model (reserved)',
desc: 'Reserved for a future iteration: when the user uploads a video, this model will sample frames and describe them. Not yet wired in v1.',
empty: 'No chat model is enabled yet.',
},
},
},
featureFlags: {
title: 'Feature Flags',
description: 'Runtime-toggleable feature switches. Edits apply immediately on the writing instance and propagate to peers within one cache refresh tick (≈30 s); no server restart required.',
empty: 'No feature flags registered.',
enabled: 'Enabled {key}',
disabled: 'Disabled {key}',
toggleFailed: 'Toggle failed, please retry',
footer: 'New features ship disabled by default. After enabling, watch the metrics and logs for the relevant module before widening the rollout.',
notWired: 'Not yet wired',
notWiredTooltip: 'This flag is reserved but the underlying feature is not implemented yet — toggling has no effect.',
scope: {
kb: 'KB whitelist',
user: 'User whitelist',
rollout: 'Rollout {pct}%',
},
descriptions: {
'wiki.ocr.enabled': 'Image OCR / vision-in pipeline for wiki uploads — captions images via a vision model on upload',
'wiki.compile.4stage.enabled': 'Four-stage knowledge base compilation pipeline (Summary / Planning / Generation / Index)',
'wiki.compile.cache.enabled': 'Prompt cache layer for the wiki compile pipeline — cuts ~30% input tokens on cache hits',
'wiki.confidence.enabled': 'Confidence taxonomy on wiki relations and pages (EXTRACTED / INFERRED / AMBIGUOUS / UNVERIFIED)',
'wiki.hot_cache.enabled': 'KB-level recent-activity snapshot injected into the agent system prompt',
'wiki.graph.insights.enabled': 'Wiki graph insights panel (surprising connections / gaps / bridges)',
'wiki.graph.adamic_adar.enabled': 'Adamic-Adar topology signal, additive to the existing four relevance signals',
'wiki.graph.boundary.enabled': 'Boundary score for surfacing dangling pages (high out-degree, low in-degree)',
'wiki.relation.cache.enabled': 'Persistent cache for wiki page-to-page relation computation (default on, performance baseline)',
},
},
modelTitle: 'Model Management',
modelDesc: 'Manage provider presets and default model selection',
systemTitle: 'System',
systemDesc: 'Language and runtime behavior settings',
aboutTitle: 'About MateClaw',
aboutDesc: 'Version and system information',
about: {
heroDesc: 'A personal AI operating system that places models inside a continuous loop of context, memory, execution, knowledge, and delivery.',
foundationTitle: 'Built With',
pillars: {
contextTitle: 'Keep context continuous',
contextDesc: 'Models, knowledge, workspace, and the active task live inside the same frame instead of starting over every turn.',
executionTitle: 'Make execution real',
executionDesc: 'Tools, channels, and async tasks are the real execution surface, not bolted-on extras.',
memoryTitle: 'Make memory compound',
memoryDesc: 'Memory is shaped, recalled, and turned into leverage for the next task.',
},
},
model: {
title: 'Model Management',
desc: 'Configure model providers, credentials, and model lists',
permissionTitle: 'Global administrator access required',
permissionDesc: 'Provider configuration contains system-level credentials. You can still switch among enabled models in Chat; contact a global administrator to add or change models.',
addProvider: 'Add Provider',
localProviders: 'Local Models',
cloudProviders: 'Cloud Models',
active: 'Active',
setActive: 'Set Active',
configured: 'Configured',
partial: 'Partial',
unavailable: 'Unavailable',
builtin: 'Built-in',
custom: 'Custom',
notSet: 'Not set',
noModels: 'No models',
baseUrl: 'Base URL',
apiKey: 'API Key',
apiKeyInput: 'Enter API Key',
leaveBlankKeep: 'Leave blank to keep the current value',
modelCount: '{count} models',
createTitle: 'Add Provider',
editTitle: 'Provider Settings',
manageTitle: 'Manage Models',
addModel: 'Add Model',
embedding: {
title: 'Embedding Models',
hint: 'Used for knowledge base semantic search; shares the provider API key with chat models',
loading: 'Loading...',
empty: 'No embedding models yet — add one with the form below.',
defaultBadge: 'Default',
builtinBadge: 'Built-in',
test: 'Test',
testing: 'Testing...',
testPassed: 'Passed · {dim} dimensions',
setDefault: 'Set as default',
delete: 'Delete',
deleteConfirm: 'Delete embedding model "{name}"?',
deleteFailed: 'Failed to delete',
deleted: 'Embedding model deleted',
addTitle: 'Add Embedding Model',
addHint: 'Pick a configured provider and enter an embedding model name it supports (e.g. text-embedding-3-small, bge-m3).',
provider: 'Provider',
selectProvider: 'Select a provider',
modelName: 'Model Name',
modelNamePlaceholder: 'e.g. text-embedding-3-small',
displayName: 'Display Name',
displayNamePlaceholder: 'Defaults to the model name',
add: 'Add',
adding: 'Adding...',
added: 'Embedding model added',
addFailed: 'Failed to add',
nameRequired: 'Select a provider and enter a model name',
noProviders: 'Enable and configure at least one provider above first.',
},
// Inline API Key on the provider card — primary entry point for the
// 90% case (paste a key, hit save). Modal stays for advanced fields.
inlineApiKeyTitle: 'API Key',
inlineApiKeyPlaceholder: 'Paste API key and press Enter',
inlineApiKeyMasked: 'Set ••••••••',
inlineApiKeyChange: 'Change',
inlineApiKeySaving: 'Saving…',
inlineApiKeySaved: 'API key saved',
inlineApiKeySaveFailed: 'Failed to save API key',
inlineApiKeyEnvHint: 'Or set {var} in your environment and restart',
// Headline status pill on the card — what the user reads first.
statusNotSet: 'NOT SET',
statusReady: 'READY',
statusError: 'ERROR',
modelCountAvailable: '{count} models available',
activeChanged: 'Active model updated',
providerSaved: 'Provider settings saved',
providerDeleted: 'Provider deleted',
modelAdded: 'Model added',
modelRemoved: 'Model removed',
modelAddFailed: 'Failed to add model',
modelRemoveFailed: 'Failed to remove model',
activeChangeFailed: 'Failed to change active model',
deleteConfirm: 'Delete provider "{name}"?',
removeConfirm: 'Remove model "{name}"?',
contextWindow: {
label: 'Context window',
edit: 'Set window',
placeholder: 'Empty = use default',
hint: 'Maximum input tokens the model accepts. Drives when history gets compacted and the context usage shown in chat. Leave empty to use the built-in window table or the global default.',
sourceConfigured: 'configured',
sourceCatalog: 'built-in table',
sourceDefault: 'global default',
invalid: 'Enter a whole number between 1024 and 20000000',
updated: 'Context window updated',
updateFailed: 'Failed to update context window',
},
generateConfigInvalidJson: 'Generate kwargs is not valid JSON',
generateConfigMustBeObject: 'Generate kwargs must be a JSON object',
advancedSettings: 'Advanced Settings',
protocolHint: 'This selects the provider API protocol, not an internal class name.',
protocolOpenAI: 'OpenAI Compatible (Chat Completions)',
protocolAnthropic: 'Anthropic (Messages API)',
protocolGemini: 'Gemini Native',
protocolDashScope: 'DashScope Native',
advancedHint: "Use this for generation options such as temperature, max_tokens, top_p, reasoning effort, enable_search/search_strategy, headers, and completions_path. Any other top-level key you add here is forwarded as-is into the outbound request body — for example, {'{'} \"chat_template_kwargs\": {'{'} \"enable_thinking\": false {'}'} {'}'} disables thinking mode on vLLM-served Qwen models. Keys nested inside a \"chatOptions\" object are not forwarded this way.",
requireApiKeyHint: 'Turn this off for internal or local OpenAI-compatible services that do not require auth. Connection tests will omit the Authorization header.',
fallbackPriorityHint: 'Pool try-order (lower wins): 0 = excluded, 1 = first in line, 2 = second, and so on. Providers sharing the same value are tried in alphabetical order of their ID.',
fallbackBadge: 'Preferred #{priority}',
fallbackBadgeTitle: 'Position in the available pool — lower numbers are tried first',
// RFC-009 Phase 4: provider pool status badges
poolBadgeInPool: 'In Pool',
poolBadgeInPoolTitle: 'Passed startup probe; participates in failover',
poolBadgeRemoved: 'Removed',
poolBadgeRemovedTitle: 'Removed from pool ({source}): {message}',
poolBadgeCooldown: 'Cooling Down',
poolBadgeCooldownTitle: 'Recent failures; recovers automatically in {seconds}s',
poolSourceAuthError: 'API key invalid',
poolSourceBilling: 'Billing/quota issue',
poolSourceModelNotFound: 'Model not found',
poolSourceInitProbe: 'Startup probe failed',
poolSourceManual: 'Manually removed',
poolReprobe: 'Reprobe',
poolReprobing: 'Probing...',
poolReprobeOk: 'Probe passed; back in the available pool',
poolReprobeFail: 'Probe failed: {error}',
// RFC-073: status-pill labels driven by Liveness
livenessLive: 'Live',
livenessCooldown: 'Cooling down',
livenessRemoved: 'Disconnected',
livenessUnprobed: 'Probing',
livenessUnprobedTooltip: 'Checking availability after startup; will refresh shortly',
livenessUnconfigured: 'Needs setup',
// RFC-074 PR-2: enable / disable / drawer
enableProviderCta: '+ Enable Provider',
addCustomProvider: '+ Custom',
enable: 'Enable',
disable: 'Disable',
alreadyEnabled: 'Enabled',
disableConfirm: 'Disable provider "{name}"? It will disappear from the dropdown but its config is kept — you can re-enable any time.',
defaultSwitchedToast: 'Default model switched to {provider} / {model}',
addProviderDrawerTitle: 'Add Provider',
addProviderDrawerSubtitle: 'Once enabled the provider appears in the model dropdown. Cloud providers still need an API key after enabling.',
catalogEmpty: 'Catalog is empty',
catalogSearchPlaceholder: 'Search providers…',
catalogSearchEmpty: 'No providers match “{query}”',
enabledToast: 'Enabled {name}',
emptyTitle: 'No providers enabled yet',
emptyDesc: 'Enable a built-in from the catalog, or create a custom provider.',
searchHint: 'When enabled, the LLM will use its built-in search engine to retrieve real-time information (DashScope/Kimi/OpenAI supported).',
searchStrategyDefault: 'Default',
oauthTitle: 'OpenAI OAuth Login',
oauthLogin: 'OAuth Login',
oauthConnected: 'Connected',
oauthDisconnected: 'Not Connected',
oauthDisconnect: 'Disconnect',
oauthHint: 'Sign in with your OpenAI account to use ChatGPT Plus/Pro member quota (not API credits).',
oauthLoginSuccess: 'OpenAI OAuth login successful',
oauthRevokeSuccess: 'OpenAI OAuth disconnected',
// Device Authorization Grant (used for remote deployments where the browser
// cannot reach localhost:1455 for the PKCE callback).
oauthDeviceTitle: 'Sign in to ChatGPT (Device Code)',
oauthDeviceStep1: '1. Open this link in any browser or on your phone:',
oauthDeviceStep2: '2. Enter this verification code:',
oauthDeviceStep3: 'This page will connect automatically once you finish ({seconds}s remaining)',
oauthDeviceExpired: 'The device code expired. Please try again.',
copy: 'Copy',
copied: 'Copied',
copyFailed: 'Clipboard refused — please select and copy manually',
// RFC-062: Claude Code OAuth (subscription piggyback)
claudeCodeOauthDetect: 'Detect Claude Code Login',
claudeCodeOauthHint: 'Reuses your local Claude Code Pro/Max subscription. Sign in via the Claude Code app first, then click "Detect" to pick up the credentials.',
claudeCodeOauthInstructions: 'No Claude Code credentials found. Install Claude Code, sign in with a Pro/Max account, then click Detect again.',
claudeCodeOauthRevokeHint: 'Sign out from the Claude Code app to revoke. MateClaw does not modify Claude Code\'s on-disk credentials.',
providerIdPlaceholder: 'e.g. my-local-gemma',
providerIdHint: 'Used only as an internal key. Lowercase letters/digits, plus . _ - are fine — no slashes or spaces (cannot be changed after create).',
providerIdInvalid: 'Provider ID may only contain letters, digits, dot, underscore, and hyphen (no slashes or spaces). Must start with a letter or digit and be 164 chars.',
fields: {
providerId: 'Provider ID',
providerName: 'Provider Name',
defaultBaseUrl: 'Default Base URL',
apiKeyPrefix: 'API Key Prefix',
requireApiKey: 'Requires API Key',
protocol: 'Protocol',
generateKwargs: 'Generate Kwargs (JSON)',
fallbackPriority: 'Pool Try-Order',
enableSearch: 'Built-in Search',
searchStrategy: 'Search Strategy',
modelId: 'Model ID',
modelDisplayName: 'Display Name',
},
actions: {
manageModels: 'Manage Models',
providerSettings: 'Settings',
},
hints: {
openai: 'OpenAI defaults to https://api.openai.com/v1',
azureOpenai: 'Use an Azure resource endpoint such as https://<resource>.openai.azure.com/openai/v1',
anthropic: 'Anthropic defaults to https://api.anthropic.com',
ollama: 'Ollama usually runs locally at http://localhost:11434',
lmstudio: 'LM Studio usually exposes an OpenAI-compatible endpoint at http://localhost:1234/v1',
gemini: 'Gemini defaults to https://generativelanguage.googleapis.com',
openrouter: 'OpenRouter defaults to https://openrouter.ai/api/v1, aggregating multiple model providers',
zhipu: 'Zhipu AI defaults to https://open.bigmodel.cn/api/paas/v4',
zhipuIntl: 'Zhipu AI International uses https://open.z.ai/api/paas/v4',
volcengine: 'Volcano Engine (Doubao) defaults to https://ark.cn-beijing.volces.com/api/v3, requires an endpoint created on the Volcengine Ark platform',
xiaomiMimo: 'Xiaomi MiMo defaults to https://api.xiaomimimo.com/v1 (OpenAI-compatible). API keys are issued at platform.xiaomimimo.com',
openaiCompatible: 'OpenAI-compatible services usually use a Base URL ending with /v1',
},
discovery: {
discover: 'Discover Models',
discovering: 'Discovering...',
configureFirst: 'Add an API Key under “Configure” first, then discover all of this providers models',
testConnection: 'Test Connection',
testing: 'Testing...',
testModel: 'Test',
testingModel: 'Testing...',
discoveredTitle: 'Discovered New Models',
discoveredCount: 'Found {total} models, {count} are new',
noNewModels: 'No new models found, all remote models already exist',
selectAll: 'Select All',
addSelected: 'Add Selected',
adding: 'Adding...',
addedCount: '{count} models added',
connectionOk: 'Connection successful',
connectionFail: 'Connection failed',
latency: 'Latency {ms}ms',
modelOk: 'Model available',
modelFail: 'Model unavailable',
probeVerified: 'Verified',
probeVerifiedTitle: 'Verified reachable',
probeFailedBanner: '⚠ {count} discovered model(s) failed the reachability probe and were not listed above:',
probeUnreachable: 'not reachable',
},
},
fields: {
name: 'Display Name',
provider: 'Provider',
modelName: 'Model Name',
description: 'Description',
temperature: 'Temperature',
maxTokens: 'Max Tokens',
topP: 'Top P',
language: 'Language',
streamEnabled: 'Stream Response',
debugMode: 'Debug Mode',
showThinking: 'Show Thinking Process',
thinkingFull: 'Keep Full Reasoning',
workspaceStorageRoot: 'Default Workspace Storage Path',
searchEnabled: 'Enable Search',
searchProvider: 'Search Provider',
searchFallbackEnabled: 'Fallback on Failure',
serperApiKey: 'Serper API Key',
weixinoaAppId: 'Official Account AppID',
weixinoaAppSecret: 'Official Account AppSecret',
serperBaseUrl: 'Serper Base URL',
tavilyApiKey: 'Tavily API Key',
tavilyBaseUrl: 'Tavily Base URL',
duckduckgoEnabled: 'DuckDuckGo (Keyless)',
searxngBaseUrl: 'SearXNG Base URL',
searchProviderAuto: 'Auto (recommended)',
sttEnabled: 'Enable Speech Recognition',
sttProvider: 'Preferred STT Provider',
sttFallbackEnabled: 'Provider Fallback',
// Issue #76: route the OpenAI-compat STT endpoint
sttOpenAiCompatProviderId: 'OpenAI-compatible Credential',
sttOpenAiCompatModel: 'OpenAI-compatible Model',
musicEnabled: 'Enable Music Generation',
musicProvider: 'Preferred Music Provider',
musicFallbackEnabled: 'Provider Fallback',
model3dEnabled: 'Enable 3D Model Generation',
model3dProvider: 'Preferred 3D Provider',
model3dFallbackEnabled: 'Provider Fallback',
ttsEnabled: 'Enable Text-to-Speech',
ttsProvider: 'Preferred TTS Provider',
ttsFallbackEnabled: 'Provider Fallback',
ttsAutoMode: 'Auto Read Aloud',
ttsSpeed: 'Speed',
imageEnabled: 'Enable Image Generation',
imageProvider: 'Preferred Image Provider',
imageFallbackEnabled: 'Provider Fallback',
openaiStatus: 'OpenAI Status',
videoEnabled: 'Enable Video Generation',
videoProvider: 'Preferred Video Provider',
videoFallbackEnabled: 'Provider Fallback',
dashscopeStatus: 'DashScope Status',
zhipuApiKey: 'Zhipu API Key',
zhipuBaseUrl: 'Zhipu API Base URL',
falApiKey: 'fal.ai API Key',
klingAccessKey: 'Kling Access Key',
klingSecretKey: 'Kling Secret Key',
runwayApiKey: 'Runway API Key',
minimaxApiKey: 'MiniMax API Key',
minimaxRegion: 'MiniMax API Region',
},
hints: {
provider: 'Current implementation applies DashScope model options at runtime.',
language: 'Interface language preference stored in backend settings.',
streamEnabled: 'Controls whether chat prefers streaming output in UI settings.',
debugMode: 'Reserved for showing more execution details later.',
showThinking: 'Render the model\'s reasoning in chat (collapsible). The "Deep Thinking" toggle in the chat input controls whether the model thinks; this only controls whether it is displayed.',
thinkingFull: 'Show every iteration\'s reasoning instead of only the one that produced the answer. A tool-heavy turn has a dozen spans; all of them is what lets you review why each tool was called, so turn this off when you only want the conclusion. Either way the full reasoning is persisted and available from the trajectory export.',
workspaceStorageRoot: 'Files of new conversations and workspaces are stored under this path (the global fallback directory). Takes effect immediately and never migrates existing data; leave blank to use the server default. Must be an absolute path.',
workspaceStorageRootPlaceholder: 'Leave blank for the server default, e.g. /data/mateclaw/workspace',
searchEnabled: 'When disabled, the search tool will be unavailable to agents.',
searchProvider: 'Primary search provider used when the search tool is invoked.',
searchFallbackEnabled: 'Automatically try the other provider when the primary one fails.',
serperApiKey: 'API key for Google Serper search, get it from serper.dev.',
weixinoaAppId: 'AppID from the Official Account admin (Development → Basic Config).',
weixinoaAppSecret: 'AppSecret (write-only, never echoed). Once set, gzh_publish can push articles into the draft box. Note: the calling server\'s public IP must be whitelisted in the Official Account admin, and a verified account is required; localhost usually cannot reach the WeChat API.',
serperBaseUrl: 'Usually no need to change unless using a custom proxy.',
tavilyApiKey: 'API key for Tavily search, get it from tavily.com.',
tavilyBaseUrl: 'Usually no need to change unless using a custom proxy.',
duckduckgoEnabled: 'Free keyless search fallback. No API key required. Enabled by default as a zero-config fallback.',
searxngBaseUrl: 'Self-hosted SearXNG instance URL. Auto-configured when deploying via Docker.',
searchProviderAuto: 'Let the system auto-pick the first configured provider by priority.',
sttEnabled: 'Enable speech-to-text for voice messages. Both providers reuse existing API keys.',
sttProvider: 'Select preferred STT provider. Auto mode picks the first available one.',
sttFallbackEnabled: 'Automatically try other configured providers if the preferred one fails.',
openaiSttInfo: 'Reuses OpenAI API Key from Model Management. Whisper model, supports multilingual auto-detection.',
dashscopeSttInfo: 'Reuses the DashScope API Key from Model Management. Qwen3-ASR Flash transcribes each complete recording over HTTP with multilingual and Chinese dialect support.',
// Issue #76
sttOpenAiCompatProviderId: 'Pick any OpenAI-compatible provider from Model Management as the credential source (baseUrl + API key). Beyond OpenAI itself this covers self-hosted FunASR, SiliconFlow, Groq, Together, Volcano, Qiniu, and any custom provider you add with the OpenAI-compatible protocol.',
sttOpenAiCompatModel: 'Model id sent in the multipart "model" field. Defaults to whisper-1; use paraformer-large for FunASR, or whatever id your vendor documents.',
sttOpenAiCompatNote: 'Tip: to plug in a private ASR service, head to Model Management → Add Custom Provider → pick the "OpenAI Compatible" protocol → fill in Base URL (and an optional API key), then come back and select it here.',
musicEnabled: 'Enable to let Agent use the music generation tool. Google Lyria reuses Google Key.',
musicProvider: 'Select preferred music provider. Auto mode prioritizes Google Lyria.',
musicFallbackEnabled: 'Automatically try other configured providers if the preferred one fails.',
googleLyriaInfo: 'Reuses Google API Key from Model Management. Lyria 3 model, supports lyrics and instrumental.',
minimaxMusicInfo: 'Reuses MiniMax API Key from video settings. music-2.5+ model, supports lyrics and instrumental.',
model3dEnabled: 'Enable to let Agent use model3d_generate (.glb output). Configure the hunyuan-3d provider with SecretId:SecretKey first under Models & Credentials.',
model3dProvider: 'Select preferred 3D provider. Auto mode picks the first available one.',
model3dFallbackEnabled: 'Automatically try other configured providers if the preferred one fails.',
hunyuan3dInfo: 'Tencent Hunyuan 3D (ai3d.tencentcloudapi.com). HY-3D-3.1 / HY-3D-3.0 use the Pro action; HY-3D-Express uses the Rapid action. Configure as SecretId:SecretKey under Models & Credentials.',
ttsEnabled: 'Enable to use TTS via Read Aloud button or auto mode. Edge TTS is free, no API key needed.',
ttsProvider: 'Select preferred TTS provider. Auto mode prioritizes free Edge TTS.',
ttsFallbackEnabled: 'Automatically try other configured providers if the preferred one fails.',
ttsAutoMode: 'off = manual read aloud only, always = auto-play voice for every AI reply.',
ttsSpeed: 'Speech playback speed. 1.0 is normal speed.',
edgeTtsInfo: 'Microsoft Edge built-in TTS. Free to use, supports Chinese, English, Japanese and more. Auto-detects language.',
dashscopeTtsInfo: 'Reuses DashScope API Key from Model Management. Uses CosyVoice model with multiple Chinese voices.',
openaiTtsInfo: 'Reuses OpenAI API Key from Model Management. Supports alloy, echo, nova and other voice styles.',
imageEnabled: 'Enable to let Agent use the image generation tool. Requires at least one configured image provider.',
imageProvider: 'Select preferred image provider. Auto mode picks the first available one.',
imageFallbackEnabled: 'Automatically try other configured providers if the preferred one fails.',
dashscopeImageStatus: 'Reuses the DashScope API Key configured in Model Management. No extra setup needed.',
openaiImageStatus: 'Reuses the OpenAI API Key configured in Model Management. No extra setup needed.',
zhipuImageApiKey: 'Get from bigmodel.cn. CogView-3-Flash model is free. Shared with video generation.',
falImageApiKey: 'Get from fal.ai for Flux image generation models. Shared with video generation.',
googleImagenInfo: 'Reuses the Gemini API Key from Model Management. Nano Banana Pro (Gemini 3 Pro Image) native generation — text-to-image and image editing.',
minimaxImageInfo: 'Reuses MiniMax API Key from video settings. image-01 model, multiple aspect ratios, up to 9 images.',
videoEnabled: 'Enable to let Agent use the video generation tool. Requires at least one configured video provider.',
videoProvider: 'Select preferred video provider. Auto mode picks the first available one.',
videoFallbackEnabled: 'Automatically try other configured providers if the preferred one fails.',
dashscopeVideoStatus: 'Reuses the DashScope API Key configured in Model Management. No extra setup needed.',
zhipuApiKey: 'Get from bigmodel.cn. CogVideoX-Flash model is free.',
zhipuBaseUrl: 'Usually no need to change unless using a custom proxy.',
falApiKey: 'Get from fal.ai. One key accesses Kling, Runway, Luma and more.',
klingAccessKey: 'Get from Kuaishou Open Platform for Kling video generation API.',
klingSecretKey: 'Paired with Access Key for JWT authentication.',
runwayApiKey: 'Get from runwayml.com. Supports gen4.5, gen4_turbo flagship models.',
minimaxApiKey: 'Get from minimaxi.com. Hailuo video with free quota, excellent for Chinese scenes.',
minimaxRegion: 'Choose the API host. Mainland China accounts must use the CN endpoint; others use Global.',
},
weixinoaTitle: 'Official Account Publishing',
weixinoaDesc: 'Configure the WeChat Official Account AppID / AppSecret so Content Studio can push articles into the draft box via gzh_publish (requires a verified account + the server IP whitelisted).',
searchTitle: 'Search Service',
searchDesc: 'Configure the built-in search tool provider and API credentials',
searchCatalogError: 'Failed to load the search provider catalog — showing built-in provider configuration only; plugin providers and status info are unavailable.',
searchResolvedLabel: 'Currently active',
searchResolvedSource: {
configured: 'manually configured',
autoDetect: 'auto-detected',
keylessFallback: 'keyless fallback',
},
searchStatusConfigured: 'Configured',
searchStatusNotConfigured: 'Not configured',
searchStatusActive: 'Active',
searchStatusNoCredential: 'No credential needed',
searchPluginManaged: 'Provided by plugin "{plugin}" — configure it on the Plugins page',
searchGoToPlugins: 'Go to Plugins →',
proxyTitle: 'Network Proxy',
proxyDesc: 'Configure a global HTTP / SOCKS proxy for all outbound requests (LLM APIs, web search, channel bridging, etc.). For environments that cannot reach overseas APIs directly or require a unified egress.',
localToolsTitle: 'Local Tools',
localToolsDesc: 'Manage the desktop local file/command tools: master switch, tunnel status, and the allowed-directory whitelist.',
localTools: {
desktopOnly: 'Local tools are only available inside the desktop client. Open this page in the MateClaw desktop app to manage them.',
enableLabel: 'Enable local tools',
enableHint: 'When on, agents can reach local file and command tools through the tunnel, constrained to the directory whitelist below.',
tunnelLabel: 'Tunnel status',
tunnelHint: 'Connection state of the local-tools channel between the desktop and the server.',
tunnelConnected: 'Connected',
tunnelDisconnected: 'Disconnected',
dirsLabel: 'Allowed directories',
dirsHint: 'Local file operations are confined to the directories below. Removal takes effect immediately.',
addDir: 'Add directory',
emptyFailClosed: 'No directories configured — all local file access is currently denied by default.',
emptyFailOpen: 'No directories configured — the entire local filesystem is currently accessible by default.',
removeTitle: 'Remove directory',
removeConfirm: 'Remove {dir} from the whitelist? Agents will no longer be able to access it.',
removeSuccess: 'Directory removed',
addSuccess: 'Directory added',
loadFail: 'Failed to load local tools configuration',
saveFail: 'Operation failed, please retry',
},
proxy: {
enableLabel: 'Enable proxy',
enableHint: 'Global switch. When on, the outbound connections below all route through the proxy.',
urlLabel: 'Proxy address',
urlHint: 'Supports HTTP, HTTPS and SOCKS5 (e.g. <code>http://127.0.0.1:7890</code>, <code>socks5://127.0.0.1:1080</code>). Optional credentials: <code>http://user:pass@host:port</code>.',
bypassLabel: 'Bypass list',
bypassHint: 'Hosts that skip the proxy, separated by |, with * wildcards (e.g. localhost|127.*|*.local). Leave blank to use the default, which bypasses loopback and private ranges.',
socksWarn: 'Note: a SOCKS proxy only applies to HttpURLConnection-based egress (search / media). LLM and streaming chat use java.net.http, which does not support SOCKS and will connect directly. Use your proxy tool\'s HTTP / mixed port for LLM.',
coverageTitle: 'Outbound routed through the proxy once enabled:',
coverage: {
llm: 'LLM APIs',
search: 'Web search',
media: 'Image / Video / Music',
channels: 'IM channel bridging',
mcp: 'Remote MCP',
browser: 'Browser tool',
},
test: 'Test connection',
testing: 'Testing…',
testOk: 'Proxy port reachable · {ms} ms',
testFail: 'Connection failed',
saveFail: 'Save failed',
},
sttTitle: 'Speech Recognition',
sttDesc: 'Configure STT speech-to-text with OpenAI Whisper and DashScope Qwen3-ASR',
sttProviderOptions: { auto: 'Auto Select' },
sttProviderTags: { reuseLlmKey: 'Reuses LLM API Key' },
musicTitle: 'Music Generation',
musicDesc: 'Configure AI music generation with Google Lyria and MiniMax Music',
musicProviderOptions: { auto: 'Auto Select' },
musicProviderTags: { reuseLlmKey: 'Reuses LLM API Key', sharedWithVideo: 'Shared with Video' },
model3dTitle: '3D Model Generation',
model3dDesc: 'Configure AI 3D model generation with Tencent Hunyuan 3D (HY-3D-3.1 / HY-3D-3.0 / HY-3D-Express)',
model3dProviderOptions: { auto: 'Auto Select', hunyuan: 'Tencent Hunyuan 3D' },
model3dProviderTags: { requiresKey: 'Requires SecretId:SecretKey' },
ttsTitle: 'Text-to-Speech',
ttsDesc: 'Configure TTS with Edge TTS (free), OpenAI TTS, and DashScope CosyVoice',
ttsProviderOptions: {
auto: 'Auto Select',
},
ttsProviderTags: {
free: 'Free',
noKeyNeeded: 'No API Key Needed',
reuseLlmKey: 'Reuses LLM API Key',
},
ttsAutoModeOptions: {
off: 'Off (Manual only)',
always: 'Always Auto Read',
},
imageTitle: 'Image Generation',
imageDesc: 'Configure AI image generation with DashScope, OpenAI DALL-E, fal.ai Flux, and Zhipu CogView',
imageProviderOptions: {
auto: 'Auto Select',
},
imageProviderTags: {
reuseLlmKey: 'Reuses LLM API Key',
freeQuota: 'Free Quota Available',
configuredInModels: 'See Model Settings',
sharedWithVideo: 'Shared with Video',
},
videoTitle: 'Video Generation',
videoDesc: 'Configure AI video generation capabilities, supporting text-to-video and image-to-video',
videoProviderOptions: {
auto: 'Auto Select',
},
videoProviderTags: {
reuseLlmKey: 'Reuses LLM API Key',
freeQuota: 'Free Quota Available',
configuredInModels: 'See Model Settings',
},
minimaxRegionGlobal: 'Global (api.minimax.io)',
minimaxRegionCn: 'China (api.minimaxi.com)',
actions: {
setDefault: 'Set Default',
saveSystem: 'Save System Settings',
},
messages: {
loadFailed: 'Failed to load settings',
saveSuccess: 'Settings saved successfully',
saveFailed: 'Failed to save settings',
deleteConfirm: 'Delete this model preset?',
modelSaved: 'Model preset saved',
modelDeleted: 'Model preset deleted',
defaultChanged: 'Default model updated',
},
languageOptions: {
zhCN: 'Simplified Chinese',
enUS: 'English',
},
},
agentContext: {
kicker: 'Context Studio',
title: 'Agent Context',
desc: 'Manage prompt files and memory for agents',
selectAgent: 'Select Employee',
unknownAgent: 'Unknown employee',
noAgent: 'Please select an agent first',
files: 'Files',
coreFiles: 'Core Files',
coreFilesDesc: 'Enabled files are concatenated in order as Agent system prompt',
newFile: 'New File',
noFiles: 'No files yet',
selectFile: 'Select a file from the left to view its content',
fileContent: 'Edit Markdown content here...',
preview: 'Preview',
editOnly: 'Edit',
splitView: 'Split',
modified: 'Modified',
saveSuccess: 'Saved successfully',
saveFailed: 'Failed to save',
deleteConfirm: 'Delete file "{name}"?',
deleteSuccess: 'File deleted',
deleteFailed: 'Failed to delete',
promptUpdated: 'System prompt files updated',
promptUpdateFailed: 'Failed to update system prompt files',
newFileTitle: 'New File',
newFilePlaceholder: 'Enter filename (e.g. AGENTS.md)',
newFileHint: 'Filename must end with .md',
fileExists: 'Filename already exists',
invalidFilename: 'Please enter a valid .md filename',
loadFailed: 'Failed to load file list',
loadFileFailed: 'Failed to load file content',
exportSnapshot: 'Export snapshot',
exportSnapshotTitle: 'Export memory snapshot',
exportSnapshotDesc: "Bundle this agent's AGENTS / MEMORY / PROFILE / SOUL / KNOWLEDGE.md plus the memory/ daily ledger into a ZIP, named with the current timestamp. The enabled / sortOrder flags are not exported.",
exportSuccess: 'Snapshot exported',
exportFailed: 'Export failed',
importSnapshot: 'Import snapshot',
importSnapshotTitle: 'Import memory snapshot',
importSelectPrompt: 'Pick a ZIP snapshot to import',
importPreviewing: 'Inspecting snapshot...',
importPreviewFailed: 'Failed to inspect snapshot',
importApplying: 'Applying...',
importApplied: 'Applied {applied}, skipped {skipped}',
importFailed: 'Import failed',
importPreviewWillCreate: 'Will create',
importPreviewWillUpdate: 'Will overwrite',
importPreviewWillSkip: 'Skipped',
importPreviewEmpty: 'Nothing to import',
importPreviewCreateNone: '(none)',
importPreviewUpdateMeta: '{old} B → {new} B',
importPreviewSkipReason: 'reason: {reason}',
importConfirmHint: 'Confirming will overwrite existing files with the same name.',
importApply: 'Confirm import',
personalMemory: 'Per-user private memory',
personalMemoryDesc: 'Memory copies the agent wrote for individual users during conversations; injected only into that user\'s sessions. Read-only here.',
personalReadonly: 'Private memory · {owner} · read-only',
},
agents: {
kicker: 'Employee Studio',
title: 'Employees',
desc: 'Hire, train, and manage your employees',
newAgent: 'Hire Employee',
views: {
roster: 'Roster',
live: 'Live',
plans: 'Plans',
},
templates: {
title: 'Choose a Role',
desc: 'Hire from a pre-written job description, or start from scratch.',
skip: 'Start from Scratch',
applied: 'Employee onboarded',
},
search: 'Search employees...',
tabs: {
all: 'All',
react: 'ReAct',
planExecute: 'Plan-Execute',
dsh: 'DSH Harness',
enabled: 'Enabled',
disabled: 'Disabled',
basic: 'Basic',
skills: 'Skills',
tools: 'Tools',
providers: 'Providers',
wiki: 'Knowledge Base',
context: 'Context',
},
columns: {
name: 'Name',
description: 'Description',
agentType: 'Type',
modelName: 'Model',
tags: 'Tags',
enabled: 'Status',
updateTime: 'Updated',
actions: 'Actions',
},
status: {
enabled: 'Enabled',
disabled: 'Disabled',
},
emptyTitle: 'No Employees Yet',
emptyDesc: 'Hire your first employee to get started',
modal: {
newTitle: 'New Employee',
editTitle: 'Edit Employee',
},
fields: {
name: 'Name',
icon: 'Icon',
type: 'Type',
runtime: 'Runtime',
runtimeConfig: 'DSH Configuration (JSON)',
runtimeNativeHint: 'Uses MateClaw\'s native Agent Loop.',
runtimeDshHint: 'Uses the DeepSeek Harness Loop; workspace and provider are checked at startup.',
runtimeConfigHint: 'JSON configuration consumed by the provider. Leave empty for provider defaults.',
runtimeReady: 'DSH runtime is ready (command and Cordis config are available).',
runtimeUnavailable: 'DSH runtime is unavailable. Check the command path and Cordis config.',
description: 'Description',
systemPrompt: 'System Prompt',
role: 'Role',
roleHint: 'A one-line job title. e.g. Senior Data Analyst, Customer Support.',
goal: 'Goal',
goalHint: 'Why this employee exists — the outcome they help you reach.',
taglinePreview: 'Card preview:',
backstory: 'Backstory',
backstoryHint: 'Invisible to users, felt in every conversation. Their experience, beliefs, and working principles.',
advanced: 'Advanced',
extraInstructions: 'Additional Instructions',
extraInstructionsHint: 'Optional. Use for output format, process checklists, or boundary rules.',
maxIterations: 'Max Iterations',
defaultThinkingLevel: 'Default Thinking Level',
workspaceBasePath: 'Working Directory',
workspaceBasePathHint: 'Optional. Set a dedicated working directory for this employee. Relative paths resolve under the workspace root; absolute paths are used as-is. Leave blank to inherit the workspace default.',
modelName: 'Model',
modelGlobalDefault: 'Use global default',
modelHint: 'Override the global default model for this employee. Leave blank to follow Settings → Models.',
tags: 'Tags',
tagsHint: 'Press Enter, space, or comma to add; click the × on a chip to remove.',
tagUndo: 'Undo',
enabled: 'Enabled',
},
guide: {
summary: 'Behavior Guide (AGENTS.md)',
open: 'Manage Behavior Guide',
desc: "This is the employee's behavior handbook (AGENTS.md): when to write memories, what goes in each file, and which memory tools are available. The Role/Goal/Backstory above are the employee's factory identity; evolving memories like PROFILE/MEMORY live in the Memory menu.",
empty: 'This employee has no behavior guide (AGENTS.md) yet.',
create: 'Create Behavior Guide',
saved: 'Saved',
addSection: 'Add Section',
addSectionPlaceholder: 'Section title',
scaffold: '## When to Write Memories\n\n(Describe when this employee should record to MEMORY.md / PROFILE.md.)\n\n## File Responsibilities\n\n(Describe what belongs in SOUL.md / PROFILE.md / MEMORY.md.)\n',
},
thinkingLevels: {
auto: 'Follow Model',
off: 'Off',
low: 'Low',
medium: 'Standard',
high: 'Deep',
max: 'Maximum',
},
types: {
react: 'ReAct (Tool Calling)',
planExecute: 'Plan-and-Execute',
},
actions: {
chat: 'Chat',
edit: 'Edit',
delete: 'Delete',
create: 'Create',
update: 'Save',
},
placeholders: {
name: 'Employee name',
icon: 'Emoji or URL',
description: 'Brief description',
systemPrompt: 'You are a helpful AI assistant...',
role: 'e.g. Senior Data Analyst',
goal: 'e.g. Turn data into actionable insights',
backstory: 'e.g. Spent 10 years in data — believes in asking the right question before writing SQL...',
extraInstructions: 'Optional: output format, process checklist, or boundary rules...',
tags: 'Enter, space, or comma to add',
workspaceBasePath: 'e.g. projects/code-review',
},
messages: {
noDescription: 'No description',
noTagline: 'No role and goal yet',
deleteConfirm: 'Let this employee go? This cannot be undone.',
loadFailed: 'Failed to load employees',
saveFailed: 'Failed to save employee',
saveSuccess: 'Employee saved',
deleteFailed: 'Failed to delete employee',
tagRemoved: 'Removed tag "{tag}"',
deleteSuccess: 'Employee let go',
toggleFailed: 'Failed to toggle status',
toggleSuccess: 'Status updated',
runtimeConfigInvalid: 'DSH configuration must be a valid JSON object',
},
runtime: {
native: 'Native Loop',
dsh: 'DSH Harness',
},
tagFilter: {
label: 'Tags',
clear: 'Clear',
search: 'Search tags',
noMatch: 'No matching tags',
},
binding: {
skillsKicker: 'Trained workflows',
skillsTagline: 'A skill is a workflow — a step-by-step playbook the LLM follows for a coherent multi-step task.',
skillsHint: 'Select skills this agent can use. Leave empty to use all enabled skills.',
disableAllSkills: 'This agent uses no skills',
disableAllSkillsHint: 'Saving with this on clears the agent\'s skill bindings and marks it as "explicitly no skills": the LLM loads no SKILL.md catalog entries and skill-expanded tools do not enter the context. System-level primitives (memory, delegation, etc.) are unaffected. When off, picking nothing still falls back to "inherit global default".',
disableAllSkillsBadge: 'Off',
toolsKicker: 'Atomic tools the agent can call',
toolsTagline: 'A tool is one call, one thing. The LLM decides when to invoke each tool autonomously.',
toolsHint: 'Select tools this agent can use. Leave empty to use all enabled tools.',
disableAllTools: 'This agent uses no user-pickable tools',
disableAllToolsHint: 'Saving with this on clears the agent\'s tool bindings and marks it as "explicitly no tools": neither user-pickable tools nor any enabled MCP tools enter the allowlist. System-level primitives (structured memory, workspace memory files, delegation, etc.) remain available so the agent can still operate. When off, picking nothing still falls back to "inherit global default".',
disableAllToolsBadge: 'Off',
searchSkills: 'Search skill name, description, or version',
searchTools: 'Search tool name, description, source, or group',
advancedToolsTitle: 'Advanced: Hand-picked atomic tools',
advancedToolsHint: 'Skill bindings already auto-expand allowed tools. Use this to pick built-in micro-utilities not packaged as a skill (e.g. datetime, delegate_agent), or tick specific MCP tools to scope which MCP tools this agent may use.',
toolUnionHint: 'Tools selected here are unioned with tools from any bound skills. If you tick specific MCP tools, this agent may call only the ticked MCP tools; if you tick none, every MCP tool enabled at the system level stays available by default. To deny an MCP tool when none are ticked, configure it under Security → Tool Guard.',
mcpAutoIncludedBadge: 'Available by default',
mcpAutoIncludedTooltip: 'No MCP tool is ticked, so every MCP tool enabled at the system level is available to this agent by default. Ticking specific MCP tools switches this to ticked-only. To deny an MCP tool in this state, configure it under Security → Tool Guard.',
mcpScopedBadge: 'Scoped',
mcpScopedTooltip: 'Specific MCP tools are ticked, so this agent may call only the ticked MCP tools; unticked MCP tools are unavailable.',
toolStaleBadge: 'Cached (offline)',
toolStaleTooltip: 'The owning MCP server is not currently connected. This tool entry comes from the last successful snapshot; the LLM will not invoke it until the connection is restored.',
toolUnavailableBadge: 'Unavailable',
toolUnavailableTooltip: 'This tool\'s name conflicts with another tool on the same server and cannot be bound ({reason}). Rename the upstream tool to resolve.',
toolOrphanGroup: 'Bound but no longer available',
toolOrphanDescription: 'This tool was previously bound but is no longer in the available catalog (its MCP server may have been removed, or the tool was retired upstream). Uncheck and save to clean up the leftover binding.',
providersHint: 'Preferred provider + model chain for this agent (lower index tried first). Each entry may pin a specific model, and the same provider may be added more than once with different models. Leave empty to use the global available-pool order. Cooling-down or pool-removed providers are still skipped automatically.',
providersAddHint: 'Click a provider below to add an entry (the same provider may be added multiple times, each with its own model):',
providerDefaultModel: 'Provider default model',
noSkills: 'No skills available',
noTools: 'No tools available',
noMatchingSkills: 'No matching skills',
noMatchingTools: 'No matching tools',
noProviderPreferences: 'No preferred-model chain set — the agent uses the global fallback chain order.',
wikiKicker: 'Knowledge Base Access',
wikiTagline: 'Limit which knowledge bases this agent can reach, so it never reads content outside its scope.',
wikiHint: 'Tick the knowledge bases this agent may access; mark one as the default (used by wiki tools when no kbId/kbName is given).',
wikiScopeAll: 'No knowledge base selected: this agent can reach every KB in the current workspace.',
wikiScopeLimited: 'This agent can only reach the {count} selected knowledge base(s).',
wikiScopeNone: '"No knowledge bases" is on: this agent cannot reach any KB, and every wiki tool returns "no knowledge base".',
disableAllWiki: 'This agent uses no knowledge bases',
disableAllWikiHint: 'Saving with this on clears the agent\'s KB bindings and marks it as "explicitly no KBs": wiki_read_page / wiki_search_pages / etc. return "no knowledge base", and the webchat /wiki/pages endpoint returns an empty list. When off, picking nothing still falls back to "can reach every KB in the workspace".',
disableAllWikiBadge: 'Off',
wikiSetPrimary: 'Set default',
wikiPrimary: 'Default',
noKBs: 'No knowledge bases available',
noKB: 'No primary KB',
wikiPages: '{count} pages',
wikiLoadFailed: 'Failed to load knowledge bases',
contextHint: 'Manage context files (e.g. AGENT.md) that define this agent\'s behavior, knowledge, and instructions.',
goToContext: 'Edit Context Files',
},
wizard: {
entry: 'AI Create',
kicker: 'AI Create',
title: 'Create an employee in one sentence',
subtitle: 'Describe the employee you need — AI configures the persona, type and tools; confirm to onboard.',
steps: { describe: 'Describe', confirm: 'Confirm', onboard: 'Onboard' },
placeholder: 'e.g. An ops assistant that can check the weather and write daily reports',
inputHint: 'Enter to generate · Shift+Enter for newline',
generate: 'Generate',
generating: 'Generating…',
tryLabel: 'Try:',
examples: ['Quarterly business report', 'Contract risk review', 'Cross-team weekly digest'],
generateFailed: 'Generation failed, please retry or rephrase',
emptyRequirement: 'Please describe the employee you need first',
aiNotice: 'The fields below were generated by AI from your description — edit anything before creating.',
fields: {
name: 'Name',
icon: 'Icon',
type: 'Type',
description: 'Description',
persona: 'Persona',
tags: 'Tags',
},
sections: {
tools: 'Tools & MCP',
skills: 'Skills',
kb: 'Knowledge base',
},
aiPicked: 'AI picked {count}',
selected: '{count} selected',
addSkill: 'Add skill',
addTool: 'Add tool',
collapse: 'Collapse',
noAiSkills: 'No skills suggested — add manually',
noAiTools: 'No tools suggested — add manually',
remove: 'Remove',
noTools: 'No tools available',
noSkills: 'No skills available',
kbNone: 'No knowledge base',
back: 'Back to describe',
confirmCreate: 'Confirm & onboard',
creating: 'Creating…',
createFailed: 'Creation failed',
successTitle: '{name} is onboarded',
successSubtitle: 'The new employee has joined your team and is ready to chat.',
stat: { skills: 'Skills', tools: 'Tools', kb: 'KB' },
startChat: 'Start chatting',
buildAnother: 'Build another',
roster: 'Roster',
},
},
security: {
title: 'Security',
kicker: 'Governance',
layoutDesc: 'Boundaries, approvals, audit, and workspace governance have to speak the same language or the system will not feel trustworthy.',
sections: {
toolGuard: 'Tool Guard',
fileGuard: 'File Guard',
auditLogs: 'Audit Logs',
members: 'Members',
workspaces: 'Workspaces',
activity: 'Activity',
},
activity: {
title: 'Activity',
desc: 'Who did what, in plain English. Click any event to see the full story.',
allActions: 'All Actions',
allResources: 'All Resources',
allSources: 'All',
sourceAudit: 'Audit',
sourceApproval: 'Approvals',
detailTitle: 'Event detail',
detailDetails: 'Detail',
loading: 'Loading…',
noEvents: 'No activity yet',
emptyHint: 'As you create agents, bind skills, or approve tool calls, they show up here.',
moreFilters: 'More filters',
changes: 'Changes',
environment: 'Environment',
absoluteTime: 'Absolute time',
noValue: '(empty)',
rawPayload: 'Raw payload',
today: 'Today',
yesterday: 'Yesterday',
justNow: 'just now',
minutesAgo: '{n} min ago',
hoursAgo: '{n} h ago',
daysAgo: '{n} d ago',
verbs: {
create: 'created',
update: 'updated',
delete: 'deleted',
enable: 'enabled',
disable: 'disabled',
login: 'signed in',
logout: 'signed out',
approvalGranted: 'approved',
approvalDenied: 'denied',
approvalPending: 'requested',
},
resources: {
agent: 'agent',
channel: 'channel',
skill: 'skill',
wiki: 'wiki page',
member: 'member',
workspace: 'workspace',
toolApproval: 'tool call',
},
columns: {
time: 'Time',
source: 'Source',
user: 'User',
action: 'Action',
resource: 'Resource',
name: 'Name',
ip: 'IP',
},
},
members: {
title: 'Workspace Members',
desc: 'Manage members and roles for the current workspace.',
addMember: 'Add Member',
noMembers: 'No members found.',
loading: 'Loading...',
columns: {
user: 'User',
role: 'Role',
joined: 'Joined',
actions: 'Actions',
},
roles: {
owner: 'Owner',
admin: 'Admin',
member: 'Member',
viewer: 'Viewer',
},
addDialog: {
title: 'Add Member',
userId: 'User ID',
userIdPlaceholder: 'Enter user ID',
user: 'Select User',
userPlaceholder: 'Search username…',
username: 'Username',
usernamePlaceholder: 'Enter username',
usernameHint: 'Account will be created automatically if user does not exist',
password: 'Password',
passwordPlaceholder: 'Required for new users',
passwordHint: 'Leave empty for existing users, required for new users',
nickname: 'Nickname',
nicknamePlaceholder: 'Optional display name',
role: 'Role',
},
actions: {
remove: 'Remove',
cancel: 'Cancel',
confirm: 'Confirm',
},
messages: {
addSuccess: 'Member added successfully',
addFailed: 'Failed to add member',
updateSuccess: 'Role updated successfully',
updateFailed: 'Failed to update role',
removeConfirm: 'Are you sure you want to remove this member?',
removeSuccess: 'Member removed',
removeFailed: 'Failed to remove member',
},
},
workspaces: {
title: 'Workspaces',
desc: 'Manage workspaces for your organization.',
manage: 'Manage Workspaces',
newWorkspace: 'New Workspace',
loading: 'Loading workspaces...',
noWorkspaces: 'No workspaces found.',
current: 'Current',
columns: {
name: 'Name',
slug: 'Slug',
description: 'Description',
basePath: 'Working Directory',
created: 'Created',
actions: 'Actions',
},
createDialog: {
title: 'New Workspace',
name: 'Name',
namePlaceholder: 'e.g. Engineering',
slug: 'Slug',
slugPlaceholder: 'e.g. engineering',
slugHint: 'Slug cannot be changed after creation.',
description: 'Description',
descriptionPlaceholder: 'Optional description',
basePath: 'Working Directory',
basePathPlaceholder: 'e.g. /home/user/project or D:\\codes\\MyProject',
basePathHint: 'Restrict agent file access to this directory. Leave empty for no restriction.',
},
editDialog: {
title: 'Edit Workspace',
},
deleteDialog: {
title: 'Delete Workspace',
confirm: 'Are you sure you want to delete {name}? This will remove all resources associated with this workspace.',
},
actions: {
edit: 'Edit',
delete: 'Delete',
cancel: 'Cancel',
create: 'Create',
save: 'Save',
},
messages: {
saveSuccess: 'Workspace saved successfully',
saveFailed: 'Failed to save workspace',
deleteSuccess: 'Workspace deleted',
deleteFailed: 'Failed to delete workspace',
},
},
toolGuard: {
title: 'Tool Guard',
desc: 'Manage tool invocation security rules and global guard settings',
enabled: 'Enable Tool Guard',
enabledHint: 'When enabled, tool invocations will be checked against security rules',
guardScope: 'Guard Scope',
scopeAll: 'All Tools',
scopeSelected: 'Selected Tools',
guardedTools: 'Guarded Tools',
guardedToolsPlaceholder: 'Type tool name and press Enter',
deniedTools: 'Denied Tools',
deniedToolsPlaceholder: 'Type tool name and press Enter',
rules: 'Security Rules',
rulesDesc: 'Manage built-in and custom security rules',
addRule: 'Add Rule',
editRule: 'Edit Rule',
columns: {
name: 'Rule Name',
severity: 'Severity',
category: 'Category',
pattern: 'Pattern',
decision: 'Decision',
enabled: 'Status',
builtin: 'Type',
actions: 'Actions',
},
builtinBadge: 'Built-in',
customBadge: 'Custom',
deleteConfirm: 'Delete rule "{name}"?',
fields: {
ruleId: 'Rule ID',
name: 'Name',
description: 'Description',
toolName: 'Target Tool',
pattern: 'Regex Pattern',
excludePattern: 'Exclude Pattern',
category: 'Category',
severity: 'Severity',
decision: 'Decision',
remediation: 'Remediation',
priority: 'Priority',
},
messages: {
ruleIdRequired: 'Rule ID is required',
nameRequired: 'Name is required',
patternRequired: 'Regex pattern is required',
ruleIdDuplicate: 'Rule ID already exists',
saveFailed: 'Failed to save rule',
},
exportBtn: 'Export JSON',
importBtn: 'Import JSON',
exportFileName: 'mateclaw-guard-rules-{date}.json',
importPromptHint: 'Upload a JSON exported by this UI. Built-in rules only sync their policy fields.',
importSummary: 'Import done: {inserted} added, {updatedBuiltin} built-in updated, {updatedCustom} custom updated, {skipped} skipped',
importFailed: 'Import failed: {msg}',
builtinLockedHint: 'Built-in rule name, pattern and category are system-managed. You can only adjust severity, decision, priority, exclude pattern and enabled state.',
},
fileGuard: {
title: 'File Guard',
desc: 'Manage sensitive file path protection',
enabled: 'Enable File Guard',
enabledHint: 'When enabled, sensitive file paths will be protected from unauthorized access',
sensitivePaths: 'Sensitive Paths',
sensitivePathsDesc: 'Configure file paths to protect (supports wildcards)',
addPath: 'Add Path',
pathPlaceholder: 'Type path and press Enter (e.g. ~/.ssh/)',
},
audit: {
title: 'Audit Logs',
desc: 'View tool security check records',
config: {
title: 'Audit Configuration',
enabled: 'Enable Audit Logging',
enabledHint: 'When disabled, tool invocation audit logs will not be recorded',
minSeverity: 'Minimum Severity',
minSeverityHint: 'Only record audit events at this severity level and above',
retentionDays: 'Retention Days',
retentionDaysHint: 'Audit logs older than this will be auto-cleaned (0=never)',
},
stats: {
total: 'Total Checks',
blocked: 'Blocked',
needsApproval: 'Needs Approval',
allowed: 'Allowed',
},
filters: {
toolName: 'Tool Name',
decision: 'Decision',
severity: 'Severity',
conversationId: 'Conversation ID',
},
columns: {
time: 'Time',
tool: 'Tool',
decision: 'Decision',
severity: 'Severity',
conversationId: 'Conversation',
},
noLogs: 'No audit logs yet',
expandFindings: 'Expand Details',
outcome: {
AUTO_GRANT: 'Auto-approved',
HARD_BLOCK: 'Blocked by safety floor',
FORCE_HUMAN: 'Dangerous pattern, human approval forced',
SEVERITY_CRITICAL: 'Not auto-approved: CRITICAL always requires a human',
SEVERITY_CEILING: 'Not auto-approved: grant ceiling below this finding ({ceiling} < {actual})',
UNKNOWN_WORKSPACE: 'Not auto-approved: conversation workspace unknown',
NO_GRANT: 'Not auto-approved: no matching grant',
createGrant: 'Create a grant',
},
},
severity: {
CRITICAL: 'Critical',
HIGH: 'High',
MEDIUM: 'Medium',
LOW: 'Low',
INFO: 'Info',
},
decision: {
ALLOW: 'Allow',
NEEDS_APPROVAL: 'Needs Approval',
BLOCK: 'Block',
},
messages: {
loadFailed: 'Failed to load security config',
saveSuccess: 'Security config saved',
saveFailed: 'Failed to save security config',
ruleCreated: 'Rule created',
ruleUpdated: 'Rule updated',
ruleDeleted: 'Rule deleted',
ruleToggled: 'Rule status updated',
},
approval: {
title: 'Approval Required',
severity: 'Risk Level',
summary: 'Summary',
findings: 'Findings',
remediation: 'Remediation',
approved: 'Approved',
denied: 'Denied',
hint: 'Type /approve to allow, or /deny to reject',
},
},
tokenUsage: {
kicker: 'Runtime Cost',
title: 'Token Usage',
desc: 'View token consumption and model invocation statistics',
refresh: 'Refresh',
startDate: 'Start',
endDate: 'End',
promptTokens: 'Prompt Tokens',
completionTokens: 'Completion Tokens',
cacheReadTokens: 'Cache Read Tokens',
cacheWriteTokens: 'Cache Write Tokens',
reasoningTokens: 'Reasoning Tokens',
assistantMessages: 'Assistant Messages',
byModel: 'By Model',
byDate: 'By Date',
model: 'Model',
provider: 'Provider',
date: 'Date',
messageCount: 'Messages',
loadFailed: 'Failed to load token usage',
noData: 'No token usage data',
},
acp: {
kicker: 'External Agents',
title: 'ACP Endpoints',
desc: 'Delegate prompts to external coding agents (codex / claude-code / opencode / qwen-code) over stdio. Enable an endpoint after installing its CLI.',
addEndpoint: 'Add Endpoint',
builtin: 'Builtin',
test: 'Test',
testing: 'Testing...',
statusUnknown: 'untested',
empty: 'No ACP endpoints yet.',
loadFailed: 'Failed to load ACP endpoints',
saveFailed: 'Failed to save endpoint',
deleteTitle: 'Confirm Delete',
deleteConfirm: 'Delete ACP endpoint "{name}"? Builtin endpoints are protected.',
deleteFailed: 'Failed to delete endpoint',
toggleFailed: 'Failed to toggle endpoint',
invalidJson: 'Invalid JSON',
columns: {
name: 'Endpoint',
command: 'Command',
status: 'Last Test',
enabled: 'Enabled',
actions: 'Actions',
},
fields: {
name: 'Name (slug)',
displayName: 'Display name',
description: 'Description',
command: 'Command',
args: 'Args (JSON array)',
env: 'Environment variables',
toolParseMode: 'Tool parse mode',
enabled: 'Enabled',
},
env: {
formMode: 'Edit as JSON',
jsonMode: 'Edit as form',
suggested: 'Suggested',
addRow: 'Add variable',
keyPlaceholder: 'KEY_NAME',
valuePlaceholder: 'value (e.g. sk-…)',
show: 'Show',
hide: 'Hide',
remove: 'Remove',
hints: {
anthropic: 'Anthropic API key (sk-ant-…). Get one at https://console.anthropic.com/settings/keys',
openai: 'OpenAI API key (sk-…). Get one at https://platform.openai.com/api-keys',
dashscope: 'Aliyun DashScope key — same value as MateClaw .env DASHSCOPE_API_KEY',
google: 'Google AI Studio API key. Get one at https://aistudio.google.com/apikey',
claudeOauth: 'Heads up: claude-code OAuth login (the one stored in ~/.claude/) does NOT work here, and worse — the Claude Agent SDK reads it BEFORE this env var, silently shadowing your API key. If you have ever run `claude login` on this host, clear the keychain first: (macOS) `claude logout` or `security delete-generic-password -s "Claude Code-credentials"`; (Linux/Windows) delete ~/.claude/credentials.json. Then put a real API key (sk-ant-…) above. Or switch to qwen-code / opencode to reuse a CLI login.',
},
},
modal: {
newTitle: 'Add ACP Endpoint',
editTitle: 'Edit ACP Endpoint',
},
},
mcp: {
kicker: 'Capability Bridge',
title: 'MCP Connections',
subtitle: 'Connect agents to external data sources and tools',
desc: 'Manage MCP (Model Context Protocol) server connections',
addServer: 'Add Connection',
addCustom: 'Custom MCP',
refreshAll: 'Refresh All',
searchPlaceholder: 'Search MCP servers…',
emptyMatch: 'No servers match your search',
catalogBadge: 'From catalog · {name}',
credentialHint: 'Replace placeholders {keys} before saving',
credentialHintRequired: 'Required credentials: {keys} (replace placeholders before saving)',
credentialHintOptional: 'Optional credentials: {keys} (fill in if you need those APIs)',
credentialHintBoth: 'Required: {required} · Optional: {optional}',
sections: {
added: 'Added',
recommended: 'Recommended',
countItems: '{n}',
},
card: {
docs: 'View docs',
add: 'Install',
toolCount: '{n} tools',
},
toggle: {
enable: 'Enable',
disable: 'Disable',
},
tier: {
core: 'Core',
extension: 'Extension',
coreHint: "Core: this server's tools are advertised to the model directly. Click to make extension.",
extensionHint: "Extension: this server's full tool schemas stay hidden and are executed through tool_call. Click to make core.",
},
kv: {
envKey: 'KEY',
envValue: 'value',
headerKey: 'Header',
headerValue: 'value',
addEnv: 'Add env var',
addHeader: 'Add header',
required: 'Required',
optional: 'Optional',
},
time: {
justNow: 'just now',
minutesAgo: '{n}m ago',
hoursAgo: '{n}h ago',
},
columns: {
name: 'Name',
transport: 'Transport',
enabled: 'Status',
lastStatus: 'Connection',
lastConnectedTime: 'Last Connected',
toolCount: 'Tools',
description: 'Description',
actions: 'Actions',
},
status: {
connected: 'Connected',
disconnected: 'Disconnected',
error: 'Error',
connecting: 'Connecting…',
},
transport: {
stdio: 'Stdio',
sse: 'SSE',
streamable_http: 'HTTP',
},
modal: {
newTitle: 'Add MCP Connection',
editTitle: 'Edit MCP Connection',
},
fields: {
name: 'Name',
description: 'Description',
transport: 'Transport',
url: 'URL',
headers: 'HTTP Headers',
command: 'Command',
args: 'Arguments (JSON array)',
env: 'Environment Variables',
cwd: 'Working Directory',
connectTimeout: 'Connect Timeout (seconds)',
readTimeout: 'Read Timeout (seconds)',
enabled: 'Enabled',
},
placeholders: {
name: 'Server name',
description: 'Brief description',
url: 'http://localhost:8080/sse',
headers: '',
command: 'npx',
args: '',
env: '',
cwd: '/path/to/dir',
},
actions: {
test: 'Test Connection',
testing: 'Testing...',
},
testResult: {
success: 'Connection successful',
failed: 'Connection failed',
tools: 'Discovered {count} tools',
latency: 'Latency {ms}ms',
},
messages: {
loadFailed: 'Failed to load MCP connections',
createSuccess: 'MCP connection created',
updateSuccess: 'MCP connection updated',
deleteSuccess: 'MCP connection deleted',
deleteConfirm: 'Delete MCP connection "{name}"?',
toggleSuccess: 'Status updated',
refreshSuccess: 'All connections refreshed',
connecting: 'Connecting in the background — status updates automatically',
saveFailed: 'Failed to save',
tierFailed: 'Failed to change disclosure tier',
empty: 'No MCP connections',
emptyDesc: 'Add an MCP connection to extend your agents\' capabilities',
},
},
sessions: {
title: 'Sessions',
desc: 'View and manage all conversation sessions',
back: 'Back',
search: 'Search sessions...',
columns: {
session: 'Session',
source: 'Source',
agent: 'Agent',
model: 'Model',
messages: 'Messages',
status: 'Status',
lastActive: 'Last Active',
actions: 'Actions',
},
status: {
active: 'Active',
closed: 'Closed',
},
empty: 'No sessions found',
emptyHeading: 'No sessions yet',
emptyDesc: 'Head over to Chat to start a conversation — every session you have will show up here.',
emptyCta: 'Start chatting',
loadFailed: 'Failed to load sessions',
deleteConfirm: 'Are you sure you want to delete this session?',
deleteTitle: 'Confirm Delete',
deleteFailed: 'Failed to delete session',
selectAll: 'Select all sessions on this page',
deselectAll: 'Deselect all',
selectSession: 'Select session "{title}"',
selectedCount: '{count} sessions selected',
clearSelection: 'Clear selection',
batchDelete: 'Delete selected',
deleting: 'Deleting...',
batchDeleteTitle: 'Confirm Bulk Delete',
batchDeleteConfirm: 'Delete the {count} selected sessions? This action cannot be undone.',
batchDeleteSuccess: 'Deleted {count} sessions',
batchDeletePartial: 'Deleted {deleted} of {total} sessions. The others may no longer exist or may not be accessible.',
batchDeleteFailed: 'Failed to delete selected sessions',
switchModel: 'Switch the model used for this conversation',
modelSwitched: 'Model switched',
modelSwitchFailed: 'Failed to switch model',
model: {
default: 'Default',
},
time: {
justNow: 'Just now',
minutesAgo: '{n}m ago',
hoursAgo: '{n}h ago',
},
},
tools: {
kicker: 'Developer Catalog',
title: 'Tools (Catalog)',
desc: 'Atomic actions exposed by installed skills. Use the Skills page for end-user binding.',
registerButton: 'Register Tool',
columns: {
tool: 'Tool',
beanName: 'Bean Name',
type: 'Type',
status: 'Status',
actions: 'Actions',
},
empty: 'No tools registered',
sections: {
core: 'Core Tools',
extension: 'Extension Tools',
countItems: '{n} items',
},
tier: {
toCore: '→ Core',
toExtension: '→ Extension',
toCoreHint: 'Move to Core: advertised to the model directly',
toExtensionHint: 'Move to Extension: hide full schemas and execute through tool_call',
locked: 'Source-owned',
lockedHint: "MCP / ACP / Skill tools are tiered by their owning server / endpoint / skill — change it there",
core: { desc: 'Advertised to the model directly' },
extension: { desc: 'Full schemas stay hidden; the model executes tools through tool_call' },
},
modal: {
editTitle: 'Edit Tool',
newTitle: 'Register Tool',
},
fields: {
name: 'Name',
beanName: 'Bean Name',
type: 'Type',
description: 'Description',
},
placeholders: {
name: 'Tool name',
beanName: 'Spring Bean name',
description: 'Tool description',
},
types: {
builtin: 'Built-in',
mcp: 'MCP',
custom: 'Custom',
},
messages: {
saveFailed: 'Failed to save tool',
deleteConfirm: 'Are you sure you want to delete this tool?',
deleteTitle: 'Confirm Delete',
deleteFailed: 'Failed to delete tool',
toggleFailed: 'Failed to toggle tool status',
tierFailed: 'Failed to change tool tier',
},
},
skillTemplates: {
kicker: 'Wizard',
title: 'Skill Templates',
desc: 'Pick a starter template, fill in a few fields, and a manifest skill is created in your workspace.',
backToSkills: '← Skills',
backToGallery: 'Back to gallery',
fields: 'fields',
useTemplate: 'Use template',
empty: 'No skill templates available yet.',
step1: 'Fill parameters',
step2: 'Preview',
step3: 'Done',
next: 'Next',
back: 'Back',
selectKb: '— Select Knowledge Base —',
previewHint: 'Preview of the SKILL.md that will be created. Click Install to commit.',
installSkill: 'Install Skill',
installing: 'Installing...',
installFailed: 'Installation failed',
installed: 'Skill installed',
installedDesc: '{name} is ready in your workspace.',
installAnother: 'Install another',
viewInSkills: 'View in Skills',
},
plugins: {
title: 'Plugins',
desc: 'Manage external plugins loaded from JAR files',
refresh: 'Refresh',
type: 'Type',
author: 'Author',
status: 'Status',
tools: 'Tools',
channels: 'Channels',
provider: 'Provider',
memoryProvider: 'Memory',
searchProviders: 'Search providers',
configure: 'Configure',
configTitle: 'Plugin configuration',
configSave: 'Save',
configCancel: 'Cancel',
configSaved: 'Configuration saved',
configFailed: 'Failed to save plugin configuration',
configRequired: 'required',
configSecretPlaceholder: '(leave blank to keep unchanged)',
noDescription: 'No description',
emptyTitle: 'No plugins installed',
emptyHint: 'Place plugin JAR files in ~/.mateclaw/plugins/ and restart the server',
loading: 'Loading plugins...',
loadFailed: 'Failed to load plugins',
enabled: '{name} enabled',
disabled: '{name} disabled',
toggleFailed: 'Failed to toggle plugin status',
},
datasources: {
kicker: 'Data Fabric',
title: 'Datasources',
desc: 'Manage external database connections for natural language queries',
addButton: 'Add Datasource',
empty: 'No datasources configured. Click Add to get started.',
columns: {
name: 'Name',
type: 'Type',
connection: 'Connection',
status: 'Status',
actions: 'Actions',
},
sections: {
basic: 'Basic Info',
connection: 'Connection',
auth: 'Authentication',
advanced: 'Advanced',
},
modal: {
editTitle: 'Edit Datasource',
newTitle: 'Add Datasource',
},
fields: {
name: 'Name',
dbType: 'Database Type',
host: 'Host',
port: 'Port',
databaseName: 'Database Name',
schemaName: 'Schema',
username: 'Username',
password: 'Password',
description: 'Description',
extraParams: 'JDBC Parameters',
},
placeholders: {
name: 'e.g. Business Database',
host: '127.0.0.1',
port: '3306',
databaseName: 'Database name',
schemaName: 'public (default)',
username: 'Database username',
password: 'Database password',
description: 'What this datasource is for (optional)',
extraParams: 'useSSL=false&serverTimezone=UTC',
},
hints: {
extraParams: 'Additional JDBC URL parameters, separated by &',
},
testButton: 'Test Connection',
messages: {
saveFailed: 'Failed to save datasource',
deleteConfirm: 'Are you sure you want to delete this datasource? The agent will no longer be able to query it.',
deleteTitle: 'Confirm Delete',
deleteFailed: 'Failed to delete datasource',
toggleFailed: 'Failed to toggle datasource status',
testSuccess: 'Connection successful',
testFailed: 'Connection failed. Please check configuration.',
},
},
wiki: {
kicker: 'Knowledge Engine',
desc: 'AI-powered structured knowledge base that digests raw materials into Wiki pages',
createKB: 'New Knowledge Base',
knowledgeBases: 'Knowledge Bases',
noKB: 'No knowledge bases yet',
selectKB: 'Select a knowledge base',
library: {
countLabel: '{count} knowledge bases',
searchPlaceholder: 'Search knowledge bases...',
empty: 'No knowledge bases yet',
emptyHint: 'Click "New Knowledge Base" in the top right to start organizing your knowledge.',
noMatch: 'No matching knowledge bases',
open: 'Open',
pages: '{count} pages',
raws: '{count} materials',
noDescription: 'No description',
updatedAt: 'Updated {time}',
sortRecent: 'Recently updated',
sortName: 'Name',
sortPages: 'Pages',
backToLibrary: 'Back to library',
deleteKB: 'Delete knowledge base',
deleteKBConfirm: 'Delete knowledge base "{name}"? This will also remove {raws} raw materials and {pages} wiki pages. This action cannot be undone.',
deleteKBSuccess: 'Deleted knowledge base "{name}"',
deleteKBFailed: 'Failed to delete knowledge base',
manageKB: 'Manage knowledge base',
toManage: 'Manage',
toReading: 'View wiki',
},
manageBadge: 'Management',
selectPage: 'Select a page from the sidebar',
pageKicker: 'Knowledge Page',
confirmDelete: 'Delete page "{title}"? This cannot be undone.',
confirmDeleteRefs: '{count} pages link to this one — their wikilinks will be rewritten to plain text.',
confirmBatchDelete: 'Delete {count} pages? This cannot be undone.',
batchSelect: 'Batch select',
selectAll: 'Select all',
kbName: 'Name',
kbNamePlaceholder: 'Enter knowledge base name',
kbDescription: 'Description',
kbDescPlaceholder: 'Briefly describe the knowledge base purpose',
rawMaterials: 'Raw Materials',
sources: {
tab: 'Sources',
autoSync: 'Auto-sync',
autoSyncInterval: 'every {sec}s',
autoSyncGlobalOffHint: 'Auto-sync needs the global switch enabled by ops',
toggleFailed: 'Failed to toggle auto-sync',
},
pages: 'Wiki Pages',
config: 'Config',
transformations: {
tab: 'Transformations',
title: 'Transformations',
desc: 'Reusable prompt templates. Each template runs against one raw material; the generated content is persisted here. Toggle "apply default" to fire it automatically after every successful ingest.',
empty: 'No transformations yet — create one to get started.',
createBtn: 'New transformation',
editBtn: 'Edit',
deleteBtn: 'Delete',
deleteConfirm: 'Delete this transformation? Past run history is kept.',
enabled: 'Enabled',
disabled: 'Disabled',
applyDefault: 'Apply by default',
applyDefaultOn: 'Runs automatically after each ingest completes',
applyDefaultOff: 'Manual or agent-tool runs only',
modelLabel: 'Model',
modelDefault: 'Default (KB / system)',
modelHelp: 'Falls back to the KB-bound chat model or the system default; once chosen this template always uses the specified model',
outputTargetLabel: 'Output target',
outputTargetNone: 'None — output stays in run history',
outputTargetPage: 'Save as wiki page (searchable / agent-accessible / linkable)',
outputTargetPageBadge: 'Auto-save to page',
outputFormatLabel: 'Output format',
outputFormatMarkdown: 'Markdown (default, human-readable)',
outputFormatJson: 'JSON (structured for downstream tools; one auto-retry on parse failure)',
outputFormatJsonBadge: 'JSON',
outputSchemaLabel: 'JSON Schema (optional)',
outputSchemaHelp: 'Schema text is injected into the prompt and checked for required fields after parsing. Leave blank to skip validation.',
outputSchemaPlaceholder: '{\n "type": "object",\n "required": ["title", "items"],\n "properties": {\n "title": { "type": "string" },\n "items": { "type": "array" }\n }\n}',
outputSchemaBadge: 'Schema',
targetPageType: 'Target type',
targetPageTypeAuto: '(Default / auto)',
targetPageTypeHelp: "The page type used when this output lands as a page, drawn from this knowledge base's pageType profile. Leave blank to use the profile's fallback type.",
saveAsPageBtn: 'Save as page',
saving: 'Saving…',
savedAsPage: 'Saved as:',
saveAsPageDone: 'Saved as wiki page',
saveAsPageFailed: 'Save as page failed',
openPage: 'Open page',
scope: 'Scope',
scopeKb: 'This knowledge base only',
scopeWorkspace: 'Workspace-wide',
name: 'Name',
namePlaceholder: 'e.g. risk-extract',
nameHelp: 'Used by agent tools; lowercase letters / digits / hyphens',
title2: 'Title',
titlePlaceholder: 'e.g. Contract risk extraction',
description: 'Description',
descriptionPlaceholder: 'One line about what this transformation produces',
prompt: 'Prompt template',
promptHelp: 'Supports {input_text} and {title} placeholders',
saveBtn: 'Save',
cancelBtn: 'Cancel',
compareBtn: 'Compare selected',
compareHint: 'Pick 1 more run to compare',
compareCheckTip: 'Tick 2 completed runs to compare side-by-side',
compareTitle: 'Compare runs',
aggregateBtn: 'Aggregate all runs',
aggregating: 'Aggregating…',
aggregateDone: 'Aggregate page produced',
aggregateFailed: 'Aggregate failed',
aggregateNoRuns: 'This template has no completed runs to aggregate yet',
runs: 'Run history',
rerunBtn: 'Re-run',
rerunning: 'Re-running…',
cancelRunBtn: 'Cancel',
cancelling: 'Cancelling…',
cancelDone: 'Cancelled',
cancelFailed: 'Cancel failed',
runOn: 'Ran on',
runStatus: 'Status',
runDuration: 'Duration',
runOutput: 'Output',
runError: 'Error',
runApply: 'Run',
running: 'Running…',
runFailed: 'Run failed',
pickRaw: 'Pick a raw material',
pickRawHint: 'Select a raw material to use as input',
noRuns: 'No runs yet.',
},
hotCache: {
tab: 'Hot Cache',
title: 'Recent Activity Snapshot (Hot Cache)',
desc: 'A rolling markdown digest of what has been happening in this knowledge base, injected into the agent system prompt so chat does not have to re-search the obvious. Rebuilt asynchronously by the listener with a 5-minute debounce.',
empty: 'No snapshot yet — one will be built after the bound agent finishes its next conversation.',
contentEmpty: '(snapshot body is empty)',
regenerate: 'Rebuild now',
regenerating: 'Rebuilding…',
regenerateQueued: 'Rebuild queued; refreshing in a few seconds',
regenerateFailed: 'Failed to schedule rebuild',
reset: 'Clear snapshot',
resetConfirm: 'Clear the current snapshot? The next agent conversation will regenerate it.',
resetDone: 'Cleared',
resetFailed: 'Clear failed',
lastUpdated: 'Last updated',
updateReason: 'Trigger reason',
rebuildCount: 'Rebuilds',
lastDuration: 'Last duration',
lastError: 'Last error',
footer: 'Toggling wiki.hot_cache.enabled off short-circuits the injection path. The "Rebuild now" button bypasses the debounce window for easier debugging.',
},
adv: {
tab: 'Advanced',
validate: 'Validate',
validOk: 'Validation passed',
profile: {
tab: 'Page Types',
title: 'Page Type Profile',
desc: 'Define this KB\'s page types, their knowledge layer, route/create/merge stage instructions and content templates. Injected into the ingest pipeline prompts.',
builtin: 'Built-in default',
placeholder: 'Paste the pageType profile JSON here…',
reset: 'Reset to default',
resetConfirm: 'Reset to the built-in default profile? Your custom config will be cleared.',
formMode: 'Form',
jsonMode: 'JSON',
fallbackType: 'Fallback type',
allowAdditionalFields: 'Keep undeclared fields',
versionLabel: 'Version',
addType: 'Add type',
removeType: 'Remove type',
removeTypeConfirm: 'Remove page type "{type}"?',
noTypes: 'No page types yet',
selectType: 'Select or create a page type on the left',
typeKey: 'Type key',
typeKeyHint: 'lowercase, unique, only letters/digits/_/-',
copyFrom: 'Copy from existing type',
copyFromNone: 'Don\'t copy (blank)',
moveUp: 'Move up',
moveDown: 'Move down',
label: 'Label',
description: 'Description',
layer: 'Knowledge layer',
layerFact: 'Fact',
layerExperience: 'Experience',
layerUnset: 'Unspecified',
fields: 'Fields',
fieldName: 'Field name',
fieldType: 'Type',
fieldRequired: 'Required',
enumValues: 'Enum values',
enumHint: 'comma-separated',
addField: 'Add field',
noFields: 'No fields yet',
advanced: 'Advanced (stage prompts / template)',
advancedIntro: 'These prompts are injected into the matching ingest-pipeline stage, appended after the system default; leave blank to use the default. Three stages: route = decide whether content belongs to this type, create = author a new page of this type, merge = fold new content into an existing page.',
stageRoute: 'Route stage instructions',
routeHint: 'Routing: extra guidance for the model when deciding whether and how a piece of raw content belongs to this page type.',
routeExample: 'e.g. Only route here when the content describes a specific person\'s life or role; passing mentions don\'t count.',
stageCreate: 'Create stage instructions',
createHint: 'Authoring: structure and style for the first-time generation of a page of this type (sections, tone, required elements).',
createExample: 'e.g. Write in third person; open with a one-sentence definition; then sections Background / Key attributes / Relations.',
stageCreateTemplate: 'Create template key',
createTemplateHint: 'The template key referenced at create time (matches "Template key" below); leave blank to use no template.',
createTemplateExample: 'e.g. concept-tpl',
stageMerge: 'Merge stage instructions',
mergeHint: 'Merging: rules for folding new content into an existing page (how to dedupe, which side wins on conflict).',
mergeExample: 'e.g. Keep the more recent version; on conflict annotate source and time rather than overwriting.',
templateKey: 'Template key',
templateKeyHint: 'Template identifier, referenced by the create stage via "Create template key" above.',
templateMarkdown: 'Template Markdown',
templateMarkdownHint: 'Body skeleton handed to the model: write the headings/sections you want. The create stage injects it verbatim into the prompt and the model fills in the prose. It is a structural example — there is no variable substitution, so parenthetical notes are hints to the model, not auto-filled field values.',
templateMarkdownExample: '## Overview\n(one-sentence definition of this entry)\n\n## Key attributes\n- \n\n## Relations\n- link related entries with [[wiki links]]',
errKeyFormat: 'Type key "{key}" is invalid: must be lowercase, non-empty, only letters/digits/_/-',
errKeyDup: 'Duplicate type key "{key}"',
errFieldDup: 'Duplicate field "{field}" in type "{type}"',
warnFallbackMissing: 'Fallback type "{type}" is not among the defined types',
errJsonParse: 'JSON parse failed — fix it before switching to the form',
},
reclassify: {
desc: 'Re-classify existing pages against the current profile: only the page type is updated, the body is untouched. Use it after editing the profile (adding/removing types) to migrate old pages to the new categories.',
button: 'Re-classify existing pages',
confirmTitle: 'Re-classify existing pages',
confirmMsg: 'Each non-system page in this knowledge base will be sent to the LLM one by one to re-determine its category. This may take time and incur costs. System pages are unaffected. Continue?',
started: 'Re-classification started; page categories will update once it finishes.',
},
layers: {
tab: 'Layers & Stale',
title: 'Knowledge Layers & Stale State',
desc: 'When a fact-layer page is updated, experience pages that depend on it are marked stale to flag they need review.',
fact: 'Fact',
experience: 'Experience',
other: 'Other',
stale: 'Stale',
empty: 'This knowledge base has no pages yet.',
page: 'Page',
layer: 'Layer',
status: 'Status',
staleTag: 'Needs review',
fresh: 'Fresh',
},
perm: {
tab: 'Permissions',
title: 'Employee Page Type Permissions',
desc: 'Configure per-pageType read/create/update/delete access for an employee in this KB; writes can require approval. No rules = allow all; once any rule exists, uncovered types are fail-safe denied.',
agent: 'Employee',
selectAgent: 'Select an employee…',
pageType: 'Page Type',
policy: 'Write policy',
noRows: 'This employee has no permission rules (allow all).',
pageTypeHint: 'page type, * for default',
addRule: 'Add rule',
deleteConfirm: 'Delete the permission rule for "{type}"?',
},
watcher: {
tab: 'Watcher',
title: 'Source Directory Watcher',
desc: 'Configure local scan paths (multiple paths and glob patterns supported); the watcher scans for new/changed files at an interval and ingests them automatically. You can also trigger a one-off scan.',
enabled: 'Watcher enabled',
active: 'Active',
interval: 'Scan interval',
sourceType: 'Source type',
directory: 'Scan paths',
dirHint: 'One path or glob pattern per line; lines starting with # are comments. Examples:\n/data/docs\n/data/ocr/**/*.txt\n/data/reports/*.{xlsx,csv}',
availableTypes: 'Available source types',
scanNow: 'Scan now',
scanDone: 'Scan complete',
scanResult: 'Scanned {scanned}, added {added}, skipped {skipped}, errors {errors}',
},
pipeline: {
tab: 'Pipelines',
title: 'Custom Pipelines',
desc: 'Define triggers (e.g. a page type reaching a count threshold, page created) and multi-step processing (llm / skill) in YAML. Once saved, the backend runs them on the trigger condition.',
name: 'Name',
trigger: 'Trigger',
enabled: 'Enabled',
runs: 'Runs',
empty: 'No pipeline definitions yet.',
editor: 'YAML definition',
editorHint: 'name: my-pipeline\ntrigger:\n type: page_type_count\n page_type: episode\n threshold: 10\nsteps:\n - executor: llm\n prompt: …',
saveDef: 'Save definition',
deleteConfirm: 'Delete pipeline "{name}"?',
runsFor: 'Runs for "{name}"',
status: 'Status',
startedAt: 'Started at',
noRuns: 'No runs yet.',
},
},
searchPages: 'Search pages...',
dropFiles: 'Drop files here or click to upload',
dropToUpload: 'Release to upload',
uploading: 'Uploading…',
uploadFailed: 'Upload failed: {name}',
addText: 'Add Text',
noRawMaterials: 'No raw materials yet',
reprocess: 'Reprocess',
resume: 'Resume',
cancel: 'Cancel processing',
cancelling: 'Cancelling…',
cancelledHint: 'Cancelled by user',
download: 'Download original file',
downloadFailed: 'Download failed',
processAll: 'Process All Pending',
materialTitle: 'Title',
materialContent: 'Content',
pasteContent: 'Paste or type content here...',
backlinks: 'Backlinks',
generatedByAi: 'AI generated',
editedManually: 'Manually edited',
configTitle: 'Wiki Processing Rules',
configDesc: 'Configure the rules AI follows when digesting raw materials',
configPlaceholder: 'Edit wiki processing rules here...',
pageCount: '{count} pages',
sourceCount: '{count} sources',
dirPlaceholder: 'Enter local directory path, e.g. /Users/me/docs',
scan: 'Scan',
scanning: 'Scanning...',
scanResult: 'Scanned {scanned} files, added {added}, skipped {skipped}',
scanFailed: 'Scan failed',
saving: 'Saving...',
status: {
active: 'ACTIVE',
uploading: 'UPLOADING',
processing: 'PROCESSING',
error: 'ERROR',
pending: 'PENDING',
completed: 'COMPLETED',
partial: 'PARTIAL',
failed: 'FAILED',
cancelled: 'CANCELLED',
cancelling: 'CANCELLING…',
},
// Raw-material filtering + batch operations (issue #506).
batch: {
allStatus: 'All statuses',
allTypes: 'All types',
keywordPlaceholder: 'Search title…',
clearFilter: 'Clear filter',
noMatch: 'No matching materials',
selectAll: 'Select all',
selectedCount: '{n} selected',
reprocess: 'Reprocess',
delete: 'Delete',
retryAllFailed: 'Retry all failed ({n})',
deleteConfirm: 'Delete the {n} selected material(s)? Their generated pages are cascade-deleted and this cannot be undone.',
reprocessDone: 'Re-queued {n} item(s)',
deleteDone: 'Deleted {n} item(s)',
},
// Friendly, localized hints keyed by the backend's structured error code.
// The raw exception text is kept as the hover tooltip for troubleshooting.
errorCode: {
AUTH_ERROR: 'Model authentication failed — check that the provider API key is correct and valid.',
BILLING: 'Provider quota exhausted or billing error — check your account balance.',
MODEL_NOT_FOUND: 'The selected model is missing or unavailable — switch models in the knowledge base settings.',
RATE_LIMIT: 'Rate-limited by the provider — please retry in a moment.',
TIMEOUT: 'The model timed out — retry later or switch to a faster model.',
SERVER_ERROR: 'The provider is temporarily unavailable (5xx) — please retry later.',
CONTENT_FILTER: 'Content was blocked by the model safety filter — adjust the material and retry.',
NO_CONTENT: 'No text could be extracted from this material — check that the file is not empty or corrupt.',
EMPTY_RESULT: 'The model generated no pages — reprocess to retry.',
UNKNOWN: 'Processing failed — see details or the server logs.',
},
// Non-blocking warnings: the material processed but an async sub-step failed.
warningCode: {
EMBEDDING_FAILED: 'Embedding failed — this material is not semantically searchable yet; check the embedding model and reprocess.',
ENTITY_EXTRACTION_FAILED: 'Entity-graph extraction failed — the knowledge graph may be incomplete; reprocess later.',
UNKNOWN: 'Some background processing did not finish — see details.',
},
failureCenter: {
title: 'Knowledge base processing issues',
open: 'Open',
},
progress: {
preparing: 'Preparing…',
uploading: 'Uploading {pct}%',
},
jobStage: {
queued: 'Queued',
routing: 'Routing',
phase_a_running: 'Analyzing',
phase_a_done: 'Analyzed',
phase_b_running: 'Generating',
enriching: 'Linking',
embedding: 'Embedding',
completed: 'Completed',
failed: 'Failed',
cancelled: 'Cancelled',
},
jobInfo: {
currentModel: 'Model',
fallbackActive: 'Fallback active',
elapsedTime: 'Elapsed',
pagesProgress: '{done}/{total} pages',
},
page: {
type: {
entity: 'Entity',
concept: 'Concept',
source: 'Source',
synthesis: 'Synthesis',
},
enriched: 'Linked',
notEnriched: 'Not linked',
enrich: 'Add cross-links',
repair: 'Repair page',
citations: '{count} source chunks',
noCitations: 'No source citations found',
citationUnknownSource: 'Unknown source',
citationChunkN: 'Chunk {n}',
citationChunkUnknown: 'Chunk unknown',
citationOffset: 'offset {start}{end}',
},
relation: {
shared_chunk: 'Shared chunk',
shared_raw: 'Shared source',
direct_link: 'Direct link',
semantic_near: 'Semantic',
relatedPages: 'Related Pages',
explainRelation: 'Explain relation',
},
configPanel: {
embeddingModel: 'Embedding Model',
embeddingModelHint: 'Semantic search model for this KB; leave empty for system default',
ingestMode: 'Ingest Mode',
ingestModeEager: 'Eager',
ingestModeLazy: 'Lazy',
ingestModeEagerHint: 'Upload runs the full LLM pipeline to generate wiki pages immediately.',
ingestModeLazyHint: 'Upload only extracts, chunks, and embeds. No page generation; search works right away.',
entityExtraction: 'Entity extraction',
entityExtractionHint: 'Extract named entities (people, organizations, locations, ...) and their relations from raw materials to build an entity-level knowledge graph. Adds processing time and token cost; off by default.',
entityExtractionEnable: 'Enable entity extraction',
entityExtractionRun: 'Extract now',
entityExtractionRunning: 'Extracting…',
entityTypesLabel: 'Entity types to extract',
entityTypesPlaceholder: 'Select or type an entity type, Enter to add',
entityTypesHint: 'Restrict which entity types are extracted; leave empty to use the built-in defaults (person, organization, location, event, product, concept).',
relationSchemaLabel: 'Relation schema',
relationSchemaHint: 'Restrict extraction to only these relation triples; other relations, and entities that don\'t participate in any of them, are ignored. Leave empty to keep open-ended relation extraction.',
relationSchemaSubject: 'Subject type',
relationSchemaPredicate: 'Relation (e.g. works_for)',
relationSchemaObject: 'Object type',
relationSchemaAdd: '+ Add relation',
modelStrategy: 'Model Strategy',
globalDefault: 'Global default',
selectModel: 'Select a model…',
otherProvider: 'Other',
searchModel: 'Search models…',
noModelMatch: 'No matching models',
addFallback: '+ Add fallback',
stepModel: {
route: 'Analysis / Routing',
create_page: 'Page Creation',
merge_page: 'Page Merge',
enrich: 'Link Annotation',
summary: 'Summary Update',
},
fallbackModels: 'Fallback Models',
searchPreview: 'Search Preview',
searchPreviewPlaceholder: 'Enter a user message to preview agent search results…',
searchPreviewRun: 'Test',
},
stats: {
enrichedRatio: '{enriched}/{total} linked',
failedJobs: '{count} failed',
},
noPages: 'No pages yet',
noResults: 'No pages match your search',
loadMore: 'Load {n} more',
filteredByRaw: 'Filtered by source material',
clearFilter: 'Clear filter',
systemPageBadge: 'System',
systemPageHint: 'overview / log are built-in system pages — they can\'t be deleted',
lockedPageBadge: 'Locked',
lockedPageHint: 'This page is locked — AI tools and batch operations skip it',
archivedSection: 'Archived',
noArchived: 'No archived pages',
unarchive: 'Restore',
lint: {
neverScanned: 'No broken-link scan yet — click to check whether [[...]] references still resolve',
running: 'Scanning for broken links…',
scanning: 'Scanning…',
scan: 'Scan dead links',
rescan: 'Rescan',
cleanResult: 'Scanned {pages} pages — no broken links',
brokenSummary: 'Found {refs} broken links across {pages} pages',
view: 'View',
dismissTitle: 'Dismiss this notice',
panelTitle: 'Wiki broken-link report',
noReport: 'No scan yet — start one from the banner',
},
pageTypes: {
concept: 'Concepts',
person: 'People',
place: 'Places',
event: 'Events',
technology: 'Technology',
organization: 'Organizations',
product: 'Products',
term: 'Terms',
process: 'Processes',
system: 'System',
synthesis: 'Synthesis',
other: 'Other',
},
entityTypes: {
person: 'Person',
organization: 'Organization',
location: 'Location',
place: 'Place',
event: 'Event',
product: 'Product',
concept: 'Concept',
technology: 'Technology',
term: 'Term',
other: 'Other',
},
graph: {
tab: 'Knowledge Graph',
nodes: 'nodes',
edges: 'edges',
orphans: 'orphans',
showOrphans: 'Show orphans',
allTypes: 'All types',
resetView: 'Reset view',
fullscreen: 'Fullscreen',
exitFullscreen: 'Exit fullscreen',
searchPlaceholder: 'Search nodes by name…',
searchNoMatch: 'No matching node',
searchClear: 'Clear',
linksTo: 'Links to',
openPage: 'Open page',
empty: 'No graph data — process some raw materials first',
modePages: 'Pages',
modeEntities: 'Entities',
entityEmpty: 'No entities yet — enable entity extraction in the KB config and reprocess',
entityType: 'Type',
mentions: 'mentions',
mentionedIn: 'Mentioned in',
relations: 'Relations',
description: 'Description',
aliases: 'Aliases',
},
},
cronField: {
edit: 'Visual editor',
placeholder: 'min hour day month weekday',
placeholderSeconds: 'sec min hour day month weekday',
dialogTitle: 'Cron Expression Builder',
presets: 'Presets',
confirm: 'Confirm',
cancel: 'Cancel',
rangeSep: 'to',
intervalStart: 'from',
intervalEvery: 'every',
intervalExec: '',
specificEmpty: 'Tap the values below to select',
everyDesc: 'Runs every {unit}',
tabs: { second: 'Second', minute: 'Minute', hour: 'Hour', day: 'Day', month: 'Month', week: 'Week' },
type: { every: 'Any', range: 'Range', interval: 'Interval', specific: 'Specific' },
unit: { second: 'second', minute: 'minute', hour: 'hour', day: 'day', month: 'month', week: 'weekday' },
weekShort: { 0: 'Sun', 1: 'Mon', 2: 'Tue', 3: 'Wed', 4: 'Thu', 5: 'Fri', 6: 'Sat' },
presetItems: {
everyMinute: 'Every minute',
every5Min: 'Every 5 minutes',
every30Min: 'Every 30 minutes',
hourly: 'Hourly',
daily0: 'Daily at 0:00',
daily9: 'Daily at 9:00',
weekday9: 'Weekdays at 9:00',
weekly1: 'Mondays at 9:00',
monthly1: 'Monthly on the 1st',
},
desc: {
everyMinute: 'Runs every minute',
everyNMin: 'Runs every {n} minutes',
everyNHour: 'Runs every {n} hours',
hourlyAt: 'Runs at minute {m} of every hour',
dailyAt: 'Runs daily at {time}',
weeklyAt: 'Runs on {days} at {time}',
monthlyAt: 'Runs on day {day} every month at {time}',
},
},
scheduler: {
kicker: 'Automation',
title: 'Scheduler',
desc: 'Scheduled jobs, event triggers and run history — managed in one place.',
tabs: {
jobs: 'Scheduled Jobs',
triggers: 'Event Triggers',
history: 'Run History',
},
actions: {
newJob: 'New Job',
newTrigger: 'New Trigger',
refresh: 'Refresh',
},
history: {
empty: 'No run history yet',
emptyDesc: 'Once a scheduled job runs, its execution records show up here.',
unknownJob: 'Job #{id}',
columns: {
job: 'Job',
trigger: 'Trigger',
status: 'Status',
startedAt: 'Started',
duration: 'Duration',
tokens: 'Tokens',
},
status: {
running: 'Running',
completed: 'Completed',
succeeded: 'Succeeded',
failed: 'Failed',
},
triggerType: {
scheduled: 'Scheduled',
manual: 'Manual',
},
},
},
cronJobs: {
kicker: 'Automation',
title: 'Cron Jobs',
desc: 'Schedule agents to run messages or goals on a timer',
createJob: 'New Job',
editJob: 'Edit Job',
noJobs: 'No cron jobs yet',
createFirst: 'Create your first cron job',
columns: {
name: 'Name',
agent: 'Agent',
taskType: 'Type',
cron: 'Cron Expression',
timezone: 'Timezone',
nextRun: 'Next Run',
lastRun: 'Last Run',
enabled: 'Enabled',
actions: 'Actions',
// RFC-063r §2.14: list-page "Last Delivery" column
lastDelivery: 'Last Delivery',
// RFC-063r post-deploy: channel binding columns
channel: 'Channel',
targetId: 'Delivery Target',
},
// RFC-063r §2.14: delivery_status state machine labels
lastDelivery: {
none: 'No delivery',
pending: 'In flight',
delivered: 'Delivered',
not_delivered: 'Failed',
},
taskTypes: { text: 'Text Message', reminder: 'Reminder', agent: 'Agent Goal', wiki_process: 'Wiki Processing' },
cronTypes: { hourly: 'Hourly', daily: 'Daily', weekly: 'Weekly', custom: 'Custom' },
days: { mon: 'Mon', tue: 'Tue', wed: 'Wed', thu: 'Thu', fri: 'Fri', sat: 'Sat', sun: 'Sun' },
fields: {
name: 'Job Name',
namePlaceholder: 'Enter job name',
agent: 'Agent',
agentPlaceholder: 'Select agent',
taskType: 'Task Type',
triggerMessage: 'Trigger Message',
triggerMessagePlaceholder: 'Message to send to the agent',
reminderText: 'Reminder Text',
reminderTextPlaceholder: 'Exact text to push when the reminder fires (no LLM rewriting)',
requestBody: 'Goal',
requestBodyPlaceholder: 'Describe the goal for the agent',
wikiKb: 'Knowledge Base',
wikiKbPlaceholder: 'Select a knowledge base to process',
wikiKbEmpty: 'No knowledge bases in this workspace — create one in the Wiki page first.',
wikiForce: 'Force reprocess',
wikiForceHint: 'When on, clears last_processed_hash and re-queues every raw material. When off, only pending materials are queued.',
cronFrequency: 'Frequency',
cronTime: 'Time',
cronDays: 'Days',
cronExpression: 'Custom Expression',
cronExpressionPlaceholder: 'min hour day month weekday, e.g. 0 9 * * 1-5',
timezone: 'Timezone',
enabled: 'Enable immediately',
deliveryChannel: 'Delivery Channel',
deliveryChannelNone: 'No delivery (task conversation only)',
deliveryChannelHint: 'When the job finishes, the final result is proactively pushed to the selected channel conversation',
targetSession: 'Target Conversation',
targetSessionPlaceholder: 'Select the conversation to deliver to',
targetSessionEmpty: 'No pushable conversations on this channel yet — a conversation appears here after the bot has received at least one message in it',
deliveryMode: 'Delivery Mode',
deliveryModeHint: 'In silent mode the job still runs and records its result, but nothing is pushed to the channel conversation',
},
deliveryModes: { deliver: 'Deliver result', silent: 'Silent (no delivery)' },
actions: { runNow: 'Run Now', edit: 'Edit', delete: 'Delete' },
messages: {
createSuccess: 'Cron job created',
updateSuccess: 'Cron job updated',
deleteSuccess: 'Cron job deleted',
deleteConfirm: 'Delete job "{name}"? This cannot be undone.',
runTriggered: 'Job triggered. Check conversation "cron_{id}" for results.',
enableSuccess: 'Job enabled',
disableSuccess: 'Job disabled',
invalidCron: 'Invalid cron expression. Use 5-field format (min hour day month weekday).',
agentRequired: 'Please select an agent',
textRequired: 'Trigger message is required',
goalRequired: 'Goal is required',
},
},
workflows: {
kicker: 'Workflows',
title: 'Workflow Editor',
desc: 'Author multi-step workflows. Save the draft as you go, compile to surface diagnostics, then publish a revision when it is ready to run.',
newWorkflow: '+ New Workflow',
defined: 'Defined ({count})',
refresh: 'Refresh',
unnamed: '(unnamed)',
draftBadge: 'draft',
publishedBadge: 'v{rev}',
listEmpty: 'No workflows yet — create one to get started.',
namePlaceholder: 'Workflow name',
descPlaceholder: 'Optional description',
actions: {
saveMeta: 'Save Meta',
saveDraft: 'Save Draft',
compile: 'Compile',
publish: 'Publish',
delete: 'Delete',
},
bodyPlaceholder: '{\'{"steps":[{"name":"step-a","agentId":1,"mode":{"type":"sequential"},"promptTemplate":"..."}]}\'}',
compileErrorsTitle: '{count} compile error(s)',
templates: {
label: 'Insert template',
placeholder: 'Pick a step template',
sequential: 'Sequential step',
fan_out: 'Parallel fan-out',
collect: 'Collect results',
conditional: 'Conditional branch',
await_approval: 'Await approval',
dispatch_channel: 'Dispatch message',
write_memory: 'Write memory',
},
jsonInvalid: 'JSON parse failed: {msg}',
jsonOk: 'JSON syntax OK.',
tabs: {
canvas: 'Canvas',
json: 'JSON',
runs: 'Runs ({count})',
},
canvas: {
empty: 'The current draft has no steps. Add one in the JSON editor or pick a template.',
layoutLR: 'Horizontal',
layoutTB: 'Vertical',
stats: '{nodes} nodes · {edges} edges',
parseError: 'JSON parse failed: {msg}',
fullscreenEnter: 'Fullscreen',
fullscreenExit: 'Exit fullscreen',
addNode: '+ Add node',
modeLabel: 'Mode',
nodeAgent: 'Digital employee',
nodeExpression: 'Condition',
nodeApprovalKind: 'Approval',
nodeChannels: 'Channels',
nodeMergeStrategy: 'Merge',
approvalDefault: 'manual',
channelsEmpty: 'unspecified',
modeLabels: {
sequential: 'sequential',
fan_out: 'fan out',
collect: 'collect',
conditional: 'conditional',
await_approval: 'await approval',
dispatch_channel: 'dispatch',
write_memory: 'write memory',
},
inspector: {
title: 'Step properties',
empty: 'Click a node on the canvas to inspect it.',
rawHeader: 'Raw step JSON',
shared: 'Shared fields',
modeFields: '{mode} fields',
modeNoFields: 'This mode takes no extra fields.',
duplicate: 'Duplicate step',
delete: 'Delete step',
},
fields: {
name: 'Name',
namePlaceholder: 'step-name',
agentPlaceholder: 'Select digital employee',
agentMissing: 'Current digital employee is not in the list: {name}',
promptTemplate: 'Prompt template',
// promptPlaceholder is intentionally not localized — hardcoded in
// StepPropertyPanel so vue-i18n's parser never sees the Pebble braces.
outputVar: 'Output variable',
outputVarPlaceholder: 'data',
outputContentType: 'Output type',
approvalKind: 'Approval kind',
approverChannels: 'Approver channels (comma-sep)',
approvalMessage: 'Approval message',
timeoutSecs: 'Timeout (s)',
channels: 'Dispatch channels',
channelsEmptyHint: 'No enabled channels in this workspace. Enable one in Channel Management first.',
channelsUnknown: 'Draft references unavailable channels: {channels}',
dispatchTarget: '{channel} target',
dispatchTargetPlaceholder: 'Group ID / user ID / conversation ID',
dispatchContent: 'Message content',
employeeId: 'Employee ID',
memoryFile: 'Memory file',
memoryContent: 'Content template',
},
},
dialogs: {
createTitle: 'New workflow',
fieldName: 'Name',
namePlaceholder: 'e.g. employee-onboarding',
fieldDescription: 'Description',
descriptionPlaceholder: 'Optional. One or two sentences on what this workflow does.',
createSubmit: 'Create',
publishTitle: 'Publish workflow',
publishHint: 'Publishing compiles the current draft into a new revision. Running instances stay on their current revision.',
publishNote: 'Publish note',
publishNotePlaceholder: 'Optional summary of what changed.',
deleteTitle: 'Delete workflow',
deleteContent: 'Delete workflow "{name}"? This is reversible via the audit log.',
},
selectHint: 'Select a workflow on the left, or click "New Workflow" to start a fresh draft.',
generate: {
entryButton: 'Generate from description',
title: 'Generate workflow draft from natural language',
hint: 'Describe your business process in a sentence — the AI generates a workflow JSON draft. The draft is saved unpublished and no triggers are auto-enabled; you review and publish afterwards.',
placeholder: 'e.g. Every Monday at 9 AM, the sales agent summarises last week\'s data; once the manager approves, post to the Feishu ops group',
submit: 'Generate',
running: 'Generating…',
retry: 'Try again',
accept: 'Accept & open editor',
compileOk: 'Compile preview OK',
compileFail: 'Compile preview failed ({count})',
compileChecking: 'Validating…',
compileError: 'compile error',
missing: 'missing',
warning: 'warning',
previewDraft: 'Preview generated JSON',
failed: 'Generate failed: {msg}',
acceptedWithTriggers: 'Draft saved and {count} trigger(s) created (disabled by default)',
acceptedSomeTriggersFailed: 'Draft saved; {ok} trigger(s) created, {fail} failed',
triggersTitle: 'Suggested triggers',
triggersHint: 'Created with enabled=false; toggle them on later from the Triggers page.',
applyTemplate: 'Apply template (skip the AI roundtrip)',
applyTemplatePlaceholder: '— Pick a canonical pattern —',
},
paused: {
header: 'Paused runs ({count})',
empty: 'No paused runs.',
pauseTokenLabel: 'pause token',
deadlineLabel: 'deadline',
pausedAtLabel: 'paused at',
runHash: 'Run #{id}',
resumeApproved: 'Approve',
resumeRejected: 'Reject',
resumeTimeout: 'Time out',
resumeCancelled: 'Cancel',
resumeOk: 'Run resumed ({outcome}).',
resumeFailed: 'Resume failed: {msg}',
},
runs: {
header: 'Recent runs ({count})',
empty: 'No runs yet.',
detailTitle: 'Run #{id} — {state}',
runHash: 'run #{id}',
},
prompts: {
newName: 'New workflow name:',
defaultName: 'untitled-workflow',
publishNote: 'Optional publish note:',
deleteConfirm: 'Delete workflow "{name}"? This is reversible via the audit log.',
},
status: {
metaSaved: 'Metadata saved.',
draftSaved: 'Draft saved.',
compileOk: 'Compile OK.',
published: 'Published.',
deleted: 'Deleted.',
createFailed: 'Create failed: {msg}',
saveFailed: 'Save failed: {msg}',
saveDraftFailed: 'Save draft failed: {msg}',
deleteFailed: 'Delete failed: {msg}',
compileFailed: '{count} compile error(s) — see panel below.',
compileFallback: 'Compile / publish failed.',
},
},
triggers: {
kicker: 'Triggers',
title: 'Workflow Triggers',
desc: 'Connect cron schedules, channel events and webhooks to your published workflows — the workflow runs automatically when the condition is met.',
newTrigger: '+ New Trigger',
emptyDesc: 'Triggers run a workflow automatically when a specific event happens — no manual start needed.',
neverFired: 'Never fired',
firedSummary: 'Fired {count}× · last {time}',
targetWorkflowSelect: 'Pick a published workflow',
targetWorkflowEmpty: 'No published workflows yet.',
patternTypeLabels: {
cron: 'Cron schedule',
channel_message: 'Channel message',
content_match: 'Content match',
agent_lifecycle: 'Agent lifecycle',
workflow_completion: 'Workflow completion',
webhook: 'Webhook',
},
patternHints: {
cron: 'Example: {"cron":"0 0 * * * *","timezone":"UTC"} — every cron change bumps pattern_version.',
channel_message: 'Optional channelType (e.g. feishu), senderEquals and contentContains narrow the match.',
webhook: 'Pass-through match. Ensure the HTTP entry validates the webhook secret.',
content_match: 'Requires substring (case-insensitive). Empty strings refuse to fire.',
agent_lifecycle: 'Optional agentId and phase (spawned / terminated / crashed).',
workflow_completion: 'Optional sourceWorkflowId and stateFilter (completed / failed / any).',
},
pattern: {
cronExpression: 'Cron expression',
cronExpressionPlaceholder: '0 0 * * * *',
cronExpressionHint: '5 fields (min hour day month weekday) or 6 fields (with seconds). Each change bumps pattern_version.',
cronTimezone: 'Timezone',
cronTimezonePlaceholder: 'UTC',
channelType: 'Channel type',
channelTypeAny: 'Any channel',
senderEquals: 'Sender equals',
senderEqualsPlaceholder: 'Optional sender ID',
senderEqualsHint: 'Empty matches any sender; filled narrows to that one sender.',
contentContains: 'Message contains',
contentContainsPlaceholder: 'Optional keyword',
contentContainsHint: 'Case-insensitive substring on the message body. Empty matches any content.',
substring: 'Substring',
substringPlaceholder: 'order',
substringHint: 'Case-insensitive. Empty strings refuse to fire (no broadcast trap).',
agentId: 'Agent ID',
agentIdPlaceholder: '0 means any',
agentIdHint: 'Empty or 0 matches any agent.',
phase: 'Lifecycle phase',
phaseAny: 'Any phase',
sourceWorkflowId: 'Source workflow',
sourceWorkflowAny: 'Any upstream',
stateFilter: 'Completion state',
stateFilterAny: 'Any state',
stateFilterHint: 'completed is an alias for succeeded; failed only matches failed terminal runs.',
webhookHeader: 'Webhook pass-through',
webhookBody: 'No filtering on the UI side — make sure the HTTP entry validates the webhook secret; every accepted request reaches the trigger.',
rawHeader: 'Raw pattern_json (advanced)',
errCronRequired: 'Cron expression cannot be empty',
errCronShape: 'Cron must be 57 fields',
errSubstringRequired: 'Substring cannot be empty or the trigger never fires',
},
unnamed: '(unnamed)',
enabled: 'enabled',
disabled: 'disabled',
empty: 'No triggers yet',
actions: {
edit: 'Edit',
delete: 'Delete',
cancel: 'Cancel',
save: 'Save',
close: 'Close',
},
formTitleNew: 'New Trigger',
formTitleEdit: 'Edit Trigger',
fields: {
name: 'Name',
namePlaceholder: 'hourly-cleanup',
patternType: 'Pattern type',
patternJson: 'Pattern JSON',
patternJsonPlaceholder: '{\'{"cron":"0 0 * * * *","timezone":"UTC"}\'}',
targetType: 'Target type',
targetId: 'Target id',
ratePerMin: 'Rate / min',
dedupWindowSecs: 'Dedup window (s)',
maxFires: 'Max fires (0 = unlimited)',
botSelfFilter: 'Bot-self filter (recommended)',
payloadTemplate: 'Payload template (Pebble)',
payloadTemplatePlaceholder: '{\'{"who":"{{ event.who }}"}\'}',
enabled: 'Enabled',
},
deleteConfirm: 'Delete trigger "{name}"?',
saveFailed: 'Save failed: {msg}',
toggleFailed: 'Toggle failed: {msg}',
deleteFailed: 'Delete failed: {msg}',
},
login: {
kicker: 'AI Operating System',
subtitle: 'Your intelligent AI assistant',
heroTitle: 'Move beyond answers. Put AI inside a system that can keep working.',
heroDesc: 'MateClaw is not about stuffing more capability into a chat box. It is about pulling context, knowledge, execution, and memory into one reliable operating surface.',
pointContext: 'Keep context continuous',
pointKnowledge: 'Organize knowledge into use',
pointExecution: 'Turn capability into action',
cardTitle: 'Sign in to the working surface',
cardDesc: 'Enter the system where models, knowledge, workspace, and runtime state move together instead of living in separate tools.',
fields: {
username: 'Username',
password: 'Password',
},
placeholders: {
username: 'Enter username',
password: 'Enter password',
},
signIn: 'Sign In',
hint: 'Default: {username} / {password}',
failed: 'Login failed. Please check your credentials.',
},
enterprise: {
eyebrow: 'Enterprise',
title: 'Enterprise AI Workbench',
subtitle: 'Verifiable judgment with cited evidence, owned approvals, and a complete audit trail — AI inside enterprise workflows, not yet another chatbox.',
tabs: { overview: 'Overview', contract: 'Contract Review', account: 'Account Intel', approvals: 'Approvals', audit: 'Audit' },
risk: { high: 'High', medium: 'Medium', low: 'Low' },
status: { aiReviewed: 'AI Reviewed', pendingLegal: 'Legal Pending', approved: 'Approved', signal: 'Signal' },
overview: {
metricPending: 'Pending', metricHighRisk: 'High Risk', metricAccounts: 'Accounts', metricCited: 'Cited',
queueTitle: 'Work Queue',
queueDesc: 'Today\'s items for legal / sales / compliance, each carrying an AI judgment and source citation.',
evidenceTitle: 'Recent Evidence',
evidenceDesc: 'Source quotes, playbook clauses, and historical precedents the AI cited.',
coverageNote: 'of this week\'s judgments link to a verifiable source',
tagSource: 'source', tagPlaybook: 'playbook', tagPrecedent: 'precedent',
filterAll: 'All', filterHighRisk: 'High Risk', filterPending: 'Legal Pending', filterToday: 'Today',
pipelineTitle: 'Contract Review Pipeline',
pipelineDesc: 'Standard flow from upload to archive — every step leaves a trace.',
step1: 'Upload', step1Desc: 'Legal or sales drops a PDF',
step2: 'Extract', step2Desc: 'OCR / text extraction',
step3: 'AI Review', step3Desc: 'Clause-level risk + playbook diff',
step4: 'Legal Review', step4Desc: 'Human in the loop',
step5: 'Approve / Archive', step5Desc: 'Approval chain + audit',
},
contract: {
listTitle: 'Contracts', filterAll: 'All', filterHigh: 'High', filterPending: 'Pending',
clausesFound: 'clause deviations', deviation: 'Deviation', suggestion: 'AI suggested rewrite',
approvalChain: 'Approval Chain',
btnApprove: 'Approve', btnReject: 'Reject', btnRequestRevision: 'Request revision',
evidenceTitle: 'Evidence', sourceContract: 'Source contract', playbookRule: 'Playbook rule', precedents: 'Precedents',
confidence: 'Confidence', model: 'Model',
},
account: {
listTitle: 'Accounts', filterAll: 'All', filterHot: 'Hot', filterCooling: 'Cooling',
opportunityScore: 'Opportunity Score', aum: 'Size',
keyPeople: 'Key People',
timelineTitle: 'Account Timeline',
tlAll: 'All', tlPublic: 'Public', tlMeeting: 'Meetings', tlDoc: 'Docs',
evidence: 'Evidence',
actionsTitle: 'Recommended Actions',
actionsSub: 'Next steps inferred from timeline signals, ranked by priority.',
btnDoIt: 'Do it', btnSnooze: 'Snooze',
stage: { prospect: 'Prospect', qualified: 'Qualified', proposal: 'Proposal', negotiation: 'Negotiation', won: 'Won' },
role: { decider: 'Decider', influencer: 'Influencer', user: 'User', procurement: 'Procurement' },
eventKind: { news: 'News', hiring: 'Hiring', funding: 'Funding', product: 'Product', meeting: 'Meeting', doc: 'Doc', competitor: 'Competitor' },
priority: { high: 'High', medium: 'Medium', low: 'Low' },
},
approvals: {
queueTitle: 'My Approval Queue',
queueDesc: 'Items waiting on your sign-off. AI judgments are ready; your call.',
filterMine: 'Mine', filterAll: 'All', filterHigh: 'High Risk',
requester: 'Requested by', target: 'Target', reason: 'Reason', evidence: 'Evidence',
btnApprove: 'Approve', btnReject: 'Reject', btnDelegate: 'Delegate',
kind: { contract: 'Contract', tool: 'Tool', transformation: 'Transform', access: 'Access' },
slaTitle: 'SLA Monitor',
slaDesc: 'Response time and auto-approval rate — granular data exposes approval bottlenecks.',
slaAvg: 'Avg response', slaWithin: 'Within SLA', slaOverdue: 'Overdue', slaAutoApproved: 'Auto-approved',
slaAutoNote: 'Matched allowlist rule',
},
audit: {
title: 'Audit Trail',
desc: 'Every AI judgment, human action, and tool call carries a timestamp and a piece of evidence. This is the minimum bar for AI in regulated workflows.',
scopeAll: 'All events', scopeContract: 'Contract', scopeAccount: 'Account', scopeTool: 'AI / Tools',
evidence: 'Linked evidence',
kind: { review: 'AI Review', approve: 'Approve', reject: 'Reject', tool: 'Tool call', access: 'Access', modify: 'Modify', agent: 'Agent action' },
},
},
channels: {
title: 'Channels',
desc: 'Connect your agents to messaging platforms and APIs',
newChannel: 'New Channel',
addChannel: 'Add Channel',
status: {
active: 'Active',
inactive: 'Inactive',
},
configure: 'Configure',
enable: 'Enable',
disable: 'Disable',
empty: {
title: 'Connect your first channel',
desc: 'Bring your agents into the apps your team already uses — Slack, WeChat Work, Telegram, and more.',
cta: 'Connect a Channel',
},
stats: {
active: '{n} active',
reconnecting: '{n} reconnecting',
errors: '{n} {n, plural, one {error} other {errors}}',
disabled: '{n} disabled',
},
cardDesc: {
connectedAs: 'Connected as {account}',
connectedAsIn: 'Connected as {account} in {team}',
// Per-type fallback used when the row has no user-provided description
// and the adapter hasn't written identity_json yet. Keeps every card
// visually populated instead of leaving a blank middle area.
typeFallback: {
web: 'Default web console channel, streaming via browser SSE.',
dingtalk: 'DingTalk bot channel, receiving messages via Outgoing Webhook / Stream.',
wecom: 'WeCom (Enterprise WeChat) app channel for member messaging.',
weixin: 'WeChat Official Account channel — handles user messages and event callbacks.',
feishu: 'Feishu / Lark channel via event subscriptions.',
telegram: 'Telegram bot channel via long polling.',
slack: 'Slack bot channel via the Events API.',
discord: 'Discord bot channel via the Gateway connection.',
qq: 'QQ / QQ Channel bot.',
matrix: 'Matrix room channel via the client-server API.',
qqbot: 'QQ Channel bot (official API).',
yuanbao: 'Tencent Yuanbao channel.',
},
empty: 'Click Configure to add a description for this channel.',
},
catalog: {
subtitle: 'Pick a service to connect. You can change settings later.',
groups: {
im: 'Messaging',
enterprise: 'Enterprise platforms',
web: 'Web & API',
},
descriptions: {
telegram: 'Bot via long-poll. No public IP needed.',
discord: 'Bot via Gateway WebSocket. No public IP needed.',
slack: 'Slack app via Socket Mode.',
qq: 'QQ Bot via WebSocket. No public IP needed.',
wecom: 'WeCom smart bot. Scan QR to connect.',
weixin: 'WeChat personal account via iLink.',
feishu: 'Feishu / Lark app. One-click QR creation.',
dingtalk: 'DingTalk bot. One-click QR creation.',
web: 'Default browser channel. Always on.',
webchat: 'Embed the chat widget, or call the key-authed backend SSE API directly.',
webhook: 'Generic HTTP inbound endpoint.',
},
},
modal: {
editTitle: 'Edit Channel',
newTitle: 'New Channel',
},
fields: {
name: 'Name',
type: 'Type',
description: 'Description',
bindAgent: 'Bind Agent',
},
placeholders: {
name: 'Channel name',
description: 'Channel description',
selectAgent: 'Select Agent...',
},
types: {
web: 'Web API',
dingtalk: 'DingTalk',
feishu: 'Feishu / Lark',
telegram: 'Telegram',
discord: 'Discord',
wecom: 'WeChat Work',
weixin: 'WeChat',
qq: 'QQ',
slack: 'Slack',
webchat: 'Web / API',
webhook: 'Webhook',
},
tabs: {
form: 'Form',
json: 'Raw JSON',
},
webhook: {
url: 'Webhook URL',
copy: 'Copy',
copied: 'Copied',
copyFailed: 'Copy failed, please copy manually',
localhostWarn: 'You are running on localhost. IM platforms cannot reach localhost. Use ngrok, frp or similar tools to expose your local service to the internet, then replace the host in the URL above.',
},
weixin: {
authHint: 'Click the button below to get a WeChat login QR code. After scanning with WeChat, the Bot Token will be automatically filled in. Requires iLink Bot beta access.',
qrcodeButton: 'Get Login QR Code',
rescanButton: 'Re-scan (replaces current account)',
qrcodeLoading: 'Fetching...',
qrcodeFailed: 'Failed to get QR code. Please check your network.',
qrcodeExpired: 'QR code expired. Please get a new one.',
scanHint: 'Scan this QR code with WeChat to log in',
polling: 'Waiting for scan...',
scanned: 'Scanned, please confirm on your phone',
confirmed: 'Login successful',
expired: 'QR code expired',
loginSuccess: 'WeChat login successful, token has been filled in',
replaceWarning: 'Re-scanning will replace the currently bound WeChat account. To bind an additional account, click the button below to add a new channel.',
addNewAccount: 'Bind New WeChat Account (Add Channel)',
newAccountName: 'WeChat',
},
wecom: {
authHint: 'Click the button to open a WeChat Work QR code window. After scanning, the Bot ID and Secret will be automatically filled in.',
authButton: 'Authorize WeChat Work Bot',
authLoading: 'Loading...',
authSuccess: 'WeChat Work bot authorized successfully',
sdkFailed: 'WeChat Work SDK failed to load. Please check your network.',
windowBlocked: 'Popup blocked by browser. Please allow popups and try again.',
authCancelled: 'Authorization cancelled',
authFailed: 'Authorization failed',
},
webHint: 'Web channel uses built-in SSE communication, no additional configuration needed.',
webchatHint: 'Web / API access can embed the MateClaw chat widget into external websites, or be called as a pure backend SSE API. With the API key configured, the frontend can load the WebChat SDK; the backend can POST directly to /api/v1/channels/webchat/stream with header X-MC-Key and body {message, visitorId, optional sessionId/agentId}.',
webchatApiKeyGenerated: 'The platform will generate the API key after save. Reopen this channel to copy it.',
webchatApiKeyReadOnly: 'This API key is generated and managed by the platform. It can be copied, but not edited manually.',
webhookHint: 'Webhook channel configuration should be edited in the "Raw JSON" tab below.',
jsonHint: 'Edit the complete JSON configuration directly. Switching to "Form" tab will sync automatically.',
advanced: 'Advanced',
accessControl: {
title: 'Access Control',
dmPolicy: 'DM Policy',
dmPolicyTooltip: 'Controls whether users can chat with the bot privately',
groupPolicy: 'Group Policy',
groupPolicyTooltip: 'Controls whether the bot responds in group chats',
policyOpen: 'Open',
policyClosed: 'Closed',
allowFrom: 'Allowed Users',
allowFromTooltip: 'Comma-separated user ID whitelist, leave empty to allow all',
allowFromPlaceholder: 'Comma-separated user IDs (empty = allow all)',
denyMessage: 'Deny Message',
denyMessagePlaceholder: 'Sorry, you do not have permission',
requireMention: "Require {'@'}mention",
requireMentionTooltip: "Whether the bot requires {'@'}mention in group chats",
},
messageFilter: {
title: 'Message Filter',
filterThinking: 'Filter Thinking',
filterThinkingTooltip: 'Yes: strip <think> content from the final answer only; No: show reasoning live (WeCom streams it in the progress bubble)',
filterToolMessages: 'Filter Tool Messages',
filterToolMessagesTooltip: 'Yes: strip inline tool_call / Action tags from the final answer, and keep tool names and the execution list out of the progress bubble too; No: show tool execution, and each tool call leaves a standalone trace message (supported on WeCom)',
messageFormat: 'Message Format',
formatAuto: 'Auto',
formatMarkdown: 'Markdown',
formatText: 'Plain Text',
formatHtml: 'HTML',
},
feishuPermissions: {
title: 'Required Feishu Permissions',
goToPermissions: 'Go to Permission Management →',
},
connection: {
connected: 'Connected',
reconnecting: 'Reconnecting',
error: 'Disconnected',
disconnected: 'Not Connected',
outOfService: 'Not Started',
retryCount: 'Retry #{n}',
errorLabel: 'Error',
},
messages: {
loadFailed: 'Failed to load channels',
saveFailed: 'Failed to save channel',
saveSuccess: 'Channel saved',
deleteFailed: 'Failed to delete channel',
deleteConfirm: 'Are you sure you want to delete this channel?',
deleteTitle: 'Confirm Delete',
toggleFailed: 'Failed to toggle channel status',
invalidJson: 'Invalid config JSON format',
},
guide: {
dingtalk: {
step1: 'Create a bot on <a href="https://open-dev.dingtalk.com/" target="_blank" rel="noopener">DingTalk Open Platform</a>, fill in <b>AppKey</b> and <b>AppSecret</b>',
step2: 'Stream mode (recommended): Select "Stream Mode" for message receiving, no public IP required',
step3: 'Webhook mode: Select "HTTP Mode", paste the Webhook URL as the callback address',
},
feishu: {
step1: 'Go to <a href="https://open.feishu.cn/" target="_blank" rel="noopener">Feishu Open Platform</a> and create an enterprise app',
step2: 'Fill in the app\'s <b>App ID</b> and <b>App Secret</b> below',
webhookStep: 'In Feishu "Event Subscriptions", set the request URL to the Webhook URL above and subscribe to <code>im.message.receive_v1</code>',
websocketStep: 'In Feishu "Event Subscriptions", choose the "Long Connection" subscription method and subscribe to <code>im.message.receive_v1</code> (no public IP required)',
permissionStep: 'For nickname display, request <code>contact:user.base:readonly</code> permission in "Permission Management"',
},
telegram: {
step1: 'Search for <a href="https://t.me/BotFather" target="_blank" rel="noopener">{at}BotFather</a> in Telegram, send <code>/newbot</code> to create a Bot',
step2: 'Fill in the <b>Bot Token</b> returned by BotFather below',
step3: '<b>Long-Polling mode</b> (recommended): Auto-receives messages via <code>getUpdates</code> polling, <b>no public IP required</b>',
step4: '<b>Webhook mode</b>: Requires a publicly accessible callback URL, switch connection mode and fill in Webhook URL',
},
discord: {
step1: 'Go to <a href="https://discord.com/developers/applications" target="_blank" rel="noopener">Discord Developer Portal</a> and create an Application',
step2: 'Create a Bot on the Bot page and copy the <b>Bot Token</b> below',
step3: 'Enable <b>MESSAGE CONTENT Intent</b> on the Bot page (Privileged Gateway Intents section)',
step4: 'In OAuth2 → URL Generator, check <b>bot</b> scope and <b>Send Messages / Read Message History</b> permissions, generate invite link to add Bot to target server',
step5: 'Auto-receives messages via <b>Gateway WebSocket</b> after startup, <b>no public IP or callback URL required</b>',
},
wecom: {
step1: 'Go to <a href="https://work.weixin.qq.com/wework_admin/frame" target="_blank" rel="noopener">WeCom Admin Console</a>, navigate to "App Management → Smart Bot" and create a new bot',
step2: 'In bot configuration, select "<b>API Mode → Configure Long Connection</b>"',
step3: 'Record the bot\'s <b>Bot ID</b> and <b>Secret</b>, fill in below',
step4: 'After starting the channel, scan QR code in WeCom to chat with the bot, <b>no public IP or callback URL required</b>',
},
weixin: {
step1: 'Ensure you have WeChat <b>iLink Bot</b> beta access (currently invite-only)',
step2: 'Click the "Get Login QR Code" button below, scan with WeChat to log in',
step3: 'After successful scan, the system auto-fills <b>bot_token</b> into config',
step4: 'After starting the channel, auto-receives messages via <b>HTTP long polling</b>, <b>no public IP or callback URL required</b>',
},
qq: {
step1: 'Go to <a href="https://q.qq.com/" target="_blank" rel="noopener">QQ Open Platform</a> and create a bot application',
step2: 'Get <b>AppID</b> and <b>AppSecret</b> from the app management page, fill in below',
step3: "Enable required message types in \"Feature Config → Message Subscription\" (C2C, Group {'@'}, Channel messages, etc.)",
step4: 'After starting the channel, auto-receives messages via <b>WebSocket long connection</b>, <b>no public IP or callback URL required</b>',
},
},
dingtalkRegister: {
title: 'One-click DingTalk Bot Creation',
hint: 'Click the button to get a QR code. Scan it with DingTalk and approve authorization — a bot app will be created in your tenant and the Client ID / Client Secret will be auto-filled below.',
button: 'Scan to Create Bot',
buttonLoading: 'Preparing QR code…',
qrcodeLoading: 'Generating QR code',
scanHint: 'Scan the QR code above with the DingTalk app and confirm authorization',
confirmed: 'Bot created. Client ID and Secret have been auto-filled.',
expired: 'QR code expired, please try again',
denied: 'Authorization was denied',
startFailed: 'Failed to fetch QR code, please check your network',
},
qqRegister: {
title: 'Scan to Bind QQ Bot',
hint: 'You must have already created a bot on q.qq.com. Click the button, scan with mobile QQ, and pick the bot to bind — its AppID / AppSecret will be auto-filled below without manual copy-paste.',
button: 'Scan to Bind Existing Bot',
buttonLoading: 'Preparing QR code…',
qrcodeLoading: 'Generating QR code',
scanHint: 'Scan the QR code above with mobile QQ and pick a bot on the page',
confirmed: 'Bound. AppID and AppSecret have been auto-filled.',
expired: 'QR code expired, please try again',
denied: 'Binding failed or was cancelled',
startFailed: 'Failed to fetch QR code, please check your network',
},
feishuRegister: {
title: 'One-click Feishu App Creation',
hint: 'Click the button to get a QR code. Scan it with Feishu and confirm authorization — an in-house app will be created in your tenant and the App ID / App Secret will be auto-filled below.',
button: 'Scan to Create App',
buttonLoading: 'Preparing QR code…',
qrcodeLoading: 'Generating QR code',
scanHint: 'Scan the QR code above with the Feishu app and confirm authorization',
confirmed: 'App created. App ID and Secret have been auto-filled.',
expired: 'QR code expired, please try again',
denied: 'Authorization was denied',
error: 'Registration failed, please retry',
startFailed: 'Failed to fetch QR code, please check your network',
},
feishu: {
perm: {
message: 'Send and receive messages',
messageReason: 'Core: send and receive messages',
receive: 'Receive message events',
receiveReason: 'Core: receive user messages',
cardkit: 'Create and update cards',
cardkitReason: 'Streaming cards: show execution trace and answer in real time',
resource: 'Access message resources',
resourceReason: 'Get message content in WebSocket mode',
reactions: 'Manage message reactions',
reactionsReason: 'Message reactions: add 👍 emoji',
contact: 'Get basic user info',
contactReason: 'Nickname: display real user name',
media: 'Access message resources',
mediaReason: 'Media download: download images and files',
},
},
wizard: {
step1: 'Configure',
step2: 'Verify',
step3: 'Ready',
configureSubtitle: 'Enter your credentials to connect {service}',
howToGet: 'How to get credentials',
saveAndTest: 'Save & Test',
back: 'Back',
continue: 'Continue',
done: 'Done',
verifying: 'Verifying with {service}…',
verifySuccess: 'Connected',
// Used on Step 2 in place of the backend's English headline so the
// success line stays in the user's locale. Backend `headline` is
// still rendered on the failed path because diagnostic strings
// (errcodes, upstream error reasons) don't translate cleanly.
verifySuccessHeadline: 'Connected — {service} accepted the credentials',
verifyFailed: 'Verification failed',
verifySkipped: 'No live check needed',
verifySkippedDetail: "This channel type doesn't have credentials to verify. You can save and start it directly.",
durationMs: '({n} ms)',
fixIt: 'Fix it',
retry: 'Retry',
readyHeadline: 'Ready to go',
readySubtitle: 'Bind an agent and send a test message to confirm.',
bindAgentLabel: 'Bind to agent',
sendTest: 'Send a test message',
sendTestHint: 'Open {service} and message the bot — replies should arrive in seconds.',
saveFailed: 'Could not save the channel',
noVerifierHint: 'No live check is available for {type}. Saving will start the channel; check the connection dot afterwards.',
oauthScanHint: 'After scanning, the credentials will be filled and verified automatically — you do not need to copy anything.',
// Identity field labels rendered on Step 3. Keys not listed here
// fall back to a camelCase-to-spaces conversion (formatIdentityKey),
// so adding a new identity field on a new verifier still renders
// — it just lacks the polished label until i18n catches up.
identity: {
accountId: 'Account ID',
accountName: 'Account',
firstName: 'First name',
team: 'Team',
teamId: 'Team ID',
botId: 'Bot ID',
transport: 'Transport',
region: 'Region',
tokenTtl: 'Token TTL',
baseUrl: 'Base URL',
isBot: 'Bot account',
verified: 'Verified',
canJoinGroups: 'Can join groups',
canReadAllGroupMessages: 'Reads all group messages',
},
},
},
skills: {
title: 'Skills',
desc: 'Manage tools and skills available to your agents',
newSkill: 'New Skill',
importSkill: 'Import Skill',
browseTemplates: 'Browse Templates',
refreshRuntime: 'Refresh Runtime',
refreshing: 'Refreshing...',
refreshSuccess: 'Active skills refreshed',
refreshFailed: 'Failed to refresh runtime',
tabs: {
all: 'All',
builtin: 'Built-in',
mcp: 'MCP',
acp: 'ACP',
dynamic: 'Dynamic',
stale: 'Stale',
archived: 'Archived',
},
lifecycle: {
section: 'Lifecycle',
state: 'State',
lastUsed: 'Last used',
neverUsed: 'Never used',
today: 'Today',
daysAgo: '{n}d ago',
states: {
active: 'Active',
stale: 'Stale',
archived: 'Archived',
},
pin: 'Pin this skill',
pinHint: 'Pinned skills are never auto-archived',
archive: 'Archive',
restore: 'Restore',
pinFailed: 'Failed to update pin',
archiveTitle: 'Archive skill',
archiveConfirm: 'Archive "{name}"? The skill will be disabled and can be restored anytime.',
archiveSuccess: 'Skill archived',
archiveFailed: 'Failed to archive',
restoreSuccess: 'Skill restored',
restoreFailed: 'Failed to restore',
boundConfirmTitle: 'Skill is bound to agents',
boundConfirmBody: '"{name}" is explicitly bound to {count} enabled agent(s) ({agents}). Archiving removes that capability from them. Continue?',
},
search: {
placeholder: 'Search by name, description, or tags...',
},
filter: {
all: 'All statuses',
enabled: 'Enabled',
disabled: 'Disabled',
scanFailed: 'Scan failed',
},
sections: {
enabled: 'Enabled',
available: 'Disabled',
countItems: '{n} items',
emptyEnabled: 'No enabled skills',
emptyEnabledDesc: 'Enable one from the Disabled section below, or add a new one from a template or import.',
emptyAvailable: 'No disabled skills',
},
sort: {
recommended: 'Recommended',
name: 'Name',
status: 'Status',
source: 'Source',
updated: 'Recently updated',
},
security: {
scanned: 'Scanned',
scanFailed: 'Scan failed',
notScanned: 'Not scanned',
findingsTitle: 'Security scan failed',
rescan: 'Rescan',
rescanning: 'Scanning...',
rescanPassed: 'Rescan passed',
rescanStillFailed: 'Rescan still failed — see findings below',
rescanFailed: 'Rescan request failed',
noPersistedFindings: 'No persisted findings for this scan. Click "Rescan" to regenerate.',
fix: 'Suggested fix',
},
status: {
ready: 'Ready',
setupNeeded: 'Setup needed',
blocked: 'Blocked',
unresolved: 'Unresolved',
disabled: 'Disabled',
checking: 'Checking…',
},
empty: 'No skills found',
emptyDesc: 'Add skills to enhance your agents\' capabilities',
noDescription: 'No description',
// Issue #83: shown on the padlock that replaces the toggle for MCP/ACP virtual skills.
virtualReadonlyHint: 'MCP/ACP-derived skills can\'t be toggled here — manage the underlying service in Settings ▸ Connections.',
modal: {
configureTitle: 'Configure Skill',
newTitle: 'New Skill',
newSimpleHint: 'Just the bones — keep configuring icon, body, and tools in the detail panel after this.',
},
fields: {
name: 'Name',
nameZh: 'Chinese display name',
nameEn: 'English display name',
type: 'Type',
icon: 'Icon',
version: 'Version',
author: 'Author',
tags: 'Tags',
description: 'Description',
configJson: 'Configuration (JSON)',
skillContent: 'Skill Content (SKILL.md)',
sourceCode: 'Source Code / Script',
},
placeholders: {
name: 'Skill name',
nameZh: '可选 — 留空则显示原始 name',
nameEn: 'Optional — leave blank to show the slug',
icon: '🛠️ (emoji or URL)',
version: '1.0.0',
author: 'Author name',
tags: 'tag1,tag2,tag3',
description: 'What does this skill do?',
configJson: '',
sourceCode: '# Python or script content...',
},
hints: {
directorySkill: '— Directory skill: used as fallback when directory is unavailable',
primaryContent: '— Primary content for agent prompt injection',
},
types: {
builtin: 'Built-in',
mcp: 'MCP',
acp: 'ACP',
dynamic: 'Dynamic',
},
source: {
builtin: 'Built-in',
synthesized: 'AI Synthesized',
local: 'Local',
},
usedByTitle: 'Number of agents that have this skill bound',
lessonsCountTitle: 'Lessons recorded — click to view',
actions: {
configure: 'Configure',
view: 'View',
setUp: 'Set Up',
delete: 'Delete',
saveChanges: 'Save Changes',
createSkill: 'Create Skill',
edit: 'Edit',
save: 'Save',
cancel: 'Cancel',
},
preflight: {
title: 'Pre-flight check',
allMet: 'All requirements satisfied',
allMetDesc: 'This skill is ready to use as soon as you bind it to an agent.',
setupNeeded: '{count} requirement(s) need attention',
setupNeededDesc: 'Install the missing pieces below, then re-check or re-resolve the skill.',
requirements: 'Requirements',
features: 'Features',
installCommands: 'Install commands',
optional: 'Optional',
noManifest: 'This skill has no v3 manifest. Status falls back to legacy dependency reporting.',
recheck: 'Re-check',
loadFailed: 'Failed to load preflight data',
},
detail: {
title: 'Skill detail',
overview: 'Overview',
body: 'Body',
files: 'Files',
manifest: 'Manifest',
tools: 'Tools',
features: 'Features',
security: 'Security',
lessons: 'Lessons',
secrets: 'Secrets',
memory: 'Memory',
scanStatus: 'Scan status',
missingDeps: 'Missing dependencies',
runtimeError: 'Runtime error',
path: 'Resolved path',
synthesized: 'Source',
identitySection: 'Identity',
manifestProjectedSection: 'From SKILL.md',
manifestProjectedHint: 'These fields mirror the SKILL.md frontmatter — editing the row directly does not stick (the next resolve overwrites it). Edit the SKILL.md body below to change them.',
displayOverridesSection: 'Display & tags',
displayOverridesHint: 'DB-only — these are not overwritten by the resolver.',
bodyHint: 'SKILL.md body — injected into the LLM system prompt at load time.',
sourceCodeHint: 'Executable body (dynamic skills only). Re-runs on next call after save.',
viewRawManifest: 'View parsed runtime manifest',
noBody: 'No SKILL.md body has been provided yet.',
builtinReadonly: 'Built-in skill: only nameZh / nameEn / tags / description / body are editable.',
virtualReadonly: 'Virtual MCP-derived skill — edit it on the MCP Connections page instead.',
editSource: 'Edit SKILL.md',
editingSource: 'Editing SKILL.md',
sourceSavedReprojection: 'Saved. The next resolve will sync icon/version/author and friends from the new frontmatter.',
noManifest: 'This skill does not declare a v3 manifest. Legacy fields apply.',
noTools: 'No allowed-tools declared by this skill. The LLM will fall back to the global tool set when this skill is bound to an agent.',
noFeatures: 'No features[] matrix declared. The skill is treated as a single default feature.',
noLessons: 'No lessons recorded yet. Lessons are written by the LLM via the record_lesson tool — ask an agent during a session "remember this lesson for {skill name}".',
noEmployees: 'This skill is not yet reachable by any agent. Either bind it explicitly in an agent\'s Skills tab, or enable the skill globally so agents without explicit bindings can use it.',
toolsHint: 'These tool names are merged into the LLM allowed-tool list when this skill is bound to an agent. Tools owned by SETUP_NEEDED features stay hidden.',
lessonsHint: 'Per-skill LESSONS.md. Auto-injected after SKILL.md body when this skill is loaded.',
memoryHint: 'Agents that can use this skill. Explicit = wired in the agent\'s Skills tab; Implicit = the agent has no explicit Skill bindings, so it sees every globally-enabled skill including this one.',
openMemory: 'Open agent memory',
bindingExplicit: 'Explicit',
bindingImplicit: 'Implicit',
clearLessons: 'Clear all',
clearLessonsConfirm: 'Delete all recorded lessons for this skill? This cannot be undone.',
secretsHint: 'Key/value pairs injected as environment variables into this skill\'s script subprocess. Stored AES-encrypted; plaintext is never returned over the API. Example: tencent-meeting needs TENCENT_MEETING_TOKEN.',
secretsEmpty: 'No secrets configured for this skill.',
secretKey: 'Key',
secretValue: 'Value',
secretUpdatedAt: 'Last updated',
secretAdd: '+ Add secret',
secretAddTitle: 'Add secret',
secretEditTitle: 'Edit secret',
secretEdit: 'Edit',
secretDelete: 'Delete',
secretDialogHint: 'Saved immediately and injected into the script subprocess on next skill execution.',
secretKeyPlaceholder: 'e.g. TENCENT_MEETING_TOKEN',
secretKeyRule: 'Letters, digits and underscore only; first char must be a letter or underscore; ≤128 chars.',
secretValuePlaceholder: 'Paste the value (AES-encrypted server-side on submit)',
secretSaveSuccess: 'Secret saved',
secretSaveFailed: 'Failed to save',
secretDeleteConfirm: 'Delete secret "{key}"? This skill\'s scripts will no longer receive this environment variable.',
secretDeleteSuccess: 'Secret deleted',
secretDeleteFailed: 'Failed to delete',
secretLoadFailed: 'Failed to load secrets',
filesTitle: 'Bundle files',
filesHint: 'Files under scripts/, references/ and templates/. The database is the single source of truth; saves sync every node\'s workspace and take effect for agents immediately.',
filesEmpty: 'This skill has no supporting files yet.',
fileNew: 'New file',
fileEdit: 'Edit',
fileDelete: 'Delete',
fileDeleteConfirm: 'Delete "{path}"? This removes both the database record and the workspace file. Cannot be undone.',
fileSaved: 'File saved',
fileDeleted: 'File deleted',
fileLoadFailed: 'Failed to read file',
fileSaveFailed: 'Save failed',
filePathPlaceholder: 'Path, e.g. references/api.md, scripts/run.py, templates/report.html',
fileContentPlaceholder: 'File content…',
builtinFilesReadonly: 'Built-in skill files are restored from the shipped bundle on upgrade — read-only here.',
},
runtime: {
disabled: 'Disabled',
unknown: 'Unknown',
securityBlocked: 'Security Blocked',
depsMissing: 'Dependencies Missing',
directoryActive: 'Directory Active',
databaseFallback: 'Database Fallback',
databaseActive: 'Database Active',
unresolved: 'Unresolved',
},
messages: {
saveFailed: 'Failed to save skill',
saveSuccess: 'Saved',
deleteConfirm: 'Are you sure you want to delete this skill? This cannot be undone.',
deleteTitle: 'Confirm Delete',
deleteFailed: 'Failed to delete skill',
toggleFailed: 'Failed to toggle skill status',
},
import: {
title: 'Import Skill',
urlTab: 'From URL',
searchTab: 'Search Hub',
urlPlaceholder: 'Enter GitHub repo URL or ClawHub skill URL',
urlHint: 'Supports GitHub repositories and ClawHub marketplace links',
examples: 'Examples',
install: 'Install',
installing: 'Installing...',
installed: 'Installed successfully',
failed: 'Installation failed',
cancelled: 'Cancelled',
cancel: 'Cancel',
overwrite: 'Overwrite existing skill',
enableAfterInstall: 'Enable after install',
searchPlaceholder: 'Search ClawHub marketplace...',
search: 'Search',
searching: 'Searching...',
searchFailed: 'Search failed',
noResults: 'No matching skills found',
statusPending: 'Pending',
zipTab: 'ZIP Import',
dropHint: 'Drop .zip file here, or click to select',
zipRequirement: 'ZIP must contain a SKILL.md file',
uploading: 'Installing...',
uploadSuccess: 'Skill installed successfully',
uploadFailed: 'Installation failed',
invalidZip: 'Please select a .zip file',
tooLarge: 'File too large (max 50MB)',
},
},
skillCurator: {
title: 'Skill Curator',
desc: 'Automatically archives long-idle skills on a time window to keep the skill catalog tidy.',
stateEnabled: 'Deployed',
stateActivated: 'Activated',
statePreview: 'Preview only',
statePaused: 'Paused',
config: 'Configuration',
scope: 'Scope',
staleAfter: 'Stale after (days)',
archiveAfter: 'Archive after (days)',
cron: 'Schedule',
counts: 'Current counts',
active: 'Active',
stale: 'Stale',
archived: 'Archived',
pinned: 'Pinned',
blockedByBindings: 'Binding-protected',
control: 'Runtime control',
lastObservedAt: 'First observed',
lastDryRunAt: 'Last preview',
lastRunAt: 'Last run',
nextScheduledRun: 'Next run',
activate: 'Activate curator',
deactivate: 'Back to preview',
pause: 'Pause',
resume: 'Resume',
runDryRun: 'Preview now',
activatedHint: 'The curator is activated and archives idle skills on schedule.',
previewHint: 'The curator is in preview-only mode and never archives. Review a preview report, then activate.',
pausedHint: 'The curator is paused; the scheduled sweep will not run.',
reports: 'Run reports',
noReports: 'No run reports yet',
reportPlanned: 'Planned',
reportApplied: 'Applied',
reportScanned: 'Scanned',
reportDryRun: 'Preview',
reportTransitions: 'Transitions',
reportConsolidations: 'Consolidations',
consolidateOn: 'Merge on',
consolidateOff: 'Merge off',
enableConsolidate: 'Enable merge',
disableConsolidate: 'Disable merge',
consolidateHint: 'Consolidation spends one LLM call to merge highly-overlapping agent-created skills into a broader one; absorbed skills are archived (recoverable). Off by default.',
routines: 'Routine mining',
routinesHint: 'A nightly pass clusters the opening request of each conversation to find the ones you make habitually. A cluster is only promoted to a skill once it clears both gates — seen {occurrences} times AND across {days} distinct days. Occurrences prove repetition; distinct days prove a habit rather than one afternoon of retries.',
noRoutines: 'No candidates yet',
routineMine: 'Mine now',
routineMineSuccess: 'Mining complete — {n} candidate(s) refreshed',
routineFilterObserving: 'Observing',
routineFilterPromoted: 'Promoted',
routineFilterDismissed: 'Dismissed',
routineFilterAll: 'All',
routineStatusObserving: 'Observing',
routineStatusReady: 'Ready to promote',
routineStatusPromoted: 'Promoted',
routineStatusDismissed: 'Dismissed',
routineOccurrences: '{n}x',
routineDays: '{n} days',
routineLastSeen: 'Last seen',
routinePromote: 'Promote to skill',
routinePromoteEarly: 'Promote early',
routinePromoteEarlyConfirm: 'This candidate has only been seen {occurrences} times across {days} days, below the automatic threshold. Promoting spends an LLM call and writes a skill the agent will consult from then on. Continue?',
routinePromoteSuccess: 'Promoted to a skill',
routineDismiss: 'Dismiss',
routineDismissSuccess: 'Dismissed — later sweeps will not reopen it',
routineReopen: 'Reopen',
routineReopenSuccess: 'Back under observation',
snapshots: 'Restore points',
snapshotsHint: 'A restore point is captured before every sweep that actually applies changes (previews need none). Sweeps archive skills and, with consolidation on, rewrite skill bodies — unattended, so a bad pass is usually noticed long after it ran. Rolling back is itself snapshotted first.',
noSnapshots: 'No restore points yet',
snapshotCapture: 'Capture now',
snapshotCaptureSuccess: 'Restore point captured',
snapshotSkillCount: '{n} skills',
snapshotRestore: 'Roll back to this',
snapshotRestoreConfirm: 'This rewrites every skill body and lifecycle state back to how they were at {time} ({n} skills), overwriting the current content. A restore point is captured first, so this is itself undoable. Continue?',
snapshotRestoreSuccess: 'Restored {restored} skill(s); {missing} no longer present and skipped',
unmanaged: 'Skills outside curation',
unmanagedHint: 'These skills are not subject to automatic archival. Handing one over puts it on the idle clock — adoption does not grant a fresh window, so an already-idle skill may be archived on the next sweep.',
managedList: 'Under curation',
unmanagedList: 'Outside curation',
noManaged: 'No skills under curation yet',
release: 'Release',
releaseSuccess: "Released '{name}'",
unobserved: 'Clock not started yet',
noUnmanaged: 'No unmanaged skills',
unmanagedReasonLegacy: 'Predates the provenance field; authorship unknowable',
unmanagedReasonUser: 'Created by a user',
unmanagedDaysIdle: 'Idle {n} day(s)',
adopt: 'Adopt',
adoptConfirm: 'Hand \'{name}\' over to autonomous curation? This does not reset its idle clock — if it is already long idle, the next sweep may archive it.',
adoptSuccess: 'Adopted \'{name}\'',
consolidateCreate: 'new',
consolidateEdit: 'edit',
activateSuccess: 'Skill curator activated',
deactivateSuccess: 'Back to preview-only mode',
pauseSuccess: 'Skill curator paused',
resumeSuccess: 'Skill curator resumed',
consolidateSuccess: 'Consolidation enabled',
disableConsolidateSuccess: 'Consolidation disabled',
dryRunSuccess: 'Preview report generated',
actionFailed: 'Action failed',
loadFailed: 'Failed to load skill curator status',
},
onboarding: {
title: 'Welcome to MateClaw',
subtitle: 'Set up your first AI model',
stepPath: 'Choose Path',
stepConfigure: 'Configure Model',
stepVerify: 'Verify',
localTitle: 'Local Model',
localDesc: 'Ollama installed? Enable local models in one click',
localDetected: 'Detected',
cloudTitle: 'Cloud API',
cloudDesc: 'Use cloud models like OpenAI, DashScope, etc.',
selectModel: 'Select Model',
setDefault: 'Set as Default',
enterApiKey: 'Enter API Key',
testConnection: 'Test Connection',
testSuccess: 'Connection Successful',
testFailed: 'Connection Failed',
saveAndContinue: 'Save & Enable',
testBeforeSave: 'Test the connection first — once it passes you can enable it',
saveFailed: 'Save failed, please try again',
verifyTitle: 'Verify Your Model',
verifyMessage: 'Hello! Tell me briefly about yourself.',
send: 'Send',
startUsing: 'Start Using MateClaw',
skip: 'Skip',
back: 'Back',
},
talk: {
title: 'Talk Mode',
ready: 'Ready',
connecting: 'Connecting...',
listening: 'Listening...',
processing: 'Thinking...',
speaking: 'Speaking...',
holdToTalk: 'Hold to talk',
releaseToSend: 'Release to send',
retry: 'Tap to retry',
you: 'You',
ai: 'AI',
micError: 'Cannot access microphone',
connectionError: 'Connection error — tap the button to retry',
playbackError: 'Audio playback failed',
tooShort: 'Recording too short — hold the button while speaking',
},
browser: {
timeline: {
title: 'Browser ({count} actions)',
},
actions: {
start: 'start',
stop: 'stop',
open: 'open',
click: 'click',
type: 'type',
screenshot: 'screenshot',
},
},
dashboard: {
title: 'Dashboard',
kicker: 'Operations Pulse',
desc: 'System usage overview and runtime status',
conversations: 'Conversations',
messages: 'Messages',
tokens: 'Token Usage',
toolCalls: 'Tool Calls',
periodDesc: 'A sharper view of how your system behaves across short, medium, and monthly horizons.',
runsDesc: 'Execution history with timing, cost, and outcome at a glance.',
trend: {
title: 'Your Week',
subtitle: 'How often you and your AI have been working together.',
},
periodComparison: 'Period Comparison',
periods: {
today: 'Today',
thisWeek: 'This Week',
thisMonth: 'This Month',
},
models: {
title: 'Model Configuration',
subtitle: 'Enabled LLM providers and the active model.',
activeModel: 'Active model',
notSet: 'Not set',
configured: 'ready',
manage: 'Manage',
empty: 'No LLM providers enabled yet.',
emptyCta: 'Configure',
},
recentRuns: 'Recent Cron Job Runs',
noRuns: 'No runs yet',
runColumns: {
time: 'Time',
job: 'Job',
status: 'Status',
trigger: 'Trigger',
duration: 'Duration',
tokens: 'Tokens',
},
operationalExport: 'Export Operational Data',
exportDescription: 'Select a date range to generate an operational report. Max 90 days.',
generateReport: 'Generate Report',
generating: 'Generating...',
downloadReport: 'Download',
regenerating: 'Regenerate',
generateFailed: 'Generation Failed',
exportInProgress: 'Export in progress...',
expiredHint: 'Report is ready, please download soon',
generatingProgress: 'Generating... ({step}/{total})',
reportReady: 'Report is ready',
exportIncludes: 'The report contains {count} data sheets',
},
memory: {
kicker: 'Memory',
title: 'Memory',
desc: 'What your agent thinks about, remembers, and lets go.',
selectAgent: 'Select Employee',
selectAgentHint: 'Select an agent to view its memory',
tabTimeline: 'Timeline',
tabMemory: 'Long-term',
tabFacts: 'Facts',
noReports: 'No dream reports yet',
modeFocused: 'Focused',
modeNightly: 'Nightly',
candidates: 'candidates',
loadMore: 'Load more',
retry: 'Retry',
memoryBrowser: {
header: 'Overview',
empty: 'No memory content yet',
userEdited: 'User edited',
files: {
memory: 'Memory',
profile: 'Profile',
soul: 'Soul',
userFacts: 'User facts',
references: 'References',
},
},
time: {
justNow: 'just now',
hoursAgo: '{n}h ago',
daysAgo: '{n}d ago',
},
status: {
success: 'Success',
failed: 'Failed',
skipped: 'Skipped',
running: 'Running',
pending: 'Pending',
},
report: {
mode: 'Mode',
topic: 'Topic',
status: 'Status',
time: 'Time',
candidates: 'Candidates',
promoted: 'Promoted',
rejected: 'Rejected',
trigger: 'Trigger',
reason: 'LLM Reason',
diff: 'Change Summary',
},
morningCard: {
title: 'Dream Recap',
dismiss: 'Got it',
promoted: '{count} memories consolidated',
},
hil: {
confirm: 'Confirm',
edit: 'Edit',
cancel: 'Cancel',
save: 'Save',
editPlaceholder: 'Edit memory content...',
confirmed: 'Confirmed',
saved: 'Saved',
moveUp: 'Move up',
moveDown: 'Move down',
},
facts: {
searchPlaceholder: 'Search facts…',
empty: 'No facts yet (enable fact projection)',
trust: 'Trust',
used: 'Used {n} times',
helpful: 'Helpful',
unhelpful: 'Not helpful',
forget: 'Forget',
forgotten: 'Forgotten',
resolved: 'Resolved',
contradictions: '{count} contradictions pending',
resolve: {
KEEP_A: 'Keep A',
KEEP_B: 'Keep B',
MERGE: 'Merge',
IGNORE: 'Ignore',
},
},
focused: {
desc: 'Re-organize memories around a specific topic, prioritizing related information.',
placeholder: 'Enter a topic, e.g. "architecture decisions"…',
trigger: 'Start',
success: 'Dream completed, timeline updated',
skipped: 'Nothing to consolidate',
},
},
// Tool display labels consumed by useToolLabel() — keys must mirror zh-CN.ts exactly.
// Unknown tools (MCP / custom skills) fall back to humanizeToolName() and are not listed here.
toolLabels: {
// Delegation
delegateToAgent: 'Delegate to Agent',
delegateParallel: 'Parallel Delegation',
listAvailableAgents: 'List Agents',
// Wiki
wiki_search_pages: 'Search Knowledge Base',
wiki_semantic_search: 'Semantic Search KB',
wiki_read_page: 'Read KB Page',
wiki_list_pages: 'List KB Pages',
wiki_related_pages: 'Find Related Pages',
wiki_trace_source: 'Trace Knowledge Source',
wiki_create_page: 'Create KB Page',
wiki_delete_page: 'Delete KB Page',
wiki_enrich_page: 'Enrich KB Page',
wiki_explain_relation: 'Explain Relation',
// Web search
search: 'Web Search',
// Memory
recall_structured: 'Recall Memory',
remember_structured: 'Save Memory',
forget_structured: 'Clear Memory',
fact_probe: 'Probe Facts',
fact_list_contradictions: 'Check Contradictions',
session_search: 'Search Sessions',
read_workspace_memory_file: 'Read Memory File',
write_workspace_memory_file: 'Write Memory File',
edit_workspace_memory_file: 'Edit Memory File',
list_workspace_memory_files: 'List Memory Files',
// Files & shell
read_file: 'Read File',
execute_shell_command: 'Execute Command',
extract_document_text: 'Extract Document',
extract_pdf_text: 'Extract PDF',
extract_docx_text: 'Extract Document',
detect_file_type: 'Detect File Type',
readMateClawDoc: 'Read System Docs',
// Generative
image_generate: 'Generate Image',
video_generate: 'Generate Video',
music_generate: 'Generate Music',
// Data
query_datasource: 'Query Datasource',
execute_sql: 'Execute SQL',
// Browser
browser_use: 'Browser Action',
// Cron
create_cron_job: 'Create Cron Job',
list_cron_jobs: 'List Cron Jobs',
toggle_cron_job: 'Toggle Cron Job',
delete_cron_job: 'Delete Cron Job',
// Time
getCurrentDateTime: 'Get Current Time',
getCurrentTime: 'Get Current Time',
getCurrentDate: 'Get Current Date',
// Skills
skill_manage: 'Manage Skills',
runSkillScript: 'Run Skill',
listSkillFiles: 'List Skill Files',
listAvailableSkills: 'List Skills',
readSkillFile: 'Read Skill File',
},
forbidden: {
title: 'Access denied',
message: 'Your role in this workspace does not include this page.',
currentRole: 'Current role: {role}',
goChat: 'Go to chat',
goBack: 'Go back',
},
goal: {
inlinePrompt: 'Want me to track this as a goal?',
inlinePromptAccept: 'Yes',
inlinePromptDecline: 'No thanks',
autoFollowup: 'Auto continuation',
sidebarActive: 'This conversation has an active goal',
completedTitle: 'Goal completed',
completedDetail: 'Stored in long-term memory; askable later',
exhaustedTitle: 'Budget exhausted',
exhaustedDetailTurns: 'Turn budget ({used}/{budget}) exhausted.',
exhaustedDetailLlm: 'LLM call budget exhausted.',
},
approval: {
grant: {
pill: {
inactive: 'Inactive',
manage: 'Manage auto-approve rules...',
},
chipLabel: 'Auto-approve active ({count})',
chipShort: 'Auto-approve {count}',
menu: 'Auto-approve',
title: 'Auto-approve rules',
desc: 'Rules let specific tool calls skip manual approval. Safety-floor patterns (rm -rf /, pipe-to-shell, etc.) always apply, and CRITICAL severity always falls back to human approval.',
scope: {
conversation: 'Conversation',
agent: 'Agent',
user: 'User',
workspace: 'Workspace',
},
kind: {
always: 'Always',
until: 'Until expiry',
conversationEnd: 'Until conversation ends',
},
severityCeiling: 'Severity ceiling',
createBtn: 'New rule',
createWorkspaceBtn: 'Create workspace-wide rule (danger)',
createWorkspaceWarning: 'This rule will auto-approve every tool call from every user in this workspace. Safety-floor patterns still block disaster commands, but every other risk gets bypassed. Confirm with your login password.',
revokeBtn: 'Revoke',
revokeConfirm: 'Revoking will disable this rule. Continue?',
revokeConfirmDetailed: 'After revoking, auto-approve for {tool} will no longer apply — the next call will require manual review again. Continue?',
anyTool: 'all tools',
viewResolutions: 'View {count} auto-approve events triggered by this rule →',
empty: 'No auto-approve rules configured yet',
columns: {
scope: 'Scope',
tool: 'Tool',
rule: 'Rule',
severity: 'Severity ceiling',
kind: 'Kind',
expire: 'Expires',
grantedBy: 'Granted by',
grantedAt: 'Granted at',
note: 'Note',
actions: 'Actions',
},
form: {
scopeType: 'Scope type',
scopeId: 'Scope id',
toolName: 'Tool name (empty = any tool)',
ruleId: 'Rule id (empty = any rule)',
maxSeverity: 'Max severity',
grantKind: 'Grant kind',
expireAt: 'Expire at',
note: 'Note',
password: 'Login password (required for sensitive rules)',
workspaceHint: 'This grant is created in workspace {name} and only matches conversations belonging to it. IM-channel conversations (WeCom / DingTalk / …) follow the channel\'s workspace binding, not the console selection.',
severityHint: 'Only findings with severity ≤ the ceiling are auto-approved; CRITICAL always requires human approval.',
severityLow: 'auto-approve LOW findings only',
severityMedium: 'auto-approve LOW/MEDIUM findings',
severityHigh: 'auto-approve LOW/MEDIUM/HIGH findings',
scopeIdPickAgent: 'Pick an agent…',
scopeIdWorkspaceHint: 'Fixed to the current workspace. To create a grant for another workspace, switch workspaces in the console first.',
scopeIdAgentHint: 'Applies to every conversation of the selected agent within this workspace.',
scopeIdUserHint: 'USER-scope grants can only target the currently logged-in user.',
scopeIdConversationHint: 'Conversation id, as shown in the audit log "session" column (e.g. wecom:xxx).',
scopeIdConversationPlaceholder: 'e.g. wecom:xxx or a conversation id',
},
},
resolution: {
source: {
userManual: 'Manual approval',
autoGrant: 'Auto-approve',
hardBlock: 'Safety floor block',
timeout: 'Approval timeout',
},
},
hardBlock: {
banner: 'This command was blocked by the safety floor; it cannot be executed under any mode.',
},
forceHuman: {
banner: 'This command requires manual approval; auto-approve rules do not apply.',
},
},
} as const