You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .agents/skills/agent-core-dev/errors.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@ Base classes and serialization are **centralized** in `_base/errors`; error **co
7
7
## Where things live
8
8
9
9
-`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.
11
11
-`src/_base/errors/serialize.ts`: `ErrorPayload`, `isCodedError`, `toErrorPayload`, `fromErrorPayload`. Wire-facing names (`KimiErrorPayload`, `toKimiErrorPayload`) mirror the protocol and are kept as-is.
-`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
17
17
18
18
-**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.
19
19
-**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.
21
21
-**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.
22
22
-**Translation is idempotent and cause-preserving.** Translators (`toHostFsError`, `toStorageIoError`) pass through an already-translated error and always keep the original as `cause`.
23
23
-**`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
35
35
## Red lines (this topic)
36
36
37
37
- 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.
39
39
- Translate foreign errors at the owning domain's boundary, idempotently, with `cause` and structured `details`; `_base/errors` never imports a business domain.
40
40
- Branch on `code` across the wire, never `instanceof`.
Copy file name to clipboardExpand all lines: .agents/skills/agent-core-dev/server-align.md
+20-20Lines changed: 20 additions & 20 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -38,28 +38,28 @@ Pick surface → Read the v1 route (if any) → Reuse / add the protocol schema
38
38
39
39
Apply the decision above. For a v1-matched endpoint, the **spec** is the protocol schema plus the existing mirror routes:
40
40
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.
42
42
-`packages/kap-server/src/routes/<resource>.ts` — the file you are writing (create it if missing); sibling route files show the conventions.
43
43
44
44
The protocol schema is the source of truth. Do not re-derive the wire shape from memory or from the v2 domain model.
45
45
46
46
### 2. Reuse (or add) the protocol schema
47
47
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.
49
49
50
50
Actions:
51
51
52
52
-**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.
55
55
56
56
#### Schema-fidelity rule (the hard rule)
57
57
58
58
For a `/api/v1` endpoint, the request and response schemas **must be the established protocol schema** (or a strict superset):
59
59
60
60
- ✅ **Adding** an optional field is allowed (`field: z.string().optional()`). Old clients ignore it; new clients may send it.
61
61
- ❌ **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`.
63
63
64
64
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.
-**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`).
129
129
-**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.
130
130
-**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.
132
132
133
133
### 4. Wire the route / actionMap entry
134
134
@@ -139,9 +139,9 @@ const route = defineRoute(
139
139
{
140
140
method: 'POST',
141
141
path: '/sessions/{session_id}/prompts',
142
-
body: promptSubmissionSchema, // ← from @moonshot-ai/protocol
142
+
body: promptSubmissionSchema, // ← from kap-server/src/protocol
143
143
params: sessionIdParamSchema,
144
-
success: { data: promptSubmitResultSchema }, // ← from @moonshot-ai/protocol
144
+
success: { data: promptSubmitResultSchema }, // ← from kap-server/src/protocol
The route translates domain `KimiError` codes into protocol `ErrorCode` numbers. Two registries must stay in sync:
170
170
171
171
-**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`.
173
173
174
174
```ts
175
175
function sendMappedError(reply, requestId, err) {
@@ -202,7 +202,7 @@ Where the route mirrors v1, the test is the regression guard for the schema-fide
202
202
### 7. Verify
203
203
204
204
-`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).
-`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.
208
208
-`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
218
218
-`/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.
219
219
-`/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.
220
220
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.
222
222
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 } }`.
224
224
225
225
**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.
226
226
@@ -230,21 +230,21 @@ Before submitting a server-align change:
230
230
231
231
-[ ] Surface chosen deliberately: `/api/v1` mirror for a v1-matched endpoint, `/api/v2` for a new native capability (both if needed).
232
232
-[ ] 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.
234
234
-[ ] Existing schema fields are unchanged in name, type, and semantics; only optional fields added (if any).
235
235
-[ ] Native v2 Service left clean; v1-only behavior isolated in a `<domain>Legacy` / `I<Domain>LegacyService` edge adapter when the semantics diverge.
236
236
-[ ] 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.
238
238
-[ ] 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.
240
240
-[ ]`lint:domain` passes; the LegacyService did not invert scope or domain direction.
241
241
242
242
## Red lines (this subskill)
243
243
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.
245
245
- 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.
246
246
- 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.
247
247
- 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.
250
250
- Events stream over WS (`listen`), never over the REST mirror; do not invent REST polling for something v1 pushed as an event.
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.
0 commit comments