Aligns the documented coverage gate with the v3.8.6 release decision
(lowered from 75/75/75/70). Matches the threshold already set in
package.json by the large feature PRs (planos 11-22).
These files were dirty in the worktree at session start and survived all
R3/R4/R5 fix work untouched — they belong to the review-discussions skill
and are independent of the AgentBridge/Traffic Inspector implementation.
Committing here so the worktree is clean for removal.
In startMitm(), read AGENTBRIDGE_UPSTREAM_CA_CERT (env wins over stored path in
<dataDir>/mitm/upstream-ca.path) and call configureUpstreamCa() at process start;
failures are caught and logged — boot continues without custom CA. In the POST
/api/tools/agent-bridge/upstream-ca handler, call configureUpstreamCa() immediately
after persisting the new path so the CA takes effect without reboot; throws → 400
with sanitizeErrorMessage (Hard Rule #12). New test file
tests/unit/mitm-upstream-ca-wiring.test.ts validates the path-selection logic and
the route wiring (8 tests, 0 failures).
Third code-review pass on plan 21 found two follow-up issues from the
previous round.
1. extractLastUserText accepted Responses API items with role===undefined
regardless of their type. function_call_output / tool_call_output /
reasoning items would slip through and be treated as user query input,
leaking the tool's reply or the model's chain of thought into the
memory retrieval query.
Fix: when role is undefined, skip items whose type is in a denylist of
non-user item types (function_call, function_call_output, tool_call,
tool_call_output, reasoning, computer_call, computer_call_output,
web_search_call, file_search_call). Also reject non-text content
parts inside multi-modal arrays (image_url, tool_use, ...) so that
image-only or tool-only user messages do not produce a query made of
irrelevant fragments.
2. MemoryEngineStatus introduced t("engine.configureCta") in round 2 but
the key was never added to en.json / pt-BR.json — even with the new
EN fallback merger, the CTA would render the literal key path.
Added "Configure" / "Configurar" to both locales.
Verified: typecheck:core clean; vitest UI 46/46; cli-memory-commands,
memory-settings, and mcp-memory-tools-strategy isolated sanity all
green; grep audit of memory.* i18n keys used by the UI confirms zero
missing keys in en.json.
Replaces 15 raw console.log/console.error calls in src/mitm/manager.ts with
structured pino logger calls via createLogger("mitm-manager"), aligning with
the project convention documented in docs/architecture/RESILIENCE_GUIDE.md.
Multi-arg console calls are converted to pino object form: logger.info({ x, y }, "msg").
Smaller fixes from the 2nd code-review pass on plan 21.
Backend / CLI / DB:
- memoryTools.ts: error-path fallback no longer hardcodes
retrievalStrategy:"exact"; uses DEFAULT_MEMORY_SETTINGS via
toMemoryRetrievalConfig (D16 / Bug #7).
- memory.mjs: applyLegacyTypeMap also runs on search / list / clear
(was only on add); legacy user/feedback/project/reference remap to
canonical types with a stderr warning (D17 / Bug #4).
- migrationRunner.ts: case "073" guards via
hasColumn(memories, needs_reindex) so an unmarked re-run of
073_memory_vec.sql is skipped cleanly (D27).
UI:
- MemoryEngineStatus: optional onConfigure callback; "Configurar →"
CTAs on the Embedding / Qdrant / Rerank rows when those components
are off or missing (matches §4.3 wireframe).
- EngineTab: scroll IDs on config cards + handleConfigure wired to
the status panel. Providers fetch moved from render body
(setState-during-render anti-pattern) into useEffect.
- RerankConfigCard: toggle is disabled when no provider has a key —
blocks turning rerank ON without a provider, still allows turning
it OFF (D13).
- MemoriesTab: Import validates each entry against the canonical
type enum before POST so invalid types are caught locally with a
clear skipped count.
Tooling:
- package.json: test:all includes test:vitest:ui so the UI suite
is no longer orphaned in CI.
Tests:
- cli-memory-commands: asserts updated for the new legacy->canonical
remap on search/clear.
- memory-embedding-resolve: drop always-true `|| reason.length > 0`
clauses that neutralized two assertions.
- memory-embedding-static-potion: model_load_failed test forces a
real load failure via MEMORY_STATIC_CACHE_DIR=/dev/null/<subdir>
and asserts EmbeddingError shape + reason + sanitized message
(replaces the previous `assert.ok(true)`).
- rerank-config-card.test.tsx: happy-path now uses a provider with
hasKey; new test covers the disabled-toggle guard.
Full memory suite green: 331/331 unit tests, 46/46 UI tests.
typecheck:core, typecheck:noimplicit:core, check:cycles clean.
D12 of master-plan-21 assumed next-intl had a built-in fallback to EN
already configured. It did not — request.ts loaded only
messages/${locale}.json and no getMessageFallback was defined, so any
key absent in the user's locale rendered the key path literally
(for example "memory.concept.title").
Plan 21 added 156 memory.* keys to en and pt-BR but the other 39
locales kept only the pre-existing 36 memory.* keys, so users on those
locales saw raw key paths across the new Memory studio.
Fix: load en.json as the base and deep-merge the locale-specific
messages on top. Existing translations are untouched; only missing
keys fall back to English. Satisfies §7 "i18n 41 locales".
Code review of plan 21 found two functional gaps:
FAIL #1 — toMemoryRetrievalConfig never forwarded the user query, so the
gate `if (config.query && useModernTable)` in retrieval.ts was always
false in the chat hot path. semantic/hybrid silently fell back to
"ORDER BY created_at DESC LIMIT 100", the pre-plan-21 behaviour.
sqlite-vec + RRF only ran in the Playground (retrievePreview, which
takes `query` positionally).
FAIL #2 — Bug #1 was not closed: searchSemanticMemory (Qdrant) was only
imported by /api/settings/qdrant/search, never by retrieval. With
vectorStore="qdrant", engineStatus reported backend="qdrant" but
retrieveMemories/retrievePreview kept using sqlite-vec — the status
diverged from the actual search path.
- settings.ts: toMemoryRetrievalConfig(settings, { query? }) accepts
and forwards the query.
- chatCore.ts: extracts the last user message from body.messages or
body.input (Chat + Responses APIs).
- retrieval.ts: adds a Qdrant branch in case "semantic", case "hybrid"
and retrievePreview; falls through to sqlite-vec on failure or empty
results so the §7 "degrades to sqlite-vec / FTS5" contract holds.
- engineStatus only reports backend="qdrant" when the user opted in
(settings.vectorStore === "qdrant") and Qdrant is healthy.
- memories[] tier union includes "qdrant".
331 memory unit tests pass; typecheck:core / typecheck:noimplicit:core /
check:cycles clean.
Add src/mitm/inspector/pricing.ts with a 10-entry USD/1M-token table and
estimateCost() helper. Wire it into extractLlmMetadata() replacing the
hardcoded null. Tests: 11 new in inspector-pricing.test.ts + 4 new
assertions in inspector-llm-metadata.test.ts (25 pass total).
recordRequestStart() now performs a cheap DB lookup (isCustomHost) before
building the InterceptedRequest. Hosts registered in inspector_custom_hosts
with enabled=1 receive source="custom-host" and agent=undefined, so they
appear correctly under the "Custom" profile filter instead of being
silently routed to "agent-bridge" entries.
Added isCustomHost() helper to inspectorCustomHosts.ts and a unit test
covering enabled custom-host, non-custom host, and disabled custom-host cases.
Wires the previously-dead appendSessionRequest() DB function to a new
POST /api/tools/traffic-inspector/sessions/[id]/requests route.
appendSessionRequest now returns the inserted seq so callers can confirm order.
InspectorSessionRequestAppendSchema (1 MB cap) guards the endpoint.
Integration test covers seq increments 1-2-3, requestCount sync, 400/404 paths,
and stack-trace-free error responses.
3 polish items from R4 acceptance audit (operator-approved scope):
- P1: BatchListTab status filter dropdown now renders translated labels (t("batchStatusInProgress") etc.) instead of raw snake_case ("in_progress", "cancelling"). STATUS_LABELS refactored to STATUS_LABEL_KEYS — a single map from raw/composite status → i18n key — so StatusBadge and the dropdown share one source of truth. Falls back to snake→space transform for unknown statuses.
- P2: 18 hardcoded English strings replaced by t() calls.
BatchListTab: title ("Batches"), count "{count} batches" (ICU placeholder), Removing…/Remove completed, 6 table headers (Status/ID/Endpoint/Model/Progress/Created/Expires), Loading…, No batches found, Validating… progress cell.
CostEstimateStep: Estimating cost…, Requests, input tok, output tok, Window.
DestinationStep: Select a provider…, Select a model…, Connect a provider.
- P3: ExpirationBadge — added <span class="sr-only">{label}:</span> in both compact and default variants so colorblind users and screen-readers get the urgency tier (Critical/Soon/Pending) instead of color-only signaling. The visual is unchanged (compact still shows just the time string).
Tests: list-regression #2 + #3 updated to look for the i18n key literal "batchListRemoveCompleted" (mock t() returns keys) instead of the now-translated "Remove completed" string. All 20 list-regression tests pass.
35 new i18n keys (14 status labels — 9 raw + 5 _with_failures composites — + 14 BatchListTab + 4 CostEstimateStep + 3 DestinationStep) added in en.json + pt-BR.json and propagated to 40 locales via fill-missing-from-en.mjs.
Note on R4 finding C1 (auditor claimed Hard Rule #9 violation from the 75→40 coverage gate drop): false positive. The audit compared CLAUDE.md in the worktree (branch refactor/pages-v3-20-... reflecting the new gate of 40, since operator explicitly requested it: "pode baixar os testes para 40/40/40") against CLAUDE.md in the repo root (branch release/v3.8.6, still at 75 because the PR has not landed yet). Same file, different branches — expected intermediate state for an active PR. Actual measured coverage remains ~77% (well above the 40 gate), so the gate change is a sanctioned threshold relaxation, not a masking workaround.
3 fixes from independent round-3 code review:
- R1: Exclude BUTTON from the wizard global Enter handler. With Back/Cancel/Create focused, the browser already activates the focused button on Enter; a global Next dispatch on top would conflict (Back focused + Enter → both back and next dispatched in the same tick, last-write-wins is indeterminate). Now Enter only fires the Next dispatch when focus is on a non-interactive element (modal body). Verified the reducer SET_STEP uses an absolute step value so even if a double-dispatch were possible, it converges — but excluding BUTTON eliminates the redundant work and the focus-on-Back failure mode.
- R2: deriveProvider in BatchListTab refactored to return a discriminator ("OpenAI"/"Anthropic"/"Gemini"/"other"/"unknown") with vendor names left un-translated (proper nouns) and other/unknown routed through t("batchListProviderOther") / t("batchListProviderUnknown") at the call-site. Heuristic expanded: gpt-, chatgpt-, /^o[1-9](-|$)/ (catches o1-preview, o3-mini, o4-mini), text-embedding-, dall-e, whisper, tts- → OpenAI; claude- → Anthropic; gemini → Gemini. Kills the "chatgpt-4o-latest → Other" misclassification + the previously dead batchListProviderUnknown key + a hardcoded English "Other" leak.
- B-2b: InputStep race guard moved from useState to useRef (isReadingRef) so concurrent processFile calls in the same tick (drop + file-pick before the next React render) cannot both pass the early-return. State mirror kept for UI rendering.
Tests: 2 new cases in list-regression — test 19 covers chatgpt-4o-latest / o1-preview / o3-mini all rendering "OpenAI" 3×; test 20 asserts unknown + null model produce t("batchListProviderOther") / t("batchListProviderUnknown") (proves no hardcoded English leak). 1 new i18n key (batchListProviderOther) added in en + pt-BR and propagated to 40 locales via fill-missing-from-en.mjs.
The round-3 C1 fix added `server.on("connect", ...)` to satisfy plan 11 §4.6's
text. R4 architectural review confirmed the handler is essentially dead code
on port 443: `https.Server` runs the HTTP parser ABOVE TLS, so a
`server.on("connect")` handler only fires for CONNECT-tunneled-inside-TLS
(HTTPS-proxy-tunneled-in-TLS), not for the "no config required" AgentBridge
DNS-spoof flow where the IDE opens TLS directly to 127.0.0.1:443. Passthrough
for unmapped hosts is structurally handled elsewhere (DNS scoping for default
mode; httpProxyServer.ts:8080 for System-wide proxy mode). Genuine on-wire
bypass-without-decrypt at :443 under direct TLS would require SNI sniffing on
the raw 'connection' event — intentionally out of scope for this release.
This commit:
- adds a block comment above the CONNECT handler explaining the real scope
so future contributors don't assume it covers the primary AgentBridge flow
- guards the `connection` listener with `socket.__mitmCounted` so the
CONNECT "target" branch's `server.emit("connection", clientSocket)`
re-entry doesn't double-increment `stats.activeConnections`
- adds 2 source-grep regression tests asserting both the doc comment and
the guard remain in place
C2 (x-omniroute-source/agent headers) was already correct and is unchanged.
The local type annotation for `recordRequestStart` in `loadAgentBridgeHook`
omitted `sourceModel`, but the call site at line 217-222 passes
`sourceModel: this.extractSourceModel(body)` — which works at runtime
(JavaScript ignores extra properties) but a future strict-mode caller relying
on the narrower local type would silently drop the field.
Add `sourceModel?: string | null` to the local recordRequestStart type to
match the actual `agentBridgeHook.recordRequestStart` shape.
Round-3 F-I18N covered ConversationTab, StatsTab, StatsCharts, and labels in
TimingWaterfall, but missed:
- TimingTab.tsx — 5 user-visible labels (Timestamp, Method, Status, Request
size, Response size) were hardcoded English.
- TimingWaterfall.tsx — empty state ("No timing data available.") and Total
latency label were also hardcoded.
- common.understand — RiskNoticeModal calls
`useTranslations("common")("understand")` but the key did not exist;
only the `|| "I understand"` fallback rescued it, leaving pt-BR users
seeing English.
Adds the missing 7 trafficInspector.timing* keys and common.understand to both
en.json and pt-BR.json, wires `useTranslations` in both components, and adds a
source-grep test asserting no English literals remain in JSX and that all 8
keys exist in both locales.
`upstreamTrust.ts` imported `Agent` and `setGlobalDispatcher` from undici at
the top level. Importing the module — which happens transitively from every
MITM handler via `base.ts` — eagerly loaded undici's full index, which in turn
instantiates `CacheStorage` and calls `webidl.util.markAsUncloneable`. That
helper is not available in the test runner's Node version, so any test that
touched the import chain crashed with `TypeError: webidl.util.markAsUncloneable
is not a function`.
Move the require inside `configureUpstreamCa()` via `node:module/createRequire`
so undici is only loaded when a CA is actually being configured. Preserves the
synchronous void return type (no caller signature change) and the Hard Rule
#12-compliant safe error message.
After: `mitm-upstream-trust.test.ts` 5/5 green (was 0/5 since F1 wrote it).
`Button.tsx` exposes only a default export, but `RiskNoticeModal.tsx` was
importing `{ Button }` (named) — so `Button` resolved to `undefined` and every
render of the modal crashed with React's "Element type is invalid".
The modal opens on first DNS activation for every agent, so this bug
effectively broke DNS interception for every agent in production. It went
undetected through 3 rounds of code review because two test artifacts masked
the failure:
1. `tests/unit/ui/agent-card.test.tsx > calls onDnsToggle when DNS button
clicked` failed since round 3 with the exact error "Element type is
invalid... Check the render method of RiskNoticeModal", but was repeatedly
dismissed as "pre-existing / flaky".
2. `tests/unit/ui/agent-card-risk-modal.test.tsx` (rodada 3) mocked Button
as a named export — which made the test green even though the production
import was broken. Classic "test alignment to broken code" anti-pattern.
This commit:
- switches RiskNoticeModal to `import Button from "@/shared/components/Button"`
- adds a regression-guard test that source-greps for the default-import shape
and asserts Button.tsx remains default-only
- updates the named mock in agent-card-risk-modal.test.tsx to `default:` so
tests now reflect the real module shape (no more masking)
- updates the agent-card.test.tsx DNS-click test to seed the per-agent
risk-accepted localStorage flag, isolating the DNS-toggle path from the
risk-modal path (the modal flow is covered by the risk-modal spec)
After: agent-card.test.tsx 4/4 green; agent-card-risk-modal.test.tsx 5/5 green;
new regression guard prevents recurrence of either pattern.
- mitm-proxy-moved-page.test.tsx: 4 tests — banner renders pageMoved.title/message, goNow triggers router.replace, setTimeout auto-redirect fires at 2500ms
- agent-bridge-server-card-a11y.test.tsx: source-inspection tests for all 6 aria-label attributes in AgentBridgeServerCard + 2 for SessionRecorderBar
- conversation-tab-separators.test.tsx: +1 test that conversationNotAvailable key resolves when body is null
- AgentBridgeServerCard: aria-label on Start, Stop, Restart, Trust Cert, Download Cert, Regenerate Cert buttons/anchor — sourced from t() keys
- CertStatusIcon: switch title from hardcoded strings to useTranslations("agentBridge").certTrusted/certNotTrusted
- SessionRecorderBar: aria-label={t("recordSession")} and aria-label={t("stopSession")} on REC and Stop buttons
Converts bare server-side redirect() to a client component that shows
an amber "This page has moved" banner for 2.5 s, then auto-redirects to
/dashboard/tools/agent-bridge. User can also click "Go now" to jump
immediately. All strings use useTranslations("agentBridge.pageMoved").
Closes Gap 2 from code review #2: ExportCodeModal, BuildTab, PresetPicker,
StudioTopBar, SearchToolsTopBar, SearchConceptCard and ProviderCatalog now
use useTranslations() from next-intl for all user-facing strings.
The F9 i18n PR (566ebb953) added the keys but several legacy hardcoded
strings still slipped through. This change wires them up and fixes
pt-BR translations that were leaking English ("Cancel" → "Cancelar",
"Save" → "Salvar", "Copy" → "Copiar", "Clear All" → "Limpar tudo", etc.).
New keys added in both pt-BR.json and en.json under the `playground`
namespace:
- close, closeExportModal, exportShort
- exportRealKeyWarning, placeholderHintPrefix, placeholderHintSuffix
- copyLangCode (with {language} placeholder)
- loadingPresets, loadPresetPlaceholder
Test updates:
- 7 UI test files: mocks updated for next-intl (useTranslations) and
assertions updated to match the i18n key-as-text returned by the mock.
- PlaygroundStudio test: 2 tests previously checked for a "F7 implementation
pending" placeholder which no longer exists (F7+F9 fully implemented those
tabs) — assertions now verify the tab becomes active instead.
- PlaygroundConfigPane test: endpoint select count bumped from 10 to 13
(D4-rev2) with a more robust selector that finds the endpoint select
regardless of PresetPicker's load-preset select position.
Validation:
- npm run typecheck:core: clean
- npm run lint: 0 errors (warnings pre-existing)
- npx vitest run tests/unit/ui: 245 tests pass (26 files)
- node --test tests/unit/playground-*.test.ts tests/unit/search-tools-*.test.ts
tests/unit/db-playground-presets.test.ts: 122 tests pass
Expands PlaygroundEndpoint type in src/lib/playground/codeExport.ts from 10 to
13 endpoints, adding `responses`, `video`, `music` to mirror the actual
OmniRoute API surface (/v1/responses, /v1/videos/generations,
/v1/music/generations).
This closes the divergence flagged in code review #2 between the contract
fixed by D4 (§3.1 of master-plan-group-C) and the dropdown values used in
ApiTab. The Monaco editor (ApiTab) keeps its own independent endpoint state
(D14) — this only aligns the codeExport-driven Export Code path used by the
Chat/Compare/Build/Scrape tabs.
Changes:
- codeExport.ts: PlaygroundEndpoint type expanded, PlaygroundStateSchema enum
updated, endpointToPath map updated, buildBody switch gains case branches
for responses/video/music with sensible defaults.
- StudioConfigPane.tsx: ENDPOINT_OPTIONS gains 3 new entries.
- playground-code-export.test.ts: endpointToPath assertion updated to 13
endpoints, +6 new tests for responses/video/music (security invariants +
defaults branches).
Also covers Gap 4 from review #2 (moderations/completions/web.fetch already
in the dropdown via the existing contract — confirmed by this audit).
Refs: _tasks/features-v3.8.6/refactorpages/_orchestration/master-plan-group-C.md
react-hooks/preserve-manual-memoization (React Compiler ESLint plugin)
was flagging the pipelineSteps useMemo because the inner tr() closure
was recreated on every render. Wrapping tr() in useCallback([t]) makes
its identity stable, and adding tr to the useMemo deps array lets the
compiler preserve the manual memoization.
This clears 11 lint errors pre-existing from commit e20330af6 (lift
session to shell). No behavior change.
The useRef-based pattern is required so that a manual user close while
forceOpen stays true does not immediately re-open the accordion on the
next render (see test 'toggle closes accordion again'). The setState
calls inside the false→true guard are an intentional sync of an external
deep-link prop into local component state — a valid use case the
react-hooks/set-state-in-effect heuristic does not recognize, so suppress
it with an inline comment explaining the rationale.
Clarified the comment on the prevForceOpen useRef to point at the
regression it prevents.
- Add `snapshotSession(sessionId)` to inspectorSessions.ts per master-plan §3.8:
returns parsed InterceptedRequest[] in seq order, or null for non-existent sessions.
Silently skips rows that fail InterceptedRequestSchema validation (defensive).
- Restore canonical no-arg form of `MitmHandlerBase.hookBufferUpdate(intercepted)`
per master-plan §3.5: when opts is omitted, derive completion fields from
the intercepted object itself (status/responseHeaders/responseBody/responseSize/
*LatencyMs) rather than no-op'ing. Extended opts form preserved for legacy callers.
- Update Zod record() calls in InterceptedRequestSchema to current (key,value) signature.
- Add 3 unit tests for snapshotSession (happy path / non-existent / silent skip).
- Add 2 unit tests for hookBufferUpdate (no-arg form + extended opts form).
Cover the new CJS routing primitives and the bypass-JSON manager write
path so the C1/C2 contracts cannot regress silently.
- `mitm-server-connect.test.ts` (27 tests): exercises the
`_internal/bypass.cjs` shim used by `server.cjs`:
- DEFAULT_BYPASS_PATTERNS shape (≥4 regexes, all RegExp)
- Default bank / gov / okta / auth0 → bypass
- Bypass beats target match (precedence)
- Known target hostname → target
- Unknown hostname → passthrough
- User glob pattern → bypass
- Empty / undefined hostname → passthrough
- Set-vs-Array shape for targetHosts
- Case-insensitive hostname matching
- bypassGlobMatch wildcard semantics + ReDoS-safe linear walk
- parseBypassJson with valid / empty / malformed / wrong-shape inputs
- Spec assertions on server.cjs source — header injection (C2),
sanitizeErrorMessage wrapping (Hard Rule #12), and CONNECT handler
registration (C1). These three guard against future regressions
that would silently re-introduce the bugs the C1/C2 fixes closed.
- `mitm-manager-bypass-json.test.ts` (5 tests): exercises
`writeBypassJson()`:
- Creates the `mitm/` dir + valid JSON file shape
- Empty array roundtrips as empty patterns
- Falls back to `getUserBypassPatterns()` when no arg passed
- Default patterns from the DB are NOT written to the file
- Overwrites prior content
The CJS proxy in `src/mitm/server.cjs` cannot import the TS
`getUserBypassPatterns` directly. Mirror the `targets.json` pattern: on
`startMitm()` the manager now also writes `<DATA_DIR>/mitm/bypass.json`
with the user-defined glob patterns from `agent_bridge_bypass`. The CJS
proxy reads the file at boot via the `_internal/bypass.cjs` shim's
`parseBypassJson` helper.
Defaults (banks / gov / okta / auth0) live hardcoded in the CJS shim —
they are not persisted to the JSON file. This matches the privacy
contract: defaults always apply, even when the DB or the JSON file is
missing or unreadable.
Plan reference: 11-agent-bridge.plan.md §4.6 + master-plan-group-A.md §3.5.
Hard Rule #13: file I/O only — no shell interpolation.
`MitmHandlerBase.fetchRouter` already injects the AgentBridge correlation
headers, but the CJS proxy in `src/mitm/server.cjs` had never been
updated to match. As a result the running Antigravity flow was hitting
the OmniRoute router with no source/agent identification, breaking the
contract documented in master-plan-group-A.md §3.5 and §12 acceptance #17.
This commit adds:
- `x-omniroute-source: agent-bridge` — distinguishes AgentBridge traffic
from other inbound clients.
- `x-omniroute-agent: <id>` — IDE agent id resolved from the Host header
via the existing `TARGET_HOST_AGENT` map (populated by `targets.json`
+ the antigravity baseline). Defensive fallback to `"unknown"` for
hosts that were never registered, so router-side filters never get
an empty value.
Antigravity non-regression preserved: `daily-cloudcode-pa.googleapis.com`
continues to resolve to `agentId="antigravity"` via the baseline seed in
`TARGET_HOST_AGENT.set(h, "antigravity")` at the top of the file.
Bring `src/mitm/server.cjs` into compliance with the AgentBridge MITM
contract (master plan §3.5 / §12 acceptance #16). Prior to this commit
the bypass/passthrough logic existed in TS (`src/mitm/passthrough.ts`,
`src/mitm/targets/index.ts::routeConnection`, `src/lib/db/agentBridgeBypass.ts`)
but was completely disconnected from the running CJS proxy.
Changes:
- Add `server.on("connect", ...)` so HTTPS proxy clients can still tunnel
to non-AgentBridge hosts without losing internet. Per host the handler
decides:
- bypass (default regex or user glob) → raw TCP pipe, NO TLS decrypt,
NO content logging (privacy: bypass = "never see content")
- target (in TARGET_HOSTS) → write 200 Connection
Established and emit `connection` so the existing
`https.createServer` decrypts and routes via the normal flow
- passthrough (anything else) → raw TCP pipe
- Introduce `src/mitm/_internal/bypass.cjs` shim that mirrors
`DEFAULT_BYPASS_PATTERNS` and `routeConnection` from the TS source.
Defaults stay hardcoded (banks, gov, okta, auth0); user patterns load
from `<DATA_DIR>/mitm/bypass.json` (written by manager — separate commit).
- Add a CJS port of `sanitizeErrorMessage` and wire it into the intercept
error path so HTTP/SSE error bodies never expose raw `err.message`.
Closes a pre-existing Hard Rule #12 violation in the file.
Defaults match `src/mitm/passthrough.ts::DEFAULT_BYPASS_PATTERNS` and
`shouldBypass` precedence is identical to `routeConnection`. Antigravity
non-regression preserved — known hosts still trigger TLS termination via
the existing request handler.
Prop was declared but destructured as _onSlugChange (never consumed).
TranslatorPageClient relies on onOpenChange callbacks on each child
accordion instead. Cleaning the interface.
Both accordions initialized open/hasOpened from forceOpen but never
reacted to forceOpen changes after mount. RawJsonPanel and PipelineView
already had this useEffect. Aligns the pattern so back/forward navigation
and inter-accordion switches work consistently.
Uses useRef to track false→true transitions only, so a manual close by the
user is not immediately overridden while forceOpen remains true.
Collapsible does not expose onOpenChange, so clicking the accordion header
opens it but leaves {hasOpened && ...} false, rendering an empty container.
The ref callback (same pattern as RawJsonPanel) detects the first DOM
mount inside the Collapsible and sets hasOpened + notifies parent.
Adds regression test covering manual click flow (existing tests only
exercised defaultOpen=true). Also updates the pre-existing lazy-render test
whose assertion was incompatible with the new ref callback (Collapsible stub
always renders children, so the ref fires immediately — asserting 0 items
was only valid without the ref; the meaningful part of the test is preserved).
- FilesListTab: add (input)/(output)/(error) role label next to batch id in Used by column + tooltip (G-AUD1, plan §4 wireframe `b1 (input)`)
- BatchListTab: add -50% inline badge on Cost column per wireframe §3 `$6.20 (-50%)` (G-AUD2) + (partial) suffix on progress when expired_with_failures (G-AUD3)
- ExpirationBadge: document <1h/<6h/<24h tier semantics + >24h graceful fallback (G-AUD4)
- 4 new i18n keys in en + pt-BR (filesListUsedByRoleInput/Output/Error, batchListProgressPartial); 40 locales auto-filled via fill-missing-from-en.mjs
- Update list-regression test #13 to assert new used-by format (truncated id + role label + tooltip carries full id+role)
AgentCard.handleRiskAccept was calling markRiskAccepted() before opening the
RiskNoticeModal, which itself writes the same key via dontShowAgainKey on accept.
Remove the redundant markRiskAccepted call and delete the now-unused helper so
RiskNoticeModal (D16) is the sole canonical persistence owner. Add a spy-based
test asserting the key is written exactly once per accept.
11 assertions covering: dynamic import with ssr:false, absence of static
recharts import in StatsTab, absence of the discarded _rechartsPreload
pattern, and presence of recharts exports in StatsCharts.