Skip to content

feat(harness-node): add Kimi (Moonshot) provider worker - #173

Merged
andersonleal merged 1 commit into
mainfrom
feat/harness-node-provider-kimi
May 20, 2026
Merged

feat(harness-node): add Kimi (Moonshot) provider worker#173
andersonleal merged 1 commit into
mainfrom
feat/harness-node-provider-kimi

Conversation

@andersonleal

@andersonleal andersonleal commented May 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds provider-kimi worker to the harness-node bundle, exposing provider::kimi::stream and provider::kimi::complete on the iii bus. Mirrors the provider-openai scaffold against https://api.moonshot.ai/v1/chat/completions. Kimi K2 is OpenAI-Chat-Completions wire compatible, so SSE parsing, wire translation, and tool-call deltas are reused unchanged.
  • Catalog: seeds kimi-k2.5 and kimi-k2.6 in models-catalog (the two flagship multimodal K2 variants surface in the chat picker). Env map: kimi → MOONSHOT_API_KEY alongside the legacy kimi-coding slug. Router: extends turn-orchestrator/provider-router with a kimi arm and a heuristic for model ids starting with kimi- or moonshot-v1-.
  • Fixes a latent /compact defect: the summariser's binary openai-vs-anthropic ternary silently routed every other provider to provider::anthropic::stream, producing NOT_FOUND_ERROR when summarising a non-openai/non-anthropic session. context-compaction/summarize.ts now routes through the canonical provider-router and always uses the session's own model (the COMPACT_SUMMARIZER_PROVIDER/MODEL env-var overrides are removed — they were a footgun that hid the mis-route).

Caveats

  • Auth slug workaround: provider-kimi/auth.ts calls auth::get_token with provider: 'kimi-coding' (not 'kimi'). The compiled Rust auth-credentials binary (the live handler on most engines today) maps kimi-coding → MOONSHOT_API_KEY but doesn't yet know the bare kimi slug we added here. Once the Rust worker is rebuilt or replaced with the harness-node port, the bare kimi slug also resolves.
  • Live runtime: the Rust context-compaction binary has the same /compact defect this PR fixes in the harness-node port. The Rust patch is out of scope for this PR.

Test plan

  • pnpm typecheck — clean
  • pnpm test — 473/473 pass, including 26 new kimi-specific cases
  • pnpm build && node dist/index.js --manifest lists provider-kimi between provider-openai and llm-budget
  • git diff main...HEAD touches only harness-node/ (31 files, +1165/−46)
  • Live smoke: set MOONSHOT_API_KEY (or auth::set_token { provider: 'kimi-coding' }), trigger provider::kimi::complete with model: 'kimi-k2.5', expect a non-error AssistantMessage
  • Live smoke: /compact in a Kimi K2.5 session succeeds (the bus call now lands on provider::kimi::stream, not anthropic)
  • models::list { provider: 'kimi' } returns exactly kimi-k2.5 and kimi-k2.6

References

@vercel

vercel Bot commented May 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview, Comment May 20, 2026 10:54pm

Request Review

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request introduces a new Kimi (Moonshot) LLM provider worker to the harness-node system, including SSE-based streaming, message translation, credential handling, and comprehensive tests. It simultaneously refactors context-compaction summarization to route through the provider-router instead of config-based selection, removing deprecated summarizer environment variables.

Changes

Kimi Provider Implementation and Integration

