Commit Graph

84 Commits

Author SHA1 Message Date
148766312e fix: use template literal for metadata.name -> ReactNode compat 2026-07-07 20:52:33 -04:00
97ec6ccbae fix: cast metadata.name to string for ReactNode compat 2026-07-07 20:49:54 -04:00
9d32d32d38 fix: TS errors in API queries and session list
- page_size -> size to match API schema
- reverse -> filters.reverse to match SessionGet body type
- session.name -> session.metadata?.name (Session type has no name field)
2026-07-07 20:42:45 -04:00
a8f0828727 "fix-sort-and-display-name" 2026-07-07 20:42:45 -04:00
Offending Commit
7e529c8c44 fix(web): stop Sidebar re-render loop from cache-event subscriber
setNow(Date.now()) was called on every query-cache event, including
events dispatched synchronously during ServerWorkspaceRows' render.
On CI, consecutive Date.now() calls cross millisecond boundaries so
each call returns a new value — React always re-renders Sidebar, which
re-renders the layout, which re-renders ServerWorkspaceRows, which
fires more cache events. After ~25 cycles React throws "Maximum update
depth exceeded."

Fix: remove setNow from the cache subscriber. setNow now fires only
from the 30s interval timer, where it correctly refreshes the "X ago"
display text without triggering a render loop.

Also adds a deterministic CI-repro test that mocks Date.now to return
incrementing values and asserts the filter-change path completes without
looping.
2026-06-03 18:11:30 -05:00
Offending Commit
3b88a41afd fix(dashboard): guard setMetricsById against same-value calls to end loop
Even with primitive useEffect deps, TanStack Query or React concurrent
rendering can cause onMetrics to fire with identical values mid-render-cycle.
Add a ref-based equality check in Dashboard.onMetrics: if all five metric
values are unchanged, skip setMetricsById entirely — no state update, no
Dashboard re-render, loop terminates.

