StartableToolSet wraps a ToolSet with lazy, single-flight start semantics. This is the canonical way to manage toolset lifecycle. It also de-duplicates failure warnings: when Start() fails repeatedly (e.g. an MCP server is down), only the *first* failure of each streak is reported via ShouldReportF
| 83 | // MCP server stuck returning "toolset not started" therefore surfaces a single |
| 84 | // warning per streak instead of one on every conversation turn. |
| 85 | type StartableToolSet struct { |
| 86 | ToolSet |
| 87 | |
| 88 | mu sync.Mutex |
| 89 | started bool |
| 90 | startStreak failureStreak // Start() failures |
| 91 | listStreak failureStreak // Tools() listing failures |
| 92 | // recoveryStreak tracks once-per-streak notices specifically for |
| 93 | // recovery failures (the toolset was previously started and working, |
| 94 | // then Start failed again). Distinct from startStreak so callers can |
| 95 | // emit a different, more targeted message (e.g. "needs re-auth" vs |
| 96 | // "start failed") for the recovery case. |
| 97 | recoveryStreak failureStreak |
| 98 | } |
| 99 | |
| 100 | // NewStartable wraps a ToolSet for lazy initialization. |
| 101 | func NewStartable(ts ToolSet) *StartableToolSet { |
nothing calls this directly
no outgoing calls
no test coverage detected