refactor: unify tools.fs workspaceOnly resolution
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { ImageContent } from "@mariozechner/pi-ai";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../config/config.js";
|
||||
import {
|
||||
injectHistoryImagesIntoMessages,
|
||||
resolveAttemptFsWorkspaceOnly,
|
||||
resolvePromptBuildHookResult,
|
||||
resolvePromptModeForSession,
|
||||
} from "./attempt.js";
|
||||
@@ -118,3 +120,45 @@ describe("resolvePromptModeForSession", () => {
|
||||
expect(resolvePromptModeForSession("agent:main:cron:job-1:run:run-abc")).toBe("full");
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveAttemptFsWorkspaceOnly", () => {
|
||||
it("uses global tools.fs.workspaceOnly when agent has no override", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
tools: {
|
||||
fs: { workspaceOnly: true },
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
resolveAttemptFsWorkspaceOnly({
|
||||
config: cfg,
|
||||
sessionAgentId: "main",
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("prefers agent-specific tools.fs.workspaceOnly override", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
tools: {
|
||||
fs: { workspaceOnly: true },
|
||||
},
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
tools: {
|
||||
fs: { workspaceOnly: false },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
resolveAttemptFsWorkspaceOnly({
|
||||
config: cfg,
|
||||
sessionAgentId: "main",
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js";
|
||||
import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js";
|
||||
import type { OpenClawConfig } from "../../../config/config.js";
|
||||
import { getMachineDisplayName } from "../../../infra/machine-name.js";
|
||||
import { MAX_IMAGE_BYTES } from "../../../media/constants.js";
|
||||
import { getGlobalHookRunner } from "../../../plugins/hook-runner-global.js";
|
||||
@@ -28,7 +29,7 @@ import { resolveUserPath } from "../../../utils.js";
|
||||
import { normalizeMessageChannel } from "../../../utils/message-channel.js";
|
||||
import { isReasoningTagProvider } from "../../../utils/provider-utils.js";
|
||||
import { resolveOpenClawAgentDir } from "../../agent-paths.js";
|
||||
import { resolveAgentConfig, resolveSessionAgentIds } from "../../agent-scope.js";
|
||||
import { resolveSessionAgentIds } from "../../agent-scope.js";
|
||||
import { createAnthropicPayloadLogger } from "../../anthropic-payload-log.js";
|
||||
import { makeBootstrapWarn, resolveBootstrapContextForRun } from "../../bootstrap-files.js";
|
||||
import { createCacheTrace } from "../../cache-trace.js";
|
||||
@@ -74,6 +75,7 @@ import {
|
||||
import { buildSystemPromptParams } from "../../system-prompt-params.js";
|
||||
import { buildSystemPromptReport } from "../../system-prompt-report.js";
|
||||
import { sanitizeToolCallIdsForCloudCodeAssist } from "../../tool-call-id.js";
|
||||
import { resolveEffectiveToolFsWorkspaceOnly } from "../../tool-fs-policy.js";
|
||||
import { resolveTranscriptPolicy } from "../../transcript-policy.js";
|
||||
import { DEFAULT_BOOTSTRAP_FILENAME } from "../../workspace.js";
|
||||
import { isRunnerAbortError } from "../abort.js";
|
||||
@@ -228,6 +230,16 @@ export function resolvePromptModeForSession(sessionKey?: string): "minimal" | "f
|
||||
return isSubagentSessionKey(sessionKey) ? "minimal" : "full";
|
||||
}
|
||||
|
||||
export function resolveAttemptFsWorkspaceOnly(params: {
|
||||
config?: OpenClawConfig;
|
||||
sessionAgentId: string;
|
||||
}): boolean {
|
||||
return resolveEffectiveToolFsWorkspaceOnly({
|
||||
cfg: params.config,
|
||||
agentId: params.sessionAgentId,
|
||||
});
|
||||
}
|
||||
|
||||
function summarizeMessagePayload(msg: AgentMessage): { textChars: number; imageBlocks: number } {
|
||||
const content = (msg as { content?: unknown }).content;
|
||||
if (typeof content === "string") {
|
||||
@@ -363,9 +375,10 @@ export async function runEmbeddedAttempt(
|
||||
config: params.config,
|
||||
agentId: params.agentId,
|
||||
});
|
||||
const effectiveFsWorkspaceOnly =
|
||||
(resolveAgentConfig(params.config ?? {}, sessionAgentId)?.tools?.fs?.workspaceOnly ??
|
||||
params.config?.tools?.fs?.workspaceOnly) === true;
|
||||
const effectiveFsWorkspaceOnly = resolveAttemptFsWorkspaceOnly({
|
||||
config: params.config,
|
||||
sessionAgentId,
|
||||
});
|
||||
// Check if the model supports native image input
|
||||
const modelHasVision = params.model.input?.includes("image") ?? false;
|
||||
const toolsRaw = params.disableTools
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { ImageContent } from "@mariozechner/pi-ai";
|
||||
import { resolveUserPath } from "../../../utils.js";
|
||||
import { loadWebMedia } from "../../../web/media.js";
|
||||
import type { ImageSanitizationLimits } from "../../image-sanitization.js";
|
||||
import { resolveSandboxedBridgeMediaPath } from "../../sandbox-media-paths.js";
|
||||
import { assertSandboxPath } from "../../sandbox-paths.js";
|
||||
import type { SandboxFsBridge } from "../../sandbox/fs-bridge.js";
|
||||
import { sanitizeImageBlocks } from "../../tool-images.js";
|
||||
@@ -199,11 +200,15 @@ export async function loadImageFromRef(
|
||||
if (ref.type === "path") {
|
||||
if (options?.sandbox) {
|
||||
try {
|
||||
const resolved = options.sandbox.bridge.resolvePath({
|
||||
filePath: targetPath,
|
||||
cwd: options.sandbox.root,
|
||||
const resolved = await resolveSandboxedBridgeMediaPath({
|
||||
sandbox: {
|
||||
root: options.sandbox.root,
|
||||
bridge: options.sandbox.bridge,
|
||||
workspaceOnly: options.workspaceOnly,
|
||||
},
|
||||
mediaPath: targetPath,
|
||||
});
|
||||
targetPath = resolved.hostPath;
|
||||
targetPath = resolved.resolved;
|
||||
} catch (err) {
|
||||
log.debug(
|
||||
`Native image: sandbox validation failed for ${ref.resolved}: ${err instanceof Error ? err.message : String(err)}`,
|
||||
@@ -213,7 +218,7 @@ export async function loadImageFromRef(
|
||||
} else if (!path.isAbsolute(targetPath)) {
|
||||
targetPath = path.resolve(workspaceDir, targetPath);
|
||||
}
|
||||
if (options?.workspaceOnly) {
|
||||
if (options?.workspaceOnly && !options?.sandbox) {
|
||||
const root = options?.sandbox?.root ?? workspaceDir;
|
||||
await assertSandboxPath({
|
||||
filePath: targetPath,
|
||||
|
||||
Reference in New Issue
Block a user