Skip to content

Fix/header icon clicks#3366

Closed
whoisjeremylam wants to merge 48 commits into
wavetermdev:mainfrom
whoisjeremylam:fix/header-icon-clicks
Closed

Fix/header icon clicks#3366
whoisjeremylam wants to merge 48 commits into
wavetermdev:mainfrom
whoisjeremylam:fix/header-icon-clicks

Conversation

@whoisjeremylam

Copy link
Copy Markdown

No description provided.

clearlyjeremy and others added 30 commits May 12, 2026 07:33
- Add local Go 1.26.2 and Zig 0.14.0 installs (gitignored)
- Add @go-task/cli as local npm dependency
- Update Taskfile.yml to use {{.GO}} and {{.ZIG}} vars instead of PATH
- Add go.mod stub in golang dir to prevent module scan errors
- Add manual macOS build workflow (.github/workflows/build-macos.yml)
- Update AGENTS.md with dev environment notes
- Add fork notes to README.md
- Add .pi/ to .gitignore
- Delete deploy-docsite.yml, testdriver-build.yml, testdriver.yml
- Change codeql.yml to manual trigger only
Completely removes all telemetry collection, local storage, and cloud upload
functionality per the remove-telemetry spec.

Key changes:
- Delete pkg/telemetry/, pkg/telemetry/telemetrydata/, pkg/wcloud/
- Remove telemetry loops and event recording from cmd/server/main-server.go
- Remove 5 telemetry RPC commands and ActivityUpdate type from wshrpc layer
- Remove telemetry call sites from wcore, conncontroller, wslconn, jobcontroller,
  aiusechat, panichandler, clientservice
- Remove TelemetryClear/TelemetryEnabled from SettingsConfig and regenerate
  metaconsts.go, gotypes.d.ts, services.ts, wshclientapi.ts, wshclient.go
- Remove wsh CLI activity tracking (activityWrap, sendActivity)
- Remove frontend recordTEvent, ActivityCommand, RecordTEventCommand call sites
- Remove Electron activity tracking (logActiveState, sendDisplaysTDataEvent,
  increment-term-commands IPC)
- Simplify onboarding: remove telemetry toggle, NoTelemetryStarPage, and
  telemetry consent flow
- Delete telemetry docs (telemetry.mdx, telemetry-old.mdx)
- Add migration to drop db_tevent and db_activity tables

Compilation fixes:
- Restore services import in onboarding.tsx for AgreeTos
- Fix emain/preload.ts and emain-ipc.ts corruption from incrementTermCommands
  removal (restore nativePaste, openBuilder, saveTextFile, setIsActive)
- Fix malformed custom.d.ts type declaration
- Replace remaining telemetry:enabled reads in AI panel files with true
…sections

- Promote Fork Notes to ## Fork Notes immediately after intro paragraph
- Remove FOSSA badge, upstream Roadmap, Links, Sponsoring sections
- Remove Free Beta AI credits reference (upstream cloud service)
- Remove upstream download/docs links from Installation section
- Remove entire ## Wave AI section
- Remove AI references from intro and Key Features (Wave AI, AI assistants, AI chat widget)
- Update Fork Notes: 'reduced AI features' -> 'remove unnecessary AI features'
- Focus README on terminal, SSH, and file management capabilities
Root cause: CloseTab launched an explicit goroutine calling
DestroyBlockController while DeleteTab -> DeleteBlock ->
BlockCloseEvent triggered the same destruction again,
causing concurrent double-Stop on ShellController and
DurableShellController.

Fixes:
- Remove redundant DestroyBlockController goroutine from CloseTab
- Add sync.Once to ShellProc.Close() as defense-in-depth
- Add trace logging for interactive diagnosis
- Add 14 unit tests covering the race conditions

Resolves: .pi/specs/bug-tabclose-crash.md
Logging was added to verify the fix; crash confirmed resolved
interactively. Stripping to avoid polluting production logs.
Logging was added to verify the fix; crash confirmed resolved
interactively. Stripping to avoid polluting production logs.
… frontend

- Remove AIPanel from builder-workspace.tsx, replace with placeholder div
- Remove WaveAIModel imports from builder-buildpanel.tsx and builder-previewtab.tsx
- Remove 'Add to Context' and 'Send Output to AI' from builder build panel
- Simplify builder-previewtab ErrorStateView (remove AI streaming check/buttons)
- Move formatFileSize from @/app/aipanel/ai-utils to @/util/util
- Update builder-filestab.tsx formatFileSize import to @/util/util
- Simplify BuilderFocusType to 'app' only, remove setWaveAIFocused()
- Remove 'AI Presets' deprecated config entry and validateAiJson from waveconfig-model.ts
- Remove inWaveAI parameter and WaveAI navigation branch from layoutModel.ts
- Update keymodel.ts switchNodeFocusInDirection call site
- Remove setWaveAIOpen from preview-electron-api.ts mock
- Remove dead rateLimitInfoAtom declaration from global-atoms.ts
The inner PanelGroup (holding VTabBar + AIPanel side-by-side) was removed
in Phase A, but the VTabBar's <Panel> wrapper was accidentally left intact,
creating an invalid react-resizable-panels structure: a <Panel> inside
another <Panel> with no <PanelGroup> parent. This caused the library to
fail layout calculations and collapse all panels to zero size, producing
a completely blank app window.

- Replace nested <Panel> with plain <div> in workspace.tsx
- Remove vtabPanelRef and ImperativePanelHandle import
- Remove syncPanelCollapse() and related refs in workspace-layout-model.ts
Remove getShellIntegrationIconButton() implementation (sparkle/Claude logo
and Wave AI tooltips) and replace with no-op stub returning null.

- Remove TermClaudeIcon import from term-model.ts
- Underlying shell integration protocol (OSC 16162) left intact; could be
  reused later for pi coding agent support (documented in .pi/decisions.md)

Docs: Add .pi/decisions.md with Claude Code integration analysis
Move all fork planning docs into the active working directory:
- Copy specs (remove-waveai, remove-telemetry, portforwarding, bug-tabclose-crash)
- Copy context.md, index.md, todos.md
- Merge decisions.md: prepend original ADRs (fork purpose, .pi/ hub, port forwarding
  config-first approach, tab-close crash fix, secret store keep) with today's
  Claude Code integration analysis for future pi agent support
- Delete docs/docs/waveai.mdx, waveai-modes.mdx, ai-presets.mdx
- Delete schema/waveai.json, schema/aipresets.json
- Remove AI config rows from docs/docs/config.mdx (ai:*, waveai:*, app:hideaibutton)
- Clean AI references from gettingstarted.mdx, index.mdx, wsh-reference.mdx
- Truncate releasenotes.mdx to v0.14.x, strip all AI mentions
- Fix misleading AI text in builder-previewtab.tsx EmptyStateView
- Remove AI schema generation from generateschema (Phase B.12 fix)
- Add sharp dependency for vite image optimizer
Removes AI RPC handlers, client helpers, web endpoints, and dead packages
that were missed in the original Phase B implementation.

Specific changes:
- wshrpctypes.go: remove 7 AI interface methods + 6 AI data types
- wshserver.go: remove 5 AI RPC handlers + aiusechat/chatstore/uctypes imports
- wshclient.go: remove 8 AI client helper functions + uctypes import
- web.go: remove /api/post-chat-message and /wave/aichat endpoints
- main-server.go: remove aiusechat.InitAIModeConfigWatcher() call
- wshcmd-blocks.go: remove waveai from view filter help/validation
- filebackup.go: rename waveai-backups -> file-backups
- wpstypes.go: remove Event_WaveAIRateLimit and Event_AIModeConfig
- tsgenevent.go: remove WaveAI event data mappings
- tsgen.go: remove uctypes/AIModeConfigUpdate from ExtraTypes
- Delete pkg/aiusechat/ (entire directory, ~8.5K lines)
- Delete cmd/testai/, cmd/testopenai/, cmd/testsummarize/
- Delete cmd/wsh/cmd/wshcmd-ai.go (entire wsh ai command)

Also updates .pi/todos.md to reflect Phase D as next active task.
Comprehensive removal of orphaned Wave AI code after Phase A/B/C cleanup:

Frontend:
- Delete frontend/app/aipanel/ (entire directory, 17 files)
- Delete frontend/app/view/waveai/ (deprecated view)
- Delete frontend/app/view/aifilediff/ (deprecated view)
- Delete frontend/app/view/waveconfig/waveaivisual.tsx
- Delete frontend/app/onboarding/fakechat.tsx
- Delete frontend/preview/previews/waveai.preview.tsx
- Delete frontend/preview/previews/aifilediff.preview.tsx + util + test

