prior-cli 1.3.3 → 1.3.5
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 +3 -40
- package/lib/agent.js +52 -3
- package/package.json +1 -1
package/bin/prior.js
CHANGED
|
@@ -568,7 +568,7 @@ async function startChat(opts = {}) {
|
|
|
568
568
|
terminal: true,
|
|
569
569
|
historySize: 100,
|
|
570
570
|
completer: line => {
|
|
571
|
-
const cmds = ['/help', '/clear', '/
|
|
571
|
+
const cmds = ['/help', '/clear', '/censored', '/uncensored', '/login', '/logout', '/exit'];
|
|
572
572
|
if (!line.startsWith('/')) return [[], line];
|
|
573
573
|
const hits = cmds.filter(cmd => cmd.startsWith(line));
|
|
574
574
|
return [hits, line];
|
|
@@ -599,7 +599,7 @@ async function startChat(opts = {}) {
|
|
|
599
599
|
console.log(c.ok(' ◉') + c.muted(' Agent mode ') + c.dim('· file web shell image prior-network'));
|
|
600
600
|
|
|
601
601
|
console.log(DIVIDER);
|
|
602
|
-
console.log(c.muted(' /help /clear /
|
|
602
|
+
console.log(c.muted(' /help /clear /censored /uncensored /exit'));
|
|
603
603
|
console.log(DIVIDER);
|
|
604
604
|
console.log('');
|
|
605
605
|
|
|
@@ -617,9 +617,7 @@ async function startChat(opts = {}) {
|
|
|
617
617
|
const SLASH_CMDS = [
|
|
618
618
|
{ cmd: '/help', desc: 'Show help' },
|
|
619
619
|
{ cmd: '/clear', desc: 'Clear screen' },
|
|
620
|
-
{ cmd: '/
|
|
621
|
-
{ cmd: '/tools', desc: 'List tools' },
|
|
622
|
-
{ cmd: '/censored', desc: 'Load standard model (qwen)' },
|
|
620
|
+
{ cmd: '/censored', desc: 'Load standard model (qwen)' },
|
|
623
621
|
{ cmd: '/uncensored', desc: 'Load uncensored model' },
|
|
624
622
|
{ cmd: '/usage', desc: 'Token usage today' },
|
|
625
623
|
{ cmd: '/learn', desc: 'Learn this directory → prior.md' },
|
|
@@ -759,39 +757,6 @@ async function startChat(opts = {}) {
|
|
|
759
757
|
console.log(c.warn(' ✓ Prior Uncensored Model Loaded\n'));
|
|
760
758
|
return loop();
|
|
761
759
|
|
|
762
|
-
case '/model':
|
|
763
|
-
if (args[0]) {
|
|
764
|
-
currentModel = args[0];
|
|
765
|
-
console.log(c.ok(` ✓ Model → ${currentModel}\n`));
|
|
766
|
-
} else {
|
|
767
|
-
console.log(c.muted(` Current model: ${currentModel || 'default'}\n`));
|
|
768
|
-
}
|
|
769
|
-
return loop();
|
|
770
|
-
|
|
771
|
-
case '/tools':
|
|
772
|
-
console.log('');
|
|
773
|
-
console.log(c.bold(' Tools available in agent mode\n'));
|
|
774
|
-
const tools = [
|
|
775
|
-
['file_read', 'Read a file'],
|
|
776
|
-
['file_write', 'Create or overwrite a file'],
|
|
777
|
-
['file_append', 'Append to a file'],
|
|
778
|
-
['file_list', 'List directory contents'],
|
|
779
|
-
['file_delete', 'Delete a file'],
|
|
780
|
-
['web_search', 'Search the web (Google)'],
|
|
781
|
-
['url_fetch', 'Fetch a webpage (Puppeteer)'],
|
|
782
|
-
['run_command', 'Execute a shell command'],
|
|
783
|
-
['clipboard_read', 'Read from clipboard'],
|
|
784
|
-
['clipboard_write', 'Write to clipboard'],
|
|
785
|
-
['generate_image', 'Generate an image (Prior Diffusion)'],
|
|
786
|
-
['prior_feed', 'Get Prior Network feed'],
|
|
787
|
-
['prior_profile', 'Get your Prior Network profile'],
|
|
788
|
-
];
|
|
789
|
-
tools.forEach(([name, desc]) => {
|
|
790
|
-
console.log(` ${c.brand('▸')} ${c.bold(name.padEnd(16))} ${c.muted(desc)}`);
|
|
791
|
-
});
|
|
792
|
-
console.log('');
|
|
793
|
-
return loop();
|
|
794
|
-
|
|
795
760
|
case '/learn': {
|
|
796
761
|
// Sanity check — warn if not a project directory
|
|
797
762
|
const cwdBase = path.basename(process.cwd()).toLowerCase();
|
|
@@ -946,8 +911,6 @@ Keep it under 350 words. Write prior.md now.`;
|
|
|
946
911
|
console.log(c.muted(' /clear ') + 'Clear screen');
|
|
947
912
|
console.log(c.muted(' /censored ') + 'Load standard model (qwen)');
|
|
948
913
|
console.log(c.muted(' /uncensored ') + 'Load uncensored model (dolphin)');
|
|
949
|
-
console.log(c.muted(' /model <name> ') + 'Switch to any specific model');
|
|
950
|
-
console.log(c.muted(' /tools ') + 'List available tools');
|
|
951
914
|
console.log(c.muted(' /usage ') + 'Token usage for today');
|
|
952
915
|
console.log(c.muted(' /learn ') + 'Scan directory and write prior.md context file');
|
|
953
916
|
console.log(c.muted(' /login ') + 'Sign in to a different account');
|
package/lib/agent.js
CHANGED
|
@@ -77,6 +77,22 @@ function parseToolCalls(text) {
|
|
|
77
77
|
} catch { /* skip */ }
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
// Primary variant: <tool name="X">{"args"}</tool> (name as attribute — used by some models)
|
|
81
|
+
const reAttr = /<tool\s+name="([^"]+)"[^>]*>([\s\S]*?)<\/tool>/g;
|
|
82
|
+
while ((m = reAttr.exec(text)) !== null) {
|
|
83
|
+
const alreadyCaptured = calls.some(c => m.index >= c.offset && m.index < c.offset + c.raw.length);
|
|
84
|
+
if (alreadyCaptured) continue;
|
|
85
|
+
const toolName = m[1];
|
|
86
|
+
try {
|
|
87
|
+
const body = m[2].trim();
|
|
88
|
+
const parsed = body ? JSON.parse(fixJsonLiterals(body)) : {};
|
|
89
|
+
const { args, ...rest } = parsed;
|
|
90
|
+
calls.push({ raw: m[0], offset: m.index, name: toolName, args: args || (Object.keys(rest).length > 0 ? rest : {}) });
|
|
91
|
+
} catch {
|
|
92
|
+
calls.push({ raw: m[0], offset: m.index, name: toolName, args: {} });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
80
96
|
// Fallback 1: <tool_name>{...}</tool_name>
|
|
81
97
|
for (const name of TOOL_NAMES) {
|
|
82
98
|
const fbRe = new RegExp(`<${name}>([\\s\\S]*?)<\\/${name}>`, 'g');
|
|
@@ -109,6 +125,21 @@ function parseToolCalls(text) {
|
|
|
109
125
|
}
|
|
110
126
|
}
|
|
111
127
|
|
|
128
|
+
// Fallback 3: <tool_name key="val" ...>...</tool_name> (HTML attribute style — used by dolphin)
|
|
129
|
+
for (const name of TOOL_NAMES) {
|
|
130
|
+
const fbRe = new RegExp(`<${name}((?:\\s+[a-zA-Z_]\\w*="[^"]*")+)\\s*(?:>[\\s\\S]*?<\\/${name}>|/>)`, 'g');
|
|
131
|
+
let fm;
|
|
132
|
+
while ((fm = fbRe.exec(text)) !== null) {
|
|
133
|
+
const alreadyCaptured = calls.some(c => fm.index >= c.offset && fm.index < c.offset + c.raw.length);
|
|
134
|
+
if (alreadyCaptured) continue;
|
|
135
|
+
const args = {};
|
|
136
|
+
const attrRe = /([a-zA-Z_]\w*)="([^"]*)"/g;
|
|
137
|
+
let am;
|
|
138
|
+
while ((am = attrRe.exec(fm[1])) !== null) args[am[1]] = am[2];
|
|
139
|
+
calls.push({ raw: fm[0], offset: fm.index, name, args });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
112
143
|
return calls;
|
|
113
144
|
}
|
|
114
145
|
|
|
@@ -137,13 +168,31 @@ function stripThink(text) {
|
|
|
137
168
|
return text.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
|
138
169
|
}
|
|
139
170
|
|
|
171
|
+
// Some models (dolphin) hallucinate fake conversation turns after their response.
|
|
172
|
+
// Truncate at common boundary markers to prevent those being parsed as real tool calls.
|
|
173
|
+
function truncateAtFakeTurn(text) {
|
|
174
|
+
const MARKERS = [
|
|
175
|
+
/\n[-─]{3,}\s*\n+(?:user|human)\b/i,
|
|
176
|
+
/\n+(?:user|human)\s*:\s/i,
|
|
177
|
+
/\n+(?:assistant|prior)\s*:\s/i,
|
|
178
|
+
];
|
|
179
|
+
let cut = text.length;
|
|
180
|
+
for (const re of MARKERS) {
|
|
181
|
+
const m = re.exec(text);
|
|
182
|
+
if (m && m.index < cut) cut = m.index;
|
|
183
|
+
}
|
|
184
|
+
return text.slice(0, cut);
|
|
185
|
+
}
|
|
186
|
+
|
|
140
187
|
// Strip any residual tool-call tags the model echoes in its text output
|
|
141
188
|
function stripToolTags(text) {
|
|
142
189
|
// <tool>...</tool>
|
|
143
190
|
let out = text.replace(/<tool>[\s\S]*?<\/tool>/gi, '');
|
|
144
|
-
// <tool_name {...}>...</tool_name> AND <tool_name>...</tool_name>
|
|
145
191
|
const namesPattern = TOOL_NAMES.join('|');
|
|
192
|
+
// <tool_name ...>...</tool_name> (with or without attributes/JSON)
|
|
146
193
|
out = out.replace(new RegExp(`<(?:${namesPattern})[^>]*>[\\s\\S]*?<\\/(?:${namesPattern})>`, 'gi'), '');
|
|
194
|
+
// Self-closing or unclosed: <tool_name attr="val" /> or <tool_name attr="val">
|
|
195
|
+
out = out.replace(new RegExp(`<(?:${namesPattern})(?:\\s[^>]*)?\\s*/?>`, 'gi'), '');
|
|
147
196
|
return out.trim();
|
|
148
197
|
}
|
|
149
198
|
|
|
@@ -187,8 +236,8 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, send
|
|
|
187
236
|
totalCompletionTokens += result.completionTokens || 0;
|
|
188
237
|
|
|
189
238
|
const raw = result.content;
|
|
190
|
-
const cleaned = stripThink(raw)
|
|
191
|
-
.replace(/<tool_result[\s\S]*?<\/tool_result>/gi, '')
|
|
239
|
+
const cleaned = truncateAtFakeTurn(stripThink(raw)
|
|
240
|
+
.replace(/<tool_result[\s\S]*?<\/tool_result>/gi, ''))
|
|
192
241
|
.trim();
|
|
193
242
|
|
|
194
243
|
const calls = [
|