diff --git a/packages/coding-agent/src/tools/ask.ts b/packages/coding-agent/src/tools/ask.ts index 6f81d985f..4a3839341 100644 --- a/packages/coding-agent/src/tools/ask.ts +++ b/packages/coding-agent/src/tools/ask.ts @@ -381,7 +381,7 @@ export class AskTool implements AgentTool { readonly label = "Ask"; readonly description: string; readonly parameters = askSchema; - readonly strict = true; + readonly strict = false; constructor(private readonly session: ToolSession) { this.description = prompt.render(askDescription); diff --git a/packages/coding-agent/src/tools/python.ts b/packages/coding-agent/src/tools/python.ts index 477b73c35..a4cdc8bed 100644 --- a/packages/coding-agent/src/tools/python.ts +++ b/packages/coding-agent/src/tools/python.ts @@ -1,4 +1,3 @@ -import type * as fs from "node:fs"; import * as path from "node:path"; import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; import type { ImageContent } from "@oh-my-pi/pi-ai"; @@ -16,7 +15,6 @@ import { DEFAULT_MAX_BYTES, OutputSink, type OutputSummary, TailBuffer } from ". import { getTreeBranch, getTreeContinuePrefix, renderCodeCell } from "../tui"; import type { ToolSession } from "."; import { formatStyledTruncationWarning, type OutputMeta } from "./output-meta"; -import { resolveToCwd } from "./path-utils"; import { formatTitle, replaceTabs, shortenPath, truncateToWidth, wrapBrackets } from "./render-utils"; import { ToolAbortError, ToolError } from "./tool-errors"; import { toolResult } from "./tool-result"; @@ -48,12 +46,11 @@ export const pythonSchema = Type.Object({ cells: Type.Array( Type.Object({ code: Type.String({ description: "python code", examples: ["print('hello')", "import json"] }), - title: Type.Optional(Type.String({ description: "cell label", examples: ["imports", "helper"] })), + title: Type.String({ description: "cell label", examples: ["imports", "helper"] }), }), { description: "cells to execute" }, ), timeout: Type.Optional(Type.Number({ description: "timeout in seconds", default: 30 })), - cwd: Type.Optional(Type.String({ description: "working directory", examples: ["src/"] })), reset: Type.Optional(Type.Boolean({ description: "restart kernel" })), }); export type PythonToolParams = Static; @@ -177,7 +174,7 @@ export class PythonTool implements AgentTool { } const session = this.session; - const { cells, timeout: rawTimeout = 30, cwd, reset } = params; + const { cells, timeout: rawTimeout = 30, reset } = params; // Clamp to reasonable range: 1s - 600s (10 min) const timeoutSec = clampTimeout("python", rawTimeout); const timeoutMs = timeoutSec * 1000; @@ -204,17 +201,6 @@ export class PythonTool implements AgentTool { } session.assertPythonExecutionAllowed?.(); - const commandCwd = cwd ? resolveToCwd(cwd, session.cwd) : session.cwd; - let cwdStat: fs.Stats; - try { - cwdStat = await Bun.file(commandCwd).stat(); - } catch { - throw new ToolError(`Working directory does not exist: ${commandCwd}`); - } - if (!cwdStat.isDirectory()) { - throw new ToolError(`Working directory is not a directory: ${commandCwd}`); - } - const tailBuffer = new TailBuffer(DEFAULT_MAX_BYTES * 2); const jsonOutputs: unknown[] = []; const images: ImageContent[] = []; @@ -273,11 +259,11 @@ export class PythonTool implements AgentTool { pushUpdate(); }, }); - const sessionId = sessionFile ? `session:${sessionFile}:cwd:${commandCwd}` : `cwd:${commandCwd}`; + const sessionId = sessionFile ? `session:${sessionFile}:cwd:${session.cwd}` : `cwd:${session.cwd}`; if (getPreludeDocs().length === 0) { const warmup = await warmPythonEnvironment( - commandCwd, + session.cwd, sessionId, session.settings.get("python.sharedGateway"), sessionFile ?? undefined, @@ -292,7 +278,7 @@ export class PythonTool implements AgentTool { } const baseExecutorOptions = { - cwd: commandCwd, + cwd: session.cwd, deadlineMs, signal: combinedSignal, sessionId, diff --git a/packages/coding-agent/test/core/atom.test.ts b/packages/coding-agent/test/core/atom.test.ts index db2bad035..a858208e7 100644 --- a/packages/coding-agent/test/core/atom.test.ts +++ b/packages/coding-agent/test/core/atom.test.ts @@ -184,7 +184,7 @@ describe("resolveAtomToolEdit — loc syntax", () => { }); it("supports path override inside loc", () => { - const resolved = resolveAtomEntryPaths([{ loc: "a.ts:1ab", set: "X" }], undefined); + const resolved = resolveAtomEntryPaths([{ loc: "a.ts:1ab", set: ["X"] }], undefined); expect(resolved[0]?.path).toBe("a.ts"); expect(resolved[0]?.loc).toBe("1ab"); }); @@ -202,7 +202,7 @@ describe("parseAnchor (atom tolerant) + applyAtomEdits", () => { it("surfaces correct anchor + content when the model invents an out-of-alphabet hash", () => { const content = "alpha\nbravo\ncharlie"; // `XG` is not in the alphabet; should be rejected with the actual anchor exposed. - const toolEdit = { path: "a.ts", loc: "2XG", set: "BRAVO" }; + const toolEdit = { path: "a.ts", loc: "2XG", set: ["BRAVO"] }; const resolved = resolveAtomToolEdit(toolEdit); expect(() => applyAtomEdits(content, resolved)).toThrow(HashlineMismatchError); try { @@ -217,20 +217,20 @@ describe("parseAnchor (atom tolerant) + applyAtomEdits", () => { it("surfaces correct anchor + content when the model omits the hash entirely", () => { const content = "alpha\nbravo\ncharlie"; - const toolEdit = { path: "a.ts", loc: "2", set: "BRAVO" }; + const toolEdit = { path: "a.ts", loc: "2", set: ["BRAVO"] }; const resolved = resolveAtomToolEdit(toolEdit); expect(() => applyAtomEdits(content, resolved)).toThrow(HashlineMismatchError); }); it("surfaces correct anchor when the model uses pipe-separator (LINE|content) form", () => { const content = "alpha\nbravo\ncharlie"; - const toolEdit = { path: "a.ts", loc: "2|bravo", set: "BRAVO" }; + const toolEdit = { path: "a.ts", loc: "2|bravo", set: ["BRAVO"] }; const resolved = resolveAtomToolEdit(toolEdit); expect(() => applyAtomEdits(content, resolved)).toThrow(HashlineMismatchError); }); it("throws a usage-style error when no line number can be extracted", () => { - const toolEdit = { path: "a.ts", loc: " if (!x) return;", set: "x" }; + const toolEdit = { path: "a.ts", loc: " if (!x) return;", set: ["x"] }; expect(() => resolveAtomToolEdit(toolEdit)).toThrow(/Could not find a line number/); }); });