prior-cli 1.4.0 → 1.4.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.
package/bin/prior.js CHANGED
@@ -927,7 +927,7 @@ Keep it under 350 words. Write prior.md now.`;
927
927
  renderToolStart(ev.name, ev.args);
928
928
  spinStart('working…');
929
929
  break;
930
- case 'tool_done': spinStop(); renderToolDone(ev.name, ev.summary); break;
930
+ case 'tool_done': spinStop(); renderToolDone(ev.name, ev.summary, ev.preview); break;
931
931
  case 'tool_error': spinStop(); renderToolError(ev.name, ev.error); break;
932
932
  case 'text':
933
933
  spinStop();
@@ -1023,10 +1023,9 @@ Keep it under 350 words. Write prior.md now.`;
1023
1023
  console.log(c.brand(' ◈') + c.dim(` ${label} attached`));
1024
1024
  }
1025
1025
 
1026
- let responseText = '';
1027
- let _progressStarted = false;
1028
- let _streamStarted = false; // true after first text_chunk
1029
- const _thinkStart = Date.now();
1026
+ let responseText = '';
1027
+ let _progressStarted = false;
1028
+ const _thinkStart = Date.now();
1030
1029
 
1031
1030
  spinStart('thinking…');
1032
1031
 
@@ -1059,42 +1058,13 @@ Keep it under 350 words. Write prior.md now.`;
1059
1058
  spinStart('thinking…');
1060
1059
  break;
1061
1060
 
1062
- case 'waiting':
1063
- spinStart(`waiting for Ollama… (${ev.attempt}/${ev.max})`);
1064
- break;
1065
-
1066
1061
  case 'cancelled':
1067
1062
  spinStop();
1068
- if (_streamStarted) process.stdout.write('\n');
1069
1063
  console.log(c.muted(' ✗ Cancelled'));
1070
1064
  break;
1071
1065
 
1072
- // ── Streaming text events ──────────────────────
1073
- case 'stream_start': {
1074
- spinStop();
1075
- const thinkTime = elapsed(Date.now() - _thinkStart);
1076
- console.log(c.brand(' Prior ') + c.muted(`· ${timeNow()} · ${thinkTime}`));
1077
- console.log('');
1078
- process.stdout.write(' ');
1079
- _streamStarted = true;
1080
- break;
1081
- }
1082
-
1083
- case 'text_chunk':
1084
- if (ev.content) {
1085
- process.stdout.write(ev.content);
1086
- responseText += ev.content;
1087
- }
1088
- break;
1089
-
1090
- case 'stream_end':
1091
- process.stdout.write('\n');
1092
- _streamStarted = false;
1093
- break;
1094
-
1095
1066
  case 'tool_start':
1096
1067
  spinStop();
1097
- if (_streamStarted) { process.stdout.write('\n'); _streamStarted = false; }
1098
1068
  _progressStarted = false;
1099
1069
  renderToolStart(ev.name, ev.args);
1100
1070
  if (!CONFIRM_TOOLS.has(ev.name)) spinStart('working…');
@@ -1148,7 +1118,6 @@ Keep it under 350 words. Write prior.md now.`;
1148
1118
 
1149
1119
  case 'error':
1150
1120
  spinStop();
1151
- if (_streamStarted) process.stdout.write('\n');
1152
1121
  console.error(c.err(` ✗ ${ev.message}`));
1153
1122
  break;
1154
1123
  }
package/lib/agent.js CHANGED
@@ -8,9 +8,9 @@ const CLI_BASE = 'https://prior.ngrok.app/cli-backend';
8
8
  const PRIOR_BASE = 'https://prior.ngrok.app';
9
9
  const MAX_ITER = 14;
10
10
 
11
- // ── Single inference call — reads NDJSON stream from backend ──
11
+ // ── Single inference call ─────────────────────────────────────
12
12
 
