agent-wire-cli 0.2.3__tar.gz → 0.2.4__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 (138) hide show
  1. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.claude-plugin/plugin.json +1 -1
  2. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.github/dependabot.yml +4 -1
  3. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.github/workflows/ci.yml +8 -8
  4. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.github/workflows/codeql.yml +1 -1
  5. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.github/workflows/release.yml +4 -4
  6. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.github/workflows/security.yml +7 -7
  7. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/PKG-INFO +62 -30
  8. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/README.md +61 -29
  9. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/SECURITY.md +10 -2
  10. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/manifests/gemini_extension.json +1 -1
  11. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/manifests/hermes_skill.md +1 -1
  12. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/pyproject.toml +1 -1
  13. agent_wire_cli-0.2.4/src/agent_wire/__init__.py +1 -0
  14. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/base.py +7 -0
  15. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/github.py +2 -2
  16. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/reddit.py +2 -2
  17. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/web.py +2 -2
  18. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/hubs.py +2 -2
  19. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/publishing.py +2 -2
  20. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier3_regional/douyin_tiktok.py +10 -3
  21. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/codemode/sandbox.py +32 -8
  22. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/firewall/outbound_privacy.py +22 -0
  23. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_channels_new.py +16 -1
  24. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_codemode.py +20 -1
  25. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_firewall_outbound.py +20 -0
  26. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/uv.lock +4 -4
  27. agent_wire_cli-0.2.3/src/agent_wire/__init__.py +0 -1
  28. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.claude-plugin/marketplace.json +0 -0
  29. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.github/scripts/mcp_smoke.py +0 -0
  30. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.gitignore +0 -0
  31. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/.mcp.json +0 -0
  32. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/LICENSE +0 -0
  33. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/agents/wire-worker.md +0 -0
  34. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/assets/agent-wire-banner-light.png +0 -0
  35. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/assets/agent-wire-banner-light.svg +0 -0
  36. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/assets/agent-wire-banner.png +0 -0
  37. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/assets/agent-wire-banner.svg +0 -0
  38. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/assets/agent-wire-logo-transparent.png +0 -0
  39. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/assets/agent-wire-logo-transparent.svg +0 -0
  40. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/assets/agent-wire-logo.png +0 -0
  41. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/assets/agent-wire-logo.svg +0 -0
  42. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/manifests/antigravity_mcp.json +0 -0
  43. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/manifests/codex.toml +0 -0
  44. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/manifests/cursor_rule.mdc +0 -0
  45. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/manifests/peridot_local.json +0 -0
  46. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/manifests/windsurf_rules.md +0 -0
  47. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/skills/agent-wire/SKILL.md +0 -0
  48. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/__main__.py +0 -0
  49. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/auth/__init__.py +0 -0
  50. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/auth/browser_paths.py +0 -0
  51. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/auth/cookie_vault.py +0 -0
  52. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/auth/rate_limiter.py +0 -0
  53. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/cache/__init__.py +0 -0
  54. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/cache/ring_buffer.py +0 -0
  55. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/cache/sqlite_ledger.py +0 -0
  56. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/__init__.py +0 -0
  57. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/__init__.py +0 -0
  58. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/hackernews.py +0 -0
  59. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/rss.py +0 -0
  60. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/search.py +0 -0
  61. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/stackoverflow.py +0 -0
  62. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/wikipedia.py +0 -0
  63. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/x_twitter.py +0 -0
  64. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier1_global/youtube.py +0 -0
  65. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/__init__.py +0 -0
  66. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/archive_bypass.py +0 -0
  67. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/arxiv_papers.py +0 -0
  68. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/devto.py +0 -0
  69. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/fediverse.py +0 -0
  70. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/linkedin.py +0 -0
  71. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/markets.py +0 -0
  72. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/meta_social.py +0 -0
  73. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/news.py +0 -0
  74. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/polymarket.py +0 -0
  75. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier2_pro/stocktwits.py +0 -0
  76. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier3_regional/bilibili.py +0 -0
  77. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier3_regional/boss_zhipin.py +0 -0
  78. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier3_regional/v2ex.py +0 -0
  79. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier3_regional/wechat.py +0 -0
  80. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier3_regional/xiaohongshu.py +0 -0
  81. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier3_regional/xiaoyuzhou.py +0 -0
  82. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier3_regional/xueqiu.py +0 -0
  83. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/channels/tier3_regional/zhihu.py +0 -0
  84. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/cli.py +0 -0
  85. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/codemode/__init__.py +0 -0
  86. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/codemode/api.py +0 -0
  87. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/compression/__init__.py +0 -0
  88. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/compression/anchor_slicer.py +0 -0
  89. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/compression/bm25_slicer.py +0 -0
  90. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/compression/dam_formatter.py +0 -0
  91. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/compression/url_compactor.py +0 -0
  92. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/config.py +0 -0
  93. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/engine/__init__.py +0 -0
  94. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/engine/challenge.py +0 -0
  95. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/engine/errors.py +0 -0
  96. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/engine/http_pool.py +0 -0
  97. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/engine/stealth_browser.py +0 -0
  98. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/engine/waterfall.py +0 -0
  99. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/firewall/__init__.py +0 -0
  100. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/firewall/inbound_shield.py +0 -0
  101. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/firewall/ssrf_guard.py +0 -0
  102. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/installers/__init__.py +0 -0
  103. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/installers/client_installer.py +0 -0
  104. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/media/__init__.py +0 -0
  105. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/media/audio_transcriber.py +0 -0
  106. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/media/subtitle_cleaner.py +0 -0
  107. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/models.py +0 -0
  108. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/__init__.py +0 -0
  109. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/brief.py +0 -0
  110. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/briefing.py +0 -0
  111. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/cluster.py +0 -0
  112. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/engine.py +0 -0
  113. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/library.py +0 -0
  114. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/planner.py +0 -0
  115. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/scoring.py +0 -0
  116. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/store.py +0 -0
  117. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/research/watch.py +0 -0
  118. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/router.py +0 -0
  119. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/server.py +0 -0
  120. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/verify/__init__.py +0 -0
  121. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/src/agent_wire/verify/citations.py +0 -0
  122. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/conftest.py +0 -0
  123. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_auth.py +0 -0
  124. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_bm25_slicer.py +0 -0
  125. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_channels_tier1.py +0 -0
  126. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_channels_tier2_3.py +0 -0
  127. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_citations.py +0 -0
  128. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_codemode_bench.py +0 -0
  129. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_compression.py +0 -0
  130. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_config.py +0 -0
  131. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_firewall_inbound.py +0 -0
  132. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_installer.py +0 -0
  133. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_models.py +0 -0
  134. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_research.py +0 -0
  135. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_ring_buffer.py +0 -0
  136. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_server.py +0 -0
  137. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_ssrf_guard.py +0 -0
  138. {agent_wire_cli-0.2.3 → agent_wire_cli-0.2.4}/tests/test_waterfall_routing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-wire",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Live web and 31-platform access for agents: token-compressed, privacy-firewalled, injection-shielded. Auto-triggers on links and live questions.",
