Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6ba29f5
Enable first-class Kimi and OMX support without forking the CLI stack
ZephaniaCN Apr 5, 2026
08b7a88
Add standup summaries and fix Kimi/OMX CLI launch semantics
ZephaniaCN Apr 5, 2026
db334e4
Harden Kimi runtime parity, quota reporting, and Hub governance flows
ZephaniaCN Apr 5, 2026
5d2307e
Keep implicit thread roots aligned with capability defaults
ZephaniaCN Apr 5, 2026
8556258
Restore the final Kimi-only provider surface after history cleanup
ZephaniaCN Apr 5, 2026
953a37d
Keep lobby threads from inheriting the runtime worktree root
ZephaniaCN Apr 5, 2026
1b4e6b8
Stabilize Kimi quota refresh semantics and unblock PR gate
ZephaniaCN Apr 5, 2026
472e726
Prevent false Kimi thinking-unavailable diagnostics during multi-even…
ZephaniaCN Apr 5, 2026
7c8f7cd
Restore Kimi session-chain context health from local session snapshots
ZephaniaCN Apr 5, 2026
ccc5fe3
Recognize Kimi markdown-prefixed @mentions in A2A routing
ZephaniaCN Apr 5, 2026
294625d
Give Kimi a distinct avatar instead of reusing Gemini art
ZephaniaCN Apr 5, 2026
4ad89be
Refresh Kimi avatar with AI-generated mascot artwork
ZephaniaCN Apr 5, 2026
d6e3be4
Differentiate Kimi with a purebred full-body Van cat identity
ZephaniaCN Apr 5, 2026
97b92c3
Restore 梵花猫 sidebar theming and labels
ZephaniaCN Apr 6, 2026
73e00c0
Refresh stale Kimi seed identity in persisted cat catalogs
ZephaniaCN Apr 6, 2026
e0f25e6
Drop pre-merge Kimi identity migration scaffolding
ZephaniaCN Apr 6, 2026
6123606
Route multiple same-line Kimi handoff mentions correctly
ZephaniaCN Apr 6, 2026
31a80b4
Recover Gemini thinking from local session snapshots
ZephaniaCN Apr 6, 2026
9a7f25e
Keep Kimi metrics and session views aligned with Kimi-specific runtim…
ZephaniaCN Apr 6, 2026
b8e52d2
Preserve Kimi session resume across symlinked worktrees [砚砚/GPT-5.4🐾]
ZephaniaCN Apr 6, 2026
356e502
test: add regression test for bound-account error propagation (F127 P1)
ZephaniaCN Apr 6, 2026
9f082ad
fix(capabilities): include project .kimi/skills in metadata discovery…
ZephaniaCN Apr 6, 2026
53c1077
feat(wizard): brand colors, avatars, and subtitles for cat selection …
ZephaniaCN Apr 6, 2026
3329ac4
refactor(kimi): split KimiAgentService into config + event-parser mod…
ZephaniaCN Apr 6, 2026
84b283e
chore: fix biome format and import ordering for CI lint
ZephaniaCN Apr 8, 2026
18bcd1e
chore: remove standup module and omx.png from Kimi PR scope
ZephaniaCN Apr 9, 2026
349145f
fix(web): remove unused PROTOCOL_OPTIONS from hub-provider-profiles.s…
ZephaniaCN Apr 10, 2026
baa7c5d
fix(security): remove .kimi/mcp.json and add .kimi/ to .gitignore
ZephaniaCN Apr 10, 2026
c13705f
merge: resolve conflicts with main (F340 accounts refactor)
zts212653 Apr 10, 2026
0e6cbb1
fix: align kimi PR with F340 accounts refactor (provider→clientId)
zts212653 Apr 10, 2026
d427505
fix: bump system prompt size limits (+100) and exclude kimi-agent-ser…
zts212653 Apr 10, 2026
8d20137
fix(test): add kimi CLI stub for CI + bump SystemPromptBuilder thresh…
ZephaniaCN Apr 10, 2026
dc3ac4d
fix(lint): merge chmodSync into node:fs import and sort imports
ZephaniaCN Apr 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ packages/web/public/worker-*.js
.augment/
.codex/
.gemini/
.kimi/
.kiro/
.trae/
.dare/
Expand Down Expand Up @@ -66,6 +67,7 @@ npm-debug.log*
pnpm-debug.log*

