agentbridge-cli 0.1.13__tar.gz → 0.1.15__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 (112) hide show
  1. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.codex/skills/build-release/SKILL.md +4 -6
  2. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.codex/skills/build-release/scripts/release_build.py +4 -2
  3. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.github/workflows/release.yml +0 -2
  4. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/PKG-INFO +6 -5
  5. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/README.md +5 -4
  6. agentbridge_cli-0.1.15/agentbridge/_build_info.py +1 -0
  7. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/dashboard.py +12 -7
  8. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/server.py +43 -70
  9. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/templates/dashboard/chat.html +138 -40
  10. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/templates/dashboard/page.html +75 -19
  11. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/templates/dashboard/requests.html +12 -11
  12. agentbridge_cli-0.1.15/design-qa.md +55 -0
  13. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/README.md +6 -6
  14. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Sources/AgentBridgeMenuBar/StatusPopover.swift +1 -2
  15. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/pyproject.toml +1 -1
  16. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/scripts/build_macos_app.sh +5 -5
  17. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/test_dashboard.py +134 -0
  18. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/test_server.py +21 -0
  19. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/uv.lock +1 -1
  20. agentbridge_cli-0.1.13/agentbridge/_build_info.py +0 -1
  21. agentbridge_cli-0.1.13/design-qa.md +0 -58
  22. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.codex/config.toml +0 -0
  23. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.codex/skills/build-release/agents/openai.yaml +0 -0
  24. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.env.example +0 -0
  25. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.github/dependabot.yml +0 -0
  26. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.github/workflows/ci.yml +0 -0
  27. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.github/workflows/dependency-review.yml +0 -0
  28. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/.gitignore +0 -0
  29. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/AGENTS.md +0 -0
  30. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/CLAUDE.md +0 -0
  31. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/LICENSE +0 -0
  32. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/Makefile +0 -0
  33. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/TODO.txt +0 -0
  34. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/__init__.py +0 -0
  35. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/config.py +0 -0
  36. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/models.py +0 -0
  37. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/pool.py +0 -0
  38. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/static/brand/android-chrome-192.png +0 -0
  39. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/static/brand/android-chrome-512.png +0 -0
  40. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/static/brand/apple-touch-icon.png +0 -0
  41. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/static/brand/favicon-16.png +0 -0
  42. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/static/brand/favicon-32.png +0 -0
  43. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/static/brand/favicon-48.png +0 -0
  44. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/static/brand/favicon.ico +0 -0
  45. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/static/brand/favicon.svg +0 -0
  46. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/static/brand/site.webmanifest +0 -0
  47. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/templates/dashboard/base.html +0 -0
  48. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/templates/dashboard/detail.html +0 -0
  49. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/agentbridge/templates/dashboard/pool.html +0 -0
  50. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/architecture.png +0 -0
  51. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/agentbridge-popover-final.png +0 -0
  52. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/agentbridge-popover-normalized.png +0 -0
  53. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/agentbridge-popover-pass-1.jpeg +0 -0
  54. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/implementation-header-activity.png +0 -0
  55. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/reference-header-activity.png +0 -0
  56. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/reference-inactive-menu-icons.png +0 -0
  57. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/reference-inactive-telegram-badge-normalized.png +0 -0
  58. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/reference-inactive-telegram-badge.png +0 -0
  59. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/reference-low-contrast-arrows-normalized.png +0 -0
  60. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/reference-low-contrast-arrows.png +0 -0
  61. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/reference-panel.png +0 -0
  62. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/reference-telegram-badge-normalized.png +0 -0
  63. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/reference-telegram-badge.png +0 -0
  64. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/status-item-active-workers.png +0 -0
  65. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/status-item-arrow-contrast-comparison.png +0 -0
  66. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/status-item-badge-comparison.png +0 -0
  67. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/status-item-inactive-comparison.png +0 -0
  68. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/design-qa/status-item-no-workers.png +0 -0
  69. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/docs/plans/2026-02-14-dashboard-design.md +0 -0
  70. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/docs/plans/2026-02-14-dashboard-implementation.md +0 -0
  71. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/hatch_build.py +0 -0
  72. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/apple-store/app-store-icon-1024.png +0 -0
  73. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/icon/icon-1024.png +0 -0
  74. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/logo/logo-1024.png +0 -0
  75. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/manifest.json +0 -0
  76. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/sources/icon-source-keyed.png +0 -0
  77. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/sources/icon-source.png +0 -0
  78. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/sources/logo-source-keyed.png +0 -0
  79. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/sources/logo-source.png +0 -0
  80. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/sources/social-background-source.png +0 -0
  81. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/sources/social-source.png +0 -0
  82. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/android-chrome-192.png +0 -0
  83. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/android-chrome-512.png +0 -0
  84. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/apple-touch-icon.png +0 -0
  85. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/favicon/favicon-16.png +0 -0
  86. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/favicon/favicon-32.png +0 -0
  87. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/favicon/favicon-48.png +0 -0
  88. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/favicon/favicon.ico +0 -0
  89. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/favicon/favicon.svg +0 -0
  90. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/og-image-1200x630.png +0 -0
  91. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/image-assets/web-seo/site.webmanifest +0 -0
  92. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/logo.png +0 -0
  93. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Package.swift +0 -0
  94. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Sources/AgentBridgeMenuBar/AgentBridgeMenuBarApp.swift +0 -0
  95. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Sources/AgentBridgeMenuBar/BrandMark.swift +0 -0
  96. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Sources/AgentBridgeMenuBar/HealthClient.swift +0 -0
  97. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Sources/AgentBridgeMenuBar/MenuBarStatusPresenter.swift +0 -0
  98. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Sources/AgentBridgeMenuBar/ServerController.swift +0 -0
  99. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Sources/AgentBridgeMenuBar/ServerLauncher.swift +0 -0
  100. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Sources/AgentBridgeMenuBar/SettingsView.swift +0 -0
  101. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Sources/AgentBridgeMenuBar/StatusModels.swift +0 -0
  102. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/macos/Tests/AgentBridgeMenuBarTests/StatusModelsTests.swift +0 -0
  103. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/scripts/dashboard_load_test.py +0 -0
  104. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/scripts/verify_macos_app.sh +0 -0
  105. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/fixtures/ocr_test_document.png +0 -0
  106. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/test_config.py +0 -0
  107. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/test_dependency_security.py +0 -0
  108. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/test_image_utils.py +0 -0
  109. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/test_model_mapping.py +0 -0
  110. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/test_models.py +0 -0
  111. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/test_pool.py +0 -0
  112. {agentbridge_cli-0.1.13 → agentbridge_cli-0.1.15}/tests/test_session_logger.py +0 -0
