Skip to content

Commit 4f99114

Browse files
authored
refactor(kap-server): drop the @moonshot-ai/protocol dependency (#1755)
1 parent 0b790cd commit 4f99114

107 files changed

Lines changed: 3888 additions & 216 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/agent-core-dev/errors.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Base classes and serialization are **centralized** in `_base/errors`; error **co
77
## Where things live
88

99
- `src/_base/errors/errors.ts`: base classes — `Error2`, `ExpectedError`, `ErrorNoTelemetry`, `BugIndicatingError`, `NotImplementedError`, plus `isError2` and `unwrapErrorCause`.
10-
- `src/_base/errors/codes.ts`: the `ErrorDomain` contract, the `ErrorCode` type (aliased to the protocol's `KimiErrorCode`), the registry (`registerErrorDomain` / `errorInfo` / `isErrorCode`), and `CoreErrors` (`internal`, `not_implemented`).
10+
- `src/_base/errors/codes.ts`: the `ErrorDomain` contract, the registry (`registerErrorDomain` / `errorInfo` / `isErrorCode`), and `CoreErrors` (`internal`, `not_implemented`). The `ErrorCode` union type is derived by `#/errors` from the aggregated domain definitions.
1111
- `src/_base/errors/serialize.ts`: `ErrorPayload`, `isCodedError`, `toErrorPayload`, `fromErrorPayload`. Wire-facing names (`KimiErrorPayload`, `toKimiErrorPayload`) mirror the protocol and are kept as-is.
1212
- `src/_base/errors/unexpectedError.ts`: `onUnexpectedError` / `setUnexpectedErrorHandler` (global handler).
1313
- `src/<domain>/errors.ts`: the domain's `XxxErrors` descriptor (codes + retryable list + per-code info overrides), self-registered on import.
@@ -17,7 +17,7 @@ Base classes and serialization are **centralized** in `_base/errors`; error **co
1717

1818
- **Throw a coded error, not a bare string.** `throw new Error2(ErrorCodes.X, …)`. Bare `new Error` only for unreachable guards; `BugIndicatingError` for caller bugs; `NotImplementedError('feature')` for stubs.
1919
- **Define codes in the owning domain**, in `<domain>/errors.ts` as an `XxxErrors` descriptor (`satisfies ErrorDomain` + `registerErrorDomain`), then wire it into the facade. Never add domain codes to `_base/errors`.
20-
- **One `code` per failure mode.** Codes read `domain.reason`. The valid code strings are fixed by the protocol (`KimiErrorCode` in `packages/protocol/src/events.ts`): **add new codes to the protocol first**. Renaming/removing a code is a major.
20+
- **One `code` per failure mode.** Codes read `domain.reason`. The valid code strings are derived from the aggregated domain definitions (`ErrorCode` in `#/errors` is computed from the `ErrorCodes` aggregate): **add new codes to the owning domain's `errors.ts`** — registration throws on cross-domain collisions. Renaming/removing a code is a major.
2121
- **Translate foreign errors at the boundary.** Provider/HTTP, fs, MCP errors are re-thrown as the owning domain's coded error. `_base/errors` never imports a business domain.
2222
- **Translation is idempotent and cause-preserving.** Translators (`toHostFsError`, `toStorageIoError`) pass through an already-translated error and always keep the original as `cause`.
2323
- **`details` is structured and JSON-serializable; `message` is a short human sentence.** Paths/errnos/scope/key go into `details`, not the message.
@@ -35,6 +35,6 @@ Base classes and serialization are **centralized** in `_base/errors`; error **co
3535
## Red lines (this topic)
3636

3737
- Throw a coded error with a `code`, not a bare string (except unreachable guards / `BugIndicatingError` / `NotImplementedError`).
38-
- Codes live in the owning domain's `errors.ts` and self-register; new codes land in the protocol first.
38+
- Codes live in the owning domain's `errors.ts` and self-register; new codes land in the owning domain first.
3939
- Translate foreign errors at the owning domain's boundary, idempotently, with `cause` and structured `details`; `_base/errors` never imports a business domain.
4040
- Branch on `code` across the wire, never `instanceof`.

.agents/skills/agent-core-dev/server-align.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,28 @@ Pick surface → Read the v1 route (if any) → Reuse / add the protocol schema
3838

3939
Apply the decision above. For a v1-matched endpoint, the **spec** is the protocol schema plus the existing mirror routes:
4040

41-
- `packages/protocol/src/rest/<resource>.ts` — the wire schema you must match.
41+
- `packages/kap-server/src/protocol/rest-<resource>.ts` — the wire schema you must match.
4242
- `packages/kap-server/src/routes/<resource>.ts` — the file you are writing (create it if missing); sibling route files show the conventions.
4343

4444
The protocol schema is the source of truth. Do not re-derive the wire shape from memory or from the v2 domain model.
4545

4646
### 2. Reuse (or add) the protocol schema
4747

48-
The wire schema lives in **`@moonshot-ai/protocol`** under `packages/protocol/src/rest/<resource>.ts` (e.g. `promptSubmissionSchema`, `promptListResponseSchema`, `configResponseSchema`). Every `/api/v1` route in `packages/kap-server` imports from it — that single import is what guarantees the server speaks the same shape released clients expect.
48+
The wire schema lives in **`packages/kap-server/src/protocol`** under `rest-<resource>.ts` (e.g. `promptSubmissionSchema`, `promptListResponseSchema`, `configResponseSchema`) — or in the owning `agent-core-v2` domain contract when the engine's service speaks the shape. Every `/api/v1` route in `packages/kap-server` imports from it — that single import is what guarantees the server speaks the same shape released clients expect.
4949

5050
Actions:
5151

5252
- **Schema already in protocol** → import it in the server-v2 route and use it in `defineRoute` (`body`, `success.data`, error `dataSchema` / `detailsSchema`). Do **not** re-declare the schema inline in server-v2.
53-
- **Schema missing in protocol** → add it to `packages/protocol/src/rest/<resource>.ts` first, with a `rest-<resource>.test.ts`, then consume it from the route. The protocol package is the source of truth; server-v2 never owns a v1 wire schema locally.
54-
- **Schema exists but only v1 uses it**move/keep it in protocol and import it into server-v2; do not fork a copy.
53+
- **Schema missing** → add it to `packages/kap-server/src/protocol/rest-<resource>.ts` first (or to the owning v2 domain contract if its service speaks the shape), then consume it from the route. The shared schema is the source of truth; server-v2 never re-declares a v1 wire schema inline.
54+
- **Schema exists but only v1 uses it** → keep it in `packages/kap-server/src/protocol` and import it into server-v2; do not fork a copy.
5555

5656
#### Schema-fidelity rule (the hard rule)
5757

5858
For a `/api/v1` endpoint, the request and response schemas **must be the established protocol schema** (or a strict superset):
5959

6060
-**Adding** an optional field is allowed (`field: z.string().optional()`). Old clients ignore it; new clients may send it.
6161
-**Renaming** a field, **changing** its type, **tightening** its validation, or **changing its meaning** is a wire break — do not do it in a mirror route. If the v2 domain genuinely needs a different shape, that shape belongs on `/api/v2`, not on the `/api/v1` mirror.
62-
- ❌ Re-declaring the schema inline in server-v2 (even if it "looks identical") is forbidden — it drifts. One schema, one home: `packages/protocol`.
62+
- ❌ Re-declaring the schema inline in server-v2 (even if it "looks identical") is forbidden — it drifts. One schema, one home: the owning `agent-core-v2` domain contract or `packages/kap-server/src/protocol`.
6363

6464
Self-check: "would a released v1 client get a byte-identical envelope from `packages/kap-server` for this request?" If you cannot answer yes from the shared schema, the route is wrong.
6565

@@ -94,8 +94,8 @@ packages/agent-core-v2/src/<domain>Legacy/
9494
Skeleton (matches `prompt/`):
9595

9696
```ts
97-
// prompt.ts — contract shaped by @moonshot-ai/protocol
98-
import type { PromptSubmitResult, PromptSubmission } from '@moonshot-ai/protocol';
97+
// prompt.ts — contract shaped by the v1 wire schema (kap-server/src/protocol)
98+
import type { PromptSubmitResult, PromptSubmission } from '../../protocol/rest-prompt';
9999
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
100100

101101
export interface IAgentPromptService {
@@ -128,7 +128,7 @@ Conventions:
128128
- **Header comment** must say it is an `L7 edge adapter` and name both the v1 contract it implements and the native v2 Service it leaves untouched (see `prompt.ts`).
129129
- **Scope** = the lifetime of the *legacy* state it holds (the `prompt` queue is per-agent → `LifecycleScope.Agent`). Apply [orient.md](orient.md) / [design.md](design.md) normally — a LegacyService is not exempt from scope rules.
130130
- **Delegate, do not duplicate** business logic. The LegacyService translates the v1 contract into native-Service calls and translates results back; the real work stays in the native Service.
131-
- **Contract types come from `@moonshot-ai/protocol`**, so the interface cannot drift from the wire shape.
131+
- **Contract types come from the v1 wire schema homes** (the owning v2 domain contract or `kap-server/src/protocol`), so the interface cannot drift from the wire shape.
132132

133133
### 4. Wire the route / actionMap entry
134134

@@ -139,9 +139,9 @@ const route = defineRoute(
139139
{
140140
method: 'POST',
141141
path: '/sessions/{session_id}/prompts',
142-
body: promptSubmissionSchema, // ← from @moonshot-ai/protocol
142+
body: promptSubmissionSchema, // ← from kap-server/src/protocol
143143
params: sessionIdParamSchema,
144-
success: { data: promptSubmitResultSchema }, // ← from @moonshot-ai/protocol
144+
success: { data: promptSubmitResultSchema }, // ← from kap-server/src/protocol
145145
errors: {
146146
[ErrorCode.SESSION_NOT_FOUND]: {},
147147
[ErrorCode.SESSION_BUSY]: {},
@@ -169,7 +169,7 @@ app.post(route.path, route.options, route.handler);
169169
The route translates domain `KimiError` codes into protocol `ErrorCode` numbers. Two registries must stay in sync:
170170

171171
- **Domain code** — register in `agent-core-v2/src/errors.ts` (`ErrorCodes`) and throw from the Service (errors.md). Co-located domain errors go in `<domain>Legacy/errors.ts` (e.g. `prompt.not_found`, `session.busy`).
172-
- **Wire code** — register the matching number in `packages/protocol/src/error-codes.ts` and reference it in the route's `errors` map and `sendMappedError`.
172+
- **Wire code** — register the matching number in `packages/kap-server/src/protocol/error-codes.ts` and reference it in the route's `errors` map and `sendMappedError`.
173173

174174
```ts
175175
function sendMappedError(reply, requestId, err) {
@@ -202,7 +202,7 @@ Where the route mirrors v1, the test is the regression guard for the schema-fide
202202
### 7. Verify
203203

204204
- `pnpm -C packages/kap-server test` — server routes green.
205-
- `pnpm -C packages/protocol test`schema tests green (incl. any new `rest-*.test.ts`).
205+
- `pnpm -C packages/kap-server test`server routes green (incl. any wire-schema guards).
206206
- `pnpm -C packages/agent-core-v2 test` — native + Legacy Service tests green.
207207
- `pnpm -C packages/agent-core-v2 run lint:domain` — a LegacyService is still inside the domain layers (edge adapter, L7); it must not pull business code into the edge or invert scope direction.
208208
- `pnpm -C packages/server-e2e ...` when a v1 parity scenario exists.
@@ -218,9 +218,9 @@ This is the reference alignment (commits `feat(server-v2): port v1 /sessions/:si
218218
- `/api/v2` keeps the native shape — `prompts:submit` / `steer` / `undo` / `clear` / `cancel` map to `IAgentRPCService` (a wire facade over the v2 turn driver) in `actionMap`. The native `IAgentPromptService` is untouched.
219219
- `/api/v1` gets an `AgentPromptLegacyService` (`prompt/`, `LifecycleScope.Agent`) that re-implements the v1 scheduler — queue, `prompt_id`, steer/abort, auto-start-next — **on top of** the native `IAgentPromptService`. The `/api/v1` routes consume the LegacyService.
220220

221-
**The schema.** Both surfaces import `promptSubmissionSchema` / `promptSubmitResultSchema` / `promptListResponseSchema` / `promptSteerRequestSchema` / `promptSteerResultSchema` / `promptAbortResponseSchema` from `@moonshot-ai/protocol`. The `/api/v1` and `/api/v2` routes are therefore compatible with released clients by construction; the LegacyService projects v2 turn results back into those protocol shapes.
221+
**The schema.** Both surfaces import `promptSubmissionSchema` / `promptSubmitResultSchema` / `promptListResponseSchema` / `promptSteerRequestSchema` / `promptSteerResultSchema` / `promptAbortResponseSchema` from the shared v1 wire schemas (see `packages/kap-server/src/protocol`). The `/api/v1` and `/api/v2` routes are therefore compatible with released clients by construction; the LegacyService projects v2 turn results back into those protocol shapes.
222222

223-
**The errors.** v1 codes (`prompt.not_found`, `session.busy`, `prompt.already_completed`) are registered in `agent-core-v2` (`prompt/errors.ts`) and in `packages/protocol` (`error-codes.ts`), then mapped in the route's `sendMappedError` — including the idempotent `prompt.already_completed``40903 { data: { aborted: false } }`.
223+
**The errors.** v1 codes (`prompt.not_found`, `session.busy`, `prompt.already_completed`) are registered in `agent-core-v2` (`prompt/errors.ts`) and in `packages/kap-server/src/protocol` (`error-codes.ts`), then mapped in the route's `sendMappedError` — including the idempotent `prompt.already_completed``40903 { data: { aborted: false } }`.
224224

225225
**The lesson.** When the v1 contract and the v2 domain disagree, add an adapter (LegacyService) at the edge; do not let the wire contract leak into the native domain. The two surfaces share the protocol schema but not the Service.
226226

@@ -230,21 +230,21 @@ Before submitting a server-align change:
230230

231231
- [ ] Surface chosen deliberately: `/api/v1` mirror for a v1-matched endpoint, `/api/v2` for a new native capability (both if needed).
232232
- [ ] For a `/api/v1` mirror, the route matches the established v1 contract (protocol schema + sibling routes) path-for-path, verb-for-verb, action-for-action.
233-
- [ ] Request and response schemas come from `@moonshot-ai/protocol` (`packages/protocol/src/rest/<resource>.ts`); no inline re-declaration in server-v2.
233+
- [ ] Request and response schemas come from their owning home (the `agent-core-v2` domain contract or `packages/kap-server/src/protocol`); no inline re-declaration in server-v2.
234234
- [ ] Existing schema fields are unchanged in name, type, and semantics; only optional fields added (if any).
235235
- [ ] Native v2 Service left clean; v1-only behavior isolated in a `<domain>Legacy` / `I<Domain>LegacyService` edge adapter when the semantics diverge.
236236
- [ ] LegacyService registered with the correct `LifecycleScope` and a header comment naming it an L7 edge adapter + the native Service it preserves.
237-
- [ ] Domain error codes registered in `agent-core-v2`; wire codes registered in `packages/protocol`; route maps them in `sendMappedError`, matching v1's status codes and idempotent envelopes.
237+
- [ ] Domain error codes registered in `agent-core-v2`; wire codes registered in `packages/kap-server/src/protocol`; route maps them in `sendMappedError`, matching v1's status codes and idempotent envelopes.
238238
- [ ] Route resolves the scope from the URL by `accessor.get(IX)`; no cached scope; finishes before disposal.
239-
- [ ] Tests assert the wire envelope + protocol shape; schema tests in `packages/protocol` added/updated.
239+
- [ ] Tests assert the wire envelope + protocol shape; wire-shape guards added/updated where the route mirrors v1.
240240
- [ ] `lint:domain` passes; the LegacyService did not invert scope or domain direction.
241241

242242
## Red lines (this subskill)
243243

244-
- One wire schema, one home: `packages/protocol`. Never re-declare a v1 wire schema inline in server-v2.
244+
- One wire schema, one home: the owning `agent-core-v2` domain contract or `packages/kap-server/src/protocol`. Never re-declare a v1 wire schema inline in server-v2.
245245
- A `/api/v1` mirror route must keep every existing schema field's name, type, and semantics; only optional additions are allowed. A different shape belongs on `/api/v2`, not on the mirror.
246246
- Do not distort the native v2 Service to satisfy a v1 quirk — add a `<domain>Legacy` edge adapter instead. The native Service serves the v2 architecture; the LegacyService serves the wire contract.
247247
- A LegacyService is still a v2 Service: it follows scope, domain-direction, and DI rules. "Edge adapter" describes its role, not an exemption.
248-
- The protocol schema (`packages/protocol/src/rest/<resource>.ts`) plus the existing mirror routes are the spec for a `/api/v1` route — match them; do not re-derive the wire shape from the v2 domain model or from memory.
249-
- Register every new error code in **both** `agent-core-v2` and `packages/protocol`; an unmapped code is a wire break.
248+
- The established wire schema (in its owning home — the `agent-core-v2` domain contract or `packages/kap-server/src/protocol`) plus the existing mirror routes are the spec for a `/api/v1` route — match them; do not re-derive the wire shape from the v2 domain model or from memory.
249+
- Register every new error code in **both** `agent-core-v2` and `packages/kap-server/src/protocol/error-codes.ts`; an unmapped code is a wire break.
250250
- Events stream over WS (`listen`), never over the REST mirror; do not invent REST polling for something v1 pushed as an event.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@moonshot-ai/kimi-code": patch
3+
---
4+
5+
Move the server's v1 wire schema definitions into the engine domains and the server package, removing the shared schema package from the v2 server stack with no behavior change.

packages/agent-core-v2/src/agent/contextMemory/messageProjection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* so REST consumers can still render the media after reload/resume.
1616
*/
1717

18-
import type { Message, MessageContent, MessageRole, ToolUseContent } from './wireMessage';
18+
import type { Message, MessageContent, MessageRole, ToolUseContent } from './protocolMessage';
1919

2020
import type { ContextMessage } from './types';
2121

packages/agent-core-v2/src/agent/contextMemory/wireMessage.ts renamed to packages/agent-core-v2/src/agent/contextMemory/protocolMessage.ts

File renamed without changes.

packages/agent-core-v2/src/agent/rpc/core-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type { ExperimentalFeatureState } from '#/app/flag/flag';
2020
import type { ResumeSessionResult } from '#/agent/replayBuilder/types';
2121
import type { SessionMeta } from '#/session/sessionMetadata/sessionMetadata';
2222
import type { ContentPart } from '#/app/llmProtocol/message';
23-
import type { SessionWarning } from '#/app/sessionLegacy/sessionWire';
23+
import type { SessionWarning } from '#/app/sessionLegacy/sessionProtocol';
2424
import type { UsageStatus } from '#/agent/usage/usage';
2525

2626
import type { ExportSessionPayload, ExportSessionResult } from '#/app/sessionExport/sessionExport';

packages/agent-core-v2/src/app/auth/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import type {
2929
OAuthLoginCancelResponse,
3030
OAuthLogoutResponse,
3131
RefreshOAuthProviderModelsResponse,
32-
} from './oauthWire';
32+
} from './oauthProtocol';
3333

3434
export interface AuthStatus {
3535
readonly loggedIn: boolean;

packages/agent-core-v2/src/app/auth/authService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import type {
3939
OAuthLoginCancelResponse,
4040
OAuthLogoutResponse,
4141
RefreshOAuthProviderModelsResponse,
42-
} from './oauthWire';
42+
} from './oauthProtocol';
4343

4444
import { InstantiationType } from '#/_base/di/extensions';
4545
import { Disposable } from '#/_base/di/lifecycle';
File renamed without changes.

packages/agent-core-v2/src/app/messageLegacy/messageLegacy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
* - `message.not_found` → 40403
2020
*/
2121

22-
import type { Message, MessageRole } from '#/agent/contextMemory/wireMessage';
22+
import type { Message, MessageRole } from '#/agent/contextMemory/protocolMessage';
2323

2424
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
2525

0 commit comments

Comments
 (0)