Repository navigation
feat(plugins): let plugins submit session messages - #63
Merged
Merged
Conversation
…imits Add ADR 0040 removing autonomous turn-chain limits and ADR 0041 letting plugins submit attributed messages that start turns in their idle session. Amend ADRs 0001, 0014, 0026, and 0030, and refine CORE-PLUGIN-010 with new CORE-RUN-008, TUI-PLUGIN-002, and MAC-PLUGIN-002 backlog items.
Implement ADR 0040 (CORE-RUN-008). Context-only reactions are admitted without a consecutive-turn counter, so the droids reaction limit, deferred-limit state, ErrReactionLimit, DroidErrorLimit, and ReactUncounted are removed, along with the session's blocked-mailbox handling. Runtime state persisted with the former fields still loads, and boundaries it held pending are delivered.
Implement kit/session/submit-message (CORE-PLUGIN-010, ADR 0041). A plugin's current generation can start a context-only turn in its idle owning session from a plugin_message boundary sourced to the plugin, returning {messageId, turnId} on admission.
- droids: add ReactTo, which records a boundary and starts its turn in one commit or records nothing, and export BoundaryFraming.
- session: admit plugin messages under transition and admission authority, wait briefly for a settling turn so continuation from agent.turn.completed works, yield to active turns and queued user follow-ups, and replay keyed retries even while busy. Transcript projections present the submitted text without model-facing framing.
- plugin/server: strict request parsing, generation fencing, and stable RPC error codes that never expose internal errors.
- contract: validate plugin_message boundary details.
- Add the session-message-demo fixture, daemon subprocess tests, and the feature documentation.
When a plugin message starts a turn, the session event stream now carries plugin.message.added with the turn ID, plugin ID, and text. Clients can then show the message while the turn runs, not only after the transcript refreshes. The TUI shows persisted plugin_message context messages and the live event as one muted row, "◆ <plugin> · <first line> ▸", labelled in the plugin identity colour. Clicking the row shows the full message beneath a left rule. Prompt history still recalls only user prompts. The session-message-demo fixture no longer toasts admissions; admitted messages appear in the transcript and only rejections are toasted. Bumps the session protocol to 45. The macOS wire model decodes the new kind; MAC-PLUGIN-002 still owns presenting it.
Plugin-submitted messages appear as one muted row with the plugin symbol, the plugin name, and the message's first line as plain text. Clicking the row shows the full message beneath a left rule. Rows appear live from plugin.message.added and give way to the recorded plugin_message row, keeping the expansion choice for the turn. Recorded plugin messages no longer show as "Compacted context".
Plugin message rows now share the tool-call row layout, extracted as DisclosureChipLabel. The row shows the plugin symbol and the plugin name, with the message's first line as a muted 11pt summary. The expanded message uses the tool-call detail frame (disclosureDetail): a 1pt rule and a scroll area capped at 280pt. The row label's title, status, and summary now share a baseline, which also fixes the offset in tool-call rows.
A plugin can receive agent.turn.completed before the session clears the finished turn's active-run marker. A plugin that submitted a message straight from that event was rejected as busy, which stopped autoresearch-style loops. The plugin admission settle wait now treats an active turn whose completion has already been delivered to plugins as settling, and waits for it instead of rejecting. A running turn and queued user follow-ups still reject at once.
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.
Summary
kit/session/submit-message(ADR 0041). A plugin submits text to the session that owns it. Kit records the text as aplugin_messagecontext boundary attributed to the plugin and starts a turn that responds to it. The model sees the message framed as[plugin_message from <plugin-id>]; it is never recorded as user speech.-32006, records nothing, and Kit never queues the message. An optional idempotency key makes retries safe: the same key with the same text returns the originalmessageId/turnId, including after a plugin reload or daemon restart; the same key with different text is a conflict (-32003).plugin.message.addedsession event so clients show the message while the turn runs. Bumps the session protocol to 45. The OpenAPI document and Swift client are regenerated.◆ <plugin> · <first line> ▸. Clicking it shows the full message beneath a left rule (TUI-PLUGIN-002).session-message-demoproject plugin. It covers kickoff, a continuation loop, keyed retries and busy rejection, and runs in the daemon subprocess tests.Test plan
gofmt -l .go build ./...go vet ./...go test ./...go test -race ./internal/droids/ ./internal/session/... ./internal/plugin/... ./internal/server/... ./api/...go test ./internal/httpapi/openapiandscripts/check-openapi-compatibility.sh v0.41.0(breaking changes acknowledged by protocol 45)apps/macos/script/generate_openapi.sh --checkswift buildandswift test(487 tests)KIT_HOMEdev daemon with the demo plugin