feat(drive): control arbitrary tool lifecycles#42
Merged
Conversation
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.
What
Add provider-neutral runtime control for arbitrary native OpenCode tools, mirroring the canonical lifecycle introduced by anomalyco/opencode#37816.
Consumers can atomically replace the dynamic tool set with
tools.attach({ tools }), claim native invocations withtools.take(callID), stream structured progress, finish or fail exactly once, and observe OpenCode-owned cancellation. Existingtools.control("shell" | "webfetch" | "websearch")adapters remain separate and unchanged.The release includes a minor Changeset for
opencode-drive.Before / After
Before: Drive could only control the fixed static adapters. During development of dynamic lifecycle support, lost acknowledgements, malformed protocol responses, server teardown, or a transport disconnect inside settlement could leave an attachment caller or settlement blocked, replay a rejected set, or report settlement before the final event barrier was proven.
After: Dynamic registration and invocation control are provider-neutral and reconnect-safe. Definitive rejection restores the last acknowledged set, generation teardown releases pending work, settlement retries a disconnected final barrier, and one terminal failure state prevents successful settlement after any producer invariant violation.
How
ToolProducerwith serialized replacement intent, invocation claiming, internal progress sequencing, bounded replay and cancellation retention, runtime payload validation, cancellation, and idempotent terminal retries.toolInputStartandtoolInputDeltawhen negotiated, retaining the existing OpenAI-compatible raw fallback for older endpoints.Scope
tool.*lifecycle capabilities; older OpenCode revisions remain compatible with static adapters and ordinary LLM control.Testing
bun run release:validateoxlintandtsgo --noEmitpassed.Flow
sequenceDiagram participant Script as Drive program participant Drive as ToolProducer participant OpenCode as OpenCode simulation Script->>Drive: tools.attach(registrations) Drive->>OpenCode: tool.attach OpenCode-->>Drive: attached OpenCode-->>Drive: tool.invocation Drive-->>Script: tools.take(callID) Script->>Drive: progress(update) Drive->>OpenCode: tool.update(id, sequence, update) opt tool socket disconnects Drive->>OpenCode: reconnect + replay replacement intent OpenCode-->>Drive: pending invocation replay end Script->>Drive: finish(output) or fail(message) Drive->>OpenCode: idempotent terminal RPC Drive->>OpenCode: tool.attach([]) settlement barrier Drive->>Drive: drain ordered events and verify no pending calls