Skip to content

feat: integrate Cortex SDK and enhance Tauri app support: - #258

Open
yashdev9274 wants to merge 1 commit into
mainfrom
supercode-cli
Open

feat: integrate Cortex SDK and enhance Tauri app support:#258
yashdev9274 wants to merge 1 commit into
mainfrom
supercode-cli

Conversation

@yashdev9274

@yashdev9274 yashdev9274 commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Description

  • Added Cortex SDK for unified TypeScript access to AI models and tools, streamlining development for AI agents.
  • Updated .gitignore to include Tauri app directories for cleaner builds.
  • Enhanced package.json scripts for easier development and building of the Jarvis app.
  • Implemented timeout handling for upstream requests in the server proxy service to prevent hanging requests.
  • Improved token budget management with dynamic daily limits and updated messaging for user feedback.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactor (no functional changes)

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

  • bun test passes
  • bun run typecheck passes
  • bun run lint passes (if applicable)

Checklist:

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works

Summary by CodeRabbit

  • New Features

    • Added voice chat with speech capture, synthesized responses, audio playback, and interruption support.
    • Added support for repository actions initiated through chat, with results summarized afterward.
    • Improved streaming responses by recognizing embedded tool calls and handling partial content.
    • Added configurable daily token budgets with clearer usage indicators.
  • Bug Fixes

    • Improved timeout, retry, and error handling for AI and voice requests.
    • Prevented incomplete streamed responses from failing silently.
  • Documentation

    • Added SDK integration and usage documentation.

- Added Cortex SDK for unified TypeScript access to AI models and tools, streamlining development for AI agents.
- Updated .gitignore to include Tauri app directories for cleaner builds.
- Enhanced package.json scripts for easier development and building of the Jarvis app.
- Implemented timeout handling for upstream requests in the server proxy service to prevent hanging requests.
- Improved token budget management with dynamic daily limits and updated messaging for user feedback.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
supercli Ready Ready Preview Aug 10, 2026 1:10pm
supercli-client Ready Ready Preview Aug 10, 2026 1:10pm
supercli-docs Ready Ready Preview Aug 10, 2026 1:10pm

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The PR adds streamed tool-call parsing, timeout and error handling, voice chat and TTS APIs, configurable token budgets, Jarvis commands, dependency pinning, and Cortex SDK documentation.

Changes

Server AI and voice capabilities

Layer / File(s) Summary
Embedded tool-call parsing
apps/supercode-cli/server/src/lib/embedded-tool-calls.ts, apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts
The new parser handles bracketed, XML, and validated JSON tool calls across streamed chunks. Tests cover chunking, malformed input, unknown tools, and nested JSON.
Upstream streaming and fallback flow
apps/supercode-cli/server/src/index.ts
ConcentrateAI and Supercode Cloud streams now use timeouts, parse embedded calls, handle failures, flush pending content, retry empty responses, and report tool_calls.
Proxy timeout and stream error handling
apps/supercode-cli/server/src/cli/ai/server-proxy-service.ts
ServerProxyService.request now propagates aborts, enforces a safety timeout, cleans up resources, and surfaces stream errors.
Voice API and conversation client
apps/supercode-cli/server/src/index.ts, apps/supercode-cli/server/src/voice/voiceChatClient.ts
The server adds authenticated TTS and voice-chat endpoints. The client captures speech, maintains message history, plays audio, and handles recoverable errors.
Chat guidance and token-budget reporting
apps/supercode-cli/server/src/cli/ai/chat/chat.ts, apps/supercode-cli/server/src/lib/token-budget.ts, apps/supercode-cli/server/src/cli/commands/slashCommands/token-limit.ts
Chat mode requires tool use for repository actions. Budget displays and errors use the configurable daily token budget.
Server package configuration
apps/supercode-cli/server/package.json
The zod dependency is pinned to 3.25.

Jarvis development tooling

Layer / File(s) Summary
Jarvis scripts and generated files
package.json, .gitignore
Root scripts invoke Jarvis development, build, and Tauri commands. Ignore rules exclude generated Jarvis artifacts.

Cortex SDK specification

Layer / File(s) Summary
SDK architecture and APIs
cortex-sdk.md
The document defines gateway, Agent Handler, Composio, and SupercodeAgent APIs, integrations, configuration, and examples.
SDK implementation and integration plan
cortex-sdk.md
The document specifies implementation phases, publishing work, monorepo integration, scope exclusions, examples, and glossary terms.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VoiceChatClient
  participant VoiceChatEndpoint
  participant ElevenLabsTTS
  participant AudioPlayer
  VoiceChatClient->>VoiceChatEndpoint: send conversation messages
  VoiceChatEndpoint->>VoiceChatClient: return reply
  VoiceChatClient->>ElevenLabsTTS: request reply audio
  ElevenLabsTTS->>VoiceChatClient: return MP3
  VoiceChatClient->>AudioPlayer: play reply audio
Loading

Possibly related PRs

Poem

A rabbit hears the stream run clear,
Tool calls hop from chunk to ear.
Voice replies now softly play,
Budgets guide the tokens’ way.
Jarvis builds, while docs take flight—
SDK paths glow in carrot light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.82% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: Cortex SDK integration and improved Tauri app support.
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.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch supercode-cli

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@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: 3

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🟠 Major comments (26)
apps/supercode-cli/server/src/cli/ai/server-proxy-service.ts-182-211 (1)

182-211: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not suppress callback failures.

The catch handles both malformed JSON and exceptions from onChunk, onReasoning, and onToolCall. If a callback fails, this code silently continues and can return a successful response. Limit the catch to JSON.parse, then let callback failures propagate.

Proposed fix
-          try {
-            const event = JSON.parse(trimmed)
-            switch (event.type) {
+          let event: any
+          try {
+            event = JSON.parse(trimmed)
+          } catch {
+            continue
+          }
+          switch (event.type) {
               case "text":
                 fullResponse += event.content
                 onChunk?.(event.content)
                 break
+            // Keep the remaining cases unchanged.
-            }
-          } catch { /* skip malformed */ }
🤖 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 `@apps/supercode-cli/server/src/cli/ai/server-proxy-service.ts` around lines
182 - 211, Update the event-processing loop around JSON.parse so the catch only
handles malformed JSON parsing; separate parsing from event dispatch before the
switch. Let failures from onChunk, onReasoning, and onToolCall propagate to the
caller instead of being swallowed, while preserving the existing skip behavior
for invalid JSON.
apps/supercode-cli/server/src/voice/voiceChatClient.ts-119-121 (1)

119-121: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Bound the conversation history.

The default maxRounds = 0 permits unlimited turns. Every request sends the complete history. Long sessions will eventually exceed request or model context limits. Preserve the system message and retain a fixed number of recent turns before the next request.

Also applies to: 136-140

🤖 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 `@apps/supercode-cli/server/src/voice/voiceChatClient.ts` around lines 119 -
121, Update the conversation-building logic around the ChatMessage history and
default maxRounds so each request preserves the system message while retaining
only a fixed number of most recent turns; ensure maxRounds = 0 no longer permits
unlimited history, and apply the same bounded-history behavior to the additional
history handling around the referenced request path.
apps/supercode-cli/server/src/voice/voiceChatClient.ts-22-33 (1)

22-33: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add a bounded timeout to the voice server calls.

chatWithLLM() awaits /api/voice/chat without a signal, so a stalled server can hold the voice loop on chatWithLLM() for an unbounded time. Use a shared bounded fetch helper for both /api/voice/chat and /api/voice/tts, and handle abort failures as recoverable fallbacks where needed.

🤖 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 `@apps/supercode-cli/server/src/voice/voiceChatClient.ts` around lines 22 - 33,
Introduce a shared bounded fetch helper in voiceChatClient.ts and use it in both
chatWithLLM() at lines 22-33 and the TTS call at lines 56-63 for /api/voice/chat
and /api/voice/tts; pass an abort signal with the configured timeout, and handle
timeout/abort failures as recoverable fallbacks where each voice flow requires
it.
apps/supercode-cli/server/src/voice/voiceChatClient.ts-135-140 (1)

135-140: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Commit the user turn only after the chat request succeeds.

A failed chatWithLLM call leaves the user message in messages. The next successful request then includes an unpaired prior turn and changes the conversation unexpectedly. Build request messages separately, then append both messages only after a reply is received.

Proposed fix
-      messages.push({ role: "user", content: userText })
+      const userMessage: ChatMessage = { role: "user", content: userText }
+      const requestMessages = [...messages, userMessage]

       // 2. Get LLM reply
-      const reply = await chatWithLLM(messages)
-      messages.push({ role: "assistant", content: reply })
+      const reply = await chatWithLLM(requestMessages)
+      messages.push(userMessage, { role: "assistant", content: reply })
🤖 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 `@apps/supercode-cli/server/src/voice/voiceChatClient.ts` around lines 135 -
140, Update the conversation flow around chatWithLLM so the user message is
included in a separate request-messages collection without mutating messages
before the request succeeds. After chatWithLLM returns successfully, append both
the user message and assistant reply to messages together, preserving the
existing roles and content.
apps/supercode-cli/server/src/index.ts-1021-1029 (1)

1021-1029: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

The ConcentrateAI and Supercode Cloud streaming branches are duplicated and now diverge. Both branches implement the same SSE read loop, pending-tool-call accumulation, parser flush, non-streaming fallback, and token accounting. The code was copied, so every defect exists twice, and the two copies have already drifted apart in flush placement and in handling of flushed.calls. Extract the shared loop into one helper so a single fix applies to both providers.

  • apps/supercode-cli/server/src/index.ts#L1021-L1029: replace the break in the catch block with a flag, so the handler still reaches the parser flush, the fallback, and the finish event.
  • apps/supercode-cli/server/src/index.ts#L1274-L1279: apply the identical break fix to the Supercode Cloud catch block.
  • apps/supercode-cli/server/src/index.ts#L1004-L1019: move the pending-tool-call flush out of the while loop so it runs once after the stream ends.
  • apps/supercode-cli/server/src/index.ts#L1259-L1272: move the mirrored pending-tool-call flush out of its while loop as well.
  • apps/supercode-cli/server/src/index.ts#L1091-L1092: assign inputTokens and outputTokens only when fbData.usage is present; otherwise estimate.
  • apps/supercode-cli/server/src/index.ts#L1325-L1326: apply the identical token-accounting fix to the Supercode Cloud fallback.

After the fixes land, extract the common streaming logic into a single function that takes the upstream URL, headers, and body. That removes the drift permanently.

🤖 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 `@apps/supercode-cli/server/src/index.ts` around lines 1021 - 1029,
apps/supercode-cli/server/src/index.ts lines 1021-1029 and 1274-1279: update
both streaming catch blocks to set an abort flag instead of breaking, allowing
parser flush, fallback handling, and the finish event to run. Lines 1004-1019
and 1259-1272: move pending-tool-call flushing outside each read loop so it
executes once after streaming ends. Lines 1091-1092 and 1325-1326: use
fbData.usage for token counts only when present; otherwise estimate. Extract the
duplicated ConcentrateAI and Supercode Cloud streaming logic, including SSE
reading, accumulation, flushing, fallback, and accounting, into one helper
accepting the upstream URL, headers, and body, then route both branches through
it.
apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts-29-38 (1)

29-38: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The split-chunk test only splits the closing marker, not the opening marker.

Chunks here are "I'll check.", "[TOOL_CALL]\n...\n[/", "TOOL_CALL]". The opener arrives whole inside one chunk. The parser handles that path.

No test splits an opener across chunks. That path is broken in apps/supercode-cli/server/src/lib/embedded-tool-calls.ts (see the comment on Lines 96-116 there): the partial opener is emitted as text and the tool call is lost.

Add a test for the opener split so the regression is caught.

💚 Proposed test to add
test("square bracket opener split across two chunks", () => {
  const out = drain(
    parseStreamedContent(),
    ["I'll check.[TOOL_", "CALL]\nrun_command --command=\"git diff\"\n[/TOOL_CALL]"],
  )
  expect(out.text).toBe("I'll check.")
  expect(out.calls).toEqual([
    { name: "run_command", args: { command: "git diff" }, id: "" },
  ])
})

test("xml opener split across two chunks", () => {
  const out = drain(
    parseStreamedContent(),
    ["Sure.<tool_", "call><invoke name=\"read_file\"><parameter name=\"path\">/x</parameter></invoke></tool_call>"],
  )
  expect(out.text).toBe("Sure.")
  expect(out.calls.length).toBe(1)
})
🤖 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 `@apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts`
around lines 29 - 38, Add regression coverage in the streamed content parser
tests for opening markers split across chunks: add square-bracket and XML opener
cases using split chunk boundaries, then assert the preceding text remains
unchanged and the tool call is parsed rather than emitted or lost. Use the
existing drain and parseStreamedContent helpers and preserve the established
call assertions.
apps/supercode-cli/server/src/index.ts-1914-1914 (1)

1914-1914: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Remove or use the dead token and timing variables.

chatStart at Lines 1914 and 1958 is assigned and never read. inputTokens and outputTokens at Lines 1946-1947 and 1992-1993 are computed and never read.

Both /api/voice/chat provider branches therefore perform no usage recording. The other chat routes in this file record usage and enforce the daily budget. These endpoints do not.

Either record usage for the authenticated user, matching the other routes, or delete the unused variables.

Also applies to: 1946-1947, 1958-1958, 1992-1993

🤖 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 `@apps/supercode-cli/server/src/index.ts` at line 1914, Update both provider
branches in the /api/voice/chat handler to record token usage and timing for the
authenticated user, reusing the existing usage-recording and daily-budget
enforcement pattern from the other chat routes; ensure chatStart, inputTokens,
and outputTokens are consumed by that flow rather than left unused.
apps/supercode-cli/server/src/lib/embedded-tool-calls.ts-193-224 (1)

193-224: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Embedded tool calls carry an empty id, so parallel calls cannot be correlated.

Both parseXmlBlock and parseSquareBlock set id: "". tryParseJsonDescriptor does the same at Line 287. apps/supercode-cli/server/src/index.ts forwards this directly as toolCallId: call.id at Lines 964 and 1214-1219.

Structured delta.tool_calls paths forward a real upstream id. Embedded calls do not. If one turn produces two embedded tool calls, the client receives two events with toolCallId: "" and cannot match results to calls.

Generate a unique id in the parser.

🐛 Proposed fix to generate ids
+let embeddedCallSeq = 0
+
+function nextCallId(): string {
+  embeddedCallSeq += 1
+  return `embedded_${Date.now().toString(36)}_${embeddedCallSeq}`
+}
+
 /** XML shape: <invoke name="X"><parameter name="Y">value</parameter></invoke> */

Then replace each id: "" with id: nextCallId() in parseXmlBlock, parseSquareBlock, and tryParseJsonDescriptor.

Update the tests in apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts that assert id: "" if you apply this change.

🤖 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 `@apps/supercode-cli/server/src/lib/embedded-tool-calls.ts` around lines 193 -
224, Generate a unique identifier for every embedded tool call by replacing the
empty IDs in parseXmlBlock, parseSquareBlock, and tryParseJsonDescriptor with
the shared nextCallId() helper. Update embedded-tool-calls tests that currently
expect id: "" while preserving parsing behavior and ensuring separate calls
receive distinct IDs.
apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts-124-131 (1)

124-131: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

This test does not exercise the unknown-tool rejection branch.

The fixture is { "name": "not_a_tool", ... } with a space after {. findJsonDescriptorStart matches /\{"name"\s*:/, which requires "name" immediately after {. The scanner therefore returns -1 and treats the whole string as prose.

The knownTools.has(name) check at apps/supercode-cli/server/src/lib/embedded-tool-calls.ts Line 284 is never reached. If that check were removed, this test would still pass.

Remove the space so the descriptor is detected and then rejected by name.

💚 Proposed fix
 test("bare json with unknown tool name is left as prose", () => {
   const out = drain(
     parseStreamedContent(),
-    ['here is some { "name": "not_a_tool", "parameters": {"q":1} } prose'],
+    ['here is some {"name":"not_a_tool","parameters":{"q":1}} prose'],
   )
   expect(out.text).toContain("prose")
+  expect(out.text).toContain("not_a_tool")
   expect(out.calls.length).toBe(0)
 })
🤖 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 `@apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts`
around lines 124 - 131, Update the JSON fixture in the “bare json with unknown
tool name is left as prose” test to place the name field immediately after the
opening brace, allowing findJsonDescriptorStart to detect it and exercise the
knownTools.has(name) rejection branch while preserving the expected prose output
and zero tool calls.
apps/supercode-cli/server/src/index.ts-1968-1981 (1)

1968-1981: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

The OpenRouter request has no timeout.

The ConcentrateAI branch passes signal: AbortSignal.timeout(30_000) at Line 1934. This branch passes no signal. If OpenRouter stalls, the handler holds the request until the default socket timeout.

Apply the same timeout for consistent behavior across providers.

🐛 Proposed fix
           temperature: 0.7,
           stream: false,
         }),
+        signal: AbortSignal.timeout(30_000),
       })
🤖 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 `@apps/supercode-cli/server/src/index.ts` around lines 1968 - 1981, Add the
same 30-second timeout signal used by the ConcentrateAI request to the
OpenRouter fetch call in the surrounding handler. Update the fetch options for
the OpenRouter request while preserving its existing method, headers, body, and
response handling.
apps/supercode-cli/server/src/lib/embedded-tool-calls.ts-118-134 (1)

118-134: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

An unclosed {"name": sequence buffers the rest of the stream and then discards it.

tryParseJsonDescriptor returns incomplete for every input where findBalancedClose fails. pump then stores the whole tail in pending. Each later push re-appends and re-fails, so no text is ever emitted again for that turn. flush() drops the buffer, so the user loses all remaining content.

The comment at Lines 270-271 states that the wait is capped, but no cap exists in tryParseJsonDescriptor or in pump.

Add the cap. If the held descriptor exceeds a byte limit, treat it as prose: emit the { and rescan from nextOpen + 1.

🐛 Proposed fix to bound the incomplete-descriptor buffer
+const MAX_PENDING_JSON = 8192
+
         if (parsed?.incomplete) {
-          // Descriptor start but not yet closed — hold the tail for next chunk.
-          pending = block.slice(nextOpen)
-          i = block.length
-          break
+          const tailLen = block.length - nextOpen
+          if (tailLen > MAX_PENDING_JSON) {
+            // Never closed within the cap — treat as prose and rescan.
+            i = nextOpen + 1
+            continue
+          }
+          // Descriptor start but not yet closed — hold the tail for next chunk.
+          pending = block.slice(nextOpen)
+          i = block.length
+          break
         }
🤖 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 `@apps/supercode-cli/server/src/lib/embedded-tool-calls.ts` around lines 118 -
134, Bound incomplete descriptor buffering in tryParseJsonDescriptor/pump using
the documented byte limit. When the held {"name": sequence exceeds that limit,
treat it as prose by emitting the opening brace and resume scanning from
nextOpen + 1; otherwise preserve the existing pending behavior for genuinely
incomplete descriptors and ensure flush does not discard unbounded content.
apps/supercode-cli/server/src/lib/embedded-tool-calls.ts-169-176 (1)

169-176: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

flush() always returns empty and discards buffered prose.

remaining is assigned but never read. Both branches return the same empty ParsedBlock. The function therefore drops everything held in buf, including plain trailing prose that contains no marker at all.

This contradicts the documented contract at Lines 76-77 ("release whatever is left (normally just trailing prose)").

It also makes the consumer code dead. In apps/supercode-cli/server/src/index.ts, Lines 1033-1042 and Lines 1346-1350 read flushed.text and flushed.calls, which can never be non-empty.

Distinguish a truncated marker from trailing prose. Drop only the marker case.

🐛 Proposed fix for `flush()`
     flush(): ParsedBlock {
       const remaining = buf
       buf = ""
       if (!remaining) return { text: "", calls: [], pending: "" }
-      // No more chunks coming: if the tail still holds an unclosed opener it
-      // is a truncated marker — drop it rather than leak raw markup.
-      return { text: "", calls: [], pending: "" }
+      // No more chunks coming: if the tail still holds an unclosed opener it
+      // is a truncated marker — drop it rather than leak raw markup.
+      const hasOpener =
+        remaining.includes("[TOOL_CALL]") ||
+        remaining.includes("[tool_call]") ||
+        remaining.includes("<tool_call>") ||
+        findJsonDescriptorStart(remaining, 0) !== -1
+      if (hasOpener) return { text: "", calls: [], pending: "" }
+      return { text: remaining, calls: [], pending: "" }
     },
🤖 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 `@apps/supercode-cli/server/src/lib/embedded-tool-calls.ts` around lines 169 -
176, Update flush() to process the buffered remaining content instead of
returning an always-empty ParsedBlock. Preserve the existing behavior for an
unclosed marker opener by dropping that truncated marker, but return trailing
plain prose in text and emit any complete buffered calls through the same
parsing logic used by the parser; keep the consumer paths reading flushed.text
and flushed.calls functional.
apps/supercode-cli/server/src/index.ts-1850-1877 (1)

1850-1877: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

/api/voice/tts has no input length cap and no upstream timeout.

text is validated only as a non-empty string. A caller can submit a very large body. ElevenLabs bills per character, so an authenticated caller can consume the account's credits in a single request.

The fetch at Line 1862 also passes no signal. If ElevenLabs stalls, the handler holds the connection until the default socket timeout. The streaming routes in this file use AbortSignal.timeout(UPSTREAM_TIMEOUT_MS) for exactly this reason.

🛡️ Proposed fix
+const TTS_MAX_CHARS = 5000
+
     const { text } = req.body
     if (!text || typeof text !== "string") {
       res.status(400).json({ error: "text is required" })
       return
     }
+    if (text.length > TTS_MAX_CHARS) {
+      res.status(400).json({ error: `text exceeds ${TTS_MAX_CHARS} characters` })
+      return
+    }
           output_format: "mp3_44100_128",
         }),
+        signal: AbortSignal.timeout(30_000),
       },
     )
🤖 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 `@apps/supercode-cli/server/src/index.ts` around lines 1850 - 1877, Update the
/api/voice/tts handler around the text validation and ElevenLabs fetch to
enforce the existing request-size convention, rejecting text that exceeds the
allowed character limit before calling the provider. Pass
AbortSignal.timeout(UPSTREAM_TIMEOUT_MS) in the fetch options, matching the
streaming routes’ upstream timeout behavior.
apps/supercode-cli/server/src/index.ts-1344-1350 (1)

1344-1350: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The Supercode Cloud flush drops tool calls and runs after the emptiness checks.

This block diverges from the equivalent ConcentrateAI block at Lines 1033-1042 in two ways.

First, it reads only flushed.text. It ignores flushed.calls. The ConcentrateAI block emits both. Any tool call released by flush() is lost on this path.

Second, it runs after the non-streaming fallback at Line 1289 and after the last-resort check at Line 1332. Flushed text therefore cannot satisfy !fullContent.trim(). A turn whose remaining content sits in the parser buffer emits the "The model returned an empty response" error and then emits the text.

Move the flush before the emptiness checks and handle flushed.calls.

🐛 Proposed fix

Move this block to immediately after the stream catch at Line 1279, before the token estimation at Line 1282:

+        // Release any trailing prose still held by the embedded-tool-call
+        // parser (narration emitted after the final marker).
+        const flushed = embedded.flush()
+        if (flushed.text) {
+          fullContent += flushed.text
+          res.write(JSON.stringify({ type: "text", content: flushed.text }) + "\n")
+        }
+        for (const call of flushed.calls) {
+          sawToolCalls = true
+          emittedToolCalls = true
+          res.write(JSON.stringify({
+            type: "tool-call",
+            toolName: call.name,
+            args: call.args,
+            toolCallId: call.id,
+          }) + "\n")
+        }
+
         // If streaming didn't include usage data, estimate from content.

Then delete the block at Lines 1344-1350.

This currently has no visible effect because flush() in apps/supercode-cli/server/src/lib/embedded-tool-calls.ts always returns empty. It becomes a live defect once that function is fixed.

🤖 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 `@apps/supercode-cli/server/src/index.ts` around lines 1344 - 1350, Move the
`embedded.flush()` block to immediately after the stream `catch`, before token
estimation and all emptiness checks, so flushed text contributes to
`fullContent` before fallback validation. In that block, preserve the existing
text emission and also emit every entry in `flushed.calls` using the same
tool-call response format as the equivalent ConcentrateAI flush block. Remove
the later flush block to avoid duplicate processing.
apps/supercode-cli/server/src/lib/token-budget.ts-6-6 (1)

6-6: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Reject non-positive and non-finite budget overrides.

Number(...) || 1_000_000 accepts values such as -1 and Infinity. With -1, checkDailyTokenBudget rejects every request because used >= -1. The CLI also passes the negative value to progressBar, where a positive usage value can produce a negative repeat count and throw. Accept only a positive safe integer, then use the fallback or fail startup.

Proposed validation
-export const DAILY_BUDGET_TOKENS = Number(process.env.SUPERCODE_DAILY_TOKEN_BUDGET) || 1_000_000
+const configuredDailyBudget = Number(process.env.SUPERCODE_DAILY_TOKEN_BUDGET)
+export const DAILY_BUDGET_TOKENS =
+  Number.isSafeInteger(configuredDailyBudget) && configuredDailyBudget > 0
+    ? configuredDailyBudget
+    : 1_000_000
🤖 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 `@apps/supercode-cli/server/src/lib/token-budget.ts` at line 6, Update the
DAILY_BUDGET_TOKENS initialization and its checkDailyTokenBudget usage to accept
an override only when it is finite, positive, and a safe integer; otherwise use
the existing 1,000,000 fallback or fail startup consistently. Ensure invalid
values such as -1, Infinity, NaN, and unsafe integers never reach progressBar or
budget comparisons.
cortex-sdk.md-94-117 (1)

94-117: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Use distinct names for the default model and model selector.

The examples use gateway.model("...") and also pass mdGateway.model as a LanguageModel. A normal object cannot expose the same property as both a model object and a callable selector. Use separate names such as .model and .getModel(id), then update the examples and types.

Proposed contract
- Returns { model(id?): LanguageModel, listModels(): ModelInfo[] }
+ Returns {
+   model: LanguageModel
+   getModel(id?: string): LanguageModel
+   listModels(): Promise<ModelInfo[]>
+ }

Also applies to: 392-397

🤖 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 `@cortex-sdk.md` around lines 94 - 117, Separate the configured default model
from model selection in the gateway API: keep `.model` as the LanguageModel
getter and introduce `.getModel(id)` for selecting a model. Update the examples
around `createGateway`, `gateway.model`, and `mdGateway.model`, plus the
corresponding types and the additional affected usage, so no property is both
callable and a model object.
cortex-sdk.md-24-30 (1)

24-30: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make the createGateway() return type consistent.

The table says createGateway() returns a LanguageModel. The examples and Phase 2 use a gateway client with listModels() and model selection. Define and document one GatewayClient return shape.

Also applies to: 117-117, 392-397

🤖 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 `@cortex-sdk.md` around lines 24 - 30, Define a single GatewayClient return
shape for createGateway(), including the LanguageModel access and
listModels/model-selection capabilities used by the examples and Phase 2. Update
the table, examples, and Phase 2 references to consistently document and use
GatewayClient instead of claiming createGateway() directly returns
LanguageModel.
cortex-sdk.md-90-92 (1)

90-92: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Return a promise from listModels().

The examples await gateway.listModels(), but Phase 2 declares listModels(): ModelInfo[]. Model discovery calls an upstream provider and is asynchronous. Use listModels(): Promise<ModelInfo[]> consistently.

Also applies to: 392-395

🤖 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 `@cortex-sdk.md` around lines 90 - 92, Update the Phase 2 `listModels`
declaration to return `Promise<ModelInfo[]>` instead of `ModelInfo[]`, matching
the asynchronous upstream model discovery and the existing `await
gateway.listModels()` examples. Apply the same return type consistently in the
additional declaration referenced by the comment.
cortex-sdk.md-346-353 (1)

346-353: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Declare the OAuth launcher dependency.

Phase 4 requires the open package, but the package manifest does not declare it. Add open as a direct runtime dependency or replace it with a runtime-neutral browser-launch adapter.

Also applies to: 442-445

🤖 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 `@cortex-sdk.md` around lines 346 - 353, Add the OAuth launcher dependency
required by Phase 4 to the manifest’s runtime dependencies, preferably declaring
open directly; alternatively, replace the launcher usage with an existing
runtime-neutral browser-launch adapter and update the OAuth flow accordingly.
cortex-sdk.md-217-227 (1)

217-227: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Move tool-loop guards into an enforceable API.

stepCountIs(8), sentinel injection, and repetition detection run at the orchestration/request layer. SupercodeAgent only exposes .model and .tools, while the consumer calls streamText directly. Add an agent.streamText or middleware API, or document these guards as consumer responsibilities.

Also applies to: 290-295, 454-488

🤖 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 `@cortex-sdk.md` around lines 217 - 227, Update SupercodeAgent and its usage
documentation so tool-loop guards—stepCountIs(8), sentinel injection, and
repetition detection—are enforced through an agent.streamText or equivalent
middleware API; alternatively, explicitly document them as consumer
responsibilities wherever direct streamText usage is shown. Ensure the behavior
is consistent across the referenced orchestration and example sections.
cortex-sdk.md-220-224 (1)

220-224: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject duplicate tool names during merging.

{ ...agent.tools } and the merged-tools contract silently overwrite duplicate names. Agent Handler and Composio can then expose the wrong implementation under one tool name. Detect duplicates and throw, or namespace tools before returning .tools.

Also applies to: 473-474

🤖 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 `@cortex-sdk.md` around lines 220 - 224, Update the tool-merging logic that
produces agent.tools to reject duplicate tool names instead of silently
overwriting them; detect collisions between Agent Handler and Composio tools and
throw an error, or namespace the conflicting tools before returning .tools.
Ensure the same behavior is applied to the related merged-tools usage.
cortex-sdk.md-164-168 (1)

164-168: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Expose authentication for server-proxied Composio mode.

createComposio({ serverUrl }) does not expose an access token or token provider. The existing server contract requires an Authorization bearer token. A standalone SDK cannot assume CLI token storage.

Add accessToken or getAccessToken to the configuration and document its use.

Proposed configuration
- Accepts { apiKey?, serverUrl? }
+ Accepts {
+   apiKey?
+   serverUrl?
+   accessToken?: string | (() => Promise<string>)
+ }

Also applies to: 433-448

🤖 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 `@cortex-sdk.md` around lines 164 - 168, Update the createComposio
configuration examples and corresponding documentation to expose authentication
for server-proxied mode: support an accessToken or getAccessToken alongside
serverUrl, and document that it supplies the required bearer token without
relying on CLI token storage. Apply the same change to the additional
server-proxy configuration section.
cortex-sdk.md-492-505 (1)

492-505: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Add the workspace dependency required by Phase 6.

Phase 6 imports cortex-sdk from apps/supercode-cli/server, but the integration plan only relies on workspace auto-discovery. Add cortex-sdk: "workspace:*" to each consuming package. Workspace discovery alone does not declare the dependency for isolated installs.

As per coding guidelines, internal package dependencies in package.json must use workspace:*.

Also applies to: 536-545

🤖 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 `@cortex-sdk.md` around lines 492 - 505, Add the internal dependency
"cortex-sdk": "workspace:*" to the package.json files for each consuming package
under apps/supercode-cli/server, ensuring every package importing createGateway,
createAgentHandler, or createComposio declares the workspace dependency
explicitly.

Source: Coding guidelines

cortex-sdk.md-56-60 (1)

56-60: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use one public type vocabulary.

The document uses AgentHandlerConfig and AgentHandlerOptions, ComposioConfig and ComposioOptions, and ConnectionStatus and ConnectorStatus. Choose canonical names and update every API and implementation phase before creating the exported types.

Also applies to: 64-66, 369-370, 461-466

🤖 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 `@cortex-sdk.md` around lines 56 - 60, Standardize the public type vocabulary
throughout the document and implementation plan by selecting one canonical name
for each pair: AgentHandlerConfig/AgentHandlerOptions,
ComposioConfig/ComposioOptions, and ConnectionStatus/ConnectorStatus. Update
every API reference, phase description, and exported-type definition
consistently before introducing the public types.
cortex-sdk.md-279-288 (1)

279-288: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Bound retries before tool side effects.

A valid tool-only response can contain no text. If the fallback treats empty text as an empty stream, it can replay the request after a tool call was emitted. That can duplicate non-idempotent actions. Do not retry after a tool call or partial output. Define an idempotency policy for retried requests.

🤖 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 `@cortex-sdk.md` around lines 279 - 288, Update the Empty-Stream Fallback
behavior in the SDK documentation to prohibit retries once a tool call or any
partial output has been emitted, including valid tool-only responses with no
text. Define the idempotency policy for requests eligible for the non-streaming
fallback, ensuring retries are limited to safely repeatable requests and cannot
duplicate non-idempotent tool actions.
cortex-sdk.md-323-340 (1)

323-340: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Add CommonJS exports for subpaths.

The root export has a require condition, but ./gateway, ./agent-handler, and ./composio only define types and import. With the planned CJS build, require("cortex-sdk/gateway") will fail package exports resolution.

Proposed export entry
"./gateway": {
  "types": "./dist/gateway/index.d.ts",
- "import": "./dist/gateway/index.js"
+ "import": "./dist/gateway/index.js",
+ "require": "./dist/gateway/index.cjs"
}
🤖 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 `@cortex-sdk.md` around lines 323 - 340, Update the package exports entries for
"./gateway", "./agent-handler", and "./composio" to include their CommonJS
require targets alongside the existing types and import conditions, using the
corresponding generated .cjs files. Preserve the root export and existing
ESM/type targets.
🟡 Minor comments (10)
apps/supercode-cli/server/package.json-72-72 (1)

72-72: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use an exact patch version for Zod.

In package manifests, 3.25 is expanded to the 3.25.x range, so it can resolve below 3.25.2. If this dependency should be pinned to 3.25.2, update apps/supercode-cli/server/package.json to "3.25.2" and update the Bun lockfile.

🤖 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 `@apps/supercode-cli/server/package.json` at line 72, Update the zod dependency
in the server package manifest from the partial version 3.25 to the exact
version 3.25.2, then regenerate the Bun lockfile so it records the pinned
version consistently.
apps/supercode-cli/server/src/cli/ai/server-proxy-service.ts-100-103 (1)

100-103: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Reject invalid timeout values.

Line 100 accepts negative and non-finite values from SUPERCODE_REQUEST_TIMEOUT_MS. A negative timeout can abort every proxy request immediately. Use a finite value greater than zero, or fall back to the 120-second default.

