v0.6.3: route all large jq inputs through tempfiles, not argv
v0.6.2 fixed the TOOLS_JSON argv overflow but four other call sites had
the same risk pattern — any of them would have crashed under Cygwin's
~32KB argv cap with large user input, large agent responses, or large
tool results:
add_user_text --arg c "$content" ← multi-paragraph prompts
add_assistant_blocks --argjson b "$blocks" ← long assistant turns
add_user_tool_results --argjson b "$blocks" ← chained tool results
agent_turn loop --arg c "$result" ← tool output (up to 250KB
for read_file, 500 lines
for ssh_exec, etc.)
agent_turn loop --arg system "$system_prompt" ← agents/*.md
total ~25KB
All five are now passed via tempfile + --rawfile (for raw strings) or
--slurpfile (for pre-parsed JSON). Same proven pattern as the v0.6.2
TOOLS_JSON fix. Tempfiles are cleaned at every return path.
Verified by pushing a 60KB user prompt through the pipeline on macOS
(also has the larger 256KB argv cap that masked these bugs locally
before, but the codepath now uses files for the large values regardless
of platform). Messages file stored the full 60025-char prompt with no
warnings.
After this commit, the only --arg / --argjson calls remaining all carry
known-small values (UUIDs, version strings, port numbers, etc.).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
0ebaacd221
commit
38d1eeede4
56
larry.sh
56
larry.sh
@ -36,7 +36,7 @@ set -o pipefail
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Config
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
LARRY_VERSION="0.6.2"
|
||||
LARRY_VERSION="0.6.3"
|
||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||
LARRY_BASE_URL="${LARRY_BASE_URL:-https://raw.githubusercontent.com/bojj27/cloverleaf-larry/main}"
|
||||
LARRY_UPDATE_URL="${LARRY_UPDATE_URL:-${LARRY_BASE_URL}/larry.sh}"
|
||||
@ -457,23 +457,37 @@ log_append() { printf '%s\n' "$1" >> "$LOG_FILE"; }
|
||||
# On MobaXterm/Cygwin the bundled jq is a Windows-native binary that can't
|
||||
# resolve Cygwin paths like /home/mobaxterm/... when they come in as argv.
|
||||
# Stdin redirection always works because bash does the path open() itself.
|
||||
# Each of these passes the value through a tempfile (--rawfile / --slurpfile)
|
||||
# rather than argv (--arg / --argjson). Argv overflow ("Argument list too
|
||||
# long") on Cygwin's ~32KB total cap was the v0.6.1 bug for TOOLS_JSON; the
|
||||
# same pattern applies to any value that could grow with user input or
|
||||
# assistant output (multi-paragraph prompts, large tool results, etc.).
|
||||
add_user_text() {
|
||||
local content="$1"
|
||||
local tmp; tmp=$(mktemp)
|
||||
jq --arg c "$content" '. + [{"role":"user","content":[{"type":"text","text":$c}]}]' < "$MESSAGES_FILE" > "$tmp" \
|
||||
local cfile tmp
|
||||
cfile=$(mktemp); tmp=$(mktemp)
|
||||
printf '%s' "$content" > "$cfile"
|
||||
jq --rawfile c "$cfile" '. + [{"role":"user","content":[{"type":"text","text":$c}]}]' < "$MESSAGES_FILE" > "$tmp" \
|
||||
&& mv "$tmp" "$MESSAGES_FILE"
|
||||
rm -f "$cfile"
|
||||
}
|
||||
add_assistant_blocks() {
|
||||
local blocks="$1"
|
||||
local tmp; tmp=$(mktemp)
|
||||
jq --argjson b "$blocks" '. + [{"role":"assistant","content":$b}]' < "$MESSAGES_FILE" > "$tmp" \
|
||||
local bfile tmp
|
||||
bfile=$(mktemp); tmp=$(mktemp)
|
||||
printf '%s' "$blocks" > "$bfile"
|
||||
jq --slurpfile b "$bfile" '. + [{"role":"assistant","content":$b[0]}]' < "$MESSAGES_FILE" > "$tmp" \
|
||||
&& mv "$tmp" "$MESSAGES_FILE"
|
||||
rm -f "$bfile"
|
||||
}
|
||||
add_user_tool_results() {
|
||||
local blocks="$1"
|
||||
local tmp; tmp=$(mktemp)
|
||||
jq --argjson b "$blocks" '. + [{"role":"user","content":$b}]' < "$MESSAGES_FILE" > "$tmp" \
|
||||
local bfile tmp
|
||||
bfile=$(mktemp); tmp=$(mktemp)
|
||||
printf '%s' "$blocks" > "$bfile"
|
||||
jq --slurpfile b "$bfile" '. + [{"role":"user","content":$b[0]}]' < "$MESSAGES_FILE" > "$tmp" \
|
||||
&& mv "$tmp" "$MESSAGES_FILE"
|
||||
rm -f "$bfile"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
@ -1040,17 +1054,19 @@ build_system_prompt() {
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
agent_turn() {
|
||||
local system_prompt="$1"
|
||||
# Write the TOOLS_JSON to a file ONCE per agent_turn rather than passing it
|
||||
# via --argjson (which puts the full 21KB blob on the jq command line and
|
||||
# blows up Cygwin/Windows argv limits with E2BIG / "Argument list too long").
|
||||
local tools_file; tools_file=$(mktemp)
|
||||
printf '%s' "$TOOLS_JSON" > "$tools_file"
|
||||
# Write the large blobs to files ONCE per agent_turn rather than passing
|
||||
# them via --arg / --argjson. Combined budget (TOOLS_JSON ~21KB + system
|
||||
# prompt ~25KB) easily exceeds Cygwin's ~32KB argv cap → E2BIG.
|
||||
local tools_file system_file
|
||||
tools_file=$(mktemp); system_file=$(mktemp)
|
||||
printf '%s' "$TOOLS_JSON" > "$tools_file"
|
||||
printf '%s' "$system_prompt" > "$system_file"
|
||||
while true; do
|
||||
local payload_file; payload_file=$(mktemp)
|
||||
jq -n \
|
||||
--arg model "$LARRY_MODEL" \
|
||||
--argjson max_tokens "$LARRY_MAX_TOKENS" \
|
||||
--arg system "$system_prompt" \
|
||||
--rawfile system "$system_file" \
|
||||
--slurpfile messages "$MESSAGES_FILE" \
|
||||
--slurpfile tools "$tools_file" \
|
||||
'{model:$model, max_tokens:$max_tokens, system:$system, messages:$messages[0], tools:$tools[0]}' \
|
||||
@ -1059,12 +1075,12 @@ agent_turn() {
|
||||
local resp; resp=$(call_api "$payload_file")
|
||||
rm -f "$payload_file"
|
||||
|
||||
if [ -z "$resp" ]; then err "empty response from API (timeout or network?)"; rm -f "$tools_file"; return 1; fi
|
||||
if [ -z "$resp" ]; then err "empty response from API (timeout or network?)"; rm -f "$tools_file" "$system_file"; return 1; fi
|
||||
|
||||
local err_type; err_type=$(printf '%s' "$resp" | jq -r '.error.type // empty' 2>/dev/null)
|
||||
if [ -n "$err_type" ]; then
|
||||
err "API error: $err_type — $(printf '%s' "$resp" | jq -r '.error.message // "no message"')"
|
||||
rm -f "$tools_file"
|
||||
rm -f "$tools_file" "$system_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@ -1100,14 +1116,20 @@ agent_turn() {
|
||||
local result; result=$(execute_tool "$name" "$input_json")
|
||||
log_append '```'; log_append "$result"; log_append '```'
|
||||
|
||||
# Tool results can be large (read_file up to 250KB, ssh_exec up to
|
||||
# 500 lines, etc.) — pass via tempfile, not --arg, to avoid Cygwin
|
||||
# argv overflow.
|
||||
local result_file; result_file=$(mktemp)
|
||||
printf '%s' "$result" > "$result_file"
|
||||
results=$(printf '%s' "$results" | jq \
|
||||
--arg id "$tu_id" --arg c "$result" \
|
||||
--arg id "$tu_id" --rawfile c "$result_file" \
|
||||
'. + [{"type":"tool_result","tool_use_id":$id,"content":$c}]')
|
||||
rm -f "$result_file"
|
||||
done < <(printf '%s' "$resp" | jq -c '.content[] | select(.type=="tool_use")')
|
||||
|
||||
add_user_tool_results "$results"
|
||||
done
|
||||
rm -f "$tools_file"
|
||||
rm -f "$tools_file" "$system_file"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Loading…
Reference in New Issue
Block a user