194 Commits

Author SHA1 Message Date
Cursor Agent
3a3e0b3e27
fix(api): satisfy typecheck for focusFactValue
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-07 10:22:30 +00:00
Cursor Agent
5387b38e54
fix(e2e): stabilize Phase 2 specs for mobile shell and empty project
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-07 10:01:41 +00:00
Cursor Agent
a5ef5dc5ce
feat(web): Phase 2 production readiness — locale, mobile, E2E matrix
Localize onboarding focus facts by profile locale (DE assertion).
Add mobile overflow/touch audits, deep-link matrix, extended console
checks, and E2E for empty project, session unavailable, and teaching
composer. Safe-area and Heute header layout on 390px viewports.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-07 09:31:52 +00:00
cynarAI
97787a0272
Production readiness phase 1: journey audit, fixes 1–4, E2E (#175)
* Production readiness phase 1: journeys doc, onboarding persistence, teardown, chat UX

- Add docs/PRODUCTION-READINESS.md with journey matrix and defect backlog
- Persist onboarding draft in session storage across reload
- Localize onboarding discovery main-bot role; German Chief of Staff string
- Destroy team sandboxes and home folders when deleting an account
- Keep composer/transcript visible during queued runs; extend send watchdog
- Add Playwright specs: reload persistence, DE intake, queued progress, console guard

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix CI: sync Lingui catalogs, onboarding name draft, E2E stability

- Localize Chief of Staff in discovery shell; sync all locale PO files
- Propagate Oo name to onboarding draft while typing
- Adjust DE intake, queued-run, and console-error E2E specs

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Format chat-queued-progress E2E spec

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* fix(api): scope account-delete sandbox teardown to owned spaces

Only destroy team sandboxes and homes for spaces the deleting user
owns with no remaining members or bots. Guard home removal under
dataDir/homes. Add unit and postgres journey coverage.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* chore: apply biome formatting

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-07 11:25:39 +02:00
cynarAI
0e1036b2ff
First-run onboarding UX: naming, locale, voices, Heute route, mobile drawer (#171)
* Fix first-run onboarding UX: naming, locale, voices, Heute route, mobile drawer

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix biome import order and formatting

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix biome formatting on OoStage and E2E specs

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix E2E specs for onboarding name step and Heute reload route

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix German onboarding heading matcher in E2E

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix German signup E2E, Escape menu stacking, and Heute rhythm click

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Make onboarding E2E helpers locale-aware for German UI

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix completeOnboarding language skip for Skip · locale label

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Use language-step Skip · locale button in completeOnboarding

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix German E2E: Nachricht composer, voice loading wait, Heute rhythm

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Stabilize Heute reload URL and German voice E2E assertions

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix Heute bootstrap redirect and German onboarding voices

When Kokoro lists no locale-matched voices, onboarding now uses offline
presets instead of showing English-only options. Shell bootstrap skips
bot redirects while the user is on /app/heute.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Relax German voice E2E name matching for voice labels

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Harden Heute route and German voice onboarding E2E

Rebased onto main with Expo SDK 57 typecheck fixes. Voice options expose
stable test ids; Shell keeps /app/heute across bootstrap and redirects
legacy /app/heute bot URLs. Heute coach E2E asserts URL after reload.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Format Shell after Heute route helper

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-06 20:15:36 +02:00
cynarAI
ba89574c83
Align Oo avatar eye spacing with evoo-logo (#173)
* Align Oo avatar eye spacing with evoo-logo overlap ratio

Derive small-eye placement from evoo-logo.svg center spacing (~0.94 of
r_big + r_small) for the shared rig, EvooMascot brand lock, and face crops.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Format brand-lock spacing test for Biome

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Align mobile Expo packages for install --check in CI

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* ci: retrigger after flaky chart-links E2E

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-06 18:56:40 +02:00
cynarAI
f8b4ce4b6d
Größere Header- und Sidebar-Icons (Web + iPhone) (#172)
* Increase header and sidebar icon scale on web and mobile

Raise shared --icon-sm/md/lg tokens (~25%) for web shell chrome, widen
chat header action controls, and align Expo home/thread header glyphs
with 44pt tap targets.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* ci: retrigger checks after flaky evo-deploy test

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-06 15:36:54 +02:00
cynarAI
6fc734b0ae
ci: pass publish-server-image digests via GHCR handoff tags (#169)
Build jobs already push each architecture by digest. Tag those manifests
with ephemeral ci-${run_id}-${image}-${arch} refs instead of uploading
digest marker files to Actions artifact storage, which is blocked by the
account-wide quota.

Publish resolves handoff tags back into RUNNER_TEMP digest markers, then
assembles semver/edge tags and attestation as before. Handoff tags are
removed after publish with continue-on-error.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-06 05:06:23 +02:00
cynarAI
a55ed44394
One product: evOo updates and name, phone actions, German on mobile, working account deletion, consistent settings (#168)
* Pull updates from evOo instead of the upstream Rakazo repository

The in-app server updater, the image-based compose files and the desktop
auto-updater still pointed at elie222/rakazo, so updating an evOo install
would have pulled Rakazo. They now follow cynarAI/evOo-local, where the
publish workflow already pushes the images. The disabled Rakazo production
deploy job is gone from CI; evo deploys itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Call the product evOo everywhere people see its name

The desktop setup window and menu, account emails, the iOS app name and
permission prompts, sign-in headings, messages bots post into connected chat
apps, the web fetch user agent, issue templates and the marketing site still
said Rakazo, or EvOo. They now say evOo. The marketing site no longer calls
evOo an alternative to itself.

Internal identifiers (package scopes, env vars, bundle ids, the desktop
productName that keys its data folder and keychain entry) stay as they are.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Open conversation actions by holding a row on a phone

Pin, move to folder, mark unread, duplicate, archive and delete only opened
on right-click, which iOS Safari never fires. Holding a conversation or a
folder header now dispatches the same contextmenu event, so the existing menu
opens under the finger; Android's own long-press event is not doubled and the
lift does not also open the conversation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Give small chrome icons a 44px touch area on phones

The browser and computer toggles, the activity bell, the create button and the
section plus buttons were 18 to 36 px targets. On touch screens each now gets
an invisible 44px hit area without changing how it looks; the create button
uses the same plus icon as the rest of the sidebar instead of a text glyph,
and the computer toggle gets an accessible name.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Add German to the mobile app

German speakers saw the phone app in English because the mobile UI only
knew English, Simplified Chinese, and Russian. The app now ships a German
catalog covering every mobile chrome string, follows a German device
language (de, de-DE, de-AT, de-CH, ...), and offers Deutsch in the Account
language picker.

Strings that also exist in the web German catalog reuse its wording so
web and phone say the same thing; the rest follow the same terms and the
informal "du".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Use the evOo sign-in strings in the German phone catalog

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Make account deletion work and give settings one section style

Deleting an account posted to the auth endpoint without a JSON content
type, so the server refused it and the button did nothing. The privacy
page now deletes through the auth client and returns to sign-in. It also
drops the claims it could not verify, the export button that only showed
an alert, and its own padding, and shows where the data and the computer
live. Notifications and media settings use the same SettingsSection.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Use status tokens, the shared switch and one German glossary

Voice stage dots, the onboarding progress bar and the Voice First button
used raw emerald, violet, cyan and rose; they now use foreground, success
and destructive like the rest of the app. The routine editor's hand-built
switch is the shared Switch. German says Unterhaltung for threads and
Space consistently on web and phone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Expect the evOo setup copy in the desktop e2e specs

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Translate the voice preview label in the German phone catalog

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Wait for the main thread URL without waiting for every resource

openMainBotThread already navigated with domcontentloaded; its URL check
then waited for the full load event, which can exceed 20 s on a busy
runner. The composer check that follows covers readiness.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-06 00:38:31 +02:00
cynarAI
b78eff1f7b
Run on the deployment's local models without any model choice, preselect Claude Opus 5.5 otherwise, explain why dictation is unavailable (#151)
* Recommend Claude Opus 5.5 by bumping the Pi model catalog

The pinned pi-ai 0.85.1 had no Claude Opus 5.5, so the connect flow fell
back to Opus 5. pi-ai and pi-agent-core 0.87.1 add it, and the keyless
recommendation now preselects it.

The new catalog names router aliases "Qwen Max Latest (Qwen3.8 Max)";
those now read "(auto-updates)" like the other alias labels, so no
picker entry claims to be "latest".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

* Name HTTPS as the cause when dictation is unavailable, and translate it

Over plain HTTP on a LAN address, Safari hides the microphone and speech
recognition, so voice mode said the browser had no dictation, in English
inside a German UI. Dictation now checks for a secure context first and
says to open the address with https://. Its other errors go through
Lingui too, and Safari's blocked recognizer gets a clear message instead
of "Dictation failed: not-allowed".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

* Serve a fake Kokoro voice server in the E2E harness

Onboarding's voice step connects to the local Kokoro server on its
default port, so the shell E2E failed on any machine where nothing (or a
misbehaving server) listened there: the failed request shows up as a
browser console error. The harness now starts an offline stand-in and
points KOKORO_BASE_URL at it, so the suite no longer depends on the
runner host's own voice server.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

* Show only the recommended sign-in until a model is connected

With no model connected, "Connect model" opened the full model settings:
provider list, tiers and a model picker, even though the recommendation
was already preselected. Until something is connected, the Models panel
now shows just the preselected model with its sign-in and API key field;
"Other providers" reveals the full settings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

* Give the second chart-links test the same cold-load allowance as the first

Each Playwright test runs in a fresh browser context, so the second test's
first navigation re-fetches the lazily imported Plot chunk just like the
first test's warm-up does. On a loaded CI runner that exceeded the default
20 s expect timeout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

* Use the recommended model as the Anthropic deployment default

A deployment that sets only ANTHROPIC_API_KEY and PI_DEFAULT_PROVIDER=anthropic
now runs Claude Opus 5.5, the same model the connect flow preselects, instead
of an older default.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

* Run every space on the deployment's local models without a connect step

A deployment with PI_DEFAULT_PROVIDER=local and RAKAZO_LOCAL_MODELS set still
asked every user to connect a model, because only OpenRouter and Anthropic
counted as a deployment model. Configured local models now do, keylessly.

RAKAZO_LOCAL_TIERS assigns local models to the tiers the agent delegates to,
for spaces that have not mapped their own, and a RAKAZO_LOCAL_MODELS entry can
name its own server (id=url) when the models run behind different endpoints.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

* Hide model choices when the deployment routes models itself

When the server provides its own models, the system picks and routes
them per task, so Settings no longer offers Models and bot settings no
longer offer Model and Thinking, on web and mobile.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

* Wait for the workspace pane's width transition before measuring it

The pane animates its width over 300 ms, so a single boundingBox read
right after it opens can land mid-transition on a slow runner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

* Allow chart re-renders inside the chart-links loops to take longer

Each navigation in the loops renders Observable Plot again. On a loaded
CI runner that exceeded the default expect timeout even after the
warm-up, so the per-iteration visibility checks get 30 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 23:18:40 +02:00
cynarAI
9062717eee
fix(evo-deploy): validate restart config before mutating checkout (#167)
Move restart-mode prerequisites (systemd unit default, tmux, compose file,
script hook, EVO_SYSTEMD_SCOPE) ahead of git fetch/install/build/migrate.
Explicit systemd with no EVO_SYSTEMD_UNIT falls back to EVO_SYSTEMD_AUTO_UNIT.
die() after checkout triggers rollback; rollback marks in-progress to avoid loops.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-05 22:39:46 +02:00
cynarAI
eb9c8847a4
Brand videos with a logo overlay and an intro before the cut (#161)
* Export videos for named platforms and check each file against its limits

Exports only knew aspect ratios, so a cut could still be rejected or
degraded on upload: too long for the platform, over its size limit, or
far off its loudness. export_video now takes platforms (TikTok,
Instagram Reels and Feed, YouTube Shorts and YouTube, Facebook Reels,
LinkedIn, X, Pinterest, Snapchat Spotlight), up to six targets with the
presets. Each platform is rendered to its frame with the export's fit,
its frame rate cap, a bitrate cap that also keeps the file under the
size limit, and its loudness target in one loudnorm pass.

Every render is probed and measured afterwards. A file over the size
limit is encoded once more at a bitrate scaled from what it averaged,
and audio more than 2 LU off the target gets one linear loudness pass.
The result is stored on the output as a pass, warn or fail check with
language-neutral issue codes. A platform the video is too long or too
short for is not rendered: the card lists it with the reason, the
other targets still render, and the bot is told to trim first.

The limits come from the earlier profile research, with its sources,
review date and unverified flag; content, disclosure and caption rules
are left out because they need inference or a review flow. Preset
definitions move to their own module so the platform schemas can use
their aspect ratios without an import cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* Add remove_silence step to edit_video

Talking-head clips are mostly cut by hand to drop pauses, and the bot had
no way to find them: it could only keep segments it guessed. The new
remove_silence step finds every pause quieter than threshold_db (default
-35 dB) and at least min_silence long (default 0.6 s), keeps padding
(default 0.12 s) on each side so word onsets survive, and cuts the rest
through the existing keep-segments graph, so picture and sound stay in
sync.

Steps run in order, so silence is detected on the timeline the steps
before it leave: the planner gets an analysis mode that sends that audio
through silencedetect into a null output, and ametadata prints the marks
to a file in the job directory. The detected speech is handed back to the
planner per step. Nothing silent makes the step a no-op; nothing left
fails with a clear error. At most 200 pieces are kept, bridging the
shortest pauses first, so the filter graph stays a sensible size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* Offer platforms in the media card's export menu and show their checks

The card's Export menu now lists the platforms first, by brand name and
untranslated, then the aspect ratios after a separator; the platform an
output was made for is left out, like its own shape. A platform export
shows the platform's name under the player, or, when its check did not
pass, one line for the first issue instead ("Too long for TikTok (max
10:00)"), in the warning or destructive color. Skipped platforms get
the same kind of line, and they stand in for the error of a job that
skipped every platform, so the card does not say the same thing twice
in two languages.

The E2E spec seeds a platform export with a failed check and a skipped
platform, and captures the card and the open menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* Add analyze_video to find cut points for the bot

To cut a social video well the bot has to know where the cuts fit, and it
could not look at a video at all. analyze_video is a read-only media job:
one ffmpeg pass prints scene changes (scene score above 0.3, one per cut),
pauses (silencedetect with the remove_silence defaults) and short-term
loudness (ebur128) to files in the job directory, and a transcript made
from the video by transcribe (an SRT whose parent is the video) adds when
each line is spoken. The job stores nothing; its card just says done.

The result reaches the bot through the existing media job path: the job
always wakes the bot that started it (the tool has no continue_after) and
the summary carries a short legend plus compact JSON. The runtime's
summary limit grows to 12,000 characters and the summary is fitted into
it: at most 200 entries per list, then every list shrinks evenly, keeping
the strongest cuts, the longest pauses and the start of the transcript,
with omitted counts. The wake prompt no longer claims the card shows the
result when a job made no files.

Jobs read derived artifacts through a new fetchDerivedArtifact, scoped to
the job's user like fetchArtifact. A migration admits the operation in the
media_jobs check and adds the tool to existing Studio specialists that
still edit video. The web card gets an icon; no new copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* fix(media): keep one skipped entry per platform on the card

A job that recovers after a crash runs again and reports the same
skipped platforms; the newest reason per platform now replaces the
earlier one instead of listing it twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* chore(db): add the widened media job operation check as NOT VALID and validate it separately

Follows the repository's migration convention: the constraint commits
without scanning media_jobs, and a second migration validates it with a
lock that lets reads and writes continue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* Brand videos with a logo overlay and an intro before the cut

edit_video gains an overlay step that lays a PNG, JPEG or WebP image over the
video at a corner or the center, sized to a share of the frame width, with
optional opacity and time range; tall frames keep the 9:16 safe areas clear.
concat takes at: "start" so an intro can go before the video. The image is
authorized like other inputs, read with its own demuxer and refused above
8192 pixels on a side. New Studio specialists are told to brand with the
user's logo and intro.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 22:39:21 +02:00
cynarAI
459d7c99e4
Pick the voice from a carousel in the app language, local voice only (#166)
* Pick the voice from a carousel in the app language, local voice only

Voice settings now list only the voices that speak the app language
(German shows German voices, English shows English ones) as a horizontal
carousel: one card per voice, a play button next to the name, tap to
make it the active voice. Changing the app language moves the saved
voice to that language. The hosted voice providers (ElevenLabs, OpenAI,
Cartesia, Fish Audio) are removed; Kokoro on the server is the only
voice. E2E runs against a stub Kokoro server so the carousel is covered.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxoSHAyzMXvdtwcDJZcn57

* test(e2e): measure the workspace panel width after its transition settles

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxoSHAyzMXvdtwcDJZcn57

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 22:20:06 +02:00
cynarAI
b2fb517286
Simplify the memory settings: what is remembered first, provider setup under Advanced (#164)
The memory pane opened with an untranslated provider description, a
provider picker, a scope toggle and API key fields, and pushed what the
agents actually remember to the bottom. Most people never connect a
semantic-recall provider, so that setup now sits behind a collapsed
Advanced disclosure (open while a provider is connected), and the
remembered documents come first.

- Drop the provider descriptions, the "Shared documents" heading and
  the per-document revision label.
- Replace the Isolated/Shared toggle pair with one "Share between bots"
  switch.
- Remove the unused standalone dialog mode of the memory pane.
- German: call the section "Gedächtnis" so it no longer reads like
  reminders ("Erinnerungen").


Claude-Session: https://claude.ai/code/session_01LWzKXyE6uDmmmzuod4oXNQ

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 20:30:30 +02:00
cynarAI
0122fef89e
Drop the transcript heading and empty-state hint in the conversation view (#163)
The encounter view showed an uppercase "Transcript" / "Conversation record"
label and "Start the conversation — <bot> is on stage above." before any
message. Both repeat what the screen already shows, so they are removed.


Claude-Session: https://claude.ai/code/session_01Vyov34VJPqjo6YgiGHnuEL

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 19:50:18 +02:00
cynarAI
3b2c46dd36
feat(media): remove pauses and find cut points in videos (#159)
* Export videos for named platforms and check each file against its limits

Exports only knew aspect ratios, so a cut could still be rejected or
degraded on upload: too long for the platform, over its size limit, or
far off its loudness. export_video now takes platforms (TikTok,
Instagram Reels and Feed, YouTube Shorts and YouTube, Facebook Reels,
LinkedIn, X, Pinterest, Snapchat Spotlight), up to six targets with the
presets. Each platform is rendered to its frame with the export's fit,
its frame rate cap, a bitrate cap that also keeps the file under the
size limit, and its loudness target in one loudnorm pass.

Every render is probed and measured afterwards. A file over the size
limit is encoded once more at a bitrate scaled from what it averaged,
and audio more than 2 LU off the target gets one linear loudness pass.
The result is stored on the output as a pass, warn or fail check with
language-neutral issue codes. A platform the video is too long or too
short for is not rendered: the card lists it with the reason, the
other targets still render, and the bot is told to trim first.

The limits come from the earlier profile research, with its sources,
review date and unverified flag; content, disclosure and caption rules
are left out because they need inference or a review flow. Preset
definitions move to their own module so the platform schemas can use
their aspect ratios without an import cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* Add remove_silence step to edit_video

Talking-head clips are mostly cut by hand to drop pauses, and the bot had
no way to find them: it could only keep segments it guessed. The new
remove_silence step finds every pause quieter than threshold_db (default
-35 dB) and at least min_silence long (default 0.6 s), keeps padding
(default 0.12 s) on each side so word onsets survive, and cuts the rest
through the existing keep-segments graph, so picture and sound stay in
sync.

Steps run in order, so silence is detected on the timeline the steps
before it leave: the planner gets an analysis mode that sends that audio
through silencedetect into a null output, and ametadata prints the marks
to a file in the job directory. The detected speech is handed back to the
planner per step. Nothing silent makes the step a no-op; nothing left
fails with a clear error. At most 200 pieces are kept, bridging the
shortest pauses first, so the filter graph stays a sensible size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* Offer platforms in the media card's export menu and show their checks

The card's Export menu now lists the platforms first, by brand name and
untranslated, then the aspect ratios after a separator; the platform an
output was made for is left out, like its own shape. A platform export
shows the platform's name under the player, or, when its check did not
pass, one line for the first issue instead ("Too long for TikTok (max
10:00)"), in the warning or destructive color. Skipped platforms get
the same kind of line, and they stand in for the error of a job that
skipped every platform, so the card does not say the same thing twice
in two languages.

The E2E spec seeds a platform export with a failed check and a skipped
platform, and captures the card and the open menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* Add analyze_video to find cut points for the bot

To cut a social video well the bot has to know where the cuts fit, and it
could not look at a video at all. analyze_video is a read-only media job:
one ffmpeg pass prints scene changes (scene score above 0.3, one per cut),
pauses (silencedetect with the remove_silence defaults) and short-term
loudness (ebur128) to files in the job directory, and a transcript made
from the video by transcribe (an SRT whose parent is the video) adds when
each line is spoken. The job stores nothing; its card just says done.

The result reaches the bot through the existing media job path: the job
always wakes the bot that started it (the tool has no continue_after) and
the summary carries a short legend plus compact JSON. The runtime's
summary limit grows to 12,000 characters and the summary is fitted into
it: at most 200 entries per list, then every list shrinks evenly, keeping
the strongest cuts, the longest pauses and the start of the transcript,
with omitted counts. The wake prompt no longer claims the card shows the
result when a job made no files.

Jobs read derived artifacts through a new fetchDerivedArtifact, scoped to
the job's user like fetchArtifact. A migration admits the operation in the
media_jobs check and adds the tool to existing Studio specialists that
still edit video. The web card gets an icon; no new copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* fix(media): keep one skipped entry per platform on the card

A job that recovers after a crash runs again and reports the same
skipped platforms; the newest reason per platform now replaces the
earlier one instead of listing it twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* chore(db): add the widened media job operation check as NOT VALID and validate it separately

Follows the repository's migration convention: the constraint commits
without scanning media_jobs, and a second migration validates it with a
lock that lets reads and writes continue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 19:16:03 +02:00
cynarAI
3291a32d37
Show About you inside General settings (#165)
The profile had its own settings tab although it is a few rows of personal
details. It now sits as one card under Account in General, hides itself
while empty, and the separate tab is gone.


Claude-Session: https://claude.ai/code/session_017gHYZ4c9Twex73v4ZasmMJ

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 18:36:54 +02:00
cynarAI
23d70971f8
feat(media): export videos for named platforms and check each file against its limits (#158)
* Export videos for named platforms and check each file against its limits

Exports only knew aspect ratios, so a cut could still be rejected or
degraded on upload: too long for the platform, over its size limit, or
far off its loudness. export_video now takes platforms (TikTok,
Instagram Reels and Feed, YouTube Shorts and YouTube, Facebook Reels,
LinkedIn, X, Pinterest, Snapchat Spotlight), up to six targets with the
presets. Each platform is rendered to its frame with the export's fit,
its frame rate cap, a bitrate cap that also keeps the file under the
size limit, and its loudness target in one loudnorm pass.

Every render is probed and measured afterwards. A file over the size
limit is encoded once more at a bitrate scaled from what it averaged,
and audio more than 2 LU off the target gets one linear loudness pass.
The result is stored on the output as a pass, warn or fail check with
language-neutral issue codes. A platform the video is too long or too
short for is not rendered: the card lists it with the reason, the
other targets still render, and the bot is told to trim first.

The limits come from the earlier profile research, with its sources,
review date and unverified flag; content, disclosure and caption rules
are left out because they need inference or a review flow. Preset
definitions move to their own module so the platform schemas can use
their aspect ratios without an import cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* Offer platforms in the media card's export menu and show their checks

The card's Export menu now lists the platforms first, by brand name and
untranslated, then the aspect ratios after a separator; the platform an
output was made for is left out, like its own shape. A platform export
shows the platform's name under the player, or, when its check did not
pass, one line for the first issue instead ("Too long for TikTok (max
10:00)"), in the warning or destructive color. Skipped platforms get
the same kind of line, and they stand in for the error of a job that
skipped every platform, so the card does not say the same thing twice
in two languages.

The E2E spec seeds a platform export with a failed check and a skipped
platform, and captures the card and the open menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* fix(media): keep one skipped entry per platform on the card

A job that recovers after a crash runs again and reports the same
skipped platforms; the newest reason per platform now replaces the
earlier one instead of listing it twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 16:49:18 +02:00
cynarAI
e55207f5d2
Open the side panel without booting the computer (#162)
Showing the right side panel (computer preview and routines) used to boot a
stopped computer right away. A boot is slow and heavy on the host, and most
of the time the panel is opened for routines, not the screen. The panel now
only reconnects a computer that already runs; a resting computer shows its
Open action without hover, and Open still boots it. A stopped computer is no
longer treated as needing maintenance.


Claude-Session: https://claude.ai/code/session_01TafdPTJdWxTYt9SYbDuKQ8

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 15:44:51 +02:00
cynarAI
568d37c48c
Merge pull request #157 from cynarAI/claude/composer-mic-send-toggle-jbg7wv
Composer: mic sits in the send slot until there is something to send
2026-10-05 15:38:32 +02:00
cynarAI
a6e611736b
fix(evo-deploy): prefer the fetched remote branch when resolving the deploy ref (#160)
A manual deploy.sh run without a SHA resolved the local 'main' first,
which lags behind origin/main in the deploy checkout, and redeployed the
old commit. Resolve <remote>/<ref> first and fall back to the ref itself
for SHAs and tags.


Claude-Session: https://claude.ai/code/session_01YMUEBB4pr4TtGSMhEAKonM

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 14:46:20 +02:00
cynarAI
4ab0d25224
Merge pull request #156 from cynarAI/claude/project-thread-vljeb4
Media library: owner folders that bots can search and import from
2026-10-05 13:22:46 +02:00
Claude
c98eb6e843
Drop the stage's own voice button now that the composer carries the mic
The empty-chat stage offered "Start talking" / "Set up voice" above the
transcript, a second entry to the same voice conversation the composer's
mic now opens. Keep the composer as the single way in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XcoERsCTbvjqDzE8qC82Wo
2026-10-05 10:51:03 +00:00
cynarAI
738811dbe7
Read the web app at 16px base size on phones (#155)
Text sizes now follow a --font-scale variable that is 1 on wider screens and
8/7 below 768px, so the 14px base text reads at 16px on phones (Safari stops
zooming into fields at that size) and every other size grows by the same
ratio. One-off pixel sizes in the shell move onto the shared text tokens so
they scale too. The stage subtitle now truncates inside the screen instead
of spilling past both edges.


Claude-Session: https://claude.ai/code/session_01PFEDgbyJm12ExL3Kn4Dk8B

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 12:42:30 +02:00
Claude
f3e438444a
Put voice in the composer's send slot until there is something to send
An empty draft shows the mic where the send arrow sits; it opens the
voice conversation as the separate Voice button did. As soon as the draft
holds text, a chip or an attachment, the slot turns back into Send. This
frees the width the extra Voice pill took on small screens.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XcoERsCTbvjqDzE8qC82Wo
2026-10-05 08:52:06 +00:00
Claude
044669edfc
Add a media library: owner folders that bots can search and import from
The deployment owner adds folders of photos and videos in the media settings. A per-folder
background job indexes them (relative path, kind, size, capture date, dimensions, duration),
re-indexes every six hours, and drops files that are gone. The owner's own runs get two tools:
search_media (words in folder and file names, kind, capture date range, newest first) and
import_media (copies one file into the conversation as an artifact). Files are never changed;
hidden entries and symlinks are skipped, and an import must resolve inside its folder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
2026-10-05 08:45:30 +00:00
cursor[bot]
404a1d1782
Merge pull request #154 from cynarAI/cursor/cleanup-self-hosted-runner-6331
Run artifact cleanup on self-hosted Linux
2026-10-05 08:29:45 +00:00
Cursor Agent
3a5ab6ef44
Run artifact cleanup on self-hosted Linux runners
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-05 08:29:20 +00:00
cursor[bot]
9930d295f2
Merge pull request #153 from cynarAI/cursor/trigger-artifact-cleanup-6331
Auto-run artifact cleanup on workflow update
2026-10-05 08:28:50 +00:00
Cursor Agent
354c59f9e3
Run artifact cleanup when the workflow file changes on main
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-05 08:28:41 +00:00
cursor[bot]
d7e4cb91d6
Merge pull request #152 from cynarAI/cursor/cleanup-actions-artifacts-6331
Add manual Actions artifact cleanup workflow
2026-10-05 08:28:20 +00:00
Cursor Agent
478a92f1a5
Add workflow_dispatch job to delete stale Actions artifacts
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-05 08:27:47 +00:00
cynarAI
b3f20967db
Preselect the recommended model while none is connected (#150)
Without a model key the settings opened on the keyless deployment default
(OpenRouter). The connect flow now opens on Claude Opus 5, so a new user
only adds a key or signs in. A connected credential, an owner-chosen
default, or a deployment key still decides as before.


Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 10:17:33 +02:00
cynarAI
2f8d7175f6
ci: stop docker build records and cap artifact retention (#149)
Disable build-push-action build record uploads in publish-server-image;
they are large and unused by the digest publish flow. Digest artifacts
already expire after one day. Set seven-day retention on desktop e2e
screenshots so CI debug uploads do not inherit the org default.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-05 08:34:55 +02:00
cynarAI
f3fb07e41f
fix(web): keep focus in place after removing a media API key (#148)
Remove unmounts itself once clicked, so focus fell to the body and the
settings dialog pulled it back to its popup a frame later. Whatever field
the user (or a test) focused in that frame lost focus again; in CI the
cleared transcription URL was still sent on the next save. Remove now
moves focus to the key field before it disappears.


Claude-Session: https://claude.ai/code/session_01UfVneQG6488JGLiHYAc9RZ

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 06:16:36 +02:00
cynarAI
ca57ebda85
feat(mobile): pick a time for the autopilot and see the night on home (#147)
The attach sheet's autopilot now offers "Until a time" with the native
time picker from Expo UI: Android's clock dialog, an iOS wheel in a sheet
with Start. The home list opens with the autopilots that run, wait or
ended within a day, as Today does on web, and each row opens its thread.
The attach and until menus move to the shared action sheet, so Android
pages them instead of dropping buttons past three.


Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 05:35:47 +02:00
cynarAI
54658ee689
feat: autopilot progress, composer start and morning summary (#146)
The autopilot's start line counts its steps and folds open to the latest
checkpoints, and its result reads as a card. The composer's plus menu
starts an autopilot with the draft as the order and a choice of until
done, tomorrow morning or a time. Today lists the autopilots that run,
wait or ended within a day, and their step runs no longer show one by
one. Autopilot runs send no completion or failure push; only a pause
notifies.


Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 04:45:49 +02:00
cynarAI
c69f00e891
feat(avatars): bots offer a new Oo look from chat (#145)
update_bot takes a look (oo1: value), validated and normalized in core.
A suggested look renders as a "New look" card with the avatar preview on
web and mobile; accepting applies it to the bot.


Claude-Session: https://claude.ai/code/session_01EKuME5CgL1ddGGN7LGvtBj

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 04:03:10 +02:00
cynarAI
460eede226
feat: autopilot that keeps working on an order until it is done (#144)
An autopilot per thread continues the user's order run after run, with
no time limit. Each run reports a checkpoint; after three runs without
progress or three failed runs it pauses with Continue and Stop. Start,
pause, stop and finish show as quiet lines in the conversation.
Approvals during an autopilot run are parked as suggestions, and the
temporary approval rules end with the autopilot.


Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 03:56:45 +02:00
cynarAI
63578bb645
feat(avatar): dress up any Oo in the Atelier on web and mobile (#143)
The Atelier replaces Avatar Studio: pick a species, colour, eyes,
glasses, hats, neckwear, arms and temperament, with every option drawn
as your own Oo wearing it. Onboarding's "Design your Oo" now offers
species looks per vibe and opens the Atelier for the rest; mobile bot
settings carry the same editor instead of a bare colour row, so picking
a colour there no longer throws away the rest of the look.


Claude-Session: https://claude.ai/code/session_01EKuME5CgL1ddGGN7LGvtBj

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-05 03:20:05 +02:00
cynarAI
6fd136e944
feat: suggestions the user accepts or dismisses with one tap (#142)
Bots can offer an exact tool call as a card. Nothing runs until the user
taps Accept; the call then runs as its own approved run of the bot it was
meant for, and the card shows the outcome. Open suggestions also wait in
the inbox and expire after a week.


Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 02:19:31 +02:00
cynarAI
55df4a6439
feat(avatar): let Oos react to what their bots do (#141)
A bot's Oo now cheers for a moment when its run finishes, the stage Oo
follows the pointer and squishes when tapped, and chat headers show the
speaking bot's own Oo instead of the generic mascot. Small avatars in
lists only animate while working or waiting so long lists stay calm.


Claude-Session: https://claude.ai/code/session_01EKuME5CgL1ddGGN7LGvtBj

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 01:51:37 +02:00
cynarAI
5f3aa8f3fe
feat: recipe cards that scale servings and cook step by step (#140)
Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 01:13:52 +02:00
cynarAI
fb5165bb26
fix: keep integrations and live screen working on a LAN origin (#139)
Opening the web app over the Mac's Wi-Fi address (an insecure context)
crashed Connect because crypto.randomUUID is unavailable there, and the
MCP OAuth redirect, Composio/Pipedream return and noVNC link all pointed
at WEB_ORIGIN (127.0.0.1). The API now derives these URLs from the
request's browser origin when it is loopback or a private network, the
same hosts CORS already trusts, and falls back to WEB_ORIGIN otherwise.


Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 00:57:27 +02:00
cynarAI
7db3a56fe8
feat(avatar): give mobile bots the same Oo face as web (#138)
Native avatars now draw the shared Oo rig with react-native-svg instead
of a visor with two equal eyes, so the Oo eyes look the same on every
surface. The robot/organic avatar setting is gone from web and mobile
because every bot is now an Oo; uploaded pictures show on native too.


Claude-Session: https://claude.ai/code/session_01EKuME5CgL1ddGGN7LGvtBj

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 00:36:46 +02:00
cynarAI
47988602e2
feat: let bots answer with cards the user taps (#137)
Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 00:23:59 +02:00
cynarAI
9f4991abe0
feat(avatar): draw every bot as a living Oo on web (#136)
Bots now render as an Oo rig built once in core: nine species, styled
Oo eyes, eyewear, headwear, neckwear, arms and moment props, with a
level of detail per size. A shared animation frame loop moves only
visible avatars and holds still under reduced motion.


Claude-Session: https://claude.ai/code/session_01EKuME5CgL1ddGGN7LGvtBj

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-04 22:40:12 +02:00
cynarAI
dbf37f86a8
feat: show routines the bot sets up from chat as one quiet line (#133)
* feat: show routines the bot sets up from chat as one quiet line

When someone mentions something recurring in passing ("every Sunday I plan the
week"), the bot now creates the routine right away and the conversation shows a
single line with the schedule, Edit and Undo, on web and mobile.

- New `event` message block for routine lines, kept in history so later turns
  know what was set up or removed.
- schedule_create reuses an active repeating routine of the same name instead
  of adding a duplicate, caps a bot at 50 active schedules, and falls back to
  default times when none was given.
- Removing a routine (Undo, the editor, or schedule_cancel) marks its lines
  removed via the new routines.noticeMessageId column.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

* test: give the routine notice test a complete job publisher

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-04 20:36:53 +02:00
cynarAI
d6dd2bef5e
feat(avatar): add the oo1 avatar value for customizable Oo looks (#135)
A bot's look is stored in its `color` string, which today only holds a
colour, an optional classic shape index or an uploaded picture. The
customizable avatars (species, eye styling, accessories, temperament)
need a richer value that still travels everywhere `color` already goes:
bot rows, group members, mentions and the inbox.

- `@rakazo/core` gains `oo-avatar`: an `oo1:` key=value codec that writes
  only non-default fields, reads every legacy value unchanged, rejects
  malformed values, and exposes `ooAvatarColor` for plain CSS consumers.
- The rig's eye anchors keep the Oo trademark (big eye = small eye × φ),
  and lenses scale with their eye so glasses stay asymmetric; the shared
  asymmetry ratio now comes from core.
- The API rejects malformed `oo1:` values on bot create and update.
- `parseBotAvatar` reads through the codec, so an `oo1:` value already
  renders with its colour and shape before the new renderer lands.
- The agent cursor and the mobile avatar used the raw value as a CSS
  colour and broke on `#hex::shape_N`; they now use the decoded colour.


Claude-Session: https://claude.ai/code/session_01EKuME5CgL1ddGGN7LGvtBj

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-04 18:56:34 +02:00
cynarAI
ad054a7586
fix(web): land new accounts in onboarding and show how to connect a model (#134)
Signing up refreshes the session, which briefly routes /onboarding to
/sign-in without a return path; the sign-in route then sent the new
account to /app. The shell's own redirect to onboarding was skipped
whenever a focus refresh landed before the bootstrap response, so a
fresh account could stay on an empty Today view.

/onboarding now sends signed-out visitors to /sign-in?next=/onboarding
and sign-in honours it, and the shell redirects an account without
any bots or conversations regardless of which refresh landed first.

Without a connected model nothing can reply, so the composer shows a
"Connect model" action that opens model settings instead of a send
error after the fact.


Claude-Session: https://claude.ai/code/session_019HKhzQ65ctE5qmkiF9Apxo

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-04 18:34:53 +02:00