Files
Djordje Lukic 8d24d42210 refactor(runtime)!: extract remote execution
HTTP/SSE transport, reconnect recovery, and remote OAuth should not be dependencies of local execution. Move their implementations and tests into runtime/remote and migrate remote callers directly, without compatibility re-exports.

Keep the existing Runtime interface, Event types, constructors, and event sinks in runtime. The dependency is intentionally one-way: remote imports runtime, while local execution never imports remote. A regression test enforces this boundary; extracting shared types can be considered separately.
2026-10-26 12:40:23 +01:00

73 lines
3.9 KiB
Go

package runtime
import (
"time"
"github.com/docker/docker-agent/pkg/tools"
)
// defaultEventChannelCapacity is the buffer size used for every Event
// channel the runtime hands back to a caller. Sized large enough that a
// reasonable producer (the run loop, a stream forwarder) can stay ahead of
// a typical consumer (the TUI, an API server) without blocking, but small
// enough to keep memory pressure bounded when consumers are slow.
//
// All event-channel constructors in pkg/runtime go through this constant
// so the buffer size is set in exactly one place.
const defaultEventChannelCapacity = 128
// defaultMaxOverflowCompactions caps the number of consecutive
// context-overflow auto-compactions that the run loop will attempt before
// giving up and surfacing the error to the caller. The runtime's
// compaction routine cannot guarantee that the resulting transcript will
// fit inside the model's context window (very large recent messages may
// still exceed it), so this bound prevents an infinite retry loop.
//
// Production uses 1 (one compaction attempt per stream); tests can change
// this via WithMaxOverflowCompactions to verify both the "compaction
// succeeded" and "compaction exhausted" branches without having to drive
// many real overflows.
const defaultMaxOverflowCompactions = 1
// toolsChangedTimeout bounds how long a single MCP-tool-change refresh
// may take. The handler is invoked outside any RunStream goroutine (it's
// a notification from a server), so a slow or stuck server cannot be
// allowed to wedge the caller indefinitely.
const toolsChangedTimeout = 5 * time.Second
// defaultStreamStoppedDeliveryTimeout bounds how long finalizeEventChannel
// waits to deliver StreamStopped when the event buffer is full. 128 buffered
// events at roughly a millisecond each for a synchronous per-delta SQLite
// write (the common back-pressure source, see #4136) drain in ~130ms; 5s is
// comfortably above that for any consumer still reading, while still bounding
// teardown once a consumer has abandoned the channel (the #3070 deadlock this
// replaces). Tests shrink it via the streamStoppedDeliveryTimeout field.
const defaultStreamStoppedDeliveryTimeout = 5 * time.Second
// defaultToolListTimeout bounds how long EmitStartupInfo waits for a single
// toolset to enumerate its tools while populating the sidebar. A toolset
// whose Tools() blocks indefinitely — e.g. an MCP stdio subprocess that
// never answers tools/list, possibly while also ignoring context
// cancellation — would otherwise stall the whole startup: the terminal
// ToolsetInfo{Loading:false} event is never emitted, so the sidebar stays
// on "Loading tools…" forever and /quit appears to hang. A timed-out
// toolset is skipped here; it stays usable for the actual agent turn, which
// lists its tools again without this startup bound. Tests override it via
// WithToolListTimeout to exercise the skip path without a real-time wait.
const defaultToolListTimeout = 10 * time.Second
// defaultToolStartTimeout bounds how long EmitStartupInfo waits for a single
// toolset to start while populating the sidebar. A toolset whose Start()
// blocks indefinitely — e.g. an MCP stdio server backed by a wedged Docker
// daemon that never finishes launching the container — would otherwise stall
// the loop the same way a hung Tools() does: the terminal
// ToolsetInfo{Loading:false} event is never emitted and the sidebar animates
// "tools available…" forever. A timed-out toolset is skipped for this startup
// pass only — the original start attempt keeps running and the toolset is
// picked up once it completes. It aliases the shared bounded-start default so
// this startup probe and the agent's turn path give up on a wedged toolset
// after the same grace period; it is longer than defaultToolListTimeout
// because a cold start can legitimately include an image pull. Tests override
// it via WithToolStartTimeout.
const defaultToolStartTimeout = tools.DefaultStartTimeout