* fix(tlon): cap SSE payload JSON.parse at 16 MiB to prevent OOM
* fix(tlon): cap SSE stream buffer and JSON.parse at 16 MiB to prevent OOM
* chore(tlon): add production-style SSE bounded proof script (#101274)
Adds extensions/tlon/proof-sse-bounded.mts which drives the real
UrbitSSEClient.processStream and processEvent against Node Readable streams
(not unit-test mocks) to demonstrate:
- normal SSE events are still delivered through the stream path;
- an unterminated stream that would grow beyond 16 MiB is rejected before
unbounded accumulation;
- a single SSE payload above 16 MiB is rejected before JSON.parse.
The script passes on this branch and fails 2/3 assertions when run against
origin/main's sse-client.ts, providing the before/after proof ClawSweeper
requested.
* chore(tlon): fix oxlint catch type in proof script (#101274)
* fix(tlon): reject oversized SSE chunk before buffer concatenation
Move the stream byte-limit check before buffer += chunkStr so a
single oversized chunk never lands in the pending buffer. The old
guard ran after concatenation, which still allowed the memory spike
this hardening is meant to prevent.
Add a single-oversized-chunk test to prove the guard fires before
the chunk is concatenated into the pending buffer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(tlon): remove PR-specific SSE bounded proof script
The proof script was review-only validation for #101274. Durable behavior
coverage lives in sse-client.test.ts (stream buffer bounding, oversized
chunk rejection before concatenation, 16 MiB boundary, normal delivery,
1000 small events). Per ClawSweeper P3 finding, drop the one-off script
from the plugin tree.
* fix(tlon): bound SSE event buffering safely
* test(tlon): avoid unsafe optional chaining
* fix(tlon): count split Unicode at SSE limit
* fix(tlon): parse split SSE delimiters at limit
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(extensions): make indexed access explicit across channel plugins
Transport-payload-safe burn-down: malformed Telegram/Discord/QQ/LINE
and sibling channel input keeps existing skip paths; no synthesized
fields, no new throws in delivery loops. Zalo escape sentinels preserve
literal matches instead of undefined replacements.
* fix(extensions): make indexed access explicit across provider and memory plugins
Stream and model iteration, tool-block guards, capture guards, and
sparse accumulators; singleton model reads carry named invariants.
* fix(extensions): make indexed access explicit across tooling plugins, flip the extensions lane
Remaining plugins (oc-path, qa-lab, browser, logbook, and siblings) plus
the tsconfig.extensions.json flag flip. Cleanup: logbook sampleFrames
NaN index at max=1, QA retry clamp at non-positive attempts, dead Canvas
probe and OpenShell no-op slice removed, twitch test setup leak excluded
from the prod lane.
* refactor(plugin-sdk): expose expectDefined via a focused SDK subpath
Extensions imported @openclaw/normalization-core directly, crossing the
external-plugin packaging boundary (it only worked because the runtime
builder bundles undeclared workspace helpers). expect-runtime joins the
canonical entrypoints JSON, generated exports, API baseline, docs, and
subpath contract test; all 78 extension imports now use the SDK seam.
Two scanner-shaped locals renamed for review-bundle hygiene.
* chore(plugin-sdk): raise surface budgets for the expect-runtime subpath
One new entrypoint with one callable export, added intentionally as the
packaging-honest seam for extension invariant helpers.
Route assistant-visible Tlon output through the shared sanitizer and prove the rendered Urbit poke payload for DMs, groups, and media captions.
Co-authored-by: masatohoshino <g515hoshino@gmail.com>
* fix(tlon): bound error response body reads to prevent OOM
Replace bare response.text() on non-ok paths with readResponseTextLimited
capped at 16 KiB so a hostile or misconfigured Urbit ship cannot force the
gateway to buffer an arbitrary-size error body into process memory.
Affected paths:
- pokeUrbitChannel (channel-ops.ts)
- channel.runtime.ts poke path
- sendSubscription (sse-client.ts)
* fix(tlon): fix lint issues in error-body-boundary test
- Remove unused beforeEach import
- Wrap if/else bodies in braces (curly)
- Use block body for Promise executors (no-promise-executor-return)
* fix(types): resolve pre-existing TS test type errors
- Fix TS2493 tuple type errors in server-cron-notifications and
server-cron tests by adding explicit type annotations on mock.calls
- Fix TS2322 in anthropic.test.ts by adding as const to resource
content block type
* chore: trigger CI
* fix(tlon): truncate approval message preview on UTF-16 boundary
formatApprovalRequest called truncate() with raw .slice() before the
trailing ellipsis. A Tlon ship display name or message preview whose
emoji landed on the 100-character preview cap was split mid-surrogate
pair, emitting a lone surrogate in the owner notification. Use
sliceUtf16Safe so the truncation always falls on a code-point boundary.
* fix(tlon): guard all three messagePreview UTF-16 truncation sites
The prior commit only guarded the formatter (truncate in approval.ts).
The root-cause sites are in monitor/index.ts where PendingApproval is
constructed:
messagePreview: rawText.slice(0, 100), // channel mention path
messagePreview: messageText.slice(0, 100), // DM path
An emoji whose high surrogate falls at position 99 was sliced mid-pair,
storing a lone \uD83D in the persistent PendingApproval before the
formatter ever ran.
Fix: replace both production sites with sliceUtf16Safe(), and also
guard the createPendingApproval() constructor itself so any future
caller that passes an untruncated preview is automatically safe.
Add regression tests that drive the full
createPendingApproval -> messagePreview -> formatApprovalRequest chain
for both the DM and channel paths with an emoji placed exactly at the
100-unit boundary.
* fix(tlon): truncate approval message preview on UTF-16 boundary
* fix(clownfish): repair validation for live-pr-inventory-20260629T032026-001 (2)
---------
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
* chore(release): close out 2026.6.10 on main
* chore(release): align native app metadata for 2026.6.10
* chore(release): sync Android 2026.6.10 notes
* docs(changelog): preserve 2026.6.9 history
* docs(changelog): preserve 2026.6.9 history
Bumps OpenClaw release metadata to 2026.5.31 across package manifests, app version files, plugin metadata, changelog headings, and generated shrinkwraps.
Verification:
- pnpm plugins:sync:check
- pnpm ios:version:check
- pnpm deps:shrinkwrap:check
- git diff --check
- stale 2026.5.30/build-code scan across changed files
- autoreview clean: no accepted/actionable findings
- PR CI green for real gates: Checks, security scans, dependency guard, app lanes, real behavior proof
Known non-code workflow issue:
- label workflow failed because this PR hits GitHub's 100-label issue cap before the size-label step.