Commit Graph
70479 Commits
Author SHA1 Message Date
Peter SteinbergerandGitHub 556a2ee276 feat(cron): add per-job dynamic cadence (#110978)
Add optional per-job pacing bounds across the cron API, CLI, tool schema, public output, and SQLite job envelope, requiring at least one bound. Allow only the currently running paced job to record a one-shot next_check proposal and carry it through isolated-run completion.

After successful runs, clamp the proposal to the job bounds and persist an exact one-shot slot marker so maintenance preserves only that timestamp. Clear the marker on runs, edits, and schedule normalization; preserve existing no-proposal, skip, timeout, and error scheduling behavior.
release-publish/556a2ee276f0-20260718
2026-07-18 23:43:39 +01:00
Peter SteinbergerandGitHub 5114b45927 chore(canvas): compact show_widget tool description with pattern anchor (-40% tokens) (#110999) 2026-07-18 23:41:29 +01:00
Peter SteinbergerandGitHub d343216510 fix(ios): keep unified voice controls recoverable (#110906)
* docs(ios): align unified chat voice copy

* fix(ios): keep active dictation control available

* fix(ios): keep active talk control available

* fix(ios): restore unified voice recovery

* fix(ios): keep pending dictation cancel visible

* chore(ios): refresh native i18n inventory

* chore(ios): sync unified voice locale memory

* fix(ios): return unified mic label view

* test(ios): lock dictation cancellation ordering
2026-07-18 23:41:00 +01:00
Peter SteinbergerandGitHub 333a3297af refactor(plugin-sdk): consolidate persistent dedupe claim loop (#110984) 2026-07-18 23:36:59 +01:00
Peter Steinberger 26e7575780 fix(release): pin trusted candidate tooling across main advances 2026-07-18 23:33:42 +01:00
Dallin RomneyandGitHub 56915896bc fix(qa): bound Matrix E2EE sends (#110968) 2026-07-18 15:32:44 -07:00
9918dd77c4 fix(scripts): bound release CI summary GitHub lookups (#110747)
* fix(scripts): bound release CI summary GitHub lookups

The release CI summary helper performs multiple sequential gh API reads
during full release validation. Each read used execFileSync without a
timeout, so one stalled GitHub request could block the surrounding
workflow job until it expires.

Route the default gh runner through a caller-owned execFileSync with a
60-second timeout and SIGKILL, and apply the same bound to artifact zip
downloads. The runner accepts an execFileSyncImpl injection so the
timeout contract can be tested without a slow timer-based test.

* test(scripts): add real behavior proof for release CI summary timeout

Add two live subprocess tests alongside the existing mocked tests:

- A 1 ms timeout against a real gh subprocess proves the SIGKILL +
  timeout surface works on actual child processes (not only mocks).
- A fast node -e 0 subprocess with the production 60-second bound
  proves the timeout does not false-positive on healthy completions.

The runReleaseCiGh runner now accepts an optional timeoutMs parameter
so tests can exercise a shorter timeout without changing the production
default.

* refactor(scripts): share bounded release GitHub runner

* test(scripts): keep release runner assertions type-safe

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:29:19 +01:00
Peter SteinbergerandGitHub 7fbfc2e8ad fix(plugins): allow intentional uninstall size drops (#110991) 2026-07-18 23:28:18 +01:00
2295fae731 fix: bound miscellaneous unbounded file reads across 5 modules (#110516)
* fix: bound misc unbounded fs.readFile calls; remove unused fs import

* fix: decode buffer to string before passing to string consumers

readRegularFile and readRegularFileSync return { buffer, stat },
not a string. All 4 new call sites passed the raw object to functions
expecting a string (JSON.parse, RegExp.test, template literals, etc.),
causing TS2345 type errors and runtime failures.

Fix each call by extracting .buffer and calling .toString('utf8')
before passing the result to string consumers.

* style: fix oxfmt formatting in config-set-input.ts

* fix: bound config and trajectory metadata reads

Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>

* refactor: isolate bounded read ownership

Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:27:26 +01:00
Peter SteinbergerandGitHub a5ec26fa3c fix: prevent LINE channel reloads from hanging on stalled deliveries (#110971)
* fix(channels): bound ingress shutdown and document retention

* docs(channels): note ingress shutdown behavior

* chore: keep release notes in pull request

* docs: refresh documentation map

* fix(line): preserve deferred claims during shutdown

* fix(line): handle late abandonment failures
2026-07-18 23:25:33 +01:00
Peter SteinbergerandGitHub 7a7d6bb51f fix: cloud-worker results are lost when the box dies before reconciliation (#110952)
* fix(cloud-workers): stage worker results durably before reconciliation

* fix(cloud-workers): model result staging test seam

* fix(cloud-workers): disable staging git hooks safely

* chore: defer cloud worker release note

* fix(cloud-workers): harden staged result recovery
2026-07-18 23:24:50 +01:00
Peter SteinbergerandGitHub a21385a372 fix(channels): scope wizard credentials to the selected account (#110969)
* fix(channels): scope wizard credentials to selected account

* fix(channels): migrate empty accounts map in wizard scope guard

* test(channels): guard resolved account id in empty-map regression
2026-07-18 23:21:45 +01:00
4e6bb619f7 fix(onboarding): preserve emoji in recommendation reasons (#110401)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:19:33 +01:00
Peter SteinbergerandGitHub 264a7b0d12 refactor(channels): consolidate manifest descriptors (#110985) 2026-07-18 23:18:28 +01:00
ee37c840f3 fix(reef): contain startup friend reconcile failures (#110918)
* fix(reef): contain startup friend reconcile failures

The periodic reconcile treats a transient relay failure as non-fatal, but
the startup reconcile was a bare await outside the lifecycle. A relay 429
there rejects startAccount, and the supervisor restart that follows is
itself what escalates relay rate limiting -- so Reef gives up permanently
after MAX_RESTARTS while the relay is still throttling.

Move the startup reconcile into runReefChannelLifecycle so both paths
share one failure policy, and activate through an onReady hook to preserve
the existing ordering: refresh peer keys, activate, then start the inbox.

* fix(reef): harden reconcile recovery

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

* docs(changelog): credit Reef reconcile fix

* test(reef): satisfy exact-head validation

* test(reef): satisfy lint contract

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:18:20 +01:00
69ad5fc7fb fix(wizard): treat not-directory paths as missing in migration snapshots (#109161)
* fix(wizard): treat not-directory paths as missing in migration snapshots

The setup migration snapshot and recovery paths checked for ENOENT only
when catching fs errors. ENOTDIR (returned when a path component that
should be a directory is a file) was not recognized, so a not-directory
migration root made fs.readdir throw instead of resolving to no recovery
record, and interrupted the onboarding migration snapshot hash.

Migrate both local isMissingPathError helpers to the shared
isNotFoundPathError guard (matches ENOENT and ENOTDIR), following the
same migration applied to plugins in #107691.

* test(wizard): cover ENOTDIR migration snapshots

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:18:16 +01:00
Peter SteinbergerandGitHub c9aa849936 fix(android): stream replies into view and shrink "Jump to latest" to a compact icon button (#110983)
* fix(android): stream replies into view and shrink jump-to-latest to a compact icon button

* fix(android): refresh native i18n inventory line offsets
2026-07-18 23:16:57 +01:00
Peter SteinbergerandGitHub bb7b2bca68 feat(ui): finish the chat session → thread rename across the Control UI (#110973)
* feat(ui): finish the chat session → thread rename across the Control UI

Completes the #110933 creation-surface rename mechanically: Threads page
(title, filters, archive/delete/rename/pin menus, transcript search),
sidebar labels and menus, welcome suggestion, palette category, tools
availability copy, workboard lifecycle/card strings, dreaming history,
board/diff/workspace-rail chrome, and the untitled main/channel fallback
names. Technical identifiers stay: Session ID, session keys, terminal
sessions, protocol wording. Living docs updated (control-ui.md);
historical release notes left as shipped.

* style(ui): format session-management e2e

* fix(ui): finish thread rename in navigation labels and stale test literals

Codex review caught the palette Threads entry and the Worktrees owner
column still saying Session; CI caught test literals asserting the old
copy (Threads page overview tile, filter tooltips, group counts, catalog
terminal notice, create-rejection error, workboard lifecycle hint). Also
completes the usage-stat cluster (Threads + hints) and the tool-catalog
group label. Native app strings (iOS/Android/macOS) keep session wording
and reconcile through their own i18n pipelines as a follow-up.
2026-07-18 23:15:13 +01:00
19d887169f fix(ai): ChatGPT Responses retries errors it classified as non-retryable (#110655)
* fix(ai): ChatGPT Responses retries errors it classified as non-retryable

The retry loop builds its friendly error and throws it from inside the same
try block whose catch treats unknown failures as retryable network errors.
Authentication and bad-request failures were therefore resent up to
maxRetries times, adding 7s of backoff before surfacing the same message.

Tag the already-classified failure with a private error type and rethrow it
unchanged from the catch, leaving the retryable paths untouched.

* test(ai): move retry classification cases to a dedicated file

The provider test file was already near the 1000-line oxlint max-lines
budget, so the new cases pushed it over. Split them out following the
existing <module>.<topic>.test.ts convention.

* refactor(ai): separate HTTP and transport retries

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

* test(ai): clarify synthetic JWT fixture

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:09:42 +01:00
01c6479311 fix(discord): long code-fence replies can exceed the message length limit (#110148)
* fix(discord): reopening a long code fence overflows maxChars

When a chunk is flushed mid code-fence, the next chunk reopens the fence
with the full original opening line (info string included), but the
reserve accounting only budgets the closing marker. A long fence opening
line therefore pushed reopened continuation chunks past maxChars, which
Discord rejects with HTTP 400.

Reopen continuation chunks with a bare marker when the full opening line
would not leave room for the close and body, and size split segments
against the reopen prefix so no chunk exceeds maxChars. This mirrors the
existing closing-fence-overflow guard on the opening/reopen side.

* fix(discord): preserve code after fence reopen

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

* fix(discord): preserve limits when fences cannot fit

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:06:46 +01:00
9f7c7521f1 fix(agents): Responses turns that end incomplete report zero tokens and zero cost (#109904)
* fix(agents): record token usage when a Responses turn ends incomplete

The agent-side Responses processor only had a terminal branch for
response.completed. A stream that ends with response.incomplete — the
max_output_tokens and content_filter cases, and what Azure emits on early
truncation — matched no branch at all, so the event was dropped: usage was
never recorded and stopReason was never set. The turn reports zero tokens and
zero cost, which is the drift in #100954.

#109615 fixed the same split on the package-side processor by finalizing
completed and incomplete through one finalizeResponse. This does the same for
the agent path, which #109615 did not touch: both terminal events now record
usage, cost and service-tier pricing through one helper.

The helper moves to its own module rather than growing openai-responses-transport.ts,
which is a legacy file the max-lines ratchet will not let grow; extracting the
block drops it from 2502 to 2444 lines.

Content-filtered turns are mapped to a provider error instead of a plain length
stop, matching what the package side already does, so the two terminal surfaces
do not disagree.

* fix(agents): keep Responses output backfill on completed turns

Terminal handling now covers response.incomplete, which also routed those
events into backfillCompletedResponseOutput. That reconstruction exists to
recover a final answer when item events never arrived; an incomplete turn has
no final answer, so replaying its partial output persisted truncated text the
streaming path never emitted. Usage and stop-reason recording still apply to
both terminal events.

* refactor(ai): share one Responses terminal usage mapper

The agent transport mapped terminal usage buckets, cost, and stop reasons in
parallel with the package-side processor, so the two could drift on token
buckets, service-tier pricing, or future terminal-event semantics.

Both now call one canonical mapper. openai-responses-shared.ts cannot be
imported across the package boundary, so the mapper lives in its own module
re-exported through the existing internal/openai entry point; no new subpath
is introduced. The agent module keeps only the reasoning-token accounting the
package path does not track.

Merging the two revealed a real disagreement on totalTokens: the package took
the reported total, the agent summed the split buckets. The canonical rule is
now max(bucket sum, reported total), which keeps the reported value while
covering both payloads that omit total_tokens and payloads whose cached_tokens
exceed input_tokens, where clamping leaves the reported total short.

* fix(agents): preserve partial incomplete Responses output

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

* test(agents): cover incomplete terminal output backfill boundaries

The head's partial-output preservation is only safe while it stays a recovery
path. Pin both sides of that guard: text that already streamed must not be
replayed from the terminal payload, and a non-length incomplete stop must not
surface partial text as an answer.

* fix(agents): avoid shadowing Responses options

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

* style(agents): apply oxfmt to the incomplete backfill test

* test(agents): register incomplete Responses suite

Co-authored-by: Yiğit ERDOĞAN <yigiterdogan023@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:03:53 +01:00
7bf263b8b6 Bound plugin bundle command file reads with size cap (#110594)
* Bound bundle command file reads with size cap

* fix: use buffer.toString for readRegularFileSync result

* fix: log oversized bundle command file diagnostic instead of silent skip

The catch block now captures the error and emits a console.warn with the file path and error detail, so upgrades do not silently remove oversized installed commands.

* test: verify oversized bundle command file is skipped and siblings continue

PR #110594: Add focused regression coverage:
- Test: an oversized bundle command markdown file (>1 MB) is skipped via catch + continue
- Test: normal sibling command files still load correctly
- Verifies console.warn diagnostic is emitted for the oversized file

* refactor(plugins): log rejected bundle commands

Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>

* style(plugins): format bundle warning

Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:03:00 +01:00
Peter SteinbergerandGitHub f3f6eb2321 fix(codex): stop fallback for superseded sessions (#110980) 2026-07-18 23:02:03 +01:00
Peter SteinbergerandGitHub cd78fb3843 fix(channels): harden prepared turn lifecycle (#110981) 2026-07-18 23:01:38 +01:00
Peter SteinbergerandGitHub 460e3e669c fix(canvas): restore shipped policy/host-disable contracts; harden workspaces doctor cleanup (#110927)
* fix(dashboard): harden canvas and workspace compatibility

* docs(web): add dashboard architecture

* chore: remove release-owned changelog entry

* docs: update dashboard architecture map
2026-07-18 23:00:00 +01:00
2f045a73f4 fix(agents): agent-created files get lowercased names on Windows (#109823)
* fix(agents): preserve filename case for agent file writes on Windows

toRelativePathUnderRoot passed root and candidate through
normalizeWindowsPathForComparison, which lowercases, and then returned the
resulting relative path. Callers build files out of that path, so an agent
asking for src/Components/MyComponent.tsx got src\components\mycomponent.tsx
on disk. NTFS is case-preserving, so nothing fails locally, but git records
the lowercased name and the imports the agent wrote break on Linux and in CI.

Lowercasing is not even case-safe for every name: "İstanbul.md" lowercases to
"i̇stanbul.md" (U+0130 becomes U+0069 U+0307), one code point longer and not
reversible, so the filename is corrupted rather than merely recased.

The lowercasing was never needed for the boundary math: path.win32.relative
already matches the root case-insensitively and returns the tail in its
original case. Extended-length prefix stripping is still needed, or a \?\
candidate relativizes to ..\..\..\?\C:\... and reads as an escape, so this
adds normalizeWindowsPathPreservingCase next to the comparison variant. It
mirrors that helper step for step, including the trim, minus the lowercasing;
a test pins the equivalence so the two cannot drift.

The containment decision is unchanged: relative(lower(a), lower(b)) and
relative(a, b) return the same structure, and that structure is all
validateRelativePathWithinBoundary inspects.

Sibling surfaces checked: the other two callers of
normalizeWindowsPathForComparison use it as a comparison key and are correct
as-is (installed-plugin-index-record-reader.ts:215 compares with ===,
fs-safe's isPathInside discards the relative and returns a boolean).
path-policy.ts was the only site returning the normalized value.

* test(agents): verify Windows filename case end to end

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

* style(agents): apply oxfmt to the workspace path case test

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:58:59 +01:00
clawsweeper[bot]GitHubopenclaw-clawsweeper[bot] <openclaw-clawsweeper[bot]@users.noreply.github.com>Claude Fable 5fuller-stack-dev
b88646cf78 feat(webchat): reply-to a message with hydrated reply context (#110791)
* feat(webchat): reply-to a message with hydrated reply context

Control UI replies now carry the target transcript id as replyToId on
chat.send. The Gateway resolves the replied-to message from session
history and hydrates the channel-agnostic ReplyToId/ReplyToBody/
ReplyToSender envelope fields, so agents receive reply_to_id,
has_reply_context, and the untrusted reply-target block exactly like
Discord replies (mirrors #90263). Reply targets without a persisted
transcript id keep the inline-quote fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(webchat): keep non-reply chat.send dispatch ordering and satisfy CI gates

* docs(webchat): match reply-context doc to webchat conversation-info policy

* fix(webchat): hydrate reply bodies from display-visible content only

---------

Co-authored-by: openclaw-clawsweeper[bot] <openclaw-clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
2026-07-18 15:57:59 -06:00
Peter SteinbergerandGitHub 4d683904df fix(agents): restore ask_user roundtrip in Gateway chats (#110961)
* fix(agents): restore ask_user channel roundtrip

* test(qa): derive ask_user proof from answers

* test(qa): isolate mock reply directives

* test(qa): wait for complete ask_user reply

* fix(agents): fail closed on stale ask_user prompts

* chore: remove release-owned changelog entry

* fix(agents): satisfy ask_user CI contracts
2026-07-18 22:55:46 +01: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
Peter SteinbergerandGitHub 74919102a8 fix(cron): preserve startup catch-up job mutations (#110977) 2026-07-18 22:53:49 +01:00
d71ab44a0c fix(sandbox): bound sandbox seed bootstrap file reads (#110017)
* fix(sandbox): bound sandbox seed bootstrap file reads

ensureSandboxWorkspace copies each seed bootstrap file (AGENTS.md / SOUL.md /
TOOLS.md / ...) into a fresh sandbox workspace with an unbounded
readFileSync after the root-boundary open. The seed path comes from user
config (seedFrom), so a misconfigured or oversized seed file forced an
unbounded allocation at sandbox-creation time. Use
readFileDescriptorBoundedSync with a 2 MiB limit (matching the workspace
bootstrap file limit) and skip the oversized seed file with a warning instead
of reading it all.

* fix(sandbox): bound workspace seed reads

Co-authored-by: 琚耀辉0668001366 <ju.yaohui@xydigit.com>

* test(agents): cover async bootstrap read retries

* test(agents): align bootstrap read mocks with main

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:53:31 +01:00
87de81a5fd Bound diagnostic config file read with size cap (#110591)
* Bound diagnostic config read with size cap

* fix: use buffer.toString for readRegularFileSync result in diagnostic export

* test: verify normal and oversized diagnostic config export behavior

PR #110591 — Add focused regression coverage for the bounded diagnostic config read:
- Test: normal config file (<8MB) produces parseOk: true in config/shape.json
- Test: oversized config file (>8MB) produces parseOk: false with graceful error handling
- Both tests verify the support bundle zip is still written with all expected contents

* refactor(logging): clarify support export config limit

Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:48:29 +01:00
Peter SteinbergerandGitHub 8ff1512454 feat(ui): sidebar rows use full width; actions overlay with a hover fade (#110959)
Session-row titles run to the sidebar edge instead of reserving the hover
action cluster's width in-flow. The trailing aside overlays the row
(pointer-events off at rest so link clicks pass through) and the link
content fades out beneath the surfaced controls via a mask, which stays
correct over rest/hover/active/selected backgrounds. Rows with a child
toggle keep it rightmost; actions land at the link edge either way. Child
rows keep the in-flow aside since their runtime trail is resting content.
Touch keeps actions visible, so the fade is permanent there.
2026-07-18 22:47:46 +01:00
4e11da2872 fix(process): UTF-8 command output corrupts at Windows byte limits (#105274)
* fix(process): preserve byte-capped UTF-8 output on Windows

* fix(process): skip codepage probe for UTF-8 output

Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:44:12 +01:00
Peter SteinbergerandGitHub 7a551bff0c fix(vault): aggregate provider outage diagnostics (#110908)
* fix(vault): aggregate provider outage diagnostics

* fix(vault): classify revoked token outages

* fix(vault): preserve scoped ACL failures

* fix(vault): keep token probes advisory

* test(vault): satisfy gateway proof gates

* fix(secrets): attribute web provider outages

* test(secrets): prove web outage fan-out in owner suite

* fix(vault): scope malformed responses per secret

* test(secrets): harden exec fanout fixtures
2026-07-18 22:44:06 +01:00
Peter SteinbergerandGitHub fa9ae68c34 docs(agents): draft-then-ready PR creation prevents the merge-ref CI race (#110970) 2026-07-18 22:41:10 +01:00
7e4a76a6d0 fix(amazon-bedrock): ignore blank region env overrides (#110676)
* fix(amazon-bedrock): ignore blank region env overrides

* fix(amazon-bedrock): normalize region envs across runtimes

Co-authored-by: ZengWen-DT <ceng.wen@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:38:51 +01:00
Peter SteinbergerandGitHub fc777a35bb test(telegram): mock core turn dispatcher (#110967) 2026-07-18 22:35:56 +01:00
d5b2749fd8 fix(ai): skip blank environment credentials during provider auth (#109691)
* fix(ai): ignore blank environment API keys

* test(ai): avoid credential-shaped fixture assignments

Co-authored-by: ZengWen-DT <ceng.wen@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:33:54 +01:00
848dbcabba fix(signal): keep REST timeout through slow-drip response bodies (#109047)
* fix(signal): keep REST timeout through slow-drip response bodies

* fix(signal): share REST operation deadlines

Co-authored-by: NIO <hu.genshen@xydigit.com>

* test(signal): preserve REST deadline errors

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:33:26 +01:00
Peter Steinberger 77a42adce4 fix(test): re-point workspace bootstrap fault injection at the bounded fd reader
#109945 replaced the readFileSync(fd) call in readWorkspaceFileWithGuards
with readFileDescriptorBounded, which consumes the descriptor via fs.read.
The two transient-read tests still injected EAGAIN through readFileSync,
so the fault never fired: one asserted a retry that never happened and the
other asserted missing=true while the real read succeeded. Inject at the
fs.read seam instead; retry classification in prod is unchanged and the
full file passes 58/58 again.
2026-07-18 14:30:28 -07:00
Peter SteinbergerandGitHub 7a70daf55d test(agents): update bootstrap read mocks (#110966) 2026-07-18 22:28:08 +01:00
Peter SteinbergerandGitHub 0135c6bf89 test: cover Codex version contracts on dependency bumps (#110958) 2026-07-18 22:09:50 +01:00
Peter SteinbergerandGitHub 5fe8d6a852 fix(gateway): clients can identify authorization failures (#110925)
* fix(gateway): structure authorization errors

* test(gateway): align authorization contracts

* fix(gateway): regenerate protocol bindings

* fix(gateway): preserve optional scope errors

* fix(gateway): report complete pairing scopes

* style(gateway): format protocol exports
2026-07-18 22:02:28 +01:00
bde17f99e9 fix(mantis): bound upload error response bodies (#109044)
* fix(mantis): bound upload error response bodies

Replace unbounded response.text() on non-ok upload error paths with
readBoundedResponseText (64 KiB cap). Only swallow the size-exceeded
diagnostic; propagate signal aborts and other failures so timeouts
during body reading are not masked as generic upload failures.

- Import shared readBoundedResponseText from scripts/lib/bounded-response.mjs
- Add MANTIS_UPLOAD_ERROR_BODY_MAX_BYTES constant (64 KiB)
- Catch only size-exceeded errors; re-throw timeouts and stream errors
- Add test: oversized body bounded at 64 KiB
- Add test: signal abort during body read propagates correctly
- Add test: small error body within bound reads normally

* fix(mantis): contextualize bounded upload failures

Co-authored-by: 胡根深 0668000903 <hu.genshen@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 22:00:15 +01:00
ff3ed99cde fix(usage-bar): validate meter width with strict integer parsing (#110410)
* fix(usage-bar): validate meter width with strict integer parsing

* fix(usage-bar): restore canonical meter width range [0, 100]

* fix(usage-bar): preserve bounded meter defaults

Co-authored-by: sheyanmin <she.yanmin@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 21:57:11 +01:00
Peter SteinbergerandGitHub 242093fb00 fix(ui): dark theme AA contrast for coral fills — deepen primary/destructive, add widget accent-fill token (#110957) 2026-07-18 21:56:24 +01:00
Wynne668andGitHub 21c341d548 fix(push): ignore blank timeout env overrides (#110677) 2026-07-18 21:54:33 +01:00
31f2c51ca2 fix(auto-reply): bound AGENTS.md read in post-compaction context (#109945)
* fix(auto-reply): bound AGENTS.md read in post-compaction context

readPostCompactionContext read the entire AGENTS.md with an unbounded
fs.readFileSync even though it only extracts a small (<=1800 char) section
for the continuation prompt. A pathologically large AGENTS.md forced an
unbounded allocation at compaction time. Use readFileDescriptorBoundedSync
with a 2 MiB limit (matching the workspace bootstrap file limit) and skip
the file with a warning when it exceeds the bound, instead of reading it all.

* fix(agents): bound workspace bootstrap reads

Co-authored-by: 琚耀辉0668001366 <ju.yaohui@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 21:52:23 +01:00
598f13a5f3 fix(zai): keep probe deadline through stalled error-body reads (#109026)
* fix(zai): keep probe deadline through stalled error-body reads

Fixes an issue where Z.AI endpoint detection could hang indefinitely
when a probe returned error headers and then stalled on the error
response body. The previous helper cleared the AbortSignal as soon as
fetch resolved headers, so a never-chunking error body bypassed the
probe deadline.

Keep one AbortController deadline across the probe request and the
bounded error-body read, and pass a clamped chunkTimeoutMs into
readResponseWithLimit so a stalled error body fails closed inside the
remaining budget. The chunkTimeoutMs is clamped to the remaining
deadline after fetch returns headers, so the idle timeout does not
outlast the overall probe deadline.

Tighten the stalled-body test timing assertion from 5s to 2x timeoutMs
to verify the deadline is actually respected.

* fix(zai): enforce probe body deadline

Co-authored-by: NIO <noreply@github.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: NIO <noreply@github.com>
2026-07-18 21:51:15 +01:00