nolo-cli 0.1.48 → 0.1.49

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.
Files changed (2) hide show
  1. package/index.js +120 -40
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -3436,6 +3436,35 @@ function accumulateToolCallDelta(accumulated, deltas) {
3436
3436
  function finalizeAccumulatedToolCalls(accumulated) {
3437
3437
  return Object.keys(accumulated).map((key2) => accumulated[Number(key2)]).filter((call) => call?.function?.name);
3438
3438
  }
3439
+ function processOpenAiCompatibleSseEvent(event, state) {
3440
+ for (const line of event.split("\n")) {
3441
+ const trimmed = line.trim();
3442
+ if (!trimmed.startsWith("data:")) continue;
3443
+ const payload = trimmed.slice(5).trim();
3444
+ if (!payload || payload === "[DONE]") continue;
3445
+ let parsed;
3446
+ try {
3447
+ parsed = JSON.parse(payload);
3448
+ } catch {
3449
+ continue;
3450
+ }
3451
+ if (parsed?.usage && typeof parsed.usage === "object") {
3452
+ state.usage = parsed.usage;
3453
+ }
3454
+ const delta = parsed?.choices?.[0]?.delta;
3455
+ if (!delta || typeof delta !== "object") continue;
3456
+ const reasoningChunk = typeof delta.reasoning_content === "string" ? delta.reasoning_content : typeof delta.reasoning === "string" ? delta.reasoning : "";
3457
+ if (reasoningChunk) state.reasoning += reasoningChunk;
3458
+ if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
3459
+ accumulateToolCallDelta(state.accumulatedToolCalls, delta.tool_calls);
3460
+ }
3461
+ const textChunk = typeof delta.content === "string" ? delta.content : "";
3462
+ if (textChunk) {
3463
+ state.content += textChunk;
3464
+ state.onTextDelta?.(textChunk);
3465
+ }
3466
+ }
3467
+ }
3439
3468
  async function readOpenAiCompatibleSseCompletion(args2) {
3440
3469
  const reader = args2.response.body?.getReader();
3441
3470
  if (!reader) {
@@ -3443,10 +3472,13 @@ async function readOpenAiCompatibleSseCompletion(args2) {
3443
3472
  }
3444
3473
  const decoder = new TextDecoder();
3445
3474
  let buffer = "";
3446
- let content = "";
3447
- let reasoning = "";
3448
- let usage2;
3449
- const accumulatedToolCalls = {};
3475
+ const state = {
3476
+ content: "",
3477
+ reasoning: "",
3478
+ usage: void 0,
3479
+ accumulatedToolCalls: {},
3480
+ onTextDelta: args2.onTextDelta
3481
+ };
3450
3482
  while (true) {
3451
3483
  const { done, value } = await reader.read();
3452
3484
  if (done) break;
@@ -3456,41 +3488,19 @@ async function readOpenAiCompatibleSseCompletion(args2) {
3456
3488
  if (boundary === -1) break;
3457
3489
  const event = buffer.slice(0, boundary);
3458
3490
  buffer = buffer.slice(boundary + 2);
3459
- for (const line of event.split("\n")) {
3460
- const trimmed = line.trim();
3461
- if (!trimmed.startsWith("data:")) continue;
3462
- const payload = trimmed.slice(5).trim();
3463
- if (!payload || payload === "[DONE]") continue;
3464
- let parsed;
3465
- try {
3466
- parsed = JSON.parse(payload);
3467
- } catch {
3468
- continue;
3469
- }
3470
- if (parsed?.usage && typeof parsed.usage === "object") {
3471
- usage2 = parsed.usage;
3472
- }
3473
- const delta = parsed?.choices?.[0]?.delta;
3474
- if (!delta || typeof delta !== "object") continue;
3475
- const reasoningChunk = typeof delta.reasoning_content === "string" ? delta.reasoning_content : typeof delta.reasoning === "string" ? delta.reasoning : "";
3476
- if (reasoningChunk) reasoning += reasoningChunk;
3477
- if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
3478
- accumulateToolCallDelta(accumulatedToolCalls, delta.tool_calls);
3479
- }
3480
- const textChunk = typeof delta.content === "string" ? delta.content : "";
3481
- if (textChunk) {
3482
- content += textChunk;
3483
- args2.onTextDelta?.(textChunk);
3484
- }
3485
- }
3491
+ processOpenAiCompatibleSseEvent(event, state);
3486
3492
  }
3487
3493
  }
3488
- const tool_calls = finalizeAccumulatedToolCalls(accumulatedToolCalls);
3494
+ buffer += decoder.decode();
3495
+ if (buffer.trim()) {
3496
+ processOpenAiCompatibleSseEvent(buffer, state);
3497
+ }
3498
+ const tool_calls = finalizeAccumulatedToolCalls(state.accumulatedToolCalls);
3489
3499
  return {
3490
- content,
3491
- ...reasoning ? { reasoning_content: reasoning } : {},
3500
+ content: state.content,
3501
+ ...state.reasoning ? { reasoning_content: state.reasoning } : {},
3492
3502
  ...tool_calls.length > 0 ? { tool_calls } : {},
3493
- ...usage2 ? { usage: usage2 } : {}
3503
+ ...state.usage ? { usage: state.usage } : {}
3494
3504
  };
3495
3505
  }
