fix(coding-agent): suppress hashline output for agents without edit tool
Hashline prefixes (LINE:HASH|content) in read/grep output exist solely to support the hashline edit mode. Agents that don't have the edit tool (e.g. explore, plan, reviewer) gain nothing from them — they just waste tokens and add noise. resolveFileDisplayMode now takes a session-like object with an optional hasEditTool flag. When false, hashlines are suppressed regardless of settings. ToolSession gains hasEditTool (set from toolNames in the SDK), and AgentSession exposes it as a getter over the tool registry.
This commit is contained in:
@@ -701,6 +701,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
||||
cwd,
|
||||
hasUI: options.hasUI ?? false,
|
||||
enableLsp,
|
||||
get hasEditTool() {
|
||||
return !options.toolNames || options.toolNames.includes("edit");
|
||||
},
|
||||
skipPythonPreflight: options.skipPythonPreflight,
|
||||
contextFiles,
|
||||
skills,
|
||||
|
||||
@@ -1072,6 +1072,11 @@ export class AgentSession {
|
||||
return this.agent.state.tools.map(t => t.name);
|
||||
}
|
||||
|
||||
/** Whether the edit tool is registered in this session. */
|
||||
get hasEditTool(): boolean {
|
||||
return this.#toolRegistry.has("edit");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a tool by name from the registry.
|
||||
*/
|
||||
@@ -1518,7 +1523,7 @@ export class AgentSession {
|
||||
if (fileMentions.length > 0) {
|
||||
const fileMentionMessages = await generateFileMentionMessages(fileMentions, this.sessionManager.getCwd(), {
|
||||
autoResizeImages: this.settings.get("images.autoResize"),
|
||||
useHashLines: resolveFileDisplayMode(this.settings).hashLines,
|
||||
useHashLines: resolveFileDisplayMode(this).hashLines,
|
||||
});
|
||||
messages.push(...fileMentionMessages);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
|
||||
readonly parameters = grepSchema;
|
||||
|
||||
constructor(private readonly session: ToolSession) {
|
||||
const displayMode = resolveFileDisplayMode(session.settings);
|
||||
const displayMode = resolveFileDisplayMode(session);
|
||||
this.description = renderPromptTemplate(grepDescription, {
|
||||
IS_HASHLINE_MODE: displayMode.hashLines,
|
||||
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
||||
@@ -103,7 +103,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
|
||||
const patternHasNewline = normalizedPattern.includes("\n") || normalizedPattern.includes("\\n");
|
||||
const effectiveMultiline = multiline ?? patternHasNewline;
|
||||
|
||||
const useHashLines = resolveFileDisplayMode(this.session.settings).hashLines;
|
||||
const useHashLines = resolveFileDisplayMode(this.session).hashLines;
|
||||
const searchPath = resolveToCwd(searchDir || ".", this.session.cwd);
|
||||
const scopePath = (() => {
|
||||
const relative = path.relative(this.session.cwd, searchPath).replace(/\\/g, "/");
|
||||
|
||||
@@ -119,6 +119,8 @@ export interface ToolSession {
|
||||
promptTemplates?: PromptTemplate[];
|
||||
/** Whether LSP integrations are enabled */
|
||||
enableLsp?: boolean;
|
||||
/** Whether the edit tool is available in this session (controls hashline output) */
|
||||
hasEditTool?: boolean;
|
||||
/** Event bus for tool/extension communication */
|
||||
eventBus?: EventBus;
|
||||
/** Output schema for structured completion (subagents) */
|
||||
|
||||
@@ -544,7 +544,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
||||
readonly #autoResizeImages: boolean;
|
||||
|
||||
constructor(private readonly session: ToolSession) {
|
||||
const displayMode = resolveFileDisplayMode(session.settings);
|
||||
const displayMode = resolveFileDisplayMode(session);
|
||||
this.#autoResizeImages = session.settings.get("images.autoResize");
|
||||
this.description = renderPromptTemplate(readDescription, {
|
||||
DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
|
||||
@@ -562,7 +562,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
||||
): Promise<AgentToolResult<ReadToolDetails>> {
|
||||
const { path: readPath, offset, limit } = params;
|
||||
|
||||
const displayMode = resolveFileDisplayMode(this.session.settings);
|
||||
const displayMode = resolveFileDisplayMode(this.session);
|
||||
|
||||
// Handle internal URLs (agent://, skill://)
|
||||
const internalRouter = this.session.internalRouter;
|
||||
@@ -848,7 +848,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
||||
async #handleInternalUrl(url: string, offset?: number, limit?: number): Promise<AgentToolResult<ReadToolDetails>> {
|
||||
const internalRouter = this.session.internalRouter!;
|
||||
|
||||
const displayMode = resolveFileDisplayMode(this.session.settings);
|
||||
const displayMode = resolveFileDisplayMode(this.session);
|
||||
|
||||
// Check if URL has query extraction (agent:// only)
|
||||
let parsed: URL;
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
/**
|
||||
* Resolve line-display mode for file-like outputs (read, grep, @file mentions).
|
||||
*/
|
||||
export interface FileDisplayModeSettings {
|
||||
get(key: "readLineNumbers" | "readHashLines" | "edit.mode"): unknown;
|
||||
}
|
||||
|
||||
export interface FileDisplayMode {
|
||||
lineNumbers: boolean;
|
||||
hashLines: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes effective line display mode from settings/env.
|
||||
* Hashline mode takes precedence and implies line-addressed output everywhere.
|
||||
*/
|
||||
export function resolveFileDisplayMode(settings: FileDisplayModeSettings): FileDisplayMode {
|
||||
const hashLines =
|
||||
settings.get("readHashLines") === true ||
|
||||
settings.get("edit.mode") === "hashline" ||
|
||||
Bun.env.PI_EDIT_VARIANT === "hashline";
|
||||
/** Session-like object providing settings and tool availability for display mode resolution. */
|
||||
export interface FileDisplayModeSession {
|
||||
/** Whether the edit tool is available. Hashlines are suppressed without it. */
|
||||
hasEditTool?: boolean;
|
||||
settings: {
|
||||
get(key: "readLineNumbers" | "readHashLines" | "edit.mode"): unknown;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes effective line display mode from session settings/env.
|
||||
* Hashline mode takes precedence and implies line-addressed output everywhere.
|
||||
* Hashlines are suppressed when the edit tool is not available (e.g. explore agents).
|
||||
*/
|
||||
export function resolveFileDisplayMode(session: FileDisplayModeSession): FileDisplayMode {
|
||||
const { settings } = session;
|
||||
const hasEditTool = session.hasEditTool ?? true;
|
||||
const hashLines =
|
||||
hasEditTool &&
|
||||
(settings.get("readHashLines") === true ||
|
||||
settings.get("edit.mode") === "hashline" ||
|
||||
Bun.env.PI_EDIT_VARIANT === "hashline");
|
||||
return {
|
||||
hashLines,
|
||||
lineNumbers: hashLines || settings.get("readLineNumbers") === true,
|
||||
|
||||
Reference in New Issue
Block a user