Layer / File(s) Summary
Provider config, auth, and type contracts
src/provider-kimi/config.ts, src/provider-kimi/types.ts, src/auth-credentials/types.ts, config.yaml
Defines WorkerConfig type, ChatCompletionsConfig contract, configFromCredential helper, and credential extraction from MOONSHOT_API_KEY environment variable. Adds Kimi provider mapping to ENV_VAR_MAP and provider_kimi defaults section to config.yaml.
SSE streaming and message/tool translation
src/provider-kimi/sse.ts, src/provider-kimi/stream.ts, src/provider-kimi/wire-messages.ts, src/provider-kimi/wire-tools.ts
Implements PartialState accumulation, SSE chunk handling with handleChunk and finish-reason mapping, error classification via classifyKimiError, async generator streamKimi with fetch/SSE parsing, message/tool wire translation to OpenAI format, and event collection via collect.
Stream and complete provider handlers
src/provider-kimi/stream-fn.ts, src/provider-kimi/complete.ts
Registers provider::kimi::stream (async handler forwarding events to caller's channel writer) and legacy provider::kimi::complete (drains stream and returns final message) with input schema validation and metadata.
Worker bootstrap and harness-node integration
src/provider-kimi/main.ts, src/provider-kimi/register.ts, src/provider-kimi/iii.worker.yaml, src/index.ts, package.json
CLI entrypoint calling bootstrapWorker, register orchestrator loading config and delegating to stream/complete handlers, worker manifest declaring auth-credentials dependency, integration into WORKERS array, and npm scripts/binary entries for dev and production.
Provider routing and context-compaction refactoring
src/turn-orchestrator/provider-router.ts, src/context-compaction/config.ts, src/context-compaction/summarize.ts
Extends RouteDecision to include kimi variant, updates decide to recognize kimi model patterns, and maps provider=kimi to provider::kimi::stream in targetFunctionId. Removes summarizerProvider/summarizerModel config exports and updates summarizeAndAppend to route via provider-router using session model's providerID/modelID.
Model catalog seeding and config defaults
src/models-catalog/models.json
Adds kimi-k2.6 and kimi-k2.5 model entries with context/output token limits, capability flags (thinking, vision, tools, cache), and SSE transport.
Comprehensive test coverage
tests/auth-credentials/env-map-kimi.test.ts, tests/provider-kimi/sse.test.ts, tests/provider-kimi/stream.test.ts, tests/provider-kimi/wire-messages.test.ts, tests/models-catalog/seed-kimi.test.ts, tests/turn-orchestrator/provider-router.test.ts, tests/context-compaction/summarize.test.ts
Tests env-var mapping for kimi and kimi-coding slugs, SSE chunk handling (usage merging, finish-reason mapping, tool-call accumulation), streaming event sequencing, wire-format translation (system prompts, tool calls, function results), model catalog seeding, provider routing (decide/targetFunctionId), and context-compaction integration with kimi sessions.
Documentation: architecture, worker guide, and implementation plan
README.md, docs/architecture.md, docs/workers/provider-kimi.md, docs/workers/context-compaction.md, docs/superpowers/plans/2026-05-20-provider-kimi.md, docs/superpowers/specs/2026-05-20-provider-kimi-design.md
Updates worker catalog and quickstart in README, extends system and boot-ordering diagrams to show provider-kimi wiring and dependencies, adds per-worker documentation for provider-kimi configuration and source layout, clarifies context-compaction summarizer inheritance via provider-router, and supplies comprehensive implementation plan and design specification.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • iii-hq/workers#155: Updates to harness-node/docs/architecture.md adding the provider-kimi worker to architecture diagrams and system wiring.
  • iii-hq/workers#170: Directly modifies harness-node/src/context-compaction/summarize.ts to change summarization provider routing, overlapping with the main PR's refactor to use provider-router.
  • iii-hq/workers#163: Touches context-compaction summarization pipeline (harness-node/src/context-compaction/summarize.ts) that the main PR's provider routing changes are layered onto.

Suggested reviewers

  • sergiofilhowz
  • ytallo

Poem

🐰 A Moonshot provider hops on board,
With Kimi's SSE streaming stored,
Wire-messages dance, chunks align,
Provider-router routes designs.
Context flows through tests so bright,
The harness-node takes flight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: adding a new Kimi (Moonshot) LLM provider worker to harness-node.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/harness-node-provider-kimi

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 11 skipped (no docs/).

Layer Result
structure
vale
ai

Three for three. Nicely done.

Adds a Kimi (Moonshot) LLM provider to the harness-node worker bundle,
exposing provider::kimi::stream and provider::kimi::complete on the iii
bus. Kimi K2 is OpenAI-Chat-Completions wire compatible; the new worker
mirrors the provider-openai scaffold (SSE state machine, message
shaping, tool-call deltas, error classification) and talks to
https://api.moonshot.ai/v1/chat/completions by default.

Surface:
- src/provider-kimi/ — 12 files (main, register, config, types, auth,
  complete, stream, stream-fn, sse, wire-messages, wire-tools,
  iii.worker.yaml)
- 26 vitest cases under tests/provider-kimi/, tests/auth-credentials/,
  tests/models-catalog/, tests/turn-orchestrator/, and
  tests/context-compaction/ covering SSE parsing, wire translation,
  fetch-mocked stream happy path, HTTP 401/429/5xx classification,
  transport failures, credential resolution, catalog seeds, router
  decisions, and /compact provider routing

Integration:
- src/index.ts wires provider-kimi into the composite entry-point
- src/auth-credentials/types.ts adds kimi -> MOONSHOT_API_KEY alongside
  the existing kimi-coding row
- src/models-catalog/models.json seeds the two flagship multimodal K2
  variants (kimi-k2.5, kimi-k2.6) for the chat picker
- src/turn-orchestrator/provider-router.ts extends RouteDecision with
  a kimi arm and a heuristic for model ids starting with kimi- or
  moonshot-v1-
- src/context-compaction/summarize.ts routes /compact through the
  canonical provider-router so the session's own model is used (fixes
  NOT_FOUND_ERROR when summarising kimi sessions); drops the
  COMPACT_SUMMARIZER_PROVIDER/MODEL env-var overrides that hid the
  mis-route
