* fix(anthropic): guard Cloudflare Anthropic fetches
Wire the Cloudflare AI Gateway Anthropic SDK client through buildGuardedModelFetch so this path gets the same SSRF and transport policy as sibling providers.
Keep the behavior proof focused on the regression: a Cloudflare Anthropic request pointed at a link-local address must fail before the SDK reaches global fetch.
* fix(anthropic): add loopback proof test to cloudflare fetch wiring
* test(anthropic-cloudflare): trim proof to SSRF-block only, remove synthetic loopback server
* fix(ios): prevent contacts.add crash from unfetched CNContactFormatter keys
ContactsService.payload(from:) formats contacts with CNContactFormatter, but
payloadKeys did not include the formatter's required-key descriptor. The
formatter then reads name components (e.g. middleName) that were not fetched,
so CNContact raises CNPropertyNotFetchedException — an Objective-C exception
that is not caught by Swift error handling and terminates the app.
Add CNContactFormatter.descriptorForRequiredKeys(for: .fullName) to payloadKeys
so every key the formatter touches is fetched.
* fix(ios): clarify contact formatter contract
---------
Co-authored-by: abdullahtas0 <“dev.abdullahtas@gmail.com”>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(infra): close fd and remove lock file on writeFile failure (#98958)
When fs.open(lockPath, "wx") succeeds but handle.writeFile() fails
(e.g. disk full / ENOSPC), close the file handle and remove the
partially-written lock file before re-throwing to avoid a file
descriptor leak and stale lock artifact.
Changes:
- src/infra/gateway-lock.ts: nested try-catch around writeFile
- src/infra/gateway-lock.test.ts: test for fd close + lock cleanup
- scripts/verify-gateway-lock-fd-leak*.mjs: fault-injection proof
Co-Authored-By: Claude <noreply@anthropic.com>
* test(infra): strengthen gateway lock cleanup proof
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(feishu): catch unhandled promise rejection in streaming card flush timer
## What Problem This Solves
The scheduled flush update in FeishuStreamingSession called
`this.update(pending)` without catching potential rejections, which
could cause unhandled promise rejections when card content updates
fail due to transient network errors.
## Why This Change Was Made
Add a `.catch()` with diagnostic logging to the scheduled flush
timer's `update()` call so transient card update failures are visible
without causing unhandled promise rejections.
## User Impact
Scheduled streaming-card updates that fail transiently will log a
diagnostic instead of producing an unhandled promise rejection.
## Evidence
node scripts/run-vitest.mjs extensions/feishu/src/media.test.ts
Test Files 1 passed (1)
Tests 44 passed (44)
pnpm exec oxfmt --check ...
All matched files use the correct format.
node scripts/run-oxlint.mjs ...
oxlint_exit=0
git diff --check
exit=0
Co-Authored-By: Claude <noreply@anthropic.com>
* test(feishu): cover rejected scheduled card flush
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(gateway): include session label in deriveSessionTitle fallback chain
When a session is renamed with /name, the user-provided label is persisted
in sessions.json but was ignored by deriveSessionTitle. This caused the
TUI session picker and other derived-title consumers to show auto-generated
or UUID-based names instead of the user's label.
Add entry.label to the deriveSessionTitle fallback chain after
displayName and subject, before the auto-derived firstUserMessage and
sessionId fallbacks. This aligns with the existing ACP translator
precedence (derivedTitle ?? displayName ?? label ?? key).
Related to #98742
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(gateway): tighten session label precedence coverage
* fix(gateway): prioritize explicit session labels
* fix(commands): preserve named-session suggestions
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>