chore(release): prepare v0.2.2

This commit is contained in:
Xubin Ren
2026-06-22 20:53:28 +08:00
parent 7170761e47
commit 83c29292d3
11 changed files with 53 additions and 49 deletions
+2 -1
View File
@@ -56,6 +56,7 @@
## 📢 News
- **2026-06-22** 🚀 Released **v0.2.2****The SDK & WebUI Durability Release** makes the Python SDK a first-class runtime, adds segmented WebUI transcripts, automation management, richer search/STT providers, token usage visibility, and stronger session/provider reliability. Please see [release notes](https://github.com/HKUDS/nanobot/releases/tag/v0.2.2) for details.
- **2026-06-19** 🔎 Firecrawl app, OpenAI image edits, safer session deletion.
- **2026-06-18** 💬 Feishu recovery, Keenable search, Mistral polish, workspace-aware git.
- **2026-06-17** 🧠 Default idle auto-compact, clearer `/dream`, macOS installer fixes.
@@ -65,11 +66,11 @@
- **2026-06-13** 🗓️ Session-bound automations, sturdier WhatsApp, faster WebUI startup.
- **2026-06-12** 💬 Slack allowlisted channels can require mentions.
- **2026-06-11** ✂️ Fenced-code message splitting.
- **2026-06-10** 📜 Segmented transcripts, Exa/Bocha search, StepFun/SiliconFlow ASR.
<details>
<summary>Earlier news</summary>
- **2026-06-10** 📜 Segmented transcripts, Exa/Bocha search, StepFun/SiliconFlow ASR.
- **2026-06-09** 🎙️ Shared voice input, more STT providers, TeX and email polish.
- **2026-06-08** 🧮 Token heatmap fix, safer MCP HTTP probing, docs cleanup.
- **2026-06-06** 🧰 SDK MCP cleanup, removable OpenAI image defaults.
+1 -1
View File
@@ -22,7 +22,7 @@ def _resolve_version() -> str:
return _pkg_version("nanobot-ai")
except PackageNotFoundError:
# Source checkouts often import nanobot without installed dist-info.
return _read_pyproject_version() or "0.2.1"
return _read_pyproject_version() or "0.2.2"
__version__ = _resolve_version()
+8 -2
View File
@@ -8,10 +8,16 @@ from typing import Any
from pydantic import Field
from nanobot.agent.tools.base import Tool, tool_parameters
from nanobot.agent.tools.schema import ArraySchema, BooleanSchema, IntegerSchema, StringSchema, tool_parameters_schema
from nanobot.security.workspace_access import current_tool_workspace
from nanobot.agent.tools.schema import (
ArraySchema,
BooleanSchema,
IntegerSchema,
StringSchema,
tool_parameters_schema,
)
from nanobot.apps.cli import CliAppError, CliAppManager, CliAppsRuntimeConfig
from nanobot.config_base import Base
from nanobot.security.workspace_access import current_tool_workspace
class CliAppsToolConfig(Base):
+1 -1
View File
@@ -14,7 +14,6 @@ from nanobot.agent.tools.schema import (
StringSchema,
tool_parameters_schema,
)
from nanobot.security.workspace_access import current_tool_workspace
from nanobot.config.paths import get_media_dir
from nanobot.config_base import Base
from nanobot.providers.image_generation import (
@@ -22,6 +21,7 @@ from nanobot.providers.image_generation import (
ImageGenerationProvider,
get_image_gen_provider,
)
from nanobot.security.workspace_access import current_tool_workspace
from nanobot.security.workspace_policy import WorkspaceBoundaryError, resolve_allowed_path
from nanobot.utils.artifacts import (
ArtifactError,
+1 -1
View File
@@ -10,9 +10,9 @@ from nanobot.agent.tools.base import Tool, tool_parameters
from nanobot.agent.tools.context import ContextAware, RequestContext
from nanobot.agent.tools.path_utils import resolve_workspace_path
from nanobot.agent.tools.schema import ArraySchema, StringSchema, tool_parameters_schema
from nanobot.security.workspace_access import current_tool_workspace
from nanobot.bus.events import OutboundMessage
from nanobot.config.paths import get_workspace_path
from nanobot.security.workspace_access import current_tool_workspace
@tool_parameters(
+1 -1
View File
@@ -11,13 +11,13 @@ from datetime import datetime
from typing import Any
import httpx
from pydantic import Field
from nanobot.bus.events import OutboundMessage
from nanobot.bus.queue import MessageBus
from nanobot.channels.base import BaseChannel
from nanobot.config.paths import get_runtime_subdir
from nanobot.config.schema import Base
from pydantic import Field
try:
import socketio
+1 -1
View File
@@ -7,12 +7,12 @@ from nanobot.config.paths import (
get_cron_dir,
get_data_dir,
get_legacy_sessions_dir,
is_default_workspace,
get_logs_dir,
get_media_dir,
get_runtime_subdir,
get_webui_dir,
get_workspace_path,
is_default_workspace,
)
from nanobot.config.schema import Config
+1 -1
View File
@@ -32,8 +32,8 @@ if TYPE_CHECKING:
from nanobot.providers.azure_openai_provider import AzureOpenAIProvider
from nanobot.providers.bedrock_provider import BedrockProvider
from nanobot.providers.github_copilot_provider import GitHubCopilotProvider
from nanobot.providers.openai_compat_provider import OpenAICompatProvider
from nanobot.providers.openai_codex_provider import OpenAICodexProvider
from nanobot.providers.openai_compat_provider import OpenAICompatProvider
def __getattr__(name: str):
@@ -78,7 +78,7 @@ def package_skill(skill_path, output_dir=None):
skill_filename = output_path / f"{skill_name}.skill"
EXCLUDED_DIRS = {".git", ".svn", ".hg", "__pycache__", "node_modules"}
excluded_dirs = {".git", ".svn", ".hg", "__pycache__", "node_modules"}
files_to_package = []
resolved_archive = skill_filename.resolve()
@@ -91,7 +91,7 @@ def package_skill(skill_path, output_dir=None):
return None
rel_parts = file_path.relative_to(skill_path).parts
if any(part in EXCLUDED_DIRS for part in rel_parts):
if any(part in excluded_dirs for part in rel_parts):
continue
if file_path.is_file():
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "nanobot-ai"
version = "0.2.1"
version = "0.2.2"
description = "A lightweight personal AI assistant framework"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
+34 -37
View File
@@ -1102,12 +1102,7 @@ describe("ThreadShell", () => {
it("scrolls to the bottom after loading a session from the blank new-chat page", async () => {
const client = makeClient();
const scrollIntoView = vi.fn();
const scrollTo = vi.fn();
const originalScrollIntoView = HTMLElement.prototype.scrollIntoView;
const originalScrollTo = HTMLElement.prototype.scrollTo;
HTMLElement.prototype.scrollIntoView = scrollIntoView;
HTMLElement.prototype.scrollTo = scrollTo;
vi.stubGlobal(
"fetch",
vi.fn(async (input: RequestInfo | URL) => {
@@ -1128,47 +1123,49 @@ describe("ThreadShell", () => {
}),
);
try {
const { rerender } = render(
const { container, rerender } = render(
wrap(
client,
<ThreadShell
session={null}
title="nanobot"
onToggleSidebar={() => {}}
onNewChat={() => {}}
/>,
),
);
expect(screen.getByText(HERO_GREETING_PATTERN)).toBeInTheDocument();
const scroller = container.querySelector(".thread-viewport-scrollbar") as HTMLElement;
Object.defineProperties(scroller, {
scrollHeight: { configurable: true, value: 2400 },
clientHeight: { configurable: true, value: 600 },
scrollTop: { configurable: true, writable: true, value: 0 },
scrollTo: { configurable: true, value: scrollTo },
});
scrollTo.mockClear();
await act(async () => {
rerender(
wrap(
client,
<ThreadShell
session={null}
title="nanobot"
session={session("chat-a")}
title="Chat chat-a"
onToggleSidebar={() => {}}
onNewChat={() => {}}
/>,
),
);
});
expect(screen.getByText(HERO_GREETING_PATTERN)).toBeInTheDocument();
scrollIntoView.mockClear();
await act(async () => {
rerender(
wrap(
client,
<ThreadShell
session={session("chat-a")}
title="Chat chat-a"
onToggleSidebar={() => {}}
onNewChat={() => {}}
/>,
),
);
});
await waitFor(() => expect(screen.getByText("loaded answer")).toBeInTheDocument());
await waitFor(() =>
expect(scrollTo).toHaveBeenCalledWith({
top: 0,
behavior: "auto",
}),
);
} finally {
HTMLElement.prototype.scrollIntoView = originalScrollIntoView;
HTMLElement.prototype.scrollTo = originalScrollTo;
}
await waitFor(() => expect(screen.getByText("loaded answer")).toBeInTheDocument());
await waitFor(() =>
expect(scrollTo).toHaveBeenCalledWith({
top: 1800,
behavior: "auto",
}),
);
});
it("opens slash commands on the blank welcome page", async () => {