Commit Graph
3960 Commits
Author SHA1 Message Date
Tam Nhu Tran 39ec84a8a9 fix(ui): only show "Copied" feedback when clipboard write succeeds
PR-Agent re-review flagged that `copyText()` swallowed clipboard
errors but the row's `setJustCopied(true)` always fired regardless,
so users in insecure contexts (or with denied clipboard permission)
saw a fake success indicator while nothing was actually copied.

Make `copyText()` return a boolean and gate the "Copied" UI state
on it. When the clipboard write fails, the button stays in its idle
state and the user can retry rather than being misled.

Refs #1138, #1151
2026-04-30 15:37:50 -04:00
Tam Nhu Tran 75cdc02c1f fix(ui): include source in trace child coalesce key
PR-Agent re-review flagged that adjacent trace rows from different
services with the same event/message/stage/level/module would still
merge because `source` was missing from the coalesce key. A request
that fans out across multiple services would lose the second service's
row behind a `× 2` badge.

Add `source` to the trace child coalesce key so each participating
service stays visible in the trace timeline.

Refs #1138, #1151
2026-04-30 15:37:21 -04:00
Tam Nhu Tran 6d266fc7e8 fix: remove raw write re-exports from facade (cache bypass)
PR-Agent #1150 review flagged that re-exporting
saveUnifiedConfig/mutateUnifiedConfig/updateUnifiedConfig
allows callers to bypass the cache. Only export the
cache-coherent wrappers (saveConfig/mutateConfig/updateConfig).
Raw functions still available via direct import from
unified-config-loader if needed.

- Remove raw write re-exports from facade
- Add test verifying raw writes are NOT exported
- Add test verifying cache-coherent wrappers ARE exported
2026-04-30 15:37:17 -04:00
Tam Nhu Tran b7cfbd14e2 fix(ui): make logs row keyboard-accessible without nested interactive markup
PR-Agent flagged that the row's copy-requestId widget was a
`<span role="button" tabIndex={-1}>` nested inside a row `<button>`.
Two issues:
1. Nested interactive elements inside a `<button>` is invalid HTML.
2. `tabIndex={-1}` made the copy widget unreachable via keyboard, so
   the new affordance was effectively mouse-only.

