@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 +1 -1
- package/package.json +2 -2
- package/packages/core/src/agent/agent-loop.ts +2 -0
- package/packages/core/src/agent/capability-search.ts +2 -1
- package/packages/core/src/agent/llm-client.ts +15 -0
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +3 -3
- package/packages/core/src/agent/llm-providers/opencode-go.ts +37 -0
- package/packages/core/src/storage/seed.ts +7 -3
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johpaz/hive-sdk",
|
|
3
|
-
"version": "0.4.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
338
|
-
|
|
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
|