106 lines
3.6 KiB
Bash
106 lines
3.6 KiB
Bash
#!/usr/bin/env bash
|
|||
|
|
# Run one task through pi in a brand-new, zero-context session.
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# scripts/run-task.sh 0.1
|
||
|
|
# scripts/run-task.sh 0.1 --dry-run # print the prompt, run nothing
|
||
|
|
#
|
||
|
|
# Each invocation is a fresh pi session (--no-session). Nothing carries over between
|
||
|
|
# tasks; that is the point. See tasks/AGENT-PROMPT.md.
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
|
|
cd "$REPO_ROOT"
|
||
|
|
|
||
|
|
TASK_ID="${1:-}"
|
||
|
|
DRY_RUN="${2:-}"
|
||
|
|
|
||
|
|
if [[ -z "$TASK_ID" ]]; then
|
||
|
|
echo "usage: scripts/run-task.sh <task-id> [--dry-run]" >&2
|
||
|
|
echo "example: scripts/run-task.sh 0.1" >&2
|
||
|
|
exit 2
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Resolve the task file from its id prefix.
|
||
|
|
shopt -s nullglob
|
||
|
|
matches=(tasks/"$TASK_ID"-*.md)
|
||
|
|
shopt -u nullglob
|
||
|
|
|
||
|
|
if [[ ${#matches[@]} -eq 0 ]]; then
|
||
|
|
echo "no task file found for id '$TASK_ID'" >&2
|
||
|
|
echo "available:" >&2
|
||
|
|
ls tasks/[0-9]*.md | sed 's|tasks/| |' >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
if [[ ${#matches[@]} -gt 1 ]]; then
|
||
|
|
echo "ambiguous task id '$TASK_ID' matches:" >&2
|
||
|
|
printf ' %s\n' "${matches[@]}" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
TASK_FILE="$(basename "${matches[0]}")"
|
||
|
|
|
||
|
|
# Extract the prompt body from the fenced block in AGENT-PROMPT.md, then substitute.
|
||
|
|
PROMPT="$(
|
||
|
|
awk '/^```$/{f=!f; next} f' tasks/AGENT-PROMPT.md \
|
||
|
|
| sed -e "s|{{TASK_ID}}|$TASK_ID|g" -e "s|{{TASK_FILE}}|$TASK_FILE|g"
|
||
|
|
)"
|
||
|
|
|
||
|
|
if [[ -z "${PROMPT// }" ]]; then
|
||
|
|
echo "failed to extract prompt template from tasks/AGENT-PROMPT.md" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [[ "$DRY_RUN" == "--dry-run" ]]; then
|
||
|
|
printf '%s\n' "$PROMPT"
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Default: claude-haiku-4-5. 200K context, reliable tool calling, needs
|
||
|
|
# ANTHROPIC_API_KEY in the environment.
|
||
|
|
#
|
||
|
|
# The two homelab models were both evaluated and rejected as defaults on 2026-08-19:
|
||
|
|
#
|
||
|
|
# reasoning 16384 context, and pi's compaction.reserveTokens defaults to 16384,
|
||
|
|
# so the compaction threshold computes to zero. pi also only evaluates
|
||
|
|
# auto-compaction at run boundaries, never mid-turn (earendil-works/pi
|
||
|
|
# issues 6339, 5512, 2871), so a tool loop grows unchecked until the
|
||
|
|
# provider rejects it. Separately, under tool_choice "auto" it returns
|
||
|
|
# tool_calls: [] and reasons in prose, which stalls an agent loop.
|
||
|
|
#
|
||
|
|
# ornith:35b 131K context and correct auto tool calling -- viable, and the right
|
||
|
|
# local choice. Occupied by other work at time of writing.
|
||
|
|
#
|
||
|
|
# Switch with env vars:
|
||
|
|
# PI_PROVIDER=homelab-ornith PI_MODEL=ornith:35b scripts/run-task.sh 0.1
|
||
|
|
#
|
||
|
|
PROVIDER="${PI_PROVIDER:-homelab-ornith}"
|
||
|
|
MODEL="${PI_MODEL:-ornith:35b}"
|
||
|
|
|
||
|
|
# One fresh session file per task. This still guarantees zero context -- each task gets
|
||
|
|
# its own new file and never reads another's -- while leaving the run inspectable:
|
||
|
|
#
|
||
|
|
# pi --session .task-runs/sessions/<task>.jsonl # open it
|
||
|
|
# pi --export .task-runs/sessions/<task>.jsonl out.html # render it
|
||
|
|
#
|
||
|
|
# --no-session would also give zero context, but persists nothing to look at afterwards.
|
||
|
|
SESSION_DIR=".task-runs/sessions"
|
||
|
|
mkdir -p "$SESSION_DIR"
|
||
|
|
SESSION_FILE="$SESSION_DIR/$TASK_ID.jsonl"
|
||
|
|
rm -f "$SESSION_FILE"
|
||
|
|
|
||
|
|
echo "task : $TASK_ID ($TASK_FILE)"
|
||
|
|
echo "model : $PROVIDER/$MODEL"
|
||
|
|
echo "session : $SESSION_FILE (fresh)"
|
||
|
|
echo
|
||
|
|
|
||
|
|
# --print is required, not cosmetic. Without it pi starts its interactive TUI;
|
||
|
|
# under run-all.sh stdout is a file and stdin may be /dev/null, so the TUI renders
|
||
|
|
# nothing until exit and can sit waiting for input that never comes. --approve
|
||
|
|
# trusts project-local files (AGENTS.md and friends) for this run, which would
|
||
|
|
# otherwise raise a prompt no one is there to answer.
|
||
|
|
exec pi --provider "$PROVIDER" --model "$MODEL" \
|
||
|
|
--print --approve \
|
||
|
|
--session "$SESSION_FILE" "$PROMPT"
|