@johpaz/hive-sdk 0.4.5 → 0.4.6

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/README.md CHANGED
@@ -271,4 +271,4 @@ npm view @johpaz/hive-sdk dist-tags # verificar después del release
271
271
 
272
272
  ---
273
273
 
274
- *Hive SDK v0.4.5 — MIT*
274
+ *Hive SDK v0.4.6 — MIT*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johpaz/hive-sdk",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "private": false,
5
5
  "description": "Hive SDK — The Agent Harness SDK. Build, deploy, and scale AI agent applications with multi-channel support, context engineering, and swarm orchestration.",
6
6
  "license": "MIT",
@@ -99,7 +99,7 @@
99
99
  "drift": "bun scripts/check-drift.ts"
100
100
  },
101
101
  "dependencies": {
102
- "@johpaz/hive-db": "^0.4.0",
102
+ "@johpaz/hive-db": "^0.5.1",
103
103
  "@anthropic-ai/sdk": "^0.74.0",
104
104
  "@google/genai": "^1.43.0",
105
105
  "@modelcontextprotocol/sdk": "^1.26.0",
@@ -521,6 +521,7 @@ export async function* runAgent(
521
521
  messages: clearOldToolResults(messages) as LLMMessage[],
522
522
  tools: ctx.tools.length > 0 ? ctx.tools : undefined,
523
523
  signal: opts.signal,
524
+ sessionId: opts.threadId,
524
525
  onToken: opts.onToken && !delegationGroupAtCall
525
526
  ? (token: string) => {
526
527
  streamedThisCall = true
@@ -1133,6 +1134,7 @@ export async function* runAgent(
1133
1134
  ...providerCfg,
1134
1135
  messages: clearOldToolResults(messages) as LLMMessage[],
1135
1136
  tools: undefined, // no tools — force text response
1137
+ sessionId: opts.threadId,
1136
1138
  })
1137
1139
  if (synthesis.usage) {
1138
1140
  totalInputTokens += synthesis.usage.input_tokens
@@ -81,7 +81,8 @@ export async function searchCapabilities(
81
81
  const k = opts.k ?? 10;
82
82
  const types = opts.types?.length ? opts.types : undefined;
83
83
  const trimmed = query.trim();
84
- if (!trimmed) return [];
84
+ // hive-db >= 0.4 rejects k <= 0 instead of returning no hits.
85
+ if (!trimmed || k <= 0) return [];
85
86
 
86
87
  const startTime = performance.now();
87
88
  const db = await getHiveDb();
@@ -97,6 +97,13 @@ export interface LLMCallOptions {
97
97
  signal?: AbortSignal
98
98
  /** Enable extended thinking for supported models (Anthropic Claude 3.7+). */
99
99
  thinking?: { enabled: boolean; budget_tokens?: number }
100
+ /**
101
+ * Stable id of the conversation this call belongs to (the agent loop's
102
+ * threadId). Providers that route or cache per session derive their own
103
+ * header from it — OpenCode Go's `x-opencode-session` — and never send it
104
+ * verbatim, since it carries user, channel and peer ids.
105
+ */
106
+ sessionId?: string
100
107
  }
101
108
 
102
109
  export interface LLMResponse {
@@ -214,6 +221,14 @@ export function describeProviderFailure(
214
221
  provider: string,
215
222
  cleanModel: string,
216
223
  ): string {
224
+ // NVIDIA usa 404 también para modelos que siguen en su catálogo público pero
225
+ // no están habilitados para esa key ("Function '…': Not found for account
226
+ // '…'"). Decir que "se retiró" mandaba a buscar un modelo que sí existe.
227
+ if (status === 404 && /not found for account/i.test((err as Error)?.message ?? "")) {
228
+ return `Tu cuenta de ${provider} no tiene habilitado el modelo "${cleanModel}" (HTTP 404): `
229
+ + `figura en su catálogo, pero no está disponible para esta API key. `
230
+ + `Elige otro modelo en Ajustes → Proveedores.`
231
+ }
217
232
  if (status === 404 || status === 410) {
218
233
  return `El modelo "${cleanModel}" ya no existe en ${provider} (HTTP ${status}). `
219
234
  + `El proveedor lo retiró de su catálogo; reintentar no sirve. `
@@ -82,8 +82,8 @@ export abstract class OpenAICompatBase implements LLMProvider {
82
82
  /** Override to true for providers running on localhost. */
83
83
  protected isLocalProvider(): boolean { return false }
84
84
 
85
- /** Override to customize the OpenAI client (e.g. strip unwanted headers, add custom fetch). */
86
- protected async resolveOpenAIClient(apiKey: string, baseURL: string | undefined): Promise<any> {
85
+ /** Override to customize the OpenAI client (e.g. strip unwanted headers, add custom fetch, per-call headers). */
86
+ protected async resolveOpenAIClient(apiKey: string, baseURL: string | undefined, _options?: LLMCallOptions): Promise<any> {
87
87
  const { default: OpenAI } = await import("openai")
88
88
  return new OpenAI({ apiKey, baseURL })
89
89
  }
@@ -135,7 +135,7 @@ export abstract class OpenAICompatBase implements LLMProvider {
135
135
  throw new Error(`API key missing for provider: ${this.providerName}. Configure it in Settings → Providers.`)
136
136
  }
137
137
 
138
- const client = await this.resolveOpenAIClient(apiKey, baseURL)
138
+ const client = await this.resolveOpenAIClient(apiKey, baseURL, options)
139
139
 
140
140
  const sanitized = sanitizeMessages(options.messages)
141
141
  const rawMessages = this.needsReasoningRoundtrip()
@@ -1,4 +1,29 @@
1
+ import { createHash, randomUUID } from "node:crypto"
2
+ import pkg from "../../../../../package.json"
1
3
  import { OpenAICompatBase } from "./openai-compat-base.ts"
4
+ import type { LLMCallOptions } from "../llm-client.ts"
5
+
6
+ /**
7
+ * OpenCode Go rechaza con 400 `MissingSessionID` toda petición sin
8
+ * `x-opencode-session` (verificado 2026-09-10 con una key real: sin el header
9
+ * 400, con él 200). Su documentación pide además que el cliente se identifique
10
+ * con su propio User-Agent en vez del genérico del SDK:
11
+ * https://opencode.ai/docs/go/#where-can-i-use-it
12
+ */
13
+ const USER_AGENT = `hive-sdk/${pkg.version}`
14
+
15
+ /** Sesión de respaldo para llamadas sin conversación (compactación, metas). */
16
+ const PROCESS_SESSION = randomUUID()
17
+
18
+ /**
19
+ * La sesión tiene que ser estable por conversación —OpenCode la usa para rutear
20
+ * y cachear el prompt—, pero el threadId lleva usuario, canal y contacto: a
21
+ * OpenCode sólo le llega un hash.
22
+ */
23
+ function sessionHeader(sessionId: string | undefined): string {
24
+ if (!sessionId) return PROCESS_SESSION
25
+ return createHash("sha256").update(`hive:${sessionId}`).digest("hex").slice(0, 32)
26
+ }
2
27
 
3
28
  export class OpenCodeGoProvider extends OpenAICompatBase {
4
29
  static readonly secretKey = "OPENCODE_GO_API_KEY"
@@ -6,4 +31,16 @@ export class OpenCodeGoProvider extends OpenAICompatBase {
6
31
  constructor() {
7
32
  super("opencode-go")
8
33
  }
34
+
35
+ protected async resolveOpenAIClient(apiKey: string, baseURL: string | undefined, options?: LLMCallOptions): Promise<any> {
36
+ const { default: OpenAI } = await import("openai")
37
+ return new OpenAI({
38
+ apiKey,
39
+ baseURL,
40
+ defaultHeaders: {
41
+ "x-opencode-session": sessionHeader(options?.sessionId),
42
+ "User-Agent": USER_AGENT,
43
+ },
44
+ })
45
+ }
9
46
  }
@@ -266,7 +266,9 @@ export const SEED_DATA: SeedData = {
266
266
  { id: "z-ai/glm-5.3-flash", providerId: "openrouter", name: "GLM 5.3 Flash (OR)", modelType: "llm", contextWindow: 1310720, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 0.075, outputPer1M: 0.25 },
267
267
  { id: "z-ai/glm-5.2", providerId: "openrouter", name: "GLM 5.2 (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 0.966, outputPer1M: 3.036 },
268
268
  // Qwen
269
- { id: "qwen/qwen3.8-max", providerId: "openrouter", name: "Qwen3.8 Max (OR)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 2, outputPer1M: 6 },
269
+ // OpenRouter renombró qwen/qwen3.8-max a su versión fechada: mismo contexto
270
+ // y precio, ahora también con imagen. Verificado 2026-09-10.
271
+ { id: "qwen/qwen3.8-max-0902", providerId: "openrouter", name: "Qwen3.8 Max (OR)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 2, outputPer1M: 6 },
270
272
  { id: "qwen/qwen3.8-flash", providerId: "openrouter", name: "Qwen3.8 Flash (OR)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0.15, outputPer1M: 0.47 },
271
273
  { id: "qwen/qwen3.7-flash", providerId: "openrouter", name: "Qwen3.7 Flash (OR)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.03, outputPer1M: 0.13 },
272
274
  // xAI
@@ -334,8 +336,10 @@ export const SEED_DATA: SeedData = {
334
336
  // provider `z-ai` directo, o el enrutado de OpenRouter.
335
337
  { id: "moonshotai/kimi-k3", providerId: "nvidia", name: "Kimi K3 (NVIDIA)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
336
338
  { id: "nvidia/nemotron-3.5-lightning-30b-a3b", providerId: "nvidia", name: "Nemotron 3.5 Lightning 30B", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
337
- { id: "moonshotai/kimi-k2.6", providerId: "nvidia", name: "Kimi K2.6 (NVIDIA)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "vision", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
338
- { id: "minimaxai/minimax-m3", providerId: "nvidia", name: "MiniMax M3 (NVIDIA)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
339
+ // MiniMax M3 (minimaxai/minimax-m3) se sacó: NVIDIA lo retiró (410 Gone, ya
340
+ // no figura en /v1/models). Kimi K2.6 (moonshotai/kimi-k2.6) se sacó por lo
341
+ // mismo que DeepSeek V4 Pro: 404 "Function not found for account" en dos
342
+ // cuentas distintas. Verificado 2026-09-10.
339
343
  { id: "nvidia/nemotron-3-ultra-550b-a55b", providerId: "nvidia", name: "Nemotron 3 Ultra 550B", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
340
344
  { id: "nvidia/nemotron-3-super-120b-a12b", providerId: "nvidia", name: "Nemotron 3 Super 120B", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
341
345
  // DeepSeek V4 Pro (deepseek-ai/deepseek-v4-pro) se sacó: devuelve 404