π Day 211 β Building a Relay Between ChatGPT and a Local Coding Agent, Guarded Against Stale and Duplicate Messages
π Topic
Building a local ChatGPT-to-OpenCode relay that keeps the workflow simple without letting stale replies, duplicate loops, or leaked worker output control the next step.
π― Goal
Let a conversation guide one local coding worker at a time, while preserving task identity, redacting outgoing content, and stopping retries from repeating work.
π What I Did
I built a simple relay mode for the local agent bridge: take the latest instruction from a chosen ChatGPT conversation, send it to an OpenCode worker, return the workerβs handover, and repeat only when a new reply exists.
The simple version deliberately retained a few non-negotiable controls:
- one loop process at a time through a PID lock;
- one conversation pinned for the duration of a run;
- a hash marking the last consumed instruction;
- a freshness check so the loop cannot consume its own previous post;
- redaction before worker output returns to the browser;
- a fresh OpenCode session when the selected conversation changes.
The key learning was that simplicity should remove unnecessary ceremony, not the controls that prevent a loop from becoming self-referential or running two tasks against one local model at once.
π Key Cybersecurity Connections
This is integrity and replay protection for an automation workflow. A repeated instruction, an old handover, or a changed browser target can all cause an agent to act on the wrong state.
π Investigation Questions
- Can a second loop start while one is active?
- What proves an instruction is newer than the last consumed one?
- Does changing conversations carry old task context forward?
- Can worker output contain secrets or internal details?
- What happens if posting the handover fails?
π¨ Detection Opportunities
Useful events include:
- refused second-loop start
- stale or self-authored reply rejected
- conversation identity changed during a run
- outgoing text modified by the redactor
- worker completed but browser handover failed
Example:
event=agent_bridge_instruction_rejected
reason=not_fresher_than_last_consumed
action=stop_loop_and_check_conversation_target
π§ MITRE ATT&CK Techniques
Relevant defensive context:
- T1059 β Command and Scripting Interpreter, because the worker is executing local development tasks
- T1552 β Unsecured Credentials, because output needs a redaction boundary before it leaves the local worker
πΊ Visual Investigation Diagram
Selected conversation
β
Freshness + identity check
β
One local worker round
β
Redact handover
β
Record consumed state
β
Await genuinely newer instruction
β Challenges
The first instinct was to make the relay clever with lots of scoring and envelope rules. Live use showed that the core loop was useful, while excessive control layers introduced their own failure modes.
π What I Learned
I learned to separate a safety boundary from workflow decoration. Conversation identity, replay prevention, one active process, and output redaction are boundaries. A rigid response format is often just friction.
β‘ Next Steps
- Keep loop state small and inspectable
- Test crash recovery around βconsumedβ and βpostedβ transitions
- Keep the worker tied to one task context at a time
π§ Reflection
Automation loops are easiest to trust when they make their state explicit: what conversation, what round, what instruction, and what was actually sent back.
π§© Lessons Learned
What worked
Keeping the loop simple while retaining hard identity and freshness checks.
What broke
Assuming a local model server could safely handle several competing loops.
Why it broke
The worker and its GPU context were shared resources without a concurrency boundary.
Fix / takeaway
One task loop, one pinned target, and explicit consumed-state tracking.
π Skill Progression Context
This moved my local-agent work closer to reliable orchestration: not just asking a model to continue, but proving which piece of work it is continuing.
π TL;DR
The useful agent loop is simpleβbut it still needs a lock, a target, a freshness check, and a redaction boundary.
