#!/usr/bin/env rc # sh: interactive chat interface for ollie AI agents. MOUNT=/home/lkn/mnt/ollie if(! ~ $#OLLIE 0) MOUNT=$OLLIE LAST=$home/.config/ollie/last-session fn load_session { awk -F'\t' -v d=$1 'NF==2 && $1==d {print $2; exit}' $LAST >[2]/dev/null } fn save_session { dir=$1 sessid=$2 mkdir -p `{dirname $LAST} tmp=`{mktemp} if(test -f $LAST) awk -F'\t' -v d=$dir 'NF==2 && $1!=d' $LAST > $tmp printf '%s\t%s\n' $dir $sessid >> $tmp mv $tmp $LAST } cmd=`{basename $0} fn die { echo $* >[1=2] exit 1 } fn cfg_get { awk -F= -v k=$1 '$1==k{gsub(/^[ \t]+|[ \t]+$/,"",$2); print $2; exit}' $S/cfg >[2]/dev/null } fn is_idle { ~ `{cfg_get state} idle } fn usage { cat <<'EOF' usage: sh [flags] Flags: -new always create a new session -resume resume last session for cwd; error if none -session ID attach to a specific session by ID -name NAME name for new session -cwd DIR working directory (default: pwd) -backend BACKEND backend override (e.g. ollama, anthropic) -model MODEL model override -agent AGENT agent config name -help show this help Input at the prompt: submit prompt, stream response /stop interrupt the current agent turn /kill kill the session and exit /compact compact conversation history /clear clear conversation history /rn rename the session /cwd [path] get or set working directory /agent [name] get or set agent /model [name] get or set model /backend [name] get or set backend /models list available models /backends list available backends /mcp list MCP servers /usage show token usage /ctxsz show context size /q enqueue prompt for later ! run shell command in session cwd C-j insert newline (multi-line input) C-m / Enter submit input Ctrl-C interrupt running turn (or exit if idle) EOF exit 0 } new=0 resume=0 while(! ~ $#* 0) { switch($1) { case -new new=1 case -resume resume=1 case -session id_flag=$2 shift case -name name=$2 shift case -backend backend=$2 shift case -model model=$2 shift case -agent agent=$2 shift case -cwd cwd=$2 shift case -help usage case * die 'unknown flag: '^$1 } shift } _dir=`{pwd} if(! ~ $#cwd 0) _dir=$cwd fn write_new { { if(! ~ $#name 0) printf 'name=%s\n' $name printf 'cwd=%s\n' $_dir if(! ~ $#backend 0) printf 'backend=%s\n' $backend if(! ~ $#model 0) printf 'model=%s\n' $model if(! ~ $#agent 0) printf 'agent=%s\n' $agent } > $MOUNT/s/new } fn create_session { if(! ~ $#name 0) { write_new id=$name return } before=`{ls $MOUNT/s >[2]/dev/null} write_new deadline=`{expr `{date +%s} + 5} while(test `{date +%s} -lt $deadline) { for(d in `{ls $MOUNT/s >[2]/dev/null}) { if(! ~ $d $before) { id=$d return } } sleep 0.05 } die 'timeout waiting for new session' } _picked=0 if(! ~ $#id_flag 0) { if(! test -d $MOUNT/s/$id_flag) die 'session not found: '^$id_flag id=$id_flag printf 'session: %s (resumed)\n' $id >[1=2] _picked=1 } if(~ $_picked 0) if(~ $resume 1) { id=`{load_session $_dir} if(~ $#id 0) die 'no resumable session for '^$_dir if(! test -d $MOUNT/s/$id) die 'no resumable session for '^$_dir printf 'session: %s (resumed)\n' $id >[1=2] _picked=1 } if(~ $_picked 0) if(~ $new 1) { create_session save_session $_dir $id printf 'session: %s\n' $id >[1=2] _picked=1 } if(~ $_picked 0) { id=`{load_session $_dir} if(! ~ $#id 0) if(test -d $MOUNT/s/$id) { printf 'session: %s (resumed)\n' $id >[1=2] _picked=1 } if(~ $_picked 0) { create_session save_session $_dir $id printf 'session: %s\n' $id >[1=2] } } S=$MOUNT/s/$id fn sigintr { printf '\n' >[1=2] echo stop > $S/ctl exit 0 } chat_off=0 if(test -f $S/chat) chat_off=`{wc -c < $S/chat} if(! test -t 0) { cat > $S/prompt || die 'submit failed' exit 0 } tail -n 40 $S/chat >[2]/dev/null while(true) { input=`` () {$MOUNT/x/readmulti '> ' '. '} if(~ $#input 0) break if(! ~ $#input 0) { switch($"input) { case /stop echo stop > $S/ctl case /kill echo kill > $S/ctl break case /compact echo compact > $S/ctl case /clear echo clear > $S/ctl case /cwd cfg_get cwd echo case '/cwd *' newcwd=`{printf '%s\n' $"input | sed 's,^/cwd ,,'} printf 'cwd=%s\n' $newcwd > $S/cfg case /agent cfg_get agent echo case '/agent *' printf 'agent=%s\n' `{printf '%s\n' $"input | sed 's,^/agent ,,'} > $S/cfg case /model cfg_get model echo case '/model *' printf 'model=%s\n' `{printf '%s\n' $"input | sed 's,^/model ,,'} > $S/cfg case /backend cfg_get backend echo case '/backend *' printf 'backend=%s\n' `{printf '%s\n' $"input | sed 's,^/backend ,,'} > $S/cfg case /models cat $S/models >[2]/dev/null case /backends cat $MOUNT/backends >[2]/dev/null case /usage cat $S/usage >[2]/dev/null case /ctxsz cat $S/ctxsz >[2]/dev/null case /mcp cat $S/mcp >[2]/dev/null case '/rn *' newid=`{printf '%s\n' $"input | sed 's,^/rn ,,'} printf 'rn %s\n' $newid > $S/ctl id=$newid S=$MOUNT/s/$id save_session $_dir $id case '/q *' printf '%s\n' `{printf '%s\n' $"input | sed 's,^/q ,,'} > $S/fifo.in printf 'queued\n' >[1=2] case /* printf 'unknown command: %s\n' `{printf '%s\n' $"input | awk '{print $1}'} >[1=2] case * printf '%s\n' $"input > $S/prompt || { printf 'submit failed\n' >[1=2] } cat $S/statewait >/dev/null >[2]/dev/null while(! is_idle) { n=`{wc -c < $S/chat >[2]/dev/null} if(~ $#n 0) n=0 if(test $n -gt $chat_off) { tail -c +`{expr $chat_off + 1} $S/chat chat_off=$n } sleep 0.05 } n=`{wc -c < $S/chat >[2]/dev/null} if(~ $#n 0) n=0 if(test $n -gt $chat_off) { tail -c +`{expr $chat_off + 1} $S/chat chat_off=$n } printf '\n' } } }