`subscribe_outbound()`, `dispatch_outbound()`, and `stop()` have zero
callers — `ChannelManager._dispatch_outbound()` handles all outbound
routing via `consume_outbound()` directly. Remove the dead methods and
their unused imports (`Callable`, `Awaitable`, `logger`).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Inject cache_control: {"type": "ephemeral"} on the system message and
last tool definition for providers that support prompt caching. Adds
supports_prompt_caching flag to ProviderSpec (enabled for Anthropic only)
and skips caching when routing through a gateway.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a shell command times out, process.kill() is called but the
process object was never awaited after that. This leaves subprocess
pipes undrained and file descriptors open. If many commands time out,
fd leaks accumulate.
Add a bounded wait (5s) after kill to let the process fully terminate
and release its resources.
- Add image upload via im.v1.image.create API
- Add file upload via im.v1.file.create API
- Support sending images (.png, .jpg, .gif, etc.) as image messages
- Support sending audio (.opus) as voice messages
- Support sending other files as file messages
- Refactor send() to handle media attachments before text content
The send() payload contains user message content (msg.content) which
may include non-ASCII characters (e.g. CJK, German umlauts, emoji).
The auth frame and Discord heartbeat/identify payloads are left
unchanged as they only carry ASCII protocol fields.
The deny pattern `\b(format|mkfs|diskpart)\b` incorrectly blocked
commands containing "format" inside URLs (e.g. `curl https://wttr.in?format=3`)
because `\b` fires at the boundary between `?` (non-word) and `f` (word).
Split into two patterns:
- `(?:^|[;&|]\s*)format\b` — only matches `format` as a standalone
command (start of line or after shell operators)
- `\b(mkfs|diskpart)\b` — kept as-is (unique enough to not false-positive)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds `_validate_schedule_for_add()` to `CronService.add_job` so that
invalid or misplaced `tz` values are rejected before a job is persisted,
regardless of which caller (CLI, tool, etc.) invoked the service.
Surfaces the resulting `ValueError` in `nanobot cron add` via a
`try/except` so the CLI exits cleanly with a readable error message.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>