Adapts @tynamite's fix from the abandoned #77945 to current main (which
moved to replaceFileAtomic after that PR was opened), and adds the docs +
changelog updates clawsweeper flagged plus a regression test for the
field condition from #80960.
When repairSessionFileIfNeeded writes a cleaned transcript, the sibling
*.bak-<pid>-<ts> snapshot is deleted after the atomic replace succeeds.
It is only retained — and only then reported via backupPath — when the
cleanup itself fails. This prevents the unbounded accumulation observed
in #80960, where a stuck operations-agent session with a persistently
malformed JSONL line caused 2,180 ~1.8 MB backup files to pile up over
~25 hours inside two gateway processes (PIDs 1220 and 2640).
Test changes:
- Replace requireBackupPath helper with expectNoRetainedBackup that
also asserts no .bak-* siblings remain on disk.
- Update the four call sites that used to read the retained backup.
- Add a regression test that drives repair five times against a file
with a recurring malformed tail and asserts zero retained backups.
Docs:
- docs/reference/transcript-hygiene.md: describe backup as transient,
retained only on cleanup failure.
Fixes#80960. Supersedes #77945. Co-authored by @tynamite — credit for
the original approach.
Co-authored-by: tynamite <35367599+tynamite@users.noreply.github.com>
Raise bounded gateway lifecycle hook wait budgets to 5 seconds for shutdown and 10 seconds for pre-restart, keeping the fix to defaults only instead of adding config surface.
Includes regression coverage, hook docs, changelog credit for @bryanbaer, and replaces #82186 with the narrower maintainer fix.
Slack link unfurls (inline message previews) are enabled by default
when unfurl_links is not explicitly set in chat.postMessage. This means
bot messages containing Slack message links or URLs automatically expand
into rich preview cards, which can be noisy in channels.
Default unfurl_links to false so outbound messages don't show inline
link previews unless the operator explicitly opts in via:
channels.slack.unfurlLinks: true
unfurlMedia remains opt-in (only sent when explicitly configured).
* fix(agents): scope provider SSRF trust by origin
* fix(provider): preserve explicit private-network deny
* docs(provider): document exact-origin SSRF trust
* test(provider): cover exact-origin SSRF edges
* docs(provider): align local model private-origin guidance
* refactor(ssrf): keep policy merging in infra
* test(ssrf): cover exact-origin trust through guard
* test(ssrf): block sibling private-origin redirects
* fix(provider): keep loopback trust origin-scoped
* fix(provider): block metadata origin trust
* fix(ssrf): keep metadata rebinding blocked
* fix(ssrf): block cloud metadata origins
* fix(ssrf): block ipv6 metadata origins
* fix(ssrf): block embedded metadata origins
* test(ssrf): cover embedded link-local metadata
* test(provider): cover custom anthropic proxy classification
* test(provider): widen transport policy mock
* test(plugin-sdk): assert metadata-IP allowedOrigins entries are rejected
Plugin authors can construct an SsrFPolicy that lists any well-formed
http(s) origin in allowedOrigins. The abuse-resistance lives one layer
deeper, in resolvePinnedHostnameWithPolicy's metadata/link-local block.
Add an SDK-level smoke test asserting that contract directly:
- AWS/Alibaba IMDS IPv4 literals, GCP metadata canonical hostname,
IPv6 ULA metadata literal, and non-metadata link-local IPv4 entries
build a policy via ssrfPolicyFromHttpBaseUrlAllowedOrigin and are
then rejected at resolvePinnedHostnameWithPolicy.
- DNS rebinding from a trusted private DNS origin to a metadata IP is
rejected even when the request hostname is origin-trusted.
This would fail if the SDK helper or resolveSsrFPolicyForUrl ever
short-circuited past the metadata block.
* chore(docs): regenerate baselines after upstream rebase
upstream/main moved between rebases; the merged source state for the
PR's `src/config/schema.help.ts` change and the upstream plugin-sdk
surface changes both produce different hashes than the committed
baselines, so `config:docs:check` and `plugin-sdk:api:check` would fail.
Regenerated via `pnpm config:docs:gen` + `pnpm plugin-sdk:api:gen` on
Crabbox; both baselines verified with their respective `--check`
generators.
* test(plugin-sdk): assert SSRF blocked error class
* fix(lint): satisfy exact-origin PR lint rules
* docs: clarify custom provider origin trust
* chore(docs): refresh plugin sdk api baseline
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>