zidane 1.8.1 → 2.0.1

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.
@@ -5,10 +5,10 @@ import {
5
5
  toAnthropic,
6
6
  toolResultsMessage,
7
7
  userMessage
8
- } from "./chunk-EC7IRWVS.js";
8
+ } from "./chunk-LN4LLLHA.js";
9
9
  import {
10
10
  matchesContextExceeded
11
- } from "./chunk-FFFDQHMA.js";
11
+ } from "./chunk-7JTBBZ2U.js";
12
12
 
13
13
  // src/providers/anthropic.ts
14
14
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
@@ -226,7 +226,14 @@ function anthropic(anthropicParams) {
226
226
  } : void 0;
227
227
  return {
228
228
  name: "anthropic",
229
- meta: { defaultModel, isOAuth },
229
+ meta: {
230
+ defaultModel,
231
+ isOAuth,
232
+ capabilities: {
233
+ vision: true,
234
+ imageInToolResult: true
235
+ }
236
+ },
230
237
  formatTools(tools) {
231
238
  return tools.map((t) => ({
232
239
  name: t.name,
@@ -373,7 +380,8 @@ function cerebras(params) {
373
380
  name: "cerebras",
374
381
  apiKey,
375
382
  baseURL: BASE_URL,
376
- defaultModel: params?.defaultModel || "zai-glm-4.7"
383
+ defaultModel: params?.defaultModel || "zai-glm-4.7",
384
+ capabilities: params?.capabilities ?? { vision: false, imageInToolResult: false }
377
385
  });
378
386
  }
379
387
 
@@ -414,11 +422,12 @@ function toPiMessages(messages, modelId) {
414
422
  const toolResults = msg.content.filter((b) => b.type === "tool_result");
415
423
  if (toolResults.length > 0) {
416
424
  for (const result of toolResults) {
425
+ const content2 = typeof result.output === "string" ? [{ type: "text", text: result.output }] : result.output.map((block) => block.type === "image" ? { type: "image", data: block.data, mimeType: block.mediaType } : { type: "text", text: block.text });
417
426
  out.push({
418
427
  role: "toolResult",
419
428
  toolCallId: result.callId,
420
429
  toolName: "",
421
- content: [{ type: "text", text: result.output }],
430
+ content: content2,
422
431
  isError: result.isError ?? false,
423
432
  timestamp: Date.now()
424
433
  });
@@ -545,7 +554,14 @@ function openai(params) {
545
554
  } : void 0;
546
555
  return {
547
556
  name: "openai",
548
- meta: { defaultModel, isOAuth: true },
557
+ meta: {
558
+ defaultModel,
559
+ isOAuth: true,
560
+ capabilities: {
561
+ vision: true,
562
+ imageInToolResult: true
563
+ }
564
+ },
549
565
  formatTools,
550
566
  userMessage,
551
567
  assistantMessage,
@@ -650,7 +666,8 @@ function openrouter(params) {
650
666
  extraHeaders: {
651
667
  "HTTP-Referer": "https://github.com/Tahul/zidane",
652
668
  "X-Title": "zidane"
653
- }
669
+ },
670
+ capabilities: params?.capabilities ?? { vision: true, imageInToolResult: false }
654
671
  });
655
672
  }
656
673
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  matchesContextExceeded
3
- } from "./chunk-FFFDQHMA.js";
3
+ } from "./chunk-7JTBBZ2U.js";
4
4
 
5
5
  // src/providers/openai-compat.ts
6
6
  var TOOL_RESULTS_TAG = "__zidane_tool_results__";
@@ -84,8 +84,28 @@ async function consumeSSE(response, callbacks, signal) {
84
84
  }));
85
85
  return { text, thinking, toolCalls, finishReason, usage };
86
86
  }
