prior-cli 1.4.0 → 1.4.2
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 +5 -36
- package/lib/agent.js +7 -96
- package/package.json +1 -1
package/bin/prior.js
CHANGED
|
@@ -408,7 +408,7 @@ function renderToolDone(name, summary, preview) {
|
|
|
408
408
|
const more = lines.length - toShow.length;
|
|
409
409
|
if (toShow.length > 0) {
|
|
410
410
|
drawBox([
|
|
411
|
-
...toShow.map(l => ({ text: l.slice(0, 80), dim: true })),
|
|
411
|
+
...toShow.map(l => ({ text: l.replace(/\r/g, '').slice(0, 80), dim: true })),
|
|
412
412
|
...(more > 0 ? [{ text: `… ${more} more line${more !== 1 ? 's' : ''}`, dim: true }] : []),
|
|
413
413
|
]);
|
|
414
414
|
}
|
|
@@ -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
|
|
1028
|
-
|
|
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
|
|
11
|
+
// ── Single inference call ─────────────────────────────────────
|
|
12
12
|
|
|
13
|
-
async function infer(messages, model, token, { cwd, uncensored, projectContext, images } = {}, signal
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
347
|
-
|
|
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
|
|