venice-cli 0.83.0__tar.gz → 0.83.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. {venice_cli-0.83.0 → venice_cli-0.83.2}/PKG-INFO +22 -56
  2. {venice_cli-0.83.0 → venice_cli-0.83.2}/README.md +21 -55
  3. venice_cli-0.83.2/src/venice/__init__.py +1 -0
  4. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/client.py +6 -1
  5. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_agent.py +8 -62
  6. venice_cli-0.83.2/src/venice/commands/_browser.py +29 -0
  7. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_code.py +8 -10
  8. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_exec.py +1 -1
  9. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_index.py +1 -1
  10. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_mailbox.py +1 -1
  11. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_mcp.py +4 -68
  12. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_memory.py +1 -1
  13. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_repl.py +1 -1
  14. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_session.py +1 -1
  15. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_steer.py +1 -1
  16. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/chat.py +17 -14
  17. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/code.py +15 -15
  18. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/memory.py +1 -1
  19. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/secret.py +1 -1
  20. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/sessions.py +1 -1
  21. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/userconfig.py +6 -9
  22. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/_drive.py +1 -1
  23. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/_venice_fake_server.py +2 -2
  24. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_agent.py +10 -28
  25. venice_cli-0.83.2/tests/test_browser.py +51 -0
  26. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_chat.py +17 -0
  27. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_client.py +109 -2
  28. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_code.py +4 -15
  29. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_code_command.py +17 -0
  30. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_drive_cli.py +1 -1
  31. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_fake_server.py +1 -1
  32. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_mcp_tools.py +14 -62
  33. venice_cli-0.83.0/src/venice/__init__.py +0 -1
  34. venice_cli-0.83.0/src/venice/commands/_browser.py +0 -386
  35. venice_cli-0.83.0/tests/test_browser.py +0 -287
  36. {venice_cli-0.83.0 → venice_cli-0.83.2}/.gitignore +0 -0
  37. {venice_cli-0.83.0 → venice_cli-0.83.2}/CONTRIBUTING.md +0 -0
  38. {venice_cli-0.83.0 → venice_cli-0.83.2}/LICENSE +0 -0
  39. {venice_cli-0.83.0 → venice_cli-0.83.2}/Makefile +0 -0
  40. {venice_cli-0.83.0 → venice_cli-0.83.2}/SECURITY.md +0 -0
  41. {venice_cli-0.83.0 → venice_cli-0.83.2}/bin/venice +0 -0
  42. {venice_cli-0.83.0 → venice_cli-0.83.2}/install.sh +0 -0
  43. {venice_cli-0.83.0 → venice_cli-0.83.2}/pyproject.toml +0 -0
  44. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/__main__.py +0 -0
  45. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/audio_player.py +0 -0
  46. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/audio_post.py +0 -0
  47. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/auth.py +0 -0
  48. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/billing.py +0 -0
  49. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/cli.py +0 -0
  50. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/__init__.py +0 -0
  51. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_audio.py +0 -0
  52. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_compact.py +0 -0
  53. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_mcp_client.py +0 -0
  54. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_models.py +0 -0
  55. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_openai.py +0 -0
  56. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_persona.py +0 -0
  57. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_queue.py +0 -0
  58. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_review.py +0 -0
  59. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/_shared.py +0 -0
  60. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/balance.py +0 -0
  61. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/bg_remove.py +0 -0
  62. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/completion.py +0 -0
  63. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/config.py +0 -0
  64. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/contact_sheet.py +0 -0
  65. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/embed.py +0 -0
  66. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/image.py +0 -0
  67. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/image_edit.py +0 -0
  68. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/index.py +0 -0
  69. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/login.py +0 -0
  70. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/master.py +0 -0
  71. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/mcp_serve.py +0 -0
  72. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/models.py +0 -0
  73. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/music.py +0 -0
  74. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/review.py +0 -0
  75. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/search.py +0 -0
  76. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/sfx.py +0 -0
  77. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/tts.py +0 -0
  78. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/upscale.py +0 -0
  79. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/commands/video.py +0 -0
  80. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/config.py +0 -0
  81. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/image_montage.py +0 -0
  82. {venice_cli-0.83.0 → venice_cli-0.83.2}/src/venice/mcp_server.py +0 -0
  83. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/__init__.py +0 -0
  84. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/_hygiene.py +0 -0
  85. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/_mcp_fake_server.py +0 -0
  86. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_auth.py +0 -0
  87. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_balance.py +0 -0
  88. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_bg_remove.py +0 -0
  89. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_cli.py +0 -0
  90. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_compact.py +0 -0
  91. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_completion.py +0 -0
  92. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_config.py +0 -0
  93. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_contact_sheet.py +0 -0
  94. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_embed.py +0 -0
  95. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_exec.py +0 -0
  96. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_image.py +0 -0
  97. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_image_edit.py +0 -0
  98. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_index.py +0 -0
  99. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_login.py +0 -0
  100. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_mailbox.py +0 -0
  101. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_master.py +0 -0
  102. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_mcp_client.py +0 -0
  103. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_mcp_serve.py +0 -0
  104. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_memory.py +0 -0
  105. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_models.py +0 -0
  106. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_music.py +0 -0
  107. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_persona.py +0 -0
  108. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_planner.py +0 -0
  109. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_profiles.py +0 -0
  110. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_repl.py +0 -0
  111. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_review.py +0 -0
  112. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_search.py +0 -0
  113. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_secret.py +0 -0
  114. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_sessions.py +0 -0
  115. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_sfx.py +0 -0
  116. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_shared.py +0 -0
  117. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_shared_openai.py +0 -0
  118. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_source_hygiene.py +0 -0
  119. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_spawn.py +0 -0
  120. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_steer.py +0 -0
  121. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_tts.py +0 -0
  122. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_upscale.py +0 -0
  123. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_video.py +0 -0
  124. {venice_cli-0.83.0 → venice_cli-0.83.2}/tests/test_web_search.py +0 -0
  125. {venice_cli-0.83.0 → venice_cli-0.83.2}/uninstall.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: venice-cli
3
- Version: 0.83.0
3
+ Version: 0.83.2
4
4
  Summary: A stdlib-only Python CLI for the Venice.ai API: audio, video, images, chat, and embeddings.
5
5
  Project-URL: Homepage, https://github.com/gobha-me/venice-cli
6
6
  Project-URL: Repository, https://github.com/gobha-me/venice-cli
@@ -905,9 +905,9 @@ Details and safety:
905
905
  | `--shell-allow CMD` | allow only these commands for `--shell` (repeatable; adds to config `shell.allow`) |