# Lockfiles (workspace uses pnpm only)
package-lock.json
packages/**/package-lock.json

# Test coverage
Expand Down Expand Up @@ -130,6 +132,9 @@ private/
# Playwright MCP cache
.playwright-mcp/

# Local OMX runtime state
.omx/

# SQLite runtime databases (Hindsight evidence store)
evidence.sqlite
evidence.sqlite-shm
Expand Down
44 changes: 44 additions & 0 deletions cat-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@
"lead": true,
"available": true,
"evaluation": "开源多模型编码猫,自带 Oh My OpenCode 多专家编排 + LSP (F105)"
},
"kimi": {
"family": "moonshot",
"roles": ["research", "writer"],
"lead": true,
"available": true,
"evaluation": "中文长文理解与总结强,适合中文表达、资料整理与结构化输出"
}
},
"reviewPolicy": {
Expand Down Expand Up @@ -638,6 +645,43 @@
}
}
]
},
{
"id": "moonshot",
"catId": "kimi",
"name": "梵花猫",
"displayName": "梵花猫",
"avatar": "/avatars/kimi.png",
"color": {
"primary": "#4B5563",
"secondary": "#E5E7EB"
},
"mentionPatterns": ["@kimi", "@moonshot", "@月之暗面", "@梵花猫"],
"roleDescription": "中文长文本助手,擅长中文语境理解、资料整理与结构化表达",
"teamStrengths": "中文长文、总结归纳、资料整理",
"caution": null,
"defaultVariantId": "kimi-default",
"variants": [
{
"id": "kimi-default",
"clientId": "kimi",
"defaultModel": "kimi-code/kimi-for-coding",
"mcpSupport": true,
"cli": {
"command": "kimi",
"outputFormat": "stream-json",
"defaultArgs": ["--print", "--output-format", "stream-json"]
},
"personality": "稳健细致,擅长长文阅读和中文语境下的结构化表达",
"strengths": ["long-context", "chinese-writing", "summarization"],
"contextBudget": {
"maxPromptTokens": 240000,
"maxContextTokens": 216000,
"maxMessages": 200,
"maxContentLengthPerMsg": 10000
}
}
]
}
]
}
44 changes: 44 additions & 0 deletions cat-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@
"lead": true,
"available": true,
"evaluation": "开源多模型编码猫,自带 Oh My OpenCode 多专家编排 + LSP (F105)"
},
"kimi": {
"family": "moonshot",
"roles": ["research", "writer"],
"lead": true,
"available": true,
"evaluation": "中文长文理解与总结强,适合中文表达、资料整理与结构化输出"
}
},
"reviewPolicy": {
Expand Down Expand Up @@ -638,6 +645,43 @@
}
}
]
},
{
"id": "moonshot",
"catId": "kimi",
"name": "梵花猫",
"displayName": "梵花猫",
"avatar": "/avatars/kimi.png",
"color": {
"primary": "#4B5563",
"secondary": "#E5E7EB"
},
"mentionPatterns": ["@kimi", "@moonshot", "@月之暗面", "@梵花猫"],
"roleDescription": "中文长文本助手,擅长中文语境理解、资料整理与结构化表达",
"teamStrengths": "中文长文、总结归纳、资料整理",
"caution": null,
"defaultVariantId": "kimi-default",
"variants": [
{
"id": "kimi-default",
"clientId": "kimi",
"defaultModel": "kimi-code/kimi-for-coding",
"mcpSupport": true,
"cli": {
"command": "kimi",
"outputFormat": "stream-json",
"defaultArgs": ["--print", "--output-format", "stream-json"]
},
"personality": "稳健细致,擅长长文阅读和中文语境下的结构化表达",
"strengths": ["long-context", "chinese-writing", "summarization"],
"contextBudget": {
"maxPromptTokens": 240000,
"maxContextTokens": 216000,
"maxMessages": 200,
"maxContentLengthPerMsg": 10000
}
}
]
}
]
}
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"build": "pnpm --dir ../shared build && tsc",
"start": "node dist/index.js",
"test": "pnpm run build && CAT_CAFE_DISABLE_SHARED_STATE_PREFLIGHT=1 node --test --test-timeout=60000 test/*.test.js test/**/*.test.js",
"test:public": "pnpm run build && CAT_CAFE_DISABLE_SHARED_STATE_PREFLIGHT=1 node --test --test-concurrency=1 $(ls test/*.test.js test/**/*.test.js 2>/dev/null | grep -v 'redis-' | grep -v 'concurrent-fault-drill' | grep -v 'task-progress-store' | grep -v 'session-strategy-phase3' | grep -v 'signal-article-store' | grep -v 'persistence-fault-drill' | grep -v 'cursor-store-atomicity' | grep -v 'workflow-sop-store' | grep -v 'dare-agent-service' | grep -v 'dare-l1-acceptance' | grep -v 'codex-agent-service' | grep -v 'claude-settings-hooks\\.test' | grep -v 'game-store\\.test' | grep -v 'test/memory/' | grep -v 'cross-cat-context\\.test' | grep -v 'thread-wiring\\.test' | grep -v 'integration/wiring\\.test' | grep -v 'antigravity-cdp-client\\.test' | grep -v 'shared-state-wiring\\.test' | grep -v 'signal-fetcher-launchd' | grep -v 'reflection-capsule-m3' | grep -v 'workspace-project-context\\.test' | grep -v 'projects-setup\\.test' | grep -v 'projects-mkdir\\.test' | grep -v 'governance-status\\.test' | grep -v 'pack-integration\\.test' | grep -v 'project-setup-flow\\.test' | grep -v 'process-liveness-probe\\.test' | tr '\\n' ' ')",
"test:public": "pnpm run build && CAT_CAFE_DISABLE_SHARED_STATE_PREFLIGHT=1 node --test --test-concurrency=1 $(ls test/*.test.js test/**/*.test.js 2>/dev/null | grep -v 'redis-' | grep -v 'concurrent-fault-drill' | grep -v 'task-progress-store' | grep -v 'session-strategy-phase3' | grep -v 'signal-article-store' | grep -v 'persistence-fault-drill' | grep -v 'cursor-store-atomicity' | grep -v 'workflow-sop-store' | grep -v 'dare-agent-service' | grep -v 'dare-l1-acceptance' | grep -v 'codex-agent-service' | grep -v 'kimi-agent-service' | grep -v 'claude-settings-hooks\\.test' | grep -v 'game-store\\.test' | grep -v 'test/memory/' | grep -v 'cross-cat-context\\.test' | grep -v 'thread-wiring\\.test' | grep -v 'integration/wiring\\.test' | grep -v 'antigravity-cdp-client\\.test' | grep -v 'shared-state-wiring\\.test' | grep -v 'signal-fetcher-launchd' | grep -v 'reflection-capsule-m3' | grep -v 'workspace-project-context\\.test' | grep -v 'projects-setup\\.test' | grep -v 'projects-mkdir\\.test' | grep -v 'governance-status\\.test' | grep -v 'pack-integration\\.test' | grep -v 'project-setup-flow\\.test' | grep -v 'process-liveness-probe\\.test' | tr '\\n' ' ')",
"test:antigravity-smoke": "RUN_ANTIGRAVITY_SMOKE=true pnpm run build && RUN_ANTIGRAVITY_SMOKE=true node --test test/antigravity-smoke.test.js",
"fetch-signals": "node dist/scripts/fetch-signals.js",
"migrate-signals": "node dist/scripts/migrate-signals.js",
Expand Down
7 changes: 6 additions & 1 deletion packages/api/src/config/account-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { readCredential } from './credentials.js';

// ── Types surviving from provider-profiles.types.ts (F136 Phase 4d) ──

export type BuiltinAccountClient = Extract<ClientId, 'anthropic' | 'openai' | 'google' | 'dare' | 'opencode'>;
export type BuiltinAccountClient = Extract<ClientId, 'anthropic' | 'openai' | 'google' | 'kimi' | 'dare' | 'opencode'>;
export type ProviderProfileKind = 'builtin' | 'api_key';

export interface RuntimeProviderProfile {
Expand All @@ -37,6 +37,7 @@ export function resolveBuiltinClientForProvider(provider: ClientId): BuiltinAcco
case 'anthropic':
case 'openai':
case 'google':
case 'kimi':
case 'dare':
case 'opencode':
return provider;
Expand All @@ -51,6 +52,7 @@ const LEGACY_BUILTIN_IDS: Record<BuiltinAccountClient, string> = {
anthropic: 'claude',
openai: 'codex',
google: 'gemini',
kimi: 'kimi',
dare: 'dare',
opencode: 'opencode',
};
Expand Down Expand Up @@ -82,6 +84,8 @@ const BUILTIN_ACCOUNT_MAP: Record<string, { client: BuiltinAccountClient; protoc
builtin_openai: { client: 'openai', protocol: 'openai' },
gemini: { client: 'google', protocol: 'google' },
builtin_google: { client: 'google', protocol: 'google' },
kimi: { client: 'kimi', protocol: 'kimi' },
builtin_kimi: { client: 'kimi', protocol: 'kimi' },
dare: { client: 'dare', protocol: 'openai' },
builtin_dare: { client: 'dare', protocol: 'openai' },
opencode: { client: 'opencode', protocol: 'anthropic' },
Expand Down Expand Up @@ -188,6 +192,7 @@ function normalizeToClient(clientOrProtocol: string): BuiltinAccountClient | nul
case 'anthropic':
case 'openai':
case 'google':
case 'kimi':
case 'dare':
case 'opencode':
return clientOrProtocol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ import {
readClaudeMcpConfig,
readCodexMcpConfig,
readGeminiMcpConfig,
readKimiMcpConfig,
writeClaudeMcpConfig,
writeCodexMcpConfig,
writeGeminiMcpConfig,
writeKimiMcpConfig,
} from './mcp-config-adapters.js';

// ────────── Constants ──────────
Expand Down Expand Up @@ -104,6 +106,7 @@ const PROVIDER_WRITERS = {
anthropic: writeClaudeMcpConfig,
openai: writeCodexMcpConfig,
google: writeGeminiMcpConfig,
kimi: writeKimiMcpConfig,
} as const;

/** Check if a descriptor has a usable transport (stdio command, local resolver, or streamableHttp URL). */
Expand Down Expand Up @@ -353,20 +356,22 @@ export interface DiscoveryPaths {
claudeConfig: string; // e.g. <projectRoot>/.mcp.json
codexConfig: string; // e.g. <projectRoot>/.codex/config.toml
geminiConfig: string; // e.g. <projectRoot>/.gemini/settings.json
kimiConfig: string; // e.g. <projectRoot>/.kimi/mcp.json
}

/**
* Discover external MCP servers from all 3 CLI configs.
* Merges by name; if same name appears in multiple, first wins.
*/
export async function discoverExternalMcpServers(paths: DiscoveryPaths): Promise<McpServerDescriptor[]> {
const [claude, codex, gemini] = await Promise.all([
const [claude, codex, gemini, kimi] = await Promise.all([
readClaudeMcpConfig(paths.claudeConfig),
readCodexMcpConfig(paths.codexConfig),
readGeminiMcpConfig(paths.geminiConfig),
readKimiMcpConfig(paths.kimiConfig),
]);
return deduplicateDiscoveredMcpServers(
[...claude, ...codex, ...gemini]
[...claude, ...codex, ...gemini, ...kimi]
.filter((server) => hasUsableTransport(server))
.map((server) => ({ ...server, source: 'external' as const })),
);
Expand Down Expand Up @@ -608,10 +613,11 @@ export interface CliConfigPaths {
anthropic: string; // e.g. <projectRoot>/.mcp.json
openai: string; // e.g. <projectRoot>/.codex/config.toml
google: string; // e.g. <projectRoot>/.gemini/settings.json
kimi: string; // e.g. <projectRoot>/.kimi/mcp.json
}

/** Providers that support streamableHttp transport (URL-based MCP). */
const STREAMABLE_HTTP_PROVIDERS = new Set(['anthropic']);
const STREAMABLE_HTTP_PROVIDERS = new Set(['anthropic', 'kimi']);

/**
* Resolve effective MCP servers for a specific cat.
Expand Down
92 changes: 90 additions & 2 deletions packages/api/src/config/capabilities/mcp-config-adapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Claude: .mcp.json — { mcpServers: { name: { command, args, env } } }
* Codex: .codex/config.toml — [mcp_servers.<name>] command/args/env/enabled
* Gemini: .gemini/settings.json — { mcpServers: { name: { command, args, env, cwd } } }
* Kimi: .kimi/mcp.json — { mcpServers: { name: { url|command, args, env, headers } } }
*/

import { mkdir, readFile, writeFile } from 'node:fs/promises';
Expand All @@ -20,6 +21,13 @@ const GEMINI_CAT_CAFE_ENV_PLACEHOLDERS: Readonly<Record<string, string>> = {
CAT_CAFE_USER_ID: '${CAT_CAFE_USER_ID}',
CAT_CAFE_SIGNAL_USER: '${CAT_CAFE_SIGNAL_USER}',
};
const KIMI_CAT_CAFE_ENV_PLACEHOLDERS: Readonly<Record<string, string>> = {
CAT_CAFE_API_URL: '${CAT_CAFE_API_URL}',
CAT_CAFE_INVOCATION_ID: '${CAT_CAFE_INVOCATION_ID}',
CAT_CAFE_CALLBACK_TOKEN: '${CAT_CAFE_CALLBACK_TOKEN}',
CAT_CAFE_USER_ID: '${CAT_CAFE_USER_ID}',
CAT_CAFE_SIGNAL_USER: '${CAT_CAFE_SIGNAL_USER}',
};

function isCatCafeServer(name: string): boolean {
return name === 'cat-cafe' || name.startsWith('cat-cafe-');
Expand All @@ -33,6 +41,14 @@ function ensureGeminiCatCafeEnv(name: string, env?: Record<string, string>): Rec
};
}

function ensureKimiCatCafeEnv(name: string, env?: Record<string, string>): Record<string, string> | undefined {
if (!isCatCafeServer(name)) return env;
return {
...KIMI_CAT_CAFE_ENV_PLACEHOLDERS,
...(env ?? {}),
};
}

// ────────── Readers ──────────

/** Read Claude .mcp.json → McpServerDescriptor[] */
Expand Down Expand Up @@ -87,6 +103,22 @@ export async function readGeminiMcpConfig(filePath: string): Promise<McpServerDe
);
}

/** Read Kimi .kimi/mcp.json → McpServerDescriptor[] */
export async function readKimiMcpConfig(filePath: string): Promise<McpServerDescriptor[]> {
const raw = await safeReadFile(filePath);
if (!raw) return [];

const data = safeJsonParse(raw);
if (!data) return [];

const servers = data.mcpServers;
if (!servers || typeof servers !== 'object') return [];

return Object.entries(servers as Record<string, Record<string, unknown>>).map(([name, cfg]) =>
toDescriptor(name, cfg, true),
);
}

// ────────── Writers ──────────

/** Write McpServerDescriptor[] → Claude .mcp.json (merge: preserves user's non-managed servers) */
Expand Down Expand Up @@ -273,9 +305,64 @@ export async function cleanStaleClaudeProjectOverrides(
return cleaned;
}

/** Write McpServerDescriptor[] → Kimi .kimi/mcp.json (merge: preserves user's non-managed servers) */
export async function writeKimiMcpConfig(filePath: string, servers: McpServerDescriptor[]): Promise<void> {
const raw = await safeReadFile(filePath);
let existing: Record<string, unknown> = {};
if (raw) {
const parsed = safeJsonParse(raw);
if (parsed) existing = parsed;
}

const existingMcp: Record<string, unknown> =
existing.mcpServers && typeof existing.mcpServers === 'object'
? { ...(existing.mcpServers as Record<string, unknown>) }
: {};

for (const s of servers) {
if (!s.enabled) {
delete existingMcp[s.name];
continue;
}
if (s.transport === 'streamableHttp') {
if (!s.url?.trim()) {
delete existingMcp[s.name];
continue;
}
const entry: Record<string, unknown> = { url: s.url };
if (s.headers && Object.keys(s.headers).length > 0) entry.headers = s.headers;
existingMcp[s.name] = entry;
continue;
}
if (!s.command || s.command.trim().length === 0) {
delete existingMcp[s.name];
continue;
}
const entry: Record<string, unknown> = { command: s.command, args: s.args };
const env = ensureKimiCatCafeEnv(s.name, s.env);
if (env && Object.keys(env).length > 0) entry.env = env;
if (s.workingDir) entry.cwd = s.workingDir;
existingMcp[s.name] = entry;
}

for (const [name, value] of Object.entries(existingMcp)) {
if (!isCatCafeServer(name)) continue;
if (!value || typeof value !== 'object' || Array.isArray(value)) continue;
const cfg = value as Record<string, unknown>;
const currentEnv = toStringRecord(cfg.env);
cfg.env = ensureKimiCatCafeEnv(name, currentEnv);
existingMcp[name] = cfg;
}

existing.mcpServers = existingMcp;
await ensureDir(filePath);
await writeFile(filePath, `${JSON.stringify(existing, null, 2)}\n`, 'utf-8');
}

// ────────── Helpers ──────────

async function safeReadFile(filePath: string): Promise<string | null> {
async function safeReadFile(filePath?: string): Promise<string | null> {
if (!filePath) return null;
try {
return await readFile(filePath, 'utf-8');
} catch {
Expand Down Expand Up @@ -308,7 +395,8 @@ function toStringRecord(val: unknown): Record<string, string> | undefined {
}

function toDescriptor(name: string, cfg: Record<string, unknown>, enabled: boolean): McpServerDescriptor {
const isHttp = cfg.type === 'streamableHttp' || cfg.type === 'http';
const isHttp =
cfg.type === 'streamableHttp' || cfg.type === 'http' || (typeof cfg.url === 'string' && cfg.url.length > 0);
const desc: McpServerDescriptor = {
name,
command: typeof cfg.command === 'string' ? cfg.command : '',
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/config/cat-catalog-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function writeFileAtomic(filePath: string, content: string): void {
}

/** F340 P5: ClientId values — used to detect old `provider` field holding a clientId. */
const CLIENT_ID_VALUES = new Set(['anthropic', 'openai', 'google', 'dare', 'antigravity', 'opencode', 'a2a']);
const CLIENT_ID_VALUES = new Set(['anthropic', 'openai', 'google', 'kimi', 'dare', 'antigravity', 'opencode', 'a2a']);

function collectCatIds(config: CatCafeConfig): Set<string> {
const catIds = new Set<string>();
Expand Down
Loading
Loading