Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions .github/workflows/nacos-live.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Nacos live acceptance

on:
workflow_dispatch:
push:
branches: [main]
paths: &paths
- '.github/workflows/nacos-live.yml'
- 'eng/NacosLiveSmoke/**'
- 'eng/nacos-live/**'
- 'eng/verify-capability-adapters.sh'
- 'src/OpenClaw.Adapters.Nacos*/**'
- 'src/OpenClaw.Agent/**'
- 'src/OpenClaw.Core/**'
- 'src/OpenClaw.Gateway/**'
- 'src/OpenClaw.Tests/**'
- 'Directory.Build.*'
pull_request:
branches: [main]
paths: *paths

permissions:
contents: read

concurrency:
group: nacos-live-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
nacos-live-native:
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68
with:
dotnet-version: '10.0.x'
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961
with:
distribution: temurin
java-version: '17'
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
with:
python-version: '3.12'
cache: pip
cache-dependency-path: eng/nacos-live/requirements.txt
- name: Install native toolchain and pinned Router
run: |
bash .github/scripts/install-nativeaot-prereqs.sh
python -m venv "$RUNNER_TEMP/nacos-python"
"$RUNNER_TEMP/nacos-python/bin/pip" install -r eng/nacos-live/requirements.txt
"$RUNNER_TEMP/nacos-python/bin/python" -c 'from chromadb.utils.embedding_functions import DefaultEmbeddingFunction; DefaultEmbeddingFunction()(["weather city"])'
curl --fail --location --retry 3 https://github.com/alibaba/nacos/releases/download/3.2.4/nacos-server-3.2.4.tar.gz -o "$RUNNER_TEMP/nacos.tar.gz"
- name: Publish NativeAOT Gateway with optional events
run: dotnet publish src/OpenClaw.Gateway -c Release -r linux-x64 -p:OpenClawEnableNacos=true -p:OpenClawEnableNacosEvents=true -p:OpenClawSkipDashboardBuild=true -o "$RUNNER_TEMP/nacos-gateway"
- name: Build acceptance executables and runtime tests
run: |
dotnet publish eng/NacosLiveSmoke -c Release -r linux-x64 -p:PublishAot=true -o "$RUNNER_TEMP/nacos-native"
dotnet build eng/NacosLiveSmoke -c Release -p:PublishAot=false
dotnet build src/OpenClaw.Tests -c Release -p:OpenClawSkipDashboardBuild=true
- name: Verify authenticated Nacos events and weather binding
run: |
"$RUNNER_TEMP/nacos-python/bin/python" eng/nacos-live/verify.py \
--archive "$RUNNER_TEMP/nacos.tar.gz" \
--managed eng/NacosLiveSmoke/bin/Release/net10.0/NacosLiveSmoke.dll \
--native "$RUNNER_TEMP/nacos-native/NacosLiveSmoke" \
--test-dll src/OpenClaw.Tests/bin/Release/net10.0/OpenClaw.Tests.dll \
--output "$RUNNER_TEMP/nacos-evidence"
- name: Upload acceptance evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: nacos-live-evidence
path: |
${{ runner.temp }}/nacos-evidence/*.json
${{ runner.temp }}/nacos-evidence/*.trx
${{ runner.temp }}/nacos-evidence/managed.log
${{ runner.temp }}/nacos-evidence/native.log
${{ runner.temp }}/nacos-evidence/runtimes.log
8 changes: 4 additions & 4 deletions docs/capability-resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ OpenClaw.NET owns deterministic capability resolution and execution. The default
| `OpenClaw.Core` | Provider, candidate, binding, invalidation, trajectory, and status contracts; no vendor SDK types |
| `OpenClaw.Agent` | Provider selection, deterministic ranking, policy-governed execution, bounded caches, circuit protection |
| `OpenClaw.Adapters.Nacos` | Router search/add/use protocol and failure normalization over the existing MCP transport |
| `OpenClaw.Adapters.Nacos.Events` | Optional Nacos SDK, configuration, listener lifecycle, generic invalidation events; JIT only |
| `OpenClaw.Adapters.Nacos.Events` | Optional Nacos SDK, configuration, listener lifecycle, generic invalidation events; JIT and NativeAOT |
| AgentQi | Ecosystem documentation, catalog curation/trust assessment, setup and operational UX |

Catalog trust is input to runtime policy, never permission to bypass local authorization, approvals, hooks, or audit. Providers implement `ICapabilityProvider`; change adapters implement `ICapabilityChangeSource` and publish through `ICapabilityInvalidationSink`.
Expand Down Expand Up @@ -43,9 +43,9 @@ The tool returns provider, server, tool, schema, schema fingerprint, and attempt
| --- | --- | --- |
| Default Gateway | local | absent |
| `-p:OpenClawEnableNacos=true` | local, nacos | absent; suitable for NativeAOT |
| Above plus `-p:OpenClawEnableNacosEvents=true -p:PublishAot=false` | local, nacos | explicit JIT adapter |
| Above plus `-p:OpenClawEnableNacosEvents=true` | local, nacos | explicit SDK adapter; JIT or NativeAOT |

The default Gateway dependency graph has no Nacos package reference. An SDK-events NativeAOT build fails with an actionable diagnostic; native event support remains [#239](https://github.com/clawdotnet/openclaw.net/issues/239). Default serialization stays source-generated. Only the explicitly selected JIT event host enables SDK-required reflection serialization.
The default Gateway dependency graph has no Nacos package reference. The optional event adapter uses RedNb.Nacos.DependencyInjection 2.1.0 and its generated protocol JSON metadata. It supports NativeAOT without enabling reflection serialization. Use `-p:PublishAot=false` for JIT publishing. The SDK stays absent unless explicitly selected.

Configure the Router as MCP server `nacos-mcp-router`, and add `provider: nacos` to capability references. See the [Router contract and deployment guide](nacos-mcp-router.md). Optional event settings live under the generic extension bag:

Expand Down Expand Up @@ -86,7 +86,7 @@ Step evidence records provider, generation, intent, candidates/attempts, selecte

Conformance tests cover both runtimes with the local provider, permission denial before discovery, authorization on cache hits, generation races, provider/security isolation, bounded expiry, circuit cooldown, and replay divergence. Router tests cover captured protocol envelopes and typed failures; listener tests cover retry, cancellation, and disposal.

Live Nacos/Router acceptance is opt-in. Historical contributor measurements in the Router guide are not a new live validation of this refactor. A correctly registered weather backend and live subscription timing still need deployment evidence; NativeAOT SDK events remain separate work. AgentQi catalog/trust and operational screen design belong in the downstream ecosystem/product backlog.
Live Nacos/Router acceptance is reproducible through [the isolated acceptance harness](../eng/nacos-live/README.md). It runs authenticated Nacos 3.2.4 and Router 0.2.2. Managed tests exercise both runtime implementations. Separate managed and NativeAOT smoke processes check real event invalidation and rebinding with JSON reflection disabled. The dedicated CI lane provisions its own services; ordinary unit tests remain independent of them. Historical contributor token measurements remain separately attributed in the Router guide. This proves the tested deployment contract, not arbitrary registry recall or production availability. AgentQi catalog/trust and operational screen design belong in the downstream ecosystem/product backlog.

Nacos target retries require an explicit operator allowlist under `adapterSettings.nacos.retrySafeTargets`, for example `["weather-mcp/get_weather"]`. Only list operations known to be safe to repeat; other targets execute once regardless of a skill's retry count. The retry weather example requires this setting.

Expand Down
36 changes: 18 additions & 18 deletions docs/nacos-mcp-router.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The current architecture and build contract is [vendor-neutral capability resolution](capability-resolution.md). Select `provider: nacos` explicitly. The Router adapter and SDK event adapter are independent optional components.

The live observations below were supplied with the contributor's `nacos` branch on 2026-09-14. They preserve its protocol evidence and measurement caveats; they are not a fresh live validation of the vendor-neutral refactor. In particular, successful weather discovery was not demonstrated on the misregistered test backend.
Zhang (@geffzhang) supplied the original implementation, protocol captures, and token measurements on 2026-09-14; those historical observations and their caveats are preserved below. The [isolated acceptance harness](../eng/nacos-live/README.md) now validates the current vendor-neutral adapters with a correctly registered `get_weather(city)` backend, both runtimes, and managed/native event invalidation.

## Contract observations

Expand Down Expand Up @@ -58,7 +58,7 @@ Live-capture findings (2026-09-14):

## Opt-in configuration

Build Gateway with `-p:OpenClawEnableNacos=true` to enable the provider. SDK events require the additional explicit JIT options below.
Build Gateway with `-p:OpenClawEnableNacos=true` to enable the provider. SDK events require the additional explicit adapter flag below, for either JIT or NativeAOT.

Keep Nacos and Router on the same host when Nacos binds only to loopback. Do not
change an existing deployment's networking or authentication for this example.
Expand Down Expand Up @@ -95,15 +95,15 @@ Merge this entry into `<storagePath>/mcp/mcp.json`; preserve existing servers:
}
```

Register a test server named `weather-mcp` using the deployment's Nacos
console/API. Verified on the referenced deployment (2026-09-14): a console
registration with a non-empty bilingual description and a stdio local config
wrapped as `{"mcpServers": {"weather-mcp": {"command": "uvx", "args": ["mcp-server-time"]}}}`.
`mcp-server-time` is a test-bed stand-in so the full `add_mcp_server` chain runs;
replace it with a real weather server. Verified 2026-09-14: with this config the
live chain `search → add → use_tool` completed for `weather-mcp` (add success
envelope `1. <name>安装完成, tool 列表为: [{name, description, inputSchema}]...`,
then `use_tool` returned the backend tool result).
For isolated acceptance, use the [provisioning harness](../eng/nacos-live/README.md).
It registers `weather-mcp` with a non-empty bilingual description and an
`mcpServers`-wrapped stdio configuration pointing to `eng/nacos-live/weather_server.py`
inside the pinned Python environment. The backend actually exposes
`get_weather(city)` and returns `city`, numeric `temperature_c`, and `source`.
Fixture observations are labelled; `--live-weather` requests Open-Meteo data.
Do not reuse the historical `mcp-server-time` registration as a weather backend.
For another deployment, register its real weather tool and adapt the skill to
that tool's verified name/schema.

The examples stay under `examples/skills/` and are not bundled or enabled by
default. Copy the two example directories into an isolated gateway workspace's
Expand Down Expand Up @@ -137,7 +137,7 @@ runtimes (static: one cached `add`, then `use_tool` per call; dynamic:
and protocol-error handling. They use no external credentials, model calls,
Nacos server, or Docker.

For a provisioned Router with a registered weather server:
For a provisioned Router with the acceptance `get_weather(city)` backend described above:

```sh
export OPENCLAW_NACOS_LIVE=1
Expand Down Expand Up @@ -300,15 +300,15 @@ cache with the recorded binding). See

## Nacos event subscription (issue #238)

Build with `-p:OpenClawEnableNacos=true -p:OpenClawEnableNacosEvents=true -p:PublishAot=false` and configure `adapterSettings.nacos`. See the [configuration example](capability-resolution.md#optional-adapter-builds).
Build with `-p:OpenClawEnableNacos=true -p:OpenClawEnableNacosEvents=true` (add `-p:PublishAot=false` for JIT) and configure `adapterSettings.nacos`. See the [configuration example](capability-resolution.md#optional-adapter-builds).

The optional SDK adapter registers in the background, retries failed setup, and reports `starting`, `degraded`, `active`, or `stopped`. Events publish generic invalidation signals that advance the cache generation. They do not overwrite the local workspace configuration. With no adapter or no address, TTL and explicit workspace reload remain available. NativeAOT SDK-event builds are rejected explicitly; #239 remains open.
The optional SDK adapter registers in the background, retries failed setup, and reports `starting`, `degraded`, `active`, or `stopped`. Events publish generic invalidation signals that advance the cache generation and clear all capability bindings; per-server invalidation is not implemented. They do not overwrite the local workspace configuration. With no adapter or no address, TTL and explicit workspace reload remain available. RedNb.Nacos 2.1.0 supplies generated protocol JSON metadata; the optional adapter supports NativeAOT and does not turn JSON reflection back on. The live harness verifies <=2 s publish-to-invalidation and static/dynamic rebinds in both managed and native processes.

## Historical contributor evidence and outstanding live acceptance
## Historical contributor evidence

The following describes the original branch, before adapter isolation. Repeat live acceptance against the refactor before claiming deployment readiness. In particular, the old dual-cache/watcher implementation below has been replaced by generic generation invalidation.
The following preserves Zhang's evidence from the original branch, before adapter isolation. The current [acceptance harness](../eng/nacos-live/README.md) tests generic generation invalidation, which replaced the old dual-cache/watcher design. The old weather registration and token data below are historical evidence, not setup instructions for the new harness.

Before closing #229 or proceeding with the dependent runtime changes:
Original acceptance record:

1. ~~Capture the real three tool schemas, success/error responses, and Router
package version from the intended Nacos 3.2.4 deployment. Redact credentials.~~
Expand Down Expand Up @@ -366,4 +366,4 @@ Before closing #229 or proceeding with the dependent runtime changes:
| Median input + output tokens (5 runs) | 0 + 0 (no LLM turn) | 17479 + 1181 (traced batch; untraced batch at the iteration cap: 29763 + 1870) |
| Router version / deployment | 0.2.2 (`@latest`, requires `mcp<2`) / local Nacos 3.2.4, streamable_http :8000 | same |

For current runtime/cache/retry/replay behavior and remaining acceptance, use [capability resolution](capability-resolution.md).
For current runtime/cache/retry/replay behavior and validation scope, use [capability resolution](capability-resolution.md).
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ sequenceDiagram
2. **Token 最小化**:模型只接触 MetaSkill DAG 结构与 Router 的少量工具描述,而非全部后端服务的 Schema。
3. **绑定可演进**:更换/升级后端服务只需修改 Nacos 注册信息,MetaSkill 定义不变。

> 实现对照(2026-09-14,#230/#231/#232/#233/#238 已落地):上图中动态槽位的 `search → add → use` 与静态槽位的 `add`(首次,幂等缓存)→ `use` 均为确定性代码路径;会话级绑定缓存(intent 哈希 + TTL/reload 失效)与节点级降级(fallback 路由 / Top-5 候选轮替 / retry 重试熔断)已实现;Nacos 变更事件订阅的失效联动(#238)已实现——变更到达即双清缓存(会话绑定缓存 + 运行时 added-server 缓存)并触发 watcher reload。
> 实现对照(2026-09-14,#230/#231/#232/#233/#238 已落地):上图中动态槽位的 `search → add → use` 与静态槽位的 `add`(首次,幂等缓存)→ `use` 均为确定性代码路径;会话级绑定缓存(intent 哈希 + TTL/reload 失效)与节点级降级(fallback 路由 / Top-5 候选轮替 / retry 重试熔断)已实现;Nacos 变更事件订阅的失效联动(#238)已实现——变更到达经通用能力失效接口推进 generation 并清除所有绑定,静态槽位重新 add、动态槽位重新解析;不触发 workspace 配置 reload。

## 7. 关键工程决策

Expand Down Expand Up @@ -251,7 +251,7 @@ Router 语义检索的质量完全取决于 Nacos 中 MCP Server 的 `descriptio
| 缓存粒度 | 会话级(默认)+ 运行时级(静态绑定) |
| 缓存键 | intent 哈希(task_description + key_words + selectionPolicy) |
| 失效机制 | TTL 过期(可配,默认 300s)+ mcp.json reload 成功清空(#232 已实现);订阅 Nacos 配置变更事件(#238 已实现,2026-09-14) |
| Nacos 变更事件订阅 | `RedNb.Nacos.All 2.0.0` LongPolling 订阅 mcp.json dataId;onChange → watcher reload → 会话绑定缓存 + 运行时 added-server 缓存双清;`ServerAddr` 未配置或 Nacos 不可达时优雅降级为 no-op,TTL/reload 兜底保持生效。运行时要求:SDK 的 gRPC 载荷走反射式 System.Text.Json,而 `PublishAot=true` 会在**所有** runtimeconfig 中注入 `System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault=false`(JIT 运行也会中招)——csproj 仅在 JIT 构建(无 `RuntimeIdentifier`)时重新开启该开关;NativeAOT 下 SDK 载荷类型被裁剪,订阅降级为 TTL/reload 兜底(后续 issue 跟进) |
| Nacos 变更事件订阅 | 显式启用 `OpenClawEnableNacosEvents=true`,由可选适配器使用 RedNb.Nacos 2.1.0 源生成协议元数据,支持 JIT 和 NativeAOT,无需开启 JSON 反射。onChange 通过通用失效接口推进缓存 generation,清除静态/动态绑定;不覆盖 workspace 配置。未配置或不可达时保持 TTL/reload,并报告 disabled/degraded 状态。当前[可复现验收](../../eng/nacos-live/README.md)覆盖认证服务器、≤2 秒失效及重新绑定;原型 +310 ms 数据保留在英文指南的 Zhang 历史记录中。 |

### 7.4 版本与准入治理

Expand Down
Loading
Loading