13
- async function infer(messages, model, token, { cwd, uncensored, projectContext, images } = {}, signal, onChunk) {
13
+ async function infer(messages, model, token, { cwd, uncensored, projectContext, images } = {}, signal) {
14
14
  const res = await fetch(`${CLI_BASE}/api/infer`, {
15
15
  method: 'POST',
16
16
  headers: { 'Content-Type': 'application/json' },
@@ -18,49 +18,11 @@ async function infer(messages, model, token, { cwd, uncensored, projectContext,
18
18
  timeout: 120000,
19
19
  signal,
20
20
  });
21
-
22
21
  if (!res.ok) {
23
22
  const err = await res.json().catch(() => ({}));
24
23
  throw new Error(err.error || `Server error: HTTP ${res.status}`);
25
24
  }
26
-
27
- let content = '';
28
- let promptTokens = 0;
29
- let completionTokens = 0;
30
-
31
- await new Promise((resolve, reject) => {
32
- let buf = '';
33
- res.body.on('data', rawChunk => {
34
- if (signal?.aborted) {
35
- res.body.destroy();
36
- return reject(Object.assign(new Error('AbortError'), { name: 'AbortError' }));
37
- }
38
- buf += rawChunk.toString();
39
- const lines = buf.split('\n');
40
- buf = lines.pop();
41
- for (const line of lines) {
42
- if (!line.trim()) continue;
43
- try {
44
- const data = JSON.parse(line);
45
- if (data.type === 'chunk') {
46
- if (onChunk) onChunk(data.content);
47
- } else if (data.type === 'done') {
48
- content = data.content || '';
49
- promptTokens = data.promptTokens || 0;
50
- completionTokens = data.completionTokens || 0;
51
- } else if (data.type === 'waiting') {
52
- if (onChunk) onChunk(null, { type: 'waiting', attempt: data.attempt, max: data.max });
53
- } else if (data.type === 'error') {
54
- reject(new Error(data.message));
55
- }
56
- } catch { /* skip malformed line */ }
57
- }
58
- });
59
- res.body.on('end', resolve);
60
- res.body.on('error', reject);
61
- });
62
-
63
- return { content, promptTokens, completionTokens };
25
+ return await res.json();
64
26
  }
65
27
 
66
28
  // ── Token usage tracking ──────────────────────────────────────
@@ -252,57 +214,12 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, imag
252
214
 
253
215
  send({ type: 'thinking' });
254
216
 
255
- // ── Per-iteration streaming state ─────────────────────────
256
- // After </think> we buffer LOOK_SIZE chars to detect tool calls before
257
- // deciding whether to stream text live or stay in buffered mode.
258
- let thinkBuf = ''; // chunks buffered while inside <think>…</think>
259
- let thinkDone = false;
260
- let lookBuf = ''; // first N chars of actual response (after think)
261
- let streaming = false; // true once we've committed to live-streaming text
262
- const LOOK_SIZE = 60;
263
-
264
- function tryStartStreaming() {
265
- if (streaming) return;
266
- const trimmed = lookBuf.replace(/^[\s\n]+/, '');
267
- // If the response starts with a tool tag, keep buffered (no live text)
268
- if (/^<(?:tool|write|append|docx)[\s>{"[]/.test(trimmed)) return;
269
- streaming = true;
270
- send({ type: 'stream_start' });
271
- if (trimmed) send({ type: 'text_chunk', content: trimmed });
272
- lookBuf = '';
273
- }
274
-
275
- const onChunk = (raw, meta) => {
276
- if (meta?.type === 'waiting') { send({ type: 'waiting', attempt: meta.attempt, max: meta.max }); return; }
277
- if (!raw) return;
278
-
279
- if (!thinkDone) {
280
- thinkBuf += raw;
281
- const idx = thinkBuf.indexOf('</think>');
282
- if (idx !== -1) {
283
- thinkDone = true;
284
- lookBuf = thinkBuf.slice(idx + 8).replace(/^[\s\n]+/, '');
285
- thinkBuf = '';
286
- if (lookBuf.length >= LOOK_SIZE) tryStartStreaming();
287
- }
288
- return;
289
- }
290
-
291
- if (!streaming) {
292
- lookBuf += raw;
293
- if (lookBuf.length >= LOOK_SIZE) tryStartStreaming();
294
- return;
295
- }
296
-
297
- send({ type: 'text_chunk', content: raw });
298
- };
299
-
300
217
  const iterImages = pendingImages;
301
218
  pendingImages = null;
302
219
 
303
220
  let result;
304
221
  try {
305
- result = await infer(history, model || 'qwen3.5:4b', token, { cwd, uncensored, projectContext, images: iterImages }, signal, onChunk);
222
+ result = await infer(history, model || 'qwen3.5:4b', token, { cwd, uncensored, projectContext, images: iterImages }, signal);
306
223
  } catch (err) {
307
224
  await trackTokenUsage(token, totalPromptTokens, totalCompletionTokens);
308
225
  if (err.name === 'AbortError' || signal?.aborted) { send({ type: 'cancelled' }); send({ type: 'done' }); return; }
@@ -311,10 +228,6 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, imag
311
228
  return;
312
229
  }
313
230
 
314
- // Flush look-ahead if stream ended before LOOK_SIZE was reached
315
- if (thinkDone && !streaming && lookBuf) tryStartStreaming();
316
- if (streaming) send({ type: 'stream_end' });
317
-
318
231
  totalPromptTokens += result.promptTokens || 0;
319
232
  totalCompletionTokens += result.completionTokens || 0;
320
233
 
@@ -337,16 +250,14 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, imag
337
250
  continue;
338
251
  }
339
252
  await trackTokenUsage(token, totalPromptTokens, totalCompletionTokens);
340
- if (!streaming) send({ type: 'text', content: finalText }); // already shown if streaming
253
+ send({ type: 'text', content: finalText });
341
254
  send({ type: 'done' });
342
255
  return;
343
256
  }
344
257
 
345
258
  // ── Text before first tool call ───────────────────────────
346
- if (!streaming) {
347
- const textBefore = stripToolTags(cleaned.slice(0, calls[0].offset)).trim();
348
- if (textBefore) send({ type: 'text', content: textBefore });
349
- }
259
+ const textBefore = stripToolTags(cleaned.slice(0, calls[0].offset)).trim();
260
+ if (textBefore) send({ type: 'text', content: textBefore });
350
261
 
351
262
  history.push({ role: 'assistant', content: raw });
352
263
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prior-cli",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "Prior Network AI — command-line interface",
5
5
  "bin": {
6
6
  "prior": "bin/prior.js"