@geminilight/mindos 0.6.38 → 0.6.39

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 (142) hide show
  1. package/README.md +14 -2
  2. package/README_zh.md +14 -2
  3. package/_standalone/.mindos-build-version +1 -1
  4. package/_standalone/.next/BUILD_ID +1 -1
  5. package/_standalone/.next/app-path-routes-manifest.json +17 -17
  6. package/_standalone/.next/build-manifest.json +2 -2
  7. package/_standalone/.next/cache/.previewinfo +1 -1
  8. package/_standalone/.next/cache/.rscinfo +1 -1
  9. package/_standalone/.next/cache/config.json +3 -3
  10. package/_standalone/.next/prerender-manifest.json +3 -3
  11. package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
  12. package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  13. package/_standalone/.next/server/app/_global-error.html +2 -2
  14. package/_standalone/.next/server/app/_global-error.rsc +1 -1
  15. package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  16. package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  17. package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  18. package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  19. package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  20. package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  21. package/_standalone/.next/server/app/_not-found/page.js +1 -1
  22. package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  23. package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  24. package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
  25. package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
  26. package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
  27. package/_standalone/.next/server/app/agents/page.js +1 -1
  28. package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
  29. package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  30. package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
  31. package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
  32. package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
  33. package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
  34. package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
  35. package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
  36. package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
  37. package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
  38. package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
  39. package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
  40. package/_standalone/.next/server/app/api/ask/route.js +29 -29
  41. package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
  42. package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
  43. package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
  44. package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
  45. package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
  46. package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
  47. package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
  48. package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
  49. package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
  50. package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
  51. package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  52. package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  53. package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
  54. package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  55. package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  56. package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
  57. package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
  58. package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
  59. package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
  60. package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
  61. package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
  62. package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
  63. package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  64. package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  65. package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
  66. package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
  67. package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  68. package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
  69. package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
  70. package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
  71. package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
  72. package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
  73. package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
  74. package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  75. package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
  76. package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
  77. package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
  78. package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  79. package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
  80. package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
  81. package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  82. package/_standalone/.next/server/app/changes/page.js +1 -1
  83. package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
  84. package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
  85. package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
  86. package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
  87. package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
  88. package/_standalone/.next/server/app/echo/page.js +1 -1
  89. package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
  90. package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
  91. package/_standalone/.next/server/app/explore/page.js +1 -1
  92. package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
  93. package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
  94. package/_standalone/.next/server/app/help/page.js +1 -1
  95. package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
  96. package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
  97. package/_standalone/.next/server/app/login/page.js +1 -1
  98. package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
  99. package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  100. package/_standalone/.next/server/app/page.js +1 -1
  101. package/_standalone/.next/server/app/page.js.nft.json +1 -1
  102. package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  103. package/_standalone/.next/server/app/setup/page.js +1 -1
  104. package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
  105. package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  106. package/_standalone/.next/server/app/trash/page.js +2 -2
  107. package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
  108. package/_standalone/.next/server/app/view/[...path]/page.js +3 -3
  109. package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
  110. package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
  111. package/_standalone/.next/server/app-paths-manifest.json +17 -17
  112. package/_standalone/.next/server/chunks/{8407.js → 1473.js} +2 -2
  113. package/_standalone/.next/server/chunks/4931.js +24 -24
  114. package/_standalone/.next/server/pages/500.html +2 -2
  115. package/_standalone/.next/server/server-reference-manifest.js +1 -1
  116. package/_standalone/.next/server/server-reference-manifest.json +1 -1
  117. package/_standalone/.next/static/chunks/{1263-79beb8734dee7bbd.js → 1263-6df1734e4273adb1.js} +2 -2
  118. package/_standalone/.next/static/chunks/app/{layout-aab90c2b4faf8d57.js → layout-d91cdbe2ecf135e1.js} +20 -20
  119. package/_standalone/.next/static/chunks/app/{page-9a15cf85f0962c59.js → page-718fdf170428b43c.js} +2 -2
  120. package/_standalone/.next/static/chunks/app/trash/page-89b1430cc8a16f18.js +1 -0
  121. package/_standalone/.next/static/chunks/app/view/[...path]/{page-35811d7e49e08157.js → page-6e3dc47699a01de1.js} +2 -2
  122. package/_standalone/.next/trace +53 -53
  123. package/_standalone/components/ask/AskContent.tsx +6 -6
  124. package/_standalone/components/settings/UpdateTab.tsx +371 -107
  125. package/_standalone/data/skills/mindos/SKILL.md +24 -9
  126. package/_standalone/data/skills/mindos-zh/SKILL.md +24 -9
  127. package/_standalone/lib/i18n/modules/settings.ts +32 -0
  128. package/app/app/api/ask/route.ts +12 -2
  129. package/app/components/ask/AskContent.tsx +6 -6
  130. package/app/components/settings/UpdateTab.tsx +371 -107
  131. package/app/data/skills/mindos/SKILL.md +24 -9
  132. package/app/data/skills/mindos-zh/SKILL.md +24 -9
  133. package/app/lib/i18n/modules/settings.ts +32 -0
  134. package/package.json +1 -1
  135. package/scripts/build-runtime-archive.sh +113 -0
  136. package/skills/mindos/SKILL.md +24 -9
  137. package/skills/mindos/references/write-supplement.md +46 -4
  138. package/skills/mindos-zh/SKILL.md +24 -9
  139. package/skills/mindos-zh/references/write-supplement.md +47 -5
  140. package/_standalone/.next/static/chunks/app/trash/page-7df0139c6b0f87e0.js +0 -1
  141. /package/_standalone/.next/static/{VAb8MoUUpoXjVJalRJogO → ushZ_NJ8rE3rG5hZfaYOX}/_buildManifest.js +0 -0
  142. /package/_standalone/.next/static/{VAb8MoUUpoXjVJalRJogO → ushZ_NJ8rE3rG5hZfaYOX}/_ssgManifest.js +0 -0
@@ -12,7 +12,19 @@ interface MindosDesktopBridge {
12
12
  onUpdateProgress?: (cb: (progress: { percent: number }) => void) => () => void;
13
13
  onUpdateReady?: (cb: () => void) => () => void;
14
14
  onUpdateError?: (cb: (info: { message?: string }) => void) => () => void;
15
- getAppInfo?: () => Promise<{ version?: string }>;
15
+ getAppInfo?: () => Promise<{ version?: string; mode?: string }>;
16
+ // Core Hot Update
17
+ checkCoreUpdate?: () => Promise<{
18
+ available: boolean; currentVersion: string; latestVersion: string;
19
+ urls: string[]; size: number; sha256: string;
20
+ minDesktopVersion: string; desktopTooOld: boolean;
21
+ }>;
22
+ downloadCoreUpdate?: (urls: string[], version: string, size: number, sha256: string) => Promise<void>;
23
+ cancelCoreDownload?: () => Promise<void>;
24
+ applyCoreUpdate?: () => Promise<{ ok: boolean; version?: string }>;
25
+ getCoreUpdatePending?: () => Promise<{ version: string | null }>;
26
+ onCoreUpdateProgress?: (cb: (progress: { percent: number; transferred: number; total: number }) => void) => () => void;
27
+ onCoreUpdateAvailable?: (cb: (info: { current: string; latest: string; ready?: boolean }) => void) => () => void;
16
28
  }
17
29
 
18
30
  function getDesktopBridge(): MindosDesktopBridge | null {
@@ -67,8 +79,262 @@ function StageIcon({ status }: { status: string }) {
67
79
  }
68
80
  }
69
81
 
