@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.
- package/README.md +14 -2
- package/README_zh.md +14 -2
- package/_standalone/.mindos-build-version +1 -1
- package/_standalone/.next/BUILD_ID +1 -1
- package/_standalone/.next/app-path-routes-manifest.json +17 -17
- package/_standalone/.next/build-manifest.json +2 -2
- package/_standalone/.next/cache/.previewinfo +1 -1
- package/_standalone/.next/cache/.rscinfo +1 -1
- package/_standalone/.next/cache/config.json +3 -3
- package/_standalone/.next/prerender-manifest.json +3 -3
- package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error.html +2 -2
- package/_standalone/.next/server/app/_global-error.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/_standalone/.next/server/app/_not-found/page.js +1 -1
- package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/page.js +1 -1
- package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask/route.js +29 -29
- package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changes/page.js +1 -1
- package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/page.js +1 -1
- package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/explore/page.js +1 -1
- package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/help/page.js +1 -1
- package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/login/page.js +1 -1
- package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/page.js +1 -1
- package/_standalone/.next/server/app/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/setup/page.js +1 -1
- package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/trash/page.js +2 -2
- package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/view/[...path]/page.js +3 -3
- package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app-paths-manifest.json +17 -17
- package/_standalone/.next/server/chunks/{8407.js → 1473.js} +2 -2
- package/_standalone/.next/server/chunks/4931.js +24 -24
- package/_standalone/.next/server/pages/500.html +2 -2
- package/_standalone/.next/server/server-reference-manifest.js +1 -1
- package/_standalone/.next/server/server-reference-manifest.json +1 -1
- package/_standalone/.next/static/chunks/{1263-79beb8734dee7bbd.js → 1263-6df1734e4273adb1.js} +2 -2
- package/_standalone/.next/static/chunks/app/{layout-aab90c2b4faf8d57.js → layout-d91cdbe2ecf135e1.js} +20 -20
- package/_standalone/.next/static/chunks/app/{page-9a15cf85f0962c59.js → page-718fdf170428b43c.js} +2 -2
- package/_standalone/.next/static/chunks/app/trash/page-89b1430cc8a16f18.js +1 -0
- package/_standalone/.next/static/chunks/app/view/[...path]/{page-35811d7e49e08157.js → page-6e3dc47699a01de1.js} +2 -2
- package/_standalone/.next/trace +53 -53
- package/_standalone/components/ask/AskContent.tsx +6 -6
- package/_standalone/components/settings/UpdateTab.tsx +371 -107
- package/_standalone/data/skills/mindos/SKILL.md +24 -9
- package/_standalone/data/skills/mindos-zh/SKILL.md +24 -9
- package/_standalone/lib/i18n/modules/settings.ts +32 -0
- package/app/app/api/ask/route.ts +12 -2
- package/app/components/ask/AskContent.tsx +6 -6
- package/app/components/settings/UpdateTab.tsx +371 -107
- package/app/data/skills/mindos/SKILL.md +24 -9
- package/app/data/skills/mindos-zh/SKILL.md +24 -9
- package/app/lib/i18n/modules/settings.ts +32 -0
- package/package.json +1 -1
- package/scripts/build-runtime-archive.sh +113 -0
- package/skills/mindos/SKILL.md +24 -9
- package/skills/mindos/references/write-supplement.md +46 -4
- package/skills/mindos-zh/SKILL.md +24 -9
- package/skills/mindos-zh/references/write-supplement.md +47 -5
- package/_standalone/.next/static/chunks/app/trash/page-7df0139c6b0f87e0.js +0 -1
- /package/_standalone/.next/static/{VAb8MoUUpoXjVJalRJogO → ushZ_NJ8rE3rG5hZfaYOX}/_buildManifest.js +0 -0
- /package/_standalone/.next/static/{VAb8MoUUpoXjVJalRJogO → ushZ_NJ8rE3rG5hZfaYOX}/_ssgManifest.js +0 -0
|
@@ -544,25 +544,25 @@ export default function AskContent({ visible, currentFile, initialMessage, initi
|
|
|
544
544
|
{isPanel ? 'MindOS Agent' : t.ask.title}
|
|
545
545
|
</span>
|
|
546
546
|
</div>
|
|
547
|
-
<div className="flex items-center gap-1">
|
|
548
|
-
<button type="button" onClick={() => setShowHistory(v => !v)} aria-pressed={showHistory} className={`p-
|
|
547
|
+
<div className="flex items-center gap-1 shrink-0">
|
|
548
|
+
<button type="button" onClick={(e) => { e.stopPropagation(); setShowHistory(v => !v); }} aria-pressed={showHistory} className={`p-2 rounded transition-colors ${showHistory ? 'bg-muted text-foreground' : 'text-muted-foreground hover:text-foreground hover:bg-muted'}`} title={t.hints.sessionHistory}>
|
|
549
549
|
<History size={iconSize} />
|
|
550
550
|
</button>
|
|
551
|
-
<button type="button" onClick={handleResetSession} disabled={isLoading} className="p-
|
|
551
|
+
<button type="button" onClick={(e) => { e.stopPropagation(); handleResetSession(); }} disabled={isLoading} className="p-2 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-40" title={t.hints.newSession}>
|
|
552
552
|
<SquarePen size={iconSize} />
|
|
553
553
|
</button>
|
|
554
554
|
{isPanel && onMaximize && (
|
|
555
|
-
<button type="button" onClick={onMaximize} className="p-
|
|
555
|
+
<button type="button" onClick={(e) => { e.stopPropagation(); onMaximize(); }} className="p-2 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title={maximized ? t.hints.restorePanel : t.hints.maximizePanel}>
|
|
556
556
|
{maximized ? <Minimize2 size={iconSize} /> : <Maximize2 size={iconSize} />}
|
|
557
557
|
</button>
|
|
558
558
|
)}
|
|
559
559
|
{onModeSwitch && (
|
|
560
|
-
<button type="button" onClick={onModeSwitch} className="p-
|
|
560
|
+
<button type="button" onClick={(e) => { e.stopPropagation(); onModeSwitch(); }} className="p-2 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title={askMode === 'popup' ? t.hints.dockToSide : t.hints.openAsPopup}>
|
|
561
561
|
{askMode === 'popup' ? <PanelRight size={iconSize} /> : <AppWindow size={iconSize} />}
|
|
562
562
|
</button>
|
|
563
563
|
)}
|
|
564
564
|
{onClose && (
|
|
565
|
-
<button type="button" onClick={onClose} className="p-
|
|
565
|
+
<button type="button" onClick={(e) => { e.stopPropagation(); onClose(); }} className="p-2 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title={t.hints.closePanel} aria-label="Close">
|
|
566
566
|
<X size={isPanel ? iconSize : 15} />
|
|
567
567
|
</button>
|
|
568
568
|
)}
|
|
@@ -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
|
-
/**
|
|
71
|
-
function
|
|
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-
|
|
139
|
-
<div className="
|
|
140
|
-
<div className="flex items-center
|
|
141
|
-
<
|
|
142
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
237
|
-
<div className="
|
|
238
|
-
<
|
|
239
|
-
|
|
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
|
-
|
|
260
|
-
|
|
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.
|
|
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 →
|
|
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 →
|
|
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 →
|
|
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
|
-
##
|
|
122
|
+
## Judgment heuristics
|
|
120
123
|
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
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
|
|