49 Commits
Author SHA1 Message Date
2c05365fbd fix(plugins): decode self-hosted discovery JSON with fatal UTF-8 validation (#111113)
* fix(plugins): decode self-hosted discovery JSON with fatal UTF-8 validation

* ci: retrigger checks

* refactor(plugins): share self-hosted JSON decoding

Co-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 02:07:45 -07:00
zengLingbiaoandGitHub 563cc9d480 fix(docs): decode docs search responses with fatal UTF-8 validation (#111288) 2026-07-19 20:56:36 -07:00
zengLingbiaoandGitHub 19314aa31a fix(agents): reject non-UTF-8 MCP App sandbox CSP metadata (#111282) 2026-07-19 20:11:34 -07:00
a8dd80afc0 fix(plugins): cancel discarded TTS contract response bodies (#111340)
* fix(plugins): cancel discarded TTS contract response bodies

* test(plugins): mark TTS API key fixture synthetic

Co-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-19 20:02:44 -07:00
0e0432e76e fix(gateway): keep xdg-open stderr truncation surrogate-safe (#111323)
* fix(gateway): keep xdg-open stderr truncation surrogate-safe

* test(gateway): pin xdg-open diagnostic cap

Assert the exact diagnostic content and UTF-16 code-unit length at the surrogate-safe boundary.\n\nCo-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-19 09:58:44 -07:00
a70f7702a9 fix(agents): keep web search error serialization surrogate-safe (#111327)
* fix(agents): keep web search error serialization surrogate-safe

* test(agents): pin web search truncation prefix

Preserve the pre-existing provider_error fallback while proving the exact UTF-16-safe retained prefix.\n\nCo-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-19 09:39:04 -07:00
zengLingbiaoandGitHub a68fdad8f5 fix(usage-bar): clear dead watcher reference after transient error (#109462)
* fix(usage-bar): clear dead watcher reference after transient error

When a usage bar template file watcher hits a transient error, the error
handler closes the FSWatcher but leaves entry.watcher pointing to the
closed instance.  loadUsageBarTemplate treats a truthy cached.watcher
as "still watching" and never re-reads the file, so a template that
became invalid stays stuck at the default forever.

Clear entry.watcher after closing so the next access that needs a
re-read can create a fresh watcher.

This matches the pattern from #109682 (config hot-reload watcher
recovery).

* fix(usage-bar): invalidate cached template on watcher error

Clear both entry.watcher and entry.template when the FSWatcher errors
so the next loadUsageBarTemplate call re-reads from disk and creates a
fresh watcher.  Previously only the watcher reference was cleared,
leaving a valid cached template that never observed future file edits.

This addresses the remaining valid-cache recovery gap from #109682.

* fix(usage-bar): fix no-promise-executor-return lint in test
2026-07-18 17:02:20 -07:00
zengLingbiaoandGitHub 0c221d2b4f fix(config): bound state-directory .env file reads with size limit (#109487)
* fix(config): bound state-directory .env file reads with size limit

Replace unbounded fs.readFileSync with readRegularFileSync capped at
1 MiB (MAX_STATE_DIR_DOTENV_BYTES) so an oversized .env file is
rejected before loading the entire file into memory.

Resolve symlinks via fs.realpathSync before the bounded read so
symlinked .env files keep working — matching the marketplace.ts
pattern for bounded manifest reads.

* fix(config): add diagnostic when oversized state .env is skipped

Log a warning when the state-directory .env file exceeds the 1 MiB
limit so operators know a configured file was skipped — matching the
pattern from #108200 (plugin catalog bounded read diagnostic).

* fix(config): bound global runtime dotenv file reads with size limit

Replace unbounded fs.readFileSync in readDotEnvFile with readRegularFileSync
capped at 1 MiB so CLI/Gateway startup dotenv loading also gets the bounded
read protection — not just the state-dir service-env path.

This addresses the P1 review finding that the original fix only capped the
helper-level reader while the shared readDotEnvFile used by
loadGlobalRuntimeDotEnvFiles was still unbounded.
2026-07-18 17:02:11 -07:00
zengLingbiaoandGitHub c28284cb31 fix(config): bound external catalog file reads with size limit (#108200)
* fix(config): bound external catalog file reads with size limit

Replace unbounded fs.readFileSync with readRegularFileSync capped at
16 MiB (MAX_EXTERNAL_CATALOG_BYTES) to prevent memory exhaustion from
oversized or malicious plugin catalog files.

Resolve symlinks via fs.realpathSync before the bounded read so
symlinked catalog files keep working — matching the marketplace.ts
pattern for bounded manifest reads.

* fix(config): add diagnostic when oversized catalog is skipped

Log a warning when an external catalog file exceeds the 16 MiB limit
so operators know a configured file was skipped. Add regression test
verifying the oversized catalog is skipped and selection continues.

* fix(config): fix false-positive oversized catalog test

Replace the broken spy-on-object-literal mock with a real sparse file
that genuinely triggers readRegularFileSync rejection via stat.size.
The previous vi.spyOn({ readRegularFileSync }, ...) intercepted a
throwaway object, never the actual module import, so the test passed
regardless of whether the fix was applied or not.

Also fix the no-unused-expressions lint error on the env-primary
assertion that used a discarded ternary (? undefined : undefined)
instead of a proper expect assertion.
2026-07-18 17:01:54 -07:00
zengLingbiaoandGitHub b1c49677ec fix(agents): use fatal UTF-8 decoding for provider JSON responses (#108849)
* fix(agents): use fatal UTF-8 decoding in provider response readers

* fix(agents): use fatal UTF-8 decoding only for JSON responses, preserve compatibility for text
2026-07-18 16:08:30 -07:00
490ab265ba fix(agents): preserve sanitized stream cancellation (#110427)
* fix(agents): add .catch() to reader.cancel() to prevent unhandled rejection

* fix(agents): preserve sanitized stream cancellation

Co-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:55:06 +01:00
fcdf06ed29 fix(config): ignore inherited keys when restoring env refs (#109463)
* fix(config): use Object.hasOwn in restoreEnvVarRefs to avoid prototype pollution

When key comes from Object.entries(incoming) on user config data,
'key in parsed' traverses the prototype chain. If a config key
collides with an Object.prototype property (e.g. 'toString'),
the check incorrectly returns true and calls restoreEnvVarRefs
with a prototype function instead of a config value.

Replace with Object.hasOwn(parsed, key) to check only own properties.

* test(config): prove inherited env refs are ignored

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
2026-07-18 10:49:53 +01:00
zengLingbiaoandGitHub 88263e34b4 test(cron): add unit tests for assertCronDeliveryInputNonBlankFields (#98668) 2026-07-17 17:21:27 -07:00
zengLingbiaoandGitHub 2666cf7174 test(shared): add unit tests for createDeferredEventBuffer (#98670) 2026-07-17 17:21:18 -07:00
zengLingbiaoandGitHub 5982882055 test(tools): add unit tests for toToolProtocolDescriptor and toToolProtocolDescriptors (#98797) 2026-07-17 17:21:08 -07:00
zengLingbiaoandGitHub 256a2ece73 test(shared): add unit tests for modelKey (#98795) 2026-07-17 17:20:59 -07:00
zengLingbiaoandGitHub e517d187d2 test(utils): add unit tests for parseJsonWithJson5Fallback (#98801)
* test(utils): add unit tests for parseJsonWithJson5Fallback

* test(utils): verify JSON.parse fast path with spy, per ClawSweeper review

- Add vi.spyOn to prove JSON.parse is called (not JSON5) for strict JSON
- Add spy to prove JSON5.parse is called on fallback path
- Run full src/utils test suite (263 tests passed)
2026-07-17 17:20:16 -07:00
zengLingbiaoandGitHub f64c35be30 test(utils): add unit tests for safeJsonStringify (#98667) 2026-07-17 17:20:03 -07:00
zengLingbiaoandGitHub 1d64790bf2 test(shared): add unit tests for normalizeNodePresenceAliveReason (#98802)
* test(shared): add unit tests for normalizeNodePresenceAliveReason

* fix(test): add missing bg_app_refresh and significant_location assertions
2026-07-17 17:19:54 -07:00
zengLingbiaoandGitHub 73685b4e7c fix(mcp): guard malformed CSP JSON decode (#108883)
When decodeMcpAppSandboxCsp receives a base64-encoded value that
decodes to non-JSON text, JSON.parse throws an unhandled exception.
Add a try-catch so the function returns undefined instead of crashing.
2026-07-17 17:09:21 -07:00
zengLingbiaoandGitHub ec35d6b5ba test(shared): add unit tests for agent liveness state helpers (#98546) 2026-07-17 17:09:02 -07:00
zengLingbiaoandGitHub 58a86297f8 test(shared): add unit tests for parseConfigPathArrayIndex (#98543) 2026-07-17 17:08:50 -07:00
zengLingbiaoandGitHub bd966771a7 test(utils): add unit tests for formatTokenCount (#98541)
* test(utils): add unit tests for formatTokenCount

* fix(test): use Number.NaN/Number.POSITIVE_INFINITY per oxlint unicorn/prefer-number-properties

* test(utils): fold formatTokenCount boundary tests into existing usage-format test

Move edge case coverage (invalid inputs, zero/negative, exact boundaries,
thousands overflow) into src/utils/usage-format.test.ts instead of a
separate colocated test file per reviewer feedback.
2026-07-17 17:08:42 -07:00
5f4f249d88 fix(prompt-probe): bound Anthropic upstream proxy fetch requests (#108832)
* fix(prompt-probe): bound Anthropic upstream proxy fetch requests

* test(prompt-probe): exercise stalled upstream deadline

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 09:04:39 -07:00
b273fd41c4 fix(e2e): bound ClickClack fixture plugin fetch requests (#108826)
* fix(e2e): bound ClickClack fixture plugin fetch requests

* test(e2e): exercise ClickClack response deadline

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 08:35:51 -07:00
c2b73a0a01 fix(envelope): accept timestamp 0 in formatEnvelopeTimestamp (#106407)
* fix(envelope): accept timestamp 0 in formatEnvelopeTimestamp

formatEnvelopeTimestamp used if (!ts) to check for missing timestamps,
which incorrectly rejected the valid numeric timestamp 0 (Unix epoch).
Replace the falsy check with an explicit undefined/null check so 0 is
treated as a valid timestamp value.

* refactor(auto-reply): tighten epoch timestamp fix

Exercise the public envelope path with an exact epoch result and keep the missing-value guard aligned with its declared input type.

Co-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 01:50:08 -07:00
47ffb49f71 fix(restart-sentinel): report persistence cleanup failures (#106385)
* fix(restart-sentinel): log DB errors instead of silently swallowing them

Replace empty catch blocks in clearRestartSentinel, readRestartSentinel,
and hasRestartSentinel with sentinelLog.warn(...) calls so SQLite errors
are visible in diagnostics while preserving best-effort fallback semantics.

Tests use deterministic fault injection via vi.mock on openclaw-state-db.js
with hoisted throw flags, replacing the previous non-deterministic state-dir
removal approach. Each error path asserts both the warning message and the
correct fallback return value.

* fix(restart-sentinel): report persistence failures

Co-authored-by: wendy-chsy <wan.wenyan@xydigit.com>

* ci: retrigger pull request checks

* chore(ci): prune stale max-lines baseline

* chore(ci): retest against repaired protocol baseline

* chore: stabilize restart sentinel changelog entry

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: wendy-chsy <wan.wenyan@xydigit.com>
2026-07-16 00:53:39 -07:00
1c71f4104c fix(http-error-body): log response body snippet read errors instead of silently returning empty string (#106393)
* fix(http-error-body): log response body snippet read errors instead of silently returning empty string

readResponseBodySnippet used a bare catch {} that silently swallowed all
errors (network failure, body already consumed, decode errors, stream
aborts) and returned an empty string with no diagnostic. This made it
impossible to distinguish between a genuinely empty response body and
a read failure.

Replace catch {} with catch (err) { errorBodyLog.warn(...); return ""; }
so failures are visible in diagnostics while preserving the best-effort
snippet contract.

* fix(http-error-body): assert subsystem warning in error visibility tests

ClawSweeper review identified that the error-path tests only checked
the empty-string fallback, which the old silent catch would also pass.
Add mockWarn assertions to verify the warning is emitted with the
formatted error text, and add beforeEach to clear mocks between tests.

* test(http-error-body): assert logged read errors

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 00:42:25 -07:00
774cd25aa6 fix(transcript-stream): propagate non-missing filesystem errors (#106412)
* fix(transcript-stream): log stat/open errors instead of silently returning empty iterator

streamSessionTranscriptLines and streamSessionTranscriptLinesReverse
used bare catch { return; } blocks that silently swallowed all fs errors
(permission denied, I/O error, etc.) and returned empty iterators. This
made it impossible to diagnose why transcript streaming failed.

Replace catch { return; } with catch (err) { transcriptLog.warn(...); return; }
so failures are visible in diagnostics.

* fix(transcript-stream): exclude ENOENT from warning logs in stat/open catch blocks

Both streamSessionTranscriptLines and streamSessionTranscriptLinesReverse
caught all fs errors and logged warnings, but ENOENT is an expected empty-
iterator case for these best-effort readers. Filter ENOENT before logging
so missing transcripts stay silent while permission/I/O errors surface.

Added deterministic regression tests with mocked fs.promises.stat/open:
- EACCES triggers a warning in both forward and reverse directions
- ENOENT stays silent in both directions

* fix(transcript): propagate session read failures

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-15 22:29:08 -07:00
692cb8dd9b fix(cron): reject non-string values in delivery target validation (#106952)
* fix(cron): reject non-string values in delivery target validation

assertNonBlankStringField had inverted logic — non-string values
(number, boolean, object) were silently accepted because typeof !== 'string'
was grouped with the undefined/null early return. The function name says
'assert non-blank string field' but it was bypassing validation for any
non-string type, defeating the purpose of input validation.

* test(cron): cover invalid delivery fields at entrypoints

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-13 21:48:15 -07:00
db25e9ec52 fix(web-shared): bound response.text() fallback to honor maxBytes (#99884)
* fix(web-shared): bound response.text() fallback to honor maxBytes

readResponseText's .text() fallback path ignored the maxBytes option,
reading the full body unbounded. When a foreign Response lacks a body
stream (no getReader) and no arrayBuffer(), the .text() call was the
last resort — but maxBytes was never applied.

- Honor maxBytes in the .text() fallback: truncate + set truncated=true
- Under-cap responses pass through unchanged
- No maxBytes set → original behavior preserved (regression safe)

* fix(web-shared): use byte-level truncation in .text() fallback

Replace text.length/text.slice (character-level) with TextEncoder/
TextDecoder (byte-level) so the maxBytes cap is respected at byte
granularity, consistent with the bounded-stream path at lines 240-298.

- bytes.byteLength > maxBytes instead of text.length > maxBytes
- TextDecoder().decode(bytes.slice(0, maxBytes)) for byte-safe truncation
- bytesRead reports actual bytes, not character count

* fix(web-shared): refuse unbounded .text() when maxBytes is set

When maxBytes is set the caller expects bounded memory. The .text()
fallback path buffers the full body before any post-read check,
defeating the cap. Fail-closed by returning empty with truncated=true
instead of calling .text() unbounded.

Without maxBytes .text() is safe — the caller accepts full allocation.

* fix(web-shared): byte-level truncation in .text() fallback with maxBytes

The .text() fallback used text.length (characters) and text.slice
(char-level) against a byte-level maxBytes cap. Use TextEncoder/
TextDecoder for byte-accurate comparison and truncation.

- bytes.byteLength > maxBytes instead of text.length > maxBytes
- TextDecoder().decode(bytes.slice(0, maxBytes)) for byte-safe truncation
- bytesRead reports actual byte count, not character count

* fix(web): fail closed on unbounded response fallbacks

* test(web): use streaming response fixtures

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 00:24:36 +01:00
acdaf8ae31 fix(markdown-core): use Object.hasOwn instead of in operator in parseFrontmatterBlock (#99129)
* fix(markdown-core): use Object.hasOwn instead of in operator in parseFrontmatterBlock

* test(markdown-core): add regression test for prototype-named null frontmatter keys

* style(markdown-core): fix unbound-method lint in regression test

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-03 22:39:25 -07:00
459b93e3dd fix(config): use Object.hasOwn instead of in operator in restoreOriginalValueOrThrow (#99152)
* fix(config): use Object.hasOwn instead of in operator in restoreOriginalValueOrThrow

* test(config): add regression test for prototype-named keys in restoreRedactedValues

* test(config): focus prototype restore regression

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-03 04:32:25 -07:00
zengLingbiaoandGitHub 09ebc1d566 test(utils): add unit tests for chunkItems (#98219)
Add 8 test cases covering fixed-size array splitting, empty input,
size <= 0, size=1, size > length, exact division, readonly input
preservation, and fractional size behavior.
2026-06-30 18:15:24 -07:00
490a4c5d9b test(gateway): add unit tests for node wake state tracking and testing seam (#98205)
Add 17 unit tests for nodes-wake-state.ts covering:
- Exported wait/poll constants
- nodeWakeById Map operations (insert, overwrite, multi-entry, inFlight)
- nodeWakeNudgeById Map operations (independent tracking)
- clearNodeWakeState function (removal, no-op, scoped deletion)
- testing seam (getNodeWakeByIdSize, hasNodeWakeEntry, resetWakeState)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 17:53:56 -07:00
zengLingbiaoandGitHub 686a2876c7 fix(cron): truncate failure alert error text on UTF-16 boundary (#97298)
* fix(cron): truncate failure alert error text on UTF-16 boundary

* test(cron): add regression test for UTF-16 safe failure alert truncation

Verify that emitFailureAlert does not produce dangling surrogates when
truncating a cron failure error message with a non-BMP character (emoji)
straddling the 200-code-unit truncation boundary.

* fix(test): cover last code unit in surrogate scan

The dangling surrogate check loop stopped at length-1, missing a
high surrogate at the final position. Extend to i < alertText.length
so charCodeAt(i+1) returns NaN for the last char, correctly failing
the high-surrogate pair assertion.

* fix(test): address lint issues in regression test

- Use template literal instead of string concatenation
- Add braces to type guard if-statement
2026-06-28 13:59:06 -07:00
zengLingbiaoandGitHub c9d2edfb7e fix(shared): use UTF-16 safe truncation in subagent line display (#97557)
* fix(shared): use UTF-16 safe truncation in subagent line display

truncateLine sliced by code units instead of preserving surrogate pairs,
causing emoji / CJK Extension B characters at the truncation boundary
to display as broken replacement characters.

* fix(shared): move import above export to satisfy import/first lint rule
2026-06-28 13:58:58 -07:00
zengLingbiaoandGitHub 19617b0b45 fix(agent-core): avoid splitting surrogate pairs in grep line truncation (#97559)
truncateLine could cut a surrogate pair when the maxChars boundary
fell between a high surrogate and its paired low surrogate, producing
a broken unpaired surrogate in grep tool output.
2026-06-28 13:26:15 -07:00
zengLingbiaoandGitHub e445d614c0 fix(shared): use UTF-16 safe truncation in assistant error formatting (#97289)
* fix(shared): truncate assistant error text on UTF-16 boundary

Use truncateUtf16Safe from the existing utf16-slice utility instead of
String.prototype.slice(0, 600), which can split a surrogate pair and
produce malformed Unicode when the error message contains characters
outside the Basic Multilingual Plane (emoji, rare CJK, etc.).

* test(shared): add regression test for UTF-16 safe error truncation

Verify formatRawAssistantErrorForUi does not produce dangling surrogates
when truncating a fallback raw error message with a non-BMP character
(emoji) straddling the 600-code-unit boundary.

* fix(agents): truncate assistant error text fallback on UTF-16 boundary

Use truncateUtf16Safe instead of String.prototype.slice(0,600) in
formatAssistantErrorText and its companion comparison in
isRawAssistantErrorPassthrough, preventing dangling surrogates when
non-BMP characters straddle the 600-code-unit truncation boundary.
2026-06-28 11:06:20 -07:00
zengLingbiaoandGitHub 1b8b8500ce fix(auto-reply): truncate user-facing text on UTF-16 boundary (#97299)
Summary:
- The PR imports `truncateUtf16Safe` and uses it for the Codex usage-limit preview and verbose working-label truncation paths in auto-reply.
- PR surface: Source +2. Total +2 across 2 files.
- Reproducibility: yes. Current main uses raw `slice(0, N)` at both reported user-facing truncation sites, and ... ludes terminal before/after output showing dangling surrogates before the fix and safe truncation after it.

Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.

Validation:
- ClawSweeper review passed for head 74a0a32ed9.
- Required merge gates passed before the squash merge.

Prepared head SHA: 74a0a32ed9
Review: https://github.com/openclaw/openclaw/pull/97299#issuecomment-4820038635

Co-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>
Approved-by: takhoffman
2026-06-27 20:39:32 +00:00
zengLingbiaoandGitHub 4c9e7f6c61 fix(nodes): return screen snapshots as media (#93499)
Merged via squash.

Prepared head SHA: 6a69c5cdcc
Co-authored-by: zenglingbiao <290951975+zenglingbiao@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-16 14:35:58 +08:00
03e3ef86af fix(agents): resolve configured default model in runEmbeddedAgent (fixes #93419) (#93428)
* fix(agents): honor configured default model in embedded runs

* fix(agents): resolve embedded defaults from runtime config

* fix(agents): preserve embedded model routing semantics

* test(agents): model current embedded attempts explicitly

---------

Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
2026-06-16 08:27:19 +08:00
zengLingbiaoandGitHub 07dfdd4bd0 fix(agents): prevent duplicate before-tool-call hooks (#93009)
Prevent duplicate `before_tool_call` execution when an already wrapped tool passes through schema normalization and coding-tool assembly. Preserve the normalized schema while replacing stale wrapper context with the current agent/session/run context.

Fixes #92973.

Co-authored-by: zengLingbiao <zeng.lingbiao@xydigit.com>
2026-06-14 11:25:43 -07:00
7fb0d45b48 fix(agents): add usage guidance to sessions_spawn tool description (fixes #91814) (#91824)
* fix(agents): add usage guidance to sessions_spawn tool description (fixes #91814)

* fix(agents): tighten sessions spawn guidance

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-14 10:49:26 +08:00
46a3442251 fix(web_fetch): sanitize URL whitespace from LLM tool call arguments (fixes #91651) (#91950)
* fix(web_fetch): sanitize URL whitespace from LLM tool call arguments (fixes #91651)

* fix(web_fetch): narrow URL repair to only strip scheme-authority whitespace

Constrain normalization to the reported malformed-scheme pattern
(whitespace between :// and authority) instead of deleting all
whitespace globally. Path and query spaces are intentionally
preserved — the WHATWG URL parser percent-encodes those correctly.

Add focused regression tests proving path/query/pct-encoded spaces
survive sanitization. (fixes #91651)

* fix(web-fetch): preserve scheme-like path text

* fix(web-fetch): preserve unicode URL text

* fix(web-fetch): retain raw URL argument text

* fix(web-fetch): trim leading unicode URL whitespace

* fix(web-fetch): normalize bare authority whitespace

* fix(web-fetch): satisfy URL sanitizer checks

* fix(web-fetch): pass URL sanitizer lint

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-11 10:21:07 +09:00
zengLingbiaoandGitHub 15498f88fb fix(memory-core): check SQLite plugin state for dreaming ingestion audit after JSON migration (fixes #92017) (#92020)
* fix(memory-core): check SQLite plugin state for dreaming ingestion audit after JSON migration (fixes #92017)

* fix: add SQLite-only regression tests for dreaming ingestion audit (fixes #92017)
2026-06-11 08:26:50 +09:00
zenglingbiaoandGitHub 3753c5e2c8 fix(inbound-meta): preserve reply-context body tails
Preserve actionable tail content in long reply-context bodies before they enter prompt JSON or inline reply context formatting.

- Apply UTF-16-safe head+tail truncation to ReplyChain JSON bodies and fallback ReplyToBody JSON blocks.
- Use the same body-aware truncation for Telegram inline ReplyToBody fallback and chat_window message bodies, so those paths cannot suppress the JSON fallback and still lose the tail.
- Adds regression coverage for ReplyChain, fallback ReplyToBody, Telegram inline ReplyToBody, chat_window reply targets, and emoji-heavy heads.

Verification:
- node scripts/run-vitest.mjs src/auto-reply/reply/inbound-meta.test.ts
- node_modules/.bin/oxfmt --check --threads=1 src/auto-reply/reply/inbound-meta.ts src/auto-reply/reply/inbound-meta.test.ts
- node scripts/run-oxlint.mjs src/auto-reply/reply/inbound-meta.ts src/auto-reply/reply/inbound-meta.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- Testbox-through-Crabbox check:changed: provider=blacksmith-testbox id=tbx_01ktgthbb5xa9d5ap58h4134s0 exit=0
- PR CI: 158/158 completed, no failures, MERGEABLE/CLEAN

Fixes #91042
2026-06-07 19:45:00 +09:00
zenglingbiaoandGitHub 09d6479681 fix(build): ship export session html assets
Align the export-session template asset copy step and build-all cache output with the runtime lookup path so published packages include the HTML export assets at `dist/export-html`.

Adds a focused build-all regression assertion for the output path contract.

Fixes #90843
2026-06-06 09:14:12 -07:00
zenglingbiaoandGitHub d896a4c7a3 fix(context-engine): forward isHeartbeat to afterTurn (fixes #89302) (#90632)
Merged via squash.

Prepared head SHA: 2f6da84c4b
Co-authored-by: zenglingbiao <290951975+zenglingbiao@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-06-05 11:27:37 -07:00