Skip to content

A message lifts a wait the agent started, and cancels nothing - #876

Open
lydakis wants to merge 14 commits into
mainfrom
claude/project-thread-jsrhrx
Open

lydakis wants to merge 14 commits into
mainfrom
claude/project-thread-jsrhrx

Conversation

@lydakis

@lydakis lydakis commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

What

One rule for a message that arrives while the agent waits: a message lifts any wait the agent started, and cancels nothing.

Before this, a steer that reached a turn while a call waited for its approval (or for its client's answer) either sat behind the wait or, if the person used the window, cancelled the call. Now:

  • The wait becomes a handle. A steer can join the turn while a call waits for its verdict, whether the wait is held live (after the same 250 ms grace a foreground shell gets) or parked. The steer then wakes the turn and answers the call with {"handle":"proc:N","background":true,"steered":true,"waits_for":"approval"|"answer"}. The turn reads the message right away.
  • Nothing is cancelled. The request stays listed (with handle), served and answerable past its turn's end. A restart or a prune keeps it. Its time goes on the run loop's schedule as it lifts. Its verdict now goes to the command:
    • An allow runs the call then, in the background, in its turn's folder, with that turn's model, effort, environment and command rules:

      • a shell command runs as a background one;
      • a terminal runs under its handle, with its first output left for the next read;
      • any other call's result becomes the command's, and its pictures are kept for the wait that collects it;
      • a call its client answers waits on for that answer.

      A pause that comes after the call's check stops it from starting, and a pause ends it while it runs, as in a turn. A call that reads or changes its own turn (wait, note, history, read of a result) fails turn_ended.

    • A deny or a lapse ends it with the result the call would have had.

    • An answer ends it, told as {"answer": TEXT}.

  • It reports back as a background command does. The bot is told the end, with the first 2,048 bytes of the call's output (or the answer), and wait on the handle collects it whole. Its row in the audit trail ends with it.
  • The agent may stop it. write_stdin with kill: true on the handle withdraws a request still waiting (never run) or kills what it started. It answers {"handle","stopped":true,"ran":BOOL}. The same kill now ends any background command, in a turn or when allowed after a lift.
  • Waits the agent did not start keep the message: a provider's pacing, a pause, a shutdown, the load gate. An attach never lifts. No timers are added.

Events and listings:

  • call_lifted {call_id, handle, waits_for}.
  • lifted_ended {call_id, handle, outcome}. The outcome is either:
    • allowed, with approvals, plus waits_for:"answer" when the call now waits for its client; or
    • one of the ends denied, expired, answered, unserved, withdrawn or stopped, which carry failed and the result as told.
  • A lifted allow emits no tool_started, because that event means a call of the running turn, for the window, ACP and the lookout.
  • tool_calls lists a lifted call with its handle. A bot's approval listing includes the steps its lifted answered calls ask for, under the call's handle.

Sonny:

  • A lifted call and its steps stay in the window's approval list and in needs-you until lifted_ended. A turn ending or turn_resumed no longer clears them.
  • The tool host keeps serving a lifted call past its turn's end, and serves one allowed later.
  • The ACP thread keeps a lifted call until it ends, so a lifted start_task still opens its task's thread. It keys the calls it shows by turn and call.

Protocol and schema

Protocol 64, schema 91:

  • processes.call holds the lifted call.
  • approvals.process and answers.process (indexed, partial) let listings find lifted requests past their turn.
  • processes_bot_running is a partial index of running commands by bot.

Performance

  • Lifting is one commit (a processes row and the request's process). It happens only when a steer meets a waiting call, so a turn with no waiting call does no new work.
  • One bot's listing reaches its lifted calls and their steps through that bot's own running commands (processes_bot_running), then approvals_lifted, answers_lifted and approvals_steps. It never reads another bot's calls, and a unit test pins that plan.
  • An allow after a lift runs on the existing background-command path. There is no new task, timer or poll. The pause check is a watch receiver taken once per check.
  • A prune's tools delete skips only the calls of commands still running, at the cost of one indexed subquery per pruned turn.

Tests

  • store_contract:
    • the lift, withdraw, allow, deny, expire and answer paths, plus restart;
    • a_lifted_answered_call_keeps_its_time_steps_listing_and_audit_row: lapse, step listing (by bot and by tag, under the handle), step verdict, prune, answer with pictures, and the audit row.
  • tool_contract: a terminal started with no yield keeps its first output for the next read.
  • Unit tests:
    • a start decided before a pull of the pause cord never begins;
    • a wait takes an answer's pictures out of its words;
    • the per-bot listing's query plan.
  • Runtime tests:
    • test_runtime::test_a_message_lifts_an_approval_wait_and_the_verdict_reports_back: parked and held waits, an allow with the note it tells, and a stop.
    • test_a_lifted_kill_stops_a_background_command_once_allowed.
    • test_images::test_a_lifted_read_hands_its_picture_to_the_wait_that_collects_it.
    • The terminal test now expects "stopped":true from a kill of a background command.
  • test_acp: a steer that lifts a start_task its client is running still opens the task's thread.
  • Workspace tests, clippy, fmt and the whole runtime runner (tests.run --jobs 4, with bubblewrap) pass locally.

🤖 Generated with Claude Code

A steer that joins a turn while a call waits for its approval or its
client's answer, held live or parked, now lifts the wait: the call becomes
a proc: handle that waits on past the turn and reports back as a
background command does, and the turn reads the message. The request stays
listed and answerable; an allow runs the call then, in the background, and
a deny, a lapse or an answer ends it with the result the call would have
had. write_stdin kill=true withdraws a lifted request or stops what it
started, and now stops any background command. Waits the agent did not
start keep the message for later.

Protocol 64, schema 91 (processes.call, approvals.process,
answers.process). Sonny keeps a lifted call in the window and needs-you
until it ends (call_lifted, lifted_ended).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-11T09:33:53.018799Z ddfebf4 New commits
🔒 Security Review ✅ Completed 2026-11-11T04:51:02.316245Z 2f9bcb2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f9bcb2245

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/turn.rs Resolved
Comment thread src/store/db.rs Resolved
Comment thread src/store/db.rs Resolved
Comment thread src/store/db.rs Resolved
Comment thread src/server/turn.rs Resolved
Comment thread src/server/turn.rs Resolved
- Its lapse goes on the schedule as it lifts, so a lifted call with a
  time of its own runs out without a restart.
- Its row in the audit trail is read before the handle's answer clears
  it, so its end is recorded.
- A prune keeps a running command's call, so a lifted answered call is
  still listed and answerable past retention.
- A bot's approval listing includes the steps of its lifted answered
  calls.
- A lifted terminal starts without reading its first output, which stays
  for the next read or its end.
- A turn-dependent call allowed after its turn ended says so without
  advice.
- `lifted_ended` carries `failed` and the result as told, and
  `waits_for: "answer"` when an allowed call now waits for its client;
  `tool_calls` lists a lifted call with its `handle`.
- Sonny's tool host keeps a lifted call it serves past its turn's end and
  serves one allowed later; the ACP thread keeps a lifted call until it
  ends, so a lifted start_task still opens its task's thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 598e8c0cc8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/store/db.rs Resolved
Comment thread src/store/db.rs Resolved
A wait on a lifted answered call hands the answer's pictures over with
its words, and a step of a lifted answer takes its verdict after its turn
has ended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c1b030986

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/turn.rs Resolved
Comment thread sonny/src/main.rs Resolved
Comment thread src/server/turn.rs Resolved
Comment thread src/server/turn.rs Resolved
Comment thread sonny/src/acp.rs Resolved
Comment thread src/store/db.rs Resolved
An allowed lifted call does not start once a pause came after it was
checked, and a pause ends it as it runs. A lifted read keeps its
pictures for the wait that collects it, and a lifted kill of a command
without a terminal stops it, as in a turn. A lifted answer's steps are
listed under its handle, so Sonny keeps them past the turn. One bot's
approvals are found through its own running commands, on a partial
index by bot. The ACP bridge keys the calls it shows by turn and call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb6c8c571c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sonny/src/needs.rs Resolved
Comment thread sonny/src/needs.rs Resolved
Comment thread src/server/turn.rs Resolved
Sonny's needs scope each call's end to its turn, and keep a lifted call's
steps past its turn until the call ends, from events and from listings
alike. A lifted call's result keeps `parts` for its pictures only: an
output that names its own stays whole as `output`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f86aea558a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sonny/src/main.rs Resolved
Comment thread src/server/turn.rs Resolved
…n kill

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 057a49595c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/turn.rs Resolved
Comment thread sonny/src/needs.rs Resolved
Comment thread src/store/db.rs Resolved
Comment thread src/server/turn.rs Resolved
Comment thread src/tools.rs Resolved
… stops

- A step of a lifted answered call carries its call's handle while the
  call's own turn still runs, as it does after.
- Sonny keeps a lifted call's marker when it is allowed and waits for its
  answer, so steps asked for then outlive the turn.
- Stopping a lifted answered call reports ran: true once its client took
  it, in the kill's answer and the stopped result; ran: false only for a
  call that never started.
- A killable run's place among those its agent can kill goes when the run
  is dropped, so a pause leaves nothing behind.
- A lifted result with pictures is built off the runtime thread, and the
  store job hands the value back instead of a copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1e887e8cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/store/db.rs Resolved
Comment thread src/server/turn.rs Resolved
- A wait whose results carry pictures takes them out on a blocking
  worker, where a result another waiter shares is copied.
- The unfiltered approvals listing gives a lifted call's step its call's
  handle, as the bot and tag listings do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d21a85e160

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/turn.rs Resolved
Comment thread sonny/src/acp.rs Resolved
- Stopping a lifted call by its handle takes its time off the run loop's
  schedule, as its verdict or answer does.
- An ACP thread that comes back puts lifted calls (and their steps) to
  the person again, whether or not a turn runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb4e21b3e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sonny/src/acp.rs Resolved
A later turn's call under a lifted call's id and request number is put
to the person afresh; a decided approval lets go of its own turn's marks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bea56d5d87

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sonny/src/needs.rs Resolved
… call

A step announced for the reviewer only and then handed to the person
keeps its parent in Sonny's needs, so it outlives its turn like a step
asked of the person from the start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d11ad107a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/turn.rs Resolved
Comment thread sonny/src/main.rs Resolved
Comment thread src/store/db.rs Resolved
…easurements

- An allowed lifted call's environment takes its turn's model and effort,
  or the bot's as that turn ran when the bot's changed since, as `turns`
  reads them, rather than the bot's now.
- A Sonny window opened with no turn running lists the calls a message
  lifted, and their steps, for its approval keys.
- docs/DAEMON_MEASUREMENTS.md records what each path pays and the Perf
  screen against the base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ddfebf4559

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/turn.rs
Comment on lines +5998 to +6001
.await;
// A pull while it opened found no terminal to end.
if cord.pulled() {
registry.end_terminal(process);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Cancel lifted terminal preparation before spawning the child

If a pause arrives while a lifted terminal awaits a lasting slot or sandbox::hold_for, the terminal is not yet registered, so Registry::end_running cannot stop it. After that await completes, Registry::terminal still spawns the child before this cord check, allowing commands to produce effects after the pause response. The fresh evidence beyond the earlier preparation-guard fix is the additional asynchronous preparation inside Registry::terminal (src/tools/terminal.rs:405–438), after the initial cord.check(). Killing the terminal after spawning cannot undo those effects; propagate cancellation into terminal preparation and check it before spawning.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants