Gate JSDoc completeness on every package export

New doc-sync gate verify-export-jsdoc walks every module-level exported
name under packages/*/*/src and requires description prose everywhere,
plus @param per parameter and @returns on non-void annotated returns for
function-like exports, public class methods, properties, and accessors.
The parsing + check helpers move out of gen-cordis-catalog.ts into a
shared scripts/jsdoc.ts so 'documented' means one thing on both gated
surfaces.

Deliberate exemptions (documented in the RFC): heritage-declared class
members (the seam declaration is the doc's one home — the one checker
query in an otherwise pure-AST walk), cordis plugin-protocol slots
(name/inject/reusable/Config/apply, top-level and static), constructors,
overload implementations, declare-module augmentation bodies, and
re-export statements (checked at the defining module).

The 203 under-documented exports the gate found at adoption are filled
in this change, so the gate lands green; generated catalogs/graphs are
regenerated for the shifted line pointers.

RFC: docs/rfc/implemented/process/2026-07-06-export-surface-jsdoc-gate.md
This commit is contained in:
Tianyi Cui
2026-07-06 22:09:30 +08:00
parent 1c999804d8
commit cd9737d569
92 changed files with 1802 additions and 289 deletions

View File

@@ -13,7 +13,9 @@ import { parseSse } from './sse.ts'
import { translate } from './translate.ts'
import type { WireError } from './types.ts'
/** Constructor options for {@link DeepSeekAdapter}; the plugin's `apply` resolves them from Config + environment. */
export interface DeepSeekAdapterOptions {
/** Bearer token sent in the `authorization` header on every request. */
apiKey: string
/** Endpoint base; `/chat/completions` is appended. */
baseURL: string
@@ -21,7 +23,11 @@ export interface DeepSeekAdapterOptions {
defaults?: RequestDefaults
}
/** Map an HTTP status to a stable LlmError code. */
/**
* Map an HTTP status to a stable LlmError code.
* @param status - status of a non-2xx provider response.
* @returns `AUTH` (401/403), `RATE_LIMIT` (429), `INVALID_REQUEST` (400), `SERVER` (5xx), or `HTTP_<status>` for anything else.
*/
export function httpErrorCode(status: number): string {
if (status === 401 || status === 403) return 'AUTH'
if (status === 429) return 'RATE_LIMIT'

View File

@@ -34,6 +34,12 @@ export type * from './types.ts'
export const name = 'llm-deepseek'
export const inject = ['llm']
/**
* Plugin config, validated by the same-named schemastery schema. Every field
* is optional in yml: credentials/endpoint fall back to the environment (a
* missing API key fails plugin load, not the first call), and omitted
* thinking fields send nothing on the wire, so the provider default applies.
*/
export interface Config {
/** API key; falls back to $DEEPSEEK_API_KEY. Required one way or the other. */
apiKey?: string

View File

@@ -66,6 +66,8 @@ function serializeAssistant(message: Message): WireMessage {
* `{role: 'tool'}` messages; the harness puts each tool result in its own
* user-role message, so a mixed user message contributes its text first and
* its tool results as separate wire messages after.
* @param messages - the harness conversation, in order.
* @returns the wire messages; order preserved, each tool result expanded into its own entry.
*/
export function serializeMessages(messages: Message[]): WireMessage[] {
const wire: WireMessage[] = []
@@ -97,7 +99,14 @@ export function serializeMessages(messages: Message[]): WireMessage[] {
return wire
}
/** Build the full wire request. */
/**
* Build the full wire request. Always streaming (`stream: true`, usage
* reporting on); optional fields are omitted rather than sent as null, so
* provider defaults apply.
* @param options - the harness request (model, history, system, tools, sampling).
* @param defaults - adapter-level thinking defaults; undefined fields put nothing on the wire.
* @returns the chat-completions request body.
*/
export function serializeRequest(options: GenerateOptions, defaults: RequestDefaults = {}): WireRequest {
const messages: WireMessage[] = []
if (options.system !== undefined) {

View File

@@ -37,6 +37,8 @@ function eventData(block: string): string | undefined {
* Parse a byte stream into SSE data payloads. Yields `[DONE]` as the final
* value and returns; throws `LlmError('STREAM_CLOSED')` when the stream ends
* without it (truncated response — the model call cannot be trusted).
* @param stream - raw SSE bytes; reads may split anywhere, including mid-UTF-8 sequence.
* @returns each event's data payload in arrival order, the `[DONE]` sentinel last.
*/
export async function* parseSse(stream: AsyncIterable<Uint8Array>): AsyncGenerator<string> {
const decoder = new TextDecoder()

View File

@@ -29,7 +29,11 @@ interface OpenBlock {
name?: string
}
/** Map the wire finish_reason vocabulary to the harness FinishReason. */
/**
* Map the wire finish_reason vocabulary to the harness FinishReason.
* @param reason - the wire `finish_reason` string.
* @returns the mapped reason; unrecognized values (content_filter, …) become `{kind: 'error'}` with the uppercased value as `code`.
*/
export function mapFinishReason(reason: string): FinishReason {
switch (reason) {
case 'stop': return { kind: 'stop' }
@@ -46,6 +50,8 @@ export function mapFinishReason(reason: string): FinishReason {
* (`prompt_tokens = prompt_cache_hit_tokens + prompt_cache_miss_tokens`,
* api/create-chat-completion); the harness TokenUsage convention is
* DISJOINT counts, so cache reads are subtracted out of `inputTokens`.
* @param usage - wire usage from the finish chunk or the trailing usage-only chunk.
* @returns disjoint harness counts; cache/reasoning fields present only when the wire reported them.
*/
export function mapUsage(usage: WireUsage): TokenUsage {
const cacheRead = usage.prompt_tokens_details?.cached_tokens ?? usage.prompt_cache_hit_tokens
@@ -75,6 +81,8 @@ function closeBlock(block: OpenBlock): ContentBlock {
/**
* Consume SSE data payloads (ending with `[DONE]`) and yield StreamChunks.
* Malformed JSON payloads abort the stream with `MALFORMED_RESPONSE`.
* @param payloads - SSE data payloads from {@link parseSse}, `[DONE]`-terminated.
* @returns deltas as they arrive; `block-end`s, `usage`, and `finish` are all deferred to the `[DONE]` sentinel.
*/
export async function* translate(payloads: AsyncIterable<string>): AsyncGenerator<StreamChunk> {
let nextIndex = 0

View File

@@ -48,12 +48,18 @@ export interface WireToolMessage {
content: string
}
/** One entry of the request `messages` array, discriminated on `role`. */
export type WireMessage =
| WireSystemMessage
| WireUserMessage
| WireAssistantMessage
| WireToolMessage
/**
* Assistant-role history message. The harness replays `content: ""` (never
* null) on tool-call-only turns — some gateways reject null — and sends null
* only when the turn carried neither text nor tool calls.
*/
export interface WireAssistantMessage {
role: 'assistant'
content: string | null
@@ -66,12 +72,14 @@ export interface WireAssistantMessage {
tool_calls?: WireToolCall[]
}
/** A completed tool call replayed on an assistant history message; `arguments` is the raw JSON string. */
export interface WireToolCall {
id: string
type: 'function'
function: { name: string; arguments: string }
}
/** One entry of the request `tools` array; `parameters` is a JSON Schema object. */
export interface WireTool {
type: 'function'
function: {
@@ -88,11 +96,13 @@ export interface WireChunk {
usage?: WireUsage | null
}
/** One streamed choice (requests always ask for a single one); `finish_reason` is non-null only on its terminal chunk. */
export interface WireChoice {
delta?: WireDelta
finish_reason?: string | null
}
/** The incremental content of one streamed choice; any subset of fields may be present per chunk. */
export interface WireDelta {
role?: string
/** Visible text. Null/empty on reasoning/tool-call chunks. */
@@ -105,6 +115,7 @@ export interface WireDelta {
tool_calls?: WireToolCallDelta[]
}
/** A streamed fragment of one tool call; fragments sharing an `index` concatenate into one call. */
export interface WireToolCallDelta {
/** Disambiguates parallel tool calls; stable across a call's deltas. */
index: number
@@ -119,6 +130,13 @@ export interface WireToolCallDelta {
}
}
/**
* Wire token accounting. `prompt_tokens` INCLUDES cache hits (it equals
* `prompt_cache_hit_tokens + prompt_cache_miss_tokens`); `mapUsage` subtracts
* them to keep the harness convention of disjoint counts.
* `prompt_tokens_details.cached_tokens` is the OpenAI-compat spelling of the
* hit count.
*/
export interface WireUsage {
prompt_tokens: number
completion_tokens: number