OpenAI/Codex OAuth streams can return text through response.output_text.delta while the final response.completed payload has response.output set to null. That made PicoClaw report an empty model response even though the backend returned valid content.
Accumulate streamed output_text delta events during the Codex response stream and use them as a fallback when the parsed final response has no content. Add a regression test covering the null final output case from issue #2953.
The auxiliary message filtering introduced in #2892 incorrectly drops
tool_calls messages when there is an active stream or a tombstone from
a recently finished stream.
This causes tool_calls to not be delivered to the UI when users make
consecutive requests, as the second request's tool_calls message arrives
while the first request's stream is still active or within the 30-second
tombstone window.
The fix excludes tool_calls from auxiliary message filtering, since they
represent new tool invocations that must be delivered to the UI, not
stale auxiliary content like feedback or thoughts.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add FUNDING.yml file to enable GitHub Sponsors button on the repo.
This makes it easy for users who benefit from PicoClaw to support
the project financially.
Closes#2912
When running PicoClaw inside Termux or termux-chroot, HTTPS
requests fail with X509 certificate errors because the Go TLS
stack does not automatically detect the Termux CA bundle path.
This change adds automatic detection of Termux environments and
sets SSL_CERT_FILE to the correct CA bundle path before any
network operations. The detection checks:
- HOME or PATH contains 'com.termux'
- Common CA bundle locations in Termux prefix
Fixes#2944
CronTool.ExecuteJob was calling ExecTool.Execute without setting
action='run' in the args map. ExecTool.Execute requires the action
field and returns ErrorResult('action is required') immediately when
it's missing. This caused all cron command jobs to silently fail.
Adds a test covering the command execution happy path.
Discord only downloaded audio attachments before passing them to the agent. Non-audio attachments (images, videos, files) were passed as raw Discord CDN URLs, which do not flow through resolveMediaRefs and are not serialized as vision inputs.
Download every attachment, store it in the MediaStore with Discord's filename and content type metadata, and emit a media placeholder tag that matches the attachment kind. This lets resolveMediaRefs replace the placeholder with the local path-bearing tag and encode supported images for vision-capable providers. If a download fails, keep the previous raw URL fallback.