prior-cli 1.2.5 → 1.2.7
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/lib/agent.js +29 -3
- package/lib/tools.js +50 -13
- package/package.json +1 -1
package/lib/agent.js
CHANGED
|
@@ -77,7 +77,7 @@ function parseToolCalls(text) {
|
|
|
77
77
|
} catch { /* skip */ }
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
// Fallback: <tool_name>{...}</tool_name>
|
|
80
|
+
// Fallback 1: <tool_name>{...}</tool_name>
|
|
81
81
|
for (const name of TOOL_NAMES) {
|
|
82
82
|
const fbRe = new RegExp(`<${name}>([\\s\\S]*?)<\\/${name}>`, 'g');
|
|
83
83
|
let fm;
|
|
@@ -93,6 +93,22 @@ function parseToolCalls(text) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// Fallback 2: <tool_name {"key":"val"}>...</tool_name> (JSON in opening tag)
|
|
97
|
+
for (const name of TOOL_NAMES) {
|
|
98
|
+
const fbRe = new RegExp(`<${name}\\s*({[\\s\\S]*?})\\s*>[\\s\\S]*?<\\/${name}>`, 'g');
|
|
99
|
+
let fm;
|
|
100
|
+
while ((fm = fbRe.exec(text)) !== null) {
|
|
101
|
+
const alreadyCaptured = calls.some(c => fm.index >= c.offset && fm.index < c.offset + c.raw.length);
|
|
102
|
+
if (alreadyCaptured) continue;
|
|
103
|
+
try {
|
|
104
|
+
const fixed = fixJsonLiterals(fm[1].trim());
|
|
105
|
+
const parsed = JSON.parse(fixed);
|
|
106
|
+
const { args, ...rest } = parsed || {};
|
|
107
|
+
calls.push({ raw: fm[0], offset: fm.index, name, args: args || (parsed && Object.keys(rest).length > 0 ? rest : {}) });
|
|
108
|
+
} catch { /* skip */ }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
96
112
|
return calls;
|
|
97
113
|
}
|
|
98
114
|
|
|
@@ -121,6 +137,16 @@ function stripThink(text) {
|
|
|
121
137
|
return text.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
|
122
138
|
}
|
|
123
139
|
|
|
140
|
+
// Strip any residual tool-call tags the model echoes in its text output
|
|
141
|
+
function stripToolTags(text) {
|
|
142
|
+
// <tool>...</tool>
|
|
143
|
+
let out = text.replace(/<tool>[\s\S]*?<\/tool>/gi, '');
|
|
144
|
+
// <tool_name {...}>...</tool_name> AND <tool_name>...</tool_name>
|
|
145
|
+
const namesPattern = TOOL_NAMES.join('|');
|
|
146
|
+
out = out.replace(new RegExp(`<(?:${namesPattern})[^>]*>[\\s\\S]*?<\\/(?:${namesPattern})>`, 'gi'), '');
|
|
147
|
+
return out.trim();
|
|
148
|
+
}
|
|
149
|
+
|
|
124
150
|
// ── Main agent loop ───────────────────────────────────────────
|
|
125
151
|
|
|
126
152
|
const CONFIRM_TOOLS = new Set(['run_command', 'file_delete', 'file_write']);
|
|
@@ -168,13 +194,13 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, send
|
|
|
168
194
|
// ── No tool calls → final answer ──────────────────────────
|
|
169
195
|
if (calls.length === 0) {
|
|
170
196
|
await trackTokenUsage(token, totalPromptTokens, totalCompletionTokens);
|
|
171
|
-
send({ type: 'text', content: cleaned });
|
|
197
|
+
send({ type: 'text', content: stripToolTags(cleaned) });
|
|
172
198
|
send({ type: 'done' });
|
|
173
199
|
return;
|
|
174
200
|
}
|
|
175
201
|
|
|
176
202
|
// ── Text before first tool call ───────────────────────────
|
|
177
|
-
const textBefore = cleaned.slice(0, calls[0].offset).trim();
|
|
203
|
+
const textBefore = stripToolTags(cleaned.slice(0, calls[0].offset)).trim();
|
|
178
204
|
if (textBefore) send({ type: 'text', content: textBefore });
|
|
179
205
|
|
|
180
206
|
history.push({ role: 'assistant', content: raw });
|
package/lib/tools.js
CHANGED
|
@@ -225,23 +225,60 @@ const TOOLS = {
|
|
|
225
225
|
};
|
|
226
226
|
},
|
|
227
227
|
|
|
228
|
-
async generate_image({ prompt, width, height, steps }, { cwd, token }) {
|
|
228
|
+
async generate_image({ prompt, width, height, steps }, { cwd, token, send }) {
|
|
229
229
|
if (!prompt) throw new Error('"prompt" is required');
|
|
230
|
-
|
|
231
|
-
const
|
|
230
|
+
const totalSteps = steps || 20;
|
|
231
|
+
const authHdr = token ? { Authorization: `Bearer ${token}` } : {};
|
|
232
|
+
|
|
233
|
+
// Step 1: Queue
|
|
234
|
+
const queueRes = await fetch(`${CLI_BASE}/api/generate-image/queue`, {
|
|
232
235
|
method: 'POST',
|
|
233
|
-
headers: {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
},
|
|
237
|
-
body: JSON.stringify({ prompt, width: width || 896, height: height || 896, steps: steps || 20 }),
|
|
238
|
-
timeout: 360000,
|
|
236
|
+
headers: { 'Content-Type': 'application/json', ...authHdr },
|
|
237
|
+
body: JSON.stringify({ prompt, width: width || 896, height: height || 896, steps: totalSteps }),
|
|
238
|
+
timeout: 15000,
|
|
239
239
|
});
|
|
240
|
-
if (!
|
|
241
|
-
const err = await
|
|
242
|
-
throw new Error(err.error || err.message || `HTTP ${
|
|
240
|
+
if (!queueRes.ok) {
|
|
241
|
+
const err = await queueRes.json().catch(() => ({}));
|
|
242
|
+
throw new Error(err.error || err.message || `HTTP ${queueRes.status}`);
|
|
243
243
|
}
|
|
244
|
-
const
|
|
244
|
+
const { promptId } = await queueRes.json();
|
|
245
|
+
if (!promptId) throw new Error('No promptId returned from image queue');
|
|
246
|
+
|
|
247
|
+
// Step 2: Poll with progress
|
|
248
|
+
let job = null;
|
|
249
|
+
for (let i = 0; i < 240; i++) {
|
|
250
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
251
|
+
try {
|
|
252
|
+
const pr = await fetch(`${CLI_BASE}/api/generate-image/progress/${promptId}`, {
|
|
253
|
+
headers: authHdr, timeout: 5000,
|
|
254
|
+
});
|
|
255
|
+
if (pr.ok) {
|
|
256
|
+
job = await pr.json();
|
|
257
|
+
if (job && send && job.step !== undefined) {
|
|
258
|
+
const pct = job.percent || Math.round((job.step / (job.total || totalSteps)) * 100);
|
|
259
|
+
send({ type: 'tool_progress', step: job.step, total: job.total || totalSteps, percent: pct });
|
|
260
|
+
}
|
|
261
|
+
if (job && (job.status === 'done' || job.status === 'error')) break;
|
|
262
|
+
}
|
|
263
|
+
} catch { /* keep polling */ }
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (!job || job.status === 'error') {
|
|
267
|
+
throw new Error(job?.error || 'Image generation failed or timed out');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Step 3: Watermark
|
|
271
|
+
const wmRes = await fetch(`${CLI_BASE}/api/generate-image/watermark`, {
|
|
272
|
+
method: 'POST',
|
|
273
|
+
headers: { 'Content-Type': 'application/json', ...authHdr },
|
|
274
|
+
body: JSON.stringify({ filename: job.filename }),
|
|
275
|
+
timeout: 30000,
|
|
276
|
+
});
|
|
277
|
+
if (!wmRes.ok) {
|
|
278
|
+
const err = await wmRes.json().catch(() => ({}));
|
|
279
|
+
throw new Error(err.error || `Watermark failed: HTTP ${wmRes.status}`);
|
|
280
|
+
}
|
|
281
|
+
const data = await wmRes.json();
|
|
245
282
|
if (!data.filename || !data.data) throw new Error('Invalid response from image generation service');
|
|
246
283
|
const savePath = path.join(cwd, data.filename);
|
|
247
284
|
fs.writeFileSync(savePath, Buffer.from(data.data, 'base64'));
|