@@ -9,7 +9,7 @@ Use the repository-owned GitHub Actions workflow and PyPI Trusted Publishing.
9
9
  Do not create a local tag or upload with local credentials: pushing a new
10
10
  `pyproject.toml` version to `main` triggers `.github/workflows/release.yml`,
11
11
  which tests, scans, builds, creates `v<version>`, creates the GitHub Release,
12
- publishes `agentbridge-cli`, and attaches ad-hoc-signed arm64 and x86_64 DMGs.
12
+ publishes `agentbridge-cli`, and attaches an ad-hoc-signed arm64 DMG.
13
13
 
14
14
  Use the next unused patch version unless the user requests another valid,
15
15
  unused semantic version. Treat an untagged version absent from PyPI as pending.
@@ -54,8 +54,8 @@ The preflight requires macOS. It checks consistent project and lock metadata, an
54
54
  unused PyPI version and tag, clean patch formatting, a frozen lock, Python 3.12
55
55
  and 3.13 tests, Ruff, Swift tests, a self-contained app/DMG build for the current
56
56
  Mac architecture, app signature and embedded-runtime smoke tests, wheel and
57
- sdist audits, and a wheel installation smoke test. CI builds both macOS
58
- architectures with ad-hoc signatures. Stop at the exact failed gate.
57
+ sdist audits, and a wheel installation smoke test. CI builds the arm64 macOS
58
+ artifact with an ad-hoc signature. Stop at the exact failed gate.
59
59
 
60
60
  4. Review and publish the release commit:
61
61
 
@@ -92,9 +92,7 @@ python3 .codex/skills/build-release/scripts/release_build.py wait-pypi --version
92
92
  gh release view v<version> --json url,tagName,assets
93
93
  ```
94
94
 
95
- Require both
96
- `AgentBridge-<version>-macos-arm64.dmg` and
97
- `AgentBridge-<version>-macos-x86_64.dmg` plus their `.sha256` files in the
95
+ Require `AgentBridge-<version>-macos-arm64.dmg` and its `.sha256` file in the
98
96
  release assets. Do not report success until PyPI returns exact-version files and
99
97
  all required GitHub assets exist. If the workflow fails, inspect
100
98
  `gh run view <run-id> --log-failed` and report the failed gate before considering
@@ -313,8 +313,10 @@ def preflight_macos_app(version: str) -> dict[str, object]:
313
313
  raise SystemExit("the AgentBridge release preflight requires macOS for app validation")
314
314
 
315
315
  architecture = platform.machine()
316
- if architecture not in {"arm64", "x86_64"}:
317
- raise SystemExit(f"unsupported macOS release architecture: {architecture}")
316
+ if architecture != "arm64":
317
+ raise SystemExit(
318
+ f"unsupported macOS release architecture: {architecture}; AgentBridge requires arm64"
319
+ )
318
320
 
319
321
  run(["swift", "test", "--package-path", "macos"])
320
322
  run(["scripts/build_macos_app.sh"])
@@ -214,8 +214,6 @@ jobs:
214
214
  include:
215
215
  - runner: macos-15
216
216
  architecture: arm64
217
- - runner: macos-15-intel
218
- architecture: x86_64
219
217
  runs-on: ${{ matrix.runner }}
220
218
  timeout-minutes: 30
221
219
  permissions:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentbridge-cli
3
- Version: 0.1.13
3
+ Version: 0.1.15
4
4
  Summary: Bridge OpenAI tools to Claude Code SDK, Codex CLI, and OpenRouter
5
5
  Project-URL: Homepage, https://github.com/tsilva/agentbridge
6
6
  Project-URL: Repository, https://github.com/tsilva/agentbridge
@@ -46,10 +46,10 @@ It supports streaming and non-streaming responses, image and PDF inputs where th
46
46
 
47
47
  ### macOS menu-bar app
48
48
 
49
- Download the DMG for your Mac architecture from the GitHub Release, drag
49
+ On an Apple silicon Mac, download the arm64 DMG from the GitHub Release, drag
50
50
  `AgentBridge.app` to Applications, and open it. The app is ad-hoc signed rather
51
51
  than Apple-notarized, so on first launch macOS may require you to Control-click
52
- the app, choose **Open**, and confirm.
52
+ the app, choose **Open**, and confirm. Intel Macs are not supported.
53
53
 
54
54
  You can also build the app locally on macOS 13 or later. This requires
55
55
  [uv](https://docs.astral.sh/uv/) and the Xcode Command Line Tools, but not an
@@ -177,6 +177,7 @@ uv build # build wheel and source d
177
177
  - Claude clients are created lazily, reused by model, and capped by the worker count. Claude sessions do not load filesystem settings and run with built-in tools disabled.
178
178
  - Codex runs one ephemeral `codex exec` process per request in a temporary directory with read-only sandboxing, no approvals, and project rules ignored. Multimodal structured-output calls also ignore user config and disable execution and image-generation tools. Native image calls use the same strict profile, keep execution disabled, and enable the image-generation capability needed for the edit.
179
179
  - Claude and Codex function calls are represented through prompted JSON; OpenRouter tool calls pass through its SDK. Session logs and extracted image or PDF attachments are saved under `~/.config/agentbridge/logs/sessions` by default.
180
+ - Streaming provider failures emit an OpenAI-shaped `error` object as an SSE `data` event before `[DONE]`; they are not returned as assistant message text.
180
181
  - Set `store: false` on chat requests to suppress session-log artifacts. The native image route requires `store: false`, accepts data URLs only, validates both rasters, locates the result from the structured Codex thread ID, and removes that thread's generated-image directory after the request.
181
182
 
182
183
  ## Publishing
@@ -185,8 +186,8 @@ Releases use the `Release` GitHub Actions workflow and PyPI Trusted Publishing
185
186
  for the `agentbridge-cli` project. The publisher is scoped to owner `tsilva`,
186
187
  repository `agentbridge`, workflow `release.yml`, and environment `pypi`; no
187
188
  PyPI API token is required. GitHub Releases contain the Python distributions
188
- plus ad-hoc-signed arm64 and x86_64 macOS DMGs and SHA-256 checksums. These DMGs
189
- are not Apple-notarized and require no Apple Developer credentials. Releases
189
+ plus an ad-hoc-signed arm64 macOS DMG and SHA-256 checksum. The DMG is not
190
+ Apple-notarized and requires no Apple Developer credentials. Releases
190
191
  through `0.1.10` remain available under the previous `agentbridge-py`
191
192
  distribution name.
192
193
 
@@ -14,10 +14,10 @@ It supports streaming and non-streaming responses, image and PDF inputs where th
14
14
 
15
15
  ### macOS menu-bar app
16
16
 
17
- Download the DMG for your Mac architecture from the GitHub Release, drag
17
+ On an Apple silicon Mac, download the arm64 DMG from the GitHub Release, drag
18
18
  `AgentBridge.app` to Applications, and open it. The app is ad-hoc signed rather
19
19
  than Apple-notarized, so on first launch macOS may require you to Control-click
20
- the app, choose **Open**, and confirm.
20
+ the app, choose **Open**, and confirm. Intel Macs are not supported.
21
21
 
22
22
  You can also build the app locally on macOS 13 or later. This requires
23
23
  [uv](https://docs.astral.sh/uv/) and the Xcode Command Line Tools, but not an
@@ -145,6 +145,7 @@ uv build # build wheel and source d
145
145
  - Claude clients are created lazily, reused by model, and capped by the worker count. Claude sessions do not load filesystem settings and run with built-in tools disabled.
146
146
  - Codex runs one ephemeral `codex exec` process per request in a temporary directory with read-only sandboxing, no approvals, and project rules ignored. Multimodal structured-output calls also ignore user config and disable execution and image-generation tools. Native image calls use the same strict profile, keep execution disabled, and enable the image-generation capability needed for the edit.
147
147
  - Claude and Codex function calls are represented through prompted JSON; OpenRouter tool calls pass through its SDK. Session logs and extracted image or PDF attachments are saved under `~/.config/agentbridge/logs/sessions` by default.
148
+ - Streaming provider failures emit an OpenAI-shaped `error` object as an SSE `data` event before `[DONE]`; they are not returned as assistant message text.
148
149
  - Set `store: false` on chat requests to suppress session-log artifacts. The native image route requires `store: false`, accepts data URLs only, validates both rasters, locates the result from the structured Codex thread ID, and removes that thread's generated-image directory after the request.
149
150
 
150
151
  ## Publishing
@@ -153,8 +154,8 @@ Releases use the `Release` GitHub Actions workflow and PyPI Trusted Publishing
153
154
  for the `agentbridge-cli` project. The publisher is scoped to owner `tsilva`,
154
155
  repository `agentbridge`, workflow `release.yml`, and environment `pypi`; no
155
156
  PyPI API token is required. GitHub Releases contain the Python distributions
156
- plus ad-hoc-signed arm64 and x86_64 macOS DMGs and SHA-256 checksums. These DMGs
157
- are not Apple-notarized and require no Apple Developer credentials. Releases
157
+ plus an ad-hoc-signed arm64 macOS DMG and SHA-256 checksum. The DMG is not
158
+ Apple-notarized and requires no Apple Developer credentials. Releases
158
159
  through `0.1.10` remain available under the previous `agentbridge-py`
159
160
  distribution name.
160
161
 
@@ -0,0 +1 @@
1
+ GIT_HASH = "690c228"
@@ -225,6 +225,8 @@ def _parse_log_file(path: Path) -> dict | None:
225
225
 
226
226
  def _get_recent_logs(limit: int = 20) -> list[dict]:
227
227
  """Read recent session log files, newest first."""
228
+ if limit <= 0:
229
+ return []
228
230
  log_dir = session_log_dir()
229
231
  if not log_dir.exists():
230
232
  return []
@@ -242,10 +244,12 @@ def _get_recent_logs(limit: int = 20) -> list[dict]:
242
244
  )
243
245
 
244
246
  results = []
245
- for path in log_files[:limit]:
247
+ for path in log_files:
246
248
  parsed = _parse_log_file(path)
247
249
  if parsed is not None:
248
250
  results.append(parsed)
251
+ if len(results) >= limit:
252
+ break
249
253
 
250
254
  return results
251
255
 
@@ -366,10 +370,11 @@ def create_dashboard_router(
366
370
  if log.get("duration_ms") is None:
367
371
  log["duration_ms"] = timing.get("duration_ms", 0)
368
372
  if log.get("input_tokens") is None:
369
- usage_data = log.get("usage")
370
- if usage_data:
371
- log["input_tokens"] = usage_data.get("input_tokens")
372
- log["output_tokens"] = usage_data.get("output_tokens")
373
+ usage_data = (
374
+ log.get("usage") if isinstance(log.get("usage"), dict) else {}
375
+ )
376
+ log["input_tokens"] = usage_data.get("input_tokens")
377
+ log["output_tokens"] = usage_data.get("output_tokens")
373
378
 
374
379
  merged = active + completed
375
380
  return merged[:limit]
@@ -441,8 +446,8 @@ def create_dashboard_router(
441
446
  if parsed is None:
442
447
  raise HTTPException(status_code=404, detail="Request not found")
443
448
 
444
- timing = parsed.get("timing", {})
445
- usage = parsed.get("usage", {})
449
+ timing = parsed.get("timing") if isinstance(parsed.get("timing"), dict) else {}
450
+ usage = parsed.get("usage") if isinstance(parsed.get("usage"), dict) else {}
446
451
  return templates.TemplateResponse(
447
452
  request,
448
453
  "detail.html",
@@ -686,6 +686,23 @@ OPENROUTER_TIMEOUT = int(
686
686
  )
687
687
 
688
688
 
689
+ def _stream_error_event(
690
+ message: str,
691
+ *,
692
+ error_type: str = "server_error",
693
+ code: str | None = None,
694
+ ) -> str:
695
+ """Return a machine-readable OpenAI-shaped error in an SSE data event."""
696
+ payload = ErrorResponse(
697
+ error=ErrorDetail(
698
+ message=message,
699
+ type=error_type,
700
+ code=code,
701
+ )
702
+ )
703
+ return f"data: {payload.model_dump_json()}\n\n"
704
+
705
+
689
706
  class BridgeHTTPException(HTTPException):
690
707
  """HTTPException with request_id for error tracing."""
691
708
 
@@ -1863,18 +1880,13 @@ async def stream_openrouter_api(
1863
1880
  )
1864
1881
  dashboard_state.request_errored(request_id, f"{type(e).__name__}: {error}")
1865
1882
  _dashboard_handled = True
1866
- error_chunk = ChatCompletionChunk(
1867
- id=request_id,
1868
- created=created,
1869
- model=request.model,
1870
- choices=[
1871
- StreamChoice(
1872
- delta=DeltaMessage(content="\n\n[Error: OpenRouter request failed.]"),
1873
- finish_reason=None,
1874
- )
1875
- ],
1883
+ yield _stream_error_event(
1884
+ "OpenRouter request timed out."
1885
+ if isinstance(e, asyncio.TimeoutError)
1886
+ else "OpenRouter request failed.",
1887
+ error_type="timeout_error" if isinstance(e, asyncio.TimeoutError) else "server_error",
1888
+ code="timeout_error" if isinstance(e, asyncio.TimeoutError) else "provider_error",
1876
1889
  )
1877
- yield f"data: {error_chunk.model_dump_json()}\n\n"
1878
1890
  yield "data: [DONE]\n\n"
1879
1891
  finally:
1880
1892
  if not _dashboard_handled:
@@ -2186,23 +2198,11 @@ async def stream_claude_sdk(
2186
2198
  )
2187
2199
  dashboard_state.request_errored(request_id, f"Timeout after {CLAUDE_TIMEOUT}s")
2188
2200
  _dashboard_handled = True
2189
- error_chunk = ChatCompletionChunk(
2190
- id=request_id,
2191
- created=created,
2192
- model=model,
2193
- choices=[
2194
- StreamChoice(
2195
- delta=DeltaMessage(
2196
- content=(
2197
- "\n\n[Error: Request timed out. Increase CLAUDE_TIMEOUT "
2198
- "env var for longer requests.]"
2199
- )
2200
- ),
2201
- finish_reason=None,
2202
- )
2203
- ],
2201
+ yield _stream_error_event(
2202
+ "Request timed out. Increase CLAUDE_TIMEOUT env var for longer requests.",
2203
+ error_type="timeout_error",
2204
+ code="timeout_error",
2204
2205
  )
2205
- yield f"data: {error_chunk.model_dump_json()}\n\n"
2206
2206
  yield "data: [DONE]\n\n"
2207
2207
  return
2208
2208
  except HTTPException as he:
@@ -2213,16 +2213,11 @@ async def stream_claude_sdk(
2213
2213
  )
2214
2214
  dashboard_state.request_errored(request_id, f"HTTP {he.status_code}: {he.detail}")
2215
2215
  _dashboard_handled = True
2216
- error_chunk = ChatCompletionChunk(
2217
- id=request_id,
2218
- created=created,
2219
- model=model,
2220
- choices=[StreamChoice(
2221
- delta=DeltaMessage(content=f"\n\n[Error: {he.detail}]"),
2222
- finish_reason=None,
2223
- )],
2216
+ yield _stream_error_event(
2217
+ str(he.detail),
2218
+ error_type="invalid_request_error" if he.status_code < 500 else "server_error",
2219
+ code="invalid_request_error" if he.status_code < 500 else "server_error",
2224
2220
  )
2225
- yield f"data: {error_chunk.model_dump_json()}\n\n"
2226
2221
  yield "data: [DONE]\n\n"
2227
2222
  return
2228
2223
  except Exception as e:
@@ -2237,16 +2232,11 @@ async def stream_claude_sdk(
2237
2232
  )
2238
2233
  dashboard_state.request_errored(request_id, f"{type(e).__name__}: {e}")
2239
2234
  _dashboard_handled = True
2240
- error_chunk = ChatCompletionChunk(
2241
- id=request_id,
2242
- created=created,
2243
- model=model,
2244
- choices=[StreamChoice(
2245
- delta=DeltaMessage(content="\n\n[Error: An internal error occurred.]"),
2246
- finish_reason=None,
2247
- )],
2235
+ yield _stream_error_event(
2236
+ "An internal error occurred.",
2237
+ error_type="server_error",
2238
+ code="server_error",
2248
2239
  )
2249
- yield f"data: {error_chunk.model_dump_json()}\n\n"
2250
2240
  yield "data: [DONE]\n\n"
2251
2241
  return
2252
2242
  finally:
@@ -2482,23 +2472,11 @@ async def stream_codex_cli(
2482
2472
  )
2483
2473
  dashboard_state.request_errored(request_id, f"Timeout after {CODEX_TIMEOUT}s")
2484
2474
  _dashboard_handled = True
2485
- error_chunk = ChatCompletionChunk(
2486
- id=request_id,
2487
- created=created,
2488
- model=model,
2489
- choices=[
2490
- StreamChoice(
2491
- delta=DeltaMessage(
2492
- content=(
2493
- "\n\n[Error: Request timed out. Increase CODEX_TIMEOUT "
2494
- "env var for longer requests.]"
2495
- )
2496
- ),
2497
- finish_reason=None,
2498
- )
2499
- ],
2475
+ yield _stream_error_event(
2476
+ "Request timed out. Increase CODEX_TIMEOUT env var for longer requests.",
2477
+ error_type="timeout_error",
2478
+ code="timeout_error",
2500
2479
  )
2501
- yield f"data: {error_chunk.model_dump_json()}\n\n"
2502
2480
  yield "data: [DONE]\n\n"
2503
2481
  return
2504
2482
  except Exception as e:
@@ -2514,16 +2492,11 @@ async def stream_codex_cli(
2514
2492
  )
2515
2493
  dashboard_state.request_errored(request_id, f"{type(e).__name__}: {e}")
2516
2494
  _dashboard_handled = True
2517
- error_chunk = ChatCompletionChunk(
2518
- id=request_id,
2519
- created=created,
2520
- model=model,
2521
- choices=[StreamChoice(
2522
- delta=DeltaMessage(content="\n\n[Error: An internal error occurred.]"),
2523
- finish_reason=None,
2524
- )],
2495
+ yield _stream_error_event(
2496
+ "An internal error occurred.",
2497
+ error_type="server_error",
2498
+ code="server_error",
2525
2499
  )
2526
- yield f"data: {error_chunk.model_dump_json()}\n\n"
2527
2500
  yield "data: [DONE]\n\n"
2528
2501
  return
2529
2502
  finally: