Skip to content

Commit 9d12e34

Browse files
committed
chore: bump version to 1.8.6
- Bump core, node-sdk, and python-sdk to 1.8.6 - Add HeadlessConfig and BrowserBackend types to config - Add WebSearchParams type and web_search() method to SDKs - Fix issue #25: return error on unknown params (engine vs engines) - Change engines parameter type from string to array in schema - Update changelog
1 parent 3f2eb5c commit 9d12e34

13 files changed

Lines changed: 395 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,26 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased] - 2026-04-05
8+
## [Unreleased] - 2026-04-10
99

1010
---
1111

12-
## [v1.7.0] - 2026-04-05
12+
## [v1.8.6] - 2026-04-10
13+
14+
### Fixed
15+
16+
#### web_search Tool
17+
18+
- **Issue #25 Fix**: The `web_search` tool now returns an error when unknown parameters are passed (e.g., `engine` instead of `engines`). Previously, unknown parameters were silently ignored, causing confusion when users specified the wrong field name.
19+
20+
### Changed
21+
22+
- `engines` parameter type changed from `string` to `array` in schema to match actual API
23+
- Updated a3s-search integration to v1.0.0
24+
25+
---
26+
27+
## [v1.8.5] - 2026-04-05
1328

1429
### Added
1530

core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "a3s-code-core"
3-
version = "1.8.5"
3+
version = "1.8.6"
44
edition = "2021"
55
authors = ["A3S Lab Team"]
66
license = "MIT"

core/skills/builtin-tools.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,10 @@ tools:
265265
type: string
266266
description: The search query
267267
engines:
268-
type: string
269-
description: "Comma-separated list of engines to use (default: ddg,wiki). Available: ddg, brave, google, wiki, baidu, sogou, bing_cn, 360"
268+
type: array
269+
items:
270+
type: string
271+
description: "List of engines to use (default: [\"ddg\",\"wiki\"]). Available: ddg (DuckDuckGo), brave (Brave Search), wiki (Wikipedia), sogou (Sogou), 360 / so360 (360 Search). Note: use 'engines' (plural), not 'engine' (singular)."
270272
limit:
271273
type: integer
272274
description: "Maximum number of results to return (default: 10, max: 50)"
@@ -381,7 +383,7 @@ Parameters are passed as JSON objects matching each tool's parameter schema defi
381383
{"url": "https://example.com", "format": "markdown", "timeout": 30}
382384

383385
// Search the web
384-
{"query": "rust async programming", "engines": "ddg,wiki", "limit": 10}
386+
{"query": "rust async programming", "engines": ["ddg", "wiki"], "limit": 10}
385387
```
386388

387389
### Git Operations

core/src/config.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,60 @@ pub struct SearchConfig {
317317
/// Engine configurations
318318
#[serde(default, rename = "engine")]
319319
pub engines: std::collections::HashMap<String, SearchEngineConfig>,
320+
321+
/// Headless browser configuration for JS-rendered engines (google, baidu, bing_cn).
322+
/// When enabled, the browser binary is auto-detected or downloaded.
323+
#[serde(default, skip_serializing_if = "Option::is_none")]
324+
pub headless: Option<HeadlessConfig>,
325+
}
326+
327+
/// Headless browser backend selection.
328+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
329+
#[serde(rename_all = "lowercase")]
330+
pub enum BrowserBackend {
331+
/// Chrome/Chromium headless. Auto-detected or downloaded from Google.
332+
Chrome,
333+
/// Lightpanda headless browser. Auto-detected or downloaded from GitHub.
334+
/// Supported on Linux and macOS only.
335+
Lightpanda,
336+
}
337+
338+
impl Default for BrowserBackend {
339+
fn default() -> Self {
340+
BrowserBackend::Chrome
341+
}
342+
}
343+
344+
/// Headless browser configuration.
345+
#[derive(Debug, Clone, Serialize, Deserialize)]
346+
#[serde(rename_all = "camelCase")]
347+
pub struct HeadlessConfig {
348+
/// Which headless backend to use.
349+
#[serde(default)]
350+
pub backend: BrowserBackend,
351+
352+
/// Path to the browser executable. If None, auto-detected or downloaded.
353+
#[serde(default, skip_serializing_if = "Option::is_none")]
354+
pub browser_path: Option<String>,
355+
356+
/// Maximum number of concurrent browser tabs.
357+
#[serde(default = "default_headless_max_tabs")]
358+
pub max_tabs: usize,
359+
360+
/// Additional launch arguments for the browser.
361+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
362+
pub launch_args: Vec<String>,
363+
}
364+
365+
impl Default for HeadlessConfig {
366+
fn default() -> Self {
367+
Self {
368+
backend: BrowserBackend::default(),
369+
browser_path: None,
370+
max_tabs: 4,
371+
launch_args: Vec::new(),
372+
}
373+
}
320374
}
321375

322376
/// Default configuration for the built-in `agentic_search` tool.
@@ -593,6 +647,10 @@ fn default_search_timeout() -> u64 {
593647
10
594648
}
595649

650+
fn default_headless_max_tabs() -> usize {
651+
4
652+
}
653+
596654
fn default_max_failures() -> u32 {
597655
3
598656
}

sdk/node/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "a3s-code-node"
3-
version = "1.8.5"
3+
version = "1.8.6"
44
edition = "2021"
55
authors = ["A3S Lab Team"]
66
license = "MIT"
@@ -11,7 +11,7 @@ description = "A3S Code Node.js bindings - Native addon via napi-rs"
1111
crate-type = ["cdylib"]
1212

1313
[dependencies]
14-
a3s-code-core = { version = "1.8.5", path = "../../core", features = ["ahp"] }
14+
a3s-code-core = { version = "1.8.6", path = "../../core", features = ["ahp"] }
1515
napi = { version = "2", features = ["async", "napi6", "serde-json"] }
1616
napi-derive = "2"
1717
tokio = { version = "1.35", features = ["full"] }

sdk/node/index.d.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,21 @@ export interface ToolResult {
189189
/** Convenience JSON view of `metadata.document_runtime` when present. */
190190
documentRuntimeJson?: string
191191
}
192+
/** Parameters for the web_search tool. */
193+
export interface JsWebSearchParams {
194+
/** The search query. */
195+
query: string
196+
/** List of search engines to use. */
197+
engines?: Array<string>
198+
/** Maximum number of results to return (default: 10, max: 50). */
199+
limit?: number
200+
/** Search timeout in seconds (default: 10, max: 60). */
201+
timeout?: number
202+
/** Proxy URL (e.g., http://127.0.0.1:8080 or socks5://127.0.0.1:1080). */
203+
proxy?: string
204+
/** Output format: "text" or "json". */
205+
format?: string
206+
}
192207
/** Result of a single `EventStream.next()` call. */
193208
export interface NextResult {
194209
value?: AgentEvent
@@ -670,6 +685,20 @@ export interface SearchEngineConfig {
670685
weight: number
671686
timeout?: number
672687
}
688+
/** Browser backend for headless search. */
689+
export const enum BrowserBackend {
690+
/** Chrome/Chromium headless. */
691+
Chrome = 0,
692+
/** Lightpanda headless browser (Linux/macOS only). */
693+
Lightpanda = 1
694+
}
695+
/** Headless browser configuration. */
696+
export interface HeadlessConfig {
697+
backend: BrowserBackend
698+
browserPath?: string
699+
maxTabs?: number
700+
launchArgs?: Array<string>
701+
}
673702
/** Health monitor configuration for search engines. */
674703
export interface SearchHealthConfig {
675704
maxFailures: number
@@ -680,6 +709,7 @@ export interface SearchConfig {
680709
timeout: number
681710
health?: SearchHealthConfig
682711
engines: Record<string, SearchEngineConfig>
712+
headless?: HeadlessConfig
683713
}
684714
/** SubAgent configuration for orchestrator. */
685715
export interface SubAgentConfig {
@@ -1082,6 +1112,8 @@ export declare class Session {
10821112
glob(pattern: string): Promise<Array<string>>
10831113
/** Search file contents with a regex pattern. */
10841114
grep(pattern: string): Promise<string>
1115+
/** Search the web using multiple search engines. */
1116+
webSearch(params: JsWebSearchParams): Promise<ToolResult>
10851117
/** Execute a git command (status, log, branch, checkout, diff, stash, remote, worktree). */
10861118
git(command: string, subcommand?: string | undefined | null, name?: string | undefined | null, path?: string | undefined | null, newBranch?: boolean | undefined | null, base?: string | undefined | null, force?: boolean | undefined | null, maxCount?: number | undefined | null, message?: string | undefined | null, includeUntracked?: boolean | undefined | null, target?: string | undefined | null, reference?: string | undefined | null): Promise<ToolResult>
10871119
/** Check if this session has a lane queue configured. */

sdk/node/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ if (!nativeBinding) {
310310
throw new Error(`Failed to load native binding`)
311311
}
312312

313-
const { EventStream, FileMemoryStore, FileSessionStore, MemorySessionStore, DefaultSecurityProvider, SkillPlugin, StdioTransport, HttpTransport, WebSocketTransport, UnixSocketTransport, Agent, Session, builtinSkills, TeamRole, TeamTaskStatus, TeamTaskBoard, Team, TeamRunner, SubAgentHandle, SubAgentEventStream, Orchestrator } = nativeBinding
313+
const { EventStream, FileMemoryStore, FileSessionStore, MemorySessionStore, DefaultSecurityProvider, SkillPlugin, StdioTransport, HttpTransport, WebSocketTransport, UnixSocketTransport, Agent, Session, builtinSkills, TeamRole, TeamTaskStatus, TeamTaskBoard, Team, TeamRunner, BrowserBackend, SubAgentHandle, SubAgentEventStream, Orchestrator } = nativeBinding
314314

315315
module.exports.EventStream = EventStream
316316
module.exports.FileMemoryStore = FileMemoryStore
@@ -330,6 +330,7 @@ module.exports.TeamTaskStatus = TeamTaskStatus
330330
module.exports.TeamTaskBoard = TeamTaskBoard
331331
module.exports.Team = Team
332332
module.exports.TeamRunner = TeamRunner
333+
module.exports.BrowserBackend = BrowserBackend
333334
module.exports.SubAgentHandle = SubAgentHandle
334335
module.exports.SubAgentEventStream = SubAgentEventStream
335336
module.exports.Orchestrator = Orchestrator

sdk/node/package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@a3s-lab/code",
3-
"version": "1.8.5",
3+
"version": "1.8.6",
44
"description": "A3S Code - Native AI coding agent library for Node.js",
55
"main": "index.js",
66
"types": "index.d.ts",
@@ -44,11 +44,11 @@
4444
"test:loop": "tsx --tsconfig examples/tsconfig.json examples/test_loop_commands.ts"
4545
},
4646
"optionalDependencies": {
47-
"@a3s-lab/code-darwin-arm64": "1.8.5",
48-
"@a3s-lab/code-linux-x64-gnu": "1.8.5",
49-
"@a3s-lab/code-linux-x64-musl": "1.8.5",
50-
"@a3s-lab/code-linux-arm64-gnu": "1.8.5",
51-
"@a3s-lab/code-linux-arm64-musl": "1.8.5",
52-
"@a3s-lab/code-win32-x64-msvc": "1.8.5"
47+
"@a3s-lab/code-darwin-arm64": "1.8.6",
48+
"@a3s-lab/code-linux-x64-gnu": "1.8.6",
49+
"@a3s-lab/code-linux-x64-musl": "1.8.6",
50+
"@a3s-lab/code-linux-arm64-gnu": "1.8.6",
51+
"@a3s-lab/code-linux-arm64-musl": "1.8.6",
52+
"@a3s-lab/code-win32-x64-msvc": "1.8.6"
5353
}
5454
}

sdk/node/src/lib.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use a3s_code_core::agent_teams::{
2929
};
3030
use a3s_code_core::commands::CommandContext as RustCommandContext;
3131
use a3s_code_core::config::{
32+
BrowserBackend as RustBrowserBackend, HeadlessConfig as RustHeadlessConfig,
3233
SearchConfig as RustSearchConfig, SearchEngineConfig as RustSearchEngineConfig,
3334
SearchHealthConfig as RustSearchHealthConfig,
3435
};
@@ -286,6 +287,28 @@ pub struct ToolResult {
286287
pub document_runtime_json: Option<String>,
287288
}
288289

290+
// ============================================================================
291+
// WebSearchParams
292+
// ============================================================================
293+
294+
/// Parameters for the web_search tool.
295+
#[napi(object)]
296+
#[derive(Clone)]
297+
pub struct JsWebSearchParams {
298+
/// The search query.
299+
pub query: String,
300+
/// List of search engines to use.
301+
pub engines: Option<Vec<String>>,
302+
/// Maximum number of results to return (default: 10, max: 50).
303+
pub limit: Option<u32>,
304+
/// Search timeout in seconds (default: 10, max: 60).
305+
pub timeout: Option<u32>,
306+
/// Proxy URL (e.g., http://127.0.0.1:8080 or socks5://127.0.0.1:1080).
307+
pub proxy: Option<String>,
308+
/// Output format: "text" or "json".
309+
pub format: Option<String>,
310+
}
311+
289312
// ============================================================================
290313
// EventStream
291314
// ============================================================================
@@ -1676,6 +1699,36 @@ impl Session {
16761699
.map_err(|e| napi::Error::from_reason(format!("{e}")))
16771700
}
16781701

1702+
/// Search the web using multiple search engines.
1703+
#[napi]
1704+
pub async fn web_search(&self, params: JsWebSearchParams) -> napi::Result<ToolResult> {
1705+
let session = self.inner.clone();
1706+
let args = serde_json::json!({
1707+
"query": params.query,
1708+
"engines": params.engines,
1709+
"limit": params.limit,
1710+
"timeout": params.timeout,
1711+
"proxy": params.proxy,
1712+
"format": params.format,
1713+
});
1714+
get_runtime()
1715+
.spawn(async move {
1716+
session
1717+
.tool("web_search", args)
1718+
.await
1719+
.map(|r| ToolResult {
1720+
name: r.name,
1721+
output: r.output,
1722+
exit_code: r.exit_code,
1723+
metadata_json: r.metadata.map(|m| serde_json::to_string(&m).ok()).flatten(),
1724+
document_runtime_json: None,
1725+
})
1726+
})
1727+
.await
1728+
.map_err(|e| napi::Error::from_reason(format!("Task join error: {e}")))?
1729+
.map_err(|e| napi::Error::from_reason(format!("{e}")))
1730+
}
1731+
16791732
/// Execute a git command (status, log, branch, checkout, diff, stash, remote, worktree).
16801733
#[napi]
16811734
pub async fn git(
@@ -3625,6 +3678,45 @@ impl From<SearchEngineConfig> for RustSearchEngineConfig {
36253678
}
36263679
}
36273680

3681+
/// Browser backend for headless search.
3682+
#[napi]
3683+
pub enum BrowserBackend {
3684+
/// Chrome/Chromium headless.
3685+
Chrome,
3686+
/// Lightpanda headless browser (Linux/macOS only).
3687+
Lightpanda,
3688+
}
3689+
3690+
impl From<BrowserBackend> for RustBrowserBackend {
3691+
fn from(b: BrowserBackend) -> Self {
3692+
match b {
3693+
BrowserBackend::Chrome => RustBrowserBackend::Chrome,
3694+
BrowserBackend::Lightpanda => RustBrowserBackend::Lightpanda,
3695+
}
3696+
}
3697+
}
3698+
3699+
/// Headless browser configuration.
3700+
#[napi(object)]
3701+
#[derive(Clone)]
3702+
pub struct HeadlessConfig {
3703+
pub backend: BrowserBackend,
3704+
pub browser_path: Option<String>,
3705+
pub max_tabs: Option<u32>,
3706+
pub launch_args: Option<Vec<String>>,
3707+
}
3708+
3709+
impl From<HeadlessConfig> for RustHeadlessConfig {
3710+
fn from(c: HeadlessConfig) -> Self {
3711+
Self {
3712+
backend: c.backend.into(),
3713+
browser_path: c.browser_path,
3714+
max_tabs: c.max_tabs.unwrap_or(4) as usize,
3715+
launch_args: c.launch_args.unwrap_or_default(),
3716+
}
3717+
}
3718+
}
3719+
36283720
/// Health monitor configuration for search engines.
36293721
#[napi(object)]
36303722
#[derive(Clone)]
@@ -3649,6 +3741,7 @@ pub struct SearchConfig {
36493741
pub timeout: u32,
36503742
pub health: Option<SearchHealthConfig>,
36513743
pub engines: std::collections::HashMap<String, SearchEngineConfig>,
3744+
pub headless: Option<HeadlessConfig>,
36523745
}
36533746

36543747
impl From<SearchConfig> for RustSearchConfig {
@@ -3657,6 +3750,7 @@ impl From<SearchConfig> for RustSearchConfig {
36573750
timeout: c.timeout as u64,
36583751
health: c.health.map(|h| h.into()),
36593752
engines: c.engines.into_iter().map(|(k, v)| (k, v.into())).collect(),
3753+
headless: c.headless.map(|h| h.into()),
36603754
}
36613755
}
36623756
}

sdk/python-bootstrap/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "a3s-code"
7-
version = "1.8.5"
7+
version = "1.8.6"
88
description = "A3S Code Python bootstrap package that fetches platform-native binaries from GitHub Releases"
99
readme = "../python/README.md"
1010
license = { text = "MIT" }

0 commit comments

Comments
 (0)