9 Commits

Author SHA1 Message Date
teknium1
7ed6534c7e Merge origin/main: browser fence composed with the dispatch/retry split (#115184); server registration, i18n, contracts 2026-09-23 03:25:06 -07:00
teknium1
d9ca819cc4 Inspired by Amp: pick the workspace a dashboard chat starts in
A fresh dashboard /chat always spawned the TUI in the dashboard process's
launch directory, so from a phone or any browser there was no way to aim
a new session at a specific repository. Amp's runners now serve many
directories and the web composer offers a picker of the runner's projects
and discovered git checkouts; this ports that mechanism onto the surface
Hermes already has: the dashboard is the phone/web front, the host's
projects.db + repo-discovery cache are the served directories.

- GET /api/chat/workspaces: the profile's projects (with folders) and
  discovered repos (session-derived + scanned), default_cwd, home;
  ?scan=1 rescans desktop.repo_scan_roots on the host, so headless
  installs (no Desktop to populate the cache) discover repos too.
- /api/pty?cwd=<dir>: validated (existing directory, fail-closed 400
  via the PTY error path) and forwarded to the TUI child as HERMES_CWD
  (self-spawned gateway cwd) + HERMES_TUI_CWD (explicit cwd on
  session.create for the dashboard's in-memory gateway, whose own cwd
  is the launch dir). Resumed sessions ignore it.
- ui-tui: session.create carries cwd when HERMES_TUI_CWD is set, so
  /new inside a dashboard chat stays in the picked workspace too.
- web: workspace selector in the Chat rail above "New chat" (projects,
  repos by recency, Other path…, rescan), remembered per profile in
  localStorage; 17 locales.
- docs: web-dashboard.md rail + REST sections.

Live E2E: real `hermes dashboard` under a scratch HOME with two git
repos under desktop.repo_scan_roots -> /api/chat/workspaces?scan=1
lists both; /api/pty?cwd=repo-a -> TUI status bar shows ~/code/repo-a;
/api/pty?cwd=<missing> -> "Working directory does not exist" + close.
2026-09-23 03:12:04 -07:00
teknium1
223ad499a9 fix(bot-screen): a display ticket is not a gateway login on /api/ws
/api/ws consumed any ticket and stamped its identity; display.observe mints
provider "bot-desktop" tickets for viewers who may only be watching a screen,
so one of those redeemed on /api/ws became a full authenticated session.
Refuse bot-desktop tickets in _ws_auth_reason (reported as ticket_invalid, same
as the display route refuses gateway tickets).

(cherry picked from commit 753c3c35975fe1efab4a5bbc8bc6bbc9532b8521)
2026-09-12 18:58:22 -07:00
Gille
a99340c247 fix(dashboard): prevent PTY input from blocking event loop (#93565)
* fix(dashboard): prevent PTY input from blocking event loop

* fix(win-pty): don't terminate a healthy ConPTY on write cancellation; log leaked write workers

Review follow-up to the backpressure fix.

CancelledError on WinPtyBridge.write() ran the same path as a timeout and
force-terminated the ConPTY. Cancellation means the owning socket went away
mid-write, which is the keep-alive session's normal reattach case, not a
wedged child; killing the process there defeats the PTY-outlives-socket
design. Give the in-flight write the shutdown grace window and only
terminate if it never lands.

When terminate() fails to unblock pywinpty, the worker stays parked in the
default executor. That was swallowed by a bare except; log it so a slow
thread-pool starvation is diagnosable.

---------

Co-authored-by: Austin Pickett <pickett.austin@gmail.com>
2026-09-04 20:24:24 -04:00
Teknium
5f1feb5344 simplify(compat): web_server — drop 221 re-exports (config/status/shutil/run_in_threadpool, lifecycle, 13 web_server_<concern> blocks, 47 route-handler legacy re-exports); web_deps.late()/LateState() take an owning-module arg; concern modules import each other directly (62 lazy sites) 2026-09-03 14:21:32 -07:00
Teknium
e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium
92e52684d1 refactor(web): tools _bad_request helper, skills hub helpers, sessions flag table, docstring compaction
- tools: _bad_request() for 16 status_code=400 raises; payload built in worker;
  drop _terminal_backend_names/_model_catalog_section one-shot helpers
- sessions: _RENAME_FLAG_SETTERS table (4 flags incl. unread), keyset export
  loop tightened, compression_root/lineage_tip compacted
- web_server_chat: single try/except for both PTY bridge imports,
  contextlib.suppress for swallow-all blocks
- docstrings/comments compacted by hand (every WHY kept); AST-neutral packing
2026-09-02 22:21:05 -07:00
Teknium
4d9007cd17 refactor(web): simplify sessions/tools/skills/chat routers and session-db helpers
- sessions: table-driven prune filters, shared scope kwargs, _project_for_display,
  _compact_json, _is_compression_edge, dedup 404 detail, flag-setter table
- web_server_sessions: descendants CTE via db._conn + dict(zip) rows,
  _is_stale_schema_error, compact docs
- tools: _BACKEND_PROBES dispatch table, _env_value/_category_providers helpers
- skills: _hub_sources/_resolve_hub_skill/_hub_lookup/_flag helpers, _API_SOURCE_IDS
- web_server_chat: _ws_client_is_allowed delegates to _ws_client_reason,
  _server_internal_ws_url unifies gateway/sidecar URL builders, _reject/_stamp_identity
- layout compaction (AST-neutral), docstring compaction keeping every WHY
2026-09-02 21:12:22 -07:00
Teknium
5608fe7cdf refactor(web_server): extract chat/PTY/WS-auth cluster to web_server_chat 2026-09-02 15:36:53 -07:00