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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/.generated/plugin-sdk-api-baseline.sha256
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ad60ccc4fe9084d47f0477e02d9296bacad32f26d7456e2a84be8d25a53a25c2 module/boolean
6e3b8ccee738f8eddde5d813b6e204d6f7c8541fda4cb279a3be3ead27f47e7d module/channel-actions
00d326fdff396dfcc657d1fd16a4015b81d60ae8bf7dad09ac1010b376814de6 module/channel-config-helpers
16dc9d32e8ca3ef78fc63e0fc4b20e6b943633e9572de1a49d24a880b6ffc66c module/channel-config-primitives
c0f910ebfa3dbf283145fb1e3b9c016d03e853ef13e70402b09f3b9d9c2f4ab0 module/channel-config-schema
3325d074d4a31b731d6e76e368023b5a252becbfdb79db10f7507908dbea7167 module/channel-config-schema
2dd98659d9600e755f09ef00dd91c36692b8562ed3700461937e94f6cd1e640a module/channel-contract
6e7bb72d2fe65cd2541af6c057dba4faa5079f410f34e08fcb5348ab38e7a666 module/channel-core
9a5aaf650f9242523bb57bdc2556c323ab64e55aa11e25e2685b73b23ee12534 module/channel-dm-policy
Expand Down Expand Up @@ -141,4 +141,4 @@ a94c9ff59cc361b04f8731a47d722c4bc88cfabf942221369a8cb67bdcda7249 module/tool-pl
7c43c2cd0f09d72cf5f7cd7df582767e436acb697a81722dde47dbe171c66d2e module/webhook-ingress
af8c5e1c84ec9d365a7aa0d15eb63f0dd9e7b50526023e6212bba020c229f224 module/webhook-request-guards
1cc469eacda2818a116ab7b63847b9ddf86225e8d02e62fac48e618ba41a9852 module/widget-html
9161b36ec0ab062ea41b363c894fcd672a7727f21cb726739f99f9c184fce69d module/zod
bc33587590cdeb0917c98eeee72efaacd96d9b83cb4cd0d325aaab5af27182d8 module/zod
149 changes: 128 additions & 21 deletions docs/platformclaw/knox-proxy-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ Target request headers:

```http
Content-Type: application/json
Authorization: Bearer <PLATFORMCLAW_SERVICE_TOKEN>
Authorization: Bearer <PLATFORMCLAW_KNOX_SERVICE_TOKEN>
```

CDEP applies its existing `verify_platformclaw_auth` dependency to the outbound
Expand Down Expand Up @@ -407,7 +407,7 @@ Recommended request:

| Field | Required | Contract |
| ------------------ | -------- | ------------------------------------------------- |
| `requestId` | Yes | Stable idempotency key for this visible message |
| `requestId` | Yes | Stable idempotency key together with `runId` |
| `runId` | Yes | PlatformClaw run correlation ID |
| `messageId` | No | Original inbound correlation ID |
| `chatroomId` | Yes | String Knox target; never use a JavaScript number |
Expand All @@ -434,30 +434,128 @@ Success response:
```

Failure returns `ok: false`, a stable error code, `retryable`, and
`retryAfterSeconds` when known. CDEP must not report a skipped send as success.
`retryAfterSeconds` when known. Explicit `retryable: false` overrides an HTTP
5xx response. CDEP must not report a skipped send as success.

### Cancellation and uncertain delivery

The versioned outbound API adds sibling `/cancel` and `/status` routes under
the same prefix as `/send`, using its existing service authentication. Legacy
authentication mode remains a proxy concern. The required cancellation request is:

```http
POST /api/v1/platformclaw/knox/outbound/cancel
```

```json
{
"requestId": "cancel:progress:msg_123",
"runId": "knox:msg_123",
"targetRequestId": "progress:msg_123",
"targetRunId": "knox:msg_123",
"chatroomId": "99343295704997888",
"conversationType": "room"
}
```

The target is the original send's composite `(requestId, runId)` key. A cancel
request has its own stable composite key. The proxy validates the target room,
persists cancellation before a late send can run, and recalls every delivered
chunk. A successful acknowledgement is `ok: true, state: "cancelled"`, with
an outcome of `recalled`, `suppressed`, or `already_cancelled`. `suppressed`
means transmission was definitely prevented; `already_cancelled` requires a
stored confirmed cancellation. Neither is inferred from a Knox not-found error.

The proxy returns a result or HTTP 202 within eight seconds; the client waits
at most ten seconds for each HTTP response. A 202 acknowledgement can omit
`messageId` and includes `state: "pending"` plus `retryAfterSeconds` when known.
Processing continues in the proxy. The client polls the persisted result using:

```http
GET /api/v1/platformclaw/knox/outbound/status?requestId=progress%3Amsg_123&runId=knox%3Amsg_123
```

The status response requires explicit `state`: `pending`, `sent`, `cancelled`,
or `failed`. A `sent` response requires the confirmed delivery `messageId`.
For cancel status, query the cancellation request's own composite key. HTTP 200
means the lookup succeeded; only the operation's state proves delivery or recall.
An existing send acknowledgement without `state` remains valid when it has
`ok: true` and a non-empty `messageId`. An ID-less duplicate acknowledgement
requires status lookup rather than a fabricated receipt.

Each operation has a bounded 30-second resolution window. After an ambiguous
POST acknowledgement, the client performs status lookups, never another POST.
Unresolved delivery/recall is a non-retryable `DELIVERY_UNKNOWN` or
`CANCEL_UNKNOWN`; an unknown final delivery leaves its previous notice visible.
An abort can interrupt polling, but cleanup uses the original send key and an
independent cancellation deadline. Status lookups must use the proxy's stored
facts and must not resend a native Knox message.

The proxy owns durable single-instance storage across restart, including each
chunk's native `msgId`, original `sentTime`, and `msgKey`. Native packet
`requestId` is generated by the proxy and is distinct from the logical string
idempotency keys above. Native cancellation uses the stored
`chatroomId + msgId + sentTime`; it never substitutes the cancellation time.
For delivery whose native acknowledgement was lost, the proxy may attempt
recall using its persisted timestamp immediately before the original send.
The measured tolerance is +/-10 seconds, and 11 seconds fails. A native
not-found reply still cannot prove whether that uncertain send happened.

Proxy success requires native `result.code = 1000`, not HTTP 200 alone. Native
4001 maps to `MESSAGE_NOT_FOUND` only when `msg` is exactly
`Not exist message.`; other 4001 responses map to `KNOX_API_ERROR`.
`MESSAGE_NOT_FOUND` is a failed recall, not a confirmed cancellation. The
observed recall window reaches 40 minutes; its upper bound is unconfirmed.
This integration is validated with a mock proxy, not a production Knox call.

## Progress and terminal messages

Knox cannot edit a temporary draft in place like some other channels. Every
progress update is a new visible message.
Knox cannot edit a temporary draft in place. Progress replacement sends a new
notice, waits for confirmed delivery, then recalls the previous temporary notice.

PlatformClaw uses this bounded policy:

1. Start a ten-second delay after the turn adopts its request. An explicit
1. Start a fifteen-second delay after the turn adopts its request. An explicit
`channels.knox.progressDelayMs` still overrides this first delay.
2. Suppress progress if the final response arrives before the delay.
3. Otherwise send `Processing your request.` or the current approval wait when
approval is already pending. Use Korean notices when the raw request contains
any Hangul character, including Jamo; use English otherwise.
4. On subsequent ten-second ticks, send a new notice only when the displayed
tool name or observed state changes. Pending approval takes priority over
running calls. A completed call does not imply its background work finished;
ending an approval wait does not imply approval was granted.
5. Do not forward token chunks, tool arguments, command output, model reasoning,
or compaction events. State comes from request-correlated runtime events.
6. Stop timers, unsubscribe observers, and abort pending notice delivery on
completion or cancellation. Send the final response separately. A relay
message already accepted before cancellation cannot be recalled.
3. Send an English digest: up to five recently completed tool calls, oldest
first, followed by current activity or approval state. No history heading,
cumulative count, random wording, or additional model call is needed. Use
a generic fallback only when neither history nor current status is available:
`⏳ 요청을 처리 중입니다.` if the raw request contains any Hangul character
(including Jamo), otherwise `⏳ Processing request`. A digest containing only
completed history has no generic status line. Tool/status labels stay English.
4. Sample request-correlated runtime events every fifteen seconds. Send only
when the rendered digest changes. Identical latest-five content makes no
additional relay call, even when different call IDs produced it. More than
five results between samples retains only the newest five.
5. Limit each complete item line to 50 Unicode graphemes, including symbol and
tool name; shorten overflow with `…`. Prefer file basenames. Allow only
redacted command, file, browser action, or URL hostname details. Never forward
command output, arbitrary arguments, URL credentials/query, model reasoning,
or compaction events. The digest body is bounded to leave room for the DM
execution indicator within 400 characters.
6. Approval takes priority over running calls at the bottom. A tool result does
not mean the request or its background work finished. Resolved approval does
not imply approval was granted.
7. Stop timers and abort pending digest sends immediately on completion or
cancellation. Recall temporary digests only after a confirmed final reply,
or immediately on cancellation. Real question prompts, intermediate answer
blocks, and final/error replies are never recalled as temporary progress.
A failed recall is recorded; further replacements stop to avoid accumulation.

These cleanup guarantees apply to the adopted request's process lifetime.
Recovering temporary-notice keys after an abrupt Gateway crash needs a durable
PlatformClaw outbox follow-up; proxy persistence alone does not recover them.

Example digest:

```text
✓ exec · pnpm build
✓ read · config.ts
✗ exec · pnpm test
⏳ browser · open · example.com
```

CDEP adds `progress` to its outbound schema and sends its text as a normal Knox
message. It also sends user-safe `error` and `timeout` text instead of silently
Expand All @@ -469,7 +567,10 @@ final outbound boundary:
- `🟢 VM 사용 중` / `🟢 Using VM` for an assigned personal VM;
- `🟠 PlatformClaw 서버 사용 중` / `🟠 Using PlatformClaw server` for the server.

The indicator uses the same raw-request language rule as progress notices.
The execution indicator retains its raw-request language rule: any Hangul
character, including Jamo, selects Korean; otherwise it uses English. The
temporary tool digest's labels use English; only its no-information fallback
uses the same raw-request language rule.

A blank line separates the indicator from the reply. The indicator applies to
`progress`, `final`, `error`, and `timeout` delivery, but not room messages or
Expand Down Expand Up @@ -610,8 +711,14 @@ Before cutover, prove:
- CDEP commands never reach an agent;
- restart and duplicate inbound delivery cannot lose an accepted event;
- restart and duplicate outbound delivery cannot create two Knox messages;
- fast runs suppress progress; slow runs emit the first notice after ten seconds
and only changed observed states on later ten-second ticks;
- fast runs suppress progress; slow runs emit the first digest after fifteen
seconds and only changed rendered digests on later fifteen-second samples;
- identical latest-five results make no extra send/cancel calls; more than five
results in thirteen seconds retains the latest five;
- long real commands, paths, emoji, and multiline inputs respect the 50-grapheme
item bound and 400-character notice budget without exposing output or secrets;
- replacement delivery precedes previous-notice recall; confirmed final delivery
precedes latest-notice cleanup; cancellation suppresses late sends;
- approval waits override running state, concurrent calls remain independent,
and completion/cancellation stops notices without cross-request leakage;
- final, error, timeout, rate-limit, and partial-chunk results remain visible;
Expand Down
6 changes: 6 additions & 0 deletions docs/plugins/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ completed migration of published readers.
| `plugin-provider-manifest-compat-aliases` | Move kind/setup/catalog ownership to manifests and model-catalog registration. | 2026-10-01 |
| `deprecated-session-store-beta5-api` | End the v2026.7.x whole-store upgrade window, including package-root aliases. | 2026-10-12 |

The beta.5 session-store record is `removal-pending` with its original October 12
review date. Removal still requires verified published-reader migration, a minimum
supported plugin version that excludes beta.5, closure of the legacy doctor upgrade
window, and explicit breaking-release approval. The elapsed date does not establish
that these conditions are satisfied.

Discord and Slack retain their published legacy renderer exports as conversion
adapters to the canonical presentation renderers. Bundled channel callers use
presentation rendering after converting legacy payloads; Telegram no longer
Expand Down
5 changes: 4 additions & 1 deletion docs/plugins/sdk-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,10 @@ timeline for current status.

Official plugins released with `v2026.7.1-beta.5` imported the four
deprecated helpers above. `openclaw/plugin-sdk/session-store-runtime` keeps
that exact bridge through 2026-10-12; new plugins must use the replacements.
that exact bridge pending its existing published-reader and legacy doctor
upgrade conditions, with 2026-10-12 preserved as the review date. Removal
requires verified exclusion of beta.5 from supported plugins and explicit
breaking-release approval; new plugins must use the replacements.
`resolveStorePath(...)` remains a supported SDK helper and is not part of
this deprecation.

Expand Down
2 changes: 1 addition & 1 deletion extensions/acpx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@agentclientprotocol/codex-acp": "1.1.7",
"acpx": "github:devSejung/acpx#d3e5dd258e5a587fa5e612fae22ff5c5697f4235",
"smol-toml": "1.7.1",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*"
Expand Down
2 changes: 1 addition & 1 deletion extensions/anthropic-vertex/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"dependencies": {
"@anthropic-ai/vertex-sdk": "0.19.0",
"google-auth-library": "10.9.1",
"undici": "8.10.2"
"undici": "8.12.2"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*"
Expand Down
2 changes: 1 addition & 1 deletion extensions/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"devDependencies": {
"@openclaw/gateway-client": "workspace:*",
"@openclaw/plugin-sdk": "workspace:*",
"undici": "8.10.2"
"undici": "8.12.2"
},
"openclaw": {
"extensions": [
Expand Down
2 changes: 1 addition & 1 deletion extensions/buzz/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"type": "module",
"dependencies": {
"nostr-tools": "2.24.1",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion extensions/clickclack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"dependencies": {
"ws": "8.21.1",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion extensions/codex/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"smol-toml": "1.7.1",
"typebox": "1.3.6",
"ws": "8.21.1",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*"
Expand Down
2 changes: 1 addition & 1 deletion extensions/cua-computer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dependencies": {
"@trycua/cua-driver": "0.14.1",
"rastermill": "0.3.1",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*"
Expand Down
2 changes: 1 addition & 1 deletion extensions/diffs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@shikijs/langs": "4.3.1",
"playwright-core": "1.62.0",
"typebox": "1.3.6",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*"
Expand Down
4 changes: 2 additions & 2 deletions extensions/discord/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
"mdast-util-from-markdown": "2.0.3",
"p-map": "7.0.6",
"typebox": "1.3.6",
"undici": "8.10.2",
"undici": "8.12.2",
"ws": "8.21.1",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion extensions/feishu/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"mdast-util-gfm-table": "2.0.0",
"micromark-extension-gfm-table": "2.1.1",
"typebox": "1.3.6",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*",
Expand Down
35 changes: 35 additions & 0 deletions extensions/feishu/src/config-schema.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Feishu tests cover config schema plugin behavior.
import { buildJsonChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
import { describe, expect, it } from "vitest";
import { FeishuChannelConfigSchema, FeishuConfigSchema } from "./config-schema.js";

Expand Down Expand Up @@ -28,6 +29,40 @@ function expectSchemaIssue(
}
}

describe("Feishu domain JSON projection", () => {
const projected = buildJsonChannelConfigSchema(FeishuChannelConfigSchema.schema);

it.each([
["feishu", true],
["lark", true],
["https://example.com", true],
["https://example.com/path?tenant=alpha", true],
["https://", false],
["https://bad host", false],
["http://example.com", false],
["ftp://example.com", false],
["not a domain", false],
])("keeps root and account acceptance for %j", (domain, accepted) => {
for (const input of [{ domain }, { accounts: { work: { domain } } }]) {
expect(FeishuConfigSchema.safeParse(input).success).toBe(accepted);
expect(projected.runtime?.safeParse(input).success).toBe(accepted);
}
});

it("preserves the root domain default and account inheritance in the JSON projection", () => {
const input = { accounts: { work: {} } };
const result = projected.runtime?.safeParse(input);
expect(result).toMatchObject({
success: true,
data: { domain: "feishu", accounts: { work: {} } },
});
expect(FeishuConfigSchema.parse(input).domain).toBe("feishu");
if (result?.success) {
expect(result.data).not.toHaveProperty("accounts.work.domain");
}
});
});

describe("FeishuConfigSchema webhook validation", () => {
it("applies top-level defaults", () => {
const result = FeishuConfigSchema.parse({});
Expand Down
3 changes: 2 additions & 1 deletion extensions/feishu/src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ const FeishuGroupPolicySchema = z.union([
]);
const FeishuDomainSchema = z.union([
z.enum(["feishu", "lark"]),
z.string().url().startsWith("https://"),
// The prefix check has its own format; keep URI validation in the JSON projection too.
z.string().url().startsWith("https://").meta({ format: "uri" }),
]);
const FeishuConnectionModeSchema = z.enum(["websocket", "webhook"]);
const FeishuWebhookPathSchema = z
Expand Down
2 changes: 1 addition & 1 deletion extensions/googlechat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"type": "module",
"dependencies": {
"google-auth-library": "10.9.1",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion extensions/imessage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"type": "module",
"dependencies": {
"typebox": "1.3.6",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*",
Expand Down
Loading