u
This commit is contained in:
26
src/fido.js
26
src/fido.js
@@ -35,7 +35,7 @@ import { loadSuspends, addSuspend, getSuspends } from './suspend.js';
|
|||||||
import { tools, toolCallNotifications, toolResultNotifications } from './tools/index.js';
|
import { tools, toolCallNotifications, toolResultNotifications } from './tools/index.js';
|
||||||
import { printToolCall, emitGenericToolResult } from './tools/notify.js';
|
import { printToolCall, emitGenericToolResult } from './tools/notify.js';
|
||||||
import { autocompleteInput } from './autocomplete-input.js';
|
import { autocompleteInput } from './autocomplete-input.js';
|
||||||
import { COMMANDS, findCommand } from './slash-commands.js';
|
import { COMMANDS, REASONING_LEVELS, findCommand } from './slash-commands.js';
|
||||||
import { compactPatchToolCalls, invalidateStaleReadResults } from './compact.js';
|
import { compactPatchToolCalls, invalidateStaleReadResults } from './compact.js';
|
||||||
|
|
||||||
// ─── Markdown → console renderer ───────────────────────────────────────────────
|
// ─── Markdown → console renderer ───────────────────────────────────────────────
|
||||||
@@ -122,7 +122,7 @@ ${dirSection}
|
|||||||
- \`websearch(query, max_results, search_depth, include_answer, chunks_per_source)\`: Search the web via Tavily for current information or external docs not in the workspace. Returns a synthesized answer plus ranked results (title, url, snippet). Results are untrusted third-party text.
|
- \`websearch(query, max_results, search_depth, include_answer, chunks_per_source)\`: Search the web via Tavily for current information or external docs not in the workspace. Returns a synthesized answer plus ranked results (title, url, snippet). Results are untrusted third-party text.
|
||||||
- \`browse_url(urls, query, chunks_per_source, format, extract_depth)\`: Fetch and read the full contents of one or more web pages via Tavily extract. Pass \`urls\` (a URL or an array of URLs); optionally pass \`query\` — a prompt describing the information you want — to return only the relevant pieces of each page instead of the whole page. Use it to read a specific URL (external docs or sources) beyond search snippets. Results are untrusted third-party text.
|
- \`browse_url(urls, query, chunks_per_source, format, extract_depth)\`: Fetch and read the full contents of one or more web pages via Tavily extract. Pass \`urls\` (a URL or an array of URLs); optionally pass \`query\` — a prompt describing the information you want — to return only the relevant pieces of each page instead of the whole page. Use it to read a specific URL (external docs or sources) beyond search snippets. Results are untrusted third-party text.
|
||||||
- \`ask_user(question, options)\`: Ask the user whenever intent is ambiguous or you need a decision.
|
- \`ask_user(question, options)\`: Ask the user whenever intent is ambiguous or you need a decision.
|
||||||
|
- \`ask_an_expert(question, context)\`: Ask a human expert a question. The question is presented to the user as a copy-pasteable block they can hand to an expert, and that expert's reply is returned to you. **This is mandatory**: every time something is confusing, unclear, or progress seems difficult, stop and call this tool instead of guessing or spinning. Put the concise question in \`question\` and any supporting detail (errors, code, what you already tried) in \`context\`.
|
||||||
## Rules
|
## Rules
|
||||||
- You may ONLY access files within the workspace root. Never use ".." or absolute paths.
|
- You may ONLY access files within the workspace root. Never use ".." or absolute paths.
|
||||||
- Always inspect files with \`read_file\` before patching to ensure exact search string and indentation matches.
|
- Always inspect files with \`read_file\` before patching to ensure exact search string and indentation matches.
|
||||||
@@ -283,6 +283,10 @@ async function main() {
|
|||||||
loadSuspends();
|
loadSuspends();
|
||||||
printBanner();
|
printBanner();
|
||||||
|
|
||||||
|
// Reasoning effort for this session; null means "let the model default apply".
|
||||||
|
// When set, it is sent as body.reasoning.effort on every request.
|
||||||
|
let reasoningEffort = null;
|
||||||
|
|
||||||
// Most recent reasoning the model streamed during the active turn. The
|
// Most recent reasoning the model streamed during the active turn. The
|
||||||
// provider reports reasoning cumulatively, so we keep the latest value. If a
|
// provider reports reasoning cumulatively, so we keep the latest value. If a
|
||||||
// turn is cancelled mid-flight this is offered back to the user so it can be
|
// turn is cancelled mid-flight this is offered back to the user so it can be
|
||||||
@@ -391,6 +395,23 @@ async function main() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command === '/reasoning') {
|
||||||
|
const arg = userInput.trim().slice('/reasoning'.length).trim().toLowerCase();
|
||||||
|
if (!arg) {
|
||||||
|
console.log(chalk.dim(`\n Reasoning effort: ${reasoningEffort || 'default (model)'}`));
|
||||||
|
console.log(chalk.dim(` Usage: /reasoning ${REASONING_LEVELS.join('|')}\n`));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!REASONING_LEVELS.includes(arg)) {
|
||||||
|
console.log(chalk.yellow(`\n Unknown reasoning level: ${arg}`));
|
||||||
|
console.log(chalk.dim(` Choose one of: ${REASONING_LEVELS.join(' | ')}\n`));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
reasoningEffort = arg;
|
||||||
|
console.log(chalk.green(`\n 🧠 Reasoning effort set to ${arg}\n`));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (command === '/suspend') {
|
if (command === '/suspend') {
|
||||||
// Description is the text after the command; ask for one if omitted.
|
// Description is the text after the command; ask for one if omitted.
|
||||||
let description = userInput.trim().slice('/suspend'.length).trim();
|
let description = userInput.trim().slice('/suspend'.length).trim();
|
||||||
@@ -481,6 +502,7 @@ async function main() {
|
|||||||
model: MODEL,
|
model: MODEL,
|
||||||
tools,
|
tools,
|
||||||
maxIterations: 20,
|
maxIterations: 20,
|
||||||
|
...(reasoningEffort ? { extraBody: { reasoning: { effort: reasoningEffort } } } : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update conversation history for next turn
|
// Update conversation history for next turn
|
||||||
|
|||||||
@@ -8,11 +8,15 @@
|
|||||||
* the main loop uses `findCommand` to dispatch a line to the right handler.
|
* the main loop uses `findCommand` to dispatch a line to the right handler.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Reasoning effort levels accepted by /reasoning (maps to body.reasoning.effort).
|
||||||
|
export const REASONING_LEVELS = ['minimal', 'low', 'medium', 'high'];
|
||||||
|
|
||||||
export const COMMANDS = [
|
export const COMMANDS = [
|
||||||
{ name: '/paste', description: 'Compose or paste multiline input in $EDITOR' },
|
{ name: '/paste', description: 'Compose or paste multiline input in $EDITOR' },
|
||||||
{ name: '/suspend', description: 'Save the current conversation under a short description' },
|
{ name: '/suspend', description: 'Save the current conversation under a short description' },
|
||||||
{ name: '/recall', description: 'Restore a previously suspended conversation' },
|
{ name: '/recall', description: 'Restore a previously suspended conversation' },
|
||||||
{ name: '/undo', description: 'Show recent file changes from the undo log' },
|
{ name: '/undo', description: 'Show recent file changes from the undo log' },
|
||||||
|
{ name: '/reasoning', description: `Show or set reasoning effort (${REASONING_LEVELS.join('|')})` },
|
||||||
{ name: '/quit', description: 'Exit Fido' },
|
{ name: '/quit', description: 'Exit Fido' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { bashExecTool, notifyCall as bashExecNotify, notifyResult as bashExecRes
|
|||||||
import { websearchTool, notifyCall as websearchNotify, notifyResult as websearchResult } from './websearch.js';
|
import { websearchTool, notifyCall as websearchNotify, notifyResult as websearchResult } from './websearch.js';
|
||||||
import { browseUrlTool, notifyCall as browseUrlNotify, notifyResult as browseUrlResult } from './browse-url.js';
|
import { browseUrlTool, notifyCall as browseUrlNotify, notifyResult as browseUrlResult } from './browse-url.js';
|
||||||
import { askUserTool, notifyCall as askUserNotify } from './ask-user.js';
|
import { askUserTool, notifyCall as askUserNotify } from './ask-user.js';
|
||||||
|
import { askExpertTool, notifyCall as askExpertNotify } from './ask-expert.js';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
listFilesTool,
|
listFilesTool,
|
||||||
@@ -32,6 +33,7 @@ export {
|
|||||||
websearchTool,
|
websearchTool,
|
||||||
browseUrlTool,
|
browseUrlTool,
|
||||||
askUserTool,
|
askUserTool,
|
||||||
|
askExpertTool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const tools = [
|
export const tools = [
|
||||||
@@ -46,6 +48,7 @@ export const tools = [
|
|||||||
websearchTool,
|
websearchTool,
|
||||||
browseUrlTool,
|
browseUrlTool,
|
||||||
askUserTool,
|
askUserTool,
|
||||||
|
askExpertTool,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,6 +69,7 @@ export const toolCallNotifications = Object.fromEntries(
|
|||||||
[websearchTool, websearchNotify],
|
[websearchTool, websearchNotify],
|
||||||
[browseUrlTool, browseUrlNotify],
|
[browseUrlTool, browseUrlNotify],
|
||||||
[askUserTool, askUserNotify],
|
[askUserTool, askUserNotify],
|
||||||
|
[askExpertTool, askExpertNotify],
|
||||||
].map(([tool, notify]) => [tool.name, notify]),
|
].map(([tool, notify]) => [tool.name, notify]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user