From c496ce4119fa7b26349dbb4224032949056b14dd Mon Sep 17 00:00:00 2001 From: Yash Dewasthale Date: Tue, 14 Jul 2026 12:00:19 +0530 Subject: [PATCH] feat: update supercode-cli version and enhance tool handlin - Bumped version to 0.1.63 in package.json. - Improved tool parameter handling in toolParams function to return a default object structure when necessary. - Enhanced nonStreamingRequest to accept tools as an optional parameter and include them in the request body. - Updated ConcentrateService to track tool events during streaming responses. - Simplified chat initialization logic in wakeUpAction for better clarity and efficiency. - Added a new model entry for "Opus 4.8" in the model list. --- apps/supercode-cli/server/package.json | 2 +- .../server/src/cli/ai/concentrate-service.ts | 16 ++++++++++++--- .../server/src/cli/commands/ai/init.ts | 20 +++++-------------- .../src/cli/commands/slashCommands/model.ts | 1 + apps/supercode-cli/server/src/index.ts | 8 ++++++-- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/apps/supercode-cli/server/package.json b/apps/supercode-cli/server/package.json index 3a45e10..dc251bd 100644 --- a/apps/supercode-cli/server/package.json +++ b/apps/supercode-cli/server/package.json @@ -1,6 +1,6 @@ { "name": "supercode-cli", - "version": "0.1.62", + "version": "0.1.63", "description": "AI-powered coding agent CLI", "main": "dist/main.js", "bin": { diff --git a/apps/supercode-cli/server/src/cli/ai/concentrate-service.ts b/apps/supercode-cli/server/src/cli/ai/concentrate-service.ts index 6d420d3..3cc7746 100644 --- a/apps/supercode-cli/server/src/cli/ai/concentrate-service.ts +++ b/apps/supercode-cli/server/src/cli/ai/concentrate-service.ts @@ -29,7 +29,7 @@ interface NonStreamingResponse { usage?: { prompt_tokens?: number; completion_tokens?: number } } -async function nonStreamingRequest(modelName: string, system: string, messages: Array<{ role: string; content: string }>): Promise { +async function nonStreamingRequest(modelName: string, system: string, messages: Array<{ role: string; content: string }>, tools?: any): Promise { const body: Record = { model: modelName, messages: system ? [{ role: "system", content: system }, ...messages] : messages, @@ -37,6 +37,12 @@ async function nonStreamingRequest(modelName: string, system: string, messages: stream: false, } if (!HIGH_VALUE_MODELS.includes(modelName)) body.max_tokens = 8192 + if (tools && typeof tools === "object") { + body.tools = Object.entries(tools).map(([name, fn]: [string, any]) => ({ + type: "function", + function: { name, description: fn.description || "", parameters: fn.parameters || { type: "object", properties: {} } }, + })) + } const res = await fetchWithRetry(`${BASE_URL}/chat/completions`, { method: "POST", headers: { Authorization: `Bearer ${CONCENTRATE_API_KEY}`, "Content-Type": "application/json" }, @@ -309,6 +315,7 @@ export class ConcentrateService { }) let toolChunkCount = 0 + let sawToolEvents = false // Iterate fullStream to surface reasoning chunks alongside text. for await (const event of result.fullStream) { if (event.type === "text-delta") { @@ -318,14 +325,17 @@ export class ConcentrateService { onChunk?.(event.text) } else if (event.type === "reasoning-delta") { if (event.text) onReasoning?.(event.text) + } else if (event.type === "tool-call" || event.type === "tool-result") { + sawToolEvents = true } } // console.error(`[d] tools streamed ${toolChunkCount} chunks resp="${fullResponse}"`) // Same empty-stream fallback as non-tools path — ConcentrateAI's // Novita proxy intermittently drops content on streaming requests. - if (!fullResponse.trim()) { - const nonStreamData = await nonStreamingRequest(this.modelName, system, nonSystemMessages.map((m: any) => ({ role: m.role, content: typeof m.content === "string" ? m.content : JSON.stringify(m.content) }))) + // Skip if the model made tool calls (tool-text-only responses are valid). + if (!fullResponse.trim() && !sawToolEvents) { + const nonStreamData = await nonStreamingRequest(this.modelName, system, nonSystemMessages.map((m: any) => ({ role: m.role, content: typeof m.content === "string" ? m.content : JSON.stringify(m.content) })), tools) const content = nonStreamData?.choices?.[0]?.message?.content ?? "" if (content) { onChunk?.(content) diff --git a/apps/supercode-cli/server/src/cli/commands/ai/init.ts b/apps/supercode-cli/server/src/cli/commands/ai/init.ts index c58165c..07b99dd 100644 --- a/apps/supercode-cli/server/src/cli/commands/ai/init.ts +++ b/apps/supercode-cli/server/src/cli/commands/ai/init.ts @@ -74,26 +74,16 @@ export const wakeUpAction = async (resumeId: string | null = null) => { } if (stored) { - switch (stored.mode) { - case "agent": - await startAgentChat(stored.provider, stored.model, resumeId, workspaceInfo ?? undefined) - break - default: - await startChat(stored.provider, stored.model, resumeId, workspaceInfo ?? undefined, stored.mode) - break + if (resumeId && stored.mode === "agent") { + await startAgentChat(stored.provider, stored.model, resumeId, workspaceInfo ?? undefined) + } else { + await startChat(stored.provider, stored.model, resumeId, workspaceInfo ?? undefined, "chat") } return } const defaults = await saveCliConfig({}) - switch (defaults.mode) { - case "agent": - await startAgentChat(defaults.provider, defaults.model, resumeId, workspaceInfo ?? undefined) - break - default: - await startChat(defaults.provider, defaults.model, resumeId, workspaceInfo ?? undefined, defaults.mode) - break - } + await startChat(defaults.provider, defaults.model, resumeId, workspaceInfo ?? undefined, "chat") } export const supercodeInit = new Command("init") diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts index ca5a97f..cce6c92 100644 --- a/apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts @@ -18,6 +18,7 @@ const MODELS: ModelEntry[] = [ { value: "kimi-k2-6", label: "Kimi K2.6", provider: "concentrateai", cost: "0.8x", desc: "Long context" }, { value: "deepseek-v4-flash", label: "DeepSeek V4 Flash", provider: "concentrateai", cost: "1.0x", desc: "Fast & capable" }, { value: "minimax-m3", label: "MiniMax M3", provider: "concentrateai", cost: "0.5x", desc: "Fast & smart" }, + { value: "anthropic/claude-opus-4-8", label: "Opus 4.8", provider: "concentrateai", cost: "40x", desc: "Deep reasoning" }, { value: "anthropic/claude-opus-4-8", label: "Opus 4.8", provider: "mergedev", cost: "40x", desc: "Via Merge Dev" }, { value: "gemini-2.5-flash", label: "Gemini 2.5 Flash", provider: "google", cost: "2.0x", desc: "Smart & fast" }, { value: "gemini-2.5-pro", label: "Gemini 2.5 Pro", provider: "google", cost: "4.0x", desc: "Deep reasoning" }, diff --git a/apps/supercode-cli/server/src/index.ts b/apps/supercode-cli/server/src/index.ts index be0366b..8de3f83 100644 --- a/apps/supercode-cli/server/src/index.ts +++ b/apps/supercode-cli/server/src/index.ts @@ -14,8 +14,11 @@ import { writeFileSync, unlinkSync } from "fs" import { randomUUID } from "crypto" function toolParams(fn: any): object { - const raw = fn.inputSchema ?? fn.parameters ?? {} - return raw && typeof raw === "object" && "_def" in raw ? {} : raw + const raw = fn.inputSchema ?? fn.parameters + if (!raw || (typeof raw === "object" && "_def" in raw)) { + return { type: "object", properties: {} } + } + return raw } loadEnvOnce() @@ -738,6 +741,7 @@ app.post("/api/ai/chat", async (req, res) => { } const finishReason = data.choices?.[0]?.finish_reason if (finishReason === "tool_calls") { + sawToolCalls = true for (const [, call] of Object.entries(pendingToolCalls)) { if (call.name && call.args) { try {