Skip to content

fix: stabilize v0.5 routes and restore TypeScript build#10

Merged
djimit merged 2 commits into
mainfrom
fix/stabilize-v0.5-routes
Jun 11, 2026
Merged

fix: stabilize v0.5 routes and restore TypeScript build#10
djimit merged 2 commits into
mainfrom
fix/stabilize-v0.5-routes

Conversation

@djimit

@djimit djimit commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

Stabilizes the v0.5 local-first baseline by removing invalid generated TypeScript fragments, validating risky route inputs, aligning version metadata, and making local development safer by default.

Key fixes

  • Repaired invalid generated code in session streaming, OpenClaw usage, LM Studio chat/stream, benchmark summaries, and route advisor scoring.
  • Added typed request-body guards for /sessions/:id/message/stream, /openclaw/usage, /lmstudio/chat, and /lmstudio/chat/stream.
  • Replaced benchmark result storage via globalThis as any with a typed in-memory benchmarkState service.
  • Preserved route names and dashboard direct WebSocket connection to backend port 3001.
  • Restricted CORS to localhost dev origins and defaulted server bind to 127.0.0.1 with a warning for non-local binds.
  • Aligned version metadata to 0.5.0 across package metadata, health response, startup banner, and dashboard badge.
  • Added workspace typecheck scripts and updated local development docs.

Files changed

  • packages/server/src/api/routes.ts
  • packages/server/src/index.ts
  • packages/server/src/services/benchmark.ts
  • packages/server/src/services/route-advisor.ts
  • packages/server/src/services/benchmark-state.ts
  • packages/server/src/config/version.ts
  • packages/dashboard/src/components/Dashboard.tsx
  • packages/dashboard/src/config/version.ts
  • package.json, package-lock.json
  • packages/server/package.json, packages/dashboard/package.json
  • README.md

Commands run

  • cd packages/server && npx tsc --noEmit
  • cd packages/dashboard && npx tsc --noEmit
  • npm run typecheck
  • npm run build
  • npm run dev:server smoke with GET http://127.0.0.1:3001/api/health
  • WebSocket smoke against ws://127.0.0.1:3001/ws
  • npm run dev:dashboard smoke against http://localhost:3000
  • git diff --check
  • Generated-placeholder scan for const const, CONST_, process.env.(, const if, and globalThis as any

Known limitations

  • Existing legacy any usages remain elsewhere in server/dashboard code; this PR removes the worst touched globalThis as any path and avoids adding new production any.
  • LM Studio lifecycle routes still use the existing lms CLI integration; this PR does not expand command execution behavior.
  • The accidental first PR-body attempt triggered shell expansion locally after the branch was already pushed; the PR branch itself was not changed, and the PR worktree is clean.

Follow-up backlog

  • Add a small Vitest suite for request-body guards and health route behavior.
  • Continue replacing legacy production any with typed service boundaries.
  • Consider serving dashboard WebSocket URL from config if non-default dev ports become supported.
  • Add explicit auth or network exposure controls before supporting remote access.

if (temperature !== undefined && (temperature < 0 || temperature > 2)) {
return { ok: false, error: 'temperature must be between 0 and 2' };
}
if (maxTokens !== undefined && (maxTokens < 1 || maxTokens > 32768)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Hard max_tokens cap of 32768 may reject valid requests for large-context models

Many modern LLMs (Llama 3, Qwen 2.5, Mistral Large, etc.) support context windows of 128k tokens or more. Capping max_tokens at 32 768 means the endpoint will return a 400 for any client that passes a higher value based on the model's actual capacity — silently breaking a previously-valid use-case. LM Studio and the underlying runtime will already enforce their own context limits, so this pre-validation adds risk without meaningful safety gain. Consider either raising the ceiling to match a realistic upper bound (e.g. 131072) or removing the upper-bound check entirely.

@@ -0,0 +1 @@
export const APP_VERSION = '0.5.0';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: APP_VERSION is defined independently in both packages/server/src/config/version.ts and packages/dashboard/src/config/version.ts. These will silently diverge on the next version bump if only one file is updated. A single source of truth is safer: on the server, process.env.npm_package_version is available at runtime (or import from package.json with assert { type: 'json' }); on the dashboard, a Vite define in vite.config.ts can inject the root package.json version at build time.

@kilo-code-bot

kilo-code-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/server/src/api/routes.ts 106 max_tokens hard cap of 32768 rejects valid requests for models with larger context windows

SUGGESTION

File Line Issue
packages/server/src/config/version.ts 1 APP_VERSION duplicated independently in server and dashboard packages — can drift on future version bumps
Other Observations (not in diff)

The PR description notes that npm audit reported 8 vulnerabilities (5 moderate, 1 high, 2 critical) in the existing dependency tree. No new dependencies were added in this PR, so these are pre-existing, but they warrant a follow-up npm audit fix run before the next release.

Files Reviewed (13 files)
  • README.md — no issues
  • package.json — no issues
  • .github/workflows/agentic-ci.yml — no issues (mkdir -p ci-results fix is correct)
  • packages/dashboard/package.json — no issues
  • packages/dashboard/src/components/Dashboard.tsx — no issues
  • packages/dashboard/src/config/version.ts — 1 issue (version duplication)
  • packages/server/package.json — no issues
  • packages/server/src/api/routes.ts — 1 issue (max_tokens cap)
  • packages/server/src/config/version.ts — 1 issue (version duplication, same as dashboard)
  • packages/server/src/index.ts — no issues (CORS localhost-restriction and 127.0.0.1 bind are correct security improvements)
  • packages/server/src/services/benchmark-state.ts — no issues
  • packages/server/src/services/benchmark.ts — no issues (broken const const patterns correctly fixed)
  • packages/server/src/services/route-advisor.ts — no issues (broken patterns correctly fixed, ResourcePressure type replaces any)

Fix these issues in Kilo Cloud


Reviewed by claude-4.6-sonnet-20260217 · 183,504 tokens

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request bumps the project version to 0.5.0, adds workspace-wide TypeScript checks, and replaces several placeholder environment variable checks with robust validation and logic. It enhances security by binding the server to localhost by default, adding a CORS origin whitelist, and warning when exposed externally. Additionally, a dedicated benchmarkState service is introduced to manage benchmark results. Feedback on the changes includes wrapping the streaming chat endpoint in a try/catch block to handle potential promise rejections, rejecting CORS requests cleanly with callback(null, false) instead of throwing an error, and using a loose inequality check for profile.firstTokenMs to guard against undefined values.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +743 to 757
await orchestrator.sendMessageStream(
req.params.id,
content,
// TODO: Move to env: (token)
const (token) = process.env.(TOKEN) || '';
parsed.value.content,
(token) => {
writeSse(res, { type: 'token', content: token });
},
(fullResponse) => {
res.write(`data: ${JSON.stringify({ type: 'done', content: fullResponse })}\n\n`);
writeSse(res, { type: 'done', content: fullResponse });
res.end();
},
(err) => {
res.write(`data: ${JSON.stringify({ type: 'error', error: String(err) })}\n\n`);
writeSse(res, { type: 'error', error: String(err) });
res.end();
}
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The asynchronous call await orchestrator.sendMessageStream(...) is not wrapped in a try/catch block. If sendMessageStream rejects (for example, if the session ID is invalid or a connection error occurs before streaming starts), it results in an unhandled promise rejection. Since headers have already been written, this can cause the Node.js process to crash or leave the connection hanging.

Wrap the call in a try/catch block to handle any synchronous or asynchronous initialization errors gracefully.

Suggested change
await orchestrator.sendMessageStream(
req.params.id,
content,
// TODO: Move to env: (token)
const (token) = process.env.(TOKEN) || '';
parsed.value.content,
(token) => {
writeSse(res, { type: 'token', content: token });
},
(fullResponse) => {
res.write(`data: ${JSON.stringify({ type: 'done', content: fullResponse })}\n\n`);
writeSse(res, { type: 'done', content: fullResponse });
res.end();
},
(err) => {
res.write(`data: ${JSON.stringify({ type: 'error', error: String(err) })}\n\n`);
writeSse(res, { type: 'error', error: String(err) });
res.end();
}
);
try {
await orchestrator.sendMessageStream(
req.params.id,
parsed.value.content,
(token) => {
writeSse(res, { type: 'token', content: token });
},
(fullResponse) => {
writeSse(res, { type: 'done', content: fullResponse });
res.end();
},
(err) => {
writeSse(res, { type: 'error', error: String(err) });
res.end();
}
);
} catch (err) {
writeSse(res, { type: 'error', error: String(err) });
res.end();
}

Comment on lines +32 to +40
const corsOptions: CorsOptions = {
origin(origin, callback) {
if (!origin || LOCALHOST_ORIGINS.has(origin)) {
callback(null, true);
return;
}
callback(new Error(`CORS origin not allowed: ${origin}`));
},
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

In Express cors middleware, passing an Error object to the callback (callback(new Error(...))) forwards the error to the Express global error handler, which typically results in a 500 Internal Server Error and logs a noisy stack trace.

To reject a CORS request cleanly without throwing a server-side exception, pass false as the second argument: callback(null, false). This blocks the CORS request at the HTTP header level (by not sending the Access-Control-Allow-Origin header) without disrupting the server's execution flow.

Suggested change
const corsOptions: CorsOptions = {
origin(origin, callback) {
if (!origin || LOCALHOST_ORIGINS.has(origin)) {
callback(null, true);
return;
}
callback(new Error(`CORS origin not allowed: ${origin}`));
},
};
const corsOptions: CorsOptions = {
origin(origin, callback) {
if (!origin || LOCALHOST_ORIGINS.has(origin)) {
callback(null, true);
return;
}
callback(null, false);
},
};

Comment on lines +275 to +279
function estimateFirstTokenMs(profile: ModelProfile): number {
if (profile.firstTokenMs !== null) return profile.firstTokenMs;
const tokS = profile.tokSGpu || profile.tokSCpu || 0;
return tokS > 0 ? Math.round(1000 / tokS) : Infinity;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The strict inequality check profile.firstTokenMs !== null does not guard against undefined. If profile.firstTokenMs is optional or missing (resulting in undefined), the function returns undefined instead of falling back to the calculation. This violates the number return type at runtime and can lead to NaN values when performing math operations like Math.round(firstToken).

Using loose inequality profile.firstTokenMs != null (or checking both null and undefined) ensures a safe fallback.

Suggested change
function estimateFirstTokenMs(profile: ModelProfile): number {
if (profile.firstTokenMs !== null) return profile.firstTokenMs;
const tokS = profile.tokSGpu || profile.tokSCpu || 0;
return tokS > 0 ? Math.round(1000 / tokS) : Infinity;
}
function estimateFirstTokenMs(profile: ModelProfile): number {
if (profile.firstTokenMs != null) return profile.firstTokenMs;
const tokS = profile.tokSGpu || profile.tokSCpu || 0;
return tokS > 0 ? Math.round(1000 / tokS) : Infinity;
}

@djimit
djimit merged commit 70aece2 into main Jun 11, 2026
3 checks passed
@djimit
djimit deleted the fix/stabilize-v0.5-routes branch June 11, 2026 23:40
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.

1 participant