Commit Graph
100 Commits
Author SHA1 Message Date
pewdiepie-archdaemon 7f71fbc3ea Email list: scroll an expanded card into view after click
When clicking an email higher up in the list, its top edge can be hiding
behind the modal header or off-screen. After applying the
.email-card-expanded class + the new minHeight, scrollIntoView(block:start)
on the next animation frame so the user sees the whole card.
2026-06-11 07:35:32 +09:00
pewdiepie-archdaemon 7017127a11 Email card: drop redundant header kebab; keep bottom '...' menu in expanded state
The expanded email card painted a kebab menu in its title row because
the per-card .memory-item-actions menu at the bottom was hidden while
expanded. Both pointed at _showCardMenu(em). Remove the duplicate:

- Drop the email-card-header-menu button (and its rightCluster
  wrapper) — title row now just holds the nav arrows.
- Remove the CSS rule that hid .memory-item-actions on
  .email-card-expanded so the bottom kebab stays visible.
- Unread-dot insert point retargets to .email-card-nav-arrows now
  that the rightCluster is gone.
2026-06-11 07:33:04 +09:00
pewdiepie-archdaemon 00643b5a4b Email library New (compose): envelope icon takes the accent color 2026-06-11 07:30:26 +09:00
pewdiepie-archdaemon e25c279e4b Email Library: drop redundant 'All emails. Click to open as a document' subtitle 2026-06-11 07:28:21 +09:00
pewdiepie-archdaemon df54d8d2bf Email library: bulk 'Done' actually marks selected emails done
state._selectedUids holds whatever the server returns for em.uid (string
or number); the bulk action looped Array.from(...) and did strict ===
against state._libEmails entries. When the types disagreed, the find()
returned undefined, the in-memory is_answered flip never happened, and
the post-loop _renderGrid() painted the cards back into their original
not-done state — looking like 'mark done' did nothing even though the
server-side call had succeeded.

- Compare via String() on both sides so the in-memory state actually
  flips.
- Surface HTTP failure from mark-answered/mark-read so the existing
  failedReadSync toast can fire if the calls don't go through.
2026-06-11 07:27:25 +09:00
pewdiepie-archdaemon 8ae31aeb13 Email library compose button: scope taller+lower variant to desktop only
Wrap the height:28px / top:0 rule in @media (min-width:769px) so it
can't leak into mobile, where a different touch-friendly variant
already sets min-height:36px + top:-2px.
2026-06-11 07:24:25 +09:00
pewdiepie-archdaemon cc86760a26 Email library: drop compose button another 2px (top -2→0) 2026-06-11 07:23:49 +09:00
pewdiepie-archdaemon 2e7cfbe1fa Email library: New (compose) button 4px taller + 2px lower
Base .memory-toolbar-btn is 24px tall at top:-4px. Bump the compose
button alone to 28px (4px taller) and top:-2px (moves down 2px) so
it reads as the primary action in the toolbar without affecting
Select/Refresh.
2026-06-11 07:22:38 +09:00
pewdiepie-archdaemon 9dbe31bfb0 Email/doc split: stop auto-tab-down when there's no room
Previously _prepareEmailWindowForDocument would:
  1. Check if there was horizontal room for both email + doc.
  2. If not, try collapsing the sidebar to recover space.
  3. If even that wasn't enough, _clearEmailDocumentSplit() — the
     email tab-down the user has been disliking.

Drop step 3. We still try collapsing the sidebar (free easy room),
but if the layout is still cramped, just dock anyway and let the
user manage their layout. _clearEmailDocumentSplit() is still
called on the legitimate close paths.
2026-06-11 07:17:26 +09:00
pewdiepie-archdaemon 2bf372b41c Tasks: optional persona for LLM + research tasks (biases output voice)
Wire the existing built-in PERSONAS catalog through to scheduled tasks
the same way I wired it to reminder synthesis. Repurposes the
dormant scheduled_tasks.character_id column.

UI (static/js/tasks.js)
- New 'Persona' select in the LLM / Research task form, with the five
  built-in characters (socrates/razor/nietzsche/spark/odysseus) plus a
  default 'no persona' option. Pre-populates from existing.character_id
  on edit. Non-llm/research types explicitly clear it on save.

API (routes/task_routes.py)
- TaskCreate + TaskUpdate gain character_id: Optional[str].
- _task_to_dict echoes character_id back so the form can hydrate on
  edit. Update endpoint stores '' as None to allow clearing.

Runner (src/task_scheduler.py)
- When task.character_id is set and matches a built-in persona, prepend
  the persona prompt to the task system prompt so the model speaks in
  that voice while still knowing it's running a scheduled task.
- crew_member.personality still wins as the base; character_id stacks
  on top.
2026-06-10 23:36:18 +09:00
pewdiepie-archdaemon a86990fc58 Email row: fix crash from leftover menu-wrap wiring after button removal
I removed the .email-menu-wrap markup from email rows earlier but
left the JS that queries it and calls .addEventListener on the
result. Since the query returns null, every _createEmailItem call
threw and the row never made it into the list — most visibly:
clicking a sender name to filter by them didn't appear to work,
because the row wiring (including the sender click handler) was
ripped out mid-construction.

- Drop the unconditional menuWrap.addEventListener('click', ...)
  block — there's no menu to open.
- Drop the early-return guard on touchstart that referenced the
  removed wrap.
- The two remaining .email-menu-wrap queries are already guarded
  with 'if (menuWrap)' so they safely no-op.
2026-06-10 23:31:23 +09:00
pewdiepie-archdaemon f4c1b264c6 Email reminder bell: re-evaluate visibility live on settings change
The bell is already gated on settings.reminder_channel === 'email', but
the check only ran at email-library init — so switching the reminder
channel in Settings didn't update the bell until you reopened Email.

- Settings/Reminders channel-change handler now dispatches
  odysseus-reminder-channel-changed { channel } after saving.
- emailLibrary listens for it and re-runs _syncEmailReminderBellVisibility
  with the new channel value.
2026-06-10 23:26:53 +09:00
pewdiepie-archdaemon 031a600725 Email accounts strip: drop redundant 'Accounts' label during load — whirlpool alone
The strip already lives where account chips render, so the text label
beside the whirlpool was redundant. Strip the label + the fallback
'Accounts...' text — the spinner alone tells the user accounts are
loading.
2026-06-10 23:22:59 +09:00
pewdiepie-archdaemon b385b25d5f Email row: remove the three-dot actions menu button
Dropped the .email-menu-wrap / .email-menu-btn from each row. Other
handlers that check 'if (e.target.closest(.email-menu-wrap)) return;'
safely no-op when the element doesn't exist. Row click + swipe still
open the email and its in-reader actions.
2026-06-10 23:21:17 +09:00
pewdiepie-archdaemon 49b72bd09c Email attachments: nudge download spinner up 2px to sit on icon baseline 2026-06-10 23:19:19 +09:00
pewdiepie-archdaemon 0a3333b961 Edge-dock resize handle: fade accent stripe in on hover
Transparent at rest, accent gradient animates in on hover with a 0.18s
ease transition. Drag affordance + col-resize cursor still work; the
stripe just stops bothering you when not touched.

Right-side handle mirrors the gradient direction (left-to-right
gradient flipped to right-to-left).
2026-06-10 23:18:07 +09:00
pewdiepie-archdaemon 1638db9c86 Email reader: theme-aware override for Gmail drive/attachment chips
Gmail composer chips arrive with inline border:1px solid #ddd + an
assumed white background, so on dark themes they read as a barely-
visible white box with the filename invisible. Override .gmail_chip /
.gmail_drive_chip inside .email-reader-body:

- Strip inline width:386px / height:20px (use auto + max-width:100%),
- Re-flow as inline-flex with a 6px gap so icon + filename align.
- Background tinted with var(--fg) 4%, border = var(--border).
- Anchor uses var(--accent) and the filename span uses var(--fg) so
  text is always legible regardless of theme.
- Icon img clamped to 16x16.
2026-06-10 23:17:18 +09:00
pewdiepie-archdaemon cd9ad1a7f2 Email attachments: swap paperclip for whirlpool spinner during download
Before: the attachment chip just dimmed (opacity 0.6) while the file
downloaded — easy to miss on a large attachment.

Now: replace the paperclip SVG with a 12px whirlpool spinner for the
duration of the fetch, restoring the original icon when the download
finishes (or errors out). Same loading vocabulary as Test / Scan /
Probe / Send buttons elsewhere in the UI.
2026-06-10 23:15:52 +09:00
pewdiepie-archdaemon 023f1ba575 Email inbox: visual flash when an email is auto-marked done after sending
When the email-answered event fires (user just sent a reply, so the
source email auto-marks as done), the row was getting the .active
class instantly with no visible cue beyond the checkbox tick. Add a
brief .email-auto-done-flash class on the row that runs two keyframe
animations:
- email-auto-done-row: tints the row background with the accent for
  ~1.2s then fades to transparent.
- email-auto-done-check: pops the done checkbox to 1.4× with an
  accent ring that expands outward over 0.6s.

Class self-removes after 1.2s so it doesn't replay on re-renders.
2026-06-10 23:06:42 +09:00
pewdiepie-archdaemon 1a4659b7fc Edge-dock resize handle: drop the visible accent stripe
The drag handle painted a 35% accent gradient strip on the page edge
of any docked panel. The col-resize cursor on hover is enough to
surface the affordance; the stripe felt like a stray UI element.
2026-06-10 23:05:39 +09:00
pewdiepie-archdaemon 965b0e143c Email accounts strip: wheel + grab-drag horizontal scroll
The single-row chip strip relied on native horizontal scroll, which is
hard to reach without a horizontal wheel. Wire two scroll mechanisms
on the strip once it's rendered:

- Vertical wheel → horizontal scroll (intercept only when overflow
  exists and the wheel motion is primarily vertical, so normal page
  scroll still works elsewhere).
- Mouse grab-and-drag: cursor goes grab/grabbing, mousedown→move
  bumps scrollLeft by the cursor delta. A 5px drag threshold cancels
  the chip click so the user can drag-scroll without accidentally
  switching accounts.
2026-06-10 23:00:29 +09:00
pewdiepie-archdaemon 1eca28e588 Email: revert single-row email-item; account chips single-row at all widths
- Revert the email row layout — sender/date stay above subject again,
  matching the original two-line item that the user actually wanted.
- The account filter chips (#email-lib-accounts) wrapped onto multiple
  rows on desktop. Promote the mobile-only horizontal-scroll rule to
  apply at every breakpoint so the chips always sit on one row with
  overflow scroll, regardless of screen size.
2026-06-10 22:55:10 +09:00
pewdiepie-archdaemon a80421efb6 Sessions sort dropdown: nudge all items 2px more left
Group row's auto-sort-sessions-btn padding-left 6→4, and
.sort-dropdown-item left padding 8→6 so 'Last Active', 'Newest First',
'By Folder', '↑↓ Rearrange', '● Select' all shift in by the same
amount, matching the Group nudge.
2026-06-10 22:49:53 +09:00
pewdiepie-archdaemon 89efd7d44b Email list: collapse to a single row — [sender] [subject] [date]
Subject was on its own line below sender/date. Move it inline so each
email occupies one row: sender capped at 35% width (ellipsis), subject
takes the remaining space (ellipsis), date pins to the right. Tighter
list density at the cost of dropping the spare line for snippet text
(none was being rendered anyway).
2026-06-10 22:45:19 +09:00
pewdiepie-archdaemon 41980df6f1 Sidebar/Chats manage button: parent-hover reveal + clearer 'manage' text
Two related fixes for the Chats section header:
- The 'manage' label only slid out when the button itself was hovered.
  Add section-header-flex:hover to the reveal rule so hovering the sort
  icon (or anywhere in the section header) also opens the label.
- Parent-hover opacity bumped 0.45 → 0.85 so the 'manage' text reads
  much more clearly when revealed. Direct hover on the button still
  pushes to full opacity 1.
2026-06-10 22:44:50 +09:00
pewdiepie-archdaemon baa4449a03 Sidebar/Chats manage button: drop hover background tint
The shared .section-header-btn:hover rule paints a tinted background
across all section header buttons. On the Chats manage button this
showed as a box behind the sliding 'manage' label, which the user
didn't want. Override background to transparent for that one button.
2026-06-10 22:42:06 +09:00
pewdiepie-archdaemon 1ee51be420 Sessions: Esc + outside-click also close the Move-to-folder submenu
The session-dropdown Esc handler only closed .session-dropdown-menu,
leaving the .session-folder-submenu (Move to folder → folder list)
orphaned on screen. Same gap on the click-away path. Extend both
selectors to cover the submenu so a single Esc / outside-click
dismisses the whole stack.
2026-06-10 22:39:23 +09:00
pewdiepie-archdaemon 94931ba59f Chats sidebar: 'manage' label sits in flex flow so its area is clickable
Email's 'new' label is absolutely positioned to the LEFT of the '+'
icon, which works there because the '+' is the visible/clickable
anchor. The chats manage button has no visible glyph at rest, so the
label was rendered outside the button's bounding box — hovering
'manage' lost the :hover state and clicking it missed.

Override list-item-plus-label inside chats-manage-btn:
  position: static (in flex flow) + max-width:0 / max-width:80px
expand-on-hover so the button's clickable rect grows alongside the
text. Hover stays sticky; click hits.
2026-06-10 22:39:05 +09:00
pewdiepie-archdaemon 49ecd806a2 Chats sidebar: 'manage' slides in from the side like email's 'new'
The list-item-plus-label slide-in needs a visible anchor element so
the button takes up consistent width and the absolutely-positioned
label can fly in to the left of it. Email uses the '+' SVG as that
anchor; here we use an empty 13x13 spacer span instead — same
footprint, no glyph. Result: empty button at rest (still visible per
the chats-manage-btn fade rules), 'manage' slides in from the left
on direct hover.
2026-06-10 22:37:06 +09:00
pewdiepie-archdaemon 1eaa5c2a81 Sessions sort: nudge auto-sort icon + 'Group' text 4px left (10→6 left padding) 2026-06-10 22:35:21 +09:00
pewdiepie-archdaemon e107c5876e Chats sidebar: drop library SVG from manage button — text-only 'manage'
Removed the book/library SVG and list-item-plus-btn/-label classes.
The button is now a plain text button styled like email's 'new' label
(9.5px, 0.02em letter-spacing), reusing the existing chats-manage-btn
opacity hover-reveal rules so it still fades until you hover the
section.
2026-06-10 22:35:06 +09:00
pewdiepie-archdaemon 4f7061fd61 Settings overhaul + UI polish pass
Two months of iteration on the Settings panel, integration forms, and
small visual nudges across the app. Highlights:

Settings restructure
- Add Models: split into separate Local + API cards (no more in-card
  tabs); each fuses Type/Provider with the URL input.
- Added Models: new dedicated sidebar tab, with Probe + Clear-offline
  pulled into its header; Local/API sub-section icons accent-tinted.
- Search: Web Search and a new Deep Research card (Model + tuning),
  with a cross-link to AI Defaults. Provider hints use real clickable
  anchors; Web Search Test button shows a whirlpool spinner.
- AI Defaults: Image Generation card returns; Research Model card
  carries only Endpoint+Model with a cross-link to Search; Vision /
  Default / Utility fallbacks unified under one numbered-row design
  matching Search's chain.
- API Permissions (was 'API Tokens'): per-row rename, inline
  Permissions toggle that expands the scope-edit panel, in-field
  copy icons (icon→check on success). Empty state accent-tinted.
- Integrations: + Add Integration drops a type-picker menu directly
  under the button (drop-up on tight viewports); each integration
  form (API, CalDAV, CardDAV, Email, Codex/Claude, Vault, MCP) uses
  the same accent-outlined Save/Test/Cancel buttons right-aligned.
- Danger Zone: Wipe→Delete with trash icons; new 'Delete everything'
  row at the bottom that loops every category.

AI Synthesis (Reminders)
- Persona dropdown sourced from PROMPT_TEMPLATES + custom preset.
- src/reminder_personas.py mirrors the five built-ins for the
  server-side synthesis path.
- dispatch_reminder() reads reminder_llm_persona and uses the
  persona's system prompt; empty/unknown falls back to warm-neutral.

Esc handling
- Kebab menus and the provider picker intercept Esc in capture phase
  so dismissing a popup no longer closes the whole Settings modal.

Accent tinting
- Scoped CSS rule across data-settings-panel=ai/services/added-models/
  search/integrations/reminders for card h2 icons + the Added Models
  sub-section icons.

Codex/Claude integration form
- No more auto-creation on form open — explicit Create token button.
- New tokens start with every scope granted; existing tokens move out
  of the integration form into the API Permissions card.
- Setup reveal: copy buttons inline inside the token + setup code
  blocks; shorter subtitle wording.

Misc visual polish
- Save/Test/Cancel uniformly accent-outlined and right-aligned on
  every integration form.
- Provider logos render inline next to the search fallback selects
  and the Deep Research Search dropdown.
- Trash icons in fallback rows bumped to 20x20 so they fill the 32px
  button.
- Image generation default flipped to off.
2026-06-10 15:15:13 +09:00
pewdiepie-archdaemon 7690860ab1 Settings/Add Models: bump Local Type select width 57→62px 2026-06-09 15:12:57 +09:00
pewdiepie-archdaemon b6366e9da5 Settings/Add Models: fuse Local Type select + URL input into one bordered group 2026-06-09 15:12:12 +09:00
pewdiepie-archdaemon 64122269e9 Settings/Add Models: shrink Local Type select by 15px (72→57) 2026-06-09 15:11:07 +09:00
pewdiepie-archdaemon 1bdd515941 Settings/Add Models: drop 'Type:' label, keep the LLM/Image select 2026-06-09 15:10:48 +09:00
pewdiepie-archdaemon 8ac0ae72dc Settings/Add Models: Local card — Type and Add inline with URL field
Lift the LLM/Image Type select to the left of the URL input and the Add
button to its right, so the primary action (URL + Add) sits on one row.
Scan / Ollama / Key / Test stay on the action row below.
2026-06-09 15:09:28 +09:00
pewdiepie-archdaemon b2458f9891 Settings/Add Models: split Local and API into separate cards, always show API key
Drop the in-card Local/API tab strip — each is now its own admin card with
a normal h2 heading (Local on top, API below). The API key input is
always visible (no more click-to-reveal toggle), matching how cloud
providers actually work. Local keeps the optional key reveal since
local servers usually don't need one.

Dead code removed: wireModelsTabs IIFE and the adm-epApiKeyBtn toggle wire.
2026-06-09 14:57:42 +09:00
pewdiepie-archdaemon 2252776a97 Settings: promote Added Models to its own sidebar menu
Move the Added Models endpoint lists out of the Add Models card into a
dedicated sidebar tab between Add Models and AI Defaults. The card now
focuses purely on adding (Local / API tabs), while the new panel owns
the existing endpoints + Probe and Clear-offline controls.

admin.js: defensive fallback so a stale 'added' value in localStorage
falls back to 'local' instead of leaving both panes hidden.
2026-06-09 14:52:48 +09:00
pewdiepie-archdaemon c9fecd53dc Settings: third 'Added Models' tab in Add Models card
Move the Added Local + Added API lists out of the per-type tabs into
a dedicated third tab. Each Add tab is now just the form; the new tab
collects both lists together with Local / API subheadings.

Card layout:
  Add Models  [Probe] [Clear offline]
    [Local]  [API]  [Added Models]

Tab content:
  Local         → Add Local form
  API           → Add API form
  Added Models  → Local list + API list (subheadings)

All endpoint list/form IDs preserved. Tab switcher JS is generic so
the new 'added' tab works without code changes.
2026-06-09 14:47:21 +09:00
pewdiepie-archdaemon 75268e7f43 Fix Cookbook serve server selection 2026-06-09 14:45:22 +09:00
pewdiepie-archdaemon 8ef9b8b215 Settings: tabbed Add Models card with Local / API tabs
Earlier split into 4 flat cards wasn't what was asked for. Restore to
a single 'Add Models' card with two tabs at the top:

  Local  → Add form + Added Local Models list
  API    → Add form + Added API Endpoints list

Probe / Clear-offline live on the card header and act on both lists.
Active tab is remembered in localStorage so the user lands back where
they were. All form/list IDs preserved (adm-epLocalUrl, adm-epList-local,
adm-epList-api, etc.) so admin.js continues to work unchanged.

Replaces the .adm-section-toggle fold-open JS with a tab-switcher; the
fold elements no longer exist so the old handler was already a no-op.
2026-06-09 14:43:28 +09:00
pewdiepie-archdaemon 459b825daa Settings: split Add/Added Models into 4 flat cards (no folds)
The previous 'Add Models' card had two collapsible folds (Local + API)
inside it and 'Added Models' had two inline subsections. Both folded
states added a click-to-expand step that wasn't earning its keep —
users coming to Settings to add a model don't want a fold, they want
the form.

Reshape: four flat admin-cards in the Services panel, each with its
own h2 title matching the rest of Settings:
  Add Local Model       (was Add Models → Local fold)
  Add API               (was Add Models → API fold)
  Added Local Models    (was Added Models → Local subsection)
  Added API Endpoints   (was Added Models → API subsection)

The collapsible JS hook in admin.js already guards on
'if (!head) return' so removing the .adm-section-toggle headers
turns it into a clean no-op — no breakage.

All input/list IDs preserved (adm-epLocalUrl, adm-epList-local,
adm-epList-api, etc.) so the rest of admin.js continues to work
unchanged. Probe / Clear-offline live on the Local card and act on
both lists together (existing behavior).
2026-06-09 14:36:44 +09:00
pewdiepie-archdaemon 3247773447 Hide Teacher Model settings card (2.0 'harden the core' deferral)
The Teacher Mode feature stays out of the default UI per the 2.0
roadmap — backend escalation is already dormant when teacher_model is
unset (its default) and we want to focus on core reliability before
surfacing escalation as a feature.

Nothing removed from the backend:
- src/teacher_escalation.py still gates on get_setting('teacher_model')
- agent_loop.py's run_teacher_inline hook is a no-op without the setting
- settings backup/restore round-trips the teacher_model key unchanged
- power users can still set it via manage_settings or the JSON backup

settings.js's initTeacherModel already early-returns when the card's
DOM ids are missing, so the JS side is clean.

To re-surface the card, revert this commit.
2026-06-09 14:31:04 +09:00
pewdiepie-archdaemon 013beab861 Add Codex and Claude document draft integration 2026-06-09 14:27:53 +09:00
pewdiepie-archdaemon c5230e85a9 Change project license to AGPL-3.0-or-later 2026-06-09 14:25:04 +09:00
pewdiepie-archdaemon d397b3db2f Restore dropped regression fixes 2026-06-09 10:31:43 +09:00
pewdiepie-archdaemon 1a529d63d9 Fix remaining CI regressions 2026-06-09 10:21:56 +09:00
pewdiepie-archdaemon 37c573d865 Fix model endpoint route test regressions 2026-06-09 10:16:38 +09:00
pewdiepie-archdaemon 6f29b287f6 Remove stale plan slash toggle 2026-06-09 09:54:46 +09:00
pewdiepie-archdaemon 4715a5505d Fix duplicate cookbook server helper export 2026-06-09 09:53:41 +09:00
pewdiepie-archdaemon 84ca74f04b Restore cookbook server key exports 2026-06-09 09:51:53 +09:00
pewdiepie-archdaemon e6b1009b89 Remove non-merge-ready workspace and terminal agent hooks 2026-06-09 09:48:59 +09:00
pewdiepie-archdaemon fa8c93ec0a Cookbook UI: Ollama browser, advanced serve fold, API tokens form, diagnosis toolbar, polish
Surface a lot of accumulated cookbook + UI work as a single non-agent
commit so the agent rework lands cleanly.

Highlights:
- Ollama as a first-class backend in the Cookbook:
  * Download input accepts ollama-style names (name:tag) → backend=ollama
  * /api/cookbook/ollama/library (cached scrape of ollama.com + curated
    fallback so classic models like qwen2.5 stay reachable)
  * "Browse Ollama library" toggle below Download with size chips
  * Engine=Ollama in hwfit toolbar merges the Ollama library into the
    main scan list as per-tag rows with the same Fit/Param/Quant/VRAM
    columns; click → fills Download input
- API Tokens form added to Integrations panel (matching wired
  loadTokens()/initTokenForm() that had no HTML)
- Serve panel polish: Advanced fold tightening (-8px nudges on vLLM
  checks, Extra args, Spec row), n_cpu_moe + Split Mode controls
  pulled up 8px to align with the row's checkboxes, GGUF File dropdown
  exposed for Ollama backend, GPU re-render on Edit serve restore,
  _forceBackend flag so saved serveState wins over backend detection,
  cookbook:servers-changed CustomEvent so panels don't need refresh
- Models page redesign: Add Models row (URL + hidden API key reveal +
  Type select + Scan/Ollama/Key/Test/Add icon buttons), Probe All +
  Clear-offline buttons in Added Models toolbar, offline-pill removed
  (opacity already conveys state), Engine dropdown gains Ollama option
- _ping_endpoint probes /v1/models then base, accepts 4xx as
  reachable (vLLM returns 404 on bare /v1, fully working endpoints
  were showing offline)
- Diagnosis card: × dismiss + Copy bundle buttons restored on the
  serve error feedback card
- Orphan tmux sweep re-enabled behind a 60s rate-limit + background
  Thread (off the main event loop) so dead serves get discovered
- cookbook_routes auto-register watchdog: drops the endpoint if the
  serve session exits non-zero within the first ~3min
- ollama-rocm sidecar awareness in download wrapper (`docker exec
  ollama-rocm ollama pull` when host ollama isn't installed)
- Skill extractor sets initial_status="published" when
  auto_approve_skills pref is on (audit demotes later)
- Skill list / model list / cookbook scan misc polish
2026-06-09 09:46:19 +09:00
pewdiepie-archdaemon 646f8bd2a9 Remove remaining plan mode frontend code 2026-06-09 09:44:22 +09:00
pewdiepie-archdaemon 2a2a93d845 Remove plan mode from merge-ready UI 2026-06-09 09:40:20 +09:00
pewdiepie-archdaemon 06a04efc59 Merge branch 'dev'
# Conflicts:
#	routes/task_routes.py
#	src/caldav_sync.py
2026-06-09 09:36:01 +09:00
pewdiepie-archdaemon 3b01760e95 Prepare tested main sync cleanup 2026-06-09 09:34:42 +09:00
fe19d072e3 Revert "fix: expose supports_tools toggle for local endpoints in UI (#3195)" (#3438)
This reverts commit 7b68413433.

Co-authored-by: pewdiepie-archdaemon <pewdiepie-archdaemon@users.noreply.github.com>
Co-authored-by: Kenny Van de Maele <kenny@kvandemaele.be>
2026-06-08 14:46:01 +02:00
PewDiePieandpewdiepie-archdaemon 09565acc1e Revert "feat(model-picker): add remove-from-recent button to Recent section rows (#2894)" (#3437)
This reverts commit 2a422c00ec.

Co-authored-by: pewdiepie-archdaemon <pewdiepie-archdaemon@users.noreply.github.com>
2026-06-08 14:41:25 +02:00
PewDiePieandpewdiepie-archdaemon c9198baa2e fix: make agent loopback base port env-configurable (#2752) (#2753)
_COOKBOOK_BASE was hardcoded to http://localhost:7000 with no env-var
override anywhere in the codebase. Tools that do an internal HTTP
loopback (app_api, trigger_research, cookbook state read/write) silently
fail with "All connection attempts failed" whenever the running uvicorn
isn't on port 7000 — which is most non-default deployments and any
side-by-side multi-instance setup. The misleading "Task triggered"
message from manage_tasks during a research request hides that the
underlying research never starts.

Resolution order, lowest to highest priority:
  1. Fallback http://127.0.0.1:7000 (preserves legacy default).
  2. APP_PORT — derive http://127.0.0.1:$APP_PORT (matches docker-compose
     which already reads APP_PORT).
  3. ODYSSEUS_INTERNAL_BASE — explicit override (e.g. behind a TLS proxy
     where loopback isn't 127.0.0.1).

127.0.0.1 instead of "localhost" avoids IPv6/DNS ambiguity for a
strictly-local call.

No API or schema change. Defaults preserved: existing setups on port
7000 are unaffected.

Caught by #2752.

Co-authored-by: pewdiepie-archdaemon <pewdiepie-archdaemon@users.noreply.github.com>
2026-06-07 18:47:47 +02:00
pewdiepie-archdaemon e0e250d023 Calendar: cross-session delete sync — 404 = success, refetch on tab focus
A stale event deleted on one device stayed undeletable on every other
session: the cached row showed up, the DELETE call returned 404 (server
already removed it), the optimistic catch-block restored the row, and
the user could never clear it.

- Treat HTTP 404 on DELETE as success — the event is already gone,
  which is the state we wanted. Skip the optimistic restore.
- Re-fetch the visible range on document `visibilitychange` (mobile
  app returns to foreground) and on window `focus` (desktop alt-tab),
  throttled to once per 10s so rapid tab-flipping doesn't hammer the
  API. Without a focus refresh, mobile only got fresh server state at
  page-load and lived on stale data until a full reload.
2026-06-05 17:05:04 +09:00
pewdiepie-archdaemon 2ba77e3aa3 Settings polish: /setup provider subs, Add API defaults to api kind, picker shows offline endpoints, doc library tracks sub-tab
- /setup gains explicit provider subcommands (deepseek, openai,
  anthropic, openrouter, groq, gemini, xai, ollama, copilot, local,
  endpoint) so the autocomplete popup surfaces "/setup de…" suggestions
  with format hints, and bare-provider invocations still prompt for
  the key.
- Add API endpoint defaults to kind=api (auto-refresh /v1/models)
  instead of kind=proxy. Proxy was a frequent footgun for OpenAI-
  compatible endpoints that DO serve /v1/models — the user got an
  empty model list and had to flip the dropdown.
- Model picker now includes offline endpoints with stale:true so a
  briefly-down local server doesn't vanish from the picker (it dims
  and shows the offline pill, clickable anyway). Dedup prefers the
  online entry when the same model is exposed by both.
- Document library modal header reflects the active sub-tab via
  _TAB_HEADERS so it no longer shows the wrong section name when
  switching between Documents / Skills / Templates.
2026-06-05 14:41:54 +09:00
pewdiepie-archdaemon fbd34334a5 Calendar overnight-event rendering + clickable [View note] link from chat
- Calendar overnight events render proportionally across day boundaries
  via --start-frac / --end-frac CSS vars instead of bleeding as full-day
  on day 2.
- Recurring-event delete strips the master uid + all master::* sibling
  instances optimistically so the row clears immediately instead of
  waiting for the next sync re-render.
- manage_notes(create) now returns note_id + open_url, and agent_loop
  appends a markdown [View note](#note-<id>) link mirroring the
  deep-research pattern.
- chatRenderer's hash-link router (already wired for #note-id) reaches
  the new notes.openNote(id) helper, which force-closes/reopens the
  Notes panel, polls for the target card, and runs a brief outline
  flash so the user can locate it on long lists.
2026-06-05 14:41:48 +09:00
pewdiepie-archdaemon e2f449f4ef Cookbook scheduler + serve: schedule via Tasks, Stop verifies kill, Ollama auto port-pick
- Schedule cookbook serves through the existing ScheduledTask system: the
  serve preset gets a ^ button next to Launch that opens a daily/hourly/
  weekly form mirroring the admin-switch style; the schedule action runs
  action_cookbook_serve, which delegates to /api/model/serve and stamps
  the resulting task with _scheduledStopAtMs. A background
  cookbook_serve_lifecycle loop ticks every 60s and kills any serve
  whose window has ended, also dropping the auto-registered endpoint
  so the model picker doesn't keep pointing at a dead server.
- Stop and remove on a Running serve now awaits the SSH/tmux kill,
  re-checks tmux has-session, and surfaces an error toast (leaving the
  row) when the kill failed. Previously fire-and-forget, so a failed
  SSH/tmux call silently left the live serve running while the row
  vanished from the UI.
- Cookbook tasks/status orphan-adoption sweep no longer requires the
  serve-/cookbook- session-id prefix; any tmux session whose pane is
  running a known model-server process gets auto-pulled into Running.
  Without this loosening, a cookbook-launched serve whose tmux id
  fell back to a bare number was invisible — you couldn't see it,
  let alone stop it.
- Ollama serve always launches a fresh process under cookbook's tmux
  (no more monitor-mode reattach to a systemd/Docker ollama Stop can't
  reach). The handler pre-picks a free port by probing the target
  host over SSH and mutates req.cmd's OLLAMA_HOST so the runner script
  AND the auto-registered endpoint agree on the same bind port.
- Auto-register uses host.docker.internal (when running inside Docker)
  instead of localhost, matching the URL /setup adds for Ollama by
  hand. Local cookbook serves now produce a chat-reachable endpoint
  on first launch.
- Cascade-delete: removing a scheduled cookbook task also deletes any
  linked calendar event (cookbook_task_id marker in the description).
- Tasks list groups cookbook_serve under a "Cookbook" category that
  sorts above the rest, so scheduler-launched serves are easy to find.
2026-06-05 14:41:43 +09:00
pewdiepie-archdaemon f8aaeab245 Merge remote-tracking branch 'origin/dev' 2026-06-05 12:14:34 +09:00
pewdiepie-archdaemon f19ac6ed03 Merge branch 'main' of github.com:pewdiepie-archdaemon/odysseus
# Conflicts:
#	static/js/cookbookRunning.js
2026-06-05 11:23:15 +09:00
pewdiepie-archdaemon a260e0abd4 Revert calendar-based cookbook scheduler
Reverts b98ee04 + 4ed48ba + a19b6d2.

Calendar events turned out to be the wrong abstraction for scheduling model serve windows. Pivoting to the existing ScheduledTask infrastructure (cron / daily / weekly recurrence, next_run tracking, edit-from-Tasks-tab UI) in a follow-up commit. The ScheduledTask path:

  - reuses dispatch logic the rest of the app already understands
  - drops the calendar dependency entirely (no auto-created "Cookbook" calendar, no calendar.js hook)
  - shows up in the Tasks UI that already exists for everything else

What this revert removes:
  - src/cookbook_scheduler.py — calendar reconciler
  - routes/cookbook_schedule_routes.py — /api/cookbook/schedule/* endpoints
  - static/js/cookbookSchedule.js — Schedule modal / settings card
  - cookbook_scheduler_enabled + cookbook_schedule_calendar_href settings keys
  - The window.cookbookOpenScheduleForm hook in calendar.js
  - The Schedule button + paired-button CSS in cookbookServe.js + style.css
2026-06-05 06:57:21 +09:00
pewdiepie-archdaemon b98ee04e2f Cookbook scheduler: reuse the standard calendar event card + auto-create Cookbook calendar
Drop the custom Schedule modal in favor of opening the calendar's existing event-creation form pre-filled with the model's name + cookbook YAML in the description. The user lands in the same event editor they already know from regular calendar use, just pointed at the auto-created "Cookbook" calendar.

Backend:
  - POST /api/cookbook/schedule/ensure-calendar — idempotent: creates a calendar named "Cookbook" if one doesn't exist for the current user, saves its href into cookbook_schedule_calendar_href, flips cookbook_scheduler_enabled on. Verifies the saved href against /api/calendar/calendars on every call so a manually-deleted calendar self-heals.

Frontend:
  - calendar.js: expose window.cookbookOpenScheduleForm(draft) which opens the calendar modal (if not open), calls _showEventForm, then pre-fills summary / description / rrule / calendar dropdown. Force-expands the "Add details" section so the user can see which calendar it's heading into.
  - cookbookSchedule.js: Schedule-button click now calls ensure-calendar, builds the cookbook: YAML block, and routes to window.cookbookOpenScheduleForm instead of openModal(). The legacy custom modal stays as a fallback for the case where calendar.js hasn't loaded.

UX tweak:
  - cookbookServe.js: replace the standalone "Schedule…" text button with a small icon-only button (clock SVG) glued to the right edge of Launch. The pair forms one visual unit — Launch on the left, schedule-now on the right — sharing a thin divider. CSS handles the rounded corners + divider.
2026-06-05 02:52:07 +09:00
pewdiepie-archdaemon 4ed48baf68 Cookbook scheduler: inline settings card at the top of the Cookbook tab
The earlier scheduler commit shipped the backend + Schedule modal but left the feature dormant — no way to toggle it from the UI. This adds the missing knob:

* DEFAULT_SETTINGS gains `cookbook_scheduler_enabled` (False) and `cookbook_schedule_calendar_href` ("") so `/api/auth/settings` POST will actually persist them. Without this, the POST silently dropped unknown keys.

* cookbookSchedule.js gains a self-contained settings card injected at the top of the Cookbook tab body whenever the cookbook modal opens. Card contents:
  - Enable toggle (writes cookbook_scheduler_enabled)
  - Calendar dropdown populated from /api/calendar/calendars (writes cookbook_schedule_calendar_href)
  - Status line: off / pick-a-calendar / N scheduled in next 24h · M running now · K skipped
  - "Reconcile now" button that POSTs /api/cookbook/schedule/reconcile-now

* The same module reveals/hides the Schedule… buttons on serve panels whenever the feature flag changes, so toggling on immediately surfaces the schedule UI without a refresh.

Settings UI lives in cookbookSchedule.js (not settings.js) so the entire scheduler surface — backend, reconciler, modal, settings — collapses cleanly: delete src/cookbook_scheduler.py + routes/cookbook_schedule_routes.py + static/js/cookbookSchedule.js, drop the two DEFAULT_SETTINGS keys, and the two app.py registration lines, and the feature is gone.
2026-06-05 02:40:35 +09:00
pewdiepie-archdaemon a19b6d2d4d Cookbook scheduler: calendar events drive model serve windows (experimental, feature-flagged)
Add a calendar-driven scheduler so a user can pick a model in Cookbook, click "Schedule…" instead of "Launch", choose time windows + days of the week + (optional) end date, and have Odysseus auto-launch the serve when the window starts and hard-kill it when the window ends. The calendar IS the source of truth — events on a designated calendar are interpreted as serve schedules, so editing the event in the calendar UI immediately changes the schedule.

Whole feature is gated by setting `cookbook_scheduler_enabled` (default False). Disabling the setting silences the reconciler and the API refuses requests; setting + three new files = entire surface, easy to revert.

New files:
  - src/cookbook_scheduler.py — background reconciler: ticks every 60s, reads next ±90s of calendar events on the designated calendar, launches/kills serves to match. Honors "refuse if GPUs busy" (skips with reason, no retry). Adopts pre-existing manual serves matching the event's model so window-end cleanup still applies. Tags scheduler-owned tasks with `_scheduledBy: <event_uid>` so it never kills serves it doesn't own.
  - routes/cookbook_schedule_routes.py — POST /api/cookbook/schedule/from-cookbook builds RRULE+ICS events from the modal's input (model, slots[], days[], until). GET /upcoming returns the next 24h with per-event status (scheduled / running / adopted / skipped / failed / ended) for the UI. POST /reconcile-now manually kicks the reconciler.
  - static/js/cookbookSchedule.js — Schedule button click handler + modal. Daily/hourly time slot picker, multi-slot ("+ add another time slot"), weekday chips with Weekdays/Weekend/Every-day quicksets, optional Until date. Calls /from-cookbook on save. Whole module is a single IIFE; deleting the file plus its <script> tag removes the UI surface.

Existing files touched (minimal):
  - app.py: register the new router + add the reconcile loop as a startup task (~10 lines, all in one block). Reconcile loop checks the feature flag on every tick, so leaving it running with the flag off costs ~one settings lookup per minute.
  - static/index.html: one new <script> tag for cookbookSchedule.js.
  - static/js/cookbookServe.js: add a "Schedule…" button next to the existing Launch button. Hidden by default; cookbookSchedule.js reveals it after confirming the feature flag is on.
  - static/style.css: ~80 lines for the modal styles (mobile-aware via @media).

User choices baked in:
  - Calendar events are the source of truth.
  - Refuse to launch if GPUs busy (skip + log reason in scheduler.events[uid].reason).
  - Hard kill at event end.
  - No retry on a skipped event within the window.
  - Multi-slot per day supported (one calendar event per slot, shared RRULE).
  - Pre-existing manual serves get adopted at window start so they're killed at end.

Known follow-ups (not in this commit):
  - Settings UI to pick the schedule calendar + toggle the feature flag.
  - Calendar event color/badge for status (running/skipped/failed).
  - "Lazy launch on first request" — currently launches at event start. Replacing _launch_serve with a proxy that defers vllm until the first chat request is a contained future change.
2026-06-05 02:35:23 +09:00
pewdiepie-archdaemon 9112861d8e cookbook agent debug loop: persistent log files, auto-adopt orphan tmux, Codex/Claude skill parity
Three converging fixes so the chat agent + external Codex/Claude skills can actually debug a crashed serve instead of staring at a post-crash neofetch banner:

* Serves now `tee` to /tmp/odysseus-tmux/SESSION.log on the host running them. Runner saves fds 3/4 before the tee and restores them right before `exec ${SHELL}`, so the post-crash interactive zsh banner does NOT pollute the log file.
* `tail_serve_output` (chat agent) and `/api/codex/cookbook/output/{sid}` (Codex+Claude skills) both prefer the persistent log file over the tmux pane. Pane is fallback for sessions predating the tee runner. Default tail bumped 150 -> 400.
* `list_served_models` "recent log" snippet seeks to the Traceback line instead of showing the last 6 lines (which was always the bash prompt).

Cookbook auto-adoption sweep on `/api/cookbook/tasks/status`: every 20s (rate-limited) the cookbook SSHes each configured server, finds `serve-*` / `cookbook-*` tmux sessions running an actual model process (vllm/python/llama-server/etc., filtered via `pane_current_command`), and writes them into state.tasks. So when the agent falls back to raw ssh+tmux, the session appears in the Cookbook UI on the next poll.

`serve_model` error path now reads `data["detail"]` in addition to `data["error"]` so the FastAPI HTTPException message ("Invalid characters in cmd") actually reaches the agent instead of being swallowed as a generic "Serve failed". Tool description updated to warn against `cd …`/`source …`/`&&` prefixes.

Intent-without-action supervisor in agent_loop: when the model writes "Let me tail the output" / "I'll check the logs" / "Let me investigate" and ends the turn without emitting a tool call, the loop injects a sharp system nudge ("You said you would X — DO IT NOW") and continues. Capped at 2 nudges per chat so a model that genuinely cannot use the tool does not pin the loop.

Codex/Claude skill parity: adds `/cookbook/cached`, `/cookbook/presets`, `/cookbook/preset/{name}`, `/cookbook/adopt` so external agents have the same surface as the chat agent. SKILL.md docs + odysseus_api.py wrapper updated for both bundles.

`adopt_served_model` promoted to the always-on tool set so the agent has a documented fallback when serve_model rejects a cmd.

Also various cookbook UI tweaks accumulated alongside the above (cookbook.js, cookbookRunning.js, cookbookServe.js, cookbook-diagnosis.js, settings.js, style.css).
2026-06-04 23:27:18 +09:00
pewdiepie-archdaemon 041c03bf11 Add dev/main branch model: PRs target dev, main is curated
Switching to a two-branch workflow: contributors open PRs against `dev`,
and `main` is fast-forwarded to a tested `dev` commit at each release.
This separates "things land in staging" (can move fast) from "things
ship to users" (slow, tested in a browser by the maintainer first).

CONTRIBUTING: add a Branch model section explaining the split + how to
retarget a PR.
PR template: add an explicit "this PR targets dev" checkbox at the top
so it's the first thing a contributor confirms.

End-users cloning the repo will now land on `dev` by default; they can
`git checkout main` if they want the curated branch.
2026-06-04 20:52:56 +09:00
pewdiepie-archdaemon 48f5182286 Merge branch 'codex-on-main' 2026-06-04 08:27:41 +09:00
pewdiepie-archdaemon 089246614d feat: Claude Agent integration + cookbook reconnect + UI polish
- Claude Agent integration: AGENT_CONFIGS.claude, INTG_TYPES.claude,
  setup_claude_routes + integrations/claude/ skill bundle. Wired in
  app.py alongside the existing Codex integration; same scope-gated
  /api/codex/* backend; agent form has new description so users know
  it's setup for an external CLI, not an agent streamed inside Odysseus.
- Remove mark_email_boundaries action: not good enough yet. Stripped
  from task UI, scheduler defaults, registry, tool schema, clear-cache
  route. Added to RETIRED_HOUSEKEEPING_ACTIONS so existing rows + their
  task_runs auto-purge on startup.
- Cookbook download reliability: "Reconnect" fix button in the crash
  diagnosis runs _reconnectTask after probing has-session. 30s confirm
  window before marking a download "done" — kills the Finished/Downloading
  flicker when tmux briefly drops between captures.
- Mobile UX: tap anywhere on a note card body opens the editor;
  Update button morphs to Archive when no text was edited; bell icon
  accent-colored; chip-trashing notif pills fade so only the icon
  rotates into the trash zone.
- Settings integrations: SVG-per-provider in email + API preset
  dropdowns, custom drop-up-aware menus, accent sub-header icons
  (IMAP/SMTP), consistent card styling between list + edit, contacts
  Edit/Delete icons, agent form description copy.
2026-06-04 08:27:26 +09:00
pewdiepie-archdaemon 67b63e9844 Revert "fix(ui): allow manual prompt bar resize (#1201)"
This reverts commit 258e6fc0d4.
2026-06-03 23:04:28 +09:00
pewdiepie-archdaemon 6e80d0de08 Revert "fix(ui): allow manual prompt bar resize (#1201)"
This reverts commit 258e6fc0d4.
2026-06-03 23:03:58 +09:00
pewdiepie-archdaemon 5939aec69f Codex Agent integration: HTTP surface + plugin bundle + Settings UI
This persists work that had been living only in the cookbook docker
container's writable layer — never committed to the host source. Brought
back to git intact, app.py registration re-applied surgically on top of
current main (not the older container copy, which would have regressed
the Windows MIME fix, asynccontextmanager lifespan, and webhook auth
exempts).

routes/codex_routes.py (new):
- GET  /api/codex/capabilities  — what this Odysseus exposes.
- GET  /api/codex/plugin.zip    — downloads integrations/codex as a zip.
- GET  /api/codex/todos         — scope-gated todos:read|write.
- POST /api/codex/todos         — scope-gated todos:write.
- GET  /api/codex/emails        — scope-gated email:read|draft|send.
- GET  /api/codex/emails/{uid}  — single-message fetch.
- _scope_owner() enforces api_token scopes before touching user data.

routes/api_token_routes.py (+103 lines):
- Adds Codex-token-specific issuance + revocation paths.

integrations/codex/ (new bundle, shipped via /api/codex/plugin.zip):
- README.md                       — install instructions.
- .codex-plugin/plugin.json       — Codex plugin manifest.
- scripts/odysseus_api.py         — Python client used by the skill.
- skills/odysseus/SKILL.md        — Codex skill definition.

static/js/settings.js (+253 lines):
- New "Codex Agent" option in the Integrations dropdown.
- Add / edit panel with plugin-bundle download link + curl-with-token
  install instructions per agent.

app.py:
- 7-line surgical change: capture email_router = setup_email_routes()
  and register setup_codex_routes(email_router=email_router) after the
  email module so the Codex routes can borrow its helpers.
2026-06-03 22:49:09 +09:00
pewdiepie-archdaemon 1f6c5ac66b Revert "Codex Agent integration: HTTP surface + plugin bundle + Settings UI"
This reverts commit 8c2705b42a.
2026-06-03 22:47:00 +09:00
pewdiepie-archdaemon 6861c41580 Reapply "Merge branch 'main' of github.com:pewdiepie-archdaemon/odysseus"
This reverts commit cc8fe2f6e3.
2026-06-03 22:47:00 +09:00
pewdiepie-archdaemon cc8fe2f6e3 Revert "Merge branch 'main' of github.com:pewdiepie-archdaemon/odysseus"
This reverts commit 8161c1253d, reversing
changes made to 8c2705b42a.
2026-06-03 22:46:19 +09:00
pewdiepie-archdaemon 8161c1253d Merge branch 'main' of github.com:pewdiepie-archdaemon/odysseus 2026-06-03 22:39:33 +09:00
pewdiepie-archdaemon 8c2705b42a Codex Agent integration: HTTP surface + plugin bundle + Settings UI
This persists work that had been living only in the cookbook docker
container's writable layer — never committed to the host source. Brought
back to git intact, app.py registration re-applied surgically on top of
current main (not the older container copy, which would have regressed
the Windows MIME fix, asynccontextmanager lifespan, and webhook auth
exempts).

routes/codex_routes.py (new):
- GET  /api/codex/capabilities  — what this Odysseus exposes.
- GET  /api/codex/plugin.zip    — downloads integrations/codex as a zip.
- GET  /api/codex/todos         — scope-gated todos:read|write.
- POST /api/codex/todos         — scope-gated todos:write.
- GET  /api/codex/emails        — scope-gated email:read|draft|send.
- GET  /api/codex/emails/{uid}  — single-message fetch.
- _scope_owner() enforces api_token scopes before touching user data.

routes/api_token_routes.py (+103 lines):
- Adds Codex-token-specific issuance + revocation paths.

integrations/codex/ (new bundle, shipped via /api/codex/plugin.zip):
- README.md                       — install instructions.
- .codex-plugin/plugin.json       — Codex plugin manifest.
- scripts/odysseus_api.py         — Python client used by the skill.
- skills/odysseus/SKILL.md        — Codex skill definition.

static/js/settings.js (+253 lines):
- New "Codex Agent" option in the Integrations dropdown.
- Add / edit panel with plugin-bundle download link + curl-with-token
  install instructions per agent.

app.py:
- 7-line surgical change: capture email_router = setup_email_routes()
  and register setup_codex_routes(email_router=email_router) after the
  email module so the Codex routes can borrow its helpers.
2026-06-03 22:38:05 +09:00
pewdiepie-archdaemon 562bc4dedc Cookbook polish: auto-reconnect, ctx slider fixes, scoring, lots of UI
Backend (services/hwfit + routes):
- VRAM column sort now shows global highest first (was special-cased to
  ascending then truncated top-N, which made "highest VRAM" mathematically
  unreachable). Every column path uses reverse=True for the truncation.
- Hardware probe cache TTL 30min -> 24h so changing filters doesn't keep
  re-probing the rig during a session; Rescan button still forces fresh.
- Multi-GPU rigs filter GGUF Q*/IQ quants (vLLM/SGLang can't serve them);
  default non-prequantized to BF16 on 2+ GPUs.
- AWQ / AWQ-8bit / GPTQ-8bit get a -1.0 quality penalty so FP8 wins ties.
- Version-aware tiebreaker (parse Mn.n / Vn) — MiniMax-M2.7 ranks above M2.5.
- hf_models.json: zai-org/GLM-5.1 added; zai-org/GLM-5 quantization flipped
  Q4_K_M -> BF16. DeepSeek-V4-Flash / -Pro + their -Base variants registered
  with new FP4-MoE-Mixed / FP8-Mixed quant keys (calibrated BPP from the
  actual 156 GB / 284 GB disk footprints).
- New FP4-MoE-Mixed + FP8-Mixed entries in QUANT_BPP / QUANT_SPEED_MULT /
  QUANT_QUALITY_PENALTY / QUANT_BYTES_PER_PARAM / PREQUANTIZED_PREFIXES.

Frontend — Scan/Download:
- Engine + Quant swapped in the toolbar; Quant defaults to "All".
- Ctx (range slider) ported from origin/main: 8k/16k/32k/50k/128k/Max. Drag
  re-sorts by vram ascending (smallest fitting first); back to Max → score.
- Ctx slider rail now visible — was background:transparent in a duplicate
  later-cascade rule. Hardcoded grey + !important.
- Search input moved to the far right of the toolbar.
- Type/Standard default; "Context" not uppercased; Search placeholder dimmed.
- Engine "?" + Quant "?" inline help chips inside their dropdown boxes.
- Fit-column dot toggles fit-only filter; un-toggling re-sorts by VRAM desc.
- Quant column truncates to 9 chars + ellipsis ("FP4-MoE-M..."), full in
  tooltip. Smart title-suffix strips the parts already in the repo name
  (QuantTrio/MiniMax-M2-AWQ + quant AWQ-4bit -> just "(4bit)").
- Conditional warning for safetensors models on non-GPU rigs only.
- Dependency Install / Installed / Installed▾ / N/A all 75.85px wide.
- Rebuild llama.cpp moved into the llama_cpp dep row, styled as a tag.
- Foldable Download admin-card (h2 chevron); line under h2 only when folded.
- HF token save gets a green ✓ + "Saved" flash.
- Cached scan no longer counts stalled rows as downloaded.
- Footer: "Request it →" link with GitHub mark to the public discussion
  (#1962) for model-add requests.

Frontend — Running tab:
- Strict download-finish check (DOWNLOAD_OK or /snapshots/, not bare
  "Download complete"). True overall % for multi-shard downloads:
  ((N-1)+frac)/total instead of hf_transfer's per-shard aggregate.
- ETA in the uptime ticker: "downloading: 12m 34s · ETA 1h 23m".
- Clear button kills the tmux session too; if the output still shows a
  live shard line, the pill is hidden + relabels as "reconnect" + revives
  on click.
- Self-heal: on cookbook open AND every bg-monitor cycle (10s, throttled
  to 8s), scan persisted done/error/crashed downloads and probe their
  tmux session — if alive, flip status back to running and reattach.
- Per-launch zombie probe: clicking Download on a model whose persisted
  state is done but tmux is still alive revives the existing task and
  refuses to start a duplicate.
- Pre-launch GPU probe: vllm / sglang / diffusers serve check
  /api/cookbook/gpus first; warns + confirms if no GPU is visible.
- Server-side state guard: rejects "done" POSTs for downloads lacking
  DOWNLOAD_OK / DOWNLOAD_FAILED / /snapshots/ when the last-mentioned
  shard is N<total — stale tabs can't poison persisted state any more.
- Running count includes tasks whose output looks active even if persisted
  status got stuck. Dir text on the running row, font matched to uptime.

Serve panel:
- Ctx text input always resets to model max on open (default 20000 when
  metadata is missing).
- Max Seqs default 8 -> 4. KV Cache dtype select 32px tall.
- Lightning icon on Launch (same as Action toggle).
- Diagnosis card simplified (no fold/copy/dismiss), suggestion font
  matches body; action buttons get icons on the left (Retry/Copy/Edit/
  Install/Kill/Switch/etc.).
- Incomplete-download serve warning when model status is
  downloading / stalled / has_incomplete.
- MTP "?" tooltip ("supported on a few model families … up to ~3× faster").
2026-06-03 20:25:25 +09:00
pewdiepie-archdaemon 3706d756f3 Merge remote-tracking branch 'origin/main' into visual-pr-playground
# Conflicts:
#	routes/cookbook_routes.py
#	routes/hwfit_routes.py
#	services/hwfit/fit.py
#	services/hwfit/models.py
#	static/js/cookbook-diagnosis.js
#	static/js/cookbook-hwfit.js
#	static/js/cookbook.js
#	static/js/cookbookRunning.js
2026-06-03 16:49:10 +09:00
pewdiepie-archdaemon eb79b76432 Cookbook: scoring fixes, UI polish, false-finished + stale-state bug fixes
Backend (services/hwfit + routes):
- rank_models picks visible set by REQUESTED column, not always score —
  sorting by Param now shows highest-param models PERIOD (incl. too_tight).
- New fit_only param. Multi-GPU rigs filter GGUF Q*/IQ quants (vLLM/SGLang
  cannot serve them); default non-prequantized to BF16 on 2+ GPUs.
- AWQ / GPTQ-8bit get a -1.0 quality penalty (was 0.0, tied with FP8), so
  FP8 wins when both fit.
- Version-aware tiebreaker (parse Mn.n / Vn) — MiniMax-M2.7 ranks above
  M2.5 on equal composite score; >=100B integers not misread as versions.
- /api/cookbook/hf-latest no longer drops models without an "NB" pattern in
  the repo id (MiniMax-M2.7, DeepSeek-V4-Pro etc. were silently filtered).
- Cached-model scan: atexit flushes models JSON even if the script is
  killed mid-walk; each scan_dir wrapped in try/except; timeout 60s -> 180s.
- KB granularity for sub-MB sizes (was "0 MB" for 12 KB shells). New
  "stalled" status for shells <1 MB with no .incomplete files.
- /api/cookbook/state POST guard: rejects "done" download tasks lacking
  DOWNLOAD_OK / DOWNLOAD_FAILED / /snapshots/ when the last-mentioned
  shard is N<total — stops stale tabs from poisoning persisted state.
- hf_models.json: add zai-org/GLM-5.1; flip zai-org/GLM-5 quantization
  Q4_K_M -> BF16 (it is the native base, not a quant).

Frontend (static/js):
- Scan/Download toolbar: quant defaults to All; ctx slider (8k/16k/32k/
  50k/128k/Max) ported from origin/main with sort=fit on drag, sort=score
  on Max. GPU toggle commits _activeCount to maxGpu on initial render. Fit
  column header tagged with active budget (RAM / GPU / N GPU).
- Foldable Download admin-card: the Download h2 is the chevron trigger;
  state persists in localStorage.
- Download card surfaces destination dir (Dir: <path>). Same dir on running
  task row, font/color matched to uptime (9px Fira Code muted, opacity .4).
- Serve panel ctx text input always resets to model max on open. Sub-MB
  cached models show with red "download stalled" badge.
- Bulk-select Cancel + Delete reset the Select button label on exit.
- Cookbook running: false-finished bug fixed — DOWNLOAD_OK or /snapshots/
  required; bare "Download complete" no longer marks the task done after
  the first config file. Clear button now sends tmux kill-session too.
  True overall % for multi-shard downloads: ((N-1)+frac)/total instead of
  hf_transfer per-shard aggregate.
- Diagnosis card simplified: removed fold toggle, copy button, dismiss X.
  Suggestion font matches message body (12px).
- HF token field flashes green check + "Saved" on save.
- Cached scan no longer counts stalled rows as downloaded in Scan/Download.

CSS:
- dep Install button width pinned to 76px to match Installed split.
- task-sub row +1px; task-status badge gets margin-right 8px.
- Ctx slider styled like gallery editor sliders (thin pill rail, red thumb).
- Bulk-select cancel button top -3px -> -5px.
2026-06-03 16:32:20 +09:00
pewdiepie-archdaemon 41a928f21b Tighten PR template and CONTRIBUTING to gate visual/style changes
The post-launch PR flood from LLM coding agents drowned the repo in PRs
that don't run the app, attach no screenshots, and invent parallel
component styling. Even tiny correctness fixes accumulated into a visual
mess. Make the rules explicit in both the PR template and CONTRIBUTING:

- Run the app and view the change in a browser before submitting.
- Required screenshot for any UI/render touch (no longer "delete if not UI").
- Explicit style requirements: reuse CSS variables, no Unicode emoji
  (use SVG icons), monospaced font, dark theme, no parallel widgets.
- Direct callout for bulk agent-generated PRs: open an issue first.

PRs that ignore these will be closed without merge, regardless of code
correctness.
2026-06-03 15:35:10 +09:00
pewdiepie-archdaemon ed7956cbd3 Owner-scope RAG doc ids so identical chunks across users don't collide (#1738, #1760)
_generate_doc_id hashed only text. add_document / add_documents_batch
early-return when the id exists, so the second owner indexing a
byte-identical chunk hit the first owner's id, was silently dropped,
and never stored under their owner — their owner-filtered search then
quietly omitted it. Hash owner + text; empty owner reproduces the
legacy id, so the unowned/base index keeps existing ids and isn't
re-churned. Same-owner identical chunks still dedupe.

Caught by #1738 and #1760 (independent reports of the same bug).
2026-06-03 11:36:31 +09:00
pewdiepie-archdaemon 8e2b9baf19 Rebuild memory vector index from the full saved set, not just the audited owner (#1747)
audit_memories saves final_entries merged with other owners' entries
(correct), but then rebuilt the shared vector collection from
final_entries alone — wiping every other owner from semantic search
until they happened to run their own audit. Keyword fallback masked
it, so it degraded silently. Capture saved_entries once and rebuild
from that.

Caught by #1747.
2026-06-03 11:36:24 +09:00
pewdiepie-archdaemon 8a10f271f7 Memory MCP delete: match exact id, not prefix (#1303)
The delete action looked up the target with startswith() to capture
full_id, but then re-applied startswith() to filter the list — so a
short or ambiguous memory_id silently deleted every memory whose id
shared the prefix, while the success message reported only the first
match. The edit action used the first match and stopped, so the two
actions disagreed on multi-match behaviour. Use full_id for both.

Caught by #1303.
2026-06-03 11:36:19 +09:00
pewdiepie-archdaemon 9960d55a41 Decrypt CalDAV password before write-back (#1731)
writeback_event read cfg["password"] (the encrypted blob) and passed it
straight to DAVClient, so every local create/edit/delete authenticated
with the literal ciphertext, the remote rejected it, and the change
never reached the server — the exact silent-write-loss this module was
built to prevent. The pull path src/caldav_sync.py already decrypts;
mirror that. decrypt() is a no-op on legacy plaintext.

Caught by #1731.
2026-06-03 11:36:12 +09:00
pewdiepie-archdaemon 6153c5ed68 Close app_api blocklist gap for bare /api/tokens and /api/users
The blocklist prefixes had trailing slashes, so path.startswith() only
matched /api/tokens/{id} but not /api/tokens itself — the bare GET (list)
and POST (mint) endpoints were reachable via app_api. Same gap on
/api/users (list/create/delete). Drop trailing slashes so both bare and
sub-resource forms are blocked. /api/auth and /api/admin had no bare
endpoints today but get the same treatment to prevent future drift.

Caught by #1462.
2026-06-03 11:20:39 +09:00
pewdiepie-archdaemon ff93a6c63b Polish email and cookbook flows 2026-06-02 22:42:07 +09:00
pewdiepie-archdaemon 966b53df77 Improve Cookbook serve diagnostics and recommendations 2026-06-02 12:15:47 +09:00
pewdiepie-archdaemon 1c9623a81d Protect memory tidy owner scope 2026-06-02 09:52:52 +09:00
pewdiepie-archdaemon da97f1b9ad Label Docker bind mounts for SELinux 2026-06-02 09:50:35 +09:00
pewdiepie-archdaemon 50b81622e0 Allow Docker startup without env file 2026-06-02 09:49:35 +09:00
pewdiepie-archdaemon 6a78b02976 Fix endpoint model preservation for tasks 2026-06-02 09:44:24 +09:00
PewDiePie d60ff44c1b Merge pull request #797 from ErnestHysa/fix/research-path-traversal
fix(research): validate session_id to block path traversal
2026-06-02 09:42:23 +09:00
PewDiePie 7187118aa6 Merge pull request #782 from tanmayraut45/fix/active-streams-toctou
Fix TOCTOU race in chat stream status endpoint
2026-06-02 09:42:07 +09:00