Compare commits

..

10 commits

Author SHA1 Message Date
pluginmd
c185261909 fix(i18n): rename README.vn.md to README.vi.md
Some checks are pending
CI Run / Detect Change Scope (push) Waiting to run
CI Run / Lint Gate (Format + Clippy + Strict Delta) (push) Blocked by required conditions
CI Run / Test (push) Blocked by required conditions
CI Run / Build (Smoke) (push) Blocked by required conditions
CI Run / Docs-Only Fast Path (push) Blocked by required conditions
CI Run / Non-Rust Fast Path (push) Blocked by required conditions
CI Run / Docs Quality (push) Blocked by required conditions
CI Run / Lint Feedback (push) Blocked by required conditions
CI Run / Workflow Owner Approval (push) Blocked by required conditions
CI Run / CI Required Gate (push) Blocked by required conditions
Test E2E / Integration / E2E Tests (push) Waiting to run
Use correct ISO 639-1 language code (vi) instead of country code (vn),
consistent with existing translations (zh-CN, ja, ru).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:01:38 +08:00
pluginmd
4abd1b4471 docs(i18n): add Vietnamese README translation
Add full Vietnamese (Tiếng Việt) translation of README.md and update
language selector links across existing README files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:01:38 +08:00
dependabot[bot]
b23c2e7ae6
chore(deps): bump rand from 0.9.2 to 0.10.0 (#1075)
* chore(deps): bump rand from 0.9.2 to 0.10.0

Bumps [rand](https://github.com/rust-random/rand) from 0.9.2 to 0.10.0.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/rand_core-0.9.2...0.10.0)

---
updated-dependencies:
- dependency-name: rand
  dependency-version: 0.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(security): keep token generation compatible with rand 0.10

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Will Sarg <12886992+willsarg@users.noreply.github.com>
2026-02-20 05:29:23 -05:00
dependabot[bot]
bd7b59151a
chore(deps): bump actions/download-artifact from 4.3.0 to 7.0.0 (#1073)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.3.0 to 7.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](d3f86a106a...37930b1c2a)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-20 05:27:25 -05:00
dependabot[bot]
e04d114814
chore(deps): bump toml from 0.8.23 to 1.0.1+spec-1.1.0 (#1074)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.23 to 1.0.1+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.23...toml-v1.0.1)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 1.0.1+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-20 05:25:41 -05:00
dependabot[bot]
ee7c437061
chore(deps): bump probe-rs from 0.30.0 to 0.31.0 (#1076)
Bumps [probe-rs](https://github.com/probe-rs/probe-rs) from 0.30.0 to 0.31.0.
- [Release notes](https://github.com/probe-rs/probe-rs/releases)
- [Changelog](https://github.com/probe-rs/probe-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/probe-rs/probe-rs/compare/v0.30.0...v0.31.0)

---
updated-dependencies:
- dependency-name: probe-rs
  dependency-version: 0.31.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-20 05:25:38 -05:00
fettpl
c649ced585
fix(security): enforce cron agent autonomy and rate gates (#626) 2026-02-20 05:23:20 -05:00
Edvard Schøyen
861137b2b3
fix(security): deny unapproved tool calls on non-CLI channels (#998)
When autonomy is set to "supervised", the approval gate only prompted
interactively on CLI. On Telegram and other channels, all tool calls
were silently auto-approved with ApprovalResponse::Yes, including
high-risk tools like shell — completely bypassing supervised mode.

On non-CLI channels where interactive prompting is not possible, deny
tool calls that require approval instead of auto-approving. Users can
expand the auto_approve list in config to explicitly allow specific
tools on non-interactive channels.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 05:22:56 -05:00
Andy Tian
9fdc4c36b1
fix(observability): use blocking OTLP HTTP exporter client (#975) (#1032) 2026-02-20 05:08:33 -05:00
Alex Gorevski
2c407f6a55
refactor(lib): restrict internal module visibility to pub(crate) (#985)
Restrict 19 internal-only modules from pub to pub(crate) in lib.rs,
reducing the public API surface of the library crate.

Modules kept pub (used by integration tests, benchmarks, or are
documented extension points per AGENTS.md):
  agent, channels, config, gateway, memory, observability,
  peripherals, providers, rag, runtime, tools

Modules restricted to pub(crate) (not imported via zeroclaw:: by any
external consumer):
  approval, auth, cost, cron, daemon, doctor, hardware, health,
  heartbeat, identity, integrations, migration, multimodal, onboard,
  security, service, skills, tunnel, util

Also restrict 6 command enums (ServiceCommands, ChannelCommands,
SkillCommands, MigrateCommands, CronCommands, IntegrationCommands)
to pub(crate) — main.rs defines its own copies and does not import
these from the library crate. HardwareCommands and PeripheralCommands
remain pub as main.rs imports them via zeroclaw::.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-20 05:06:41 -05:00
10 changed files with 1219 additions and 213 deletions

View file

@ -145,7 +145,7 @@ jobs:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Download all artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: artifacts

241
Cargo.lock generated
View file

@ -47,7 +47,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"cpufeatures 0.2.17",
]
[[package]]
@ -304,7 +304,7 @@ dependencies = [
"futures-lite",
"parking",
"polling",
"rustix 1.1.3",
"rustix",
"slab",
"windows-sys 0.61.2",
]
@ -475,6 +475,12 @@ version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "basic-udev"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a45f9771ced8a774de5e5ebffbe520f52e3943bf5a9a6baa3a5d14a5de1afe6"
[[package]]
name = "bincode"
version = "2.0.1"
@ -559,7 +565,7 @@ dependencies = [
"cc",
"cfg-if",
"constant_time_eq",
"cpufeatures",
"cpufeatures 0.2.17",
]
[[package]]
@ -695,7 +701,18 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"cpufeatures 0.2.17",
]
[[package]]
name = "chacha20"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"rand_core 0.10.0",
]
[[package]]
@ -705,7 +722,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20",
"chacha20 0.9.1",
"cipher",
"poly1305",
"zeroize",
@ -947,16 +964,6 @@ dependencies = [
"url",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation"
version = "0.10.1"
@ -991,6 +998,15 @@ dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
@ -1135,7 +1151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.17",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
@ -1989,6 +2005,7 @@ dependencies = [
"cfg-if",
"libc",
"r-efi",
"rand_core 0.10.0",
"wasip2",
"wasip3",
]
@ -2181,12 +2198,6 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hermit-abi"
version = "0.5.2"
@ -2205,12 +2216,12 @@ version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "565dd4c730b8f8b2c0fb36df6be12e5470ae10895ddcc4e9dcfbfb495de202b0"
dependencies = [
"basic-udev",
"cc",
"cfg-if",
"libc",
"nix 0.27.1",
"pkg-config",
"udev",
"windows-sys 0.48.0",
]
@ -2707,17 +2718,6 @@ dependencies = [
"mach2 0.5.0",
]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi 0.3.9",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "ipnet"
version = "2.11.0"
@ -2926,22 +2926,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "libudev-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
dependencies = [
"libc",
"pkg-config",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
@ -3685,43 +3669,24 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
dependencies = [
"hermit-abi 0.5.2",
"hermit-abi",
"libc",
]
[[package]]
name = "nusb"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f861541f15de120eae5982923d073bfc0c1a65466561988c82d6e197734c19e"
dependencies = [
"atomic-waker",
"core-foundation 0.9.4",
"core-foundation-sys",
"futures-core",
"io-kit-sys 0.4.1",
"libc",
"log",
"once_cell",
"rustix 0.38.44",
"slab",
"windows-sys 0.48.0",
]
[[package]]
name = "nusb"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5750d884c774a2862b0049b0318aea27cecc9e873485540af5ed8ab8841247da"
dependencies = [
"core-foundation 0.10.1",
"core-foundation",
"core-foundation-sys",
"futures-core",
"io-kit-sys 0.5.0",
"linux-raw-sys 0.11.0",
"linux-raw-sys",
"log",
"once_cell",
"rustix 1.1.3",
"rustix",
"slab",
"windows-sys 0.61.2",
]
@ -4161,9 +4126,9 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi 0.5.2",
"hermit-abi",
"pin-project-lite",
"rustix 1.1.3",
"rustix",
"windows-sys 0.61.2",
]
@ -4173,7 +4138,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures",
"cpufeatures 0.2.17",
"opaque-debug",
"universal-hash",
]
@ -4185,7 +4150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
dependencies = [
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.17",
"opaque-debug",
"universal-hash",
]
@ -4294,9 +4259,9 @@ dependencies = [
[[package]]
name = "probe-rs"
version = "0.30.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee27329ac37fa02b194c62a4e3c1aa053739884ea7bcf861249866d3bf7de00"
checksum = "ee50102aaa214117fc4fbe1311077835f0f4faa71e4a769bf65f955cc020ee34"
dependencies = [
"anyhow",
"async-io",
@ -4313,8 +4278,8 @@ dependencies = [
"ihex",
"itertools 0.14.0",
"jep106",
"nusb 0.1.14",
"object 0.37.3",
"nusb",
"object 0.38.1",
"parking_lot",
"probe-rs-target",
"rmp-serde",
@ -4330,9 +4295,9 @@ dependencies = [
[[package]]
name = "probe-rs-target"
version = "0.30.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2239aca5dc62c68ca6d8ff0051fe617cb8363b803380fbc60567e67c82b474df"
checksum = "031bed1313b45d93dae4ca8f0fee098530c6632e4ebd9e2769d5a49cdef273d3"
dependencies = [
"base64",
"indexmap",
@ -4348,7 +4313,7 @@ version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
dependencies = [
"toml_edit 0.23.10+spec-1.0.0",
"toml_edit",
]
[[package]]
@ -4618,6 +4583,17 @@ dependencies = [
"rand_core 0.9.5",
]
[[package]]
name = "rand"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
dependencies = [
"chacha20 0.10.0",
"getrandom 0.4.1",
"rand_core 0.10.0",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
@ -5081,19 +5057,6 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.11.0",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
]
[[package]]
name = "rustix"
version = "1.1.3"
@ -5103,7 +5066,7 @@ dependencies = [
"bitflags 2.11.0",
"errno",
"libc",
"linux-raw-sys 0.11.0",
"linux-raw-sys",
"windows-sys 0.61.2",
]
@ -5251,7 +5214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38"
dependencies = [
"bitflags 2.11.0",
"core-foundation 0.10.1",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
@ -5396,15 +5359,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "1.0.4"
@ -5462,7 +5416,7 @@ checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b"
dependencies = [
"bitflags 2.11.0",
"cfg-if",
"core-foundation 0.10.1",
"core-foundation",
"core-foundation-sys",
"io-kit-sys 0.4.1",
"mach2 0.4.3",
@ -5479,7 +5433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.17",
"digest",
]
@ -5490,7 +5444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.17",
"digest",
]
@ -5759,7 +5713,7 @@ dependencies = [
"fastrand",
"getrandom 0.4.1",
"once_cell",
"rustix 1.1.3",
"rustix",
"windows-sys 0.61.2",
]
@ -6048,18 +6002,6 @@ dependencies = [
"tokio-util",
]
[[package]]
name = "toml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
"toml_edit 0.22.27",
]
[[package]]
name = "toml"
version = "0.9.12+spec-1.1.0"
@ -6067,7 +6009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
dependencies = [
"serde_core",
"serde_spanned 1.0.4",
"serde_spanned",
"toml_datetime 0.7.5+spec-1.1.0",
"toml_parser",
"winnow 0.7.14",
@ -6081,22 +6023,13 @@ checksum = "bbe30f93627849fa362d4a602212d41bb237dc2bd0f8ba0b2ce785012e124220"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned 1.0.4",
"serde_spanned",
"toml_datetime 1.0.0+spec-1.1.0",
"toml_parser",
"toml_writer",
"winnow 0.7.14",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
dependencies = [
"serde",
]
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
@ -6115,20 +6048,6 @@ dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap",
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
"toml_write",
"winnow 0.7.14",
]
[[package]]
name = "toml_edit"
version = "0.23.10+spec-1.0.0"
@ -6150,12 +6069,6 @@ dependencies = [
"winnow 0.7.14",
]
[[package]]
name = "toml_write"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "toml_writer"
version = "1.0.6+spec-1.1.0"
@ -6392,18 +6305,6 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6"
[[package]]
name = "udev"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50051c6e22be28ee6f217d50014f3bc29e81c20dc66ff7ca0d5c5226e1dcc5a1"
dependencies = [
"io-lifetimes",
"libc",
"libudev-sys",
"pkg-config",
]
[[package]]
name = "uf2-decode"
version = "0.2.0"
@ -7683,7 +7584,7 @@ dependencies = [
"lettre",
"mail-parser",
"matrix-sdk",
"nusb 0.2.2",
"nusb",
"opentelemetry",
"opentelemetry-otlp",
"opentelemetry_sdk",
@ -7693,7 +7594,7 @@ dependencies = [
"probe-rs",
"prometheus",
"prost 0.14.3",
"rand 0.9.2",
"rand 0.10.0",
"regex",
"reqwest",
"ring",
@ -7748,7 +7649,7 @@ dependencies = [
"thiserror 2.0.18",
"tokio",
"tokio-test",
"toml 0.8.23",
"toml 1.0.1+spec-1.1.0",
"tracing",
]

View file

@ -72,7 +72,7 @@ sha2 = "0.10"
hex = "0.4"
# CSPRNG for secure token generation
rand = "0.9"
rand = "0.10"
# serde-big-array for wa-rs storage (large array serialization)
serde-big-array = { version = "0.5", optional = true }
@ -142,7 +142,7 @@ tokio-serial = { version = "5", default-features = false, optional = true }
nusb = { version = "0.2", default-features = false, optional = true }
# probe-rs for STM32/Nucleo memory read (Phase B)
probe-rs = { version = "0.30", optional = true }
probe-rs = { version = "0.31", optional = true }
# PDF extraction for datasheet RAG (optional, enable with --features rag-pdf)
pdf-extract = { version = "0.10", optional = true }

View file

@ -25,7 +25,7 @@ Built by students and members of the Harvard, MIT, and Sundai.Club communities.
</p>
<p align="center">
🌐 <strong>Languages:</strong> <a href="README.md">English</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.ru.md">Русский</a>
🌐 <strong>Languages:</strong> <a href="README.md">English</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.ru.md">Русский</a> · <a href="README.vi.md">Tiếng Việt</a>
</p>
<p align="center">

1051
README.vi.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -30,7 +30,7 @@ tokio = { version = "1.42", features = ["rt-multi-thread", "macros", "time", "sy
# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.8"
toml = "1.0"
# HTTP client (for Ollama vision)
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }

View file

@ -1099,11 +1099,13 @@ pub(crate) async fn run_tool_call_loop(
arguments: call.arguments.clone(),
};
// Only prompt interactively on CLI; auto-approve on other channels.
// On CLI, prompt interactively. On other channels where
// interactive approval is not possible, deny the call to
// respect the supervised autonomy setting.
let decision = if channel_name == "cli" {
mgr.prompt_cli(&request)
} else {
ApprovalResponse::Yes
ApprovalResponse::No
};
mgr.record_decision(&call.name, &call.arguments, decision, channel_name);

View file

@ -61,7 +61,7 @@ async fn execute_job_with_retry(
for attempt in 0..=retries {
let (success, output) = match job.job_type {
JobType::Shell => run_job_command(config, security, job).await,
JobType::Agent => run_agent_job(config, job).await,
JobType::Agent => run_agent_job(config, security, job).await,
};
last_output = output;
@ -116,7 +116,31 @@ async fn execute_and_persist_job(
(job.id.clone(), success)
}
async fn run_agent_job(config: &Config, job: &CronJob) -> (bool, String) {
async fn run_agent_job(
config: &Config,
security: &SecurityPolicy,
job: &CronJob,
) -> (bool, String) {
if !security.can_act() {
return (
false,
"blocked by security policy: autonomy is read-only".to_string(),
);
}
if security.is_rate_limited() {
return (
false,
"blocked by security policy: rate limit exceeded".to_string(),
);
}
if !security.record_action() {
return (
false,
"blocked by security policy: action budget exhausted".to_string(),
);
}
let name = job.name.clone().unwrap_or_else(|| "cron-job".to_string());
let prompt = job.prompt.clone().unwrap_or_default();
let prefixed_prompt = format!("[cron:{} {name}] {prompt}", job.id);
@ -653,13 +677,43 @@ mod tests {
let mut job = test_job("");
job.job_type = JobType::Agent;
job.prompt = Some("Say hello".into());
let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir);
let (success, output) = run_agent_job(&config, &job).await;
assert!(!success, "Agent job without provider key should fail");
assert!(
!output.is_empty(),
"Expected non-empty error output from failed agent job"
);
let (success, output) = run_agent_job(&config, &security, &job).await;
assert!(!success);
assert!(output.contains("agent job failed:"));
}
#[tokio::test]
async fn run_agent_job_blocks_readonly_mode() {
let tmp = TempDir::new().unwrap();
let mut config = test_config(&tmp);
config.autonomy.level = crate::security::AutonomyLevel::ReadOnly;
let mut job = test_job("");
job.job_type = JobType::Agent;
job.prompt = Some("Say hello".into());
let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir);
let (success, output) = run_agent_job(&config, &security, &job).await;
assert!(!success);
assert!(output.contains("blocked by security policy"));
assert!(output.contains("read-only"));
}
#[tokio::test]
async fn run_agent_job_blocks_rate_limited() {
let tmp = TempDir::new().unwrap();
let mut config = test_config(&tmp);
config.autonomy.max_actions_per_hour = 0;
let mut job = test_job("");
job.job_type = JobType::Agent;
job.prompt = Some("Say hello".into());
let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir);
let (success, output) = run_agent_job(&config, &security, &job).await;
assert!(!success);
assert!(output.contains("blocked by security policy"));
assert!(output.contains("rate limit exceeded"));
}
#[tokio::test]

View file

@ -39,41 +39,41 @@ use clap::Subcommand;
use serde::{Deserialize, Serialize};
pub mod agent;
pub mod approval;
pub mod auth;
pub(crate) mod approval;
pub(crate) mod auth;
pub mod channels;
pub mod config;
pub mod cost;
pub mod cron;
pub mod daemon;
pub mod doctor;
pub(crate) mod cost;
pub(crate) mod cron;
pub(crate) mod daemon;
pub(crate) mod doctor;
pub mod gateway;
pub mod hardware;
pub mod health;
pub mod heartbeat;
pub mod identity;
pub mod integrations;
pub(crate) mod hardware;
pub(crate) mod health;
pub(crate) mod heartbeat;
pub(crate) mod identity;
pub(crate) mod integrations;
pub mod memory;
pub mod migration;
pub mod multimodal;
pub(crate) mod migration;
pub(crate) mod multimodal;
pub mod observability;
pub mod onboard;
pub(crate) mod onboard;
pub mod peripherals;
pub mod providers;
pub mod rag;
pub mod runtime;
pub mod security;
pub mod service;
pub mod skills;
pub(crate) mod security;
pub(crate) mod service;
pub(crate) mod skills;
pub mod tools;
pub mod tunnel;
pub mod util;
pub(crate) mod tunnel;
pub(crate) mod util;
pub use config::Config;
/// Service management subcommands
#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ServiceCommands {
pub(crate) enum ServiceCommands {
/// Install daemon service unit for auto-start and restart
Install,
/// Start daemon service
@ -90,7 +90,7 @@ pub enum ServiceCommands {
/// Channel management subcommands
#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ChannelCommands {
pub(crate) enum ChannelCommands {
/// List all configured channels
List,
/// Start all configured channels (handled in main.rs for async)
@ -139,7 +139,7 @@ Examples:
/// Skills management subcommands
#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum SkillCommands {
pub(crate) enum SkillCommands {
/// List all installed skills
List,
/// Install a new skill from a git URL (HTTPS/SSH) or local path
@ -156,7 +156,7 @@ pub enum SkillCommands {
/// Migration subcommands
#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum MigrateCommands {
pub(crate) enum MigrateCommands {
/// Import memory from an `OpenClaw` workspace into this `ZeroClaw` workspace
Openclaw {
/// Optional path to `OpenClaw` workspace (defaults to ~/.openclaw/workspace)
@ -171,7 +171,7 @@ pub enum MigrateCommands {
/// Cron subcommands
#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum CronCommands {
pub(crate) enum CronCommands {
/// List all scheduled tasks
List,
/// Add a new scheduled task
@ -286,7 +286,7 @@ Examples:
/// Integration subcommands
#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum IntegrationCommands {
pub(crate) enum IntegrationCommands {
/// Show details about a specific integration
Info {
/// Integration name

View file

@ -215,9 +215,7 @@ fn generate_code() -> String {
/// on macOS). The 32 random bytes (256 bits) are hex-encoded for a
/// 64-character token, providing 256 bits of entropy.
fn generate_token() -> String {
use rand::RngCore;
let mut bytes = [0u8; 32];
rand::rng().fill_bytes(&mut bytes);
let bytes: [u8; 32] = rand::random();
format!("zc_{}", hex::encode(bytes))
}