Go Backend:
- Remove AiSettingsType, GetAiSettings(), MergeAiSettings() from settingsconfig.go
- Remove all AI fields from SettingsType (AiClear, AiPreset, AiApiType, etc.)
- Remove AIModeConfigType, AIModeConfigUpdate structs
- Remove WaveAIModes from FullConfigType
- Remove CountCustomAIPresets(), CountCustomAIModes() methods
- Remove AI meta fields from waveobj.MetaTSType (wtypemeta.go)
- Remove AI fields from waveobj.ObjRTInfo (objrtinfo.go)
- Delete pkg/wconfig/defaultconfig/waveai.json
- Delete pkg/wconfig/defaultconfig/presets/ai.json
- Clean AI defaults from pkg/wconfig/defaultconfig/settings.json
- Fix embed directive in defaultconfig.go (removed all:*/*.json subdir pattern)

TypeScript:
- Remove setWaveAIOpen and AIModeConfigWithMode from custom.d.ts
- Regenerate gotypes.d.ts, waveevent.d.ts, wshclientapi.ts (auto-cleaned)
- Regenerate metaconsts.go (auto-cleaned)
- Regenerate schema/settings.json (auto-cleaned)

Verification:
- go build ./... passes cleanly
- task generate produces no AI-related output
- Zero AI references remain in pkg/, cmd/, frontend/app/, frontend/preview/
Additional cleanup found during verification sweep:

- Remove dangling waveAIButtonRef and hideAiButton from tabbar.tsx
  (ref was never attached to any DOM element, always null)
- Remove app:hideaibutton from tabbarenv.ts, vtabbarenv.ts type unions
- Remove hideAiButton state/checkbox from tabbar.preview.tsx, vtabbar.preview.tsx
- Remove AppHideAiButton field from SettingsType
- Remove app:hideaibutton default from settings.json
- Remove WaveAI keybindings from docs/docs/keybindings.mdx
- Regenerate gotypes.d.ts, metaconsts.go, schema/settings.json
* Remove trace logging added for tab-close crash diagnosis

Logging was added to verify the fix; crash confirmed resolved
interactively. Stripping to avoid polluting production logs.

* Phase A review fixes: remove remaining AI references from builder and frontend

- Remove AIPanel from builder-workspace.tsx, replace with placeholder div
- Remove WaveAIModel imports from builder-buildpanel.tsx and builder-previewtab.tsx
- Remove 'Add to Context' and 'Send Output to AI' from builder build panel
- Simplify builder-previewtab ErrorStateView (remove AI streaming check/buttons)
- Move formatFileSize from @/app/aipanel/ai-utils to @/util/util
- Update builder-filestab.tsx formatFileSize import to @/util/util
- Simplify BuilderFocusType to 'app' only, remove setWaveAIFocused()
- Remove 'AI Presets' deprecated config entry and validateAiJson from waveconfig-model.ts
- Remove inWaveAI parameter and WaveAI navigation branch from layoutModel.ts
- Update keymodel.ts switchNodeFocusInDirection call site
- Remove setWaveAIOpen from preview-electron-api.ts mock
- Remove dead rateLimitInfoAtom declaration from global-atoms.ts

* fix(workspace): remove invalid nested Panel causing blank screen

The inner PanelGroup (holding VTabBar + AIPanel side-by-side) was removed
in Phase A, but the VTabBar's <Panel> wrapper was accidentally left intact,
creating an invalid react-resizable-panels structure: a <Panel> inside
another <Panel> with no <PanelGroup> parent. This caused the library to
fail layout calculations and collapse all panels to zero size, producing
a completely blank app window.

- Replace nested <Panel> with plain <div> in workspace.tsx
- Remove vtabPanelRef and ImperativePanelHandle import
- Remove syncPanelCollapse() and related refs in workspace-layout-model.ts

* fix(term): remove AI sparkle icon from terminal block header

Remove getShellIntegrationIconButton() implementation (sparkle/Claude logo
and Wave AI tooltips) and replace with no-op stub returning null.

- Remove TermClaudeIcon import from term-model.ts
- Underlying shell integration protocol (OSC 16162) left intact; could be
  reused later for pi coding agent support (documented in .pi/decisions.md)

Docs: Add .pi/decisions.md with Claude Code integration analysis

* docs: merge .pi/ project memory from waveterm to waveterm-remote

Move all fork planning docs into the active working directory:
- Copy specs (remove-waveai, remove-telemetry, portforwarding, bug-tabclose-crash)
- Copy context.md, index.md, todos.md
- Merge decisions.md: prepend original ADRs (fork purpose, .pi/ hub, port forwarding
  config-first approach, tab-close crash fix, secret store keep) with today's
  Claude Code integration analysis for future pi agent support

* Phase C: remove AI docs, schemas, and clean generator

- Delete docs/docs/waveai.mdx, waveai-modes.mdx, ai-presets.mdx
- Delete schema/waveai.json, schema/aipresets.json
- Remove AI config rows from docs/docs/config.mdx (ai:*, waveai:*, app:hideaibutton)
- Clean AI references from gettingstarted.mdx, index.mdx, wsh-reference.mdx
- Truncate releasenotes.mdx to v0.14.x, strip all AI mentions
- Fix misleading AI text in builder-previewtab.tsx EmptyStateView
- Remove AI schema generation from generateschema (Phase B.12 fix)
- Add sharp dependency for vite image optimizer

* Mark builder-previewtab.tsx fix complete in todos

* fix(gap): complete Phase B — remove all remaining Go backend AI wiring

Removes AI RPC handlers, client helpers, web endpoints, and dead packages
that were missed in the original Phase B implementation.

Specific changes:
- wshrpctypes.go: remove 7 AI interface methods + 6 AI data types
- wshserver.go: remove 5 AI RPC handlers + aiusechat/chatstore/uctypes imports
- wshclient.go: remove 8 AI client helper functions + uctypes import
- web.go: remove /api/post-chat-message and /wave/aichat endpoints
- main-server.go: remove aiusechat.InitAIModeConfigWatcher() call
- wshcmd-blocks.go: remove waveai from view filter help/validation
- filebackup.go: rename waveai-backups -> file-backups
- wpstypes.go: remove Event_WaveAIRateLimit and Event_AIModeConfig
- tsgenevent.go: remove WaveAI event data mappings
- tsgen.go: remove uctypes/AIModeConfigUpdate from ExtraTypes
- Delete pkg/aiusechat/ (entire directory, ~8.5K lines)
- Delete cmd/testai/, cmd/testopenai/, cmd/testsummarize/
- Delete cmd/wsh/cmd/wshcmd-ai.go (entire wsh ai command)

Also updates .pi/todos.md to reflect Phase D as next active task.

* Phase D: delete all dead AI code and regenerate types

Comprehensive removal of orphaned Wave AI code after Phase A/B/C cleanup:

Frontend:
- Delete frontend/app/aipanel/ (entire directory, 17 files)
- Delete frontend/app/view/waveai/ (deprecated view)
- Delete frontend/app/view/aifilediff/ (deprecated view)
- Delete frontend/app/view/waveconfig/waveaivisual.tsx
- Delete frontend/app/onboarding/fakechat.tsx
- Delete frontend/preview/previews/waveai.preview.tsx
- Delete frontend/preview/previews/aifilediff.preview.tsx + util + test

Go Backend:
- Remove AiSettingsType, GetAiSettings(), MergeAiSettings() from settingsconfig.go
- Remove all AI fields from SettingsType (AiClear, AiPreset, AiApiType, etc.)
- Remove AIModeConfigType, AIModeConfigUpdate structs
- Remove WaveAIModes from FullConfigType
- Remove CountCustomAIPresets(), CountCustomAIModes() methods
- Remove AI meta fields from waveobj.MetaTSType (wtypemeta.go)
- Remove AI fields from waveobj.ObjRTInfo (objrtinfo.go)
- Delete pkg/wconfig/defaultconfig/waveai.json
- Delete pkg/wconfig/defaultconfig/presets/ai.json
- Clean AI defaults from pkg/wconfig/defaultconfig/settings.json
- Fix embed directive in defaultconfig.go (removed all:*/*.json subdir pattern)

TypeScript:
- Remove setWaveAIOpen and AIModeConfigWithMode from custom.d.ts
- Regenerate gotypes.d.ts, waveevent.d.ts, wshclientapi.ts (auto-cleaned)
- Regenerate metaconsts.go (auto-cleaned)
- Regenerate schema/settings.json (auto-cleaned)

Verification:
- go build ./... passes cleanly
- task generate produces no AI-related output
- Zero AI references remain in pkg/, cmd/, frontend/app/, frontend/preview/

* Phase D follow-up: remove missed WaveAI references

Additional cleanup found during verification sweep:

- Remove dangling waveAIButtonRef and hideAiButton from tabbar.tsx
  (ref was never attached to any DOM element, always null)
- Remove app:hideaibutton from tabbarenv.ts, vtabbarenv.ts type unions
- Remove hideAiButton state/checkbox from tabbar.preview.tsx, vtabbar.preview.tsx
- Remove AppHideAiButton field from SettingsType
- Remove app:hideaibutton default from settings.json
- Remove WaveAI keybindings from docs/docs/keybindings.mdx
- Regenerate gotypes.d.ts, metaconsts.go, schema/settings.json

---------

Co-authored-by: Jeremy Lam <jeremy.lam@clearpool.io>
Moves all fork planning, specs, and agent context from waveterm parent
to this fork repository where active development will happen.

Includes:
- context.md, index.md, decisions.md, todos.md
- specs/ (remove-telemetry, remove-waveai, portforwarding, bug-tabclose-crash)
- reviews/ (telemetry removal reviews)

Updates .gitignore:
- Removes .pi/ (now tracked)
- Adds .pi/delegate-results/ (large session logs stay local)

Also adds new high-priority bug to todos:
- Tmux mouse integration lost on durable session reconnect
* fix: restore DEC private modes on durable session reconnect

Tracks active DEC private modes (CSI ? h / ? l) in the frontend xterm.js
instance. Persists them to the block cache file meta as 'decmodes'. On
durable reconnect (when loading from cache), replays the active mode-enable
sequences to the new xterm.js Terminal so its internal state matches the
remote application (tmux, vim, etc).

