diff --git a/packages/coding-agent/src/exec/bash-cwd-sync.ts b/packages/coding-agent/src/exec/bash-cwd-sync.ts deleted file mode 100644 index 2e9546113..000000000 --- a/packages/coding-agent/src/exec/bash-cwd-sync.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as fs from "node:fs/promises"; -import * as path from "node:path"; - -import { logger } from "@oh-my-pi/pi-utils"; - -import type { BashResult } from "./bash-executor"; - -export interface BashCwdSyncOptions { - /** Completed bash result whose native shell state carries the post-command cwd. */ - result: BashResult; - /** Session cwd before the user bash command ran. */ - currentCwd: string; - /** Apply the discovered cwd to the owning session. */ - applyCwd: (cwd: string) => Promise; -} - -/** - * Synchronize a completed bash command's native working directory back into the owning session. - * - * Use real shell path strings for the no-op check and update so symlinked/logical cwd changes - * remain visible to the host. Existence validation still follows symlinks via `stat`. - */ -export async function syncBashSessionCwd(options: BashCwdSyncOptions): Promise { - const nextCwd = options.result.workingDir; - if (!nextCwd || !path.isAbsolute(nextCwd)) return null; - - try { - if (path.resolve(nextCwd) === path.resolve(options.currentCwd)) return null; - const stat = await fs.stat(nextCwd); - if (!stat.isDirectory()) return null; - await options.applyCwd(nextCwd); - return nextCwd; - } catch (error) { - logger.debug("Failed to apply bash session cwd", { cwd: nextCwd, error: String(error) }); - return null; - } -} diff --git a/packages/coding-agent/src/session/agent-session.ts b/packages/coding-agent/src/session/agent-session.ts index 1ac189cc0..91425bbff 100644 --- a/packages/coding-agent/src/session/agent-session.ts +++ b/packages/coding-agent/src/session/agent-session.ts @@ -132,7 +132,6 @@ import { prompt, relativePathWithinRoot, Snowflake, - setProjectDir, withTimeout, } from "@oh-my-pi/pi-utils"; import * as snapcompact from "@oh-my-pi/snapcompact"; @@ -175,7 +174,6 @@ import { } from "../config/model-resolver"; import { MODEL_ROLE_IDS, MODEL_ROLES } from "../config/model-roles"; import { expandPromptTemplate, type PromptTemplate } from "../config/prompt-templates"; -import { applyProviderGlobalsFromSettings } from "../config/provider-globals"; import { buildServiceTierByFamily, serviceTierForAllFamilies, serviceTierSettingToTier } from "../config/service-tier"; import type { Settings, SkillsSettings } from "../config/settings"; import { getDefault, onAppendOnlyModeChanged, validateProviderMaxInFlightRequests } from "../config/settings"; @@ -192,7 +190,6 @@ import { } from "../eval/py/executor"; import { disposeRubyKernelSessionsByOwner } from "../eval/rb/executor"; import { defaultEvalSessionId } from "../eval/session-id"; -import { syncBashSessionCwd } from "../exec/bash-cwd-sync"; import { type BashResult, executeBash as executeBashCommand } from "../exec/bash-executor"; import type { TtsrManager, TtsrMatchContext } from "../export/ttsr"; import type { LoadedCustomCommand } from "../extensibility/custom-commands"; @@ -13312,17 +13309,6 @@ export class AgentSession { onMinimizedSave: originalText => this.#saveBashOriginalArtifact(originalText), useUserShell: options?.useUserShell, }); - await syncBashSessionCwd({ - result, - currentCwd: cwd, - applyCwd: async nextCwd => { - await this.sessionManager.moveTo(nextCwd); - setProjectDir(nextCwd); - await this.settings.reloadForCwd(nextCwd); - applyProviderGlobalsFromSettings(this.settings); - resetCapabilities(); - }, - }); this.recordBashResult(command, result, options); return result; diff --git a/packages/coding-agent/test/bash-executor.test.ts b/packages/coding-agent/test/bash-executor.test.ts index 0271c48af..8058e0279 100644 --- a/packages/coding-agent/test/bash-executor.test.ts +++ b/packages/coding-agent/test/bash-executor.test.ts @@ -3,7 +3,6 @@ import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import { resetSettingsForTest, Settings, type ShellMinimizerSettings } from "@oh-my-pi/pi-coding-agent/config/settings"; -import { syncBashSessionCwd } from "@oh-my-pi/pi-coding-agent/exec/bash-cwd-sync"; import { buildMinimizerOptions, executeBash } from "@oh-my-pi/pi-coding-agent/exec/bash-executor"; import { DEFAULT_MAX_BYTES } from "@oh-my-pi/pi-coding-agent/session/streaming-output"; import * as shellSnapshot from "@oh-my-pi/pi-coding-agent/utils/shell-snapshot"; @@ -138,80 +137,6 @@ describe("executeBash", () => { expect(result.output.trim()).toBe(tempDir); }); - it("returns and syncs persistent shell directory changes back to the session", async () => { - if (!configureBashUserShell(tempDir)) return; - const childDir = path.join(tempDir, "child"); - fs.mkdirSync(childDir); - const realChildDir = fs.realpathSync(childDir); - const sessionKey = `cwd-sync-${Date.now()}`; - const result = await executeBash(`cd ${shellQuote(childDir)}`, { sessionKey, timeout: 5000, useUserShell: true }); - - expect(result.workingDir ? fs.realpathSync(result.workingDir) : undefined).toBe(realChildDir); - - const applied: string[] = []; - const synced = await syncBashSessionCwd({ - result, - currentCwd: tempDir, - applyCwd: async cwd => { - applied.push(cwd); - }, - }); - - expect(synced ? fs.realpathSync(synced) : undefined).toBe(realChildDir); - expect(applied.map(cwd => fs.realpathSync(cwd))).toEqual([realChildDir]); - }); - - it("does not clobber the persistent shell status while syncing cwd", async () => { - if (!configureBashUserShell(tempDir)) return; - const childDir = path.join(tempDir, "child-status"); - fs.mkdirSync(childDir); - const sessionKey = `cwd-status-${Date.now()}`; - const result = await executeBash(`cd ${shellQuote(childDir)}; false`, { - sessionKey, - cwd: tempDir, - timeout: 5000, - useUserShell: true, - }); - - expect(result.exitCode).toBe(1); - await syncBashSessionCwd({ - result, - currentCwd: tempDir, - applyCwd: async () => {}, - }); - - const status = await executeBash(`printf '%s\n' "$?"`, { sessionKey, timeout: 5000, useUserShell: true }); - expect(status.output.trim()).toBe("1"); - }); - - it("does not reset OLDPWD when synchronized cwd is passed to the next command", async () => { - if (!configureBashUserShell(tempDir)) return; - const childDir = path.join(tempDir, "child-oldpwd"); - fs.mkdirSync(childDir); - const realTempDir = fs.realpathSync(tempDir); - const realChildDir = fs.realpathSync(childDir); - const sessionKey = `cwd-oldpwd-${Date.now()}`; - const result = await executeBash(`cd ${shellQuote(childDir)}`, { - sessionKey, - cwd: tempDir, - timeout: 5000, - useUserShell: true, - }); - const synced = await syncBashSessionCwd({ - result, - currentCwd: tempDir, - applyCwd: async () => {}, - }); - - expect(synced ? fs.realpathSync(synced) : undefined).toBe(realChildDir); - const back = await executeBash("cd - >/dev/null; pwd", { - sessionKey, - cwd: synced ?? childDir, - timeout: 5000, - useUserShell: true, - }); - expect(back.output.trim() ? fs.realpathSync(back.output.trim()) : undefined).toBe(realTempDir); - }); it("honors symlinked cwd requests in persistent shells", async () => { if (process.platform === "win32") {