@@ -104,19 +104,28 @@ ${CURRENT_DETAIL} (${ELAPSED}s)"
104104 gh api " repos/{owner}/{repo}/issues/comments/${DASHBOARD_COMMENT_ID} " \
105105 -X PATCH -f body=" ${DASHBOARD_BODY} " 2> /dev/null || log " Warning: Dashboard update failed"
106106 fi
107+
108+ # 5. Telegram live dashboard (edit-in-place, not new messages)
109+ TG_DASH=" ${EMOJI} <b>Agent #${ISSUE} </b> — ${CURRENT_STATUS}
110+ <i>${ISSUE_TITLE:- issue # ${ISSUE} } </i>
111+ Step ${STEP_NUM} /${TOTAL_STEPS} : ${CURRENT_DETAIL}
112+ Elapsed: ${ELAPSED} s"
113+ update_telegram_dashboard " ${TG_DASH} "
107114}
108115
109116escape_html () {
110117 echo " $1 " | sed ' s/&/\&/g; s/</\</g; s/>/\>/g'
111118}
112119
120+ TG_DASHBOARD_MSG_ID=" "
121+
113122send_telegram () {
114123 if [ -n " ${TELEGRAM_BOT_TOKEN} " ] && [ -n " ${TELEGRAM_CHAT_ID} " ]; then
115- # Write message to temp file to avoid JSON escaping issues with special chars
116124 local msg_file=" /tmp/tg_msg_$$ .json"
125+ local escaped_text
126+ escaped_text=$( echo " $1 " | sed ' s/"/\\"/g; s/$/\\n/' | tr -d ' \n' | sed ' s/\\n$//' )
117127 printf ' {"chat_id":"%s","text":"%s","parse_mode":"HTML"}' \
118- " ${TELEGRAM_CHAT_ID} " " $( echo " $1 " | sed ' s/"/\\"/g; s/$/\\n/' | tr -d ' \n' | sed ' s/\\n$//' ) " \
119- > " ${msg_file} "
128+ " ${TELEGRAM_CHAT_ID} " " ${escaped_text} " > " ${msg_file} "
120129 curl -s -X POST " https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN} /sendMessage" \
121130 -H " Content-Type: application/json" \
122131 -d " @${msg_file} " \
@@ -126,6 +135,37 @@ send_telegram() {
126135 fi
127136}
128137
138+ # Update existing Telegram message (reduces spam, stays under rate limit)
139+ update_telegram_dashboard () {
140+ if [ -n " ${TELEGRAM_BOT_TOKEN} " ] && [ -n " ${TELEGRAM_CHAT_ID} " ]; then
141+ local msg_file=" /tmp/tg_dash_$$ .json"
142+ local escaped_text
143+ escaped_text=$( echo " $1 " | sed ' s/"/\\"/g; s/$/\\n/' | tr -d ' \n' | sed ' s/\\n$//' )
144+
145+ if [ -z " ${TG_DASHBOARD_MSG_ID} " ]; then
146+ # First call: send new message, capture message_id
147+ printf ' {"chat_id":"%s","text":"%s","parse_mode":"HTML"}' \
148+ " ${TELEGRAM_CHAT_ID} " " ${escaped_text} " > " ${msg_file} "
149+ local resp
150+ resp=$( curl -s -X POST " https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN} /sendMessage" \
151+ -H " Content-Type: application/json" \
152+ -d " @${msg_file} " \
153+ --connect-timeout 5 --max-time 10 2> /dev/null || true)
154+ TG_DASHBOARD_MSG_ID=$( echo " ${resp} " | grep -o ' "message_id":[0-9]*' | head -1 | cut -d: -f2)
155+ else
156+ # Subsequent calls: edit existing message
157+ printf ' {"chat_id":"%s","message_id":%s,"text":"%s","parse_mode":"HTML"}' \
158+ " ${TELEGRAM_CHAT_ID} " " ${TG_DASHBOARD_MSG_ID} " " ${escaped_text} " > " ${msg_file} "
159+ curl -s -X POST " https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN} /editMessageText" \
160+ -H " Content-Type: application/json" \
161+ -d " @${msg_file} " \
162+ --connect-timeout 5 --max-time 10 \
163+ 2> /dev/null || log " Warning: Telegram edit failed"
164+ fi
165+ rm -f " ${msg_file} "
166+ fi
167+ }
168+
129169# ═══════════════════════════════════════════════════════════════════════════════
130170# EVENT STREAM (OpenHands-style structured events)
131171# ═══════════════════════════════════════════════════════════════════════════════
@@ -239,6 +279,7 @@ cleanup() {
239279 if [ -n " ${WORKTREE_PATH} " ] && [ -d " ${WORKTREE_PATH} " ]; then
240280 log " Cleaning up worktree on exit..."
241281 cd /bare-repo.git 2> /dev/null || true
282+ git worktree unlock " ${WORKTREE_PATH} " 2> /dev/null || true
242283 git worktree remove " ${WORKTREE_PATH} " --force 2> /dev/null || true
243284 log " Worktree removed: ${WORKTREE_PATH} "
244285 fi
@@ -325,7 +366,9 @@ if ! retry "git worktree add -b 'agent-${ISSUE}' '${WORKTREE_PATH}' main 2>/dev/
325366fi
326367cd " ${WORKTREE_PATH} "
327368
328- log " Worktree created at ${WORKTREE_PATH} "
369+ # Lock worktree to prevent accidental pruning
370+ git worktree lock " ${WORKTREE_PATH} " 2> /dev/null || true
371+ log " Worktree created and locked at ${WORKTREE_PATH} "
329372
330373# === 3. Prepare SOUL.md ===
331374log " Injecting soul..."
0 commit comments