906
906
  | `--shell-deny PATTERN` | refuse commands matching these globs (repeatable; adds to config `shell.deny`) |
907
907
  | `--shell-unrestricted` | acknowledge an empty allowlist under `--yes` (required for that combination) |
908
- | `--browser` | add `web_fetch` + `browser_capture` tools (fetch a URL / headless-render a page); implies `--tools` |
909
- | `--browser-allow HOST` | allow only these hosts for the browser tools (repeatable; adds to config `browser.allow`) |
910
- | `--browser-deny PATTERN` | refuse URLs whose host/URL matches these globs (repeatable; adds to config `browser.deny`) |
908
+ | `--browser` | reserved; temporarily exits 2 without network access while the browser rail is security-disabled |
909
+ | `--browser-allow HOST` | retained browser-policy config for compatibility; inert while `--browser` is disabled |
910
+ | `--browser-deny PATTERN` | retained browser-policy config for compatibility; inert while `--browser` is disabled |
911
911
  | `--memory` | add persistent memory + task tools (durable notes + a checklist); implies `--tools` (see [Memory & tasks](#memory--tasks---memory)) |
912
912
  | `--mcp NAME` | attach a registered external MCP server's tools (repeatable) |
913
913
  | `--no-mcp` | attach no MCP servers (overrides a configured default) |
@@ -948,66 +948,32 @@ venice config set shell.allow '["git", "gh", "ls", "cat"]'
948
948
 
949
949
  #### Web & browser tools (`--browser`)
950
950
 
951
- `--browser` adds two tools so the agent can look at the web — useful for verifying a page
952
- it (or you) just built. Implies `--tools`. Both are read-only and **not** spend-gated;
953
- the URL policy below is the guard.
954
-
955
- - **`web_fetch`** — a stdlib `urllib` GET that returns the page as text (tags stripped) or
956
- raw HTML (`mode=html`). Zero extra dependencies; good for non-JS "read this page."
957
- - **`browser_capture`** — headless-renders the page with a **Chromium-family browser**
958
- (`chromium`/`chrome`/`brave`) and returns the **post-JS DOM** (`mode=dom`/`text`) and/or
959
- a **screenshot PNG** (`mode=screenshot`/`both`, written to `--output` and returned as a
960
- path, never inline). Pass `assert_contains="…"` to deterministically check the rendered
961
- DOM contains a string — the robust "did the JS actually appear" check (beats eyeballing a
962
- screenshot). If only **Firefox** is installed it degrades to screenshot-only (its
963
- headless CLI can't dump the DOM); with no browser at all it reports "no headless browser
964
- available," like the `git` rail.
951
+ `web_fetch` and `browser_capture` are temporarily unavailable while
952
+ [GHSA-mqjr-2vh8-6fvg](https://github.com/sirkitree/venice/security/advisories/GHSA-mqjr-2vh8-6fvg)
953
+ is contained. In v0.83.2, passing `--browser` exits with status 2 before loading the SDK,
954
+ credentials, or any network client. The direct implementation seams also return an error,
955
+ and the agent does not advertise either tool.
965
956
 
966
- ```sh
967
- venice chat --browser "Fetch https://example.com and summarize it."
968
- venice chat --browser --browser-allow 'localhost' \
969
- "Open http://localhost:8123/ and confirm the game cards rendered (assert_contains)."
970
- ```
971
-
972
- **URL safety.** The scheme must be `http`/`https` and the cloud-metadata endpoint
973
- (`169.254.169.254`) is **always** blocked — these hard stops are not configurable. `file://`
974
- and everything non-http is refused. On top of that, an operator **allow/deny** policy (CLI
975
- flags add to a shared top-level `browser` section in [config](#config)) scopes which hosts
976
- are reachable:
977
-
978
- ```sh
979
- venice config set browser.deny '["*.internal", "10.*"]'
980
- venice config set browser.allow '["localhost", "*.example.com"]'
981
- ```
982
-
983
- - **Deny** globs match the URL host and the full URL, are always enforced, and win over
984
- allow. A non-empty **allow** list restricts to matching hosts; empty = any host (still
985
- subject to the hard stops + deny). `localhost` is reachable by default (the common case is
986
- verifying a locally-served page).
987
- - The headless browser runs untrusted page JS under an **allowlisted environment** (the
988
- Venice API key and other ambient tokens are dropped) and a throwaway profile dir.
989
- - DOM/text output and downloads are size-capped; tune with `defaults.browser.*`
990
- (`wait_ms`, `timeout`, `max_bytes`).
991
- - **Not exposed over `venice mcp-serve`** in this release (chat/code only), like
992
- `project_search`/`reindex`.
957
+ The `--browser-allow`, `--browser-deny`, and `browser.*` config keys remain accepted only
958
+ to preserve existing configuration. They do not enable network access. Restoring this rail
959
+ requires an enforced egress boundary that covers redirects, DNS rebinding, browser
960
+ subresources, and page scripts—not another URL-only filter.
993
961
 
994
962
  #### Web search (`--web-search`)
995
963
 
996
- `--browser` can only fetch a URL the agent **already knows**. `--web-search` closes the
997
- other half of the loop — **discovery**. It adds one tool, `venice_web_search`, so the coding
964
+ `--web-search` adds one discovery tool, `venice_web_search`, so the coding
998
965
  agent can look something up on the web when a fix needs documentation it doesn't have a link
999
966
  for (an API's docs, a library's usage, an error message).
1000
967
 
1001
968
  `venice_web_search(query)` makes **one** Venice web-search completion (server-side
1002
969
  `enable_web_search` + `enable_web_citations`, the same feature behind `venice chat
1003
970
  --web-search`) and returns a short **answer** plus the **cited URLs**. To then read a cited
1004
- page in full, follow up with `web_fetch` — so pair `--web-search` with `--browser`, and every
1005
- fetched URL stays under the `browser.*` allow/deny policy above. **Search discovers; the
1006
- browser policy still governs what gets read.**
971
+ page in full, open it outside Venice: the direct `web_fetch` follow-up rail is temporarily
972
+ disabled as described above.
1007
973
 
1008
974
  ```sh
1009
- # discover + read: search finds the doc, the browser fetches it (under browser.* policy)
1010
- venice code --web-search --browser --auto \
975
+ # discover: search returns an answer and its cited URLs
976
+ venice code --web-search --auto \
1011
977
  "The stripe SDK call is failing with an idempotency error — look up the fix and apply it."
1012
978
  ```
1013
979
 
@@ -1060,7 +1026,7 @@ venice memory tasks # the project checklist (--status filters)
1060
1026
  - **Locations:** project notes/tasks at `<root>/.venice/memory/` (git-ignored by default,
1061
1027
  like the semantic index); global notes at `~/.config/venice/memory/`
1062
1028
  (`$VENICE_MEMORY_DIR` overrides). All store files are `0600`.
1063
- - **Hygiene (CLAUDE.md):** a note **name** is refused if it's secret-shaped
1029
+ - **Hygiene (AGENTS.md):** a note **name** is refused if it's secret-shaped
1064
1030
  (`credentials`, `id_rsa*`, `*.key`, `.env`, `*secrets*`, …), so the store can't be used
1065
1031
  to label or stash a credential.
1066
1032
  - **Not exposed over `venice mcp-serve`** (chat/code only), like the browser rails.
@@ -1465,7 +1431,7 @@ jq '.usage.billed_total' ~/.config/venice/sessions/<id>.json
1465
1431
  | `run` | run a shell command (`/bin/sh -c`) at the **active** root | yes |
1466
1432
  | `attach_root` | register another directory as a project root (for work spanning repos) and, by default, switch the active root into it so relative paths and `run`/`git` follow — writes outside the writable roots fail loudly | no |
1467
1433
  | `venice_image` / `venice_image_edit` / `venice_sfx` / `venice_music` / `venice_tts` / `venice_upscale` / `venice_bg_remove` / `venice_video` | generate/edit images, audio & video into the project — **opt-in with `--assets`** | yes |
1468
- | `web_fetch` / `browser_capture` | fetch a URL (text/HTML) or headless-render a page (post-JS DOM + screenshot) to verify its own work — **opt-in with `--browser`** (see [Web & browser tools](#web--browser-tools---browser)) | no |
1434
+ | `web_fetch` / `browser_capture` | temporarily unavailable for security; not advertised to the agent (see [Web & browser tools](#web--browser-tools---browser)) | no |
1469
1435
  | `memory_write` / `memory_read` / `memory_search` / `memory_list` | durable notes the agent recalls across turns/sessions (two tiers: project + global) — **opt-in with `--memory`** (see [Memory & tasks](#memory--tasks---memory)) | no |
1470
1436
  | `task_add` / `task_update` / `task_list` | a project-only checklist the agent tracks (`pending`/`in_progress`/`done`) — **opt-in with `--memory`** | no |
1471
1437
  | `venice_scout` | delegate a read-only investigation to a disposable subagent with a fresh context; returns a structured report so exploration doesn't pollute the main context — **opt-in with `--scout`** (see [Scout subagent](#scout-subagent---scout)) | no |
@@ -1503,7 +1469,7 @@ it unrestricted (unchanged behavior).
1503
1469
  | `--max-tool-calls N` | cap tool invocations before forcing a final answer (default 25) |
1504
1470
  | `--exec-timeout SECS` | timeout for `run`/`git` (default 120) |
1505
1471
  | `--shell-allow CMD` / `--shell-deny PATTERN` | scope the `run` tool with the shared allow/deny policy (repeatable; adds to config `shell.*`) |
1506
- | `--browser` / `--browser-allow HOST` / `--browser-deny PATTERN` | expose `web_fetch` + `browser_capture` so the agent can verify a rendered page; scope hosts with the `browser.*` allow/deny policy (see [Web & browser tools](#web--browser-tools---browser)) |
1472
+ | `--browser` / `--browser-allow HOST` / `--browser-deny PATTERN` | compatibility flags for the security-disabled browser rail; `--browser` exits 2 without network access (see [Web & browser tools](#web--browser-tools---browser)) |
1507
1473
  | `--assets` | also expose the in-process asset-generation tools (image / image-edit / sfx / music / tts / upscale / bg-remove / video) so the agent can create images, audio & video in the project; paid — each confirms per call unless `--auto` |
1508
1474
  | `--scout` | expose `venice_scout`: delegate a read-only investigation to a disposable subagent with a fresh context; keeps exploration out of the main context (see [Scout subagent](#scout-subagent---scout)) |
1509
1475
  | `--spawn` | expose `venice_spawn`: delegate a bounded **write/paid** task to a disposable **worker** subagent with a fresh context and a role-scoped subset of your tools; edit churn stays quarantined and it returns a structured report to merge (see [Worker subagent](#worker-subagent---spawn)) |
@@ -1511,7 +1477,7 @@ it unrestricted (unchanged behavior).
1511
1477
  | `--subagent-max-tokens N` | per-subagent cap on the cumulative prompt+completion **tokens** a `venice_scout` **or** `venice_spawn` subagent spends across its turns (default **off**; `<= 0` disables); once crossed the subagent is asked to wrap up and its report carries the token count. A cumulative-usage ceiling, **not** a context-window size limit, and distinct from `--max-tokens` (per-turn output); config `defaults.code.subagent_max_tokens` |
1512
1478
  | `--planner` | planner harness: implies `--scout --spawn --memory`, mandates the decompose → dispatch → track → **merge** protocol, and adds `venice_merge` — a consolidated rollup of every dispatch (see [Planner harness](#planner-harness---planner)) |
1513
1479
  | `--parallel` | dispatch **independent** `venice_scout`/`venice_spawn` subagents **concurrently** (bounded pool) instead of one at a time, so a planner's independent units overlap in wall-clock; opt-in, serial otherwise; config `defaults.code.parallel` (see [Parallel dispatch](#parallel-dispatch---parallel)) |
1514
- | `--web-search` / `--web-search-model MODEL` | expose `venice_web_search` so the agent can **discover** documentation on the web (answer + cited URLs); pairs with `--browser` to then read a cited page under the `browser.*` policy (see [Web search](#web-search---web-search)) |
1480
+ | `--web-search` / `--web-search-model MODEL` | expose `venice_web_search` so the agent can **discover** documentation on the web (answer + cited URLs; see [Web search](#web-search---web-search)) |
1515
1481
  | `--review` | expose `venice_review`: hand the current diff to a **cold-context reviewer** (fresh context, read-only tools, a different model where one exists) and fix what it reports before handing work back. Capped at **3 reviews per session**. Findings only — a review is not a merge gate (see [Reviewer rail](#reviewer-rail---review)) |
1516
1482
  | `--review-model MODEL` | model for `--review` (default: a function-calling model from a **different family** than the coding model; falls back to the coding model with a warning); config `defaults.code.review_model` |
1517
1483
  | `--review-rounds N` | passes `venice_review` makes over the same diff (default **1**, max 3); config `defaults.code.review_rounds` |
@@ -865,9 +865,9 @@ Details and safety:
865
865
  | `--shell-allow CMD` | allow only these commands for `--shell` (repeatable; adds to config `shell.allow`) |
866
866
  | `--shell-deny PATTERN` | refuse commands matching these globs (repeatable; adds to config `shell.deny`) |
867
867
  | `--shell-unrestricted` | acknowledge an empty allowlist under `--yes` (required for that combination) |
868
- | `--browser` | add `web_fetch` + `browser_capture` tools (fetch a URL / headless-render a page); implies `--tools` |
869
- | `--browser-allow HOST` | allow only these hosts for the browser tools (repeatable; adds to config `browser.allow`) |
870
- | `--browser-deny PATTERN` | refuse URLs whose host/URL matches these globs (repeatable; adds to config `browser.deny`) |
868
+ | `--browser` | reserved; temporarily exits 2 without network access while the browser rail is security-disabled |
869
+ | `--browser-allow HOST` | retained browser-policy config for compatibility; inert while `--browser` is disabled |
870
+ | `--browser-deny PATTERN` | retained browser-policy config for compatibility; inert while `--browser` is disabled |
871
871
  | `--memory` | add persistent memory + task tools (durable notes + a checklist); implies `--tools` (see [Memory & tasks](#memory--tasks---memory)) |
872
872
  | `--mcp NAME` | attach a registered external MCP server's tools (repeatable) |
873
873
  | `--no-mcp` | attach no MCP servers (overrides a configured default) |
@@ -908,66 +908,32 @@ venice config set shell.allow '["git", "gh", "ls", "cat"]'
908
908
 
909
909
  #### Web & browser tools (`--browser`)
910
910
 
911
- `--browser` adds two tools so the agent can look at the web — useful for verifying a page
912
- it (or you) just built. Implies `--tools`. Both are read-only and **not** spend-gated;
913
- the URL policy below is the guard.
914
-
915
- - **`web_fetch`** — a stdlib `urllib` GET that returns the page as text (tags stripped) or
916
- raw HTML (`mode=html`). Zero extra dependencies; good for non-JS "read this page."
917
- - **`browser_capture`** — headless-renders the page with a **Chromium-family browser**
918
- (`chromium`/`chrome`/`brave`) and returns the **post-JS DOM** (`mode=dom`/`text`) and/or
919
- a **screenshot PNG** (`mode=screenshot`/`both`, written to `--output` and returned as a
920
- path, never inline). Pass `assert_contains="…"` to deterministically check the rendered
921
- DOM contains a string — the robust "did the JS actually appear" check (beats eyeballing a
922
- screenshot). If only **Firefox** is installed it degrades to screenshot-only (its
923
- headless CLI can't dump the DOM); with no browser at all it reports "no headless browser
924
- available," like the `git` rail.
911
+ `web_fetch` and `browser_capture` are temporarily unavailable while
912
+ [GHSA-mqjr-2vh8-6fvg](https://github.com/sirkitree/venice/security/advisories/GHSA-mqjr-2vh8-6fvg)
913
+ is contained. In v0.83.2, passing `--browser` exits with status 2 before loading the SDK,
914
+ credentials, or any network client. The direct implementation seams also return an error,
915
+ and the agent does not advertise either tool.
925
916
 
926
- ```sh
927
- venice chat --browser "Fetch https://example.com and summarize it."
928
- venice chat --browser --browser-allow 'localhost' \
929
- "Open http://localhost:8123/ and confirm the game cards rendered (assert_contains)."
930
- ```
931
-
932
- **URL safety.** The scheme must be `http`/`https` and the cloud-metadata endpoint
933
- (`169.254.169.254`) is **always** blocked — these hard stops are not configurable. `file://`
934
- and everything non-http is refused. On top of that, an operator **allow/deny** policy (CLI
935
- flags add to a shared top-level `browser` section in [config](#config)) scopes which hosts
936
- are reachable:
937
-
938
- ```sh
939
- venice config set browser.deny '["*.internal", "10.*"]'
940
- venice config set browser.allow '["localhost", "*.example.com"]'
941
- ```
942
-
943
- - **Deny** globs match the URL host and the full URL, are always enforced, and win over
944
- allow. A non-empty **allow** list restricts to matching hosts; empty = any host (still
945
- subject to the hard stops + deny). `localhost` is reachable by default (the common case is
946
- verifying a locally-served page).
947
- - The headless browser runs untrusted page JS under an **allowlisted environment** (the
948
- Venice API key and other ambient tokens are dropped) and a throwaway profile dir.
949
- - DOM/text output and downloads are size-capped; tune with `defaults.browser.*`
950
- (`wait_ms`, `timeout`, `max_bytes`).
951
- - **Not exposed over `venice mcp-serve`** in this release (chat/code only), like
952
- `project_search`/`reindex`.
917
+ The `--browser-allow`, `--browser-deny`, and `browser.*` config keys remain accepted only
918
+ to preserve existing configuration. They do not enable network access. Restoring this rail
919
+ requires an enforced egress boundary that covers redirects, DNS rebinding, browser
920
+ subresources, and page scripts—not another URL-only filter.
953
921
 
954
922
  #### Web search (`--web-search`)
955
923
 
956
- `--browser` can only fetch a URL the agent **already knows**. `--web-search` closes the
957
- other half of the loop — **discovery**. It adds one tool, `venice_web_search`, so the coding
924
+ `--web-search` adds one discovery tool, `venice_web_search`, so the coding
958
925
  agent can look something up on the web when a fix needs documentation it doesn't have a link
959
926
  for (an API's docs, a library's usage, an error message).
960
927
 
961
928
  `venice_web_search(query)` makes **one** Venice web-search completion (server-side
962
929
  `enable_web_search` + `enable_web_citations`, the same feature behind `venice chat
963
930
  --web-search`) and returns a short **answer** plus the **cited URLs**. To then read a cited
964
- page in full, follow up with `web_fetch` — so pair `--web-search` with `--browser`, and every
965
- fetched URL stays under the `browser.*` allow/deny policy above. **Search discovers; the
966
- browser policy still governs what gets read.**
931
+ page in full, open it outside Venice: the direct `web_fetch` follow-up rail is temporarily
932
+ disabled as described above.
967
933
 
968
934
  ```sh
969
- # discover + read: search finds the doc, the browser fetches it (under browser.* policy)
970
- venice code --web-search --browser --auto \
935
+ # discover: search returns an answer and its cited URLs
936
+ venice code --web-search --auto \
971
937
  "The stripe SDK call is failing with an idempotency error — look up the fix and apply it."
972
938
  ```
973
939
 
@@ -1020,7 +986,7 @@ venice memory tasks # the project checklist (--status filters)
1020
986
  - **Locations:** project notes/tasks at `<root>/.venice/memory/` (git-ignored by default,
1021
987
  like the semantic index); global notes at `~/.config/venice/memory/`
1022
988
  (`$VENICE_MEMORY_DIR` overrides). All store files are `0600`.
1023
- - **Hygiene (CLAUDE.md):** a note **name** is refused if it's secret-shaped
989
+ - **Hygiene (AGENTS.md):** a note **name** is refused if it's secret-shaped
1024
990
  (`credentials`, `id_rsa*`, `*.key`, `.env`, `*secrets*`, …), so the store can't be used
1025
991
  to label or stash a credential.
1026
992
  - **Not exposed over `venice mcp-serve`** (chat/code only), like the browser rails.
@@ -1425,7 +1391,7 @@ jq '.usage.billed_total' ~/.config/venice/sessions/<id>.json
1425
1391
  | `run` | run a shell command (`/bin/sh -c`) at the **active** root | yes |
1426
1392
  | `attach_root` | register another directory as a project root (for work spanning repos) and, by default, switch the active root into it so relative paths and `run`/`git` follow — writes outside the writable roots fail loudly | no |
1427
1393
  | `venice_image` / `venice_image_edit` / `venice_sfx` / `venice_music` / `venice_tts` / `venice_upscale` / `venice_bg_remove` / `venice_video` | generate/edit images, audio & video into the project — **opt-in with `--assets`** | yes |
1428
- | `web_fetch` / `browser_capture` | fetch a URL (text/HTML) or headless-render a page (post-JS DOM + screenshot) to verify its own work — **opt-in with `--browser`** (see [Web & browser tools](#web--browser-tools---browser)) | no |
1394
+ | `web_fetch` / `browser_capture` | temporarily unavailable for security; not advertised to the agent (see [Web & browser tools](#web--browser-tools---browser)) | no |
1429
1395
  | `memory_write` / `memory_read` / `memory_search` / `memory_list` | durable notes the agent recalls across turns/sessions (two tiers: project + global) — **opt-in with `--memory`** (see [Memory & tasks](#memory--tasks---memory)) | no |
1430
1396
  | `task_add` / `task_update` / `task_list` | a project-only checklist the agent tracks (`pending`/`in_progress`/`done`) — **opt-in with `--memory`** | no |
1431
1397
  | `venice_scout` | delegate a read-only investigation to a disposable subagent with a fresh context; returns a structured report so exploration doesn't pollute the main context — **opt-in with `--scout`** (see [Scout subagent](#scout-subagent---scout)) | no |
@@ -1463,7 +1429,7 @@ it unrestricted (unchanged behavior).
1463
1429
  | `--max-tool-calls N` | cap tool invocations before forcing a final answer (default 25) |
1464
1430
  | `--exec-timeout SECS` | timeout for `run`/`git` (default 120) |
1465
1431
  | `--shell-allow CMD` / `--shell-deny PATTERN` | scope the `run` tool with the shared allow/deny policy (repeatable; adds to config `shell.*`) |
1466
- | `--browser` / `--browser-allow HOST` / `--browser-deny PATTERN` | expose `web_fetch` + `browser_capture` so the agent can verify a rendered page; scope hosts with the `browser.*` allow/deny policy (see [Web & browser tools](#web--browser-tools---browser)) |
1432
+ | `--browser` / `--browser-allow HOST` / `--browser-deny PATTERN` | compatibility flags for the security-disabled browser rail; `--browser` exits 2 without network access (see [Web & browser tools](#web--browser-tools---browser)) |
1467
1433
  | `--assets` | also expose the in-process asset-generation tools (image / image-edit / sfx / music / tts / upscale / bg-remove / video) so the agent can create images, audio & video in the project; paid — each confirms per call unless `--auto` |
1468
1434
  | `--scout` | expose `venice_scout`: delegate a read-only investigation to a disposable subagent with a fresh context; keeps exploration out of the main context (see [Scout subagent](#scout-subagent---scout)) |
1469
1435
  | `--spawn` | expose `venice_spawn`: delegate a bounded **write/paid** task to a disposable **worker** subagent with a fresh context and a role-scoped subset of your tools; edit churn stays quarantined and it returns a structured report to merge (see [Worker subagent](#worker-subagent---spawn)) |
@@ -1471,7 +1437,7 @@ it unrestricted (unchanged behavior).
1471
1437
  | `--subagent-max-tokens N` | per-subagent cap on the cumulative prompt+completion **tokens** a `venice_scout` **or** `venice_spawn` subagent spends across its turns (default **off**; `<= 0` disables); once crossed the subagent is asked to wrap up and its report carries the token count. A cumulative-usage ceiling, **not** a context-window size limit, and distinct from `--max-tokens` (per-turn output); config `defaults.code.subagent_max_tokens` |
1472
1438
  | `--planner` | planner harness: implies `--scout --spawn --memory`, mandates the decompose → dispatch → track → **merge** protocol, and adds `venice_merge` — a consolidated rollup of every dispatch (see [Planner harness](#planner-harness---planner)) |
1473
1439
  | `--parallel` | dispatch **independent** `venice_scout`/`venice_spawn` subagents **concurrently** (bounded pool) instead of one at a time, so a planner's independent units overlap in wall-clock; opt-in, serial otherwise; config `defaults.code.parallel` (see [Parallel dispatch](#parallel-dispatch---parallel)) |
1474
- | `--web-search` / `--web-search-model MODEL` | expose `venice_web_search` so the agent can **discover** documentation on the web (answer + cited URLs); pairs with `--browser` to then read a cited page under the `browser.*` policy (see [Web search](#web-search---web-search)) |
1440
+ | `--web-search` / `--web-search-model MODEL` | expose `venice_web_search` so the agent can **discover** documentation on the web (answer + cited URLs; see [Web search](#web-search---web-search)) |
1475
1441
  | `--review` | expose `venice_review`: hand the current diff to a **cold-context reviewer** (fresh context, read-only tools, a different model where one exists) and fix what it reports before handing work back. Capped at **3 reviews per session**. Findings only — a review is not a merge gate (see [Reviewer rail](#reviewer-rail---review)) |
1476
1442
  | `--review-model MODEL` | model for `--review` (default: a function-calling model from a **different family** than the coding model; falls back to the coding model with a warning); config `defaults.code.review_model` |
1477
1443
  | `--review-rounds N` | passes `venice_review` makes over the same diff (default **1**, max 3); config `defaults.code.review_rounds` |
@@ -0,0 +1 @@
1
+ __version__ = "0.83.2"
@@ -70,7 +70,6 @@ class VeniceClient:
70
70
  url += "?" + urllib.parse.urlencode(params, doseq=True)
71
71
 
72
72
  headers = {
73
- "Authorization": f"Bearer {self.api_key}",
74
73
  "Accept": "application/json, audio/*, image/*, video/*",
75
74
  "User-Agent": self.user_agent,
76
75
  }
@@ -80,6 +79,12 @@ class VeniceClient:
80
79
  headers["Content-Type"] = "application/json"
81
80
 
82
81
  req = urllib.request.Request(url, data=data, headers=headers, method=method)
82
+ # Bind the credential to this request. urllib copies regular headers
83
+ # when it constructs a redirected request, but deliberately leaves
84
+ # unredirected headers behind.
85
+ req.add_unredirected_header(
86
+ "Authorization", f"Bearer {self.api_key}"
87
+ )
83
88
  try:
84
89
  with urllib.request.urlopen(req, timeout=self.timeout) as resp:
85
90
  body = resp.read()
@@ -1695,8 +1695,8 @@ def dispatch_map(tools: List[Tool]) -> Dict[str, Tool]:
1695
1695
 
1696
1696
  # --------------------------------------------------------------------------- #
1697
1697
  # Web search (#77): one server-side Venice completion with `enable_web_search`, so
1698
- # the coding agent / scout can DISCOVER documentation -- not just fetch a URL it
1699
- # already knows (the `--browser` rail, #71). Rides the normal completion path (same
1698
+ # the coding agent / scout can DISCOVER documentation and return citations. Rides the
1699
+ # normal completion path (same
1700
1700
  # key, same billing), so the per-agent tool-call budget bounds it. The `venice_web_search`
1701
1701
  # rail Tool wrapper + `supportsWebSearch` model resolution live in `_code`; this module
1702
1702
  # owns only the profile-agnostic completion helper.
@@ -2435,67 +2435,13 @@ def _tool_section(name: str) -> str:
2435
2435
  return name[len("venice_"):] if name.startswith("venice_") else name
2436
2436
 
2437
2437
 
2438
- def _browser_args(arguments) -> dict:
2439
- """Model-supplied browser-tool args with policy/loop-controlled keys stripped: the
2440
- model must not set `allow`/`deny` (widen its URL policy) or the loop-controlled keys."""
2441
- return {k: v for k, v in _clean(arguments).items() if k not in ("allow", "deny")}
2442
-
2443
-
2444
2438
  def browser_tools(*, allow=(), deny=(), output_dir=None, config=None) -> List[Tool]:
2445
- """The `web_fetch` + `browser_capture` rails (issue #71).
2439
+ """Return no browser tools while the browser rail is security-disabled.
2446
2440
 
2447
- The URL allow/deny policy is bound HERE (from the operator's config/flags), so the
2448
- model can't widen it via tool arguments -- same discipline as the `shell` rail. Safe
2449
- knobs still honor `defaults.browser.*` (#58), layered under the model's arguments.
2450
- Both tools are free (no spend gate) and never require confirmation; the URL policy is
2451
- the guard.
2441
+ Keep this compatibility seam so callers cannot accidentally re-expose the direct
2442
+ tool implementations while GHSA-mqjr-2vh8-6fvg is contained.
2452
2443
  """
2453
- fetch_defaults = userconfig.config_defaults_for("browser", _mcp.web_fetch_tool, config)
2454
- cap_defaults = userconfig.config_defaults_for("browser", _mcp.browser_capture_tool, config)
2455
-
2456
- def _web_fetch_invoke(arguments, *, confirm: bool = False):
2457
- return _mcp.web_fetch_tool(
2458
- allow=allow, deny=deny, **{**fetch_defaults, **_browser_args(arguments)})
2459
-
2460
- def _browser_capture_invoke(arguments, *, confirm: bool = False):
2461
- return _mcp.browser_capture_tool(
2462
- allow=allow, deny=deny, output_dir=output_dir,
2463
- **{**cap_defaults, **_browser_args(arguments)})
2464
-
2465
- return [
2466
- Tool(
2467
- name="web_fetch",
2468
- description=(
2469
- "Fetch an http(s) URL with stdlib urllib and return its text (mode=text, "
2470
- "default) or raw HTML (mode=html). Zero-dep; good for non-SPA pages. For "
2471
- "JS-rendered pages use browser_capture. Read-only; not spend-gated. "
2472
- "file://, the cloud metadata endpoint, and any host the operator denies "
2473
- "are refused."
2474
- ),
2475
- parameters=_WEB_FETCH_SCHEMA,
2476
- invoke=_web_fetch_invoke,
2477
- paid=False,
2478
- category="web",
2479
- tags=("read", "network"),
2480
- ),
2481
- Tool(
2482
- name="browser_capture",
2483
- description=(
2484
- "Headless-render an http(s) URL and return the post-JS DOM (mode=dom/text) "
2485
- "and/or a screenshot PNG path (mode=screenshot/both) -- use it to verify a "
2486
- "page's JS-injected content actually appeared. Pass assert_contains to "
2487
- "check the DOM contains a substring (deterministic). DOM modes need a "
2488
- "Chromium-family browser (Firefox is screenshot-only); reports 'no "
2489
- "headless browser available' when none is installed. Read-only; not "
2490
- "spend-gated."
2491
- ),
2492
- parameters=_BROWSER_CAPTURE_SCHEMA,
2493
- invoke=_browser_capture_invoke,
2494
- paid=False,
2495
- category="web",
2496
- tags=("read", "network"),
2497
- ),
2498
- ]
2444
+ return []
2499
2445
 
2500
2446
 
2501
2447
  def memory_tools() -> List[Tool]:
@@ -2646,8 +2592,8 @@ def builtin_tools(
2646
2592
  rail, not a venice API tool, so it isn't part of the selectable `_BUILTINS`
2647
2593
  set) and is never exposed via `mcp-serve`, which builds its own wrappers.
2648
2594
 
2649
- `browser` (issue #71) likewise appends the `web_fetch`/`browser_capture` rails,
2650
- scoped by the `browser_allow`/`browser_deny` URL policy (see `browser_tools`).
2595
+ `browser` (issue #71) is retained as a compatibility argument but appends no tools
2596
+ while GHSA-mqjr-2vh8-6fvg is contained (see `browser_tools`).
2651
2597
 
2652
2598
  `memory` (issue #49) appends the persistent memory + task rails (`memory_tools`):
2653
2599
  free, local notes (two tiers) + a project task list. Also a rail (added after the
@@ -0,0 +1,29 @@
1
+ """Fail-closed browser rail containment.
2
+
3
+ The former implementation launched a headless browser after checking only the
4
+ initial URL. Redirects, DNS resolution, subresources, and page-script requests
5
+ then escaped that policy boundary. Until the rail has an enforced egress
6
+ boundary, keep every entry point unavailable and dependency-free.
7
+ """
8
+ from __future__ import annotations
9
+
10
+
11
+ UNAVAILABLE_MESSAGE = (
12
+ "web and browser tools are temporarily disabled for security; "
13
+ "see GHSA-mqjr-2vh8-6fvg"
14
+ )
15
+
16
+
17
+ def web_fetch(
18
+ url, *, mode="text", max_bytes=None, timeout=None, allow=(), deny=()
19
+ ) -> dict:
20
+ """Fail closed without parsing, resolving, or opening ``url``."""
21
+ return {"ok": False, "error": UNAVAILABLE_MESSAGE}
22
+
23
+
24
+ def capture(
25
+ url, *, out_path=None, mode="dom", wait_ms=None, window=None,
26
+ timeout=None, assert_contains=None, allow=(), deny=(),
27
+ ) -> dict:
28
+ """Fail closed without probing or launching a browser."""
29
+ return {"ok": False, "error": UNAVAILABLE_MESSAGE}
@@ -812,8 +812,8 @@ def code_tools(
812
812
  Empty lists leave `run` unrestricted (only the confirm gate), preserving the prior
813
813
  behavior of autonomous `venice code` runs.
814
814
 
815
- `browser` (#71) appends the `web_fetch`/`browser_capture` rails, scoped by the
816
- `browser_allow`/`browser_deny` URL policy (see `_agent.browser_tools`).
815
+ `browser` (#71) is retained for call compatibility but appends no tools while
816
+ GHSA-mqjr-2vh8-6fvg is contained (see `_agent.browser_tools`).
817
817
 
818
818
  `memory` (#49) appends the persistent memory + task rails (`_agent.memory_tools`):
819
819
  free, local notes (two tiers) + a project task list the agent maintains across
@@ -956,8 +956,8 @@ def code_tools(
956
956
  ))
957
957
 
958
958
  if browser:
959
- # web_fetch/browser_capture rails (#71): no Venice API, so no client needed.
960
- # Screenshots land in $VENICE_MCP_OUTPUT_DIR or under the project root.
959
+ # Compatibility seam only: browser_tools returns [] while the rail is disabled
960
+ # for GHSA-mqjr-2vh8-6fvg.
961
961
  tools.extend(_agent.browser_tools(
962
962
  allow=browser_allow, deny=browser_deny,
963
963
  output_dir=os.environ.get("VENICE_MCP_OUTPUT_DIR") or root,
@@ -1135,9 +1135,8 @@ def web_search_tool(oai, model: str, *, models=None, search_model=None,
1135
1135
 
1136
1136
  Makes one Venice web-search completion (via `_agent.run_web_search`) against a
1137
1137
  `supportsWebSearch` model and returns the answer plus the cited URLs. The agent
1138
- follows a citation with `web_fetch` (the `--browser` rail, #71), which keeps every
1139
- fetched URL under the operator's `browser.*` policy -- search discovers, the browser
1140
- policy governs what gets read.
1138
+ returns citations for the operator to inspect. The former direct `web_fetch` follow-up
1139
+ rail is disabled while GHSA-mqjr-2vh8-6fvg is contained.
1141
1140
 
1142
1141
  `paid=False` mirrors `venice_chat` (a bounded, billed sub-completion, not a media
1143
1142
  purchase): it MUST be free so a read-only SCOUT can carry it (`_agent.run_scout`
@@ -1181,9 +1180,8 @@ def web_search_tool(oai, model: str, *, models=None, search_model=None,
1181
1180
  _agent.WEB_SEARCH_TOOL_NAME,
1182
1181
  "Search the web for documentation or answers and get back a short summary plus "
1183
1182
  "the source URLs it cited. Use it to DISCOVER pages you do not already have a URL "
1184
- "for (API docs, library usage, an error message). To read a cited page in full, "
1185
- "follow up with web_fetch (needs --browser). Read-only; cannot edit files or run "
1186
- "commands.",
1183
+ "for (API docs, library usage, an error message). Read-only; cannot edit files "
1184
+ "or run commands.",
1187
1185
  _agent._WEB_SEARCH_SCHEMA, invoke, paid=False,
1188
1186
  category="web", tags=("read", "network"),
1189
1187
  )
@@ -37,7 +37,7 @@ from typing import Optional
37
37
  MAX_OUTPUT_CHARS = 20_000 # cap per stdout/stderr stream
38
38
  DEFAULT_EXEC_TIMEOUT = 120 # seconds
39
39
 
40
- # Secrets never inherited into an exec'd child (CLAUDE.md credential hygiene).
40
+ # Secrets never inherited into an exec'd child (AGENTS.md credential hygiene).
41
41
  _SECRET_ENV = ("VENICE_API_KEY", "VENICE_EMBED_API_KEY")
42
42
 
43
43
  # Substrings that turn `/bin/sh -c` into more than a single simple command. When an
@@ -61,7 +61,7 @@ _DIR_DENYLIST = frozenset({
61
61
  "env", ".idea", ".vscode", "dist", "build", ".egg-info",
62
62
  })
63
63
 
64
- # Exact filenames never indexed (credential/secret-shaped -- CLAUDE.md invariant).
64
+ # Exact filenames never indexed (credential/secret-shaped -- AGENTS.md invariant).
65
65
  _SECRET_NAMES = frozenset({"credentials", ".env", ".netrc", ".pgpass"})
66
66
  # Glob patterns (matched on basename) never indexed.
67
67
  _SECRET_GLOBS = (
@@ -18,7 +18,7 @@ channel):
18
18
  * Consumed in ``(mtime, name)`` order and deleted on read; a queued message
19
19
  survives an agent restart -- ``--resume`` drains it on the loop's first turn.
20
20
 
21
- Trust model (CLAUDE.md): a steer is *additive user input* at the same trust level
21
+ Trust model (AGENTS.md): a steer is *additive user input* at the same trust level
22
22
  as the original task author. The mailbox is a local, owner-only directory, NOT a
23
23
  remote-control channel; nothing here reads or writes the API key.
24
24
  """
@@ -1205,32 +1205,8 @@ def web_fetch_tool(
1205
1205
  url, *, mode: str = "text", max_bytes: Optional[int] = None,
1206
1206
  timeout: Optional[int] = None, allow=(), deny=(),
1207
1207
  ) -> dict:
1208
- """Fetch an http(s) URL with stdlib urllib and return its text or raw HTML (issue #71).
1209
-
1210
- Read-only, no browser, zero extra deps -- good for non-SPA "read this page". For
1211
- JS-rendered pages use `browser_capture`. Not spend-gated. URL safety: only http/https,
1212
- the cloud metadata endpoint is always blocked, redirects are re-checked against the
1213
- policy, and the operator's browser allow/deny globs apply. `allow`/`deny` are supplied
1214
- by the tool wiring (bound from config), never by the model.
1215
- """
1216
- if not url or not str(url).strip():
1217
- return _err("web_fetch: url is required")
1218
- res = _browser.web_fetch(str(url), mode=(mode or "text"), max_bytes=max_bytes,
1219
- timeout=timeout, allow=allow, deny=deny)
1220
- if not res.get("ok"):
1221
- return _err(f"web_fetch: {res.get('error', 'failed')}")
1222
- out = {
1223
- "status": "ok",
1224
- "url": str(url),
1225
- "final_url": res.get("final_url"),
1226
- "content_type": res.get("content_type"),
1227
- "truncated": res.get("truncated", False),
1228
- }
1229
- if "text" in res:
1230
- out["text"] = res["text"]
1231
- if "html" in res:
1232
- out["html"] = res["html"]
1233
- return out
1208
+ """Fail closed while the browser rail has no enforced egress boundary."""
1209
+ return _err(f"web_fetch: {_browser.UNAVAILABLE_MESSAGE}")
1234
1210
 
1235
1211
 
1236
1212
  def browser_capture_tool(
@@ -1238,48 +1214,8 @@ def browser_capture_tool(
1238
1214
  assert_contains: Optional[str] = None, output_dir: Optional[str] = None,
1239
1215
  allow=(), deny=(),
1240
1216
  ) -> dict:
1241
- """Headless-render an http(s) URL: post-JS DOM and/or a screenshot PNG (issue #71).
1242
-
1243
- Chromium-family browsers (chromium/chrome/brave) give a post-JS DOM (mode `dom`/`text`)
1244
- and/or a screenshot; Firefox is a screenshot-only fallback. Writes any screenshot to the
1245
- output dir and returns its PATH (never an inline blob, like `venice_image`); returns the
1246
- DOM inline (capped). Pass `assert_contains` to deterministically check the rendered DOM
1247
- contains a substring -- the robust "did the JS land" check (beats eyeballing a shot).
1248
- Degrades gracefully ("no headless browser available") when no browser is installed. Not
1249
- spend-gated. URL safety mirrors `web_fetch`; `allow`/`deny` come from the wiring, not the
1250
- model.
1251
- """
1252
- if not url or not str(url).strip():
1253
- return _err("browser_capture: url is required")
1254
- m = mode or "dom"
1255
- out_path = None
1256
- if m in ("screenshot", "both"):
1257
- out_dir = resolve_output_dir(output_dir)
1258
- try:
1259
- out_dir.mkdir(parents=True, exist_ok=True)
1260
- except OSError as e:
1261
- return _err(f"browser_capture: cannot create output dir {out_dir}: {e}")
1262
- out_path = str(out_dir / _browser.capture_filename(url))
1263
- res = _browser.capture(str(url), out_path=out_path, mode=m, wait_ms=wait_ms,
1264
- timeout=timeout, assert_contains=assert_contains,
1265
- allow=allow, deny=deny)
1266
- if not res.get("ok"):
1267
- return _err(f"browser_capture: {res.get('error', 'failed')}")
1268
- out = {"status": "ok", "url": str(url), "mode": m,
1269
- "browser": res.get("browser"), "family": res.get("family")}
1270
- if res.get("screenshot_path"):
1271
- out["screenshot_path"] = res["screenshot_path"]
1272
- if "dom" in res:
1273
- out["dom"] = res["dom"]
1274
- out["truncated"] = res.get("truncated", False)
1275
- if "contains" in res:
1276
- out["assert_contains"] = res.get("assert_contains")
1277
- out["contains"] = res["contains"]
1278
- elif assert_contains is not None:
1279
- out["assert_contains"] = str(assert_contains)
1280
- out["contains"] = None
1281
- out["note"] = "assert_contains needs a DOM (mode dom/text/both on a Chromium browser)"
1282
- return out
1217
+ """Fail closed while the browser rail has no enforced egress boundary."""
1218
+ return _err(f"browser_capture: {_browser.UNAVAILABLE_MESSAGE}")
1283
1219
 
1284
1220
 
1285
1221
  def models_tool(client, *, type: str) -> dict:
@@ -28,7 +28,7 @@ file. The lock is **in-process only**: two separate ``venice`` processes writing
28
28
  store remain last-writer-wins (like ``secrets.json`` / ``sessions`` / the index) -- guard
29
29
  that with per-entry files or an OS lock if it ever becomes real; callers need not change.
30
30
 
31
- Hygiene (CLAUDE.md): an entry NAME is validated like a persona/secret name (no
31
+ Hygiene (AGENTS.md): an entry NAME is validated like a persona/secret name (no
32
32
  traversal) and refused if secret-shaped (``is_secret_path``) so the store can't be
33
33
  used to label/stash credentials; the name is only ever a dict key, so there is no
34
34
  filesystem-path surface. The store lives under ``.venice/`` -- already pruned by
@@ -17,7 +17,7 @@ Slash-commands (minimal set): ``/system`` ``/model`` ``/reset`` ``/save``
17
17
  ``/exit`` (plus ``/help`` and the ``/quit`` alias). Transcripts round-trip as a
18
18
  JSON list of messages via ``--resume FILE`` and ``/save``.
19
19
 
20
- Secret hygiene (CLAUDE.md): the REPL prints only model output and message
20
+ Secret hygiene (AGENTS.md): the REPL prints only model output and message
21
21
  content; it never echoes the API key. A saved transcript holds only the
22
22
  ``messages`` array (roles + content), never auth material.
23
23
  """