Skip to content

fix(config): respect HOME env var in default_config_dir for Windows#2688

Closed
killf wants to merge 386 commits intomasterfrom
fix/default-config-dir-home-env
Closed

fix(config): respect HOME env var in default_config_dir for Windows#2688
killf wants to merge 386 commits intomasterfrom
fix/default-config-dir-home-env

Conversation

@killf
Copy link

@killf killf commented Mar 4, 2026

Summary

  • Fixes load_or_init_uses_persisted_active_workspace_marker test failure on Windows
  • default_config_dir() now respects HOME environment variable before falling back to UserDirs::new()

Problem

The test was failing because on Windows, UserDirs::new() queries the Windows API for the user profile directory, which doesn't respect the HOME environment variable set in tests.

Solution

Check HOME environment variable first before falling back to UserDirs::new(). This is useful for:

  • Testing scenarios that need to override home directory
  • Docker containers that rely on HOME environment variable

Risk

  • Low risk: Only affects how the default config directory is determined when HOME is explicitly set
  • Backward compatible: Existing behavior is preserved when HOME is not set

Rollback

Revert commit if issues arise.

Test

  • All 261 config schema tests pass
  • All workspace marker tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Configuration directory resolution now properly respects the HOME environment variable, ensuring consistent behavior across different environments.

guitaripod and others added 30 commits February 24, 2026 20:28
The Anthropic provider had no Image variant in NativeContentOut, so
[IMAGE:data:image/jpeg;base64,...] markers produced by the multimodal
pipeline were sent to the API as plain text. The API counted every
base64 character as a token, reliably exceeding the 200k token limit
for any real image (a typical Telegram-compressed photo produced
~130k tokens of base64 text alone).

Fix:
- Add ImageSource struct and Image variant to NativeContentOut that
  serializes to the Anthropic Messages API image content block format
- Add parse_inline_image() to decode data URI markers into Image blocks
- Add build_user_content_blocks() to split user message content into
  Text and Image blocks using the existing parse_image_markers helper
- Update convert_messages() user arm to use build_user_content_blocks()
- Handle Image in the apply_cache_to_last_message no-op arm

Fixes #1626
format_attachment_content was matching only Photo for [IMAGE:] routing.
Documents with image extensions (jpg, png, gif, webp, bmp) were formatted as
[Document: name] /path, bypassing the multimodal pipeline entirely.

Extend the match arm to cover Document when is_image_extension returns true,
so both Photos and image Documents produce [IMAGE:/path] and reach the provider
as proper vision input blocks.

Adds regression tests covering Document+image extension → [IMAGE:] and
Document+non-image extension → [Document:] paths.
…cking