3496
3506
  async function executeOpenAiCompatibleChatCompletion(args2) {
@@ -64754,7 +64764,9 @@ function createCliLocalRuntimeAdapter(deps) {
64754
64764
  const data2 = parsePlatformChatCompletionData(raw2);
64755
64765
  throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data2)}`);
64756
64766
  }
64757
- if (stream && options?.onTextDelta) {
64767
+ const contentType = res.headers.get("content-type") ?? "";
64768
+ const shouldStream = Boolean(stream && options?.onTextDelta) && contentType.includes("text/event-stream");
64769
+ if (shouldStream && options?.onTextDelta) {
64758
64770
  const streamed = await readOpenAiCompatibleSseCompletion({
64759
64771
  response: res,
64760
64772
  onTextDelta: options.onTextDelta
@@ -68114,6 +68126,64 @@ function formatAssistantDisplay(text, mode = "rich") {
68114
68126
  }
68115
68127
  return polished.split("\n").map((line) => styleRichMarkdownLine(line)).join("\n");
68116
68128
  }
68129
+ function emitFormattedAssistantBlock(write2, text, renderMode, trailingNewline = false) {
68130
+ if (!text) return;
68131
+ write2(formatAssistantDisplay(text, renderMode));
68132
+ if (trailingNewline) write2("\n");
68133
+ }
68134
+ function createRenderAwareStreamWriter(args2) {
68135
+ let buffer = "";
68136
+ const flushCompleteBlocks = () => {
68137
+ if (args2.renderMode === "plain") {
68138
+ if (!buffer) return;
68139
+ args2.write(buffer);
68140
+ buffer = "";
68141
+ return;
68142
+ }
68143
+ while (buffer.includes("\n")) {
68144
+ const lines = buffer.split("\n");
68145
+ if (lines.length < 2) break;
68146
+ if (isTableRow(lines[0] ?? "") && isTableSeparator(lines[1] ?? "")) {
68147
+ let end = 2;
68148
+ while (end < lines.length && isTableRow(lines[end] ?? "") && !isTableSeparator(lines[end] ?? "")) {
68149
+ end += 1;
68150
+ }
68151
+ const tableComplete = end < lines.length || buffer.endsWith("\n");
68152
+ if (!tableComplete) break;
68153
+ emitFormattedAssistantBlock(
68154
+ args2.write,
68155
+ lines.slice(0, end).join("\n"),
68156
+ args2.renderMode,
68157
+ true
68158
+ );
68159
+ buffer = lines.slice(end).join("\n");
68160
+ continue;
68161
+ }
68162
+ emitFormattedAssistantBlock(args2.write, lines[0] ?? "", args2.renderMode, true);
68163
+ buffer = lines.slice(1).join("\n");
68164
+ }
68165
+ };
68166
+ return {
68167
+ push(chunk) {
68168
+ if (!chunk) return;
68169
+ if (args2.renderMode === "plain") {
68170
+ args2.write(chunk);
68171
+ return;
68172
+ }
68173
+ buffer += chunk;
68174
+ flushCompleteBlocks();
68175
+ },
68176
+ flush() {
68177
+ if (!buffer) return;
68178
+ if (args2.renderMode === "plain") {
68179
+ args2.write(buffer);
68180
+ } else {
68181
+ emitFormattedAssistantBlock(args2.write, buffer, args2.renderMode);
68182
+ }
68183
+ buffer = "";
68184
+ }
68185
+ };
68186
+ }
68117
68187
 
68118
68188
  // packages/cli/client/thinkingOutput.ts
68119
68189
  var THINK_OPEN = /<think>/i;
@@ -68920,12 +68990,16 @@ async function runLocalAgentTurnForCli(options, settings) {
68920
68990
  const traceLocalTools = shouldEmitToolEvents(toolDisplayMode);
68921
68991
  const formatToolEvent = createToolEventFormatter(toolDisplayMode);
68922
68992
  const eventMode = resolveAgentEventMode(options);
68923
- let wroteToolTrace = false;
68924
68993
  let streamedAssistantText = false;
68925
68994
  let printedAssistantLabel = false;
68926
68995
  const thinkingMode = resolveThinkingDisplayMode(options.env);
68996
+ const renderMode = resolveRenderDisplayMode(options.env);
68997
+ const renderWriter = createRenderAwareStreamWriter({
68998
+ write: (chunk) => options.output.write(chunk),
68999
+ renderMode
69000
+ });
68927
69001
  const thinkingFilter = createThinkingAwareStreamFilter(
68928
- (chunk) => options.output.write(chunk),
69002
+ (chunk) => renderWriter.push(chunk),
68929
69003
  thinkingMode
68930
69004
  );
68931
69005
  const subjectRefs = buildSubjectRefs(options);
@@ -68959,7 +69033,6 @@ async function runLocalAgentTurnForCli(options, settings) {
68959
69033
  spinner.stop();
68960
69034
  const chunk = eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolEvent(event);
68961
69035
  if (chunk) {
68962
- wroteToolTrace = true;
68963
69036
  options.output.write(chunk);
68964
69037
  }
68965
69038
  }
@@ -68980,6 +69053,7 @@ ${options.agentName} > `);
68980
69053
  spinner.stop();
68981
69054
  if (streamedAssistantText) {
68982
69055
  thinkingFilter.flush();
69056
+ renderWriter.flush();
68983
69057
  options.output.write("\n");
68984
69058
  } else {
68985
69059
  const content = formatAssistantResponseForCli(result.content.trim(), options);
@@ -69017,8 +69091,13 @@ async function readStreamingAgentRun(options, res) {
69017
69091
  }
69018
69092
  const decoder = new TextDecoder();
69019
69093
  const thinkingMode = resolveThinkingDisplayMode(options.env);
69094
+ const renderMode = resolveRenderDisplayMode(options.env);
69095
+ const renderWriter = createRenderAwareStreamWriter({
69096
+ write: (chunk) => options.output.write(chunk),
69097
+ renderMode
69098
+ });
69020
69099
  const writer = createStreamingTextWriter({
69021
- write: (chunk) => options.output.write(chunk)
69100
+ write: (chunk) => renderWriter.push(chunk)
69022
69101
  });
69023
69102
  const thinkingFilter = createThinkingAwareStreamFilter(
69024
69103
  (chunk) => writer.push(chunk),
@@ -69091,6 +69170,7 @@ ${options.agentName} > `);
69091
69170
  } finally {
69092
69171
  writer.flushAll();
69093
69172
  thinkingFilter.flush();
69173
+ renderWriter.flush();
69094
69174
  }
69095
69175
  if (!content) {
69096
69176
  options.output.write(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nolo-cli",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
4
4
  "type": "module",
5
5
  "description": "Agent-first terminal workspace for Nolo",
6
6
  "bin": {