Skip to content

Release dev → master: agent OS control framework, macOS-dark theme, store/desktop/chat redesigns#883

Merged
jaylfc merged 57 commits into
masterfrom
dev
Jun 14, 2026
Merged

Release dev → master: agent OS control framework, macOS-dark theme, store/desktop/chat redesigns#883
jaylfc merged 57 commits into
masterfrom
dev

Conversation

@jaylfc

@jaylfc jaylfc commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Promote dev to master so all users get the updates. Every commit here was individually CI'd + bot-reviewed into dev, and the whole set is deployed + verified live on the Pi.

Highlights

Agent OS control framework (new, this session) — the taOS agent can drive the desktop and build in a project, fully offline:

Theme + visuals

Apps

Infra/fixes

Merge method: standard merge (preserve history). Do NOT delete the dev branch.

Summary by CodeRabbit

Release Notes

  • New Features

    • Desktop environment control: agents can now open apps and manage windows via new open_app and arrange_windows tools.
    • GitHub OAuth device-flow authentication for secure identity management.
    • Enhanced wallpaper system with light-theme variants and animated particles with customizable density, speed, and glow.
    • Agent project tooling: create projects, add tasks, and embed images directly from agent actions.
    • Desktop file browser for workspace management.
  • Enhancements

    • Rebranded from "TinyAgentOS" to "taOS" across UI and documentation.
    • Updated accent color scheme from violet to cyan throughout the interface.
    • Improved agent row UI with loading states and status indicators.
    • Added light theme support with full UI token overrides.

jaylfc added 30 commits June 13, 2026 16:15
…ed (#843)

* fix(store): model install works when rkllama runs but is unregistered

The model-store Install button failed for rkllama models on the Pi with
'backend install failed: script not found: scripts/install-rkllama.sh'.
Two compounding bugs (found by live test of the store route on the Pi):

1. get_device_capability built installed_backends only from the registry,
   so a running-but-unregistered rkllama was treated as not installed and
   every model install took the install_chain branch. Now it also live-probes
   rkllama (new rkllama_is_running() checks the taOS + legacy ports) and adds
   it, so the resolver returns action=use and goes straight to the model pull.
2. The rkllama backend manifest's install.script pointed at a non-existent
   scripts/install-rkllama.sh; repointed to the real scripts/install-rknpu.sh
   as a safety net for when the chain is genuinely needed.

This is the real symptom behind the #783 store-UI caveat; the rkllama
connection logic itself was already correct (probes 7833 then falls back to
8080). Adds rkllama_is_running tests.

* fix(store): offload the rkllama liveness probe to a thread

Addresses a Gitar review note: rkllama_is_running() does blocking socket
I/O, and get_device_capability is async, so run it via asyncio.to_thread
to avoid stalling the event loop during a model install.

* fix(store): drop the manifest script repoint from this PR

Qodo flagged that scripts/install-rknpu.sh exit-0-false-succeeds in a
non-interactive shell without TAOS_RKNPU_SETUP, so the install_chain would
mark rkllama installed when it did nothing. That is worse than the original
loud 'script not found'. The chain path is not the bug this PR fixes (the
live-probe handles the running-rkllama case), so revert the manifest change
and track the chain fix separately.

* fix(store): log the rkllama runtime-detection fallback instead of swallowing it
* docs: brand contact details to info@taos.my and add taos.my website link

Swaps the personal gmail for the branded info@taos.my across the README, LICENSE
commercial-contact, code of conduct enforcement, CONTRIBUTING, and getting-started
docs, and surfaces taos.my as the project website. Leaves the gitea-migration
runbook config value (infra, not public contact) untouched.

* docs(readme): rename the TinyAgentOS brand to taOS

Replaces the long-form 'TinyAgentOS' display name with 'taOS' throughout the
README, keeping one bracketed mention up top ('taOS (short for TinyAgentOS)')
so first-time readers learn what taOS stands for. Lowercase tinyagentos in
URLs, paths, and the package/repo slug is unchanged.
…efault (#848)

Adds a third builtin theme (Default dark / Light / Matrix Terminal). Three parts:

- builtin-themes.ts: the Light palette over the existing --color-shell-* tokens
  (cool off-white body, slate accent, frosted near-white chrome, softer shadows,
  light wallpaper). Contrast-checked: body ink hits ~14:1, secondary ~6.4:1.
- theme-store.ts: applyThemeConfig now derives a light/dark scheme from the
  theme's --color-shell-bg luminance and tags the root data-scheme. Works for
  the builtin Light theme and any agent-generated light theme alike.
- tokens.css: a light-scheme compatibility layer that inverts the ~800 hardcoded
  white-overlay utilities (bg-white/N, border-white/N) and ~140 text-white uses
  that apps still carry, so they read dark-on-light instead of vanishing. Gated
  on data-scheme=light via attribute selectors whose specificity beats Tailwind
  utilities without !important, so the dark theme is provably untouched.

The dark Default theme is unchanged. Verified live: computed styles invert under
the light scheme and stay identical under dark.
… target to es2022 (#849)

Dependabot flagged esbuild < 0.28.1 (missing binary integrity verification, RCE
via NPM_CONFIG_REGISTRY). esbuild is a transitive dep of vite 6, and the bump
needs a build target of es2022+ (vite's default es2020/chrome87 target hit an
esbuild 0.28 destructuring-downlevel error). es2022 covers Chrome 94+, Safari
15.4+, Firefox 93+ — all 2021/2022, fine for a modern self-hosted desktop.

Pinned via the existing overrides block. tsc + vite build verified green.
…side the input) (#850)

The attach and screenshot buttons sat as detached siblings to the left of a
tall textarea, sinking to the bottom-left and reading as out of place. Wrap
them into one rounded composer (chat-app convention): attach + screenshot as
borderless ghost icons inside on the left, the field borderless in the middle,
a filled accent send button on the right, bottom-aligned so the icons sit on
the last line as the field grows. Shell tokens only, so it flips for the Light
theme; switched the send glyph to an up-arrow.
…(Install Update 500) (#852)

* fix(update): reset desktop/package-lock.json before the ff-only pull

The desktop rebuild runs npm install, which rewrites desktop/package-lock.json
and leaves it dirty. When an incoming update also touches that file (e.g. the
esbuild bump), git pull --ff-only refuses to overwrite the local change and the
Install Update button 500s. The endpoint already restored tsbuildinfo and
static/desktop before pulling; add package-lock.json to that reset so updates
keep working across rebuilds.

* docs(update-log): record the package-lock.json Install Update 500 (#852)
* feat(agents): redesign the Agents app to Apple-grade cards (identity tile, live status, refined states)

* feat(agents): show the system agent in the standard layout with a model indicator line

The taOS system agent baked its model into the display name (taOS agent - <model>).
Render it like any standard agent instead (name 'taOS agent', framework 'opencode'
sub-label) and move the model to a new indicator line (extensible for future
indicators) via an optional Agent.model field + an IndicatorRow on the card.

* feat(agents): drop the System pill from the system agent card

Keep the subtle elevated ring/tint, just remove the 'System' chip per design
review. Update the AgentRow test to assert protected hides destructive actions
without the chip.

* fix(agents): don't repeat the host as the sub-label for framework-less agents

For none/generic-framework agents the sub-label fell back to the host, which is
already shown in the metadata column, so the host appeared twice on the card
(Gitar review). Omit the sub-label when there's no framework instead.
…low-ups, refined reactions/threads) (#853)

* feat(chat): Slack-polish the message rows (avatar gutter, grouped follow-ups, refined reactions/threads)

* fix(chat): use a single sky shade for the agent accent (drop OS-keyed dark: variants)

taOS themes via data-scheme + tokens, not Tailwind's dark: class, so dark:
keyed off the OS preference rather than the active taOS theme. Use one sky
shade legible on both the dark and light theme backgrounds instead.
A quick-access dropdown in the top bar to stop a runaway agent without opening
the Agents app: Kill all agents (with a live running count) plus each running
agent by name. Every action is gated behind a confirmation dialog (kill is
destructive). Wired to POST /api/agents/bulk/stop and /api/agents/{name}/stop;
follows the existing Power-menu dropdown pattern (radix), confirm via radix
Dialog, all on shell tokens.
…857) (#859)

* fix(agents): surface kill failures instead of closing silently (Gitar review on #857)

- If a stop request fails, keep the dialog open and show an error + 'Try again'
  rather than closing as if it succeeded (silent-failure fix).
- Retain the last target in a 'shown' state so the dialog title no longer
  flashes 'Kill ?' during the close animation.
- Add a render/contract test for AgentKillSwitch.

* fix(agents): reset kill-switch error state on dialog close + reset test stubs

Address bot review: clear the failed state when the confirm dialog closes (via
Cancel or dismiss) so a stale error cannot linger into the next open; add
afterEach(unstubAllGlobals) to the test.
…x Terminal) (#860)

The theme chooser now shows just taOS Dark (the default) and taOS Light. Remove
the Matrix Terminal builtin and rename Default -> taOS Dark, Light -> taOS Light.
Update the theme tests that referenced matrix-terminal.
* fix(agents): show the model on every agent + align the row columns

- Map the agent's model from /api/agents so every agent (not just the system
  agent) shows the model indicator chip.
- Give the status and actions columns fixed widths so the metadata columns line
  up row to row; the protected system agent's 3 action icons now reserve the
  same column as a deployed agent's 4 (right-aligned), so nothing drifts.

* fix(agents): show the model as plain text, no pill or icon

Drop the chip styling and the Cpu icon from the model label; it now reads as a
plain muted mono line under the framework sub-label.
 phase 1) (#862)

* feat(github): device-flow connect for GitHub identities in Secrets (#858 phase 1)

* fix(github): dedupe identities on reconnect + RFC slow_down backoff (Gitar review)

- Reconnecting an already-connected GitHub account (same login) refreshes the
  token in place instead of creating a duplicate identity row.
- Device poll signals slow_down so the frontend increases its poll interval by
  5s per RFC 8628 §3.5. Tests for both.
…tte, mobile audit, wallpaper Phase 1, island v2, GitHub phases
…863)

Drop the SYSTEM AGENT group title (all three render branches) and give every
card a uniform gap: the system agent gets mb-3 above the deployed list, and the
deployed list is space-y-3 to match.
…acked-out elements) (#867)

* fix(theme): force WebKit to repaint backdrop-filter layers on theme switch

Safari keeps backdrop-filter elements (dock, top bar, modals, widgets) on
cached GPU compositing layers and does not re-rasterize them when the theme
custom properties on :root change at runtime. Switching dark<->light could
leave those surfaces rendering black on the live screen, while screenshots
looked correct because the screenshot path forces a full raster (which is
also why the bug was invisible to screenshot-based testing).

Drop backdrop-filter for a single frame via a [data-theme-switching]
attribute set in applyThemeConfig/revertTheme, forcing WebKit to rebuild
each backdrop layer against the new tokens. Imperceptible (~1 frame) and a
no-op in Chrome/Firefox, which re-raster eagerly.

* fix(theme): single repaint per theme apply + timer fallback for hidden tabs

Addresses Gitar review on the Safari backdrop-filter repaint:
- revertTheme now takes a silent flag; applyThemeConfig calls it silently and
  owns the single repaint after the new tokens are written, avoiding a
  redundant forced reflow (and an early repaint before tokens existed).
- forceCompositingRepaint adds a setTimeout(250ms) fallback so the
  data-theme-switching attribute is always cleared even when rAF is paused on
  a hidden/background tab.
… (#868)

* feat(theme): macOS-dark graphite palette + live neural wallpaper

Retunes the taOS Dark theme off the blue-indigo base (#1a1b2e) to a neutral
macOS-style graphite charcoal, and replaces the static default wallpaper with
an adaptive live wallpaper component.

Theme tokens (desktop/src/theme/tokens.css @theme):
- shell-bg #1a1b2e -> #1d1d1f, shell-bg-deep #151625 -> #171717
- dock/top bar rgba(22,25,32,.92) -> neutral rgba(29,29,31,.92)
- accent grey #8b92a3 and the traffic lights are unchanged

Live neural wallpaper (NeuralWallpaper.tsx):
- adaptive <canvas> of drifting nodes + proximity links over a graphite
  field, inspired by the original neural wallpaper but neutral. Renders at
  native resolution for any aspect ratio (16:10, ultrawide 32:10, mobile
  portrait) from one source, so no per-resolution image assets are needed.
- density scales with viewport area; the loop pauses while hidden; honours
  prefers-reduced-motion with a static frame.
- "taOS" wordmark overlay is optional, toggled in the wallpaper picker and
  persisted locally (default on). The toggle only shows for neural wallpapers.

Wiring:
- theme-store gains a wallpaper "kind" ("image" | "neural"); the new
  Neural (Graphite) entry is the taOS Dark default, the original neural PNG
  stays selectable as Neural (Classic).
- Desktop.tsx and the mobile shell render the live wallpaper when active and
  suppress the CSS background image in that case.
- Wallpaper picker shows a graphite preview swatch for neural wallpapers.

Part of #865. Live wallpapers as a shareable package format + agent authoring
guidelines are a follow-up feature (brainstorm next).

* refactor(theme): generic wallpaper kind + slogan overlay (decouple from "neural")

Per feedback: "neural" should not be the category, and the text should not be
welded to it. Not all animated wallpapers with text will be neural.

- Wallpaper.kind is now "image" | "animated" (was "image" | "neural"); the
  specific renderer is a separate `component` field ("neural" is one renderer).
- The wordmark becomes a generic slogan overlay (WallpaperTextOverlay) that
  draws centered text above ANY wallpaper, driven by a per-wallpaper
  `overlayText` default + a user toggle (showOverlayText, persisted). Colour /
  size / style / effects use defaults for now; configurable in a follow-up.
- NeuralWallpaper is now a pure renderer (no embedded text).
- The picker toggle reads "Show slogan (<text>)" and only appears when the
  active wallpaper defines a slogan.
- Wallpapers relabelled: "Graphite" (animated, slogan taOS) is the taOS Dark
  default; the original PNG is "Classic" (no slogan, text already baked in).

* perf(wallpaper): cache gradients, drop per-link string alloc, reactive reduced-motion

Addresses Gitar review on the neural wallpaper (Pi perf target):
- Precompute the 3 gradients in build() and reuse them every frame instead of
  reallocating ~180 gradient objects/sec.
- Draw links with a fixed strokeStyle + per-pair globalAlpha rather than a
  toFixed rgba string per pair in the O(n^2) loop; same for node fills.
- prefers-reduced-motion now reacts to live OS changes via a matchMedia change
  listener instead of being read once at mount.
…phite (#869)

The context menu, top-bar dropdown, kill-switch menu, notification toast +
centre, search palette, model browser and mobile bottom nav still hardcoded the
old blue-indigo base (rgba(26,27,46)/(30,31,50)/etc), so they clashed with the
new macOS graphite theme. Point them at var(--color-dock-bg) (the frosted
chrome token) so they match and adapt to the light theme too.

Also neutralise the navy widget card + mobile app-window backgrounds
(rgba(20,20,35)/(30,30,60)) to graphite, keeping the frosted alpha.

Verified in a real build+preview: the desktop context menu now renders
rgba(29,29,31,0.92).
jaylfc added 16 commits June 14, 2026 03:11
…ansport + bot fixes, standing rules, rename idea, mobile PWA task
…l (transport) (#877)

* feat(desktop): controller->browser command channel for agent OS control

Phase 1 of the agent-OS-control transport (the linchpin for the offline
agent-driven demo). The desktop subscribes to an SSE command stream and
re-dispatches each command to the existing taos:open-app / taos:window
receivers, so the controller (and through it the taOS agent) can open apps
and drive windows on a user's desktop.

- DesktopCommandBroker: per-user pub/sub, NO replay (commands are one-shot;
  replaying to a reconnecting desktop would re-open closed apps)
- GET /api/desktop/stream (SSE, scoped to the caller's user_id) +
  POST /api/desktop/command (emits to the caller's own desktop only)
- use-desktop-command-stream.ts re-dispatches to the existing event receivers
- 8 backend + 5 frontend tests; docs/desktop-control.md updated

Next: agent MCP tools (open_app/arrange + data tools) that POST commands, with
the agent-manual entry, land together so the manual only advertises real
capabilities. Live agent-drives-desktop visual is a Pi-verify.

* fix(desktop): address bot review on command channel

- CRITICAL: scope by request.state.user_id (AuthMiddleware's field), not the
  never-set request.state.user; prevents all callers collapsing to one shared
  channel (cross-user desktop control)
- guard against JSON.parse returning null/non-object in the stream hook
- bound per-subscriber queue (128) with drop-oldest so a stalled SSE consumer
  can't grow memory unbounded; +test
- SSE no-cache / X-Accel-Buffering headers so proxies don't buffer commands
- docs: curl example notes the session-cookie scoping
…se 2) (#878)

* feat(agent): open_app + arrange_windows agent tools (agent OS control, phase 2)

The two agent-facing tools that complete the agent-OS-control framework: an
agent can now open an app on the user's desktop and arrange windows. Each tool
just emits onto the per-user DesktopCommandBroker (phase 1, #877); the controller
streams it to the browser which acts on it. Kept minimal on purpose.

- tinyagentos/tools/desktop_tools.py: execute_open_app / execute_arrange_windows
  (scoped to the calling user via request.state.user_id — never another user)
- wired into skill_exec SKILL_IMPLEMENTATIONS + seeded in skills.py (category
  'desktop'); assignable to any agent
- agent-manual: new 09-os-control.md + recompiled docs/taos-agent-manual.md
- 6 tool tests (emit, props, scoping, validation); 27 related tests green

* fix(agent): address bot review on desktop tools

- refuse open_app/arrange_windows when there is no authenticated user instead of
  emitting to a shared 'system' bucket (Gitar) + test
- backfill builtin skills on existing installs: _post_init now always runs the
  seed with INSERT OR IGNORE, so new desktop-control tools appear after upgrade
  without disturbing user-installed skills (Kilo)
- agent-manual 09-os-control: drop the data-tool actions this PR doesn't implement
  yet so it only advertises open_app/arrange_windows (Kilo)
* style(theme): purge purple/violet/indigo from the dark theme

Jay: get rid of all the purple in the dark theme. The graphite dark theme's
accent token is already neutral grey; this removes the hardcoded purple-family
colours still scattered across apps and swaps them for a cohesive non-purple
(cyan; sky where it would collide with an existing teal sibling).

- 15 app/component files: violet/purple/indigo Tailwind classes + purple hexes -> cyan
- ProvidersApp: cloud badge -> sky (stays distinct from local's teal)
- tokens.css: --board-accent-violet (#a78bfa) renamed -> --board-accent-cyan (#22d3ee)
  + the two board module.css usages updated
- LEFT intentionally: the opt-in 'Deep Indigo' wallpaper preset + the accurate
  'no indigo' token comments

Build clean. (Pre-existing AgentsApp.test failures are unrelated + not in the CI gate.)

* fix(theme): address purple-purge review (dup color + board rgba purples)

- constants.ts COLORS: the 2nd #06b6d4 (was indigo #6366f1) collided with the
  cyan from #8b5cf6 -> distinct teal #2dd4bf (Gitar/CodeRabbit: dup key/colour)
- board CSS active/focus/hover states still used rgba(167,139,250) violet
  (TaskCard/BoardToolbar/TaskModal) -> cyan rgba(34,211,238) (Kilo)

Comprehensive scan now clean (no purple class/hex/rgba) bar the opt-in Deep Indigo
name + accurate token comments. Build clean.
…, light-mode fix) (#880)

* feat(messages): polish the mobile channel list (avatars, theme tokens, timestamps)

The mobile Messages view (which the /chat-pwa 'taOS talk' standalone PWA mirrors
1:1, no separate build) looked plain and used hardcoded white rgba that broke in
light theme. This polishes it toward a mobile Slack/Discord feel:

- agent avatar per DM row (reuses MessageAvatar); Hash/Bot tile for topic/a2a
- name + relative last-activity time; unread rows bold with a clear badge
- all colours via scheme-aware --color-shell-* / accent tokens, so it reads
  correctly in BOTH light and dark (was white-on-white in light)
- polished section headers + empty states via tokens

Affects both the in-OS mobile Messages view and the taOS talk PWA. tsc + build clean.

* fix(messages): address mobile-list review (avatar gate, ticking time, preview)

- only DM channels get an agent avatar; topics/groups/a2a get a glyph tile
  (Hash/Users/Bot) — a topic/group can include agent members too (Gitar)
- relative timestamp now passes nowMs so it refreshes on the 60s tick (Kilo)
- added the lastPreview line under each row (avatar + name + preview + time) for
  a proper mobile Slack/Discord feel
The unread badge stays a clear blue: readable in both light and dark, intentional
notification accent (not part of the neutral token pass).
…/dark (#881)

The standalone taOS talk PWA (/chat-pwa) only imported tokens.css and never
applied the user's selected theme, so it always rendered base dark and ignored
light mode. Call restoreActiveTheme() on boot, mirroring the desktop shell
(App.tsx).
…_image) (#882)

* feat(agent): project data tools (create_project, add_task, canvas_add_image)

Phase 3 of agent OS control: the agent can now BUILD inside a project, not just
open apps. These call the existing project/task/canvas stores in-process (the
same methods the REST routes use), so effects stream live to an open Projects
app via the existing project_event_broker SSE — the board fills and artwork
lands while the user watches, with no new transport. Completes the storybook-demo
flow (open Projects -> create_project -> add_task x N -> generate_image ->
canvas_add_image).

- tinyagentos/tools/project_tools.py (scoped to request.state.user_id; refuse if
  unauthenticated)
- wired into skill_exec + seeded in skills.py (category 'projects')
- agent-manual 09-os-control updated + recompiled
- 8 tests (create/add/image, slugify, field validation, no-user refuse); 24 related green

* fix(agent): enforce project ownership on add_task/canvas_add_image

Gitar/Kilo: the tools accepted an arbitrary project_id and wrote to it without
checking the caller owns it — a cross-user write. Now fetch the project and
require user_id == project.user_id (or admin) before adding a task/image; refuse
with 'not your project' / 'project not found' otherwise. +ownership/admin/missing
tests (11 total).

* fix(agent): harden project-tool arg typing (CodeRabbit)

- add_task/canvas_add_image require string project_id/title/file_id
- canvas_add_image validates x/y are numeric and returns a clean error
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@github-actions

Copy link
Copy Markdown

👋 Thanks for the PR! This one targets master, which is our
stable branch (it's what live installs track). Please retarget it to
dev — click Edit next to the PR title and change the base
branch dropdown from master to dev. Your commits and any review
carry over, nothing is lost.

See CONTRIBUTING.md for the branch model.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR adds desktop control and GitHub device authorization flows, expands theme and wallpaper handling, refreshes multiple desktop UI surfaces, adds workspace rename support and project tools, updates runtime/update maintenance, and revises project documentation and branding.

Changes

Product surface updates

Layer / File(s) Summary
Docs and project text refresh
README.md, docs/*, CONTRIBUTING.md, CODE_OF_CONDUCT.md, LICENSE, .gitignore
Documentation, branding, contact details, setup text, status notes, and repository metadata are updated to taOS wording and current project guidance.
Theme scheme and wallpaper expansion
desktop/src/stores/theme-store.ts, desktop/src/components/WallpaperPicker.tsx, desktop/src/components/ParticlesWallpaper.tsx, desktop/src/App.tsx, desktop/src/theme/*, desktop/src/components/*
Theme state adds light-scheme detection, animated wallpaper parameters, overlay text controls, new built-in themes, and broader shell surface token usage.
Desktop control, workspace actions, and project tools
desktop/src/hooks/*desktop*, desktop/src/components/Desktop*.tsx, desktop/src/components/DockIcon.tsx, tinyagentos/desktop_control/*, tinyagentos/routes/desktop_control.py, tinyagentos/tools/{desktop_tools,project_tools}.py, tinyagentos/routes/user_workspace.py, tests/test_desktop_control.py, tests/test_desktop_tools.py, tests/test_project_tools.py, tests/test_routes_user_workspace.py, docs/desktop-control.md, docs/agent-manual/*, docs/taos-agent-manual.md
The desktop now accepts streamed control commands, exposes a browser control API, supports desktop icons and rename flows, and adds desktop and project skill handlers plus matching docs and tests.
GitHub device flow and identity storage
desktop/src/apps/secrets/GitHubConnect.tsx, desktop/src/lib/github.ts, desktop/src/apps/SecretsApp.tsx, tinyagentos/github_*, tinyagentos/routes/github_oauth.py, tests/test_github_oauth.py, tinyagentos/app.py, tinyagentos/routes/__init__.py
GitHub device authorization, encrypted identity storage, client API helpers, and the Secrets app connection panel are added and wired into the app.
Agents, chat, store, and desktop UI refresh
desktop/src/apps/agents/*, desktop/src/apps/MessagesApp.tsx, desktop/src/apps/chat/*, desktop/src/components/AgentKillSwitch.tsx, desktop/src/components/TopBar.tsx, desktop/src/apps/*App.tsx, desktop/src/components/TaosAssistantPanel.tsx, desktop/src/apps/ProjectsApp/board/*, desktop/src/apps/StoreApp/*
Agent rows, loading states, message rendering, avatars, kill controls, store metadata types, and many accent and styling updates are applied across desktop apps and shared components.
Runtime detection and update cleanup
tinyagentos/routes/settings.py, tinyagentos/routes/store_install.py, tinyagentos/installers/rkllama_installer.py, tests/test_rkllama_installer.py, docs/UPDATE_BREAKAGE_LOG.md
Update cleanup restores an additional generated file before pull, and rkllama runtime detection is added to install capability checks and tests.

Sequence Diagram(s)

sequenceDiagram
  participant SkillExec
  participant DesktopAPI as /api/desktop/command
  participant Stream as /api/desktop/stream
  participant Browser as useDesktopCommandStream
  participant Control as useDesktopControl

  SkillExec->>DesktopAPI: emit open-app/window command
  DesktopAPI->>Stream: enqueue user-scoped command
  Stream->>Browser: SSE data event
  Browser->>Control: dispatch taos:open-app / taos:window
  Control->>Control: open, move, resize, arrange window
Loading
sequenceDiagram
  participant SecretsApp
  participant GitHubConnect
  participant Backend as /api/github/oauth/device/*
  participant GitHub
  participant Store as GitHubIdentitiesStore

  SecretsApp->>GitHubConnect: render connect panel
  GitHubConnect->>Backend: start device flow
  Backend->>GitHub: request device code
  GitHub-->>GitHubConnect: user code and verification URL
  GitHubConnect->>Backend: poll device flow
  Backend->>GitHub: exchange device code and fetch user
  Backend->>Store: save encrypted identity
  Backend-->>GitHubConnect: connected identity
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~100 minutes

Possibly related PRs

  • jaylfc/taOS#877: Also changes the desktop command transport path used by Desktop.tsx and use-desktop-command-stream.ts.
  • jaylfc/taOS#862: Overlaps directly with the GitHub device-flow UI, client helpers, encrypted identity store, and /api/github/* routes.
  • jaylfc/taOS#873: Touches the same desktop icon and new-folder flow, including desktop event wiring and workspace rename support.

Poem

🐇 I hopped through themes of graphite light,
And taught the desktop tricks just right.
GitHub codes now bloom and stream,
While windows dance on cue like dream.
New icons sprout where folders hide—
A taOS warren, bright with pride.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Dashboard-icons CDN helper
------------------------------------------------------------------ */

const di = (slug: string) =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Store icons fetched from external CDN contradicts offline goal

StoreApp/index.tsx renders app icons via a jsdelivr CDN helper (di = (slug) => https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/${slug}.png) and repo logos via github.com. The PR markets taOS as "fully offline" (agent OS control validated offline, no-cloud homelab apps), yet the redesigned Store depends on external network resources to show icons. On an air-gapped Pi every catalog/homelab icon will fail to load.

This degrades gracefully — the <img onError={() => setIconFailed(true)}> handlers fall back to a <Package/> lucide icon — so it is not a functional break, but the polished real-logo experience silently disappears offline. Consider bundling the dashboard-icons set locally (or caching via the service worker / a backend proxy) so the Store looks the same with no internet.

Was this helpful? React with 👍 / 👎

Comment on lines +78 to +82
// pending -> back off by 5s on slow_down (RFC 8628 §3.5), then poll again
if ("slow_down" in result && result.slow_down) {
intervalMs += 5000;
}
pollTimer.current = setTimeout(tick, intervalMs);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Device-flow poll ignores GitHub-provided slow_down interval

In GitHubConnect.tsx beginPolling, on a slow_down response the client unconditionally adds a fixed 5s to its interval (intervalMs += 5000). RFC 8628 §3.5 specifies that the token-endpoint slow_down response carries a new interval value the client should adopt, and the backend device_poll does not forward GitHub's returned interval either. The fixed +5s is usually adequate, but if GitHub requests a larger backoff the client can keep polling too fast and receive repeated slow_down responses, prolonging the connect flow. Consider forwarding the interval field from the token response through device_poll and using it in beginPolling.

Was this helpful? React with 👍 / 👎

Comment on lines +80 to +92
:root[data-scheme="light"] [class~="text-white"] { color: rgba(0, 0, 0, 0.88); }
:root[data-scheme="light"] [class~="text-white/90"] { color: rgba(0, 0, 0, 0.84); }
:root[data-scheme="light"] [class~="text-white/80"] { color: rgba(0, 0, 0, 0.78); }
:root[data-scheme="light"] [class~="text-white/70"] { color: rgba(0, 0, 0, 0.68); }
:root[data-scheme="light"] [class~="text-white/60"] { color: rgba(0, 0, 0, 0.58); }
:root[data-scheme="light"] [class~="text-white/50"] { color: rgba(0, 0, 0, 0.50); }
:root[data-scheme="light"] [class~="text-white/45"] { color: rgba(0, 0, 0, 0.48); }
:root[data-scheme="light"] [class~="text-white/40"] { color: rgba(0, 0, 0, 0.45); }
:root[data-scheme="light"] [class~="text-white/35"] { color: rgba(0, 0, 0, 0.44); }
:root[data-scheme="light"] [class~="text-white/30"] { color: rgba(0, 0, 0, 0.42); }
:root[data-scheme="light"] [class~="text-white/25"] { color: rgba(0, 0, 0, 0.40); }
:root[data-scheme="light"] [class~="text-white/20"] { color: rgba(0, 0, 0, 0.40); }
:root[data-scheme="light"] [class~="text-white/15"] { color: rgba(0, 0, 0, 0.38); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Light-scheme inversion may reduce contrast on colored buttons

The new light-scheme compatibility layer in tokens.css globally rewrites text-white to near-black (color: rgba(0,0,0,0.88)) under :root[data-scheme="light"]. This is correct for text-white sitting on white-overlay surfaces, but it also hits text-white used as the label on saturated colored fills (e.g. a primary button with bg-emerald-500 text-white, or the red/amber chips in AgentRow). On a mid/dark colored background, dark text can drop below the WCAG contrast it had as white, harming readability in light mode. Since the inversion is class-based and unscoped, it cannot distinguish "white text on overlay" from "white text on a colored fill." Worth spot-checking colored CTAs/badges in light theme and adding explicit overrides where needed.

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Jun 14, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 0 resolved / 3 findings

Integrates agent OS control framework, macOS-dark theme, and app redesigns into master. Address potential findings regarding external CDN icon fetching, GitHub device-flow polling intervals, and light-mode contrast on colored buttons.

💡 Quality: Store icons fetched from external CDN contradicts offline goal

📄 desktop/src/apps/StoreApp/index.tsx:23 📄 desktop/src/apps/StoreApp/index.tsx:308 📄 desktop/src/apps/StoreApp/index.tsx:452-453 📄 desktop/src/apps/StoreApp/index.tsx:574-575 📄 desktop/src/apps/StoreApp/index.tsx:633-634

StoreApp/index.tsx renders app icons via a jsdelivr CDN helper (di = (slug) => https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/${slug}.png) and repo logos via github.com. The PR markets taOS as "fully offline" (agent OS control validated offline, no-cloud homelab apps), yet the redesigned Store depends on external network resources to show icons. On an air-gapped Pi every catalog/homelab icon will fail to load.

This degrades gracefully — the <img onError={() => setIconFailed(true)}> handlers fall back to a <Package/> lucide icon — so it is not a functional break, but the polished real-logo experience silently disappears offline. Consider bundling the dashboard-icons set locally (or caching via the service worker / a backend proxy) so the Store looks the same with no internet.

💡 Quality: Device-flow poll ignores GitHub-provided slow_down interval

📄 desktop/src/apps/secrets/GitHubConnect.tsx:78-82 📄 tinyagentos/routes/github_oauth.py:164-168

In GitHubConnect.tsx beginPolling, on a slow_down response the client unconditionally adds a fixed 5s to its interval (intervalMs += 5000). RFC 8628 §3.5 specifies that the token-endpoint slow_down response carries a new interval value the client should adopt, and the backend device_poll does not forward GitHub's returned interval either. The fixed +5s is usually adequate, but if GitHub requests a larger backoff the client can keep polling too fast and receive repeated slow_down responses, prolonging the connect flow. Consider forwarding the interval field from the token response through device_poll and using it in beginPolling.

💡 Quality: Light-scheme inversion may reduce contrast on colored buttons

📄 desktop/src/theme/tokens.css:80-92

The new light-scheme compatibility layer in tokens.css globally rewrites text-white to near-black (color: rgba(0,0,0,0.88)) under :root[data-scheme="light"]. This is correct for text-white sitting on white-overlay surfaces, but it also hits text-white used as the label on saturated colored fills (e.g. a primary button with bg-emerald-500 text-white, or the red/amber chips in AgentRow). On a mid/dark colored background, dark text can drop below the WCAG contrast it had as white, harming readability in light mode. Since the inversion is class-based and unscoped, it cannot distinguish "white text on overlay" from "white text on a colored fill." Worth spot-checking colored CTAs/badges in light theme and adding explicit overrides where needed.

🤖 Prompt for agents
Code Review: Integrates agent OS control framework, macOS-dark theme, and app redesigns into master. Address potential findings regarding external CDN icon fetching, GitHub device-flow polling intervals, and light-mode contrast on colored buttons.

1. 💡 Quality: Store icons fetched from external CDN contradicts offline goal
   Files: desktop/src/apps/StoreApp/index.tsx:23, desktop/src/apps/StoreApp/index.tsx:308, desktop/src/apps/StoreApp/index.tsx:452-453, desktop/src/apps/StoreApp/index.tsx:574-575, desktop/src/apps/StoreApp/index.tsx:633-634

   StoreApp/index.tsx renders app icons via a jsdelivr CDN helper (`di = (slug) => https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/${slug}.png`) and repo logos via github.com. The PR markets taOS as "fully offline" (agent OS control validated offline, no-cloud homelab apps), yet the redesigned Store depends on external network resources to show icons. On an air-gapped Pi every catalog/homelab icon will fail to load.
   
   This degrades gracefully — the `<img onError={() => setIconFailed(true)}>` handlers fall back to a `<Package/>` lucide icon — so it is not a functional break, but the polished real-logo experience silently disappears offline. Consider bundling the dashboard-icons set locally (or caching via the service worker / a backend proxy) so the Store looks the same with no internet.

2. 💡 Quality: Device-flow poll ignores GitHub-provided slow_down interval
   Files: desktop/src/apps/secrets/GitHubConnect.tsx:78-82, tinyagentos/routes/github_oauth.py:164-168

   In GitHubConnect.tsx `beginPolling`, on a `slow_down` response the client unconditionally adds a fixed 5s to its interval (`intervalMs += 5000`). RFC 8628 §3.5 specifies that the token-endpoint `slow_down` response carries a new `interval` value the client should adopt, and the backend `device_poll` does not forward GitHub's returned interval either. The fixed +5s is usually adequate, but if GitHub requests a larger backoff the client can keep polling too fast and receive repeated `slow_down` responses, prolonging the connect flow. Consider forwarding the `interval` field from the token response through `device_poll` and using it in `beginPolling`.

3. 💡 Quality: Light-scheme inversion may reduce contrast on colored buttons
   Files: desktop/src/theme/tokens.css:80-92

   The new light-scheme compatibility layer in tokens.css globally rewrites `text-white` to near-black (`color: rgba(0,0,0,0.88)`) under `:root[data-scheme="light"]`. This is correct for `text-white` sitting on white-overlay surfaces, but it also hits `text-white` used as the label on saturated colored fills (e.g. a primary button with `bg-emerald-500 text-white`, or the red/amber chips in AgentRow). On a mid/dark colored background, dark text can drop below the WCAG contrast it had as white, harming readability in light mode. Since the inversion is class-based and unscoped, it cannot distinguish "white text on overlay" from "white text on a colored fill." Worth spot-checking colored CTAs/badges in light theme and adding explicit overrides where needed.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

}, []);

useEffect(() => stopPolling, [stopPolling]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Polling cleanup effect clears timers immediately

useEffect(() => stopPolling, [stopPolling]) runs after every render. After beginPolling schedules pollTimer/expiryTimer, the next render clears them, so the device-flow poll loop will never run.

Suggested change
useEffect(() => () => stopPolling(), [stopPolling]);

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

# Identities: list / delete (NO tokens ever returned)
# ---------------------------------------------------------------------------

@router.get("/api/github/identities")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Identity list is global across users

list_identities() returns every row in github_identities without filtering by request.state.user_id. In a multi-user install, any authenticated user can see other users' connected GitHub logins/avatars and identity ids. Store rows should include an owner user_id and list should filter by the caller.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

return await store.list()


@router.delete("/api/github/identities/{identity_id}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Identity deletion is not scoped to the caller

delete_identity() accepts any UUID and deletes that row globally. Any authenticated user can delete another user's connected GitHub identity because the route does not read request.state.user_id or filter the delete by owner. Add user_id to the identity schema and require WHERE id = ? AND user_id = ?.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

if dst.exists():
return JSONResponse({"error": "Target already exists"}, status_code=409)

dst.parent.mkdir(parents=True, exist_ok=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Rename can error when moving a directory into itself

The endpoint allows dst to be inside src (for example src=dir, dst=dir/nested/file). dst.parent.mkdir(...) may create the nested parent, then src.rename(dst) raises an OSError instead of returning a controlled 400. Add an ancestor check before creating parents/rename.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Jun 14, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 4 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 2
WARNING 2
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
desktop/src/apps/secrets/GitHubConnect.tsx 52 Cleanup effect clears polling timers immediately, preventing the device-flow poll loop from running.
tinyagentos/routes/github_oauth.py 189 Identity deletion is global across users; any authenticated user can delete another user's connected GitHub identity.

WARNING

File Line Issue
tinyagentos/routes/github_oauth.py 181 Identity list is global across users; any authenticated user can see other users' connected GitHub identities.
tinyagentos/routes/user_workspace.py 236 Rename can raise an uncontrolled OSError when moving a directory into itself.
Other Observations (not in diff)

No additional non-diff code issues found.

Files Reviewed (101 files)
  • desktop/src/apps/secrets/GitHubConnect.tsx - 1 issue
  • tinyagentos/routes/github_oauth.py - 2 issues
  • tinyagentos/routes/user_workspace.py - 1 issue
  • Remaining changed files reviewed at diff level only; no additional inline issues posted.

Fix these issues in Kilo Cloud: https://app.kilo.ai/cloud-agent-fork/review/a996e706-437e-4e09-8e0f-95a0c5fcd623


Reviewed by nex-n2-pro:free · 5,577,457 tokens

@jaylfc jaylfc merged commit c9c5b0c into master Jun 14, 2026
13 of 14 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in TinyAgentOS Roadmap Jun 14, 2026
jaylfc added a commit that referenced this pull request Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

1 participant