Adds the Agent Workbench HTTP surface wiring the open-agent-sdk loop into
BoosAPI:
- POST /api/agent/chat (SSE): runs an agentic turn and streams session /
text / thinking / tool_use / tool_result / approval_request / result /
error / done events. The agent's LLM calls are routed back through
BoosAPI's own OpenAI-compatible /v1 using the built-in downstream key
(constructed via the SDK's OpenAIProvider), so any aggregated model works
and usage flows through the normal accounting pipeline.
- POST /api/agent/approve: resolves a pending tool approval by request id.
- GET /api/agent/status: engine info + a session's accounting.
Safety is enforced in the SDK permission callback: every tool first passes
sandbox path validation (no escaping the per-session working dir), then the
risk/mode matrix — writes and command execution require an interactive
approval round-trip (default mode) that blocks the loop until the user
answers or the request times out (300s -> deny). Each session runs in its
own data/agent_sessions/{id}/ sandbox and turn usage is persisted.
Registered in the API router and added open-agent-sdk as a path dependency.
Adds the agent_sessions table (migration) and a session_store module that
owns its lifecycle:
- ensure_session: idempotently creates the session row on first chat.
- record_turn: accumulates input/output/total tokens, cost and turn count
after each completed turn, bumping updated_at.
- get_session: user-scoped fetch so a tenant can only read its own session.
Backed by SQLite via SQLx, matching the existing service style. Unit-tested
against an in-memory database (idempotency, accumulation, user scoping).
Introduces the SDK-free building blocks for a server-side AI agent:
- permission: tool risk classification (ReadOnly/Write/Execute) and the
per-mode decision matrix (Default/AcceptEdits/FullAccess). Default mode
auto-allows reads and requires approval for writes/executes.
- sandbox: per-session isolated working directory under
data/agent_sessions/{id}/ plus path validation that confines every tool
path argument within the session directory (rejects ../, absolute and
symlink escapes), mirroring the AQBot security model.
- approval: process-global registry bridging the SSE approval round-trip via
oneshot channels, so the permission callback can block until the user
approves or denies a risky tool.
All three modules are unit-tested in isolation (17 tests) and carry no
dependency on the agent SDK; the HTTP layer maps their results onto the
SDK permission callback in a later commit.
digital_human.rs uses url::Url::parse but the crate was only a transitive
dependency, not declared in Cargo.toml, so the lib failed to compile with
E0433. Add the explicit dependency (url 2.x was already in Cargo.lock).
Implement the four Gemini native inbound endpoints so Gemini CLI / Google
GenAI SDKs can point their base URL at the gateway:
- GET /v1beta/models — directory listing in Gemini shape, built from
configured routes (like /v1/models); no upstream call, no billing.
- POST /v1beta/models/{model}:generateContent — non-streaming, routed
through ProxyConductor with failover + usage_logs + billing.
- POST /v1beta/models/{model}:streamGenerateContent — SSE passthrough via
open_stream_with_failover + sse_passthrough_response.
- POST /v1beta/models/{model}:countTokens — metadata call, routed for a
channel but not billed.
Auth accepts the gateway key from ?key= or x-goog-api-key (Gemini
convention) and reuses the downstream-key authorization + limits.
Billing closes the loop: gemini_extract_usage reads usageMetadata
(promptTokenCount / candidatesTokenCount) for non-streaming, and
gemini_stream_usage_from_line feeds the shared StreamUsageAccumulator so
streamed usageMetadata is billed too.
Unit tests cover model:action parsing, usage extraction, the stream usage
parser, and query forwarding (key/alt dropped).
Add Gemini-shaped upstream calls that mirror AQBot's native passthrough
and BoosAPI's /v1/responses forwarding: the model lives in the URL path
(/v1beta/models/{model}:{op}) and the key in ?key=, so the body is sent
verbatim with no model rewrite.
- gemini_base_url / gemini_operation_url normalize the v1beta root and
build {base}/v1beta/models/{model}:{op}?key=...&{extra}, stripping a
leading models/ prefix and handling the no-key case.
- execute_gemini_operation runs non-streaming ops (generateContent /
countTokens) and returns upstream JSON verbatim.
- execute_gemini_stream_response opens streamGenerateContent for SSE
passthrough, enforcing alt=sse via merge_alt_sse.
Unit tests cover URL building and alt=sse merging.
Pre-existing breakage on this branch (introduced by the billing commits),
unrelated to the Gemini work but blocking both `cargo build` and
`cargo test`:
- admin/users.rs imported routes::auth::DEFAULT_NEW_USER_MAX_COST_USD,
which was never defined. Add it to auth.rs as $1.0 (the billing design's
"new users default to $1" starter allowance).
- AppConfig gained `payment` and `pixelle_video` fields but several test
helpers still used struct literals missing them. Initialize both with
Default::default() at each affected test site.
Add GET /api/cli-profiles/templates returning copy-ready config snippets
for pointing common AI clients at this gateway: Cursor, VS Code Continue,
Claude Code, and a generic OpenAI-compatible blob.
Ported from AQBot's list_gateway_templates (a desktop app that hardcodes
localhost:8080). Adapted for server-side use: the gateway origin is resolved
per request — ?base_url= override, then X-Forwarded-Proto + Host (correct
behind a TLS-terminating proxy), then config server.host:port fallback
(0.0.0.0/:: rewritten to 127.0.0.1, matching the comfyui-agent local-base
convention). OpenAI-compatible clients get the /v1 base; Claude Code gets
the bare origin since it appends /v1/messages itself.
Scope guard: this only returns config strings + copy hints. It deliberately
does not rewrite local CLI config files (AQBot's desktop cli_config behavior),
which has no meaning on a server.
New module proxy::cli_profiles::templates holds the snippet content; the admin
handler resolves the origin. Covered by 9 unit tests.