87
- function toOAIMessages(system, messages) {
87
+ function toImageUrlPart(img) {
88
+ return {
89
+ type: "image_url",
90
+ image_url: { url: `data:${img.mediaType};base64,${img.data}` }
91
+ };
92
+ }
93
+ function summarizeToolResultOutput(output) {
94
+ if (typeof output === "string")
95
+ return { text: output, images: [] };
96
+ const texts = [];
97
+ const images = [];
98
+ for (const block of output) {
99
+ if (block.type === "text")
100
+ texts.push(block.text);
101
+ else if (block.type === "image")
102
+ images.push({ mediaType: block.mediaType, data: block.data });
103
+ }
104
+ return { text: texts.join("\n"), images };
105
+ }
106
+ function toOAIMessages(system, messages, options = {}) {
88
107
  const out = [{ role: "system", content: system }];
108
+ const nativeImageInTool = options.imageInToolResult === true;
89
109
  for (const msg of messages) {
90
110
  const toolResults = msg.content.filter((b) => b.type === "tool_result");
91
111
  const toolCalls = msg.content.filter((b) => b.type === "tool_call");
@@ -93,7 +113,33 @@ function toOAIMessages(system, messages) {
93
113
  const imageBlocks = msg.content.filter((b) => b.type === "image");
94
114
  if (toolResults.length > 0) {
95
115
  for (const tr of toolResults) {
96
- out.push({ role: "tool", tool_call_id: tr.callId, content: tr.output });
116
+ if (typeof tr.output === "string") {
117
+ out.push({ role: "tool", tool_call_id: tr.callId, content: tr.output });
118
+ continue;
119
+ }
120
+ if (nativeImageInTool) {
121
+ const parts = tr.output.map((block) => block.type === "image" ? toImageUrlPart({ mediaType: block.mediaType, data: block.data }) : { type: "text", text: block.text });
122
+ out.push({ role: "tool", tool_call_id: tr.callId, content: parts });
123
+ continue;
124
+ }
125
+ const { text, images } = summarizeToolResultOutput(tr.output);
126
+ if (images.length === 0) {
127
+ out.push({ role: "tool", tool_call_id: tr.callId, content: text });
128
+ continue;
129
+ }
130
+ const noun = images.length === 1 ? "image" : "images";
131
+ const attachedMarker = `[${images.length} ${noun} attached \u2014 see next user message]`;
132
+ const toolMarker = text.length > 0 ? `${text}
133
+
134
+ ${attachedMarker}` : attachedMarker;
135
+ out.push({ role: "tool", tool_call_id: tr.callId, content: toolMarker });
136
+ out.push({
137
+ role: "user",
138
+ content: [
139
+ ...images.map(toImageUrlPart),
140
+ { type: "text", text: `(${noun} returned by tool call ${tr.callId})` }
141
+ ]
142
+ });
97
143
  }
98
144
  continue;
99
145
  }
@@ -244,9 +290,13 @@ function openaiCompat(params) {
244
290
  const authHeaderValue = params.authHeader?.scheme ? `${params.authHeader.scheme} ${params.apiKey}` : params.authHeader ? params.apiKey : `Bearer ${params.apiKey}`;
245
291
  const extraHeaders = params.extraHeaders ?? {};
246
292
  const endpoint = `${params.baseURL.replace(TRAILING_SLASH_RE, "")}/chat/completions`;
293
+ const capabilities = {
294
+ vision: params.capabilities?.vision ?? false,
295
+ imageInToolResult: params.capabilities?.imageInToolResult ?? false
296
+ };
247
297
  return {
248
298
  name,
249
- meta: { defaultModel },
299
+ meta: { defaultModel, capabilities },
250
300
  formatTools,
251
301
  userMessage,
252
302
  assistantMessage,
@@ -254,7 +304,9 @@ function openaiCompat(params) {
254
304
  classifyError: classifyOpenAICompatError,
255
305
  async stream(options, callbacks) {
256
306
  const modelId = options.model || defaultModel;
257
- const messages = toOAIMessages(options.system, options.messages);
307
+ const messages = toOAIMessages(options.system, options.messages, {
308
+ imageInToolResult: capabilities.imageInToolResult === true
309
+ });
258
310
  const maxTokens = options.thinkingBudget ? options.thinkingBudget + options.maxTokens : options.maxTokens;
259
311
  const body = {
260
312
  model: modelId,
@@ -306,6 +358,48 @@ function openaiCompat(params) {
306
358
  }
307
359
 
308
360
  // src/session/messages.ts
361
+ function decodeAnthropicToolResultContent(content) {
362
+ if (typeof content === "string")
363
+ return content;
364
+ if (!Array.isArray(content))
365
+ return JSON.stringify(content);
366
+ const blocks = [];
367
+ for (const raw of content) {
368
+ if (!raw || typeof raw !== "object")
369
+ continue;
370
+ const b = raw;
371
+ if (b.type === "text" && typeof b.text === "string") {
372
+ blocks.push({ type: "text", text: b.text });
373
+ continue;
374
+ }
375
+ if (b.type === "image" && b.source && typeof b.source === "object") {
376
+ const src = b.source;
377
+ if (src.type === "base64" && typeof src.data === "string" && typeof src.media_type === "string") {
378
+ blocks.push({ type: "image", mediaType: src.media_type, data: src.data });
379
+ continue;
380
+ }
381
+ }
382
+ blocks.push({ type: "text", text: JSON.stringify(raw) });
383
+ }
384
+ if (blocks.length === 0)
385
+ return "";
386
+ const hasNonText = blocks.some((b) => b.type !== "text");
387
+ if (!hasNonText)
388
+ return blocks.map((b) => b.text).join("\n");
389
+ return blocks;
390
+ }
391
+ function encodeAnthropicToolResultContent(output) {
392
+ if (typeof output === "string")
393
+ return output;
394
+ return output.map((b) => {
395
+ if (b.type === "text")
396
+ return { type: "text", text: b.text };
397
+ return {
398
+ type: "image",
399
+ source: { type: "base64", media_type: b.mediaType, data: b.data }
400
+ };
401
+ });
402
+ }
309
403
  function fromAnthropic(msg) {
310
404
  const role = msg.role;
311
405
  const content = [];
@@ -328,7 +422,7 @@ function fromAnthropic(msg) {
328
422
  } else if (b.type === "tool_use") {
329
423
  content.push({ type: "tool_call", id: b.id, name: b.name, input: b.input });
330
424
  } else if (b.type === "tool_result") {
331
- const output = typeof b.content === "string" ? b.content : JSON.stringify(b.content);
425
+ const output = decodeAnthropicToolResultContent(b.content);
332
426
  content.push({ type: "tool_result", callId: b.tool_use_id, output });
333
427
  } else if (b.type === "thinking") {
334
428
  content.push({ type: "thinking", text: b.thinking, signature: b.signature });
@@ -398,7 +492,7 @@ function toAnthropic(msg) {
398
492
  case "tool_call":
399
493
  return { type: "tool_use", id: block.id, name: block.name, input: block.input };
400
494
  case "tool_result":
401
- return { type: "tool_result", tool_use_id: block.callId, content: block.output };
495
+ return { type: "tool_result", tool_use_id: block.callId, content: encodeAnthropicToolResultContent(block.output) };
402
496
  case "thinking":
403
497
  return { type: "thinking", thinking: block.text, signature: block.signature };
404
498
  default:
@@ -0,0 +1,22 @@
1
+ import {
2
+ validateSkillForWrite
3
+ } from "./chunk-BCXXXJ3G.js";
4
+
5
+ // src/skills/index.ts
6
+ function defineSkill(config) {
7
+ const normalized = {
8
+ ...config,
9
+ source: config.source ?? "inline"
10
+ };
11
+ const result = validateSkillForWrite(normalized);
12
+ if (!result.valid) {
13
+ const summary = result.errors.map((e) => ` - [${e.code}] ${e.message}`).join("\n");
14
+ throw new Error(`Invalid skill "${normalized.name}":
15
+ ${summary}`);
16
+ }
17
+ return normalized;
18
+ }
19
+
20
+ export {
21
+ defineSkill
22
+ };
@@ -0,0 +1,14 @@
1
+ // src/types.ts
2
+ function toolResultToText(content) {
3
+ if (typeof content === "string")
4
+ return content;
5
+ return content.map((block) => {
6
+ if (block.type === "text")
7
+ return block.text;
8
+ return `[image: ${block.mediaType} \u2014 ${block.data.length} b64 bytes]`;
9
+ }).join("\n");
10
+ }
11
+
12
+ export {
13
+ toolResultToText
14
+ };
@@ -4,7 +4,7 @@ import {
4
4
  shell,
5
5
  spawn,
6
6
  writeFile
7
- } from "./chunk-4N5ADW7A.js";
7
+ } from "./chunk-PASFWG7S.js";
8
8
 
9
9
  // src/harnesses/basic.ts
10
10
  var basicTools = { shell, readFile, writeFile, listFiles };