thincoder 0.12.14 → 0.12.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thincoder",
3
- "version": "0.12.14",
3
+ "version": "0.12.15",
4
4
  "description": "Thin coding agent - zero dependencies, no build step, Node.js native. Sharp code, zero bloat.",
5
5
  "keywords": [
6
6
  "ai",
@@ -3,6 +3,25 @@
3
3
  * Extracted from core.mjs. Parses Server-Sent Events for LLM chat responses.
4
4
  */
5
5
  export async function readSSE(response, { onToken, onReasoning, rules, signal, firedPatterns: sharedFired }) {
6
+ // Early intercept: non-SSE responses are error bodies, not streams.
7
+ // Read as text to preserve the full error detail (status + body).
8
+ const contentType = response.headers.get("content-type") || ""
9
+ if (!contentType.includes("event-stream")) {
10
+ const body = await response.text().catch(() => "")
11
+ let errorMsg = ""
12
+ try {
13
+ const parsed = JSON.parse(body)
14
+ errorMsg = parsed?.error?.message
15
+ || parsed?.base_resp?.status_msg
16
+ || parsed?.detail
17
+ || parsed?.message
18
+ || parsed?.msg
19
+ || (typeof parsed.error === "string" ? parsed.error : "")
20
+ } catch { /* not JSON */ }
21
+ if (!errorMsg) errorMsg = body.slice(0, 500) // fallback: show raw body preview
22
+ throw new Error(`API error: HTTP ${response.status} — ${errorMsg}`)
23
+ }
24
+
6
25
  const result = { content: "", reasoning: "", toolCalls: [], usage: null, finishReason: null }
7
26
  const decoder = new TextDecoder()
8
27
  let buffer = ""
@@ -86,26 +105,20 @@ export async function readSSE(response, { onToken, onReasoning, rules, signal, f
86
105
  }
87
106
 
88
107
  if (!hasChoices) {
89
- const contentType = response.headers.get("content-type") || ""
108
+ // Stream started as SSE but no choices were parsed — unusual. Include status for debugging.
109
+ const raw = buffer.trim()
90
110
  let errorMsg = ""
91
111
  try {
92
- const raw = buffer.trim() || ""
93
- if (raw) {
94
- const parsed = JSON.parse(raw)
95
- errorMsg = parsed?.error?.message
96
- || parsed?.base_resp?.status_msg
97
- || parsed?.detail
98
- || parsed?.message
99
- || parsed?.msg
100
- || (typeof parsed.error === "string" ? parsed.error : "")
101
- }
112
+ const parsed = raw ? JSON.parse(raw) : null
113
+ errorMsg = parsed?.error?.message
114
+ || parsed?.base_resp?.status_msg
115
+ || parsed?.detail
116
+ || parsed?.message
117
+ || parsed?.msg
118
+ || (typeof parsed.error === "string" ? parsed.error : "")
102
119
  } catch { /* not JSON */ }
103
- if (!errorMsg && !contentType.includes("event-stream")) {
104
- errorMsg = `Response is not SSE (Content-Type: ${contentType || "unknown"})`
105
- }
106
- if (errorMsg) {
107
- throw new Error(`API error: ${errorMsg}`)
108
- }
120
+ if (!errorMsg) errorMsg = `SSE stream contained no choices (HTTP ${response.status})`
121
+ throw new Error(`API error: ${errorMsg}`)
109
122
  }
110
123
 
111
124
  return result