@johpaz/hive-sdk 0.4.8 → 0.5.0

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.
@@ -47,6 +47,8 @@ export type ExecuteToolBatchOptions = {
47
47
  }
48
48
  hiveConfig?: Config
49
49
  workerPool?: ToolRuntimeConfig
50
+ /** Per-batch decision (Jev). Undefined preserves the configured behavior. */
51
+ parallelToolCalls?: boolean
50
52
  mainThreadToolNames?: string[]
51
53
  signal?: AbortSignal
52
54
  }
@@ -199,6 +201,7 @@ const DEFAULT_MAIN_THREAD_TOOL_NAMES = new Set([
199
201
  // These tools depend on process-local singleton state (HiveDB handle, live
200
202
  // channel senders, schedulers, browser sessions, or in-memory services).
201
203
  "search_knowledge",
204
+ "conversation_read",
202
205
  "save_note",
203
206
  "memory_write",
204
207
  "memory_read",
@@ -670,6 +673,7 @@ export async function executeToolBatch(options: ExecuteToolBatchOptions): Promis
670
673
 
671
674
  async function executeToolBatchInner(options: ExecuteToolBatchOptions): Promise<ToolBatchResult[]> {
672
675
  const runtimeConfig = resolveRuntimeConfig(options.workerPool)
676
+ const parallelToolCalls = options.parallelToolCalls ?? runtimeConfig.parallelToolCalls
673
677
  const hiveConfig = options.hiveConfig ?? loadConfig()
674
678
  const mainThreadToolNames = [
675
679
  ...DEFAULT_MAIN_THREAD_TOOL_NAMES,
@@ -690,11 +694,26 @@ async function executeToolBatchInner(options: ExecuteToolBatchOptions): Promise<
690
694
 
691
695
  // A null pool means workers are disabled, unnecessary (single call), or
692
696
  // unavailable in this build — all three degrade to the main thread.
693
- const pool = runtimeConfig.enabled && runtimeConfig.parallelToolCalls && options.toolCalls.length > 1
697
+ const pool = runtimeConfig.enabled && parallelToolCalls && options.toolCalls.length > 1
694
698
  ? getPool(runtimeConfig.maxWorkers)
695
699
  : null
696
700
 
697
701
  if (!pool) {
702
+ // Jev judged the batch independent: run it concurrently even without workers.
703
+ if (options.parallelToolCalls === true && options.toolCalls.length > 1) {
704
+ return Promise.all(options.toolCalls.map(async (toolCall) => {
705
+ const startedAt = performance.now()
706
+ const effectiveTimeout = resolveToolTimeout(toolCall.function.name, options.allTools, hiveConfig, runtimeConfig.toolTimeoutMs)
707
+ const result = await executeInMainThreadWithTimeout({
708
+ toolCall, allTools: options.allTools, toolConfig: options.toolConfig, signal: options.signal,
709
+ }, effectiveTimeout)
710
+ return {
711
+ toolCall, toolName: toolCall.function.name, result,
712
+ ok: !(result && typeof result === "object" && (result as any).error === true),
713
+ durationMs: Math.round(performance.now() - startedAt),
714
+ }
715
+ }))
716
+ }
698
717
  const results: ToolBatchResult[] = []
699
718
  for (const toolCall of options.toolCalls) {
700
719
  const startedAt = performance.now()
@@ -60,7 +60,9 @@ export const getAvailableModelsTool: Tool = {
60
60
  .filter(m => m.enabled && providersById.has(m.provider_id));
61
61
 
62
62
  if (providerId) models = models.filter(m => m.provider_id === providerId);
63
- if (modelType) models = models.filter(m => m.model_type === modelType);
63
+ // Decision models (Jev) only answer the Decisions API, never chat: an
64
+ // agent assigned one would fail every turn.
65
+ models = modelType ? models.filter(m => m.model_type === modelType) : models.filter(m => m.model_type !== "decision");
64
66
  if (capabilities) models = models.filter(m => (m.capabilities ?? "").includes(capabilities));
65
67
 
66
68
  // Transformar a formato amigable
@@ -14,7 +14,7 @@ import {
14
14
  type CapabilityType,
15
15
  } from "../../agent/capability-search.ts";
16
16
  import { CORE_TOOL_CATALOG } from "../../agent/tool-selector.ts";
17
- import { saveScratchpadNote } from "../../agent/conversation-store.ts";
17
+ import { saveScratchpadNote, getRecentMessages, getScratchpad } from "../../agent/conversation-store.ts";
18
18
 
19
19
  const log = logger.child("core");
20
20
 
@@ -503,6 +503,37 @@ export const reportProgressTool: Tool = {
503
503
  },
504
504
  };
505
505
 
506
+ /** Recover a small, scoped slice of conversation omitted by Jev's context plan. */
507
+ export const conversationReadTool: Tool = {
508
+ name: "conversation_read",
509
+ description: "Read earlier messages from the current conversation by message IDs or text query when selected context is insufficient.",
510
+ parameters: {
511
+ type: "object",
512
+ properties: {
513
+ message_ids: { type: "array", description: "Message IDs shown in the context plan", items: { type: "number" } },
514
+ note_keys: { type: "array", description: "Scratchpad note keys shown in the context plan", items: { type: "string" } },
515
+ query: { type: "string", description: "Optional case-insensitive text to search in earlier messages" },
516
+ },
517
+ },
518
+ execute: async (params, config) => {
519
+ const threadId = config?.configurable?.thread_id as string | undefined
520
+ if (!threadId) return { ok: false, error: "Conversation scope unavailable" }
521
+ const ids = new Set(Array.isArray(params.message_ids) ? params.message_ids.map(Number).filter(Number.isFinite) : [])
522
+ const noteKeys = new Set(Array.isArray(params.note_keys) ? params.note_keys.map(String) : [])
523
+ const query = String(params.query ?? "").trim().toLocaleLowerCase()
524
+ if (!ids.size && !noteKeys.size && !query) return { ok: false, error: "Provide message_ids, note_keys or query" }
525
+ const rows = await getRecentMessages(threadId, 200)
526
+ const notes = await getScratchpad(threadId)
527
+ return {
528
+ ok: true,
529
+ messages: rows.filter(row => (ids.size && ids.has(row.id)) || (query && row.content.toLocaleLowerCase().includes(query)))
530
+ .slice(-8).map(row => ({ id: row.id, role: row.role, source: row.source, content: row.content.slice(0, 2000) })),
531
+ notes: notes.filter(note => noteKeys.has(note.key) || (query && `${note.key} ${note.value}`.toLocaleLowerCase().includes(query)))
532
+ .slice(0, 8).map(note => ({ key: note.key, value: note.value.slice(0, 2000) })),
533
+ }
534
+ },
535
+ };
536
+
506
537
  export function createTools(): Tool[] {
507
- return [searchKnowledgeTool, notifyTool, saveNoteTool, reportProgressTool];
538
+ return [searchKnowledgeTool, notifyTool, saveNoteTool, reportProgressTool, conversationReadTool];
508
539
  }