- Base branch target: dev
  - Problem: ZeroClaw agents have no structured way to decompose complex tasks into trackable steps, falling behind
  every comparable agent runtime
  - Why it matters: Without task tracking, multi-step work is fragile (lost on context compression), invisible to users
   (no progress signal), and error-prone (agent loses track of what's done vs. pending)
  - What changed: Added a session-scoped task_plan tool with create/add/update/list/delete actions, integrated with
  SecurityPolicy, registered in the tool factory
  - What did not change: No config schema changes, no persistence layer, no CLI subcommand, no changes to agent loop or
   any other subsystem

  Label Snapshot

  - Risk label: risk: low
  - Size label: size: S
  - Scope labels: tool
  - Module labels: tool: task_plan
  - Contributor tier label: (auto-managed)
  - If any auto-label is incorrect: N/A

  Change Metadata

  - Change type: feature
  - Primary scope: tool

  Linked Issue

  - Closes #(issue number)
  - Related: N/A
  - Depends on: N/A
  - Supersedes: N/A

  Supersede Attribution

  N/A — no superseded PRs.

  Validation Evidence

  cargo fmt --all -- --check    # pass (no output)
  cargo clippy --all-targets -- -D warnings  # task_plan.rs: 0 warnings (pre-existing warnings in other files
  unrelated)
  cargo test --lib tools::task_plan  # 15/15 passed

  - Evidence provided: test output (15 passed, 0 failed)
  - If any command is intentionally skipped: cargo clippy reports pre-existing warnings in unrelated files
  (onboard/wizard.rs etc.); task_plan.rs itself has zero clippy warnings

  Security Impact

  - New permissions/capabilities? No — uses existing ToolOperation::Act enforcement
  - New external network calls? No
  - Secrets/tokens handling changed? No
  - File system access scope changed? No

  Privacy and Data Hygiene

  - Data-hygiene status: pass
  - Redaction/anonymization notes: No identity data in code or tests. Test fixtures use neutral strings ("step one",
  "do thing", "first")
  - Neutral wording confirmation: All naming follows ZeroClaw/project-native conventions

  Compatibility / Migration

  - Backward compatible? Yes
  - Config/env changes? No
  - Migration needed? No

  i18n Follow-Through

  - i18n follow-through triggered? No — no docs or user-facing wording changes

  Human Verification

  - Verified scenarios: Ran ./target/debug/zeroclaw agent -m "调用 task_plan 工具,action=list" — agent correctly
  identified and called task_plan, returned "No tasks."
  - Edge cases checked: read-only mode blocks mutations, empty task list, invalid action names, missing required
  parameters, create replaces existing list, ID auto-increment after add
  - What was not verified: Behavior with non-CLI channels (Telegram, Discord); behavior with XML-fallback dispatcher
  (non-native-tool providers)

  Side Effects / Blast Radius

  - Affected subsystems/workflows: src/tools/ only — tool factory gains one additional entry
  - Potential unintended effects: Marginally increases tool spec payload size sent to LLM (one more tool definition).
  Could theoretically cause tool name confusion with schedule if LLM descriptions are ambiguous — mitigated by distinct
   naming (task_plan vs schedule) and different description wording.
  - Guardrails/monitoring for early detection: Standard tool dispatch logging. Tool is session-scoped so no persistent
  side effects on failure.

  Agent Collaboration Notes

  - Agent tools used: Claude Code for implementation assistance and review
  - Workflow/plan summary: Implement Tool trait → register in factory → validate with tests → manual agent session test
  - Verification focus: Security policy enforcement, parameter validation edge cases, all 5 action paths
  - Confirmation: naming + architecture boundaries followed (CLAUDE.md §6.3, §6.4, §7.3): Yes

  Rollback Plan

  - Fast rollback command/path: git revert <commit> — removes 3 lines from mod.rs and deletes task_plan.rs
  - Feature flags or config toggles: None needed — tool is stateless and session-scoped
  - Observable failure symptoms: Tool not appearing in agent tool list, or tool returning errors on valid input

  Risks and Mitigations

  - Risk: LLM may occasionally confuse task_plan (action: list) with schedule (action: list) due to similar parameter
  structure
    - Mitigation: Distinct tool names and descriptions; task_plan description emphasizes "session checklist" while
  schedule emphasizes "cron/recurring tasks"
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.2 to 6.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v4.6.2...b7c566a)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Automated replay on latest dev.
…ints

Add an OpenAI-compatible API surface to the gateway so that standard
OpenAI client libraries can interact with ZeroClaw directly.

Endpoints:
- POST /v1/chat/completions — supports both streaming (SSE) and
  non-streaming responses, bearer token auth, rate limiting
- GET /v1/models — returns the gateway's configured model

The chat completions endpoint accepts the standard OpenAI request format
(model, messages, temperature, stream) and returns responses in the
OpenAI envelope format. Streaming uses SSE with delta chunks and a
[DONE] sentinel. A 512KB body limit is applied (vs 64KB default) since
chat histories can be large.

When the underlying provider doesn't support native streaming, the
handler falls back to wrapping the non-streaming response in a single
SSE chunk for transparent compatibility.

Includes 8 unit tests for request/response serialization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the streaming provider trait methods for Bedrock, enabling
real-time token-by-token responses via the ConverseStream endpoint.

Key implementation details:
- Uses /model/{id}/converse-stream endpoint with SigV4 signing
- Parses AWS binary event-stream format (application/vnd.amazon.eventstream)
  with a minimal parser (~60 lines) — no new crate dependencies needed
