A descriptor can carry a vendor-hosted MCP server URL (mcp_url); its curated
tools are ConnectorToolDefs named mcp__<connector>__<vendor tool>, so Access
bullets, per-tool toggles, session gating, and read/write approval all ride
the existing machinery. Connect seeds the server config (include_tools = the
pin, re-derived at session build so stale config can only shrink the surface)
and runs the local DCR OAuth flow — no cloud sign-in, no vendor app, no broker.
Connector-backed servers are hidden from the Settings MCP tab; disconnect
forgets tokens + registration + config. jira keeps its manual token path (the
profile mode picks which tool set is live); monday.com is new and one-click
only, with a custom brand glyph. asana's pins ship dormant — their V2 server
rejects dynamic client registration, so its one-click waits on a broker-routed
callback. Also: catalog typeahead aliases for jira/monday/asana.
- outlook_list_events now uses Graph calendarView (recurrences expanded,
ordered by start) with a default now→+7d window and optional start/end
- outlook_create_event gains attendees, location, and a teams_meeting flag
- new tools: outlook_update_event (PATCH, only provided fields),
outlook_delete_event, outlook_respond_event (accept/decline/tentative)
- connector copy now covers calendar (blurb, About, Access bullets, onboarding row)
- descriptors gain an aliases field surfaced in /v1/connectors; the Access
catalog typeahead matches on it (searching "calendar" finds Outlook)
OAuth 2.1 + PKCE + Dynamic Client Registration via the SDK's client
provider; tokens in the SecretStore, loopback callback on the sidecar.
MCP tab gets sign-in/out states and a curated Granola quick-add.
Step 2's card gallery becomes six benefit rows (one-line details) + one
combined grayed Google row; the pinned band flips from sign-in ask to green
congrats in place, rows grow Connect pills — zero layout shift at sign-in.
One footer button morphs quiet 'Continue without sign-in' → black Next;
static footnote. Modal 700→560px; the shared form's custom-endpoint
disclosure moves below the key-help line.
The post-Test auto-return stashed the just-saved key via a stale closure, so
revisiting a connected provider restored the plaintext key instead of the
masked placeholder + saved pill. Coming-soon cards get a static status line
(hover-only read as broken); attio joins the one-click gallery so the grayed
Google pair sits together on an even last row.
Appearance renames to General and absorbs Files; Token savings moves under
Models. The onboarding provider gallery/key form is extracted to a shared
providers/ProviderSetup module rendered by both surfaces, with Settings-only
extras: last-used on cards, Remove key (new DELETE /v1/providers/{name}),
and a cross-provider composer-picker card.
Settings/Connectors sub-nav headers sat under the floating reveal button
(and traffic lights in the overlay build) when the sidebar was collapsed —
collapsed state now pads the sub-nav below that strip. Personas management
(Settings tab + Manage personas… menu entry) hides behind a launch flag,
default off; e2e keeps the flows covered via the localStorage override.
One-click connect failed with "outlook has no managed OAuth path" — the
connector→provider map never got an outlook entry, though the descriptor,
broker provider, and scopes were all ready. Guard test now asserts every
managed connector has a mapping.
PDFs attach as OpenAI file parts; Anthropic/Gemini convert to their
native blocks. Models without native PDF support get a local fallback
at send time — pypdf text extraction or pypdfium2 page images, chosen
in Settings -> Token savings. Attach-time page/size thresholds
(default 20 pages / 10 MB) reject oversized PDFs with a notice.
Updater plugin with signed artifacts (minisign), latest.json composed by
release CI, update prompt in-app. Endpoints: download.opencoworker.app
with GitHub Releases fallback. Version 0.1.2.
- Boot: sidecar ships as a onedir bundle via the resources slot (onefile
self-extraction cost ~7s of splash every launch; now ~0.5s warm); build
scripts stage+sign the folder; splash wordmark aligned to the traffic
lights; minimumSystemVersion 12.0 (whisper.cpp build floor).
- Cloud sign-in: connection restore runs in the background after the
callback responds; GUI polls fast (500ms) while a sign-in is in flight.
- Onboarding: provider page keeps unsaved drafts across switches and shows
the connected state on the form; tools page rebuilt to the owner design
(value headline, Secure-by-design card, single primary action).
- Composer: OS file drags reach the existing drop handlers in the desktop
shell (native drag-drop interception disabled + window drop guards).
Replace the hand-drawn connector glyphs with official monochrome brand marks
(via simple-icons; Slack/Salesforce/Outlook/Canva vendored as path data), and
backfill logo + brand_color on every descriptor. Add six new connectors with
tools (reads never gate, writes require approval) plus two catalog placeholders.
Without it Tauri defaults the release deployment target to 10.13, and
whisper.cpp (voice input) uses std::filesystem, available only from
10.15 — so release DMG builds fail while cargo check/test and tauri dev
(no target pinning) all pass. 12.0 was already the honest floor: the
app ships Apple-Silicon-only and §37's compatibility gate requires
macOS 12+ for voice. NB when changing this value: cmake caches the old
target — purge target/release/build/whisper-rs-sys-* or the build keeps
failing after the fix.
Notarization adds a minutes-long round-trip that local iteration doesn't
need — locally built DMGs carry no quarantine flag, so Gatekeeper never
prompts on the building machine. The flag signs (seconds) and skips
submit/staple with a do-not-distribute banner; unset identity remains the
fully-unsigned path.
Signing alone still leaves downloads facing macOS's 'Move to Trash' malware
dialog — notarization is what removes it (learned live 2026-07-15; spctl
rejected a signed-but-unstapled DMG). When APPLE_SIGNING_IDENTITY is set,
step 5 now signs the .dmg container, submits via notarytool with App Store
Connect API-key vars (exported, $OCW_NOTARY_ENV, or .ocw-notary.env one
directory above the repo — shared across clones/worktrees, never committed),
staples, and runs the same spctl assessment Gatekeeper runs on download,
failing the build rather than shipping the dialog. Missing vars degrade to
a signed DMG with a loud warning; unsigned dev builds are untouched.
Review fixes: hardened-runtime signing needs com.apple.security.device.audio-input
for microphone capture — without it voice input works in dev and is denied by
macOS in the shipped app (Info.plist's usage string is only prompt text).
start_dictation becomes async + spawn_blocking: opening the input device blocks
on the one-time macOS permission dialog, which froze the UI from a sync command.
The model download gets 30s connect/read timeouts so a stalled connection errors
instead of hanging (cancel is only observed between reads). §37 wording now
matches the code (SHA-256; restart, not resume). New unit tests drive the
composer through a mocked desktop bridge: no mic in the browser build, muted
mic deep-links to Settings, record→stop inserts an editable draft with Send
protected, and a start failure surfaces without wedging the control.
send_message text rendered as an unclamped quote and PreviewBlock clamped
by LINES only, so a one-paragraph Slack digest (no newlines) ballooned the
card to full-transcript height (owner repro 2026-07-15). Previews now clamp
by characters too (5 lines / 420 chars, expandable in place); short
one-liners keep the inline quote. send_file comments get the same treatment.
A clean clone had no runnable backend and no instructions to create one —
the GUI README assumed platform/.venv existed and still described the
desktop shell as future work. Add setup_dev_env.sh (venv + editable
coworker install + the aisuite .pth this checkout's import wiring needs,
verified by booting the server to a 200 health check) and rewrite the
README's from-source flows: browser two-terminal dev and tauri dev, which
already falls back to the venv server when no packaged sidecar exists.
Several people can run the app into one channel and every post arrives as
the same bot. The managed install already records WHO connected (Slack's
authed_user): store that member id on the slack:team profile, resolve the
display name once via users.info (users:read, granted since wave 1),
cache it, and prefix send_message text and send_file comments with
"[<name>] ". Truthfulness rules: manual Socket-Mode installs have no
authed_user so their posts stay bare; DMs skip the prefix; resolution
failure degrades to no prefix and never blocks a send. P2 (native
username override via chat:write.customize) replaces the text prefix.
Six fixes from the 2026-07-12 external bug-report doc:
- Agents get a folder-scope guardrail in the environment block (stay in
the workspace + granted roots, request_directory otherwise, shell
commands included) and the bundle gains Info.plist usage strings, so
macOS folder/photo permission prompts stop appearing unexplained at
moments the user can't map to any action.
- Answering a live approval no longer flashes the same request again:
the resolve handlers optimistically drop the parked per-session Inbox
mirror (it stayed 'pending' in the polled copy for up to a cycle and
the dock slot fell through to it). Regression e2e fails pre-fix.
- Onboarding model step: Continue verifies automatically (enabled once
the key fields are filled; 'Checking…'; failure stays with the error).
Test remains an optional explicit check.
- Recipe step: the gated Create-automation button names its missing
piece ('Pick a channel to post to first') instead of graying silently.
- Artifact 'Copy path' copies the absolute path (server now returns
abs_path alongside the workspace-relative identifier).
- The session drawer's persona why-connect pane is hidden for now
(SHOW_PERSONA_PANE flag; no persona mentions until relaunch).
tsc + 28 unit + 107 e2e + 708 pytest green.
- Provider key fields lose the storage-note help line (registry-level,
so Settings/Models loses it too — manual territory, not UI).
- Any base_url on a keyed provider collapses behind 'Configure custom
endpoint' — OpenAI's default-less endpoint was still visible on the
first pass.
- Verify success moves onto the Test button ('Connected'); the status
line keeps errors only.
- Done step: the specialist-gallery card and the per-session-scope line
are hidden for now (gallery plumbing kept for its return).
- Modal height fixed at 700px across all three steps, sized to the
tallest (recipe ~593px content, measured); steps are flex columns
with bottom-pinned action rows; overflow scrolls inside the step.
141 provider pytest + tsc + 28 unit + 106 e2e green.
Automations, Activity, and Inbox each had their own indentation and head
style, and inbox-delivery config hid under Connectors with the mirror
channel editable in two places.
- Every top-level page now uses the Connectors/Activity shell: centered
column, PanelHead, cards. Automations drops the icon-title and the
boxed banner; its list/detail/templates restyle onto the shell.
- Inbox gains page-level tabs: Pending (the queue, unchanged) and
Configure — the former Messaging-routing page moved whole into
InboxConfigure.tsx (mirror, DM route, subscriptions, Unrouted with
the warning badge). Pending's routing line is read-only and links to
Configure; the duplicate inline mirror editor is deleted. The mirror
target and routing line show channel names when known.
- Connectors sub-nav shrinks to Connectors and MCP servers.
- Legacy page-shell CSS removed (page-col, sa-view-*, sched-banner,
tmpl-card family, sa-back, new-action).
- e2e: inbox routing test rewritten against the Configure tab;
sidebar-account's Unrouted-location test follows the move.
tsc + 28 unit + 106 e2e green.
ChannelPicker separates display from value: at rest the box shows
#channel-name when known (from a pick, the recent list, or a roster
hit); focusing flips to the raw address for editing, the tooltip always
carries it, and a pick commits by blurring. The stored target and the
automation instructions keep the raw address. Applies to both surfaces
(onboarding recipe + session channel subscriptions); the consent line
names the channel too.
The fixed day+time cadence pairs were limiting: 'When' is now a day
picker (Mon-Sun, Weekdays, Every day) beside a free time field, with
the cron assembled from the two. Digest titles and instructions are
cadence-neutral ('since the last digest' instead of 'the past week').
106 e2e / 28 unit / tsc green.
The App install page dead-ends when the App is already installed on the
user's account: GitHub shows 'Configure' and never fires our callback,
so the desktop waits forever (owner hit this on a fresh state dir —
reinstalls and second machines will hit it routinely). The broker and
sidecar already support the authorize flow for exactly this (plain
OAuth; the broker lists the user's installations and posts the same
loopback payload as a fresh install) — it just wasn't reachable from
the GUI.
connectManaged() now passes flow through; the Add-installation modal
and the onboarding GitHub row gain an 'Already installed? Link it'
secondary action. New e2e pins the modal link to flow=authorize.
106 e2e / 28 unit / tsc green.
The broker-bounce change (eda23c9, 07-09) pointed the authorize leg's
redirect_uri at the broker's stable callback but left the token
exchange sending the old loopback URI. RFC 6749 requires the exchange
redirect_uri to byte-match the authorize one, so the auth server
rejected every exchange — all cloud sign-ins since 07-09 failed with
'token exchange failed' after a successful browser leg (caught by the
owner's fresh-install walkthrough; also explains the earlier onboarding
Slack-connect failure, which begins with sign-in).
The unit test now derives the redirect_uri from begin_login's authorize
URL and pins the exchange to it — the old test mocked the POST without
asserting redirect_uri, which is exactly how this slipped.
708 pytest green.