* Voice UX: simple speak toggle, onboarding pick, chat voice change
Replace Kokoro-branded settings with a Kann sprechen / Spricht nicht toggle,
onboarding voice personalization, and local chat commands for voice changes.
German voice presets (Eva, Bernd) surface as friendly names; Kokoro stays
under the hood via KOKORO_BASE_URL.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Fix import order in voice onboarding and settings
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>
* fix(adapters): stop desktop shell from inheriting host secrets
The this-mac shell spawned with the server process environment and ignored
the command environment, so Autopilot could read deployment credentials that
are not on the run redaction list. Drop credential-shaped host variables,
then apply the request environment.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* style(adapters): biome format desktop-sandbox after env scrub
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>
- ConversationBotPicker now receives full Bot[] instead of SpaceBot[]
- conversationSidebarGroups render simplified to BotAvatar-only (no groups)
- projectSidebarGroups render uses GroupAvatar with proper type cast
- Dead code eliminated: kind===group checks in conversation sidebar
Co-Authored-By: Claude Code <noreply@anthropic.com>
Extend the existing NewSpaceDialog project flow so an optional host
folder becomes the space workspace root. When .git metadata is present,
remote URL, default branch, and head commit are stored on the space.
Team Computer file APIs expose the linked folder under project/ without
a second VCS stack. Desktop adds a native folder picker; web falls back
to an absolute path field.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Replace Mission Control iframe on the Heute pillar with a native React
brief that surfaces actionable runs, unread replies, and today’s threads.
Mission Control remains available via a small footer link for power users.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
- Sidebar: Unterhaltungen (1:1-Bots im Home-Space) und Projekte
(Projekt-Spaces + Gruppen) getrennt; Projekt-Spaces erscheinen nicht
mehr unter Team/Unterhaltungen
- + in Unterhaltungen öffnet Bot-Picker (bestehenden Bot öffnen oder
neuen Bot erstellen)
- Chat-Header: Bot-Avatar wieder sichtbar, Status nur bei Aktivität,
kein doppeltes „1:1 conversation“
- Composer: generisches „Nachricht…“ statt Name dreifach
- OoStage: kein doppelter Bot-Name bei laufendem Verlauf
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
When deliverDelegatedReplies could not resolve the Slack/Teams origin thread,
it still set teamChatMirroredAt and permanently dropped the delegated reply.
Leave the mirror timestamp unset when sourceMessageId exists so reconciliation
retries once the wake chain can be resolved again.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
When a run worker loses its lease while the runtime yields no events
(long tool or model work), lease loss was only detected on the next
heartbeat renewal (up to 60s). A reclaimed worker could execute in
parallel during that window.
Add a dedicated lease poll that aborts stale workers within a few
seconds and call runtime.abort so pi-runtime stops promptly.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Group channel fan-out used event.content only, dropping mediaUrl that
DM handling already merges into message text. Photos with empty captions
in SendBlue groups reached member bots with blank prompts and blocks.
Also skip fan-out when the merged text is empty, matching DM tapback
behavior.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
The executor calls finalizeRun with toolCallBudgetCount and
toolCallBudgetExceeded in the updateMany data clause, but these
fields were missing from the Prisma schema, causing an Invalid
updateMany invocation error that blocked all runs from completing.
Also updated CLAUDE-RESUME-NOW with full infrastructure status.
The GraphileJobWorkerHost was never started when using graphile as the
wakeup driver. Added else-if branch to start the worker host with the
job pool, enabling actual job consumption (previously jobs were queued
but never processed).
The executor loaded every agent secret in a Space and injected decrypted
values into shell environment variables for any member's bot run. Restore
agentSecretScopeForRun scoping accidentally dropped in 0a8a831.
Add an executor test that fails when findMany is scoped only by spaceId.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Commit 928d9ad refactored requireSpaceOwner into isSpaceOwner but
accidentally dropped getMemoryProviderConfig, breaking API boot while
router.ts still imported it. Restore the owner-gated read helper.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
When a user records a taught skill on one Team Computer bot, manual
takeover on another bot in the same space cleared the teaching lease
without stopping the session. Teaching input then failed silently and
skills/stop could not release the replacement takeover lease.
Reject computer/takeover while another bot on the same computer is
recording, matching the existing Stop teaching first messaging guard.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Team mode stores one disk per Space. computer/readFile and computer/files
only verified the caller owns the entry bot; an explicit bots/<otherBotId>/…
path let one member read another member's workspace. Wire existing path guards
into the API router and executor file/shell tools, and add a shared-Space
authorization regression test.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
claimSteering matched steering rows by runId even after they were
claimed, and still returned them when updateMany updated zero rows.
That could inject duplicate user context into an active run.
Filter findMany to claimedAt: null and return nothing when the claim
update does not land.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
threads/clear called memoryProviders.resolve with a bare spaceId string.
The resolver destructures { spaceId, userId }, so both were undefined and
purgeHistory was silently skipped after every clear.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* fix(api): scope routine remove to acting user in shared Spaces
routines/remove only filtered by spaceId, so any member of a shared Space
could delete another member's routine by ID. Match update/testRun by also
requiring userId in the lookup.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* test: drop unused ownerActor in routine remove authz case
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>
deliverDelegatedReplies used allowSilence and marked teamChatMirroredAt
even when no Slack/Teams message was sent, leaving tool-only delegated
runs silently dropped with no retry path.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
- Add teamBotWorkspaceDirectory import to router.ts
- Add exportOptions to scope team exports to one bot workspace
- Add authorization test: member export only sees their workspace files
- Add LocalAgentHomeStore and computerHomeKey imports to testkit
Group threads/followUp always steered to members[0], so a follow-up
while a later member had waiting_input went to the wrong bot or started
a fresh run on the first member instead of continuing the ask.
Resolve the target bot like group sends: prefer waiting ask/takeover on
any member, then the newest steerable active run, then @mentions, then
the default first member. Extract followUpGroupThreadMessage and add
regression tests.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Non-owner shared-space members could read the space memory provider id
and internal endpoint origin via aiConsent/status, bypassing the owner-only
policy that draft PR #46 applies to memory/providerConfig.
Owners still see full provider disclosure; members get a generic Space
memory recipient when a provider is configured, without loading settings.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
- Replace findSpaceMemoryConfig inline call with getMemoryProviderConfig(deps, actor)
- Add owner gate: space members get 403, owners get config
- Add authorization tests for providerConfig/connectProvider/setDefaultScope/disconnectProvider
- Add wiring test to ensure router.ts uses the owner-gated handler
Group intros were stamped as posted before the outbox drain ran. When
delivery enqueue failed or the intro row exhausted retries, the channel
stayed marked and no retry path existed — unlinked participants never saw
the onboarding message.
Stamp introPostedAt in the drain path after a successful provider send,
matching the messagingMirroredAt pattern from #42.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
answerRunInput already blocked cross-member answers for credential
secret asks and always-allow approvals, but one-time allow/deny and
generic OTP secret asks had no run.userId check. Align the DB gate so
shared-space prompt answers cannot be hijacked at the persistence layer.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Group threads/followUp always steered to members[0], so a follow-up
while a later member had waiting_input went to the wrong bot or started
a fresh run on the first member instead of continuing the ask.
Resolve the target bot like group sends: prefer waiting ask/takeover on
any member, then the newest steerable active run, then @mentions, then
the default first member. Extract followUpGroupThreadMessage and add
regression tests.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
spawnKey is unique per Space, not per user. createBot already rejects
cross-member replay, but spawnBot caught that error and returned the
other member's bot anyway. Match createBot by requiring existing.userId
=== spawnedBy.userId before treating a spawnKey conflict as idempotent.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Team mode shares one disk per Space. export/bot walked the entire
team home, so a member exporting their own bot received every other
member's workspace files too.
Scope exportHome with the exporting bot's bots/<id>/ prefix and add
regression coverage for the shared-Space case.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Mission Control previously set Access-Control-Allow-Origin: * on every
route. That widens cross-origin browser access when Cockpit binds beyond
loopback, even with bearer auth.
Add server-config helpers compatible with PR #28 bind/token patterns:
- loopback binds default to local web/API origins for the Heute shell
- privileged /api/* routes never receive *; only reflected allowlist hits
- non-loopback binds require explicit EVOO_COCKPIT_CORS_ORIGIN
- default bind moves to 127.0.0.1 with token gate off-loopback
Document env vars in .env.example and cover config/CORS in unit tests.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Computer-busy requeues previously retried forever via retryForever,
which could hang runs and burn worker cycles on a stuck computer.
Add a small retry policy helper that tracks consecutive busy waits in
an internal run.error marker, preserves that marker across lease claims,
caps retries at 24 with existing exponential backoff, and fails the run
with a clear message when the bound is reached. Database connection
pressure retries remain unbounded.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Remove hostRoots from this-mac desktop sandbox so shell execution stays
inside the bot computer workspace, matching container path rules.
Add resolveDesktopExecuteCwd to map virtual /home/rakazo paths onto the
local home and reject host-absolute cwd requests via containment checks.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
The respond_agent_connection builtin could flip a pending agent connection
to approved without the target owner's YES/NO DM or authenticated API
response. Treat it like other security-boundary builtins so the executor
shows an approval card before the status change runs.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>