Commit Graph
380 Commits
Author SHA1 Message Date
James Yang 8cfd5b5bfe Gate workspace MCP config behind WorkspaceTrustStore.
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
2026-07-26 17:41:56 -04:00
Osamaali313 330e12131e Fix attachment de-dup stripping real digits from the filename
The collision-rename loop in email_download_attachment used
`target.stem.rstrip('-0123456789')`, which strips every trailing digit and
dash, not just a previously-appended `-N` suffix. So saving a second
`invoice_2024.pdf` produced `invoice_-1.pdf`, `IMG_20240115.jpg` became
`IMG_-1.jpg`, and Outlook's `image001.png` became `image-1.png`.

Strip only a trailing `-<number>` suffix with a regex, preserving the
original digits (and still incrementing correctly on repeated collisions).
2026-07-26 23:40:00 +03:00
Rocky DeStefano 6fb3b67b00 test: fix flaky ui-refresh e2e by quiescing the auto-title call before mute assertions
When the approved turn completes, mark_idle spawns the auto-title completion
as a fire-and-forget task running provider.complete via asyncio.to_thread. The
scripted E2EProvider records that call (then raises on the exhausted turn
queue, which autotitle swallows), so one extra provider.calls entry lands at a
nondeterministic time - frequently between the calls_before snapshot and the
'provider not re-invoked' assertion in the mute step. Wait for SID to leave
mgr._autotitle_inflight (the settling idiom test_autotitle.py already uses)
before snapshotting, so the title call is deterministically included in the
baseline. The mute guarantee is asserted exactly as before. Verified 15/15.
2026-07-26 12:46:06 -06:00
Rocky DeStefano 0d9df38ae9 test: fix flaky test_scheduler_runs_due_task_and_advances near minute boundaries
The test forces an every-minute cron task due and sleeps a fixed 0.2s with
tick_seconds=0.05. After the first (catch-up) run the scheduler correctly
advances next_run to the next minute boundary; when the test happens to start
within ~0.2s of a boundary, that boundary falls inside the sleep window and
the task legitimately fires a second time, failing 'assert ran == [t.id]'.
Harness race, not a scheduler bug (the running-guard and post-run advance are
correct). Wait on an event set by the fake runner and stop the scheduler
immediately after the first run - the advance is synchronous once the runner
returns, so no second tick can fire. Verified 15/15 plus an adversarial
shifted-clock repro at the minute boundary.
2026-07-26 12:45:54 -06:00
Rohit C Prasad b719227a9a Bedrock settings: one auth method at a time
'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.
2026-07-25 22:55:28 -07:00
Rohit C Prasad 2a882c09d3 Add Nemotron Super 3 120B to the Bedrock model matrix
Live-verified on Converse; tool calls are sequential, so parallel stays off.
2026-07-25 22:11:08 -07:00
Rohit C Prasad 333f589c80 Support Bedrock API keys (bearer auth)
New optional field: paste the console-generated key, no CLI/IAM setup needed.
Takes precedence over SigV4 credentials, matching boto3; live-tested on Converse.
2026-07-25 21:58:24 -07:00
NIKHIL PENTAPALLI b922311a79 fix(inbox): match approval keywords as whole words, not substrings
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.
2026-07-25 18:33:16 -07:00
Rohit C Prasad 50463bba00 Package boto3 for Bedrock and pin google-auth
New [bedrock] extra; desktop builds and CI install it; PyInstaller collects
boto3/botocore so the lazy import works in the bundled sidecar.
2026-07-25 16:23:56 -07:00
Rohit C Prasad 241af5e15f GUI: multi-field provider credentials and add-model family dropdown
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.
2026-07-25 16:22:27 -07:00
Rohit C Prasad 050cc894e7 Add Google Vertex AI provider with per-family dispatch
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.
2026-07-25 16:19:00 -07:00
Rohit C Prasad 8cb9524f1f Add AWS Bedrock provider with per-family dispatch
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.
2026-07-25 16:15:18 -07:00
Rohit C Prasad ee495b9006 Add OpenRouter as an OpenAI-compatible reseller provider
Descriptor + curated matrix rows + sk-or- key auto-detect (server and GUI).
2026-07-25 16:07:33 -07:00
Rex e241019e61 Create the sidecar resource dir in build.rs so a fresh clone builds
tauri-build validates every bundle.resources path on each cargo build, dev
included, but binaries/sidecar is only staged by the release scripts and
/binaries is gitignored — so `npm run tauri dev` on a fresh checkout failed
with `resource path 'binaries/sidecar' doesn't exist`.

Create the empty placeholder in the build script. Dev needs no packaged
sidecar (server_bin() falls back to the venv server) and tauri-utils skips
empty resource directories, so the bundle is unchanged. Putting it in
build.rs rather than setup_dev_env.sh also covers Windows, where the bash
bootstrap script never runs.

