diff --git a/toys/ollie.sh b/toys/ollie.sh
deleted file mode 100755
index 9715918..0000000
--- a/toys/ollie.sh
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env bash
-# ollie.sh: a simplified, fully-functional chat interface for ollie AI agents.
-set -o history
-
-MOUNT=${OLLIE_9MOUNT:-$HOME/mnt/ollie}
-LAST=$HOME/.config/ollie/last-session
-
-# load_session
→ prints session ID for that dir, or nothing
-load_session() {
- awk -F'\t' -v d="$1" 'NF==2 && $1==d {print $2; exit}' "$LAST" 2>/dev/null
-}
-
-# save_session → upserts {dir}\t{id} in $LAST
-save_session() {
- local dir="$1" id="$2" tmp
- mkdir -p "$(dirname "$LAST")"
- tmp=$(mktemp)
- awk -F'\t' -v d="$dir" 'NF==2 && $1!=d' "$LAST" 2>/dev/null > "$tmp" || true
- printf '%s\t%s\n' "$dir" "$id" >> "$tmp"
- mv "$tmp" "$LAST"
-}
-
-die() { printf 'error: %s\n' "$*" >&2; exit 1; }
-is_idle() { [ "$(tr -d '[:space:]' < "$S/state" 2>/dev/null)" = "idle" ]; }
-drain() {
- local n
- n=$(wc -c < "$S/chat" 2>/dev/null || echo 0)
- [ "$n" -gt "$chat_off" ] && tail -c "+$((chat_off+1))" "$S/chat" | head -c "$((n-chat_off))"
- chat_off=$n
-}
-
-# Parse flags
-new=0; resume=0; id_flag=
-backend=; model=; agent=; cwd=
-while [ $# -gt 0 ]; do
- case $1 in
- -new) new=1 ;;
- -resume) resume=1 ;;
- -session) id_flag=$2; shift ;;
- -backend) backend=$2; shift ;;
- -model) model=$2; shift ;;
- -agent) agent=$2; shift ;;
- -cwd) cwd=$2; shift ;;
- *) die "unknown flag: $1" ;;
- esac
- shift
-done
-
-# Build "new" ctl command with opts
-new_cmd() {
- local cmd=new
- [ -n "$backend" ] && cmd="$cmd backend=$backend"
- [ -n "$model" ] && cmd="$cmd model=$model"
- [ -n "$agent" ] && cmd="$cmd agent=$agent"
- cmd="$cmd cwd=${cwd:-$(pwd)}"
- printf '%s\n' "$cmd"
-}
-
-# Create a fresh session, sets $id
-create_session() {
- local before d deadline
- before=$(ls "$MOUNT/s" 2>/dev/null)
- new_cmd > "$MOUNT/ctl" || die "write ctl"
- deadline=$(($(date +%s) + 5))
- while [ "$(date +%s)" -lt "$deadline" ]; do
- for d in $(ls "$MOUNT/s" 2>/dev/null); do
- printf '%s\n' "$before" | grep -qxF "$d" || { id=$d; return; }
- done
- sleep 0.05
- done
- die "timeout waiting for new session"
-}
-
-# Resolve session
-_dir=${cwd:-$(pwd)}
-if [ -n "$id_flag" ]; then
- [ -d "$MOUNT/s/$id_flag" ] || die "session not found: $id_flag"
- id=$id_flag; printf 'session: %s (resumed)\n' "$id" >&2
-elif [ "$resume" = 1 ]; then
- id=$(load_session "$_dir")
- [ -n "$id" ] && [ -d "$MOUNT/s/$id" ] || die "no resumable session for $_dir"
- printf 'session: %s (resumed)\n' "$id" >&2
-elif [ "$new" = 1 ]; then
- create_session
- save_session "$_dir" "$id"
- printf 'session: %s\n' "$id" >&2
-else
- id=$(load_session "$_dir")
- if [ -n "$id" ] && [ -d "$MOUNT/s/$id" ]; then
- printf 'session: %s (resumed)\n' "$id" >&2
- else
- create_session
- save_session "$_dir" "$id"
- printf 'session: %s\n' "$id" >&2
- fi
-fi
-
-S=$MOUNT/s/$id
-chat_off=$(wc -c < "$S/chat" 2>/dev/null || echo 0)
-
-trap '
- [ "$(tr -d "[:space:]" < "$S/state" 2>/dev/null)" = "idle" ] && exit
- printf "\n" >&2; printf "stop\n" > "$S/ctl"
-' INT
-
-while true; do
- printf '> '
- IFS= read -re input || break
- [ -z "$input" ] && continue
- history -s "$input"
- case "$input" in
- /kill) printf 'kill %s\n' "$id" > "$MOUNT/ctl"; break ;;
- "/q "*) printf '%s\n' "${input#/q }" > "$S/enqueue"
- printf 'queued\n' >&2; continue ;;
- /*) printf '%s\n' "$input" > "$S/ctl" ;;
- *) printf '%s' "$input" > "$S/prompt" || { printf 'submit failed\n' >&2; continue; } ;;
- esac
- sleep 0.15
- while true; do
- drain
- is_idle && { drain; printf '\n'; break; }
- sleep 0.05
- done
-done
diff --git a/toys/oq b/toys/oq
new file mode 100755
index 0000000..95fd9c0
--- /dev/null
+++ b/toys/oq
@@ -0,0 +1,88 @@
+#!/usr/bin/env bash
+# oq: non-interactive ollie query via b/ namespace.
+#
+# Usage: oq [flags] [prompt...]
+# echo "prompt" | oq [flags]
+#
+# Flags:
+# -name base name for the job(s); suffixed with -0, -1, ...
+# -cwd working directory (default: pwd)
+# -agent agent config name
+# -backend backend override
+# -model model override
+# -output output format hint (e.g. json)
+# -parallel run the same prompt N times in parallel (default: 1)
+# -keep do not remove job(s) after printing result
+
+set -euo pipefail
+
+MOUNT=${OLLIE_9MOUNT:-$HOME/mnt/ollie}
+
+die() { printf 'oq: %s\n' "$*" >&2; exit 1; }
+
+name=; cwd=; agent=; backend=; model=; output=; parallel=1; keep=0
+
+while [ $# -gt 0 ]; do
+ case $1 in
+ -name) name=$2; shift ;;
+ -cwd) cwd=$2; shift ;;
+ -agent) agent=$2; shift ;;
+ -backend) backend=$2; shift ;;
+ -model) model=$2; shift ;;
+ -output) output=$2; shift ;;
+ -parallel) parallel=$2; shift ;;
+ -keep) keep=1 ;;
+ --) shift; break ;;
+ -*) die "unknown flag: $1" ;;
+ *) break ;;
+ esac
+ shift
+done
+
+# Prompt from remaining args or stdin.
+if [ $# -gt 0 ]; then
+ prompt="$*"
+elif ! [ -t 0 ]; then
+ prompt=$(cat)
+else
+ die "prompt required: pass as arguments or pipe via stdin"
+fi
+
+[ -z "$prompt" ] && die "empty prompt"
+
+cwd=${cwd:-$(pwd)}
+[ -z "$name" ] && name=$(date +%s%N)-$$
+
+{
+ printf 'name=%s\n' "$name"
+ printf 'cwd=%s\n' "$cwd"
+ [ -n "$agent" ] && printf 'agent=%s\n' "$agent"
+ [ -n "$backend" ] && printf 'backend=%s\n' "$backend"
+ [ -n "$model" ] && printf 'model=%s\n' "$model"
+ [ -n "$output" ] && printf 'output=%s\n' "$output"
+ printf 'parallel=%s\n' "$parallel"
+ printf -- '---\n'
+ printf '%s\n' "$prompt"
+} > "$MOUNT/b/new" || die "failed to submit job"
+
+rc=0
+for i in $(seq 0 $((parallel - 1))); do
+ id="${name}-${i}"
+ B="$MOUNT/b/$id"
+
+ while true; do
+ status=$(tr -d '[:space:]' < "$B/status" 2>/dev/null) || { sleep 0.05; continue; }
+ case "$status" in
+ done*) break ;;
+ failed*) rc=1; break ;;
+ *) sleep 0.05 ;;
+ esac
+ done
+
+ [ "$parallel" -gt 1 ] && printf '=== %s ===\n' "$id"
+ cat "$B/result" 2>/dev/null
+
+ [ "$keep" -eq 0 ] && rm -r "$B" 2>/dev/null || true
+done
+
+exit $rc
diff --git a/toys/osh b/toys/osh
index 21b520b..9715918 100755
--- a/toys/osh
+++ b/toys/osh
@@ -1,88 +1,124 @@
#!/usr/bin/env bash
-# osh: non-interactive ollie query via b/ namespace.
-#
-# Usage: osh [flags] [prompt...]
-# echo "prompt" | osh [flags]
-#
-# Flags:
-# -name base name for the job(s); suffixed with -0, -1, ...
-# -cwd working directory (default: pwd)
-# -agent agent config name
-# -backend backend override
-# -model model override
-# -output output format hint (e.g. json)
-# -parallel run the same prompt N times in parallel (default: 1)
-# -keep do not remove job(s) after printing result
-
-set -euo pipefail
+# ollie.sh: a simplified, fully-functional chat interface for ollie AI agents.
+set -o history
MOUNT=${OLLIE_9MOUNT:-$HOME/mnt/ollie}
+LAST=$HOME/.config/ollie/last-session
-die() { printf 'osh: %s\n' "$*" >&2; exit 1; }
+# load_session → prints session ID for that dir, or nothing
+load_session() {
+ awk -F'\t' -v d="$1" 'NF==2 && $1==d {print $2; exit}' "$LAST" 2>/dev/null
+}
-name=; cwd=; agent=; backend=; model=; output=; parallel=1; keep=0
+# save_session → upserts {dir}\t{id} in $LAST
+save_session() {
+ local dir="$1" id="$2" tmp
+ mkdir -p "$(dirname "$LAST")"
+ tmp=$(mktemp)
+ awk -F'\t' -v d="$dir" 'NF==2 && $1!=d' "$LAST" 2>/dev/null > "$tmp" || true
+ printf '%s\t%s\n' "$dir" "$id" >> "$tmp"
+ mv "$tmp" "$LAST"
+}
+die() { printf 'error: %s\n' "$*" >&2; exit 1; }
+is_idle() { [ "$(tr -d '[:space:]' < "$S/state" 2>/dev/null)" = "idle" ]; }
+drain() {
+ local n
+ n=$(wc -c < "$S/chat" 2>/dev/null || echo 0)
+ [ "$n" -gt "$chat_off" ] && tail -c "+$((chat_off+1))" "$S/chat" | head -c "$((n-chat_off))"
+ chat_off=$n
+}
+
+# Parse flags
+new=0; resume=0; id_flag=
+backend=; model=; agent=; cwd=
while [ $# -gt 0 ]; do
case $1 in
- -name) name=$2; shift ;;
- -cwd) cwd=$2; shift ;;
- -agent) agent=$2; shift ;;
- -backend) backend=$2; shift ;;
- -model) model=$2; shift ;;
- -output) output=$2; shift ;;
- -parallel) parallel=$2; shift ;;
- -keep) keep=1 ;;
- --) shift; break ;;
- -*) die "unknown flag: $1" ;;
- *) break ;;
+ -new) new=1 ;;
+ -resume) resume=1 ;;
+ -session) id_flag=$2; shift ;;
+ -backend) backend=$2; shift ;;
+ -model) model=$2; shift ;;
+ -agent) agent=$2; shift ;;
+ -cwd) cwd=$2; shift ;;
+ *) die "unknown flag: $1" ;;
esac
shift
done
-# Prompt from remaining args or stdin.
-if [ $# -gt 0 ]; then
- prompt="$*"
-elif ! [ -t 0 ]; then
- prompt=$(cat)
+# Build "new" ctl command with opts
+new_cmd() {
+ local cmd=new
+ [ -n "$backend" ] && cmd="$cmd backend=$backend"
+ [ -n "$model" ] && cmd="$cmd model=$model"
+ [ -n "$agent" ] && cmd="$cmd agent=$agent"
+ cmd="$cmd cwd=${cwd:-$(pwd)}"
+ printf '%s\n' "$cmd"
+}
+
+# Create a fresh session, sets $id
+create_session() {
+ local before d deadline
+ before=$(ls "$MOUNT/s" 2>/dev/null)
+ new_cmd > "$MOUNT/ctl" || die "write ctl"
+ deadline=$(($(date +%s) + 5))
+ while [ "$(date +%s)" -lt "$deadline" ]; do
+ for d in $(ls "$MOUNT/s" 2>/dev/null); do
+ printf '%s\n' "$before" | grep -qxF "$d" || { id=$d; return; }
+ done
+ sleep 0.05
+ done
+ die "timeout waiting for new session"
+}
+
+# Resolve session
+_dir=${cwd:-$(pwd)}
+if [ -n "$id_flag" ]; then
+ [ -d "$MOUNT/s/$id_flag" ] || die "session not found: $id_flag"
+ id=$id_flag; printf 'session: %s (resumed)\n' "$id" >&2
+elif [ "$resume" = 1 ]; then
+ id=$(load_session "$_dir")
+ [ -n "$id" ] && [ -d "$MOUNT/s/$id" ] || die "no resumable session for $_dir"
+ printf 'session: %s (resumed)\n' "$id" >&2
+elif [ "$new" = 1 ]; then
+ create_session
+ save_session "$_dir" "$id"
+ printf 'session: %s\n' "$id" >&2
else
- die "prompt required: pass as arguments or pipe via stdin"
+ id=$(load_session "$_dir")
+ if [ -n "$id" ] && [ -d "$MOUNT/s/$id" ]; then
+ printf 'session: %s (resumed)\n' "$id" >&2
+ else
+ create_session
+ save_session "$_dir" "$id"
+ printf 'session: %s\n' "$id" >&2
+ fi
fi
-[ -z "$prompt" ] && die "empty prompt"
+S=$MOUNT/s/$id
+chat_off=$(wc -c < "$S/chat" 2>/dev/null || echo 0)
-cwd=${cwd:-$(pwd)}
-[ -z "$name" ] && name=$(date +%s%N)-$$
-
-{
- printf 'name=%s\n' "$name"
- printf 'cwd=%s\n' "$cwd"
- [ -n "$agent" ] && printf 'agent=%s\n' "$agent"
- [ -n "$backend" ] && printf 'backend=%s\n' "$backend"
- [ -n "$model" ] && printf 'model=%s\n' "$model"
- [ -n "$output" ] && printf 'output=%s\n' "$output"
- printf 'parallel=%s\n' "$parallel"
- printf -- '---\n'
- printf '%s\n' "$prompt"
-} > "$MOUNT/b/new" || die "failed to submit job"
-
-rc=0
-for i in $(seq 0 $((parallel - 1))); do
- id="${name}-${i}"
- B="$MOUNT/b/$id"
+trap '
+ [ "$(tr -d "[:space:]" < "$S/state" 2>/dev/null)" = "idle" ] && exit
+ printf "\n" >&2; printf "stop\n" > "$S/ctl"
+' INT
+while true; do
+ printf '> '
+ IFS= read -re input || break
+ [ -z "$input" ] && continue
+ history -s "$input"
+ case "$input" in
+ /kill) printf 'kill %s\n' "$id" > "$MOUNT/ctl"; break ;;
+ "/q "*) printf '%s\n' "${input#/q }" > "$S/enqueue"
+ printf 'queued\n' >&2; continue ;;
+ /*) printf '%s\n' "$input" > "$S/ctl" ;;
+ *) printf '%s' "$input" > "$S/prompt" || { printf 'submit failed\n' >&2; continue; } ;;
+ esac
+ sleep 0.15
while true; do
- status=$(tr -d '[:space:]' < "$B/status" 2>/dev/null) || { sleep 0.05; continue; }
- case "$status" in
- done*) break ;;
- failed*) rc=1; break ;;
- *) sleep 0.05 ;;
- esac
+ drain
+ is_idle && { drain; printf '\n'; break; }
+ sleep 0.05
done
-
- [ "$parallel" -gt 1 ] && printf '=== %s ===\n' "$id"
- cat "$B/result" 2>/dev/null
-
- [ "$keep" -eq 0 ] && rm -r "$B" 2>/dev/null || true
done
-
-exit $rc