Fixes the bug where tmux mouse integration was lost after WaveTerm restart
because the new terminal instance had no memory of previously-enabled modes.

Changes:
- pkg/service/blockservice/blockservice.go: extend SaveTerminalState with decModes param
- frontend/app/view/term/termwrap.ts: track, serialize, replay DEC modes
- frontend/app/store/services.ts: regenerated TS bindings

Resolves #2

* fix(dec-modes): multi-param CSI tracking, clear-all reset, stale cache purge, replay whitelist

- Fix CSI ? h and ? l handlers to track all parameters, not just params[0]
- Fix CSI ? l with no params to clear activeDecModes (defensive)
- Fix Go backend to always write decmodes meta, overwriting stale values
- Add SafeReplayDecModes whitelist (1000-1006, 2004) to prevent replaying
  alternate screen (47/1049), cursor visibility (25), sync output (2026)

Fixes gaps from af669bc reported in #2

* test(dec-modes): unit tests for DEC mode tracking with mocked xterm.js

- 14 Vitest tests covering serializeDecModes, replayDecModes, and CSI handlers
- Mocked Terminal, addons, and DOM to avoid heavy integration setup
- Tests multi-param tracking, empty-param clear, 2026 sync transaction,
  whitelist filtering, and full round-trip behavior

Covers gaps from af669bc reported in #2

* docs(readme): list fixed bugs in fork notes

- tmux mouse integration lost on durable reconnect (01f5073, #2)
- crash on tab close after SSH session exit (0cd6489)
- af669bc: original DEC mode restore
- 01f5073: multi-param CSI, clear-all reset, stale cache purge, whitelist
- f839f8a: 14 Vitest unit tests
- GitHub comment posted to #2, README updated
…nect bugs issue

- Moved MOSH to backlog after research: no port forwarding, no OSC52,
  C++ only, slow development. tsshd noted as more relevant alternative.
- Added "Remote file paste" as feature task (image paste + drag-drop
  for SSH sessions, primary use case: pi / Claude Code TUI)
- Drafted GitHub issue #4 content for auto-reconnect bugs
- Priority order: auto-reconnect fixes > port forwarding > remote file paste > MOSH
…et-follow-focus spec

- README: replace "MOSH support" with "remote file paste" in planned changes
- Auto-reconnect draft: add missing detection (system wake, network-online,
  SSH keepalive), edge cases, and P0/P1/P2 priority breakdown
- New spec: widget-follow-focus — system widgets inherit connection from
  focused terminal
- Todos: reflect updated priorities
* fix(jobcontroller): P0 auto-reconnect reliability fixes

- Bug #1: move cooldown timestamp set after connection check passes
- Bug #2: replace processed bool with generation counters (actualGen/procGen)
- Bug #3: split singleflight into reconnectConnGroup and reconnectRouteGroup
- Add 12 unit tests covering all three fixes

* docs: record auto-reconnect decision and tmux feature ideas from 2026-05-23 session

* fix: ensure connection is alive before starting durable shell

When a remote SSH connection drops (e.g. host goes down), the connection
status is set to Disconnected and the route is removed from wshrouter.
However, when a new block tries to start a durable shell,
startNewJob() used MaybeGetConn() which returns the stale connection
object without checking if it's actually alive.

This adds an EnsureConnection() call before using the connection route.
EnsureConnection handles reconnection for Disconnected status by
calling Connect() which re-establishes the SSH session and re-registers
the route in wshrouter.

Fixes: durable sessions getting stuck after remote host downtime
Root cause: startNewJob() assumed connection was alive without verifying
* fix(jobcontroller): P0 auto-reconnect reliability fixes

- Bug #1: move cooldown timestamp set after connection check passes
- Bug #2: replace processed bool with generation counters (actualGen/procGen)
- Bug #3: split singleflight into reconnectConnGroup and reconnectRouteGroup
- Add 12 unit tests covering all three fixes

* docs: record auto-reconnect decision and tmux feature ideas from 2026-05-23 session

* fix: ensure connection is alive before starting durable shell

When a remote SSH connection drops (e.g. host goes down), the connection
status is set to Disconnected and the route is removed from wshrouter.
However, when a new block tries to start a durable shell,
startNewJob() used MaybeGetConn() which returns the stale connection
object without checking if it's actually alive.

This adds an EnsureConnection() call before using the connection route.
EnsureConnection handles reconnection for Disconnected status by
calling Connect() which re-establishes the SSH session and re-registers
the route in wshrouter.

Fixes: durable sessions getting stuck after remote host downtime
Root cause: startNewJob() assumed connection was alive without verifying

* docs: setup auto-reconnect detection gaps branch — Phase 1/2/3 plan for issues #7/#8

* feat(connmonitor): auto-disconnect on persistent stall (Phase 1 — Gap C)

- Add ConnStallAutoDisconnect and ConnStallDisconnectThreshold to ConnKeywords
- ConnMonitor tracks StallStartTime when stall is first detected
- When stall persists beyond threshold (default 30s) and user is not actively typing,
  trigger conn.Close() to set Status=Disconnected
- This makes sleep/Wi-Fi/VPN interruptions self-healing via existing onConnectionUp
- Guardrails: only disconnect when Status is Connected/Connecting; spawn Close()
  in goroutine to avoid blocking monitor; respect conn:stallautodisconnect=false

* fix(phase1-gaps): address all GAP-1 through GAP-4 from spec review

- GAP-2: Remove !urgent guard from stall-disconnect — keystrokes on a stalled
  connection go nowhere, so disconnect regardless of input activity
- GAP-1: Add reconnect scheduler in onConnectionDown — periodic Connect()
  attempts (every 30s, up to 5min) for connections with running durable jobs;
  deduplicated via connectionReconnectSchedulers sync map
- GAP-1: Add conncontroller.AttemptReconnect() helper for named reconnects
- GAP-3: Add conncontroller_test.go with 11 tests for AttemptReconnect,
  stall tracking, disconnectOnStall, config helpers; add deduplication test
  in jobcontroller_test.go
- GAP-4: Document conn:stallautodisconnect and conn:stalldisconnectthreshold
  in docs/docs/connections.mdx
- Add test hooks (connectInternalTestHook, getConnectionConfigTestHook) to
  conncontroller package for mockability without real SSH infrastructure

* docs(phase1-gaps): update spec and todos with resolution status

* feat(phase2): implement NotifySystemResumeCommand for macOS sleep/wake fast-path

- Replace no-op NotifySystemResumeCommand in wshserver.go with actual logic:
  calls jobcontroller.HandleSystemResume(ctx)
- Add HandleSystemResume in jobcontroller.go:
  - Iterate all SSH connections via GetAllConnStatus()
  - Skip local, non-durable, and already-healthy connections
  - Force disconnect on stalled zombies (post-sleep dead TCP)
  - Spawn AttemptReconnect() goroutines immediately (bypass 30s scheduler)
- Add hasRunningDurableJobsTestHook for testability
- Add TestHandleSystemResumeSmoke in jobcontroller_test.go
- Electron side already wired: emain.ts powerMonitor.on('resume') fires RPC

End-to-end flow on macOS wake:
1. powerMonitor resume event → NotifySystemResumeCommand RPC
2. HandleSystemResume → disconnect stalled + AttemptReconnect for all durable conns
3. Connect() succeeds when network is back → onConnectionUp → ReconnectJob
4. Durable sessions restored in ~1-2s instead of ~60s

* feat(phase3): aggressive reconnect scheduler on network-unreachable errors

- Add isNetworkUnreachableError() helper: detects dial tcp i/o timeout,
  no route to host, network unreachable, DNS failure (no such host)
- scheduleConnectionReconnect switches to aggressive mode (5s interval)
  when AttemptReconnect fails with a network-level error
- Aggressive window extends 2 minutes per failure, catches Wi-Fi/VPN
  return much faster than 30s polling
- Returns to normal 30s interval when aggressive window expires
- Zero native code: pure Go, cross-platform, no build risk

Completes the auto-reconnect trilogy:
- Phase 1: Auto-disconnect on stall (keepalive timeout)
- Phase 2: macOS sleep/wake fast-path (powerMonitor event)
- Phase 3: Aggressive scheduler for Wi-Fi/VPN changes

* docs(phase1-3): update spec with Phase 2 and Phase 3 completion status

* fix(review): address critical race and timing issues from code review

- Issue #1 (race): Close() in HandleSystemResume is now synchronous
  (not async goroutine) so status is Disconnected before AttemptReconnect
- Issue #2 (double scheduler): Delete stale scheduler entry from
  connectionReconnectSchedulers before fast-path reconnect to avoid races
- Issue #5 (timeout pile-up): In aggressive mode, connect timeout reduced
  from 30s -> 8s so 5s ticks don't overlap
- Issue #6 (job-check timeout): Increased from 5s -> 15s so slow DB
  queries don't prematurely stop the scheduler
…reconnects

- Set Timeout on ssh.ClientConfig so TCP dial has a fallback timeout
- Enforce a 5s deadline on net.Conn during ssh.NewClientConn to prevent
  indefinite handshake stalls that block lifecycleLock
- Clear deadline after handshake and close connection on error
- Fix TimeoutFromContext to never return negative durations
- Defensive ctx.Err() check in StartConnServer before reading version

Fixes #13
whoisjeremylam and others added 18 commits May 29, 2026 05:20
…aps from #13 review

Fixes the following gaps identified in issue #13 code review:

Comment 1 (Code Review of 4604df0):
- Gap #1: Proxy jump connections now use goroutine-with-timeout pattern since
  chanConn.SetDeadline() silently fails (returns ssh: tcpChan: deadline not supported)
- Gap #2: Interactive auth safety — handshake deadline now uses context deadline
  when present (allows passphrase/password/host-key prompts to complete), with
  only a 5s minimum from now to prevent indefinite stalls. No longer blanket-capped
  at 5s.
- Gap #3: Removed dead DefaultConnectionTimeout constants from conncontroller.go
  and wslconn.go (only active definition remains in sshclient.go)
- Gap #4: Added unit tests for TimeoutFromContext covering expired, cancelled,
  negative, future, and no-deadline contexts

Comment 2 (SSID Switch Analysis):
- Gap A: Added can't assign requested address and address not available to
  isNetworkUnreachableError patterns so SSID-switch errors trigger aggressive
  reconnect mode
- Gap B: Restructured scheduleConnectionReconnect to attempt immediate first
  reconnect without waiting 30s. Also fixes latent bug where ticker reassignment
  inside for range ticker.C had no effect.

Closes #13
Upstream commits:
- a5ac096 Bump google.golang.org/api 0.275.0 -> 0.277.0
- 81f7b1a Update Google Gemini model details in documentation
- c0687de Bump qs 6.14.2 -> 6.15.2, express 4.22.1 -> 4.22.2

Resolved: docs/docs/waveai-modes.mdx deleted in fork, modified upstream
-> kept deletion (WaveAI docs removed in fork)
…026-46598)