Fixes #131
2026-07-25 18:08:12 +02:00
malin1997 671b321f66 gui: complete Chinese translations and enforce locale contracts
Complete the remaining Simplified Chinese UI copy, align runtime interpolation arguments with locale placeholders, and render translated rich text through Trans. Add contract tests so placeholder drift and unresolved values fail in CI.
2026-07-25 22:41:25 +08:00
malin1997 03c145cf3c gui: decode percent-encoded non-ASCII artifact paths
micromark percent-encodes non-ASCII characters in link hrefs, so an
artifact link to a Chinese/Japanese/Korean filename dispatched the encoded
path and the backend 404ed on the literal %E6… name. Decode the path (and
strip a leading slash so it stays workspace-relative) before opening.
2026-07-25 22:20:56 +08:00
malin1997 6ff0f0fac9 gui: add i18n infrastructure with English and Chinese locales
Introduces react-i18next across the GUI (fixes the frontend portion of #121):
all user-facing strings in ~50 components move from hardcoded English
literals to t() keys, with complete en and zh-Hans locale files under
src/locales/. English remains the default; the language follows the system
locale unless the user picks one explicitly in the new Settings > General
language switcher. Vitest initializes i18n with the English resources so
existing assertions keep passing.
2026-07-25 22:19:40 +08:00
Rohit Prasad db93d75bf6 Merge pull request #101 from andrewyng/meta-muse-spark
Add Meta Model API provider with Muse Spark 1.1
2026-07-25 01:27:39 -07:00
Rohit C Prasad fc6ce501dd Add Meta Model API provider with Muse Spark 1.1 2026-07-25 01:21:07 -07:00
Rohit Prasad f467c4ca73 Merge pull request #115 from andrewyng/rpSlackApprovalOwners
Harden Slack approval handling
2026-07-25 00:38:50 -07:00
Rohit P 7656952692 security: harden Slack approval handling 2026-07-24 22:33:13 -07:00
Rohit Prasad 56b3c62864 Merge pull request #107 from andrewyng/rpSecurityHardeningFollowup
Security Hardening
2026-07-24 22:05:43 -07:00
Rohit P ac83bc0490 security: complete local access protections 2026-07-24 18:44:39 -07:00
Rohit P 3f5ac872ca security: refine workspace trust controls 2026-07-24 18:44:39 -07:00
Rohit P 8ee0a0d082 security: strengthen session handling 2026-07-24 18:44:39 -07:00
Rohit P 9cc2761998 security: tighten permission handling 2026-07-24 18:44:39 -07:00
Rohit Prasad 54b4bfd82d Merge pull request #103 from andrewyng/fix-ci-ollama-probe
Pin the Ollama probe in the curated-models test
2026-07-24 17:37:29 -07:00
Rohit Prasad dd33f2cab0 Merge pull request #49 from fahadsiddiqui/harden/boundary-oauth-shell-ws
fix: harden local trust boundaries (shell allowlist, MCP OAuth loopback, WS ingestion)
2026-07-24 15:43:59 -07:00
Rohit C Prasad 2325728f3f Pin the Ollama probe in the curated-models test
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.
2026-07-24 12:48:38 -07:00
SEUNGWOO LEE 2b588fb8cc Make the conversation-log shrink rewrite atomic
ConversationStore.save() appends new messages on the common path, but when a
turn reduces the message count (context compaction / summarization) it rewrites
the whole .jsonl with open(..., "w"), which truncates the file at open(). A crash
mid-rewrite then leaves a truncated or empty log, permanently losing the
conversation history.

Write the reduced log to a temp file and replace() it in one atomic step -- the
same tmp-then-replace pattern subscriptions.ChannelBuffer._save() already uses.

Add tests/test_conversation_atomicity.py covering the crash path (history
preserved when the write fails partway) and the happy path (reduced set
persisted, no leftover temp file).
2026-07-24 14:28:16 +09:00
Saidheerajgollu 8c199ffdda Tolerate a corrupt line when loading a conversation .jsonl
`_read_jsonl` parsed every line with a bare `json.loads` inside a list
comprehension, so a single malformed line raised `JSONDecodeError` and
took the whole `load()` down. An append interrupted mid-write (process
crash, full disk) leaves exactly that: one truncated trailing line — and
from then on every surface that opens the session errors on load, with
no way back short of hand-editing the file. The session is effectively
bricked, including its recoverable history.

Skip unparseable lines and keep the good messages. This matches how the
rest of this module already treats JSON (the inline-blob and roots/grants
loaders all swallow `JSONDecodeError` and fall back) — `_read_jsonl` was
the one strict outlier on the hot session-load path.
2026-07-23 14:13:07 -07:00
Saidheerajgollu 03c4a16f95 Reject path-traversal session ids in the conversation store
A session id is joined straight into a filesystem path (`<id>.jsonl`),
and session ids come from client-controlled surfaces — the
`/ws/session/{session_id}` WebSocket route and REST paths all take the
id from the URL. Nothing validated it, so an id like `../evil` escaped
the conversations directory: `ConversationStore.save(SessionRecord(
session_id="../evil", ...))` wrote `evil.jsonl` one level ABOVE
`conversations/`, and a crafted id could clobber or place files
elsewhere under the state dir.

`_file()` — the single chokepoint every conversation-file path flows
through — now rejects ids that aren't a safe single path component and
confirms the resolved path stays inside `conv_dir`. The accepted
charset (`[A-Za-z0-9_-]{1,128}`) is a superset of every id the app
generates (uuid4 hex, and the `__run__`/`__task__`-prefixed automation
threads), so no legitimate session is affected; `load()` of an unknown
or unsafe id still returns None (the DB lookup misses before any file
IO), not an error.

The public `is_safe_session_id` helper is exported so the one other
site that turns a session id into a path — `_provision_scratch` in the
server manager — can reuse the same guard in a follow-up.
2026-07-23 14:09:28 -07:00
asklokesh c2adf7320c Fix automation scheduling: DST-correct 'local' times and off-by-one weekday labels
Two schedule bugs, both in the default-local, everyday path.

1. DST: one-time 'local' tasks fired at the wrong wall-clock across a DST
   boundary (coworker/automation/store.py)

   _tz("local") returned datetime.now().astimezone().tzinfo, a FIXED offset
   equal to whatever was in effect at compute time. A "once" task created in
   summer (EDT, -04:00) for a winter date (EST, -05:00) bound the naive
   datetime to -04:00, so 08:00 fired at 07:00 - an hour early - and for a
   one-shot task next_run is computed once at creation and never self-heals.

   Fix: _tz returns None for 'local' (and for an unknown IANA name) instead of
   a frozen offset, and the naive datetime is left naive. datetime.timestamp()
   and croniter over a naive local base apply the correct local DST offset for
   the actual fire date via the C library. Named IANA zones still anchor via
   ZoneInfo. No new dependency.

2. Weekday labels were a day late (coworker/automation/models.py)

   Cron day-of-week is 0/7=Sunday, 1=Monday..6=Saturday, but _DOW started at
   Monday, so `_DOW[int(dow) % 7]` rendered dow 1 (Monday) as "Tuesday" and
   dow 0 (Sunday) as "Monday" on every weekly automation card. Reordered the
   list to start at Sunday to match cron semantics.

Regression tests added; an existing assertion that encoded the off-by-one
("Monday" for cron dow 0) is corrected to "Sunday".
2026-07-23 16:44:21 -04:00
Fahad Siddiqui 657cf03460 fix: harden local trust boundaries (shell allowlist, MCP OAuth loopback, WS ingestion)
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).
2026-07-24 01:41:05 +05:00
Yashas 4766e59c47 Keep ripgrep searches out of generated directories (#10)
* Skip generated directories in ripgrep searches

* Apply ripgrep exclusions after user globs
2026-07-23 12:29:19 -07:00
Rohit P 4ffc73f1e8 README: Minor updates. 2026-07-23 11:26:19 -07:00
Rohit C Prasad f7c70a2478 README: add how-it-works diagram from the website 2026-07-23 09:29:00 -07:00
Rohit C Prasad 062b1b12b3 Update README 2026-07-23 09:23:56 -07:00
Rohit C Prasad 0e48499075 README: link the website 2026-07-23 09:14:26 -07:00
Rohit C Prasad 9fc4bc43e6 Rewrite README: download links, capabilities, architecture, run-from-source 2026-07-23 09:01:29 -07:00
Rohit C Prasad da1d25373c Prepare app release 0.1.6: version bump v0.1.6 2026-07-23 08:49:30 -07:00
Rohit C Prasad 27d72e5c5e Add Kimi K2.7 Code via Together to the model matrix 2026-07-23 08:41:21 -07:00
Rohit C Prasad eae5fbd8d0 Fix Anthropic extended thinking for current model families
Adaptive thinking for 4.6+/newest models, budgets kept for older ones.
Safety-refusal fallback to Opus for the newest tier; add Inkling via Together.
2026-07-23 08:25:47 -07:00
Rohit C Prasad ba99978e03 Enable Claude extended thinking by default, drop the settings field
Fixed 8192 budget; the provider profile key stays a hidden override (0 = off).
Per-turn composer control is future work.
2026-07-23 07:57:04 -07:00
Rohit C Prasad 2968254713 Keep the BETA chip on the wordmark line
Smaller raised chip, nowrap on the wordmark — no second line under the traffic lights.
2026-07-23 07:50:27 -07:00
Rohit C Prasad a9458524e8 Blur-save non-secret provider fields on configured providers
The Test button was the form's only save path — extras like the thinking budget
silently never persisted. Blur saves with a Saved flash; empty clears.
2026-07-23 07:43:24 -07:00
Rohit C Prasad 3d00187310 Add BETA tag: sidebar wordmark, boot splash, onboarding, README
Quiet gray chip, presentation-only — never in bundle or artifact names (updater safety).
2026-07-23 07:43:24 -07:00
Rohit C Prasad f2b8fde428 Keep Retry offered across model switches
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.
2026-07-23 07:43:24 -07:00
Rohit C Prasad d10990bdf7 Coerce union-typed JSON Schema for Gemini tool declarations
Vendor MCP schemas with type: [a, b] failed SDK validation and killed every Gemini turn.
Null unions become nullable; multi-type unions become anyOf.
2026-07-23 07:43:24 -07:00
Rohit C Prasad 52038c2136 Fix boot splash mark and stuck model picker on cold start
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.
2026-07-23 07:43:24 -07:00