@geminilight/mindos 0.6.57 → 0.6.58
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/_standalone/.mindos-build-version +1 -1
- package/_standalone/.next/BUILD_ID +1 -1
- package/_standalone/.next/app-path-routes-manifest.json +21 -21
- 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 +2 -2
- 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.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.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/agents/custom/detect/route.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/route.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask/route.js +4 -4
- package/_standalone/.next/server/app/api/ask/route.js.nft.json +1 -1
- 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.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/changes/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/export/route.js.nft.json +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.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/raw/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/graph/route.js.nft.json +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/inbox/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/init/route.js.nft.json +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.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route.js.nft.json +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.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.js +1 -1
- package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/monitoring/route.js.nft.json +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.js.nft.json +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.js.nft.json +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.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.js +1 -1
- package/_standalone/.next/server/app/api/settings/route.js.nft.json +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.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.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.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.js.nft.json +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.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changes/page.js +2 -2
- 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 +2 -2
- 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 +2 -2
- 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/inbox/history/page.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/inbox/history/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 +2 -2
- 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 +3 -3
- package/_standalone/.next/server/app/trash/page.js.nft.json +1 -1
- 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/wiki/page.js +1 -1
- package/_standalone/.next/server/app/wiki/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/wiki/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app-paths-manifest.json +21 -21
- package/_standalone/.next/server/chunks/1550.js +1 -1
- package/_standalone/.next/server/chunks/1750.js +1 -1
- package/_standalone/.next/server/chunks/3484.js +1 -1
- package/_standalone/.next/server/chunks/530.js +23 -23
- package/_standalone/.next/server/chunks/6539.js +1 -1
- package/_standalone/.next/server/chunks/{2159.js → 6793.js} +2 -2
- 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/1053-fe009233cff06e72.js +29 -0
- package/_standalone/.next/static/chunks/7249-fa98ca10e9a10f39.js +11 -0
- package/_standalone/.next/static/chunks/app/{layout-b3919384ec2eb979.js → layout-7e02ddf4144b01f1.js} +11 -11
- package/_standalone/.next/static/chunks/app/page-6a6a12bd6d6812d0.js +7 -0
- package/_standalone/.next/static/chunks/app/trash/page-54cbd5c98d9de69b.js +1 -0
- package/_standalone/.next/static/chunks/app/view/[...path]/page-ca7bdcbf27f88a46.js +12 -0
- package/_standalone/.next/trace +64 -64
- package/_standalone/__tests__/api/mcp-install.test.ts +3 -2
- package/_standalone/__tests__/ask/non-streaming-api.test.ts +281 -0
- package/_standalone/__tests__/core/skill-install-logic.test.ts +1 -0
- package/_standalone/components/agents/CustomAgentModal.tsx +32 -8
- package/_standalone/data/skills/mindos/SKILL.md +14 -9
- package/_standalone/data/skills/mindos-zh/SKILL.md +13 -4
- package/_standalone/tsconfig.tsbuildinfo +1 -1
- package/app/app/api/agents/custom/route.ts +8 -0
- package/app/app/api/ask/route.ts +171 -10
- package/app/app/api/mcp/agents/route.ts +5 -3
- package/app/app/api/settings/route.ts +9 -0
- package/app/app/api/settings/test-key/route.ts +13 -1
- package/app/components/agents/CustomAgentModal.tsx +32 -8
- package/app/data/skills/mindos/SKILL.md +14 -9
- package/app/data/skills/mindos-zh/SKILL.md +13 -4
- package/app/lib/custom-agents.ts +45 -2
- package/app/lib/i18n/modules/panels.ts +10 -2
- package/app/lib/mcp-agents.ts +10 -0
- package/bin/lib/mcp-agents.js +10 -0
- package/package.json +1 -1
- package/skills/mindos/SKILL.md +10 -4
- package/skills/mindos-max/SKILL.md +191 -0
- package/skills/mindos-max-workspace/evals/evals.json +23 -0
- package/skills/mindos-max-workspace/iteration-1/capture-debugging-experience-en/eval_metadata.json +11 -0
- package/skills/mindos-max-workspace/iteration-1/capture-debugging-experience-en/old_skill/grading.json +28 -0
- package/skills/mindos-max-workspace/iteration-1/capture-debugging-experience-en/old_skill/outputs/transcript.md +203 -0
- package/skills/mindos-max-workspace/iteration-1/capture-debugging-experience-en/with_skill/grading.json +28 -0
- package/skills/mindos-max-workspace/iteration-1/capture-debugging-experience-en/with_skill/outputs/transcript.md +271 -0
- package/skills/mindos-max-workspace/iteration-1/save-meeting-decision-zh/eval_metadata.json +11 -0
- package/skills/mindos-max-workspace/iteration-1/save-meeting-decision-zh/old_skill/grading.json +28 -0
- package/skills/mindos-max-workspace/iteration-1/save-meeting-decision-zh/old_skill/outputs/transcript.md +121 -0
- package/skills/mindos-max-workspace/iteration-1/save-meeting-decision-zh/with_skill/grading.json +28 -0
- package/skills/mindos-max-workspace/iteration-1/save-meeting-decision-zh/with_skill/outputs/transcript.md +168 -0
- package/skills/mindos-max-workspace/iteration-1/search-past-decision-zh/eval_metadata.json +11 -0
- package/skills/mindos-max-workspace/iteration-1/search-past-decision-zh/old_skill/grading.json +28 -0
- package/skills/mindos-max-workspace/iteration-1/search-past-decision-zh/old_skill/outputs/transcript.md +143 -0
- package/skills/mindos-max-workspace/iteration-1/search-past-decision-zh/with_skill/grading.json +28 -0
- package/skills/mindos-max-workspace/iteration-1/search-past-decision-zh/with_skill/outputs/transcript.md +233 -0
- package/skills/mindos-max-workspace/skill-snapshot/mindos-original/SKILL.md +165 -0
- package/skills/mindos-max-workspace/skill-snapshot/mindos-original/references/README.md +12 -0
- package/skills/mindos-max-workspace/skill-snapshot/mindos-original/references/post-task-hooks.md +27 -0
- package/skills/mindos-max-workspace/skill-snapshot/mindos-original/references/preference-capture.md +41 -0
- package/skills/mindos-max-workspace/skill-snapshot/mindos-original/references/sop-template.md +74 -0
- package/skills/mindos-max-workspace/skill-snapshot/mindos-original/references/write-supplement.md +119 -0
- package/skills/mindos-max-zh/SKILL.md +192 -0
- package/skills/mindos-zh/SKILL.md +11 -3
- package/_standalone/.next/static/chunks/1053-5cb008a24930e271.js +0 -29
- package/_standalone/.next/static/chunks/7249-8cd568ad23656622.js +0 -11
- package/_standalone/.next/static/chunks/app/page-6436a99cda35132b.js +0 -7
- package/_standalone/.next/static/chunks/app/trash/page-8dc388695344fdd4.js +0 -1
- package/_standalone/.next/static/chunks/app/view/[...path]/page-b292b55305ecc021.js +0 -12
- /package/_standalone/.next/static/{zOaEtgJbdRMncnCBucULp → 2ksXveDzEcnCMRIElDkLq}/_buildManifest.js +0 -0
- /package/_standalone/.next/static/{zOaEtgJbdRMncnCBucULp → 2ksXveDzEcnCMRIElDkLq}/_ssgManifest.js +0 -0
|
@@ -49,6 +49,7 @@ export async function POST(req: NextRequest) {
|
|
|
49
49
|
...(overrides.presenceDirs && { presenceDirs: overrides.presenceDirs }),
|
|
50
50
|
...(overrides.presenceCli && { presenceCli: overrides.presenceCli }),
|
|
51
51
|
...(overrides.globalNestedKey && { globalNestedKey: overrides.globalNestedKey }),
|
|
52
|
+
...(overrides.skillDir && { skillDir: overrides.skillDir }),
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
customs.push(agent);
|
|
@@ -106,6 +107,7 @@ export async function PUT(req: NextRequest) {
|
|
|
106
107
|
...(updates.preferredTransport && { preferredTransport: updates.preferredTransport }),
|
|
107
108
|
...(updates.presenceCli !== undefined && { presenceCli: updates.presenceCli || undefined }),
|
|
108
109
|
...(updates.globalNestedKey !== undefined && { globalNestedKey: updates.globalNestedKey || undefined }),
|
|
110
|
+
...(updates.skillDir !== undefined && { skillDir: updates.skillDir || undefined }),
|
|
109
111
|
};
|
|
110
112
|
|
|
111
113
|
// Update presenceDirs: explicit override takes priority, then baseDir-derived default
|
|
@@ -115,6 +117,12 @@ export async function PUT(req: NextRequest) {
|
|
|
115
117
|
updated.presenceDirs = [updates.baseDir.endsWith('/') ? updates.baseDir : updates.baseDir + '/'];
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
// Update skillDir default when baseDir changes and no explicit skillDir
|
|
121
|
+
if (!updates.skillDir && updates.baseDir) {
|
|
122
|
+
const bd = updates.baseDir.endsWith('/') ? updates.baseDir : updates.baseDir + '/';
|
|
123
|
+
updated.skillDir = bd + 'skills/';
|
|
124
|
+
}
|
|
125
|
+
|
|
118
126
|
customs[idx] = updated;
|
|
119
127
|
saveCustomAgents(customs);
|
|
120
128
|
|
package/app/app/api/ask/route.ts
CHANGED
|
@@ -405,6 +405,143 @@ function toPiCustomToolDefinitions(tools: AgentTool<any>[]): ToolDefinition<any,
|
|
|
405
405
|
// Non-streaming fallback for proxies that don't support stream + tools
|
|
406
406
|
// ---------------------------------------------------------------------------
|
|
407
407
|
|
|
408
|
+
/**
|
|
409
|
+
* Reassemble SSE chunks (from proxies that ignore stream:false) into a
|
|
410
|
+
* single OpenAI-style chat completion response.
|
|
411
|
+
*
|
|
412
|
+
* SSE format: data: {"choices":[{"delta":{"content":"He"}}]}
|
|
413
|
+
* Output: {"choices":[{"message":{"role":"assistant","content":"Hello!"},"finish_reason":"stop"}]}
|
|
414
|
+
*/
|
|
415
|
+
function reassembleSSE(sseText: string): any {
|
|
416
|
+
const lines = sseText.split('\n');
|
|
417
|
+
let content = '';
|
|
418
|
+
let role = 'assistant';
|
|
419
|
+
let finishReason = 'stop';
|
|
420
|
+
const toolCalls: Map<number, { id: string; type: string; function: { name: string; arguments: string } }> = new Map();
|
|
421
|
+
|
|
422
|
+
for (const line of lines) {
|
|
423
|
+
const trimmed = line.trim();
|
|
424
|
+
if (!trimmed.startsWith('data:')) continue;
|
|
425
|
+
const payload = trimmed.slice(5).trim();
|
|
426
|
+
if (payload === '[DONE]') break;
|
|
427
|
+
|
|
428
|
+
let chunk: any;
|
|
429
|
+
try {
|
|
430
|
+
chunk = JSON.parse(payload);
|
|
431
|
+
} catch {
|
|
432
|
+
continue; // skip unparseable lines
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const delta = chunk?.choices?.[0]?.delta;
|
|
436
|
+
if (!delta) continue;
|
|
437
|
+
|
|
438
|
+
if (delta.role) role = delta.role;
|
|
439
|
+
if (delta.content) content += delta.content;
|
|
440
|
+
if (chunk.choices[0].finish_reason) finishReason = chunk.choices[0].finish_reason;
|
|
441
|
+
|
|
442
|
+
// Accumulate tool calls (they arrive in incremental deltas)
|
|
443
|
+
if (delta.tool_calls) {
|
|
444
|
+
for (const tc of delta.tool_calls) {
|
|
445
|
+
const idx = tc.index ?? 0;
|
|
446
|
+
const existing = toolCalls.get(idx);
|
|
447
|
+
if (!existing) {
|
|
448
|
+
toolCalls.set(idx, {
|
|
449
|
+
id: tc.id ?? '',
|
|
450
|
+
type: tc.type ?? 'function',
|
|
451
|
+
function: {
|
|
452
|
+
name: tc.function?.name ?? '',
|
|
453
|
+
arguments: tc.function?.arguments ?? '',
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
} else {
|
|
457
|
+
if (tc.id) existing.id = tc.id;
|
|
458
|
+
if (tc.function?.name) existing.function.name += tc.function.name;
|
|
459
|
+
if (tc.function?.arguments) existing.function.arguments += tc.function.arguments;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const message: any = { role, content: content || null };
|
|
466
|
+
if (toolCalls.size > 0) {
|
|
467
|
+
message.tool_calls = Array.from(toolCalls.values());
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return {
|
|
471
|
+
choices: [{ message, finish_reason: finishReason }],
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Convert pi-ai format messages to OpenAI API format.
|
|
477
|
+
* pi-ai messages have nested structures; OpenAI format is flatter with tool_calls array.
|
|
478
|
+
*/
|
|
479
|
+
function piMessagesToOpenAI(piMessages: any[]): any[] {
|
|
480
|
+
return piMessages.map(msg => {
|
|
481
|
+
const role = msg.role;
|
|
482
|
+
|
|
483
|
+
// Skip system role (will be added separately)
|
|
484
|
+
if (role === 'system') return null;
|
|
485
|
+
|
|
486
|
+
// Pass through user messages (simple string content)
|
|
487
|
+
if (role === 'user') {
|
|
488
|
+
return {
|
|
489
|
+
role: 'user',
|
|
490
|
+
content: typeof msg.content === 'string' ? msg.content : msg.content,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Assistant messages: flatten content array into text + tool_calls
|
|
495
|
+
if (role === 'assistant') {
|
|
496
|
+
const content = msg.content;
|
|
497
|
+
let textContent = '';
|
|
498
|
+
const toolCalls: any[] = [];
|
|
499
|
+
|
|
500
|
+
if (Array.isArray(content)) {
|
|
501
|
+
for (const part of content) {
|
|
502
|
+
if (part.type === 'text' && part.text) {
|
|
503
|
+
textContent += part.text;
|
|
504
|
+
} else if (part.type === 'toolCall') {
|
|
505
|
+
toolCalls.push({
|
|
506
|
+
id: part.id ?? `call_${Date.now()}`,
|
|
507
|
+
type: 'function',
|
|
508
|
+
function: {
|
|
509
|
+
name: part.name ?? 'unknown',
|
|
510
|
+
arguments: JSON.stringify(part.arguments ?? {}),
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const result: any = { role: 'assistant' };
|
|
518
|
+
// Always include content (even if empty) for tool-call-only messages
|
|
519
|
+
// OpenAI API may handle this differently, but it's safer to include empty string
|
|
520
|
+
result.content = textContent || '';
|
|
521
|
+
if (toolCalls.length > 0) result.tool_calls = toolCalls;
|
|
522
|
+
return result;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Tool result messages
|
|
526
|
+
if (role === 'toolResult') {
|
|
527
|
+
const contentText = Array.isArray(msg.content)
|
|
528
|
+
? msg.content
|
|
529
|
+
.filter((c: any) => c.type === 'text')
|
|
530
|
+
.map((c: any) => c.text ?? '')
|
|
531
|
+
.join('\n')
|
|
532
|
+
: String(msg.content ?? '');
|
|
533
|
+
|
|
534
|
+
return {
|
|
535
|
+
role: 'tool',
|
|
536
|
+
tool_call_id: msg.toolCallId ?? 'unknown',
|
|
537
|
+
content: contentText,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return null;
|
|
542
|
+
}).filter(Boolean);
|
|
543
|
+
}
|
|
544
|
+
|
|
408
545
|
/**
|
|
409
546
|
* Mini agent loop using non-streaming OpenAI-compatible API.
|
|
410
547
|
* Used when a proxy silently breaks stream+tools by returning plain text.
|
|
@@ -415,7 +552,7 @@ async function runNonStreamingFallback(opts: {
|
|
|
415
552
|
apiKey: string;
|
|
416
553
|
model: string;
|
|
417
554
|
systemPrompt: string;
|
|
418
|
-
historyMessages:
|
|
555
|
+
historyMessages: any[];
|
|
419
556
|
userContent: string;
|
|
420
557
|
tools: AgentTool<any>[];
|
|
421
558
|
send: (event: MindOSSSEvent) => void;
|
|
@@ -433,9 +570,12 @@ async function runNonStreamingFallback(opts: {
|
|
|
433
570
|
},
|
|
434
571
|
}));
|
|
435
572
|
|
|
436
|
-
|
|
573
|
+
// Convert pi-ai format messages to OpenAI format
|
|
574
|
+
const openaiMessages = piMessagesToOpenAI(historyMessages);
|
|
575
|
+
|
|
576
|
+
const messages: { role: string; content?: unknown; tool_calls?: unknown; tool_call_id?: string }[] = [
|
|
437
577
|
{ role: 'system', content: systemPrompt },
|
|
438
|
-
...
|
|
578
|
+
...openaiMessages,
|
|
439
579
|
{ role: 'user', content: userContent },
|
|
440
580
|
];
|
|
441
581
|
|
|
@@ -447,6 +587,9 @@ async function runNonStreamingFallback(opts: {
|
|
|
447
587
|
if (signal.aborted) throw new Error('Request aborted');
|
|
448
588
|
step++;
|
|
449
589
|
|
|
590
|
+
// Use stream:true and parse SSE ourselves.
|
|
591
|
+
// Many proxies ignore stream:false (returning SSE with empty choices or broken JSON).
|
|
592
|
+
// Using stream:true is the most universally compatible approach.
|
|
450
593
|
const resp = await fetch(endpoint, {
|
|
451
594
|
method: 'POST',
|
|
452
595
|
headers: {
|
|
@@ -458,7 +601,7 @@ async function runNonStreamingFallback(opts: {
|
|
|
458
601
|
messages,
|
|
459
602
|
tools: openaiTools.length > 0 ? openaiTools : undefined,
|
|
460
603
|
tool_choice: openaiTools.length > 0 ? 'auto' : undefined,
|
|
461
|
-
stream:
|
|
604
|
+
stream: true,
|
|
462
605
|
}),
|
|
463
606
|
signal,
|
|
464
607
|
});
|
|
@@ -468,11 +611,28 @@ async function runNonStreamingFallback(opts: {
|
|
|
468
611
|
throw new Error(`Non-streaming API error ${resp.status}: ${errText.slice(0, 200)}`);
|
|
469
612
|
}
|
|
470
613
|
|
|
471
|
-
|
|
614
|
+
// Read the full response body (SSE or JSON)
|
|
615
|
+
const rawText = await resp.text();
|
|
616
|
+
const trimmed = rawText.trimStart();
|
|
617
|
+
|
|
618
|
+
let data: any;
|
|
619
|
+
if (trimmed.startsWith('data:')) {
|
|
620
|
+
// Response is SSE — reassemble chunks into a single OpenAI response
|
|
621
|
+
data = reassembleSSE(trimmed);
|
|
622
|
+
} else {
|
|
623
|
+
// Some endpoints might still return plain JSON even with stream:true
|
|
624
|
+
try {
|
|
625
|
+
data = JSON.parse(rawText);
|
|
626
|
+
} catch {
|
|
627
|
+
throw new Error(`API returned invalid response: ${rawText.slice(0, 200)}`);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
472
631
|
const choice = data?.choices?.[0];
|
|
473
632
|
if (!choice) throw new Error('Empty response from API');
|
|
474
633
|
|
|
475
|
-
|
|
634
|
+
// reassembleSSE always produces .message; standard JSON may use .message or .delta
|
|
635
|
+
const msg = choice.message ?? choice.delta ?? {};
|
|
476
636
|
const finishReason: string = choice.finish_reason ?? 'stop';
|
|
477
637
|
|
|
478
638
|
// Emit text content in chunks to simulate streaming appearance
|
|
@@ -1185,11 +1345,12 @@ export async function POST(req: NextRequest) {
|
|
|
1185
1345
|
if (lastPromptError) throw lastPromptError;
|
|
1186
1346
|
|
|
1187
1347
|
metrics.recordRequest(Date.now() - requestStartTime);
|
|
1188
|
-
if (!hasContent && lastModelError) {
|
|
1189
|
-
// No content received —
|
|
1190
|
-
//
|
|
1348
|
+
if (!hasContent && (lastModelError || (baseUrl && provider === 'openai'))) {
|
|
1349
|
+
// No content received — either a model error or proxy compatibility issue.
|
|
1350
|
+
// For OpenAI-compatible endpoints with custom baseUrl, always try the fallback
|
|
1351
|
+
// even without an explicit error (some proxies silently return empty responses).
|
|
1191
1352
|
if (baseUrl && provider === 'openai') {
|
|
1192
|
-
send({ type: 'status', message: t.proxyCompatDetecting });
|
|
1353
|
+
send({ type: 'status', message: lastModelError ? t.proxyCompatDetecting : t.proxyCompatMode });
|
|
1193
1354
|
try {
|
|
1194
1355
|
await runNonStreamingFallback({
|
|
1195
1356
|
baseUrl,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
detectAgentInstalledSkills,
|
|
15
15
|
resolveSkillWorkspaceProfile,
|
|
16
16
|
} from '@/lib/mcp-agents';
|
|
17
|
-
import { getAllAgents, loadCustomAgents } from '@/lib/custom-agents';
|
|
17
|
+
import { getAllAgents, loadCustomAgents, scanCustomAgentSkills } from '@/lib/custom-agents';
|
|
18
18
|
import { readSettings } from '@/lib/settings';
|
|
19
19
|
import { scanSkillDirs } from '@/lib/pi-integration/skills';
|
|
20
20
|
import { getMindRoot } from '@/lib/fs';
|
|
@@ -105,7 +105,9 @@ export async function GET() {
|
|
|
105
105
|
status = detectInstalled(key);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
const skillProfile =
|
|
108
|
+
const skillProfile = isCustom
|
|
109
|
+
? { mode: 'additional' as const, skillAgentName: key, workspacePath: expandHome(customByKey[key]?.skillDir || agent.presenceDirs?.[0] + 'skills/') }
|
|
110
|
+
: resolveSkillWorkspaceProfile(key);
|
|
109
111
|
const runtime = isCustom
|
|
110
112
|
? { hiddenRootPath: '', hiddenRootPresent: false, conversationSignal: false, usageSignal: false, lastActivityAt: undefined }
|
|
111
113
|
: detectAgentRuntimeSignals(key);
|
|
@@ -113,7 +115,7 @@ export async function GET() {
|
|
|
113
115
|
? { servers: [] as string[], sources: [] as string[] }
|
|
114
116
|
: detectAgentConfiguredMcpServers(key);
|
|
115
117
|
const installedSkills = isCustom
|
|
116
|
-
?
|
|
118
|
+
? scanCustomAgentSkills(customByKey[key])
|
|
117
119
|
: detectAgentInstalledSkills(key);
|
|
118
120
|
|
|
119
121
|
return {
|
|
@@ -124,6 +124,15 @@ export async function POST(req: NextRequest) {
|
|
|
124
124
|
};
|
|
125
125
|
|
|
126
126
|
writeSettings(next);
|
|
127
|
+
// Clear proxy compat cache when AI config changes — stale cache causes
|
|
128
|
+
// the non-streaming fallback to be used even when streaming would work.
|
|
129
|
+
if (JSON.stringify(next.ai) !== JSON.stringify(current.ai)) {
|
|
130
|
+
const { readSettings: rs, writeSettings: ws } = await import('@/lib/settings');
|
|
131
|
+
const s = rs();
|
|
132
|
+
if (s.baseUrlCompat && Object.keys(s.baseUrlCompat).length > 0) {
|
|
133
|
+
ws({ ...s, baseUrlCompat: {} });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
127
136
|
if (next.mindRoot !== current.mindRoot) invalidateCache();
|
|
128
137
|
return NextResponse.json({ ok: true });
|
|
129
138
|
} catch (err) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const dynamic = 'force-dynamic';
|
|
2
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
import { complete } from '@mariozechner/pi-ai';
|
|
4
|
-
import { effectiveAiConfig } from '@/lib/settings';
|
|
4
|
+
import { effectiveAiConfig, readBaseUrlCompat, writeSettings, readSettings } from '@/lib/settings';
|
|
5
5
|
import { getModelConfig } from '@/lib/agent/model';
|
|
6
6
|
import { type ProviderId, isProviderId } from '@/lib/agent/providers';
|
|
7
7
|
|
|
@@ -84,6 +84,18 @@ export async function POST(req: NextRequest) {
|
|
|
84
84
|
signal: ctrl.signal,
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
// Clear stale proxy compat cache for this baseUrl on successful test.
|
|
88
|
+
// Prevents stale 'non-streaming' cache from forcing the fallback path.
|
|
89
|
+
if (baseUrl) {
|
|
90
|
+
const compat = readBaseUrlCompat();
|
|
91
|
+
if (compat[baseUrl]) {
|
|
92
|
+
const s = readSettings();
|
|
93
|
+
const updated = { ...(s.baseUrlCompat ?? {}) };
|
|
94
|
+
delete updated[baseUrl];
|
|
95
|
+
writeSettings({ ...s, baseUrlCompat: updated });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
87
99
|
return NextResponse.json({ ok: true, latency: Date.now() - start });
|
|
88
100
|
} catch (e) {
|
|
89
101
|
return NextResponse.json({ ok: false, ...classifyPiAiError(e) });
|
|
@@ -16,6 +16,9 @@ interface DetectResult {
|
|
|
16
16
|
detectedFormat?: 'json' | 'toml';
|
|
17
17
|
detectedConfigKey?: string;
|
|
18
18
|
hasSkillsDir: boolean;
|
|
19
|
+
detectedSkillDir?: string;
|
|
20
|
+
skillCount?: number;
|
|
21
|
+
skillNames?: string[];
|
|
19
22
|
suggestedName?: string;
|
|
20
23
|
}
|
|
21
24
|
|
|
@@ -28,6 +31,7 @@ interface FormState {
|
|
|
28
31
|
preferredTransport: 'stdio' | 'http';
|
|
29
32
|
project: string;
|
|
30
33
|
presenceCli: string;
|
|
34
|
+
skillDir: string;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
interface CustomAgentModalProps {
|
|
@@ -85,6 +89,7 @@ export default function CustomAgentModal({
|
|
|
85
89
|
preferredTransport: 'stdio',
|
|
86
90
|
project: '',
|
|
87
91
|
presenceCli: '',
|
|
92
|
+
skillDir: '',
|
|
88
93
|
});
|
|
89
94
|
|
|
90
95
|
// Reset state when modal opens/closes or editAgent changes
|
|
@@ -100,6 +105,7 @@ export default function CustomAgentModal({
|
|
|
100
105
|
preferredTransport: editAgent.preferredTransport || 'stdio',
|
|
101
106
|
project: editAgent.projectPath || '',
|
|
102
107
|
presenceCli: '',
|
|
108
|
+
skillDir: editAgent.skillWorkspacePath || '',
|
|
103
109
|
});
|
|
104
110
|
setPhase('result');
|
|
105
111
|
setCustomizeOpen(true);
|
|
@@ -115,6 +121,7 @@ export default function CustomAgentModal({
|
|
|
115
121
|
preferredTransport: 'stdio',
|
|
116
122
|
project: '',
|
|
117
123
|
presenceCli: '',
|
|
124
|
+
skillDir: '',
|
|
118
125
|
});
|
|
119
126
|
setPhase('input');
|
|
120
127
|
setCustomizeOpen(false);
|
|
@@ -182,7 +189,9 @@ export default function CustomAgentModal({
|
|
|
182
189
|
// Detect API only scans ~/ paths for security; for other absolute paths,
|
|
183
190
|
// skip detection and go straight to Phase B with sensible defaults.
|
|
184
191
|
if (!dir.startsWith('~/')) {
|
|
185
|
-
|
|
192
|
+
const normalized = dir.endsWith('/') ? dir : dir + '/';
|
|
193
|
+
setField('global', normalized + 'mcp.json');
|
|
194
|
+
setField('skillDir', normalized + 'skills/');
|
|
186
195
|
setDetectResult({ exists: false, hasSkillsDir: false });
|
|
187
196
|
setPhase('result');
|
|
188
197
|
setDetecting(false);
|
|
@@ -216,11 +225,14 @@ export default function CustomAgentModal({
|
|
|
216
225
|
}
|
|
217
226
|
if (data.detectedFormat) setField('format', data.detectedFormat);
|
|
218
227
|
if (data.detectedConfigKey) setField('configKey', data.detectedConfigKey);
|
|
228
|
+
setField('skillDir', data.detectedSkillDir || (dir.endsWith('/') ? dir : dir + '/') + 'skills/');
|
|
219
229
|
|
|
220
230
|
setPhase('result');
|
|
221
231
|
} catch {
|
|
222
232
|
clearTimeout(timeout);
|
|
223
|
-
|
|
233
|
+
const normalized = dir.endsWith('/') ? dir : dir + '/';
|
|
234
|
+
setField('global', normalized + 'mcp.json');
|
|
235
|
+
setField('skillDir', normalized + 'skills/');
|
|
224
236
|
setDetectResult({ exists: false, hasSkillsDir: false });
|
|
225
237
|
setDetectTimedOut(true);
|
|
226
238
|
setPhase('result');
|
|
@@ -246,6 +258,7 @@ export default function CustomAgentModal({
|
|
|
246
258
|
preferredTransport: form.preferredTransport,
|
|
247
259
|
project: form.project.trim() || null,
|
|
248
260
|
presenceCli: form.presenceCli.trim() || undefined,
|
|
261
|
+
skillDir: form.skillDir.trim() || undefined,
|
|
249
262
|
}
|
|
250
263
|
: {
|
|
251
264
|
name: form.name.trim(),
|
|
@@ -256,6 +269,7 @@ export default function CustomAgentModal({
|
|
|
256
269
|
preferredTransport: form.preferredTransport,
|
|
257
270
|
project: form.project.trim() || null,
|
|
258
271
|
presenceCli: form.presenceCli.trim() || undefined,
|
|
272
|
+
skillDir: form.skillDir.trim() || undefined,
|
|
259
273
|
};
|
|
260
274
|
|
|
261
275
|
try {
|
|
@@ -410,12 +424,12 @@ export default function CustomAgentModal({
|
|
|
410
424
|
<span className="text-muted-foreground">{p.customAgentTransportLabel}</span>
|
|
411
425
|
<span className="text-foreground">{form.preferredTransport}</span>
|
|
412
426
|
|
|
413
|
-
{
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
427
|
+
<span className="text-muted-foreground">{p.customAgentSkillsLabel}</span>
|
|
428
|
+
<span className="text-foreground">
|
|
429
|
+
{detectResult?.hasSkillsDir
|
|
430
|
+
? p.customAgentSkillsFound(detectResult.skillCount ?? 0)
|
|
431
|
+
: p.customAgentSkillsNone ?? '—'}
|
|
432
|
+
</span>
|
|
419
433
|
</div>
|
|
420
434
|
</div>
|
|
421
435
|
|
|
@@ -490,6 +504,16 @@ export default function CustomAgentModal({
|
|
|
490
504
|
</div>
|
|
491
505
|
</Field>
|
|
492
506
|
|
|
507
|
+
{/* Skills Directory */}
|
|
508
|
+
<Field label={p.customAgentSkillDir} hint={p.customAgentSkillDirHint}>
|
|
509
|
+
<Input
|
|
510
|
+
value={form.skillDir}
|
|
511
|
+
onChange={(e) => setField('skillDir', e.target.value)}
|
|
512
|
+
placeholder={p.customAgentSkillDirPlaceholder}
|
|
513
|
+
autoComplete="off"
|
|
514
|
+
/>
|
|
515
|
+
</Field>
|
|
516
|
+
|
|
493
517
|
{/* Project Config */}
|
|
494
518
|
<Field label={p.customAgentProjectConfig}>
|
|
495
519
|
<Input
|
|
@@ -3,14 +3,19 @@ name: mindos
|
|
|
3
3
|
description: >
|
|
4
4
|
Operate a MindOS knowledge base: update notes, search, organize files, execute SOPs/workflows,
|
|
5
5
|
retrospective, append CSV, cross-agent handoff, route unstructured input to the right files,
|
|
6
|
-
distill experience, sync related docs.
|
|
7
|
-
|
|
6
|
+
distill experience, sync related docs.
|
|
7
|
+
更新笔记, 搜索知识库, 整理文件, 执行SOP/工作流, 复盘, 追加CSV, 跨Agent交接,
|
|
8
|
+
路由非结构化输入到对应文件, 提炼经验, 同步关联文档.
|
|
8
9
|
NOT for editing app source, project docs, or paths outside the KB.
|
|
9
10
|
Core concepts: Space, Instruction (INSTRUCTION.md), Skill (SKILL.md); notes can embody both.
|
|
10
|
-
Trigger when user asks to: save or record
|
|
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
|
|
13
|
-
|
|
11
|
+
Trigger when user asks to: save or record anything, search for prior notes or context, update or
|
|
12
|
+
edit a file, organize notes, run a workflow or SOP, capture decisions from a session, append rows
|
|
13
|
+
to a table or CSV, hand off context to another agent, check if something was discussed before,
|
|
14
|
+
look up a past decision, distill lessons learned, prepare context for a meeting.
|
|
15
|
+
Also trigger on Chinese: 帮我记下来, 搜一下笔记, 更新知识库, 整理文件, 复盘, 提炼经验,
|
|
16
|
+
保存, 记录, 交接, 查一下之前的, 有没有相关笔记, 把这个存起来.
|
|
17
|
+
When the user's question implies stored context may exist (past decisions, previous discussions,
|
|
18
|
+
meeting records), consider searching MindOS even if they don't explicitly mention it.
|
|
14
19
|
---
|
|
15
20
|
|
|
16
21
|
# MindOS Skill
|
|
@@ -115,9 +120,9 @@ User request
|
|
|
115
120
|
## Judgment heuristics
|
|
116
121
|
|
|
117
122
|
**Save intent boundary:**
|
|
118
|
-
- "
|
|
119
|
-
- "
|
|
120
|
-
- "
|
|
123
|
+
- "save this" / "record" / "write down" = write
|
|
124
|
+
- "search" / "summarize" / "look up" = read-only
|
|
125
|
+
- "organize" → ask: display only, or write back?
|
|
121
126
|
|
|
122
127
|
**File location uncertainty:**
|
|
123
128
|
- Can't decide in 5 seconds → use nearest existing directory, inform user
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mindos-zh
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
仅 mindRoot 知识库内任务。不用于:改代码仓库/项目文档/KB 外路径。
|
|
4
|
+
MindOS 知识库操作:更新笔记、搜索知识库、整理文件、执行SOP/工作流、复盘、追加CSV、跨Agent交接、
|
|
5
|
+
路由非结构化输入到对应文件、提炼经验、同步关联文档。
|
|
6
|
+
仅 mindRoot 知识库内任务。不用于:改代码仓库、项目源码、KB 外路径。
|
|
8
7
|
核心概念:空间、指令(INSTRUCTION.md)、技能(SKILL.md);笔记可承载指令与技能。
|
|
8
|
+
触发场景:保存或记录任何内容、搜索历史笔记或上下文、更新或编辑文件、整理或重组文件结构、
|
|
9
|
+
执行SOP或工作流、捕获对话中的决策、复盘或总结经验、追加表格或CSV数据、跨Agent交接上下文、
|
|
10
|
+
提炼经验教训、同步关联文档、查找之前是否讨论过某事、查询历史决策、查找模板或SOP、
|
|
11
|
+
为会议准备上下文。
|
|
12
|
+
触发词:帮我记下来、搜一下我的笔记、更新知识库、整理文件、执行工作流、保存到知识库、
|
|
13
|
+
记录一下、整理笔记、复盘、提炼经验、同步文档、查一下之前的记录、有没有相关的笔记、
|
|
14
|
+
我之前写过什么、把这个存起来、总结一下保存、交接给其他Agent、追加到表格。
|
|
15
|
+
English triggers: save, record, search notes, organize, retrospective, handoff, lessons learned.
|
|
16
|
+
当用户的问题暗示可能存在历史记录(过去的决策、之前的讨论、会议纪要)时,
|
|
17
|
+
即使没有明确提到知识库,也应该考虑搜索 MindOS。
|
|
9
18
|
---
|
|
10
19
|
|
|
11
20
|
# MindOS 技能
|
package/app/lib/custom-agents.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface CustomAgentDef {
|
|
|
26
26
|
presenceDirs: string[];
|
|
27
27
|
presenceCli?: string;
|
|
28
28
|
globalNestedKey?: string;
|
|
29
|
+
/** Skills directory path. Defaults to `baseDir + 'skills/'`. */
|
|
30
|
+
skillDir?: string;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export interface DetectResult {
|
|
@@ -34,6 +36,9 @@ export interface DetectResult {
|
|
|
34
36
|
detectedFormat?: 'json' | 'toml';
|
|
35
37
|
detectedConfigKey?: string;
|
|
36
38
|
hasSkillsDir: boolean;
|
|
39
|
+
detectedSkillDir?: string;
|
|
40
|
+
skillCount?: number;
|
|
41
|
+
skillNames?: string[];
|
|
37
42
|
suggestedName?: string;
|
|
38
43
|
}
|
|
39
44
|
|
|
@@ -87,6 +92,7 @@ export function inferDefaults(name: string, baseDir: string): Omit<CustomAgentDe
|
|
|
87
92
|
format: 'json',
|
|
88
93
|
preferredTransport: 'stdio',
|
|
89
94
|
presenceDirs: [dir],
|
|
95
|
+
skillDir: dir + 'skills/',
|
|
90
96
|
};
|
|
91
97
|
}
|
|
92
98
|
|
|
@@ -112,8 +118,23 @@ export function detectBaseDir(baseDir: string): DetectResult {
|
|
|
112
118
|
const dirName = path.basename(expanded.replace(/\/$/, ''));
|
|
113
119
|
result.suggestedName = dirName.charAt(0).toUpperCase() + dirName.slice(1);
|
|
114
120
|
|
|
115
|
-
// Check for skills/ subdirectory
|
|
116
|
-
|
|
121
|
+
// Check for skills/ subdirectory and scan contents
|
|
122
|
+
const skillsPath = path.join(expanded, 'skills');
|
|
123
|
+
if (fs.existsSync(skillsPath)) {
|
|
124
|
+
result.hasSkillsDir = true;
|
|
125
|
+
result.detectedSkillDir = baseDir.endsWith('/') ? baseDir + 'skills/' : baseDir + '/skills/';
|
|
126
|
+
try {
|
|
127
|
+
const skillEntries = fs.readdirSync(skillsPath, { withFileTypes: true });
|
|
128
|
+
const skillNames = skillEntries
|
|
129
|
+
.filter(e => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith('.'))
|
|
130
|
+
.map(e => e.name)
|
|
131
|
+
.sort((a, b) => a.localeCompare(b));
|
|
132
|
+
result.skillCount = skillNames.length;
|
|
133
|
+
result.skillNames = skillNames;
|
|
134
|
+
} catch {
|
|
135
|
+
result.skillCount = 0;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
117
138
|
|
|
118
139
|
// Scan top-level files (max 20)
|
|
119
140
|
let entries: string[];
|
|
@@ -239,6 +260,28 @@ export function getAllAgents(): Record<string, AgentDef> {
|
|
|
239
260
|
return result;
|
|
240
261
|
}
|
|
241
262
|
|
|
263
|
+
/* ─── Skill Scanning ─── */
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Scan skills installed in a custom agent's skill directory.
|
|
267
|
+
* Returns the same shape as detectAgentInstalledSkills.
|
|
268
|
+
*/
|
|
269
|
+
export function scanCustomAgentSkills(custom: CustomAgentDef): { skills: string[]; sourcePath: string } {
|
|
270
|
+
const skillDir = custom.skillDir || (custom.baseDir.endsWith('/') ? custom.baseDir + 'skills/' : custom.baseDir + '/skills/');
|
|
271
|
+
const expanded = expandHome(skillDir);
|
|
272
|
+
if (!fs.existsSync(expanded)) return { skills: [], sourcePath: expanded };
|
|
273
|
+
try {
|
|
274
|
+
const entries = fs.readdirSync(expanded, { withFileTypes: true });
|
|
275
|
+
const skills = entries
|
|
276
|
+
.filter(e => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith('.'))
|
|
277
|
+
.map(e => e.name)
|
|
278
|
+
.sort((a, b) => a.localeCompare(b));
|
|
279
|
+
return { skills, sourcePath: expanded };
|
|
280
|
+
} catch {
|
|
281
|
+
return { skills: [], sourcePath: expanded };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
242
285
|
/* ─── Validation ─── */
|
|
243
286
|
|
|
244
287
|
export function validateCustomAgentInput(input: {
|
|
@@ -333,7 +333,11 @@ export const panelsEn = {
|
|
|
333
333
|
customAgentFormatLabel: 'Format',
|
|
334
334
|
customAgentTransportLabel: 'Transport',
|
|
335
335
|
customAgentSkillsLabel: 'Skills',
|
|
336
|
-
customAgentSkillsFound: (n: number) => `${n} found`,
|
|
336
|
+
customAgentSkillsFound: (n: number) => `${n} skill${n !== 1 ? 's' : ''} found`,
|
|
337
|
+
customAgentSkillsNone: 'Not detected',
|
|
338
|
+
customAgentSkillDir: 'Skills Directory',
|
|
339
|
+
customAgentSkillDirHint: 'Where skills are installed for this agent',
|
|
340
|
+
customAgentSkillDirPlaceholder: '~/.xxx/skills/',
|
|
337
341
|
customAgentCustomize: 'Customize these settings',
|
|
338
342
|
customAgentAdd: 'Add Agent',
|
|
339
343
|
customAgentAdding: 'Adding...',
|
|
@@ -1017,7 +1021,11 @@ export const panelsZh = {
|
|
|
1017
1021
|
customAgentFormatLabel: '格式',
|
|
1018
1022
|
customAgentTransportLabel: '传输协议',
|
|
1019
1023
|
customAgentSkillsLabel: '技能',
|
|
1020
|
-
customAgentSkillsFound: (n: number) => `发现 ${n}
|
|
1024
|
+
customAgentSkillsFound: (n: number) => `发现 ${n} 个技能`,
|
|
1025
|
+
customAgentSkillsNone: '未检测到',
|
|
1026
|
+
customAgentSkillDir: '技能目录',
|
|
1027
|
+
customAgentSkillDirHint: '此 Agent 安装技能的目录',
|
|
1028
|
+
customAgentSkillDirPlaceholder: '~/.xxx/skills/',
|
|
1021
1029
|
customAgentCustomize: '自定义配置',
|
|
1022
1030
|
customAgentAdd: '添加 Agent',
|
|
1023
1031
|
customAgentAdding: '添加中...',
|
package/app/lib/mcp-agents.ts
CHANGED
|
@@ -235,6 +235,15 @@ export const MCP_AGENTS: Record<string, AgentDef> = {
|
|
|
235
235
|
presenceCli: 'codex',
|
|
236
236
|
presenceDirs: ['~/.codex/'],
|
|
237
237
|
},
|
|
238
|
+
'antigravity': {
|
|
239
|
+
name: 'Antigravity',
|
|
240
|
+
project: '.antigravity/mcp_config.json',
|
|
241
|
+
global: '~/.gemini/antigravity/mcp_config.json',
|
|
242
|
+
key: 'mcpServers',
|
|
243
|
+
preferredTransport: 'stdio',
|
|
244
|
+
presenceCli: 'agy',
|
|
245
|
+
presenceDirs: ['~/.gemini/antigravity/'],
|
|
246
|
+
},
|
|
238
247
|
};
|
|
239
248
|
|
|
240
249
|
/**
|
|
@@ -261,6 +270,7 @@ export const SKILL_AGENT_REGISTRY: Record<string, SkillAgentRegistration> = {
|
|
|
261
270
|
'roo': { mode: 'additional', skillAgentName: 'roo' },
|
|
262
271
|
'github-copilot': { mode: 'universal' },
|
|
263
272
|
'codex': { mode: 'universal' },
|
|
273
|
+
'antigravity': { mode: 'additional', skillAgentName: 'antigravity' },
|
|
264
274
|
};
|
|
265
275
|
|
|
266
276
|
export interface SkillWorkspaceProfile {
|