openai_provider is now the compat workhorse (vendors, resellers, Ollama,
custom endpoints); the effort pin stays for GPT-5.6 reached through a
custom endpoint. base.py lists the current provider set.
_build_openai: no custom base_url -> OpenAIResponsesProvider; a custom
endpoint (Azure /openai/v1, vLLM, compat gateways) keeps the Chat
Completions OpenAIProvider, as do Ollama and every compat vendor. Verify
path (raw GET /models) and matrix ids are untouched.
/v1/chat/completions rejects function tools with any reasoning_effort
other than none on GPT-5.6, so native OpenAI has run with reasoning OFF.
OpenAIResponsesProvider speaks /v1/responses instead: reasoning + tools
at real effort, streamed reasoning summaries into the existing
reasoning_delta plumbing, and CoT continuity across tool round-trips via
store:false + encrypted reasoning replayed through a new _openai message
sidecar (same extras contract as _anthropic/_gemini). Not routed yet.
Coworkers remember durable things you tell them and use them in future sessions.
One Settings screen lists everything remembered - edit, delete, or stop new saves; standing instructions ride along.
Knowledge is session-stable, the save switch is per-message; sqlite gains a summary column via in-place migration.
Two ephemeral breakpoints per request (last system block, final message's
last block) so append-only history re-reads the prior turns' cache;
outbound-only, persisted history stays clean.
Quiet chip (context-fill meter + session total) opening a per-model
input/output/cache breakdown popover; accumulation from live events,
rebuilt from persisted sidecars on load; unit + e2e coverage.
Normalized TokenUsage (input/output/cache split) captured in every provider's
stream and complete paths, persisted as an assistant-message sidecar and sent
on the assistant_message event; matrix gains verified context-window sizes.
Model listing 403/404s under plain ADC; countTokens is free and proves
project+location+API in one call. Verified live: Gemini (global), Qwen MaaS (us-south1).
Segmented track + inset per-method panel with its own Test & save footer.
Vertex gains the same treatment: Google Cloud login (default), service account,
and API key (express mode, Gemini-only with a clear error elsewhere).
Untrusted repos must not define stdio MCP servers that spawn at session open. Skip <.coworker/mcp.json> until the workspace is trusted, matching allowed_commands consent.
Fixes#213
'Connect with' segmented choice (API key / profile / IAM keys) shows only that
method's fields; non-selected fields are dropped at build so stale values can't leak.
New optional field: paste the console-generated key, no CLI/IAM setup needed.
Takes precedence over SigV4 credentials, matching boto3; live-tested on Converse.
resolve_from_reply decided allow/deny with 'in' checks on the whole message,
so "disallow" resolved as allow (checked first, and it contains "allow") and
replies containing words like "note" or "not" resolved as deny instead of
being recorded as free-text answers. Since this gates parked unattended
actions, a false allow is the worst-case direction.
Keyword intent now requires word boundaries (with the common -d forms added);
emoji checks stay as substring matches. Anything that matches neither list
falls through to the existing free-text path, which records the reply verbatim
instead of acting on it.
Test button and saved pill follow the required-secret field (or the first field for
cloud providers); Bedrock/Vertex add-model rows get a family selector.
gemini/ and claude/ ids reuse the native providers; openweight/ goes through the
MaaS OpenAI-compat endpoint with an auto-refreshed google-auth bearer.
Credentials: service-account JSON or Application Default Credentials.
claude/ ids use Anthropic's native Bedrock client; everything else goes via Converse.
Credentials: explicit keys, named profile (incl. SSO), or the ambient AWS chain.
The picker gates ollama:* on a live local probe, so this passed only where Ollama runs and failed in CI.
The probe's own behaviour stays covered by test_ollama_models_gated_on_liveness.
Boundary-hardening pass addressing three audit findings on the local sidecar.
Shell command allowlist (andrewyng/openworker#28):
- Replace prefix-string matching in PermissionEngine._command_allowed with
argv-aware matching: reject any command containing shell operators
(; & | > < ` $( ( and newlines) before consulting the allowlist, then require
the allowlisted entry's tokens to be an exact argv prefix. This closes the
auto-run bypass where an allowlisted "git status" also auto-ran
"git status && rm -rf ~", pipes, redirection, and command substitution.
- Drop language interpreters / package managers (python, python3, node, npm,
npx) from DEFAULT_ALLOWED_COMMANDS — allowlisting an interpreter allowlists
arbitrary code (python3 -c "..."), defeating approval gating. Read-only
inspection commands and pytest remain.
MCP OAuth loopback (andrewyng/openworker#29):
- Verify the OAuth state at the loopback boundary. The MCP SDK already validates
state (compare_digest), so this is not a CSRF fix but defense-in-depth: capture
the state from the authorize URL and have deliver_callback ignore a callback
whose state does not match WITHOUT consuming the pending future, so a stray or
forged local hit can no longer abort a user's in-progress sign-in. Falls back to
prior accept-any behavior when no state was captured.
WebSocket ingestion caps (andrewyng/openworker#38):
- Bound a single user_message frame in the session WS loop: max text length,
max attachment count, and max total attachment bytes. Oversized frames get a
visible error frame and are dropped instead of being buffered into a turn; the
socket stays alive. Guards the unauthenticated loopback socket against cheap
memory spikes.
Tests:
- Allowlist: reject operator chaining (8 variants), argv-boundary matching, and
interpreters-not-auto-allowed-by-default.
- OAuth: state extraction, and mismatched/missing state ignored without consuming
the flow while the matching state still resolves it.
- WS: oversized text and too-many-attachments rejected with an error frame, and a
normal message still works afterwards.
Full suite: 865 passed (1 pre-existing unrelated failure in
test_provider_router::test_manager_curated_models, present on origin/main).