Also fixes vi.fn<TFunction>() typing in server-workspace-rows.test.tsx to
satisfy tsc (Vitest 4 single-type-arg signature).
2026-06-03 17:48:22 -05:00
Offending Commit
9cc8637dc7 fix(dashboard): use primitive deps in onMetrics effect to break render loop
Using a metrics object as a useEffect dep causes the loop:
  onMetrics → setMetricsById → Dashboard re-renders → ServerWorkspaceRows
  re-renders → useQueries runs → TanStack Query cache subscriber fires
  (Sidebar's setNow) → query result objects are new references → metrics
  useMemo returns new object → effect dep changed → onMetrics again → ∞

Fix: depend on the five primitive values (workspaceCount, conclusionCount,
queueActive, queuePending, health) directly. React compares primitives by
value, so the effect only fires when actual data changes, not on reference
churn from re-renders.

Also adds server-workspace-rows.test.tsx with three focused unit tests:
correct health:ok report, stability after load (no re-fire), and health
transition coverage.
2026-06-03 17:48:21 -05:00
Offending Commit
e59bfd908a Merge branch 'main' into feat/dashboard-fleet-merge 2026-06-03 17:28:52 -05:00
Offending Commit
3c99af7143 refactor(dashboard): drop unused lastSeen local var after metrics fix 2026-06-03 17:27:55 -05:00
Offending Commit
f79cdafba7 fix(dashboard): remove lastSeen from metrics useMemo deps to break render loop
lastSeen tracked workspacesQ.dataUpdatedAt which updates on every background
refetch, creating a new metrics object reference each cycle. The useEffect
dep on metrics then fired unconditionally, calling onMetrics → setMetricsById
→ Dashboard re-render → another refetch → infinite loop.

computeFleetAggregates never reads lastSeen, so reporting it upward was
pointless. Hardcode null and drop it from the dep array entirely.
2026-06-03 17:27:42 -05:00
Offending Commit
36fb6ee519 fix(test): remove unused within import from fleet.test.tsx 2026-06-03 17:10:32 -05:00
Offending Commit
da126b2e74 feat(web): redirect /fleet to Dashboard; update fleet tests
/fleet now redirects to / so bookmarks and muscle-memory links land on the
unified Dashboard rather than a 404-style dead route. Fleet tests updated
to assert the redirect and the per-instance rows the Dashboard renders.
2026-06-03 17:10:02 -05:00
Offending Commit
699ec38480 fix(web): reset serverFilter when the selected instance is removed
If the user filters to a specific server and that instance is then deleted
(e.g. from Settings), shownInstances becomes [] — empty table, no message.
A useEffect resets the filter to ALL_SERVERS whenever the selected ID
disappears from the instances list.
2026-06-03 17:03:10 -05:00
Offending Commit
173f096e33 fix(web): break metricsEqual lastSeen dep-loop; add staleTime to test
Remove lastSeen from metricsEqual in ServerWorkspaceRows: computeFleetAggregates
never reads lastSeen, so comparing it in metricsEqual causes background TanStack
Query refetches (dataUpdatedAt changes) to trigger spurious onMetrics callbacks,
cascading into a Dashboard → ServerWorkspaceRows render loop that hits React's
100-render limit in CI.

Add staleTime: Infinity to the test QueryClient to prevent background refetches
from interfering with test assertions.
2026-06-03 17:01:19 -05:00
Offending Commit
1c28cae3f2 fix(ci): replace userEvent.selectOptions with fireEvent.change; bump setup-node to v6
userEvent.selectOptions hangs in jsdom when firing pointer + change event
sequences — fireEvent.change fires the React-controlled onChange directly
and is deterministic. Removes userEvent import (no longer used).

Bump actions/setup-node from v4 to v6 to clear the Node.js 20 deprecation
warning on GitHub Actions runners.
2026-06-03 16:41:40 -05:00
Offending Commit
e66f927f89 feat(web): merge Fleet into a server-filterable Dashboard
Implements Phase 1 of the UI navigation rework (docs/superpowers/specs/
2026-06-02-ui-navigation-rework.md): the Dashboard now lists every workspace
across every configured server as <workspace> (<server>), filterable by server,
with cross-server aggregate cards (reusing computeFleetAggregates). Opening a
workspace activates its server then drills into the existing detail route.
Per-server fan-out lives in a ServerWorkspaceRows child (rules-of-hooks safe,
mirrors FleetRow). Fleet removed from the sidebar nav; the /fleet route is left
intact for now (full removal deferred to avoid churning fleet.test.tsx while #54
is open).
2026-06-02 16:41:51 -05:00
Offending Commit
5a2543592a Merge pull request #54 from offendingcommit/feat/web-api-proxy
feat(web): eliminate browser CORS via header-driven /api proxy
2026-06-02 15:19:32 -05:00
Offending Commit
239eb3327a test(web): use a documentation IP instead of a tailnet-range fixture
Swap the 100.x CGNAT example for 192.0.2.10 (RFC 5737 TEST-NET-1) in the
token-transport guard tests — keeps the non-loopback-HTTP assertion, drops an
environment-specific address.
2026-06-02 15:11:17 -05:00
Offending Commit
3ea6a73833 test(web): cover instance store CRUD and legacy migration
Closes the multi-instance coverage gap: add (first becomes active, later adds
don't steal focus, insertion order), switch (incl. unknown-id no-op), delete
(active->first-remaining fallback, non-active unchanged, last clears active),
update (patch + unknown-id no-op), active config, and legacy-key migration.
2026-06-02 14:36:23 -05:00
Offending Commit
96dff8900e refactor(web): use top-level z.url() over deprecated z.string().url()
Zod v4 deprecates the chained .url() in favor of the top-level format validator.
2026-06-02 14:31:40 -05:00
Offending Commit
409d7d8be7 fix(web): raise connection-test timeout for cold upstreams
A cold/idle self-hosted Honcho can take ~5s on its first request (DB pool, tunnel
wake); the hardcoded 5s budget aborted just before the response and reported a
live instance as 'Connection timed out'. Extract CONNECTION_TIMEOUT_MS (15s),
make checkConnection's timeout injectable, and cover the budget behavior.
2026-06-02 14:18:19 -05:00
Offending Commit
6b602c05bb fix(web): strip content-encoding from vite dev proxy responses
undici fetch auto-decompresses the body, so re-sending the upstream
content-encoding/length would cause ERR_CONTENT_DECODING_FAILED if Honcho
gzips. Drop those and hop-by-hop headers when relaying. nginx is unaffected.
2026-06-02 13:28:32 -05:00
Offending Commit
b4fac95f37 fix(web): enforce upstream allowlist in vite dev proxy
Mirrors the nginx allowlist (spec section D) so make dev-web matches prod: when
OPENCONCHO_UPSTREAM_ALLOWLIST is set, non-matching upstreams get 403 +
X-Honcho-Proxy-Reject; unset stays open.
2026-06-02 13:25:12 -05:00
Offending Commit
ab8a1ba866 feat(web): dev /api proxy middleware mirroring nginx 2026-06-02 13:13:48 -05:00
Offending Commit
b29fa240a6 refactor(web): drop same-origin sentinel from runtime config 2026-06-02 11:53:42 -05:00
Offending Commit
9893230cde feat(web): route checkConnection and discovery through the proxy 2026-06-02 11:52:17 -05:00
Offending Commit
0935099bc2 feat(web): route web build through same-origin /api proxy
client.current and createScopedClient resolve transport via dispatchFor:
web -> absolute origin + /api base with an X-Honcho-Upstream header; Tauri ->
absolute instance URL + reqwest. Absolute base (not bare "/api") so openapi-fetch
can construct a Request under node/undici and in the browser alike. Fleet fan-out
is unchanged; fleet.test.tsx now asserts the proxy contract.
2026-06-02 11:48:13 -05:00
Offending Commit
9945e4cf14 feat(web): add dispatchFor transport helper for same-origin proxy 2026-06-02 11:38:58 -05:00
Offending Commit
d4452abcea refactor(web): extract isTauri into a leaf platform module 2026-06-02 11:36:21 -05:00
Offending Commit
04104d1f92 Merge pull request #43 from offendingcommit/feat/docker-compose-support
feat(docker): full self-hosted Compose support
2026-05-28 17:09:23 -05:00
Offending Commit
ab69b0045f Merge pull request #42 from offendingcommit/feat/fleet-dashboard
feat(web): add Fleet dashboard for cross-instance observability
2026-05-28 17:08:35 -05:00
Offending Commit
282ba1b76c feat(docker): full self-hosted Compose support
Make the web image drop-in for a Honcho docker-compose stack:
- nginx reverse-proxies /v3 and /health to $HONCHO_UPSTREAM (variable +
  Docker resolver so it starts even before the upstream resolves), giving
  the SPA a same-origin path to Honcho with no browser CORS.
- Runtime config: an entrypoint writes config.js from
  OPENCONCHO_DEFAULT_HONCHO_URL, so one prebuilt image targets any backend
  ("same-origin" | absolute URL | empty). SPA seeds a first-run default
  instance from it (additive; no-op in dev/desktop).
- docker-compose.yml example service + GHCR multi-arch publish workflow on
  release.
- nginx.conf -> envsubst template; docs rewritten.

Closes #21. Closes #31.
2026-05-28 16:06:03 -05:00
Agents
37eb9bdf59 test(web): add E2E for Fleet route nav + per-instance row render
Two focused Playwright tests, mirroring the existing sidebar.spec
pattern (no backend dependency — instances point at unreachable ports
and we only assert on rendered names + row count, not health):

- Fleet link in the sidebar navigates to /fleet
- /fleet renders one row per configured instance under the table role

Also adds a Fleet link assertion to the existing sidebar.spec so the
nav entry is covered on the dashboard route alongside the other top
links. Both new tests run under the existing `pnpm test:e2e` (not
gated in CI by design — matches the current Compare/Dashboard E2E
posture).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 15:57:17 -05:00
Agents
12712bb0b0 feat(web): add Fleet dashboard view for cross-instance observability
Adds a new /fleet route that shows a fleet-wide overview of all
configured Honcho instances. Each row renders per-instance metrics
(workspace count, total conclusions, queue activity, last seen, health)
by fanning out scoped fetches via createScopedClient, and aggregates
into top-level metric cards.

Reuses the Phase 2 scoped-client pattern. Extends compareQueries.ts
with useScopedQueueStatus and useScopedConclusionsCount, plus option
builders so useQueries can fan out per-workspace requests inside
FleetRow without duplicating query logic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 15:57:17 -05:00
Offending Commit
3de6832a5d feat(web): configurable peer display name
Human peers surface as raw ids (WhatsApp `…-lid`, UUIDs). Read an
optional `display_name` from peer metadata and prefer it over the id in
the peer header and breadcrumb, with the raw id kept as a sub-caption.
An inline edit on the header writes `display_name` via useUpdatePeer
(merging existing metadata); blank clears it. Falls back to the id when
unset.

Closes #32.
2026-05-28 15:56:42 -05:00
Offending Commit
0d86a96560 Merge pull request #28 from offendingcommit/feat/dialectic-playground
feat(web): add dialectic reasoning playground
2026-05-28 14:26:39 -05:00
Offending Commit
f52961a112 Merge pull request #27 from offendingcommit/feat/dream-viewer
feat(web): add dream output viewer
2026-05-28 14:25:45 -05:00
Offending Commit
a17b5776be Merge pull request #20 from offendingcommit/feat/peer-card-seed-kits
feat(web): add peer card seed kits
2026-05-28 14:24:48 -05:00
Offending Commit
7d2f7209ea Merge pull request #29 from offendingcommit/feat/live-dream-progress
feat(web): add live dream progress panel with adaptive polling
2026-05-28 14:23:55 -05:00
Offending Commit
aa46d47fc8 Merge pull request #30 from offendingcommit/feat/desktop-auto-discover
feat(desktop): auto-discover Honcho instances on localhost
2026-05-28 14:23:49 -05:00
Offending Commit
972fd8323a Merge pull request #18 from offendingcommit/feat/sidebar-last-updated
feat(web): show sidebar last-updated status
2026-05-28 14:22:54 -05:00
Offending Commit
94c423522a test(web): add v8 coverage with truthful baseline floor
Adds @vitest/coverage-v8 (catalog-pinned), a test:coverage script wired
through turbo (inputs + coverage/** output), and coverage thresholds set
to a floor measured on main (lines 18 / funcs 10 / branches 10 / stmts
17 against current 19.3/11.4/11.7/18.9). Ratchet upward as tests grow.

Adapts the coverage config from @BenSheridanEdwards's fork; the multi-job
CI fan-out from that branch was intentionally left out.

Co-authored-by: Ben Sheridan-Edwards <BenSheridanEdwards@users.noreply.github.com>
2026-05-28 14:21:42 -05:00
Offending Commit
ac20ab6271 Merge pull request #22 from offendingcommit/fix/web-security-hardening
fix: harden token and URL transport security
2026-05-28 14:20:51 -05:00
Ben Sheridan-Edwards
7355884051 feat(desktop): auto-discover Honcho instances on localhost (#1)
* feat(desktop): auto-discover Honcho instances on localhost

First-launch and on-demand discovery of running Honcho instances on
127.0.0.1:8000-8100. Desktop-only — the browser can't port-scan due to
CORS, so the scan runs in the Tauri Rust shell.

- New Rust command `discover_honcho_instances(start_port?, end_port?)`
  in src-tauri/src/discover.rs: parallel TCP probe of each port with
  150ms connect timeout + 250ms total request budget, looking for a
  /health endpoint returning `{"status":"ok"}`.
- New TS helper `discoverHonchoInstances()` in src/lib/discovery.ts:
  detects Tauri runtime; web build degrades to an empty result.
- `suggestNameForInstance()` fetches the first workspace and derives a
  friendly name from its id ("neo-personal" -> "Neo"). Used to seed the
  Name field for each discovered instance.
- `DiscoveredInstances` component: list of found instances with
  editable suggested names + per-row checkbox + "Add N instances"
  button. Rows are pre-checked by default and filter out already-
  configured baseUrls.
- Wired into `InstancesManager`:
  - First launch (no instances): renders above the connection-type
    chooser with autoRun=true. User sees results immediately.
  - With instances: adds a "Discover instances" button to the list
    view that opens the discovery flow on demand.
  - Both paths gate on `isTauri()`; the web build keeps its existing
    behaviour unchanged.

Adds tokio (with net + io-util + time + rt + macros) and futures to
the Tauri shell to get async TCP + join_all.

Tests:
- deriveNameFromWorkspaceId handles hyphenated, multi-segment, and
  no-hyphen ids

* test(desktop): add probe tests including live Hermes stack integration

- rejects_inverted_port_range
- ignores_ports_with_no_listener
- finds_live_hermes_stacks (#[ignore]d; opt-in with --ignored)

The ignored test exercises discover_honcho_instances against ports
8000-8010, expecting exactly [8001, 8002, 8003, 8004, 8005]. Verified
locally — the probe finds all 5 stacks in <10ms.

---------

Co-authored-by: Agents <agents@Jarviss-Mac-mini.local>
2026-05-28 14:09:45 -05:00
Agents
247ab846e9 docs(web): link panel to plastic-labs/honcho#724
Filed the upstream feature request asking /queue/status to surface
per-work-unit observer/observed, specialist phase, and token telemetry.
Panel now links to the concrete tracking issue instead of a pre-filled
new-issue form. Re-captured the showcase screenshots so the visual
state matches.
2026-05-28 14:08:57 -05:00
Agents
5f61769adc docs(web): add Dream Progress screenshots + capture script
Wires up the DEV-only /dream-progress showcase to a Playwright script
that captures idle/active/stalled variants for documentation. Also
regenerates routeTree.gen.ts to include the new queue + showcase routes
and folds in Biome formatter fixes.

Run `node scripts/screenshot-dream-progress.mjs` from packages/web with
the dev server up to refresh the images.
2026-05-28 14:08:57 -05:00
Agents
93c117b16b test(web): cover queue polling and stale-dream detection
- pickQueueRefetchInterval picks 2.5s when work is active, 10s otherwise
- useStaleQueueDetection only flags after 30 min without forward progress
- stall anchor resets when completed advances or all work finishes
2026-05-28 14:08:56 -05:00
Agents
17f8a5a7bf feat(web): add live dream progress panel with adaptive polling
Watching dreams was a 5-15min black box. Now a dedicated
/workspaces/:id/queue route polls /v3/workspaces/:id/queue/status
every 2.5s while work is in-flight (10s when idle) and surfaces a
warning when in-progress work hasn't advanced the completed count
for >30 minutes.

The Honcho API only exposes aggregate counts (no observer/observed
pair, specialist phase, or token telemetry per work-unit), so the
panel links out to an upstream issue for those.

- queries: adaptive refetchInterval via TanStack Query callback form
- hooks/useStaleQueueDetection: client-side stall detection
- routes/workspaces_.\$workspaceId_.queue: dedicated live view
- routes/_dev.dream-progress: DEV-only showcase for screenshots
- WorkspaceDetail: new "Queue & dreams" nav card
2026-05-28 14:08:56 -05:00
Ben Sheridan Edwards
2340e65028 feat(web): add dialectic reasoning playground
Lets users fire one query at every Honcho reasoning level (minimal / low /
medium / high / max) in parallel and compare answers + latency side-by-side
in a 5-column grid.

- New route: /workspaces/:ws/peers/:peer/playground
- New component: DialecticPlayground with per-column state, level checkboxes
  to skip expensive levels, and a single Run button that fans out via
  Promise.all
- Extends useChat() to accept an optional reasoning_level parameter (was
  hardcoded to "low")
- Adds a Playground button next to Chat on the peer detail page
- Tests cover the parallel fan-out (peak concurrency = 5, no serial
  awaiting) and per-column latency measurement
- Screenshot Playwright spec (idle / mid-flight / settled) drives the
  images in docs/screenshots/ — regen with PLAYWRIGHT_BASE_URL=... pnpm
  exec playwright test e2e/playground.screenshots.spec.ts
2026-05-28 14:08:08 -05:00
Agents
e549200f5d test(web): add playwright e2e for Dreams + docs screenshots
- packages/web/e2e/dreams.spec.ts: 4 tests covering sidebar entry,
  heading/breadcrumb resolution, mocked-API clustering into rows with
  count chips, and the premise-tree expansion path. Uses a function
  matcher for context.route so the trailing ?page=&page_size= query
  string doesn't break a glob.
- docs/screenshots/: dark + light mode list and expanded-detail PNGs
  captured at 2880x1800 (retina) via playwright against the dev
  server with a tiny mock Honcho.
- DreamList.tsx: add a literal space to CountChip's label fragment so
  the rendered text reads "3 explicit" instead of "3explicit" (gap-1
  styles the visual layout but doesn't add a text-node space). Better
  for screen readers, copy/paste, and assertion via toContainText.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 14:07:22 -05:00