Proposed fix
-    const timeoutMs = Number(process.env.SUPERCODE_REQUEST_TIMEOUT_MS) || 120_000
+    const configuredTimeoutMs = Number(process.env.SUPERCODE_REQUEST_TIMEOUT_MS)
+    const timeoutMs =
+      Number.isFinite(configuredTimeoutMs) && configuredTimeoutMs > 0
+        ? configuredTimeoutMs
+        : 120_000
🤖 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 `@apps/supercode-cli/server/src/cli/ai/server-proxy-service.ts` around lines
100 - 103, Update the timeoutMs initialization in the proxy request flow to
accept SUPERCODE_REQUEST_TIMEOUT_MS only when it parses to a finite value
greater than zero; otherwise use the existing 120-second default. Keep the
setTimeout behavior and controller abort handling unchanged.
apps/supercode-cli/server/src/voice/voiceChatClient.ts-1-7 (1)

1-7: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the required filename, aliases, and import groups.

Rename voiceChatClient.ts to voice-chat-client.ts and update its import sites. Replace the relative internal imports with @/ aliases. Separate the Node imports from internal alias imports with a blank line.

As per coding guidelines, "Use kebab-case for non-component filenames" and "Use absolute imports with @/ path aliases ... Order imports ... with blank lines between groups."

🤖 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 `@apps/supercode-cli/server/src/voice/voiceChatClient.ts` around lines 1 - 7,
Rename voiceChatClient.ts to voice-chat-client.ts and update all import sites
accordingly. In the renamed module, replace the relative imports for
getStoredToken, voiceCaptureFlow, and stripForSpeech with the configured `@/`
aliases, keeping Node imports grouped separately above them with a blank line.

Source: Coding guidelines

apps/supercode-cli/server/src/index.ts-1889-1891 (1)

1889-1891: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

The 500 handler returns the raw error string to the caller.

String(error) can include internal paths, upstream hostnames, or stack detail depending on the thrown value. Return a fixed message and log the detail server-side.

🛡️ Proposed fix
   } catch (error) {
-    res.status(500).json({ error: String(error) })
+    console.error("[voice] request failed", error)
+    res.status(500).json({ error: "Internal server error" })
   }

Apply the same change to both handlers.

Also applies to: 2000-2002

🤖 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 `@apps/supercode-cli/server/src/index.ts` around lines 1889 - 1891, Update both
500-error catch handlers near the shown response blocks to stop returning
String(error) in the JSON response; log the caught error server-side, then
return a fixed generic error message to callers while preserving the 500 status.
apps/supercode-cli/server/src/lib/embedded-tool-calls.ts-249-254 (1)

249-254: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The descriptor-start regex rejects whitespace after {.

/\{"name"\s*:/ allows whitespace only before the colon. A model that emits { "name": "run_command", "parameters": { ... } } is not detected, so the descriptor leaks into the chat as prose.

Allow whitespace after the opening brace.

🐛 Proposed fix
-  const re = /\{"name"\s*:/g
+  const re = /\{\s*"name"\s*:/g

This change also affects the test at apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts Line 127, which currently uses { "name": "not_a_tool" ... }. That test passes today only because the space prevents detection, not because the known-tool check rejects it.

🤖 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 `@apps/supercode-cli/server/src/lib/embedded-tool-calls.ts` around lines 249 -
254, Update the descriptor-start regex in findJsonDescriptorStart to allow
optional whitespace between the opening brace and "name", while preserving the
existing whitespace handling before the colon. Update the affected
embedded-tool-calls test so its not_a_tool case verifies rejection by the
known-tool check rather than relying on the preceding space to prevent
detection.
apps/supercode-cli/server/src/cli/ai/chat/chat.ts-365-365 (1)

365-365: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not advertise unavailable web tools.

This note always names Exa and Firecrawl as available. Later, streamAIResponse removes exa_* and firecrawl_* tools when no provider key or MCP connector exists. The model can therefore select a capability that is absent from its tool schema. Build this note after tool filtering, or state that the model must use only tools present in the tool schema.

🤖 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 `@apps/supercode-cli/server/src/cli/ai/chat/chat.ts` at line 365, The Chat Mode
Note in the promptContent construction must not advertise Exa or Firecrawl
unless those tools remain available. Update the flow around streamAIResponse and
its tool filtering so the note is generated after filtering, or dynamically
lists only tools present in the final tool schema while preserving the existing
approval and mandatory tool-use guidance.
apps/supercode-cli/server/src/cli/commands/slashCommands/token-limit.ts-147-150 (1)

147-150: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle budgets below 1,000 tokens.

DAILY_BUDGET_TOKENS accepts values below 1,000, but this branch always divides by 1,000. A budget of 500 displays as 1K budget, and a budget of 1 displays as 0K budget. Add a raw-number branch or reuse formatTokens from apps/supercode-cli/server/src/lib/token-budget.ts.

🤖 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 `@apps/supercode-cli/server/src/cli/commands/slashCommands/token-limit.ts`
around lines 147 - 150, Update the budgetLabel calculation in the token-limit
command to handle DAILY_BUDGET_TOKENS values below 1,000 without rounding them
to an incorrect K value. Add a raw-token branch for sub-1,000 budgets or reuse
formatTokens from token-budget.ts, while preserving the existing M and K
formatting for larger budgets.
cortex-sdk.md-50-54 (1)

50-54: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use one filename for the ConcentrateAI provider.

The package tree uses concentreai.ts, but Phase 2 uses concentrateai.ts. Use one spelling in the tree, implementation phase, exports, and imports.

Also applies to: 378-386

🤖 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 `@cortex-sdk.md` around lines 50 - 54, Standardize the ConcentrateAI provider
filename spelling across the package tree, Phase 2 documentation, exports, and
imports. Use the existing `concentreai.ts` or `concentrateai.ts` spelling
consistently everywhere, including all references to the provider module.
cortex-sdk.md-549-560 (1)

549-560: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use tasks instead of pipeline in the Turborepo block.

This repo uses Turborepo v2 and already defines tasks in turbo.json, so the documentation should add the cortex-sdk build task under tasks instead of the v1 pipeline key.

🤖 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 `@cortex-sdk.md` around lines 549 - 560, Update the documented turbo.json
example to use the existing Turborepo v2 tasks block instead of pipeline, adding
the cortex-sdk build task with its dependsOn and outputs settings under tasks
while preserving the shown configuration.
cortex-sdk.md-269-273 (1)

269-273: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Narrow the ai peer range to the supported major.

This release declares @ai-sdk/mcp v2, which targets the AI SDK v5 tooling, but the peer range also accepts ai v4 and v6. Keep ai v5, or add explicit compatibility CI for v4/v6 before publishing the declared range.

🤖 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 `@cortex-sdk.md` around lines 269 - 273, Update the Vercel AI SDK
peer-dependency documentation to declare only the supported ai v5 major,
removing v4 and v6 from the listed range unless explicit compatibility CI has
been added for them.
🧹 Nitpick comments (5)
apps/supercode-cli/server/src/voice/voiceChatClient.ts (1)

4-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use Bun file APIs for temporary audio.

Replace the synchronous fs write and delete calls with Bun file APIs. This avoids blocking file operations and follows the server runtime standard. Verify the declared Bun target before selecting the cleanup API.

As per coding guidelines, "Prefer Bun.file over Node.js node:fs readFile/writeFile for file operations."

Also applies to: 70-72, 94-100

🤖 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 `@apps/supercode-cli/server/src/voice/voiceChatClient.ts` at line 4, Replace
the synchronous writeFileSync and unlinkSync usage in the voice chat
temporary-audio flow with Bun.file-based asynchronous file operations,
preserving the existing file contents and cleanup behavior. Verify the declared
Bun target and use the compatible Bun cleanup API for deleting the temporary
file.

Source: Coding guidelines

apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts (2)

133-140: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a regression test for an unclosed JSON descriptor.

No test covers content that starts a descriptor and never closes it. That input pins the remainder of the stream in pending and then discards it on flush. See the comment on apps/supercode-cli/server/src/lib/embedded-tool-calls.ts Lines 118-134.

💚 Proposed test to add
test("unclosed json descriptor does not swallow the rest of the stream", () => {
  const out = drain(
    parseStreamedContent(),
    ['{"name":"run_command","parameters":{"command":"x"', " and then a lot more prose follows here"],
  )
  expect(out.text).toContain("prose")
})
🤖 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 `@apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts`
around lines 133 - 140, Add a regression test alongside the existing JSON
descriptor tests for an unclosed descriptor followed by prose, using
parseStreamedContent and drain. Assert the flushed output preserves the trailing
prose in out.text, ensuring pending incomplete descriptor content does not
swallow the remainder of the stream.

4-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Type drain with the exported parser types instead of any.

embedded-tool-calls.ts exports EmbeddedToolCall and ParsedBlock. Using them makes these tests fail at type-check time if the parser contract changes. The current any annotations remove that protection.

♻️ Proposed refactor
 import { test, expect } from "bun:test"
-import { parseStreamedContent } from "../embedded-tool-calls"
+import { parseStreamedContent } from "../embedded-tool-calls"
+import type { EmbeddedToolCall, ParsedBlock } from "../embedded-tool-calls"

-function drain(parser: any, chunks: string[]) {
+type StreamParser = { push(chunk: string): ParsedBlock; flush(): ParsedBlock }
+
+function drain(parser: StreamParser, chunks: string[]): { text: string; calls: EmbeddedToolCall[] } {
   const text: string[] = []
-  const calls: any[] = []
+  const calls: EmbeddedToolCall[] = []

Consider also exporting the parser handle type from embedded-tool-calls.ts so the test does not restate it.

As per coding guidelines: "explicitly export types needed by consumers, and prefer explicit return types on library functions".

🤖 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 `@apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts`
around lines 4 - 16, Update the test helper drain to use the exported
EmbeddedToolCall and ParsedBlock types instead of any, including an explicit
return type for its combined result. Export the parser handle type from
embedded-tool-calls.ts if needed, then annotate the parser parameter with that
type so the test remains checked against the parser contract.

Source: Coding guidelines

apps/supercode-cli/server/src/index.ts (1)

1845-1845: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

These new routes use Express, but the path instructions require Bun.serve().

The two new endpoints register on the existing Express app. The path instructions for this file specify Bun.serve() with routes support.

Migrating the whole file is out of scope for this PR. Track the migration separately, or confirm that Express is an accepted exception here.

As per path instructions: "apps/supercode-cli/server/**/{index,server}.{ts,tsx}: Use Bun.serve() with WebSocket and routes support instead of Express.js for server implementations".

Also applies to: 1897-1897

🤖 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 `@apps/supercode-cli/server/src/index.ts` at line 1845, Replace the new Express
registrations for the voice TTS endpoints in the `/api/voice/tts` and related
route handlers with entries in the file’s `Bun.serve()` routes configuration,
preserving their existing request and response behavior. Do not migrate
unrelated Express routes; if the existing server structure cannot support this
scoped change, track the migration separately rather than adding another Express
exception.

Source: Path instructions

cortex-sdk.md (1)

260-263: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align the Composio dependency declaration.

The document calls @composio/core an optional peer dependency, but the manifest declares it as an optional direct dependency. Choose one contract. Use peerDependencies with optional peer metadata for consumer-provided local mode, or update the documentation to describe an optional runtime dependency.

Also applies to: 346-353

🤖 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 `@cortex-sdk.md` around lines 260 - 263, Align the Composio dependency contract
between the `Composio` documentation section and the package manifest: either
declare `@composio/core` in `peerDependencies` with optional peer metadata, or
describe it as an optional runtime dependency wherever documented. Apply the
same choice consistently in all referenced Composio documentation sections.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 70913a90-ef3b-493c-a511-f49abcbb746a

📥 Commits

Reviewing files that changed from the base of the PR and between c439cf1 and 0ebe5d1.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • .gitignore
  • apps/supercode-cli/server/package.json
  • apps/supercode-cli/server/src/cli/ai/chat/chat.ts
  • apps/supercode-cli/server/src/cli/ai/server-proxy-service.ts
  • apps/supercode-cli/server/src/cli/commands/slashCommands/token-limit.ts
  • apps/supercode-cli/server/src/index.ts
  • apps/supercode-cli/server/src/lib/__tests__/embedded-tool-calls.test.ts
  • apps/supercode-cli/server/src/lib/embedded-tool-calls.ts
  • apps/supercode-cli/server/src/lib/token-budget.ts
  • apps/supercode-cli/server/src/voice/voiceChatClient.ts
  • cortex-sdk.md
  • package.json

Comment on lines +1021 to 1029
} catch (err: any) {
// Upstream hung/froze and AbortSignal.timeout fired — we treat this
// as "stream produced nothing", emit whatever we already have (if
// anything) and fall through to the non-streaming fallback below.
if (err?.name !== "AbortError") {
res.write(JSON.stringify({ type: "error", message: `Upstream failure: ${err?.message ?? String(err)}` }) + "\n")
break
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

break in this catch block is either a syntax error or it skips stream termination.

The try opens at Line 940 and the while (true) loop closes at Line 1020. The catch at Line 1021 is therefore outside the loop. A break statement inside it has no enclosing loop or switch within the catch.

Two outcomes are possible:

  • If no outer loop encloses this try/catch, break is an illegal break statement and the module fails to parse.
  • If an outer loop does enclose it, break exits that outer loop. The handler then skips the parser flush at Lines 1033-1042, the non-streaming fallback at Line 1055, and the finish event at Line 1121. The client receives an error line but never a stream terminator.

Neither outcome is correct. Replace break with code that stops reading and continues to the termination path.

🐛 Proposed fix
         } catch (err: any) {
           // Upstream hung/froze and AbortSignal.timeout fired — we treat this
           // as "stream produced nothing", emit whatever we already have (if
           // anything) and fall through to the non-streaming fallback below.
           if (err?.name !== "AbortError") {
             res.write(JSON.stringify({ type: "error", message: `Upstream failure: ${err?.message ?? String(err)}` }) + "\n")
-            break
           }
         }

If the intent is to skip the fallback for non-abort errors, set a flag and test it at Line 1055 instead:

+        let upstreamFailed = false
           if (err?.name !== "AbortError") {
             res.write(JSON.stringify({ type: "error", message: `Upstream failure: ${err?.message ?? String(err)}` }) + "\n")
-            break
+            upstreamFailed = true
           }
-        if (!fullContent.trim() && !emittedToolCalls) {
+        if (!upstreamFailed && !fullContent.trim() && !emittedToolCalls) {

The same pattern exists at Lines 1274-1279.

Run this to confirm whether an outer loop encloses the try/catch:

#!/bin/bash
# Description: Inspect control flow around the streaming catch blocks.
set -euo pipefail

fd -t f 'index.ts' apps/supercode-cli/server/src --max-depth 1 --exec sed -n '860,945p' {}
echo "=== catch block 1 ==="
fd -t f 'index.ts' apps/supercode-cli/server/src --max-depth 1 --exec sed -n '1015,1035p' {}
echo "=== catch block 2 ==="
fd -t f 'index.ts' apps/supercode-cli/server/src --max-depth 1 --exec sed -n '1270,1285p' {}
🤖 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 `@apps/supercode-cli/server/src/index.ts` around lines 1021 - 1029, Replace the
invalid or prematurely exiting break statements in both streaming catch blocks
with control flow that records the non-AbortError failure, stops further
reading, and proceeds through parser flushing and stream termination; ensure the
fallback is skipped when appropriate while the response still emits its finish
terminator.

Comment on lines +1897 to +1903
app.post("/api/voice/chat", async (req, res) => {
try {
const { messages, model: modelParam, provider = "concentrateai" } = req.body
if (!messages || !Array.isArray(messages) || messages.length === 0) {
res.status(400).json({ error: "messages array is required" })
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.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

/api/voice/chat has no authentication.

/api/voice/tts calls getUserFromBearer at Line 1847 and returns 401 for an unauthenticated caller. This handler performs no such check.

Any caller who can reach this route proxies arbitrary prompts to ConcentrateAI or OpenRouter using process.env.CONCENTRATEAI_API_KEY and process.env.OPENROUTER_API_KEY. The caller controls messages, model, and provider. The request bypasses the token-budget enforcement applied to the other chat routes in this file.

Add the same bearer check used by /api/voice/tts.

🔒️ Proposed fix
 app.post("/api/voice/chat", async (req, res) => {
   try {
+    const user = await getUserFromBearer(req)
+    if (!user) { res.status(401).json({ error: "Unauthorized" }); return }
+
     const { messages, model: modelParam, provider = "concentrateai" } = req.body
     if (!messages || !Array.isArray(messages) || messages.length === 0) {
       res.status(400).json({ error: "messages array is required" })
       return
     }
📝 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
app.post("/api/voice/chat", async (req, res) => {
try {
const { messages, model: modelParam, provider = "concentrateai" } = req.body
if (!messages || !Array.isArray(messages) || messages.length === 0) {
res.status(400).json({ error: "messages array is required" })
return
}
app.post("/api/voice/chat", async (req, res) => {
try {
const user = await getUserFromBearer(req)
if (!user) { res.status(401).json({ error: "Unauthorized" }); return }
const { messages, model: modelParam, provider = "concentrateai" } = req.body
if (!messages || !Array.isArray(messages) || messages.length === 0) {
res.status(400).json({ error: "messages array is required" })
return
}
🤖 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 `@apps/supercode-cli/server/src/index.ts` around lines 1897 - 1903, Add the
same getUserFromBearer authentication check used by /api/voice/tts to the
/api/voice/chat handler before processing req.body, returning HTTP 401 for
unauthenticated callers. Preserve the existing validation and chat flow for
authenticated requests.

Comment on lines +96 to +116
while (i < block.length) {
// Find the next opener (square, xml, or bare JSON descriptor).
let nextOpen = -1
let openKind: "square" | "xml" | "json" = "square"
const squareAt = block.indexOf("[TOOL_CALL]", i)
const squareAtLow = block.indexOf("[tool_call]", i)
const xmlAt = block.indexOf("<tool_call>", i)
const jsonAt = findJsonDescriptorStart(block, i)
let sq = squareAt
if (squareAtLow !== -1 && (sq === -1 || squareAtLow < sq)) sq = squareAtLow
if (sq !== -1) { nextOpen = sq; openKind = "square" }
if (xmlAt !== -1 && (nextOpen === -1 || xmlAt < nextOpen)) { nextOpen = xmlAt; openKind = "xml" }
if (jsonAt !== -1 && (nextOpen === -1 || jsonAt < nextOpen)) { nextOpen = jsonAt; openKind = "json" }

if (nextOpen === -1) {
text.push(block.slice(i))
break
}

// Emit everything before the opener.
text.push(block.slice(i, nextOpen))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Partial opener markers split across chunks leak to the client.

pump emits the whole remaining block as text when it finds no complete opener. It does not hold back a trailing partial opener. If a chunk ends mid-marker, the parser leaks the marker fragment and loses the tool call.

Example sequence: push("Checking.[TOOL_") then push("CALL]\nrun_command --command=\"git diff\"\n[/TOOL_CALL]").

  • Call 1 returns text: "Checking.[TOOL_" and pending: "", so buf is cleared.
  • Call 2 never sees a complete [TOOL_CALL] opener. The client receives CALL]\nrun_command --command="git diff"\n[/TOOL_CALL] as raw prose, and no tool call is emitted.

The same failure applies to <tool_call> and to the {"name": JSON descriptor start. MiniMax streams token-sized deltas, so openers split across chunk boundaries are expected, not rare. This is the exact garbage-leak the module header describes preventing.

Withhold any trailing suffix of the block that is a proper prefix of a supported opener.

🐛 Proposed fix to buffer partial openers
       if (nextOpen === -1) {
-        text.push(block.slice(i))
+        const rest = block.slice(i)
+        const hold = partialOpenerLength(rest)
+        if (hold > 0) {
+          text.push(rest.slice(0, rest.length - hold))
+          pending = rest.slice(rest.length - hold)
+        } else {
+          text.push(rest)
+        }
         break
       }

Add the helper alongside findJsonDescriptorStart:

const OPENERS = ["[TOOL_CALL]", "[tool_call]", "<tool_call>", '{"name"']

// Length of the trailing suffix of `s` that is a proper prefix of an opener.
function partialOpenerLength(s: string): number {
  const max = Math.min(s.length, Math.max(...OPENERS.map((o) => o.length)) - 1)
  for (let len = max; len > 0; len--) {
    const suffix = s.slice(s.length - len)
    if (OPENERS.some((o) => o.length > len && o.startsWith(suffix))) return len
  }
  return 0
}

Note that flush() must then release a held partial opener that never completed, or a truncated tail is silently dropped.

🤖 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 `@apps/supercode-cli/server/src/lib/embedded-tool-calls.ts` around lines 96 -
116, Update pump’s no-opener path to retain any trailing suffix that is a proper
prefix of a supported opener ([TOOL_CALL], [tool_call], <tool_call>, or the JSON
descriptor) in the pending buffer instead of emitting it as text, while still
emitting preceding content. Add a helper near findJsonDescriptorStart to
calculate this partial-opener length, and update flush() to release any held
partial opener as text when the stream ends.

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