@geminilight/mindos 0.6.28 → 0.6.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +10 -4
  2. package/app/app/api/a2a/agents/route.ts +9 -0
  3. package/app/app/api/a2a/delegations/route.ts +9 -0
  4. package/app/app/api/a2a/discover/route.ts +2 -0
  5. package/app/app/api/a2a/route.ts +6 -6
  6. package/app/app/api/acp/config/route.ts +82 -0
  7. package/app/app/api/acp/detect/route.ts +114 -0
  8. package/app/app/api/acp/install/route.ts +51 -0
  9. package/app/app/api/acp/registry/route.ts +31 -0
  10. package/app/app/api/acp/session/route.ts +185 -0
  11. package/app/app/api/ask/route.ts +116 -13
  12. package/app/app/api/workflows/route.ts +156 -0
  13. package/app/app/layout.tsx +2 -0
  14. package/app/app/page.tsx +7 -2
  15. package/app/components/ActivityBar.tsx +12 -4
  16. package/app/components/AskModal.tsx +4 -1
  17. package/app/components/DirView.tsx +64 -2
  18. package/app/components/FileTree.tsx +40 -10
  19. package/app/components/GuideCard.tsx +7 -17
  20. package/app/components/HomeContent.tsx +1 -0
  21. package/app/components/MarkdownView.tsx +2 -0
  22. package/app/components/Panel.tsx +1 -0
  23. package/app/components/RightAskPanel.tsx +5 -1
  24. package/app/components/SearchModal.tsx +234 -80
  25. package/app/components/SidebarLayout.tsx +6 -0
  26. package/app/components/agents/AgentDetailContent.tsx +266 -52
  27. package/app/components/agents/AgentsContentPage.tsx +32 -6
  28. package/app/components/agents/AgentsPanelA2aTab.tsx +684 -0
  29. package/app/components/agents/AgentsPanelSessionsTab.tsx +166 -0
  30. package/app/components/agents/SkillDetailPopover.tsx +4 -9
  31. package/app/components/agents/agents-content-model.ts +2 -2
  32. package/app/components/ask/AgentSelectorCapsule.tsx +218 -0
  33. package/app/components/ask/AskContent.tsx +197 -239
  34. package/app/components/ask/FileChip.tsx +82 -17
  35. package/app/components/ask/MentionPopover.tsx +21 -3
  36. package/app/components/ask/MessageList.tsx +30 -9
  37. package/app/components/ask/SlashCommandPopover.tsx +21 -3
  38. package/app/components/help/HelpContent.tsx +9 -9
  39. package/app/components/panels/AgentsPanel.tsx +2 -0
  40. package/app/components/panels/AgentsPanelAgentDetail.tsx +5 -8
  41. package/app/components/panels/AgentsPanelHubNav.tsx +16 -2
  42. package/app/components/panels/EchoPanel.tsx +5 -1
  43. package/app/components/panels/EchoSidebarStats.tsx +136 -0
  44. package/app/components/panels/WorkflowsPanel.tsx +206 -0
  45. package/app/components/renderers/workflow-yaml/StepEditor.tsx +157 -0
  46. package/app/components/renderers/workflow-yaml/WorkflowEditor.tsx +201 -0
  47. package/app/components/renderers/workflow-yaml/WorkflowRunner.tsx +226 -0
  48. package/app/components/renderers/workflow-yaml/WorkflowYamlRenderer.tsx +126 -0
  49. package/app/components/renderers/workflow-yaml/execution.ts +177 -0
  50. package/app/components/renderers/workflow-yaml/index.ts +6 -0
  51. package/app/components/renderers/workflow-yaml/manifest.ts +21 -0
  52. package/app/components/renderers/workflow-yaml/parser.ts +172 -0
  53. package/app/components/renderers/workflow-yaml/selectors.tsx +522 -0
  54. package/app/components/renderers/workflow-yaml/serializer.ts +56 -0
  55. package/app/components/renderers/workflow-yaml/types.ts +46 -0
  56. package/app/components/settings/KnowledgeTab.tsx +3 -6
  57. package/app/components/settings/McpSkillsSection.tsx +4 -5
  58. package/app/components/settings/McpTab.tsx +6 -8
  59. package/app/components/setup/StepSecurity.tsx +4 -5
  60. package/app/components/setup/index.tsx +5 -11
  61. package/app/components/ui/Toaster.tsx +39 -0
  62. package/app/hooks/useA2aRegistry.ts +6 -1
  63. package/app/hooks/useAcpConfig.ts +96 -0
  64. package/app/hooks/useAcpDetection.ts +120 -0
  65. package/app/hooks/useAcpRegistry.ts +86 -0
  66. package/app/hooks/useAskModal.ts +12 -5
  67. package/app/hooks/useAskPanel.ts +8 -5
  68. package/app/hooks/useAskSession.ts +19 -2
  69. package/app/hooks/useDelegationHistory.ts +49 -0
  70. package/app/hooks/useImageUpload.ts +152 -0
  71. package/app/lib/a2a/client.ts +49 -5
  72. package/app/lib/a2a/orchestrator.ts +0 -1
  73. package/app/lib/a2a/task-handler.ts +4 -4
  74. package/app/lib/a2a/types.ts +15 -0
  75. package/app/lib/acp/acp-tools.ts +95 -0
  76. package/app/lib/acp/agent-descriptors.ts +274 -0
  77. package/app/lib/acp/bridge.ts +144 -0
  78. package/app/lib/acp/index.ts +40 -0
  79. package/app/lib/acp/registry.ts +202 -0
  80. package/app/lib/acp/session.ts +717 -0
  81. package/app/lib/acp/subprocess.ts +495 -0
  82. package/app/lib/acp/types.ts +274 -0
  83. package/app/lib/agent/model.ts +18 -3
  84. package/app/lib/agent/to-agent-messages.ts +25 -2
  85. package/app/lib/agent/tools.ts +2 -1
  86. package/app/lib/i18n/_core.ts +22 -0
  87. package/app/lib/i18n/index.ts +35 -0
  88. package/app/lib/i18n/modules/ai-chat.ts +215 -0
  89. package/app/lib/i18n/modules/common.ts +71 -0
  90. package/app/lib/i18n/modules/features.ts +153 -0
  91. package/app/lib/i18n/modules/knowledge.ts +429 -0
  92. package/app/lib/i18n/modules/navigation.ts +153 -0
  93. package/app/lib/i18n/modules/onboarding.ts +523 -0
  94. package/app/lib/i18n/modules/panels.ts +1196 -0
  95. package/app/lib/i18n/modules/settings.ts +585 -0
  96. package/app/lib/i18n-en.ts +2 -1518
  97. package/app/lib/i18n-zh.ts +2 -1542
  98. package/app/lib/i18n.ts +3 -6
  99. package/app/lib/pi-integration/skills.ts +21 -6
  100. package/app/lib/renderers/index.ts +2 -2
  101. package/app/lib/settings.ts +10 -0
  102. package/app/lib/toast.ts +79 -0
  103. package/app/lib/types.ts +12 -1
  104. package/app/next-env.d.ts +1 -1
  105. package/app/package.json +3 -1
  106. package/bin/cli.js +25 -25
  107. package/bin/commands/file.js +29 -2
  108. package/bin/commands/space.js +249 -91
  109. package/package.json +1 -1
  110. package/templates/en/.mindos/workflows/Sprint Release.flow.yaml +130 -0
  111. package/templates/zh/.mindos/workflows//345/221/250/350/277/255/344/273/243/346/243/200/346/237/245.flow.yaml +84 -0
  112. package/app/components/renderers/workflow/WorkflowRenderer.tsx +0 -409
  113. package/app/components/renderers/workflow/manifest.ts +0 -14
@@ -0,0 +1,274 @@
1
+ /**
2
+ * ACP (Agent Client Protocol) — Core Types for MindOS ACP integration.
3
+ * ACP uses JSON-RPC 2.0 over stdio (subprocess model), not HTTP.
4
+ * Sessions are stateful with prompt turns, tool calls, and streaming updates.
5
+ * Reference: https://agentclientprotocol.com
6
+ */
7
+
8
+ /* ── Transport ────────────────────────────────────────────────────────── */
9
+
10
+ /** How an ACP agent is spawned */
11
+ export type AcpTransportType = 'stdio' | 'npx' | 'uvx' | 'binary';
12
+
13
+ /* ── ContentBlock (ACP prompt format) ─────────────────────────────────── */
14
+
15
+ export type AcpContentBlock =
16
+ | { type: 'text'; text: string }
17
+ | { type: 'image'; data: string; mimeType: string }
18
+ | { type: 'audio'; data: string; mimeType: string }
19
+ | { type: 'resource_link'; uri: string; name: string }
20
+ | { type: 'resource'; resource: { uri: string; text?: string; blob?: string } };
21
+
22
+ /* ── StopReason ───────────────────────────────────────────────────────── */
23
+
24
+ export type AcpStopReason = 'end_turn' | 'max_tokens' | 'max_turn_requests' | 'refusal' | 'cancelled';
25
+
26
+ /* ── Modes & Config ───────────────────────────────────────────────────── */
27
+
28
+ export interface AcpMode {
29
+ id: string;
30
+ name: string;
31
+ description?: string;
32
+ }
33
+
34
+ export interface AcpConfigOptionEntry {
35
+ id: string;
36
+ label: string;
37
+ }
38
+
39
+ export interface AcpConfigOption {
40
+ type: 'select';
41
+ configId: string;
42
+ category: 'mode' | 'model' | 'thought_level' | 'other' | string;
43
+ label?: string;
44
+ currentValue: string;
45
+ options: AcpConfigOptionEntry[];
46
+ }
47
+
48
+ /* ── Auth ──────────────────────────────────────────────────────────────── */
49
+
50
+ export interface AcpAuthMethod {
51
+ id: string;
52
+ name: string;
53
+ description?: string;
54
+ }
55
+
56
+ /* ── Capabilities ─────────────────────────────────────────────────────── */
57
+
58
+ /** What the agent declares it supports (from initialize response). */
59
+ export interface AcpAgentCapabilities {
60
+ loadSession?: boolean;
61
+ mcpCapabilities?: { http?: boolean; sse?: boolean };
62
+ promptCapabilities?: { audio?: boolean; embeddedContext?: boolean; image?: boolean };
63
+ sessionCapabilities?: { list?: boolean };
64
+ }
65
+
66
+ /** What MindOS declares as a client (sent in initialize request). */
67
+ export interface AcpClientCapabilities {
68
+ fs?: { readTextFile?: boolean; writeTextFile?: boolean };
69
+ terminal?: boolean;
70
+ }
71
+
72
+ /* ── Session ──────────────────────────────────────────────────────────── */
73
+
74
+ export type AcpSessionState = 'idle' | 'active' | 'error';
75
+
76
+ export interface AcpSession {
77
+ id: string;
78
+ agentId: string;
79
+ state: AcpSessionState;
80
+ cwd?: string;
81
+ createdAt: string;
82
+ lastActivityAt: string;
83
+ /** Agent capabilities from initialize response */
84
+ agentCapabilities?: AcpAgentCapabilities;
85
+ /** Modes available from session/new or session/load response */
86
+ modes?: AcpMode[];
87
+ /** Config options from session/new or session/load response */
88
+ configOptions?: AcpConfigOption[];
89
+ /** Auth methods from initialize response */
90
+ authMethods?: AcpAuthMethod[];
91
+ }
92
+
93
+ /** Lightweight session info returned by session/list. */
94
+ export interface AcpSessionInfo {
95
+ sessionId: string;
96
+ title?: string;
97
+ cwd?: string;
98
+ updatedAt?: string;
99
+ }
100
+
101
+ /* ── JSON-RPC (ACP uses JSON-RPC 2.0 over stdio) ─────────────────────── */
102
+
103
+ export interface AcpJsonRpcRequest {
104
+ jsonrpc: '2.0';
105
+ id: string | number;
106
+ method: string;
107
+ params?: Record<string, unknown>;
108
+ }
109
+
110
+ export interface AcpJsonRpcResponse {
111
+ jsonrpc: '2.0';
112
+ id: string | number | null;
113
+ result?: unknown;
114
+ error?: AcpJsonRpcError;
115
+ }
116
+
117
+ export interface AcpJsonRpcError {
118
+ code: number;
119
+ message: string;
120
+ data?: unknown;
121
+ }
122
+
123
+ /* ── Prompt ────────────────────────────────────────────────────────────── */
124
+
125
+ export interface AcpPromptRequest {
126
+ sessionId: string;
127
+ prompt: AcpContentBlock[];
128
+ context?: { cwd?: string };
129
+ stream?: boolean;
130
+ }
131
+
132
+ export interface AcpPromptResponse {
133
+ sessionId: string;
134
+ text: string;
135
+ done: boolean;
136
+ stopReason?: AcpStopReason;
137
+ toolCalls?: AcpToolCall[];
138
+ metadata?: Record<string, unknown>;
139
+ }
140
+
141
+ /* ── ToolCall (full ACP model) ────────────────────────────────────────── */
142
+
143
+ export type AcpToolCallKind =
144
+ | 'read' | 'edit' | 'delete' | 'move' | 'search'
145
+ | 'execute' | 'think' | 'fetch' | 'switch_mode' | 'other';
146
+
147
+ export type AcpToolCallStatus = 'pending' | 'in_progress' | 'completed' | 'failed';
148
+
149
+ export interface AcpToolCall {
150
+ id: string;
151
+ name: string;
152
+ arguments: Record<string, unknown>;
153
+ }
154
+
155
+ /** Full tool call with status, kind, and content — used in session updates. */
156
+ export interface AcpToolCallFull {
157
+ toolCallId: string;
158
+ title?: string;
159
+ kind?: AcpToolCallKind;
160
+ status: AcpToolCallStatus;
161
+ rawInput?: string;
162
+ rawOutput?: string;
163
+ content?: AcpContentBlock[];
164
+ locations?: { path: string; line?: number }[];
165
+ }
166
+
167
+ export interface AcpToolResult {
168
+ callId: string;
169
+ result: string;
170
+ isError?: boolean;
171
+ }
172
+
173
+ /* ── Plan ──────────────────────────────────────────────────────────────── */
174
+
175
+ export type AcpPlanEntryStatus = 'pending' | 'in_progress' | 'completed';
176
+ export type AcpPlanEntryPriority = 'high' | 'medium' | 'low';
177
+
178
+ export interface AcpPlanEntry {
179
+ content: string;
180
+ status: AcpPlanEntryStatus;
181
+ priority: AcpPlanEntryPriority;
182
+ }
183
+
184
+ export interface AcpPlan {
185
+ entries: AcpPlanEntry[];
186
+ }
187
+
188
+ /* ── Session Updates (streaming) — Full ACP spec ──────────────────────── */
189
+
190
+ export type AcpUpdateType =
191
+ | 'user_message_chunk'
192
+ | 'agent_message_chunk'
193
+ | 'agent_thought_chunk'
194
+ | 'tool_call'
195
+ | 'tool_call_update'
196
+ | 'plan'
197
+ | 'available_commands_update'
198
+ | 'current_mode_update'
199
+ | 'config_option_update'
200
+ | 'session_info_update'
201
+ // Legacy compat (mapped internally)
202
+ | 'text'
203
+ | 'tool_result'
204
+ | 'done'
205
+ | 'error';
206
+
207
+ export interface AcpSessionUpdate {
208
+ sessionId: string;
209
+ type: AcpUpdateType;
210
+ /** Text content for message chunk types */
211
+ text?: string;
212
+ /** Structured tool call data */
213
+ toolCall?: AcpToolCallFull;
214
+ /** Tool result (legacy) */
215
+ toolResult?: AcpToolResult;
216
+ /** Plan entries */
217
+ plan?: AcpPlan;
218
+ /** Available commands (opaque to client) */
219
+ availableCommands?: unknown[];
220
+ /** Current mode ID */
221
+ currentModeId?: string;
222
+ /** Updated config options */
223
+ configOptions?: AcpConfigOption[];
224
+ /** Session info update */
225
+ sessionInfo?: { title?: string; updatedAt?: string };
226
+ /** Error message */
227
+ error?: string;
228
+ }
229
+
230
+ /* ── Permission ───────────────────────────────────────────────────────── */
231
+
232
+ export type AcpPermissionOutcome = 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always';
233
+
234
+ /* ── Registry ─────────────────────────────────────────────────────────── */
235
+
236
+ /** An entry from the ACP registry (registry.json) */
237
+ export interface AcpRegistryEntry {
238
+ id: string;
239
+ name: string;
240
+ description: string;
241
+ version?: string;
242
+ transport: AcpTransportType;
243
+ command: string;
244
+ /** npm package name for npx-based agents (e.g. "@google/gemini-cli") */
245
+ packageName?: string;
246
+ args?: string[];
247
+ env?: Record<string, string>;
248
+ tags?: string[];
249
+ homepage?: string;
250
+ }
251
+
252
+ /** Parsed registry response */
253
+ export interface AcpRegistry {
254
+ version: string;
255
+ agents: AcpRegistryEntry[];
256
+ fetchedAt: string;
257
+ }
258
+
259
+ /* ── Error Codes ──────────────────────────────────────────────────────── */
260
+
261
+ export const ACP_ERRORS = {
262
+ SESSION_NOT_FOUND: { code: -32001, message: 'Session not found' },
263
+ SESSION_BUSY: { code: -32002, message: 'Session is busy' },
264
+ AGENT_NOT_FOUND: { code: -32003, message: 'Agent not found in registry' },
265
+ SPAWN_FAILED: { code: -32004, message: 'Failed to spawn agent process' },
266
+ TRANSPORT_ERROR: { code: -32005, message: 'Transport error' },
267
+ AUTH_REQUIRED: { code: -32000, message: 'Authentication required' },
268
+ RESOURCE_NOT_FOUND: { code: -32002, message: 'Resource not found' },
269
+ PARSE_ERROR: { code: -32700, message: 'Parse error' },
270
+ INVALID_REQUEST: { code: -32600, message: 'Invalid request' },
271
+ METHOD_NOT_FOUND: { code: -32601, message: 'Method not found' },
272
+ INVALID_PARAMS: { code: -32602, message: 'Invalid params' },
273
+ INTERNAL_ERROR: { code: -32603, message: 'Internal error' },
274
+ } as const;
@@ -1,6 +1,19 @@
1
1
  import { getModel as piGetModel, type Model } from '@mariozechner/pi-ai';
2
2
  import { effectiveAiConfig } from '@/lib/settings';
3
3
 
4
+ /** Check if any message in the conversation contains images */
5
+ export function hasImages(messages: Array<{ images?: unknown[] }>): boolean {
6
+ return messages.some(m => m.images && m.images.length > 0);
7
+ }
8
+
9
+ /** Ensure model input includes 'image' when images are present */
10
+ function ensureVisionCapable(model: Model<any>): Model<any> {
11
+ const inputs = model.input as readonly string[];
12
+ if (inputs.includes('image')) return model;
13
+ // Upgrade input to include image — most modern models support it
14
+ return { ...model, input: [...inputs, 'image'] as any };
15
+ }
16
+
4
17
  /**
5
18
  * Build a pi-ai Model for the configured provider.
6
19
  *
@@ -11,7 +24,7 @@ import { effectiveAiConfig } from '@/lib/settings';
11
24
  *
12
25
  * Returns { model, modelName, apiKey } — Agent needs model + apiKey via getApiKey hook.
13
26
  */
14
- export function getModelConfig(): {
27
+ export function getModelConfig(options?: { hasImages?: boolean }): {
15
28
  model: Model<any>;
16
29
  modelName: string;
17
30
  apiKey: string;
@@ -77,7 +90,8 @@ export function getModelConfig(): {
77
90
  }
78
91
  }
79
92
 
80
- return { model, modelName, apiKey: cfg.openaiApiKey, provider: 'openai' };
93
+ const finalModel = options?.hasImages ? ensureVisionCapable(model) : model;
94
+ return { model: finalModel, modelName, apiKey: cfg.openaiApiKey, provider: 'openai' };
81
95
  }
82
96
 
83
97
  // Anthropic
@@ -104,5 +118,6 @@ export function getModelConfig(): {
104
118
  };
105
119
  }
106
120
 
107
- return { model, modelName, apiKey: cfg.anthropicApiKey, provider: 'anthropic' };
121
+ const finalModel = options?.hasImages ? ensureVisionCapable(model) : model;
122
+ return { model: finalModel, modelName, apiKey: cfg.anthropicApiKey, provider: 'anthropic' };
108
123
  }
@@ -13,13 +13,36 @@
13
13
  * - Orphaned tool calls (running/pending from interrupted streams): supply empty result
14
14
  * - Reasoning parts: filtered out (display-only, not sent back to LLM)
15
15
  */
16
- import type { Message as FrontendMessage, ToolCallPart as FrontendToolCallPart } from '@/lib/types';
16
+ import type { Message as FrontendMessage, ToolCallPart as FrontendToolCallPart, ImagePart } from '@/lib/types';
17
17
  import type { AgentMessage } from '@mariozechner/pi-agent-core';
18
18
  import type { UserMessage, AssistantMessage, ToolResultMessage } from '@mariozechner/pi-ai';
19
19
 
20
20
  // Re-export for convenience
21
21
  export type { AgentMessage } from '@mariozechner/pi-agent-core';
22
22
 
23
+ /** Build multimodal content array for user messages with images */
24
+ function buildUserContent(text: string, images?: ImagePart[]): string | any[] {
25
+ // Filter out stripped images (empty data from persisted sessions)
26
+ const validImages = images?.filter(img => img.data);
27
+ if (!validImages || validImages.length === 0) return text;
28
+
29
+ // Multimodal content: images first, then text
30
+ // Use pi-ai ImageContent format: { type: 'image', data: base64, mimeType }
31
+ // The SDK converts this to the provider-specific format (Anthropic/OpenAI) internally
32
+ const parts: any[] = [];
33
+ for (const img of validImages) {
34
+ parts.push({
35
+ type: 'image',
36
+ data: img.data,
37
+ mimeType: img.mimeType,
38
+ });
39
+ }
40
+ if (text) {
41
+ parts.push({ type: 'text', text });
42
+ }
43
+ return parts;
44
+ }
45
+
23
46
  export function toAgentMessages(messages: FrontendMessage[]): AgentMessage[] {
24
47
  const result: AgentMessage[] = [];
25
48
 
@@ -29,7 +52,7 @@ export function toAgentMessages(messages: FrontendMessage[]): AgentMessage[] {
29
52
  if (msg.role === 'user') {
30
53
  result.push({
31
54
  role: 'user',
32
- content: msg.content,
55
+ content: buildUserContent(msg.content, msg.images),
33
56
  timestamp,
34
57
  } satisfies UserMessage as AgentMessage);
35
58
  continue;
@@ -10,6 +10,7 @@ import {
10
10
  import { readSkillContentByName, scanSkillDirs } from '@/lib/pi-integration/skills';
11
11
  import { callMcporterTool, createMcporterAgentTools, listMcporterServers, listMcporterTools } from '@/lib/pi-integration/mcporter';
12
12
  import { a2aTools } from '@/lib/a2a/a2a-tools';
13
+ import { acpTools } from '@/lib/acp/acp-tools';
13
14
 
14
15
  // Max chars per file to avoid token overflow (~100k chars ≈ ~25k tokens)
15
16
  const MAX_FILE_CHARS = 20_000;
@@ -187,7 +188,7 @@ export function getOrganizeTools(): AgentTool<any>[] {
187
188
  }
188
189
 
189
190
  export async function getRequestScopedTools(): Promise<AgentTool<any>[]> {
190
- const baseTools = [...knowledgeBaseTools, ...a2aTools];
191
+ const baseTools = [...knowledgeBaseTools, ...a2aTools, ...acpTools];
191
192
  try {
192
193
  const result = await listMcporterServers();
193
194
  const okServers = (result.servers ?? []).filter((server) => server.status === 'ok');
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Recursively widen literal string types to `string` so zh values can differ from en.
3
+ *
4
+ * Without Widen<T>, TypeScript would require zh strings to be the *exact* literal values from en.
5
+ * E.g., if en has `common: { hello: "hello" as const }`, zh would be required to also have "hello".
6
+ *
7
+ * This type converts all string literals to generic `string`, allowing Chinese translations to use
8
+ * completely different values while maintaining structure compatibility.
9
+ *
10
+ * Example:
11
+ * ```
12
+ * en = { msg: "Hello" as const } // string literal "Hello"
13
+ * zh_without = { msg: "你好" } satisfies typeof en // ❌ ERROR: "你好" not assignable to "Hello"
14
+ * zh_with = { msg: "你好" } satisfies Widen<typeof en> // ✅ OK: string satisfies string
15
+ * ```
16
+ */
17
+ export type Widen<T> =
18
+ T extends string ? string :
19
+ T extends readonly (infer U)[] ? readonly Widen<U>[] :
20
+ T extends (...args: infer A) => infer R ? (...args: A) => Widen<R> :
21
+ T extends object ? { [K in keyof T]: Widen<T[K]> } :
22
+ T;
@@ -0,0 +1,35 @@
1
+ import type { Widen } from './_core';
2
+ import { commonEn, commonZh } from './modules/common';
3
+ import { navigationEn, navigationZh } from './modules/navigation';
4
+ import { aiChatEn, aiChatZh } from './modules/ai-chat';
5
+ import { knowledgeEn, knowledgeZh } from './modules/knowledge';
6
+ import { panelsEn, panelsZh } from './modules/panels';
7
+ import { settingsEn, settingsZh } from './modules/settings';
8
+ import { onboardingEn, onboardingZh } from './modules/onboarding';
9
+ import { featuresEn, featuresZh } from './modules/features';
10
+
11
+ export const en = {
12
+ ...commonEn,
13
+ ...navigationEn,
14
+ ...aiChatEn,
15
+ ...knowledgeEn,
16
+ ...panelsEn,
17
+ ...settingsEn,
18
+ ...onboardingEn,
19
+ ...featuresEn,
20
+ } as const;
21
+
22
+ export const zh: Widen<typeof en> = {
23
+ ...commonZh,
24
+ ...navigationZh,
25
+ ...aiChatZh,
26
+ ...knowledgeZh,
27
+ ...panelsZh,
28
+ ...settingsZh,
29
+ ...onboardingZh,
30
+ ...featuresZh,
31
+ };
32
+
33
+ export type Locale = 'en' | 'zh';
34
+ export const messages = { en, zh } as const;
35
+ export type Messages = typeof en;
@@ -0,0 +1,215 @@
1
+ // ask + changes + hints
2
+
3
+ export const aiChatEn = {
4
+ ask: {
5
+ title: 'MindOS Agent',
6
+ fabLabel: 'Ask AI',
7
+ placeholder: 'Ask a question... @ files, / skills',
8
+ emptyPrompt: 'Ask anything about your knowledge base',
9
+ send: 'send',
10
+ newlineHint: 'new line',
11
+ panelComposerResize: 'Drag up to enlarge the input area',
12
+ panelComposerFooter: 'Resize height',
13
+ panelComposerResetHint: 'Double-click to reset height',
14
+ panelComposerKeyboard: 'Arrow keys adjust height; Home/End min/max',
15
+ attachFile: 'Context',
16
+ uploadedFiles: 'Uploaded',
17
+ skillsHint: 'skills',
18
+ attachCurrent: 'attach current file',
19
+ stopTitle: 'Stop',
20
+ cancelReconnect: 'Cancel reconnect',
21
+ connecting: 'Thinking with you...',
22
+ thinking: 'Thinking...',
23
+ thinkingLabel: 'Thinking',
24
+ searching: 'Searching knowledge base...',
25
+ generating: 'Generating response...',
26
+ stopped: 'Generation stopped.',
27
+ errorNoResponse: 'AI returned no content. Possible causes: model does not support streaming, proxy compatibility issue, or request exceeds context limit.',
28
+ reconnecting: (attempt: number, max: number) => `Connection lost. Reconnecting (${attempt}/${max})...`,
29
+ reconnectFailed: 'Connection failed after multiple attempts.',
30
+ retry: 'Retry',
31
+ suggestions: [
32
+ 'Summarize this document',
33
+ 'List all action items and TODOs',
34
+ 'What are the key points?',
35
+ 'Find related notes on this topic',
36
+ ],
37
+ sessionHistory: 'Session History',
38
+ clearAll: 'Clear all',
39
+ confirmClear: 'Confirm clear?',
40
+ noSessions: 'No saved sessions.',
41
+ draftingHint: 'AI is still running — you can draft the next step now.',
42
+ },
43
+ changes: {
44
+ unreadBanner: (n: number) => `${n} content change${n === 1 ? '' : 's'} unread`,
45
+ reviewNow: 'Review now',
46
+ dismiss: 'Dismiss notification',
47
+ title: 'Content changes',
48
+ subtitle: 'Review recent edits across user and agent operations.',
49
+ eventsCount: (n: number) => `${n} event${n === 1 ? '' : 's'}`,
50
+ unreadCount: (n: number) => `${n} unread`,
51
+ refresh: 'Refresh',
52
+ markSeen: 'Mark seen',
53
+ markAllRead: 'Mark all read',
54
+ filters: {
55
+ filePath: 'File path',
56
+ filePathPlaceholder: 'e.g. Projects/plan.md',
57
+ source: 'Agents (source)',
58
+ operation: 'Tools (operation)',
59
+ operationAll: 'All operations',
60
+ keyword: 'Keyword',
61
+ keywordPlaceholder: 'summary / op / path',
62
+ all: 'All',
63
+ agent: 'Agent',
64
+ user: 'User',
65
+ system: 'System',
66
+ },
67
+ loading: 'Loading changes...',
68
+ empty: 'No content changes yet.',
69
+ open: 'Open',
70
+ unchangedLines: (n: number) => `... ${n} unchanged lines ...`,
71
+ relativeTime: {
72
+ justNow: 'just now',
73
+ minutesAgo: (n: number) => `${n}m ago`,
74
+ hoursAgo: (n: number) => `${n}h ago`,
75
+ daysAgo: (n: number) => `${n}d ago`,
76
+ },
77
+ },
78
+ /** Disabled-state and contextual tooltip hints */
79
+ hints: {
80
+ noValidFiles: 'No valid files selected',
81
+ aiOrganizing: 'AI is organizing',
82
+ importInProgress: 'Import in progress',
83
+ templateInitializing: 'Another template is being initialized',
84
+ configureAiKey: 'Configure API key in Settings → AI',
85
+ syncInProgress: 'Sync already in progress',
86
+ toggleInProgress: 'Toggle operation in progress',
87
+ typeMessage: 'Type a message',
88
+ mentionInProgress: 'Mention or command in progress',
89
+ cleanupInProgress: 'Cleanup already in progress',
90
+ tokenResetInProgress: 'Token reset in progress',
91
+ aiNotConfigured: 'AI not configured or generation in progress',
92
+ generationInProgress: 'Generation in progress or AI not configured',
93
+ cannotJumpForward: 'Cannot jump forward in setup',
94
+ testInProgressOrNoKey: 'Test in progress or no API key',
95
+ workflowStepRunning: 'Workflow step already running',
96
+ workflowRunning: 'Workflow step is running',
97
+ sessionHistory: 'Session history',
98
+ newSession: 'New session',
99
+ attachFile: 'Attach local file',
100
+ maximizePanel: 'Maximize panel',
101
+ restorePanel: 'Restore panel',
102
+ dockToSide: 'Dock to side panel',
103
+ openAsPopup: 'Open as popup',
104
+ closePanel: 'Close',
105
+ newChat: 'New chat',
106
+ closeSession: 'Close session',
107
+ },
108
+ } as const;
109
+
110
+ export const aiChatZh = {
111
+ ask: {
112
+ title: 'MindOS Agent',
113
+ fabLabel: 'AI 助手',
114
+ placeholder: '输入问题… @ 附加文件,/ 技能',
115
+ emptyPrompt: '可以问任何关于知识库的问题',
116
+ send: '发送',
117
+ newlineHint: '换行',
118
+ panelComposerResize: '向上拖拽以拉高输入区',
119
+ panelComposerFooter: '拉高输入区',
120
+ panelComposerResetHint: '双击恢复默认高度',
121
+ panelComposerKeyboard: '方向键调节高度;Home/End 最小或最大',
122
+ attachFile: '上下文',
123
+ uploadedFiles: '已上传',
124
+ skillsHint: '技能',
125
+ attachCurrent: '附加当前文件',
126
+ stopTitle: '停止',
127
+ cancelReconnect: '取消重连',
128
+ connecting: '正在和你一起思考...',
129
+ thinking: '思考中...',
130
+ thinkingLabel: '思考中',
131
+ searching: '正在搜索知识库...',
132
+ generating: '正在生成回复...',
133
+ stopped: '已停止生成。',
134
+ errorNoResponse: 'AI 未返回有效内容。可能原因:模型不支持流式输出、中转站兼容性问题、或请求超出上下文限制。',
135
+ reconnecting: (attempt: number, max: number) => `连接中断,正在重连 (${attempt}/${max})...`,
136
+ reconnectFailed: '多次重连失败,请检查网络后重试。',
137
+ retry: '重试',
138
+ suggestions: [
139
+ '总结这篇文档',
140
+ '列出所有待办事项',
141
+ '这篇文档的核心要点是什么?',
142
+ '查找与这个主题相关的笔记',
143
+ ],
144
+ sessionHistory: '对话历史',
145
+ clearAll: '清除全部',
146
+ confirmClear: '确认清除?',
147
+ noSessions: '暂无历史对话。',
148
+ draftingHint: 'AI 仍在执行,你可以先输入下一步。',
149
+ },
150
+ changes: {
151
+ unreadBanner: (n: number) => `${n} 条内容变更未读`,
152
+ reviewNow: '立即查看',
153
+ dismiss: '关闭提醒',
154
+ title: '内容变更',
155
+ subtitle: '集中查看用户与 Agent 的最近编辑记录。',
156
+ eventsCount: (n: number) => `${n} 条事件`,
157
+ unreadCount: (n: number) => `${n} 条未读`,
158
+ refresh: '刷新',
159
+ markSeen: '标记已读',
160
+ markAllRead: '全部已读',
161
+ filters: {
162
+ filePath: '文件路径',
163
+ filePathPlaceholder: '例如:Projects/plan.md',
164
+ source: 'Agents(来源)',
165
+ operation: 'Tools(操作)',
166
+ operationAll: '全部操作',
167
+ keyword: '关键词',
168
+ keywordPlaceholder: '摘要 / 操作 / 路径',
169
+ all: '全部',
170
+ agent: 'Agent',
171
+ user: '用户',
172
+ system: '系统',
173
+ },
174
+ loading: '正在加载变更...',
175
+ empty: '暂无内容变更。',
176
+ open: '打开',
177
+ unchangedLines: (n: number) => `... ${n} 行未变更 ...`,
178
+ relativeTime: {
179
+ justNow: '刚刚',
180
+ minutesAgo: (n: number) => `${n} 分钟前`,
181
+ hoursAgo: (n: number) => `${n} 小时前`,
182
+ daysAgo: (n: number) => `${n} 天前`,
183
+ },
184
+ },
185
+ /** 禁用态和上下文提示文案 */
186
+ hints: {
187
+ noValidFiles: '未选择有效文件',
188
+ aiOrganizing: 'AI 正在整理中',
189
+ importInProgress: '正在导入',
190
+ templateInitializing: '正在初始化另一个模板',
191
+ configureAiKey: '请在 设置 → AI 中配置 API 密钥',
192
+ syncInProgress: '正在同步中',
193
+ toggleInProgress: '正在切换中',
194
+ typeMessage: '请输入消息',
195
+ mentionInProgress: '正在输入提及或命令',
196
+ cleanupInProgress: '正在清理中',
197
+ tokenResetInProgress: '正在重置令牌',
198
+ aiNotConfigured: 'AI 未配置或正在生成',
199
+ generationInProgress: '正在生成或 AI 未配置',
200
+ cannotJumpForward: '无法跳过前序步骤',
201
+ testInProgressOrNoKey: '正在测试或未配置 API 密钥',
202
+ workflowStepRunning: '工作流步骤正在运行',
203
+ workflowRunning: '工作流步骤正在运行',
204
+ sessionHistory: '会话历史',
205
+ newSession: '新会话',
206
+ attachFile: '附加本地文件',
207
+ maximizePanel: '最大化面板',
208
+ restorePanel: '还原面板',
209
+ dockToSide: '停靠到侧边栏',
210
+ openAsPopup: '弹窗模式',
211
+ closePanel: '关闭',
212
+ newChat: '新对话',
213
+ closeSession: '关闭会话',
214
+ },
215
+ };