Repository navigation
Conversation
run.interrupt and the interrupt request and result items carry the optional createdBy and senderThreadId that message.dispatch uses. Both MCP interrupt paths record the agent and calling thread, client commands get createdBy stamped like messages, and the result text comes from the request: by user, by an agent, or no attribution when nothing requested the stop. Web and mobile link an agent stop to the thread that sent it. Fixes pingdotgg#16110
Contributor
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused, backwards-compatible fix that propagates interrupt provenance through existing server paths and updates web/mobile presentation. The added attribution and thread-link behavior is small, isolated to run interruption, and covered by targeted tests. You can add or adjust custom eligibility rules. Learn more. |
main's pingdotgg#16002 moved task_cancel onto thread.stop. thread.stop now carries the stop's createdBy and senderThreadId into its run.interrupt, the request row markStoppedRun writes, and every delegated task stopDelegatedTasks stops, so an agent's cancel still says "Run interrupted by an agent".
main's pingdotgg#15442 finishes a stalled run on Stop and writes its interrupt result itself. That result now reads the run's stop request too, from the same command when the session is already dead, or from the store when the run settles later, so a recovered run says who stopped it. A client's run.interrupt also drops any senderThreadId it sends, since only an agent's stop links to a thread.
Resolve the RunExecutionService conflict with main's ProviderAdapter namespace import. Make the interrupt attribution fields exact optional keys so turn items still fit the history paging row type added in pingdotgg#17387.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fixes #16110. When an agent stops another thread's run through
t3_thread_interruptortask_cancel, the stopped thread shows "Run interrupted by user".makeInterruptResultTurnItemhardcoded that text for every interrupted run, including runs nobody asked to stop (for example an OpenCode turn finished as interrupted after a reconnect).Change
This follows the fix area in the #16110 triage.
run.interrupt,run_interrupt_requestandrun_interrupt_resultget the optionalcreatedByandsenderThreadIdthatmessage.dispatchalready uses.createdBy: "agent"and the calling thread. Client commands getcreatedBystamped bywithCreationProvenance, as messages do, so a client can't claim to be an agent. The request row recordscreatedBy, defaulting touser.runInterruptSenderThreadIdin@t3tools/shared/orchestrationV2Timeline.Old rows keep the text they were written with. The new text also reaches later turns' history and handoff context, so models see who stopped the run.
After #16002:
task_cancelnow stops the child withthread.stop. This PR givesthread.stopthe same optional fields and carries them into therun.interruptit sends, the request rowmarkStoppedRunwrites when the turn can't be interrupted, and every delegated taskstopDelegatedTasksstops below it. An agent's cancel says "by an agent" all the way down. A user's Stop that cascades to delegated tasks says "by user", with no link.thread.stopis an internal command, so clients can't set these fields.After #15442: Stop can now finish a stalled run itself, writing the result row in the orchestrator. That row reads the run's stop request too, so a recovered run also says who stopped it. A client's
run.interruptdrops anysenderThreadIdit sends, since only an agent's stop links to a thread. If a run is stopped twice before it ends, the label names the last stop, the same waymainalready rewrites the request row on a repeat Stop.Scope and approval
Triaged in #16110 (
bug,via-triage). This implements the outlined direction, including the mobile link and the pre-start attribution.Verification
ping -c 150 127.0.0.1, and a second thread's agent calledt3_thread_interrupton it. The stored rows havecreatedBy: "agent"and the second thread assenderThreadId. The divider reads "Run interrupted by an agent", and clicking it opens the second thread.RunExecutionService.test.ts: agent, user and no-request results.OrchestratorMcpToolkit.integration.test.ts:t3_thread_interruptandtask_cancelend to end record agent attribution.CodexReplayFixturesturn_interrupt: an unattributed Stop reads "Run interrupted by user", through the real request lookup.OrchestratorMcpService.test.ts:task_cancelsendsthread.stopwith agent attribution.ThreadStop.test.ts: athread.stopthat can't interrupt the turn still records who asked.BackgroundWorkStop.integration.test.ts: the 5 stalled-run Stop cases record agent attribution.ThreadManagementService.test.ts: a client can't spoofcreatedByorsenderThreadId.orchestrationV2Timeline.test.ts: when a row links to the sender.vp test runon those files plusthreadActivity.test.tsandruntimeLayer.test.ts: 241 passed. One test inOrchestratorMcpToolkit.integration.test.tssometimes times out on "mcp-fanout-parent". Unmodifiedmainfails the same way in about 1 of 3 runs.main.main(through fix(mobile): browser picture in picture opens from the header button #17731): the server orchestration and MCP tests (2,012), the shared tests (1,466) and the contracts tests (644) pass. TwoAntigravityAdapterV2.test.tspath tests fail on macOS temp paths, outside this PR. Typecheck passes for contracts, shared, client-runtime, server, web and mobile. The attribution fields are nowSchema.optionalKey, so turn items still fit the history paging row type from fix(server): speed up long thread message sync #17387.task_cancelby hand (covered by the integration test).Before (0.0.46 nightly) / after (this branch):
Before:
After:
Built with Claude Opus 5.5 through Claude Code, running in T3 Code.