Compare commits

..

1 Commits

Author SHA1 Message Date
Dallin Romney
b8bc882853 fix: preserve exec policy layer base 2026-06-22 18:19:16 -07:00
367 changed files with 6604 additions and 15998 deletions

View File

@@ -15,7 +15,7 @@ committed `inventory/` report tree.
This skill owns the operational workflow for:
- `taxonomy.yaml`
- `qa/maturity-scores.yaml`
- `docs/maturity-scores.yaml`
- `docs/concepts/qa-e2e-automation.md`
- `qa/scenarios/index.yaml`
@@ -37,35 +37,28 @@ out of this repo. If a score needs private evidence, use the redacted
coverage IDs. Do not promote generic IDs into standalone feature names.
- Avoid duplicate coverage-ID bundles under different feature names in one
category.
- `qa/maturity-scores.yaml` is the committed aggregate source for Quality,
Completeness, and LTS review state.
- `extensions/qa-lab/src/scorecard-taxonomy.ts` exports
`qaMaturityScoresSchema` and `readValidatedQaMaturityScoreSources`; use those
QA Lab utilities to validate score output.
- Generated public docs are `docs/maturity/scorecard.md` and
`docs/maturity/taxonomy.md`; both come from `pnpm maturity:render`. Do not
hand-edit generated Markdown to change score results.
- `qa-evidence.json` artifacts provide per-run QA scorecard evidence. Release
profile artifacts are the source of truth for Coverage. They can enrich
generated artifact docs, but they are not committed as inventory.
- `docs/maturity-scores.yaml` is the aggregate score source committed in this
repo. It is the only committed score data; do not add generated inventory
directories.
- There is no committed maturity-doc renderer or `pnpm maturity:*` script in
this repo. Do not invent generated scorecard files; update the source YAML
and current docs directly.
- `qa-evidence.json` artifacts provide per-run QA scorecard evidence. They can
enrich generated artifact docs, but they are not committed as inventory.
## Commands
Run from the openclaw repo root.
Validate taxonomy YAML structure and the maturity score schema after source
edits:
Validate YAML structure after source edits:
```bash
node --import tsx --input-type=module <<'NODE'
import fs from "node:fs";
import YAML from "yaml";
import { readValidatedQaMaturityScoreSources } from "./extensions/qa-lab/src/scorecard-taxonomy.ts";
for (const file of ["taxonomy.yaml", "qa/scenarios/index.yaml"]) {
node <<'NODE'
const fs = require("node:fs");
const YAML = require("yaml");
for (const file of ["taxonomy.yaml", "docs/maturity-scores.yaml", "qa/scenarios/index.yaml"]) {
YAML.parse(fs.readFileSync(file, "utf8"));
}
readValidatedQaMaturityScoreSources();
NODE
```
@@ -90,17 +83,17 @@ When asked to score or refresh a surface:
`.agents/skills/claw-score/references/completeness/`.
3. Gather public repo evidence from docs, source, tests, and QA scenario
metadata.
4. Prefer existing release profile `qa-evidence.json` artifacts for executed
proof.
5. Update `qa/maturity-scores.yaml` only for Quality, Completeness, and LTS
review state backed by public or redacted artifact evidence.
6. Run the schema validation command from this skill.
4. Prefer existing `qa-evidence.json` artifacts for executed proof. Do not use
discrawl or unredacted private archives.
5. Update `docs/maturity-scores.yaml` only when the score change is backed by
public or redacted artifact evidence.
6. Run the YAML validation command from this skill.
7. Run `pnpm check:docs` if docs prose changed, and focused QA coverage checks
if coverage IDs or profile membership changed.
For subjective score changes, make the smallest defensible edit and leave the
evidence path in the PR or task summary. Keep manual prose in current docs and
keep score data in `qa/maturity-scores.yaml`.
keep score data in `docs/maturity-scores.yaml`.
## Default Completeness Process
@@ -159,16 +152,15 @@ Default Completeness bands:
## Score Semantics
- Coverage: deterministic release validation coverage derived from the release
profile `qa-evidence.json.scorecard` feature fulfillment data.
- Coverage: public or redacted proof that the feature is exercised by docs,
tests, QA scenarios, live lanes, or release evidence.
- Quality: reliability, maintainability, operator safety, and regression
confidence for the category.
- Completeness: how much of the intended operator-visible workflow exists for
the category. Use the default completeness process plus any surface-specific
variation before changing this score.
- LTS: derived from Quality, release-evidence Coverage, and
`human_lts_override`; do not hand-edit generated Markdown to change LTS
status.
- LTS: derived from score thresholds and `human_lts_override`; do not hand-edit
generated Markdown to change LTS status.
Bands:

View File

@@ -261,6 +261,6 @@ jobs:
- name: Run Testbox
uses: useblacksmith/run-testbox@3f60ff9ceb2c10c3feefa87dc0c6490cffae059d
if: always()
if: success()
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

View File

@@ -179,6 +179,6 @@ jobs:
- name: Run Testbox
uses: useblacksmith/run-testbox@5ca05834db1d3813554d1dd109e5f2087a8d7cbc
if: always()
if: success()
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

View File

@@ -33,7 +33,7 @@ jobs:
contents: read
name: "check"
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: ${{ fromJSON(inputs.timeout_minutes || '120') }}
timeout-minutes: ${{ fromJSON(inputs.timeout_minutes || '30') }}
steps:
- name: Begin Testbox
uses: useblacksmith/begin-testbox@233448af4bfdc6fca509a7f0974411ac6d8a8043
@@ -168,6 +168,6 @@ jobs:
- name: Run Testbox
uses: useblacksmith/run-testbox@3f60ff9ceb2c10c3feefa87dc0c6490cffae059d
if: always()
if: success()
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

View File

@@ -1177,9 +1177,7 @@ jobs:
timeout-minutes: ${{ matrix.timeout_minutes || 60 }}
strategy:
fail-fast: false
# The canonical main path waits for the admission debounce above, so
# modestly widen this large matrix without recreating registration bursts.
max-parallel: 16
max-parallel: 12
matrix: ${{ fromJson(needs.preflight.outputs.checks_node_core_nondist_matrix) }}
steps:
- name: Checkout
@@ -2235,7 +2233,7 @@ jobs:
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ~/.android-sdk
key: ${{ runner.os }}-android-sdk-v1-cmdline-14742923-platform-36-build-tools-36.0.0
key: ${{ runner.os }}-android-sdk-v1-cmdline-14742923-platform-37.0-build-tools-36.0.0
restore-keys: |
${{ runner.os }}-android-sdk-v1-
@@ -2265,7 +2263,7 @@ jobs:
yes | sdkmanager --sdk_root="${ANDROID_SDK_ROOT}" --licenses >/dev/null
sdkmanager --sdk_root="${ANDROID_SDK_ROOT}" --install \
"platform-tools" \
"platforms;android-36" \
"platforms;android-37.0" \
"build-tools;36.0.0"
- name: Run Android ${{ matrix.task }}

View File

@@ -22,6 +22,12 @@ on:
push:
branches:
- main
paths:
- ".github/actions/**"
- ".github/codeql/**"
- ".github/workflows/**"
- "packages/**"
- "src/**"
schedule:
- cron: "0 6 * * *"
@@ -49,32 +55,32 @@ jobs:
include:
- language: javascript-typescript
category: core-auth-secrets
runs_on: ubuntu-24.04
runs_on: blacksmith-8vcpu-ubuntu-2404
timeout_minutes: 25
config_file: ./.github/codeql/codeql-core-auth-secrets-critical-security.yml
- language: javascript-typescript
category: channel-runtime-boundary
runs_on: ubuntu-24.04
runs_on: blacksmith-8vcpu-ubuntu-2404
timeout_minutes: 25
config_file: ./.github/codeql/codeql-channel-runtime-boundary-critical-security.yml
- language: javascript-typescript
category: network-ssrf-boundary
runs_on: ubuntu-24.04
runs_on: blacksmith-4vcpu-ubuntu-2404
timeout_minutes: 25
config_file: ./.github/codeql/codeql-network-ssrf-boundary-critical-security.yml
- language: javascript-typescript
category: mcp-process-tool-boundary
runs_on: ubuntu-24.04
runs_on: blacksmith-4vcpu-ubuntu-2404
timeout_minutes: 25
config_file: ./.github/codeql/codeql-mcp-process-tool-boundary-critical-security.yml
- language: javascript-typescript
category: plugin-trust-boundary
runs_on: ubuntu-24.04
runs_on: blacksmith-4vcpu-ubuntu-2404
timeout_minutes: 25
config_file: ./.github/codeql/codeql-plugin-trust-boundary-critical-security.yml
- language: actions
category: actions
runs_on: ubuntu-24.04
runs_on: blacksmith-8vcpu-ubuntu-2404
timeout_minutes: 10
config_file: ./.github/codeql/codeql-actions-critical-security.yml
steps:

View File

@@ -27,8 +27,10 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const zlib = require("node:zlib");
const childProcess = require("node:child_process");
const marker = "<!-- openclaw-ios-periphery-dead-code -->";
const run = context.payload.workflow_run;
@@ -124,7 +126,10 @@ jobs:
archive_format: "zip",
});
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "ios-periphery-"));
const archivePath = path.join(dir, "artifact.zip");
const archiveBuffer = Buffer.from(archive.data);
fs.writeFileSync(archivePath, archiveBuffer);
const allowedArtifactFiles = new Set([
"periphery.json",
@@ -235,59 +240,19 @@ jobs:
return;
}
entries.set(name, {
compressedSize,
compressionMethod,
localHeaderOffset: readUInt32(offset + 42),
uncompressedSize,
});
entries.set(name, { uncompressedSize });
offset = nextOffset;
}
const readZipEntry = (name, entry) => {
const localHeaderOffset = entry.localHeaderOffset;
if (
localHeaderOffset + 30 > archiveBuffer.length ||
readUInt32(localHeaderOffset) !== 0x04034b50
) {
throw new Error(`${name} has an invalid local header.`);
}
const localNameLength = readUInt16(localHeaderOffset + 26);
const localExtraLength = readUInt16(localHeaderOffset + 28);
const dataStart = localHeaderOffset + 30 + localNameLength + localExtraLength;
const dataEnd = dataStart + entry.compressedSize;
if (dataEnd > archiveBuffer.length) {
throw new Error(`${name} exceeds archive bounds.`);
}
const compressed = archiveBuffer.subarray(dataStart, dataEnd);
let contents;
if (entry.compressionMethod === 0) {
contents = compressed;
} else {
try {
contents = zlib.inflateRawSync(compressed, { maxOutputLength: maxEntryBytes });
} catch (error) {
if (error && error.code === "ERR_BUFFER_TOO_LARGE") {
throw new Error(`${name} exceeded the per-file size limit while reading.`);
}
throw error;
}
}
if (contents.length !== entry.uncompressedSize || contents.length > maxEntryBytes) {
throw new Error(`${name} exceeded the per-file size limit while reading.`);
}
return contents.toString("utf8");
};
const files = new Map();
for (const [name, entry] of entries) {
let contents;
try {
contents = readZipEntry(name, entry);
} catch (error) {
core.warning(`Skipping ${artifactName}; ${error instanceof Error ? error.message : String(error)}`);
const contents = childProcess.execFileSync("unzip", ["-p", archivePath, name], {
encoding: "utf8",
maxBuffer: Math.max(1, entry.uncompressedSize + 1024),
timeout: 5000,
});
if (Buffer.byteLength(contents, "utf8") > maxEntryBytes) {
core.warning(`Skipping ${artifactName}; ${name} exceeded the per-file size limit while reading.`);
return;
}
files.set(name, contents);

View File

@@ -220,7 +220,7 @@ jobs:
with:
name: ios-periphery-dead-code-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/ios-periphery
if-no-files-found: error
if-no-files-found: warn
retention-days: 14
- name: Fail on dead code

View File

@@ -171,4 +171,4 @@ jobs:
name: mantis-discord-smoke-${{ github.run_id }}-${{ github.run_attempt }}
path: .artifacts/qa-e2e/mantis/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn

View File

@@ -540,7 +540,7 @@ jobs:
name: mantis-discord-status-reactions-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_mantis.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Create Mantis GitHub App token
id: mantis_app_token

View File

@@ -547,7 +547,7 @@ jobs:
with:
name: mantis-discord-thread-attachment-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_mantis.outputs.output_dir }}
if-no-files-found: error
if-no-files-found: warn
retention-days: 14
- name: Create Mantis GitHub App token

View File

@@ -458,7 +458,7 @@ jobs:
name: mantis-slack-desktop-smoke-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_mantis.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Create Mantis GitHub App token
id: mantis_app_token

View File

@@ -556,7 +556,7 @@ jobs:
name: mantis-telegram-desktop-proof-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.inspect.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Create Mantis GitHub App token
id: mantis_app_token

View File

@@ -506,7 +506,7 @@ jobs:
name: mantis-telegram-live-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_mantis.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Create Mantis GitHub App token
id: mantis_app_token

View File

