Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions apps/worker/src/run-task/agent-home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { SLACK_POSTING_TOOL_EXCLUSIONS } from './slack-posting-tools';
import { SLACK_STOP_HOOK_SCRIPT } from './slack-stop-hook-script';
import { OPENCODE_SLACK_HOOKS_PLUGIN_SCRIPT } from './opencode-slack-hooks-plugin-script';
import { OPENCODE_CHATGPT_GATEWAY_PLUGIN_SCRIPT } from './opencode-chatgpt-gateway-plugin-script';
import { OPENCODE_TOOL_SAFETY_PLUGIN_SCRIPT } from './opencode-tool-safety-plugin-script';
import { resolveOpenCodeModelSelection } from './opencode-model';
import {
createProofRunnerAgentPrompt,
Expand Down Expand Up @@ -192,6 +193,8 @@ const ROOMOTE_OPENCODE_SLACK_HOOKS_PLUGIN_FILE_NAME = 'roomote-slack-hooks.js';
const ROOMOTE_OPENCODE_CHATGPT_GATEWAY_PLUGIN_FILE_NAME =
'roomote-chatgpt-gateway.js';

const ROOMOTE_OPENCODE_TOOL_SAFETY_PLUGIN_FILE_NAME = 'roomote-tool-safety.js';

const OPENCODE_ALLOW_ALL_PERMISSION = {
read: 'allow',
edit: 'allow',
Expand Down Expand Up @@ -620,6 +623,10 @@ function writeOpenCodeManagedFiles(openCodeConfigDir: string): void {
pluginsDir,
ROOMOTE_OPENCODE_CHATGPT_GATEWAY_PLUGIN_FILE_NAME,
);
const toolSafetyPluginPath = path.join(
pluginsDir,
ROOMOTE_OPENCODE_TOOL_SAFETY_PLUGIN_FILE_NAME,
);
const silenceHookPath = path.join(
openCodeConfigDir,
ROOMOTE_OPENCODE_SLACK_SILENCE_HOOK_FILE_NAME,
Expand All @@ -636,6 +643,11 @@ function writeOpenCodeManagedFiles(openCodeConfigDir: string): void {
OPENCODE_CHATGPT_GATEWAY_PLUGIN_SCRIPT,
'utf8',
);
fs.writeFileSync(
toolSafetyPluginPath,
OPENCODE_TOOL_SAFETY_PLUGIN_SCRIPT,
'utf8',
);
fs.writeFileSync(silenceHookPath, SLACK_SILENCE_HOOK_SCRIPT, 'utf8');
fs.writeFileSync(stopHookPath, SLACK_STOP_HOOK_SCRIPT, 'utf8');
fs.chmodSync(silenceHookPath, 0o755);
Expand Down
61 changes: 61 additions & 0 deletions apps/worker/src/run-task/opencode-tool-safety-plugin-script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
export const OPENCODE_TOOL_SAFETY_PLUGIN_SCRIPT = `import { realpath } from 'node:fs/promises';

const UNSUPPORTED_READ_IMAGE_EXTENSIONS = new Set(['.cur', '.ico']);

function getReadPath(input, context) {
const args = context?.args ?? input?.args;

if (!args || typeof args !== 'object') {
return undefined;
}

return typeof args.filePath === 'string'
? args.filePath
: typeof args.file_path === 'string'
? args.file_path
: typeof args.path === 'string'
? args.path
: undefined;
}

function getExtension(filePath) {
const normalized = filePath.split(/[?#]/u, 1)[0]?.toLowerCase() ?? '';
const basename = normalized.split(/[\\/]/u).pop() ?? '';
const extensionIndex = basename.lastIndexOf('.');

return extensionIndex >= 0 ? basename.slice(extensionIndex) : '';
}

async function resolvesToUnsupportedImage(filePath) {
if (UNSUPPORTED_READ_IMAGE_EXTENSIONS.has(getExtension(filePath))) {
return true;
}

try {
const resolvedPath = await realpath(filePath.split(/[?#]/u, 1)[0]);
return UNSUPPORTED_READ_IMAGE_EXTENSIONS.has(getExtension(resolvedPath));
} catch {
// Let the read tool report missing or inaccessible paths itself.
return false;
}
}

export const RoomoteOpenCodeToolSafety = async () => ({
'tool.execute.before': async (input, context) => {
if (input?.tool !== 'read') {
return;
}

const filePath = getReadPath(input, context);

if (!filePath || !(await resolvesToUnsupportedImage(filePath))) {
return;
}

throw new Error(
'The read tool cannot safely attach ICO or CUR image files to the model conversation. ' +
'Inspect metadata with a text-only command or convert the image to PNG in a temporary directory first.',
);
},
});
`;

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading