Commit Graph

39346 Commits

Author SHA1 Message Date
Peter Steinberger aad27e2629 test(worktrees): honor host umask in mode assertions (#111075) 2026-07-18 17:44:39 -07:00
Peter Steinberger 6c2973dee4 feat(ui): automatic session attention states in the sidebar (#111035)
* feat(ui): automatic session attention states in the sidebar (question, approval, failed run)

* test(ui): split attention presentation and read-marker cases under line budgets

* fix(ui): restore precise chat-pane test-support signatures
2026-07-18 17:43:59 -07:00
Yuval Dinodia 0a588fa795 fix(acp): /acp sessions exposes every gateway session to non-owner senders (#110745)
* fix(acp): scope /acp sessions listing for non-owner senders

/acp sessions listed every ACP session on the gateway for any sender
allowlisted via commands.allowFrom, exposing other senders' session
labels, agent ids, runtime state, and thread bindings. The handler now
returns only the current bound or requester session for non-owner
senders, while owner identity and operator.admin clients keep the full
gateway-wide listing, matching the documented contract.

Fixes #103055

* test(acp): cover empty and missing-session cases for /acp sessions scoping

* fix(acp): avoid non-owner session scans

* docs(acp): remove duplicated session scope text

* test(acp): cover internal session visibility

* fix(acp): require current ACP metadata

* test(acp): reject sessions target tokens

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 18:38:48 -06:00
Peter Steinberger c745e7d66c fix(gateway): reject unsafe explicit approval IDs (#111055)
* fix(gateway): validate explicit approval ids

* fix(gateway): reject trailing approval controls

* test(gateway): cover embedded approval controls

* fix(gateway): reject empty explicit approval ids

* test(gateway): align approval projections with safe ids

* style(gateway): format approval id validation

* fix(gateway): reject approval url dot segments
2026-07-18 17:38:21 -07:00
Peter Steinberger ec740e79a4 fix(agents): honor Anthropic retry-after cooldowns (#111072) 2026-07-18 17:28:04 -07:00
mushuiyu886 7abcd6c691 fix(skills): distinguish duplicate ClawHub search results by publisher (#110903)
* fix(skills): distinguish duplicate search results by publisher

* test(skills): cover owner-qualified search output

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 17:27:25 -07:00
shaoohh e669e97194 fix(acp): persist confirmed output from cancelled turns (#110300)
* fix(acp): persist cancelled partial replies

* fix(acp): persist delivered output for cancelled turns

Normalize terminal status at the ACP manager boundary and settle routed/direct delivery outcomes before persisting cancelled bound turns.

Co-authored-by: shaoohh <150606856+shaoohh@users.noreply.github.com>

* fix(acp): require confirmed cancelled-turn delivery

Persist cancelled-turn output only when the core dispatcher reports successful delivery. Keep canonical ACP history independent of outbound-only hook rewrites and prove backend cancellation with the real dispatcher.

Co-authored-by: shaoohh <150606856+shaoohh@users.noreply.github.com>

* test(acp): satisfy cancellation proof lint

Keep the pending-delivery race assertion behavior while avoiding a return value from the Promise executor.

Co-authored-by: shaoohh <150606856+shaoohh@users.noreply.github.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 17:24:42 -07:00
Kris Wu e5b2ca3c6b feat(config): add compaction.thinkingLevel to override thinking level during compaction (#98074)
* feat(config): add compaction.thinkingLevel to override thinking during compaction

* fix: make compaction.thinkingLevel override inherited session thinking

* fix: warn on compaction.thinkingLevel ignored by Codex native runtime

* feat(config): add compaction thinking override

Co-authored-by: wu.duozhen@xydigit.com <wu.duozhen@xydigit.com>

* test(agents): remove duplicate compaction harness

Co-authored-by: wu.duozhen@xydigit.com <wu.duozhen@xydigit.com>

* test(plugin-sdk): account for thinking level export

Co-authored-by: wu.duozhen@xydigit.com <wu.duozhen@xydigit.com>

* chore: keep release note in PR metadata

* chore: reconcile plugin SDK surface budgets

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 17:18:28 -07:00
Peter Steinberger b4187ced90 feat: add live-validated Zoom meeting guest plugin (#111048)
* feat(zoom-meetings): add browser guest transport

* fix(zoom-meetings): type reusable refresh result

* test(meeting-bot): bind reusable refresh callback

* chore(zoom-meetings): use existing plugin label

* chore(zoom-meetings): defer changelog to release

* test(zoom-meetings): cover node setup through public host
2026-07-18 17:17:26 -07:00
Yuval Dinodia 4e8f035912 fix(agents): keep run-mode subagent bundle MCP runtimes alive for approved follow-up turns (#101830)
Run-mode subagent cleanup retired the session bundle MCP runtime even when an
approved exec follow-up turn still held an active materialization lease, so the
follow-up turn failed with "bundle-mcp runtime disposed for session ...".

Retire run-mode subagent runtimes with preserveActiveLeases so retirement defers
while a lease is held, and complete the deferred retirement when the run's
materialized tool runtime releases its lease.
2026-07-18 17:15:25 -07:00
xingzhou 9b42782f76 fix(plugins): agent runs hang when tool-result middleware never settles (#110731)
* fix(plugins): prevent tool-result middleware from freezing runs

* test(plugins): cover middleware timeout policy

* test(plugins): provide hook context in timeout test

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 17:11:59 -07:00
zengLingbiao 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
zengLingbiao 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
zengLingbiao 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
Peter Steinberger a040f10e85 fix(gateway): reconcile config after watcher recovery (#111049)
Reload the current on-disk config once replacement native or polling watchers finish their initial scan, so edits made during recovery backoff are not lost.
2026-07-18 17:00:40 -07:00
Ted Li 10f5a7ac58 fix(groq): keep default Llama agent turns within TPM limit (#104904)
* fix(groq): recover oversized requests safely

* fix(groq): clear parallel tool aliases on fallback

* fix(groq): require provenance-aware host

* fix(groq): preserve configured request budgets

---------
authored-by: @MonkeyLeeT
Co-authored-by: Colin <colin@solvely.net>
2026-07-18 20:00:12 -04:00
Peter Steinberger 9a94beace7 fix(process): preserve descendant output under event-loop stalls (#111040)
* fix(process): drain buffered descendant output after exit

* test(process): cover deferred output release phase
2026-07-18 17:00:08 -07:00
Peter Steinberger 8853217ec7 fix(gateway): cancel run-bound approvals on abort and expose approval-park lifecycle (#110993)
* fix: harden approval abort lifecycle

* fix(gateway): recheck abort before committing detached exec authorization

* fix(gateway): recheck abort after inline exec authorization commit

* fix(gateway): propagate run-abort cancellation on the inline approval path

* fix(ci): refresh approval protocol bindings
2026-07-19 00:52:21 +01:00
cxbAsDev 8899940d5a fix(cron): support current and session: targets in initial delivery resolution (#99115)
* fix(cron): support current and session: targets in initial delivery resolution

resolveInitialCronDelivery only checked sessionTarget === 'isolated' for
default announce delivery. All other cron paths (delivery-plan.ts:106-114,
normalize.ts:680-684, jobs.ts:288-298) already treat 'isolated', 'current',
and 'session:' as equivalent. This gap meant cron jobs created with
--session-target current or --session-target session:... would not get
default announce delivery at creation time.

Real behavior proof:
  Input:  sessionTarget='current', payload.kind='agentTurn'
  OLD:    undefined  <- missing default announce delivery
  NEW:    {mode:'announce'}  <- consistent with other cron code paths

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

* test(cron): add typed initial delivery resolution tests for all session targets

* test(cron): document direct-service delivery contract and cover service callers

* refactor(cron): centralize detached-delivery-target predicate into shared helper

Extract isDetachedDeliveryTarget(sessionTarget, payloadKind) in normalize.ts
and use it from all three callers:

- normalizeCronJobInput (write-time default, normalize.ts)
- resolveCronDeliveryPlan (read-time default, delivery-plan.ts)
- resolveInitialCronDelivery (service-bypass default, initial-delivery.ts)

This removes the third manually synchronized copy that the original PR
added, addressing ClawSweeper P1 finding.

* fix(cron): align initial delivery targets

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

* test(cron): consolidate initial delivery coverage

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

* test(cron): keep delivery matrix type-safe

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

* chore: keep release note in PR metadata

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-19 00:51:05 +01:00
Jason (Json) 7c070d6df6 feat: allow standalone MCP Apps to use bound tools and resources (#110515)
* Gateway: enable ticketed MCP App bridge

* Gateway: make MCP App operation switch exhaustive

* refactor(gateway): drop no-op MCP App route change

* fix(gateway): enforce standalone MCP App tool authority

* docs(gateway): clarify standalone MCP App ticket authority

* fix(gateway): require standalone MCP App initialization

* refactor(gateway): keep standalone route load explicit
2026-07-18 17:50:37 -06:00
Peter Steinberger 56eef79989 fix(channels): recover dead-lettered inbound events (#111029)
* feat(channels): add ingress dead-letter recovery

* fix(channels): preserve dead-letter compatibility

* docs: refresh CLI map for channel dead letters
2026-07-19 00:44:23 +01:00
Peter Steinberger 7562b79465 refactor(channels): share durable ingress monitor (#111017)
* refactor(channels): share durable ingress monitor

* fix(channels): harden ingress monitor shutdown

* docs: refresh SDK documentation map
2026-07-19 00:42:08 +01:00
Peter Steinberger c1e714e3df refactor(agents): shared spawn orchestration pipeline behind backend adapters (#111007)
* refactor(agents): share spawn orchestration pipeline

* fix(agents): preserve ACP spawn failure contract

* fix(agents): progress hooks keep per-backend session-key semantics

* fix(agents): ACP registration keeps the resolved requester agent id

* chore(agents): drop orphaned exports surfaced by the spawn dedup
2026-07-19 00:40:22 +01:00
Peter Steinberger ec998a0f3f feat(canvas): export widgets as PNG — copy or download from the card menu (#110992)
* feat(canvas): export widgets as PNG — copy to clipboard or download from the card menu

* fix(ui): widget-export lint, knip, and lines-budget cleanup
2026-07-19 00:39:59 +01:00
mushuiyu886 d411559dfb fix(acp): reject malformed session list cursors (#107895)
* fix(acp): reject malformed session list cursors

* fix(session-catalog): validate pagination cursors exactly

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

* refactor(session-catalog): align search normalization owner

* refactor(session-catalog): keep parsing helpers private

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

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-19 00:38:02 +01:00
Peter Steinberger f7024690ac refactor(gateway): split chat handlers by workflow (#111022) 2026-07-19 00:35:42 +01:00
Peter Steinberger 4362e1a4fe fix(agents): recognize nested message delivery receipts (#111012) 2026-07-19 00:33:33 +01:00
Peter Steinberger dc8f90197e fix(tui): guarantee exit after drained teardown (#111015) 2026-07-19 00:33:16 +01:00
zengLingbiao 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
Peter Steinberger 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.
2026-07-18 23:43:39 +01:00
Peter Steinberger 5114b45927 chore(canvas): compact show_widget tool description with pattern anchor (-40% tokens) (#110999) 2026-07-18 23:41:29 +01:00
Peter Steinberger 333a3297af refactor(plugin-sdk): consolidate persistent dedupe claim loop (#110984) 2026-07-18 23:36:59 +01:00
Peter Steinberger 7fbfc2e8ad fix(plugins): allow intentional uninstall size drops (#110991) 2026-07-18 23:28:18 +01:00
cxbAsDev 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 Steinberger 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 Steinberger 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 Steinberger 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
Leon-SK668 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 Steinberger 264a7b0d12 refactor(channels): consolidate manifest descriptors (#110985) 2026-07-18 23:18:28 +01:00
juyaohuidt 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
Yiğit ERDOĞAN 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
cxbAsDev 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 Steinberger f3f6eb2321 fix(codex): stop fallback for superseded sessions (#110980) 2026-07-18 23:02:03 +01:00
Peter Steinberger cd78fb3843 fix(channels): harden prepared turn lifecycle (#110981) 2026-07-18 23:01:38 +01:00
Peter Steinberger 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
Yiğit ERDOĞAN 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] 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 Steinberger 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
zengLingbiao 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 Steinberger 74919102a8 fix(cron): preserve startup catch-up job mutations (#110977) 2026-07-18 22:53:49 +01:00