From 75395625917754ac6c8e849da5516f0826159932 Mon Sep 17 00:00:00 2001 From: Leonard Kugis Date: Sun, 1 Feb 2026 21:18:50 +0100 Subject: execute_command: Making return loop friendly Default behaviour of aichat will terminate the request immediately, if a tool returns with exit code != 0. This is inappropriate if the model is capable to fix commands itself. Thus, this patch of execute_command will not forward the exit code, but print it to stderr and always return exit code 0. --- tools/execute_command.sh | 83 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 4 deletions(-) (limited to 'tools/execute_command.sh') diff --git a/tools/execute_command.sh b/tools/execute_command.sh index eb58eba..b98aa55 100755 --- a/tools/execute_command.sh +++ b/tools/execute_command.sh @@ -1,16 +1,91 @@ #!/usr/bin/env bash -set -e +set -euo pipefail # @describe Execute the shell command. # @option --command! The command to execute. - # @env LLM_OUTPUT=/dev/stdout The output path ROOT_DIR="${LLM_ROOT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +OUT_PATH="${LLM_OUTPUT:-/dev/stdout}" + +# Max bytes per stream to avoid flooding aichat +MAX_BYTES="${LLM_MAX_BYTES:-200000}" + +sanitize() { + # Ensures valid UTF-8 output; drops invalid byte sequences + if command -v iconv >/dev/null 2>&1; then + iconv -f UTF-8 -t UTF-8 -c + else + # Fallback: best-effort pass-through + cat + fi +} main() { - "$ROOT_DIR/utils/guard_operation.sh" - eval "$argc_command" >> "$LLM_OUTPUT" + # Truncate output file so each tool call is clean + : > "$OUT_PATH" + + # Guard operation (but never hard-fail the tool) + set +e + guard_out="$(mktemp)" + guard_err="$(mktemp)" + "$ROOT_DIR/utils/guard_operation.sh" >"$guard_out" 2>"$guard_err" + guard_rc=$? + set -e + + if [[ $guard_rc -ne 0 ]]; then + { + echo "EXIT_CODE: $guard_rc" + echo "STDOUT:" + cat "$guard_out" | sanitize | head -c "$MAX_BYTES" + echo + echo "STDERR:" + cat "$guard_err" | sanitize | head -c "$MAX_BYTES" + echo + echo "ERROR: guard_operation_failed" + } >> "$OUT_PATH" + rm -f "$guard_out" "$guard_err" + exit 0 + fi + rm -f "$guard_out" "$guard_err" + + # Basic sanity: block meaningless commands (reduces ':' / pasted listings etc.) + cmd="$(printf "%s" "${argc_command:-}" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')" + if [[ -z "$cmd" || "$cmd" == ":" || "$cmd" == ";" ]]; then + { + echo "EXIT_CODE: 127" + echo "STDOUT:" + echo + echo "STDERR:" + echo "bad_command: empty or meaningless command" + } >> "$OUT_PATH" + exit 0 + fi + + out_file="$(mktemp)" + err_file="$(mktemp)" + trap 'rm -f "$out_file" "$err_file"' EXIT + + # Run command (do not let failures exit the tool) + set +e + bash -lc "$cmd" >"$out_file" 2>"$err_file" + rc=$? + set -e + + { + echo "EXIT_CODE: $rc" + echo "STDOUT:" + cat "$out_file" | sanitize | head -c "$MAX_BYTES" + echo + echo "STDERR:" + cat "$err_file" | sanitize | head -c "$MAX_BYTES" + echo + } >> "$OUT_PATH" + + # Always succeed from tool perspective, but communicate real rc in output + exit 0 } eval "$(argc --argc-eval "$0" "$@")" +main + -- cgit v1.2.3