Feat/20260706 visualized card - #4
Open
AnoobFeng wants to merge 20 commits into
Open
Conversation
Implement a reusable Human Input Card flow for ask_clarification while keeping the existing text fallback for older clients and IM channels. Backend: - Add structured ToolMessage.artifact.human_input payloads for clarification requests. - Preserve ToolMessage.content as the readable Markdown/text fallback. - Normalize clarification options from native lists, JSON strings, plain strings, mixed scalar values, None, and missing options. - Derive input_mode as choice_with_other when options exist, otherwise free_text. - Keep disable_clarification non-interactive behavior as a plain ToolMessage with no human_input artifact. - Cover artifact persistence and Gateway message metadata preservation in tests. Frontend: - Add human input protocol types, runtime guards, extractors, response builders, and thread-state helpers. - Add reusable HumanInputCard with option buttons, free-text input, pending, read-only, disabled, and answered states. - Render structured clarification cards from artifact.human_input, with Markdown fallback for malformed or legacy tool messages. - Preserve line breaks in structured question/context/option text. - Hide submitted clarification bridge messages from the chat UI via additional_kwargs.hide_from_ui. - Send structured human_input_response metadata through the fourth sendMessage options argument, preserving run context in the third argument. - Wire submissions for normal chats, custom agent chats, agent bootstrap chats, and sidecar chats. - Derive answered state from raw thread.messages so hidden replies still update the original card. - Clear pending state when the hidden reply arrives, dispatch is dropped, or a later async stream failure appears on thread.error.
- Support Enter key to submit text input (Shift+Enter for newline) - Render question and context fields as Markdown instead of plain text - Replace deprecated FormEventHandler type with structural typing
…ance#3740) * fix(frontend): address mobile workspace polish blockers * fix(frontend): prevent mobile landing overflow * fix(frontend): keep landing sections within mobile viewport Section titles used a fixed text-5xl with no word breaking, and the <section> flex items had default min-width:auto, so wider Linux font metrics in CI pushed content past the viewport (scrollWidth 345 > 320). Make titles/subtitles responsive with break-words, constrain section width with min-w-0, and add an overflow-x-clip guard on the page root. * fix(frontend): address review feedback on mobile landing PR - add hamburger Sheet nav below sm: so mobile users keep docs/blog access - switch useIsMobile to useSyncExternalStore to avoid hydration swap flash - memoize artifactContent so it isn't rebuilt on every streamed token - use slot-based key in HeroWordRotate; drop flex-wrap so SuperAgent never orphans at 320px - delete unused word-rotate.tsx dead code - move section gutter to <main> for a uniform mobile padding contract - harden e2e: per-viewport overflow tests, locale-stable artifact selector, focus-ring asserts light vs dark differ --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
* feat: add boxlite SDK as harness dependency * test: add fake SimpleBox fixtures for BoxLite warm pool tests * feat: add deterministic sandbox_id and warm pool fields to BoxliteProvider * feat: pass deterministic sandbox_id to SimpleBox name * feat: warm pool lifecycle — park on release, reclaim on acquire - release(): parks VMs in _warm_pool with timestamp instead of closing - _reclaim_warm_pool(): health checks warm boxes via echo ok - acquire(): tries warm pool reclaim before creating new boxes - Deterministic sandbox_id ensures thread isolation * feat(boxlite): idle reaper, replica enforcement, warm-pool shutdown/reset - Task 6: idle reaper daemon thread destroys expired warm-pool boxes - Task 7: replica enforcement evicts oldest warm-pool box when at capacity - Task 8: shutdown() stops idle checker first, destroys all boxes (active+warm); reset() clears warm pool Tests: 17 passed (4 new: idle reaper, replica enforcement, shutdown, reset) * fix(boxlite): harden warm pool lifecycle races * docs(boxlite): document warm pool configuration * fix(sandbox): log warning when evicting oldest warm box is failed Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix(boxlite): stop provider lifecycle on reset * fix(boxlite): make runtime an optional dependency * test(boxlite): remove unused warm pool lookup * docs(boxlite): clarify optional runtime support * refactor: extract shared WarmPoolLifecycleMixin for sandbox warm-pool lifecycle Introduce deerflow.community.warm_pool_lifecycle.WarmPoolLifecycleMixin owning idle-checker loop, warm-pool expiry, oldest-warm eviction, replica counting, and soft-cap logging. Move AioSandboxProvider and BoxliteProvider onto the mixin; keep AIO active-idle cleanup local and delegate only warm-pool expiry to the shared helper. BoxliteProvider also gains: - Prefixed box names (deer-flow-boxlite-*) for startup orphan reconciliation - _reconcile_orphans() adopting surviving boxes from a prior process - Pinned timeout forwarding: command timeout now bounds both BoxLite SDK exec(timeout=...) and the loop bridge .result(timeout) - reset() reworked as lightweight registry clear (boxes -> warm pool, no close, no idle-reaper stop, no loop close) so reset_sandbox_provider() config switches are safe; shutdown() remains the teardown path Backward-compatible: AIO DEFAULT_IDLE_TIMEOUT/DEFAULT_REPLICAS/ IDLE_CHECK_INTERVAL stay importable; Boxlite IDLE_CHECK_INTERVAL stays monkeypatchable. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: add assistant turn branching * fix(threads): skip workspace clone when branching from historical turn Workspace files are not checkpointed, so cloning them onto a branch rooted at an older assistant turn leaked files created in a later timeline. Restrict the best-effort workspace copy to branches taken from the latest turn; historical-turn branches now report workspace_clone_mode="skipped_historical_turn" and keep only the restored message history. * style(frontend): fix prettier formatting in e2e mock-api Collapse the branch-title normalization chain onto a single line to satisfy the frontend lint (prettier --check) CI gate. --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
…tial result (bytedance#3875 Phase 2) (bytedance#3949) * fix(subagents): surface turn-budget cap as MAX_TURNS_REACHED with partial result (bytedance#3875) Phase 2 of bytedance#3875. When a subagent exhausts its turn budget (recursion_limit == max_turns), LangGraph raises GraphRecursionError from agent.astream. The generic except Exception in _aexecute misclassified it as FAILED and discarded the partial work already streamed into final_state, so the lead could not tell 'broken subagent' from 'out of budget' and got an empty failure. Catch GraphRecursionError specifically (before the generic handler) and set a distinct SubagentStatus.MAX_TURNS_REACHED terminal status, recovering the partial result from the last streamed chunk via a shared _extract_final_result helper (refactored out of the normal-completion path so both paths render content identically). Extend the cross-language status contract so the new value travels on additional_kwargs.subagent_status: a capped run is result-bearing, so make_subagent_additional_kwargs / read_subagent_result_metadata carry subagent_result_brief + subagent_result_sha256 (the recovered work, like completed) AND the cap notice on subagent_error -- the one status that carries both. task_tool.py returns it via the shared _task_result_command; the delegation ledger prefers the partial result_brief and renders model-facing guidance (reuse / retry tighter / raise max_turns). Frontend collapses max_turns_reached to the failed pill with the cap notice on error. No agent-loop, runner, or persistence behavior touched; default max_turns is unchanged. * refactor(subagents): consolidate content-stringify onto shared helper Address review feedback on bytedance#3949 (willem-bd, copilot-pull-request-reviewer): - executor.py: drop the private `_stringify_message_content` — a third near-duplicate of `utils/messages.py::message_content_to_text`. `_extract_final_result` now delegates to that canonical helper; the "No response generated" sentinel is pushed down to the consumer (the shared helper returns "" for no-text, matching every other call site). - task_tool.py: align the live `task_failed` event's error string with the canonical "Reached max_turns=N" used by the logger, the structured `error=`, and the executor (was "Reached max turns (N)"). Behavior for real AIMessage content is unchanged; only atypical edge inputs (consecutive bare-string list items; empty content) now match the canonical helper that every other call site already uses. `extract_response_text` is intentionally left as-is: it filters by OpenAI content-block `type`, a different shape with many callers and its own tests. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
) * fix(docker): keep mutable config mounts stable * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- Preserve IME composition safety for human input card Enter submits - Treat hidden human input responses as genuine user messages for sanitization - Keep hidden card replies in memory filtering while excluding malformed/internal hidden messages - Add regression coverage for card IME handling and hidden reply sanitization
…chat (bytedance#3959) * fix(sidecar): correct text-selection toolbar actions inside the side chat The sidecar panel reuses MessageList, but it did not distinguish the side chat surface from the main conversation, so selecting text inside the side chat behaved as if it were the main list: - The "Ask in side chat" action was shown even though the user is already in the side chat, which is a no-op interaction. - "Add to conversation" routed the snippet to the main composer's quotes (conversationQuotes) instead of the side chat's own composer, so the reference landed in the wrong input box. Add a `sidecarSurface` prop to MessageList. On the sidecar surface, hide "Ask in side chat" and route "Add to conversation" to `sidecar.openContext` so the snippet attaches to the side chat's own composer (activeReferences). Main-list behavior is unchanged. * docs(sidecar): drop AGENTS.md note for the toolbar surface change The sidecarSurface behavior is self-evident from the code; no dedicated Interaction Ownership entry is needed.
…ytedance#3860) * feat(memory): add staleness review to prune silently-outdated facts Facts created long ago may become outdated without any future conversation explicitly contradicting them ("Silent Staleness"). This adds a staleness review mechanism that surfaces aged facts to the LLM during the normal memory-update call so it can semantically judge whether each is still valid. - New MemoryConfig fields: staleness_review_enabled, staleness_age_days, staleness_min_candidates, staleness_max_removals_per_cycle, staleness_protected_categories - New STALENESS_REVIEW_PROMPT section injected into MEMORY_UPDATE_PROMPT when enough stale candidates exist - New staleFactsToRemove output field in the LLM response schema - Safety cap limits max removals per cycle, keeping lowest-confidence entries when the LLM returns more than the cap - Correction facts (category=correction) are protected by default - Observability via structured logging of each removal with reason - 32 unit tests covering parsing, selection, triggers, formatting, normalization, safety cap, and integration * fix(memory): add deterministic guardrail for staleness removals _apply_updates previously removed any fact id the LLM returned in staleFactsToRemove without verifying it was in the actual staleness candidate set. An LLM slip could silently delete protected-category facts (e.g. correction) or fresh facts, defeating the stated guarantee. Now intersect stale_ids_to_remove with _select_stale_candidates before the safety cap, making the protection independent of both model behavior and the staleness_review_enabled flag. Add three regression tests: - test_protected_category_fact_refused_at_apply - test_non_aged_fact_refused_at_apply - test_guardrail_runs_when_staleness_review_disabled * docs(memory): sync AGENTS.md staleness config + simplify datetime parsing Address reviewer feedback from PR bytedance#3860: - Add staleness workflow step and 5 new config fields to backend/AGENTS.md - Simplify _parse_fact_datetime: drop manual Z→+00:00 replace, Python 3.12+ fromisoformat handles Z natively --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
…ytedance#3960) Why: issue bytedance#3948 identifies four correctness breakers when GATEWAY_WORKERS > 1. Work item 1 adds a startup gate that refuses to boot when database.backend is not postgres, giving operators a clear error instead of silent SQLite write-lock corruption. The gate runs inside langgraph_runtime() before init_engine_from_config, so a misconfigured deploy never opens a listener or writes to disk. Non-integer env values fall back to 1 so uvicorn's own validation is unaffected.
- Reject empty hidden human input response values - Remove invalid list ARIA role from human input card options - Add backend coverage for empty response payloads
…ests (follow-up to bytedance#3764) (bytedance#3783) * refactor(frontend): extract placeholder detection utility with unit tests * fix(frontend): pass prompt text directly to onSelectPlaceholder to avoid stale DOM read The onSelectPlaceholder callback was reading textarea.value immediately after textInput.setInput(prompt), but React state updates are async so the DOM had not yet reflected the new value. This caused the placeholder auto-selection to silently fail when the textarea was previously empty. Fix: accept the new text as a parameter instead of reading from the DOM. * fix(frontend): resolve duplicate findSuggestionTemplatePlaceholder identifier After rebasing onto main, the function existed both inline in input-box-helpers.ts (from bytedance#3764) and as an import from our new placeholders module, causing TS2300 duplicate identifier errors. - Remove duplicate import in input-box.tsx - Replace inline function in input-box-helpers with re-export from @/core/suggestions/placeholders - Export SUGGESTION_TEMPLATE_PLACEHOLDER_PATTERN from placeholders module * refactor(frontend): remove dead hasUnreplacedPlaceholder export No production call site uses this boolean wrapper — both existing checks need the {start,end} range from findSuggestionTemplatePlaceholder. Drop the function and its two unit tests.
…tate machine (bytedance#3601) * feat(middleware): add structured tool result meta and tool-progress state machine feat: - Add tool_result_meta.py: ToolResultMeta dataclass (status/error_type/retryable/ recoverable_by_model/recommended_next_action/source) + normalize_tool_result and stamp_exception_meta utilities; classifies every ToolMessage regardless of path - Add ToolProgressMiddleware: per-(thread_id, tool_name) state machine ACTIVE → WARNED (hint injected as HumanMessage) → BLOCKED (call short-circuited); Jaccard near-duplicate detection for repeated successful results; auth/config/internal errors bypass WARNED and go directly to BLOCKED; LRU-bounded thread state store - Add ToolProgressConfig: all thresholds configurable (stagnation_threshold, warn_escalation_count, jaccard_similarity_threshold, exempt_tools, etc.); disabled by default (enabled: false) - Wire ToolProgressMiddleware as outer wrapper around ToolErrorHandlingMiddleware in _build_runtime_middlewares so it receives results already carrying deerflow_tool_meta fix: - ToolErrorHandlingMiddleware now calls stamp_exception_meta on exception path and normalize_tool_result on success path so every ToolMessage carries deerflow_tool_meta test: - Add test_tool_result_meta.py: 26 cases covering all classification paths, stamp_exception_meta, and normalize_tool_result Command passthrough - Add test_tool_progress_middleware.py: 27 cases including full async paths, Jaccard duplicate detection, LRU eviction, hint injection, and malformed meta passthrough - Extend test_tool_error_handling_middleware.py: middleware ordering invariant and meta stamping on exception docs: - Add tool_progress section to config.example.yaml with all fields and descriptions - Update CLAUDE.md middleware chain documentation (entries 8-9) Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> * fix(middleware): recoverable errors stay WARNED; fix auth keyword shadowing fix: - WARNED is terminal for recoverable_by_model=True errors (no_results, not_found, permission); hint re-injected on each problem call instead of escalating to BLOCKED, so the model can retry with different parameters (e.g. fresh query, new URL) without being hard-blocked by a prior stagnation count. Non-recoverable (rate_limited, transient) still escalate WARNED → BLOCKED after warn_escalation_count more problems; auth/config/internal remain immediately BLOCKED. - Remove bare "api key" keyword from auth classification rule so "no api key configured" correctly classifies as config (not auth), producing the accurate block-reason text for the model. docs: - CLAUDE.md: document all three ToolProgressMiddleware transition paths - config.example.yaml: update inline state-machine comment to match new paths test: - test_recoverable_errors_stay_warned_indefinitely: WARNED never escalates for recoverable errors regardless of how many problem calls accumulate - test_recoverable_error_re_injects_hint_past_escalation: hints continue past the escalation zone for recoverable errors - test_no_api_key_is_config_not_auth: regression guard for keyword shadowing fix Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> * fix(tool_result_meta): add JSON error extraction and fix source classification fix: - Fix non-standard error path: source was "exception" but should be "tool_return" - Add _extract_json_error_text to isolate JSON error fields from noisy JSON bodies (e.g. Brave Search {"error": "...", "query": "..."} — query keywords no longer pollute error classification) - Add success-path JSON extraction to catch tools that return HTTP 200 with a JSON error body (status="success" but {"error": "API key not configured"}) - Add _SEMANTIC_ZERO_ERROR_STRINGS frozenset to suppress false positives from tools that use {"error": "none"} / {"error": "null"} / {"error": "ok"} as success signals - Document that stamp_exception_meta always overwrites existing TOOL_META_KEY (exception-derived classification is authoritative over tool return-time stamps) test: - Add parametrized regression tests for all semantic-zero error strings - Add tests for non-standard error path source field - Add tests for JSON error extraction (nonstd, success-path, numeric, falsy values) - Correct test comment for test_no_api_key_is_config_not_auth Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> * fix(tool_progress_middleware): fix 6 bugs, add terminal guard and structured logging fix: - H1: fix exempt_tools empty-set silently ignored — use `is not None` instead of truthiness check so ToolProgressConfig(exempt_tools=set()) correctly disables all exemptions - Fix _get_block_reason creating phantom LRU entries via _get_state (write path); now uses dict.get + explicit move_to_end on read path only - Fix _pending memory leak: LRU eviction of _phase_states now synchronously removes all (evicted_thread, *) keys from _pending - Fix _assess_and_transition missing terminal guard for blocked state — a recoverable error result could silently demote blocked → warned in concurrent-race scenarios; early return preserves terminal semantics - Fix recent_word_sets window: stored [-5:] but is_near_duplicate only compared [-3:]; align to [-3:] and change type list→tuple (prevents accidental in-place mutation across dataclasses.replace shallow copies) - Fix _format_hint missing "success" key and "continue" action: Jaccard near-duplicate results produced the generic fallback instead of a specific actionable message feat: - Add structured state-transition logging (ACTIVE/WARNED/BLOCKED transitions, blocked intercepts, hint injection debug log) test: - Add regression tests for all 6 bug fixes (H1, phantom LRU, pending leak, terminal guard, window alignment, format_hint near-dup) - Add Jaccard near-threshold boundary test (7/9 vs 8/9 Jaccard) - Add production min_words=10 skip test for short content - Add exempt_tools empty-set and None round-trip tests - Add _augment_request deduplication test - Add before_agent current-run preservation test - Add structured logging tests (WARNED/BLOCKED/ACTIVE/intercepted/debug) Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> * chore(config): remove unused backward-compat fields from ToolProgressConfig Remove max_calls_per_intent and window_size fields that were marked "Retained for backward compatibility; not used by the current state machine" when the state machine was introduced. Pydantic v2 ignores unknown fields by default, so existing config.yaml files with these keys remain valid. Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> * fix(tool_progress): address PR review and multi-agent review findings fix: - Remove <80-char length gate for partial_success; only _PARTIAL_MARKERS now - Add word-boundary regex for numeric HTTP codes (401/403/404/500) to avoid false positives like "500ms" or "4010 rows" triggering hard-block - Add "task" to default exempt_tools (delegation primitive, not a search tool) - Remove move_to_end() from _get_block_reason read path; blocked threads were permanently warm in LRU, starving active threads of eviction slots - Add _reset_blocked_states in before_agent: scope BLOCKED and WARNED states to a single run; clear recent_word_sets so stale Jaccard windows don't cause false near-duplicate detections in the next run - Compute word_set() lazily (only for success results); cap content at 8192 chars to bound memory and CPU cost on large tool results - Remove unused retryable field from ToolResultMeta (no consumer existed) - Add isinstance-based ordering guard and warning log for missing meta - Fix JSON-without-error-key fallback: use _UNKNOWN_ERROR instead of classifying incidental field values (e.g. {"user_id": 401} → auth → stop) - Fix _extract_json_error_text: use json.dumps for dict/list error fields instead of str() which produced Python repr matching config rules spuriously - Add "no results found"/"no content found"/"no images found" to _PARTIAL_MARKERS so success responses with empty results trigger stagnation detection - Fix immediate-block path to increment consecutive_problems (was left at 0) - Fix _queue_assessment: skip phantom _pending entries for evicted threads - Bump config_version 13→16 (upstream added 14/15; tool_progress is additive) test: - Update test_short_content_is_partial → test_short_terse_success_is_not_partial - Add parametrized test_numeric_keyword_word_boundary (8 positive + negative cases) - Add test_before_agent_resets_blocked_states_for_new_run (strengthened assertions) - Add test_before_agent_resets_warned_states_for_new_run - Add test_missing_meta_on_non_exempt_tool_emits_warning - Add test_middleware_ordering_guard_raises_when_progress_is_inner - Add test_auth_error_immediately_blocked asserts consecutive_problems == 1 - Add tests for JSON-without-error-key, dict error field, no-results partial_success Co-Authored-By: Claude <noreply@anthropic.com> * fix(tool_progress): address second PR review — perf, architecture doc, concurrency note fix: - Extract content.lower() once before _PARTIAL_MARKERS check in normalize_tool_message; previously computed up to 7× per call inside the generator (once per marker) docs: - Add division-of-labor paragraph to ToolProgressMiddleware module docstring explaining coexistence with LoopDetectionMiddleware: result-quality guard (per-tool BLOCK) vs call-pattern guard (whole-turn hard-stop); no shared state, no double-stop risk - Add threading.Lock comment explaining why asyncio.Lock is not used (short critical sections, must also protect sync wrap_tool_call path from subagent executor threads) - Update backend/CLAUDE.md entry 8 with division-of-labor summary; fix entry 9 (remove stale retryable field reference, add missing recoverable_by_model/source) test: - Add test_tool_progress_and_loop_detection_coexist_without_interfering: drives both middlewares to WARNED state simultaneously, verifies independent state, independent hint queues, and no cross-contamination; uses snapshot copy for final assertion Co-Authored-By: Claude <noreply@anthropic.com> * fix(tool_progress): reset all tool states at run boundary; fix semantic-zero test validity fix: - _reset_run_states (formerly _reset_blocked_states) drops the phase filter and resets all tracked (thread, tool) pairs unconditionally at before_agent; ACTIVE tools with sub-threshold consecutive_problems or cached recent_word_sets no longer bleed into the next run, preventing spurious WARNED transitions on clean R2 calls - test_normalize_json_semantic_zero_error_string_not_treated_as_error: replace {error_value!r} f-string (produces invalid JSON with single quotes) with json.dumps so _extract_json_error_text actually parses the payload and the _SEMANTIC_ZERO_ERROR_STRINGS guard is exercised, not bypassed at json.loads test: - add test_before_agent_resets_active_state_consecutive_problems_and_word_sets to lock the ACTIVE-phase run-boundary reset: drives tool to active/cp=1/ws≠() in R1, asserts both fields are zero/empty after before_agent fires for R2 * docs(tool_progress): document intentional per-run reset vs LoopDetection thread-scoped retention Addresses reviewer observation in PR bytedance#3601 that _reset_run_states diverges from LoopDetectionMiddleware's cross-run scoping policy without explanation. Expands the _reset_run_states docstring to record the intentional design choice: ToolProgressMiddleware resets per-run because result-quality errors (rate_limited, transient) are time-bound and may resolve between turns — retaining stale counters would risk false-positive BLOCKED calls. LoopDetectionMiddleware retains history across runs because call-pattern loops are time-invariant. The divergence is by design, not oversight. Co-Authored-By: Claude <noreply@anthropic.com> * fix(middleware): restore ReadBeforeWriteMiddleware as outermost write gate A merge conflict resolution had accidentally placed ReadBeforeWriteMiddleware after ToolErrorHandlingMiddleware (inner), reversing the original intent from b81334c where it was the outermost write gate before ToolErrorHandling. fix: - Restore ReadBeforeWriteMiddleware to outer position: ReadBeforeWrite → ToolProgress → ToolErrorHandling. Blocked writes now return immediately without consuming a ToolProgress slot. - Add normalize_tool_result call on blocked ToolMessages so they carry deerflow_tool_meta (recoverable_by_model=True) even though they bypass ToolErrorHandlingMiddleware. test: - Add test_blocked_write_has_deerflow_tool_meta (sync + async) to lock the normalize_tool_result behavior on blocked writes. - Fix chain order assertions in TestChainWiring and test_build_lead_runtime_middlewares_chain_order_matches_agents_md. docs: - Renumber AGENTS.md items: 10→ReadBeforeWrite, 11→ToolProgress, 12→ToolErrorHandling; update descriptions to reflect outermost-gate design. - Fix stale cross-reference: LoopDetectionMiddleware (item 23) → (item 25). Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
…ytedance#3962) On conversation pages the composer renders an opaque `bg-background` strip just below itself to mask scrolled content peeking past the rounded corners. The strip was a child of `PromptInput`, the element that draws the focus ring. A parent's box-shadow always paints beneath its own descendants, so the strip covered the bottom ~3px of the ring — the blue focus outline looked cut off along the bottom edge whenever the composer sat flush at the viewport bottom. Move the strip out of `PromptInput` to be a sibling with a lower stacking order and give the composer `relative z-10`, so the ring composites above the strip. The strip still masks the same region; only the paint order changes. Welcome mode is unaffected (it never renders the strip).
AnoobFeng
pushed a commit
that referenced
this pull request
Jul 27, 2026
…tedance#4190) * fix(helm): default sandbox Services to ClusterIP (bytedance#3929) The K8s sandbox provisioner supports both NodePort and ClusterIP via SANDBOX_SERVICE_TYPE (added in bytedance#4016), but the Helm chart never set it, so real-cluster installs inherited the NodePort default. That bound the code-execution sandbox on every node's interfaces - including externally reachable ones on GKE/EKS/AKS - and pinned every sandbox URL to one node IP (SPOF on node reboot/drain/ephemeral-IP). Default the chart to ClusterIP: the provisioner returns a cluster-DNS URL (http://sandbox-<id>-svc.<ns>.svc.cluster.local:8080) so the gateway-> sandbox hop stays inside the cluster network - no node IP, no 30xxx port, no external exposure. The chart always runs the gateway in-cluster, so ClusterIP is always correct there. NodePort remains an opt-in (provisioner.sandboxServiceType: NodePort + nodeHost) for the Docker-Compose/hybrid path where the gateway is not in K8s and cannot resolve .svc.cluster.local; the provisioner code default stays NodePort for that path. - values.yaml: add provisioner.sandboxServiceType ("ClusterIP") - provisioner-deployment.yaml: emit SANDBOX_SERVICE_TYPE; gate the NODE_HOST block on NodePort mode (default "ClusterIP" for upgrade safety) - NOTES.txt + README.md: document ClusterIP default + NodePort opt-in No change to docker/provisioner/app.py (already mode-aware since bytedance#4016) or RBAC (services verbs already cover ClusterIP). * test(helm): assert sandbox Service-type gating + CHANGELOG the default flip (bytedance#3929) Address review on bytedance#4190: - Add scripts/check_chart_sandbox_service.sh: renders the chart for the default (ClusterIP, no NODE_HOST), the NodePort opt-in (both emitted), and NodePort+nodeHost (literal value, not downward API). Locks in the bytedance#3929 gating so a regression (e.g. re-adding an unconditional NODE_HOST, or dropping the `default "ClusterIP"` upgrade-safety fallback) fails CI. Wired into .github/workflows/chart.yaml validate-chart job. (#2) - CHANGELOG [Unreleased] -> Changed: note the NodePort->ClusterIP default flip on upgrade + the `sandboxServiceType: NodePort` opt-back-in. (#4) No chart template changes (the gating itself landed in the first commit). --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
test card