chore(release): prepare v0.2.2
This commit is contained in:
@@ -56,6 +56,7 @@
|
|||||||
|
|
||||||
## 📢 News
|
## 📢 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-19** 🔎 Firecrawl app, OpenAI image edits, safer session deletion.
|
||||||
- **2026-06-18** 💬 Feishu recovery, Keenable search, Mistral polish, workspace-aware git.
|
- **2026-06-18** 💬 Feishu recovery, Keenable search, Mistral polish, workspace-aware git.
|
||||||
- **2026-06-17** 🧠 Default idle auto-compact, clearer `/dream`, macOS installer fixes.
|
- **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-13** 🗓️ Session-bound automations, sturdier WhatsApp, faster WebUI startup.
|
||||||
- **2026-06-12** 💬 Slack allowlisted channels can require mentions.
|
- **2026-06-12** 💬 Slack allowlisted channels can require mentions.
|
||||||
- **2026-06-11** ✂️ Fenced-code message splitting.
|
- **2026-06-11** ✂️ Fenced-code message splitting.
|
||||||
- **2026-06-10** 📜 Segmented transcripts, Exa/Bocha search, StepFun/SiliconFlow ASR.
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Earlier news</summary>
|
<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-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-08** 🧮 Token heatmap fix, safer MCP HTTP probing, docs cleanup.
|
||||||
- **2026-06-06** 🧰 SDK MCP cleanup, removable OpenAI image defaults.
|
- **2026-06-06** 🧰 SDK MCP cleanup, removable OpenAI image defaults.
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ def _resolve_version() -> str:
|
|||||||
return _pkg_version("nanobot-ai")
|
return _pkg_version("nanobot-ai")
|
||||||
except PackageNotFoundError:
|
except PackageNotFoundError:
|
||||||
# Source checkouts often import nanobot without installed dist-info.
|
# 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()
|
__version__ = _resolve_version()
|
||||||
|
|||||||
@@ -8,10 +8,16 @@ from typing import Any
|
|||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from nanobot.agent.tools.base import Tool, tool_parameters
|
from nanobot.agent.tools.base import Tool, tool_parameters
|
||||||
from nanobot.agent.tools.schema import ArraySchema, BooleanSchema, IntegerSchema, StringSchema, tool_parameters_schema
|
from nanobot.agent.tools.schema import (
|
||||||
from nanobot.security.workspace_access import current_tool_workspace
|
ArraySchema,
|
||||||
|
BooleanSchema,
|
||||||
|
IntegerSchema,
|
||||||
|
StringSchema,
|
||||||
|
tool_parameters_schema,
|
||||||
|
)
|
||||||
from nanobot.apps.cli import CliAppError, CliAppManager, CliAppsRuntimeConfig
|
from nanobot.apps.cli import CliAppError, CliAppManager, CliAppsRuntimeConfig
|
||||||
from nanobot.config_base import Base
|
from nanobot.config_base import Base
|
||||||
|
from nanobot.security.workspace_access import current_tool_workspace
|
||||||
|
|
||||||
|
|
||||||
class CliAppsToolConfig(Base):
|
class CliAppsToolConfig(Base):
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ from nanobot.agent.tools.schema import (
|
|||||||
StringSchema,
|
StringSchema,
|
||||||
tool_parameters_schema,
|
tool_parameters_schema,
|
||||||
)
|
)
|
||||||
from nanobot.security.workspace_access import current_tool_workspace
|
|
||||||
from nanobot.config.paths import get_media_dir
|
from nanobot.config.paths import get_media_dir
|
||||||
from nanobot.config_base import Base
|
from nanobot.config_base import Base
|
||||||
from nanobot.providers.image_generation import (
|
from nanobot.providers.image_generation import (
|
||||||
@@ -22,6 +21,7 @@ from nanobot.providers.image_generation import (
|
|||||||
ImageGenerationProvider,
|
ImageGenerationProvider,
|
||||||
get_image_gen_provider,
|
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.security.workspace_policy import WorkspaceBoundaryError, resolve_allowed_path
|
||||||
from nanobot.utils.artifacts import (
|
from nanobot.utils.artifacts import (
|
||||||
ArtifactError,
|
ArtifactError,
|
||||||
|
|||||||
@@ -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.context import ContextAware, RequestContext
|
||||||
from nanobot.agent.tools.path_utils import resolve_workspace_path
|
from nanobot.agent.tools.path_utils import resolve_workspace_path
|
||||||
from nanobot.agent.tools.schema import ArraySchema, StringSchema, tool_parameters_schema
|
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.bus.events import OutboundMessage
|
||||||
from nanobot.config.paths import get_workspace_path
|
from nanobot.config.paths import get_workspace_path
|
||||||
|
from nanobot.security.workspace_access import current_tool_workspace
|
||||||
|
|
||||||
|
|
||||||
@tool_parameters(
|
@tool_parameters(
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ from datetime import datetime
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
from nanobot.bus.events import OutboundMessage
|
from nanobot.bus.events import OutboundMessage
|
||||||
from nanobot.bus.queue import MessageBus
|
from nanobot.bus.queue import MessageBus
|
||||||
from nanobot.channels.base import BaseChannel
|
from nanobot.channels.base import BaseChannel
|
||||||
from nanobot.config.paths import get_runtime_subdir
|
from nanobot.config.paths import get_runtime_subdir
|
||||||
from nanobot.config.schema import Base
|
from nanobot.config.schema import Base
|
||||||
from pydantic import Field
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import socketio
|
import socketio
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ from nanobot.config.paths import (
|
|||||||
get_cron_dir,
|
get_cron_dir,
|
||||||
get_data_dir,
|
get_data_dir,
|
||||||
get_legacy_sessions_dir,
|
get_legacy_sessions_dir,
|
||||||
is_default_workspace,
|
|
||||||
get_logs_dir,
|
get_logs_dir,
|
||||||
get_media_dir,
|
get_media_dir,
|
||||||
get_runtime_subdir,
|
get_runtime_subdir,
|
||||||
get_webui_dir,
|
get_webui_dir,
|
||||||
get_workspace_path,
|
get_workspace_path,
|
||||||
|
is_default_workspace,
|
||||||
)
|
)
|
||||||
from nanobot.config.schema import Config
|
from nanobot.config.schema import Config
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ if TYPE_CHECKING:
|
|||||||
from nanobot.providers.azure_openai_provider import AzureOpenAIProvider
|
from nanobot.providers.azure_openai_provider import AzureOpenAIProvider
|
||||||
from nanobot.providers.bedrock_provider import BedrockProvider
|
from nanobot.providers.bedrock_provider import BedrockProvider
|
||||||
from nanobot.providers.github_copilot_provider import GitHubCopilotProvider
|
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_codex_provider import OpenAICodexProvider
|
||||||
|
from nanobot.providers.openai_compat_provider import OpenAICompatProvider
|
||||||
|
|
||||||
|
|
||||||
def __getattr__(name: str):
|
def __getattr__(name: str):
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ def package_skill(skill_path, output_dir=None):
|
|||||||
|
|
||||||
skill_filename = output_path / f"{skill_name}.skill"
|
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 = []
|
files_to_package = []
|
||||||
resolved_archive = skill_filename.resolve()
|
resolved_archive = skill_filename.resolve()
|
||||||
@@ -91,7 +91,7 @@ def package_skill(skill_path, output_dir=None):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
rel_parts = file_path.relative_to(skill_path).parts
|
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
|
continue
|
||||||
|
|
||||||
if file_path.is_file():
|
if file_path.is_file():
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "nanobot-ai"
|
name = "nanobot-ai"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
description = "A lightweight personal AI assistant framework"
|
description = "A lightweight personal AI assistant framework"
|
||||||
readme = { file = "README.md", content-type = "text/markdown" }
|
readme = { file = "README.md", content-type = "text/markdown" }
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
|||||||
@@ -1102,12 +1102,7 @@ describe("ThreadShell", () => {
|
|||||||
|
|
||||||
it("scrolls to the bottom after loading a session from the blank new-chat page", async () => {
|
it("scrolls to the bottom after loading a session from the blank new-chat page", async () => {
|
||||||
const client = makeClient();
|
const client = makeClient();
|
||||||
const scrollIntoView = vi.fn();
|
|
||||||
const scrollTo = 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(
|
vi.stubGlobal(
|
||||||
"fetch",
|
"fetch",
|
||||||
vi.fn(async (input: RequestInfo | URL) => {
|
vi.fn(async (input: RequestInfo | URL) => {
|
||||||
@@ -1128,47 +1123,49 @@ describe("ThreadShell", () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
const { container, rerender } = render(
|
||||||
const { 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(
|
wrap(
|
||||||
client,
|
client,
|
||||||
<ThreadShell
|
<ThreadShell
|
||||||
session={null}
|
session={session("chat-a")}
|
||||||
title="nanobot"
|
title="Chat chat-a"
|
||||||
onToggleSidebar={() => {}}
|
onToggleSidebar={() => {}}
|
||||||
onNewChat={() => {}}
|
onNewChat={() => {}}
|
||||||
/>,
|
/>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
expect(screen.getByText(HERO_GREETING_PATTERN)).toBeInTheDocument();
|
await waitFor(() => expect(screen.getByText("loaded answer")).toBeInTheDocument());
|
||||||
scrollIntoView.mockClear();
|
await waitFor(() =>
|
||||||
|
expect(scrollTo).toHaveBeenCalledWith({
|
||||||
await act(async () => {
|
top: 1800,
|
||||||
rerender(
|
behavior: "auto",
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("opens slash commands on the blank welcome page", async () => {
|
it("opens slash commands on the blank welcome page", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user