diff options
| author | Leonard Kugis <leonard@kug.is> | 2026-02-01 21:18:50 +0100 |
|---|---|---|
| committer | Leonard Kugis <leonard@kug.is> | 2026-02-01 21:18:50 +0100 |
| commit | 75395625917754ac6c8e849da5516f0826159932 (patch) | |
| tree | de38889b6953651e627c72fbde32ac887548ccd6 | |
| parent | 94000f6b6d07b31151deccc4e3e1fdccf5b5c487 (diff) | |
| download | llm-functions-docker-75395625917754ac6c8e849da5516f0826159932.tar.gz | |
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.
| -rwxr-xr-x | tools/execute_command.sh | 83 |
1 files changed, 79 insertions, 4 deletions
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 + |