70
- /** Desktop (Electron) update: uses electron-updater via preload IPC bridge */
71
- function DesktopUpdateTab() {
82
+ /** Format bytes to human-readable */
83
+ function formatSize(bytes: number): string {
84
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
85
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
86
+ }
87
+
88
+ /** Desktop Core update card — downloads runtime packages, restarts services only */
89
+ function DesktopCoreCard() {
90
+ const { t } = useLocale();
91
+ const u = t.settings.update;
92
+ const bridge = getDesktopBridge()!;
93
+
94
+ type CoreState = 'idle' | 'checking' | 'available' | 'downloading' | 'cancelling' | 'ready' | 'applying' | 'error' | 'desktopTooOld';
95
+ const [state, setState] = useState<CoreState>('idle');
96
+ const [currentVersion, setCurrentVersion] = useState('');
97
+ const [latestVersion, setLatestVersion] = useState('');
98
+ const [updateInfo, setUpdateInfo] = useState<{
99
+ urls: string[]; size: number; sha256: string;
100
+ } | null>(null);
101
+ const [progress, setProgress] = useState(0);
102
+ const [errorMsg, setErrorMsg] = useState('');
103
+ const [minDesktopVersion, setMinDesktopVersion] = useState('');
104
+
105
+ useEffect(() => {
106
+ // Always fetch current version first
107
+ const init = async () => {
108
+ // Get current Core version via a check call (also probes for updates)
109
+ try {
110
+ const info = await bridge.checkCoreUpdate?.();
111
+ if (info) setCurrentVersion(info.currentVersion);
112
+ } catch { /* ignore */ }
113
+
114
+ // Check for pending download (from previous session)
115
+ try {
116
+ const r = await bridge.getCoreUpdatePending?.();
117
+ if (r?.version) {
118
+ setLatestVersion(r.version);
119
+ setState('ready');
120
+ return;
121
+ }
122
+ } catch { /* ignore */ }
123
+
124
+ // Otherwise do a full remote check
125
+ handleCheck();
126
+ };
127
+ init();
128
+
129
+ const cleanups: Array<() => void> = [];
130
+ if (bridge.onCoreUpdateProgress) {
131
+ cleanups.push(bridge.onCoreUpdateProgress((p) => setProgress(Math.round(p.percent))));
132
+ }
133
+ if (bridge.onCoreUpdateAvailable) {
134
+ cleanups.push(bridge.onCoreUpdateAvailable((info) => {
135
+ setCurrentVersion(info.current);
136
+ setLatestVersion(info.latest);
137
+ setState(info.ready ? 'ready' : 'available');
138
+ }));
139
+ }
140
+ return () => cleanups.forEach((fn) => fn());
141
+ // eslint-disable-next-line react-hooks/exhaustive-deps
142
+ }, []);
143
+
144
+ const handleCheck = async () => {
145
+ if (!bridge.checkCoreUpdate) return;
146
+ setState('checking');
147
+ setErrorMsg('');
148
+ try {
149
+ const info = await bridge.checkCoreUpdate();
150
+ setCurrentVersion(info.currentVersion);
151
+ if (info.desktopTooOld) {
152
+ setLatestVersion(info.latestVersion);
153
+ setMinDesktopVersion(info.minDesktopVersion);
154
+ setState('desktopTooOld');
155
+ } else if (info.available) {
156
+ setLatestVersion(info.latestVersion);
157
+ setUpdateInfo({ urls: info.urls, size: info.size, sha256: info.sha256 });
158
+ setState('available');
159
+ } else {
160
+ setState('idle');
161
+ }
162
+ } catch {
163
+ setState('idle'); // Silent — can't reach CDN, not an error
164
+ }
165
+ };
166
+
167
+ const handleDownload = async () => {
168
+ if (!bridge.downloadCoreUpdate || !updateInfo) return;
169
+ setState('downloading');
170
+ setProgress(0);
171
+ try {
172
+ await bridge.downloadCoreUpdate(updateInfo.urls, latestVersion, updateInfo.size, updateInfo.sha256);
173
+ setState('ready');
174
+ } catch (err) {
175
+ const msg = err instanceof Error ? err.message : String(err);
176
+ if (msg.includes('aborted')) {
177
+ // User cancelled — go back to available
178
+ setState('available');
179
+ } else {
180
+ setState('error');
181
+ setErrorMsg(msg || 'Download failed');
182
+ }
183
+ }
184
+ };
185
+
186
+ const handleCancel = () => {
187
+ bridge.cancelCoreDownload?.();
188
+ // Don't immediately go to 'available' — the download IPC will reject with 'aborted',
189
+ // and the catch in handleDownload will set state to 'error'. We set 'cancelling' to
190
+ // block re-entry, and handleDownload's catch will detect the abort and go to 'available'.
191
+ setState('cancelling');
192
+ };
193
+
194
+ const handleApply = async () => {
195
+ if (!bridge.applyCoreUpdate) return;
196
+ setState('applying');
197
+ try {
198
+ const result = await bridge.applyCoreUpdate();
199
+ if (result?.version) setCurrentVersion(result.version);
200
+ setState('idle');
201
+ // Page will be reloaded by main process
202
+ } catch (err) {
203
+ setState('error');
204
+ setErrorMsg(err instanceof Error ? err.message : 'Apply failed');
205
+ }
206
+ };
207
+
208
+ // Don't show if no Core update bridge available
209
+ if (!bridge.checkCoreUpdate) return null;
210
+
211
+ return (
212
+ <div className="rounded-xl border border-border bg-card p-4 space-y-3">
213
+ <div className="flex items-center justify-between">
214
+ <span className="text-sm font-medium text-foreground">{u?.coreTitle ?? 'MindOS Core'}</span>
215
+ {currentVersion && <span className="text-xs font-mono text-muted-foreground">v{currentVersion}</span>}
216
+ </div>
217
+
218
+ {state === 'checking' && (
219
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
220
+ <Loader2 size={13} className="animate-spin" />
221
+ {u?.checking ?? 'Checking for updates...'}
222
+ </div>
223
+ )}
224
+
225
+ {state === 'idle' && (
226
+ <div className="flex items-center gap-2 text-xs text-success">
227
+ <CheckCircle2 size={13} />
228
+ {u?.coreUpToDate ?? 'Core is up to date'}
229
+ </div>
230
+ )}
231
+
232
+ {state === 'available' && (
233
+ <div className="flex items-center gap-2 text-xs text-[var(--amber-text)]">
234
+ <Download size={13} />
235
+ v{latestVersion} {u?.coreAvailable ? u.coreAvailable(formatSize(updateInfo?.size ?? 0)) : `available (${formatSize(updateInfo?.size ?? 0)})`}
236
+ </div>
237
+ )}
238
+
239
+ {(state === 'downloading' || state === 'cancelling') && (
240
+ <div className="space-y-2">
241
+ <div className="flex items-center justify-between text-xs">
242
+ <div className="flex items-center gap-2 text-foreground">
243
+ <Loader2 size={13} className="animate-spin text-[var(--amber)]" />
244
+ {state === 'cancelling'
245
+ ? (u?.coreCancel ?? 'Cancelling...')
246
+ : <>{ u?.coreDownloading ?? 'Downloading...'} v{latestVersion}</>}
247
+ </div>
248
+ {state === 'downloading' && (
249
+ <button onClick={handleCancel} className="text-muted-foreground hover:text-foreground transition-colors">
250
+ {u?.coreCancel ?? 'Cancel'}
251
+ </button>
252
+ )}
253
+ </div>
254
+ <div className="h-1 rounded-full bg-muted overflow-hidden">
255
+ <div className="h-full rounded-full bg-[var(--amber)] transition-all duration-300"
256
+ style={{ width: `${Math.max(progress, 3)}%` }} />
257
+ </div>
258
+ </div>
259
+ )}
260
+
261
+ {state === 'ready' && (
262
+ <div className="flex items-center gap-2 text-xs text-success">
263
+ <CheckCircle2 size={13} />
264
+ v{latestVersion} {u?.coreReady ?? 'ready — restart services to apply'}
265
+ </div>
266
+ )}
267
+
268
+ {state === 'applying' && (
269
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
270
+ <Loader2 size={13} className="animate-spin text-[var(--amber)]" />
271
+ {u?.coreApplying ?? 'Applying update...'}
272
+ </div>
273
+ )}
274
+
275
+ {state === 'desktopTooOld' && (
276
+ <div className="space-y-1">
277
+ <div className="flex items-center gap-2 text-xs text-[var(--amber-text)]">
278
+ <AlertCircle size={13} />
279
+ {u?.coreDesktopTooOld ? u.coreDesktopTooOld(latestVersion) : `v${latestVersion} requires a newer Desktop.`}
280
+ </div>
281
+ <p className="text-2xs text-muted-foreground">
282
+ {u?.coreDesktopTooOldHint ?? 'Please update MindOS Desktop first.'} (Desktop ≥ v{minDesktopVersion})
283
+ </p>
284
+ </div>
285
+ )}
286
+
287
+ {state === 'error' && (
288
+ <div className="flex items-center gap-2 text-xs text-destructive">
289
+ <AlertCircle size={13} />
290
+ {errorMsg || (u?.coreError ?? 'Core update failed.')}
291
+ </div>
292
+ )}
293
+
294
+ {/* Actions */}
295
+ <div className="flex items-center gap-2 pt-1">
296
+ {(state === 'idle' || state === 'error') && (
297
+ <button onClick={handleCheck} disabled={state !== 'idle' && state !== 'error'}
298
+ className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors">
299
+ <RefreshCw size={14} />
300
+ {u?.checkButton ?? 'Check for Updates'}
301
+ </button>
302
+ )}
303
+
304
+ {state === 'available' && (
305
+ <button onClick={handleDownload}
306
+ className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors">
307
+ <Download size={14} />
308
+ {u?.updateButton ? u.updateButton(latestVersion) : `Update to v${latestVersion}`}
309
+ </button>
310
+ )}
311
+
312
+ {state === 'ready' && (
313
+ <button onClick={handleApply}
314
+ className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors">
315
+ <RefreshCw size={14} />
316
+ {u?.coreRestartServices ?? 'Restart Services'}
317
+ </button>
318
+ )}
319
+
320
+ {state === 'error' && (
321
+ <button onClick={updateInfo ? handleDownload : handleCheck}
322
+ className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors">
323
+ <RefreshCw size={14} />
324
+ {u?.coreRetry ?? 'Retry'}
325
+ </button>
326
+ )}
327
+ </div>
328
+
329
+ <p className="text-2xs text-muted-foreground/60">
330
+ {u?.coreHint ?? 'Core updates only restart services — no app restart needed.'}
331
+ </p>
332
+ </div>
333
+ );
334
+ }
335
+
336
+ /** Desktop shell update card — uses electron-updater (requires app restart) */
337
+ function DesktopShellCard() {
72
338
  const { t } = useLocale();
73
339
  const u = t.settings.update;
74
340
  const bridge = getDesktopBridge()!;
@@ -135,119 +401,101 @@ function DesktopUpdateTab() {
135
401
  };
136
402
 
137
403
  return (
138
- <div className="space-y-6">
139
- <div className="rounded-xl border border-border bg-card p-4 space-y-3">
140
- <div className="flex items-center justify-between">
141
- <div className="flex items-center gap-2">
142
- <Monitor size={14} className="text-muted-foreground" />
143
- <span className="text-sm font-medium text-foreground">MindOS Desktop</span>
144
- </div>
145
- {appVersion && <span className="text-xs font-mono text-muted-foreground">v{appVersion}</span>}
404
+ <div className="rounded-xl border border-border bg-card p-4 space-y-3">
405
+ <div className="flex items-center justify-between">
406
+ <div className="flex items-center gap-2">
407
+ <Monitor size={14} className="text-muted-foreground" />
408
+ <span className="text-sm font-medium text-foreground">{u?.shellTitle ?? 'MindOS Desktop'}</span>
146
409
  </div>
410
+ {appVersion && <span className="text-xs font-mono text-muted-foreground">v{appVersion}</span>}
411
+ </div>
147
412
 
148
- {state === 'checking' && (
149
- <div className="flex items-center gap-2 text-xs text-muted-foreground">
150
- <Loader2 size={13} className="animate-spin" />
151
- {u?.checking ?? 'Checking for updates...'}
152
- </div>
153
- )}
154
-
155
- {state === 'idle' && !available && (
156
- <div className="flex items-center gap-2 text-xs text-success">
157
- <CheckCircle2 size={13} />
158
- {u?.upToDate ?? "You're up to date"}
159
- </div>
160
- )}
413
+ {state === 'checking' && (
414
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
415
+ <Loader2 size={13} className="animate-spin" />
416
+ {u?.checking ?? 'Checking for updates...'}
417
+ </div>
418
+ )}
161
419
 
162
- {state === 'idle' && available && (
163
- <div className="flex items-center gap-2 text-xs text-[var(--amber-text)]">
164
- <Download size={13} />
165
- {version ? `Update available: v${version}` : 'Update available'}
166
- </div>
167
- )}
420
+ {state === 'idle' && !available && (
421
+ <div className="flex items-center gap-2 text-xs text-success">
422
+ <CheckCircle2 size={13} />
423
+ {u?.upToDate ?? "You're up to date"}
424
+ </div>
425
+ )}
168
426
 
169
- {state === 'downloading' && (
170
- <div className="space-y-2">
171
- <div className="flex items-center gap-2 text-xs text-foreground">
172
- <Loader2 size={13} className="animate-spin text-[var(--amber)]" />
173
- {u?.desktopDownloading ?? 'Downloading update...'}
174
- </div>
175
- <div className="h-1 rounded-full bg-muted overflow-hidden">
176
- <div
177
- className="h-full rounded-full bg-[var(--amber)] transition-all duration-300"
178
- style={{ width: `${Math.max(progress, 3)}%` }}
179
- />
180
- </div>
181
- </div>
182
- )}
427
+ {state === 'idle' && available && (
428
+ <div className="flex items-center gap-2 text-xs text-[var(--amber-text)]">
429
+ <Download size={13} />
430
+ {version ? `Update available: v${version}` : 'Update available'}
431
+ </div>
432
+ )}
183
433
 
184
- {state === 'ready' && (
185
- <div className="flex items-center gap-2 text-xs text-success">
186
- <CheckCircle2 size={13} />
187
- {u?.desktopReady ?? 'Update downloaded. Restart to apply.'}
434
+ {state === 'downloading' && (
435
+ <div className="space-y-2">
436
+ <div className="flex items-center gap-2 text-xs text-foreground">
437
+ <Loader2 size={13} className="animate-spin text-[var(--amber)]" />
438
+ {u?.desktopDownloading ?? 'Downloading update...'}
188
439
  </div>
189
- )}
190
-
191
- {state === 'error' && (
192
- <div className="flex items-center gap-2 text-xs text-destructive">
193
- <AlertCircle size={13} />
194
- {errorMsg}
440
+ <div className="h-1 rounded-full bg-muted overflow-hidden">
441
+ <div className="h-full rounded-full bg-[var(--amber)] transition-all duration-300"
442
+ style={{ width: `${Math.max(progress, 3)}%` }} />
195
443
  </div>
196
- )}
197
- {/* Actions */}
198
- <div className="flex items-center gap-2 pt-1">
199
- <button
200
- onClick={handleCheck}
201
- disabled={state === 'checking' || state === 'downloading'}
202
- className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
203
- >
204
- <RefreshCw size={14} className={state === 'checking' ? 'animate-spin' : ''} />
205
- {u?.checkButton ?? 'Check for Updates'}
206
- </button>
207
-
208
- {state === 'idle' && available && (
209
- <button
210
- onClick={handleInstall}
211
- className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors"
212
- >
213
- <Download size={14} />
214
- {version ? `Update to v${version}` : 'Update'}
215
- </button>
216
- )}
444
+ </div>
445
+ )}
217
446
 
218
- {state === 'ready' && (
219
- <button
220
- onClick={async () => {
221
- try {
222
- await bridge.installUpdate();
223
- } catch {
224
- setState('error');
225
- setErrorMsg(u?.error ?? 'Failed to install update. Please try again.');
226
- }
227
- }}
228
- className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors"
229
- >
230
- <RefreshCw size={14} />
231
- {u?.desktopRestart ?? 'Restart Now'}
232
- </button>
233
- )}
447
+ {state === 'ready' && (
448
+ <div className="flex items-center gap-2 text-xs text-success">
449
+ <CheckCircle2 size={13} />
450
+ {u?.desktopReady ?? 'Update downloaded. Restart to apply.'}
234
451
  </div>
452
+ )}
235
453
 
236
- {/* Info */}
237
- <div className="border-t border-border/50 pt-3 space-y-2">
238
- <a
239
- href={CHANGELOG_URL}
240
- target="_blank"
241
- rel="noopener noreferrer"
242
- className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors"
243
- >
244
- <ExternalLink size={12} />
245
- {u?.releaseNotes ?? 'View release notes'}
246
- </a>
247
- <p className="text-2xs text-muted-foreground/60">
248
- {u?.desktopHint ?? 'Updates are delivered through the Desktop app auto-updater.'}
249
- </p>
454
+ {state === 'error' && (
455
+ <div className="flex items-center gap-2 text-xs text-destructive">
456
+ <AlertCircle size={13} />
457
+ {errorMsg}
250
458
  </div>
459
+ )}
460
+
461
+ {/* Actions */}
462
+ <div className="flex items-center gap-2 pt-1">
463
+ <button onClick={handleCheck} disabled={state === 'checking' || state === 'downloading'}
464
+ className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors">
465
+ <RefreshCw size={14} className={state === 'checking' ? 'animate-spin' : ''} />
466
+ {u?.checkButton ?? 'Check for Updates'}
467
+ </button>
468
+
469
+ {state === 'idle' && available && (
470
+ <button onClick={handleInstall}
471
+ className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors">
472
+ <Download size={14} />
473
+ {version ? `Update to v${version}` : 'Update'}
474
+ </button>
475
+ )}
476
+
477
+ {state === 'ready' && (
478
+ <button onClick={async () => {
479
+ try { await bridge.installUpdate(); }
480
+ catch { setState('error'); setErrorMsg(u?.error ?? 'Failed to install update.'); }
481
+ }}
482
+ className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors">
483
+ <RefreshCw size={14} />
484
+ {u?.desktopRestart ?? 'Restart Now'}
485
+ </button>
486
+ )}
487
+ </div>
488
+
489
+ {/* Info */}
490
+ <div className="border-t border-border/50 pt-3 space-y-2">
491
+ <a href={CHANGELOG_URL} target="_blank" rel="noopener noreferrer"
492
+ className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors">
493
+ <ExternalLink size={12} />
494
+ {u?.releaseNotes ?? 'View release notes'}
495
+ </a>
496
+ <p className="text-2xs text-muted-foreground/60">
497
+ {u?.desktopHint ?? 'Updates are delivered through the Desktop app auto-updater.'}
498
+ </p>
251
499
  </div>
252
500
  </div>
253
501
  );
@@ -256,8 +504,24 @@ function DesktopUpdateTab() {
256
504
  /** Router: Desktop uses electron-updater IPC; browser/CLI uses npm API */
257
505
  export function UpdateTab() {
258
506
  const [isDesktop, setIsDesktop] = useState(false);
259
- useEffect(() => { setIsDesktop(!!getDesktopBridge()); }, []);
260
- if (isDesktop) return <DesktopUpdateTab />;
507
+ const [isLocal, setIsLocal] = useState(true);
508
+ useEffect(() => {
509
+ const bridge = getDesktopBridge();
510
+ setIsDesktop(!!bridge);
511
+ if (bridge?.getAppInfo) {
512
+ bridge.getAppInfo().then((info) => {
513
+ if (info && 'mode' in info) setIsLocal((info as { mode?: string }).mode !== 'remote');
514
+ }).catch(() => {});
515
+ }
516
+ }, []);
517
+ if (isDesktop) {
518
+ return (
519
+ <div className="space-y-6">
520
+ {isLocal && <DesktopCoreCard />}
521
+ <DesktopShellCard />
522
+ </div>
523
+ );
524
+ }
261
525
  return <BrowserUpdateTab />;
262
526
  }
263
527
 
@@ -7,11 +7,15 @@ description: >
7
7
  Use when the task targets files inside the user's MindOS KB (mindRoot).
8
8
  NOT for editing app source, project docs, or paths outside the KB.
9
9
  Core concepts: Space, Instruction (INSTRUCTION.md), Skill (SKILL.md); notes can embody both.
10
+ Trigger when user asks to: save or record a note, search their knowledge base, update or edit
11
+ a file, organize notes, run a workflow or SOP, capture decisions from a session, append rows
12
+ to a table or CSV, hand off context to another agent — or says "帮我记下来" / "搜一下我的笔记" /
13
+ "更新知识库" / "整理文件" / "执行工作流".
10
14
  ---
11
15
 
12
16
  # MindOS Skill
13
17
 
14
- <!-- version: 1.3.1 -->
18
+ <!-- version: 1.3.2 -->
15
19
 
16
20
  **Before every task, internalize these 5 rules:**
17
21
 
@@ -83,8 +87,7 @@ User request
83
87
  └─ ASK for clarification. Propose 2-3 specific options based on KB state. Do NOT start editing.
84
88
  ```
85
89
 
86
- **For any write/SOP/structural path above → read [references/write-supplement.md](./references/write-supplement.md) first.**
87
- It covers: startup protocol, write tool selection, and step-by-step execution details for each path.
90
+ **For any write/SOP/structural path above → the write supplement is already loaded in context (see `## Write & Workflow Supplement` section below or in the next context block).**
88
91
 
89
92
  ---
90
93
 
@@ -97,7 +100,7 @@ It covers: startup protocol, write tool selection, and step-by-step execution de
97
100
  | Find files | `mindos_search_notes` (2-4 parallel keyword variants) | Single-keyword search |
98
101
  | Read content | `mindos_read_file` or `mindos_read_lines` (for large files) | Reading entire large file when you need 10 lines |
99
102
 
100
- Write, structural, and history tools → [references/write-supplement.md](./references/write-supplement.md).
103
+ Write, structural, and history tools → see Write & Workflow Supplement (loaded below or in next context block).
101
104
 
102
105
  ### Fallbacks
103
106
 
@@ -112,15 +115,27 @@ Write, structural, and history tools → [references/write-supplement.md](./refe
112
115
  |---------|------|-----------|
113
116
  | **Read-only Q&A** | Lookup / summarize / quote | Tree reasoning → search → read → answer with citations → state gaps |
114
117
 
115
- For write, SOP, structural, and handoff patterns → [references/write-supplement.md](./references/write-supplement.md).
118
+ For write, SOP, structural, and handoff patterns → see Write & Workflow Supplement (loaded below or in next context block).
116
119
 
117
120
  ---
118
121
 
119
- ## Interaction rules
122
+ ## Judgment heuristics
120
123
 
121
- - **Ambiguous request?** Ask first. Propose 2-3 options based on KB state (recent changes, directory structure). Never start reorganizing without understanding scope.
122
- - **Cite sources.** Every fact from the KB gets a file path so the user can verify.
123
- - **Brevity first.** Show the most likely match, not an exhaustive list.
124
+ **Save intent read vs. write boundary**
125
+ - "帮我记下来" / "记录一下" / "保存这个" = write
126
+ - "搜一下有没有关于 X 的笔记" / "总结上周的工作" = read-only
127
+ - Ambiguous boundary ("整理一下这些内容") → ask first: display only, or write back to KB?
128
+
129
+ **File location uncertainty**
130
+ - 扫目录树 5 秒内定不下来 → don't invent a new directory; use the nearest semantically-fit existing one and inform the user
131
+ - User says "just put it somewhere" → place in inbox or top-level general directory, propose classification after (triggers Structure classification hook)
132
+
133
+ **Scope creep signals**
134
+ - Single input routes to >5 files → pause, confirm scope; users rarely intend bulk rewrites
135
+ - "Update all of these" + content spans multiple topics → split into batches, confirm each, don't execute all at once
136
+
137
+ **Citation discipline**
138
+ - KB-cited facts must include the file path so the user can verify
124
139
 
125
140
  ---
126
141
 
@@ -6,11 +6,15 @@ description: >
6
6
  update notes, search knowledge base, organize, SOP, retrospective, CSV, handoff, route notes, distill experience.
7
7
  仅 mindRoot 知识库内任务。不用于:改代码仓库/项目文档/KB 外路径。
8
8
  核心概念:空间、指令(INSTRUCTION.md)、技能(SKILL.md);笔记可承载指令与技能。
9
+ 当用户说"帮我记下来"、"搜一下我的笔记"、"更新知识库"、"整理文件"、"执行工作流"、
10
+ "记录这次会议的决策"、"追加到表格"、"交接给下一个 Agent" 时触发。
11
+ 也适用于:save/record a note, search KB, update/edit files, run SOP, capture decisions,
12
+ append CSV rows, cross-agent handoff — 所有 mindRoot 知识库内的读写操作。
9
13
  ---
10
14
 
11
15
  # MindOS Skill
12
16
 
13
- <!-- version: 1.3.1 -->
17
+ <!-- version: 1.3.2 -->
14
18
 
15
19
  **每次任务前,内化这 5 条规则:**
16
20
 
@@ -82,8 +86,7 @@ description: >
82
86
  └─ 先问清楚。基于 KB 状态提出 2-3 个具体选项。不要开始编辑。
83
87
  ```
84
88
 
85
- **以上任何写入 / SOP / 结构路径 → 先读 [references/write-supplement.md](./references/write-supplement.md)。**
86
- 包含:启动协议、写工具选型、各路径详细执行步骤。
89
+ **以上任何写入 / SOP / 结构路径 → 写入补充已加载到上下文(见下方或下一个上下文块中的 `## 写入与工作流补充` 章节)。**
87
90
 
88
91
  ---
89
92
 
@@ -96,7 +99,7 @@ description: >
96
99
  | 找文件 | `mindos_search_notes`(2-4 条并行关键词变体)| 单关键词搜索 |
97
100
  | 读内容 | `mindos_read_file` 或 `mindos_read_lines`(大文件) | 只需 10 行却读整文件 |
98
101
 
99
- 写入、结构变更、历史工具 → [references/write-supplement.md](./references/write-supplement.md)。
102
+ 写入、结构变更、历史工具 → 见写入与工作流补充(已加载到下方或下一个上下文块)。
100
103
 
101
104
  ### 回退
102
105
 
@@ -111,15 +114,27 @@ description: >
111
114
  |------|----------|----------|
112
115
  | **只读问答** | 查找 / 总结 / 引用 | 目录树推断 → 搜索 → 读取 → 标注来源 → 明确说信息缺口 |
113
116
 
114
- 写入模式详细执行步骤 → [references/write-supplement.md](./references/write-supplement.md)。
117
+ 写入模式详细执行步骤 → 见写入与工作流补充(已加载到下方或下一个上下文块)。
115
118
 
116
119
  ---
117
120
 
118
- ## 交互规则
121
+ ## 判断启发式规则
119
122
 
120
- - **模糊请求?** 先问。基于 KB 现状提出 2-3 个选项。不要在没理解范围前开始编辑。
121
- - **标注来源。** KB 中的事实附带文件路径,便于验证。
122
- - **简洁优先。** 展示最可能的匹配,不列全部。
123
+ **保存意图 读写边界识别**
124
+ - "帮我记下来" / "记录一下" / "保存这个" = 写入
125
+ - "搜一下有没有关于 X 的笔记" / "总结上周的工作" = 只读
126
+ - 模糊边界("整理一下这些内容")→ 先问:是读取展示,还是写回 KB?
127
+
128
+ **文件位置不确定时**
129
+ - 扫目录树 5 秒内定不下来 → 不要自创新目录;放语义最近的已有目录,告知用户
130
+ - 用户说"随便放个地方" → 放 inbox 或最顶层通用目录,任务后提议归类(触发 Structure classification hook)
131
+
132
+ **范围蔓延信号**
133
+ - 单条输入导致路由表 > 5 个文件 → 暂停,先确认范围;用户鲜少意图批量重写
134
+ - "把这些都更新一下" + 内容跨越多个主题 → 拆成批次确认,不一次性执行
135
+
136
+ **引用纪律**
137
+ - KB 中引用的事实必须附带文件路径,便于用户核查
123
138
 
124
139
  ---
125
140