- main.ts: unhandledRejection/uncaughtException also print the stack (or
message) via console.error so it is not lost when the logger only prints a
single line; bot.start(...) is no longer fire-and-forget -- a polling
failure is logged and exits the process instead of failing silently.
- bridge.ts: /start now greets before showing the same status block as
/status (split back into two command handlers). Command detection now
checks for a bot_command entity at offset 0 instead of a leading "/", so a
message that merely starts with a slash (e.g. a path) reaches the agent
instead of getting "Unknown command.". An unsupported media message with
no text and no other notes (sticker, location, video note, ...) now gets
an explicit "I can handle text, photos, voice notes, and files." reply
instead of silently doing nothing.
- outbound.ts sendMarkdown: only falls back to plain text for Telegram's
400 "can't parse entities" style errors; any other error (network, etc.)
propagates instead of being masked as a silent plain-text resend.
- runner.ts resolveShell: added the per-user Git for Windows bash.exe under
%LOCALAPPDATA% as a third candidate; README's Windows section now
mentions SECRETARY_SHELL for that case.
- docker-compose.yml: added init: true, since Node does not reap zombies
left by killed script process groups.
Claude-Session: https://claude.ai/code/session_01JtuZTCMW9TyogEmBgGf8JT
- runner.ts: cap captured stdout/stderr at 64 KiB per stream via a new
Capture helper, keeping the first 48 KiB and last 16 KiB once a stream
exceeds the cap, joined by "\n...(truncated)...\n". Prevents a runaway
watcher script from ballooning memory or a Telegram message.
- runner.ts runScript: delete TELEGRAM_BOT_TOKEN from the child environment
before spawning, since model-written scripts must never be able to read
and exfiltrate it. Spec section 6.2 updated to describe the exclusion.
- scheduler.test.ts: the "drain() gives up after its timeout" test no longer
calls s.stop() at the end -- no timer is armed after the at job fires, and
its delivery never resolves, so stop()'s 10s grace period would run out
for nothing. Full suite now runs in ~1.5s.
- runner.test.ts: relax the timeout-kill elapsed-time assertion from 3000ms
to 4000ms to reduce flakiness, per the review.
Claude-Session: https://claude.ai/code/session_01JtuZTCMW9TyogEmBgGf8JT
- pi-backend.ts start(): after confirming a model resolved, also confirm the
provider has usable credentials via ModelRuntime.hasConfiguredAuth /
checkAuth, so a missing-auth failure surfaces as a clear startup error
instead of the first prompt failing later. checkAuth resolves to an
AuthCheck object or undefined (not a boolean), so the existing
`!(await mr.checkAuth(provider))` truthiness check already does the right
thing with no further adaptation.
- The inline extension's before_agent_start handler now also appends the
current date/time in the configured timezone, so the model knows "now"
without a tool call. Extracted as system-prompt.ts's currentTimeLine(),
unit tested with a fixed date.
Claude-Session: https://claude.ai/code/session_01JtuZTCMW9TyogEmBgGf8JT
Pi drains queued follow-ups and steers inside the same run with no
agent_end/agent_start between them, so when a scheduled job's follow-up (or
a user steer) merges into a run, the model can answer twice before
agent_settled fires once. AgentHost.settle() used only the last assistant
message as finalText, so the first answer was lost (it only ever appeared
as progress-message narration, which gets deleted).
RunRecord now tracks `finals`: assistant texts whose stopReason is not
toolUse (and, as before, not error/aborted), collected alongside `texts`.
settle() joins `finals` with blank lines, falling back to `texts` and then
to the placeholder, matching the documented fallback chain.
Claude-Session: https://claude.ai/code/session_01JtuZTCMW9TyogEmBgGf8JT
Docker build verified locally (docker build -t volcanic-secretary .
succeeded on this machine). Step 6's manual end-to-end checklist
against a real Telegram bot was not run in this environment (no bot
token or owner id available); the checklist is kept in the README's
Tests section for the owner to run before relying on this in
production.
Claude-Session: https://claude.ai/code/session_01JtuZTCMW9TyogEmBgGf8JT