Fixes two HIGH severity CVEs directly relevant to SSH-first workflow:
- CVE-2026-39827: SSH memory leak on rejected channels
- CVE-2026-46598: Pathological ed25519 inputs panic in ssh/agent

Also bumps transitive deps via go mod tidy:
- golang.org/x/net 0.53.0 -> 0.54.0
- golang.org/x/sys 0.43.0 -> 0.45.0
- golang.org/x/term 0.42.0 -> 0.43.0
- golang.org/x/text 0.36.0 -> 0.37.0
After network drop/SSID switch, SSH connections stall and the disconnect
button appears unresponsive. The connection also never auto-reconnects.

Root cause: Close() and waitForDisconnect() deferred FireConnChangeEvent()
until after closeInternal_withlifecyclelock() returned. When TCP is dead,
client.Close() blocks indefinitely sending SSH disconnect packets. The event
never fires, so:
- Frontend stays stuck in 'stalled' overlay (disconnect button seems broken)
- jobcontroller.connReconcileWorker never sees disconnect, so onConnectionDown
  is never called, and the reconnect scheduler never starts

Fix: Move FireConnChangeEvent() to fire immediately after setting
Status = Disconnected and clearing ConnHealthStatus, before the blocking
cleanup. Apply same pattern to waitForDisconnect() for consistency.

Also includes prior uncommitted artifacts:
- .pi/todos.md: upstream dependency merge notes (CVE bumps)
- schema/connections.json + gotypes.d.ts: stall auto-disconnect config fields

Closes #15
)

Adds diagnostic logging to trace why aggressive retry mode may not
trigger after SSID switch, causing ~30s reconnect delays:

- jobcontroller/scheduleConnectionReconnect:
  - Log scheduler start, attempt start/end with duration
  - Log whether isNetworkUnreachableError matched (true/false)
  - Distinguish aggressive vs normal mode per attempt

- sshclient/connectInternal:
  - Log TCP dial duration and classify error:
    * context canceled/deadline (ctx-err)
    * actual network error (subcode)
  - Log SSH handshake duration (direct + proxy jump paths)

Theory: if DialContext hangs until the 30s context timeout expires,
the error is 'context deadline exceeded' which does NOT match
isNetworkUnreachableError patterns. Aggressive mode never triggers,
so the next attempt waits the full 30s interval instead of 5s.

Refs #15
…nt lifecycleLock deadlock (#15)

HandleSystemResume calls conn.Close() synchronously for stalled
connections, then starts a fast-path reconnect goroutine. But
closeInternal's client.Close() blocks on dead TCP, holding
lifecycleLock indefinitely. The reconnect goroutine's Connect() call
also needs lifecycleLock, so it deadlocks — the reconnect popup
appears but clicking it does nothing.

Fix: closeInternal now captures old references (client, listener,
controller) under conn.lock, nils them immediately so Connect() sees
clean state, then runs the actual blocking Close() calls in a
background goroutine. This frees lifecycleLock immediately while
still logging slow Close() operations for diagnostics.

Also preserves the existing FireConnChangeEvent fix from prior commit.

Closes #15
#16)

Race condition: after macOS sleep/wake, a stale waitForDisconnect goroutine
(waiting on old client_A.Wait()) could acquire lifecycleLock after Connect()
had already established a new client_B. This caused:
- Status overwritten from Connected to Disconnected
- New connection's resources (Client, Monitor, DomainSockListener,
  ConnController) closed by the stale goroutine
- Cascading disconnect/reconnect cycles and 900% CPU spikes

Primary fix: stale client guard in waitForDisconnect() — after acquiring
lifecycleLock, compare conn.GetClient() with the captured client. If a
new connection is active, return early without disrupting it.

Secondary fix: closeInternal_withlifecyclelock(expectedClient) —
defense-in-depth guard that prevents resource theft when expectedClient
doesn't match the current client. Moved Monitor cleanup into the same
WithLock block so all resources are protected by the guard.

Tests: 4 new unit tests covering the race scenario, normal disconnect,
defense-in-depth guard, and nil expectedClient behavior.
Three PR-ready specs:
- fast-reconnect-hardcoded.md: PR #1 with hardcoded fast thresholds
- reconnect-ui-overlay.md: PR #2 with retry transparency UI
- configurable-reconnect-thresholds.md: PR #3 with per-connection settings
…osed mux

When closeInternal_withlifecyclelock runs its async cleanup goroutine, it
previously closed oldClient first, then oldListener. This caused a 100% CPU
spin because:

1. oldClient.Close() closes the SSH mux, which closes globalResponses channel
2. oldListener.Close() calls mux.SendRequest(cancel-streamlocal-forward, wantReply=true)
3. SendRequest's drain loop does: select { case <-m.globalResponses: ... default: break }
4. When globalResponses is CLOSED, receiving always succeeds (zero value), so the
   default case never fires and the loop spins forever

Fix: close the listener FIRST while the mux is still alive. Use a 5-second timeout
to handle the dead-network case where writePacket could block indefinitely.

Updates #16
Root cause: mux.SendRequest and channel.SendRequest drain loops spin
forever on closed channels after SSH disconnect, burning 100%+ CPU.

The drain loop does:
    case <-m.globalResponses:  // or <-ch.msg
    default:
        break drain

When the channel is closed, receiving always succeeds immediately
(returning zero value), so the default case is never reached.

Upstream fix (comma-ok idiom):
    case _, ok := <-m.globalResponses:
        if !ok { break drain }
    default:
        break drain

Upstream commits 4c4d20b (mux.go) and e3e62d9 (channel.go) landed on
x/crypto main May 27, 2026 but are not yet in a tagged release.

Also rolls back the issue #22 reorder workaround (commit eb2c659).
The original close order (Client.Close before DomainSockListener.Close)
is correct because Client.Close force-closes the transport, unblocking
pending writePacket calls on dead networks. With the mux patch, the
drain loop exits immediately on a closed channel regardless of close
order, making the reorder + timeout unnecessary.

go.mod replace directive: golang.org/x/crypto v0.52.0 => ./local_crypto_patch/contents
Roll back when x/crypto >= v0.53.0 is released (see local_crypto_patch/README.md).

Fixes: #22
See: golang/go#79658, golang/go#79763
Adds the waitForDisconnect stale client guard from PR #20:
- After acquiring lifecycleLock, compare conn.GetClient() with captured client
- If a new connection is active, return early without disrupting it
- closeInternal_withlifecyclelock now accepts expectedClient parameter for
  defense-in-depth guard against resource theft
- Monitor cleanup moved into WithLock block, protected by the guard
- 4 new unit tests covering the race scenario

Fixes: #16
…op fix (#23)

* feat: fast reconnect with hardcoded thresholds (#17)

Tighten connection monitor and reconnect scheduler thresholds to reduce
reconnection delay from 30-60s to 5-10s after network changes.

Monitor changes:
- Keepalive threshold: 10s → 3s (1s urgent)
- Stall threshold: 10s → 3s (2s urgent)
- Auto-disconnect default: 30s → 5s
- Ticker interval: 5s → 3s

Scheduler changes:
- Reconnect interval: 30s → 5s
- Aggressive interval: 5s → 3s
- Connect timeout: 30s/8s → 5s/5s
- First attempt: immediate (no delay)
- Aggressive mode on network-unreachable errors

Additional changes:
- Add context deadline exceeded to isNetworkUnreachableError
- Add needsInteractiveAuth helper to skip scheduler for password auth
- Fix duplicate FireConnChangeEvent in waitForDisconnect
- Fix HandleSystemResume timeout (10s → 5s)
- Remove dead if aggressiveMode branch
- Add logging for skipped reconnect attempts
- Update stale 30s comment to 5s

Documentation:
- Add docs/docs/reconnect.mdx (Connection Resilience)
- Cross-reference from connections.mdx and durable-sessions.mdx

Refs: #17

* feat: reconnect UI overlay with retry transparency (#18)

Add rich overlay states for the reconnect lifecycle so users see
what's happening instead of a frozen terminal.

Backend:
- Add ReconnectAttempt, ReconnectNextAttempt, ReconnectError to ConnStatus
- Add retry state fields and methods to SSHConn
- Emit retry state events from scheduleConnectionReconnect

Frontend:
- DisconnectedOverlay: shows on disconnect with error detail and countdown
- RetryingOverlay: spinner + 'Attempt N — connecting…'
- CountdownOverlay: live countdown timer + last error + 'Reconnect now' button
- ConnectionButton: spinning icon during reconnect, clock icon during wait

Refs: #18

* fix: close DomainSockListener before Client to prevent CPU spin on closed mux

When closeInternal_withlifecyclelock runs its async cleanup goroutine, it
previously closed oldClient first, then oldListener. This caused a 100% CPU
spin because:

1. oldClient.Close() closes the SSH mux, which closes globalResponses channel
2. oldListener.Close() calls mux.SendRequest(cancel-streamlocal-forward, wantReply=true)
3. SendRequest's drain loop does: select { case <-m.globalResponses: ... default: break }
4. When globalResponses is CLOSED, receiving always succeeds (zero value), so the
   default case never fires and the loop spins forever

Fix: close the listener FIRST while the mux is still alive. Use a 5-second timeout
to handle the dead-network case where writePacket could block indefinitely.

Updates #16

* fix: patch x/crypto/ssh drain loop spin bug (golang/go#79658)

Root cause: mux.SendRequest and channel.SendRequest drain loops spin
forever on closed channels after SSH disconnect, burning 100%+ CPU.

The drain loop does:
    case <-m.globalResponses:  // or <-ch.msg
    default:
        break drain

When the channel is closed, receiving always succeeds immediately
(returning zero value), so the default case is never reached.

Upstream fix (comma-ok idiom):
    case _, ok := <-m.globalResponses:
        if !ok { break drain }
    default:
        break drain

Upstream commits 4c4d20b (mux.go) and e3e62d9 (channel.go) landed on
x/crypto main May 27, 2026 but are not yet in a tagged release.

Also rolls back the issue #22 reorder workaround (commit eb2c659).
The original close order (Client.Close before DomainSockListener.Close)
is correct because Client.Close force-closes the transport, unblocking
pending writePacket calls on dead networks. With the mux patch, the
drain loop exits immediately on a closed channel regardless of close
order, making the reorder + timeout unnecessary.

go.mod replace directive: golang.org/x/crypto v0.52.0 => ./local_crypto_patch/contents
Roll back when x/crypto >= v0.53.0 is released (see local_crypto_patch/README.md).

Fixes: #22
See: golang/go#79658, golang/go#79763
* spec: update portforwarding spec with verified changes

- Add copyBoth helper for bidirectional tunneling
- Fix tunnel goroutines: use explicit parameter passing instead of no-op self-assignment
- Remove non-existent MonitorUpdate and io.CopyBoth references
- Correct closeInternal cleanup to match capture-in-lock/close-in-goroutine pattern
- Correct cmd/test-conn call site notes (no direct ConnectToClient calls)
- Update lifecycle table to reflect SSH transport activity

* feat: add SshLocalForward and SshRemoteForward fields to ConnKeywords

Add two new fields to the ConnKeywords struct for port forwarding support:
- SshLocalForward: list of local forwarding rules (bind_addr dest)
- SshRemoteForward: list of remote forwarding rules (bind_addr dest)

These follow the existing pattern for list-type SSH keywords like
IdentityFile and ProxyJump.

* feat: parse LocalForward/RemoteForward from SSH config and return merged keywords

- findSshConfigKeywords: parse LocalForward/RemoteForward via GetAll()
- findSshDefaults: initialize empty slices for forwarding
- mergeKeywords: add merge logic following SshProxyJump pattern
- ConnectToClient: change signature to return *wconfig.ConnKeywords
  so conncontroller can access the merged forwarding rules
- Recursive ProxyJump call uses _ for keywords (no forwarding on proxies)

* feat: implement port forwarding runtime in conncontroller

- Add LocalForwardListeners and RemoteForwardListeners to SSHConn struct
- Add copyBoth helper for bidirectional connection tunneling
- Add startPortForwarding method that:
  - Parses forwarding rules (format: 'bind_addr dest')
  - Skips malformed rules with logging
  - Spawns goroutines with panichandler for each tunnel
  - Stores listeners on SSHConn for cleanup
- Update connectInternal to call startPortForwarding after wsh setup
- Update closeInternal_withlifecyclelock to close forwarding listeners

* test: add unit tests for port forwarding

sshclient_test.go (new file):
- TestMergeKeywords_LocalForward_Override: new overrides old
- TestMergeKeywords_LocalForward_NilPreserves: nil preserves old
- TestMergeKeywords_RemoteForward_Override: same for remote
- TestMergeKeywords_RemoteForward_NilPreserves: same for remote
- TestMergeKeywords_BothForward_MultipleRules: multiple rules
- TestMergeKeywords_Forward_EmptyOverrides: empty slice overrides

conncontroller_test.go (additions):
- TestCopyBoth: bidirectional data transfer via net.Pipe
- TestLocalForwardStartsAndStops: listener lifecycle
- TestStartPortForwarding_MalformedRule: malformed rules skipped
- TestStartPortForwarding_NilClient: nil client returns immediately

* docs: add port forwarding documentation

- Add LocalForward and RemoteForward to SSH Config Parsing table
- Add ssh:localforward and ssh:remoteforward to Internal SSH Config table
- Add Port Forwarding example section with ssh config and connections.json examples

* docs: add release notes entry for SSH port forwarding

Adds v0.14.5-fork release notes entry documenting LocalForward and
RemoteForward support from ~/.ssh/config and connections.json.

* debug: add logging for SSH port forwarding diagnostics

- conncontroller: log port forwarding keywords before startPortForwarding
- sshclient: log parsed LocalForward/RemoteForward from SSH config

* feat: comprehensive SSH LocalForward/RemoteForward parser

Replace the naive strings.Fields parser with a spec-compliant parser
that handles all ssh_config LocalForward/RemoteForward syntax forms:

- Port-only bind (8765 → 127.0.0.1:8765) — fixes the listen address bug
- Explicit bind address (localhost:8765, 192.168.1.1:8765)
- Wildcard bind (*:8765)
- IPv6 bracket notation ([::1]:8765)
- Unix domain sockets (detected by '/' in path)
- RemoteForward SOCKS proxy mode (single-arg, no destination)

Unsupported features (Unix sockets, SOCKS proxy) are parsed correctly
and logged as "not supported" rather than causing errors. This keeps
the parser stable when new features are added — only the handler changes.

The handler (startPortForwarding) now uses a switch on parsed types:
- TCP→TCP: fully implemented (listen + dial + copyBoth)
- Unix sockets: logged and skipped
- SOCKS proxy: logged and skipped

Includes table-driven tests for all syntax forms and error cases.

* fix(port-forwarding): half-close tunnel endpoints on EOF

copyBoth() previously closed both connections after both io.Copy
directions finished, but never signaled EOF to the peers when one
direction ended. This caused HTTP keep-alive connections to hang:
the browser would try to reuse a tunnel connection whose remote end
had already closed, but since no FIN was sent, subsequent requests
silently failed.

Fix: when one direction's io.Copy returns, call CloseWrite() on the
destination connection (via halfCloser interface). This sends TCP FIN
for *net.TCPConn or SSH_MSG_CHANNEL_EOF for ssh.Channel, so the peer
knows the stream is done.

Also:
- Add copyBothWithName variant with [portforward] diagnostic logging
- Add logging to startLocalForwardTCP and startRemoteForwardTCP
- Add TestCopyBothHalfClose — verifies CloseWrite is called on both
  tunnel endpoints when peers close, using real TCP connections
- Add ssh:localforward and ssh:remoteforward to connection schema
  and gotypes.d.ts

* chore(port-forwarding): switch logging to conn.Debugf

Port forwarding logs were using log.Printf (stderr, always visible).
Switch to conn.Debugf which only writes to the connection block's
output when verbose mode is enabled — keeps normal operation quiet
while preserving diagnostics when needed.

copyBothWithName now accepts ctx+conn for debug logging. copyBoth
(tests) passes nil for both.
- Add startConnServerWithRetry: retries wsh startup 3x with linear backoff
- Fail fast in connectInternal when wsh fails (unless user explicitly opted out)
- Skip disk persistence of conn:wshenabled=false on technical failures so
  next attempt retries wsh instead of permanently recording transient error
- Fix data race in TestCopyBothHalfClose: closeWriteCalled → atomic.Bool
* spec: update portforwarding spec with verified changes

- Add copyBoth helper for bidirectional tunneling
- Fix tunnel goroutines: use explicit parameter passing instead of no-op self-assignment
- Remove non-existent MonitorUpdate and io.CopyBoth references
- Correct closeInternal cleanup to match capture-in-lock/close-in-goroutine pattern
- Correct cmd/test-conn call site notes (no direct ConnectToClient calls)
- Update lifecycle table to reflect SSH transport activity

* feat: add SshLocalForward and SshRemoteForward fields to ConnKeywords

Add two new fields to the ConnKeywords struct for port forwarding support:
- SshLocalForward: list of local forwarding rules (bind_addr dest)
- SshRemoteForward: list of remote forwarding rules (bind_addr dest)

These follow the existing pattern for list-type SSH keywords like
IdentityFile and ProxyJump.

* feat: parse LocalForward/RemoteForward from SSH config and return merged keywords

- findSshConfigKeywords: parse LocalForward/RemoteForward via GetAll()
- findSshDefaults: initialize empty slices for forwarding
- mergeKeywords: add merge logic following SshProxyJump pattern
- ConnectToClient: change signature to return *wconfig.ConnKeywords
  so conncontroller can access the merged forwarding rules
- Recursive ProxyJump call uses _ for keywords (no forwarding on proxies)

* feat: implement port forwarding runtime in conncontroller

- Add LocalForwardListeners and RemoteForwardListeners to SSHConn struct
- Add copyBoth helper for bidirectional connection tunneling
- Add startPortForwarding method that:
  - Parses forwarding rules (format: 'bind_addr dest')
  - Skips malformed rules with logging
  - Spawns goroutines with panichandler for each tunnel
  - Stores listeners on SSHConn for cleanup
- Update connectInternal to call startPortForwarding after wsh setup
- Update closeInternal_withlifecyclelock to close forwarding listeners

* test: add unit tests for port forwarding

sshclient_test.go (new file):
- TestMergeKeywords_LocalForward_Override: new overrides old
- TestMergeKeywords_LocalForward_NilPreserves: nil preserves old
- TestMergeKeywords_RemoteForward_Override: same for remote
- TestMergeKeywords_RemoteForward_NilPreserves: same for remote
- TestMergeKeywords_BothForward_MultipleRules: multiple rules
- TestMergeKeywords_Forward_EmptyOverrides: empty slice overrides

conncontroller_test.go (additions):
- TestCopyBoth: bidirectional data transfer via net.Pipe
- TestLocalForwardStartsAndStops: listener lifecycle
- TestStartPortForwarding_MalformedRule: malformed rules skipped
- TestStartPortForwarding_NilClient: nil client returns immediately

* docs: add port forwarding documentation

- Add LocalForward and RemoteForward to SSH Config Parsing table
- Add ssh:localforward and ssh:remoteforward to Internal SSH Config table
- Add Port Forwarding example section with ssh config and connections.json examples

* docs: add release notes entry for SSH port forwarding

Adds v0.14.5-fork release notes entry documenting LocalForward and
RemoteForward support from ~/.ssh/config and connections.json.

* debug: add logging for SSH port forwarding diagnostics

- conncontroller: log port forwarding keywords before startPortForwarding
- sshclient: log parsed LocalForward/RemoteForward from SSH config

* feat: comprehensive SSH LocalForward/RemoteForward parser

Replace the naive strings.Fields parser with a spec-compliant parser
that handles all ssh_config LocalForward/RemoteForward syntax forms:

- Port-only bind (8765 → 127.0.0.1:8765) — fixes the listen address bug
- Explicit bind address (localhost:8765, 192.168.1.1:8765)
- Wildcard bind (*:8765)
- IPv6 bracket notation ([::1]:8765)
- Unix domain sockets (detected by '/' in path)
- RemoteForward SOCKS proxy mode (single-arg, no destination)

Unsupported features (Unix sockets, SOCKS proxy) are parsed correctly
and logged as "not supported" rather than causing errors. This keeps
the parser stable when new features are added — only the handler changes.

The handler (startPortForwarding) now uses a switch on parsed types:
- TCP→TCP: fully implemented (listen + dial + copyBoth)
- Unix sockets: logged and skipped
- SOCKS proxy: logged and skipped

Includes table-driven tests for all syntax forms and error cases.

* fix(port-forwarding): half-close tunnel endpoints on EOF

copyBoth() previously closed both connections after both io.Copy
directions finished, but never signaled EOF to the peers when one
direction ended. This caused HTTP keep-alive connections to hang:
the browser would try to reuse a tunnel connection whose remote end
had already closed, but since no FIN was sent, subsequent requests
silently failed.

Fix: when one direction's io.Copy returns, call CloseWrite() on the
destination connection (via halfCloser interface). This sends TCP FIN
for *net.TCPConn or SSH_MSG_CHANNEL_EOF for ssh.Channel, so the peer
knows the stream is done.

Also:
- Add copyBothWithName variant with [portforward] diagnostic logging
- Add logging to startLocalForwardTCP and startRemoteForwardTCP
- Add TestCopyBothHalfClose — verifies CloseWrite is called on both
  tunnel endpoints when peers close, using real TCP connections
- Add ssh:localforward and ssh:remoteforward to connection schema
  and gotypes.d.ts

* chore(port-forwarding): switch logging to conn.Debugf

Port forwarding logs were using log.Printf (stderr, always visible).
Switch to conn.Debugf which only writes to the connection block's
output when verbose mode is enabled — keeps normal operation quiet
while preserving diagnostics when needed.

copyBothWithName now accepts ctx+conn for debug logging. copyBoth
(tests) passes nil for both.

* feat: add port forwarding UI status indicators

Show active port forwarding rules in the block header with a plug icon,
count badge, and hover tooltip. Leverages existing ConnStatus event flow
instead of adding a separate RPC endpoint.

* fix: remove redundant rule count from port-forward tooltip; fix race condition where active tab missed forwarding rules on restore

- Tooltip fix: the plug badge already shows the count, so remove the
  parenthetical count from the hover tooltip.

- Restore fix: startLocalForwardTCP and startRemoteForwardTCP previously
  wrapped the entire listener setup (net.Listen/client.Listen + struct
  append) inside a goroutine. This caused startPortForwarding to return
  immediately, so FireConnChangeEvent after connectInternal read empty
  forwarding slices. The initial connchange event then carried
  forwardingrules: [], and the active tab (which rendered first on
  restore) would display no plug.

  Fix: move listener creation and ForwardingRule append out of the
  goroutines so they execute synchronously. Only the Accept loops stay
  async. This guarantees the forwarding slices are populated before
  connectInternal returns, so the first FireConnChangeEvent includes the
  correct rules.

- Updated panic handler labels to reflect they now guard only the
  Accept loop goroutines (localforward-accept / remoteforward-accept).
* docs: add spec for removing auto-updater

Detailed spec covering 5 phases to disable and hide all update
checking, downloading, and installation from the fork.

- Phase A: configureAutoUpdater() → no-op
- Phase B: Hide update banner + About modal update channel
- Phase C: Stub updater IPC APIs
- Phase D: Default autoupdate:enabled to false
- Phase E: Remove publish config from electron-builder

Follows fork convention: disable and hide, don't delete.

* feat: disable auto-updater

Remove all update checking, downloading, and installation from
the fork. No network calls to dl.waveterm.dev.

- Phase A: configureAutoUpdater() → no-op (stops all network activity)
- Phase B: UpdateStatusBanner always renders null; remove
  Update Channel from About modal
- Phase C: Stub updater IPC APIs in preload; simplify
  updaterStatusAtom to static value
- Phase D: Default autoupdate:enabled to false
- Phase E: Remove publish URL from electron-builder config

Keeps electron-updater dependency and Go autoupdate settings
fields intact for upstream compatibility.

* fix: align mock getUpdaterChannel to return "latest"

Matches the preload.ts stub for consistency.

* feat: delete updater dead code

Remove all unreachable updater code left by the disable-phase.

- Remove "Check for Updates" menu item from emain-menu.ts
- Remove updater?.status guards from wavesrv restart and window quit handlers
- Remove handle_getupdatechannel RPC handler from emain-wsh.ts
- Remove updater import + updater?.stop() from emain.ts
- Delete emain/updater.ts entirely (232 lines)
- Remove electron-updater from package.json
- Update package-lock.json

Phases A-C complete. Phases D (stub IPC cleanup) and E
(Go settings cleanup) deferred per spec.

* fix(tsgen): update WaveEventName test assertion for multi-line union output

The generator produces a multi-line union type but the test expected
a single-line format. Split the check into two independent substring
matches so it works regardless of formatting.

* fix: restore port forwarding code accidentally removed in updater branch

The remove-updater branch incorrectly reverted the SSH port forwarding
feature (LocalForward/RemoteForward), which was already completed on main.
This commit restores all port-forwarding files to match main, fixing three
regressions:

1. persistWshInstalled guard: transient wsh failures no longer permanently
   disable wsh by writing conn:wshenabled=false to connections.json
2. wsh failure handling: technical wsh failures once again fail the entire
   connection (return fmt.Errorf), triggering reconnect instead of silently
   continuing in a degraded state
3. startConnServerWithRetry: retry wrapper restored (3 attempts with linear
   backoff) for transient wsh startup failures

Also restores:
- Port forwarding runtime code in conncontroller.go (startPortForwarding,
  copyBoth, parseForwardRule, halfCloser, etc.)
- Port forwarding tests in conncontroller_test.go and sshclient_test.go
- ConnKeywords fields (SshLocalForward, SshRemoteForward)
- ConnStatus.ForwardingRules field
- ConnectToClient 4-return-value signature
- Port-forward-status UI component
- Documentation (connections.mdx, releasenotes.mdx)
- Schema (connections.json)
- Planning docs (.pi/todos.md, .pi/specs/portforwarding.md, etc.)

* fix: clean up remaining updater dead code (Phase D)

Remove all orphaned updater references that remained after deleting
emain/updater.ts:

- Remove orphaned GetUpdateChannelCommand RPC from Go backend
  (wshrpctypes.go, wshclient.go) and TypeScript API (wshclientapi.ts)
- Remove updater IPC stubs from preload.ts (4 methods)
- Remove UpdaterStatus type and IPC declarations from custom.d.ts
- Remove updaterStatusAtom from global-atoms.ts and atoms export
- Remove updaterStatusAtom/installAppUpdate from tabbarenv.ts and vtabbarenv.ts
- Delete updatebanner.tsx (component already returned null)
- Remove UpdateStatusBanner from tabbar.tsx and vtabbar.tsx
- Remove updater UI controls from tabbar/vtabbar previews
- Remove updaterStatusAtom from mockwaveenv.ts
- Remove updater API stubs from preview-electron-api.ts
- Fix stale QUIC comment in emain.ts (was attributed to updater)

* Fix build: remove undefined GetUpdateChannelCommand from wsh version

The GetUpdateChannelCommand RPC function was removed from wshclient but
wshcmd-version.go still referenced it, causing all cross-compilation builds
to fail with undefined symbol errors. Remove the call and the update-channel
field from version output since auto-updater features were removed upstream.
The useLongClick hook depended on ref.current in its useEffect deps,
which is not reactive state. On first render ref.current is null and
the effect early-returns without attaching listeners. A subsequent
re-render would fix it, but only if an atom change triggered one.

Add a mounted state flag to ensure the effect re-runs after the ref
is assigned. Also add position:relative + z-index to the block header
to prevent Electron webview compositor layers from stealing clicks.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
0 out of 3 committers have signed the CLA.

❌ Jeremy Lam
❌ clearlyjeremy
❌ whoisjeremylam


Jeremy Lam seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 298 files, which is 148 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 49741118-2097-4d57-a8ae-85a37d14f796

📥 Commits

Reviewing files that changed from the base of the PR and between a5ac096 and dfbdff1.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • local_crypto_patch/contents/go.sum is excluded by !**/*.sum
📒 Files selected for processing (298)
  • .github/workflows/build-macos.yml
  • .github/workflows/codeql.yml
  • .github/workflows/deploy-docsite.yml
  • .github/workflows/testdriver-build.yml
  • .github/workflows/testdriver.yml
  • .gitignore
  • .pi/context.md
  • .pi/decisions.md
  • .pi/draft-issue-autoconnect-bugs.md
  • .pi/index.md
  • .pi/reviews/remove-telemetry-independent-review.md
  • .pi/reviews/remove-telemetry-review.md
  • .pi/specs/bug-tabclose-crash.md
  • .pi/specs/configurable-reconnect-thresholds.md
  • .pi/specs/fast-reconnect-hardcoded.md
  • .pi/specs/phase1-gaps.md
  • .pi/specs/portforwarding.md
  • .pi/specs/reconnect-ui-overlay.md
  • .pi/specs/remove-telemetry.md
  • .pi/specs/remove-updater-delete.md
  • .pi/specs/remove-updater.md
  • .pi/specs/remove-waveai.md
  • .pi/specs/widget-follow-focus.md
  • .pi/specs/wsh-agent-api.md
  • .pi/todos.md
  • AGENTS.md
  • README.md
  • Taskfile.yml
  • cmd/generatego/main-generatego.go
  • cmd/generateschema/main-generateschema.go
  • cmd/server/main-server.go
  • cmd/testai/main-testai.go
  • cmd/testai/testschema.json
  • cmd/testopenai/main-testopenai.go
  • cmd/testsummarize/main-testsummarize.go
  • cmd/wsh/cmd/wshcmd-ai.go
  • cmd/wsh/cmd/wshcmd-badge.go
  • cmd/wsh/cmd/wshcmd-blocks.go
  • cmd/wsh/cmd/wshcmd-debug.go
  • cmd/wsh/cmd/wshcmd-debugterm.go
  • cmd/wsh/cmd/wshcmd-deleteblock.go
  • cmd/wsh/cmd/wshcmd-editconfig.go
  • cmd/wsh/cmd/wshcmd-editor.go
  • cmd/wsh/cmd/wshcmd-file.go
  • cmd/wsh/cmd/wshcmd-focusblock.go
  • cmd/wsh/cmd/wshcmd-getmeta.go
  • cmd/wsh/cmd/wshcmd-getvar.go
  • cmd/wsh/cmd/wshcmd-launch.go
  • cmd/wsh/cmd/wshcmd-notify.go
  • cmd/wsh/cmd/wshcmd-root.go
  • cmd/wsh/cmd/wshcmd-run.go
  • cmd/wsh/cmd/wshcmd-secret.go
  • cmd/wsh/cmd/wshcmd-setbg.go
  • cmd/wsh/cmd/wshcmd-setconfig.go
  • cmd/wsh/cmd/wshcmd-setmeta.go
  • cmd/wsh/cmd/wshcmd-setvar.go
  • cmd/wsh/cmd/wshcmd-ssh.go
  • cmd/wsh/cmd/wshcmd-tabindicator.go
  • cmd/wsh/cmd/wshcmd-term.go
  • cmd/wsh/cmd/wshcmd-termscrollback.go
  • cmd/wsh/cmd/wshcmd-version.go
  • cmd/wsh/cmd/wshcmd-view.go
  • cmd/wsh/cmd/wshcmd-wavepath.go
  • cmd/wsh/cmd/wshcmd-web.go
  • cmd/wsh/cmd/wshcmd-wsl.go
  • db/migrations-wstore/000012_drop_telemetry.down.sql
  • db/migrations-wstore/000012_drop_telemetry.up.sql
  • docs/docs/ai-presets.mdx
  • docs/docs/config.mdx
  • docs/docs/connections.mdx
  • docs/docs/durable-sessions.mdx
  • docs/docs/faq.mdx
  • docs/docs/gettingstarted.mdx
  • docs/docs/index.mdx
  • docs/docs/keybindings.mdx
  • docs/docs/reconnect.mdx
  • docs/docs/releasenotes.mdx
  • docs/docs/telemetry-old.mdx
  • docs/docs/telemetry.mdx
  • docs/docs/waveai-modes.mdx
  • docs/docs/waveai.mdx
  • docs/docs/wsh-reference.mdx
  • electron-builder.config.cjs
  • emain/emain-activity.ts
  • emain/emain-ipc.ts
  • emain/emain-menu.ts
  • emain/emain-tabview.ts
  • emain/emain-wavesrv.ts
  • emain/emain-window.ts
  • emain/emain-wsh.ts
  • emain/emain.ts
  • emain/preload.ts
  • emain/updater.ts
  • frontend/app/aipanel/ai-utils.ts
  • frontend/app/aipanel/aidroppedfiles.tsx
  • frontend/app/aipanel/aifeedbackbuttons.tsx
  • frontend/app/aipanel/aimessage.tsx
  • frontend/app/aipanel/aimode.tsx
  • frontend/app/aipanel/aipanel-contextmenu.ts
  • frontend/app/aipanel/aipanel.tsx
  • frontend/app/aipanel/aipanelheader.tsx
  • frontend/app/aipanel/aipanelinput.tsx
  • frontend/app/aipanel/aipanelmessages.tsx
  • frontend/app/aipanel/airatelimitstrip.tsx
  • frontend/app/aipanel/aitooluse.tsx
  • frontend/app/aipanel/aitypes.ts
  • frontend/app/aipanel/byokannouncement.tsx
  • frontend/app/aipanel/restorebackupmodal.tsx
  • frontend/app/aipanel/telemetryrequired.tsx
  • frontend/app/aipanel/waveai-focus-utils.ts
  • frontend/app/aipanel/waveai-model.tsx
  • frontend/app/app.tsx
  • frontend/app/block/block.scss
  • frontend/app/block/blockenv.ts
  • frontend/app/block/blockframe-header.tsx
  • frontend/app/block/blockframe.tsx
  • frontend/app/block/blockregistry.ts
  • frontend/app/block/blockutil.tsx
  • frontend/app/block/connectionbutton.tsx
  • frontend/app/block/connstatusoverlay.tsx
  • frontend/app/block/durable-session-flyover.tsx
  • frontend/app/block/port-forward-status.tsx
  • frontend/app/hook/useLongClick.tsx
  • frontend/app/modals/about.tsx
  • frontend/app/monaco/schemaendpoints.ts
  • frontend/app/onboarding/fakechat.tsx
  • frontend/app/onboarding/onboarding-durable.tsx
  • frontend/app/onboarding/onboarding-features.tsx
  • frontend/app/onboarding/onboarding-starask.tsx
  • frontend/app/onboarding/onboarding-upgrade-minor.tsx
  • frontend/app/onboarding/onboarding.tsx
  • frontend/app/store/focusManager.ts
  • frontend/app/store/global-atoms.ts
  • frontend/app/store/global.ts
  • frontend/app/store/keymodel.ts
  • frontend/app/store/services.ts
  • frontend/app/store/tabrpcclient.ts
  • frontend/app/store/wshclientapi.ts
  • frontend/app/tab/tab.tsx
  • frontend/app/tab/tabbar.tsx
  • frontend/app/tab/tabbarenv.ts
  • frontend/app/tab/tabcontextmenu.ts
  • frontend/app/tab/updatebanner.tsx
  • frontend/app/tab/vtabbar.tsx
  • frontend/app/tab/vtabbarenv.ts
  • frontend/app/view/aifilediff/aifilediff.tsx
  • frontend/app/view/term/dec-modes.test.ts
  • frontend/app/view/term/osc-handlers.ts
  • frontend/app/view/term/term-model.ts
  • frontend/app/view/term/termwrap.ts
  • frontend/app/view/waveai/waveai.tsx
  • frontend/app/view/waveconfig/waveaivisual.tsx
  • frontend/app/view/waveconfig/waveconfig-model.ts
  • frontend/app/view/waveconfig/waveconfigenv.ts
  • frontend/app/workspace/widgets.tsx
  • frontend/app/workspace/workspace-layout-model.ts
  • frontend/app/workspace/workspace.tsx
  • frontend/builder/builder-buildpanel.tsx
  • frontend/builder/builder-workspace.tsx
  • frontend/builder/store/builder-focusmanager.ts
  • frontend/builder/tabs/builder-filestab.tsx
  • frontend/builder/tabs/builder-previewtab.tsx
  • frontend/layout/lib/layoutModel.ts
  • frontend/preview/mock/defaultconfig.ts
  • frontend/preview/mock/mockwaveenv.ts
  • frontend/preview/mock/preview-electron-api.ts
  • frontend/preview/previews/aifilediff.preview-util.ts
  • frontend/preview/previews/aifilediff.preview.test.ts
  • frontend/preview/previews/aifilediff.preview.tsx
  • frontend/preview/previews/modal-about.preview.tsx
  • frontend/preview/previews/onboarding.preview.tsx
  • frontend/preview/previews/tabbar.preview.tsx
  • frontend/preview/previews/vtabbar.preview.tsx
  • frontend/preview/previews/waveai.preview.tsx
  • frontend/types/custom.d.ts
  • frontend/types/gotypes.d.ts
  • frontend/types/waveevent.d.ts
  • frontend/util/util.ts
  • frontend/wave.ts
  • go.mod
  • local_crypto_patch/README.md
  • local_crypto_patch/contents/.gitattributes
  • local_crypto_patch/contents/.gitignore
  • local_crypto_patch/contents/CONTRIBUTING.md
  • local_crypto_patch/contents/LICENSE
  • local_crypto_patch/contents/PATENTS
  • local_crypto_patch/contents/README.md
  • local_crypto_patch/contents/acme/acme.go
  • local_crypto_patch/contents/acme/acme_test.go
  • local_crypto_patch/contents/acme/autocert/autocert.go
  • local_crypto_patch/contents/acme/autocert/autocert_test.go
  • local_crypto_patch/contents/acme/autocert/cache.go
  • local_crypto_patch/contents/acme/autocert/cache_test.go
  • local_crypto_patch/contents/acme/autocert/example_test.go
  • local_crypto_patch/contents/acme/autocert/internal/acmetest/ca.go
  • local_crypto_patch/contents/acme/autocert/listener.go
  • local_crypto_patch/contents/acme/autocert/renewal.go
  • local_crypto_patch/contents/acme/autocert/renewal_test.go
  • local_crypto_patch/contents/acme/http.go
  • local_crypto_patch/contents/acme/http_test.go
  • local_crypto_patch/contents/acme/internal/acmeprobe/prober.go
  • local_crypto_patch/contents/acme/jws.go
  • local_crypto_patch/contents/acme/jws_test.go
  • local_crypto_patch/contents/acme/pebble_test.go
  • local_crypto_patch/contents/acme/rfc8555.go
  • local_crypto_patch/contents/acme/rfc8555_test.go
  • local_crypto_patch/contents/acme/types.go
  • local_crypto_patch/contents/acme/types_test.go
  • local_crypto_patch/contents/argon2/argon2.go
  • local_crypto_patch/contents/argon2/argon2_test.go
  • local_crypto_patch/contents/argon2/blake2b.go
  • local_crypto_patch/contents/argon2/blamka_amd64.go
  • local_crypto_patch/contents/argon2/blamka_amd64.s
  • local_crypto_patch/contents/argon2/blamka_generic.go
  • local_crypto_patch/contents/argon2/blamka_ref.go
  • local_crypto_patch/contents/bcrypt/base64.go
  • local_crypto_patch/contents/bcrypt/bcrypt.go
  • local_crypto_patch/contents/bcrypt/bcrypt_test.go
  • local_crypto_patch/contents/blake2b/blake2b.go
  • local_crypto_patch/contents/blake2b/blake2bAVX2_amd64.go
  • local_crypto_patch/contents/blake2b/blake2bAVX2_amd64.s
  • local_crypto_patch/contents/blake2b/blake2b_amd64.s
  • local_crypto_patch/contents/blake2b/blake2b_generic.go
  • local_crypto_patch/contents/blake2b/blake2b_ref.go
  • local_crypto_patch/contents/blake2b/blake2b_test.go
  • local_crypto_patch/contents/blake2b/blake2x.go
  • local_crypto_patch/contents/blake2b/register.go
  • local_crypto_patch/contents/blake2s/blake2s.go
  • local_crypto_patch/contents/blake2s/blake2s_386.go
  • local_crypto_patch/contents/blake2s/blake2s_386.s
  • local_crypto_patch/contents/blake2s/blake2s_amd64.go
  • local_crypto_patch/contents/blake2s/blake2s_amd64.s
  • local_crypto_patch/contents/blake2s/blake2s_generic.go
  • local_crypto_patch/contents/blake2s/blake2s_ref.go
  • local_crypto_patch/contents/blake2s/blake2s_test.go
  • local_crypto_patch/contents/blake2s/blake2x.go
  • local_crypto_patch/contents/blowfish/block.go
  • local_crypto_patch/contents/blowfish/blowfish_test.go
  • local_crypto_patch/contents/blowfish/cipher.go
  • local_crypto_patch/contents/blowfish/const.go
  • local_crypto_patch/contents/bn256/bn256.go
  • local_crypto_patch/contents/bn256/bn256_test.go
  • local_crypto_patch/contents/bn256/constants.go
  • local_crypto_patch/contents/bn256/curve.go
  • local_crypto_patch/contents/bn256/example_test.go
  • local_crypto_patch/contents/bn256/gfp12.go
  • local_crypto_patch/contents/bn256/gfp2.go
  • local_crypto_patch/contents/bn256/gfp6.go
  • local_crypto_patch/contents/bn256/optate.go
  • local_crypto_patch/contents/bn256/twist.go
  • local_crypto_patch/contents/cast5/cast5.go
  • local_crypto_patch/contents/cast5/cast5_test.go
  • local_crypto_patch/contents/chacha20/chacha_arm64.go
  • local_crypto_patch/contents/chacha20/chacha_arm64.s
  • local_crypto_patch/contents/chacha20/chacha_generic.go
  • local_crypto_patch/contents/chacha20/chacha_noasm.go
  • local_crypto_patch/contents/chacha20/chacha_ppc64x.go
  • local_crypto_patch/contents/chacha20/chacha_ppc64x.s
  • local_crypto_patch/contents/chacha20/chacha_s390x.go
  • local_crypto_patch/contents/chacha20/chacha_s390x.s
  • local_crypto_patch/contents/chacha20/chacha_test.go
  • local_crypto_patch/contents/chacha20/vectors_test.go
  • local_crypto_patch/contents/chacha20/xor.go
  • local_crypto_patch/contents/chacha20poly1305/chacha20poly1305.go
  • local_crypto_patch/contents/chacha20poly1305/chacha20poly1305_amd64.go
  • local_crypto_patch/contents/chacha20poly1305/chacha20poly1305_amd64.s
  • local_crypto_patch/contents/chacha20poly1305/chacha20poly1305_generic.go
  • local_crypto_patch/contents/chacha20poly1305/chacha20poly1305_noasm.go
  • local_crypto_patch/contents/chacha20poly1305/chacha20poly1305_test.go
  • local_crypto_patch/contents/chacha20poly1305/chacha20poly1305_vectors_test.go
  • local_crypto_patch/contents/chacha20poly1305/fips140only_compat.go
  • local_crypto_patch/contents/chacha20poly1305/fips140only_go1.26.go
  • local_crypto_patch/contents/chacha20poly1305/xchacha20poly1305.go
  • local_crypto_patch/contents/codereview.cfg
  • local_crypto_patch/contents/cryptobyte/asn1.go
  • local_crypto_patch/contents/cryptobyte/asn1/asn1.go
  • local_crypto_patch/contents/cryptobyte/asn1_test.go
  • local_crypto_patch/contents/cryptobyte/builder.go
  • local_crypto_patch/contents/cryptobyte/cryptobyte_test.go
  • local_crypto_patch/contents/cryptobyte/example_test.go
  • local_crypto_patch/contents/cryptobyte/string.go
  • local_crypto_patch/contents/curve25519/curve25519.go
  • local_crypto_patch/contents/curve25519/curve25519_test.go
  • local_crypto_patch/contents/curve25519/vectors_test.go
  • local_crypto_patch/contents/ed25519/ed25519.go
  • local_crypto_patch/contents/ed25519/ed25519_test.go
  • local_crypto_patch/contents/go.mod
  • local_crypto_patch/contents/hkdf/example_test.go
  • local_crypto_patch/contents/hkdf/hkdf.go
  • local_crypto_patch/contents/hkdf/hkdf_test.go
  • local_crypto_patch/contents/internal/alias/alias.go
  • local_crypto_patch/contents/internal/alias/alias_purego.go
  • local_crypto_patch/contents/internal/alias/alias_test.go
  • local_crypto_patch/contents/internal/poly1305/mac_noasm.go
  • local_crypto_patch/contents/internal/poly1305/poly1305.go
  • local_crypto_patch/contents/internal/poly1305/poly1305_test.go
  • local_crypto_patch/contents/internal/poly1305/sum_amd64.s
  • local_crypto_patch/contents/internal/poly1305/sum_asm.go

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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.

3 participants