aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeonard Kugis <leonard@kug.is>2026-02-01 21:18:50 +0100
committerLeonard Kugis <leonard@kug.is>2026-02-01 21:18:50 +0100
commit75395625917754ac6c8e849da5516f0826159932 (patch)
treede38889b6953651e627c72fbde32ac887548ccd6
parent94000f6b6d07b31151deccc4e3e1fdccf5b5c487 (diff)
downloadllm-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-xtools/execute_command.sh83
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
+