@vtstech/pi-react-fallback 1.0.7 → 1.0.8
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/package.json +2 -2
- package/react-fallback.js +87 -56
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtstech/pi-react-fallback",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "ReAct fallback extension for Pi Coding Agent",
|
|
5
5
|
"main": "react-fallback.js",
|
|
6
6
|
"keywords": ["pi-extensions"],
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"url": "https://github.com/VTSTech/pi-coding-agent"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@vtstech/pi-shared": "1.0.
|
|
17
|
+
"@vtstech/pi-shared": "1.0.8"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@mariozechner/pi-coding-agent": ">=0.66"
|
package/react-fallback.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// .build-npm/react-fallback/react-fallback.temp.ts
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
2
5
|
import { section, ok, fail, warn, info } from "@vtstech/pi-shared/format";
|
|
3
6
|
function sanitizeModelJson(text) {
|
|
4
7
|
text = text.replace(/:\s*True\b/g, ": true");
|
|
@@ -273,82 +276,103 @@ function looksLikeSchemaDump(text) {
|
|
|
273
276
|
const matches = indicators.filter((i) => lower.includes(i.toLowerCase())).length;
|
|
274
277
|
return matches >= 2;
|
|
275
278
|
}
|
|
279
|
+
var REACT_CONFIG_PATH = path.join(os.homedir(), ".pi", "agent", "react-mode.json");
|
|
280
|
+
function readReactConfig() {
|
|
281
|
+
try {
|
|
282
|
+
if (fs.existsSync(REACT_CONFIG_PATH)) {
|
|
283
|
+
const raw = JSON.parse(fs.readFileSync(REACT_CONFIG_PATH, "utf-8"));
|
|
284
|
+
if (typeof raw.enabled === "boolean") return raw;
|
|
285
|
+
}
|
|
286
|
+
} catch {
|
|
287
|
+
}
|
|
288
|
+
return { enabled: false };
|
|
289
|
+
}
|
|
290
|
+
function writeReactConfig(config) {
|
|
291
|
+
const dir = path.dirname(REACT_CONFIG_PATH);
|
|
292
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
293
|
+
fs.writeFileSync(REACT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
294
|
+
}
|
|
276
295
|
function react_fallback_temp_default(pi2) {
|
|
277
|
-
let reactModeEnabled =
|
|
296
|
+
let reactModeEnabled = readReactConfig().enabled;
|
|
278
297
|
let stats = { bridgeCalls: 0, fuzzyMatches: 0, argNormalizations: 0, parseFailures: 0 };
|
|
279
298
|
const branding = [
|
|
280
|
-
` \u26A1 Pi ReAct Fallback Extension v1.0.
|
|
299
|
+
` \u26A1 Pi ReAct Fallback Extension v1.0.8`,
|
|
281
300
|
` Written by VTSTech`,
|
|
282
301
|
` GitHub: https://github.com/VTSTech`,
|
|
283
302
|
` Website: www.vts-tech.org`
|
|
284
303
|
].join("\n");
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
304
|
+
function registerBridgeTool() {
|
|
305
|
+
pi2.registerTool({
|
|
306
|
+
name: "tool_call",
|
|
307
|
+
label: "Universal Tool Call",
|
|
308
|
+
description: `Universal tool call bridge. Use this to call any available tool by specifying its name and arguments as JSON.
|
|
289
309
|
|
|
290
310
|
To use: call tool_call with:
|
|
291
311
|
- name: the exact tool name (e.g. "bash", "read", "write", "edit")
|
|
292
312
|
- arguments: a JSON string of the tool's arguments (e.g. '{"command": "ls -la"}')
|
|
293
313
|
|
|
294
314
|
The bridge will match your tool name (fuzzy matching supported) and normalize argument names automatically.`,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
315
|
+
promptSnippet: "tool_call - universal bridge for calling any tool",
|
|
316
|
+
promptGuidelines: [
|
|
317
|
+
"When you need to use a tool but are unsure of the exact name, use tool_call with the tool name and arguments.",
|
|
318
|
+
`Example: tool_call(name='bash', arguments='{"command": "ls -la"}')`
|
|
319
|
+
],
|
|
320
|
+
parameters: {
|
|
321
|
+
type: "object",
|
|
322
|
+
properties: {
|
|
323
|
+
name: { type: "string", description: "Name of the tool to call (fuzzy matching supported)" },
|
|
324
|
+
arguments: { type: "string", description: "Tool arguments as a JSON object string" }
|
|
325
|
+
},
|
|
326
|
+
required: ["name", "arguments"]
|
|
305
327
|
},
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
328
|
+
execute: async (toolCallId, params, signal, onUpdate, ctx) => {
|
|
329
|
+
const p = params;
|
|
330
|
+
const requestedName = p.name || "";
|
|
331
|
+
const argsStr = p.arguments || "{}";
|
|
332
|
+
stats.bridgeCalls++;
|
|
333
|
+
let args;
|
|
334
|
+
try {
|
|
335
|
+
args = JSON.parse(argsStr);
|
|
336
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
337
|
+
args = { input: argsStr };
|
|
338
|
+
}
|
|
339
|
+
} catch {
|
|
317
340
|
args = { input: argsStr };
|
|
318
341
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
342
|
+
const allTools = pi2.getAllTools();
|
|
343
|
+
let targetToolName = null;
|
|
344
|
+
if (allTools.includes(requestedName)) {
|
|
345
|
+
targetToolName = requestedName;
|
|
346
|
+
} else {
|
|
347
|
+
targetToolName = fuzzyMatchToolName(requestedName, allTools);
|
|
348
|
+
if (targetToolName) stats.fuzzyMatches++;
|
|
349
|
+
}
|
|
350
|
+
if (!targetToolName) {
|
|
351
|
+
stats.parseFailures++;
|
|
352
|
+
return {
|
|
353
|
+
content: [{ type: "text", text: `Error: Unknown tool "${requestedName}". Available tools: ${allTools.join(", ")}` }],
|
|
354
|
+
isError: true
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
const normalizedArgs = Object.keys(args).length > 0 ? args : {};
|
|
358
|
+
stats.argNormalizations++;
|
|
359
|
+
const argsJson = JSON.stringify(normalizedArgs);
|
|
332
360
|
return {
|
|
333
|
-
content: [{
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
const normalizedArgs = Object.keys(args).length > 0 ? args : {};
|
|
338
|
-
stats.argNormalizations++;
|
|
339
|
-
const argsJson = JSON.stringify(normalizedArgs);
|
|
340
|
-
return {
|
|
341
|
-
content: [{
|
|
342
|
-
type: "text",
|
|
343
|
-
text: `[ReAct Bridge] Tool resolved: ${requestedName} \u2192 ${targetToolName}${targetToolName !== requestedName ? " (fuzzy matched)" : ""}
|
|
361
|
+
content: [{
|
|
362
|
+
type: "text",
|
|
363
|
+
text: `[ReAct Bridge] Tool resolved: ${requestedName} \u2192 ${targetToolName}${targetToolName !== requestedName ? " (fuzzy matched)" : ""}
|
|
344
364
|
|
|
345
365
|
Please call ${targetToolName} with these arguments:
|
|
346
366
|
${argsJson}`
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
367
|
+
}],
|
|
368
|
+
isError: false
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
if (reactModeEnabled) {
|
|
374
|
+
registerBridgeTool();
|
|
375
|
+
}
|
|
352
376
|
pi2.on("context", (event) => {
|
|
353
377
|
if (!reactModeEnabled) return;
|
|
354
378
|
const model = event.messages;
|
|
@@ -367,18 +391,25 @@ ${argsJson}`
|
|
|
367
391
|
description: "Toggle ReAct fallback mode for models without native tool calling",
|
|
368
392
|
handler: async (_args, ctx) => {
|
|
369
393
|
reactModeEnabled = !reactModeEnabled;
|
|
394
|
+
writeReactConfig({ enabled: reactModeEnabled });
|
|
370
395
|
const status = reactModeEnabled ? "ENABLED" : "DISABLED";
|
|
371
396
|
ctx.ui.notify(`ReAct mode ${status}`, "success");
|
|
372
397
|
const lines = [branding];
|
|
373
398
|
lines.push(section("REACT FALLBACK MODE"));
|
|
374
399
|
lines.push(info(`Status: ${status}`));
|
|
400
|
+
lines.push(info(`Config: ${REACT_CONFIG_PATH}`));
|
|
375
401
|
lines.push(info(`Bridge calls: ${stats.bridgeCalls}`));
|
|
376
402
|
lines.push(info(`Fuzzy matches: ${stats.fuzzyMatches}`));
|
|
377
403
|
lines.push(info(`Argument normalizations: ${stats.argNormalizations}`));
|
|
378
404
|
lines.push(info(`Parse failures: ${stats.parseFailures}`));
|
|
379
405
|
if (reactModeEnabled) {
|
|
406
|
+
registerBridgeTool();
|
|
380
407
|
lines.push(ok("The tool_call bridge tool is now available to the model"));
|
|
381
408
|
lines.push(info("ReAct system prompt instructions have been added"));
|
|
409
|
+
lines.push(info("Run /reload to make the bridge tool available to the current model"));
|
|
410
|
+
} else {
|
|
411
|
+
lines.push(warn("The tool_call bridge tool has been unregistered"));
|
|
412
|
+
lines.push(info("Run /reload to remove the tool from the current model"));
|
|
382
413
|
}
|
|
383
414
|
const report = lines.join("\n");
|
|
384
415
|
pi2.sendMessage({
|