- config.yaml, package.json (dev:provider-kimi + iii-provider-kimi
  bin), README.md, docs/architecture.md, docs/workers/provider-kimi.md,
  docs/workers/context-compaction.md

Workaround for the live runtime: the credential lookup uses the
kimi-coding slug because the compiled Rust auth-credentials binary
predates the bare kimi row. Once the Rust worker is rebuilt or replaced
with the harness-node port, the bare kimi slug also resolves.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (2)
harness-node/docs/superpowers/plans/2026-05-20-provider-kimi.md (1)

100-102: 💤 Low value

Add language specifiers to shell code blocks.

Multiple shell command blocks (lines 100, 117, 125, 173, 216, 224, and many others throughout the file) are missing language specifiers. Add bash or shell after the opening backticks to satisfy markdownlint and improve syntax highlighting.

Example fix for line 100
-```
+```bash
 pnpm test tests/auth-credentials/env-map-kimi.test.ts
</details>

Apply the same pattern to all shell command blocks throughout the file.


Also applies to: 117-119, 125-129, 173-175, 216-218, 224-228

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @harness-node/docs/superpowers/plans/2026-05-20-provider-kimi.md around lines
100 - 102, Several fenced code blocks containing shell commands (e.g., the block
showing "pnpm test tests/auth-credentials/env-map-kimi.test.ts" and other blocks
around lines referenced) are missing language specifiers; update every shell
command fence by adding a language tag (bash or shell) immediately after the
opening triple backticks (e.g., ```bash) for each occurrence (including the
blocks at the reported ranges and the other similar blocks throughout the
document) so markdownlint passes and syntax highlighting works.


</details>

</blockquote></details>
<details>
<summary>harness-node/src/provider-kimi/complete.ts (1)</summary><blockquote>

`12-17`: _⚡ Quick win_

**Consider schema-based validation for consistency.**

The `complete` handler uses manual parsing with `requireString` and type guards, while `stream-fn.ts` uses `ProviderStreamRuntimeInputSchema.parse()`. Using a schema (e.g., `ProviderCompleteRuntimeInputSchema`) would improve consistency, type safety, and maintainability across both endpoints.




<details>
<summary>📋 Suggested refactor</summary>

Define a schema in `types/provider.ts` (or similar location):

```typescript
export const ProviderCompleteRuntimeInputSchema = z.object({
  model: z.string(),
  system_prompt: z.string().optional(),
  messages: z.array(z.any()).optional(), // or AgentMessageSchema
  tools: z.array(z.any()).optional(),    // or AgentFunctionSchema
});
```

Then replace manual parsing:

```diff
 export function register(iii: ISdk, worker: WorkerConfig): void {
   iii.registerFunction(
     'provider::kimi::complete',
     async (payload: unknown) => {
-      const obj = (payload ?? {}) as Record<string, unknown>;
-      const model = requireString(obj, 'model');
-      const system_prompt = typeof obj.system_prompt === 'string' ? obj.system_prompt : '';
-      const messages = Array.isArray(obj.messages) ? (obj.messages as AgentMessage[]) : [];
-      const tools = Array.isArray(obj.tools) ? (obj.tools as AgentFunction[]) : [];
+      const input = ProviderCompleteRuntimeInputSchema.parse(payload);
+      const model = input.model;
+      const system_prompt = input.system_prompt ?? '';
+      const messages = (input.messages ?? []) as AgentMessage[];
+      const tools = (input.tools ?? []) as AgentFunction[];
       const cfg = await buildConfig(iii, worker, model);
       return await collect(streamKimi({ cfg, system_prompt, messages, tools }));
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness-node/src/provider-kimi/complete.ts` around lines 12 - 17, The
complete handler currently manually extracts fields using requireString and type
guards (see the async handler, requireString, system_prompt, messages, tools) —
replace that ad hoc parsing with a Zod schema (e.g., add
ProviderCompleteRuntimeInputSchema in types/provider.ts) and call
ProviderCompleteRuntimeInputSchema.parse(payload) at the start of the async
handler to validate and coerce model, system_prompt, messages, and tools; adjust
subsequent code to use the parsed object and remove the manual
requireString/type checks so the handler matches stream-fn.ts’s
ProviderStreamRuntimeInputSchema approach.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @harness-node/docs/superpowers/plans/2026-05-20-provider-kimi.md:

  • Around line 823-840: The documentation snippet for fetchCredential is
    inconsistent: the iii.trigger call uses payload: { provider: 'kimi' } but
    runtime uses 'kimi-coding' as a workaround; update the payload in the example to
    payload: { provider: 'kimi-coding' } or add a one-line comment next to the
    iii.trigger invocation explaining the runtime workaround (refer to function
    fetchCredential and the iii.trigger payload field) so the docs match the
    implementation.

In @harness-node/docs/superpowers/specs/2026-05-20-provider-kimi-design.md:

  • Around line 196-212: Update the diagram comment to match the runtime
    implementation by changing the auth trigger payload provider value from 'kimi'
    to 'kimi-coding' in the provider-kimi::stream-fn auth flow (the
    iii.trigger(auth::get_token, { provider: 'kimi' }) comment); ensure the
    auth.buildConfig / iii.trigger line and any adjacent auth credential note
    reflect provider: 'kimi-coding' so the diagram matches the actual use of
    kimi-coding at runtime.
  • Line 86: Update the design spec to match the implementation by changing the
    documented provider value used in auth.ts/auth::get_token: where the spec says
    the payload should use provider: 'kimi' and returned config.provider_name
    'kimi', change it to provider: 'kimi-coding' and provider_name: 'kimi-coding'
    (or explicitly note this as a documented deviation) so the spec aligns with the
    runtime workaround implemented in provider-openai/auth.ts and the
    auth::get_token behavior.

In @harness-node/docs/workers/provider-kimi.md:

  • Around line 8-9: Update the docs to match the runtime workaround: change the
    reference to the auth worker call from auth::get_token with provider 'kimi' to
    the actual implementation used at runtime (provider 'kimi-coding'), or add a
    short note explaining that provider-kimi/auth.ts currently uses 'kimi-coding' as
    a temporary workaround; reference the auth::get_token call and the
    provider-kimi/auth.ts module so readers can find the implementation.

In @harness-node/src/provider-kimi/stream.ts:

  • Around line 92-124: The SSE parser can drop trailing buffered data and only
    matches "data: " exactly—fix by treating both "data:" and "data: " as valid in
    parseDataLine, accept single-newline as an event separator in addition to
    "\n\n", and after the read loop completes (or when done is true) process any
    remaining buf by running it through parseDataLine/JSON parse/handleChunk before
    yielding the final done event; update the loop that uses reader.read(),
    decoder.decode, buf, parseDataLine, handleChunk, buildFinal and
    syntheticErrorEvent so leftover data is parsed rather than discarded and ensure
    buildFinal/syntheticErrorEvent are only yielded once at the end.
  • Around line 49-59: The upstream fetch in the block that assigns resp is
    missing a timeout and can hang; wrap the fetch call with an AbortController,
    pass controller.signal to fetch, and set a timer (configurable via cfg.timeout
    or a sane default like 30s) to call controller.abort() when elapsed; handle the
    abort/timeout case in the catch (detect AbortError or aborted signal) and yield
    syntheticErrorEvent with a clear timeout message (including cfg.model and
    cfg.provider_name) before returning, and ensure the timer is cleared on
    successful response to avoid leaks.

In @harness-node/src/turn-orchestrator/provider-router.ts:

  • Around line 22-32: The code's current path silently maps any unknown explicit
    req.provider to Anthropic; update the logic in decide (use the p = (req.provider
    ?? '').toLowerCase() branch) so that if p is non-empty and is not one of the
    recognized providers ('openai' or 'kimi' — and 'anthropic' if you want it
    explicit) you do not fall back to Anthropic but instead return/throw a clear
    error or sentinel (e.g., throw new Error or return { error: 'unsupported
    provider' }) indicating an unsupported provider; modify the early-if checks
    around p and the final return to ensure only empty p triggers heuristic
    model-based routing and explicit-but-unknown p triggers the error path.

Nitpick comments:
In @harness-node/docs/superpowers/plans/2026-05-20-provider-kimi.md:

  • Around line 100-102: Several fenced code blocks containing shell commands
    (e.g., the block showing "pnpm test tests/auth-credentials/env-map-kimi.test.ts"
    and other blocks around lines referenced) are missing language specifiers;
    update every shell command fence by adding a language tag (bash or shell)
    immediately after the opening triple backticks (e.g., ```bash) for each
    occurrence (including the blocks at the reported ranges and the other similar
    blocks throughout the document) so markdownlint passes and syntax highlighting
    works.

In @harness-node/src/provider-kimi/complete.ts:

  • Around line 12-17: The complete handler currently manually extracts fields
    using requireString and type guards (see the async handler, requireString,
    system_prompt, messages, tools) — replace that ad hoc parsing with a Zod schema
    (e.g., add ProviderCompleteRuntimeInputSchema in types/provider.ts) and call
    ProviderCompleteRuntimeInputSchema.parse(payload) at the start of the async
    handler to validate and coerce model, system_prompt, messages, and tools; adjust
    subsequent code to use the parsed object and remove the manual
    requireString/type checks so the handler matches stream-fn.ts’s
    ProviderStreamRuntimeInputSchema approach.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: Organization UI

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `1f6664e6-aeda-4c9e-97e8-e7d3e18eb2ef`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 241509a70c6f537eda3cb3dd9595e8ad278f5cc0 and 134e84de4d4aa806a268980534e2ebda05f28ff7.

</details>

<details>
<summary>📒 Files selected for processing (33)</summary>

* `harness-node/README.md`
* `harness-node/config.yaml`
* `harness-node/docs/architecture.md`
* `harness-node/docs/superpowers/plans/2026-05-20-provider-kimi.md`
* `harness-node/docs/superpowers/specs/2026-05-20-provider-kimi-design.md`
* `harness-node/docs/workers/context-compaction.md`
* `harness-node/docs/workers/provider-kimi.md`
* `harness-node/package.json`
* `harness-node/src/auth-credentials/types.ts`
* `harness-node/src/context-compaction/config.ts`
* `harness-node/src/context-compaction/summarize.ts`
* `harness-node/src/index.ts`
* `harness-node/src/models-catalog/models.json`
* `harness-node/src/provider-kimi/auth.ts`
* `harness-node/src/provider-kimi/complete.ts`
* `harness-node/src/provider-kimi/config.ts`
* `harness-node/src/provider-kimi/iii.worker.yaml`
* `harness-node/src/provider-kimi/main.ts`
* `harness-node/src/provider-kimi/register.ts`
* `harness-node/src/provider-kimi/sse.ts`
* `harness-node/src/provider-kimi/stream-fn.ts`
* `harness-node/src/provider-kimi/stream.ts`
* `harness-node/src/provider-kimi/types.ts`
* `harness-node/src/provider-kimi/wire-messages.ts`
* `harness-node/src/provider-kimi/wire-tools.ts`
* `harness-node/src/turn-orchestrator/provider-router.ts`
* `harness-node/tests/auth-credentials/env-map-kimi.test.ts`
* `harness-node/tests/context-compaction/summarize.test.ts`
* `harness-node/tests/models-catalog/seed-kimi.test.ts`
* `harness-node/tests/provider-kimi/sse.test.ts`
* `harness-node/tests/provider-kimi/stream.test.ts`
* `harness-node/tests/provider-kimi/wire-messages.test.ts`
* `harness-node/tests/turn-orchestrator/provider-router.test.ts`

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* harness-node/src/context-compaction/config.ts

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +823 to +840

```ts
import type { Credential } from '../auth-credentials/types.js';
import type { ISdk } from '../runtime/iii.js';
import type { WorkerConfig } from './config.js';
import { type ChatCompletionsConfig, configFromCredential } from './types.js';

export async function fetchCredential(iii: ISdk): Promise<Credential> {
const cred = await iii.trigger<unknown, Credential | null>({
function_id: 'auth::get_token',
payload: { provider: 'kimi' },
timeoutMs: 5_000,
});
if (!cred || typeof cred !== 'object' || !('type' in cred)) {
throw new Error('auth::get_token returned no credential for provider=kimi');
}
return cred;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Documentation inconsistency with implementation.

The code snippet in Task 5 shows payload: { provider: 'kimi' } (line 833), but according to the PR objectives, the actual implementation uses provider: 'kimi-coding' as a runtime workaround. Update the snippet to match the implementation or add a comment explaining the workaround.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness-node/docs/superpowers/plans/2026-05-20-provider-kimi.md` around lines
823 - 840, The documentation snippet for fetchCredential is inconsistent: the
iii.trigger call uses payload: { provider: 'kimi' } but runtime uses
'kimi-coding' as a workaround; update the payload in the example to payload: {
provider: 'kimi-coding' } or add a one-line comment next to the iii.trigger
invocation explaining the runtime workaround (refer to function fetchCredential
and the iii.trigger payload field) so the docs match the implementation.

| `main.ts` | `provider-openai/main.ts` | `bootstrapWorker` name → `provider-kimi`; description updated |
| `register.ts` | `provider-openai/register.ts` | Loads `provider_kimi` config section |
| `config.ts` | `provider-openai/config.ts` | Section key → `provider_kimi`; `DEFAULT_API_URL` → `https://api.moonshot.ai/v1/chat/completions` |
| `auth.ts` | `provider-openai/auth.ts` | `auth::get_token` payload `provider: 'kimi'`; `provider_name: 'kimi'` in returned config |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Documentation inconsistency with implementation.

Line 86 states that auth.ts should use payload with provider: 'kimi', but according to the PR objectives, the actual implementation uses provider: 'kimi-coding' as a runtime workaround. Update the design spec to reflect the current implementation or document this as a known deviation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness-node/docs/superpowers/specs/2026-05-20-provider-kimi-design.md` at
line 86, Update the design spec to match the implementation by changing the
documented provider value used in auth.ts/auth::get_token: where the spec says
the payload should use provider: 'kimi' and returned config.provider_name
'kimi', change it to provider: 'kimi-coding' and provider_name: 'kimi-coding'
(or explicitly note this as a documented deviation) so the spec aligns with the
runtime workaround implemented in provider-openai/auth.ts and the
auth::get_token behavior.

Comment on lines +196 to +212
```
turn-orchestrator
▼ iii.trigger(provider::kimi::stream, { model, system_prompt, messages, tools, writer_ref })
provider-kimi::stream-fn
│ (auto-hydrates writer_ref into ChannelWriter)
├──▶ auth.buildConfig
│ └──▶ iii.trigger(auth::get_token, { provider: 'kimi' })
│ └──▶ auth-credentials (stored cred or MOONSHOT_API_KEY env)
├──▶ stream.streamKimi
│ ├──▶ wire-messages.toOpenaiMessages
│ ├──▶ wire-tools.functionsToOpenai
│ ├──▶ fetch POST api.moonshot.ai/v1/chat/completions
│ └──▶ sse.handleChunk loop
└──▶ writer.sendMessage(JSON.stringify(event)) per AssistantMessageEvent
writer.close() on terminal event
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Documentation inconsistency in data flow diagram.

Line 204 shows provider: 'kimi' in the auth flow comment, but the actual implementation uses provider: 'kimi-coding' as a runtime workaround. Update the diagram comment to match the implementation.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 196-196: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness-node/docs/superpowers/specs/2026-05-20-provider-kimi-design.md`
around lines 196 - 212, Update the diagram comment to match the runtime
implementation by changing the auth trigger payload provider value from 'kimi'
to 'kimi-coding' in the provider-kimi::stream-fn auth flow (the
iii.trigger(auth::get_token, { provider: 'kimi' }) comment); ensure the
auth.buildConfig / iii.trigger line and any adjacent auth credential note
reflect provider: 'kimi-coding' so the diagram matches the actual use of
kimi-coding at runtime.

Comment on lines +8 to +9
The iii-side bridge to Moonshot's Chat Completions API. It pulls a
credential from the auth worker (`auth::get_token`, provider `kimi`),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Documentation inconsistency with implementation.

The documentation states the worker calls auth::get_token with provider: 'kimi', but according to the PR objectives, the actual implementation in provider-kimi/auth.ts uses provider: 'kimi-coding' as a runtime workaround. Update this line to reflect the current implementation or add a note about the temporary workaround.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness-node/docs/workers/provider-kimi.md` around lines 8 - 9, Update the
docs to match the runtime workaround: change the reference to the auth worker
call from auth::get_token with provider 'kimi' to the actual implementation used
at runtime (provider 'kimi-coding'), or add a short note explaining that
provider-kimi/auth.ts currently uses 'kimi-coding' as a temporary workaround;
reference the auth::get_token call and the provider-kimi/auth.ts module so
readers can find the implementation.

Comment on lines +49 to +59
let resp: Response;
try {
resp = await fetch(cfg.url, {
method: 'POST',
headers,
body: JSON.stringify(body),
});
} catch (err) {
yield syntheticErrorEvent(`kimi fetch failed: ${String(err)}`, cfg.model, cfg.provider_name);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a timeout to the upstream fetch call.

This external call can hang indefinitely on network stalls, which can pin worker resources and degrade reliability under load.

Suggested fix
   let resp: Response;
+  const controller = new AbortController();
+  const timeout = setTimeout(() => controller.abort(), 30_000);
   try {
     resp = await fetch(cfg.url, {
       method: 'POST',
       headers,
       body: JSON.stringify(body),
+      signal: controller.signal,
     });
   } catch (err) {
     yield syntheticErrorEvent(`kimi fetch failed: ${String(err)}`, cfg.model, cfg.provider_name);
     return;
+  } finally {
+    clearTimeout(timeout);
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let resp: Response;
try {
resp = await fetch(cfg.url, {
method: 'POST',
headers,
body: JSON.stringify(body),
});
} catch (err) {
yield syntheticErrorEvent(`kimi fetch failed: ${String(err)}`, cfg.model, cfg.provider_name);
return;
}
let resp: Response;
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30_000);
try {
resp = await fetch(cfg.url, {
method: 'POST',
headers,
body: JSON.stringify(body),
signal: controller.signal,
});
} catch (err) {
yield syntheticErrorEvent(`kimi fetch failed: ${String(err)}`, cfg.model, cfg.provider_name);
return;
} finally {
clearTimeout(timeout);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness-node/src/provider-kimi/stream.ts` around lines 49 - 59, The upstream
fetch in the block that assigns resp is missing a timeout and can hang; wrap the
fetch call with an AbortController, pass controller.signal to fetch, and set a
timer (configurable via cfg.timeout or a sane default like 30s) to call
controller.abort() when elapsed; handle the abort/timeout case in the catch
(detect AbortError or aborted signal) and yield syntheticErrorEvent with a clear
timeout message (including cfg.model and cfg.provider_name) before returning,
and ensure the timer is cleared on successful response to avoid leaks.

Comment thread harness-node/src/provider-kimi/stream.ts
Comment on lines 22 to 32
const p = (req.provider ?? '').toLowerCase();
if (p === 'openai') return { provider: 'openai', model: req.model };
if (p === 'kimi') return { provider: 'kimi', model: req.model };
// Heuristic for missing provider: model name disambiguates.
if (!p && /^gpt-|^o\d-/i.test(req.model)) {
return { provider: 'openai', model: req.model };
}
if (!p && /^kimi-|^moonshot-v1-/i.test(req.model)) {
return { provider: 'kimi', model: req.model };
}
return { provider: 'anthropic', model: req.model };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid silent fallback to Anthropic for unknown explicit providers.

When req.provider is present but unsupported, decide() currently returns Anthropic. That can silently misroute /compact (now session-provider-driven) to the wrong worker/model pair.

💡 Suggested fix
 export function decide(req: RouteRequest): RouteDecision {
   const p = (req.provider ?? '').toLowerCase();
+  if (p && p !== 'anthropic' && p !== 'openai' && p !== 'kimi') {
+    throw new Error(`Unsupported provider: ${req.provider}`);
+  }
   if (p === 'openai') return { provider: 'openai', model: req.model };
   if (p === 'kimi') return { provider: 'kimi', model: req.model };
   // Heuristic for missing provider: model name disambiguates.
   if (!p && /^gpt-|^o\d-/i.test(req.model)) {
     return { provider: 'openai', model: req.model };
   }
   if (!p && /^kimi-|^moonshot-v1-/i.test(req.model)) {
     return { provider: 'kimi', model: req.model };
   }
   return { provider: 'anthropic', model: req.model };
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const p = (req.provider ?? '').toLowerCase();
if (p === 'openai') return { provider: 'openai', model: req.model };
if (p === 'kimi') return { provider: 'kimi', model: req.model };
// Heuristic for missing provider: model name disambiguates.
if (!p && /^gpt-|^o\d-/i.test(req.model)) {
return { provider: 'openai', model: req.model };
}
if (!p && /^kimi-|^moonshot-v1-/i.test(req.model)) {
return { provider: 'kimi', model: req.model };
}
return { provider: 'anthropic', model: req.model };
const p = (req.provider ?? '').toLowerCase();
if (p && p !== 'anthropic' && p !== 'openai' && p !== 'kimi') {
throw new Error(`Unsupported provider: ${req.provider}`);
}
if (p === 'openai') return { provider: 'openai', model: req.model };
if (p === 'kimi') return { provider: 'kimi', model: req.model };
// Heuristic for missing provider: model name disambiguates.
if (!p && /^gpt-|^o\d-/i.test(req.model)) {
return { provider: 'openai', model: req.model };
}
if (!p && /^kimi-|^moonshot-v1-/i.test(req.model)) {
return { provider: 'kimi', model: req.model };
}
return { provider: 'anthropic', model: req.model };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness-node/src/turn-orchestrator/provider-router.ts` around lines 22 - 32,
The code's current path silently maps any unknown explicit req.provider to
Anthropic; update the logic in decide (use the p = (req.provider ??
'').toLowerCase() branch) so that if p is non-empty and is not one of the
recognized providers ('openai' or 'kimi' — and 'anthropic' if you want it
explicit) you do not fall back to Anthropic but instead return/throw a clear
error or sentinel (e.g., throw new Error or return { error: 'unsupported
provider' }) indicating an unsupported provider; modify the early-if checks
around p and the final return to ensure only empty p triggers heuristic
model-based routing and explicit-but-unknown p triggers the error path.

@andersonleal
andersonleal merged commit e4e4d24 into main May 20, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants