fix(sandbox): preserve filesystem cwd semantics
This commit is contained in:
@@ -95,7 +95,7 @@ export function applyEditTool(ctx: Context, sandbox: FsSandboxSurface): void {
|
||||
// Resolve the per-call sandbox policy (approved mode > session override
|
||||
// > backend default, plus the session cwd root) BEFORE anything executes.
|
||||
const sandboxPolicy = await sandbox.resolvePolicy('edit', args, exec)
|
||||
const target = await ctx.fs.resolve(input.filePath, sessionResolveOptions(exec))
|
||||
const target = await ctx.fs.resolve(input.filePath, sessionResolveOptions(exec, sandboxPolicy?.workspaceRoot))
|
||||
// Single-slot decision: the policy plugin returns { version: vObserved } or
|
||||
// throws FS_NOT_OBSERVED; the bare default is undefined (unconditional edit).
|
||||
// No stat — the bare default never manufactures a version basis.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
import type { ToolExecution } from '@deepseek-ai/dsh-tools'
|
||||
import { canonicalPath } from '@deepseek-ai/dsh-sandbox'
|
||||
|
||||
/**
|
||||
* The session workspace cwd for this call, or `undefined` when none applies.
|
||||
@@ -16,16 +17,18 @@ import type { ToolExecution } from '@deepseek-ai/dsh-tools'
|
||||
* @returns the calling agent's session cwd, or undefined for a non-agent caller (the backend then applies its own default).
|
||||
*/
|
||||
export function sessionCwd(exec: ToolExecution): string | undefined {
|
||||
return exec.agent?.session.header.cwd
|
||||
const cwd = exec.agent?.session.header.cwd
|
||||
return cwd === undefined ? undefined : canonicalPath(cwd)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolution options shared by all model-facing filesystem tools.
|
||||
* @param exec - the tool-execution context supplying session cwd and cancellation.
|
||||
* @param policyWorkspaceRoot - resolved per-call root, when a mutation carries sandbox policy.
|
||||
* @returns provider resolution options for the current tool call.
|
||||
*/
|
||||
export function sessionResolveOptions(exec: ToolExecution): { cwd?: string; signal?: AbortSignal } {
|
||||
const cwd = sessionCwd(exec)
|
||||
export function sessionResolveOptions(exec: ToolExecution, policyWorkspaceRoot?: string): { cwd?: string; signal?: AbortSignal } {
|
||||
const cwd = policyWorkspaceRoot ?? sessionCwd(exec)
|
||||
return {
|
||||
...cwd !== undefined ? { cwd } : {},
|
||||
...exec.signal !== undefined ? { signal: exec.signal } : {},
|
||||
|
||||
@@ -80,7 +80,7 @@ export function applyWriteTool(ctx: Context, sandbox: FsSandboxSurface): void {
|
||||
// > backend default, plus the session cwd root) BEFORE anything executes;
|
||||
// an escalating call throws its distinct text on any non-grant.
|
||||
const sandboxPolicy = await sandbox.resolvePolicy('write', args, exec)
|
||||
const target = await ctx.fs.resolve(input.filePath, sessionResolveOptions(exec))
|
||||
const target = await ctx.fs.resolve(input.filePath, sessionResolveOptions(exec, sandboxPolicy?.workspaceRoot))
|
||||
// Single-slot decision: the policy plugin produces createIfAbsent/
|
||||
// replaceIfVersion; the bare default is undefined (unconditional). No stat.
|
||||
const intent = await ctx.waterfall('fs/write-intent', target, exec, () => undefined)
|
||||
|
||||
Reference in New Issue
Block a user