refactor: remove remaining dead code

This commit is contained in:
chengyongru
2026-08-23 19:08:00 +08:00
committed by chengyongru
parent 3852956a71
commit 961b1fdd7d
46 changed files with 85 additions and 250 deletions
@@ -145,14 +145,9 @@ class _FakeChannel:
class _FakeInteractionResponse: class _FakeInteractionResponse:
def __init__(self) -> None: def __init__(self) -> None:
self.messages: list[dict] = [] self.messages: list[dict] = []
self._done = False
async def send_message(self, content: str, *, ephemeral: bool = False) -> None: async def send_message(self, content: str, *, ephemeral: bool = False) -> None:
self.messages.append({"content": content, "ephemeral": ephemeral}) self.messages.append({"content": content, "ephemeral": ephemeral})
self._done = True
def is_done(self) -> bool:
return self._done
def _make_interaction( def _make_interaction(
-3
View File
@@ -53,7 +53,6 @@ class MattermostConfig(Base):
include_thread_context: bool = True include_thread_context: bool = True
thread_context_limit: int = 20 thread_context_limit: int = 20
streaming: bool = True streaming: bool = True
streaming_max_chars: int = 16000
react_emoji: str = "eyes" react_emoji: str = "eyes"
done_emoji: str = "white_check_mark" done_emoji: str = "white_check_mark"
send_progress: bool = True send_progress: bool = True
@@ -106,7 +105,6 @@ class MattermostChannel(BaseChannel):
self._ws_task: asyncio.Task[None] | None = None self._ws_task: asyncio.Task[None] | None = None
self._self_id: str | None = None self._self_id: str | None = None
self._self_username: str | None = None self._self_username: str | None = None
self._self_email: str | None = None
self._usernames: dict[str, str] = {} self._usernames: dict[str, str] = {}
self._user_emails: dict[str, str] = {} self._user_emails: dict[str, str] = {}
self._channel_types: dict[str, str] = {} self._channel_types: dict[str, str] = {}
@@ -138,7 +136,6 @@ class MattermostChannel(BaseChannel):
me = cast(dict[str, Any], resp.json()) me = cast(dict[str, Any], resp.json())
self._self_id = me.get("id") self._self_id = me.get("id")
self._self_username = me.get("username") self._self_username = me.get("username")
self._self_email = me.get("email", "")
self.logger.info("bot @{} connected", self._self_username) self.logger.info("bot @{} connected", self._self_username)
except Exception as e: except Exception as e:
self.logger.error("Failed to identify bot user: {}", e) self.logger.error("Failed to identify bot user: {}", e)
@@ -31,8 +31,6 @@ class _FakeHTTPClient:
self.delete_calls: list[dict[str, Any]] = [] self.delete_calls: list[dict[str, Any]] = []
self._get_responses: dict[str, Any] = {} self._get_responses: dict[str, Any] = {}
self._post_responses: dict[str, Any] = {} self._post_responses: dict[str, Any] = {}
self._put_responses: dict[str, Any] = {}
self._delete_status: int | None = None
def _req(self, method: str, path: str) -> httpx.Request: def _req(self, method: str, path: str) -> httpx.Request:
return httpx.Request(method, f"https://chat.example.com{path}") return httpx.Request(method, f"https://chat.example.com{path}")
@@ -46,12 +44,6 @@ class _FakeHTTPClient:
def set_post_response(self, path: str, data: Any) -> None: def set_post_response(self, path: str, data: Any) -> None:
self._post_responses[path] = data self._post_responses[path] = data
def set_put_response(self, path: str, data: Any) -> None:
self._put_responses[path] = data
def set_delete_status(self, status: int) -> None:
self._delete_status = status
async def get(self, path: str, **kwargs) -> httpx.Response: async def get(self, path: str, **kwargs) -> httpx.Response:
self.get_calls.append({"path": path, **kwargs}) self.get_calls.append({"path": path, **kwargs})
data = self._get_responses.get(path, {"id": "resp_" + path.split("/")[-1]}) data = self._get_responses.get(path, {"id": "resp_" + path.split("/")[-1]})
@@ -71,13 +63,11 @@ class _FakeHTTPClient:
async def put(self, path: str, *, json: dict[str, Any] | None = None, **kwargs) -> httpx.Response: async def put(self, path: str, *, json: dict[str, Any] | None = None, **kwargs) -> httpx.Response:
self.put_calls.append({"path": path, "json": json}) self.put_calls.append({"path": path, "json": json})
data = self._put_responses.get(path, {"id": path.split("/")[-1]}) return self._resp(200, {"id": path.split("/")[-1]}, "PUT", path)
return self._resp(200, data, "PUT", path)
async def delete(self, path: str, **kwargs) -> httpx.Response: async def delete(self, path: str, **kwargs) -> httpx.Response:
self.delete_calls.append({"path": path}) self.delete_calls.append({"path": path})
status = self._delete_status if self._delete_status is not None else 200 return self._resp(200, {}, "DELETE", path)
return self._resp(status, {}, "DELETE", path)
async def aclose(self) -> None: async def aclose(self) -> None:
pass pass
@@ -119,7 +109,6 @@ def test_config_defaults():
assert config.server_url == "" assert config.server_url == ""
assert config.token == "" assert config.token == ""
assert config.streaming is True assert config.streaming is True
assert config.streaming_max_chars == 16000
assert config.send_tool_hints is True assert config.send_tool_hints is True
assert config.dm.enabled is True assert config.dm.enabled is True
assert config.dm.policy == "open" assert config.dm.policy == "open"
@@ -150,7 +139,6 @@ def test_config_camelcase_aliases():
"serverUrl": "https://mm.example.com", "serverUrl": "https://mm.example.com",
"token": "abc123", "token": "abc123",
"allowFromMatchMode": "username", "allowFromMatchMode": "username",
"streamingMaxChars": 8000,
"replyInThread": False, "replyInThread": False,
"sendToolHints": False, "sendToolHints": False,
} }
@@ -158,7 +146,6 @@ def test_config_camelcase_aliases():
assert config.server_url == "https://mm.example.com" assert config.server_url == "https://mm.example.com"
assert config.token == "abc123" assert config.token == "abc123"
assert config.allow_from_match_mode == "username" assert config.allow_from_match_mode == "username"
assert config.streaming_max_chars == 8000
assert config.reply_in_thread is False assert config.reply_in_thread is False
assert config.send_tool_hints is False assert config.send_tool_hints is False
@@ -194,7 +181,6 @@ async def test_start_identifies_bot():
assert channel._self_id == "botuserid123" assert channel._self_id == "botuserid123"
assert channel._self_username == "nanobot" assert channel._self_username == "nanobot"
assert channel._self_email == "bot@example.com"
assert not start_task.done() assert not start_task.done()
user_me_calls = [c for c in fake.get_calls[calls_before:] if "/api/v4/users/me" in c["path"]] user_me_calls = [c for c in fake.get_calls[calls_before:] if "/api/v4/users/me" in c["path"]]
assert len(user_me_calls) == 1 assert len(user_me_calls) == 1
+4 -4
View File
@@ -277,7 +277,7 @@ class MochatChannel(BaseChannel):
self.config: MochatConfig = config self.config: MochatConfig = config
self._http: httpx.AsyncClient | None = None self._http: httpx.AsyncClient | None = None
self._socket: Any = None self._socket: Any = None
self._ws_connected = self._ws_ready = False self._ws_ready = False
self._state_dir = get_runtime_subdir("mochat") self._state_dir = get_runtime_subdir("mochat")
self._cursor_path = self._state_dir / "session_cursors.json" self._cursor_path = self._state_dir / "session_cursors.json"
@@ -346,7 +346,7 @@ class MochatChannel(BaseChannel):
if self._http: if self._http:
await self._http.aclose() await self._http.aclose()
self._http = None self._http = None
self._ws_connected = self._ws_ready = False self._ws_ready = False
async def send(self, msg: OutboundMessage) -> None: async def send(self, msg: OutboundMessage) -> None:
"""Send outbound message to session or panel.""" """Send outbound message to session or panel."""
@@ -422,7 +422,7 @@ class MochatChannel(BaseChannel):
) )
async def connect() -> None: async def connect() -> None:
self._ws_connected, self._ws_ready = True, False self._ws_ready = False
self.logger.info("websocket connected") self.logger.info("websocket connected")
subscribed = await self._subscribe_all() subscribed = await self._subscribe_all()
self._ws_ready = subscribed self._ws_ready = subscribed
@@ -431,7 +431,7 @@ class MochatChannel(BaseChannel):
async def disconnect() -> None: async def disconnect() -> None:
if not self._running: if not self._running:
return return
self._ws_connected = self._ws_ready = False self._ws_ready = False
self.logger.warning("websocket disconnected") self.logger.warning("websocket disconnected")
await self._ensure_fallback_workers() await self._ensure_fallback_workers()
@@ -363,13 +363,6 @@ def test_reported_daily_brief_pattern():
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def _resolve_chunk_styles(text: str, max_len: int) -> tuple[list[str], list[list[str]]]:
"""Helper: full markdown → signal pipeline, including chunking."""
plain, styles = _markdown_to_signal(text)
chunks = split_message(plain, max_len) if plain else [""]
return chunks, _partition_styles(plain, chunks, styles)
def test_partition_styles_single_chunk_passthrough(): def test_partition_styles_single_chunk_passthrough():
plain, styles = _markdown_to_signal("**bold** plain *it*") plain, styles = _markdown_to_signal("**bold** plain *it*")
parts = _partition_styles(plain, [plain], styles) parts = _partition_styles(plain, [plain], styles)
-1
View File
@@ -69,7 +69,6 @@ class SlackConfig(Base):
webhook_path: str = "/slack/events" webhook_path: str = "/slack/events"
bot_token: str = "" bot_token: str = ""
app_token: str = "" app_token: str = ""
user_token_read_only: bool = True
reply_in_thread: bool = True reply_in_thread: bool = True
react_emoji: str = "eyes" react_emoji: str = "eyes"
done_emoji: str = "white_check_mark" done_emoji: str = "white_check_mark"
@@ -1244,39 +1244,6 @@ async def test_pairing_routes_require_token_and_approve_or_deny(
assert "Missing pairing code" in missing_code.text assert "Missing pairing code" in missing_code.text
def test_api_service_settings_read_api_key_from_webui_payload(bus: MagicMock) -> None:
channel = _ch(bus)
request = _FakeReq(path="/api/settings/api-service/start")
setattr(
request,
"_nanobot_webui_mutation_payload",
{"host": "0.0.0.0", "port": 8900, "timeout": 120, "api_key": "secret-token"},
)
query = channel.gateway.http.settings_routes._parse_api_service_settings_query(request)
assert query == {
"host": ["0.0.0.0"],
"port": ["8900"],
"timeout": ["120"],
"api_key": ["secret-token"],
}
def test_api_service_settings_reject_non_string_api_key(bus: MagicMock) -> None:
from nanobot.webui.settings_api import WebUISettingsError
channel = _ch(bus)
request = _FakeReq(path="/api/settings/api-service/start")
setattr(
request,
"_nanobot_webui_mutation_payload",
{"host": "127.0.0.1", "api_key": 123},
)
with pytest.raises(WebUISettingsError, match="API key must be a string"):
channel.gateway.http.settings_routes._parse_api_service_settings_query(request)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_nanobot_feature_remote_install_requires_opt_in( async def test_nanobot_feature_remote_install_requires_opt_in(
bus: MagicMock, bus: MagicMock,
@@ -202,12 +202,6 @@ class WsTestClient:
assert msg.event == "delta", f"Expected 'delta' event, got '{msg.event}'" assert msg.event == "delta", f"Expected 'delta' event, got '{msg.event}'"
return msg return msg
async def recv_stream_end(self, timeout: float = 10.0) -> WsMessage:
"""Receive and validate a 'stream_end' event."""
msg = await self.recv(timeout)
assert msg.event == "stream_end", f"Expected 'stream_end' event, got '{msg.event}'"
return msg
async def collect_stream(self, timeout: float = 10.0) -> list[WsMessage]: async def collect_stream(self, timeout: float = 10.0) -> list[WsMessage]:
"""Collect all deltas and the final stream_end into a list.""" """Collect all deltas and the final stream_end into a list."""
messages: list[WsMessage] = [] messages: list[WsMessage] = []
@@ -232,10 +226,6 @@ class WsTestClient:
"""Send a JSON frame.""" """Send a JSON frame."""
await self.ws.send(json.dumps(data, ensure_ascii=False)) await self.ws.send(json.dumps(data, ensure_ascii=False))
async def send_content(self, content: str) -> None:
"""Send content in the preferred JSON format ``{"content": ...}``."""
await self.send_json({"content": content})
# -- Connection introspection ----------------------------------------- # -- Connection introspection -----------------------------------------
@property @property
-16
View File
@@ -96,22 +96,6 @@ class ManagedProcessRuntime(Generic[_StartOptionsT]):
# it; poll() both reaps it and reports the real lifecycle state. # it; poll() both reaps it and reports the real lifecycle state.
self._owned_process: Any | None = None self._owned_process: Any | None = None
@classmethod
def refresh_state_pid(cls, *, paths: ProcessRuntimePaths) -> None:
"""Update a managed state file after the recorded process restarts."""
if not paths.state_path.exists():
return
try:
state = json.loads(paths.state_path.read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError):
return
state["pid"] = os.getpid()
runtime = cls(paths=paths)
state.pop("stable_identity", None)
state.update(runtime.process_identity_record(os.getpid()))
state["started_at"] = _utc_now()
runtime._write_state(state)
def start_background(self, options: _StartOptionsT) -> ProcessResult: def start_background(self, options: _StartOptionsT) -> ProcessResult:
"""Start the configured command as a detached process.""" """Start the configured command as a detached process."""
with self._lifecycle_lock(): with self._lifecycle_lock():
-9
View File
@@ -685,15 +685,6 @@ class WebuiTurnCoordinator:
) )
) )
async def publish_run_status(
self,
msg: InboundMessage,
status: str,
*,
started_at: float | None = None,
) -> None:
await publish_turn_run_status(self.bus, msg, status, started_at=started_at)
async def handle_turn_end( async def handle_turn_end(
self, self,
msg: InboundMessage, msg: InboundMessage,
-3
View File
@@ -63,9 +63,6 @@ class GatewayTokenStore:
self.api_tokens[token_value] = expiry self.api_tokens[token_value] = expiry
return token_value return token_value
def take_issued_token_if_valid(self, token_value: str | None) -> bool:
return self.take_issued_token_audience(token_value) is not None
def take_issued_token_audience( def take_issued_token_audience(
self, self,
token_value: str | None, token_value: str | None,
-61
View File
@@ -36,7 +36,6 @@ from nanobot.webui.nanobot_features_api import (
nanobot_features_payload, nanobot_features_payload,
) )
from nanobot.webui.settings_api import ( from nanobot.webui.settings_api import (
WebUISettingsError,
complete_oauth_provider, complete_oauth_provider,
create_model_configuration, create_model_configuration,
create_provider_settings, create_provider_settings,
@@ -490,17 +489,6 @@ class WebUISettingsRouter:
lambda: request_image_generation_reload(self.bus), lambda: request_image_generation_reload(self.bus),
) )
async def _apply_image_generation_runtime_change(
self,
payload: dict[str, Any],
) -> dict[str, Any]:
updated, restart_cleared = (
await self._apply_image_generation_runtime_change_result(payload)
)
if restart_cleared:
self._restart_sections.discard("image")
return updated
async def _reload_mcp_runtime(self) -> dict[str, Any]: async def _reload_mcp_runtime(self) -> dict[str, Any]:
if self._mcp_reload is None: if self._mcp_reload is None:
return { return {
@@ -531,47 +519,9 @@ class WebUISettingsRouter:
def _parse_mcp_settings_query(self, request: WsRequest) -> QueryParams: def _parse_mcp_settings_query(self, request: WsRequest) -> QueryParams:
return self._query(request) return self._query(request)
def _parse_provider_settings_query(self, request: WsRequest) -> QueryParams:
return self._query(request)
def _parse_api_service_settings_query(self, request: WsRequest) -> QueryParams:
payload = _mutation_payload(request)
if payload is not None:
api_key = payload.get("api_key")
if api_key is not None and not isinstance(api_key, str):
raise WebUISettingsError("API service API key must be a string")
return self._query(request)
def _api_runtime(self) -> ApiRuntime: def _api_runtime(self) -> ApiRuntime:
return ApiRuntime(paths=api_runtime_paths(self.settings.config.path)) return ApiRuntime(paths=api_runtime_paths(self.settings.config.path))
def _api_service_payload(
self,
*,
last_action: str | None = None,
) -> dict[str, Any]:
return capability_domain.api_service_payload(
self.settings,
self._api_runtime(),
last_action=last_action,
)
@staticmethod
def _masked_secret(value: str) -> str | None:
return capability_domain.masked_api_secret(value)
@staticmethod
def _api_runtime_message(message: str) -> str:
return capability_domain.api_runtime_message(message)
def _parse_channel_values(self, request: WsRequest) -> dict[str, Any]:
return self._system.parse_channel_values(
SettingsRequest(
query=self._query(request),
payload=_mutation_payload(request),
)
)
def _save_channel_config_values( def _save_channel_config_values(
self, self,
name: str, name: str,
@@ -610,17 +560,6 @@ class WebUISettingsRouter:
allow_install=allow_install, allow_install=allow_install,
) )
@staticmethod
def _feature_runtime_fallback(
payload: dict[str, Any],
*,
message: str,
) -> dict[str, Any]:
return system_domain.SystemSettingsHandler.feature_runtime_fallback(
payload,
message=message,
)
def _allow_feature_package_install( def _allow_feature_package_install(
self, self,
connection: Any, connection: Any,
-1
View File
@@ -29,7 +29,6 @@ dependencies = [
"pydantic-settings>=2.12.0,<3.0.0", "pydantic-settings>=2.12.0,<3.0.0",
# Feishu's lark-oapi currently requires websockets<16; core supports 15 and 16. # Feishu's lark-oapi currently requires websockets<16; core supports 15 and 16.
"websockets>=15.0,<17.0", "websockets>=15.0,<17.0",
"websocket-client>=1.9.0,<2.0.0",
"httpx[socks]>=0.28.0,<1.0.0", "httpx[socks]>=0.28.0,<1.0.0",
"ddgs>=9.5.5,<10.0.0", "ddgs>=9.5.5,<10.0.0",
"oauth-cli-kit>=0.1.6,<1.0.0", "oauth-cli-kit>=0.1.6,<1.0.0",
+1 -1
View File
@@ -5,7 +5,7 @@ import { PickerMenu, type PickerMenuTheme } from "./picker-menu"
export type CommandMenuTheme = PickerMenuTheme export type CommandMenuTheme = PickerMenuTheme
export type TuiCommandAction = type TuiCommandAction =
| "sessions" | "sessions"
| "new-chat" | "new-chat"
| "context" | "context"
+3 -3
View File
@@ -29,14 +29,14 @@ export interface FileEditEvent {
diff?: FileDiff diff?: FileDiff
} }
export interface FileDiff { interface FileDiff {
format: "unified" | string format: "unified" | string
context?: number context?: number
truncated?: boolean truncated?: boolean
text?: string text?: string
} }
export interface MediaAttachment { interface MediaAttachment {
kind: "image" | "video" | "file" kind: "image" | "video" | "file"
url: string url: string
name?: string name?: string
@@ -225,7 +225,7 @@ export interface SessionContextSnapshot {
lastUsage: TokenUsage | null lastUsage: TokenUsage | null
} }
export interface SessionMention { interface SessionMention {
name: string name: string
session_key: string session_key: string
title?: string title?: string
+1 -1
View File
@@ -1,6 +1,6 @@
import type { TFunction } from "i18next"; import type { TFunction } from "i18next";
export type ChannelFieldMessages = { type ChannelFieldMessages = {
label: string; label: string;
placeholder?: string; placeholder?: string;
help?: string; help?: string;
+3 -3
View File
@@ -137,7 +137,7 @@ export function CapabilityMentionToken({
return <SessionMentionToken mention={segment.mention} label={segment.text} variant={variant} />; return <SessionMentionToken mention={segment.mention} label={segment.text} variant={variant} />;
} }
export function SessionMentionToken({ function SessionMentionToken({
mention, mention,
label, label,
variant, variant,
@@ -172,7 +172,7 @@ export function SessionMentionToken({
); );
} }
export function CliAppMentionToken({ function CliAppMentionToken({
app, app,
label, label,
variant, variant,
@@ -229,7 +229,7 @@ export function CliAppMentionToken({
); );
} }
export function McpPresetMentionToken({ function McpPresetMentionToken({
preset, preset,
label, label,
variant, variant,
+2 -2
View File
@@ -151,7 +151,7 @@ export function splitFilePath(path: string): { directory: string; name: string }
}; };
} }
export function fileKindForPath(path: string): FileReferenceKind { function fileKindForPath(path: string): FileReferenceKind {
const normalized = path.toLowerCase(); const normalized = path.toLowerCase();
const name = normalized.split(/[\\/]/).pop() ?? normalized; const name = normalized.split(/[\\/]/).pop() ?? normalized;
const ext = name.includes(".") ? name.split(".").pop() ?? "" : ""; const ext = name.includes(".") ? name.split(".").pop() ?? "" : "";
@@ -193,7 +193,7 @@ export function fileKindForPath(path: string): FileReferenceKind {
} }
} }
export function FileReferenceIcon({ kind }: { kind: FileReferenceKind }) { function FileReferenceIcon({ kind }: { kind: FileReferenceKind }) {
if (kind === "python") { if (kind === "python") {
return ( return (
<svg <svg
+2 -2
View File
@@ -967,7 +967,7 @@ interface ReasoningBubbleProps {
hasBodyBelow: boolean; hasBodyBelow: boolean;
} }
export function ReasoningBubble({ function ReasoningBubble({
text, text,
streaming, streaming,
hasBodyBelow, hasBodyBelow,
@@ -993,7 +993,7 @@ interface TraceGroupProps {
* collapsed because tool traces are supporting evidence, not the answer. * collapsed because tool traces are supporting evidence, not the answer.
* A single click expands the exact calls when the user wants details. * A single click expands the exact calls when the user wants details.
*/ */
export function TraceGroup({ message }: TraceGroupProps) { function TraceGroup({ message }: TraceGroupProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const lines = message.traces ?? [message.content]; const lines = message.traces ?? [message.content];
const count = lines.length; const count = lines.length;
@@ -174,7 +174,7 @@ export function ChannelLogo({
); );
} }
export function channelDisplayName(feature: NanobotFeatureInfo): string { function channelDisplayName(feature: NanobotFeatureInfo): string {
return channelUiPresentation(feature.name, feature.webui)?.displayName ?? feature.display_name; return channelUiPresentation(feature.name, feature.webui)?.displayName ?? feature.display_name;
} }
@@ -112,7 +112,7 @@ export function ChannelSetupLinks({
); );
} }
export function ChannelOfficialLink({ function ChannelOfficialLink({
feature, feature,
setup, setup,
}: { }: {
@@ -6,7 +6,7 @@ import { Input } from "@/components/ui/input";
import type { ChannelConfigField } from "@/components/settings/channels/catalog"; import type { ChannelConfigField } from "@/components/settings/channels/catalog";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export function channelFieldValue(field: ChannelConfigField, values: Record<string, string>): string { function channelFieldValue(field: ChannelConfigField, values: Record<string, string>): string {
return values[field.key] ?? field.defaultValue ?? field.options?.[0]?.value ?? ""; return values[field.key] ?? field.defaultValue ?? field.options?.[0]?.value ?? "";
} }
@@ -27,7 +27,7 @@ export type ChannelSetupPresentation = {
presets?: ChannelProviderPreset[]; presets?: ChannelProviderPreset[];
}; };
export type ChannelCatalogSetupPresentation = { type ChannelCatalogSetupPresentation = {
mode?: "webui" | "credentials" | "connect"; mode?: "webui" | "credentials" | "connect";
command?: string; command?: string;
docsUrl?: string; docsUrl?: string;
@@ -38,15 +38,15 @@ export type ChannelCatalogSetupPresentation = {
presets?: ChannelProviderPresetDefinition[]; presets?: ChannelProviderPresetDefinition[];
}; };
export type ChannelFieldPresentation = { type ChannelFieldPresentation = {
key: string; key: string;
}; };
export type ChannelSetupActionDefinition = Omit<ChannelSetupAction, "label">; type ChannelSetupActionDefinition = Omit<ChannelSetupAction, "label">;
export type ChannelProviderPresetDefinition = Omit<ChannelProviderPreset, "label">; export type ChannelProviderPresetDefinition = Omit<ChannelProviderPreset, "label">;
export type ChannelSetupAction = { type ChannelSetupAction = {
id: string; id: string;
label: string; label: string;
url?: string; url?: string;
@@ -72,7 +72,7 @@ export type ChannelConfigField = {
options?: ChannelConfigOption[]; options?: ChannelConfigOption[];
}; };
export type ChannelConfigOption = { type ChannelConfigOption = {
value: string; value: string;
label: string; label: string;
}; };
+1 -1
View File
@@ -14,7 +14,7 @@ export type SettingsSectionKey =
| "runtime" | "runtime"
| "advanced"; | "advanced";
export type PendingRestartSection = "runtime" | "browser" | "image"; type PendingRestartSection = "runtime" | "browser" | "image";
export type PendingRestartSections = Record<PendingRestartSection, boolean>; export type PendingRestartSections = Record<PendingRestartSection, boolean>;
export type RestartAwarePayload = { export type RestartAwarePayload = {
@@ -51,7 +51,7 @@ import type { CliAppInfo, McpPresetInfo, ToolProgressEvent, UIFileEdit, UIMessag
const ACTIVITY_SCROLL_NEAR_BOTTOM_PX = 24; const ACTIVITY_SCROLL_NEAR_BOTTOM_PX = 24;
export { isAgentActivityMember, isReasoningOnlyAssistant }; export { isAgentActivityMember };
interface ActivityCounts { interface ActivityCounts {
reasoningSteps: number; reasoningSteps: number;
@@ -155,7 +155,7 @@ function FileEditRow({
); );
} }
export function hasVisibleDiffStats(edit: Pick<FileEditSummary, "added" | "deleted">): boolean { function hasVisibleDiffStats(edit: Pick<FileEditSummary, "added" | "deleted">): boolean {
return edit.added > 0 || edit.deleted > 0; return edit.added > 0 || edit.deleted > 0;
} }
@@ -3,7 +3,7 @@ import { compactActivityPath, redactActivityText } from "./activity-text";
export type GenericToolStatus = "running" | "done" | "error"; export type GenericToolStatus = "running" | "done" | "error";
export type ToolFamily = "content-search" | "file-search" | "list" | "read" | "memory" | "generic"; export type ToolFamily = "content-search" | "file-search" | "list" | "read" | "memory" | "generic";
export interface ToolField { interface ToolField {
key: key:
| "query" | "query"
| "pattern" | "pattern"
@@ -7,7 +7,7 @@ import { displayWebHost, formatCompactWebUrl, parseSafeActivityHttpUrl } from ".
export type WebSearchStatus = "running" | "done" | "error"; export type WebSearchStatus = "running" | "done" | "error";
export type WebSearchTarget = "web" | "x"; export type WebSearchTarget = "web" | "x";
export interface WebSearchSource { interface WebSearchSource {
title: string; title: string;
href: string; href: string;
host: string; host: string;
@@ -26,13 +26,13 @@ export function userPromptAnchors(messages: UIMessage[]): PromptAnchor[] {
}); });
} }
export function promptLabel(content: string, index: number): string { function promptLabel(content: string, index: number): string {
const text = content.replace(/\s+/g, " ").trim(); const text = content.replace(/\s+/g, " ").trim();
if (!text) return `Prompt ${index + 1}`; if (!text) return `Prompt ${index + 1}`;
return truncatePreview(text, 80); return truncatePreview(text, 80);
} }
export function promptPreview(content: string, index: number): string { function promptPreview(content: string, index: number): string {
const text = compactPreview(content); const text = compactPreview(content);
if (!text) return `Prompt ${index + 1}`; if (!text) return `Prompt ${index + 1}`;
return truncatePreview(text, 320); return truncatePreview(text, 320);
+1 -1
View File
@@ -33,7 +33,7 @@ const buttonVariants = cva(
}, },
); );
export interface ButtonProps interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>, extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> { VariantProps<typeof buttonVariants> {
asChild?: boolean; asChild?: boolean;
+1 -1
View File
@@ -3,7 +3,7 @@ import * as React from "react";
import { formControlFocusClassName } from "@/components/ui/form-control"; import { formControlFocusClassName } from "@/components/ui/form-control";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>; type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
const Input = React.forwardRef<HTMLInputElement, InputProps>( const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => { ({ className, type, ...props }, ref) => {
+1 -1
View File
@@ -3,7 +3,7 @@ import * as React from "react";
import { formControlFocusClassName } from "@/components/ui/form-control"; import { formControlFocusClassName } from "@/components/ui/form-control";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>; type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => { ({ className, ...props }, ref) => {
@@ -12,7 +12,7 @@ export type {
export const MAX_WORKBENCH_PANES = 4; export const MAX_WORKBENCH_PANES = 4;
export const WORKBENCH_LAYOUTS = [ const WORKBENCH_LAYOUTS = [
"columns", "columns",
"rows", "rows",
"grid", "grid",
+5 -5
View File
@@ -9,10 +9,10 @@ import type { WebUIIngressLimits } from "@/lib/types";
* - ``ready`` ``dataUrl`` available; safe to submit * - ``ready`` ``dataUrl`` available; safe to submit
* - ``error`` validation / decode failure; chip shows inline error * - ``error`` validation / decode failure; chip shows inline error
*/ */
export type AttachmentStatus = "encoding" | "ready" | "error"; type AttachmentStatus = "encoding" | "ready" | "error";
export type AttachmentKind = "image" | "file"; export type AttachmentKind = "image" | "file";
export interface AttachedAttachment { interface AttachedAttachment {
id: string; id: string;
kind: AttachmentKind; kind: AttachmentKind;
file: File; file: File;
@@ -32,7 +32,7 @@ export interface AttachedAttachment {
export type AttachedImage = AttachedAttachment; export type AttachedImage = AttachedAttachment;
export interface RestoredReadyAttachment { interface RestoredReadyAttachment {
dataUrl: string; dataUrl: string;
name?: string; name?: string;
kind?: AttachmentKind; kind?: AttachmentKind;
@@ -55,8 +55,8 @@ export type AttachmentError =
| "io"; // file read failed at the browser layer | "io"; // file read failed at the browser layer
export const MAX_ATTACHMENTS_PER_MESSAGE = 4; export const MAX_ATTACHMENTS_PER_MESSAGE = 4;
export const MAX_ATTACHMENT_BYTES = 6 * 1024 * 1024; const MAX_ATTACHMENT_BYTES = 6 * 1024 * 1024;
export const MAX_TOTAL_ATTACHMENT_BYTES = 24 * 1024 * 1024; const MAX_TOTAL_ATTACHMENT_BYTES = 24 * 1024 * 1024;
/** MIME whitelist — mirrors the server's and the ``<input accept>`` attr. */ /** MIME whitelist — mirrors the server's and the ``<input accept>`` attr. */
const ACCEPTED_IMAGE_MIMES: ReadonlySet<string> = new Set([ const ACCEPTED_IMAGE_MIMES: ReadonlySet<string> = new Set([
+2 -2
View File
@@ -11,7 +11,7 @@ import { acceptedAttachmentKind } from "@/hooks/useAttachedImages";
* - Plain text pasted alongside attachments is *not* consumed by this helper, * - Plain text pasted alongside attachments is *not* consumed by this helper,
* so the caller can still let the textarea receive it naturally. * so the caller can still let the textarea receive it naturally.
*/ */
export function extractImageFilesFromPaste( function extractImageFilesFromPaste(
event: ClipboardEvent | React.ClipboardEvent, event: ClipboardEvent | React.ClipboardEvent,
): File[] { ): File[] {
const clipboard = (event as ClipboardEvent).clipboardData const clipboard = (event as ClipboardEvent).clipboardData
@@ -27,7 +27,7 @@ export function extractImageFilesFromPaste(
} }
/** Extract dropped attachment files, mirroring ``extractImageFilesFromPaste``. */ /** Extract dropped attachment files, mirroring ``extractImageFilesFromPaste``. */
export function extractImageFilesFromDrop( function extractImageFilesFromDrop(
event: DragEvent | React.DragEvent, event: DragEvent | React.DragEvent,
): File[] { ): File[] {
const dt = (event as DragEvent).dataTransfer const dt = (event as DragEvent).dataTransfer
+2 -2
View File
@@ -5,7 +5,7 @@ import { normalizeWorkbenchState } from "@/components/workbench/workbench-model"
import { fetchSidebarState } from "@/lib/api"; import { fetchSidebarState } from "@/lib/api";
import type { ChatSummary, SidebarStatePayload } from "@/lib/types"; import type { ChatSummary, SidebarStatePayload } from "@/lib/types";
export const DEFAULT_SIDEBAR_STATE: SidebarStatePayload = { const DEFAULT_SIDEBAR_STATE: SidebarStatePayload = {
schema_version: 1, schema_version: 1,
pinned_keys: [], pinned_keys: [],
archived_keys: [], archived_keys: [],
@@ -74,7 +74,7 @@ function boolMap(value: unknown): Record<string, boolean> {
return out; return out;
} }
export function normalizeSidebarState(raw: unknown): SidebarStatePayload { function normalizeSidebarState(raw: unknown): SidebarStatePayload {
if (!raw || typeof raw !== "object" || Array.isArray(raw)) { if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
return { ...DEFAULT_SIDEBAR_STATE, view: { ...DEFAULT_SIDEBAR_STATE.view } }; return { ...DEFAULT_SIDEBAR_STATE, view: { ...DEFAULT_SIDEBAR_STATE.view } };
} }
+1 -1
View File
@@ -51,7 +51,7 @@ export function normalizeLocale(
return baseMatch?.code ?? defaultLocale; return baseMatch?.code ?? defaultLocale;
} }
export function readStoredLocale(): SupportedLocale | null { function readStoredLocale(): SupportedLocale | null {
if (typeof window === "undefined") return null; if (typeof window === "undefined") return null;
try { try {
const raw = window.localStorage.getItem(LOCALE_STORAGE_KEY); const raw = window.localStorage.getItem(LOCALE_STORAGE_KEY);
+1 -3
View File
@@ -10,7 +10,6 @@ import {
applyDocumentLocale, applyDocumentLocale,
defaultLocale, defaultLocale,
fallbackLocale, fallbackLocale,
LOCALE_STORAGE_KEY,
normalizeLocale, normalizeLocale,
persistLocale, persistLocale,
resolveInitialLocale, resolveInitialLocale,
@@ -47,7 +46,7 @@ export function currentLocale(): SupportedLocale {
return normalizeLocale(i18n.resolvedLanguage ?? i18n.language ?? defaultLocale); return normalizeLocale(i18n.resolvedLanguage ?? i18n.language ?? defaultLocale);
} }
export async function loadLocaleResources( async function loadLocaleResources(
locale: SupportedLocale, locale: SupportedLocale,
): Promise<LocaleResource> { ): Promise<LocaleResource> {
const existing = resourcePromises.get(locale); const existing = resourcePromises.get(locale);
@@ -131,5 +130,4 @@ function syncLocaleSideEffects(language: string) {
persistLocale(locale); persistLocale(locale);
} }
export { LOCALE_STORAGE_KEY };
export default i18n; export default i18n;
+1 -1
View File
@@ -3,7 +3,7 @@ export type AnsiSegment = {
style?: AnsiStyle; style?: AnsiStyle;
}; };
export type AnsiStyle = { type AnsiStyle = {
backgroundColor?: string; backgroundColor?: string;
color?: string; color?: string;
fontStyle?: "italic"; fontStyle?: "italic";
+1 -1
View File
@@ -1,4 +1,4 @@
export const DEFAULT_HTTP_TIMEOUT_MS = 20_000; const DEFAULT_HTTP_TIMEOUT_MS = 20_000;
export async function fetchWithTimeout( export async function fetchWithTimeout(
input: RequestInfo | URL, input: RequestInfo | URL,
+1 -1
View File
@@ -11,7 +11,7 @@ import {
type EncodeResponse, type EncodeResponse,
} from "@/workers/imageEncode.worker"; } from "@/workers/imageEncode.worker";
export type { EncodeResponse, EncodeSuccess, EncodeFailure } from "@/workers/imageEncode.worker"; export type { EncodeResponse, EncodeFailure } from "@/workers/imageEncode.worker";
type Pending = { type Pending = {
resolve: (r: EncodeResponse) => void; resolve: (r: EncodeResponse) => void;
+1 -1
View File
@@ -113,7 +113,7 @@ interface PendingWebUIRequest extends PendingRequest<unknown> {
serializedFrame: string; serializedFrame: string;
} }
export class WebUIMutationError extends Error { class WebUIMutationError extends Error {
status: number; status: number;
constructor(status: number, message: string) { constructor(status: number, message: string) {
+2 -2
View File
@@ -124,7 +124,7 @@ export function logoFallbackUrls(logoUrl: string | null | undefined): string[] {
return urls; return urls;
} }
export const PROVIDER_BRAND_ALIASES: Record<string, string> = { const PROVIDER_BRAND_ALIASES: Record<string, string> = {
brave_search: "brave", brave_search: "brave",
byteplus_coding_plan: "byteplus", byteplus_coding_plan: "byteplus",
mimo: "xiaomi_mimo", mimo: "xiaomi_mimo",
@@ -137,7 +137,7 @@ export const PROVIDER_BRAND_ALIASES: Record<string, string> = {
volcengine_coding_plan: "volcengine", volcengine_coding_plan: "volcengine",
}; };
export const PROVIDER_LABEL_ALIASES: Record<string, string> = { const PROVIDER_LABEL_ALIASES: Record<string, string> = {
brave_search: "Brave Search", brave_search: "Brave Search",
byteplus_coding_plan: "BytePlus", byteplus_coding_plan: "BytePlus",
minimaxAnthropic: "MiniMax", minimaxAnthropic: "MiniMax",
+3 -3
View File
@@ -10,7 +10,7 @@ export interface RuntimeHost {
exportDiagnostics?: () => Promise<string>; exportDiagnostics?: () => Promise<string>;
} }
export interface HostRuntimeInfo { interface HostRuntimeInfo {
surface: "native"; surface: "native";
app_version: string; app_version: string;
engine_status: "starting" | "ready" | "restarting" | "stopped" | "crashed"; engine_status: "starting" | "ready" | "restarting" | "stopped" | "crashed";
@@ -23,7 +23,7 @@ export interface HostRuntimeInfo {
engine_transport?: "unix_socket"; engine_transport?: "unix_socket";
} }
export interface NanobotHostApi { interface NanobotHostApi {
getRuntimeInfo?(): Promise<HostRuntimeInfo>; getRuntimeInfo?(): Promise<HostRuntimeInfo>;
restartEngine?(): Promise<void>; restartEngine?(): Promise<void>;
pickFolder?(): Promise<string | null>; pickFolder?(): Promise<string | null>;
@@ -40,7 +40,7 @@ export interface NanobotHostApi {
): () => void; ): () => void;
} }
export type HostSocketEvent = type HostSocketEvent =
| { id: string; type: "open" } | { id: string; type: "open" }
| { data: string; id: string; type: "message" } | { data: string; id: string; type: "message" }
| { id: string; message: string; type: "error" } | { id: string; message: string; type: "error" }
+27 -27
View File
@@ -1,8 +1,8 @@
export type Role = "user" | "assistant" | "tool" | "system"; type Role = "user" | "assistant" | "tool" | "system";
/** "trace" rows are intermediate agent breadcrumbs (tool-call hints, /** "trace" rows are intermediate agent breadcrumbs (tool-call hints,
* progress pings) that should not be rendered as conversational replies. */ * progress pings) that should not be rendered as conversational replies. */
export type MessageKind = "message" | "trace"; type MessageKind = "message" | "trace";
export type UITurnPhase = "user" | "reasoning" | "activity" | "answer" | "complete"; export type UITurnPhase = "user" | "reasoning" | "activity" | "answer" | "complete";
export type MessageDeliveryStatus = "sending" | "accepted" | "failed"; export type MessageDeliveryStatus = "sending" | "accepted" | "failed";
@@ -37,7 +37,7 @@ export interface UIMediaAttachment {
name?: string; name?: string;
} }
export interface UIMessageSource { kind: "cron" | "local_trigger" | "trigger" | string; label?: string; } interface UIMessageSource { kind: "cron" | "local_trigger" | "trigger" | string; label?: string; }
export interface TurnUsage { export interface TurnUsage {
prompt_tokens?: number; prompt_tokens?: number;
@@ -144,7 +144,7 @@ export interface SessionHandle {
name: string; name: string;
} }
export interface UISessionMessage { interface UISessionMessage {
message_id: string; message_id: string;
session: SessionHandle; session: SessionHandle;
} }
@@ -226,14 +226,14 @@ export interface SkillSummary {
unavailable_reason?: string; unavailable_reason?: string;
} }
export interface SkillRequirements { interface SkillRequirements {
bins: string[]; bins: string[];
env: string[]; env: string[];
missing_bins: string[]; missing_bins: string[];
missing_env: string[]; missing_env: string[];
} }
export interface SkillInstallOption { interface SkillInstallOption {
id: string; id: string;
kind: string; kind: string;
label: string; label: string;
@@ -303,7 +303,7 @@ export interface SkillInstallPayload extends SkillsPayload {
} }
/** Structured UI blob on ``progress`` WS frames; channels may add more ``kind`` values later. */ /** Structured UI blob on ``progress`` WS frames; channels may add more ``kind`` values later. */
export interface AgentUIBlob { interface AgentUIBlob {
kind: string; kind: string;
data?: unknown; data?: unknown;
} }
@@ -455,16 +455,16 @@ export interface BootstrapResponse {
runtime_capabilities?: RuntimeCapabilities; runtime_capabilities?: RuntimeCapabilities;
} }
export interface WebUITransportLimits { interface WebUITransportLimits {
max_frame_bytes: number; max_frame_bytes: number;
envelope_reserve_bytes: number; envelope_reserve_bytes: number;
} }
export interface WebUIMessageLimits { interface WebUIMessageLimits {
max_text_bytes: number; max_text_bytes: number;
} }
export interface WebUIAttachmentLimits { interface WebUIAttachmentLimits {
max_count: number; max_count: number;
max_file_bytes: number; max_file_bytes: number;
max_total_bytes: number; max_total_bytes: number;
@@ -477,8 +477,8 @@ export interface WebUIIngressLimits {
} }
export type RuntimeSurface = "browser" | "native"; export type RuntimeSurface = "browser" | "native";
export type RestartBehavior = "none" | "nextTurn" | "engineRestart" | "appRestart"; type RestartBehavior = "none" | "nextTurn" | "engineRestart" | "appRestart";
export type SettingsApplyStatus = type SettingsApplyStatus =
| "idle" | "idle"
| "pending" | "pending"
| "applying" | "applying"
@@ -492,7 +492,7 @@ export interface RuntimeCapabilities {
can_export_diagnostics: boolean; can_export_diagnostics: boolean;
} }
export interface ProviderModelInfo { interface ProviderModelInfo {
id: string; id: string;
label?: string | null; label?: string | null;
description?: string | null; description?: string | null;
@@ -783,12 +783,12 @@ export interface ApiServicePayload {
last_action?: "started" | "stopped" | string; last_action?: "started" | "stopped" | string;
} }
export interface AppPackageRef { interface AppPackageRef {
manager: string; manager: string;
name?: string; name?: string;
} }
export interface AppCapability { interface AppCapability {
type: "cli" | "mcp" | "skill" | string; type: "cli" | "mcp" | "skill" | string;
entry_point?: string; entry_point?: string;
package?: AppPackageRef; package?: AppPackageRef;
@@ -806,20 +806,20 @@ export interface AppCapability {
}>; }>;
} }
export interface AppPlan { interface AppPlan {
supported: boolean; supported: boolean;
strategy?: string; strategy?: string;
managed_paths?: string[]; managed_paths?: string[];
verification?: string[]; verification?: string[];
} }
export interface AppTrust { interface AppTrust {
registry: string; registry: string;
level: string; level: string;
review_status: string; review_status: string;
} }
export interface AppManifest { interface AppManifest {
schema: "agent-app.v1" | string; schema: "agent-app.v1" | string;
id: string; id: string;
display_name: string; display_name: string;
@@ -935,7 +935,7 @@ export interface NanobotFeaturesPayload {
}; };
} }
export type ChannelSetupStatus = type ChannelSetupStatus =
| "connected" | "connected"
| "configured" | "configured"
| "needs_setup" | "needs_setup"
@@ -943,9 +943,9 @@ export type ChannelSetupStatus =
| "unsupported" | "unsupported"
| string; | string;
export type ChannelValidationCheckStatus = "pass" | "warn" | "fail" | "skipped" | string; type ChannelValidationCheckStatus = "pass" | "warn" | "fail" | "skipped" | string;
export interface ChannelValidationCheck { interface ChannelValidationCheck {
id: string; id: string;
label: string; label: string;
status: ChannelValidationCheckStatus; status: ChannelValidationCheckStatus;
@@ -953,7 +953,7 @@ export interface ChannelValidationCheck {
action_url?: string; action_url?: string;
} }
export interface ChannelIdentity { interface ChannelIdentity {
name?: string; name?: string;
workspace?: string; workspace?: string;
account?: string; account?: string;
@@ -993,7 +993,7 @@ export interface PairingPayload {
}; };
} }
export interface McpPresetField { interface McpPresetField {
name: string; name: string;
label: string; label: string;
secret: boolean; secret: boolean;
@@ -1033,7 +1033,7 @@ export interface McpPresetInfo {
manifest?: AppManifest; manifest?: AppManifest;
} }
export type McpOAuthFlowStatus = type McpOAuthFlowStatus =
| "starting" | "starting"
| "authorization_required" | "authorization_required"
| "connecting" | "connecting"
@@ -1089,7 +1089,7 @@ export interface McpPresetsPayload {
}; };
} }
export type ChannelConnectStatus = "pending" | "succeeded" | "expired" | "cancelled" | "failed"; type ChannelConnectStatus = "pending" | "succeeded" | "expired" | "cancelled" | "failed";
export interface ChannelConnectPayload { export interface ChannelConnectPayload {
session_id: string; session_id: string;
@@ -1234,7 +1234,7 @@ export type ConnectionStatus =
| "closed" | "closed"
| "error"; | "error";
export interface InboundTurnMetadata { interface InboundTurnMetadata {
turn_id?: string; turn_id?: string;
turn_phase?: UITurnPhase; turn_phase?: UITurnPhase;
turn_seq?: number; turn_seq?: number;
@@ -1430,7 +1430,7 @@ export interface OutboundMcpPresetMention {
} }
/** Response shape for ``GET .../webui-thread`` (server-built transcript replay). */ /** Response shape for ``GET .../webui-thread`` (server-built transcript replay). */
export interface WebuiThreadPagePayload { interface WebuiThreadPagePayload {
before_cursor?: string | null; before_cursor?: string | null;
has_more_before?: boolean; has_more_before?: boolean;
loaded_message_count?: number; loaded_message_count?: number;
+1 -1
View File
@@ -23,7 +23,7 @@ export type EncodeInput = {
file: File; file: File;
}; };
export type EncodeSuccess = { type EncodeSuccess = {
id: string; id: string;
ok: true; ok: true;
dataUrl: string; dataUrl: string;