Convert the row's outer element from `<button>` to `<div role="row"
tabIndex={0}>` with `onKeyDown` for Enter/Space → select. Now the row
itself is keyboard-focusable and the copy widget can be a real
`<button>` with focus-visible styling — keyboard users can Tab to it
and Enter to copy the requestId.

Refs #1138, #1151
2026-04-30 15:26:29 -04:00
Tam Nhu Tran a465e3fec1 fix(ui): include message in coalesce key so distinct entries stay visible
PR-Agent flagged that the leaf and trace-child coalesce keys collapsed
on `(event, module, level, requestId, source)` which merges adjacent
entries that share that tuple but report different content (e.g.
`User logged in: alice` vs `User logged in: bob` would appear as a
single `× 2` row, hiding bob).

Add `message` to both coalesce keys so distinct content stays
distinct. Skip `latencyMs` and `metadata` — those drift per request
even on truly redundant polls and would defeat the dedup the feature
exists for.

Refs #1138, #1151
2026-04-30 15:26:06 -04:00
Tam Nhu Tran b8ed36e370 fix: round 3 red-team — test false-positive, dead code,
edge-case coverage

- Fix false-positive test: use CCSError with recoverable=false
  instead of plain Error (never exercised recoverable check)
- Remove redundant retryableCheck ?? defaultRetryableCheck
  (destructuring already defaults)
- Add test: retryAfter > maxDelayMs (server directive wins)
- Add test: baseDelayMs=0 produces immediate retries
- Add test: onRetry not called when maxRetries=0
2026-04-30 15:16:07 -04:00
Tam Nhu Tran 18e865ea36 fix: round 2 red-team fixes — onRetry safety, validation, barrel
- retry-strategy: wrap onRetry in try/catch to prevent
  callback errors from aborting retries
- retry-strategy: validate baseDelayMs >= 0
- retry-strategy: update JSDoc to clarify
  retryAfter/maxDelayMs interaction
- errors/index.ts: add ValidationError to barrel
- Tests: onRetry throw test, negative baseDelayMs test
2026-04-30 14:59:44 -04:00
Tam Nhu Tran 0dfb7b8583 fix(ui): reserve leading chevron column so trace and leaf rows align
Trace rows pushed the TIME column ~30px right because the expand chevron
was an inline flex child while leaf rows had nothing in that position.
Result: every column drifted right whenever a trace row was visible.

Reserve a 16px slot at the start of every row (header + leaf + trace).
Leaf rows render an empty span; trace rows render the chevron centered
inside the slot. All columns now share the same x-coordinates regardless
of row type.

Refs #1138
2026-04-30 14:56:32 -04:00
Tam Nhu Tran 9bb1bdbad9 fix: address red-team review findings — cache aliasing, jitter cap, cause shadowing
- config-loader-facade: use structuredClone() to prevent cache aliasing
- retry-strategy: re-cap delay after jitter to enforce maxDelayMs boundary
- retry-strategy: wire retryAfter from RetryableError into delay computation
- retry-strategy: guard against negative maxRetries
- error-types: rename RetryableError.cause to originalError to avoid shadowing Error.cause
- Tests updated for all fixes
2026-04-30 14:37:28 -04:00
Tam Nhu Tran 2d95e40ff9 chore(pr): pin force-UI evidence directory to PR #1151
Refs #1138
2026-04-30 14:33:50 -04:00
Tam Nhu Tran 2c0bbae943 chore(pr): add force-UI evidence for #1138 final-touches audit fixes
Light-theme captures from Playwright MCP against `bun run dev` with
`?mock=logs` deterministic fixture and real backend (no mock):

1. `01-after-overview-mock.png` — LOGS.STREAM marker, Live activity
   title, ENTRIES/TRACES/ERRORS stat strip, populated detail panel
2. `02-after-real-backend-clean.png` — real backend with default filter
   active: 0 entries because all are dashboard internals; no noise
3. `03-hide-internals-toggle.png` — advanced filters showing the new
   labelled toggle with red callout
4. `04-internals-shown-with-coalesce.png` — toggle off: dashboard
   internals visible but a 149-stage trace coalesces to 3 rows; detail
   panel shows full requestId UUID + module (C1 fix proven)
5. `05-after-mobile.png` — 390x844: header marker + title + stat strip
   wrap, expanded trace renders cleanly

Asset directory will be renamed from PLACEHOLDER_PR to actual PR number;
HEAD SHA placeholder pinned after final commit.

Refs #1138, #1141, #1142
2026-04-30 14:31:57 -04:00
Tam Nhu Tran 4a77251021 feat(ui): hide dashboard self-polling internals via default filter
On real backend, the logs page was drowning in dashboard self-polling
noise — every `/api/logs` poll emitted a 149-stage `web-server:http`
trace, burying real provider activity.

- New workspace state `hideDashboardInternals` defaults ON. Entries
  whose `source` matches `/^web-server:/i` are filtered client-side
  before they reach the list.
- Toggle exposed in advanced filters as a labelled checkbox/switch with
  a one-line description, so users can opt in to see internals when
  debugging the dashboard itself.
- `clearAdvancedFilters` resets the toggle back to ON to keep the
  default signal-clean experience.
- Header stat strip (entries / traces / errors) wired through the shell.

Backend-side `web-server:*` instrumentation stays intact (still useful
for ops debugging) — just hidden from the user-facing log feed by
default.

Refs #1138, #1141, #1142
2026-04-30 14:31:32 -04:00
Tam Nhu Tran e04598eef7 feat(ui): align logs page visual language with dashboard markers
Logs page was visually disconnected from the rest of the dashboard. Health
uses ornamental `HEALTH.ATTENTIONREQUIRED` markers, Home uses `LIVE
Account Monitor` + stat cards. Logs shipped with a 14px h1 + clinical
table that felt like a different product.

- Header now opens with a `LOGS.STREAM` mono-uppercase marker matching
  the dashboard's `HEALTH.X` style, plus a 16px "Live activity" title
  and a status pill.
- Below the header, a stat strip mirrors the home page's monitor layout:
  ENTRIES / TRACES / ERRORS counters, errors highlighted red when >0.
- Trace row + child row font sizes lifted from 11px to 12-13px;
  request-id column widened to 112px to match the standalone-row table.
- Stage-hint fallback derived from event names so the trace timeline
  still renders meaningful chips when backend entries lack an explicit
  `stage` field (e.g. dashboard self-polling).
- Intra-trace coalesce: identical consecutive child rows collapse to a
  single row with `× N` badge so a 149-stage self-poll trace renders as
  3 rows of signal instead of 149 rows of noise.

Refs #1138, #1141, #1142
2026-04-30 14:30:54 -04:00
Tam Nhu Tran cb8b34b36d Merge branch 'kai/refactor/1135-item2-config-loader-facade' into kai/refactor/1135-structural-maintainability 2026-04-30 14:10:42 -04:00
Tam Nhu Tran b5fae5e8c9 Merge branch 'kai/refactor/1135-item9-error-hierarchy-retry' into kai/refactor/1135-structural-maintainability 2026-04-30 14:10:30 -04:00
Tam Nhu Tran b7aea78512 Revert "Revert "refactor(config): reorganize unified-config-types into schemas directory""
This reverts commit 06bce198eb.
2026-04-30 14:07:32 -04:00
Tam Nhu Tran 0868e92bb1 Revert "Revert "refactor(config): reorganize unified-config-types into schemas directory""
This reverts commit 06bce198eb.
2026-04-30 14:05:33 -04:00
Tam Nhu Tran 1e9a7f3fa0 refactor(config): add config-loader-facade with memoization
Single import path for all config loading. Re-exports all 26 functions
from unified-config-loader and 4 from config-manager. Adds memoization
for loadOrCreateUnifiedConfig via getCachedConfig() with automatic cache
invalidation on write operations (mutateConfig, updateConfig). Pure
structural refactor -- no existing imports modified.
2026-04-30 14:03:48 -04:00
Tam Nhu Tran 06bce198eb Revert "refactor(config): reorganize unified-config-types into schemas directory"
This reverts commit 51df0ee55b.
2026-04-30 13:57:08 -04:00
Tam Nhu Tran 51df0ee55b refactor(config): reorganize unified-config-types into schemas directory
Split the 1,128-line unified-config-types.ts into focused schema modules
under src/config/schemas/ for maintainability. Each file is under 200 LOC.

New schema files:
- version.ts: UNIFIED_CONFIG_VERSION constant
- auth.ts: AccountConfig, ProfileConfig, OAuthAccounts, CLIProxyAuthConfig, etc.
- cliproxy.ts: CLIProxyConfig, CompositeTierConfig, routing/safety types
- copilot-cursor.ts: CopilotConfig, CursorConfig + defaults
- proxy-server.ts: CliproxyServerConfig, GlobalEnvConfig, ImageAnalysisConfig
- quota.ts: QuotaManagementConfig + all quota types and defaults
- thinking.ts: ThinkingConfig + tier defaults
- channels.ts: OfficialChannelsConfig (Telegram, Discord, iMessage)
- websearch.ts: All WebSearch backend types (DuckDuckGo, Brave, Exa, etc.)
- browser.ts: BrowserConfig, BrowserClaudeConfig, BrowserCodexConfig
- logging.ts: LoggingConfig, PreferencesConfig
- unified-config.ts: UnifiedConfig interface, factory, type guard
- index.ts: Barrel re-export of all schema modules

unified-config-types.ts is now a thin backward-compatible barrel that
re-exports everything from schemas/index. All 67 existing imports across
the codebase continue to resolve unchanged.
2026-04-30 13:54:40 -04:00
Tam Nhu Tran d089ab06c2 refactor(errors): add RetryableError and retry-strategy utility
Extract retryable error class and reusable withRetry wrapper from
scattered retry logic in glmt-proxy and binary/downloader.
2026-04-30 13:46:33 -04:00
Tam Nhu Tran d5b14bec1c fix(ui): align logs row and detail panel via shared accessors
Both surfaces now consume `getDisplayModule / getDisplayStage /
getDisplayRequestId / getDisplayLatency` from `utils.ts`, replacing the
previous divergence where the list row fell back to `source` while the
detail panel showed em-dash placeholders for the same entry.

Also widens the request-id column in the row table from 88px to 112px
and adds a hover-revealed clipboard icon for one-click copy of the full
requestId. List header bumped to 12px sans-serif uppercase + 9h height
for legibility.

Refs #1138, #1141, #1142
2026-04-30 13:37:02 -04:00
Kai (Tam Nhu) Tran b892739756 Merge pull request #1146 from kaitranntt/kai/feat/1138-logs-ui-redesign
feat(ui): redesign dashboard logs page (#1142)
2026-04-30 13:14:47 -04:00
Kai (Tam Nhu) Tran 04901a1642 Merge pull request #1145 from kaitranntt/kai/feat/1138-logging-core-instrumentation
feat(logging): structured contract and repo-wide instrumentation (#1141)
2026-04-30 13:14:36 -04:00
Tam Nhu Tran fde0b16807 chore(pr): pin force-UI evidence to PR #1146 and HEAD SHA
Rename .github/pr-assets/PLACEHOLDER_PR -> .github/pr-assets/1146 and
substitute {HEAD_SHA_PLACEHOLDER} with the current HEAD so commit-pinned
raw GitHub URLs in the HTML report resolve correctly.

The pinned SHA in this commit is the parent commit; once this commit is
pushed, the PR body references will resolve via the htmlpreview.github.io
proxy against the parent SHA. Reviewers see frozen-in-time evidence.

Refs #1142, #1138
2026-04-30 13:07:04 -04:00
Tam Nhu Tran 1c814787a6 chore(pr): add force-UI evidence assets for #1142 logs UI redesign
Self-contained HTML report + 7 light-theme PNG captures with DOM-overlay
red callouts (#ef4444, 3px, 8px pad). Captured via Playwright MCP against
local 'bun run dev' instance with ?mock=logs deterministic 150-entry
fixture.

Surfaces:
1. Overview - default state, callouts on Live pill + ? button
2. Trace grouping - expanded vs collapsed states
3. Live-tail paused (Resume tail button)
4. Advanced filters expanded
5. Empty state - filtered-to-nothing + active filter chip
6. Keyboard shortcuts dialog
7. Mobile responsive (390x844)

Targeted-mode evidence; the 'before' UI is captured in the parent issue's
contributor screenshot. Asset directory will be renamed from PLACEHOLDER_PR
to actual PR number; HTML and asset URLs use {HEAD_SHA_PLACEHOLDER}
tokens that the maintainer pins after final commit.

Refs #1142, #1138
2026-04-30 13:05:02 -04:00
Tam Nhu Tran a10e570310 test(ui): cover logs page redesign with virtuoso-mocked render
Three component tests under jsdom: skeleton render, search filter wiring,
row-click -> detail-panel-header flow. react-virtuoso is mocked at module
level (vi.mock) so jsdom can render rows without layout measurements.
Production behavior unchanged.

Refs #1142, #1138
2026-04-30 13:04:21 -04:00
github-actions[bot] a68a3aee66 chore(release): 7.75.0-dev.8 2026-04-30 13:04:09 -04:00
Tam Nhu Tran ae3240d4c8 feat(ui): redesign dashboard logs page with virtualized 3-pane shell
Replace the ornamental logs page (radial-gradient overlays, faux-HUD
captions, scale-on-click animations, two competing tab shells, zero
virtualization) with a calm, accessible, performant surface.

- Calm 48px header + tabs + 3-pane shell (filters | list | detail)
- List virtualized via react-virtuoso; trace-grouped rows by requestId
  with expandable per-stage timeline and per-stage latencyMs
- Filters split into primary (search / level / source) + advanced
  (module / stage / requestId / time-window) with 250ms debounce and
  in-flight react-query cancellation on filter change
- Live-tail pause / resume with rotation-safe id-set diff for the
  new-entries pill; visibility-aware polling
- Detail panel: Overview / Context / Raw tabs; Copy JSON, Copy requestId,
  Show-trace jump; redaction-aware rendering
- Designed empty / loading / error states (4 empty variants, retry on error)
- Keyboard nav (j / k / Enter / Esc / / / Space / ?) with visible focus
  rings; reduced-motion respected; AA-tuned level palette
- New header button for keyboard-shortcuts discoverability (alongside ?
  keypress) -- pairs with the shortcuts dialog
- Optional fields added to LogsEntry to consume the #1141 contract; UI
  ships against fixture (?mock=logs URL flag) so backend isn't a hard
  prerequisite. Dev-only assertLogsEntryShape warns once on drift
- Deleted dead logs-overview-cards.tsx (was unmounted)

Closes #1142
Refs #1138, #1141
2026-04-30 13:03:47 -04:00
Tam Nhu Tran c0c856af98 docs(logging): add structured contract reference and bump Node engines to 18+
docs/logging-contract.md defines the canonical LogEntry shape, the 8
lifecycle stages, requestId propagation rules, redaction policy, and the
backend ordering guarantee (per-requestId monotonic ts at emit time) that
the dashboard logs UI consumes.

CLAUDE.md updated: Node.js 14+ -> 18+ (AsyncLocalStorage is more stable on
18+ across timer / microtask / dynamic-import boundaries).

Refs #1141, #1138
2026-04-30 13:01:21 -04:00
Tam Nhu Tran c0a019bf7e test(logging): add cross-stage requestId correlation integration test in slow bucket
Verifies that a single requestId propagates across at least 3 lifecycle
stages emitted from different modules, and that ALS context survives across
async boundaries (setImmediate, microtask, promise.then). Registered in
test:slow bucket per validate:ci-parity gate.

Refs #1141, #1138
2026-04-30 13:01:05 -04:00
Tam Nhu Tran 8afee344e1 feat(targets,management): instrument adapter spawn lifecycle and instance manager
Each target adapter (claude, codex, droid) now emits dispatch on child
spawn and respond on child exit with latencyMs. instance-manager emits
lifecycle events for instance create / start / stop so multi-instance flows
are traceable per requestId.

Refs #1141, #1138
2026-04-30 13:00:49 -04:00
Tam Nhu Tran 1ff4329ee4 feat(providers): instrument copilot, cursor, and glmt across daemons and executors
Provider modules now emit structured stage tags around daemon spawn /
ready / stop, executor invocations, and upstream dispatch. glmt-transformer
gets cleanup-stage error conversion; legacy glmt-proxy adds minimal
listen + retry instrumentation (full per-request stages live in proxy-server
since glmt-proxy is compat-only).

Refs #1141, #1138
2026-04-30 13:00:33 -04:00
Tam Nhu Tran bf8759460d feat(auth,commands): instrument oauth handler, profile registry, and doctor pipeline
Emit auth-stage events around OAuth round-trips and profile lookups so auth
flows are traceable end-to-end. Doctor command emits dispatch stages around
each health-check phase for clearer diagnostic logs.

Refs #1141, #1138
2026-04-30 13:00:16 -04:00
Tam Nhu Tran 4700727915 feat(proxy,cli): emit lifecycle stages with x-ccs-request-id propagation
Wrap proxy server entry edge in withRequestContext so every inbound request
gets a requestId (reused from x-ccs-request-id header when valid UUID-ish,
freshly minted otherwise). messages-route emits 7 stages: intake / auth /
transform / route / dispatch / upstream / respond, each with latencyMs and
structured error metadata on failure.

CCS CLI entry (ccs.ts) wraps main() in runWithRequestId and emits
cli.command.start / complete / failed stages so command lifecycle is
correlatable end-to-end.

Refs #1141, #1138
2026-04-30 13:00:00 -04:00
Kai (Tam Nhu) Tran 9ed6107c8f Merge pull request #1144 from kaitranntt/kai/fix/1135-colocated-test-bucket
test: include colocated cliproxy tests in buckets
2026-04-30 12:59:45 -04:00
Tam Nhu Tran 7a22e89046 feat(logging): add structured contract, ALS context, stage helpers, and redaction coverage
Establish the structured-log foundation that downstream modules consume:
- LogStage union (intake|route|auth|dispatch|upstream|transform|respond|cleanup)
- AsyncLocalStorage-backed log-context for end-to-end requestId propagation
- Logger.stage() emit helper with optional latencyMs and structured metadata
- Extended log-redaction patterns for proxy-authorization, x-goog-api-key,
  oauth-code, refresh-token classes; Bearer/Basic/Token value-shape masking
- Public exports: getRecentLogEntries, withRequestContext, runWithRequestId,
  getRequestId

Refs #1141, #1138
2026-04-30 12:59:42 -04:00
Tam Nhu Tran 33a1553431 test: include colocated cliproxy tests in buckets 2026-04-30 12:49:57 -04:00
github-actions[bot] de6bdde66b chore(release): 7.75.0-dev.7 2026-04-30 12:48:31 -04:00
Kai (Tam Nhu) Tran c9dcca887d Merge pull request #1143 from kaitranntt/kai/fix/1132-openai-reasoning-chat-payload
fix(proxy): shape direct OpenAI reasoning chat payloads
2026-04-30 12:44:58 -04:00
Tam Nhu Tran d9ace607e5 fix(proxy): shape direct OpenAI reasoning chat payloads 2026-04-30 12:33:02 -04:00
github-actions[bot] 65e10b3473 chore(release): 7.75.0-dev.6 2026-04-30 11:01:45 -04:00
Kai (Tam Nhu) Tran 54e15c430e Merge pull request #1140 from kaitranntt/kai/feat/1139-claude-quota-bars
feat(ui): clarify Claude quota bars
2026-04-30 10:58:11 -04:00
Tam Nhu Tran 476c6d827f fix(ui): improve split quota bar readability 2026-04-30 10:32:43 -04:00
github-actions[bot] e43f84575a chore(release): 7.75.0-dev.5 2026-04-30 10:09:31 -04:00
Tam Nhu Tran a8b686d643 feat(ui): show split Claude quota bars 2026-04-30 10:05:19 -04:00
Kai (Tam Nhu) Tran fc6bf24510 Merge pull request #1137 from kaitranntt/kai/refactor/1135-structural-maintainability
refactor(cliproxy): flatten module structure and colocate tests
2026-04-30 10:05:05 -04:00
Tam Nhu Tran 20c2005df9 fix(cliproxy): clear stale cleanup timer and fix session/tunnel edge cases
- Stop cleanup interval when all auth sessions are removed (unregister,
  cancel, cancelAll) so the Node event loop isn't held alive.
- getActiveSessionForProvider now returns the most recent session by
  startedAt instead of the first map entry.
- HTTPS tunnel includes port in Host header when non-443.
2026-04-29 18:13:15 -04:00
Tam Nhu Tran 6633bf456b fix(cliproxy): handle all invalid prefixes and reverse-order collisions
forceValidChars() now ensures leading char is letter or underscore
(catches dot, hyphen, colon, slash — not just digits). registerTools()
always registers in mapping so collisions are detected regardless of
tool registration order.
2026-04-29 18:04:19 -04:00
Tam Nhu Tran 218d0cf6c6 fix(cliproxy): sanitize invalid tool name chars and disambiguate collisions
sanitizeToolName() fallback now forces valid characters (replaces
unsupported chars with underscores, ensures leading letter/underscore)
instead of only truncating. registerTools() disambiguates colliding
sanitized names with numeric suffixes so restoreToolUse() always maps
back to the correct original.
2026-04-29 17:57:11 -04:00