@@ -1,358 +0,0 @@
name: Maturity scorecard
on:
workflow_dispatch:
inputs:
qa_evidence_run_id:
description: Optional workflow run id containing qa-evidence.json
required: false
type: string
ref:
description: OpenClaw branch, tag, or SHA containing the maturity score source
required: true
default: main
type: string
permissions:
actions: read
contents: read
concurrency:
group: ${{ format('{0}-{1}-{2}', github.workflow, inputs.ref, inputs.qa_evidence_run_id || github.run_id) }}
cancel-in-progress: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
NODE_VERSION: "24.x"
jobs:
validate_selected_ref:
name: Validate selected ref
runs-on: ubuntu-24.04
outputs:
selected_revision: ${{ steps.validate.outputs.selected_revision }}
trusted_reason: ${{ steps.validate.outputs.trusted_reason }}
steps:
- name: Checkout selected ref
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
ref: ${{ inputs.ref }}
fetch-depth: 0
- name: Validate selected ref
id: validate
env:
INPUT_REF: ${{ inputs.ref }}
shell: bash
run: |
set -euo pipefail
selected_revision="$(git rev-parse HEAD)"
trusted_reason=""
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
if git merge-base --is-ancestor "$selected_revision" refs/remotes/origin/main; then
trusted_reason="main-ancestor"
elif git tag --points-at "$selected_revision" | grep -Eq '^v'; then
trusted_reason="release-tag"
elif [[ "$INPUT_REF" =~ ^release/[0-9]{4}\.[0-9]+\.[0-9]+$ ]]; then
git fetch --no-tags origin "+refs/heads/${INPUT_REF}:refs/remotes/origin/${INPUT_REF}"
release_branch_sha="$(git rev-parse "refs/remotes/origin/${INPUT_REF}")"
if [[ "$selected_revision" == "$release_branch_sha" ]]; then
trusted_reason="release-branch-head"
fi
fi
if [[ -z "$trusted_reason" ]]; then
echo "Ref '${INPUT_REF}' resolved to $selected_revision, which is not trusted for this secret-bearing maturity scorecard run." >&2
echo "Allowed refs must be on main, point to a release tag, or match a release branch head." >&2
exit 1
fi
echo "selected_revision=$selected_revision" >> "$GITHUB_OUTPUT"
echo "trusted_reason=$trusted_reason" >> "$GITHUB_OUTPUT"
{
echo "### Target"
echo
echo "- Requested ref: \`${INPUT_REF}\`"
echo "- Resolved SHA: \`$selected_revision\`"
echo "- Trust reason: \`$trusted_reason\`"
} >> "$GITHUB_STEP_SUMMARY"
generate_qa_evidence:
name: Generate full taxonomy QA evidence
needs: validate_selected_ref
if: ${{ inputs.qa_evidence_run_id == '' }}
uses: ./.github/workflows/qa-profile-evidence.yml
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
qa_profile: all
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
publish:
name: Publish maturity docs PR
needs:
- validate_selected_ref
- generate_qa_evidence
if: ${{ always() && needs.validate_selected_ref.result == 'success' && (inputs.qa_evidence_run_id != '' || needs.generate_qa_evidence.result == 'success') }}
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
actions: read
contents: read
steps:
- name: Checkout selected ref
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: ${{ env.NODE_VERSION }}
install-bun: "false"
- name: Download provided QA evidence artifact
if: ${{ inputs.qa_evidence_run_id != '' }}
env:
GH_TOKEN: ${{ github.token }}
QA_EVIDENCE_RUN_ID: ${{ inputs.qa_evidence_run_id }}
run: |
set -euo pipefail
mkdir -p .artifacts/maturity-evidence
gh run download "$QA_EVIDENCE_RUN_ID" \
--repo "$GITHUB_REPOSITORY" \
--dir .artifacts/maturity-evidence
- name: Download generated QA evidence artifact
if: ${{ inputs.qa_evidence_run_id == '' }}
env:
GENERATED_ARTIFACT_NAME: ${{ needs.generate_qa_evidence.outputs.artifact_name }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
if [[ -z "${GENERATED_ARTIFACT_NAME:-}" ]]; then
echo "Generated QA evidence workflow did not expose an artifact name." >&2
exit 1
fi
mkdir -p .artifacts/maturity-evidence
gh run download "$GITHUB_RUN_ID" \
--repo "$GITHUB_REPOSITORY" \
--name "$GENERATED_ARTIFACT_NAME" \
--dir .artifacts/maturity-evidence
- name: Require one QA evidence file
id: evidence
env:
QA_EVIDENCE_RUN_ID: ${{ inputs.qa_evidence_run_id }}
run: |
set -euo pipefail
mapfile -t evidence_paths < <(find .artifacts/maturity-evidence -type f -name qa-evidence.json | sort)
if [[ "${#evidence_paths[@]}" -eq 0 ]]; then
echo "Expected a qa-evidence.json file in the downloaded QA evidence artifact." >&2
exit 1
fi
if [[ "${#evidence_paths[@]}" -gt 1 ]]; then
echo "Expected exactly one qa-evidence.json file, found ${#evidence_paths[@]}:" >&2
printf '%s\n' "${evidence_paths[@]}" >&2
exit 1
fi
echo "qa_evidence_path=${evidence_paths[0]}" >> "$GITHUB_OUTPUT"
{
echo "### QA evidence"
echo
echo "- Evidence path: \`${evidence_paths[0]}\`"
echo "- Evidence source run: \`${QA_EVIDENCE_RUN_ID:-$GITHUB_RUN_ID}\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Validate QA evidence manifest
env:
QA_EVIDENCE_PATH: ${{ steps.evidence.outputs.qa_evidence_path }}
TARGET_SHA: ${{ needs.validate_selected_ref.outputs.selected_revision }}
run: |
set -euo pipefail
node --input-type=module <<'NODE'
import fs from "node:fs";
import path from "node:path";
const evidencePath = process.env.QA_EVIDENCE_PATH;
const targetSha = process.env.TARGET_SHA;
if (!evidencePath) {
throw new Error("QA_EVIDENCE_PATH is required");
}
if (!targetSha) {
throw new Error("TARGET_SHA is required");
}
const evidence = JSON.parse(fs.readFileSync(evidencePath, "utf8"));
if (evidence.profile !== "all") {
throw new Error(`qa-evidence.json profile must be all, got ${JSON.stringify(evidence.profile)}`);
}
const artifactDir = path.dirname(evidencePath);
const manifestNames = fs
.readdirSync(artifactDir)
.filter((name) => name.endsWith("qa-profile-evidence-manifest.json"))
.sort();
if (manifestNames.length !== 1) {
throw new Error(
`Expected exactly one QA profile evidence manifest next to qa-evidence.json, found ${manifestNames.length}`,
);
}
const manifestPath = path.join(artifactDir, manifestNames[0]);
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
const manifestProfile = manifest.qaProfile ?? evidence.profile;
if (manifestProfile !== "all") {
throw new Error(`QA evidence manifest profile must be all, got ${JSON.stringify(manifestProfile)}`);
}
if (manifest.targetSha !== targetSha) {
throw new Error(`QA evidence manifest targetSha ${manifest.targetSha} does not match selected ref ${targetSha}`);
}
NODE
- name: Validate maturity score sources
run: |
node --import tsx --input-type=module <<'NODE'
import { readValidatedQaMaturityScoreSources } from "./extensions/qa-lab/src/scorecard-taxonomy.ts";
const { warnings } = readValidatedQaMaturityScoreSources({
scoresPath: "qa/maturity-scores.yaml",
taxonomyPath: "taxonomy.yaml",
});
for (const warning of warnings) {
console.error(`warning: ${warning}`);
}
NODE
- name: Render artifact docs
run: |
set -euo pipefail
pnpm maturity:render -- \
--output-dir .artifacts/maturity-docs \
--static-assets-dir .artifacts/maturity-docs/assets/maturity \
--scores qa/maturity-scores.yaml \
--evidence-dir .artifacts/maturity-evidence \
--strict-inputs
{
echo "### Maturity scorecard docs"
echo
echo "- Source validation: passed"
echo "- Artifact docs: \`.artifacts/maturity-docs\`"
echo "- Strict inputs: \`true\`"
echo "- QA evidence: included"
} >> "$GITHUB_STEP_SUMMARY"
- name: Render committed docs preview
run: |
set -euo pipefail
pnpm maturity:render -- \
--output-dir docs \
--scores qa/maturity-scores.yaml \
--evidence-dir .artifacts/maturity-evidence \
--strict-inputs
- name: Create generated docs PR app token
id: app-token
continue-on-error: true
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
- name: Create generated docs PR fallback app token
if: ${{ steps.app-token.outcome == 'failure' }}
id: app-token-fallback
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
with:
app-id: "2971289"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
permission-contents: write
permission-pull-requests: write
- name: Open generated docs PR
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
QA_EVIDENCE_RUN_ID: ${{ inputs.qa_evidence_run_id }}
REF_INPUT: ${{ inputs.ref }}
run: |
set -euo pipefail
if [[ -z "${GH_TOKEN:-}" ]]; then
echo "Maturity scorecard PR creation requires the OpenClaw GitHub App token secrets." >&2
exit 1
fi
if [[ -z "$(git status --porcelain -- qa/maturity-scores.yaml docs/maturity-scores.yaml docs/maturity/scorecard.md docs/maturity/taxonomy.md)" ]]; then
{
echo
echo "- Pull request: skipped; generated scorecard matches selected ref"
} >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
evidence_run_id="${QA_EVIDENCE_RUN_ID:-$GITHUB_RUN_ID}"
branch="automation/maturity-scorecard-${evidence_run_id}"
base_branch="${REF_INPUT:-main}"
if ! git ls-remote --exit-code --heads origin "$base_branch" >/dev/null 2>&1; then
base_branch="main"
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
gh auth setup-git
git fetch --no-tags --depth=1 origin "refs/heads/${branch}:refs/remotes/origin/${branch}" || true
git switch -C "$branch"
git add qa/maturity-scores.yaml docs/maturity/scorecard.md docs/maturity/taxonomy.md
if git ls-files --error-unmatch docs/maturity-scores.yaml >/dev/null 2>&1 || [[ -e docs/maturity-scores.yaml ]]; then
git add docs/maturity-scores.yaml
fi
git commit -m "docs: update maturity scorecard"
git push --force-with-lease origin "$branch"
body_file=".artifacts/maturity-scorecard-pr-body.md"
mkdir -p "$(dirname "$body_file")"
cat > "$body_file" <<BODY
## Summary
- render maturity scorecard docs from \`qa/maturity-scores.yaml\` and release QA evidence
- maturity source ref: ${REF_INPUT}
- QA evidence run: ${evidence_run_id}
## Verification
- QA Lab maturity score validation passed
- Maturity scorecard workflow rendered docs from release profile qa-evidence.json artifacts with strict inputs
BODY
pr_url="$(gh pr list --head "$branch" --state open --json url --jq '.[0].url // ""')"
if [[ -n "$pr_url" ]]; then
gh pr edit "$pr_url" \
--title "docs: update maturity scorecard" \
--body-file "$body_file"
else
pr_url="$(gh pr create \
--base "$base_branch" \
--head "$branch" \
--title "docs: update maturity scorecard" \
--body-file "$body_file")"
fi
{
echo
echo "- Pull request: ${pr_url}"
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload maturity docs artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: maturity-scorecard-docs-${{ github.run_id }}-${{ github.run_attempt }}
path: .artifacts/maturity-docs/
retention-days: 30
if-no-files-found: error

View File

@@ -273,4 +273,4 @@ jobs:
name: npm-telegram-beta-e2e-${{ github.run_id }}-${{ github.run_attempt }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn

View File

@@ -853,7 +853,7 @@ jobs:
name: release-qa-parity-${{ matrix.lane }}-${{ needs.resolve_target.outputs.revision }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Record advisory status
if: always()
@@ -959,7 +959,7 @@ jobs:
name: release-qa-parity-${{ needs.resolve_target.outputs.revision }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Record advisory status
if: always()
@@ -1131,7 +1131,7 @@ jobs:
name: release-qa-runtime-parity-${{ needs.resolve_target.outputs.revision }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Record advisory status
if: always()
@@ -1241,13 +1241,13 @@ jobs:
--output .artifacts/qa-e2e/runtime-parity-standard-report/qa-runtime-tool-coverage-report.md
- name: Upload runtime tool coverage artifacts
if: ${{ always() && steps.verify_runtime_parity_status.outputs.ready == 'true' }}
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: release-qa-runtime-tool-coverage-${{ needs.resolve_target.outputs.revision }}
path: .artifacts/qa-e2e/runtime-parity-standard-report/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
qa_live_matrix_release_checks:
name: Run QA Lab live Matrix lane
@@ -1327,7 +1327,7 @@ jobs:
name: release-qa-live-matrix-${{ needs.resolve_target.outputs.revision }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Record advisory status
if: always()
@@ -1467,7 +1467,7 @@ jobs:
name: release-qa-live-telegram-${{ needs.resolve_target.outputs.revision }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Record advisory status
if: always()
@@ -1607,7 +1607,7 @@ jobs:
name: release-qa-live-discord-${{ needs.resolve_target.outputs.revision }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Record advisory status
if: always()
@@ -1750,7 +1750,7 @@ jobs:
name: release-qa-live-whatsapp-${{ needs.resolve_target.outputs.revision }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Record advisory status
if: always()
@@ -1890,7 +1890,7 @@ jobs:
name: release-qa-live-slack-${{ needs.resolve_target.outputs.revision }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
- name: Record advisory status
if: always()

View File

@@ -1466,9 +1466,9 @@ jobs:
fi
- name: Upload postpublish evidence
if: ${{ always() && inputs.publish_openclaw_npm }}
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: openclaw-release-postpublish-evidence-${{ inputs.tag }}
path: ${{ runner.temp }}/openclaw-release-postpublish-evidence
if-no-files-found: error
if-no-files-found: ignore

View File

@@ -66,5 +66,5 @@ jobs:
with:
name: opengrep-full-sarif
path: .opengrep-out/precise.sarif
if-no-files-found: error
if-no-files-found: warn
retention-days: 30

View File

@@ -97,5 +97,5 @@ jobs:
with:
name: opengrep-pr-diff-sarif
path: .opengrep-out/precise.sarif
if-no-files-found: error
if-no-files-found: warn
retention-days: 30

View File

@@ -226,7 +226,7 @@ jobs:
name: qa-parity-${{ github.run_id }}-${{ github.run_attempt }}
path: .artifacts/qa-e2e/
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
run_live_runtime_token_efficiency:
name: Run live runtime token-efficiency lane
@@ -315,7 +315,7 @@ jobs:
name: qa-live-runtime-token-efficiency-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_lane.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
run_live_matrix:
name: Run Matrix live QA lane
@@ -391,7 +391,7 @@ jobs:
name: qa-live-matrix-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_lane.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
run_live_matrix_sharded:
name: Run Matrix live QA lane (${{ matrix.profile }})
@@ -475,7 +475,7 @@ jobs:
name: qa-live-matrix-${{ matrix.profile }}-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_lane.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
run_live_telegram:
name: Run Telegram live QA lane with Convex leases
@@ -570,7 +570,7 @@ jobs:
name: qa-live-telegram-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_lane.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
run_live_discord:
name: Run Discord live QA lane with Convex leases
@@ -665,7 +665,7 @@ jobs:
name: qa-live-discord-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_lane.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
run_live_whatsapp:
name: Run WhatsApp live QA lane with Convex leases
@@ -763,7 +763,7 @@ jobs:
name: qa-live-whatsapp-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_lane.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn
run_live_slack:
name: Run Slack live QA lane with Convex leases
@@ -859,4 +859,4 @@ jobs:
name: qa-live-slack-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_lane.outputs.output_dir }}
retention-days: 14
if-no-files-found: error
if-no-files-found: warn

View File

@@ -16,7 +16,7 @@ on:
default: ""
type: string
qa_profile:
description: Taxonomy QA profile id to run (for example release or all)
description: Taxonomy QA profile id to run
required: true
default: release
type: string
@@ -35,10 +35,11 @@ on:
description: Taxonomy QA profile id to run
required: true
type: string
secrets:
OPENAI_API_KEY:
description: OpenAI API key used by live QA profile scenarios
required: true
fail_on_qa_failure:
description: Fail the reusable workflow when the QA profile command exits non-zero
required: false
default: false
type: boolean
outputs:
artifact_name:
description: Uploaded QA profile evidence artifact name
@@ -243,9 +244,6 @@ jobs:
NODE_OPTIONS: --max-old-space-size=8192
run: node scripts/build-all.mjs qaRuntime
- name: Ensure Playwright Chromium
run: node scripts/ensure-playwright-chromium.mjs
- name: Run QA profile
id: run_profile
env:
@@ -360,8 +358,8 @@ jobs:
retention-days: 30
if-no-files-found: error
- name: Fail if QA profile failed
if: always()
- name: Fail if configured QA gate failed
if: always() && (github.event_name == 'workflow_dispatch' || inputs.fail_on_qa_failure)
env:
QA_EXIT_CODE: ${{ steps.run_profile.outputs.qa_exit_code }}
QA_PROFILE: ${{ steps.profile.outputs.profile }}

View File

@@ -150,7 +150,6 @@ jobs:
git --version
- name: Run Testbox
if: always()
shell: bash
run: |
set -euo pipefail

View File

@@ -61,7 +61,7 @@ We prioritize secure defaults, but also expose clear knobs for trusted high-powe
## Plugins & Memory
OpenClaw has an extensive plugin API.
Core stays lean; optional capabilities should usually ship as plugins.
Core stays lean; optional capability should usually ship as plugins.
We are generally slimming down core while expanding what plugins can do.
If a useful feature cannot be built as a plugin yet, we welcome PRs and design discussions that extend the plugin API instead of adding one-off core behavior.

View File

@@ -59,7 +59,7 @@ plugins {
android {
namespace = "ai.openclaw.app"
compileSdk = 36
compileSdk = 37
// Release signing is local-only; keep the keystore path and passwords out of the repo.
signingConfigs {

View File

@@ -49,8 +49,18 @@ import java.util.concurrent.Executors
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
private fun createDnsResolver(context: Context): DnsResolver =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN) {
createContextDnsResolver(context)
} else {
createLegacyDnsResolver()
}
@RequiresApi(Build.VERSION_CODES.CINNAMON_BUN)
private fun createContextDnsResolver(context: Context): DnsResolver = DnsResolver(context, null)
@Suppress("DEPRECATION")
private fun createDnsResolver(): DnsResolver = DnsResolver.getInstance()
private fun createLegacyDnsResolver(): DnsResolver = DnsResolver.getInstance()
/**
* Watches local DNS-SD and optional wide-area DNS-SD for reachable OpenClaw gateways.
@@ -61,7 +71,7 @@ class GatewayDiscovery(
) {
private val nsd = context.getSystemService(NsdManager::class.java)
private val connectivity = context.getSystemService(ConnectivityManager::class.java)
private val dns = createDnsResolver()
private val dns = createDnsResolver(context)
private val serviceType = "_openclaw-gw._tcp."
private val wideAreaDomain = System.getenv("OPENCLAW_WIDE_AREA_DOMAIN")
private val logTag = "OpenClaw/GatewayDiscovery"

View File

@@ -5,7 +5,7 @@ plugins {
android {
namespace = "ai.openclaw.app.benchmark"
compileSdk = 36
compileSdk = 37
defaultConfig {
minSdk = 31

View File

@@ -4,7 +4,7 @@ androidx-activity = "1.13.0"
androidx-benchmark = "1.4.1"
androidx-camera = "1.6.0"
androidx-compose-bom = "2026.05.01"
androidx-core = "1.18.0"
androidx-core = "1.19.0"
androidx-exifinterface = "1.4.2"
androidx-lifecycle = "2.10.0"
androidx-security = "1.1.0"

View File

@@ -67,9 +67,9 @@ Release behavior:
- App Store release uses canonical `ai.openclawfoundation.app*` bundle IDs through a temporary generated xcconfig in `apps/ios/build/AppStoreRelease.xcconfig`.
- App Store release uses manual `Apple Distribution` signing with profile names pinned in `apps/ios/Config/AppStoreSigning.json`.
- Fastlane owns one-time Developer Portal setup, encrypted `match` signing sync to the repo/branch pinned in `apps/ios/Config/AppStoreSigning.json`, and release handling.
- App Store release also switches the app to `OpenClawPushMode=appStore`, which derives relay transport, official distribution, the canonical production relay, production APNs, production relay profile, `appleStrict` proof, and the App-Attest-capable entitlement file.
- App Store release also switches the app to `OpenClawPushTransport=relay`, `OpenClawPushDistribution=official`, `OpenClawPushAPNsEnvironment=production`, `OpenClawPushRelayProfile=production`, `OpenClawPushProofPolicy=appleStrict`, and the App-Attest-capable entitlement file.
- `pnpm ios:release:upload` generates App Store screenshots and uploads release notes before archiving and uploading the IPA.
- The release archive is validated before upload by inspecting the exported IPA's signed entitlements, embedded App Store profile, and push mode. The upload fails if the IPA is not an App Store production relay build.
- `pnpm ios:release` remains a compatibility alias for `pnpm ios:release:upload`; prefer the explicit upload command in new release docs and automation.
- App Review submission is manual in App Store Connect. The release lane uploads a build and metadata, but does not submit for review.
- The release flow does not modify `apps/ios/.local-signing.xcconfig` or `apps/ios/LocalSigning.xcconfig`.
- `apps/ios/version.json` is the pinned iOS release version source.
@@ -83,8 +83,9 @@ Release behavior:
Relay behavior for App Store builds:
- App Store release builds use the canonical hosted relay at `https://ios-push-relay.openclaw.ai`.
- App Store release builds reject custom relay URL overrides. Future self-hosted relay support should use a separate explicit release path, not the public App Store build lane.
- Release builds default to `https://ios-push-relay.openclaw.ai`.
- Optional custom relay override: `OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com`
This must be a plain `https://host[:port][/path]` base URL without whitespace, query params, fragments, or xcconfig metacharacters.
Signing setup commands:
@@ -161,19 +162,25 @@ This should create `apps/ios/fastlane/.env` with non-secret App Store Connect va
Use `pnpm ios:release:signing:setup` for the initial portal setup, then `MATCH_PASSWORD=... pnpm ios:release:signing:sync:push` to publish encrypted Fastlane match assets to the shared private repo.
4. If you are starting a brand-new production release train, pin iOS to the current gateway version first:
4. Optional: set a custom official relay URL for the build. If unset, the release flow uses `https://ios-push-relay.openclaw.ai`.
```bash
export OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com
```
5. If you are starting a brand-new production release train, pin iOS to the current gateway version first:
```bash
pnpm ios:version:pin -- --from-gateway
```
5. Upload the build:
6. Upload the build:
```bash
pnpm ios:release:upload
```
6. Expected behavior:
7. Expected behavior:
- Fastlane reads `apps/ios/version.json`
- verifies synced iOS versioning artifacts
- resolves the next App Store Connect build number for that short version
@@ -181,16 +188,15 @@ pnpm ios:release:upload
- uploads release notes and screenshots to the editable App Store version
- generates `apps/ios/build/AppStoreRelease.xcconfig`
- archives `OpenClaw`
- validates the exported IPA's push mode, signed entitlements, and embedded App Store profile
- uploads the IPA to App Store Connect for TestFlight/App Review use
- leaves App Review submission for a maintainer to complete manually
7. Expected outputs after a successful run:
8. Expected outputs after a successful run:
- `apps/ios/build/app-store/OpenClaw-<version>.ipa`
- `apps/ios/build/app-store/OpenClaw-<version>.app.dSYM.zip`
- Fastlane log line like `Uploaded iOS App Store build: version=<version> short=<short> build=<build>`
8. If this is a fresh clone on a maintainer machine that already works elsewhere, it is OK to copy the non-secret `apps/ios/fastlane/.env` from another trusted local clone on the same Mac. The Keychain-backed private key remains machine-local and is not stored in the repo.
9. If this is a fresh clone on a maintainer machine that already works elsewhere, it is OK to copy the non-secret `apps/ios/fastlane/.env` from another trusted local clone on the same Mac. The Keychain-backed private key remains machine-local and is not stored in the repo.
## iOS Versioning Workflow
@@ -240,13 +246,13 @@ See `apps/ios/VERSIONING.md` for the detailed spec.
- `apps/ios/Sources/OpenClaw.entitlements` derives `aps-environment` from the active build configuration/signing override.
- App Attest relay builds use `apps/ios/Sources/OpenClawAppAttest.entitlements`; local/direct builds do not require App Attest provisioning.
- APNs token registration to gateway happens only after gateway connection (`push.apns.register`).
- Local/manual Debug builds default to `OpenClawPushMode=localSandbox`, direct APNs registration, and a development `aps-environment` entitlement. Local/manual Release builds default to `OpenClawPushMode=localProduction` and direct production APNs registration.
- Local/manual builds default to `OpenClawPushTransport=direct`, `OpenClawPushDistribution=local`, and a development `aps-environment` entitlement.
- Your selected team/profile must support Push Notifications for the app bundle ID you are signing.
- If push capability or provisioning is wrong, APNs registration fails at runtime (check Xcode logs for `APNs registration failed`).
- The gateway host also needs direct APNs auth configured separately with `OPENCLAW_APNS_TEAM_ID`, `OPENCLAW_APNS_KEY_ID`, and either `OPENCLAW_APNS_PRIVATE_KEY_P8` or `OPENCLAW_APNS_PRIVATE_KEY_PATH`.
- Recommended gateway-host storage for the APNs `.p8` file is `~/.openclaw/credentials/apns/AuthKey_<KEYID>.p8` with restrictive permissions, then point `OPENCLAW_APNS_PRIVATE_KEY_PATH` at that file.
- `apps/ios/fastlane/.env` only covers App Store Connect / Fastlane auth; it does not provide gateway APNs credentials for local direct-push testing.
- Debug builds default to sandbox APNs through `OpenClawPushMode=localSandbox`; Release builds default to production APNs through `OpenClawPushMode=localProduction`.
- Debug builds default to `OpenClawPushAPNsEnvironment=sandbox`; Release builds default to `production`.
## APNs Expectations For Official Builds
@@ -255,7 +261,7 @@ See `apps/ios/VERSIONING.md` for the detailed spec.
- The relay registration is bound to the gateway identity fetched from `gateway.identity.get`, so another gateway cannot reuse that stored registration.
- The app persists the relay handle metadata locally so reconnects can republish the gateway registration without re-registering on every connect.
- If the relay base URL changes in a later build, the app refreshes the relay registration instead of reusing the old relay origin.
- App Store release mode uses the internal `production` relay profile, production APNs, App Attest, and a StoreKit app transaction JWS during registration.
- Production relay mode uses the `production` relay profile, production APNs, App Attest, and a StoreKit app transaction JWS during registration.
- Gateway-side relay sending is configured through `gateway.push.apns.relay.baseUrl` in `openclaw.json`. `OPENCLAW_APNS_RELAY_BASE_URL` remains a temporary env override only.
## Official Build Relay Trust Model

View File

@@ -152,7 +152,6 @@ extension SettingsProTab {
}
let notificationSettings = await UNUserNotificationCenter.current().notificationSettings()
self.applyNotificationStatus(notificationSettings.authorizationStatus)
self.registerForRemoteNotificationsIfEnrollmentReady()
let issueCount = SettingsDiagnostics.issueCount(
gatewayConnected: self.gatewayDiagnosticConnected,
@@ -418,7 +417,6 @@ extension SettingsProTab {
let status = settings.authorizationStatus
Task { @MainActor in
self.applyNotificationStatus(status)
self.registerForRemoteNotificationsIfEnrollmentReady()
}
}
}
@@ -439,7 +437,6 @@ extension SettingsProTab {
func requestNotificationAuthorizationFromSettings() {
guard !self.isRequestingNotificationAuthorization else { return }
PushEnrollmentConsent.markDisclosureAccepted()
self.isRequestingNotificationAuthorization = true
Task {
let granted = await (try? UNUserNotificationCenter.current().requestAuthorization(options: [
@@ -451,19 +448,12 @@ extension SettingsProTab {
await MainActor.run {
self.isRequestingNotificationAuthorization = false
self.notificationStatus = SettingsNotificationStatus(settings.authorizationStatus)
guard granted else { return }
self.registerForRemoteNotificationsIfEnrollmentReady()
guard granted, self.notificationStatus.allowsNotifications else { return }
UIApplication.shared.registerForRemoteNotifications()
}
}
}
@MainActor
func registerForRemoteNotificationsIfEnrollmentReady() {
guard PushEnrollmentConsent.disclosureAccepted else { return }
guard self.notificationStatus.allowsNotifications else { return }
UIApplication.shared.registerForRemoteNotifications()
}
@MainActor
func applyNotificationStatus(_ status: UNAuthorizationStatus) {
self.notificationStatus = SettingsNotificationStatus(status)

View File

@@ -443,54 +443,12 @@ enum GatewaySettingsStore {
}
enum GatewayDiagnostics {
struct ScopedLogger {
private let prefix: String
fileprivate init(prefix: String) {
self.prefix = prefix
}
func stage(_ message: String) {
GatewayDiagnostics.log("\(self.prefix): \(GatewayDiagnostics.sanitizeScopedMessage(message))")
}
func skipped(_ reason: String) {
self.stage("registration skipped reason=\(reason)")
}
func failed(_ stage: String, error: Error) {
let nsError = error as NSError
self
.stage(
"\(stage) failed errorType=\(String(reflecting: type(of: error))) domain=\(nsError.domain) code=\(nsError.code)")
}
}
private static let logger = Logger(subsystem: "ai.openclawfoundation.app", category: "GatewayDiag")
private static let queue = DispatchQueue(label: "ai.openclawfoundation.app.gateway.diagnostics")
private static let maxLogBytes: Int64 = 512 * 1024
private static let keepLogBytes: Int64 = 256 * 1024
private static let logSizeCheckEveryWrites = 50
private static let logWritesSinceCheck = OSAllocatedUnfairLock(initialState: 0)
private static let maxScopedMessageCharacters = 320
/// Keep relay diagnostics stage-based. Push tokens, relay grants, proofs,
/// receipts, signed payloads, and handles must never enter this cache log.
static let pushRelay = ScopedLogger(prefix: "push relay")
private static func sanitizeScopedMessage(_ value: String) -> String {
let collapsed = value
.replacingOccurrences(of: "\r", with: " ")
.replacingOccurrences(of: "\n", with: " ")
.replacingOccurrences(of: "\t", with: " ")
.trimmingCharacters(in: .whitespacesAndNewlines)
guard collapsed.count > self.maxScopedMessageCharacters else {
return collapsed
}
let end = collapsed.index(collapsed.startIndex, offsetBy: self.maxScopedMessageCharacters)
return String(collapsed[..<end]) + "..."
}
private static func isoTimestamp() -> String {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]

View File

@@ -82,10 +82,18 @@
<string>$(OPENCLAW_APP_GROUP_ID)</string>
<key>OpenClawCanonicalVersion</key>
<string>$(OPENCLAW_IOS_VERSION)</string>
<key>OpenClawPushMode</key>
<string>$(OPENCLAW_PUSH_MODE)</string>
<key>OpenClawPushAPNsEnvironment</key>
<string>$(OPENCLAW_PUSH_APNS_ENVIRONMENT)</string>
<key>OpenClawPushDistribution</key>
<string>$(OPENCLAW_PUSH_DISTRIBUTION)</string>
<key>OpenClawPushProofPolicy</key>
<string>$(OPENCLAW_PUSH_PROOF_POLICY)</string>
<key>OpenClawPushRelayBaseURL</key>
<string>$(OPENCLAW_PUSH_RELAY_BASE_URL)</string>
<key>OpenClawPushRelayProfile</key>
<string>$(OPENCLAW_PUSH_RELAY_PROFILE)</string>
<key>OpenClawPushTransport</key>
<string>$(OPENCLAW_PUSH_TRANSPORT)</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>

View File

@@ -4102,87 +4102,42 @@ extension NodeAppModel {
}
private func registerAPNsTokenIfNeeded() async {
let usesRelayTransport = await self.pushRegistrationManager.usesRelayTransport
guard await self.canPublishAPNsRegistration(usesRelayTransport: usesRelayTransport) else {
return
}
guard self.gatewayConnected else {
if usesRelayTransport {
GatewayDiagnostics.pushRelay.skipped("gateway_offline")
}
return
}
guard self.gatewayConnected else { return }
guard let token = self.apnsDeviceTokenHex?.trimmingCharacters(in: .whitespacesAndNewlines),
!token.isEmpty
else {
if usesRelayTransport {
GatewayDiagnostics.pushRelay.skipped("missing_apns_token")
}
return
}
let usesRelayTransport = await self.pushRegistrationManager.usesRelayTransport
if !usesRelayTransport, token == self.apnsLastRegisteredTokenHex {
return
}
guard let topic = Bundle.main.bundleIdentifier?.trimmingCharacters(in: .whitespacesAndNewlines),
!topic.isEmpty
else {
if usesRelayTransport {
GatewayDiagnostics.pushRelay.skipped("missing_topic")
}
return
}
do {
let gatewayIdentity: PushRelayGatewayIdentity?
if usesRelayTransport {
guard self.operatorConnected else {
GatewayDiagnostics.pushRelay.skipped("operator_offline")
return
}
GatewayDiagnostics.pushRelay.stage("gateway identity request start")
guard self.operatorConnected else { return }
gatewayIdentity = try await self.fetchPushRelayGatewayIdentity()
GatewayDiagnostics.pushRelay.stage("gateway identity request complete")
} else {
gatewayIdentity = nil
}
if usesRelayTransport {
GatewayDiagnostics.pushRelay.stage("gateway registration payload start")
}
let payloadJSON = try await self.pushRegistrationManager.makeGatewayRegistrationPayload(
apnsTokenHex: token,
topic: topic,
gatewayIdentity: gatewayIdentity)
await self.nodeGateway.sendEvent(event: "push.apns.register", payloadJSON: payloadJSON)
self.apnsLastRegisteredTokenHex = token
if usesRelayTransport {
GatewayDiagnostics.pushRelay.stage("gateway registration event published")
}
} catch {
self.pushWakeLogger.error(
"APNs registration publish failed: \(error.localizedDescription, privacy: .public)")
if usesRelayTransport {
GatewayDiagnostics.pushRelay.failed("registration", error: error)
}
}
}
private func canPublishAPNsRegistration(usesRelayTransport: Bool) async -> Bool {
guard PushEnrollmentConsent.disclosureAccepted else {
if usesRelayTransport {
GatewayDiagnostics.pushRelay.skipped("enrollment_disclosure_not_accepted")
}
return false
}
let status = await self.notificationAuthorizationStatus()
guard Self.isNotificationAuthorizationAllowed(status) else {
if usesRelayTransport {
GatewayDiagnostics.pushRelay.skipped("notifications_not_authorized")
}
return false
}
return true
}
private func fetchPushRelayGatewayIdentity() async throws -> PushRelayGatewayIdentity {
let response = try await self.operatorGateway.request(
method: "gateway.identity.get",
@@ -5146,10 +5101,6 @@ extension NodeAppModel {
self.setOperatorConnected(connected)
}
func _test_canPublishAPNsRegistration(usesRelayTransport: Bool = true) async -> Bool {
await self.canPublishAPNsRegistration(usesRelayTransport: usesRelayTransport)
}
nonisolated static func _test_makeWatchChatItems(from raw: [OpenClawKit.AnyCodable]) -> [OpenClawWatchChatItem] {
self.makeWatchChatItems(from: raw)
}

View File

@@ -123,28 +123,8 @@ final class OpenClawAppDelegate: NSObject, UIApplicationDelegate, @preconcurrenc
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.delegate = self
ExecApprovalNotificationBridge.registerCategory(center: notificationCenter)
Task { @MainActor in
await self.registerForRemoteNotificationsIfEnrollmentReady(application)
}
return true
}
private func registerForRemoteNotificationsIfEnrollmentReady(_ application: UIApplication) async {
guard PushEnrollmentConsent.disclosureAccepted else { return }
guard await Self.isNotificationAuthorizationAllowed() else { return }
application.registerForRemoteNotifications()
}
private static func isNotificationAuthorizationAllowed() async -> Bool {
let settings = await UNUserNotificationCenter.current().notificationSettings()
switch settings.authorizationStatus {
case .authorized, .provisional, .ephemeral:
return true
case .denied, .notDetermined:
return false
@unknown default:
return false
}
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

View File

@@ -27,16 +27,7 @@ enum PushProofPolicy: String {
case internalSimulator
}
enum PushBuildMode: String {
case localSandbox
case localProduction
case appStore
case deviceSandbox
case simulatorSandbox
}
struct PushBuildConfig {
let mode: PushBuildMode
let transport: PushTransportMode
let distribution: PushDistributionMode
let relayBaseURL: URL?
@@ -63,64 +54,31 @@ struct PushBuildConfig {
}
init(bundle: Bundle = .main) {
self.init(readValue: { bundle.object(forInfoDictionaryKey: $0) })
self.transport = Self.readEnum(
bundle: bundle,
key: "OpenClawPushTransport",
fallback: .direct)
self.distribution = Self.readEnum(
bundle: bundle,
key: "OpenClawPushDistribution",
fallback: .local)
self.apnsEnvironment = Self.readEnum(
bundle: bundle,
key: "OpenClawPushAPNsEnvironment",
fallback: Self.defaultAPNsEnvironment)
self.relayProfile = Self.readEnum(
bundle: bundle,
key: "OpenClawPushRelayProfile",
fallback: Self.defaultRelayProfile(apnsEnvironment: self.apnsEnvironment))
self.proofPolicy = Self.readEnum(
bundle: bundle,
key: "OpenClawPushProofPolicy",
fallback: Self.defaultProofPolicy(relayProfile: self.relayProfile))
self.relayBaseURL = Self.readURL(bundle: bundle, key: "OpenClawPushRelayBaseURL")
}
init(infoDictionary: [String: Any]) {
self.init(readValue: { infoDictionary[$0] })
}
private init(readValue: (String) -> Any?) {
self.mode = Self.readEnum(
readValue: readValue,
key: "OpenClawPushMode",
fallback: .localSandbox)
let relayBaseURLOverride = Self.readURL(
readValue: readValue,
key: "OpenClawPushRelayBaseURL")
switch self.mode {
case .localSandbox:
self.transport = .direct
self.distribution = .local
self.relayBaseURL = nil
self.apnsEnvironment = .sandbox
self.relayProfile = .deviceSandbox
self.proofPolicy = .appleDevelopment
case .localProduction:
self.transport = .direct
self.distribution = .local
self.relayBaseURL = nil
self.apnsEnvironment = .production
self.relayProfile = .production
self.proofPolicy = .appleStrict
case .appStore:
self.transport = .relay
self.distribution = .official
self.relayBaseURL = URL(string: "https://\(Self.openClawHostedRelayHost)")!
self.apnsEnvironment = .production
self.relayProfile = .production
self.proofPolicy = .appleStrict
case .deviceSandbox:
self.transport = .relay
self.distribution = .official
self.relayBaseURL = relayBaseURLOverride
?? URL(string: "https://\(Self.openClawSandboxRelayHost)")!
self.apnsEnvironment = .sandbox
self.relayProfile = .deviceSandbox
self.proofPolicy = .appleDevelopment
case .simulatorSandbox:
self.transport = .relay
self.distribution = .official
self.relayBaseURL = relayBaseURLOverride
?? URL(string: "https://\(Self.openClawSandboxRelayHost)")!
self.apnsEnvironment = .sandbox
self.relayProfile = .simulatorSandbox
self.proofPolicy = .internalSimulator
}
}
private static func readURL(readValue: (String) -> Any?, key: String) -> URL? {
guard let raw = readValue(key) as? String else { return nil }
private static func readURL(bundle: Bundle, key: String) -> URL? {
guard let raw = bundle.object(forInfoDictionaryKey: key) as? String else { return nil }
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return nil }
guard let components = URLComponents(string: trimmed),
@@ -138,12 +96,29 @@ struct PushBuildConfig {
}
private static func readEnum<T: RawRepresentable>(
readValue: (String) -> Any?,
bundle: Bundle,
key: String,
fallback: T)
-> T where T.RawValue == String {
guard let raw = readValue(key) as? String else { return fallback }
guard let raw = bundle.object(forInfoDictionaryKey: key) as? String else { return fallback }
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
return T(rawValue: trimmed) ?? T(rawValue: trimmed.lowercased()) ?? fallback
}
private static let defaultAPNsEnvironment: PushAPNsEnvironment = .sandbox
private static func defaultRelayProfile(apnsEnvironment: PushAPNsEnvironment) -> PushRelayProfile {
apnsEnvironment == .production ? .production : .deviceSandbox
}
private static func defaultProofPolicy(relayProfile: PushRelayProfile) -> PushProofPolicy {
switch relayProfile {
case .production:
.appleStrict
case .deviceSandbox:
.appleDevelopment
case .simulatorSandbox:
.internalSimulator
}
}
}

View File

@@ -1,19 +0,0 @@
import Foundation
enum PushEnrollmentConsent {
static let disclosureAcceptedKey = "push.enrollment.disclosureAccepted"
static var disclosureAccepted: Bool {
UserDefaults.standard.bool(forKey: disclosureAcceptedKey)
}
static func markDisclosureAccepted() {
UserDefaults.standard.set(true, forKey: self.disclosureAcceptedKey)
}
#if DEBUG
static func reset() {
UserDefaults.standard.removeObject(forKey: self.disclosureAcceptedKey)
}
#endif
}

View File

@@ -69,16 +69,12 @@ actor PushRegistrationManager {
async throws -> String {
guard self.buildConfig.distribution == .official else {
throw PushRelayError.relayMisconfigured(
"Relay transport requires an official push build mode")
"Relay transport requires OpenClawPushDistribution=official")
}
try Self.validateRelayContract(
relayProfile: self.buildConfig.relayProfile,
apnsEnvironment: self.buildConfig.apnsEnvironment,
proofPolicy: self.buildConfig.proofPolicy)
GatewayDiagnostics.pushRelay.stage(
"contract validated apns=\(self.buildConfig.apnsEnvironment.rawValue) "
+ "profile=\(self.buildConfig.relayProfile.rawValue) "
+ "proof=\(self.buildConfig.proofPolicy.rawValue)")
guard let relayClient = self.relayClient else {
throw PushRelayError.relayBaseURLMissing
}
@@ -106,7 +102,6 @@ actor PushRegistrationManager {
stored.lastAPNsTokenHashHex == tokenHashHex,
!Self.isExpired(stored.relayHandleExpiresAtMs)
{
GatewayDiagnostics.pushRelay.stage("using cached relay registration")
return try Self.encodePayload(
RelayGatewayPushRegistrationPayload(
relayHandle: stored.relayHandle,
@@ -120,7 +115,6 @@ actor PushRegistrationManager {
tokenDebugSuffix: stored.tokenDebugSuffix))
}
GatewayDiagnostics.pushRelay.stage("relay registration cache miss")
let response = try await relayClient.register(PushRelayRegistrationInput(
installationId: installationId,
bundleId: bundleId,
@@ -145,7 +139,6 @@ actor PushRegistrationManager {
relayProfile: self.buildConfig.relayProfile.rawValue,
proofPolicy: self.buildConfig.proofPolicy.rawValue)
_ = PushRelayRegistrationStore.saveRegistrationState(registrationState)
GatewayDiagnostics.pushRelay.stage("stored relay registration hasExpiry=\(response.expiresAtMs != nil)")
return try Self.encodePayload(
RelayGatewayPushRegistrationPayload(
relayHandle: response.relayHandle,

View File

@@ -276,20 +276,7 @@ final class PushRelayClient: @unchecked Sendable {
}
func register(_ input: PushRelayRegistrationInput) async throws -> PushRelayRegisterResponse {
GatewayDiagnostics.pushRelay.stage(
"registration start origin=\(self.normalizedBaseURLString) "
+ "apns=\(input.environment.rawValue) "
+ "profile=\(input.relayProfile.rawValue) "
+ "proof=\(input.proofPolicy.rawValue)")
let challenge: PushRelayChallengeResponse
do {
GatewayDiagnostics.pushRelay.stage("challenge request start")
challenge = try await self.fetchChallenge()
GatewayDiagnostics.pushRelay.stage("challenge received")
} catch {
GatewayDiagnostics.pushRelay.failed("challenge request", error: error)
throw error
}
let challenge = try await self.fetchChallenge()
let signedPayload = PushRelayRegisterSignedPayload(
challengeId: challenge.challengeId,
installationId: input.installationId,
@@ -308,38 +295,15 @@ final class PushRelayClient: @unchecked Sendable {
apnsEnvironment: input.environment.rawValue,
relayProfile: input.relayProfile.rawValue,
proofPolicy: input.proofPolicy.rawValue)
let appAttest: PushRelayAppAttestProof?
do {
GatewayDiagnostics.pushRelay.stage("app attest proof start")
appAttest = try await self.createAppAttestProofIfNeeded(
proofPolicy: input.proofPolicy,
challenge: challenge.challenge,
signedPayloadData: signedPayloadData,
scope: appAttestScope)
GatewayDiagnostics.pushRelay.stage("app attest proof complete included=\(appAttest != nil)")
} catch {
GatewayDiagnostics.pushRelay.failed("app attest proof", error: error)
throw error
}
let receipt: PushRelayReceiptPayload?
do {
GatewayDiagnostics.pushRelay.stage("receipt proof start")
receipt = try await self.createReceiptIfNeeded(proofPolicy: input.proofPolicy)
GatewayDiagnostics.pushRelay.stage("receipt proof complete included=\(receipt != nil)")
} catch {
GatewayDiagnostics.pushRelay.failed("receipt proof", error: error)
throw error
}
let simulatorProof: PushRelaySimulatorProofPayload?
do {
simulatorProof = try self.createSimulatorProofIfNeeded(
proofPolicy: input.proofPolicy,
signedPayloadData: signedPayloadData)
GatewayDiagnostics.pushRelay.stage("simulator proof complete included=\(simulatorProof != nil)")
} catch {
GatewayDiagnostics.pushRelay.failed("simulator proof", error: error)
throw error
}
let appAttest = try await self.createAppAttestProofIfNeeded(
proofPolicy: input.proofPolicy,
challenge: challenge.challenge,
signedPayloadData: signedPayloadData,
scope: appAttestScope)
let receipt = try await self.createReceiptIfNeeded(proofPolicy: input.proofPolicy)
let simulatorProof = try self.createSimulatorProofIfNeeded(
proofPolicy: input.proofPolicy,
signedPayloadData: signedPayloadData)
let requestBody = PushRelayRegisterRequest(
challengeId: signedPayload.challengeId,
installationId: signedPayload.installationId,
@@ -370,17 +334,8 @@ final class PushRelayClient: @unchecked Sendable {
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try self.jsonEncoder.encode(requestBody)
let data: Data
let response: URLResponse
do {
GatewayDiagnostics.pushRelay.stage("register request start")
(data, response) = try await self.session.data(for: request)
} catch {
GatewayDiagnostics.pushRelay.failed("register request", error: error)
throw error
}
let (data, response) = try await self.session.data(for: request)
let status = Self.statusCode(from: response)
GatewayDiagnostics.pushRelay.stage("register response status=\(status)")
guard (200..<300).contains(status) else {
if status == 401 {
// If the relay rejects registration, drop local App Attest state so the next
@@ -388,20 +343,11 @@ final class PushRelayClient: @unchecked Sendable {
_ = PushRelayRegistrationStore.clearAppAttestKeyID(scope: appAttestScope)
_ = PushRelayRegistrationStore.clearAttestedKeyID(scope: appAttestScope)
}
let relayError = PushRelayError.requestFailed(
throw PushRelayError.requestFailed(
status: status,
message: Self.decodeErrorMessage(data: data))
GatewayDiagnostics.pushRelay.stage("register response failed status=\(status)")
throw relayError
}
do {
let decoded = try self.decode(PushRelayRegisterResponse.self, from: data)
GatewayDiagnostics.pushRelay.stage("registration response decoded")
return decoded
} catch {
GatewayDiagnostics.pushRelay.failed("registration response decode", error: error)
throw error
}
return try self.decode(PushRelayRegisterResponse.self, from: data)
}
private func createAppAttestProofIfNeeded(

View File

@@ -76,7 +76,6 @@ Sources/Permissions/PermissionRequestBridge.swift
Sources/Push/ExecApprovalNotificationBridge.swift
Sources/Push/BackgroundAliveBeacon.swift
Sources/Push/PushBuildConfig.swift
Sources/Push/PushEnrollmentConsent.swift
Sources/Push/PushRegistrationManager.swift
Sources/Push/PushRelayClient.swift
Sources/Push/PushRelayKeychainStore.swift

View File

@@ -1377,24 +1377,6 @@ private final class MockBootstrapNotificationCenter: NotificationCentering, @unc
#expect(center.addCalls == 1)
}
@Test @MainActor func apnsRegistrationRequiresDisclosureAndNotificationAuthorization() async {
let center = MockBootstrapNotificationCenter()
center.status = .authorized
let appModel = NodeAppModel(notificationCenter: center)
PushEnrollmentConsent.reset()
defer { PushEnrollmentConsent.reset() }
#expect(await appModel._test_canPublishAPNsRegistration() == false)
#expect(await appModel._test_canPublishAPNsRegistration(usesRelayTransport: false) == false)
PushEnrollmentConsent.markDisclosureAccepted()
center.status = .notDetermined
#expect(await appModel._test_canPublishAPNsRegistration() == false)
center.status = .authorized
#expect(await appModel._test_canPublishAPNsRegistration())
}
@Test @MainActor func chatPushWithoutSpeechReturnsUnavailableWhenNotificationsOff() async throws {
let center = MockBootstrapNotificationCenter()
center.status = .notDetermined

View File

@@ -1,50 +0,0 @@
import Foundation
import Testing
@testable import OpenClaw
struct PushBuildConfigTests {
@Test func `app store mode derives production relay contract`() {
let config = PushBuildConfig(infoDictionary: [
"OpenClawPushMode": "appStore",
"OpenClawPushRelayBaseURL": "https://wrong.example.com",
])
#expect(config.mode == .appStore)
#expect(config.transport == .relay)
#expect(config.distribution == .official)
#expect(config.relayBaseURL?.absoluteString == "https://ios-push-relay.openclaw.ai")
#expect(config.apnsEnvironment == .production)
#expect(config.relayProfile == .production)
#expect(config.proofPolicy == .appleStrict)
}
@Test func `simulator sandbox mode derives internal proof contract`() {
let config = PushBuildConfig(infoDictionary: [
"OpenClawPushMode": "simulatorSandbox",
"OpenClawPushRelayBaseURL": "https://staging-relay.example.com",
])
#expect(config.mode == .simulatorSandbox)
#expect(config.transport == .relay)
#expect(config.distribution == .official)
#expect(config.relayBaseURL?.absoluteString == "https://staging-relay.example.com")
#expect(config.apnsEnvironment == .sandbox)
#expect(config.relayProfile == .simulatorSandbox)
#expect(config.proofPolicy == .internalSimulator)
}
@Test func `local release mode remains direct production push`() {
let config = PushBuildConfig(infoDictionary: [
"OpenClawPushMode": "localProduction",
"OpenClawPushRelayBaseURL": "https://ios-push-relay.openclaw.ai",
])
#expect(config.mode == .localProduction)
#expect(config.transport == .direct)
#expect(config.distribution == .local)
#expect(config.relayBaseURL == nil)
#expect(config.apnsEnvironment == .production)
#expect(config.relayProfile == .production)
#expect(config.proofPolicy == .appleStrict)
}
}

View File

@@ -550,20 +550,6 @@ struct RootTabsSourceGuardTests {
#expect(docsSource.contains(".accessibilityHint(\"Opens Settings / Gateway\")"))
}
@Test func `push enrollment stays behind notification disclosure flow`() throws {
let appSource = try String(contentsOf: Self.openClawAppSourceURL(), encoding: .utf8)
let actionsSource = try String(contentsOf: Self.settingsProTabActionsSourceURL(), encoding: .utf8)
let modelSource = try String(contentsOf: Self.nodeAppModelSourceURL(), encoding: .utf8)
#expect(appSource.contains("PushEnrollmentConsent.disclosureAccepted"))
#expect(appSource.contains("await Self.isNotificationAuthorizationAllowed()"))
#expect(actionsSource.contains("PushEnrollmentConsent.markDisclosureAccepted()"))
#expect(actionsSource.contains("self.registerForRemoteNotificationsIfEnrollmentReady()"))
#expect(modelSource.contains("PushEnrollmentConsent.disclosureAccepted"))
#expect(modelSource.contains("notifications_not_authorized"))
#expect(modelSource.contains("enrollment_disclosure_not_accepted"))
}
@Test func `gateway settings keeps pairing trust diagnostics and tailscale actions`() throws {
let settingsSource = try String(contentsOf: Self.settingsProTabSourceURL(), encoding: .utf8)
let sectionsSource = try String(contentsOf: Self.settingsProTabSectionsSourceURL(), encoding: .utf8)
@@ -800,13 +786,6 @@ struct RootTabsSourceGuardTests {
.appendingPathComponent("Sources/Design/SettingsProTab.swift")
}
private static func openClawAppSourceURL() -> URL {
URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.deletingLastPathComponent()
.appendingPathComponent("Sources/OpenClawApp.swift")
}
private static func notificationPermissionGuidanceDialogSourceURL() -> URL {
URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()

View File

@@ -15,12 +15,13 @@
import Foundation
import XCTest
@MainActor
var deviceLanguage = ""
var locale = ""
func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) {
Snapshot.setupSnapshot(app, waitForAnimations: waitForAnimations)
}
@MainActor
func snapshot(_ name: String, waitForLoadingIndicator: Bool) {
if waitForLoadingIndicator {
Snapshot.snapshot(name)
@@ -32,7 +33,6 @@ func snapshot(_ name: String, waitForLoadingIndicator: Bool) {
/// - Parameters:
/// - name: The name of the snapshot
/// - timeout: Amount of seconds to wait until the network loading indicator disappears. Pass `0` if you don't want to wait.
@MainActor
func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) {
Snapshot.snapshot(name, timeWaitingForIdle: timeout)
}
@@ -52,7 +52,6 @@ enum SnapshotError: Error, CustomDebugStringConvertible {
}
@objcMembers
@MainActor
open class Snapshot: NSObject {
static var app: XCUIApplication?
static var waitForAnimations = true
@@ -60,8 +59,6 @@ open class Snapshot: NSObject {
static var screenshotsDirectory: URL? {
return cacheDirectory?.appendingPathComponent("screenshots", isDirectory: true)
}
static var deviceLanguage = ""
static var currentLocale = ""
open class func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) {
@@ -106,17 +103,17 @@ open class Snapshot: NSObject {
do {
let trimCharacterSet = CharacterSet.whitespacesAndNewlines
currentLocale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)
locale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)
} catch {
NSLog("Couldn't detect/set locale...")
}
if currentLocale.isEmpty && !deviceLanguage.isEmpty {
currentLocale = Locale(identifier: deviceLanguage).identifier
if locale.isEmpty && !deviceLanguage.isEmpty {
locale = Locale(identifier: deviceLanguage).identifier
}
if !currentLocale.isEmpty {
app.launchArguments += ["-AppleLocale", "\"\(currentLocale)\""]
if !locale.isEmpty {
app.launchArguments += ["-AppleLocale", "\"\(locale)\""]
}
}
@@ -168,7 +165,7 @@ open class Snapshot: NSObject {
}
let screenshot = XCUIScreen.main.screenshot()
#if os(iOS) && !targetEnvironment(macCatalyst)
#if os(iOS)
let image = XCUIDevice.shared.orientation.isLandscape ? fixLandscapeOrientation(image: screenshot.image) : screenshot.image
#else
let image = screenshot.image
@@ -184,7 +181,7 @@ open class Snapshot: NSObject {
let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png")
#if swift(<5.0)
try UIImagePNGRepresentation(image)?.write(to: path, options: .atomic)
UIImagePNGRepresentation(image)?.write(to: path, options: .atomic)
#else
try image.pngData()?.write(to: path, options: .atomic)
#endif
@@ -284,7 +281,6 @@ private extension XCUIElementQuery {
return self.containing(isNetworkLoadingIndicator)
}
@MainActor
var deviceStatusBars: XCUIElementQuery {
guard let app = Snapshot.app else {
fatalError("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().")
@@ -310,4 +306,4 @@ private extension CGFloat {
// Please don't remove the lines below
// They are used to detect outdated configuration files
// SnapshotHelperVersion [1.30]
// SnapshotHelperVersion [1.27]

View File

@@ -10,24 +10,7 @@ default_platform(:ios)
APP_STORE_APP_IDENTIFIER = "ai.openclawfoundation.app"
DEFAULT_APP_STORE_CONNECT_KEYCHAIN_SERVICE = "openclaw-app-store-connect-key"
DEFAULT_SNAPSHOT_DEVICE_FAMILIES = [
{
label: "iPhone",
patterns: [
/\AiPhone .* Pro Max\z/,
/\AiPhone .* Plus\z/,
/\AiPhone .*\z/
]
},
{
label: "13-inch iPad",
patterns: [
/\AiPad Pro 13-inch/,
/\AiPad Air 13-inch/,
/\AiPad .*13-inch/
]
}
].freeze
DEFAULT_SNAPSHOT_DEVICES = ["iPhone 16 Pro Max", "iPad Pro 13-inch (M4)"].freeze
DEFAULT_WATCH_SNAPSHOT_DEVICE = "Apple Watch Ultra 3 (49mm)"
WATCH_SCREENSHOT_MODE_DEFAULTS_KEY = "openclaw.watch.screenshotMode"
WATCH_SNAPSHOT_STATUS_BAR_TIME = "09:41"
@@ -94,23 +77,11 @@ end
def snapshot_devices
raw = ENV["OPENCLAW_SNAPSHOT_DEVICES"].to_s.strip
return default_snapshot_devices if raw.empty?
return DEFAULT_SNAPSHOT_DEVICES if raw.empty?
raw.split(",").map(&:strip).reject(&:empty?)
end
def default_snapshot_devices
names = available_simulator_devices.map { |device| device["name"].to_s }.reject(&:empty?).uniq
DEFAULT_SNAPSHOT_DEVICE_FAMILIES.map do |family|
match = family.fetch(:patterns).filter_map do |pattern|
names.find { |name| name.match?(pattern) }
end.first
UI.user_error!("No available #{family.fetch(:label)} simulator found for App Store screenshots.") if match.nil?
match
end
end
def watch_snapshot_device
raw = ENV["OPENCLAW_WATCH_SNAPSHOT_DEVICE"].to_s.strip
raw.empty? ? DEFAULT_WATCH_SNAPSHOT_DEVICE : raw
@@ -142,51 +113,6 @@ def resolve_simulator_device(name)
fallback
end
def install_ready_for_review_edit_state_lookup!
require "spaceship"
app_class = Spaceship::ConnectAPI::App
app_class.class_eval do
unless method_defined?(:openclaw_get_edit_app_store_version_without_ready_for_review)
alias_method :openclaw_get_edit_app_store_version_without_ready_for_review, :get_edit_app_store_version
end
unless method_defined?(:openclaw_fetch_edit_app_info_without_ready_for_review)
alias_method :openclaw_fetch_edit_app_info_without_ready_for_review, :fetch_edit_app_info
end
def get_edit_app_store_version(client: nil, platform: nil, includes: Spaceship::ConnectAPI::AppStoreVersion::ESSENTIAL_INCLUDES)
version = openclaw_get_edit_app_store_version_without_ready_for_review(client: client, platform: platform, includes: includes)
return version if version
# First public releases can leave the only version in READY_FOR_REVIEW.
# Fastlane 2.236.1 excludes that state and then tries to create an illegal
# second version; use the existing review-ready version as the edit target.
client ||= Spaceship::ConnectAPI
platform ||= Spaceship::ConnectAPI::Platform::IOS
filter = {
appVersionState: Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::READY_FOR_REVIEW,
platform: platform
}
get_app_store_versions(client: client, filter: filter, includes: includes)
.sort_by { |candidate| Gem::Version.new(candidate.version_string) }
.last
end
def fetch_edit_app_info(client: nil, includes: Spaceship::ConnectAPI::AppInfo::ESSENTIAL_INCLUDES)
app_info = openclaw_fetch_edit_app_info_without_ready_for_review(client: client, includes: includes)
return app_info if app_info
client ||= Spaceship::ConnectAPI
client
.get_app_infos(app_id: id, includes: includes)
.to_models
.find { |candidate| candidate.state == Spaceship::ConnectAPI::AppInfo::State::READY_FOR_REVIEW }
end
end
end
def bundle_identifier_for_product(product_path)
info_plist_path = File.join(product_path, "Info.plist")
UI.user_error!("Expected Info.plist at #{info_plist_path}.") unless File.exist?(info_plist_path)
@@ -284,7 +210,6 @@ def normalize_watch_screenshot_status_bar(path)
script = <<~SWIFT
import AppKit
import Foundation
import ImageIO
let path = CommandLine.arguments[1]
let timeText = CommandLine.arguments[2]
@@ -296,37 +221,36 @@ def normalize_watch_screenshot_status_bar(path)
exit(2)
}
let width = cgImage.width
let height = cgImage.height
let drawWidth = CGFloat(width)
let drawHeight = CGFloat(height)
let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) ?? CGColorSpaceCreateDeviceRGB()
guard let bitmapContext = CGContext(
data: nil,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: width * 4,
space: colorSpace,
bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue)
let width = CGFloat(cgImage.width)
let height = CGFloat(cgImage.height)
guard let bitmap = NSBitmapImageRep(
bitmapDataPlanes: nil,
pixelsWide: Int(width),
pixelsHigh: Int(height),
bitsPerSample: 8,
samplesPerPixel: 4,
hasAlpha: true,
isPlanar: false,
colorSpaceName: .deviceRGB,
bytesPerRow: 0,
bitsPerPixel: 0),
let graphicsContext = NSGraphicsContext(bitmapImageRep: bitmap)
else {
fputs("Failed to create normalized screenshot bitmap at \\(path)\\n", stderr)
exit(3)
}
let graphicsContext = NSGraphicsContext(cgContext: bitmapContext, flipped: false)
bitmap.size = NSSize(width: width, height: height)
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = graphicsContext
NSColor.black.setFill()
NSBezierPath(rect: NSRect(x: 0, y: 0, width: drawWidth, height: drawHeight)).fill()
source.draw(
in: NSRect(x: 0, y: 0, width: drawWidth, height: drawHeight),
from: NSRect(x: 0, y: 0, width: drawWidth, height: drawHeight),
operation: .sourceOver,
in: NSRect(x: 0, y: 0, width: width, height: height),
from: NSRect(x: 0, y: 0, width: width, height: height),
operation: .copy,
fraction: 1.0)
NSColor.black.setFill()
NSBezierPath(rect: NSRect(x: drawWidth - 146, y: drawHeight - 92, width: 124, height: 70)).fill()
NSBezierPath(rect: NSRect(x: width - 146, y: height - 92, width: 124, height: 70)).fill()
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .right
@@ -336,26 +260,17 @@ def normalize_watch_screenshot_status_bar(path)
.paragraphStyle: paragraphStyle,
]
timeText.draw(
in: NSRect(x: drawWidth - 134, y: drawHeight - 82, width: 102, height: 44),
in: NSRect(x: width - 134, y: height - 82, width: 102, height: 44),
withAttributes: attributes)
NSGraphicsContext.restoreGraphicsState()
guard let output = bitmapContext.makeImage(),
let destination = CGImageDestinationCreateWithURL(
URL(fileURLWithPath: path) as CFURL,
"public.png" as CFString,
1,
nil)
guard let png = bitmap.representation(using: .png, properties: [:])
else {
fputs("Failed to encode normalized screenshot at \\(path)\\n", stderr)
exit(4)
}
CGImageDestinationAddImage(destination, output, nil)
guard CGImageDestinationFinalize(destination) else {
fputs("Failed to write normalized screenshot at \\(path)\\n", stderr)
exit(5)
}
try png.write(to: URL(fileURLWithPath: path))
SWIFT
Tempfile.create(["openclaw-watch-status-bar", ".swift"]) do |file|
@@ -839,11 +754,6 @@ def prepare_app_store_release!(version:, build_number:)
release_xcconfig
end
def validate_app_store_ipa!(ipa_path)
script_path = File.join(repo_root, "scripts", "ios-validate-app-store-ipa.sh")
sh(shell_join(["bash", script_path, "--ipa", ipa_path]))
end
def build_app_store_release(context)
version = context[:version]
project_path = File.join(ios_root, "OpenClaw.xcodeproj")
@@ -894,7 +804,6 @@ def build_app_store_release(context)
UI.user_error!("xcodebuild export produced multiple IPAs in #{output_directory}: #{exported_ipas.join(", ")}") if exported_ipas.length > 1
exported_ipa = exported_ipas.first
FileUtils.mv(exported_ipa, expected_ipa_path) unless exported_ipa == expected_ipa_path
validate_app_store_ipa!(expected_ipa_path)
{
archive_path: archive_path,
@@ -1014,12 +923,25 @@ platform :ios do
ENV.delete("XCODE_XCCONFIG_FILE")
end
desc "Build + upload an App Store distribution build to App Store Connect"
lane :app_store do
context = prepare_app_store_context(require_api_key: true)
build = build_app_store_release(context)
upload_to_testflight(
api_key: context[:api_key],
ipa: build[:ipa_path],
skip_waiting_for_build_processing: true,
uses_non_exempt_encryption: false
)
UI.success("Uploaded iOS App Store build: version=#{build[:version]} short=#{build[:short_version]} build=#{build[:build_number]}")
ensure
ENV.delete("XCODE_XCCONFIG_FILE")
end
desc "Generate screenshots, update App Store version metadata, then upload an App Store build"
lane :release_upload do
unless ENV["OPENCLAW_IOS_RELEASE_WRAPPER"] == "1"
UI.user_error!("Use `pnpm ios:release:upload`; direct Fastlane TestFlight upload is disabled.")
end
release_signing_check!
preserve_local_signing do
screenshots
@@ -1046,7 +968,6 @@ platform :ios do
desc "Upload App Store metadata (and optionally screenshots)"
lane :metadata do
install_ready_for_review_edit_state_lookup!
sync_ios_versioning!
version_metadata = read_ios_version_metadata
api_key = app_store_connect_api_key_config

View File

@@ -104,7 +104,7 @@ Generate deterministic App Store screenshots:
pnpm ios:screenshots
```
The screenshot lane runs the app with `--openclaw-screenshot-mode`, which enters the built-in connected screenshot fixture instead of pairing with a live gateway. By default it chooses one available large iPhone simulator and one available 13-inch iPad simulator from the installed Xcode runtime; override devices with a comma-separated `OPENCLAW_SNAPSHOT_DEVICES` value when the requested simulators exist locally.
The screenshot lane runs the app with `--openclaw-screenshot-mode`, which enters the built-in connected screenshot fixture instead of pairing with a live gateway. By default it captures the tab set on `iPhone 16 Pro Max` and `iPad Pro 13-inch (M4)`; override devices with a comma-separated `OPENCLAW_SNAPSHOT_DEVICES` value when the requested simulators exist locally.
Upload to App Store Connect:
@@ -112,9 +112,12 @@ Upload to App Store Connect:
pnpm ios:release:upload
```
Direct Fastlane TestFlight upload is disabled. Use the package script so the
release wrapper, App Store push mode, and exported-IPA validation gate all run
in the same path.
Direct Fastlane entry point:
```bash
cd apps/ios
fastlane ios release_upload
```
Maintainer recovery path for a fresh clone on the same Mac:
@@ -141,7 +144,13 @@ fastlane ios auth_check
pnpm ios:version:pin -- --from-gateway
```
5. Upload:
5. Set the official relay URL before release:
```bash
export OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com
```
6. Upload:
```bash
pnpm ios:release:upload
@@ -150,7 +159,6 @@ pnpm ios:release:upload
Quick verification after upload:
- confirm `apps/ios/build/app-store/OpenClaw-<version>.ipa` exists
- confirm Fastlane validates the exported IPA before upload
- confirm Fastlane prints `Uploaded iOS App Store build: version=<version> short=<short> build=<build>`
- remember that App Store Connect/TestFlight processing can take a few minutes after the upload succeeds
@@ -167,7 +175,5 @@ Versioning rules:
- `pnpm ios:version:check` validates that checked-in iOS version artifacts are in sync
- The release flow regenerates `apps/ios/OpenClaw.xcodeproj` from `apps/ios/project.yml` before archiving
- Local App Store signing uses a temporary generated xcconfig with profile names from `apps/ios/Config/AppStoreSigning.json` and leaves local development signing overrides untouched
- App Store release uses `OpenClawPushMode=appStore`, which derives the canonical production hosted relay, production APNs, production relay profile, and `appleStrict` proof. The release lane rejects custom production relay URL overrides.
- The exported IPA is validated before upload by inspecting its push mode, signed entitlements, and embedded App Store profile.
- `pnpm ios:release:upload` generates and uploads screenshots and release notes before archiving, then uploads the IPA without submitting it for App Review
- See `apps/ios/VERSIONING.md` for the detailed workflow

View File

@@ -2,9 +2,10 @@ project("OpenClaw.xcodeproj")
scheme("OpenClawUITests")
configuration("Debug")
# The Fastfile screenshot lane resolves concrete device names from the installed
# Xcode simulators. Fastlane validates Snapfile devices before lane overrides, so
# this file intentionally does not hardcode simulator model names.
devices([
"iPhone 16 Pro Max",
"iPad Pro 13-inch (M4)",
])
languages([
"en-US",

View File

@@ -122,13 +122,21 @@ targets:
Debug:
OPENCLAW_CODE_SIGN_ENTITLEMENTS: Sources/OpenClaw.entitlements
OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT: development
OPENCLAW_PUSH_MODE: localSandbox
OPENCLAW_PUSH_TRANSPORT: direct
OPENCLAW_PUSH_DISTRIBUTION: local
OPENCLAW_PUSH_RELAY_BASE_URL: ""
OPENCLAW_PUSH_APNS_ENVIRONMENT: sandbox
OPENCLAW_PUSH_RELAY_PROFILE: deviceSandbox
OPENCLAW_PUSH_PROOF_POLICY: appleDevelopment
Release:
OPENCLAW_CODE_SIGN_ENTITLEMENTS: Sources/OpenClaw.entitlements
OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT: production
OPENCLAW_PUSH_MODE: localProduction
OPENCLAW_PUSH_TRANSPORT: direct
OPENCLAW_PUSH_DISTRIBUTION: local
OPENCLAW_PUSH_RELAY_BASE_URL: ""
OPENCLAW_PUSH_APNS_ENVIRONMENT: production
OPENCLAW_PUSH_RELAY_PROFILE: production
OPENCLAW_PUSH_PROOF_POLICY: appleStrict
info:
path: Sources/Info.plist
properties:
@@ -170,8 +178,12 @@ targets:
NSSpeechRecognitionUsageDescription: OpenClaw uses on-device speech recognition for talk mode and voice wake.
NSSupportsLiveActivities: true
ITSAppUsesNonExemptEncryption: false
OpenClawPushMode: "$(OPENCLAW_PUSH_MODE)"
OpenClawPushTransport: "$(OPENCLAW_PUSH_TRANSPORT)"
OpenClawPushDistribution: "$(OPENCLAW_PUSH_DISTRIBUTION)"
OpenClawPushRelayBaseURL: "$(OPENCLAW_PUSH_RELAY_BASE_URL)"
OpenClawPushAPNsEnvironment: "$(OPENCLAW_PUSH_APNS_ENVIRONMENT)"
OpenClawPushRelayProfile: "$(OPENCLAW_PUSH_RELAY_PROFILE)"
OpenClawPushProofPolicy: "$(OPENCLAW_PUSH_PROOF_POLICY)"
UISupportedInterfaceOrientations:
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown

View File

@@ -1,3 +1,4 @@
// Bundled A2UI runtime resource embedded by OpenClawKit.
var __defProp$1 = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
let target = {};
@@ -11935,10 +11936,6 @@ var __runInitializers = function(thisArg, initializers, value) {
};
return _classThis;
})();
/**
* Canvas A2UI browser bootstrap that installs theme overrides and native bridge
* helpers.
*/
const modalStyles = i$10`
dialog {
position: fixed;

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

View File

@@ -1,2 +1,2 @@
da3373338b7f9c5f5639ad8233a32897d2346a0babe69a77386a7bff154cdcb1 plugin-sdk-api-baseline.json
17404d885e0d64ebc8e3c99443921058a8f1aebf76a5e612eb1f0cd7817d48f0 plugin-sdk-api-baseline.jsonl
8d38dc64627e6bfcebc25215d499a30841953799133dea16ffce902cf301273f plugin-sdk-api-baseline.json
d7927d51588fd006d743fc56cc22d779141b487f82655d88054d2be12f3093ff plugin-sdk-api-baseline.jsonl

View File

@@ -943,14 +943,6 @@
"source": "Matrix QA",
"target": "Matrix QA"
},
{
"source": "Maturity scorecard",
"target": "成熟度评分卡"
},
{
"source": "Maturity taxonomy",
"target": "成熟度分类法"
},
{
"source": "Matrix presentation metadata",
"target": "Matrix 呈现元数据"

View File

@@ -23,8 +23,8 @@ OpenClaw agent or Gateway.
```bash
openclaw skills search "calendar"
openclaw skills install @owner/<slug>
openclaw skills update @owner/<slug>
openclaw skills install <slug>
openclaw skills update <slug>
openclaw skills verify <slug>
openclaw plugins search "calendar"

View File

@@ -24,13 +24,13 @@ where you have publisher access.
Skills are published from a skill folder. The public page is:
```text
https://clawhub.ai/<owner>/skills/<slug>
https://clawhub.ai/<owner>/<slug>
```
Example:
```text
https://clawhub.ai/alice/skills/review-helper
https://clawhub.ai/alice/review-helper
```
The publish request includes the selected owner, slug, version, changelog, and

View File

@@ -212,7 +212,6 @@ Notes:
- `--agent` or `--workspace` can be used to select the target agent.
- If you rely on `--workspace` and multiple agents share that workspace, the command fails and asks you to pass `--agent`.
- Local workspace-relative avatar image files are limited to 2 MB. HTTP(S) URLs and `data:` URIs are not checked with the local file-size limit.
- When no explicit identity fields are provided, the command reads identity data from `IDENTITY.md`.
Load from `IDENTITY.md`:

View File

@@ -305,16 +305,6 @@ does not import plugin runtime code, run a package manager, or repair missing
dependencies.
</Note>
If startup logs `plugins.allow is empty; discovered non-bundled plugins may auto-load: ...`,
run `openclaw plugins list --enabled --verbose` or
`openclaw plugins inspect <id>` with a listed plugin id to confirm the plugin
ids and copy trusted ids into `plugins.allow` in `openclaw.json`. When the
warning can list every discovered plugin, it prints a ready-to-paste
`plugins.allow` snippet that already includes those ids. If a plugin loads
without install/load-path provenance, inspect that plugin id, then either pin
the trusted id in `plugins.allow` or reinstall the plugin from a trusted source
so OpenClaw records install provenance.
`plugins search` is a remote ClawHub catalog lookup. It does not inspect local
state, mutate config, install packages, or load plugin runtime code. Search
results include the ClawHub package name, family, channel, version, summary, and

View File

@@ -25,16 +25,16 @@ Related:
```bash
openclaw skills search "calendar"
openclaw skills search --limit 20 --json
openclaw skills install @owner/<slug>
openclaw skills install @owner/<slug> --version <version>
openclaw skills install <slug>
openclaw skills install <slug> --version <version>
openclaw skills install git:owner/repo
openclaw skills install git:owner/repo@main
openclaw skills install ./path/to/skill --as custom-name
openclaw skills install @owner/<slug> --force
openclaw skills install @owner/<slug> --agent <id>
openclaw skills install @owner/<slug> --global
openclaw skills update @owner/<slug>
openclaw skills update @owner/<slug> --global
openclaw skills install <slug> --force
openclaw skills install <slug> --agent <id>
openclaw skills install <slug> --global
openclaw skills update <slug>
openclaw skills update <slug> --global
openclaw skills update --all
openclaw skills update --all --agent <id>
openclaw skills update --all --global
@@ -64,8 +64,8 @@ openclaw skills workshop reject <proposal-id> --reason "Not reusable"
openclaw skills workshop quarantine <proposal-id> --reason "Needs security review"
```
`search`, `update`, and `verify` use ClawHub directly. `install @owner/<slug>`
installs a ClawHub skill, `install git:owner/repo[@ref]` clones a Git skill, and
`search`, `update`, and `verify` use ClawHub directly. `install <slug>` installs
a ClawHub skill, `install git:owner/repo[@ref]` clones a Git skill, and
`install ./path` copies a local skill directory. By default, `install`, `update`,
and `verify` target the active workspace `skills/` directory; with `--global`,
they target the shared managed skills directory. `list`/`info`/`check` still
@@ -94,15 +94,15 @@ Notes:
`SKILL.md`.
- `install --as <slug>` overrides the inferred slug for Git and local directory
installs.
- `install --version <version>` applies only to ClawHub skill refs.
- `install --version <version>` applies only to ClawHub skill slugs.
- `install --force` overwrites an existing workspace skill folder for the same
slug.
- `--global` targets the shared managed skills directory and cannot be combined
with `--agent <id>`.
- `--agent <id>` targets one configured agent workspace and overrides current
working directory inference.
- `update @owner/<slug>` updates a single tracked skill. Add `--global` to
target the shared managed skills directory instead of the workspace.
- `update <slug>` updates a single tracked skill. Add `--global` to target the
shared managed skills directory instead of the workspace.
- `update --all` updates tracked ClawHub installs in the selected workspace, or
in the shared managed skills directory when combined with `--global`.
- `verify <slug>` prints ClawHub's `clawhub.skill.verify.v1` JSON envelope by

View File

@@ -31,7 +31,7 @@ script aliases; both forms are supported.
| Command | Purpose |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `qa run` | Bundled QA self-check without `--qa-profile`; taxonomy-backed maturity profile runner with `--qa-profile smoke-ci`, `--qa-profile release`, or `--qa-profile all`. |
| `qa run` | Bundled QA self-check without `--qa-profile`; taxonomy-backed maturity profile runner with `--qa-profile smoke-ci` or `--qa-profile release`. |
| `qa suite` | Run repo-backed scenarios against the QA gateway lane. Aliases: `pnpm openclaw qa suite --runner multipass` for a disposable Linux VM. |
| `qa coverage` | Print the YAML scenario-coverage inventory (`--json` for machine output). |
| `qa parity-report` | Compare two `qa-suite-summary.json` files and write the agentic parity report, or use `--runtime-axis --token-efficiency` to write Codex-vs-OpenClaw runtime parity and token-efficiency reports from one runtime-pair summary. |
@@ -75,10 +75,8 @@ pnpm openclaw qa run \
Use `smoke-ci` for deterministic profile proof with mock model providers and
Crabline fake provider servers. Use `release` for Stable/LTS proof against live
channels. Use `all` only for explicit full-taxonomy evidence runs; it selects
every active maturity category and can be dispatched through the `QA Profile
Evidence` workflow with `qa_profile=all`. When a command also needs an OpenClaw
root profile, put the root profile before the QA command:
channels. When a command also needs an OpenClaw root profile, put the root
profile before the QA command:
```bash
pnpm openclaw --profile work qa run --qa-profile smoke-ci

View File

@@ -1103,7 +1103,6 @@ for provider examples and precedence.
- `models`: optional per-agent model catalog/runtime overrides keyed by full `provider/model` ids. Use `models["provider/model"].agentRuntime` for per-agent runtime exceptions.
- `runtime`: optional per-agent runtime descriptor. Use `type: "acp"` with `runtime.acp` defaults (`agent`, `backend`, `mode`, `cwd`) when the agent should default to ACP harness sessions.
- `identity.avatar`: workspace-relative path, `http(s)` URL, or `data:` URI.
- Local workspace-relative `identity.avatar` image files are limited to 2 MB. `http(s)` URLs and `data:` URIs are not checked with the local file-size limit.
- `identity` derives defaults: `ackReaction` from `emoji`, `mentionPatterns` from `name`/`emoji`.
- `subagents.allowAgents`: allowlist of configured agent ids for explicit `sessions_spawn.agentId` targets (`["*"]` = any configured target; default: same agent only). Include the requester id when self-targeted `agentId` calls should be allowed. Stale entries whose agent config was deleted are rejected by `sessions_spawn` and omitted from `agents_list`; run `openclaw doctor --fix` to clean them up, or add a minimal `agents.list[]` entry if that target should remain spawnable while inheriting defaults.
- Sandbox inheritance guard: if the requester session is sandboxed, `sessions_spawn` rejects targets that would run unsandboxed.

View File

@@ -602,7 +602,7 @@ See [Inferred commitments](/concepts/commitments).
- `remote.transport`: `ssh` (default) or `direct` (ws/wss). For `direct`, `remote.url` must be `wss://` for public hosts; plaintext `ws://` is accepted only for loopback, LAN, link-local, `.local`, `.ts.net`, and Tailscale CGNAT hosts.
- `remote.remotePort`: gateway port on the remote SSH host. Defaults to `18789`; use this when the local tunnel port differs from the remote gateway port.
- `gateway.remote.token` / `.password` are remote-client credential fields. They do not configure gateway auth by themselves.
- `gateway.push.apns.relay.baseUrl`: base HTTPS URL for the external APNs relay used after relay-backed iOS builds publish registrations to the gateway. Public App Store/TestFlight builds use the hosted OpenClaw relay. Custom relay URLs must match a deliberately separate iOS build/deployment path whose relay URL points at that relay.
- `gateway.push.apns.relay.baseUrl`: base HTTPS URL for the external APNs relay used by official/TestFlight iOS builds after they publish relay-backed registrations to the gateway. This URL must match the relay URL compiled into the iOS build.
- `gateway.push.apns.relay.timeoutMs`: gateway-to-relay send timeout in milliseconds. Defaults to `10000`.
- Relay-backed registrations are delegated to a specific gateway identity. The paired iOS app fetches `gateway.identity.get`, includes that identity in the relay registration, and forwards a registration-scoped send grant to the gateway. Another gateway cannot reuse that stored registration.
- `OPENCLAW_APNS_RELAY_BASE_URL` / `OPENCLAW_APNS_RELAY_TIMEOUT_MS`: temporary env overrides for the relay config above.

View File

@@ -337,9 +337,9 @@ candidate contains redacted secret placeholders such as `***`.
</Accordion>
<Accordion title="Enable relay-backed push for official iOS builds">
Relay-backed push for public App Store/TestFlight builds uses the hosted OpenClaw relay: `https://ios-push-relay.openclaw.ai`.
Relay-backed push uses the hosted OpenClaw relay by default: `https://ios-push-relay.openclaw.ai`.
Custom relay deployments require a deliberately separate iOS build/deployment path whose relay URL matches the gateway relay URL. If you are using a custom relay build, set this in gateway config:
To use a custom relay, set this in gateway config:
```json5
{
@@ -369,12 +369,12 @@ candidate contains redacted secret placeholders such as `***`.
- Uses a registration-scoped send grant forwarded by the paired iOS app. The gateway does not need a deployment-wide relay token.
- Binds each relay-backed registration to the gateway identity that the iOS app paired with, so another gateway cannot reuse the stored registration.
- Keeps local/manual iOS builds on direct APNs. Relay-backed sends apply only to official distributed builds that registered through the relay.
- Must match the relay base URL baked into the iOS build, so registration and send traffic reach the same relay deployment.
- Must match the relay base URL baked into the official/TestFlight iOS build, so registration and send traffic reach the same relay deployment.
End-to-end flow:
1. Install an official/TestFlight iOS build.
2. Optional: configure `gateway.push.apns.relay.baseUrl` on the gateway only when using a deliberately separate custom relay build.
2. Optional: configure `gateway.push.apns.relay.baseUrl` on the gateway only when using a custom relay deployment.
3. Pair the iOS app to the gateway and let both node and operator sessions connect.
4. The iOS app fetches the gateway identity, registers with the relay using App Attest plus the app receipt, and then publishes the relay-backed `push.apns.register` payload to the paired gateway.
5. The gateway stores the relay handle and send grant, then uses them for `push.test`, wake nudges, and reconnect wakes.
@@ -387,7 +387,7 @@ candidate contains redacted secret placeholders such as `***`.
Compatibility note:
- `OPENCLAW_APNS_RELAY_BASE_URL` and `OPENCLAW_APNS_RELAY_TIMEOUT_MS` still work as temporary env overrides.
- Custom gateway relay URLs must match the relay base URL baked into the iOS build. The public App Store release lane rejects custom iOS relay URL overrides.
- Custom gateway relay URLs must match the relay base URL baked into the official/TestFlight iOS build.
- `OPENCLAW_APNS_RELAY_ALLOW_HTTP=true` remains a loopback-only development escape hatch; do not persist HTTP relay URLs in config.
See [iOS App](/platforms/ios#relay-backed-push-for-official-builds) for the end-to-end flow and [Authentication and trust flow](/platforms/ios#authentication-and-trust-flow) for the relay security model.

View File

@@ -346,10 +346,10 @@ lives on the [First-run FAQ](/help/faq-first-run).
```bash
openclaw skills search "calendar"
openclaw skills search --limit 20
openclaw skills install @owner/<skill-slug>
openclaw skills install @owner/<skill-slug> --version <version>
openclaw skills install @owner/<skill-slug> --force
openclaw skills install @owner/<skill-slug> --global
openclaw skills install <skill-slug>
openclaw skills install <skill-slug> --version <version>
openclaw skills install <skill-slug> --force
openclaw skills install <skill-slug> --global
openclaw skills update --all
openclaw skills update --all --global
openclaw skills list --eligible
@@ -433,11 +433,11 @@ lives on the [First-run FAQ](/help/faq-first-run).
Install skills:
```bash
openclaw skills install @owner/<skill-slug>
openclaw skills install <skill-slug>
openclaw skills update --all
```
Native installs land in the active workspace `skills/` directory. For shared skills across all local agents, use `openclaw skills install @owner/<skill-slug> --global` (or place them manually in `~/.openclaw/skills/<name>/SKILL.md`). If only some agents should see a shared install, configure `agents.defaults.skills` or `agents.list[].skills`. Some skills expect binaries installed via Homebrew; on Linux that means Linuxbrew (see the Homebrew Linux FAQ entry above). See [Skills](/tools/skills), [Skills config](/tools/skills-config), and [ClawHub](/tools/clawhub).
Native installs land in the active workspace `skills/` directory. For shared skills across all local agents, use `openclaw skills install <slug> --global` (or place them manually in `~/.openclaw/skills/<name>/SKILL.md`). If only some agents should see a shared install, configure `agents.defaults.skills` or `agents.list[].skills`. Some skills expect binaries installed via Homebrew; on Linux that means Linuxbrew (see the Homebrew Linux FAQ entry above). See [Skills](/tools/skills), [Skills config](/tools/skills-config), and [ClawHub](/tools/clawhub).
</Accordion>

View File

@@ -190,10 +190,7 @@ inside every shard.
- When dispatched by `pnpm openclaw qa run --qa-profile <profile>`, embeds the
selected taxonomy profile scorecard in the same `qa-evidence.json`.
`smoke-ci` writes slim evidence, which sets `evidenceMode: "slim"` and omits
per-entry `execution`. `release` covers the curated release-readiness slice;
`all` selects every active maturity category and is intended for explicit QA
Profile Evidence workflow dispatches when a full scorecard artifact is
needed.
per-entry `execution`.
- Runs multiple selected scenarios in parallel by default with isolated
gateway workers. `qa-channel` defaults to concurrency 4 (bounded by the
selected scenario count). Use `--concurrency <count>` to tune the worker

5361
docs/maturity-scores.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,382 +0,0 @@
---
title: "Maturity scorecard"
summary: "OpenClaw release readiness scores for product areas, integrations, and supported workflows."
---
# Maturity scorecard
These scores summarize release readiness across OpenClaw product areas, integrations, and supported workflows.
The current scorecard covers 50 surfaces and 281 capability areas.
## Overall scores
| Basis | Coverage | Quality | Completeness |
| ---------------- | ------------------- | ------------- | ------------ |
| Surface average | `Experimental (1%)` | `Alpha (63%)` | `Beta (70%)` |
| Category average | `Experimental (1%)` | `Alpha (64%)` | `Beta (71%)` |
- Coverage is derived from QA profile evidence.
- Quality measures reliability and operational confidence.
- Completeness measures how much of the expected user workflow is available.
## Score bands
| Label | Score range |
| ------------ | ----------- |
| Lovable | 95-100% |
| Stable | 80-95% |
| Beta | 70-80% |
| Alpha | 50-70% |
| Experimental | 0-50% |
## Surface scorecard
| Surface | Family | Level | Coverage | Quality | Completeness | Long-term support | Areas |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | --------------- | -------------------- | -------------------- | -------------------- | ----------------- | ----- |
| [Gateway runtime](/maturity/taxonomy#gateway-runtime) | Core | M4 Stable | `Experimental (3%)` | `Stable (81%)` | `Stable (89%)` | partial (12) | 13 |
| [CLI](/maturity/taxonomy#cli) | Core | M4 Stable | `Experimental (2%)` | `Stable (83%)` | `Stable (90%)` | partial (6) | 7 |
| [Plugins](/maturity/taxonomy#plugins) | Core | M3 Beta | `Experimental (2%)` | `Beta (72%)` | `Beta (79%)` | partial (7) | 9 |
| [Agent Runtime](/maturity/taxonomy#agent-runtime) | Core | M3 Beta | `Experimental (2%)` | `Beta (78%)` | `Beta (79%)` | partial (6) | 9 |
| [Session, memory, and context engine](/maturity/taxonomy#session-memory-and-context-engine) | Core | M3 Beta | `Experimental (0%)` | `Beta (77%)` | `Beta (79%)` | partial (6) | 9 |
| [Channel framework](/maturity/taxonomy#channel-framework) | Core | M3 Beta | `Experimental (0%)` | `Beta (76%)` | `Beta (79%)` | partial (5) | 8 |
| [Security, auth, pairing, and secrets](/maturity/taxonomy#security-auth-pairing-and-secrets) | Core | M3 Beta | `Experimental (0%)` | `Beta (72%)` | `Beta (79%)` | partial (5) | 6 |
| [Observability](/maturity/taxonomy#observability) | Core | M3 Beta | `Experimental (6%)` | `Beta (75%)` | `Beta (79%)` | partial (3) | 5 |
| [Automation: cron, hooks, tasks, polling](/maturity/taxonomy#automation-cron-hooks-tasks-polling) | Core | M3 Beta | `Experimental (0%)` | `Beta (72%)` | `Beta (79%)` | none | 6 |
| [Media understanding and media generation](/maturity/taxonomy#media-understanding-and-media-generation) | Core | M2 Alpha | `Experimental (1%)` | `Alpha (64%)` | `Alpha (68%)` | none | 6 |
| [Voice and realtime talk](/maturity/taxonomy#voice-and-realtime-talk) | Core | M2 Alpha | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | none | 6 |
| [Gateway Web App](/maturity/taxonomy#gateway-web-app) | Core | M3 Beta | `Experimental (0%)` | `Beta (74%)` | `Beta (79%)` | none | 6 |
| [TUI](/maturity/taxonomy#tui) | Core | M2 Alpha | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | none | 5 |
| [ClawHub](/maturity/taxonomy#clawhub) | Core | M2 Alpha | `Experimental (0%)` | `Alpha (58%)` | `Alpha (62%)` | none | 4 |
| [OpenClaw App SDK](/maturity/taxonomy#openclaw-app-sdk) | Core | M2 Alpha | `Experimental (0%)` | `Alpha (54%)` | `Alpha (53%)` | none | 6 |
| [macOS Gateway host](/maturity/taxonomy#macos-gateway-host) | Platform | M4 Stable | `Experimental (0%)` | `Beta (74%)` | `Stable (88%)` | none | 7 |
| [macOS companion app](/maturity/taxonomy#macos-companion-app) | Platform | M3 Beta | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | none | 8 |
| [Linux Gateway host](/maturity/taxonomy#linux-gateway-host) | Platform | M4 Stable | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | partial (4) | 5 |
| [Linux companion app](/maturity/taxonomy#linux-companion-app) | Platform | M0 Planned | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | none | 5 |
| [Windows via WSL2](/maturity/taxonomy#windows-via-wsl2) | Platform | M3 Beta | `Experimental (3%)` | `Alpha (69%)` | `Beta (79%)` | partial (5) | 6 |
| [Native Windows](/maturity/taxonomy#native-windows) | Platform | M2 Alpha | `Experimental (0%)` | `Alpha (58%)` | `Alpha (66%)` | partial (1) | 4 |
| [Native Windows companion app](/maturity/taxonomy#native-windows-companion-app) | Platform | M0 Planned | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | none | 5 |
| [Android app](/maturity/taxonomy#android-app) | Platform | M2 Alpha | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | none | 7 |
| [iOS app](/maturity/taxonomy#ios-app) | Platform | M1 Experimental | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | none | 8 |
| [watchOS companion surfaces](/maturity/taxonomy#watchos-companion-surfaces) | Platform | M1 Experimental | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | none | 5 |
| [Raspberry Pi and small Linux devices](/maturity/taxonomy#raspberry-pi-and-small-linux-devices) | Platform | M3 Beta | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | none | 4 |
| [Docker and Podman hosting](/maturity/taxonomy#docker-and-podman-hosting) | Platform | M3 Beta | `Experimental (5%)` | `Beta (71%)` | `Beta (79%)` | none | 4 |
| [Kubernetes hosting](/maturity/taxonomy#kubernetes-hosting) | Platform | M2 Alpha | `Experimental (0%)` | `Alpha (55%)` | `Alpha (61%)` | none | 4 |
| [Nix install path](/maturity/taxonomy#nix-install-path) | Platform | M1 Experimental | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | none | 5 |
| [Discord](/maturity/taxonomy#discord) | Channel | M4 Stable | `Experimental (0%)` | `Beta (73%)` | `Stable (87%)` | partial (4) | 6 |
| [Telegram](/maturity/taxonomy#telegram) | Channel | M3 Beta | `Experimental (0%)` | `Alpha (68%)` | `Beta (78%)` | full (5) | 5 |
| [WhatsApp](/maturity/taxonomy#whatsapp) | Channel | M3 Beta | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | none | 5 |
| [Slack](/maturity/taxonomy#slack) | Channel | M3 Beta | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | full (5) | 5 |
| [iMessage and BlueBubbles](/maturity/taxonomy#imessage-and-bluebubbles) | Channel | M3 Beta | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | none | 5 |
| [Signal](/maturity/taxonomy#signal) | Channel | M2 Alpha | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | none | 5 |
| [Google Chat](/maturity/taxonomy#google-chat) | Channel | M2 Alpha | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | none | 5 |
| [Matrix](/maturity/taxonomy#matrix) | Channel | M2 Alpha | `Experimental (0%)` | `Alpha (60%)` | `Alpha (67%)` | none | 6 |
| [Microsoft Teams](/maturity/taxonomy#microsoft-teams) | Channel | M2 Alpha | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | none | 5 |
| [Mattermost, LINE, IRC, Nextcloud Talk, Nostr, Twitch, Tlon, Synology Chat](/maturity/taxonomy#mattermost-line-irc-nextcloud-talk-nostr-twitch-tlon-synology-chat) | Channel | M2 Alpha | `Experimental (0%)` | `Alpha (53%)` | `Alpha (54%)` | none | 4 |
| [Feishu, QQ Bot, WeChat, Yuanbao, Zalo, Zalo Personal, regional channels](/maturity/taxonomy#feishu-qq-bot-wechat-yuanbao-zalo-zalo-personal-regional-channels) | Channel | M2 Alpha | `Experimental (0%)` | `Alpha (55%)` | `Alpha (58%)` | none | 4 |
| [Voice Call channel](/maturity/taxonomy#voice-call-channel) | Channel | M1 Experimental | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | none | 5 |
| [OpenAI and Codex provider path](/maturity/taxonomy#openai-and-codex-provider-path) | Provider and tool | M3 Beta | `Experimental (8%)` | `Beta (74%)` | `Beta (79%)` | partial (3) | 5 |
| [Anthropic provider path](/maturity/taxonomy#anthropic-provider-path) | Provider and tool | M3 Beta | `Experimental (0%)` | `Beta (71%)` | `Beta (78%)` | none | 5 |
| [Google provider path](/maturity/taxonomy#google-provider-path) | Provider and tool | M3 Beta | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | none | 5 |
| [OpenRouter provider path](/maturity/taxonomy#openrouter-provider-path) | Provider and tool | M3 Beta | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | none | 4 |
| [Local model providers: Ollama, vLLM, SGLang, LM Studio](/maturity/taxonomy#local-model-providers-ollama-vllm-sglang-lm-studio) | Provider and tool | M2 Alpha | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | none | 5 |
| [Long-tail hosted providers](/maturity/taxonomy#long-tail-hosted-providers) | Provider and tool | M2 Alpha | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | none | 3 |
| [Web search tools](/maturity/taxonomy#web-search-tools) | Provider and tool | M3 Beta | `Experimental (7%)` | `Beta (74%)` | `Beta (79%)` | none | 4 |
| [Browser automation, exec, and sandbox tools](/maturity/taxonomy#browser-automation-exec-and-sandbox-tools) | Provider and tool | M3 Beta | `Experimental (15%)` | `Beta (75%)` | `Beta (79%)` | partial (2) | 3 |
| [Image, video, and music generation tools](/maturity/taxonomy#image-video-and-music-generation-tools) | Provider and tool | M2 Alpha | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | none | 5 |
## QA evidence summary
The checks below show which scorecard areas were exercised by QA profile evidence.
| Check set | Completed | Checks run | Results | Areas reviewed | Capabilities reviewed |
| ------------------------ | ------------------------ | ---------- | ----------------------------------------- | -------------- | --------------------- |
| Full taxonomy validation | 2026-06-23T04:16:43.175Z | 96 | 88 passed, 6 failed, 2 blocked, 0 skipped | 0 of 281 (0%) | 20 of 1675 (1.2%) |
### Readiness by area
| Check set | Surface | Area | Status | Capabilities reviewed | Follow-up |
| ------------------------ | ------------------------------------------------------------------------- | ------------------------------------------ | ------------------ | --------------------- | ------------------ |
| Full taxonomy validation | Agent Runtime | Agent Turn Execution | Needs review | 0 of 3 (0%) | 15 capability gaps |
| Full taxonomy validation | Agent Runtime | External Runtimes and Subagents | Needs review | 0 of 4 (0%) | 7 capability gaps |
| Full taxonomy validation | Agent Runtime | Hosted Provider Execution | Partially reviewed | 1 of 5 (20%) | 4 capability gaps |
| Full taxonomy validation | Agent Runtime | Local and Self-hosted Providers | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Agent Runtime | Model and Runtime Selection | Needs review | 0 of 4 (0%) | 6 capability gaps |
| Full taxonomy validation | Agent Runtime | Provider Auth | Needs review | 0 of 10 (0%) | 13 capability gaps |
| Full taxonomy validation | Agent Runtime | Streaming and Progress | Needs review | 0 of 2 (0%) | 4 capability gaps |
| Full taxonomy validation | Agent Runtime | Tool Calls and Response Handling | Needs review | 0 of 3 (0%) | 8 capability gaps |
| Full taxonomy validation | Agent Runtime | Tool Execution Controls | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Android app | Connection Setup | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Android app | Device Runtime | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Android app | Distribution | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Android app | Media Capture | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Android app | Mobile Chat | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Android app | Settings | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Android app | Voice | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Anthropic provider path | Media Inputs | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Anthropic provider path | Model and Runtime Selection | Needs review | 0 of 10 (0%) | 12 capability gaps |
| Full taxonomy validation | Anthropic provider path | Prompt Cache and Context | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Anthropic provider path | Provider Auth and Recovery | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Anthropic provider path | Request Transport and Turn Semantics | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Automation: cron, hooks, tasks, polling | Automation Hooks | Needs review | 0 of 11 (0%) | 11 capability gaps |
| Full taxonomy validation | Automation: cron, hooks, tasks, polling | Background Tasks and Flows | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Automation: cron, hooks, tasks, polling | Cron Jobs | Needs review | 0 of 15 (0%) | 15 capability gaps |
| Full taxonomy validation | Automation: cron, hooks, tasks, polling | Event Ingress | Needs review | 0 of 15 (0%) | 15 capability gaps |
| Full taxonomy validation | Automation: cron, hooks, tasks, polling | Heartbeat | Needs review | 0 of 5 (0%) | 6 capability gaps |
| Full taxonomy validation | Automation: cron, hooks, tasks, polling | Polling Controls | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Browser automation, exec, and sandbox tools | Browser Automation | Partially reviewed | 1 of 8 (12.5%) | 7 capability gaps |
| Full taxonomy validation | Browser automation, exec, and sandbox tools | Sandbox and Tool Policy | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Browser automation, exec, and sandbox tools | Tool Invocation and Execution | Partially reviewed | 2 of 6 (33.3%) | 4 capability gaps |
| Full taxonomy validation | Gateway Web App | Browser Access and Trust | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Gateway Web App | Browser Realtime Talk | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Gateway Web App | Browser UI | Needs review | 0 of 10 (0%) | 11 capability gaps |
| Full taxonomy validation | Gateway Web App | Configuration | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Gateway Web App | Operator Console | Needs review | 0 of 10 (0%) | 11 capability gaps |
| Full taxonomy validation | Gateway Web App | WebChat Conversations | Needs review | 0 of 15 (0%) | 18 capability gaps |
| Full taxonomy validation | Channel framework | Channel Actions Commands and Approvals | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Channel framework | Channel Setup | Needs review | 0 of 5 (0%) | 6 capability gaps |
| Full taxonomy validation | Channel framework | Conversation Routing and Delivery | Needs review | 0 of 10 (0%) | 22 capability gaps |
| Full taxonomy validation | Channel framework | Group Thread and Ambient Room Behavior | Needs review | 0 of 5 (0%) | 7 capability gaps |
| Full taxonomy validation | Channel framework | Inbound Access and Identity Gates | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Channel framework | Media Attachments and Rich Channel Data | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Channel framework | Outbound Delivery and Reply Pipeline | Needs review | 0 of 4 (0%) | 13 capability gaps |
| Full taxonomy validation | Channel framework | Status Health and Operator Controls | Needs review | 0 of 4 (0%) | 6 capability gaps |
| Full taxonomy validation | ClawHub | Catalog Discovery | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | ClawHub | Compatibility and Trust | Needs review | 0 of 12 (0%) | 12 capability gaps |
| Full taxonomy validation | ClawHub | Plugin Lifecycle and Health | Needs review | 0 of 26 (0%) | 26 capability gaps |
| Full taxonomy validation | ClawHub | Publishing | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | CLI | CLI Observability | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | CLI | CLI Setup | Partially reviewed | 1 of 6 (16.7%) | 5 capability gaps |
| Full taxonomy validation | CLI | Doctor | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | CLI | Gateway Service Management | Needs review | 0 of 5 (0%) | 6 capability gaps |
| Full taxonomy validation | CLI | Onboarding and Auth Setup | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | CLI | Plugin and Channel Setup | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | CLI | Updates and Upgrades | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Discord | Access and Identity | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Discord | Channel Setup and Operations | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Discord | Conversation Routing and Delivery | Needs review | 0 of 12 (0%) | 12 capability gaps |
| Full taxonomy validation | Discord | Media and Rich Content | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Discord | Native Controls and Approvals | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Discord | Realtime Voice and Calls | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Docker and Podman hosting | Agent Sandbox and Tooling | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Docker and Podman hosting | Container Operations | Needs review | 0 of 11 (0%) | 11 capability gaps |
| Full taxonomy validation | Docker and Podman hosting | Container Setup | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Docker and Podman hosting | Image Release and Validation | Partially reviewed | 1 of 5 (20%) | 5 capability gaps |
| Full taxonomy validation | Feishu, QQ Bot, WeChat, Yuanbao, Zalo, Zalo Personal, regional channels | Access and Identity | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Feishu, QQ Bot, WeChat, Yuanbao, Zalo, Zalo Personal, regional channels | Channel Setup and Operations | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Feishu, QQ Bot, WeChat, Yuanbao, Zalo, Zalo Personal, regional channels | Conversation Routing and Delivery | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Feishu, QQ Bot, WeChat, Yuanbao, Zalo, Zalo Personal, regional channels | Media and Rich Content | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Gateway runtime | Approvals and Remote Execution | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Gateway runtime | Device Auth and Pairing | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Gateway runtime | Gateway Lifecycle | Needs review | 0 of 7 (0%) | 8 capability gaps |
| Full taxonomy validation | Gateway runtime | Gateway RPC APIs and Events | Needs review | 0 of 20 (0%) | 20 capability gaps |
| Full taxonomy validation | Gateway runtime | Health, Diagnostics, and Repair | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | Gateway runtime | Hosted Web Surface | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Gateway runtime | HTTP APIs | Partially reviewed | 1 of 4 (25%) | 3 capability gaps |
| Full taxonomy validation | Gateway runtime | Network Access and Discovery | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Gateway runtime | Nodes and Remote Capabilities | Needs review | 0 of 8 (0%) | 8 capability gaps |
| Full taxonomy validation | Gateway runtime | Protocol Compatibility | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | Gateway runtime | Roles and Permissions | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Gateway runtime | Security Controls | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Gateway runtime | WebSocket Connection | Partially reviewed | 1 of 8 (12.5%) | 7 capability gaps |
| Full taxonomy validation | Google Chat | Access and Identity | Needs review | 0 of 11 (0%) | 11 capability gaps |
| Full taxonomy validation | Google Chat | Channel Setup and Operations | Needs review | 0 of 16 (0%) | 16 capability gaps |
| Full taxonomy validation | Google Chat | Conversation Routing and Delivery | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Google Chat | Media and Rich Content | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Google Chat | Native Controls and Approvals | Needs review | 0 of 16 (0%) | 16 capability gaps |
| Full taxonomy validation | Google provider path | Direct Gemini Runtime | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Google provider path | Media, Search, and Realtime | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Google provider path | Model Routing and Endpoints | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Google provider path | Prompt Caching | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Google provider path | Provider Setup and Credentials | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Image, video, and music generation tools | Image Generation | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Image, video, and music generation tools | Media Routing and Discovery | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Image, video, and music generation tools | Music Generation | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Image, video, and music generation tools | Task Lifecycle and Delivery | Needs review | 0 of 12 (0%) | 12 capability gaps |
| Full taxonomy validation | Image, video, and music generation tools | Video Generation | Needs review | 0 of 11 (0%) | 11 capability gaps |
| Full taxonomy validation | iMessage and BlueBubbles | Access and Identity | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | iMessage and BlueBubbles | Channel Setup and Operations | Needs review | 0 of 11 (0%) | 11 capability gaps |
| Full taxonomy validation | iMessage and BlueBubbles | Conversation Routing and Delivery | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | iMessage and BlueBubbles | Media and Rich Content | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | iMessage and BlueBubbles | Native Controls and Approvals | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | iOS app | Canvas and Screen | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | iOS app | Chat and Sessions | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | iOS app | Device Commands | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | iOS app | Distribution | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | iOS app | Gateway Setup and Diagnostics | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | iOS app | Media and Sharing | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | iOS app | Notifications and Background | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | iOS app | Voice | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Kubernetes hosting | Access and Exposure | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Kubernetes hosting | Cluster Lifecycle | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Kubernetes hosting | Configuration and Secrets | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Kubernetes hosting | Deployment Setup | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Linux companion app | App Distribution | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Linux companion app | Chat and Sessions | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Linux companion app | Desktop Capabilities | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Linux companion app | Gateway Connectivity | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Linux companion app | Status and Diagnostics | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | Linux Gateway host | Deployment Targets | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Linux Gateway host | Diagnostics and Repair | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Linux Gateway host | Gateway Runtime and Service Control | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Linux Gateway host | Host Setup and Updates | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Linux Gateway host | Remote Access and Security | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Local model providers: Ollama, vLLM, SGLang, LM Studio | Local Memory and Embeddings | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Local model providers: Ollama, vLLM, SGLang, LM Studio | Native Provider Plugins | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Local model providers: Ollama, vLLM, SGLang, LM Studio | Network Safety and Prompt Controls | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Local model providers: Ollama, vLLM, SGLang, LM Studio | OpenAI-Compatible Runtime Compatibility | Needs review | 0 of 8 (0%) | 8 capability gaps |
| Full taxonomy validation | Local model providers: Ollama, vLLM, SGLang, LM Studio | Provider Setup, Lifecycle, and Diagnostics | Needs review | 0 of 12 (0%) | 12 capability gaps |
| Full taxonomy validation | Long-tail hosted providers | Hosted LLM Providers | Needs review | 0 of 12 (0%) | 12 capability gaps |
| Full taxonomy validation | Long-tail hosted providers | Hosted Media Providers | Needs review | 0 of 8 (0%) | 8 capability gaps |
| Full taxonomy validation | Long-tail hosted providers | Provider Operations | Needs review | 0 of 12 (0%) | 12 capability gaps |
| Full taxonomy validation | macOS companion app | Canvas | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | macOS companion app | Local Setup | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | macOS companion app | Native Capabilities | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | macOS companion app | Remote Connections | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | macOS companion app | Remote WebChat | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | macOS companion app | Status and Settings | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | macOS companion app | Voice and Talk | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | macOS companion app | WebChat | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | macOS Gateway host | CLI Setup | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | macOS Gateway host | Diagnostics and Observability | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | macOS Gateway host | Gateway Service Lifecycle | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | macOS Gateway host | Local Gateway Integration | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | macOS Gateway host | Permissions and Native Capabilities | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | macOS Gateway host | Profiles and Isolation | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | macOS Gateway host | Remote Gateway Mode | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Matrix | Access and Identity | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | Matrix | Channel Setup and Operations | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Matrix | Conversation Routing and Delivery | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Matrix | Encryption and Verification | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Matrix | Media and Rich Content | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Matrix | Native Controls and Approvals | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Mattermost, LINE, IRC, Nextcloud Talk, Nostr, Twitch, Tlon, Synology Chat | Access and Identity | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Mattermost, LINE, IRC, Nextcloud Talk, Nostr, Twitch, Tlon, Synology Chat | Channel Setup and Operations | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Mattermost, LINE, IRC, Nextcloud Talk, Nostr, Twitch, Tlon, Synology Chat | Conversation Routing and Delivery | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Mattermost, LINE, IRC, Nextcloud Talk, Nostr, Twitch, Tlon, Synology Chat | Media and Rich Content | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Media understanding and media generation | Channel Media Handling | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Media understanding and media generation | Media Configuration | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Media understanding and media generation | Media Generation | Partially reviewed | 1 of 17 (5.9%) | 18 capability gaps |
| Full taxonomy validation | Media understanding and media generation | Media Intake and Access | Needs review | 0 of 8 (0%) | 8 capability gaps |
| Full taxonomy validation | Media understanding and media generation | Media Understanding | Needs review | 0 of 12 (0%) | 13 capability gaps |
| Full taxonomy validation | Media understanding and media generation | Text-to-Speech Delivery | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Microsoft Teams | Access and Identity | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Microsoft Teams | Channel Setup and Operations | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Microsoft Teams | Conversation Routing and Delivery | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Microsoft Teams | Media and Rich Content | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Microsoft Teams | Native Controls and Approvals | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Native Windows | CLI | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Native Windows | Gateway Management | Needs review | 0 of 11 (0%) | 11 capability gaps |
| Full taxonomy validation | Native Windows | Networking | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Native Windows | Updates | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Native Windows companion app | Chat Sessions | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Native Windows companion app | Desktop Tools and Permissions | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Native Windows companion app | Gateway Connection | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Native Windows companion app | Installation and Updates | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Native Windows companion app | Status and Repair | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Nix install path | Activation and App UX | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | Nix install path | Config and State | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | Nix install path | Install Handoff | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Nix install path | Plugin Lifecycle | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Nix install path | Service Runtime and Guards | Needs review | 0 of 8 (0%) | 8 capability gaps |
| Full taxonomy validation | OpenAI and Codex provider path | Image and Multimodal Input | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | OpenAI and Codex provider path | Model and Auth | Partially reviewed | 1 of 6 (16.7%) | 5 capability gaps |
| Full taxonomy validation | OpenAI and Codex provider path | Native Codex Harness | Needs review | 0 of 2 (0%) | 5 capability gaps |
| Full taxonomy validation | OpenAI and Codex provider path | Responses and Tool Compatibility | Partially reviewed | 1 of 4 (25%) | 3 capability gaps |
| Full taxonomy validation | OpenAI and Codex provider path | Voice and Realtime Audio | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | OpenClaw App SDK | Agent Conversations | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | OpenClaw App SDK | Client API | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | OpenClaw App SDK | Compatibility | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | OpenClaw App SDK | Events and Approvals | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | OpenClaw App SDK | Gateway Access | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | OpenClaw App SDK | Resource Helpers | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | OpenRouter provider path | Chat Runtime and Normalization | Needs review | 0 of 15 (0%) | 15 capability gaps |
| Full taxonomy validation | OpenRouter provider path | Media Generation and Speech | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | OpenRouter provider path | Provider Recovery and Diagnostics | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | OpenRouter provider path | Provider Setup and Auth | Needs review | 0 of 14 (0%) | 14 capability gaps |
| Full taxonomy validation | Plugins | Authoring and Packaging plugins | Needs review | 0 of 8 (0%) | 8 capability gaps |
| Full taxonomy validation | Plugins | Bundled plugins | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Plugins | Canvas plugin | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Plugins | Channel plugins | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Plugins | Installing and running plugins | Needs review | 0 of 6 (0%) | 13 capability gaps |
| Full taxonomy validation | Plugins | Plugin approvals | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Plugins | Provider and tool plugins | Partially reviewed | 1 of 6 (16.7%) | 12 capability gaps |
| Full taxonomy validation | Plugins | Publishing plugins | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Plugins | Testing plugins | Needs review | 0 of 6 (0%) | 8 capability gaps |
| Full taxonomy validation | Raspberry Pi and small Linux devices | Gateway Runtime | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Raspberry Pi and small Linux devices | Performance and Diagnostics | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Raspberry Pi and small Linux devices | Remote Access and Auth | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Raspberry Pi and small Linux devices | Setup and Compatibility | Needs review | 0 of 12 (0%) | 12 capability gaps |
| Full taxonomy validation | Security, auth, pairing, and secrets | Approval Policy and Tool Safeguards | Needs review | 0 of 2 (0%) | 3 capability gaps |
| Full taxonomy validation | Security, auth, pairing, and secrets | Channel Access Control | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Security, auth, pairing, and secrets | Credential and Secret Hygiene | Needs review | 0 of 5 (0%) | 6 capability gaps |
| Full taxonomy validation | Security, auth, pairing, and secrets | Device and Node Pairing | Needs review | 0 of 11 (0%) | 11 capability gaps |
| Full taxonomy validation | Security, auth, pairing, and secrets | Gateway Auth and Remote Access | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Security, auth, pairing, and secrets | Plugin Trust | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Session, memory, and context engine | CLI Session and Transcript Management | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Session, memory, and context engine | Context Engine | Needs review | 0 of 2 (0%) | 3 capability gaps |
| Full taxonomy validation | Session, memory, and context engine | Core Prompts and Context | Needs review | 0 of 2 (0%) | 5 capability gaps |
| Full taxonomy validation | Session, memory, and context engine | Cross-client History and Session Parity | Needs review | 0 of 2 (0%) | 3 capability gaps |
| Full taxonomy validation | Session, memory, and context engine | Diagnostics, Maintenance, and Recovery | Needs review | 0 of 3 (0%) | 6 capability gaps |
| Full taxonomy validation | Session, memory, and context engine | Memory | Needs review | 0 of 5 (0%) | 7 capability gaps |
| Full taxonomy validation | Session, memory, and context engine | Session Routing | Needs review | 0 of 2 (0%) | 3 capability gaps |
| Full taxonomy validation | Session, memory, and context engine | Token Management | Needs review | 0 of 3 (0%) | 6 capability gaps |
| Full taxonomy validation | Session, memory, and context engine | Transcript Persistence | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Signal | Access and Identity | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Signal | Channel Setup and Operations | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | Signal | Conversation Routing and Delivery | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Signal | Media and Rich Content | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | Signal | Native Controls and Approvals | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Slack | Access and Identity | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Slack | Channel Setup and Operations | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Slack | Conversation Routing and Delivery | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Slack | Media and Rich Content | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Slack | Native Controls and Approvals | Needs review | 0 of 8 (0%) | 8 capability gaps |
| Full taxonomy validation | Telegram | Access and Identity | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Telegram | Channel Setup and Operations | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Telegram | Conversation Routing and Delivery | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Telegram | Media and Rich Content | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Telegram | Native Controls and Approvals | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Observability | Diagnostic Collection | Partially reviewed | 1 of 8 (12.5%) | 7 capability gaps |
| Full taxonomy validation | Observability | Health and Repair | Partially reviewed | 1 of 12 (8.3%) | 13 capability gaps |
| Full taxonomy validation | Observability | Logging | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Observability | Session Diagnostics | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Observability | Telemetry Export | Partially reviewed | 1 of 13 (7.7%) | 14 capability gaps |
| Full taxonomy validation | TUI | Input and Commands | Needs review | 0 of 8 (0%) | 8 capability gaps |
| Full taxonomy validation | TUI | Local Shell Execution | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | TUI | Rendering and Output Safety | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | TUI | Runtime Modes | Needs review | 0 of 14 (0%) | 14 capability gaps |
| Full taxonomy validation | TUI | Session Management | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Voice and realtime talk | Native App Talk | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Voice and realtime talk | Realtime Talk Sessions | Needs review | 0 of 11 (0%) | 11 capability gaps |
| Full taxonomy validation | Voice and realtime talk | Speech and Transcription | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Voice and realtime talk | Talk Observability | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | Voice and realtime talk | Talk Providers | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | Voice and realtime talk | Voice Wake and Routing | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Voice Call channel | Access and Identity | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Voice Call channel | Channel Setup and Operations | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Voice Call channel | Conversation Routing and Delivery | Needs review | 0 of 1 (0%) | 1 capability gap |
| Full taxonomy validation | Voice Call channel | Media and Rich Content | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Voice Call channel | Realtime Voice and Calls | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | watchOS companion surfaces | Delivery and Recovery | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | watchOS companion surfaces | Distribution and Support | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | watchOS companion surfaces | Exec Approvals | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | watchOS companion surfaces | Notifications and Replies | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | watchOS companion surfaces | Watch App UI | Needs review | 0 of 3 (0%) | 3 capability gaps |
| Full taxonomy validation | Web search tools | Network Safety | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | Web search tools | Search Providers | Partially reviewed | 2 of 19 (10.5%) | 17 capability gaps |
| Full taxonomy validation | Web search tools | Setup and Diagnostics | Needs review | 0 of 9 (0%) | 9 capability gaps |
| Full taxonomy validation | Web search tools | Tool Availability and Fetch | Partially reviewed | 2 of 11 (18.2%) | 9 capability gaps |
| Full taxonomy validation | WhatsApp | Access and Identity | Needs review | 0 of 7 (0%) | 7 capability gaps |
| Full taxonomy validation | WhatsApp | Channel Setup and Operations | Needs review | 0 of 5 (0%) | 5 capability gaps |
| Full taxonomy validation | WhatsApp | Conversation Routing and Delivery | Needs review | 0 of 4 (0%) | 4 capability gaps |
| Full taxonomy validation | WhatsApp | Media and Rich Content | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | WhatsApp | Native Controls and Approvals | Needs review | 0 of 2 (0%) | 2 capability gaps |
| Full taxonomy validation | Windows via WSL2 | Browser and Control UI | Needs review | 0 of 6 (0%) | 6 capability gaps |
| Full taxonomy validation | Windows via WSL2 | CLI | Needs review | 0 of 8 (0%) | 8 capability gaps |
| Full taxonomy validation | Windows via WSL2 | Diagnostics and Repair | Partially reviewed | 1 of 6 (16.7%) | 5 capability gaps |
| Full taxonomy validation | Windows via WSL2 | Gateway Access and Exposure | Needs review | 0 of 11 (0%) | 11 capability gaps |
| Full taxonomy validation | Windows via WSL2 | Gateway Service Lifecycle | Needs review | 0 of 10 (0%) | 10 capability gaps |
| Full taxonomy validation | Windows via WSL2 | WSL Setup | Needs review | 0 of 6 (0%) | 6 capability gaps |
> Last updated: 2026-06-22

View File

@@ -1,774 +0,0 @@
---
title: "Maturity taxonomy"
summary: "Detailed reference for the product areas and checks behind the OpenClaw maturity scorecard."
---
# Maturity taxonomy
This page explains the product areas and capability groups behind the maturity scorecard.
## Maturity levels
| Level | Label | Meaning | Promotion bar |
| ----- | ------------ | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `M0` | Planned | Direction is known, but no supported user path exists. | Design issue, owner, and target surface exist. |
| `M1` | Experimental | Implemented behind caveats, flags, source builds, or maintainer-only flows. | Maintainer can run the scenario from current main. |
| `M2` | Alpha | Real users can try it, but breaking changes and incomplete UX are expected. | Documented setup, basic tests, known caveats, and at least one real-environment proof. |
| `M3` | Beta | Public path exists and the main workflow is usable with bounded caveats. | Install/update docs, regression tests, support runbook, and successful scenario proof across the expected environment. |
| `M4` | Stable | Recommended path for normal users. Failures are treated as regressions. | Release gate, doctor/troubleshooting path, broad docs, and repeated real-world proof. |
| `M5` | Lovable | Polished, delightful, well-instrumented, and competitive with the best comparable workflow. | Stable plus user scorecard pass across representative users. |
## Product areas
### Core surfaces
- [Gateway runtime](#gateway-runtime)
- [CLI](#cli)
- [Plugins](#plugins)
- [Agent Runtime](#agent-runtime)
- [Session, memory, and context engine](#session-memory-and-context-engine)
- [Channel framework](#channel-framework)
- [Security, auth, pairing, and secrets](#security-auth-pairing-and-secrets)
- [Observability](#observability)
- [Automation: cron, hooks, tasks, polling](#automation-cron-hooks-tasks-polling)
- [Media understanding and media generation](#media-understanding-and-media-generation)
- [Voice and realtime talk](#voice-and-realtime-talk)
- [Gateway Web App](#gateway-web-app)
- [TUI](#tui)
- [ClawHub](#clawhub)
- [OpenClaw App SDK](#openclaw-app-sdk)
### Platform surfaces
- [macOS Gateway host](#macos-gateway-host)
- [macOS companion app](#macos-companion-app)
- [Linux Gateway host](#linux-gateway-host)
- [Linux companion app](#linux-companion-app)
- [Windows via WSL2](#windows-via-wsl2)
- [Native Windows](#native-windows)
- [Native Windows companion app](#native-windows-companion-app)
- [Android app](#android-app)
- [iOS app](#ios-app)
- [watchOS companion surfaces](#watchos-companion-surfaces)
- [Raspberry Pi and small Linux devices](#raspberry-pi-and-small-linux-devices)
- [Docker and Podman hosting](#docker-and-podman-hosting)
- [Kubernetes hosting](#kubernetes-hosting)
- [Nix install path](#nix-install-path)
### Channel surfaces
- [Discord](#discord)
- [Telegram](#telegram)
- [WhatsApp](#whatsapp)
- [Slack](#slack)
- [iMessage and BlueBubbles](#imessage-and-bluebubbles)
- [Signal](#signal)
- [Google Chat](#google-chat)
- [Matrix](#matrix)
- [Microsoft Teams](#microsoft-teams)
- [Mattermost, LINE, IRC, Nextcloud Talk, Nostr, Twitch, Tlon, Synology Chat](#mattermost-line-irc-nextcloud-talk-nostr-twitch-tlon-synology-chat)
- [Feishu, QQ Bot, WeChat, Yuanbao, Zalo, Zalo Personal, regional channels](#feishu-qq-bot-wechat-yuanbao-zalo-zalo-personal-regional-channels)
- [Voice Call channel](#voice-call-channel)
### Provider and tool surfaces
- [OpenAI and Codex provider path](#openai-and-codex-provider-path)
- [Anthropic provider path](#anthropic-provider-path)
- [Google provider path](#google-provider-path)
- [OpenRouter provider path](#openrouter-provider-path)
- [Local model providers: Ollama, vLLM, SGLang, LM Studio](#local-model-providers-ollama-vllm-sglang-lm-studio)
- [Long-tail hosted providers](#long-tail-hosted-providers)
- [Web search tools](#web-search-tools)
- [Browser automation, exec, and sandbox tools](#browser-automation-exec-and-sandbox-tools)
- [Image, video, and music generation tools](#image-video-and-music-generation-tools)
## Details
### Core
#### Gateway runtime
- Level: M4 Stable
- Rationale: Core architecture, auth, pairing, protocol docs, daemon docs, and CLI runbooks are broad and current.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | -------------- | -------------- | ----------------- |
| Approvals and Remote Execution | 6 | [Protocol](/gateway/protocol), [Index](/gateway/security/index) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| HTTP APIs | 4 | [Index](/gateway/index), [Openai Http Api](/gateway/openai-http-api), [Openresponses Http Api](/gateway/openresponses-http-api), [Tools Invoke Http Api](/gateway/tools-invoke-http-api), [Hooks](/automation/hooks), [Index](/web/index) | `Experimental (25%)` | `Stable (90%)` | `Stable (90%)` | Yes |
| Hosted Web Surface | 4 | [Index](/gateway/index), [Architecture](/concepts/architecture), [Control Ui](/web/control-ui), [Webchat](/web/webchat), [Canvas](/refactor/canvas) | `Experimental (0%)` | `Stable (89%)` | `Stable (90%)` | Yes |
| Gateway RPC APIs and Events | 20 | [Protocol](/gateway/protocol), [Index](/gateway/index), [Architecture](/concepts/architecture) | `Experimental (0%)` | `Stable (90%)` | `Stable (90%)` | Yes |
| Device Auth and Pairing | 10 | [Protocol](/gateway/protocol), [Pairing](/gateway/pairing), [Index](/gateway/security/index) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Network Access and Discovery | 6 | [Index](/gateway/index), [Discovery](/gateway/discovery), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Nodes and Remote Capabilities | 8 | [Protocol](/gateway/protocol), [Architecture](/concepts/architecture), [Index](/nodes/index) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | No |
| Health, Diagnostics, and Repair | 7 | [Index](/gateway/index), [Diagnostics](/gateway/diagnostics), [Doctor](/gateway/doctor) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Protocol Compatibility | 7 | [Protocol](/gateway/protocol), [Architecture](/concepts/architecture), [Typebox](/concepts/typebox), [Bridge Protocol](/gateway/bridge-protocol) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Roles and Permissions | 5 | [Protocol](/gateway/protocol), [Index](/gateway/security/index) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Gateway Lifecycle | 7 | [Index](/gateway/index), [Architecture](/concepts/architecture) | `Experimental (0%)` | `Stable (90%)` | `Stable (90%)` | Yes |
| Security Controls | 6 | [Index](/gateway/security/index), [Protocol](/gateway/protocol), [Discovery](/gateway/discovery) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| WebSocket Connection | 8 | [Protocol](/gateway/protocol), [Architecture](/concepts/architecture) | `Experimental (13%)` | `Stable (90%)` | `Stable (90%)` | Yes |
#### CLI
- Level: M4 Stable
- Rationale: Normal setup and repair paths are documented across install, CLI, and gateway docs. Platform-specific Windows paths are tracked in the Windows via WSL2 and Native Windows rows.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| -------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------- | -------------------- | -------------- | -------------- | ----------------- |
| CLI Setup | 6 | [Index](/install/index), [Installer](/install/installer), [Node](/install/node), [Updating](/install/updating) | `Experimental (17%)` | `Stable (89%)` | `Stable (90%)` | Yes |
| Onboarding and Auth Setup | 5 | [Onboard](/cli/onboard), [Configure](/cli/configure), [Onboarding Overview](/start/onboarding-overview) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Plugin and Channel Setup | 5 | [Onboard](/cli/onboard), [Plugins](/cli/plugins), [Channels](/cli/channels) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | No |
| Gateway Service Management | 5 | [Gateway](/cli/gateway), [Updating](/install/updating), [Troubleshooting](/gateway/troubleshooting) | `Experimental (0%)` | `Stable (87%)` | `Stable (90%)` | Yes |
| CLI Observability | 5 | [Status](/cli/status), [Health](/cli/health), [Logs](/cli/logs), [Diagnostics](/gateway/diagnostics) | `Experimental (0%)` | `Stable (89%)` | `Stable (90%)` | Yes |
| Doctor | 10 | [Doctor](/cli/doctor), [Doctor](/gateway/doctor), [Secrets](/gateway/secrets), [Troubleshooting](/gateway/troubleshooting) | `Experimental (0%)` | `Stable (89%)` | `Stable (90%)` | Yes |
| Updates and Upgrades | 5 | [Updating](/install/updating), [Update](/cli/update), [Troubleshooting](/gateway/troubleshooting) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
#### Plugins
- Level: M3 Beta
- Rationale: Broad docs and strong internal runtime evidence exist across manifests, discovery, loading, provider/tool architecture, and approval boundaries. Keep the row at beta until public SDK API/subpaths and external distribution proof are stronger.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------- | ------------ | ----------------- |
| Authoring and Packaging plugins | 8 | [Building Plugins](/plugins/building-plugins), [Sdk Overview](/plugins/sdk-overview), [Sdk Entrypoints](/plugins/sdk-entrypoints), [Sdk Subpaths](/plugins/sdk-subpaths), [Manifest](/plugins/manifest), [Reference](/plugins/reference) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Bundled plugins | 5 | [Plugin Inventory](/plugins/plugin-inventory), [Plugins](/cli/plugins), [Architecture Internals](/plugins/architecture-internals) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Canvas plugin | 6 | [Canvas](/plugins/reference/canvas), [Canvas](/refactor/canvas), [Configuration Reference](/gateway/configuration-reference) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Installing and running plugins | 6 | [Architecture](/plugins/architecture), [Architecture Internals](/plugins/architecture-internals), [Plugins](/cli/plugins) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Channel plugins | 5 | [Sdk Channel Plugins](/plugins/sdk-channel-plugins), [Sdk Channel Inbound](/plugins/sdk-channel-inbound), [Sdk Channel Outbound](/plugins/sdk-channel-outbound) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Provider and tool plugins | 6 | [Sdk Provider Plugins](/plugins/sdk-provider-plugins), [Tool Plugins](/plugins/tool-plugins), [Adding Capabilities](/plugins/adding-capabilities) | `Experimental (17%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Plugin approvals | 6 | [Plugin Permission Requests](/plugins/plugin-permission-requests), [Exec Approvals](/tools/exec-approvals), [Sdk Channel Plugins](/plugins/sdk-channel-plugins) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Publishing plugins | 6 | [Plugins](/cli/plugins), [Compatibility](/plugins/compatibility), [Publishing](/clawhub/publishing) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Testing plugins | 6 | [Sdk Testing](/plugins/sdk-testing), [Sdk Setup](/plugins/sdk-setup), [Codex Harness](/plugins/codex-harness) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
#### Agent Runtime
- Level: M3 Beta
- Rationale: Main loop, models, provider routing, and tool streaming are first-class, but provider behavior shifts weekly and needs scenario proof per release.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| -------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------- | ------------ | ----------------- |
| Agent Turn Execution | 3 | [Agent Loop](/concepts/agent-loop), [Agent](/cli/agent), [Agent Runtimes](/concepts/agent-runtimes) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| External Runtimes and Subagents | 4 | [Agent Runtimes](/concepts/agent-runtimes), [Anthropic](/providers/anthropic), [Google](/providers/google), [Subagents](/tools/subagents) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Hosted Provider Execution | 5 | [Openai](/providers/openai), [Anthropic](/providers/anthropic), [Google](/providers/google), [Models](/concepts/models) | `Experimental (20%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Local and Self-hosted Providers | 5 | [Ollama](/providers/ollama), [Models](/concepts/models), [Agent](/cli/agent) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Model and Runtime Selection | 4 | [Models](/concepts/models), [Models](/cli/models), [Openai](/providers/openai), [Agent Runtimes](/concepts/agent-runtimes) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Provider Auth | 10 | [Models](/concepts/models), [Agent](/cli/agent), [Models](/cli/models), [Openai](/providers/openai), [Anthropic](/providers/anthropic), [Google](/providers/google), [Subagents](/tools/subagents) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Streaming and Progress | 2 | [Streaming](/concepts/streaming), [Agent Loop](/concepts/agent-loop) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Tool Calls and Response Handling | 3 | [Agent Loop](/concepts/agent-loop), [Ollama](/providers/ollama) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Tool Execution Controls | 6 | [Sandbox Vs Tool Policy Vs Elevated](/gateway/sandbox-vs-tool-policy-vs-elevated), [Agent Loop](/concepts/agent-loop), [Subagents](/tools/subagents) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
#### Session, memory, and context engine
- Level: M3 Beta
- Rationale: Strong docs and active implementation. Maturity depends on transcript durability, compaction quality, and cross-client parity.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| CLI Session and Transcript Management | 2 | [Session](/concepts/session), [Session Management Compaction](/reference/session-management-compaction), [Sessions](/cli/sessions) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Token Management | 3 | [Compaction](/concepts/compaction), [Context](/concepts/context), [Session Management Compaction](/reference/session-management-compaction) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Context Engine | 2 | [Context](/concepts/context), [Context Engine](/concepts/context-engine), [Codex Context Engine Harness](/plan/codex-context-engine-harness) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Cross-client History and Session Parity | 2 | [Webchat](/web/webchat), [Android](/platforms/android), [Channel Routing](/channels/channel-routing) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Diagnostics, Maintenance, and Recovery | 3 | [Diagnostics](/gateway/diagnostics), [Session Management Compaction](/reference/session-management-compaction), [Flags](/diagnostics/flags) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Core Prompts and Context | 2 | [Context](/concepts/context), [Transcript Hygiene](/reference/transcript-hygiene), [Discord](/channels/discord) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Memory | 5 | [Memory Config](/reference/memory-config), [Memory Qmd](/concepts/memory-qmd), [Memory](/concepts/memory), [Discord](/channels/discord) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Session Routing | 2 | [Session](/concepts/session), [Channel Routing](/channels/channel-routing), [Discord](/channels/discord) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Transcript Persistence | 2 | [Session Management Compaction](/reference/session-management-compaction), [Transcript Hygiene](/reference/transcript-hygiene) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
#### Channel framework
- Level: M3 Beta
- Rationale: Many channels share Gateway delivery and routing contracts, but channel behavior varies by upstream API and account-policy constraints.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Channel Actions Commands and Approvals | 5 | [Groups](/channels/groups), [Discord](/channels/discord), [Googlechat](/channels/googlechat), [Signal](/channels/signal), [Matrix](/channels/matrix) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Channel Setup | 5 | [Index](/channels/index), [Pairing](/channels/pairing), [Troubleshooting](/channels/troubleshooting), [Sdk Channel Plugins](/plugins/sdk-channel-plugins) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Group Thread and Ambient Room Behavior | 5 | [Groups](/channels/groups), [Group Messages](/channels/group-messages), [Ambient Room Events](/channels/ambient-room-events), [Broadcast Groups](/channels/broadcast-groups), [Discord](/channels/discord) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Inbound Access and Identity Gates | 5 | [Access Groups](/channels/access-groups), [Groups](/channels/groups), [Discord](/channels/discord), [Line](/channels/line) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Media Attachments and Rich Channel Data | 4 | [Line](/channels/line), [Signal](/channels/signal), [Googlechat](/channels/googlechat), [Matrix](/channels/matrix), [Discord](/channels/discord) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Outbound Delivery and Reply Pipeline | 4 | [Groups](/channels/groups), [Ambient Room Events](/channels/ambient-room-events), [Discord](/channels/discord), [Matrix](/channels/matrix), [Config Channels](/gateway/config-channels) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Conversation Routing and Delivery | 10 | [Channel Routing](/channels/channel-routing), [Groups](/channels/groups), [Discord](/channels/discord), [Matrix](/channels/matrix), [Troubleshooting](/channels/troubleshooting), [Configuration Reference](/gateway/configuration-reference) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Status Health and Operator Controls | 4 | [Health](/gateway/health), [Configuration Reference](/gateway/configuration-reference), [Troubleshooting](/channels/troubleshooting), [Discord](/channels/discord) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
#### Security, auth, pairing, and secrets
- Level: M3 Beta
- Rationale: Good docs and hardening surfaces exist. Promote after regular upgrade/security scenario runs prove no setup regressions.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ----------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Approval Policy and Tool Safeguards | 2 | [Exec Approvals](/tools/exec-approvals), [Approvals](/cli/approvals), [Plugin Permission Requests](/plugins/plugin-permission-requests), [Audit Checks](/gateway/security/audit-checks) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Gateway Auth and Remote Access | 9 | [Index](/gateway/security/index), [Exposure Runbook](/gateway/security/exposure-runbook), [Trusted Proxy Auth](/gateway/trusted-proxy-auth), [Tailscale](/gateway/tailscale), [Remote](/gateway/remote), [Configuration Reference](/gateway/configuration-reference), [Gateway](/cli/gateway), [Doctor](/cli/doctor), [Control Ui](/web/control-ui), [Browser Control](/tools/browser-control), [Audit Checks](/gateway/security/audit-checks) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Channel Access Control | 3 | [Pairing](/channels/pairing), [Telegram](/channels/telegram), [Access Groups](/channels/access-groups), [Audit Checks](/gateway/security/audit-checks) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Device and Node Pairing | 11 | [Protocol](/gateway/protocol), [Devices](/cli/devices), [Pairing](/channels/pairing), [Pairing](/gateway/pairing), [Operator Scopes](/gateway/operator-scopes), [Control Ui](/web/control-ui), [Webchat](/web/webchat), [Approvals](/cli/approvals) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Plugin Trust | 2 | [Manifest](/plugins/manifest), [Plugin Permission Requests](/plugins/plugin-permission-requests), [Manage Plugins](/plugins/manage-plugins), [Audit Checks](/gateway/security/audit-checks) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Credential and Secret Hygiene | 5 | [Authentication](/gateway/authentication), [Models](/cli/models), [Openai](/providers/openai), [Oauth](/concepts/oauth), [Secrets](/gateway/secrets), [Secrets](/cli/secrets), [Secretref Credential Surface](/reference/secretref-credential-surface), [Audit Checks](/gateway/security/audit-checks) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
#### Observability
- Level: M3 Beta
- Rationale: OTel, Prometheus, logging, and diagnostics docs exist. Needs a public "what operators should look at first" maturity pass.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------- | ------------ | ----------------- |
| Health and Repair | 12 | [Health](/gateway/health), [Telegram](/channels/telegram), [Doctor](/cli/doctor), [Doctor](/gateway/doctor), [Sdk Subpaths](/plugins/sdk-subpaths), [Health](/cli/health), [Protocol](/gateway/protocol) | `Experimental (8%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Logging | 5 | [Logging](/logging), [Logging](/gateway/logging), [Logs](/cli/logs) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
| Diagnostic Collection | 8 | [Diagnostics](/gateway/diagnostics), [Health](/gateway/health), [Codex Harness](/plugins/codex-harness), [Protocol](/gateway/protocol) | `Experimental (13%)` | `Beta (79%)` | `Beta (79%)` | No |
| Telemetry Export | 13 | [Hooks](/plugins/hooks), [Opentelemetry](/gateway/opentelemetry), [Logging](/logging), [Sdk Subpaths](/plugins/sdk-subpaths), [Diagnostics Otel](/plugins/reference/diagnostics-otel), [Prometheus](/gateway/prometheus), [Diagnostics Prometheus](/plugins/reference/diagnostics-prometheus) | `Experimental (8%)` | `Beta (79%)` | `Beta (79%)` | No |
| Session Diagnostics | 4 | [Opentelemetry](/gateway/opentelemetry), [Prometheus](/gateway/prometheus), [Diagnostics](/gateway/diagnostics), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
#### Automation: cron, hooks, tasks, polling
- Level: M3 Beta
- Rationale: Documented and usable, but scenario proof should cover unattended delivery, retries, and failure visibility.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| -------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Cron Jobs | 15 | [Cron Jobs](/automation/cron-jobs), [Cron](/cli/cron), [Protocol](/gateway/protocol), [Tasks](/automation/tasks), [Discord](/channels/discord) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Event Ingress | 15 | [Telegram](/channels/telegram), [Zalo](/channels/zalo), [Troubleshooting](/channels/troubleshooting), [Imessage From Bluebubbles](/channels/imessage-from-bluebubbles), [Gmail Pubsub Integration](/automation/cron-jobs#gmail-pubsub-integration), [Gmail Pubsub](/automation/gmail-pubsub), [Webhooks](/cli/webhooks), [Webhooks](/automation/cron-jobs#webhooks), [Webhook](/automation/webhook) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Automation Hooks | 11 | [Hooks](/automation/hooks), [Hooks](/cli/hooks), [Hooks](/plugins/hooks), [Plugin Permission Requests](/plugins/plugin-permission-requests), [Sdk Subpaths](/plugins/sdk-subpaths) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Background Tasks and Flows | 10 | [Tasks](/automation/tasks), [Index](/automation/index), [Tasks](/cli/tasks), [Taskflow](/automation/taskflow), [Sdk Runtime](/plugins/sdk-runtime) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Heartbeat | 5 | [Index](/automation/index), [Heartbeat](/gateway/heartbeat), [Commitments](/concepts/commitments) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Polling Controls | 10 | [Poll](/automation/poll), [Message](/cli/message), [Telegram](/channels/telegram), [Msteams](/channels/msteams), [Background Process](/gateway/background-process) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
#### Media understanding and media generation
- Level: M2 Alpha
- Rationale: Broad capability surface exists, but provider variance, file limits, and node/app parity make this not stable yet.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ----------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Media Intake and Access | 8 | [Media Overview](/tools/media-overview), [Media Understanding](/nodes/media-understanding), [Secure File Operations](/gateway/security/secure-file-operations), [Pdf](/tools/pdf), [Image Generation](/tools/image-generation), [Qr](/cli/qr), [Line](/channels/line), [Whatsapp](/channels/whatsapp) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Channel Media Handling | 5 | [Images](/nodes/images), [Media Overview](/tools/media-overview), [Discord](/channels/discord) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Media Configuration | 1 | [Media Overview](/tools/media-overview), [Image Generation](/tools/image-generation), [Manifest](/plugins/manifest), [Codex Harness](/plugins/codex-harness) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Text-to-Speech Delivery | 2 | [Tts](/tools/tts), [Media Overview](/tools/media-overview), [Discord](/channels/discord) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Media Understanding | 12 | [Audio](/nodes/audio), [Media Understanding](/nodes/media-understanding), [Media Overview](/tools/media-overview), [Whatsapp](/channels/whatsapp), [Images](/nodes/images), [Infer](/cli/infer), [Pdf](/tools/pdf) | `Experimental (0%)` | `Alpha (69%)` | `Alpha (69%)` | No |
| Media Generation | 17 | [Image Generation](/tools/image-generation), [Media Overview](/tools/media-overview), [Skills](/tools/skills), [Music Generation](/tools/music-generation), [Video Generation](/tools/video-generation) | `Experimental (6%)` | `Alpha (69%)` | `Alpha (69%)` | No |
#### Voice and realtime talk
- Level: M2 Alpha
- Rationale: Multiple implementations exist across Control UI, apps, and providers. Needs latency, failure-mode, and setup scorecards before beta.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Talk Providers | 7 | [Openai](/providers/openai), [Google](/providers/google), [Sdk Provider Plugins](/plugins/sdk-provider-plugins), [Talk](/nodes/talk), [Control Ui](/web/control-ui) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Realtime Talk Sessions | 11 | [Talk](/nodes/talk), [Control Ui](/web/control-ui) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Speech and Transcription | 5 | [Talk](/nodes/talk), [Openai](/providers/openai), [Google](/providers/google) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Native App Talk | 4 | [Talk](/nodes/talk), [Voicewake](/platforms/mac/voicewake) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Voice Wake and Routing | 4 | [Voicewake](/nodes/voicewake), [Voicewake](/platforms/mac/voicewake), [Voice Overlay](/platforms/mac/voice-overlay) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Talk Observability | 5 | [Control Ui](/web/control-ui), [Voice Overlay](/platforms/mac/voice-overlay), [Talk](/nodes/talk) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
#### Gateway Web App
- Level: M3 Beta
- Rationale: Web UI is documented with pairing, chat, PWA, Talk, push, and remote Gateway flows. Promote after cross-browser and mobile-PWA scorecards.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Browser Realtime Talk | 5 | [Control Ui](/web/control-ui), [Protocol](/gateway/protocol), [Talk](/nodes/talk) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Browser Access and Trust | 5 | [Control Ui](/web/control-ui), [Dashboard](/web/dashboard), [Tailscale](/gateway/tailscale), [Remote](/gateway/remote) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Configuration | 5 | [Control Ui](/web/control-ui), [Configuration](/gateway/configuration) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Browser UI | 10 | [Control Ui](/web/control-ui), [Index](/web/index), [Dashboard](/web/dashboard), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| WebChat Conversations | 15 | [Control Ui](/web/control-ui), [Webchat](/web/webchat), [Getting Started](/start/getting-started), [Channel Routing](/channels/channel-routing), [Secure File Operations](/gateway/security/secure-file-operations) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
| Operator Console | 10 | [Control Ui](/web/control-ui), [Health](/gateway/health), [Protocol](/gateway/protocol), [Dashboard](/web/dashboard) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | No |
#### TUI
- Level: M2 Alpha
- Rationale: Present in docs and source, but less visible as a primary user workflow. Needs explicit scenario definition.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------- | ------------ | -------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Runtime Modes | 14 | [Tui](/cli/tui), [Tui](/web/tui), [Index](/cli/index) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Input and Commands | 8 | [Tui](/web/tui) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Session Management | 3 | [Tui](/web/tui), [Sessions](/cli/sessions) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Local Shell Execution | 4 | [Tui](/web/tui), [Tui](/cli/tui) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Rendering and Output Safety | 4 | [Tui](/web/tui), [Qr](/cli/qr), [Logs](/cli/logs), [Completion](/cli/completion) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
#### ClawHub
- Level: M2 Alpha
- Rationale: Public docs and ecosystem concept exist. Needs install, trust, update, rollback, and compatibility scorecards.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Publishing | 7 | [Publishing](/clawhub/publishing), [Creating Skills](/tools/creating-skills), [Community](/plugins/community) | `Experimental (0%)` | `Alpha (54%)` | `Alpha (55%)` | No |
| Catalog Discovery | 5 | [Plugin](/tools/plugin), [Plugins](/cli/plugins), [Skills](/cli/skills), [Skills](/tools/skills), [Community](/plugins/community) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Compatibility and Trust | 12 | [Plugin](/tools/plugin), [Plugins](/cli/plugins), [Compatibility](/plugins/compatibility), [Plugin Inventory](/plugins/plugin-inventory), [Publishing](/clawhub/publishing), [Skills](/tools/skills), [Skills Config](/tools/skills-config) | `Experimental (0%)` | `Alpha (55%)` | `Alpha (56%)` | No |
| Plugin Lifecycle and Health | 26 | [Plugin](/tools/plugin), [Plugins](/cli/plugins), [Skills](/cli/skills), [Skills](/tools/skills), [Protocol](/gateway/protocol), [Bundles](/plugins/bundles), [Dependency Resolution](/plugins/dependency-resolution) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
#### OpenClaw App SDK
- Level: M2 Alpha
- Rationale: OpenClaw App SDK is a distinct external app contract separate from Gateway runtime and Plugin SDK. Current scoring shows a real `@openclaw/sdk` path with gaps around public packaging, auto-discovery, approvals, helpers, and compatibility.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| -------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Client API | 4 | [Openclaw Sdk](/gateway/external-apps), [Openclaw Sdk Api Design](/gateway/external-apps) | `Experimental (0%)` | `Alpha (51%)` | `Alpha (50%)` | No |
| Gateway Access | 5 | [Openclaw Sdk](/gateway/external-apps), [Openclaw Sdk Api Design](/gateway/external-apps), [Protocol](/gateway/protocol), [Index](/gateway/security/index) | `Experimental (0%)` | `Alpha (53%)` | `Alpha (54%)` | No |
| Agent Conversations | 6 | [Openclaw Sdk](/gateway/external-apps), [Openclaw Sdk Api Design](/gateway/external-apps), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Alpha (52%)` | `Alpha (52%)` | No |
| Events and Approvals | 5 | [Openclaw Sdk](/gateway/external-apps), [Openclaw Sdk Api Design](/gateway/external-apps), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Alpha (52%)` | `Alpha (52%)` | No |
| Resource Helpers | 5 | [Openclaw Sdk](/gateway/external-apps), [Openclaw Sdk Api Design](/gateway/external-apps) | `Experimental (0%)` | `Alpha (62%)` | `Alpha (53%)` | No |
| Compatibility | 5 | [Openclaw Sdk Api Design](/gateway/external-apps), [Typebox](/concepts/typebox), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Alpha (54%)` | `Alpha (55%)` | No |
### Platform
#### macOS Gateway host
- Level: M4 Stable
- Rationale: LaunchAgent service path, local/remote Gateway modes, CLI install, and app integration are documented.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ----------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | -------------- | ----------------- |
| CLI Setup | 4 | [Macos](/platforms/macos), [Bundled Gateway](/platforms/mac/bundled-gateway), [Installer](/install/installer), [Node](/install/node) | `Experimental (0%)` | `Beta (74%)` | `Stable (88%)` | No |
| Local Gateway Integration | 9 | [Macos](/platforms/macos), [Bundled Gateway](/platforms/mac/bundled-gateway), [Remote](/platforms/mac/remote), [Index](/gateway/index), [Gateway](/cli/gateway), [Bonjour](/gateway/bonjour) | `Experimental (0%)` | `Beta (74%)` | `Stable (88%)` | No |
| Remote Gateway Mode | 5 | [Remote](/platforms/mac/remote), [Remote](/gateway/remote), [Tailscale](/gateway/tailscale) | `Experimental (0%)` | `Beta (74%)` | `Stable (88%)` | No |
| Gateway Service Lifecycle | 10 | [Macos](/platforms/macos), [Bundled Gateway](/platforms/mac/bundled-gateway), [Gateway](/cli/gateway), [Index](/gateway/index), [Update](/cli/update), [Updating](/install/updating), [Uninstall](/install/uninstall), [Troubleshooting](/gateway/troubleshooting) | `Experimental (0%)` | `Beta (74%)` | `Stable (88%)` | No |
| Diagnostics and Observability | 4 | [Bundled Gateway](/platforms/mac/bundled-gateway), [Macos](/platforms/macos), [Gateway](/cli/gateway), [Doctor](/gateway/doctor), [Troubleshooting](/gateway/troubleshooting) | `Experimental (0%)` | `Beta (74%)` | `Stable (88%)` | No |
| Permissions and Native Capabilities | 4 | [Macos](/platforms/macos), [Remote](/platforms/mac/remote) | `Experimental (0%)` | `Beta (74%)` | `Stable (88%)` | No |
| Profiles and Isolation | 5 | [Multiple Gateways](/gateway/multiple-gateways), [Index](/gateway/index), [Gateway](/cli/gateway) | `Experimental (0%)` | `Beta (74%)` | `Stable (88%)` | No |
#### macOS companion app
- Level: M3 Beta
- Rationale: Rich menu bar app, permissions, node mode, Canvas, voice wake, WebChat, and remote mode exist. Still fast-moving enough to avoid Stable.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------- | ------------ | ----------------- |
| Canvas | 4 | [Canvas](/platforms/mac/canvas), [Macos](/platforms/macos), [Webchat](/web/webchat) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Local Setup | 7 | [Bundled Gateway](/platforms/mac/bundled-gateway), [Macos](/platforms/macos), [Child Process](/platforms/mac/child-process), [Dev Setup](/platforms/mac/dev-setup) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Status and Settings | 5 | [Menu Bar](/platforms/mac/menu-bar), [Icon](/platforms/mac/icon), [Macos](/platforms/macos), [Health](/platforms/mac/health), [Logging](/platforms/mac/logging), [Remote](/platforms/mac/remote) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Native Capabilities | 5 | [Macos](/platforms/macos), [Xpc](/platforms/mac/xpc), [Permissions](/platforms/mac/permissions), [Signing](/platforms/mac/signing), [Peekaboo](/platforms/mac/peekaboo) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Remote Connections | 3 | [Remote](/platforms/mac/remote), [Macos](/platforms/macos), [Remote](/gateway/remote) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Voice and Talk | 3 | [Voicewake](/platforms/mac/voicewake), [Voice Overlay](/platforms/mac/voice-overlay), [Talk](/nodes/talk), [Macos](/platforms/macos) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| WebChat | 3 | [Webchat](/platforms/mac/webchat), [Macos](/platforms/macos), [Webchat](/web/webchat) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Remote WebChat | 5 | [Webchat](/platforms/mac/webchat), [Remote](/gateway/remote), [Remote](/platforms/mac/remote) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
#### Linux Gateway host
- Level: M4 Stable
- Rationale: Node runtime is recommended, systemd user service is documented, and VPS/container guidance is broad.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ----------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | -------------- | ----------------- |
| Host Setup and Updates | 4 | [Index](/install/index), [Updating](/install/updating), [Linux](/platforms/linux), [Index](/platforms/index) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Gateway Runtime and Service Control | 6 | [Index](/gateway/index), [Gateway](/cli/gateway), [Linux](/platforms/linux), [Vps](/vps) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Remote Access and Security | 6 | [Remote](/gateway/remote), [Tailscale](/gateway/tailscale), [Exposure Runbook](/gateway/security/exposure-runbook), [Authentication](/gateway/authentication), [Secrets](/gateway/secrets) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Diagnostics and Repair | 4 | [Status](/cli/status), [Logs](/cli/logs), [Doctor](/cli/doctor), [Diagnostics](/gateway/diagnostics), [Index](/gateway/index) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | Yes |
| Deployment Targets | 3 | [Vps](/vps), [Docker](/install/docker), [Hetzner](/install/hetzner), [Digitalocean](/install/digitalocean), [Kubernetes](/install/kubernetes), [Podman](/install/podman) | `Experimental (0%)` | `Beta (75%)` | `Stable (89%)` | No |
#### Linux companion app
- Level: M0 Planned
- Rationale: Docs say native Linux companion apps are planned; Gateway is the supported Linux path today.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ---------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | -------------------- | -------------------- | ----------------- |
| App Distribution | 3 | [Linux](/platforms/linux), [Index](/platforms/index), [Index](/install/index) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
| Gateway Connectivity | 4 | [Linux](/platforms/linux), [Index](/gateway/index), [Pairing](/gateway/pairing), [Remote](/gateway/remote) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
| Chat and Sessions | 3 | [Linux](/platforms/linux), [Protocol](/gateway/protocol), [Webchat](/web/webchat) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
| Desktop Capabilities | 9 | [Linux](/platforms/linux), [Exec Approvals](/tools/exec-approvals), [Secrets](/gateway/secrets), [Index](/nodes/index), [Exec](/tools/exec), [Talk](/nodes/talk), [Camera](/nodes/camera) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
| Status and Diagnostics | 7 | [Linux](/platforms/linux), [Openclaw](/start/openclaw), [Doctor](/gateway/doctor) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
#### Windows via WSL2
- Level: M3 Beta
- Rationale: Recommended Windows path with systemd/user-service guidance and boot-chain docs. Promote after repeated install/update scorecards.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------- | ------------ | ----------------- |
| WSL Setup | 6 | [Windows](/platforms/windows), [Getting Started](/start/getting-started) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | Yes |
| CLI | 8 | [Windows](/platforms/windows), [Getting Started](/start/getting-started), [Updating](/install/updating), [Onboard](/cli/onboard), [Doctor](/cli/doctor), [Status](/cli/status), [Logs](/cli/logs) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | Yes |
| Gateway Service Lifecycle | 10 | [Windows](/platforms/windows), [Index](/gateway/index), [Doctor](/gateway/doctor) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | Yes |
| Gateway Access and Exposure | 11 | [Authentication](/gateway/authentication), [Secrets](/gateway/secrets), [Remote](/gateway/remote), [Exposure Runbook](/gateway/security/exposure-runbook), [Windows](/platforms/windows) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | Yes |
| Diagnostics and Repair | 6 | [Windows](/platforms/windows), [Status](/cli/status), [Logs](/cli/logs), [Doctor](/cli/doctor), [Doctor](/gateway/doctor) | `Experimental (17%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Browser and Control UI | 6 | [Browser Wsl2 Windows Remote Cdp Troubleshooting](/tools/browser-wsl2-windows-remote-cdp-troubleshooting), [Browser](/tools/browser), [Control Ui](/web/control-ui) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | No |
#### Native Windows
- Level: M2 Alpha
- Rationale: Core CLI/Gateway flows work, but docs still recommend WSL2 for the full experience and list native caveats.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| CLI | 9 | [Index](/install/index), [Installer](/install/installer), [Windows](/platforms/windows), [Getting Started](/start/getting-started), [Onboard](/cli/onboard) | `Experimental (0%)` | `Alpha (54%)` | `Alpha (64%)` | Yes |
| Gateway Management | 11 | [Windows](/platforms/windows), [Index](/gateway/index), [Gateway](/cli/gateway), [Doctor](/cli/doctor) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Networking | 4 | [Windows](/platforms/windows), [Index](/gateway/index), [Gateway](/cli/gateway) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Updates | 4 | [Updating](/install/updating), [Ci](/ci) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
#### Native Windows companion app
- Level: M0 Planned
- Rationale: Planned only.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ----------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | -------------------- | -------------------- | ----------------- |
| Installation and Updates | 4 | [Windows](/platforms/windows), [Index](/install/index) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
| Gateway Connection | 3 | [Windows](/platforms/windows), [Index](/gateway/index), [Pairing](/gateway/pairing), [Remote](/gateway/remote) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
| Chat Sessions | 2 | [Windows](/platforms/windows), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
| Status and Repair | 5 | [Windows](/platforms/windows), [Doctor](/gateway/doctor), [Index](/gateway/index) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
| Desktop Tools and Permissions | 10 | [Windows](/platforms/windows), [Index](/nodes/index), [Exec](/tools/exec), [Exec Approvals](/tools/exec-approvals), [Index](/gateway/security/index) | `Experimental (0%)` | `Experimental (19%)` | `Experimental (21%)` | No |
#### Android app
- Level: M2 Alpha
- Rationale: Public Google Play path exists, but app docs still describe the rebuild as extremely alpha and call out release hardening work.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ---------------- | ------------ | ------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Media Capture | 1 | [Android](/platforms/android), [Camera](/nodes/camera) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Mobile Chat | 1 | [Android](/platforms/android) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Connection Setup | 1 | [Android](/platforms/android), [Bonjour](/gateway/bonjour), [Pairing](/gateway/pairing) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Distribution | 3 | [Android](/platforms/android) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Settings | 1 | [Android](/platforms/android) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Voice | 1 | [Android](/platforms/android), [Talk](/nodes/talk) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Device Runtime | 2 | [Android](/platforms/android), [Troubleshooting](/nodes/troubleshooting), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
#### iOS app
- Level: M1 Experimental
- Rationale: Internal preview / super-alpha. TestFlight and relay-backed push flows exist, but no public distribution yet.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ----------------------------- | ------------ | ----------------------------------------------------------------------------- | ------------------- | -------------------- | -------------------- | ----------------- |
| Media and Sharing | 1 | [Ios](/platforms/ios), [Camera](/nodes/camera) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Canvas and Screen | 1 | [Ios](/platforms/ios), [Canvas](/plugins/reference/canvas) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Chat and Sessions | 1 | [Ios](/platforms/ios), [Webchat](/web/webchat), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Gateway Setup and Diagnostics | 7 | [Ios](/platforms/ios), [Pairing](/channels/pairing) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Distribution | 1 | [Ios](/platforms/ios) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Device Commands | 2 | [Ios](/platforms/ios), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Notifications and Background | 1 | [Ios](/platforms/ios), [Configuration](/gateway/configuration) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Voice | 1 | [Ios](/platforms/ios), [Talk](/nodes/talk) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
#### watchOS companion surfaces
- Level: M1 Experimental
- Rationale: Source has Watch app/extension surfaces; public docs do not yet present this as a user feature.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------------- | ------------ | -------------------------------------------------------------- | ------------------- | -------------------- | -------------------- | ----------------- |
| Delivery and Recovery | 7 | [Ios](/platforms/ios) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Exec Approvals | 3 | [Exec Approvals](/tools/exec-approvals), [Ios](/platforms/ios) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Distribution and Support | 6 | [Ios](/platforms/ios) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Notifications and Replies | 7 | [Ios](/platforms/ios) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Watch App UI | 3 | [Ios](/platforms/ios) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
#### Raspberry Pi and small Linux devices
- Level: M3 Beta
- Rationale: Platform docs exist and Gateway path is Linux-based. Needs hardware-specific release smoke proof to move higher.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Setup and Compatibility | 12 | [Raspberry Pi](/install/raspberry-pi), [Index](/install/index), [Faq First Run](/help/faq-first-run), [Faq](/help/faq), [Linux](/platforms/linux), [Installer](/install/installer) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | No |
| Remote Access and Auth | 9 | [Raspberry Pi](/install/raspberry-pi), [Authentication](/gateway/authentication), [Secrets](/gateway/secrets), [Pairing](/gateway/pairing), [Devices](/cli/devices), [Remote](/gateway/remote), [Tailscale](/gateway/tailscale) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | No |
| Gateway Runtime | 10 | [Index](/gateway/index), [Gateway](/cli/gateway), [Raspberry Pi](/install/raspberry-pi), [Linux](/platforms/linux), [Vps](/vps) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | No |
| Performance and Diagnostics | 5 | [Raspberry Pi](/install/raspberry-pi), [Linux](/platforms/linux), [Health](/gateway/health), [Diagnostics](/gateway/diagnostics) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | No |
#### Docker and Podman hosting
- Level: M3 Beta
- Rationale: Install docs exist and are common deployment paths. Promote after recurring release smoke captures upgrade and volume behavior.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ---------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------- | ------------ | ----------------- |
| Container Setup | 6 | [Docker](/install/docker), [Podman](/install/podman) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Container Operations | 11 | [Podman](/install/podman), [Docker Vm Runtime](/install/docker-vm-runtime), [Docker](/install/docker), [Hetzner](/install/hetzner), [Hostinger](/install/hostinger) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Image Release and Validation | 5 | [Docker](/install/docker), [Docker Vm Runtime](/install/docker-vm-runtime), [Full Release Validation](/reference/full-release-validation) | `Experimental (20%)` | `Beta (79%)` | `Beta (79%)` | No |
| Agent Sandbox and Tooling | 3 | [Docker](/install/docker), [Docker Vm Runtime](/install/docker-vm-runtime) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
#### Kubernetes hosting
- Level: M2 Alpha
- Rationale: Kubernetes hosting is a distinct Kustomize-based cluster deployment path. Current scoring shows a real minimal deployment path with gaps around Kubernetes-specific CI, ingress/TLS/NetworkPolicy packaging, backup/restore, and production exposure hardening.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Deployment Setup | 5 | [Kubernetes](/install/kubernetes), [Index](/install/index) | `Experimental (0%)` | `Alpha (55%)` | `Alpha (61%)` | No |
| Configuration and Secrets | 5 | [Kubernetes](/install/kubernetes), [Secrets](/gateway/secrets), [Environment](/help/environment) | `Experimental (0%)` | `Alpha (55%)` | `Alpha (61%)` | No |
| Access and Exposure | 5 | [Kubernetes](/install/kubernetes), [Authentication](/gateway/authentication), [Remote](/gateway/remote), [Exposure Runbook](/gateway/security/exposure-runbook) | `Experimental (0%)` | `Alpha (55%)` | `Alpha (61%)` | No |
| Cluster Lifecycle | 5 | [Kubernetes](/install/kubernetes), [Index](/gateway/index) | `Experimental (0%)` | `Alpha (55%)` | `Alpha (61%)` | No |
#### Nix install path
- Level: M1 Experimental
- Rationale: Optional install flow. Needs clearer support promise before alpha/beta promotion.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| -------------------------- | ------------ | --------------------------------------------------------------------------------------- | ------------------- | -------------------- | -------------------- | ----------------- |
| Install Handoff | 4 | [Nix](/install/nix), [Index](/install/index), [Docs Directory](/start/docs-directory) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Plugin Lifecycle | 4 | [Manage Plugins](/plugins/manage-plugins), [Plugin](/tools/plugin), [Nix](/install/nix) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Activation and App UX | 7 | [Nix](/install/nix) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Config and State | 7 | [Nix](/install/nix), [Setup](/cli/setup), [Environment](/help/environment) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Service Runtime and Guards | 8 | [Nix](/install/nix), [Setup](/cli/setup), [Doctor](/cli/doctor), [Update](/cli/update) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
### Channel
#### Discord
- Level: M4 Stable
- Rationale: Deep docs and broad feature coverage. Voice/delegation paths should stay separately scored as beta/alpha.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | -------------- | ----------------- |
| Channel Setup and Operations | 10 | [Discord](/channels/discord), [Discord](/plugins/reference/discord), [Fly](/install/fly), [Slash Commands](/tools/slash-commands), [Health](/gateway/health), [Channels](/cli/channels), [Config Channels](/gateway/config-channels) | `Experimental (0%)` | `Beta (73%)` | `Stable (87%)` | Yes |
| Access and Identity | 6 | [Discord](/channels/discord), [Pairing](/channels/pairing), [Access Groups](/channels/access-groups), [Groups](/channels/groups) | `Experimental (0%)` | `Beta (73%)` | `Stable (87%)` | Yes |
| Conversation Routing and Delivery | 12 | [Discord](/channels/discord), [Channel Routing](/channels/channel-routing), [Groups](/channels/groups), [Access Groups](/channels/access-groups), [Acp Agents](/tools/acp-agents), [Subagents](/tools/subagents) | `Experimental (0%)` | `Beta (73%)` | `Stable (87%)` | Yes |
| Media and Rich Content | 1 | [Discord](/channels/discord) | `Experimental (0%)` | `Beta (73%)` | `Stable (87%)` | Yes |
| Native Controls and Approvals | 5 | [Discord](/channels/discord), [Slash Commands](/tools/slash-commands) | `Experimental (0%)` | `Beta (73%)` | `Stable (87%)` | No |
| Realtime Voice and Calls | 5 | [Discord](/channels/discord), [Openai](/providers/openai), [Elevenlabs](/providers/elevenlabs), [Qa E2e Automation](/concepts/qa-e2e-automation), [Config Channels](/gateway/config-channels) | `Experimental (0%)` | `Beta (73%)` | `Stable (87%)` | No |
#### Telegram
- Level: M3 Beta
- Rationale: Core channel is mature enough for regular use, but high-variance UX and media edge cases need recurring scenario proof.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------- | ------------ | ----------------- |
| Channel Setup and Operations | 10 | [Telegram](/channels/telegram), [Config Channels](/gateway/config-channels), [Channels](/cli/channels) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | Yes |
| Access and Identity | 10 | [Telegram](/channels/telegram), [Pairing](/channels/pairing), [Access Groups](/channels/access-groups), [Groups](/channels/groups), [Multi Agent](/concepts/multi-agent) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | Yes |
| Conversation Routing and Delivery | 1 | [Telegram](/channels/telegram), [Groups](/channels/groups), [Multi Agent](/concepts/multi-agent) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | Yes |
| Media and Rich Content | 1 | [Telegram](/channels/telegram), [Location](/channels/location) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | Yes |
| Native Controls and Approvals | 9 | [Telegram](/channels/telegram), [Exec Approvals](/tools/exec-approvals), [Reactions](/tools/reactions) | `Experimental (0%)` | `Beta (77%)` | `Beta (79%)` | Yes |
#### WhatsApp
- Level: M3 Beta
- Rationale: Core path is important and documented; upstream Baileys/session volatility keeps it below Stable.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Channel Setup and Operations | 5 | [Whatsapp](/channels/whatsapp), [Config Channels](/gateway/config-channels), [Whatsapp](/plugins/reference/whatsapp), [Qa E2e Automation](/concepts/qa-e2e-automation), [Doctor](/gateway/doctor) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Access and Identity | 7 | [Whatsapp](/channels/whatsapp), [Config Channels](/gateway/config-channels), [Qa E2e Automation](/concepts/qa-e2e-automation), [Pairing](/channels/pairing) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Conversation Routing and Delivery | 4 | [Whatsapp](/channels/whatsapp), [Group Messages](/channels/group-messages) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Media and Rich Content | 2 | [Whatsapp](/channels/whatsapp) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Native Controls and Approvals | 2 | [Whatsapp](/channels/whatsapp) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
#### Slack
- Level: M3 Beta
- Rationale: First-class channel docs and routing surface. Needs workspace install/admin scenario scorecards.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Channel Setup and Operations | 10 | [Slack](/channels/slack), [Slack](/plugins/reference/slack), [Secrets](/gateway/secrets), [Qa E2e Automation](/concepts/qa-e2e-automation), [Troubleshooting](/channels/troubleshooting) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | Yes |
| Access and Identity | 1 | [Slack](/channels/slack), [Pairing](/channels/pairing) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | Yes |
| Conversation Routing and Delivery | 5 | [Slack](/channels/slack), [Bot Loop Protection](/channels/bot-loop-protection), [Pairing](/channels/pairing) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | Yes |
| Media and Rich Content | 1 | [Slack](/channels/slack), [Qa E2e Automation](/concepts/qa-e2e-automation) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | Yes |
| Native Controls and Approvals | 8 | [Slack](/channels/slack), [Slash Commands](/tools/slash-commands), [Exec Approvals](/tools/exec-approvals) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | Yes |
#### iMessage and BlueBubbles
- Level: M3 Beta
- Rationale: Supported iMessage runs through imsg on a signed-in macOS Messages host; legacy BlueBubbles configs require migration. Keep macOS permissions, SSH wrapper, SIP/private API, and migration caveats visible.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Channel Setup and Operations | 11 | [Bluebubbles Imessage](/announcements/bluebubbles-imessage), [Imessage From Bluebubbles](/channels/imessage-from-bluebubbles), [Config Channels](/gateway/config-channels), [Imessage](/channels/imessage) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Access and Identity | 6 | [Imessage](/channels/imessage), [Imessage From Bluebubbles](/channels/imessage-from-bluebubbles), [Config Channels](/gateway/config-channels) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Conversation Routing and Delivery | 4 | [Imessage](/channels/imessage) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Media and Rich Content | 7 | [Imessage](/channels/imessage), [Imessage From Bluebubbles](/channels/imessage-from-bluebubbles), [Config Channels](/gateway/config-channels) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Native Controls and Approvals | 3 | [Imessage](/channels/imessage) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
#### Signal
- Level: M2 Alpha
- Rationale: Supported channel docs exist; needs stronger install and reconnect proof.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | --------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Channel Setup and Operations | 7 | [Signal](/channels/signal), [Signal](/plugins/reference/signal) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Access and Identity | 6 | [Signal](/channels/signal) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Conversation Routing and Delivery | 1 | [Signal](/channels/signal) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Media and Rich Content | 7 | [Signal](/channels/signal) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Native Controls and Approvals | 3 | [Signal](/channels/signal) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
#### Google Chat
- Level: M2 Alpha
- Rationale: Documented channel, but enterprise/admin setup raises maturity risk.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Channel Setup and Operations | 16 | [Googlechat](/channels/googlechat), [Googlechat](/plugins/reference/googlechat), [Config Channels](/gateway/config-channels), [Wizard Cli Reference](/start/wizard-cli-reference), [Secrets](/gateway/secrets), [Secretref Credential Surface](/reference/secretref-credential-surface), [Health](/gateway/health), [Plugin Inventory](/plugins/plugin-inventory), [Index](/channels/index) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Access and Identity | 11 | [Googlechat](/channels/googlechat), [Pairing](/channels/pairing), [Access Groups](/channels/access-groups), [Config Channels](/gateway/config-channels), [Bot Loop Protection](/channels/bot-loop-protection), [Channel Routing](/channels/channel-routing) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Conversation Routing and Delivery | 1 | [Googlechat](/channels/googlechat), [Bot Loop Protection](/channels/bot-loop-protection), [Access Groups](/channels/access-groups), [Channel Routing](/channels/channel-routing) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Media and Rich Content | 1 | [Googlechat](/channels/googlechat), [Message](/cli/message), [Media Understanding](/nodes/media-understanding), [Secretref Credential Surface](/reference/secretref-credential-surface) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Native Controls and Approvals | 16 | [Googlechat](/channels/googlechat), [Message](/cli/message), [Media Understanding](/nodes/media-understanding), [Secretref Credential Surface](/reference/secretref-credential-surface), [Reactions](/tools/reactions), [Slash Commands](/tools/slash-commands), [Config Agents](/gateway/config-agents), [Message Lifecycle Refactor](/concepts/message-lifecycle-refactor) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
#### Matrix
- Level: M2 Alpha
- Rationale: Supported via bundled plugin. Needs bridge, auth, and room lifecycle scorecards.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------ | ------------------- | ------------- | ------------- | ----------------- |
| Channel Setup and Operations | 5 | [Matrix](/channels/matrix), [Matrix Migration](/channels/matrix-migration) | `Experimental (0%)` | `Alpha (60%)` | `Alpha (67%)` | No |
| Access and Identity | 7 | [Matrix](/channels/matrix), [Groups](/channels/groups), [Bot Loop Protection](/channels/bot-loop-protection) | `Experimental (0%)` | `Alpha (60%)` | `Alpha (67%)` | No |
| Conversation Routing and Delivery | 1 | [Matrix](/channels/matrix) | `Experimental (0%)` | `Alpha (60%)` | `Alpha (67%)` | No |
| Media and Rich Content | 1 | [Matrix](/channels/matrix) | `Experimental (0%)` | `Alpha (60%)` | `Alpha (67%)` | No |
| Native Controls and Approvals | 6 | [Matrix](/channels/matrix) | `Experimental (0%)` | `Alpha (60%)` | `Alpha (67%)` | No |
| Encryption and Verification | 3 | [Matrix](/channels/matrix), [Matrix Migration](/channels/matrix-migration) | `Experimental (0%)` | `Alpha (60%)` | `Alpha (67%)` | No |
#### Microsoft Teams
- Level: M2 Alpha
- Rationale: Enterprise auth/admin flows need explicit scenario proof.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Channel Setup and Operations | 9 | [Msteams](/channels/msteams), [Msteams](/plugins/reference/msteams), [Config Channels](/gateway/config-channels), [Health](/gateway/health) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Access and Identity | 9 | [Msteams](/channels/msteams), [Pairing](/channels/pairing), [Access Groups](/channels/access-groups) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Conversation Routing and Delivery | 5 | [Msteams](/channels/msteams), [Groups](/channels/groups), [Channel Routing](/channels/channel-routing) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Media and Rich Content | 5 | [Msteams](/channels/msteams) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
| Native Controls and Approvals | 5 | [Msteams](/channels/msteams), [Exec Approvals Advanced](/tools/exec-approvals-advanced) | `Experimental (0%)` | `Alpha (59%)` | `Alpha (66%)` | No |
#### Mattermost, LINE, IRC, Nextcloud Talk, Nostr, Twitch, Tlon, Synology Chat
- Level: M2 Alpha
- Rationale: Supported surfaces exist, but maturity likely varies by upstream and maintainer coverage. Score individually later.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ---- | ------------------- | ------------- | ------------- | ----------------- |
| Channel Setup and Operations | 1 | | `Experimental (0%)` | `Alpha (53%)` | `Alpha (54%)` | No |
| Access and Identity | 1 | | `Experimental (0%)` | `Alpha (53%)` | `Alpha (54%)` | No |
| Conversation Routing and Delivery | 1 | | `Experimental (0%)` | `Alpha (53%)` | `Alpha (54%)` | No |
| Media and Rich Content | 1 | | `Experimental (0%)` | `Alpha (53%)` | `Alpha (54%)` | No |
#### Feishu, QQ Bot, WeChat, Yuanbao, Zalo, Zalo Personal, regional channels
- Level: M2 Alpha
- Rationale: Important regional coverage, but public support level should be calibrated per account type, upstream approval, and maintainer proof.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------- | ------------- | ----------------- |
| Channel Setup and Operations | 6 | [Index](/channels/index), [Pairing](/channels/pairing), [Feishu](/plugins/reference/feishu), [Architecture Internals](/plugins/architecture-internals) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Access and Identity | 1 | | `Experimental (0%)` | `Alpha (53%)` | `Alpha (54%)` | No |
| Conversation Routing and Delivery | 1 | | `Experimental (0%)` | `Alpha (53%)` | `Alpha (54%)` | No |
| Media and Rich Content | 1 | | `Experimental (0%)` | `Alpha (53%)` | `Alpha (54%)` | No |
#### Voice Call channel
- Level: M1 Experimental
- Rationale: Optional/plugin path with complex realtime behavior. Needs scenario scorecard before public beta.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | --------------------------------------------------------------------------------------------- | ------------------- | -------------------- | -------------------- | ----------------- |
| Channel Setup and Operations | 2 | [Voicecall](/cli/voicecall), [Voice Call](/plugins/voice-call), [Protocol](/gateway/protocol) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Access and Identity | 1 | [Voice Call](/plugins/voice-call), [Voicecall](/cli/voicecall) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Conversation Routing and Delivery | 1 | [Voice Call](/plugins/voice-call) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Media and Rich Content | 2 | [Voice Call](/plugins/voice-call), [Plugin Inventory](/plugins/plugin-inventory) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
| Realtime Voice and Calls | 2 | [Voice Call](/plugins/voice-call) | `Experimental (0%)` | `Experimental (41%)` | `Experimental (44%)` | No |
### Provider and tool
#### OpenAI and Codex provider path
- Level: M3 Beta
- Rationale: Deep docs, OAuth/subscription path, realtime voice, image, and compatibility behavior. Provider churn keeps this from Stable without release-scorecard proof.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| -------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------- | ------------ | ----------------- |
| Model and Auth | 6 | [Openai](/providers/openai), [Codex Harness](/plugins/codex-harness), [Models](/concepts/models), [Oauth](/concepts/oauth), [Codex Harness Reference](/plugins/codex-harness-reference), [Auth Monitoring](/automation/auth-monitoring) | `Experimental (17%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Responses and Tool Compatibility | 4 | [Openai](/providers/openai), [Openresponses Http Api](/gateway/openresponses-http-api), [Openai Http Api](/gateway/openai-http-api), [Codex Native Plugins](/plugins/codex-native-plugins) | `Experimental (25%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Native Codex Harness | 2 | [Codex Harness](/plugins/codex-harness), [Codex Harness Runtime](/plugins/codex-harness-runtime), [Codex Harness Reference](/plugins/codex-harness-reference), [Codex Native Plugins](/plugins/codex-native-plugins) | `Experimental (0%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Image and Multimodal Input | 2 | [Openai](/providers/openai), [Image Generation](/tools/image-generation), [Images](/nodes/images) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | No |
| Voice and Realtime Audio | 2 | [Openai](/providers/openai), [Discord](/channels/discord), [Voice Call](/plugins/voice-call) | `Experimental (0%)` | `Alpha (67%)` | `Beta (79%)` | No |
#### Anthropic provider path
- Level: M3 Beta
- Rationale: First-class model provider. Needs recurring auth/catalog/tool-call scenario proof.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------------------------ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Provider Auth and Recovery | 9 | [Anthropic](/providers/anthropic), [Doctor](/gateway/doctor), [Configuration Examples](/gateway/configuration-examples), [Troubleshooting](/gateway/troubleshooting), [Prompt Caching](/reference/prompt-caching) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Model and Runtime Selection | 10 | [Anthropic](/providers/anthropic), [Config Agents](/gateway/config-agents), [Models](/concepts/models), [Cli Backends](/gateway/cli-backends) | `Experimental (0%)` | `Beta (78%)` | `Beta (79%)` | No |
| Request Transport and Turn Semantics | 10 | [Anthropic](/providers/anthropic), [Prompt Caching](/reference/prompt-caching), [Troubleshooting](/gateway/troubleshooting), [Cli Backends](/gateway/cli-backends), [Model Providers](/concepts/model-providers) | `Experimental (0%)` | `Beta (77%)` | `Beta (79%)` | No |
| Prompt Cache and Context | 5 | [Anthropic](/providers/anthropic), [Prompt Caching](/reference/prompt-caching), [Troubleshooting](/gateway/troubleshooting), [Heartbeat](/gateway/heartbeat) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Media Inputs | 4 | [Anthropic](/providers/anthropic), [Config Agents](/gateway/config-agents) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
#### Google provider path
- Level: M3 Beta
- Rationale: First-class provider with model and realtime surfaces. Needs separate Live/Talk scoring.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------------------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Provider Setup and Credentials | 10 | [Google](/providers/google), [Model Providers](/concepts/model-providers) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Model Routing and Endpoints | 10 | [Google](/providers/google), [Model Providers](/concepts/model-providers), [Google](/plugins/reference/google), [Gemini Search](/tools/gemini-search) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Direct Gemini Runtime | 9 | [Google](/providers/google), [Model Providers](/concepts/model-providers), [Faq Models](/help/faq-models), [Testing Live](/help/testing-live) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Media, Search, and Realtime | 10 | [Google](/plugins/reference/google), [Google](/providers/google) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Prompt Caching | 5 | [Prompt Caching](/reference/prompt-caching), [Google](/providers/google), [Model Providers](/concepts/model-providers), [Token Use](/reference/token-use) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
#### OpenRouter provider path
- Level: M3 Beta
- Rationale: Unified provider path is documented and valuable, but model-specific behavior varies.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------ | ----------------- |
| Provider Setup and Auth | 14 | [Openrouter](/providers/openrouter), [Model Providers](/concepts/model-providers), [Configure](/cli/configure), [Authentication](/gateway/authentication), [Environment](/help/environment), [Models](/cli/models), [Models](/concepts/models) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Chat Runtime and Normalization | 15 | [Openrouter](/providers/openrouter), [Model Providers](/concepts/model-providers), [Prompt Caching](/reference/prompt-caching) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Provider Recovery and Diagnostics | 5 | [Model Failover](/concepts/model-failover), [Openrouter](/providers/openrouter), [Models](/cli/models) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
| Media Generation and Speech | 7 | [Openrouter](/providers/openrouter), [Image Generation](/tools/image-generation), [Music Generation](/tools/music-generation), [Media Overview](/tools/media-overview), [Video Generation](/tools/video-generation), [Tts](/tools/tts) | `Experimental (0%)` | `Alpha (66%)` | `Beta (78%)` | No |
#### Local model providers: Ollama, vLLM, SGLang, LM Studio
- Level: M2 Alpha
- Rationale: Useful and documented, but environment variance is high.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ------------------------------------------ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Provider Setup, Lifecycle, and Diagnostics | 12 | [Local Models](/gateway/local-models), [Lmstudio](/providers/lmstudio), [Ollama](/providers/ollama), [Vllm](/providers/vllm), [Local Model Services](/gateway/local-model-services), [Config Agents](/gateway/config-agents), [Troubleshooting](/gateway/troubleshooting), [Doctor](/gateway/doctor) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Native Provider Plugins | 10 | [Ollama](/providers/ollama), [Lmstudio](/providers/lmstudio) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| OpenAI-Compatible Runtime Compatibility | 8 | [Vllm](/providers/vllm), [Sglang](/providers/sglang), [Local Models](/gateway/local-models), [Lmstudio](/providers/lmstudio) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Local Memory and Embeddings | 5 | [Memory](/concepts/memory), [Doctor](/gateway/doctor) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Network Safety and Prompt Controls | 2 | [Index](/gateway/security/index), [Config Tools](/gateway/config-tools), [Local Models](/gateway/local-models) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
#### Long-tail hosted providers
- Level: M2 Alpha
- Rationale: Many docs/reference pages exist; score should be generated from provider metadata plus live smoke coverage.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ---------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- | ------------- | ----------------- |
| Hosted LLM Providers | 12 | [Index](/providers/index), [Model Providers](/concepts/model-providers), [Testing Live](/help/testing-live), [Onboard](/cli/onboard) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Hosted Media Providers | 8 | [Manifest](/plugins/manifest), [Testing Live](/help/testing-live), [Index](/providers/index) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Provider Operations | 12 | [Index](/providers/index), [Model Providers](/concepts/model-providers), [Manifest](/plugins/manifest), [Testing Live](/help/testing-live), [Models](/cli/models) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
#### Web search tools
- Level: M3 Beta
- Rationale: Multiple providers and docs exist. Needs quota/error/SSRF proof per provider family.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- | ------------- | ------------ | ----------------- |
| Search Providers | 19 | [Web](/tools/web), [Brave Search](/tools/brave-search), [Tavily](/tools/tavily), [Exa Search](/tools/exa-search), [Firecrawl](/tools/firecrawl), [Perplexity Search](/tools/perplexity-search), [Duckduckgo Search](/tools/duckduckgo-search), [Searxng Search](/tools/searxng-search), [Gemini Search](/tools/gemini-search), [Grok Search](/tools/grok-search), [Kimi Search](/tools/kimi-search), [Minimax Search](/tools/minimax-search), [Ollama Search](/tools/ollama-search), [Sdk Subpaths](/plugins/sdk-subpaths), [Sdk Overview](/plugins/sdk-overview), [Manifest](/plugins/manifest) | `Experimental (11%)` | `Beta (79%)` | `Beta (79%)` | No |
| Setup and Diagnostics | 9 | [Web](/tools/web), [Web Fetch](/tools/web-fetch), [Faq](/help/faq), [Api Usage Costs](/reference/api-usage-costs), [Brave Search](/tools/brave-search), [Perplexity Search](/tools/perplexity-search), [Tavily](/tools/tavily), [Firecrawl](/tools/firecrawl) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Network Safety | 4 | [Web](/tools/web), [Web Fetch](/tools/web-fetch), [Firecrawl](/tools/firecrawl), [Searxng Search](/tools/searxng-search) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | No |
| Tool Availability and Fetch | 11 | [Config Tools](/gateway/config-tools), [Web Fetch](/tools/web-fetch), [Web](/tools/web), [Faq](/help/faq) | `Experimental (18%)` | `Beta (79%)` | `Beta (79%)` | No |
#### Browser automation, exec, and sandbox tools
- Level: M3 Beta
- Rationale: Core tools are documented, but host security and permission UX should stay under active scorecard review.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| ----------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------- | ------------ | ----------------- |
| Browser Automation | 8 | [Browser Control](/tools/browser-control), [Testing](/help/testing), [Browser](/tools/browser), [Index](/gateway/security/index), [Audit Checks](/gateway/security/audit-checks) | `Experimental (13%)` | `Beta (79%)` | `Beta (79%)` | No |
| Tool Invocation and Execution | 6 | [Exec](/tools/exec), [Background Process](/gateway/background-process), [Tools Invoke Http Api](/gateway/tools-invoke-http-api), [Operator Scopes](/gateway/operator-scopes), [Protocol](/gateway/protocol), [Exec Approvals](/tools/exec-approvals), [Exec Approvals Advanced](/tools/exec-approvals-advanced), [Elevated](/tools/elevated) | `Experimental (33%)` | `Beta (79%)` | `Beta (79%)` | Yes |
| Sandbox and Tool Policy | 6 | [Sandboxing](/gateway/sandboxing), [Sandbox Vs Tool Policy Vs Elevated](/gateway/sandbox-vs-tool-policy-vs-elevated), [Multi Agent Sandbox Tools](/tools/multi-agent-sandbox-tools), [Codex Harness Reference](/plugins/codex-harness-reference), [Config Tools](/gateway/config-tools) | `Experimental (0%)` | `Alpha (68%)` | `Beta (79%)` | Yes |
#### Image, video, and music generation tools
- Level: M2 Alpha
- Rationale: Capability exists across providers, but quality, latency, and parameter compatibility vary too much for beta without per-provider proof.
| Area | Capabilities | Docs | Coverage | Quality | Completeness | Long-term support |
| --------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------- | ------------- | ----------------- |
| Media Routing and Discovery | 4 | [Config Agents](/gateway/config-agents), [Image Generation](/tools/image-generation), [Video Generation](/tools/video-generation), [Music Generation](/tools/music-generation) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Task Lifecycle and Delivery | 12 | [Media Overview](/tools/media-overview), [Image Generation](/tools/image-generation), [Video Generation](/tools/video-generation), [Music Generation](/tools/music-generation) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Image Generation | 9 | [Image Generation](/tools/image-generation), [Infer](/cli/infer), [Media Overview](/tools/media-overview) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Video Generation | 11 | [Video Generation](/tools/video-generation), [Runway](/providers/runway), [Pixverse](/providers/pixverse), [Fal](/providers/fal), [Openrouter](/providers/openrouter) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |
| Music Generation | 6 | [Music Generation](/tools/music-generation) | `Experimental (0%)` | `Alpha (61%)` | `Alpha (68%)` | No |

View File

@@ -75,9 +75,9 @@ openclaw gateway call node.list --params "{}"
Official distributed iOS builds use the external push relay instead of publishing the raw APNs
token to the gateway.
Official/TestFlight builds from the public App Store release lane use the hosted relay at `https://ios-push-relay.openclaw.ai`.
By default, official/TestFlight builds and gateways use the hosted relay at `https://ios-push-relay.openclaw.ai`.
Custom relay deployments require a deliberately separate iOS build/deployment path whose relay URL matches the gateway relay URL. The public App Store release lane does not accept custom relay URL overrides. If you are using a custom relay build, set the matching gateway relay URL:
Custom relay deployments can override the gateway relay URL:
```json5
{
@@ -100,7 +100,7 @@ How the flow works:
- The iOS app fetches the paired gateway identity and includes it in relay registration, so the relay-backed registration is delegated to that specific gateway.
- The app forwards that relay-backed registration to the paired gateway with `push.apns.register`.
- The gateway uses that stored relay handle for `push.test`, background wakes, and wake nudges.
- Custom gateway relay URLs must match the relay URL baked into the iOS build.
- Custom gateway relay URLs must match the relay URL baked into the official/TestFlight iOS build.
- If the app later connects to a different gateway or a build with a different relay base URL, it refreshes the relay registration instead of reusing the old binding.
What the gateway does **not** need for this path:
@@ -111,7 +111,7 @@ What the gateway does **not** need for this path:
Expected operator flow:
1. Install the official/TestFlight iOS build.
2. Optional: set `gateway.push.apns.relay.baseUrl` on the gateway only when using a deliberately separate custom relay build.
2. Optional: set `gateway.push.apns.relay.baseUrl` on the gateway only when using a custom relay deployment.
3. Pair the app to the gateway and let it finish connecting.
4. The app publishes `push.apns.register` automatically after it has an APNs token, the operator session is connected, and relay registration succeeds.
5. After that, `push.test`, reconnect wakes, and wake nudges can use the stored relay-backed registration.
@@ -130,7 +130,7 @@ compatible but does not count as a durable last-seen update.
Compatibility note:
- `OPENCLAW_APNS_RELAY_BASE_URL` still works as a temporary env override for the gateway.
- The public App Store release lane rejects `OPENCLAW_PUSH_RELAY_BASE_URL` for iOS builds.
- `OPENCLAW_PUSH_RELAY_BASE_URL` still works as a temporary env override for official/TestFlight iOS builds.
## Authentication and trust flow

View File

@@ -150,12 +150,6 @@ the same directory), or `~/.openclaw/agents/<agentId>/copilot` otherwise.
Override with `copilotHome: <path>` on the attempt input when you need a
custom location (for example, a shared mount for migration).
Live harness tests use `OPENCLAW_COPILOT_AGENT_LIVE_TOKEN` when a direct token
is needed. The shared live-test setup intentionally scrubs `COPILOT_GITHUB_TOKEN`,
`GH_TOKEN`, and `GITHUB_TOKEN` after staging real auth profiles into the isolated
test home, so passing a `gh auth token` value through the dedicated live-test
variable avoids false skips without exposing the token to unrelated suites.
## Configuration surface
The harness reads its config from per-attempt input
@@ -320,19 +314,13 @@ right scope, and `session_status: "current"` resolves to a stale
sandbox key. The bridge builder is in
`extensions/copilot/src/tool-bridge.ts` and mirrors the PI
authoritative call at
`src/agents/pi-embedded-runner/run/attempt.ts:1029-1117`. `runAttempt`
already resolves sandbox context through the shared
`resolveSandboxContext` seam, passes the SDK an effective working
directory, and forwards `sandbox` plus the subagent-spawn workspace into
the tool bridge. The bridge also forwards the bounded tool-construction
controls it can enforce at the SDK boundary: `includeCoreTools`, the
runtime tool allowlist, and `toolConstructionPlan`.
The remaining PI tool-search/code-mode fields are intentionally **not**
forwarded at MVP and tracked as follow-ups: `toolSearchCatalogRef`,
`includeToolSearchControls`, and `toolSearchCatalogExecutor`. Those
controls drive PI's native tool-search UI and have no direct Copilot SDK
analog yet.
`src/agents/pi-embedded-runner/run/attempt.ts:1029-1117`. Two PI fields
are intentionally **not** forwarded at MVP and tracked as follow-ups:
`sandbox` (the harness does not yet route through `resolveSandboxContext`)
and the PI tool-search/code-mode machinery
(`toolSearchCatalogRef`, `includeCoreTools`,
`includeToolSearchControls`, `toolSearchCatalogExecutor`,
`toolConstructionPlan`), which has no analog at the SDK boundary.
### Session-level GitHub token

View File

@@ -319,56 +319,10 @@ Cron-driven runs also expose `ctx.jobId` (the originating cron job id) so
plugin hooks can scope metrics, side effects, or state to a specific scheduled
job.
For channel-originated runs, `ctx.channel` and `ctx.messageProvider` identify
the provider surface such as `discord` or `telegram`, while `ctx.channelId` is
the conversation target identifier when OpenClaw can derive one from the session
key or delivery metadata.
When sender identity is available, agent hook contexts also include:
- `ctx.senderId` — channel-scoped sender ID (e.g. Feishu `open_id`, Discord
user ID). Populated when the run originates from a user message with known
sender metadata.
- `ctx.chatId` — transport-native conversation identifier (e.g. Feishu
`chat_id`, Telegram `chat_id`). Populated when the originating channel
provides a native conversation ID.
- `ctx.channelContext.sender.id` — the same sender ID as `ctx.senderId`, under a
channel-owned object that plugins can extend with channel-specific fields.
- `ctx.channelContext.chat.id` — the same conversation ID as `ctx.chatId`, under a
channel-owned object that plugins can extend with channel-specific fields.
Core only defines the nested `id` fields. Channel plugins that pass richer
sender or chat metadata through the inbound helper can augment
`PluginHookChannelSenderContext` or `PluginHookChannelChatContext` from
`openclaw/plugin-sdk/channel-inbound`:
```ts
declare module "openclaw/plugin-sdk/channel-inbound" {
interface PluginHookChannelSenderContext {
unionId?: string;
userId?: string;
}
}
```
Channel plugins pass those fields through the inbound SDK helper:
```ts
buildChannelInboundEventContext({
// ...
channelContext: {
sender: { id: senderOpenId, unionId, userId },
chat: { id: chatId },
},
});
```
These fields are optional and absent for system-originated runs (heartbeat,
cron, exec-event).
`ctx.senderExternalId` remains as a deprecated source-compatibility field for
older plugins. Core does not populate it; new channel-specific sender identities
should live under `ctx.channelContext.sender` through module augmentation.
For channel-originated runs, `ctx.messageProvider` is the provider surface such
as `discord` or `telegram`, while `ctx.channelId` is the conversation target
identifier when OpenClaw can derive one from the session key or delivery
metadata.
`agent_end` is an observation hook. Gateway and persistent harness paths run it
fire-and-forget after the turn, while short-lived one-shot CLI paths wait for the

View File

@@ -191,7 +191,6 @@ or npm install metadata. Those belong in your plugin code and `package.json`.
| `skills` | No | `string[]` | Skill directories to load, relative to the plugin root. |
| `name` | No | `string` | Human-readable plugin name. |
| `description` | No | `string` | Short summary shown in plugin surfaces. |
| `icon` | No | `string` | HTTPS image URL for marketplace/catalog cards. ClawHub accepts any valid `https://` URL and falls back to the default plugin icon when this is omitted or invalid. |
| `version` | No | `string` | Informational plugin version. |
| `uiHints` | No | `Record<string, object>` | UI labels, placeholders, and sensitivity hints for config fields. |

View File

@@ -29,10 +29,7 @@ import {
```
- `buildChannelInboundEventContext(...)`: project normalized channel facts into
the prompt/session context. Use `channelContext` to pass channel-owned
sender/chat metadata through to plugin hook `ctx.channelContext`; augment
`PluginHookChannelSenderContext` or `PluginHookChannelChatContext` from this
subpath for channel-specific fields.
the prompt/session context.
- `runChannelInboundEvent(...)`: run ingest, classify, preflight, resolve,
record, dispatch, and finalize for one inbound platform event.
- `dispatchChannelInboundReply(...)`: record and dispatch an already assembled

View File

@@ -67,7 +67,7 @@ Wraps papla.media TTS and sends results as Telegram voice notes (no annoying aut
<img src="/assets/showcase/papla-tts.jpg" alt="Telegram voice note output from TTS" />
</Card>
<Card title="CodexMonitor" icon="eye" href="https://clawhub.ai/odrobnik/skills/codexmonitor">
<Card title="CodexMonitor" icon="eye" href="https://clawhub.ai/odrobnik/codexmonitor">
**@odrobnik** • `devtools` `codex` `brew`
Homebrew-installed helper to list, inspect, and watch local OpenAI Codex sessions (CLI + VS Code).
@@ -75,7 +75,7 @@ Homebrew-installed helper to list, inspect, and watch local OpenAI Codex session
<img src="/assets/showcase/codexmonitor.png" alt="CodexMonitor on ClawHub" />
</Card>
<Card title="Bambu 3D Printer Control" icon="print" href="https://clawhub.ai/tobiasbischoff/skills/bambu-cli">
<Card title="Bambu 3D Printer Control" icon="print" href="https://clawhub.ai/tobiasbischoff/bambu-cli">
**@tobiasbischoff** • `hardware` `3d-printing` `skill`
Control and troubleshoot BambuLab printers: status, jobs, camera, AMS, calibration, and more.
@@ -83,7 +83,7 @@ Control and troubleshoot BambuLab printers: status, jobs, camera, AMS, calibrati
<img src="/assets/showcase/bambu-cli.png" alt="Bambu CLI skill on ClawHub" />
</Card>
<Card title="Vienna transport (Wiener Linien)" icon="train" href="https://clawhub.ai/hjanuschka/skills/wienerlinien">
<Card title="Vienna transport (Wiener Linien)" icon="train" href="https://clawhub.ai/hjanuschka/wienerlinien">
**@hjanuschka** • `travel` `transport` `skill`
Real-time departures, disruptions, elevator status, and routing for Vienna's public transport.
@@ -97,7 +97,7 @@ Real-time departures, disruptions, elevator status, and routing for Vienna's pub
Automated UK school meal booking via ParentPay. Uses mouse coordinates for reliable table cell clicking.
</Card>
<Card title="R2 upload (Send Me My Files)" icon="cloud-arrow-up" href="https://clawhub.ai/julianengel/skills/r2-upload">
<Card title="R2 upload (Send Me My Files)" icon="cloud-arrow-up" href="https://clawhub.ai/skills/r2-upload">
**@julianengel** • `files` `r2` `presigned-urls`
Upload to Cloudflare R2/S3 and generate secure presigned download links. Useful for remote OpenClaw instances.
@@ -267,7 +267,7 @@ Speech-first entry points, phone bridges, and transcription-heavy workflows.
Vapi voice assistant to OpenClaw HTTP bridge. Near real-time phone calls with your agent.
</Card>
<Card title="OpenRouter transcription" icon="microphone" href="https://clawhub.ai/obviyus/skills/openrouter-transcribe">
<Card title="OpenRouter transcription" icon="microphone" href="https://clawhub.ai/obviyus/openrouter-transcribe">
**@obviyus** • `transcription` `multilingual` `skill`
Multi-lingual audio transcription via OpenRouter (Gemini, and more). Available on ClawHub.
@@ -289,8 +289,8 @@ Packaging, deployment, and integrations that make OpenClaw easier to run and ext
OpenClaw gateway running on Home Assistant OS with SSH tunnel support and persistent state.
</Card>
<Card title="Home Assistant skill" icon="toggle-on" href="https://clawhub.ai/homeofe/skills/openclaw-homeassistant">
**@homeofe** • `homeassistant` `skill` `automation`
<Card title="Home Assistant skill" icon="toggle-on" href="https://clawhub.ai/skills/homeassistant">
**ClawHub**`homeassistant` `skill` `automation`
Control and automate Home Assistant devices via natural language.
@@ -303,8 +303,8 @@ Control and automate Home Assistant devices via natural language.
Batteries-included nixified OpenClaw configuration for reproducible deployments.
</Card>
<Card title="CalDAV calendar" icon="calendar" href="https://clawhub.ai/asleep123/skills/caldav-calendar">
**@asleep123** • `calendar` `caldav` `skill`
<Card title="CalDAV calendar" icon="calendar" href="https://clawhub.ai/skills/caldav-calendar">
**ClawHub**`calendar` `caldav` `skill`
Calendar skill using khal and vdirsyncer. Self-hosted calendar integration.

View File

@@ -225,7 +225,7 @@ See [Skill Workshop](/tools/skill-workshop) for the full proposal lifecycle.
metadata:
```bash
openclaw skills install @openclaw/clawhub-publish
openclaw skills install clawhub-publish
```
</Step>

View File

@@ -92,8 +92,6 @@ Notes:
- Host execution (`gateway`/`node`) rejects `env.PATH` and loader overrides (`LD_*`/`DYLD_*`) to
prevent binary hijacking or injected code.
- OpenClaw sets `OPENCLAW_SHELL=exec` in the spawned command environment (including PTY and sandbox execution) so shell/profile rules can detect exec-tool context.
- For channel-origin runs, OpenClaw also exposes a narrow sender/chat identity JSON payload in
`OPENCLAW_CHANNEL_CONTEXT` when the channel provided those ids.
- `openclaw channels login` is blocked from `exec` because it is an interactive channel-auth flow; run it in a terminal on the gateway host, or use the channel-native login tool from chat when one exists.
- Important: sandboxing is **off by default**. If sandboxing is off, implicit `host=auto`
resolves to `gateway`. Explicit `host=sandbox` still fails closed instead of silently

View File

@@ -88,9 +88,8 @@ still returns one synthesized answer with citations rather than an N-result
list.
`freshness` accepts `day`, `week`, `month`, `year`, and the shared shortcuts
`pd`, `pw`, `pm`, and `py`. `day`/`pd` adds a recency instruction to the Gemini
query instead of a hard 24-hour range. `week`, `month`, `year`, and explicit
`date_after`/`date_before` ranges set Gemini Google Search grounding's
`pd`, `pw`, `pm`, and `py`. OpenClaw converts these values, or an explicit
`date_after`/`date_before` range, into Gemini Google Search grounding's
`timeRangeFilter`. `country`, `language`, and `domain_filter` are not supported.
## Model selection

View File

@@ -207,19 +207,6 @@ Key policy rules:
`codex` plugin owns Codex app-server runtime for canonical `openai/*` agent
refs, explicit `agentRuntime.id: "codex"`, and legacy `codex/*` refs.
When `plugins.allow` is unset and non-bundled plugins are auto-discovered from
the workspace or global plugin roots, startup logs
`plugins.allow is empty; discovered non-bundled plugins may auto-load: ...`.
The warning includes discovered plugin ids and, for short lists, a minimal
`plugins.allow` snippet. Run
[`openclaw plugins list --enabled --verbose`](/cli/plugins#list) or
[`openclaw plugins inspect <id>`](/cli/plugins#inspect) with the listed plugin
id before copying trusted plugins into `openclaw.json`. The same trust-pinning
guidance applies when diagnostics say a plugin loaded
`without install/load-path provenance`: inspect that plugin id, then pin the
trusted id in `plugins.allow` or reinstall from a trusted source so OpenClaw
records install provenance.
Run `openclaw doctor` or `openclaw doctor --fix` when config validation reports
stale plugin ids, allowlist/tool mismatches, or legacy bundled plugin paths.

View File

@@ -145,12 +145,12 @@ publish and sync.
| Action | Command |
| ---------------------------------- | ------------------------------------------------------ |
| Install a skill into the workspace | `openclaw skills install @owner/<slug>` |
| Install a skill into the workspace | `openclaw skills install <slug>` |
| Install from a Git repository | `openclaw skills install git:owner/repo@ref` |
| Install a local skill directory | `openclaw skills install ./path/to/skill --as my-tool` |
| Install for all local agents | `openclaw skills install @owner/<slug> --global` |
| Install for all local agents | `openclaw skills install <slug> --global` |
| Update all workspace skills | `openclaw skills update --all` |
| Update a shared managed skill | `openclaw skills update @owner/<slug> --global` |
| Update a shared managed skill | `openclaw skills update <slug> --global` |
| Update all shared managed skills | `openclaw skills update --all --global` |
| Verify a skill's trust envelope | `openclaw skills verify <slug>` |
| Print the generated Skill Card | `openclaw skills verify <slug> --card` |
@@ -179,7 +179,7 @@ publish and sync.
with detail pages for VirusTotal, ClawScan, and static analysis. The
command exits non-zero when ClawHub marks verification as failed. Publishers
recover false positives through the ClawHub dashboard or
`clawhub skill rescan @owner/<slug>`.
`clawhub skill rescan <slug>`.
</Accordion>
<Accordion title="Private archive installs">

View File

@@ -389,8 +389,8 @@ show the `x_search` prompt.
freshness ranges require both start and end dates.
Gemini, Grok, and Kimi return one synthesized answer with citations. They
accept `count` for shared-tool compatibility, but it does not change the
grounded answer shape. Gemini treats `day` freshness as a recency hint; wider
freshness values and explicit dates set Google Search grounding time ranges.
grounded answer shape. Gemini supports `freshness`, `date_after`, and
`date_before` by converting them to Google Search grounding time ranges.
Perplexity behaves the same way when you use the Sonar/OpenRouter
compatibility path (`plugins.entries.perplexity.config.webSearch.baseUrl` /
`model` or `OPENROUTER_API_KEY`).

View File

@@ -1,6 +1,5 @@
{
"id": "alibaba",
"icon": "https://cdn.simpleicons.org/alibabacloud/111111",
"activation": {
"onStartup": false
},

View File

@@ -2,7 +2,6 @@
"id": "anthropic-vertex",
"name": "Anthropic Vertex",
"description": "OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI.",
"icon": "https://cdn.simpleicons.org/anthropic/111111",
"activation": {
"onStartup": false
},

View File

@@ -1,6 +1,5 @@
{
"id": "anthropic",
"icon": "https://cdn.simpleicons.org/anthropic/111111",
"activation": {
"onStartup": false
},

View File

@@ -2,7 +2,6 @@
"id": "brave",
"name": "Brave",
"description": "OpenClaw Brave Search provider plugin for web search.",
"icon": "https://cdn.simpleicons.org/brave/111111",
"activation": {
"onStartup": false
},

View File

@@ -15,12 +15,8 @@ import { resolvePnpmRunner } from "./pnpm-runner.mjs";
const pluginDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const rootDir = path.resolve(pluginDir, "../..");
const require = createRequire(import.meta.url);
const hashFile =
process.env.OPENCLAW_A2UI_BUNDLE_HASH_FILE ??
path.join(pluginDir, "src", "host", "a2ui", ".bundle.hash");
const outputFile =
process.env.OPENCLAW_A2UI_BUNDLE_OUT ??
path.join(pluginDir, "src", "host", "a2ui", "a2ui.bundle.js");
const hashFile = path.join(pluginDir, "src", "host", "a2ui", ".bundle.hash");
const outputFile = path.join(pluginDir, "src", "host", "a2ui", "a2ui.bundle.js");
const a2uiAppDir = path.join(pluginDir, "src", "host", "a2ui-app");
const repoInputPaths = getBundleHashRepoInputPaths(rootDir);
const relativeRepoInputPaths = repoInputPaths.map((inputPath) =>

View File

@@ -11,9 +11,7 @@ const repoRoot = path.resolve(here, "../../../../..");
const require = createRequire(import.meta.url);
const uiRoot = path.resolve(repoRoot, "ui");
const fromHere = (p) => path.resolve(here, p);
const outputFile = process.env.OPENCLAW_A2UI_BUNDLE_OUT
? path.resolve(process.env.OPENCLAW_A2UI_BUNDLE_OUT)
: path.resolve(here, "..", "a2ui", "a2ui.bundle.js");
const outputFile = path.resolve(here, "..", "a2ui", "a2ui.bundle.js");
const a2uiLitIndex = require.resolve("@a2ui/lit");
const a2uiLitUi = require.resolve("@a2ui/lit/ui");

View File

@@ -1,6 +1,5 @@
{
"id": "cloudflare-ai-gateway",
"icon": "https://cdn.simpleicons.org/cloudflare/111111",
"activation": {
"onStartup": false
},

View File

@@ -1,6 +1,5 @@
{
"id": "copilot-proxy",
"icon": "https://cdn.simpleicons.org/githubcopilot/111111",
"activation": {
"onStartup": false
},

View File

@@ -2,7 +2,6 @@
"id": "copilot",
"name": "GitHub Copilot agent runtime",
"description": "Registers the GitHub Copilot agent runtime.",
"icon": "https://cdn.simpleicons.org/githubcopilot/111111",
"version": "2026.6.2",
"activation": {
"onStartup": false,

View File

@@ -1,6 +1,5 @@
{
"id": "deepgram",
"icon": "https://cdn.simpleicons.org/deepgram/111111",
"activation": {
"onStartup": false
},

View File

@@ -1,6 +1,5 @@
{
"id": "deepseek",
"icon": "https://cdn.simpleicons.org/deepseek/111111",
"activation": {
"onStartup": false
},

View File

@@ -2,7 +2,6 @@
"id": "discord",
"name": "Discord",
"description": "OpenClaw Discord channel plugin for channels, DMs, commands, and app events.",
"icon": "https://cdn.simpleicons.org/discord/111111",
"skills": ["./skills"],
"activation": {
"onStartup": false

View File

@@ -78,17 +78,6 @@ describe("chunkDiscordText", () => {
}
});
it("keeps chunks within maxChars when a closing fence line carries trailing text", () => {
// A line that both closes the fence and carries a long tail must still reserve closing-fence
// space; otherwise a mid-line flush appended "```" and overflowed maxChars (e.g. 2004 > 2000).
for (let pad = 1990; pad <= 2000; pad++) {
const text = "hi\n```lang\n```" + "z".repeat(pad);
for (const chunk of chunkDiscordText(text, { maxChars: 2000, maxLines: 100 })) {
expect(chunk.length).toBeLessThanOrEqual(2000);
}
}
});
it("preserves whitespace when splitting long lines", () => {
const text = Array.from({ length: 40 }, () => "word").join(" ");
const chunks = chunkDiscordText(text, { maxChars: 20, maxLines: 50 });

View File

@@ -207,12 +207,8 @@ export function chunkDiscordText(text: string, opts: ChunkDiscordTextOpts = {}):
}
}
// A flush can fire mid-line, before `openFence` advances to `nextOpenFence` below, so it closes
// against the still-open `openFence`. A fence-closing line that also carries trailing text would
// otherwise reserve 0 yet still get a closing fence appended on flush, overflowing maxChars.
const fenceToReserve = nextOpenFence ?? openFence;
const reserveChars = fenceToReserve ? closeFenceLine(fenceToReserve).length + 1 : 0;
const reserveLines = fenceToReserve ? 1 : 0;
const reserveChars = nextOpenFence ? closeFenceLine(nextOpenFence).length + 1 : 0;
const reserveLines = nextOpenFence ? 1 : 0;
const effectiveMaxChars = maxChars - reserveChars;
const effectiveMaxLines = maxLines - reserveLines;
const charLimit = effectiveMaxChars > 0 ? effectiveMaxChars : maxChars;

View File

@@ -1,6 +1,5 @@
{
"id": "duckduckgo",
"icon": "https://cdn.simpleicons.org/duckduckgo/111111",
"activation": {
"onStartup": false
},

View File

@@ -1,6 +1,5 @@
{
"id": "elevenlabs",
"icon": "https://cdn.simpleicons.org/elevenlabs/111111",
"activation": {
"onStartup": false
},

View File

@@ -2,7 +2,6 @@
"id": "google-meet",
"name": "Google Meet",
"description": "OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports.",
"icon": "https://cdn.simpleicons.org/googlemeet/111111",
"enabledByDefault": false,
"commandAliases": [{ "name": "googlemeet" }],
"activation": {

View File

@@ -1,6 +1,5 @@
{
"id": "google",
"icon": "https://cdn.simpleicons.org/google/111111",
"activation": {
"onStartup": false
},

View File

@@ -73,8 +73,6 @@ const GEMINI_FRESHNESS_DAYS: Record<GeminiFreshness, number> = {
year: 365,
};
const GEMINI_DAY_FRESHNESS_HINT = "Prioritize web sources published in the last 24 hours.";
// Gemini's google_search.time_range_filter accepts second-precision RFC 3339
// only. Despite the underlying google.protobuf.Timestamp type accepting "0, 3,
// 6 or 9 fractional digits", the Search grounding endpoint rejects any
@@ -101,18 +99,11 @@ function freshnessStartTime(freshness: GeminiFreshness, now: Date): string {
return toGeminiTimeRangeTimestamp(start);
}
function queryWithSoftFreshness(query: string, freshness?: "day"): string {
if (freshness !== "day") {
return query;
}
return `${query}\n\nSearch recency instruction: ${GEMINI_DAY_FRESHNESS_HINT} If no matching recent sources are available, state that limitation and use the most relevant available sources.`;
}
function resolveGeminiTimeRangeFilter(
args: Record<string, unknown>,
now = new Date(),
):
| { timeRangeFilter?: GeminiTimeRangeFilter; freshness?: "day" }
| { timeRangeFilter?: GeminiTimeRangeFilter }
| {
error:
| "invalid_freshness"
@@ -142,13 +133,6 @@ function resolveGeminiTimeRangeFilter(
const { freshness, dateAfter, dateBefore } = parsedTimeFilters;
if (freshness) {
// Gemini rejects 24-hour google_search.timeRangeFilter windows, while
// wider freshness windows still preserve the hard grounding contract.
if (freshness === "day") {
return {
freshness,
};
}
return {
timeRangeFilter: {
startTime: freshnessStartTime(freshness, now),
@@ -337,7 +321,6 @@ export async function executeGeminiSearch(
resolveSearchCount(count, DEFAULT_SEARCH_COUNT),
baseUrl,
model,
timeRange.freshness,
timeRange.timeRangeFilter?.startTime,
timeRange.timeRangeFilter?.endTime,
]);
@@ -348,7 +331,7 @@ export async function executeGeminiSearch(
const start = Date.now();
const result = await runGeminiSearch({
query: queryWithSoftFreshness(query, timeRange.freshness),
query,
apiKey,
baseUrl,
model,

View File

@@ -40,8 +40,7 @@ const GEMINI_TOOL_PARAMETERS = {
language: { type: "string", description: "Not supported by Gemini." },
freshness: {
type: "string",
description:
"Filter Gemini search freshness: week, month, and year use hard Google Search time ranges; day prioritizes the last 24 hours as a recency hint.",
description: "Limit Google Search grounding to recent results: day, week, month, or year.",
},
date_after: {
type: "string",

View File

@@ -547,62 +547,6 @@ describe("google transport stream", () => {
]);
});
it("keeps duplicate tool-call ids distinct while retaining the first signature", async () => {
guardedFetchMock.mockResolvedValueOnce(
buildSseResponse([
{
candidates: [
{
content: {
parts: [
{
functionCall: {
id: "call_1",
name: "first",
args: { value: 1 },
},
thoughtSignature: "first_signature",
},
{
functionCall: {
id: "call_1",
name: "second",
args: { value: 2 },
},
},
],
},
finishReason: "STOP",
},
],
},
]),
);
const streamFn = createGoogleGenerativeAiTransportStreamFn();
const stream = await Promise.resolve(
streamFn(buildGeminiModel(), {
messages: [{ role: "user", content: "hello", timestamp: 0 }],
} as never),
);
const result = await stream.result();
const toolCalls = result.content.filter((block) => block.type === "toolCall");
expect(toolCalls).toHaveLength(2);
expect(toolCalls[0]).toMatchObject({
id: "call_1",
name: "first",
arguments: { value: 1 },
thoughtSignature: "first_signature",
});
expect(toolCalls[1]).toMatchObject({
name: "second",
arguments: { value: 2 },
thoughtSignature: "first_signature",
});
expect(toolCalls[1]?.id).not.toBe("call_1");
});
it("keeps explicit thinking signatures after tool-call SSE parts", async () => {
guardedFetchMock.mockResolvedValueOnce(
buildSseResponse([

Some files were not shown because too many files have changed in this diff Show More