5
5
  "author": {
6
6
  "name": "uncoalesced"
@@ -1,7 +1,8 @@
1
1
  version: 2
2
2
  updates:
3
3
  # Python dependencies in uv.lock. Security fixes come as separate PRs straight away
4
- # (Dependabot security updates); this keeps routine upgrades to one weekly batch.
4
+ # (Dependabot security updates); this keeps routine upgrades to one weekly batch. Routine
5
+ # upgrades wait 7 days after a release, so a hijacked version is usually pulled before we take it.
5
6
  - package-ecosystem: uv
6
7
  directory: /
7
8
  target-branch: dev
@@ -13,6 +14,8 @@ updates:
13
14
  patterns: ["*"]
14
15
  update-types: [minor, patch]
15
16
  open-pull-requests-limit: 5
17
+ cooldown:
18
+ default-days: 7
16
19
 
17
20
  # Actions are pinned to commit SHAs; this bumps the SHA and the version comment together.
18
21
  - package-ecosystem: github-actions
@@ -17,10 +17,10 @@ jobs:
17
17
  runs-on: ubuntu-latest
18
18
  timeout-minutes: 10
19
19
  steps:
20
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
20
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
21
21
  with:
22
22
  persist-credentials: false
23
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
23
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
24
24
  - name: Lockfile matches pyproject.toml
25
25
  run: uv lock --check
26
26
  - name: Install
@@ -44,10 +44,10 @@ jobs:
44
44
  runs-on: ${{ matrix.os }}
45
45
  timeout-minutes: 20
46
46
  steps:
47
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
47
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
48
48
  with:
49
49
  persist-credentials: false
50
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
50
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
51
51
  with:
52
52
  python-version: ${{ matrix.python }}
53
53
  - name: Install
@@ -59,10 +59,10 @@ jobs:
59
59
  runs-on: ubuntu-latest
60
60
  timeout-minutes: 10
61
61
  steps:
62
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
62
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
63
63
  with:
64
64
  persist-credentials: false
65
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
65
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
66
66
  - name: Build sdist and wheel
67
67
  run: uv build
68
68
  - name: Package metadata renders on PyPI
@@ -93,10 +93,10 @@ jobs:
93
93
  runs-on: ${{ matrix.os }}
94
94
  timeout-minutes: 15
95
95
  steps:
96
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
96
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
97
97
  with:
98
98
  persist-credentials: false
99
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
99
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
100
100
  with:
101
101
  python-version: ${{ matrix.python }}
102
102
  - name: Build wheel
@@ -27,7 +27,7 @@ jobs:
27
27
  contents: read
28
28
  security-events: write # upload results to the Security tab
29
29
  steps:
30
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
30
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
31
31
  with:
32
32
  persist-credentials: false
33
33
  - uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
@@ -15,10 +15,10 @@ jobs:
15
15
  runs-on: ubuntu-latest
16
16
  timeout-minutes: 20
17
17
  steps:
18
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
18
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
19
19
  with:
20
20
  persist-credentials: false
21
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
21
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
22
22
  with:
23
23
  enable-cache: false # never build a release from a cache another run could have written
24
24
  - name: Tag matches the package version
@@ -29,7 +29,7 @@ jobs:
29
29
  - run: uv run pytest -q
30
30
  - run: uv build
31
31
  - run: uvx twine check --strict dist/*
32
- - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
32
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
33
33
  with:
34
34
  name: dist
35
35
  path: dist/
@@ -42,7 +42,7 @@ jobs:
42
42
  permissions:
43
43
  id-token: write # PyPI trusted publishing
44
44
  steps:
45
- - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
45
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
46
46
  with:
47
47
  name: dist
48
48
  path: dist/
@@ -21,10 +21,10 @@ jobs:
21
21
  runs-on: ubuntu-latest
22
22
  timeout-minutes: 10
23
23
  steps:
24
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
24
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
25
25
  with:
26
26
  persist-credentials: false
27
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
27
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
28
28
  - name: Export the lockfile
29
29
  run: uv export --frozen --no-hashes --all-extras --no-emit-project -o requirements-audit.txt
30
30
  - name: pip-audit
@@ -35,10 +35,10 @@ jobs:
35
35
  runs-on: ubuntu-latest
36
36
  timeout-minutes: 10
37
37
  steps:
38
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
38
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
39
39
  with:
40
40
  persist-credentials: false
41
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
41
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
42
42
  - name: bandit
43
43
  run: uvx bandit==1.9.4 -r src -ll
44
44
 
@@ -47,10 +47,10 @@ jobs:
47
47
  runs-on: ubuntu-latest
48
48
  timeout-minutes: 10
49
49
  steps:
50
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
50
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
51
51
  with:
52
52
  persist-credentials: false
53
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
53
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
54
54
  - name: zizmor
55
55
  run: uvx zizmor==1.31.1 --format plain .github/workflows
56
56
  env:
@@ -62,7 +62,7 @@ jobs:
62
62
  runs-on: ubuntu-latest
63
63
  timeout-minutes: 10
64
64
  steps:
65
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
65
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
66
66
  with:
67
67
  persist-credentials: false
68
68
  - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agent-wire-cli
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: High-speed, privacy-first, token-compressed live web and multi-platform intelligence engine for AI agents.
5
5
  Project-URL: Homepage, https://github.com/uncoalesced/agent-wire
6
6
  License: MIT
@@ -43,6 +43,7 @@ Description-Content-Type: text/markdown
43
43
  <a href="https://github.com/uncoalesced/agent-wire/actions/workflows/ci.yml"><img alt="CI" height="20" src="https://img.shields.io/github/actions/workflow/status/uncoalesced/agent-wire/ci.yml?branch=main&label=ci"></a>
44
44
  <a href="LICENSE"><img alt="MIT license" height="20" src="https://img.shields.io/badge/license-MIT-f5b884"></a>
45
45
  <a href="https://m8ven.ai/mcp/uncoalesced/agent-wire?s=readme"><img alt="M8ven Score" height="20" src="https://m8ven.ai/badge/mcp/uncoalesced/agent-wire"></a>
46
+ <a href="https://mcpservers.org/servers/uncoalesced/agent-wire"><img alt="Listed on mcpservers.org" height="20" src="https://mcpservers.org/badge.svg"></a>
46
47
  </p>
47
48
 
48
49
  <p align="center">
@@ -459,52 +460,65 @@ Tokens are the cost an agent feels first: money on an API plan, rate limits on a
459
460
 
460
461
  ### What a page costs
461
462
 
462
- We read a set of pages cold, from India, with a fresh `WIRE_HOME`, on 2026-10-20 (v0.2.1), and counted tokens with tiktoken. "Raw HTML" is what a plain fetch returned. Six of the pages are below.
463
-
464
- | Page | Raw HTML tokens | agent-wire tokens | Saved |
465
- |---|---|---|---|
466
- | GitHub issue (uv #1495) | 197,594 | 2,066 | 99% |
467
- | YouTube video page | ~533,000 | 3,040 | 99% |
468
- | Wikipedia (Model Context Protocol) | 79,926 | 1,868 | 98% |
469
- | dev.to/t/ai | 52,636 | 835 | 98% |
470
- | docs.python.org asyncio page | 50,059 | 2,989 | 94% |
471
- | arXiv abstract (Attention Is All You Need) | 12,961 | 3,031 | 77% |
472
-
473
- Across the benchmark set the typical saving was 94% (the median, counting only pages where a plain fetch worked), and the arXiv abstract was the weakest case. Hacker News, Stack Overflow and Medium refused the plain fetch outright, so there is no raw count to compare against. agent-wire read all three.
474
-
475
- A cold read of one URL took about 1.4 to 1.9 seconds at the median, from 0.15 seconds (dev.to) to about 10 seconds (Reddit through the archive fallback). One search that auto-reads its top hits took about 2.3 seconds and returned around 2,400 tokens.
463
+ On 2026-11-04 we read 16 pages from India, three times each, every time cold with a fresh `WIRE_HOME`, on the `dev` branch after v0.2.3 (commit `e7077af`). Tokens are counted with tiktoken (`o200k_base`), and the agent-wire count includes its header line, since that reaches the model too. "Plain fetch" is one ordinary GET with a browser user agent. [Jina Reader](https://jina.ai/reader/) is the free `r.jina.ai` endpoint with no key, which is what Agent-Reach uses first for web reads.
464
+
465
+ | Page | Plain fetch | Jina Reader | agent-wire | Saved vs plain |
466
+ |---|---|---|---|---|
467
+ | GitHub issue (uv #1495) | 184,922 | failed (403) | 2,066 | 99% |
468
+ | GitHub repo README (uv) | 152,386 | failed (403) | 3,003 | 98% |
469
+ | YouTube video page | 630,615 | failed (bot check) | 3,031 | 99.5% |
470
+ | Wikipedia (Model Context Protocol) | 80,494 | 8,326 | 1,868 | 98% |
471
+ | dev.to/t/ai | 56,834 | 12,290 | 1,017 | 98% |
472
+ | Cloudflare blog post | 105,755 | 2,369 | 2,051 | 98% |
473
+ | MDN Fetch API | 41,251 | 1,355 | 633 | 98% |
474
+ | Bluesky profile | 15,673 | 142 | 756 | 95% |
475
+ | Hacker News thread | 32,712 | 16,656 | 1,933 | 94% |
476
+ | docs.python.org asyncio | 6,941 | 883 | 584 | 92% |
477
+ | arXiv abstract (Attention Is All You Need) | 12,961 | 772 | 3,031 | 77% |
478
+ | Stack Overflow question | blocked (403) | 25,821 | 2,211 | |
479
+ | Medium post | blocked (403) | failed (403) | 2,827 | |
480
+ | Reddit r/Python | blocked | failed (blocked) | 2,395 | |
481
+ | npm package page (react) | blocked (403) | failed (CAPTCHA) | 123 | |
482
+ | Cloudflare challenge page (nowsecure.nl) | challenge page | 52 | 87 | |
483
+
484
+ - **Pages read:** agent-wire 16 of 16, Jina Reader 10 of 16, plain fetch 11 of 16.
485
+ - **Saving vs plain fetch:** median 98% on the 11 pages a plain fetch could read. The arXiv abstract was the weakest at 77%, because agent-wire opens the full paper rather than the abstract page and fills its token budget from it.
486
+ - **Against Jina Reader:** on the 10 pages both could read, agent-wire returned 14,171 tokens in total and Jina 68,666, so 79% fewer, and fewer on 7 of the 10. Jina was smaller on the arXiv abstract (it returns only the abstract page), the Bluesky profile (a thin cached snapshot) and the nowsecure.nl page. The comparison isn't like for like: agent-wire trims every read to a budget (3,000 tokens by default, ranked by relevance when you pass a `query`), and Jina returns the whole page. Long Hacker News and Stack Overflow threads are where that shows most.
487
+ - **Speed:** a cold read took 0.76 seconds at the median over 48 reads, and 3.1 seconds at the 90th percentile. The slowest was the Cloudflare challenge page, which goes through the stage 4 browser: 6 to 16 seconds. Jina's median was 0.7 seconds on the pages it could read, with a few 15 to 16 second outliers.
488
+ - **Search:** five searches that auto-read their top hits took 2.4 seconds at the median and returned about 2,500 tokens each.
476
489
 
477
490
  ### What it costs to have it installed
478
491
 
479
- This is the part that doesn't flatter agent-wire, so here it is plainly. On 2026-10-29 we counted what each tool puts into context before it does any work, using the `o200k_base` tokenizer. Claude's tokenizer counts differently, so read these as comparable to each other and not as an exact bill.
492
+ This is the part that doesn't flatter agent-wire, so here it is plainly. We counted what each tool puts into context before it does any work, using the `o200k_base` tokenizer. Claude's tokenizer counts differently, so read these as comparable to each other and not as an exact bill. The agent-wire rows were re-counted on 2026-11-04 from the server's live tool list, taking each tool's name, description and input schema, which is what an API client sends to the model. That method counts a little more than the one we used on 2026-10-29 (1,603 for the standard profile then); the tools themselves haven't changed since v0.2.3.
480
493
 
481
494
  | What loads | Tokens | When |
482
495
  |---|---|---|
483
- | agent-wire tool definitions, standard profile (8 tools) | 1,603, plus 150 for server instructions | every session |
484
- | agent-wire tool definitions, `--profile local` (7 tools) | 740 | every session |
485
- | agent-wire tool definitions, `--code-mode` (2 tools) | 471 | every session |
486
- | agent-wire skill (`SKILL.md`) | 1,354 | when the skill triggers, depending on the client |
487
- | [last30days](https://github.com/mvanhorn/last30days-skill) skill (`SKILL.md`, v3.31.0) | 7,029 | when the skill triggers |
488
- | [Agent-Reach](https://github.com/Panniantong/Agent-Reach) skill (`SKILL.md`) | 1,933 | when the skill triggers |
496
+ | agent-wire tool definitions, standard profile (8 tools) | 1,838, plus 150 for server instructions | every session |
497
+ | agent-wire tool definitions, `--profile local` (7 tools) | 961 | every session |
498
+ | agent-wire tool definitions, `--code-mode` (2 tools) | 500 | every session |
499
+ | agent-wire tool definitions, `--profile local --code-mode` (2 tools) | 155 | every session |
500
+ | agent-wire skill (`SKILL.md`) | 1,342 | when the skill triggers, depending on the client |
501
+ | [last30days](https://github.com/mvanhorn/last30days-skill) skill (`SKILL.md`, v3.31.0, counted 2026-10-29) | 7,029 | when the skill triggers |
502
+ | [Agent-Reach](https://github.com/Panniantong/Agent-Reach) skill (`SKILL.md`, counted 2026-10-29) | 1,933 | when the skill triggers |
489
503
 
490
- An MCP server pays its tool definitions in every session, used or not. A skill costs roughly its one-paragraph description until it triggers. So on a day you never touch the web, agent-wire is the most expensive of the three to have installed. On a day you do, the order flips: its tool list plus its skill come to about 3,100 tokens, and last30days's skill alone is about 7,000. If the 1,603 bothers you, `--profile local` and `--code-mode` cut it to 740 and 471.
504
+ An MCP server pays its tool definitions in every session, used or not. A skill costs roughly its one-paragraph description until it triggers. So on a day you never touch the web, agent-wire is the most expensive of the three to have installed. On a day you do, the order flips: its tool list, instructions and skill come to about 3,300 tokens, and last30days's skill alone is about 7,000. If the 1,838 bothers you, `--profile local` and `--code-mode` cut it to 961 and 500, and both together to 155.
491
505
 
492
506
  The comparison isn't clean. last30days's `SKILL.md` also carries a setup wizard and formatting rules, and Agent-Reach's is largely Chinese text, so its count isn't directly comparable with an English file's.
493
507
 
494
508
  ### What a task costs
495
509
 
496
- Code Mode is where the biggest single saving is. The bundled benchmark (`pytest -s tests/test_codemode_bench.py`) asks for the top four Hacker News threads on a topic with each thread's three best comments, offline. It counts every token that enters the model's context: the tool arguments or the script, plus every tool result.
510
+ Code Mode is where the biggest single saving is. The task: find the top four Hacker News threads on "rust async" and give each thread's three best comments. We count every token that enters the model's context: the tool arguments or the script, plus every tool result. The live run is from 2026-11-04 (three cold runs, same result each time); the offline one is the bundled benchmark (`pytest -s tests/test_codemode_bench.py`) on canned pages.
497
511
 
498
- | Route | Context tokens | Round trips |
499
- |---|---|---|
500
- | Classic tools (one search, one batched read) | 3,394 | 2 |
501
- | `wire_run` script | 468 | 1 |
512
+ | Route | Context tokens, live | Context tokens, offline | Round trips |
513
+ |---|---|---|---|
514
+ | Classic tools (one search, one batched read) | 2,864 | 3,394 | 2 |
515
+ | `wire_run` script | 459 | 468 | 1 |
502
516
 
503
- That is 86% fewer. On a live run over Reddit and Hacker News, a script fetched 41,005 tokens of pages and returned 61 to the model.
517
+ That is 84% fewer live and 86% offline. In the live run the script's two calls pulled in 210,569 tokens of pages, and 297 tokens of answer came back to the model; the whole script took about 2.4 seconds. A second script that read r/LocalLLaMA and the Hacker News front page in parallel fetched 13,469 tokens and returned 569.
504
518
 
505
519
  ### What we didn't measure
506
520
 
507
- We have not run last30days or Agent-Reach against agent-wire on the same pages, so we have no per-page or per-task comparison to show. Neither project publishes per-run token figures in its README (we checked both), so there was nothing to quote either.
521
+ We have not run last30days or Agent-Reach themselves against agent-wire on the same pages. The Jina Reader column above covers the first step of Agent-Reach's web reads, not the whole tool, and we have no per-task comparison with either. Neither project publishes per-run token figures in its README (we checked both), so there was nothing to quote either.
508
522
 
509
523
  What their own docs say is structural. Agent-Reach tries an ordered list of tools for each action (web reads go to Jina Reader first, then Exa) and returns what they produce. Its README says some channels, such as Xueqiu, V2EX and GitHub, come back as organized content, but it describes no token budget or slicing step. last30days collects and ranks evidence across sources, and its `SKILL.md` has the host model write the brief from that evidence, so the evidence lands in your context and the model's brief is output tokens on top. Those are differences in design, not measurements, and either could be smaller than it looks.
510
524
 
@@ -636,6 +650,24 @@ Pages get the opposite treatment. Anything a person reading the page couldn't se
636
650
 
637
651
  Logins you add with `wire auth login <site>` live in an AES-GCM vault whose key is protected by Windows DPAPI, or by a 0600 key file on macOS and Linux.
638
652
 
653
+ ### Supply chain
654
+
655
+ A lot of recent malware has arrived through package registries: a hijacked npm package, or a post-install script that runs the moment you install something. Here is what agent-wire pulls in and how it gets to you, as of a scan on 2026-11-04.
656
+
657
+ - **No npm, no Node.** agent-wire is a Python package on PyPI. `uvx` or `pip` installs it, and nothing runs `npm` or `npx`.
658
+ - **No install scripts.** Every package in the lockfile ships prebuilt wheels, and installing a wheel unpacks files without running any of its code. Only the optional extras fetch more later, and only when you ask: `stealth` needs `playwright install chromium`, and `local-whisper` downloads a speech model on first use.
659
+ - **Small direct list.** 13 direct dependencies (12 on Python 3.13 and newer), all long-standing projects. The youngest is `quickjs-ng`, the Code Mode sandbox engine, first released in February 2026. With everything they pull in, that is 94 packages, or 117 with every optional extra.
660
+ - **Pinned and hashed.** `uv.lock` pins every package to one version with sha256 hashes, and CI installs from it. `pip install` and `uvx` resolve fresh versions within the ranges in `pyproject.toml`, so if you want our exact set, install from a checkout with `uv sync --locked`.
661
+ - **Scanned.** `pip-audit` over the full locked set (runtime plus every extra) found one advisory: CVE-2026-119284 in `joserfc`, pulled in through `fastmcp`. agent-wire never parses the JSON web tokens it concerns, and the lock now uses the fixed version, so there are no known vulnerabilities. Bandit found nothing at medium severity or above, and zizmor finds nothing in the workflows. CodeQL flagged seven host checks that matched by substring (so `evilnpmjs.com` counted as npm); those now match the exact host or its subdomains. Cookies were never at risk, because the vault picks them by the real request host.
662
+ - **Routine upgrades wait.** Dependabot holds routine dependency and Actions upgrades for 7 days after a release, so a hijacked version usually gets yanked before we take it. Security fixes still arrive straight away.
663
+ - **Releases you can check.** Releases are built and uploaded by GitHub Actions through PyPI Trusted Publishing, so no upload token exists to steal. Each file on PyPI carries a signed attestation that ties it to `release.yml` in this repository. Actions are pinned to commit SHAs, and every pull request runs `pip-audit`, bandit, zizmor, dependency review and CodeQL.
664
+
665
+ To check a release yourself:
666
+
667
+ ```bash
668
+ uvx --from pypi-attestations pypi-attestations verify pypi --repository https://github.com/uncoalesced/agent-wire pypi:agent_wire_cli-0.2.3-py3-none-any.whl
669
+ ```
670
+
639
671
  ## Privacy Policy
640
672
 
641
673
  agent-wire runs on your machine. There's no agent-wire server, no account, no analytics and no telemetry.
@@ -13,6 +13,7 @@
13
13
  <a href="https://github.com/uncoalesced/agent-wire/actions/workflows/ci.yml"><img alt="CI" height="20" src="https://img.shields.io/github/actions/workflow/status/uncoalesced/agent-wire/ci.yml?branch=main&label=ci"></a>
14
14
  <a href="LICENSE"><img alt="MIT license" height="20" src="https://img.shields.io/badge/license-MIT-f5b884"></a>
15
15
  <a href="https://m8ven.ai/mcp/uncoalesced/agent-wire?s=readme"><img alt="M8ven Score" height="20" src="https://m8ven.ai/badge/mcp/uncoalesced/agent-wire"></a>
16
+ <a href="https://mcpservers.org/servers/uncoalesced/agent-wire"><img alt="Listed on mcpservers.org" height="20" src="https://mcpservers.org/badge.svg"></a>
16
17
  </p>
17
18
 
18
19
  <p align="center">
@@ -429,52 +430,65 @@ Tokens are the cost an agent feels first: money on an API plan, rate limits on a
429
430
 
430
431
  ### What a page costs
431
432
 
432
- We read a set of pages cold, from India, with a fresh `WIRE_HOME`, on 2026-10-20 (v0.2.1), and counted tokens with tiktoken. "Raw HTML" is what a plain fetch returned. Six of the pages are below.
433
-
434
- | Page | Raw HTML tokens | agent-wire tokens | Saved |
435
- |---|---|---|---|
436
- | GitHub issue (uv #1495) | 197,594 | 2,066 | 99% |
437
- | YouTube video page | ~533,000 | 3,040 | 99% |
438
- | Wikipedia (Model Context Protocol) | 79,926 | 1,868 | 98% |
439
- | dev.to/t/ai | 52,636 | 835 | 98% |
440
- | docs.python.org asyncio page | 50,059 | 2,989 | 94% |
441
- | arXiv abstract (Attention Is All You Need) | 12,961 | 3,031 | 77% |
442
-
443
- Across the benchmark set the typical saving was 94% (the median, counting only pages where a plain fetch worked), and the arXiv abstract was the weakest case. Hacker News, Stack Overflow and Medium refused the plain fetch outright, so there is no raw count to compare against. agent-wire read all three.
444
-
445
- A cold read of one URL took about 1.4 to 1.9 seconds at the median, from 0.15 seconds (dev.to) to about 10 seconds (Reddit through the archive fallback). One search that auto-reads its top hits took about 2.3 seconds and returned around 2,400 tokens.
433
+ On 2026-11-04 we read 16 pages from India, three times each, every time cold with a fresh `WIRE_HOME`, on the `dev` branch after v0.2.3 (commit `e7077af`). Tokens are counted with tiktoken (`o200k_base`), and the agent-wire count includes its header line, since that reaches the model too. "Plain fetch" is one ordinary GET with a browser user agent. [Jina Reader](https://jina.ai/reader/) is the free `r.jina.ai` endpoint with no key, which is what Agent-Reach uses first for web reads.
434
+
435
+ | Page | Plain fetch | Jina Reader | agent-wire | Saved vs plain |
436
+ |---|---|---|---|---|
437
+ | GitHub issue (uv #1495) | 184,922 | failed (403) | 2,066 | 99% |
438
+ | GitHub repo README (uv) | 152,386 | failed (403) | 3,003 | 98% |
439
+ | YouTube video page | 630,615 | failed (bot check) | 3,031 | 99.5% |
440
+ | Wikipedia (Model Context Protocol) | 80,494 | 8,326 | 1,868 | 98% |
441
+ | dev.to/t/ai | 56,834 | 12,290 | 1,017 | 98% |
442
+ | Cloudflare blog post | 105,755 | 2,369 | 2,051 | 98% |
443
+ | MDN Fetch API | 41,251 | 1,355 | 633 | 98% |
444
+ | Bluesky profile | 15,673 | 142 | 756 | 95% |
445
+ | Hacker News thread | 32,712 | 16,656 | 1,933 | 94% |
446
+ | docs.python.org asyncio | 6,941 | 883 | 584 | 92% |
447
+ | arXiv abstract (Attention Is All You Need) | 12,961 | 772 | 3,031 | 77% |
448
+ | Stack Overflow question | blocked (403) | 25,821 | 2,211 | |
449
+ | Medium post | blocked (403) | failed (403) | 2,827 | |
450
+ | Reddit r/Python | blocked | failed (blocked) | 2,395 | |
451
+ | npm package page (react) | blocked (403) | failed (CAPTCHA) | 123 | |
452
+ | Cloudflare challenge page (nowsecure.nl) | challenge page | 52 | 87 | |
453
+
454
+ - **Pages read:** agent-wire 16 of 16, Jina Reader 10 of 16, plain fetch 11 of 16.
455
+ - **Saving vs plain fetch:** median 98% on the 11 pages a plain fetch could read. The arXiv abstract was the weakest at 77%, because agent-wire opens the full paper rather than the abstract page and fills its token budget from it.
456
+ - **Against Jina Reader:** on the 10 pages both could read, agent-wire returned 14,171 tokens in total and Jina 68,666, so 79% fewer, and fewer on 7 of the 10. Jina was smaller on the arXiv abstract (it returns only the abstract page), the Bluesky profile (a thin cached snapshot) and the nowsecure.nl page. The comparison isn't like for like: agent-wire trims every read to a budget (3,000 tokens by default, ranked by relevance when you pass a `query`), and Jina returns the whole page. Long Hacker News and Stack Overflow threads are where that shows most.
457
+ - **Speed:** a cold read took 0.76 seconds at the median over 48 reads, and 3.1 seconds at the 90th percentile. The slowest was the Cloudflare challenge page, which goes through the stage 4 browser: 6 to 16 seconds. Jina's median was 0.7 seconds on the pages it could read, with a few 15 to 16 second outliers.
458
+ - **Search:** five searches that auto-read their top hits took 2.4 seconds at the median and returned about 2,500 tokens each.
446
459
 
447
460
  ### What it costs to have it installed
448
461
 
449
- This is the part that doesn't flatter agent-wire, so here it is plainly. On 2026-10-29 we counted what each tool puts into context before it does any work, using the `o200k_base` tokenizer. Claude's tokenizer counts differently, so read these as comparable to each other and not as an exact bill.
462
+ This is the part that doesn't flatter agent-wire, so here it is plainly. We counted what each tool puts into context before it does any work, using the `o200k_base` tokenizer. Claude's tokenizer counts differently, so read these as comparable to each other and not as an exact bill. The agent-wire rows were re-counted on 2026-11-04 from the server's live tool list, taking each tool's name, description and input schema, which is what an API client sends to the model. That method counts a little more than the one we used on 2026-10-29 (1,603 for the standard profile then); the tools themselves haven't changed since v0.2.3.
450
463
 
451
464
  | What loads | Tokens | When |
452
465
  |---|---|---|
453
- | agent-wire tool definitions, standard profile (8 tools) | 1,603, plus 150 for server instructions | every session |
454
- | agent-wire tool definitions, `--profile local` (7 tools) | 740 | every session |
455
- | agent-wire tool definitions, `--code-mode` (2 tools) | 471 | every session |
456
- | agent-wire skill (`SKILL.md`) | 1,354 | when the skill triggers, depending on the client |
457
- | [last30days](https://github.com/mvanhorn/last30days-skill) skill (`SKILL.md`, v3.31.0) | 7,029 | when the skill triggers |
458
- | [Agent-Reach](https://github.com/Panniantong/Agent-Reach) skill (`SKILL.md`) | 1,933 | when the skill triggers |
466
+ | agent-wire tool definitions, standard profile (8 tools) | 1,838, plus 150 for server instructions | every session |
467
+ | agent-wire tool definitions, `--profile local` (7 tools) | 961 | every session |
468
+ | agent-wire tool definitions, `--code-mode` (2 tools) | 500 | every session |
469
+ | agent-wire tool definitions, `--profile local --code-mode` (2 tools) | 155 | every session |
470
+ | agent-wire skill (`SKILL.md`) | 1,342 | when the skill triggers, depending on the client |
471
+ | [last30days](https://github.com/mvanhorn/last30days-skill) skill (`SKILL.md`, v3.31.0, counted 2026-10-29) | 7,029 | when the skill triggers |
472
+ | [Agent-Reach](https://github.com/Panniantong/Agent-Reach) skill (`SKILL.md`, counted 2026-10-29) | 1,933 | when the skill triggers |
459
473
 
460
- An MCP server pays its tool definitions in every session, used or not. A skill costs roughly its one-paragraph description until it triggers. So on a day you never touch the web, agent-wire is the most expensive of the three to have installed. On a day you do, the order flips: its tool list plus its skill come to about 3,100 tokens, and last30days's skill alone is about 7,000. If the 1,603 bothers you, `--profile local` and `--code-mode` cut it to 740 and 471.
474
+ An MCP server pays its tool definitions in every session, used or not. A skill costs roughly its one-paragraph description until it triggers. So on a day you never touch the web, agent-wire is the most expensive of the three to have installed. On a day you do, the order flips: its tool list, instructions and skill come to about 3,300 tokens, and last30days's skill alone is about 7,000. If the 1,838 bothers you, `--profile local` and `--code-mode` cut it to 961 and 500, and both together to 155.
461
475
 
462
476
  The comparison isn't clean. last30days's `SKILL.md` also carries a setup wizard and formatting rules, and Agent-Reach's is largely Chinese text, so its count isn't directly comparable with an English file's.
463
477
 
464
478
  ### What a task costs
465
479
 
466
- Code Mode is where the biggest single saving is. The bundled benchmark (`pytest -s tests/test_codemode_bench.py`) asks for the top four Hacker News threads on a topic with each thread's three best comments, offline. It counts every token that enters the model's context: the tool arguments or the script, plus every tool result.
480
+ Code Mode is where the biggest single saving is. The task: find the top four Hacker News threads on "rust async" and give each thread's three best comments. We count every token that enters the model's context: the tool arguments or the script, plus every tool result. The live run is from 2026-11-04 (three cold runs, same result each time); the offline one is the bundled benchmark (`pytest -s tests/test_codemode_bench.py`) on canned pages.
467
481
 
468
- | Route | Context tokens | Round trips |
469
- |---|---|---|
470
- | Classic tools (one search, one batched read) | 3,394 | 2 |
471
- | `wire_run` script | 468 | 1 |
482
+ | Route | Context tokens, live | Context tokens, offline | Round trips |
483
+ |---|---|---|---|
484
+ | Classic tools (one search, one batched read) | 2,864 | 3,394 | 2 |
485
+ | `wire_run` script | 459 | 468 | 1 |
472
486
 
473
- That is 86% fewer. On a live run over Reddit and Hacker News, a script fetched 41,005 tokens of pages and returned 61 to the model.
487
+ That is 84% fewer live and 86% offline. In the live run the script's two calls pulled in 210,569 tokens of pages, and 297 tokens of answer came back to the model; the whole script took about 2.4 seconds. A second script that read r/LocalLLaMA and the Hacker News front page in parallel fetched 13,469 tokens and returned 569.
474
488
 
475
489
  ### What we didn't measure
476
490
 
477
- We have not run last30days or Agent-Reach against agent-wire on the same pages, so we have no per-page or per-task comparison to show. Neither project publishes per-run token figures in its README (we checked both), so there was nothing to quote either.
491
+ We have not run last30days or Agent-Reach themselves against agent-wire on the same pages. The Jina Reader column above covers the first step of Agent-Reach's web reads, not the whole tool, and we have no per-task comparison with either. Neither project publishes per-run token figures in its README (we checked both), so there was nothing to quote either.
478
492
 
479
493
  What their own docs say is structural. Agent-Reach tries an ordered list of tools for each action (web reads go to Jina Reader first, then Exa) and returns what they produce. Its README says some channels, such as Xueqiu, V2EX and GitHub, come back as organized content, but it describes no token budget or slicing step. last30days collects and ranks evidence across sources, and its `SKILL.md` has the host model write the brief from that evidence, so the evidence lands in your context and the model's brief is output tokens on top. Those are differences in design, not measurements, and either could be smaller than it looks.
480
494
 
@@ -606,6 +620,24 @@ Pages get the opposite treatment. Anything a person reading the page couldn't se
606
620
 
607
621
  Logins you add with `wire auth login <site>` live in an AES-GCM vault whose key is protected by Windows DPAPI, or by a 0600 key file on macOS and Linux.
608
622
 
623
+ ### Supply chain
624
+
625
+ A lot of recent malware has arrived through package registries: a hijacked npm package, or a post-install script that runs the moment you install something. Here is what agent-wire pulls in and how it gets to you, as of a scan on 2026-11-04.
626
+
627
+ - **No npm, no Node.** agent-wire is a Python package on PyPI. `uvx` or `pip` installs it, and nothing runs `npm` or `npx`.
628
+ - **No install scripts.** Every package in the lockfile ships prebuilt wheels, and installing a wheel unpacks files without running any of its code. Only the optional extras fetch more later, and only when you ask: `stealth` needs `playwright install chromium`, and `local-whisper` downloads a speech model on first use.
629
+ - **Small direct list.** 13 direct dependencies (12 on Python 3.13 and newer), all long-standing projects. The youngest is `quickjs-ng`, the Code Mode sandbox engine, first released in February 2026. With everything they pull in, that is 94 packages, or 117 with every optional extra.
630
+ - **Pinned and hashed.** `uv.lock` pins every package to one version with sha256 hashes, and CI installs from it. `pip install` and `uvx` resolve fresh versions within the ranges in `pyproject.toml`, so if you want our exact set, install from a checkout with `uv sync --locked`.
631
+ - **Scanned.** `pip-audit` over the full locked set (runtime plus every extra) found one advisory: CVE-2026-119284 in `joserfc`, pulled in through `fastmcp`. agent-wire never parses the JSON web tokens it concerns, and the lock now uses the fixed version, so there are no known vulnerabilities. Bandit found nothing at medium severity or above, and zizmor finds nothing in the workflows. CodeQL flagged seven host checks that matched by substring (so `evilnpmjs.com` counted as npm); those now match the exact host or its subdomains. Cookies were never at risk, because the vault picks them by the real request host.
632
+ - **Routine upgrades wait.** Dependabot holds routine dependency and Actions upgrades for 7 days after a release, so a hijacked version usually gets yanked before we take it. Security fixes still arrive straight away.
633
+ - **Releases you can check.** Releases are built and uploaded by GitHub Actions through PyPI Trusted Publishing, so no upload token exists to steal. Each file on PyPI carries a signed attestation that ties it to `release.yml` in this repository. Actions are pinned to commit SHAs, and every pull request runs `pip-audit`, bandit, zizmor, dependency review and CodeQL.
634
+
635
+ To check a release yourself:
636
+
637
+ ```bash
638
+ uvx --from pypi-attestations pypi-attestations verify pypi --repository https://github.com/uncoalesced/agent-wire pypi:agent_wire_cli-0.2.3-py3-none-any.whl
639
+ ```
640
+
609
641
  ## Privacy Policy
610
642
 
611
643
  agent-wire runs on your machine. There's no agent-wire server, no account, no analytics and no telemetry.
@@ -34,7 +34,15 @@ agent-wire sits between an AI agent and the open web, so it treats two things as
34
34
  - Getting blocked, rate-limited or challenged by a site.
35
35
  - Problems that need someone who can already edit your `WIRE_HOME`, your agent client config or your environment variables.
36
36
 
37
+ ## Supply chain
38
+
39
+ Dependency problems are in scope too: a vulnerable or malicious package in `uv.lock`, a release file that doesn't match this repository, or a workflow that could leak a secret or publish something we didn't build.
40
+
41
+ - **Releases.** A pushed `v*` tag runs `.github/workflows/release.yml`, which builds the wheel and sdist, runs the tests and uploads to PyPI through Trusted Publishing. No PyPI token is stored anywhere. Each file gets a PEP 740 attestation naming this repository and workflow. Check one with `uvx --from pypi-attestations pypi-attestations verify pypi --repository https://github.com/uncoalesced/agent-wire pypi:<file name>`.
42
+ - **Dependencies.** `uv.lock` pins every package with sha256 hashes, and CI installs with `uv sync --locked`. Every locked package ships wheels, so installing runs no package code. There is no npm or Node dependency. Routine upgrades come through Dependabot, which waits 7 days after a release; security updates come straight away.
43
+ - **Checks on every push and pull request.** `pip-audit` over the full locked set including extras, bandit, zizmor on the workflows, CodeQL, and dependency review on pull requests. Actions are pinned to commit SHAs.
44
+ - **Last manual scan, 2026-11-04.** `pip-audit` found CVE-2026-119284 in `joserfc` (via `fastmcp`, in a code path agent-wire doesn't use); the lock was moved to the fixed version. No other known vulnerabilities. CodeQL's substring host-check alerts in the channel routers were fixed; the ones left in tests are assertions on test data.
45
+
37
46
  ## Known limitations
38
47
 
39
- - **Private addresses.** `wire_read`, Code Mode's `wire.read` and the citation checker don't yet refuse `localhost`, private-network or cloud-metadata URLs. A fix is in review.
40
- - **Secret redaction.** Output scrubbing removes bearer tokens, session cookies, private IPs and your saved vault values. It does not catch every API-key format that might appear in public page text.
48
+ - **Secret redaction.** Output scrubbing removes bearer tokens, session cookies, private IPs, your saved vault values and well-known prefixed API keys (GitHub, OpenAI, Anthropic, AWS, Slack, Google, GitLab, Stripe, Hugging Face, npm). Keys with no recognizable prefix that appear in public page text are not caught.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-wire",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Live web and platform access: token-compressed, privacy-firewalled, injection-shielded.",
5
5
  "contextFileName": "GEMINI.md",
6
6
  "mcpServers": {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-wire
3
3
  description: Live web and platform access (Reddit, X, GitHub, YouTube, HN, StackOverflow, arXiv, markets, Bilibili, Xiaohongshu). Use whenever the user shares a URL, asks about live facts or community opinion, or about their own social account activity.
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  metadata:
6
6
  hermes:
7
7
  tags: [web, search, research, social]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agent-wire-cli"
7
- version = "0.2.3"
7
+ version = "0.2.4"
8
8
  description = "High-speed, privacy-first, token-compressed live web and multi-platform intelligence engine for AI agents."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1 @@
1
+ __version__ = "0.2.4"
@@ -6,6 +6,7 @@ import re
6
6
  from dataclasses import dataclass, field
7
7
  from datetime import UTC, datetime, timedelta
8
8
  from typing import ClassVar
9
+ from urllib.parse import urlsplit
9
10
 
10
11
  from selectolax.lexbor import LexborHTMLParser
11
12
 
@@ -79,6 +80,12 @@ class Channel:
79
80
  return WireDocument(url=url, channel=self.name, **fields)
80
81
 
81
82
 
83
+ def on_host(url: str, domain: str) -> bool:
84
+ """URL (or bare host) is `domain` or a subdomain of it; look-alikes like evil-domain.com don't match."""
85
+ host = (urlsplit(url if "//" in url else f"https://{url}").hostname or "").lower()
86
+ return host == domain or host.endswith("." + domain)
87
+
88
+
82
89
  def ts(value) -> datetime | None:
83
90
  """Epoch seconds/ms or ISO-8601 -> aware datetime."""
84
91
  if value in (None, ""):
@@ -9,7 +9,7 @@ from urllib.parse import quote, urlsplit
9
9
 
10
10
  from selectolax.lexbor import LexborHTMLParser
11
11
 
12
- from agent_wire.channels.base import Channel, WireUnsupported, compact_count, listing, ts
12
+ from agent_wire.channels.base import Channel, WireUnsupported, compact_count, listing, on_host, ts
13
13
  from agent_wire.compression.anchor_slicer import parse_line_anchor, slice_lines, slice_section
14
14
  from agent_wire.models import WireComment, WireDocument, WireSearchHit
15
15
 
@@ -85,7 +85,7 @@ class GitHubChannel(Channel):
85
85
  parts = urlsplit(url)
86
86
  host, segs = parts.hostname or "", [s for s in parts.path.split("/") if s]
87
87
  anchor = section or parts.fragment or None
88
- if host.endswith("githubusercontent.com"):
88
+ if on_host(host, "githubusercontent.com"):
89
89
  return await self._blob_raw(url, segs[0], segs[1], "/".join(segs[3:]), url)
90
90
  if host == "gist.github.com" and len(segs) >= 2:
91
91
  return await self._gist(url, segs[-1])
@@ -15,7 +15,7 @@ from urllib.parse import quote, urlencode, urlsplit
15
15
 
16
16
  import defusedxml.ElementTree as ET # untrusted feeds: no entity expansion, no DTDs
17
17
 
18
- from agent_wire.channels.base import Channel, WireUnsupported, html_text, listing, ts
18
+ from agent_wire.channels.base import Channel, WireUnsupported, html_text, listing, on_host, ts
19
19
  from agent_wire.engine.waterfall import WireFetchError
20
20
  from agent_wire.firewall.outbound_privacy import parse_json
21
21
  from agent_wire.models import WireComment, WireSearchHit
@@ -95,7 +95,7 @@ def listing_items(data: dict) -> list[dict]:
95
95
 
96
96
 
97
97
  def post_hit(d: dict, archived: bool = False) -> WireSearchHit:
98
- link = None if d.get("is_self") or "reddit.com" in (d.get("url") or "") else d.get("url")
98
+ link = None if d.get("is_self") or on_host(d.get("url") or "", "reddit.com") else d.get("url")
99
99
  return WireSearchHit(
100
100
  url=WWW + d.get("permalink", ""), title=d.get("title") or "",
101
101
  snippet="" if d.get("selftext") in ("[removed]", "[deleted]") else (d.get("selftext") or "")[:300],
@@ -6,7 +6,7 @@ from urllib.parse import urlsplit
6
6
 
7
7
  import trafilatura
8
8
 
9
- from agent_wire.channels.base import Channel, WireUnsupported, html_to_markdown, ts
9
+ from agent_wire.channels.base import Channel, WireUnsupported, html_to_markdown, on_host, ts
10
10
  from agent_wire.compression.anchor_slicer import parse_line_anchor, slice_lines, slice_section
11
11
  from agent_wire.firewall.inbound_shield import clean_html
12
12
  from agent_wire.models import WireDocument
@@ -87,7 +87,7 @@ class WebChannel(Channel):
87
87
  if anchor:
88
88
  body = slice_section(body, anchor) or body
89
89
  meta = {"site": info["site"]} if info["site"] else {}
90
- if f.stage == 3 and "web.archive.org" in f.url:
90
+ if f.stage == 3 and on_host(f.url, "web.archive.org"):
91
91
  meta["archived"] = f.url
92
92
  return self.doc(
93
93
  f, url, title=info["title"], author_handle=info["author"],
@@ -6,7 +6,7 @@ from urllib.parse import quote, urlsplit
6
6
  from curl_cffi.requests.exceptions import RequestException
7
7
  from selectolax.lexbor import LexborHTMLParser
8
8
 
9
- from agent_wire.channels.base import Channel, WireUnsupported, html_to_markdown
9
+ from agent_wire.channels.base import Channel, WireUnsupported, html_to_markdown, on_host
10
10
  from agent_wire.engine.waterfall import WireFetchError
11
11
  from agent_wire.models import WireSearchHit
12
12
 
@@ -33,7 +33,7 @@ class HubsChannel(Channel):
33
33
  host, segs = (p.hostname or "").removeprefix("www."), [s for s in p.path.split("/") if s]
34
34
  if host == "pypi.org" and segs[:1] == ["project"] and len(segs) > 1:
35
35
  return await self._pypi(url, segs[1], segs[2] if len(segs) > 2 else None)
36
- if host.endswith("npmjs.com") and segs[:1] == ["package"] and len(segs) > 1:
36
+ if on_host(host, "npmjs.com") and segs[:1] == ["package"] and len(segs) > 1:
37
37
  name = "/".join(segs[1:3]) if segs[1].startswith("@") else segs[1]
38
38
  return await self._npm(url, name)
39
39
  if host == "crates.io" and segs[:1] == ["crates"] and len(segs) > 1: