How to Implement Custom Agent Profiles in Kimi Code Using Contribution, Registry, and Catalog Extension Points
To implement custom agent profiles in Kimi Code, create a ProfileData definition, register it via definePartialInstance with the CatalogService, and let the IAgentProfileService bind it at runtime.
Kimi Code's agent architecture exposes a clean three-layer extension system that lets you add new agent profiles without modifying core engine code. This article walks through the contribution → registry → catalog pipeline, using actual source paths from the MoonshotAI/kimi-code repository.
Understanding the Three-Layer Architecture
Kimi Code organizes profile discovery through three distinct layers:
- Contribution — A module that exports a static object describing the profile
- Registry — The
CatalogServicethat discovers all contributions at startup - Catalog — The runtime lookup performed by
IAgentProfileService
The built-in implementation in packages/agent-core-v2/src/kosong/provider/providers/kimi/kimi.contrib.ts demonstrates this pattern. The CatalogService at packages/agent-core-v2/src/kosong/model/catalogService.ts registers each contribution under CatalogService.register, and the profile service at packages/agent-core-v2/src/agent/profile/profile.ts calls catalog.get(profileName) to retrieve and bind definitions.
Step 1: Create a Profile Definition
Start by defining your profile data. Create a new TypeScript file in a custom provider folder, such as packages/agent-core-v2/src/kosong/provider/providers/my-profile/myProfile.contribute.ts.
Export a constant that satisfies the ProfileData type defined in packages/agent-core-v2/src/kosong/model/catalog.ts:
import type { ProfileData } from '#/kosong/model/catalog';
export const myProfile: ProfileData = {
profileName: 'my-assistant',
systemPrompt: 'You are a helpful assistant that always speaks in rhymes.',
modelAlias: 'gpt-4o-mini',
thinkingLevel: 'off',
disallowedTools: ['FileSystem'],
};
Key ProfileData fields include:
profileName— Unique identifier for the profilesystemPrompt— The agent's personality and instructionsmodelAlias— Default model to usethinkingLevel— Controls reasoning depth ('off','low','medium','high')disallowedTools— Array of tool names to disable
Step 2: Declare the Contribution
Register your profile with the global catalog using definePartialInstance. Add this declaration at the bottom of your contribution file:
import { definePartialInstance } from '@moonshot-ai/kosong';
import { CatalogService } from '#/kosong/model/catalogService';
definePartialInstance(CatalogService, {
add: (catalog) => catalog.register(myProfile),
});
This mirrors the pattern in kimi.contrib.ts. The CatalogService automatically scans for these contributions via its contribution loader.
Step 3: Update Workspace Configuration
Ensure your new provider package is included in the build. Update both:
pnpm-workspace.yaml— Add the new package pathflake.nix— Include the source directory
These changes follow the monorepo's workspace maintenance rules documented in AGENTS.md at the repository root.
Step 4: Use the Custom Profile
When creating a session, pass the profile name in your request. The IAgentProfileService performs catalog lookup, binds the configuration, and applies it to the agent runtime.
REST API example:
POST /v1/sessions/xyz/profile
Content-Type: application/json
{ "profile": "my-assistant" }
Node SDK example:
import { Klient } from '@moonshot-ai/klient';
import { Session } from '@moonshot-ai/klient/session';
async function startSession() {
const klient = new Klient();
const session = await Session.create(klient, { id: 'demo' });
// Bind the custom profile
await session.profile.bind({ profile: 'my-assistant' });
// Session now uses custom system prompt and model
const reply = await session.prompt('Explain photosynthesis in rhyme.');
console.log(reply);
}
Complete Custom Profile Example
Here's a full implementation file for reference:
// packages/agent-core-v2/src/kosong/provider/providers/my-profile/myProfile.contribute.ts
import type { ProfileData } from '#/kosong/model/catalog';
import { definePartialInstance } from '@moonshot-ai/kosong';
import { CatalogService } from '#/kosong/model/catalogService';
export const myProfile: ProfileData = {
profileName: 'code-reviewer',
systemPrompt:
'You are a meticulous code reviewer. Focus on security, performance, and maintainability. Always suggest concrete improvements with code examples.',
modelAlias: 'gpt-4o',
thinkingLevel: 'medium',
disallowedTools: ['WebSearch', 'CodeExecution'],
};
definePartialInstance(CatalogService, {
add: (catalog) => catalog.register(myProfile),
});
Step 5: Verify Your Implementation
Run the existing test suite to validate the contribution pipeline:
pnpm test packages/agent-core-v2/test/_base/contribution/registry.test.ts
Consider adding a dedicated test to assert that catalog.get('my-assistant') returns expected data and binds without errors.
Debug your bound profile via the API:
GET /api/v1/sessions/demo/profile
The response includes the resolved ProfileData with system prompt, model alias, and all configuration fields.
Key Source Files Reference
| File | Role |
|---|---|
packages/agent-core-v2/src/agent/profile/profile.ts |
Implements IAgentProfileService; performs profile lookup and binding |
packages/agent-core-v2/src/kosong/model/catalog.ts |
Declares the ProfileData interface |
packages/agent-core-v2/src/kosong/model/catalogService.ts |
Central registry; provides catalog.get(name) and register() |
packages/agent-core-v2/src/kosong/provider/providers/kimi/kimi.contrib.ts |
Reference implementation of built-in profiles |
packages/agent-core-v2/test/_base/contribution/registry.test.ts |
Validates contribution loading |
Summary
- Custom agent profiles extend Kimi Code through the contribution → registry → catalog pipeline
- Define profiles using the
ProfileDatainterface fromcatalog.ts - Register via
definePartialInstancewithCatalogService.register - The
IAgentProfileServicebinds profiles at runtime usingcatalog.get() - Update
pnpm-workspace.yamlandflake.nixfor new provider packages - Test using the built-in contribution registry tests
Frequently Asked Questions
Can I override built-in profiles with custom ones?
Profile names must be unique within the catalog. The CatalogService throws if you attempt to register a duplicate name. To extend an existing profile, create a new name and reference the base configuration in your systemPrompt or use composition patterns in your contribution file.
What happens if a requested profile isn't found?
According to the source in profile.ts, the IAgentProfileService attempts catalog.get(profileName) and fails with a clear error if no match exists. The error propagates to the session creation or profile binding call, allowing you to catch and handle missing profiles gracefully.
Can profiles be defined in JSON instead of TypeScript?
The CatalogService registers ProfileData objects directly. While the built-in contributions use TypeScript for type safety and definePartialInstance integration, you can load JSON definitions and wrap them in a TypeScript contribution file that calls catalog.register(). The runtime only sees the resolved object.
How do I restrict which tools an agent can use?
Use the disallowedTools array in your ProfileData. List tool names exactly as registered in the tool registry. The agent runtime consults this list before executing any tool call, rejecting attempts to use forbidden tools with an appropriate error message.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →