@askalf/dario 5.5.85 → 5.5.86

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.
@@ -228,6 +228,7 @@ export function createResponsesTranslator(model) {
228
228
  let usage = null;
229
229
  const toolCalls = new Map();
230
230
  let nextToolIndex = 0;
231
+ let roleSent = false;
231
232
  const frame = (delta, finish) => `data: ${JSON.stringify({
232
233
  id,
233
234
  object: 'chat.completion.chunk',
@@ -235,6 +236,25 @@ export function createResponsesTranslator(model) {
235
236
  model,
236
237
  choices: [{ index: 0, delta, finish_reason: finish }],
237
238
  })}\n\n`;
239
+ /**
240
+ * Prefix the role-only opening frame the first time we emit anything.
241
+ *
242
+ * The reference OpenAI stream always opens with
243
+ * `delta: {"role":"assistant","content":""}` before any content, and SDK
244
+ * accumulators (openai-node's ChatCompletionStream, and every harness built
245
+ * on it — Cursor, Continue, Aider) use that frame to OPEN the assistant
246
+ * message. Without it the assembled message has no role, which reads fine in
247
+ * a terminal and then fails when the harness sends that history back.
248
+ * Emitted at `response.created` in the normal case; the flag makes every
249
+ * other branch safe too, so a stream that somehow starts with a text delta
250
+ * still cannot put content on the wire before the role.
251
+ */
252
+ const opened = (rest) => {
253
+ if (roleSent)
254
+ return rest;
255
+ roleSent = true;
256
+ return frame({ role: 'assistant', content: '' }, null) + rest;
257
+ };
238
258
  return {
239
259
  /** Feed one raw SSE line. Returns the line to forward, or null. */
240
260
  chunk(line) {
@@ -255,14 +275,14 @@ export function createResponsesTranslator(model) {
255
275
  const r = e.response;
256
276
  if (r?.id)
257
277
  id = `chatcmpl-${r.id.replace(/^resp_/, '')}`;
258
- return null;
278
+ return opened('');
259
279
  }
260
280
  if (type === 'response.output_text.delta') {
261
281
  const d = typeof e.delta === 'string' ? e.delta : '';
262
282
  if (!d)
263
283
  return null;
264
284
  text += d;
265
- return frame({ content: d }, null);
285
+ return opened(frame({ content: d }, null));
266
286
  }
267
287
  if (type === 'response.output_item.added') {
268
288
  const item = e.item;
@@ -276,7 +296,7 @@ export function createResponsesTranslator(model) {
276
296
  args: '',
277
297
  };
278
298
  toolCalls.set(key, acc);
279
- return frame({ tool_calls: [{ index: acc.index, id: acc.id, type: 'function', function: { name: acc.name, arguments: '' } }] }, null);
299
+ return opened(frame({ tool_calls: [{ index: acc.index, id: acc.id, type: 'function', function: { name: acc.name, arguments: '' } }] }, null));
280
300
  }
281
301
  if (type === 'response.function_call_arguments.delta') {
282
302
  const key = String(e.item_id ?? '');
@@ -285,7 +305,7 @@ export function createResponsesTranslator(model) {
285
305
  if (!acc || !d)
286
306
  return null;
287
307
  acc.args += d;
288
- return frame({ tool_calls: [{ index: acc.index, function: { arguments: d } }] }, null);
308
+ return opened(frame({ tool_calls: [{ index: acc.index, function: { arguments: d } }] }, null));
289
309
  }
290
310
  if (type === 'response.completed' || type === 'response.incomplete') {
291
311
  const r = e.response;
@@ -297,7 +317,7 @@ export function createResponsesTranslator(model) {
297
317
  total_tokens: (u.input_tokens ?? 0) + (u.output_tokens ?? 0),
298
318
  };
299
319
  }
300
- return `${frame({}, toolCalls.size > 0 ? 'tool_calls' : 'stop')}data: [DONE]\n\n`;
320
+ return opened(`${frame({}, toolCalls.size > 0 ? 'tool_calls' : 'stop')}data: [DONE]\n\n`);
301
321
  }
302
322
  return null;
303
323
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "5.5.85",
3
+ "version": "5.5.86",
4
4
  "description": "Use your Claude Pro/Max subscription in any tool — Cursor, Cline, Aider, the Agent SDK, your scripts — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint.",
5
5
  "type": "module",
6
6
  "bin": {