- Handles contentBlockDelta events for text extraction, plus error and
  exception events
- Uses mpsc channel + stream::unfold pattern (matching compatible.rs)
- Clones credentials for async task ownership

The binary event-stream parser extracts frame lengths, header sections
(looking for :event-type), and payload bytes. CRC validation is skipped
since TLS already provides integrity guarantees.

Includes 10 new tests for URL formatting, binary parsing, and
deserialization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add underscores to long numeric literals (1234567890 → 1_234_567_890)
- Allow cast_possible_truncation for rough token estimates
- Replace loop/match with while-let for event stream parsing
- Merge identical match arms for event types
- Add #[allow(clippy::cast_possible_truncation)] on test helper

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The default trait implementation returned a single error chunk that the
SSE mapper silently converted to `data: [DONE]`, producing empty
streaming responses from the OpenAI-compatible endpoint. Mirror the
non-streaming chat_with_history pattern: extract system + last user
message and delegate to stream_chat_with_system, which all providers
already implement.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add zh-CN translations and locale normalization in i18n\n- type locale context/state and support three-language cycle in header

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 4814e80)
parse_attachment_markers used .find(']') which returns the first ']', so
filenames containing brackets (e.g. yt-dlp output 'Video [G4PvTrTp7Tc].mp4')
were truncated at the inner bracket, producing a wrong path and a send failure.

Replace the naive search with find_matching_close, a depth-tracking scanner
that correctly skips nested '[...]' pairs and returns the index of the
outermost closing bracket.

Adds regression tests for the bracket-in-filename case and for the
unclosed-bracket fallback (no match → message passed through unchanged).
Replace default UTC timer with ChronoLocal::rfc_3339() so daemon and
CLI log lines display the operator's local time, making correlation
with external events easier.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Automated conflict recovery via changed-file replay on latest dev.
Voice messages were being silently ignored when transcription was disabled
or user was unauthorized, making it difficult to diagnose configuration
issues. This change adds:

- Debug log when voice/audio message received but transcription disabled
- Debug log when voice message skipped due to unauthorized user
- Info log on successful voice transcription

Closes #1469

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added config Auto scope: src/config/** changed. and removed agent Auto scope: src/agent/** changed. channel Auto scope: src/channels/** changed. gateway Auto scope: src/gateway/** changed. memory Auto scope: src/memory/** changed. observability Auto scope: src/observability/** changed. provider Auto scope: src/providers/** changed. security Auto scope: src/security/** changed. skills Auto scope: src/skills/** changed. tool Auto scope: src/tools/** changed. tests Auto scope: tests/** changed. scripts Auto scope: scripts/** changed. size: XL Auto size: >1000 non-doc changed lines. risk: high Auto risk: security/runtime/gateway/tools/workflows. config Auto scope: src/config/** changed. daemon: core Auto module: daemon core files changed. onboard: wizard Auto module: onboard/wizard changed. cron: scheduler Auto module: cron/scheduler changed. labels Mar 4, 2026
theonlyhennygod
theonlyhennygod previously approved these changes Mar 5, 2026
Copy link
Collaborator

@theonlyhennygod theonlyhennygod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed: change is scoped and correct for Windows/test environments. Approving for merge.

The test load_or_init_uses_persisted_active_workspace_marker was
failing on Windows because default_config_dir() used UserDirs::new()
which queries the Windows API for the user profile directory, not the
HOME environment variable.

Fix: Check HOME environment variable first before falling back to
UserDirs::new(). This is useful for testing and Docker scenarios.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
theonlyhennygod
theonlyhennygod previously approved these changes Mar 5, 2026
Copy link
Collaborator

@theonlyhennygod theonlyhennygod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed on dev base: scoped config fix remains correct. Approving.

@theonlyhennygod
Copy link
Collaborator

Closing as part of branch cleanup (March 2026). If this work is still needed, please rebase onto master and open a fresh PR. See discussion #3419 for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config: core Auto module: config core files changed. experienced contributor Contributor with 10+ merged PRs. risk: medium Auto risk: src/** or dependency/config changes. size: XS Auto size: <=80 non-doc changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.