Skip to content

Comments

Dev → Main: chat activity flags, swarm done column, markdown rendering, scroll, notebook width#10

Merged
GoDoming merged 42 commits intomainfrom
dev
Feb 24, 2026
Merged

Dev → Main: chat activity flags, swarm done column, markdown rendering, scroll, notebook width#10
GoDoming merged 42 commits intomainfrom
dev

Conversation

@GoDoming
Copy link
Member

Summary of changes

feat(chat): has_activity flag for efficient wake polling

  • Add has_activity boolean to chat_members (migration 0008)
  • Partial index on (identity) WHERE has_activity = true
  • Set true for all non-sender members on message send
  • Cleared on markChannelRead (triggered by GET messages or POST /read)
  • Wake endpoint now surfaces a chat source with message previews

feat(chat): markdown rendering + smart auto-scroll

  • Chat messages rendered as markdown via marked + DOMPurify
  • Auto-scroll to bottom on initial channel open (was broken before)
  • Smart scroll: only force-scroll on new messages if already near bottom
  • Reset scroll flag when switching channels

feat(swarm): always show done column with 12h default window

  • Done column always visible in kanban and list modes
  • Default: tasks completed in past 12 hours
  • "Show more items" button expands by 1 month per click
  • Removed the Show/Hide done toggle

feat(notebook): expand page width to 90vw

  • All notebook page containers widened from max-w-2xl/3xl to 90vw

feat(nav): constrain desktop header to 90vw

  • Nav bar inner content aligned to 90vw to match page content

fix(swarm): remove leftover showCompleted reference

…rop mailbox_tokens.is_admin

- Replace all MAILBOX_TOKEN_*/MAILBOX_ADMIN_TOKEN/UI_MAILBOX_KEYS env vars with
  a single SUPERUSER_TOKEN + SUPERUSER_NAME (+ optional SUPERUSER_DISPLAY_NAME)
- authenticateFromDb now JOINs to users table to derive isAdmin — tokens no
  longer carry admin status
- Drop is_admin column from mailbox_tokens (migration: 0004_drop_mailbox_tokens_is_admin.sql)
- Auto-upsert superuser users row on startup (ensureSuperuser)
- Clear auth cache on users.isAdmin change in admin patch endpoint
- Update doctor health check to warn if SUPERUSER_TOKEN is not configured
- Simplify users.get.ts (no more env-only identity merging)
- Update .env.example, docker-compose.yml, docker-compose.dev.yml, README.md, SKILL.md
- server/plugins/startup.ts: Nitro plugin that runs drizzle SQL migrations
  automatically on every server start (idempotent, tracked in _hive_migrations)
- Dockerfile: copy drizzle/ into production image so migrations are available
- server/routes/api/auth/setup-profile.post.ts: endpoint to set own display name
- src/components/setup-profile.tsx: first-run welcome screen asking for display name
- src/routes/index.tsx: detect first-run (admin + ≤1 user) and show setup before inbox
- src/components/login-gate.tsx: rename 'mailbox key' → 'Hive key', improve error msg
- docs/quickstart.md: replace MAILBOX_ADMIN_TOKEN with SUPERUSER_TOKEN model,
  add first-run flow description, add docker-compose.test.yml option
- docs/configuration.md: rewrite auth section for SUPERUSER_TOKEN model,
  remove stale HIVE_TOKEN_*/MAILBOX_TOKEN_* references
DialogContent gets max-h-[85vh] flex flex-col. View mode splits into
a flex-1 overflow-y-auto body (detail text, linked pages, etc.) and a
shrink-0 pinned footer (status buttons + edit). Edit form gets same
overflow-y-auto treatment. Status actions are now always visible
regardless of task detail length.
Replace drizzle sql tagged templates (potential Nitro bundling issue) with
raw postgres.js client calls. Uses sql.unsafe() for executing migration SQL
statements. Creates its own short-lived connection that closes after migrations
run. Simpler and avoids any ORM compatibility issues at startup.
Will re-add with proper Nitro import once root cause is confirmed.
Production already has all migrations applied — no data impact.
The TanStack Start/Nitro version in this project exports definePlugin,
not defineNitroPlugin. The previous crash was calling an undefined global.
Verified: import { definePlugin } from 'nitro' resolves correctly.
Follow same pattern as startScheduler() — call startMigrations() at module
load time in health.get.ts. Removes the Nitro plugin approach (definePlugin
was not invoked correctly). Migration code is bundled into the health route
chunk and runs when the server first handles a request.
- admin.tsx: invite/webhook URLs use window.location.origin
- admin.tsx: Online stat shows real user count from /api/users
- onboard.tsx: quickstart examples use window.location.origin + result.identity
- notebook.tsx: remove hardcoded 'chris' admin check, use isAdmin from verify
- .env.example: SUPERUSER_NAME example changed from 'chris' to 'admin'
- .env.example: added HIVE_HOSTNAME/HIVE_TLS_CERTRESOLVER docs
- docker-compose.yml: Traefik labels use ${HIVE_HOSTNAME}/${HIVE_TLS_CERTRESOLVER}
- skill docs: YOUR_HIVE_URL placeholder now replaced with HIVE_BASE_URL at serve time
- src/lib/skill-helpers.ts: new renderSkillDoc() utility for URL substitution
Compose override file for installations using an internal CA (step-ca, etc.).
Mounts the CA root cert and sets NODE_EXTRA_CA_CERTS so Node.js trusts it.
Default cert path matches Dokploy/step-ca layout; override with CA_CERT_PATH env var.
Closed status:
- New 'closed' status for tasks that won't be completed (won't-fix/won't-do)
- Distinct from 'complete' — preserved for historical record, hidden by default
- XCircle icon, muted gray styling
- Excluded from default task lists (same as complete), included with includeCompleted=true
- completedAt set on close (same as complete)
- Status buttons updated to skip quick actions for closed tasks
- Admin panel: STATUS_LABELS, statuses list, active task count, swarm breakdown all updated
- Swarm skill doc updated with closed status explanation

Chat unread badge on Presence:
- Nav now fetches /api/chat/channels every 30s and sums unread_count
- Badge shown on Presence button (desktop header + mobile tab bar)
- Matches existing inbox unread badge style
- Login screen now shows a hint explaining that the key is SUPERUSER_TOKEN
  (first-time admins) or HIVE_TOKEN (agents) — visible before any failed attempt
- Admin page shows a 'Getting Started' banner when only 1 user exists,
  pointing admins directly to the Invites tab and explaining the agent setup flow
Login screen:
- Add show/hide toggle for the key input field
- Explain what SUPERUSER_TOKEN vs HIVE_TOKEN is, upfront (not just on error)

Inbox:
- Empty message list now shows 'Messages sent directly to you will appear here'
- Empty right panel now hints to use the compose button

Navigation:
- All nav items now have descriptive tooltips (Buzz, Swarm, etc. were opaque to new users)

Presence page:
- 'Never seen' → 'Not yet active' (friendlier, less alarming)
- 'Online via api/sse/unknown' → clean 'Online' or 'Online (streaming)'

Admin page:
- 'Online via unknown' → 'Active now'
- 'Never seen' → 'Not yet active'
- Wake button now has a tooltip explaining what it does
Showing 'None' next to every agent was unexplained noise. Now only
shows the connection type (Webhook, SSE, API Poll) when one is
actually configured.
docs/quickstart.md:
- Add HIVE_HOSTNAME and HIVE_TLS_CERTRESOLVER env vars (Traefik/production)
- Add HIVE_BASE_URL to minimum required vars
- Add /api/doctor as step 3 in First Steps
- Clarify invite flow (Admin → Auth tab)
- Add migration troubleshooting tip
- Remove any biginformatics.net references

/api/doctor:
- Catch SUPERUSER_TOKEN still set to default placeholder (fail)
- Warn if SUPERUSER_NAME is empty
- Warn if SUPERUSER_TOKEN is too short (<24 chars)
- Warn/fail if HIVE_BASE_URL is localhost in production
- New probe: users table + user count (warns on 0 users, fails if table missing)
- New probe: migrations tracking (_hive_migrations row count, warns if empty)

setup-profile.tsx:
- After setting display name, show a 'what to do next' checklist
  (invites, HIVE_BASE_URL, webhooks, doctor) before proceeding
- Then redirect to /admin for immediate invite creation

onboard.tsx:
- Move API docs link above the fold as primary next step
- Restructure next steps to be generic (HIVE_TOKEN, webhook, wake)
- Move OpenClaw-specific steps (gateway config, ~/.openclaw/.env)
  into a collapsible <details> section
…tent_project_tags, attachments

These 4 tables existed in the schema and on production but had no migration file,
meaning fresh installs would be missing them entirely. Discovered during onboarding test.
Fixes failure on production where tables already existed.
Fresh installs will create all 4 tables; existing installs skip cleanly.
The API was silently ignoring ?status=ready from the docs example,
returning all tasks instead. Now supports both:
- ?statuses=ready,in_progress  (canonical, comma-separated)
- ?status=ready                 (singular alias, backward compat)

Also updated docs/features/swarm.md to show the correct ?statuses=
parameter with examples for single + multi-status filtering.
Agents and users can now use ?assignee=me to scope tasks to themselves
without knowing their own identity string. Resolves part of the
task-scoping privacy concern.
Add optional tagged_users field to swarm_projects. When empty/null,
the project (and all its tasks) is visible to all team members.
When set, only the listed identities can see the project or its tasks.

- schema: add taggedUsers to swarmProjects
- migration: 0006_add_swarm_project_visibility.sql
- listProjects: filter by visibility when identity is provided
- listTasks: filter out tasks in projects caller can't see
- GET /api/swarm/projects: pass auth.identity for visibility scoping
- GET /api/swarm/tasks: pass auth.identity for visibility scoping
- PATCH /api/swarm/projects/:id: accept taggedUsers to set visibility

API usage:
  # Restrict a project to specific users
  PATCH /api/swarm/projects/:id
  { "taggedUsers": ["chris", "domingo"] }

  # Make it open again
  { "taggedUsers": [] }
…icted

When a project has taggedUsers set (visibility restriction), the task
create and edit forms now only show users from that allowed list in the
assignee and next-assignee dropdowns.

Open projects still show all known users (no change in behavior).
await import('./base-url') inside getWakeItems() could fail in production
bundling. All other files use static imports — make wake.ts consistent.
Using a Drizzle column reference (swarmTasks.projectId) inside a raw
sql template doesn't generate a column name - it serializes the column
object, producing invalid SQL. Switch to a two-query approach:
1. Fetch visible project IDs from swarm_projects (with taggedUsers filter)
2. Filter swarmTasks where projectId IN (visible IDs) OR projectId IS NULL
The toolbar 'Show done' button is easy to miss. List view now shows
a bottom-of-list link 'Show completed tasks' (and 'Hide completed
tasks' when enabled) so the toggle is discoverable in context —
exactly where you'd notice done items are missing.
Round 1 — readBody type annotations:
- All route handlers: readBody<Record<string,any>>(event) ?? {}
  (readBody<any> doesn't work due to H3's InferEventInput generic)

Round 2 — genuine bugs:
- chat/channels/[id]/messages.post.ts: createdAt.toISOString() — Date
  was emitted as object over SSE instead of ISO string
- broadcast.ts: capture forUser before filter closure to satisfy
  narrowing (params.forUser possibly undefined inside callback)
- setup-profile.tsx: onComplete() was never called after save —
  parent was never notified that setup completed
- tests/base-url.test.ts: vi missing from vitest imports

Round 3 — unused symbols (real cleanup):
- middleware/input-validation.ts: remove _MAX_TITLE/_MAX_BODY/_MAX_CONTENT
  (declared, never used — planned validation that wasn't implemented)
- notebook/ws.ts: remove _SAVE_INTERVAL_MS (same)
- src/routes/notebook.tsx: remove _saveTimer ref (same)
- src/components/inbox.tsx: remove _updated assignments (API return
  values were captured but state was updated manually anyway)
- src/components/markdown-editor.tsx: remove _isExternalUpdate ref

Round 4 — type fixes:
- attachments/[id].get.ts + avatars/[identity].get.ts: Readable.toWeb()
  for proper ReadableStream type (Node ReadStream → Web ReadableStream)
- stream.get.ts: cast req to any before accessing .signal (Bun-specific)
- swarm.tsx: add taggedUsers to local SwarmProject interface
- swarm.tsx: wrap Lucide icons in <span title> (title prop not in LucideProps)
- buzz.tsx: type bodyJson as Record<string,unknown>|unknown[]|null
- buzz.tsx: explicit BroadcastEvent[] annotation on evts
- message-detail.tsx: useRef initial value to satisfy strict types

Remaining (3 errors, pre-existing, not bugs):
- user-select.tsx, directory.tsx, notebook.tsx: asChild={true} type
  conflict from Radix UI version mismatch — runtime works fine
…nth increments

- Done column (complete/closed) always visible in kanban and list modes
- Defaults to showing tasks completed in the past 12 hours
- 'Show more items (N older)' button at bottom of each done section
- Each click expands window by 1 additional month
- Removed 'Show done / Hide done' toggle (no longer needed)
- Applies to both board columns and list view sections
- Add has_activity BOOLEAN to chat_members (default false)
- Partial index on (identity) WHERE has_activity = true for fast wake queries
- Set true for all non-sender members when a message is sent
- Cleared (false) on markChannelRead — triggered by GET messages or POST /read
- Wake endpoint now includes chat source: queries has_activity channels,
  fetches new messages since last_read_at, builds summary with sender/preview
- WakeItem type extended with channelId, channelType, channelName fields
- Action map includes chat entry pointing to /api/skill/chat
- Render chat message bodies as markdown via marked + DOMPurify
- prose-sm styling on bubbles; prose-invert for 'me' primary bubbles
- Auto-scroll to bottom on initial channel open (was broken: fired before messages loaded)
- Smart scroll on new messages: only force-scroll if already within 120px of bottom
- Reset scroll flag when switching channels
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b36be8e12

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@GoDoming
Copy link
Member Author

Addressed Codex findings:

  1. Auth tokens + users join upgrade safety
  • Added startup backfill: insert missing rows for any active identities.
  • Added per-request fallback in DB auth: if join returns no row, look up token identity, insert row, retry once.
  1. Guard GRANT in migration 0007
  • Removed unconditional from to prevent fresh installs failing when role is absent.
  • Added runtime optional grant in : checks for and applies GRANT if present; non-fatal if not.

Pushed in commit 314c3ad.

@GoDoming
Copy link
Member Author

Addressed Codex findings:

  1. Auth tokens + users join upgrade safety
  • Added startup backfill: insert missing users rows for any active mailbox_tokens identities.
  • Added per-request fallback in DB auth: if join returns no row, look up token identity, insert users row, retry once.
  1. Guard GRANT in migration 0007
  • Removed unconditional GRANT ... TO team_user from drizzle/0007_add_swarm_project_visibility.sql to prevent fresh installs failing when role is absent.
  • Added runtime optional grant in src/lib/migrate.ts: checks pg_roles for team_user and applies GRANT if present; non-fatal if not.

Pushed in commit 314c3ad.

@GoDoming GoDoming merged commit 280caf6 into main Feb 24, 2026
4 checks passed
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 314c3ad881

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants