@holoscript/holoscript-agent 2.1.2 → 2.1.3

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/dist/index.js CHANGED
@@ -941,6 +941,8 @@ function isProductiveToolUse(use) {
941
941
  return true;
942
942
  case "str_replace":
943
943
  return true;
944
+ case "vision_analyze":
945
+ return true;
944
946
  default:
945
947
  return false;
946
948
  }
@@ -1077,6 +1079,28 @@ var MESH_TOOLS = [
1077
1079
  },
1078
1080
  required: ["title"]
1079
1081
  }
1082
+ },
1083
+ {
1084
+ name: "vision_analyze",
1085
+ description: "Analyze an image using the local Fara-7B vision model (Ollama on loopback). Reads the image file at `image_path`, sends it to fara:7b via the local Ollama API, and returns the model's text analysis. Counts as a productive tool call \u2014 use for GUI-grounding, visual QA, image captioning, or any task that requires perceiving image content. Only available on surfaces with a local Ollama instance running fara:7b.",
1086
+ input_schema: {
1087
+ type: "object",
1088
+ properties: {
1089
+ image_path: {
1090
+ type: "string",
1091
+ description: "Absolute path to the image file (png, jpg, webp, gif)"
1092
+ },
1093
+ prompt: {
1094
+ type: "string",
1095
+ description: 'Instruction for the vision model (default: "Describe this image in detail.")'
1096
+ },
1097
+ model: {
1098
+ type: "string",
1099
+ description: 'Ollama model tag to use (default: "fara:7b")'
1100
+ }
1101
+ },
1102
+ required: ["image_path"]
1103
+ }
1080
1104
  }
1081
1105
  ];
1082
1106
  function resolveActiveTools(brain, opts = {}) {
@@ -1320,6 +1344,49 @@ ${truncated}`);
1320
1344
  const result = await opts.addTask([{ title, description, priority: priority2, source, tags }]);
1321
1345
  return okResult(use.id, `delegate_task: posted "${title}" to board \u2014 ${result.added} task(s) added`);
1322
1346
  }
1347
+ if (use.name === "vision_analyze") {
1348
+ const imagePath = String(use.input.image_path ?? "").trim();
1349
+ if (!imagePath) return errResult(use.id, "vision_analyze: image_path is required");
1350
+ const denied = checkReadAllowed(imagePath);
1351
+ if (denied) return errResult(use.id, `vision_analyze: ${denied}`);
1352
+ const prompt = String(use.input.prompt ?? "Describe this image in detail.");
1353
+ const model = String(use.input.model ?? "fara:7b");
1354
+ const ollamaBase = process.env.HOLOSCRIPT_AGENT_LOCAL_LLM_BASE_URL;
1355
+ if (!ollamaBase) {
1356
+ return errResult(
1357
+ use.id,
1358
+ "vision_analyze: HOLOSCRIPT_AGENT_LOCAL_LLM_BASE_URL is not set \u2014 configure it to point to your local Ollama instance (e.g. http://holojetson.local:11434)"
1359
+ );
1360
+ }
1361
+ const TIMEOUT_MS = 12e4;
1362
+ const controller = new AbortController();
1363
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
1364
+ try {
1365
+ const imageBytes = await readFile3(imagePath);
1366
+ const imageB64 = imageBytes.toString("base64");
1367
+ const res = await fetch(`${ollamaBase}/api/generate`, {
1368
+ method: "POST",
1369
+ headers: { "content-type": "application/json" },
1370
+ body: JSON.stringify({ model, prompt, images: [imageB64], stream: false }),
1371
+ signal: controller.signal
1372
+ });
1373
+ clearTimeout(timer);
1374
+ if (!res.ok) {
1375
+ const text = await res.text();
1376
+ return errResult(use.id, `vision_analyze: Ollama HTTP ${res.status}: ${text.slice(0, 500)}`);
1377
+ }
1378
+ const json = await res.json();
1379
+ if (json.error) return errResult(use.id, `vision_analyze: model error \u2014 ${json.error}`);
1380
+ return okResult(use.id, json.response ?? "");
1381
+ } catch (err) {
1382
+ clearTimeout(timer);
1383
+ const msg = err instanceof Error ? err.message : String(err);
1384
+ return errResult(
1385
+ use.id,
1386
+ msg.includes("abort") ? `vision_analyze: timed out after ${TIMEOUT_MS}ms` : `vision_analyze: ${msg}`
1387
+ );
1388
+ }
1389
+ }
1323
1390
  return errResult(use.id, `unknown tool: ${use.name}`);
1324
1391
  } catch (err) {
1325
1392
  return errResult(use.id, err instanceof Error ? err.message : String(err));