Poke AI
What a poke is
Every job has a Poke AI button in its right side panel, and every unresolved bug, task, grouped
task, question, and suggestion shows a compact Poke AI control in its expanded header. Pressing one
sends the message Start <short code>, for example Start J-6, to the AI terminals connected
through the Uclusion MCP proxy. On a connected terminal your
agent receives the message through uclusion codex, uclusion listen, or uclusion wait, loads the
item with get_job, and begins the workflow without you typing anything in its chat. See
Cursor IDE chat for Cursor.
Start is reserved for this explicit human action in the UI. Automatic collaborator events use the
event-style Added, Updated, and Responded messages described below instead.
Any message about a nested item includes its parent as well: a comment on a job carries the job
(Start T-all-180 of J-all-123), a reply on a view-level thread carries its root, and anything
inside a question’s options — whose short codes are local to the question’s inline decision
market — carries the enclosing question, for example Start C-1 of Q-all-556. The parent is
always a globally resolvable code, so the agent can classify the message and load the enclosing
work without any lookup on the nested code itself.
Cursor IDE chat
Cursor IDE chat does not consume Poke AI deliveries or the automatic Added, Updated, and
Responded messages on this page. Cursor does not offer a way to start a turn in an idle IDE chat
after a Uclusion change. That is a Cursor limitation. Start the next turn yourself by typing
Responded or another message. Name the relevant short code when starting different work. Use
find_work when no concrete work was named.
Automatic responds
You do not need to poke after responding to the AI. Every semantic human reply, option vote, or
Resolve on AI-authored assistance sends Responded <short code> naming what you responded on
(releases before mid-2026 sent a bare Responded.). This includes input from someone who is not a
current job assignee: that response is advisory and does not make the question answerable, but it still wakes
the AI so the advice is not missed. Any human Resolve delegates the choice to the AI and unblocks the
question; it does not silently select an option. A human reply in a task or information thread the AI participates in also sends
Responded with the reply’s short code and the job as parent, such as
Responded C-all-90 of J-all-123. Every response carries its own short code this way,
including the one that answers the job’s last open assistance; the job’s changed stage
shows in the reload rather than in the message identity. A response inside an option uses the same compound identity
with the question as parent, such as Responded C-2 of Q-all-500, and a reply on a
standalone view-level bug, question, or suggestion carries its root the same way, such as
Responded C-all-91 of B-all-52. Only a change to the root itself, a reopen or a
resolution, names the thread root alone, such as Responded B-all-52, because the root
is the changed item. The waiting agent reloads that item and continues on its own.
Automatic events for collaborators
After the AI has participated in a job by commenting, replying, voting, or approving, Uclusion also wakes it for response-worthy human changes:
- Creating a task, grouped task, question, suggestion, or blocker sends
Added <item short code> of <job short code>. - Editing, moving, or deleting one of those items sends
Updated <item short code> of <job short code>. Assignment changes, job-description changes, and explicit stage changes sendUpdated <job short code>. - Editing the current intent/design capsule body sends
Updated <capsule short code> of <job short code>. - Proposing a new option on a question sends
Added <option code> of <question short code>; editing or promoting one sendsUpdated <option code> of <question short code>.
Added and Updated are additive events, not instructions to replace the work already in progress.
The agent reloads the enclosing job, incorporates the change into its current work, and keeps the
active job as its scope. If nothing is active, the loaded target becomes active work. A reloaded
stage may lock further execution; the agent respects that live stage without treating the event as
a new instruction to start the job. For a deleted job item, get_job explicitly says that the
requested item was deleted and returns the current enclosing job with that item absent.
A capsule body update is the exception to additive context. It replaces the selected target’s implementation contract. The agent reloads that capsule and Reports, resolves its obsolete review naming the same capsule, and reconciles active work before continuing. If no lane is active, it performs that cleanup without treating the event as permission to start implementation.
Adding an item can itself change the job’s stage or readiness—for example, adding a blocker can move
a Doable job to Blocked. In that case Uclusion holds the item’s Added event until the derived
workflow write commits, then sends that same item event. The agent’s resulting get_job reload
contains both the newly added item and the current stage, without a second stage event or a window
where the agent is told about the blocker but still reads Doable. Other derived stage changes do
not emit an additional stage Added/Updated event; votes may still produce their normal
Responded hand-back. Cosmetic metadata remains silent.
Codex must be started through uclusion for pokes to work
uclusion codex
Arguments for Codex go after --; for example, this starts the session with an initial task:
uclusion codex -- "Run find_work"
Do not pass --remote: the launcher owns Codex’s connection so it can deliver Pokes to the right
chat. Configuration and feature flags such as -c, --config, --enable, --disable, and
--strict-config may be passed normally after --. The launcher applies them to the visible
Codex session and the private Codex runtime where needed.
A Poke has the same input behavior as typing its message into that chat:
- If Codex is idle, the Poke starts a turn.
- If a regular turn is active, the Poke is steered into it like pressing Enter and is handled at Codex’s next safe model or tool boundary. Stacked Pokes retain their arrival order.
- An inline Codex review in the primary chat, or a manual compaction, cannot accept steered input. The Poke remains queued until that operation changes or ends.
The bridge does not mark a Poke delivered merely because Codex accepted an API request. It waits until Codex commits that exact user message. It can retry input proven unable to commit without duplicating a user message that already committed.
Known issues
Codex can remain silent after accepting a Poke. This behavior matches
open Codex bug #23807, a five-minute stall between a tool result and the next model request.
Uclusion has observed the same failure shape after uclusion codex delivered and Codex committed a
Poke: the chat showed that it was working without producing output.
Codex may retry automatically after about five minutes. To recover sooner, interrupt the turn, then
type continue in the same Codex chat. Do not press Poke AI again for the same item because the
original message may already be in the chat and a second Poke can duplicate the instruction.
Choosing whether Codex processes the retained backlog
By default, after the bridge acquires ownership, it establishes a startup cutoff and skips every
Poke already waiting at that cutoff. For example, switching from Claude to Codex does not make the
new Codex session process the last week of retained Claude Pokes. To process that retained backlog
deliberately, put --deliver-existing-pokes before the passthrough separator:
uclusion codex --deliver-existing-pokes -- "Run find_work"
With the option, the bridge processes its retained backlog in arrival order and then continues
with later Pokes. Without it, Pokes arriving after the launch cutoff are still delivered. The
default cutoff does not delete queue records, skip update notices, or advance the cursor used by
listen, wait, or another named consumer. It also cannot cancel a Poke that Codex had already
committed before the bridge restarted.
Which Codex chat receives the Poke
The primary, input-owning Codex chat receives Pokes. Starting a new primary chat, resuming one, or
forking one changes that target in order with Poke admission, so the Poke is sent to the primary
that was current when Codex accepted it. A later intentional switch is allowed and can therefore
hide a still-running Poke in the old chat. Temporarily viewing /side or an /agent transcript
does not retarget Pokes, so work can appear in the primary chat while one of those views is on
screen. Update notices wait for the primary chat to become idle.
uclusion codex must launch the session; it cannot attach autonomous delivery to a Codex process
that is already running. It requires Codex CLI 0.145.0 or newer. Only one bridge may own a
workspace and environment at a time. The launcher currently supports Linux and macOS, where it
can verify that the visible Codex terminal is still alive.
How uclusion listen and uclusion wait work
The MCP proxy keeps a websocket subscription open for your account and writes every inbound prompt
into a local, user-private queue on your machine (~/.uclusion/poke_inbox.sqlite3), de-duplicated
by message id so broadcast and retry copies deliver once. Two commands read that queue, both
polling it every quarter second and taking the same environment flag as every other CLI command:
uclusion listennever exits. It prints each prompt as one line the moment it arrives, so an AI harness that can raise a running command’s output lines as events (such as Claude Code’s persistent Monitor) is woken per prompt with no restart between deliveries. Prefer this form wherever the harness supports it.uclusion wait --timeout <seconds>is for harnesses that only receive a command’s output when it exits (for exampleuclusion -e stage wait --timeout 3600). When at least one prompt is pending it prints the whole pending backlog, one prompt per line, and exits, so a burst of pokes costs a single wait; exiting silently means the timeout expired with nothing new.
While idle both commands also check about every fifteen minutes whether a newer Uclusion release is
available and print a one-time update notice when they first see one — wait exits after printing
it, while listen keeps running.
Prompts are not removed when they are read. They stay in the queue until they age out after
seven days, and delivery tracks each session’s own cursor: uclusion listen automatically
generates a private per-session identity (the UCLUSION_CONSUMER environment variable or
--consumer overrides it), while a bare uclusion wait shares the default cursor so
turn-start drains keep treating the pending backlog as live work. Every session therefore
receives every prompt that arrives while it is armed, and a freshly started session begins
clean: its cursor starts at arm time, so the retained backlog is never redelivered — anything
still needing attention already shows on the agent’s find work list. Session cursors idle past
the retention window are garbage collected automatically.
Starting without an old backlog
Fresh sessions do this automatically: a new listener’s cursor starts at arm time, so the
prompts that piled up while you were away — for instance a week of Claude Pokes after time
spent in Codex — are simply never delivered, and old activity, including an old Start click,
never hijacks a new session. Whatever still needs attention is on the agent’s find work list.
The --ignore-existing-pokes flag remains for the shared default cursor used by a bare
uclusion wait, whose pending backlog is otherwise live work at turn start. (A session on an
older CLI may still deliver the backlog with a (replayed) suffix on each line; agents drop
those lines.)
Skipping advances only that consumer’s own cursor. Prompts arriving afterward are still delivered, nothing is deleted from the queue, update notices still appear, and other consumers — including the Codex bridge — are unaffected.
The backlog stays available on request: ask your agent at the start of a session and it arms
its listener with --deliver-existing-pokes, which keeps the fresh cursor at zero so the whole
retained backlog is delivered as that session’s private copy — nothing else is affected. That
also supports schemes built on the queue itself, like sessions that scan the backlog for the
first Start no other session has already taken.
The form of the messages
The message text is the contract, and the first word tells you which kind arrived:
| Kind | Form | Sent when |
|---|---|---|
| Poke | Start <short code> |
A person explicitly presses Poke AI in the Uclusion UI |
| Added | Added <short code> |
A person creates a response-worthy task, grouped task, question, suggestion, or blocker on a job where AI collaborates |
| Updated | Updated <short code> |
A person makes a response-worthy edit, including a current capsule body replacement, move, deletion, assignment change, description change, or explicit stage change on a job where AI collaborates |
| Respond | Responded <short code> |
A semantic human reply, vote, or Resolve hands activity back to the AI; advisory responses are included even when they do not unblock work |
The first word is the contract: an explicit UI poke begins Start, creation begins Added, a
response-worthy mutation begins Updated, and a human hand-back begins Responded. Agents and any
scheme you build on top of the queue can rely on that prefix. Any nested item appends
of <parent short code> to the applicable form — the enclosing job for a comment on a job, the
thread root for a view-level reply, and the enclosing question for anything inside its option
market. The parent is always globally resolvable, which is what lets an agent set aside messages
about work it does not own without looking anything up. An older release may append
(replayed) to a line delivered from a new session’s backlog replay — schemes parsing the
queue should accept that suffix on any form, and agents treat any such line as history to drop
rather than an instruction to act. (Very old releases may still deliver a bare Responded.
with no short code, and releases before mid-2026 sent nested comment messages without the
of <parent> suffix.)
Multi-agent by default
Every agent session automatically gets its own delivery cursor: each uclusion listen generates
a private per-session identity, and the Codex bridge keeps its dedicated cursor. Every session therefore receives every prompt —
nothing is stolen by whichever process happened to poll first — so running a second session,
deliberately or by accident, never makes a Poke disappear. Only a bare uclusion wait with no
session identity still shares the default cursor, which is what surfaces that drain at turn
start rely on.
The installed workflow files tell each agent to process its stream single-threaded: prompts about
the job or bug it is currently working are handled immediately, anything else is briefly
mentioned and set aside, and when the current work finishes the agent runs its find work list,
which already reflects whatever arrived meanwhile — a set-aside Start never auto-starts; if you
still want it worked, you pick it from that list. Dividing the labor between concurrent sessions
stays your responsibility: tell each session what it owns, and the set-aside behavior keeps them
out of each other’s lanes.
For schemes that need explicit, durable identities — a leader agent distributing work, or a named
lane that survives restarts — set the UCLUSION_CONSUMER environment variable for a session or
launch with --consumer <name>: every distinct name receives every prompt exactly once, in
order. Beyond delivery, the opt-in work claim lock below keeps idle agents from starting the same
work; anything finer stays yours to manage through the instructions you give them.
The opt-in work claim lock
Delivery alone cannot stop two idle agents, possibly on different machines, from both running their find work list and starting the same job or bug. The work claim lock closes that gap. It is off by default; enable it with:
uclusion update --work-claims
and disable it with uclusion update --no-work-claims. For a non-production environment, put its
option before the subcommand, for example uclusion -e stage update --work-claims. Restart the
affected AI client sessions (or reconnect their Uclusion MCP servers) after the command completes
so the new setting loads.
With the lock enabled, each agent’s Uclusion connection exposes a claim_work tool and the
installed workflow tells the agent to use it: right before starting work, the agent claims its
candidates in preference order in a single call and is granted the first one that is free, so an
agent behind several busy peers lands directly on unclaimed work instead of collecting one denial
per taken item. The agent starts the item it was granted and releases the claim when the work
hands off. A claim denied outright means everything listed is already being worked. Merely
reading an item never claims it, so agents can triage freely without blocking each other.
The lock is held server side against the agent’s live connection and its expiry is refreshed by the connection’s normal heartbeat, so it needs no attention:
- An agent that hands work off releases its claim immediately.
- An agent that exits cleanly releases all of its claims on shutdown.
- An agent that crashes or loses its network simply stops refreshing; the claim expires on its own within a few minutes and the work becomes claimable again.
- A brief network drop does not lose the lock. The reconnecting agent re-asserts the short codes it holds and recovers each one whose old connection is gone, while a code another agent legitimately claimed in the meantime stays with its new owner.
The lock is a coordination aid, not a permission system: agents on the same account cooperate through it, and if the lock service is ever unreachable the agent proceeds without it rather than stopping work.