Skip to content

feat(drive): control arbitrary tool lifecycles#42

Merged
kitlangton merged 3 commits into
anomalyco:mainfrom
kitlangton:feat/tool-lifecycle
Jul 20, 2026
Merged

feat(drive): control arbitrary tool lifecycles#42
kitlangton merged 3 commits into
anomalyco:mainfrom
kitlangton:feat/tool-lifecycle

Conversation

@kitlangton

@kitlangton kitlangton commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

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 with tools.take(callID), stream structured progress, finish or fail exactly once, and observe OpenCode-owned cancellation. Existing tools.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

  • Copies the canonical registration, progress, output, invocation, cancellation, RPC, capability, and provider-neutral partial-input schemas into the Drive protocol.
  • Adds a reconnect-safe ToolProducer with serialized replacement intent, invocation claiming, internal progress sequencing, bounded replay and cancellation retention, runtime payload validation, cancellation, and idempotent terminal retries.
  • Uses a separate tool-only backend connection so uncertain idempotent lifecycle RPCs can reconnect without weakening the LLM controller's terminal disconnect semantics.
  • Preserves an unacknowledged replacement intent across reconnect, restores the acknowledged set after a known rejection, and rejects static/dynamic effective-name collisions locally.
  • Clears dynamic registrations, permits reconnect while clearing, drains the ordered event stream, and atomically closes backend creation before settlement checks unresolved invocations.
  • Rolls back failed server launches and surfaces permanent tool reconnect or malformed-protocol failures instead of retrying forever.
  • Emits toolInputStart and toolInputDelta when negotiated, retaining the existing OpenAI-compatible raw fallback for older endpoints.
  • Documents the public API, lifecycle architecture, and agent workflow.

Scope

  • Dynamic attachment requires all six canonical tool.* lifecycle capabilities; older OpenCode revisions remain compatible with static adapters and ordinary LLM control.
  • Cancellation remains OpenCode-owned; this does not add a public cancel method.
  • This does not add OpenCode-specific flow IDs, taxonomies, captures, or review UI to the generic package.
  • This does not add aliases or convenience protocol methods.

Testing

  • bun run release:validate
  • 30 Effect test files, 202 tests passed.
  • 54 CLI integration tests passed.
  • oxlint and tsgo --noEmit passed.
  • Package dry-run passed with 95 files and all public API compile checks.
  • End-to-end fake-driver coverage verifies tool-only reconnect, invocation replay, progress, terminal settlement, settlement barriers, launch rollback, and permanent reconnect failure propagation.

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
Loading

@kitlangton
kitlangton merged commit c20d147 into anomalyco:main Jul 20, 2026
1 check passed
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.

1 participant