Skip to content

Commit e1b3749

Browse files
committed
bridge: auto-start from start.sh, survive agent restarts
- start.sh: clean stale sockets before launch (fuser check) - start.sh: start bridge in tmux with restart loop before pi - bridge: don't crash if no pi socket on startup (resolve lazily) - bridge: refreshSocket() before every send, not just after errors - bridge: reply 'agent starting up' instead of erroring when pi not ready Fixes the bug where a systemctl restart kills the bridge and it never comes back because the LLM has to remember to run a startup checklist.
1 parent c03d44e commit e1b3749

2 files changed

Lines changed: 60 additions & 4 deletions

File tree

slack-bridge/bridge.mjs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,20 @@ const app = new App({
188188
socketMode: true,
189189
});
190190

191-
let socketPath = findSessionSocket(process.env.PI_SESSION_ID);
192-
console.log(`🔌 Using pi session socket: ${socketPath}`);
191+
let socketPath = null;
192+
try {
193+
socketPath = findSessionSocket(process.env.PI_SESSION_ID);
194+
console.log(`🔌 Using pi session socket: ${socketPath}`);
195+
} catch {
196+
console.log(`⏳ No pi session socket found yet — will resolve when first message arrives`);
197+
}
193198

194199
/** Re-resolve the socket path (handles session restarts). */
195200
function refreshSocket() {
196201
try {
197202
socketPath = findSessionSocket(process.env.PI_SESSION_ID);
198203
} catch {
199-
// keep current — will fail on next send with a clear error
204+
socketPath = null;
200205
}
201206
}
202207

@@ -233,6 +238,13 @@ async function handleMessage(userMessage, event, say) {
233238
} catch {}
234239

235240
try {
241+
// Always re-resolve the socket before sending (handles agent restarts)
242+
refreshSocket();
243+
if (!socketPath) {
244+
await say({ text: "⏳ Agent is starting up — try again in a moment.", thread_ts: event.ts });
245+
return;
246+
}
247+
236248
// Wrap the message with security boundaries before sending to agent
237249
const contextMessage = wrapExternalContent({
238250
text: userMessage,
@@ -263,7 +275,7 @@ async function handleMessage(userMessage, event, say) {
263275
} catch {}
264276
} catch (err) {
265277
console.error("Error:", err.message);
266-
refreshSocket(); // try to reconnect on next message
278+
refreshSocket();
267279
await say({ text: `❌ Error: ${err.message}`, thread_ts: event.ts });
268280
}
269281
}
@@ -299,6 +311,12 @@ app.event("message", async ({ event, say }) => {
299311
threadTs: event.ts,
300312
});
301313
try {
314+
// Re-resolve socket before sending
315+
refreshSocket();
316+
if (!socketPath) {
317+
console.log("⏳ Sentry alert dropped — agent not ready yet");
318+
return;
319+
}
302320
// Fire and forget — don't wait for agent response, don't reply in channel
303321
await enqueue(() => sendToAgent(socketPath, contextMessage));
304322
} catch (err) {

start.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,44 @@ umask 077
3333
# Redact any secrets that leaked into session logs
3434
~/runtime/bin/redact-logs.sh 2>/dev/null || true
3535

36+
# Clean stale session sockets from previous runs
37+
SOCKET_DIR="$HOME/.pi/session-control"
38+
if [ -d "$SOCKET_DIR" ]; then
39+
echo "Cleaning stale session sockets..."
40+
for sock in "$SOCKET_DIR"/*.sock; do
41+
[ -e "$sock" ] || continue
42+
# If no process has the socket open, it's stale
43+
if ! fuser "$sock" &>/dev/null 2>&1; then
44+
rm -f "$sock"
45+
fi
46+
done
47+
# Clean broken alias symlinks
48+
for alias in "$SOCKET_DIR"/*.alias; do
49+
[ -L "$alias" ] || continue
50+
target=$(readlink "$alias")
51+
if [ ! -e "$SOCKET_DIR/$target" ] && [ ! -e "$target" ]; then
52+
rm -f "$alias"
53+
fi
54+
done
55+
fi
56+
57+
# Start Slack bridge in the background (before pi, so it's ready for messages).
58+
# The bridge resolves the pi session socket lazily — it will wait for the
59+
# control-agent to come up and create its socket before forwarding messages.
60+
if [ -n "${SLACK_BOT_TOKEN:-}" ] && [ -n "${SLACK_APP_TOKEN:-}" ]; then
61+
# Kill any existing bridge
62+
tmux kill-session -t slack-bridge 2>/dev/null || true
63+
echo "Starting Slack bridge..."
64+
tmux new-session -d -s slack-bridge \
65+
"export PATH=$HOME/.varlock/bin:$HOME/opt/node-v22.14.0-linux-x64/bin:\$PATH && \
66+
cd ~/runtime/slack-bridge && \
67+
while true; do \
68+
varlock run --path ~/.config/ -- node bridge.mjs; \
69+
echo '⚠️ Bridge exited (\$?), restarting in 5s...'; \
70+
sleep 5; \
71+
done"
72+
fi
73+
3674
# Set session name (read by auto-name.ts extension)
3775
export PI_SESSION_NAME="control-agent"
3876

0 commit comments

Comments
 (0)