mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-24 00:04:25 +08:00
Compare commits
58 Commits
codex/refa
...
v2026.6.10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87b40c7160 | ||
|
|
b8108e4794 | ||
|
|
dec86febb6 | ||
|
|
a2e555338a | ||
|
|
8bb6472c4d | ||
|
|
e18d316734 | ||
|
|
cfb07d0d3a | ||
|
|
8608fa2046 | ||
|
|
3f67778c52 | ||
|
|
cbef591210 | ||
|
|
de982a0224 | ||
|
|
684e440137 | ||
|
|
f4d93c855b | ||
|
|
c4694f84ff | ||
|
|
6c29f88913 | ||
|
|
7f5423ca97 | ||
|
|
9509aa063c | ||
|
|
20aec98554 | ||
|
|
d1e190fbe8 | ||
|
|
2d42e52ac5 | ||
|
|
075091d0ca | ||
|
|
cd32d9ff91 | ||
|
|
8845f2fd61 | ||
|
|
e4bf1e6774 | ||
|
|
f5ff56640e | ||
|
|
9540a69b24 | ||
|
|
d91c1607c4 | ||
|
|
cf13590c1c | ||
|
|
83785a6e79 | ||
|
|
195890f815 | ||
|
|
0f8df48a91 | ||
|
|
7b28b73e78 | ||
|
|
3650766f26 | ||
|
|
eb03d0ee2b | ||
|
|
38c8b0c196 | ||
|
|
0030a192c8 | ||
|
|
34806b39cd | ||
|
|
b0f21f8af7 | ||
|
|
5af318b95d | ||
|
|
8d2e6d7686 | ||
|
|
b039e949b6 | ||
|
|
2ca5b7c93e | ||
|
|
51d1789cea | ||
|
|
89e73240a1 | ||
|
|
f3ee317f71 | ||
|
|
ecb82f1be9 | ||
|
|
f1a48dac18 | ||
|
|
cba9c02095 | ||
|
|
715dc718fc | ||
|
|
85f71f4c8f | ||
|
|
66f84a9bf1 | ||
|
|
50c2cc6a45 | ||
|
|
e3ccf8743f | ||
|
|
d4c2fa7aed | ||
|
|
6c1041339d | ||
|
|
db54a3268b | ||
|
|
9750d887f5 | ||
|
|
fce586538a |
@@ -4,6 +4,7 @@ import { execFileSync } from "node:child_process";
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
|
||||
const repo = "openclaw/openclaw";
|
||||
const commitAssociationQueryBatchSize = 20;
|
||||
const excludedHandles = new Set(["openclaw", "clawsweeper", "claude", "codex", "steipete"]);
|
||||
const nonEditorialTypes = new Set([
|
||||
"build",
|
||||
@@ -618,13 +619,25 @@ function graphql(query) {
|
||||
let lastError;
|
||||
for (let attempt = 0; attempt < 5; attempt += 1) {
|
||||
try {
|
||||
return githubApi(["graphql", "-f", `query=${query}`]).data;
|
||||
const response = githubApi(["graphql", "-f", `query=${query}`]);
|
||||
if (response?.data && typeof response.data === "object") {
|
||||
return response.data;
|
||||
}
|
||||
const errors = Array.isArray(response?.errors)
|
||||
? response.errors.map((error) => error?.message).filter(Boolean)
|
||||
: [];
|
||||
const detail = [...errors, response?.message].filter(Boolean).join("\n");
|
||||
throw new Error(
|
||||
detail
|
||||
? `GitHub GraphQL response did not include data:\n${detail}`
|
||||
: "GitHub GraphQL response did not include data.",
|
||||
);
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
const message = [error?.message, error?.stdout, error?.stderr].filter(Boolean).join("\n");
|
||||
// Historical ranges batch hundreds of objects; only retry transient transport failures.
|
||||
if (
|
||||
!/(?:operation timed out|ECONNRESET|ETIMEDOUT|EAI_AGAIN|TLS handshake timeout|stream error: .*CANCEL|unexpected end of JSON input|upstream connect error|connection termination|error connecting to api\.github\.com|Unexpected token '<')/i.test(
|
||||
!/(?:operation timed out|ECONNRESET|ETIMEDOUT|EAI_AGAIN|TLS handshake timeout|stream error: .*CANCEL|unexpected end of JSON input|upstream connect error|connection termination|connection reset by peer|error connecting to api\.github\.com|Unexpected token '<'|something went wrong|temporarily unavailable|internal server error|rate limit)/i.test(
|
||||
message,
|
||||
)
|
||||
) {
|
||||
@@ -657,8 +670,8 @@ function resolveAssociatedPullRequests(commitHashes, targetTimestamp) {
|
||||
pending.push({ commitHash, cursor: connection.pageInfo.endCursor });
|
||||
}
|
||||
}
|
||||
for (let index = 0; index < commitHashes.length; index += 40) {
|
||||
const chunk = commitHashes.slice(index, index + 40);
|
||||
for (let index = 0; index < commitHashes.length; index += commitAssociationQueryBatchSize) {
|
||||
const chunk = commitHashes.slice(index, index + commitAssociationQueryBatchSize);
|
||||
const fields = chunk
|
||||
.map(
|
||||
(hash, offset) =>
|
||||
|
||||
107
.github/workflows/full-release-validation.yml
vendored
107
.github/workflows/full-release-validation.yml
vendored
@@ -70,7 +70,7 @@ on:
|
||||
default: ""
|
||||
type: string
|
||||
npm_telegram_package_spec:
|
||||
description: Optional published package spec for the package Telegram E2E lane
|
||||
description: Optional published package spec for the focused package Telegram E2E rerun
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
@@ -95,7 +95,7 @@ on:
|
||||
default: ""
|
||||
type: string
|
||||
npm_telegram_provider_mode:
|
||||
description: Provider mode for the package Telegram E2E lane
|
||||
description: Provider mode for the focused package Telegram E2E rerun
|
||||
required: false
|
||||
default: mock-openai
|
||||
type: choice
|
||||
@@ -103,7 +103,7 @@ on:
|
||||
- mock-openai
|
||||
- live-frontier
|
||||
npm_telegram_scenario:
|
||||
description: Optional comma-separated Telegram scenario ids for the package Telegram lane
|
||||
description: Optional comma-separated Telegram scenario ids for the focused package Telegram E2E rerun
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
@@ -200,14 +200,16 @@ jobs:
|
||||
if [[ -n "${RELEASE_PACKAGE_SPEC// }" ]]; then
|
||||
echo "- Published release package: \`${RELEASE_PACKAGE_SPEC}\`"
|
||||
fi
|
||||
if [[ -n "${NPM_TELEGRAM_PACKAGE_SPEC// }" ]]; then
|
||||
if [[ "$RERUN_GROUP" == "npm-telegram" && -n "${NPM_TELEGRAM_PACKAGE_SPEC// }" ]]; then
|
||||
echo "- Published-package Telegram E2E: \`${NPM_TELEGRAM_PACKAGE_SPEC}\`"
|
||||
elif [[ -n "${RELEASE_PACKAGE_SPEC// }" ]]; then
|
||||
elif [[ "$RERUN_GROUP" == "npm-telegram" && -n "${RELEASE_PACKAGE_SPEC// }" ]]; then
|
||||
echo "- Published-package Telegram E2E: \`${RELEASE_PACKAGE_SPEC}\`"
|
||||
elif [[ "$RERUN_GROUP" == "all" && "$RELEASE_PROFILE" == "full" ]]; then
|
||||
echo "- Package Telegram E2E: parent \`release-package-under-test\` artifact"
|
||||
elif [[ "$RERUN_GROUP" == "npm-telegram" ]]; then
|
||||
echo "- Package Telegram E2E: focused rerun requires \`release_package_spec\` or \`npm_telegram_package_spec\`"
|
||||
elif [[ "$RERUN_GROUP" == "all" || "$RERUN_GROUP" == "release-checks" || "$RERUN_GROUP" == "package" ]]; then
|
||||
echo "- Package Telegram E2E: OpenClaw Release Checks Package Acceptance"
|
||||
else
|
||||
echo "- Package Telegram E2E: skipped unless \`release_profile=full\`, \`release_package_spec\`, or \`npm_telegram_package_spec\` is provided"
|
||||
echo "- Package Telegram E2E: skipped by rerun group"
|
||||
fi
|
||||
if [[ -n "${EVIDENCE_PACKAGE_SPEC// }" ]]; then
|
||||
echo "- Private evidence package proof: \`${EVIDENCE_PACKAGE_SPEC}\`"
|
||||
@@ -764,80 +766,10 @@ jobs:
|
||||
|
||||
dispatch_and_wait openclaw-release-checks.yml "${args[@]}"
|
||||
|
||||
prepare_release_package:
|
||||
name: Prepare release package artifact
|
||||
needs: [resolve_target, docker_runtime_assets_preflight]
|
||||
if: ${{ always() && needs.resolve_target.result == 'success' && inputs.npm_telegram_package_spec == '' && inputs.release_package_spec == '' && inputs.rerun_group == 'all' && inputs.release_profile == 'full' && needs.docker_runtime_assets_preflight.result == 'success' }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
artifact_name: ${{ steps.artifact.outputs.name }}
|
||||
package_sha256: ${{ steps.package.outputs.sha256 }}
|
||||
package_version: ${{ steps.package.outputs.package_version }}
|
||||
source_sha: ${{ steps.package.outputs.source_sha }}
|
||||
steps:
|
||||
- name: Checkout trusted workflow ref
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ github.ref_name }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set artifact metadata
|
||||
id: artifact
|
||||
run: echo "name=release-package-under-test" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
install-bun: "true"
|
||||
install-deps: "false"
|
||||
|
||||
- name: Resolve release package artifact
|
||||
id: package
|
||||
shell: bash
|
||||
env:
|
||||
PACKAGE_REF: ${{ needs.resolve_target.outputs.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node scripts/resolve-openclaw-package-candidate.mjs \
|
||||
--source ref \
|
||||
--package-ref "$PACKAGE_REF" \
|
||||
--output-dir .artifacts/docker-e2e-package \
|
||||
--output-name openclaw-current.tgz \
|
||||
--metadata .artifacts/docker-e2e-package/package-candidate.json \
|
||||
--github-output "$GITHUB_OUTPUT"
|
||||
digest="$(node -p "JSON.parse(require('fs').readFileSync('.artifacts/docker-e2e-package/package-candidate.json', 'utf8')).sha256")"
|
||||
version="$(node -p "JSON.parse(require('fs').readFileSync('.artifacts/docker-e2e-package/package-candidate.json', 'utf8')).version")"
|
||||
source_sha="$(node -p "JSON.parse(require('fs').readFileSync('.artifacts/docker-e2e-package/package-candidate.json', 'utf8')).packageSourceSha")"
|
||||
echo "source_sha=$source_sha" >> "$GITHUB_OUTPUT"
|
||||
{
|
||||
echo "## Release package artifact"
|
||||
echo
|
||||
echo "- Artifact: \`release-package-under-test\`"
|
||||
echo "- Package ref: \`$PACKAGE_REF\`"
|
||||
echo "- SHA-256: \`$digest\`"
|
||||
echo "- Version: \`$version\`"
|
||||
echo "- Source SHA: \`$source_sha\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload release package artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-package-under-test
|
||||
path: |
|
||||
.artifacts/docker-e2e-package/openclaw-current.tgz
|
||||
.artifacts/docker-e2e-package/package-candidate.json
|
||||
if-no-files-found: error
|
||||
|
||||
npm_telegram:
|
||||
name: Run package Telegram E2E
|
||||
needs: [resolve_target, prepare_release_package]
|
||||
if: ${{ always() && contains(fromJSON('["all","npm-telegram"]'), inputs.rerun_group) && (inputs.npm_telegram_package_spec != '' || inputs.release_package_spec != '' || (inputs.rerun_group == 'all' && inputs.release_profile == 'full')) }}
|
||||
needs: [resolve_target]
|
||||
if: ${{ always() && needs.resolve_target.result == 'success' && inputs.rerun_group == 'npm-telegram' && (inputs.npm_telegram_package_spec != '' || inputs.release_package_spec != '') }}
|
||||
continue-on-error: ${{ startsWith(github.ref, 'refs/heads/tideclaw/alpha/') }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: ${{ inputs.release_profile == 'full' && 360 || 60 }}
|
||||
@@ -853,8 +785,6 @@ jobs:
|
||||
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
|
||||
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
|
||||
PACKAGE_SPEC: ${{ inputs.npm_telegram_package_spec || inputs.release_package_spec }}
|
||||
PACKAGE_ARTIFACT_NAME: ${{ needs.prepare_release_package.outputs.artifact_name }}
|
||||
PREPARE_PACKAGE_RESULT: ${{ needs.prepare_release_package.result }}
|
||||
PROVIDER_MODE: ${{ inputs.npm_telegram_provider_mode }}
|
||||
SCENARIO: ${{ inputs.npm_telegram_scenario }}
|
||||
run: |
|
||||
@@ -883,18 +813,7 @@ jobs:
|
||||
return "$status"
|
||||
}
|
||||
|
||||
args=(-f package_spec="${PACKAGE_SPEC:-openclaw@beta}" -f harness_ref="$TARGET_SHA" -f provider_mode="$PROVIDER_MODE")
|
||||
if [[ -z "${PACKAGE_SPEC// }" ]]; then
|
||||
if [[ "$PREPARE_PACKAGE_RESULT" != "success" || -z "${PACKAGE_ARTIFACT_NAME// }" ]]; then
|
||||
echo "Full release Telegram requires either npm_telegram_package_spec or a prepared release-package-under-test artifact." >&2
|
||||
exit 1
|
||||
fi
|
||||
args+=(
|
||||
-f package_artifact_name="$PACKAGE_ARTIFACT_NAME"
|
||||
-f package_artifact_run_id="${GITHUB_RUN_ID}"
|
||||
-f package_label="full-release-${TARGET_SHA:0:12}"
|
||||
)
|
||||
fi
|
||||
args=(-f package_spec="$PACKAGE_SPEC" -f harness_ref="$TARGET_SHA" -f provider_mode="$PROVIDER_MODE")
|
||||
if [[ -n "${SCENARIO// }" ]]; then
|
||||
args+=(-f scenario="$SCENARIO")
|
||||
fi
|
||||
|
||||
@@ -717,7 +717,6 @@ jobs:
|
||||
published_upgrade_survivor_baselines: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'last-stable-4 2026.4.23 2026.5.2 2026.4.15' || '' }}
|
||||
published_upgrade_survivor_scenarios: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'reported-issues' || '' }}
|
||||
telegram_mode: mock-openai
|
||||
telegram_scenarios: telegram-help-command,telegram-commands-command,telegram-tools-compact-command,telegram-whoami-command,telegram-status-command,telegram-other-bot-command-gating,telegram-context-command,telegram-mentioned-message-reply,telegram-long-final-reuses-preview,telegram-mention-gating
|
||||
secrets:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
||||
|
||||
25
.github/workflows/workflow-sanity.yml
vendored
25
.github/workflows/workflow-sanity.yml
vendored
@@ -129,11 +129,28 @@ jobs:
|
||||
trusted_config="$RUNNER_TEMP/pre-commit-base.yaml"
|
||||
trusted_zizmor_config="$RUNNER_TEMP/zizmor-base.yml"
|
||||
|
||||
if ! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
|
||||
timeout --signal=TERM --kill-after=10s 30s git fetch --no-tags --depth=1 origin \
|
||||
"+${BASE_SHA}:refs/remotes/origin/security-base" ||
|
||||
fetch_base_ref() {
|
||||
local ref="$1"
|
||||
local target="$2"
|
||||
local fetch_status
|
||||
for attempt in 1 2 3; do
|
||||
timeout --signal=TERM --kill-after=10s 30s git fetch --no-tags --depth=1 origin \
|
||||
"+refs/heads/${BASE_REF}:refs/remotes/origin/${BASE_REF}"
|
||||
"+${ref}:${target}" && return 0
|
||||
fetch_status="$?"
|
||||
if [ "$fetch_status" != "124" ] && [ "$fetch_status" != "137" ]; then
|
||||
return "$fetch_status"
|
||||
fi
|
||||
if [ "$attempt" = "3" ]; then
|
||||
return "$fetch_status"
|
||||
fi
|
||||
echo "::warning::trusted base fetch for '$ref' timed out on attempt $attempt; retrying"
|
||||
sleep 5
|
||||
done
|
||||
}
|
||||
|
||||
if ! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
|
||||
fetch_base_ref "$BASE_SHA" "refs/remotes/origin/security-base" ||
|
||||
fetch_base_ref "refs/heads/${BASE_REF}" "refs/remotes/origin/${BASE_REF}"
|
||||
fi
|
||||
|
||||
if git cat-file -e "${BASE_SHA}:.pre-commit-config.yaml" 2>/dev/null; then
|
||||
|
||||
37
CHANGELOG.md
37
CHANGELOG.md
@@ -2,6 +2,43 @@
|
||||
|
||||
Docs: https://docs.openclaw.ai
|
||||
|
||||
## 2026.6.10
|
||||
|
||||
### Highlights
|
||||
|
||||
- **Automatic fast mode for talks:** OpenClaw can enable fast mode for short conversational turns, then return to normal mode for longer runs with bounded fallback and delivery behavior. (#85104) Thanks @alexph-dev and @vincentkoc.
|
||||
- **More reliable model routing:** Zai model synthesis, GLM overload failover, and native reasoning-level selection now follow the active model catalog more consistently. (#94461, #93241, #94067, #94136) Thanks @Pandah97, @chrysb, @0xghost42, @zhengli0922, @openperf, @civiltox, and @BorClaw.
|
||||
- **Safer session and channel state:** channel switches reset stale origin fields, and cron delivery awareness stays attached to the target session. (#95328, #93580) Thanks @ZengWen-DT, @jalehman, @gorkem2020, and @scotthuang.
|
||||
- **Trusted policies survive hook composition:** composed hook registries keep the trusted tool policies required by approval-sensitive flows. (#94545) Thanks @jesse-merhi.
|
||||
|
||||
### Changes
|
||||
|
||||
- **Agent and channel runtime:** fast-mode state now survives retries, fallback transitions, progress events, and embedded/CLI/ACP normalization; session and channel routing retain the current target and delivery context. (#85104, #93580, #95328) Thanks @alexph-dev, @vincentkoc, @scotthuang, @ZengWen-DT, @jalehman, and @gorkem2020.
|
||||
- **Provider behavior:** model catalogs now supply the correct Zai base URL, overload classification, and native reasoning controls for live-discovered models. (#94461, #93241, #94067, #94136) Thanks @Pandah97, @chrysb, @0xghost42, @zhengli0922, @openperf, @civiltox, and @BorClaw.
|
||||
|
||||
### Fixes
|
||||
|
||||
- **Fast-mode and policy correctness:** fallback cutoffs and reset notices are bounded, repeated progress events remain visible, Codex service-tier state is normalized, and trusted policies are not lost when hook registries are composed. (#85104, #94545) Thanks @alexph-dev, @vincentkoc, and @jesse-merhi.
|
||||
- **Model and delivery edge cases:** Zai and GLM failover paths use the right runtime metadata, while stale channel-origin state no longer leaks across session changes. (#94461, #93241, #95328) Thanks @Pandah97, @chrysb, @0xghost42, @zhengli0922, @ZengWen-DT, @jalehman, and @gorkem2020.
|
||||
|
||||
### Complete contribution record
|
||||
|
||||
This audited record covers the complete v2026.6.9..HEAD history: 11 merged PRs. The generation manifest also supplies direct commits as editorial input; the grouped notes above prioritize user impact.
|
||||
|
||||
#### Pull requests
|
||||
|
||||
- **PR #86627** Keep core doctor health in contribution order. Thanks @giodl73-repo.
|
||||
- **PR #93580** fix: preserve cron delivery awareness for target sessions. Thanks @scotthuang and @jalehman.
|
||||
- **PR #95030** refactor: add SDK transcript identity target API. Thanks @jalehman.
|
||||
- **PR #94838** refactor(copilot): complete harness lifecycle parity. Thanks @vincentkoc.
|
||||
- **PR #95328** fix(sessions): reset stale per-channel origin fields on channel switch. Related #95325. Thanks @ZengWen-DT and @jalehman and @gorkem2020.
|
||||
- **PR #94461** fix(zai): fall back to manifest baseUrl for synthesized GLM-5 models. Related #94269. Thanks @Pandah97 and @chrysb.
|
||||
- **PR #93241** fix(agents): classify Zhipu GLM overload as overloaded for failover. Related #93211. Thanks @0xghost42 and @zhengli0922.
|
||||
- **PR #94067** fix(channels): resolve native /think menu levels via runtime catalog for live-discovered models. Related #93835. Thanks @openperf and @civiltox.
|
||||
- **PR #94136** fix(zai): expose GLM-5.2 reasoning levels [AI-assisted]. Thanks @BorClaw.
|
||||
- **PR #85104** feat: fast talks auto mode. Related #85087. Thanks @alexph-dev.
|
||||
- **PR #94545** fix: keep trusted policies with hook registry. Thanks @jesse-merhi.
|
||||
|
||||
## 2026.6.9
|
||||
|
||||
### Highlights
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# Source of truth: apps/android/version.json
|
||||
# Generated by scripts/android-sync-versioning.ts.
|
||||
|
||||
OPENCLAW_ANDROID_VERSION_NAME=2026.6.2
|
||||
OPENCLAW_ANDROID_VERSION_CODE=2026060201
|
||||
OPENCLAW_ANDROID_VERSION_NAME=2026.6.10
|
||||
OPENCLAW_ANDROID_VERSION_CODE=2026061001
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
OpenClaw is now available on Android.
|
||||
|
||||
Connect to your OpenClaw Gateway to chat with your assistant, use realtime Talk mode, review approvals, and bring Android device capabilities like camera, location, screen, and notifications into your private automation workflows.
|
||||
Maintenance update for the current OpenClaw Android release.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"version": "2026.6.2",
|
||||
"versionCode": 2026060201
|
||||
"version": "2026.6.10",
|
||||
"versionCode": 2026061001
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# OpenClaw iOS Changelog
|
||||
|
||||
## 2026.6.10 - 2026-06-21
|
||||
|
||||
Maintenance update for the current OpenClaw beta release.
|
||||
|
||||
- Improved notification cleanup, Watch app compatibility, and native file input handling.
|
||||
|
||||
## 2026.6.9 - 2026-06-20
|
||||
|
||||
Maintenance update for the current OpenClaw release.
|
||||
|
||||
- Added Apple Watch controls for common agent actions.
|
||||
- Improved Gateway setup, notification settings, and share-extension identity handling.
|
||||
- Updated the Watch app integration for current Xcode compatibility.
|
||||
|
||||
## 2026.6.2 - 2026-06-02
|
||||
|
||||
OpenClaw is now available on iPhone.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Source of truth: apps/ios/version.json
|
||||
// Generated by scripts/ios-sync-versioning.ts.
|
||||
|
||||
OPENCLAW_IOS_VERSION = 2026.6.2
|
||||
OPENCLAW_MARKETING_VERSION = 2026.6.2
|
||||
OPENCLAW_IOS_VERSION = 2026.6.10
|
||||
OPENCLAW_MARKETING_VERSION = 2026.6.10
|
||||
OPENCLAW_BUILD_VERSION = 1
|
||||
|
||||
#include? "../build/Version.xcconfig"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
OpenClaw is now available on iPhone.
|
||||
Maintenance update for the current OpenClaw beta release.
|
||||
|
||||
Connect to your OpenClaw Gateway to chat with your assistant, use realtime Talk mode, review approvals, share content from iOS, and bring device capabilities like camera, location, screen, and notifications into your private automation workflows.
|
||||
- Improved notification cleanup, Watch app compatibility, and native file input handling.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": "2026.6.2"
|
||||
"version": "2026.6.10"
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.6.2</string>
|
||||
<string>2026.6.9</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2026060200</string>
|
||||
<string>2026060900</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>OpenClaw</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -7273,7 +7273,9 @@ public struct ChatSendParams: Codable, Sendable {
|
||||
public let sessionid: String?
|
||||
public let message: String
|
||||
public let thinking: String?
|
||||
public let fastmode: Bool?
|
||||
public let fastmodevalue: AnyCodable?
|
||||
public var fastmode: Bool? { fastmodevalue?.value as? Bool }
|
||||
public let fastautoonseconds: Int?
|
||||
public let deliver: Bool?
|
||||
public let originatingchannel: String?
|
||||
public let originatingto: String?
|
||||
@@ -7286,6 +7288,46 @@ public struct ChatSendParams: Codable, Sendable {
|
||||
public let suppresscommandinterpretation: Bool?
|
||||
public let idempotencykey: String
|
||||
|
||||
public init(
|
||||
sessionkey: String,
|
||||
agentid: String? = nil,
|
||||
sessionid: String?,
|
||||
message: String,
|
||||
thinking: String?,
|
||||
fastmodevalue: AnyCodable?,
|
||||
fastautoonseconds: Int?,
|
||||
deliver: Bool?,
|
||||
originatingchannel: String?,
|
||||
originatingto: String?,
|
||||
originatingaccountid: String?,
|
||||
originatingthreadid: String?,
|
||||
attachments: [AnyCodable]?,
|
||||
timeoutms: Int?,
|
||||
systeminputprovenance: [String: AnyCodable]?,
|
||||
systemprovenancereceipt: String?,
|
||||
suppresscommandinterpretation: Bool?,
|
||||
idempotencykey: String)
|
||||
{
|
||||
self.sessionkey = sessionkey
|
||||
self.agentid = agentid
|
||||
self.sessionid = sessionid
|
||||
self.message = message
|
||||
self.thinking = thinking
|
||||
self.fastmodevalue = fastmodevalue
|
||||
self.fastautoonseconds = fastautoonseconds
|
||||
self.deliver = deliver
|
||||
self.originatingchannel = originatingchannel
|
||||
self.originatingto = originatingto
|
||||
self.originatingaccountid = originatingaccountid
|
||||
self.originatingthreadid = originatingthreadid
|
||||
self.attachments = attachments
|
||||
self.timeoutms = timeoutms
|
||||
self.systeminputprovenance = systeminputprovenance
|
||||
self.systemprovenancereceipt = systemprovenancereceipt
|
||||
self.suppresscommandinterpretation = suppresscommandinterpretation
|
||||
self.idempotencykey = idempotencykey
|
||||
}
|
||||
|
||||
public init(
|
||||
sessionkey: String,
|
||||
agentid: String? = nil,
|
||||
@@ -7305,23 +7347,25 @@ public struct ChatSendParams: Codable, Sendable {
|
||||
suppresscommandinterpretation: Bool?,
|
||||
idempotencykey: String)
|
||||
{
|
||||
self.sessionkey = sessionkey
|
||||
self.agentid = agentid
|
||||
self.sessionid = sessionid
|
||||
self.message = message
|
||||
self.thinking = thinking
|
||||
self.fastmode = fastmode
|
||||
self.deliver = deliver
|
||||
self.originatingchannel = originatingchannel
|
||||
self.originatingto = originatingto
|
||||
self.originatingaccountid = originatingaccountid
|
||||
self.originatingthreadid = originatingthreadid
|
||||
self.attachments = attachments
|
||||
self.timeoutms = timeoutms
|
||||
self.systeminputprovenance = systeminputprovenance
|
||||
self.systemprovenancereceipt = systemprovenancereceipt
|
||||
self.suppresscommandinterpretation = suppresscommandinterpretation
|
||||
self.idempotencykey = idempotencykey
|
||||
self.init(
|
||||
sessionkey: sessionkey,
|
||||
agentid: agentid,
|
||||
sessionid: sessionid,
|
||||
message: message,
|
||||
thinking: thinking,
|
||||
fastmodevalue: fastmode.map { AnyCodable($0) },
|
||||
fastautoonseconds: nil,
|
||||
deliver: deliver,
|
||||
originatingchannel: originatingchannel,
|
||||
originatingto: originatingto,
|
||||
originatingaccountid: originatingaccountid,
|
||||
originatingthreadid: originatingthreadid,
|
||||
attachments: attachments,
|
||||
timeoutms: timeoutms,
|
||||
systeminputprovenance: systeminputprovenance,
|
||||
systemprovenancereceipt: systemprovenancereceipt,
|
||||
suppresscommandinterpretation: suppresscommandinterpretation,
|
||||
idempotencykey: idempotencykey)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
@@ -7330,7 +7374,8 @@ public struct ChatSendParams: Codable, Sendable {
|
||||
case sessionid = "sessionId"
|
||||
case message
|
||||
case thinking
|
||||
case fastmode = "fastMode"
|
||||
case fastmodevalue = "fastMode"
|
||||
case fastautoonseconds = "fastAutoOnSeconds"
|
||||
case deliver
|
||||
case originatingchannel = "originatingChannel"
|
||||
case originatingto = "originatingTo"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ac06b6c20a93a8543ec1bd3748ef4f7bdae5006839dd93b3fff874d0da4244aa config-baseline.json
|
||||
e7965566fdaedef445bcd562141f4f3ea1a499cf8ea5956418af7c98049bf242 config-baseline.core.json
|
||||
7ce9a1d8d4a69eab05d700241472db66f268f20c144692dc69d38ba22f06d741 config-baseline.json
|
||||
5d355e16324e0e68d6e034e135e487947f67397a99a6ff91949c6aa07645e982 config-baseline.core.json
|
||||
2d735389858305509528e74329b6f8c65d311e1471c3b4e91dc17aaab8e63a80 config-baseline.channel.json
|
||||
0039da0cf2ba2845b37db52c4cf3a0f25e367cf3d2d507c5d6f8a5e5bdfdc4d4 config-baseline.plugin.json
|
||||
d2e2114f1cd43dc894fe1a4836677b42a2a5af825537d6c4a932da832d58a590 config-baseline.plugin.json
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
6f442c09ff2fa618f6f68cc866091a713d2c730090380dd726a9845f4d0fd9bd plugin-sdk-api-baseline.json
|
||||
d6b1929a42117759a3d0908fb68866e721ee7f0840279dce905a975b461c5b67 plugin-sdk-api-baseline.jsonl
|
||||
4bbf1636d7e0b410fffa3d12a366371b2f66524f7c730fdc4de789d0f482aeb2 plugin-sdk-api-baseline.json
|
||||
c5194a29911287897eee44c3579878bda552a6a7baa135bba9c719fa2ded9ab8 plugin-sdk-api-baseline.jsonl
|
||||
|
||||
@@ -183,7 +183,7 @@ Model-selection precedence for isolated jobs is:
|
||||
3. User-selected stored cron session model override
|
||||
4. Agent/default model selection
|
||||
|
||||
Fast mode follows the resolved live selection too. If the selected model config has `params.fastMode`, isolated cron uses that by default. A stored session `fastMode` override still wins over config in either direction.
|
||||
Fast mode follows the resolved live selection too. If the selected model config has `params.fastMode`, isolated cron uses that by default. A stored session `fastMode` override still wins over config in either direction. Auto mode uses the selected model's `params.fastAutoOnSeconds` cutoff when present, defaulting to 60 seconds.
|
||||
|
||||
If an isolated run hits a live model-switch handoff, cron retries with the switched provider/model and persists that live selection for the active run before retrying. When the switch also carries a new auth profile, cron persists that auth profile override for the active run too. Retries are bounded: after the initial attempt plus 2 switch retries, cron aborts instead of looping forever.
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ Every lane uploads GitHub artifacts. When `CLAWGRIT_REPORTS_TOKEN` is configured
|
||||
|
||||
## Full Release Validation
|
||||
|
||||
`Full Release Validation` is the manual umbrella workflow for "run everything before release." It accepts a branch, tag, or full commit SHA, dispatches the manual `CI` workflow with that target, dispatches `Plugin Prerelease` for release-only plugin/package/static/Docker proof, and dispatches `OpenClaw Release Checks` for install smoke, package acceptance, cross-OS package checks, QA Lab parity, Matrix, and Telegram lanes. Stable and full profiles always include exhaustive live/E2E and Docker release-path soak coverage; the beta profile can opt in with `run_release_soak=true`. With `rerun_group=all` and `release_profile=full`, it also runs `NPM Telegram Beta E2E` against the `release-package-under-test` artifact from release checks. After publishing, pass `release_package_spec` to reuse the shipped npm package across release checks, Package Acceptance, Docker, cross-OS, and Telegram without rebuilding. Use `npm_telegram_package_spec` only when Telegram must prove a different package. The Codex plugin live package lane uses the same selected state by default: published `release_package_spec=openclaw@<tag>` derives `codex_plugin_spec=npm:@openclaw/codex@<tag>`, while SHA/artifact runs pack `extensions/codex` from the selected ref. Set `codex_plugin_spec` explicitly for custom plugin sources such as `npm:`, `npm-pack:`, or `git:` specs.
|
||||
`Full Release Validation` is the manual umbrella workflow for "run everything before release." It accepts a branch, tag, or full commit SHA, dispatches the manual `CI` workflow with that target, dispatches `Plugin Prerelease` for release-only plugin/package/static/Docker proof, and dispatches `OpenClaw Release Checks` for install smoke, package acceptance, cross-OS package checks, QA Lab parity, Matrix, and Telegram lanes. Stable and full profiles always include exhaustive live/E2E and Docker release-path soak coverage; the beta profile can opt in with `run_release_soak=true`. The canonical package Telegram E2E runs inside Package Acceptance, so a full candidate does not start a duplicate live poller. After publishing, pass `release_package_spec` to reuse the shipped npm package across release checks, Package Acceptance, Docker, cross-OS, and Telegram without rebuilding. Use `npm_telegram_package_spec` only for a focused published-package Telegram rerun. The Codex plugin live package lane uses the same selected state by default: published `release_package_spec=openclaw@<tag>` derives `codex_plugin_spec=npm:@openclaw/codex@<tag>`, while SHA/artifact runs pack `extensions/codex` from the selected ref. Set `codex_plugin_spec` explicitly for custom plugin sources such as `npm:`, `npm-pack:`, or `git:` specs.
|
||||
|
||||
See [Full release validation](/reference/full-release-validation) for the
|
||||
stage matrix, exact workflow job names, profile differences, artifacts, and
|
||||
|
||||
@@ -197,7 +197,7 @@ Isolated cron resolves the active model in this order:
|
||||
|
||||
### Fast mode
|
||||
|
||||
Isolated cron fast mode follows the resolved live model selection. Model config `params.fastMode` applies by default, but a stored session `fastMode` override still wins over config.
|
||||
Isolated cron fast mode follows the resolved live model selection. Model config `params.fastMode` applies by default, but a stored session `fastMode` override still wins over config. When the resolved mode is `auto`, the cutoff uses the selected model's `params.fastAutoOnSeconds` value, defaulting to 60 seconds.
|
||||
|
||||
### Live model switch retries
|
||||
|
||||
|
||||
@@ -1098,7 +1098,7 @@ for provider examples and precedence.
|
||||
- `skills`: optional per-agent skill allowlist. If omitted, the agent inherits `agents.defaults.skills` when set; an explicit list replaces defaults instead of merging, and `[]` means no skills.
|
||||
- `thinkingDefault`: optional per-agent default thinking level (`off | minimal | low | medium | high | xhigh | adaptive | max`). Overrides `agents.defaults.thinkingDefault` for this agent when no per-message or session override is set. The selected provider/model profile controls which values are valid; for Google Gemini, `adaptive` keeps provider-owned dynamic thinking (`thinkingLevel` omitted on Gemini 3/3.1, `thinkingBudget: -1` on Gemini 2.5).
|
||||
- `reasoningDefault`: optional per-agent default reasoning visibility (`on | off | stream`). Overrides `agents.defaults.reasoningDefault` for this agent when no per-message or session reasoning override is set.
|
||||
- `fastModeDefault`: optional per-agent default for fast mode (`true | false`). Applies when no per-message or session fast-mode override is set.
|
||||
- `fastModeDefault`: optional per-agent default for fast mode (`"auto" | true | false`). Applies when no per-message or session fast-mode override is set.
|
||||
- `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.
|
||||
|
||||
@@ -445,6 +445,7 @@ enumeration of `src/gateway/server-methods/*.ts`.
|
||||
- `sessions.get` returns the full stored session row.
|
||||
- Chat execution still uses `chat.history`, `chat.send`, `chat.abort`, and `chat.inject`. `chat.history` is display-normalized for UI clients: inline directive tags are stripped from visible text, plain-text tool-call XML payloads (including `<tool_call>...</tool_call>`, `<function_call>...</function_call>`, `<tool_calls>...</tool_calls>`, `<function_calls>...</function_calls>`, and truncated tool-call blocks) and leaked ASCII/full-width model control tokens are stripped, pure silent-token assistant rows such as exact `NO_REPLY` / `no_reply` are omitted, and oversized rows can be replaced with placeholders.
|
||||
- `chat.message.get` is the additive bounded full-message reader for a single visible transcript entry. Clients pass `sessionKey`, optional `agentId` when the session selection is agent-scoped, plus a transcript `messageId` previously surfaced through `chat.history`, and the Gateway returns the same display-normalized projection without the lightweight history truncation cap when the stored entry is still available and not oversized.
|
||||
- `chat.send` accepts one-turn `fastMode: "auto"` to use fast mode for model calls started before the auto cutoff, then start later retry, fallback, tool-result, or continuation calls without fast mode. The cutoff defaults to 60 seconds and can be configured per model with `agents.defaults.models["<provider>/<model>"].params.fastAutoOnSeconds`. A `chat.send` caller can pass one-turn `fastAutoOnSeconds` to override the cutoff for that request.
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ troubleshooting, see the main [FAQ](/help/faq).
|
||||
|
||||
- **Per session:** send `/fast on` while the session is using `openai/gpt-5.5`.
|
||||
- **Per model default:** set `agents.defaults.models["openai/gpt-5.5"].params.fastMode` to `true`.
|
||||
- **Automatic cutoff:** use `/fast auto` or `params.fastMode: "auto"` to start new model calls fast until the auto cutoff, then start later retry, fallback, tool-result, or continuation calls without fast mode. The cutoff defaults to 60 seconds; set `params.fastAutoOnSeconds` on the active model to change it.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -184,7 +185,8 @@ troubleshooting, see the main [FAQ](/help/faq).
|
||||
models: {
|
||||
"openai/gpt-5.5": {
|
||||
params: {
|
||||
fastMode: true,
|
||||
fastMode: "auto",
|
||||
fastAutoOnSeconds: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -193,7 +195,7 @@ troubleshooting, see the main [FAQ](/help/faq).
|
||||
}
|
||||
```
|
||||
|
||||
For OpenAI, fast mode maps to `service_tier = "priority"` on supported native Responses requests. Session `/fast` overrides beat config defaults.
|
||||
For OpenAI, fast mode maps to `service_tier = "priority"` on supported native Responses requests. Session `/fast` overrides beat config defaults. Codex app-server turns can only receive the tier at turn start, so `auto` applies on the next OpenClaw-started model turn rather than inside one already-running app-server turn.
|
||||
|
||||
See [Thinking and fast mode](/tools/thinking) and [OpenAI fast mode](/providers/openai#fast-mode).
|
||||
|
||||
|
||||
@@ -291,7 +291,7 @@ Each entry lists the package, distribution route, and description.
|
||||
|
||||
- **[slack](/plugins/reference/slack)** (`@openclaw/slack`) - npm; ClawHub. OpenClaw Slack channel plugin for channels, DMs, commands, and app events.
|
||||
|
||||
- **[stepfun](/plugins/reference/stepfun)** (`@openclaw/stepfun-provider`) - npm. Adds StepFun, StepFun Plan model provider support to OpenClaw.
|
||||
- **[stepfun](/plugins/reference/stepfun)** (`@openclaw/stepfun-provider`) - npm; ClawHub: `clawhub:@openclaw/stepfun-provider`. Adds StepFun, StepFun Plan model provider support to OpenClaw.
|
||||
|
||||
- **[synology-chat](/plugins/reference/synology-chat)** (`@openclaw/synology-chat`) - npm; ClawHub. Synology Chat channel plugin for OpenClaw channels and direct messages.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Adds StepFun, StepFun Plan model provider support to OpenClaw.
|
||||
## Distribution
|
||||
|
||||
- Package: `@openclaw/stepfun-provider`
|
||||
- Install route: npm
|
||||
- Install route: npm; ClawHub: `clawhub:@openclaw/stepfun-provider`
|
||||
|
||||
## Surface
|
||||
|
||||
|
||||
@@ -915,17 +915,17 @@ the Server-side compaction accordion below.
|
||||
<Accordion title="Fast mode">
|
||||
OpenClaw exposes a shared fast-mode toggle for `openai/*`:
|
||||
|
||||
- **Chat/UI:** `/fast status|on|off`
|
||||
- **Chat/UI:** `/fast status|auto|on|off`
|
||||
- **Config:** `agents.defaults.models["<provider>/<model>"].params.fastMode`
|
||||
|
||||
When enabled, OpenClaw maps fast mode to OpenAI priority processing (`service_tier = "priority"`). Existing `service_tier` values are preserved, and fast mode does not rewrite `reasoning` or `text.verbosity`.
|
||||
When enabled, OpenClaw maps fast mode to OpenAI priority processing (`service_tier = "priority"`). Existing `service_tier` values are preserved, and fast mode does not rewrite `reasoning` or `text.verbosity`. `fastMode: "auto"` starts new model calls fast until the auto cutoff, then starts later retry, fallback, tool-result, or continuation calls without fast mode. The cutoff defaults to 60 seconds; set `params.fastAutoOnSeconds` on the active model to change it.
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.5": { params: { fastMode: true } },
|
||||
"openai/gpt-5.5": { params: { fastMode: "auto", fastAutoOnSeconds: 30 } },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -28,8 +28,10 @@ The provider includes:
|
||||
| ------------------------------- | --------------------- |
|
||||
| `opencode-go/glm-5` | GLM-5 |
|
||||
| `opencode-go/glm-5.1` | GLM-5.1 |
|
||||
| `opencode-go/glm-5.2` | GLM-5.2 |
|
||||
| `opencode-go/kimi-k2.5` | Kimi K2.5 |
|
||||
| `opencode-go/kimi-k2.6` | Kimi K2.6 (3x limits) |
|
||||
| `opencode-go/kimi-k2.7-code` | Kimi K2.7 Code |
|
||||
| `opencode-go/deepseek-v4-pro` | DeepSeek V4 Pro |
|
||||
| `opencode-go/deepseek-v4-flash` | DeepSeek V4 Flash |
|
||||
| `opencode-go/mimo-v2-omni` | MiMo V2 Omni |
|
||||
@@ -39,6 +41,8 @@ The provider includes:
|
||||
| `opencode-go/qwen3.5-plus` | Qwen3.5 Plus |
|
||||
| `opencode-go/qwen3.6-plus` | Qwen3.6 Plus |
|
||||
|
||||
GLM-5.2 uses a 1M-token context window and supports up to 131K output tokens.
|
||||
|
||||
## Getting started
|
||||
|
||||
<Tabs>
|
||||
|
||||
@@ -126,6 +126,11 @@ The manifest-backed catalog currently includes:
|
||||
GLM models are available as `zai/<model>` (example: `zai/glm-5`).
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
GLM-5.2 supports `off`, `low`, `high`, and `max` thinking levels. OpenClaw maps
|
||||
`low` and `high` to Z.AI high reasoning effort, and `max` to max effort.
|
||||
</Tip>
|
||||
|
||||
<Note>
|
||||
Coding Plan setup defaults to `zai/glm-5.2`; general API setup keeps
|
||||
`zai/glm-5.1`. Endpoint auto-detection falls back to `glm-5.1` or `glm-4.7`
|
||||
|
||||
@@ -228,9 +228,9 @@ release state.
|
||||
`OpenClaw Release Checks` for install smoke, package acceptance, cross-OS
|
||||
package checks, QA Lab parity, Matrix, and Telegram lanes. Stable and full
|
||||
runs always include exhaustive live/E2E and Docker release-path soak;
|
||||
`run_release_soak=true` is retained for an explicit beta soak. With
|
||||
`release_profile=full` and `rerun_group=all`, it also runs package Telegram
|
||||
E2E against the `release-package-under-test` artifact from release checks.
|
||||
`run_release_soak=true` is retained for an explicit beta soak. Package
|
||||
Acceptance provides the canonical package Telegram E2E during candidate
|
||||
validation, avoiding a second concurrent live poller.
|
||||
Provide `release_package_spec` after publishing a beta to reuse the shipped
|
||||
npm package across release checks, Package Acceptance, and package Telegram
|
||||
E2E without rebuilding the release tarball. Provide
|
||||
@@ -460,20 +460,16 @@ gh workflow run full-release-validation.yml \
|
||||
```
|
||||
|
||||
The workflow resolves the target ref, dispatches manual `CI` with
|
||||
`target_ref=<release-ref>`, dispatches `OpenClaw Release Checks`, prepares a
|
||||
parent `release-package-under-test` artifact for package-facing checks, and
|
||||
dispatches standalone package Telegram E2E when `release_profile=full` with
|
||||
`rerun_group=all` or when `release_package_spec` or
|
||||
`npm_telegram_package_spec` is set. `OpenClaw Release
|
||||
Checks` then fans out install smoke, cross-OS release checks, live/E2E Docker
|
||||
release-path coverage when soak is enabled, Package Acceptance with Telegram
|
||||
package QA, QA Lab parity, live Matrix, and live Telegram. A full/all run is
|
||||
only acceptable when the `Full Release Validation` summary shows `normal_ci`,
|
||||
`plugin_prerelease`, and `release_checks` as successful, unless a focused rerun
|
||||
intentionally skipped the separate `Plugin Prerelease` child. In full/all mode,
|
||||
the `npm_telegram` child must also be successful; outside full/all it is skipped
|
||||
unless a published `release_package_spec` or `npm_telegram_package_spec` was
|
||||
provided. The final
|
||||
`target_ref=<release-ref>`, then dispatches `OpenClaw Release Checks`.
|
||||
`OpenClaw Release Checks` fans out install smoke, cross-OS release checks,
|
||||
live/E2E Docker release-path coverage when soak is enabled, Package Acceptance
|
||||
with the canonical Telegram package E2E, QA Lab parity, live Matrix, and live
|
||||
Telegram. A full/all run is only acceptable when the `Full Release Validation`
|
||||
summary shows `normal_ci`, `plugin_prerelease`, and `release_checks` as
|
||||
successful, unless a focused rerun intentionally skipped the separate `Plugin
|
||||
Prerelease` child. Use the standalone `npm-telegram` child only for a focused
|
||||
published-package rerun with `release_package_spec` or
|
||||
`npm_telegram_package_spec`. The final
|
||||
verifier summary includes slowest-job tables for each child run, so the release
|
||||
manager can see the current critical path without downloading logs.
|
||||
See [Full release validation](/reference/full-release-validation) for the
|
||||
@@ -558,8 +554,8 @@ runs only the release-only plugin child, `release-checks` runs every release
|
||||
box, and the narrower release groups are `install-smoke`, `cross-os`,
|
||||
`live-e2e`, `package`, `qa`, `qa-parity`, `qa-live`, and `npm-telegram`.
|
||||
Focused `npm-telegram` reruns require `release_package_spec` or
|
||||
`npm_telegram_package_spec`; full/all runs with `release_profile=full` use the
|
||||
release-checks package artifact. Focused
|
||||
`npm_telegram_package_spec`; full/all runs use the canonical package Telegram
|
||||
E2E inside Package Acceptance. Focused
|
||||
cross-OS reruns can add `cross_os_suite_filter=windows/packaged-upgrade` or
|
||||
another OS/suite filter. QA release-check failures block normal release
|
||||
validation, including required OpenClaw dynamic tool drift in the standard tier.
|
||||
|
||||
@@ -53,8 +53,7 @@ that plugin, then runs Codex CLI preflight and same-session OpenAI agent turns.
|
||||
| Vitest and normal CI | **Job:** `Run normal full CI`<br />**Child workflow:** `CI`<br />**Proves:** manual full CI graph against the target ref, including Linux Node lanes, bundled plugin shards, plugin and channel contract shards, Node 22 compatibility, `check-*`, `check-additional-*`, built-artifact smoke checks, docs checks, Python skills, Windows, macOS, Control UI i18n, and Android via the umbrella.<br />**Rerun:** `rerun_group=ci`. |
|
||||
| Plugin prerelease | **Job:** `Run plugin prerelease validation`<br />**Child workflow:** `Plugin Prerelease`<br />**Proves:** release-only plugin static checks, agentic plugin coverage, full extension batch shards, plugin prerelease Docker lanes, and a non-blocking `plugin-inspector-advisory` artifact for compatibility triage.<br />**Rerun:** `rerun_group=plugin-prerelease`. |
|
||||
| Release checks | **Job:** `Run release/live/Docker/QA validation`<br />**Child workflow:** `OpenClaw Release Checks`<br />**Proves:** install smoke, cross-OS package checks, Package Acceptance, QA Lab parity, live Matrix, and live Telegram. Stable and full profiles also run exhaustive live/E2E suites and Docker release-path chunks; beta can opt in with `run_release_soak=true`.<br />**Rerun:** `rerun_group=release-checks` or a narrower release-checks handle. |
|
||||
| Package artifact | **Job:** `Prepare release package artifact`<br />**Child workflow:** none<br />**Proves:** creates the parent `release-package-under-test` tarball early enough for package-facing checks that do not need to wait for `OpenClaw Release Checks`.<br />**Rerun:** rerun the umbrella or provide `release_package_spec` for published-package reruns. |
|
||||
| Package Telegram | **Job:** `Run package Telegram E2E`<br />**Child workflow:** `NPM Telegram Beta E2E`<br />**Proves:** parent-artifact-backed Telegram package proof for `rerun_group=all` with `release_profile=full`, or published-package Telegram proof when `release_package_spec` or `npm_telegram_package_spec` is set.<br />**Rerun:** `rerun_group=npm-telegram` with `release_package_spec` or `npm_telegram_package_spec`. |
|
||||
| Package Telegram | **Job:** `Run package Telegram E2E`<br />**Child workflow:** `NPM Telegram Beta E2E`<br />**Proves:** a focused published-package Telegram E2E when `release_package_spec` or `npm_telegram_package_spec` is set. Full candidate validation uses the canonical Package Acceptance Telegram E2E instead.<br />**Rerun:** `rerun_group=npm-telegram` with `release_package_spec` or `npm_telegram_package_spec`. |
|
||||
| Umbrella verifier | **Job:** `Verify full validation`<br />**Child workflow:** none<br />**Proves:** re-checks recorded child run conclusions and appends slowest-job tables from child workflows.<br />**Rerun:** rerun only this job after rerunning a failed child to green. |
|
||||
|
||||
For `ref=main` and `rerun_group=all`, a newer umbrella supersedes an older one.
|
||||
@@ -76,7 +75,7 @@ or Docker-facing stages need it.
|
||||
| Cross-OS | **Job:** `cross_os_release_checks`<br />**Backing workflow:** `OpenClaw Cross-OS Release Checks (Reusable)`<br />**Tests:** fresh and upgrade lanes on Linux, Windows, and macOS for the selected provider and mode, using the candidate tarball plus a baseline package.<br />**Rerun:** `rerun_group=cross-os`. |
|
||||
| Repo and live E2E | **Job:** `Run repo/live E2E validation`<br />**Backing workflow:** `OpenClaw Live And E2E Checks (Reusable)`<br />**Tests:** repository E2E, live cache, OpenAI websocket streaming, native live provider and plugin shards, and Docker-backed live model/backend/gateway harnesses selected by `release_profile`.<br />**Runs:** `run_release_soak=true`, `release_profile=full`, or focused `rerun_group=live-e2e`.<br />**Rerun:** `rerun_group=live-e2e`, optionally with `live_suite_filter`. |
|
||||
| Docker release path | **Job:** `Run Docker release-path validation`<br />**Backing workflow:** `OpenClaw Live And E2E Checks (Reusable)`<br />**Tests:** release-path Docker chunks against the shared package artifact.<br />**Runs:** `run_release_soak=true`, `release_profile=full`, or focused `rerun_group=live-e2e`.<br />**Rerun:** `rerun_group=live-e2e`. |
|
||||
| Package Acceptance | **Job:** `Run package acceptance`<br />**Backing workflow:** `Package Acceptance`<br />**Tests:** offline plugin package fixtures, plugin update, mock-OpenAI Telegram package acceptance, and published-upgrade survivor checks against the same tarball. Blocking release checks use the default latest published baseline; soak checks expand to every stable npm release at or after `2026.4.23` plus reported-issue fixtures.<br />**Rerun:** `rerun_group=package`. |
|
||||
| Package Acceptance | **Job:** `Run package acceptance`<br />**Backing workflow:** `Package Acceptance`<br />**Tests:** offline plugin package fixtures, plugin update, the canonical mock-OpenAI Telegram package E2E, and published-upgrade survivor checks against the same tarball. Blocking release checks use the default latest published baseline; soak checks expand to every stable npm release at or after `2026.4.23` plus reported-issue fixtures.<br />**Rerun:** `rerun_group=package`. |
|
||||
| QA parity | **Job:** `Run QA Lab parity lane` and `Run QA Lab parity report`<br />**Backing workflow:** direct jobs<br />**Tests:** candidate and baseline agentic parity packs, then the parity report.<br />**Rerun:** `rerun_group=qa-parity` or `rerun_group=qa`. |
|
||||
| QA live Matrix | **Job:** `Run QA Lab live Matrix lane`<br />**Backing workflow:** direct job<br />**Tests:** fast live Matrix QA profile in the `qa-live-shared` environment.<br />**Rerun:** `rerun_group=qa-live` or `rerun_group=qa`. |
|
||||
| QA live Telegram | **Job:** `Run QA Lab live Telegram lane`<br />**Backing workflow:** direct job<br />**Tests:** live Telegram QA with Convex CI credential leases.<br />**Rerun:** `rerun_group=qa-live` or `rerun_group=qa`. |
|
||||
@@ -107,9 +106,9 @@ commands with package artifact and image reuse inputs when available.
|
||||
It does not remove normal full CI, Plugin Prerelease, install smoke, package
|
||||
acceptance, or QA Lab. Stable and full profiles always run exhaustive repo/live
|
||||
E2E and Docker release-path soak coverage. The beta profile can opt in with
|
||||
`run_release_soak=true`. The full profile also makes the umbrella run package
|
||||
Telegram E2E against the parent release package artifact when `rerun_group=all`,
|
||||
so a full pre-publish candidate does not silently skip that Telegram package lane.
|
||||
`run_release_soak=true`. Package Acceptance supplies the canonical package
|
||||
Telegram E2E for every full candidate, so the umbrella does not duplicate that
|
||||
live poller.
|
||||
|
||||
| Profile | Intended use | Included live/provider coverage |
|
||||
| --------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -189,7 +188,7 @@ workflow first, then rerun the smallest matching handle above.
|
||||
|
||||
Useful artifacts:
|
||||
|
||||
- `release-package-under-test` from the Full Release Validation parent and `OpenClaw Release Checks`
|
||||
- `release-package-under-test` from `OpenClaw Release Checks`
|
||||
- Docker release-path artifacts under `.artifacts/docker-tests/`
|
||||
- Package Acceptance `package-under-test` and Docker acceptance artifacts
|
||||
- Cross-OS release-check artifacts for each OS and suite
|
||||
|
||||
@@ -198,7 +198,7 @@ plugins.
|
||||
| `/think <level\|default>` | Set the thinking level or clear the session override. Aliases: `/thinking`, `/t` |
|
||||
| `/verbose on\|off\|full` | Toggle verbose output. Alias: `/v` |
|
||||
| `/trace on\|off` | Toggle plugin trace output for the current session |
|
||||
| `/fast [status\|on\|off\|default]` | Show, set, or clear fast mode |
|
||||
| `/fast [status\|auto\|on\|off\|default]` | Show, set, or clear fast mode |
|
||||
| `/reasoning [on\|off\|stream]` | Toggle reasoning visibility. Alias: `/reason` |
|
||||
| `/elevated [on\|off\|ask\|full]` | Toggle elevated mode. Alias: `/elev` |
|
||||
| `/exec host=<auto\|sandbox\|gateway\|node> security=<deny\|allowlist\|full> ask=<off\|on-miss\|always> node=<id>` | Show or set exec defaults |
|
||||
@@ -211,7 +211,7 @@ plugins.
|
||||
<Accordion title="verbose / trace / fast / reasoning safety">
|
||||
- `/verbose` is for debugging — keep it **off** in normal use.
|
||||
- `/trace` reveals only plugin-owned trace/debug lines; normal verbose chatter stays off.
|
||||
- `/fast on|off` persists a session override; use the Sessions UI `inherit` option to clear it.
|
||||
- `/fast auto|on|off` persists a session override; use the Sessions UI `inherit` option to clear it.
|
||||
- `/fast` is provider-specific: OpenAI/Codex map it to `service_tier=priority`; direct Anthropic requests map it to `service_tier=auto` or `standard_only`.
|
||||
- `/reasoning`, `/verbose`, and `/trace` are risky in group settings — they may reveal internal reasoning or plugin diagnostics. Keep them off in group chats.
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ title: "Thinking levels"
|
||||
- Stale configured OpenRouter Hunter Alpha refs skip proxy reasoning injection because that retired route could return final answer text through reasoning fields.
|
||||
- Google Gemini maps `/think adaptive` to Gemini's provider-owned dynamic thinking. Gemini 3 requests omit a fixed `thinkingLevel`, while Gemini 2.5 requests send `thinkingBudget: -1`; fixed levels still map to the closest Gemini `thinkingLevel` or budget for that model family.
|
||||
- MiniMax M2.x (`minimax/MiniMax-M2*`) on the Anthropic-compatible streaming path defaults to `thinking: { type: "disabled" }` unless you explicitly set thinking in model params or request params. This avoids leaked `reasoning_content` deltas from M2.x's non-native Anthropic stream format. MiniMax-M3 (and M3.x) is exempt: M3 emits proper Anthropic thinking blocks and returns empty content when thinking is disabled, so OpenClaw keeps M3 on the provider's omitted/adaptive thinking path.
|
||||
- Z.AI (`zai/*`) only supports binary thinking (`on`/`off`). Any non-`off` level is treated as `on` (mapped to `low`).
|
||||
- Z.AI (`zai/*`) is binary (`on`/`off`) for most GLM models. GLM-5.2 is the exception: it exposes `/think off|low|high|max`, maps `low` and `high` to Z.AI `reasoning_effort: "high"`, and maps `max` to `reasoning_effort: "max"`.
|
||||
- Moonshot Kimi K2.7 Code (`moonshot/kimi-k2.7-code`) always thinks. Its profile exposes only `on`, and OpenClaw omits the outbound `thinking` field as required by Moonshot. Other `moonshot/*` models map `/think off` to `thinking: { type: "disabled" }` and any non-`off` level to `thinking: { type: "enabled" }`. When thinking is enabled, Moonshot only accepts `tool_choice` `auto|none`; OpenClaw normalizes incompatible values to `auto`.
|
||||
|
||||
## Resolution order
|
||||
@@ -60,21 +60,22 @@ title: "Thinking levels"
|
||||
|
||||
## Fast mode (/fast)
|
||||
|
||||
- Levels: `on|off|default`.
|
||||
- Directive-only message toggles a session fast-mode override and replies `Fast mode enabled.` / `Fast mode disabled.`. Use `/fast default` to clear the session override and inherit the configured default; aliases include `inherit`, `clear`, `reset`, and `unpin`.
|
||||
- Levels: `auto|on|off|default`.
|
||||
- Directive-only message toggles a session fast-mode override and replies `Fast mode set to auto.`, `Fast mode enabled.`, or `Fast mode disabled.`. Use `/fast default` to clear the session override and inherit the configured default; aliases include `inherit`, `clear`, `reset`, and `unpin`.
|
||||
- Send `/fast` (or `/fast status`) with no mode to see the current effective fast-mode state.
|
||||
- OpenClaw resolves fast mode in this order:
|
||||
1. Inline/directive-only `/fast on|off` override (`/fast default` clears this layer)
|
||||
1. Inline/directive-only `/fast auto|on|off` override (`/fast default` clears this layer)
|
||||
2. Session override
|
||||
3. Per-agent default (`agents.list[].fastModeDefault`)
|
||||
4. Per-model config: `agents.defaults.models["<provider>/<model>"].params.fastMode`
|
||||
5. Fallback: `off`
|
||||
- `auto` keeps the session/config mode as auto but resolves each new model call independently. Calls that start before the auto cutoff have fast mode enabled; later retry, fallback, tool-result, or continuation calls start with fast mode disabled. The cutoff defaults to 60 seconds; set `agents.defaults.models["<provider>/<model>"].params.fastAutoOnSeconds` on the active model to change it.
|
||||
- For `openai/*`, fast mode maps to OpenAI priority processing by sending `service_tier=priority` on supported Responses requests.
|
||||
- For Codex-backed `openai/*` models, fast mode sends the same `service_tier=priority` flag on Codex Responses. OpenClaw keeps one shared `/fast` toggle across both auth paths.
|
||||
- For Codex-backed `openai/*` / `openai-codex/*` models, fast mode sends the same `service_tier=priority` flag on Codex Responses. Native Codex app-server turns receive the tier only on `turn/start` or thread start/resume, so `auto` cannot retier one already-running app-server turn; it applies to the next model turn OpenClaw starts.
|
||||
- For direct public `anthropic/*` requests, including OAuth-authenticated traffic sent to `api.anthropic.com`, fast mode maps to Anthropic service tiers: `/fast on` sets `service_tier=auto`, `/fast off` sets `service_tier=standard_only`.
|
||||
- For `minimax/*` on the Anthropic-compatible path, `/fast on` (or `params.fastMode: true`) rewrites `MiniMax-M2.7` to `MiniMax-M2.7-highspeed`.
|
||||
- Explicit Anthropic `serviceTier` / `service_tier` model params override the fast-mode default when both are set. OpenClaw still skips Anthropic service-tier injection for non-Anthropic proxy base URLs.
|
||||
- `/status` shows `Fast` only when fast mode is enabled.
|
||||
- `/status` shows `Fast` when fast mode is enabled and `Fast:auto` when the configured mode is auto.
|
||||
|
||||
## Verbose directives (/verbose or /v)
|
||||
|
||||
|
||||
4
extensions/acpx/npm-shrinkwrap.json
generated
4
extensions/acpx/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/acpx",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/acpx",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/claude-agent-acp": "0.39.0",
|
||||
"@zed-industries/codex-acp": "0.15.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/acpx",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -26,10 +26,10 @@
|
||||
"minHostVersion": ">=2026.4.25"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"staticAssets": [
|
||||
{
|
||||
"source": "./src/runtime-internals/mcp-proxy.mjs",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/admin-http-rpc",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw admin HTTP RPC endpoint",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/alibaba-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw Alibaba Model Studio video provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "0.100.1",
|
||||
"@aws/bedrock-token-generator": "1.1.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -24,10 +24,10 @@
|
||||
"minHostVersion": ">=2026.5.12-beta.1"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
4
extensions/amazon-bedrock/npm-shrinkwrap.json
generated
4
extensions/amazon-bedrock/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/amazon-bedrock-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-bedrock": "3.1056.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "3.1056.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -28,10 +28,10 @@
|
||||
"minHostVersion": ">=2026.5.12-beta.1"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
4
extensions/anthropic-vertex/npm-shrinkwrap.json
generated
4
extensions/anthropic-vertex/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/vertex-sdk": "0.16.1"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -23,10 +23,10 @@
|
||||
"minHostVersion": ">=2026.5.12-beta.1"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw Anthropic provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
createAnthropicServiceTierWrapper,
|
||||
createAnthropicThinkingPrefillWrapper,
|
||||
resolveAnthropicBetas,
|
||||
resolveAnthropicFastMode,
|
||||
wrapAnthropicProviderStream,
|
||||
} from "./stream-wrappers.js";
|
||||
|
||||
@@ -172,6 +173,10 @@ describe("anthropic stream wrappers", () => {
|
||||
expect(captured.headers?.["anthropic-beta"]).toContain(OAUTH_BETA);
|
||||
expect(captured.headers?.["anthropic-beta"]).not.toContain(CONTEXT_1M_BETA);
|
||||
});
|
||||
|
||||
it("ignores unresolved auto fast mode at the provider boundary", () => {
|
||||
expect(resolveAnthropicFastMode({ fastMode: "auto" })).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("createAnthropicThinkingPrefillWrapper", () => {
|
||||
@@ -282,6 +287,19 @@ describe("Anthropic service_tier payload wrappers", () => {
|
||||
expect(payload?.service_tier).toBe("standard_only");
|
||||
});
|
||||
|
||||
it("fast mode resolves dynamic service_tier for each stream call", () => {
|
||||
let enabled = true;
|
||||
const first = runPayloadWrapper({ apiKey: "sk-ant-api03-test-key" }, (base) =>
|
||||
createAnthropicFastModeWrapper(base, () => enabled),
|
||||
);
|
||||
enabled = false;
|
||||
const second = runPayloadWrapper({ apiKey: "sk-ant-api03-test-key" }, (base) =>
|
||||
createAnthropicFastModeWrapper(base, () => enabled),
|
||||
);
|
||||
expect(first?.service_tier).toBe("auto");
|
||||
expect(second?.service_tier).toBe("standard_only");
|
||||
});
|
||||
|
||||
it("explicit service tier injects service_tier=standard_only for regular API keys", () => {
|
||||
const payload = serviceTierWrapperCases[1].run({
|
||||
apiKey: "sk-ant-api03-test-key",
|
||||
|
||||
@@ -44,6 +44,7 @@ const OPENCLAW_OAUTH_ANTHROPIC_BETAS = [
|
||||
] as const;
|
||||
|
||||
type AnthropicServiceTier = "auto" | "standard_only";
|
||||
type DynamicFastMode = boolean | (() => boolean | undefined);
|
||||
|
||||
function isAnthropic1MModel(modelId: string): boolean {
|
||||
const normalized = normalizeLowercaseStringOrEmpty(modelId);
|
||||
@@ -157,9 +158,20 @@ export function createAnthropicBetaHeadersWrapper(
|
||||
/** Wrap a stream function with the Anthropic fast-mode service tier. */
|
||||
export function createAnthropicFastModeWrapper(
|
||||
baseStreamFn: StreamFn | undefined,
|
||||
enabled: boolean,
|
||||
enabled: DynamicFastMode,
|
||||
): StreamFn {
|
||||
return createAnthropicServiceTierWrapper(baseStreamFn, resolveAnthropicFastServiceTier(enabled));
|
||||
const underlying = baseStreamFn ?? streamSimple;
|
||||
return (model, context, options) => {
|
||||
const resolved = typeof enabled === "function" ? enabled() : enabled;
|
||||
if (resolved === undefined) {
|
||||
return underlying(model, context, options);
|
||||
}
|
||||
return createAnthropicServiceTierWrapper(underlying, resolveAnthropicFastServiceTier(resolved))(
|
||||
model,
|
||||
context,
|
||||
options,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/** Wrap a stream function with an explicit Anthropic service tier when allowed. */
|
||||
@@ -204,9 +216,12 @@ export function createAnthropicThinkingPrefillWrapper(
|
||||
export function resolveAnthropicFastMode(
|
||||
extraParams: Record<string, unknown> | undefined,
|
||||
): boolean | undefined {
|
||||
return normalizeFastMode(
|
||||
(extraParams?.fastMode ?? extraParams?.fast_mode) as string | boolean | null | undefined,
|
||||
);
|
||||
const raw = extraParams?.fastMode ?? extraParams?.fast_mode;
|
||||
const fastMode =
|
||||
typeof raw === "function"
|
||||
? normalizeFastMode((raw as () => unknown)() as string | boolean | null | undefined)
|
||||
: normalizeFastMode(raw as string | boolean | null | undefined);
|
||||
return fastMode === "auto" ? undefined : fastMode;
|
||||
}
|
||||
|
||||
/** Resolve Anthropic service tier from model extra params. */
|
||||
@@ -232,7 +247,9 @@ export function wrapAnthropicProviderStream(
|
||||
hasConfiguredAnthropicBeta(ctx.extraParams) ||
|
||||
(ctx.extraParams?.context1m === true && isAnthropic1MModel(ctx.modelId));
|
||||
const serviceTier = resolveAnthropicServiceTier(ctx.extraParams);
|
||||
const fastMode = resolveAnthropicFastMode(ctx.extraParams);
|
||||
const hasFastModeParam =
|
||||
ctx.extraParams !== undefined &&
|
||||
(Object.hasOwn(ctx.extraParams, "fastMode") || Object.hasOwn(ctx.extraParams, "fast_mode"));
|
||||
return composeProviderStreamWrappers(
|
||||
ctx.streamFn,
|
||||
needsAnthropicBetaWrapper
|
||||
@@ -241,8 +258,9 @@ export function wrapAnthropicProviderStream(
|
||||
serviceTier
|
||||
? (streamFn) => createAnthropicServiceTierWrapper(streamFn, serviceTier)
|
||||
: undefined,
|
||||
fastMode !== undefined
|
||||
? (streamFn) => createAnthropicFastModeWrapper(streamFn, fastMode)
|
||||
hasFastModeParam
|
||||
? (streamFn) =>
|
||||
createAnthropicFastModeWrapper(streamFn, () => resolveAnthropicFastMode(ctx.extraParams))
|
||||
: undefined,
|
||||
(streamFn) => createAnthropicThinkingPrefillWrapper(streamFn),
|
||||
);
|
||||
|
||||
4
extensions/arcee/npm-shrinkwrap.json
generated
4
extensions/arcee/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/arcee-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/arcee-provider",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/arcee-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Arcee provider plugin.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"minHostVersion": ">=2026.6.8"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/azure-speech",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw Azure Speech plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/bonjour",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Bonjour/mDNS gateway discovery",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
4
extensions/brave/npm-shrinkwrap.json
generated
4
extensions/brave/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/brave-plugin",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/brave-plugin",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/brave-plugin",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Brave Search provider plugin for web search.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"allowInvalidConfigRecovery": true
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8"
|
||||
"openclawVersion": "2026.6.10-beta.2"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/browser-plugin",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw browser tool plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/byteplus-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw BytePlus provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/canvas-plugin",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw Canvas plugin",
|
||||
"type": "module",
|
||||
|
||||
4
extensions/cerebras/npm-shrinkwrap.json
generated
4
extensions/cerebras/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/cerebras-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/cerebras-provider",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/cerebras-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Cerebras provider plugin.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"minHostVersion": ">=2026.6.8"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
4
extensions/chutes/npm-shrinkwrap.json
generated
4
extensions/chutes/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/chutes-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/chutes-provider",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/chutes-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Chutes.ai provider plugin.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"minHostVersion": ">=2026.6.8"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/clickclack",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw ClickClack channel plugin",
|
||||
"type": "module",
|
||||
@@ -18,7 +18,7 @@
|
||||
"openclaw": "2026.5.28"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.6.8"
|
||||
"openclaw": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Cloudflare AI Gateway provider plugin.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"minHostVersion": ">=2026.6.8"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/codex-supervisor",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw Codex app-server fleet supervision plugin.",
|
||||
"type": "module",
|
||||
|
||||
4
extensions/codex/npm-shrinkwrap.json
generated
4
extensions/codex/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/codex",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/codex",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"dependencies": {
|
||||
"@openai/codex": "0.139.0",
|
||||
"typebox": "1.1.39",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/codex",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -34,10 +34,10 @@
|
||||
]
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8"
|
||||
"openclawVersion": "2026.6.10-beta.2"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -189,7 +189,23 @@ export function isRawToolOutputCompletionNotification(
|
||||
return false;
|
||||
}
|
||||
const item = isJsonObject(notification.params.item) ? notification.params.item : undefined;
|
||||
return item ? readString(item, "type") === "custom_tool_call_output" : false;
|
||||
switch (item ? readString(item, "type") : undefined) {
|
||||
case "custom_tool_call_output":
|
||||
case "function_call_output":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isRawFunctionToolOutputCompletionNotification(
|
||||
notification: CodexServerNotification,
|
||||
): boolean {
|
||||
if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) {
|
||||
return false;
|
||||
}
|
||||
const item = isJsonObject(notification.params.item) ? notification.params.item : undefined;
|
||||
return item ? readString(item, "type") === "function_call_output" : false;
|
||||
}
|
||||
|
||||
/** Returns true for progress on Codex-native tool item types. */
|
||||
|
||||
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
abortAgentHarnessRun,
|
||||
abortAndDrainAgentHarnessRun,
|
||||
type EmbeddedRunAttemptParams,
|
||||
} from "openclaw/plugin-sdk/agent-harness";
|
||||
import { AUTH_PROFILE_RUNTIME_CONTRACT } from "openclaw/plugin-sdk/agent-runtime-test-contracts";
|
||||
@@ -65,10 +65,9 @@ const DISABLED_CODEX_WEB_SEARCH_THREAD_CONFIG_FINGERPRINT = JSON.stringify({
|
||||
"features.standalone_web_search": false,
|
||||
web_search: "disabled",
|
||||
});
|
||||
const APP_SERVER_START_WAIT = { interval: 1, timeout: 5_000 } as const;
|
||||
|
||||
function writeCodexAppServerBinding(
|
||||
...args: Parameters<typeof writeRawCodexAppServerBinding>
|
||||
) {
|
||||
function writeCodexAppServerBinding(...args: Parameters<typeof writeRawCodexAppServerBinding>) {
|
||||
const [sessionFile, binding, lookup] = args;
|
||||
return writeRawCodexAppServerBinding(
|
||||
sessionFile,
|
||||
@@ -176,7 +175,7 @@ function createCodexAuthProfileHarness(params: { startMethod: "thread/start" | "
|
||||
seenAgentDirs,
|
||||
async waitForMethod(method: string) {
|
||||
await vi.waitFor(() => expect(requests.map((entry) => entry.method)).toContain(method), {
|
||||
interval: 1,
|
||||
...APP_SERVER_START_WAIT,
|
||||
});
|
||||
},
|
||||
async completeTurn() {
|
||||
@@ -202,7 +201,13 @@ describe("Auth profile runtime contract - Codex app-server adapter", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
vi.useRealTimers();
|
||||
abortAgentHarnessRun(AUTH_PROFILE_RUNTIME_CONTRACT.sessionId);
|
||||
await abortAndDrainAgentHarnessRun({
|
||||
sessionId: AUTH_PROFILE_RUNTIME_CONTRACT.sessionId,
|
||||
sessionKey: AUTH_PROFILE_RUNTIME_CONTRACT.sessionKey,
|
||||
settleMs: 1_000,
|
||||
forceClear: true,
|
||||
reason: "test_cleanup",
|
||||
});
|
||||
resetCodexAppServerClientFactoryForTest();
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
@@ -220,7 +225,7 @@ describe("Auth profile runtime contract - Codex app-server adapter", () => {
|
||||
expect(harness.seenAuthProfileIds).toEqual([
|
||||
AUTH_PROFILE_RUNTIME_CONTRACT.openAiCodexProfileId,
|
||||
]),
|
||||
{ interval: 1 },
|
||||
APP_SERVER_START_WAIT,
|
||||
);
|
||||
expect(harness.seenAgentDirs).toEqual([tmpDir]);
|
||||
await harness.waitForMethod("turn/start");
|
||||
@@ -246,7 +251,7 @@ describe("Auth profile runtime contract - Codex app-server adapter", () => {
|
||||
expect(harness.seenAuthProfileIds).toEqual([
|
||||
AUTH_PROFILE_RUNTIME_CONTRACT.openAiCodexProfileId,
|
||||
]),
|
||||
{ interval: 1 },
|
||||
APP_SERVER_START_WAIT,
|
||||
);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await harness.completeTurn();
|
||||
@@ -271,7 +276,7 @@ describe("Auth profile runtime contract - Codex app-server adapter", () => {
|
||||
expect(harness.seenAuthProfileIds).toEqual([
|
||||
AUTH_PROFILE_RUNTIME_CONTRACT.openAiCodexProfileId,
|
||||
]),
|
||||
{ interval: 1 },
|
||||
APP_SERVER_START_WAIT,
|
||||
);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await harness.completeTurn();
|
||||
|
||||
@@ -188,7 +188,7 @@ export type CodexAppServerRuntimeOptions = {
|
||||
approvalPolicySource?: CodexAppServerApprovalPolicySource;
|
||||
sandbox: CodexAppServerSandboxMode;
|
||||
approvalsReviewer: CodexAppServerApprovalsReviewer;
|
||||
serviceTier?: CodexServiceTier;
|
||||
serviceTier?: CodexServiceTier | null;
|
||||
networkProxy?: ResolvedCodexAppServerNetworkProxyConfig;
|
||||
};
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ export type CodexAppServerToolTelemetry = {
|
||||
|
||||
export type CodexAppServerEventProjectorOptions = {
|
||||
nativePostToolUseRelayEnabled?: boolean;
|
||||
onNativeToolResultRecorded?: () => void | Promise<void>;
|
||||
trajectoryRecorder?: CodexTrajectoryRecorder | null;
|
||||
};
|
||||
|
||||
@@ -632,7 +633,7 @@ export class CodexAppServerEventProjector {
|
||||
}
|
||||
this.recordToolMeta(item);
|
||||
this.emitStandardItemEvent({ phase: "start", item });
|
||||
this.emitNormalizedToolItemEvent({ phase: "start", item });
|
||||
await this.emitNormalizedToolItemEvent({ phase: "start", item });
|
||||
this.recordNativeToolTranscriptCall(item);
|
||||
this.emitToolResultSummary(item);
|
||||
this.emitAgentEvent({
|
||||
@@ -696,7 +697,7 @@ export class CodexAppServerEventProjector {
|
||||
}
|
||||
this.recordToolMeta(item);
|
||||
this.emitStandardItemEvent({ phase: "end", item });
|
||||
this.emitNormalizedToolItemEvent({ phase: "result", item });
|
||||
await this.emitNormalizedToolItemEvent({ phase: "result", item });
|
||||
this.recordNativeToolTranscriptCall(item);
|
||||
this.recordNativeToolTranscriptResult(item);
|
||||
this.emitToolResultSummary(item);
|
||||
@@ -816,7 +817,7 @@ export class CodexAppServerEventProjector {
|
||||
this.emitPlanUpdate({ explanation: undefined, steps: splitPlanText(item.text) });
|
||||
}
|
||||
this.recordToolMeta(item);
|
||||
this.emitSnapshotOnlyNativeToolProgress(item);
|
||||
await this.emitSnapshotOnlyNativeToolProgress(item);
|
||||
this.recordNativeToolTranscriptCall(item);
|
||||
this.recordNativeToolTranscriptResult(item);
|
||||
this.emitAfterToolCallObservation(item);
|
||||
@@ -827,7 +828,7 @@ export class CodexAppServerEventProjector {
|
||||
await this.maybeEndReasoning();
|
||||
}
|
||||
|
||||
private emitSnapshotOnlyNativeToolProgress(item: CodexThreadItem): void {
|
||||
private async emitSnapshotOnlyNativeToolProgress(item: CodexThreadItem): Promise<void> {
|
||||
if (
|
||||
!shouldSynthesizeToolProgressForItem(item) ||
|
||||
!this.isCurrentTurnSnapshotItem(item) ||
|
||||
@@ -839,11 +840,11 @@ export class CodexAppServerEventProjector {
|
||||
const wasStarted = this.activeItemIds.has(item.id);
|
||||
if (!wasStarted) {
|
||||
this.emitStandardItemEvent({ phase: "start", item });
|
||||
this.emitNormalizedToolItemEvent({ phase: "start", item });
|
||||
await this.emitNormalizedToolItemEvent({ phase: "start", item });
|
||||
}
|
||||
this.activeItemIds.delete(item.id);
|
||||
this.emitStandardItemEvent({ phase: "end", item });
|
||||
this.emitNormalizedToolItemEvent({ phase: "result", item });
|
||||
await this.emitNormalizedToolItemEvent({ phase: "result", item });
|
||||
this.completedItemIds.add(item.id);
|
||||
}
|
||||
|
||||
@@ -1116,10 +1117,10 @@ export class CodexAppServerEventProjector {
|
||||
});
|
||||
}
|
||||
|
||||
private emitNormalizedToolItemEvent(params: {
|
||||
private async emitNormalizedToolItemEvent(params: {
|
||||
phase: "start" | "result";
|
||||
item: CodexThreadItem | undefined;
|
||||
}): void {
|
||||
}): Promise<void> {
|
||||
const { item } = params;
|
||||
if (!item || !shouldSynthesizeToolProgressForItem(item)) {
|
||||
return;
|
||||
@@ -1139,6 +1140,7 @@ export class CodexAppServerEventProjector {
|
||||
if (!shouldEmitTranscriptToolProgress(name, args)) {
|
||||
if (params.phase === "result") {
|
||||
this.emitAfterToolCallObservation(item);
|
||||
await this.options.onNativeToolResultRecorded?.();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1162,6 +1164,7 @@ export class CodexAppServerEventProjector {
|
||||
});
|
||||
if (params.phase === "result") {
|
||||
this.emitAfterToolCallObservation(item);
|
||||
await this.options.onNativeToolResultRecorded?.();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5617,6 +5617,50 @@ describe("runCodexAppServerAttempt", () => {
|
||||
expect(resumeRequestParams?.approvalsReviewer).toBe("guardian_subagent");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ name: "fast on", fastMode: true, expectedServiceTier: "priority" },
|
||||
{
|
||||
name: "fast off",
|
||||
fastMode: false,
|
||||
configuredServiceTier: "priority",
|
||||
expectedServiceTier: null,
|
||||
},
|
||||
{
|
||||
name: "fast auto active",
|
||||
fastMode: () => true,
|
||||
expectedServiceTier: "priority",
|
||||
},
|
||||
] satisfies Array<{
|
||||
name: string;
|
||||
fastMode: EmbeddedRunAttemptParams["fastMode"];
|
||||
configuredServiceTier?: "priority";
|
||||
expectedServiceTier?: "priority" | null;
|
||||
}>)(
|
||||
"maps $name to app-server resume and turn service tier",
|
||||
async ({ fastMode, configuredServiceTier, expectedServiceTier }) => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
await writeExistingBinding(sessionFile, workspaceDir, { model: "gpt-5.2" });
|
||||
const { requests, waitForMethod, completeTurn } = createResumeHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.fastMode = fastMode;
|
||||
|
||||
const options = configuredServiceTier
|
||||
? { pluginConfig: { appServer: { serviceTier: configuredServiceTier } } }
|
||||
: {};
|
||||
const run = runCodexAppServerAttempt(params, options);
|
||||
await waitForMethod("turn/start");
|
||||
await completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
|
||||
await run;
|
||||
|
||||
for (const method of ["thread/resume", "turn/start"]) {
|
||||
const request = requests.find((entry) => entry.method === method);
|
||||
const requestParams = request?.params as Record<string, unknown> | undefined;
|
||||
expect(requestParams?.serviceTier).toBe(expectedServiceTier);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("reuses the bound auth profile for app-server startup when params omit it", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
@@ -5662,4 +5706,314 @@ describe("runCodexAppServerAttempt", () => {
|
||||
expect(seenAgentDirs).toEqual([path.join(tempDir, "agent")]);
|
||||
expect(requests.map((entry) => entry.method)).toContain("turn/start");
|
||||
});
|
||||
|
||||
it("announces Codex app-server fast auto progress after the crossing tool result", async () => {
|
||||
const now = vi.spyOn(Date, "now").mockReturnValue(1_000);
|
||||
const onToolResult = vi.fn();
|
||||
const onAgentEvent = vi.fn();
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.verboseLevel = "full";
|
||||
params.fastModeAuto = true;
|
||||
params.fastModeStartedAtMs = 1_000;
|
||||
params.fastModeAutoOnSeconds = 30;
|
||||
params.onToolResult = onToolResult;
|
||||
params.onAgentEvent = onAgentEvent;
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
|
||||
const notifyCommand = async (id: string, output: string, nowMs: number) => {
|
||||
await harness.notify({
|
||||
method: "item/started",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
item: {
|
||||
type: "commandExecution",
|
||||
id,
|
||||
command: `echo ${id}`,
|
||||
cwd: workspaceDir,
|
||||
status: "inProgress",
|
||||
},
|
||||
},
|
||||
});
|
||||
now.mockReturnValue(nowMs);
|
||||
await harness.notify({
|
||||
method: "item/completed",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
item: {
|
||||
type: "commandExecution",
|
||||
id,
|
||||
command: `echo ${id}`,
|
||||
cwd: workspaceDir,
|
||||
status: "completed",
|
||||
aggregatedOutput: output,
|
||||
exitCode: 0,
|
||||
durationMs: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
await notifyCommand("tool-before", "before", 20_000);
|
||||
await notifyCommand("tool-crossing", "crossing", 35_500);
|
||||
await notifyCommand("tool-after", "after", 42_000);
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await run;
|
||||
|
||||
const payloads = onToolResult.mock.calls.map(([payload]) => payload) as Array<{
|
||||
channelData?: Record<string, unknown>;
|
||||
text?: string;
|
||||
}>;
|
||||
const texts = payloads.map((payload) => payload.text ?? "");
|
||||
expect(texts.filter((text) => text.startsWith("💨Fast: auto-off"))).toEqual([
|
||||
"💨Fast: auto-off(34s>=30s)",
|
||||
]);
|
||||
expect(texts.filter((text) => text === "💨Fast: auto-on")).toHaveLength(1);
|
||||
const offIndex = texts.indexOf("💨Fast: auto-off(34s>=30s)");
|
||||
const onIndex = texts.indexOf("💨Fast: auto-on");
|
||||
expect(offIndex).toBeGreaterThan(0);
|
||||
expect(onIndex).toBeGreaterThan(offIndex + 1);
|
||||
expect(texts.slice(offIndex + 1, onIndex).some((text) => !text.startsWith("💨Fast:"))).toBe(
|
||||
true,
|
||||
);
|
||||
expect(payloads[offIndex]?.channelData).toEqual({
|
||||
openclawProgressKind: "fast-mode-auto",
|
||||
});
|
||||
expect(payloads[onIndex]?.channelData).toEqual({
|
||||
openclawProgressKind: "fast-mode-auto",
|
||||
});
|
||||
const fastEvents = onAgentEvent.mock.calls
|
||||
.map(([event]) => event)
|
||||
.filter((event) => event.stream === "item" && event.data?.title === "Fast");
|
||||
expect(fastEvents.map((event) => event.data?.summary)).toEqual([
|
||||
"💨Fast: auto-off(34s>=30s)",
|
||||
"💨Fast: auto-on",
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not announce Codex fast auto progress for explicit fast mode", async () => {
|
||||
const now = vi.spyOn(Date, "now").mockReturnValue(1_000);
|
||||
const onToolResult = vi.fn();
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.fastModeAuto = false;
|
||||
params.fastModeStartedAtMs = 1_000;
|
||||
params.fastModeAutoOnSeconds = 30;
|
||||
params.onToolResult = onToolResult;
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
now.mockReturnValue(35_500);
|
||||
await harness.notify({
|
||||
method: "rawResponseItem/completed",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
item: {
|
||||
type: "function_call_output",
|
||||
id: "call-raw-output",
|
||||
call_id: "call-raw-output",
|
||||
output: "tool output",
|
||||
},
|
||||
},
|
||||
});
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await run;
|
||||
|
||||
const texts = onToolResult.mock.calls.map(([payload]) => payload.text ?? "");
|
||||
expect(texts.filter((text) => text.startsWith("💨Fast:"))).toEqual([]);
|
||||
});
|
||||
|
||||
it("announces Codex app-server fast auto progress for snapshot-only tool results", async () => {
|
||||
const now = vi.spyOn(Date, "now").mockReturnValue(1_000);
|
||||
const onToolResult = vi.fn();
|
||||
const onAgentEvent = vi.fn();
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.verboseLevel = "full";
|
||||
params.fastModeAuto = true;
|
||||
params.fastModeStartedAtMs = 1_000;
|
||||
params.fastModeAutoOnSeconds = 30;
|
||||
params.onToolResult = onToolResult;
|
||||
params.onAgentEvent = onAgentEvent;
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await new Promise<void>((resolve) => {
|
||||
setImmediate(resolve);
|
||||
});
|
||||
|
||||
now.mockReturnValue(35_500);
|
||||
await harness.notify({
|
||||
method: "turn/completed",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
turn: {
|
||||
id: "turn-1",
|
||||
status: "completed",
|
||||
items: [
|
||||
{
|
||||
type: "commandExecution",
|
||||
id: "tool-crossing",
|
||||
command: "echo crossing",
|
||||
commandActions: [],
|
||||
cwd: workspaceDir,
|
||||
processId: null,
|
||||
source: "agent",
|
||||
status: "completed",
|
||||
aggregatedOutput: "crossing",
|
||||
exitCode: 0,
|
||||
durationMs: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
await run;
|
||||
|
||||
const texts = onToolResult.mock.calls.map(([payload]) => payload.text ?? "");
|
||||
expect(texts.filter((text) => text.startsWith("💨Fast: auto-off"))).toEqual([
|
||||
"💨Fast: auto-off(34s>=30s)",
|
||||
]);
|
||||
expect(texts.filter((text) => text === "💨Fast: auto-on")).toHaveLength(1);
|
||||
const fastEvents = onAgentEvent.mock.calls
|
||||
.map(([event]) => event)
|
||||
.filter((event) => event.stream === "item" && event.data?.title === "Fast");
|
||||
expect(fastEvents.map((event) => event.data?.summary)).toEqual([
|
||||
"💨Fast: auto-off(34s>=30s)",
|
||||
"💨Fast: auto-on",
|
||||
]);
|
||||
});
|
||||
|
||||
it("announces Codex app-server fast auto progress for raw function call outputs", async () => {
|
||||
const now = vi.spyOn(Date, "now").mockReturnValue(1_000);
|
||||
const onToolResult = vi.fn();
|
||||
const onAgentEvent = vi.fn();
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.verboseLevel = "full";
|
||||
params.fastModeAuto = true;
|
||||
params.fastModeStartedAtMs = 1_000;
|
||||
params.fastModeAutoOnSeconds = 30;
|
||||
params.onToolResult = onToolResult;
|
||||
params.onAgentEvent = onAgentEvent;
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await new Promise<void>((resolve) => {
|
||||
setImmediate(resolve);
|
||||
});
|
||||
|
||||
now.mockReturnValue(35_500);
|
||||
await harness.notify({
|
||||
method: "rawResponseItem/completed",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
item: {
|
||||
type: "function_call_output",
|
||||
id: "call-raw-output",
|
||||
call_id: "call-raw-output",
|
||||
output: "tool output",
|
||||
},
|
||||
},
|
||||
});
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await run;
|
||||
|
||||
const texts = onToolResult.mock.calls.map(([payload]) => payload.text ?? "");
|
||||
expect(texts.filter((text) => text.startsWith("💨Fast: auto-off"))).toEqual([
|
||||
"💨Fast: auto-off(34s>=30s)",
|
||||
]);
|
||||
expect(texts.filter((text) => text === "💨Fast: auto-on")).toHaveLength(1);
|
||||
const fastEvents = onAgentEvent.mock.calls
|
||||
.map(([event]) => event)
|
||||
.filter((event) => event.stream === "item" && event.data?.title === "Fast");
|
||||
expect(fastEvents.map((event) => event.data?.summary)).toEqual([
|
||||
"💨Fast: auto-off(34s>=30s)",
|
||||
"💨Fast: auto-on",
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not duplicate Codex app-server fast auto progress already announced by the outer runner", async () => {
|
||||
const now = vi.spyOn(Date, "now").mockReturnValue(1_000);
|
||||
const onToolResult = vi.fn();
|
||||
const onAgentEvent = vi.fn();
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.verboseLevel = "full";
|
||||
params.fastModeAuto = true;
|
||||
params.fastModeStartedAtMs = 1_000;
|
||||
params.fastModeAutoOnSeconds = 30;
|
||||
params.fastModeAutoProgressState = {
|
||||
offAnnounced: true,
|
||||
resetAnnounced: false,
|
||||
};
|
||||
params.onToolResult = onToolResult;
|
||||
params.onAgentEvent = onAgentEvent;
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await harness.notify({
|
||||
method: "item/started",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
item: {
|
||||
type: "commandExecution",
|
||||
id: "tool-1",
|
||||
command: "echo tool-1",
|
||||
cwd: workspaceDir,
|
||||
status: "inProgress",
|
||||
},
|
||||
},
|
||||
});
|
||||
now.mockReturnValue(35_500);
|
||||
await harness.notify({
|
||||
method: "item/completed",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
item: {
|
||||
type: "commandExecution",
|
||||
id: "tool-1",
|
||||
command: "echo tool-1",
|
||||
cwd: workspaceDir,
|
||||
status: "completed",
|
||||
aggregatedOutput: "tool output",
|
||||
exitCode: 0,
|
||||
durationMs: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await run;
|
||||
|
||||
const texts = onToolResult.mock.calls.map(([payload]) => payload.text ?? "");
|
||||
expect(texts.filter((text) => text.startsWith("💨Fast: auto-off"))).toEqual([]);
|
||||
expect(texts.filter((text) => text === "💨Fast: auto-on")).toHaveLength(1);
|
||||
expect(params.fastModeAutoProgressState).toEqual({
|
||||
offAnnounced: true,
|
||||
resetAnnounced: true,
|
||||
});
|
||||
const fastEvents = onAgentEvent.mock.calls
|
||||
.map(([event]) => event)
|
||||
.filter((event) => event.stream === "item" && event.data?.title === "Fast");
|
||||
expect(fastEvents.map((event) => event.data?.summary)).toEqual(["💨Fast: auto-on"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
embeddedAgentLog,
|
||||
emitAgentEvent as emitGlobalAgentEvent,
|
||||
finalizeHarnessContextEngineTurn,
|
||||
FAST_MODE_AUTO_PROGRESS_KIND,
|
||||
formatFastModeAutoProgressText,
|
||||
formatErrorMessage,
|
||||
getAgentHarnessHookRunner,
|
||||
getBeforeToolCallPolicyDiagnosticState,
|
||||
@@ -27,9 +29,11 @@ import {
|
||||
runAgentHarnessLlmInputHook,
|
||||
runAgentHarnessLlmOutputHook,
|
||||
runHarnessContextEngineMaintenance,
|
||||
resolveFastModeForElapsed,
|
||||
setActiveEmbeddedRun,
|
||||
supportsModelTools,
|
||||
runAgentCleanupStep,
|
||||
type FastModeAutoProgressState,
|
||||
type EmbeddedRunAttemptParams,
|
||||
type EmbeddedRunAttemptResult,
|
||||
type NativeHookRelayEvent,
|
||||
@@ -85,6 +89,7 @@ import {
|
||||
isCurrentThreadOptionalTurnRequestParams,
|
||||
isCurrentThreadTurnRequestParams,
|
||||
isNativeResponseStreamDeltaNotification,
|
||||
isRawFunctionToolOutputCompletionNotification,
|
||||
isTerminalTurnStatus,
|
||||
readCodexNotificationItem,
|
||||
readRawResponseToolCallId,
|
||||
@@ -274,6 +279,22 @@ const CODEX_APP_SERVER_PROJECTED_CHARS_PER_TOKEN = 4;
|
||||
const CODEX_APP_SERVER_ACTIVE_NATIVE_TURN_WAIT_TIMEOUT_MS = 30_000;
|
||||
const ensuredCodexWorkspaceDirs = new Set<string>();
|
||||
|
||||
function withCodexAppServerFastModeServiceTier(
|
||||
appServer: CodexAppServerRuntimeOptions,
|
||||
params: EmbeddedRunAttemptParams,
|
||||
): CodexAppServerRuntimeOptions {
|
||||
const fastMode = typeof params.fastMode === "function" ? params.fastMode() : params.fastMode;
|
||||
const serviceTier =
|
||||
fastMode === undefined ? appServer.serviceTier : fastMode ? "priority" : undefined;
|
||||
if (serviceTier === appServer.serviceTier) {
|
||||
return appServer;
|
||||
}
|
||||
if (serviceTier) {
|
||||
return { ...appServer, serviceTier };
|
||||
}
|
||||
return { ...appServer, serviceTier: null };
|
||||
}
|
||||
|
||||
function estimateCodexAppServerProjectedTurnTokens(params: {
|
||||
prompt: string;
|
||||
developerInstructions?: string;
|
||||
@@ -306,10 +327,10 @@ async function ensureCodexWorkspaceDirOnce(workspaceDir: string): Promise<void>
|
||||
ensuredCodexWorkspaceDirs.add(normalized);
|
||||
}
|
||||
|
||||
function emitCodexAppServerEvent(
|
||||
async function emitCodexAppServerEvent(
|
||||
params: EmbeddedRunAttemptParams,
|
||||
event: Parameters<NonNullable<EmbeddedRunAttemptParams["onAgentEvent"]>>[0],
|
||||
): void {
|
||||
): Promise<void> {
|
||||
try {
|
||||
emitGlobalAgentEvent({
|
||||
runId: params.runId,
|
||||
@@ -321,10 +342,7 @@ function emitCodexAppServerEvent(
|
||||
embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
|
||||
}
|
||||
try {
|
||||
const maybePromise = params.onAgentEvent?.(event);
|
||||
void Promise.resolve(maybePromise).catch((error: unknown) => {
|
||||
embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
|
||||
});
|
||||
await params.onAgentEvent?.(event);
|
||||
} catch (error) {
|
||||
// Event consumers are observational; they must not abort or strand the
|
||||
// canonical app-server turn lifecycle.
|
||||
@@ -416,6 +434,14 @@ export async function runCodexAppServerAttempt(
|
||||
);
|
||||
const codexModelContentCapture = resolveDiagnosticModelContentCapturePolicy(params.config);
|
||||
const codexModelCallId = `${params.runId}:codex-model:1`;
|
||||
const fastModeAutoStartedAtMs =
|
||||
typeof params.fastModeStartedAtMs === "number" && Number.isFinite(params.fastModeStartedAtMs)
|
||||
? params.fastModeStartedAtMs
|
||||
: undefined;
|
||||
const fastModeAutoProgressState: FastModeAutoProgressState = params.fastModeAutoProgressState ?? {
|
||||
offAnnounced: false,
|
||||
resetAnnounced: false,
|
||||
};
|
||||
// Startup phase timings are profiler-gated because this function runs before
|
||||
// every Codex turn; normal production should not do timing bookkeeping here.
|
||||
const preDynamicStartupStages = createCodexDynamicToolBuildStageTracker({
|
||||
@@ -764,7 +790,9 @@ export async function runCodexAppServerAttempt(
|
||||
onYieldDetected: () => {
|
||||
yieldDetected = true;
|
||||
},
|
||||
onCodexAppServerEvent: (event) => emitCodexAppServerEvent(params, event),
|
||||
onCodexAppServerEvent: (event) => {
|
||||
void emitCodexAppServerEvent(params, event);
|
||||
},
|
||||
onPersistentWebSearchPolicyResolved: (allowed) => {
|
||||
persistentWebSearchAllowed = allowed;
|
||||
},
|
||||
@@ -791,7 +819,9 @@ export async function runCodexAppServerAttempt(
|
||||
onYieldDetected: () => {
|
||||
yieldDetected = true;
|
||||
},
|
||||
onCodexAppServerEvent: (event) => emitCodexAppServerEvent(params, event),
|
||||
onCodexAppServerEvent: (event) => {
|
||||
void emitCodexAppServerEvent(params, event);
|
||||
},
|
||||
});
|
||||
const toolBridge = createCodexDynamicToolBridge({
|
||||
tools,
|
||||
@@ -1424,13 +1454,15 @@ export async function runCodexAppServerAttempt(
|
||||
};
|
||||
};
|
||||
try {
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: { phase: "startup" },
|
||||
});
|
||||
const attemptAppServer = withCodexAppServerFastModeServiceTier(appServer, params);
|
||||
pluginAppServer = attemptAppServer;
|
||||
const startupResult = await startCodexAttemptThread({
|
||||
attemptClientFactory,
|
||||
appServer,
|
||||
appServer: attemptAppServer,
|
||||
pluginConfig,
|
||||
computerUseConfig,
|
||||
startupAuthProfileId,
|
||||
@@ -1469,7 +1501,7 @@ export async function runCodexAppServerAttempt(
|
||||
codexSandboxPolicy = startupResult.sandboxPolicy;
|
||||
releaseSharedClientLease = startupResult.releaseSharedClientLease;
|
||||
restartContextEngineCodexThread = startupResult.restartContextEngineCodexThread;
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: { phase: "thread_ready", threadId: thread.threadId },
|
||||
});
|
||||
@@ -1713,7 +1745,7 @@ export async function runCodexAppServerAttempt(
|
||||
};
|
||||
|
||||
const emitLifecycleStart = () => {
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "lifecycle",
|
||||
data: { phase: "start", startedAt: attemptStartedAt },
|
||||
});
|
||||
@@ -1724,7 +1756,7 @@ export async function runCodexAppServerAttempt(
|
||||
if (!lifecycleStarted || lifecycleTerminalEmitted) {
|
||||
return;
|
||||
}
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "lifecycle",
|
||||
data: {
|
||||
startedAt: attemptStartedAt,
|
||||
@@ -1760,6 +1792,75 @@ export async function runCodexAppServerAttempt(
|
||||
emitExecutionPhaseOnce,
|
||||
});
|
||||
};
|
||||
const emitFastModeAutoProgress = async (payload: {
|
||||
enabled: boolean;
|
||||
elapsedSeconds: number;
|
||||
fastAutoOnSeconds?: number;
|
||||
}): Promise<void> => {
|
||||
const summary = formatFastModeAutoProgressText(payload);
|
||||
await emitCodexAppServerEvent(params, {
|
||||
stream: "item",
|
||||
data: {
|
||||
kind: "status",
|
||||
title: "Fast",
|
||||
phase: "update",
|
||||
summary,
|
||||
},
|
||||
});
|
||||
try {
|
||||
await params.onToolResult?.({
|
||||
text: summary,
|
||||
channelData: { openclawProgressKind: FAST_MODE_AUTO_PROGRESS_KIND },
|
||||
});
|
||||
} catch (error) {
|
||||
embeddedAgentLog.debug("codex app-server fast mode auto progress delivery failed", {
|
||||
error,
|
||||
});
|
||||
}
|
||||
};
|
||||
const maybeAnnounceFastModeAutoOff = async (): Promise<void> => {
|
||||
if (
|
||||
params.fastModeAuto !== true ||
|
||||
fastModeAutoStartedAtMs === undefined ||
|
||||
fastModeAutoProgressState.offAnnounced
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const next = resolveFastModeForElapsed({
|
||||
mode: "auto",
|
||||
startedAtMs: fastModeAutoStartedAtMs,
|
||||
fastAutoOnSeconds: params.fastModeAutoOnSeconds,
|
||||
});
|
||||
if (next.enabled) {
|
||||
return;
|
||||
}
|
||||
fastModeAutoProgressState.offAnnounced = true;
|
||||
await emitFastModeAutoProgress(next);
|
||||
};
|
||||
const maybeEmitFastModeAutoReset = async (): Promise<void> => {
|
||||
if (
|
||||
params.fastModeAuto !== true ||
|
||||
!fastModeAutoProgressState.offAnnounced ||
|
||||
fastModeAutoProgressState.resetAnnounced
|
||||
) {
|
||||
return;
|
||||
}
|
||||
fastModeAutoProgressState.resetAnnounced = true;
|
||||
await emitFastModeAutoProgress({
|
||||
enabled: true,
|
||||
elapsedSeconds: 0,
|
||||
fastAutoOnSeconds: params.fastModeAutoOnSeconds,
|
||||
});
|
||||
};
|
||||
const maybeEmitFastModeAutoResetBestEffort = async (): Promise<void> => {
|
||||
try {
|
||||
await maybeEmitFastModeAutoReset();
|
||||
} catch (error) {
|
||||
embeddedAgentLog.warn(
|
||||
`codex app-server fast mode auto reset progress failed: ${formatErrorMessage(error)}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const isTerminalTurnNotificationForTurn = (
|
||||
notification: CodexServerNotification,
|
||||
@@ -1806,6 +1907,13 @@ export async function runCodexAppServerAttempt(
|
||||
try {
|
||||
await waitForCodexNotificationDispatchTurn();
|
||||
await projector.handleNotification(notification);
|
||||
if (
|
||||
notificationState.isCurrentTurnNotification &&
|
||||
activeTurnItemIds.size === 0 &&
|
||||
isRawFunctionToolOutputCompletionNotification(notification)
|
||||
) {
|
||||
await maybeAnnounceFastModeAutoOff();
|
||||
}
|
||||
} catch (error) {
|
||||
embeddedAgentLog.debug("codex app-server projector notification threw", {
|
||||
method: notification.method,
|
||||
@@ -2082,7 +2190,7 @@ export async function runCodexAppServerAttempt(
|
||||
const toolArgs = sanitizeCodexToolArguments(call.arguments);
|
||||
const shouldEmitDynamicToolProgress = shouldEmitTranscriptToolProgress(call.tool, toolArgs);
|
||||
if (shouldEmitDynamicToolProgress) {
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "tool",
|
||||
data: {
|
||||
phase: "start",
|
||||
@@ -2172,7 +2280,7 @@ export async function runCodexAppServerAttempt(
|
||||
});
|
||||
if (shouldEmitDynamicToolProgress) {
|
||||
const progressResponse = toCodexDynamicToolProgressResponse(response, protocolResponse);
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "tool",
|
||||
data: {
|
||||
phase: "result",
|
||||
@@ -2322,10 +2430,12 @@ export async function runCodexAppServerAttempt(
|
||||
throw error;
|
||||
};
|
||||
const startCodexTurn = async (): Promise<CodexTurnStartResponse> => {
|
||||
const turnAppServer = withCodexAppServerFastModeServiceTier(pluginAppServer, params);
|
||||
pluginAppServer = turnAppServer;
|
||||
const turnStartParams = buildTurnStartParams(params, {
|
||||
threadId: thread.threadId,
|
||||
cwd: codexExecutionCwd,
|
||||
appServer: pluginAppServer,
|
||||
appServer: turnAppServer,
|
||||
promptText: codexTurnPromptText,
|
||||
sandboxPolicy: codexSandboxPolicy,
|
||||
environmentSelection: codexEnvironmentSelection,
|
||||
@@ -2357,7 +2467,7 @@ export async function runCodexAppServerAttempt(
|
||||
"codex app-server resumed thread has active native turn; waiting before turn/start",
|
||||
{ threadId: thread.threadId, activeTurnIds: activeNativeTurnIds },
|
||||
);
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: {
|
||||
phase: "turn_start_waiting_for_native_turn",
|
||||
@@ -2380,7 +2490,7 @@ export async function runCodexAppServerAttempt(
|
||||
ctx: hookContext,
|
||||
hookRunner,
|
||||
});
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: { phase: "turn_starting", threadId: thread.threadId },
|
||||
});
|
||||
@@ -2397,7 +2507,7 @@ export async function runCodexAppServerAttempt(
|
||||
);
|
||||
const compactTurnCompleted = await waitForActiveNativeTurnCompletion();
|
||||
if (compactTurnCompleted && !runAbortController.signal.aborted) {
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: { phase: "turn_start_retry_after_compact", threadId: thread.threadId },
|
||||
});
|
||||
@@ -2479,7 +2589,7 @@ export async function runCodexAppServerAttempt(
|
||||
);
|
||||
}
|
||||
}
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: { phase: "thread_ready_retry", threadId: thread.threadId },
|
||||
});
|
||||
@@ -2509,7 +2619,7 @@ export async function runCodexAppServerAttempt(
|
||||
error: turnStartErrorMessage,
|
||||
});
|
||||
}
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: { phase: "turn_start_failed", error: turnStartErrorMessage },
|
||||
});
|
||||
@@ -2632,6 +2742,7 @@ export async function runCodexAppServerAttempt(
|
||||
nativeHookRelay?.allowedEvents.includes("post_tool_use") === true &&
|
||||
nativeHookRelay.shouldRelayEvent("post_tool_use"),
|
||||
trajectoryRecorder,
|
||||
onNativeToolResultRecorded: maybeAnnounceFastModeAutoOff,
|
||||
},
|
||||
);
|
||||
if (
|
||||
@@ -2892,7 +3003,7 @@ export async function runCodexAppServerAttempt(
|
||||
});
|
||||
const terminalAssistantText = collectTerminalAssistantText(result);
|
||||
if (terminalAssistantText && !finalAborted && !finalPromptError) {
|
||||
emitCodexAppServerEvent(params, {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "assistant",
|
||||
data: { text: terminalAssistantText },
|
||||
});
|
||||
@@ -3024,6 +3135,9 @@ export async function runCodexAppServerAttempt(
|
||||
systemPromptReport,
|
||||
};
|
||||
} finally {
|
||||
if (params.isFinalFallbackAttempt !== false) {
|
||||
await maybeEmitFastModeAutoResetBestEffort();
|
||||
}
|
||||
codexModelCallDiagnostics.emitError(
|
||||
"codex app-server run completed without model-call terminal event",
|
||||
);
|
||||
|
||||
@@ -1113,7 +1113,9 @@ export function buildThreadStartParams(
|
||||
approvalPolicy: options.appServer.approvalPolicy,
|
||||
approvalsReviewer: options.appServer.approvalsReviewer,
|
||||
...codexThreadSandboxOrPermissions(options.appServer),
|
||||
...(options.appServer.serviceTier ? { serviceTier: options.appServer.serviceTier } : {}),
|
||||
...(options.appServer.serviceTier !== undefined
|
||||
? { serviceTier: options.appServer.serviceTier }
|
||||
: {}),
|
||||
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
||||
serviceName: "OpenClaw",
|
||||
config: buildCodexRuntimeThreadConfigForRun(params, options.config, {
|
||||
@@ -1193,7 +1195,9 @@ export function buildThreadResumeParams(
|
||||
approvalPolicy: options.appServer.approvalPolicy,
|
||||
approvalsReviewer: options.appServer.approvalsReviewer,
|
||||
...codexThreadSandboxOrPermissions(options.appServer),
|
||||
...(options.appServer.serviceTier ? { serviceTier: options.appServer.serviceTier } : {}),
|
||||
...(options.appServer.serviceTier !== undefined
|
||||
? { serviceTier: options.appServer.serviceTier }
|
||||
: {}),
|
||||
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
||||
config: buildCodexRuntimeThreadConfigForRun(params, options.config, {
|
||||
nativeCodeModeEnabled: options.nativeCodeModeEnabled,
|
||||
@@ -1428,7 +1432,9 @@ export function buildTurnStartParams(
|
||||
}),
|
||||
model: modelSelection.model,
|
||||
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
||||
...(options.appServer.serviceTier ? { serviceTier: options.appServer.serviceTier } : {}),
|
||||
...(options.appServer.serviceTier !== undefined
|
||||
? { serviceTier: options.appServer.serviceTier }
|
||||
: {}),
|
||||
effort: resolveReasoningEffort(params.thinkLevel, modelSelection.model),
|
||||
...(options.environmentSelection ? { environments: options.environmentSelection } : {}),
|
||||
collaborationMode: buildTurnCollaborationMode(params, {
|
||||
|
||||
@@ -511,7 +511,7 @@ async function writeThreadBindingFromResponse(
|
||||
sandbox: resolved.execPolicy?.touched
|
||||
? resolved.runtime.sandbox
|
||||
: (params.sandbox ?? resolved.runtime.sandbox),
|
||||
serviceTier: params.serviceTier ?? resolved.runtime.serviceTier,
|
||||
serviceTier: params.serviceTier ?? resolved.runtime.serviceTier ?? undefined,
|
||||
networkProxyProfileName: resolved.runtime.networkProxy?.profileName,
|
||||
networkProxyConfigFingerprint: resolved.runtime.networkProxy?.configFingerprint,
|
||||
},
|
||||
@@ -689,7 +689,7 @@ async function runBoundTurn(params: {
|
||||
}),
|
||||
approvalPolicy: typeof approvalPolicy === "string" ? approvalPolicy : undefined,
|
||||
sandbox,
|
||||
serviceTier,
|
||||
serviceTier: serviceTier ?? undefined,
|
||||
networkProxyProfileName: modelScopedRuntime.networkProxy?.profileName,
|
||||
networkProxyConfigFingerprint: modelScopedRuntime.networkProxy?.configFingerprint,
|
||||
},
|
||||
|
||||
@@ -192,7 +192,7 @@ export async function setCodexConversationModel(params: {
|
||||
modelProvider: response.modelProvider ?? modelSelection.modelProvider,
|
||||
approvalPolicy: binding.approvalPolicy,
|
||||
sandbox: binding.sandbox,
|
||||
serviceTier: binding.serviceTier ?? runtime.serviceTier,
|
||||
serviceTier: binding.serviceTier ?? runtime.serviceTier ?? undefined,
|
||||
},
|
||||
lookup,
|
||||
);
|
||||
|
||||
4
extensions/cohere/npm-shrinkwrap.json
generated
4
extensions/cohere/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/cohere-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/cohere-provider",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/cohere-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Cohere provider plugin.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"minHostVersion": ">=2026.6.8"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": true
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/comfy-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw ComfyUI provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/copilot-proxy",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw Copilot Proxy provider plugin",
|
||||
"type": "module",
|
||||
|
||||
4
extensions/copilot/npm-shrinkwrap.json
generated
4
extensions/copilot/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/copilot",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/copilot",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"dependencies": {
|
||||
"@github/copilot-sdk": "1.0.0-beta.9"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/copilot",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw GitHub Copilot agent runtime plugin (registers a `github-copilot` AgentHarness backed by @github/copilot-sdk over JSON-RPC to the GitHub Copilot CLI)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -25,10 +25,10 @@
|
||||
"minHostVersion": ">=2026.5.28"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/deepgram-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw Deepgram media-understanding provider",
|
||||
"type": "module",
|
||||
|
||||
4
extensions/deepinfra/npm-shrinkwrap.json
generated
4
extensions/deepinfra/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/deepinfra-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/deepinfra-provider",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/deepinfra-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw DeepInfra provider plugin.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"minHostVersion": ">=2026.6.8"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
4
extensions/deepseek/npm-shrinkwrap.json
generated
4
extensions/deepseek/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/deepseek-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/deepseek-provider",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/deepseek-provider",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw DeepSeek provider plugin.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"minHostVersion": ">=2026.6.8"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
4
extensions/diagnostics-otel/npm-shrinkwrap.json
generated
4
extensions/diagnostics-otel/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/diagnostics-otel",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/diagnostics-otel",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "1.9.1",
|
||||
"@opentelemetry/api-logs": "0.219.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/diagnostics-otel",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw diagnostics OpenTelemetry exporter for metrics, traces, and logs.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -34,10 +34,10 @@
|
||||
"minHostVersion": ">=2026.4.25"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8"
|
||||
"openclawVersion": "2026.6.10-beta.2"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/diagnostics-prometheus",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/diagnostics-prometheus",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/diagnostics-prometheus",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw diagnostics Prometheus exporter for runtime metrics.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"minHostVersion": ">=2026.4.25"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8"
|
||||
"openclawVersion": "2026.6.10-beta.2"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/diffs-language-pack",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/diffs-language-pack",
|
||||
"version": "2026.6.8"
|
||||
"version": "2026.6.10-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/diffs-language-pack",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw diffs viewer syntax highlighting language pack",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -22,13 +22,13 @@
|
||||
"minHostVersion": ">=2026.5.27"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"assetScripts": {
|
||||
"build": "node ../../scripts/build-diffs-viewer-runtime.mjs full"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"staticAssets": [
|
||||
{
|
||||
"source": "./assets/viewer-runtime.js",
|
||||
|
||||
4
extensions/diffs/npm-shrinkwrap.json
generated
4
extensions/diffs/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/diffs",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/diffs",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"dependencies": {
|
||||
"@pierre/diffs": "1.2.4",
|
||||
"@pierre/theme": "1.0.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/diffs",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw read-only diff viewer plugin and file renderer for agents.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -29,13 +29,13 @@
|
||||
"minHostVersion": ">=2026.4.30"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"assetScripts": {
|
||||
"build": "node ../../scripts/build-diffs-viewer-runtime.mjs curated"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8",
|
||||
"openclawVersion": "2026.6.10-beta.2",
|
||||
"staticAssets": [
|
||||
{
|
||||
"source": "./assets/viewer-runtime.js",
|
||||
|
||||
6
extensions/discord/npm-shrinkwrap.json
generated
6
extensions/discord/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/discord",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/discord",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"dependencies": {
|
||||
"@discordjs/voice": "0.19.2",
|
||||
"discord-api-types": "0.38.48",
|
||||
@@ -16,7 +16,7 @@
|
||||
"ws": "8.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.6.8"
|
||||
"openclaw": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/discord",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"description": "OpenClaw Discord channel plugin for channels, DMs, commands, and app events.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -20,7 +20,7 @@
|
||||
"openclaw": "2026.5.28"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.6.8"
|
||||
"openclaw": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -67,10 +67,10 @@
|
||||
"allowInvalidConfigRecovery": true
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.8"
|
||||
"pluginApi": ">=2026.6.10-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.8"
|
||||
"openclawVersion": "2026.6.10-beta.2"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
} from "openclaw/plugin-sdk/runtime-config-snapshot";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { logVerboseMock } = vi.hoisted(() => ({
|
||||
const { loadModelCatalogMock, logVerboseMock } = vi.hoisted(() => ({
|
||||
loadModelCatalogMock: vi.fn(),
|
||||
logVerboseMock: vi.fn(),
|
||||
}));
|
||||
const { loggerWarnMock } = vi.hoisted(() => ({
|
||||
@@ -32,6 +33,7 @@ vi.mock("openclaw/plugin-sdk/runtime-env", async () => {
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/agent-runtime", () => ({
|
||||
loadModelCatalog: loadModelCatalogMock,
|
||||
resolveHumanDelayConfig: () => undefined,
|
||||
}));
|
||||
|
||||
@@ -227,6 +229,7 @@ describe("createDiscordNativeCommand option wiring", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
clearRuntimeConfigSnapshot();
|
||||
loadModelCatalogMock.mockReset().mockResolvedValue([]);
|
||||
logVerboseMock.mockReset();
|
||||
loggerWarnMock.mockReset();
|
||||
});
|
||||
@@ -257,6 +260,30 @@ describe("createDiscordNativeCommand option wiring", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("uses the provider-startup catalog snapshot for /think autocomplete", async () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
dm: { enabled: true, policy: "open", allowFrom: ["*"] },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const command = createNativeCommand("think", { cfg });
|
||||
const level = requireOption(command, "level");
|
||||
const autocomplete = requireAutocomplete(level, "think level option did not wire autocomplete");
|
||||
|
||||
await runAutocomplete(autocomplete, {
|
||||
userId: "owner",
|
||||
channelType: ChannelType.DM,
|
||||
channelId: "dm-1",
|
||||
channelName: "dm-1",
|
||||
focusedValue: "",
|
||||
});
|
||||
|
||||
expect(loadModelCatalogMock).toHaveBeenCalledWith({ cacheOnly: true });
|
||||
expect(loadModelCatalogMock).toHaveBeenCalledWith({ config: cfg });
|
||||
});
|
||||
|
||||
it("keeps static choices for non-acp string action arguments", () => {
|
||||
const command = createNativeCommand("config");
|
||||
const action = requireOption(command, "action");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Discord plugin module implements native command.options behavior.
|
||||
import { ApplicationCommandOptionType } from "discord-api-types/v10";
|
||||
import { loadModelCatalog } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import {
|
||||
resolveCommandArgChoices,
|
||||
@@ -117,12 +118,17 @@ export function buildDiscordCommandOptions(params: {
|
||||
? await resolveChoiceContext(interaction)
|
||||
: null;
|
||||
const currentCfg = resolveConfig?.() ?? cfg;
|
||||
// Autocomplete cannot defer beyond Discord's three-second deadline.
|
||||
// Cache-only catalog reads never start discovery or filesystem work.
|
||||
const choiceCatalog =
|
||||
command.key === "think" ? await loadModelCatalog({ cacheOnly: true }) : undefined;
|
||||
const choices = resolveCommandArgChoices({
|
||||
command,
|
||||
arg,
|
||||
cfg: currentCfg,
|
||||
provider: context?.provider,
|
||||
model: context?.model,
|
||||
...(choiceCatalog?.length ? { catalog: choiceCatalog } : {}),
|
||||
});
|
||||
const filtered = focusValue
|
||||
? choices.filter((choice) =>
|
||||
@@ -132,6 +138,11 @@ export function buildDiscordCommandOptions(params: {
|
||||
await interaction.respond(
|
||||
filtered.slice(0, 25).map((choice) => ({ name: choice.label, value: choice.value })),
|
||||
);
|
||||
if (command.key === "think" && !choiceCatalog?.length) {
|
||||
// The interaction is acknowledged now, so a failed startup warmup can retry
|
||||
// discovery without risking Discord's response deadline.
|
||||
void loadModelCatalog({ config: currentCfg });
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
const choices =
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Discord plugin module implements native command behavior.
|
||||
import { ApplicationCommandOptionType } from "discord-api-types/v10";
|
||||
import { loadModelCatalog } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import { resolveNativeCommandSessionTargets } from "openclaw/plugin-sdk/command-auth-native";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { buildPairingReply } from "openclaw/plugin-sdk/conversation-runtime";
|
||||
@@ -485,12 +486,18 @@ async function dispatchDiscordCommandInteraction(params: {
|
||||
threadBindings,
|
||||
})
|
||||
: null;
|
||||
// Native /think choices need live-discovery metadata; empty keeps config fallback.
|
||||
const menuModelCatalog =
|
||||
command.key === "think" && menuNeedsModelContext
|
||||
? await loadModelCatalog({ config: cfg })
|
||||
: undefined;
|
||||
const menu = resolveCommandArgMenu({
|
||||
command,
|
||||
args: commandArgs,
|
||||
cfg,
|
||||
provider: menuModelContext?.provider,
|
||||
model: menuModelContext?.model,
|
||||
...(menuModelCatalog?.length ? { catalog: menuModelCatalog } : {}),
|
||||
});
|
||||
if (menu) {
|
||||
const menuPayload = buildDiscordCommandArgMenu({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { loadModelCatalog } from "openclaw/plugin-sdk/agent-runtime";
|
||||
// Discord provider module implements model/runtime integration.
|
||||
import type { ChannelRuntimeSurface } from "openclaw/plugin-sdk/channel-contract";
|
||||
import {
|
||||
@@ -395,6 +396,11 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
|
||||
let earlyGatewayEmitter = gatewaySupervisor?.emitter;
|
||||
let onEarlyGatewayDebug: ((msg: unknown) => void) | undefined;
|
||||
try {
|
||||
if (nativeEnabled && commandSpecs.some((command) => command.name === "think")) {
|
||||
// Autocomplete cannot defer. Warm opportunistically before interactions begin,
|
||||
// but never let provider discovery block Discord startup.
|
||||
void loadModelCatalog({ config: cfg });
|
||||
}
|
||||
const { commands, components, modals } = createDiscordProviderInteractionSurface({
|
||||
cfg,
|
||||
discordConfig: discordCfg,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/document-extract-plugin",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw local document extraction plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/duckduckgo-plugin",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw DuckDuckGo plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/elevenlabs-speech",
|
||||
"version": "2026.6.8",
|
||||
"version": "2026.6.10-beta.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw ElevenLabs speech plugin",
|
||||
"type": "module",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user