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).
Switch notices no longer consume the retry guard or hide the button.
Error, switch model, Retry is the intended recovery path; retry runs on the new model.
Splash shows the real 6-point OpenWorker star, not the 4-point sparkle glyph.
Settings reload after the health check lands, so the picker can't stay on Loading models.
Gmail/Calendar/Drive show a disabled Coming-soon button; the server refuses the flow too.
Manual token connect and already-connected accounts are untouched.
New reasoning_delta event; traces persist as a display sidecar stripped from provider feeds.
Sources: compat vendors' reasoning_content and Gemini thought summaries (include_thoughts).
Live-verified on GLM via Together and Gemini 3; Gemini tool loops stay healthy.
Picker stays live for the session; switches persist a model_switch notice (§17 revised).
Rebinds refused mid-turn; images become placeholders for non-vision targets at send time.
Signatures ride the assistant message as a _gemini sidecar and are reattached in tool loops.
Thought-flagged parts are filtered from answer text; foreign sidecars stripped on the OpenAI wire.
Live-verified both Gemini 3 models end-to-end; without the echo they 400 on every tool loop.
Engine appends a display-only notice message on error/interrupted; providers never see it.
New retry frame re-runs a failed turn with no new user message, guarded on the error tail.
GUI renders persisted notices on reload and a Retry button on the trailing error.
GUI flushes the streaming buffer into a durable item on interrupted/error.
Engine persists partial text on the provider-error path like the stop path.
e2e red-green verified; full suites pass.
Shell injects the OS; Windows/Linux keep the native title bar with no traffic-light insets.
Thin scrollbars on Windows/Linux so panels stop losing width to classic scrollbars.
Stream drops between chunks (+ the pre-first-token wait); pending approvals/questions/plans resolve as interrupted.
Running shell commands die via an executor interrupt hook; skipped tool calls still get results (no orphans).
Also fixes delete_session calling a nonexistent engine.interrupt().
Keyless is not configured — ollama:* picker entries render only while /api/tags answers (30s cached probe).
Stops phantom local models on machines without Ollama.
Until /v1/settings supplies the list the picker is a disabled "Loading models…" chip.
The baked-in list had gone stale and offered phantom ids during the boot race.
A top-level arguments key named "items" shadows minijinja's .items() in Together's GLM-5.2 chat template — every replayed turn 400s.
Old key still executes; GUI renders both; regression tests added.
Split-scene carousel on the Slack connector page teaching mentions, threads, and approvals.
Post-it annotations per beat; static under prefers-reduced-motion.
Co-authored-by: Devika <devikaverma11@gmail.com>
Slack installer joins the workspace allow-list on managed connect; MCP interactive oauth only from explicit connects.
Run-started toast over a new app-wide /ws/events socket; Automations e2e locators scoped to the account menu.
Release workflow adapted to the root layout (no monorepo paths, no .pth venv wiring).
New CI runs the coworker pytest suite and GUI unit tests; e2e joins once the inherited failures are fixed.
Mention-spawned sessions now list chronologically like any other session, in
both layouts; the row's origin icon (with the channel · workspace tooltip)
carries the provenance. The collapsed band hid fresh mentions below week-old
sessions. UX-DECISIONS §31 revised.
Personas are disabled for the first release, so the subtitle repeated the same
label on every row. When personas return they surface on hover, not as a second
line. UX-DECISIONS §7 updated; e2e spec added.
The venv moves to .venv at the repo root and aisuite now installs from the
git-pinned dependency instead of a .pth into a parent checkout (bootstrap +
PyInstaller pathex). Updater/notary env files keep their one-directory-above-
the-repo convention. Guard the empty updater-overlay array expansion — under
set -u on stock macOS bash 3.2 it aborted every keyless (fresh-clone) build.
Imported from andrewyng/aisuite@1b4bbf303e
(contents of its platform/ directory, hoisted to the repo root).
Development history prior to this commit lives in that repository.
Co-authored-by: Devika <devikaverma11@gmail.com>