Adds a `send` custom tool so the agent can push files and images from the
workspace to the owner as Telegram attachments, closing the gap where the
bot could receive files but only ever reply with text.
Two review followups:
- Consume the piped stderr from @playwright/mcp so browser-launch
failures surface in our own logs (was set to "pipe" but never read).
- Drop the wrong ["mcp-server-playwright"] lookup; the actual bin key
is "playwright-mcp". Falling through Object.values worked but was
dead code.
Bridge @playwright/mcp as a stdio subprocess and re-export its tools
as Pi custom tools, gated on PLAYWRIGHT_ENABLED. Uses Type.Unsafe to
pass MCP inputSchema through to the model without hand-mapping.
Extends the calendar tool to create, modify, and delete recurring events
via raw RFC 5545 recurrence lines, with instance vs. series scope on
update/delete and a list_instances action to fetch a series' occurrences.
Telegram delivers voice messages with a .oga extension (audio-only OGG),
which OpenAI's transcription API rejects even though the container bytes
are identical to .ogg.
Install tzdata so TZ resolves, default SECRETARY_TIMEZONE to
Europe/Warsaw, and pass it through as the container-wide TZ so OS
timestamps (logs, date, cron) match the app's scheduling zone.
pi-claude-code-provider fences off Claude Code's MCP servers by design,
so managed MCPs like Google Calendar can't reach the bot through that
provider. Implement calendar access as a first-class Pi tool instead —
it lives alongside `schedule` and calls Google Calendar API v3 directly
with a subscription-free refresh-token grant.
Config: three new optional env vars (GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKEN) — set all three to enable
the tool, leave all three blank to disable. Partial config raises a
ConfigError at startup so mis-set env is caught early.
Tool: list / create / update / delete on any calendar (default
'primary'). Access tokens are cached until expiry and refreshed on
demand from the long-lived refresh token. Invalid_grant surfaces a
clear "re-run device flow" error.
The provider validates on every session init that Claude launched with
exactly its 10 mcp__pi__* proposal tools. Removing --tools "" and
--setting-sources "" made Claude Code load its built-ins plus every
extension/skill/managed-MCP tool on the user's account, which trips
the "unexpected tool set" isolation check and refuses the session.
Bump the install marker to .installed.v2 and rm -rf the package dir
before install so bind-mounted volumes carrying the previously-patched
copy get a clean reinstall. Google Calendar access via MCP through
this provider is not viable — the provider is adversarially designed
against exactly this. Implement calendar as a native Pi custom tool
if we want it in the bot.
pi-claude-code-provider deliberately isolates its Claude subprocess by
hard-coding --strict-mcp-config, --setting-sources "" and --tools "",
which hides every user-configured MCP server (Google Calendar, etc.)
from the model. Patch those three flag-pairs out of the installed
provider's src/claude-args.ts on every start (idempotent — no-op once
they're gone). Trade-off: Claude can now invoke MCP/web/file tools
invisibly, forfeiting the provider's execution-authority guarantee.
Acceptable for a personal Telegram bot.
pi-backend passed a hard-coded `tools: ENABLED_TOOLS` allowlist into
createAgentSessionFromServices. Pi treats that as `allowedToolNames`
and filters every tool — built-in, extension, MCP — whose name isn't
in the list. Any MCP server the user enabled (Google Calendar, ...)
was silently invisible to the model.
Drop the allowlist. Instead, after bindExtensions returns (once MCP
servers have registered their tools), activate every tool in the
registry via setActiveToolsByName. That covers built-ins the SDK
doesn't turn on by default (grep, find, ls), custom tools like
`schedule`, and every MCP tool the user has connected.
Switch from named volumes to bind mounts under one host directory so
the workspace, pi-agent, and claude state is easy to back up / inspect
on the Coolify host.
Also re-adds the sleep-infinity override so the container stays up
while claude auth is redone against the new (empty) claude bind mount.
Two additions:
- ENV PATH prepends /app/node_modules/.bin so `pi` is on PATH inside
the container (previously `docker exec ... pi ...` failed with
"command not found").
- `npm install -g @anthropic-ai/claude-code` so the `claude` CLI is
available — pi-claude-code-provider shells out to it.
The node:24 base image sets ENTRYPOINT ["docker-entrypoint.sh"]. We
override that script but the ENTRYPOINT still runs on every start, so
compose's `command:` was being handed to our script as args (and
silently ignored — we always exec'd `node dist/main.js`). That made the
`sleep infinity` override useless: the crash-looping app kept running.
Fix by execing "$@" when args are present so compose overrides work,
and set CMD to ["node","dist/main.js"] so normal (no-override) starts
still boot the app.
Override the compose command with `sleep infinity` for now — the deploy
failed because Pi has no auth for pi.dev yet. Once the user has run
`pi login` (or dropped an auth.json in /pi-agent) inside the container,
remove the `command:` line to restore normal app startup.
The Pi coding agent stores installed packages under $PI_CODING_AGENT_DIR
(=/pi-agent in the image), which is a volume — so anything installed at
build time is masked at runtime. Wrap the CMD in an entrypoint that runs
`pi install npm:pi-claude-code-provider` the first time the volume is
seen and drops a marker so subsequent starts are a no-op.