From 8ce01b11059c8a3563b6da3b8b8f33a1d1d466d2 Mon Sep 17 00:00:00 2001 From: dangoldbj Date: Sun, 19 Jul 2026 21:06:13 +0200 Subject: [PATCH 1/5] Support OpenClaw 2026.7.1 and build schemas with the SDK zod instance --- CHANGELOG.md | 14 + package.json | 19 +- pnpm-lock.yaml | 893 ++++++++------------------ src/cli/plugin-cli.ts | 36 +- src/config/config-schema.test.ts | 14 +- src/config/config-schema.ts | 2 +- src/gateway/methods.ts | 19 +- src/params.ts | 18 + src/simplex/state/keyed-store.test.ts | 10 + 9 files changed, 328 insertions(+), 697 deletions(-) create mode 100644 src/params.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 49fe65a..77c2274 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ All notable changes to this project will be documented in this file. +## [1.8.0] - 2026-07-19 + +### Changed + +- Raised the minimum supported OpenClaw version to `2026.7.1` and aligned package compatibility metadata with the current plugin SDK surfaces. +- Version floors now carry an explicit `-0` prerelease suffix (`>=2026.7.1-0`). npm's `latest` tag resolves to `2026.7.1-2`, which sorts below `2026.7.1` under semver, so a plain floor would have rejected the version a default `npm install openclaw` produces. +- Zod schemas are now built with OpenClaw's own zod instance from `openclaw/plugin-sdk/zod` instead of a plugin-owned `zod` dependency, and `zod` was dropped from runtime dependencies. +- Replaced the duplicated hand-rolled positive-integer parsers in the gateway methods and plugin CLI with a shared `readRequiredPositiveInteger` helper backed by the SDK's `readPositiveIntegerParam`. + +### Fixed + +- Fixed SimpleX channel config schema construction against OpenClaw `2026.7.x`, which inlines zod's types into its own bundled declarations. A plugin-owned zod no longer shares declaration identity with the host, so `buildCatchallMultiAccountChannelSchema` and `buildChannelConfigSchema` rejected the account schema and collapsed the inferred config type, breaking account-scoped policy fields in the runtime doctor and account resolution. +- Fixed protocol id parsing so unsafe integers beyond `Number.MAX_SAFE_INTEGER` are rejected instead of being silently rounded to a different id. + ## [1.7.3] - 2026-06-07 ### Added diff --git a/package.json b/package.json index 03e5d57..5d584de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dangoldbj/openclaw-simplex", - "version": "1.7.3", + "version": "1.8.0", "description": "SimpleX channel plugin for OpenClaw using an external WebSocket runtime", "homepage": "https://openclaw-simplex.mintlify.app/", "repository": { @@ -51,11 +51,10 @@ "dependencies": { "@sinclair/typebox": "^0.34.49", "qrcode": "^1.5.4", - "ws": "^8.21.0", - "zod": "^4.4.3" + "ws": "^8.21.0" }, "peerDependencies": { - "openclaw": ">=2026.5.27" + "openclaw": ">=2026.7.1-0" }, "devDependencies": { "@biomejs/biome": "^2.4.13", @@ -63,7 +62,7 @@ "@types/node": "^25.6.0", "@types/qrcode": "^1.5.5", "@types/ws": "^8.18.1", - "openclaw": "2026.5.27", + "openclaw": "2026.7.1", "tsup": "^8.5.1", "typescript": "^6.0.3", "vitest": "^4.1.5" @@ -73,12 +72,12 @@ "./dist/index.js" ], "compat": { - "pluginApi": ">=2026.5.27", - "minGatewayVersion": "2026.5.27" + "pluginApi": ">=2026.7.1-0", + "minGatewayVersion": "2026.7.1-0" }, "build": { - "openclawVersion": "2026.5.27", - "pluginSdkVersion": "2026.5.27" + "openclawVersion": "2026.7.1", + "pluginSdkVersion": "2026.7.1" }, "channel": { "id": "openclaw-simplex", @@ -109,7 +108,7 @@ "install": { "npmSpec": "@dangoldbj/openclaw-simplex", "defaultChoice": "npm", - "minHostVersion": ">=2026.5.27" + "minHostVersion": ">=2026.7.1-0" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f496583..ab0527b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,9 +17,6 @@ importers: ws: specifier: ^8.21.0 version: 8.21.0 - zod: - specifier: ^4.4.3 - version: 4.4.3 devDependencies: '@biomejs/biome': specifier: ^2.4.13 @@ -37,8 +34,8 @@ importers: specifier: ^8.18.1 version: 8.18.1 openclaw: - specifier: 2026.5.27 - version: 2026.5.27 + specifier: 2026.7.1 + version: 2026.7.1(@aws-sdk/credential-provider-node@3.972.47)(@smithy/signature-v4@5.4.5) tsup: specifier: ^8.5.1 version: 8.5.1(jiti@1.21.7)(postcss@8.5.6)(typescript@6.0.3)(yaml@2.9.0) @@ -51,8 +48,8 @@ importers: packages: - '@agentclientprotocol/sdk@0.22.1': - resolution: {integrity: sha512-DfqXtl/8gO9NImq094MTaCXEU2vkhh6v7q/kT+9UjZxUqj8hYaya2OjLVIqn16MzNHcXEpShTR2RIauLSYeDQQ==} + '@agentclientprotocol/sdk@1.1.0': + resolution: {integrity: sha512-NT2KqphUJ3w6EksUL51ZhJgIYgq/ZLGcBPkyMKgRSO5PMVwe9DnKKX+Htnvk6KHh6dUuh34UHK4gKp+4te1Mdg==} peerDependencies: zod: ^3.25.0 || ^4.0.0 @@ -64,8 +61,8 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@anthropic-ai/sdk@0.91.1': - resolution: {integrity: sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==} + '@anthropic-ai/sdk@0.109.1': + resolution: {integrity: sha512-q9OnEKLr5H9nxSuXdgDgJhxfYMiE+AaUEBze2Gk91UcaaLnsN+Lx5fbCYywiqurU/APLdwv23x03Wm6WN3EBsg==} hasBin: true peerDependencies: zod: ^3.25.0 || ^4.0.0 @@ -102,10 +99,6 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-bedrock-runtime@3.1048.0': - resolution: {integrity: sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==} - engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.974.15': resolution: {integrity: sha512-UpA0rTGW/tHGITcCqHisbuuEPraYg9GG+mWmXjY5+RxZBMLGe6aL9oe0ix50LztwAcPIkGZLH0yWdMIkCM10hw==} engines: {node: '>=20.0.0'} @@ -142,18 +135,6 @@ packages: resolution: {integrity: sha512-CDhzKdb2onv5bpnjn/acgdNmJOQthPDLsPizU7rZflsEcgMMp8Mlri+U5hdxf8ldvZJpvM3vLU6D56vfJm5AMQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/eventstream-handler-node@3.972.18': - resolution: {integrity: sha512-QPQhwY/fstR8fMZFWrsJRNoTP6D1RjRPHGRX7u9/VkF3opCsvD0oXPz6qzkX94SchzvuS5vyFZbJbPcMEs2Jeg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-eventstream@3.972.14': - resolution: {integrity: sha512-DoZ4djVj/74XQ6M/IwxuKh543tTvLCL7u1Dx+VDHMgW9yGNrFSJJ1l0LrUQRaekic5CB12wUiiOoHL0VI6H0gg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-websocket@3.972.23': - resolution: {integrity: sha512-F0d4A9pJFiwljyKgSwU1Z5n+CXSv8bp+V5SthbS2rftB8wBN9z1K2Yyv3xbeK0AM2T0g4q6Ptf0shFF+oQZyiA==} - engines: {node: '>= 14.0.0'} - '@aws-sdk/nested-clients@3.997.13': resolution: {integrity: sha512-2pA6eyb5nSo/ZD2cayhOTEMoGQYgspq0RI05GDLkzQ3ajZ6isS6waV6E92Am/hz4LIlLUTrbwPLurJ/fuiHvkg==} engines: {node: '>=20.0.0'} @@ -162,18 +143,10 @@ packages: resolution: {integrity: sha512-HULDLMVzkmTSEv6//7kx2kRevp/VYUpm8hJNNFbmhxDn0fUiGTxVcM9yg31TukvTq8nyOBDUN2gH0o5IRbKjdw==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1048.0': - resolution: {integrity: sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==} - engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1056.0': resolution: {integrity: sha512-81duvlltQlsfn5K+o8zILcystBRdbT1G2JJYVCML5NZHBz4CL/zf+sAemCtBh/uh6RQUMyInGeZLQ7/8igZhbA==} engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.973.8': - resolution: {integrity: sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==} - engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.973.9': resolution: {integrity: sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==} engines: {node: '>=20.0.0'} @@ -261,30 +234,16 @@ packages: '@canvas/image-data@1.1.0': resolution: {integrity: sha512-QdObRRjRbcXGmM1tmJ+MrHcaz1MftF2+W7YI+MsphnsCrmtyfS0d5qJbk0MeSbUeyM/jCb0hmnkXPsy026L7dA==} - '@clack/core@1.3.1': - resolution: {integrity: sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA==} + '@clack/core@1.4.2': + resolution: {integrity: sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ==} engines: {node: '>= 20.12.0'} - '@clack/prompts@1.4.0': - resolution: {integrity: sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA==} + '@clack/prompts@1.6.0': + resolution: {integrity: sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==} engines: {node: '>= 20.12.0'} - '@earendil-works/pi-agent-core@0.75.5': - resolution: {integrity: sha512-LHygOgsW2pgXKb3IkXkOAeZPovHr9VF+EixgXVsDNuB4jmhEOXgshy/zksZ7slkUAx10OQ9W1Ed/2jsnhd1NqA==} - engines: {node: '>=22.19.0'} - - '@earendil-works/pi-ai@0.75.5': - resolution: {integrity: sha512-zf1F5kXk1pqZeFShXOqq9ibUk8QdtRoLCDPAjO+hj44e3EUs9/GFO2qnhTC5+JA2uwVCx+WCNe1PiCjlBYWm5w==} - engines: {node: '>=22.19.0'} - hasBin: true - - '@earendil-works/pi-coding-agent@0.75.5': - resolution: {integrity: sha512-O3CCQDYy28D4uwtP6zZkdEwzHN6X22v49Sb0+SZTC7x37V/YfmogrWPiaFoWeoc2hmdKhSATI7ZAK5bQbJG5NA==} - engines: {node: '>=22.19.0'} - hasBin: true - - '@earendil-works/pi-tui@0.75.5': - resolution: {integrity: sha512-LkXUM1/49pvzzeI39Y5wjBMlgafcCf67HCLhB9Z7yuXHy4XgT+VqxWcZVW5hBdhQsHZd0znjJotfGH1BzxMfiA==} + '@earendil-works/pi-tui@0.80.3': + resolution: {integrity: sha512-2BJI6qwRQfnM0Q7seL1+SbacU/jRRjBnN7Hu3n9BjAn7/s5FaBNnvdD1qBQYRsFTHfjqMaDsjYqanPyqwXj99w==} engines: {node: '>=22.19.0'} '@emnapi/runtime@1.10.0': @@ -461,17 +420,8 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@google/genai@1.52.0': - resolution: {integrity: sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==} - engines: {node: '>=20.0.0'} - peerDependencies: - '@modelcontextprotocol/sdk': ^1.25.2 - peerDependenciesMeta: - '@modelcontextprotocol/sdk': - optional: true - - '@google/genai@2.6.0': - resolution: {integrity: sha512-HjoW3mPuEn7pnuKABJl9VbDoWDSF4nbwYKYvYYor7YjPeDxrrBxHzu2d1Prcd+BAuC4w+85UP6y7ZdcrQAoO7g==} + '@google/genai@2.10.0': + resolution: {integrity: sha512-e4cFxj3tiuMtsgOT4G9c1hXyGJhg7/Buj7VVeBacRY3fRtkRZZ59Q3nuVp2xbq8BGQXLXCDB253qMhklMOeUDg==} engines: {node: '>=20.0.0'} peerDependencies: '@modelcontextprotocol/sdk': ^1.25.2 @@ -491,11 +441,11 @@ packages: peerDependencies: grammy: ^1.0.0 - '@grammyjs/types@3.27.3': - resolution: {integrity: sha512-yUKMLliGsGbnxu96YUJ7km7B0zy4PzeH/Jvti5705R/LeKDMqkDV4DckMSt+OrliWQpTwQljHE0QLol5zgxBkg==} + '@grammyjs/types@3.28.0': + resolution: {integrity: sha512-4JvXCdxRZHCje0M4gHzLwtB4bLno3WD28xd8CNfk4POWIu73BFnSvGeW6OQ5gPem4eYTEwkD9yDaXssixl6tMQ==} - '@homebridge/ciao@1.3.8': - resolution: {integrity: sha512-lNhpCsZVbdbjz2trFjQdzQ3cUIMZQMIMksi7wd3ntTIYgdaGLqT1Ms97DfVIJYHzRuduf56ISvgU8RRLTpK/ng==} + '@homebridge/ciao@1.3.9': + resolution: {integrity: sha512-TMy9zy173jDOpnFXDqL3BPIQn5lfcAkSsivYQatCCakoHk4fLGd7QjfAaNGYE3Ox+/ZI6Lq0e1gGcz1qdw/IbA==} hasBin: true '@hono/node-server@1.19.14': @@ -814,69 +764,6 @@ packages: '@lydell/node-pty@1.2.0-beta.12': resolution: {integrity: sha512-qIK890UwPupoj07osVvgOIa++1mxeHbcGry4PKRHhNVNs81V2SCG34eJr46GybiOmBtc8Sj5PB1/GGM5PL549g==} - '@mariozechner/clipboard-darwin-arm64@0.3.6': - resolution: {integrity: sha512-HjaisYCAbHi/1+N1yDAQHc8ZXGffufIUT5NSOSVR3f3AuMDusxTtnbK8tZ7JFDkShua1oNGZoNwQHsc8MPtE0Q==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@mariozechner/clipboard-darwin-universal@0.3.6': - resolution: {integrity: sha512-8BWtPjOtJOJoykml3w0fx0zRrfWP31mXrJwfoA7xzNprkZw1uolCNfgmjDiVBseoKjp16EGITz7bN+61qn8dWA==} - engines: {node: '>= 10'} - os: [darwin] - - '@mariozechner/clipboard-darwin-x64@0.3.6': - resolution: {integrity: sha512-p9syiZD1kU4I+1ya7f7g+zD1GiUvR8fdlRlNmgsZNWlyjtc8rlV2EjTLd/35x1LsdBq020GVvtzp0ZmPgBI09Q==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@mariozechner/clipboard-linux-arm64-gnu@0.3.6': - resolution: {integrity: sha512-5JFf5rGofrm+V29HNF+wLthXphHdQpMbKDUYJ5tML6/Z5DLlLOV/9Ak4kDPtYyZ+Dzf+kAusE0VsFg4+tfP1IA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@mariozechner/clipboard-linux-arm64-musl@0.3.6': - resolution: {integrity: sha512-JlVjxxw0GbGC0djXYWRIqyteO3J1KZ/QG3udlEFaOD5TLOM1FnmXXAPDQBqr+aBVr720ef9K00dirYnJ0LDCtw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@mariozechner/clipboard-linux-riscv64-gnu@0.3.6': - resolution: {integrity: sha512-4t8BUi5zZ+L77otFQVnVSlaTyAX4TVk9EqQm4syMrEQp96trFEHEwwNHcNEBGzYv5+K7mxay50TthYkz47OWzQ==} - engines: {node: '>= 10'} - cpu: [riscv64] - os: [linux] - - '@mariozechner/clipboard-linux-x64-gnu@0.3.6': - resolution: {integrity: sha512-trtPwcNLW37irwQCJLtCxLw757jjJZk3TSnY/MU9bhtWtA3K9b/eLW0e4RGhUXDoFRds9opNWWaUDuFLa8dm0w==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@mariozechner/clipboard-linux-x64-musl@0.3.6': - resolution: {integrity: sha512-WfnzIvOCCWQiN0MmltCEo6cLceUDbYe+I7xyFZjaps5A+2Op/M2CY7Rey+C4ucQhrvmpoHmTSFgY9ODWk7snoA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@mariozechner/clipboard-win32-arm64-msvc@0.3.6': - resolution: {integrity: sha512-+8+1aHYsBPUjmW3otmWlg+Hijt0iJvoBBs5e0mxFeUd4gDaKMB8Bn6x7c6KVtscg7E5j5NFXnwQqNSIAO4p8zQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@mariozechner/clipboard-win32-x64-msvc@0.3.6': - resolution: {integrity: sha512-S4xfPmERC8ZkiLHe3vekZCjdDwNEETCuvCgQK2kP6/TnvmUkq1y2Pk+DjM4t8uh9KMX9bH4zs5ePcKa8GTXmfg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@mariozechner/clipboard@0.3.6': - resolution: {integrity: sha512-MXdtr+6+ntlIVHdrZYuZNQydu6o8yZswFJ2Ln81j2O/Y9B/LDHvEaIm95xWNPkjGTWriSOeLnQJRFs6dYb60bg==} - engines: {node: '>= 10'} - '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} @@ -943,8 +830,13 @@ packages: '@mintlify/validation@0.1.686': resolution: {integrity: sha512-XmI6HB8CuCxgyCA9m8geu9a6BQO6DfDtDXteiqj55Qz+S+cwZ1TW9YDI6hGzHbVQPpzuwyUU3I8l3TxaLfYPCw==} - '@mistralai/mistralai@2.2.1': - resolution: {integrity: sha512-uKU8CZmL2RzYKmplsU01hii4p3pe4HqJefpWNRWXm1Tcm0Sm4xXfwSLIy4k7ZCPlbETCGcp69E7hZs+WOJ5itQ==} + '@mistralai/mistralai@2.4.0': + resolution: {integrity: sha512-t6hCx242MTGolB76CI+17jDtPIe/bzLsMdUTMMoMn9Qo1h02N2G5jQYHmKDGU3X//OgR2wvngTD7tO6tPp5poQ==} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true '@modelcontextprotocol/sdk@1.29.0': resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} @@ -960,76 +852,6 @@ packages: resolution: {integrity: sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==} engines: {node: '>=14.0.0'} - '@napi-rs/canvas-android-arm64@0.1.100': - resolution: {integrity: sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [android] - - '@napi-rs/canvas-darwin-arm64@0.1.100': - resolution: {integrity: sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@napi-rs/canvas-darwin-x64@0.1.100': - resolution: {integrity: sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': - resolution: {integrity: sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - - '@napi-rs/canvas-linux-arm64-gnu@0.1.100': - resolution: {integrity: sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@napi-rs/canvas-linux-arm64-musl@0.1.100': - resolution: {integrity: sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': - resolution: {integrity: sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==} - engines: {node: '>= 10'} - cpu: [riscv64] - os: [linux] - - '@napi-rs/canvas-linux-x64-gnu@0.1.100': - resolution: {integrity: sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@napi-rs/canvas-linux-x64-musl@0.1.100': - resolution: {integrity: sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@napi-rs/canvas-win32-arm64-msvc@0.1.100': - resolution: {integrity: sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@napi-rs/canvas-win32-x64-msvc@0.1.100': - resolution: {integrity: sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@napi-rs/canvas@0.1.100': - resolution: {integrity: sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==} - engines: {node: '>= 10'} - '@nodable/entities@2.1.0': resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} @@ -1048,9 +870,13 @@ packages: '@openapi-contrib/openapi-schema-to-json-schema@3.2.0': resolution: {integrity: sha512-Gj6C0JwCr8arj0sYuslWXUBSP/KnUlEGnPW4qxlXvAl543oaNQgMgIgkQUA6vs5BCCvwTEiL8m/wdWzfl4UvSw==} - '@openclaw/fs-safe@0.3.0': - resolution: {integrity: sha512-uIBE441CIt1kIURoP9qRGKZ8LkGyfD9ZzeESjwAd29ZPWtghws/5GR3Pjb67jKdcJHP1I6roNXcvnhzAU7lHlA==} - engines: {node: '>=20.11'} + '@openclaw/ai@2026.7.1': + resolution: {integrity: sha512-FsKy5DXSHf4qyN8Huoz/10HZRgoEwLF4uk8UWaCafaIler+q5Fsl51HcrIqIrEe0S38OT7LOaxnR++MOshAlmw==} + engines: {node: '>=22.19.0'} + + '@openclaw/fs-safe@0.4.1': + resolution: {integrity: sha512-hQi+BxO10KdRFlYUot1syC+hTaUnGeQNdqX5kwkKJig8CFq1tKsYJLPm+zkiiGsSKOprPAquQl/txejEhpKPgg==} + engines: {node: '>=22'} '@openclaw/proxyline@0.3.3': resolution: {integrity: sha512-sftHnW69NHQqLjCxBTvQ8f/eQl+peZ5pHCBQtuTWBbeuYRHZ0/GXVTmw/O/YKsShMbqPWhJB0UYtPPdvCUSS8w==} @@ -1058,6 +884,10 @@ packages: peerDependencies: undici: '>=8.3.0 <9' + '@opentelemetry/semantic-conventions@1.43.0': + resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} + engines: {node: '>=14'} + '@posthog/core@1.7.1': resolution: {integrity: sha512-kjK0eFMIpKo9GXIbts8VtAknsoZ18oZorANdtuTj1CbgS28t4ZVq//HAWhnxEuXRTrtkd+SUJ6Ux3j2Af8NCuA==} @@ -1502,10 +1332,6 @@ packages: resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/node-http-handler@4.7.3': - resolution: {integrity: sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==} - engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.7.5': resolution: {integrity: sha512-3dA9TQ+ybRSZ/m0wnbZhiBy4Dezjgq1Ib/ZZrYTpJDBgpoLLU/SDzZc/g0x0MNAdOJe1wPcM+x2PBRmoOur+Sw==} engines: {node: '>=18.0.0'} @@ -1514,10 +1340,6 @@ packages: resolution: {integrity: sha512-QBJKWGqIknH0dc9LWpfH1mkdokAx6iXYN3UcQ3eY6uIEyScuoQAhfl94ge7ozUy9WgFUdE8xsvwBjaYBbWmPNA==} engines: {node: '>=18.0.0'} - '@smithy/types@4.14.1': - resolution: {integrity: sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==} - engines: {node: '>=18.0.0'} - '@smithy/types@4.14.2': resolution: {integrity: sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==} engines: {node: '>=18.0.0'} @@ -1533,6 +1355,9 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -2149,6 +1974,11 @@ packages: peerDependencies: devtools-protocol: '*' + clawpdf@0.3.0: + resolution: {integrity: sha512-41+3AnKk9yek2sm+/9XvUlDTN8Wi+ag7fmxZuqw+ySn4lqaf/fCgLeamqPLiXY4gVbizKEHGoTG/JrIIFNE2rw==} + engines: {node: '>=20'} + hasBin: true + clean-stack@4.2.0: resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} engines: {node: '>=12'} @@ -2212,9 +2042,9 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@14.0.3: - resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} - engines: {node: '>=20'} + commander@15.0.0: + resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} + engines: {node: '>=22.12.0'} commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2441,8 +2271,8 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - diff@8.0.4: - resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + diff@9.0.0: + resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==} engines: {node: '>=0.3.1'} dijkstrajs@1.0.3: @@ -2695,6 +2525,9 @@ packages: fast-memoize@2.5.2: resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} @@ -2938,8 +2771,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - grammy@1.43.0: - resolution: {integrity: sha512-7dYm06A945mXuIk/5HUlSjeyIYChW8vCEiU2dkOKKqJJzwAWxTkCc91Eqbz7TgODh2rtFFKWI/fekowWHOkmjQ==} + grammy@1.44.0: + resolution: {integrity: sha512-gGVykS5+c5f1tPV97LuU6IDRMawE2NzpwM9pNz58HQ35IZXDnYL3VOLvNzYognPSeBIOSzQXRu5w96V0aY8y8A==} engines: {node: ^12.20.0 || >=14.13.1} has-bigints@1.1.0: @@ -3029,16 +2862,17 @@ packages: resolution: {integrity: sha512-NQO+lgVUCtHxZ792FodgW0zflK+ozS9X9dwGp9XvvmPlH7pyxd588cn24TD3rmPm/N0AIRXF10Otah8yKqGw4w==} engines: {node: '>=12'} - highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} hono@4.12.16: resolution: {integrity: sha512-jN0ZewiNAWSe5khM3EyCmBb250+b40wWbwNILNfEvq84VREWwOIkuUsFONk/3i3nqkz7Oe1PcpM2mwQEK2L9Kg==} engines: {node: '>=16.9.0'} - hosted-git-info@9.0.3: - resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} - engines: {node: ^20.17.0 || >=22.9.0} + hosted-git-info@10.1.1: + resolution: {integrity: sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} @@ -3159,10 +2993,6 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - ipaddr.js@2.4.0: - resolution: {integrity: sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==} - engines: {node: '>= 10'} - is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -3461,9 +3291,6 @@ packages: canvas: optional: true - linkify-it@5.0.0: - resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3510,16 +3337,12 @@ packages: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} - markdown-it@14.1.1: - resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} - hasBin: true - markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - marked@15.0.12: - resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} - engines: {node: '>= 18'} + marked@18.0.5: + resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==} + engines: {node: '>= 20'} hasBin: true math-intrinsics@1.1.0: @@ -3589,9 +3412,6 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -3969,25 +3789,21 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} - openai@6.26.0: - resolution: {integrity: sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==} - hasBin: true + openai@6.45.0: + resolution: {integrity: sha512-5DQVNErssk0afNpTTHUm/qZPU4iKR9OYdNid8Ib4puq4gHNNvGWZht2zY4h9a8JMF949Ik6m8gQutllVPbjdnw==} peerDependencies: + '@aws-sdk/credential-provider-node': '>=3.972.0 <4' + '@smithy/hash-node': '>=4.3.0 <5' + '@smithy/signature-v4': '>=5.4.0 <6' ws: ^8.18.0 zod: ^3.25 || ^4.0 peerDependenciesMeta: - ws: + '@aws-sdk/credential-provider-node': optional: true - zod: + '@smithy/hash-node': + optional: true + '@smithy/signature-v4': optional: true - - openai@6.39.0: - resolution: {integrity: sha512-O61LIsimY3acVabwvomwFhwrnN36yvHY2quIfy9keEcFytGgWeV35yLHQ6NVMLSBxRpHmcg2yuhCnlu2HT4pLQ==} - hasBin: true - peerDependencies: - ws: ^8.18.0 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: ws: optional: true zod: @@ -3996,9 +3812,9 @@ packages: openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - openclaw@2026.5.27: - resolution: {integrity: sha512-2N93zhdAo88KAbHt6T7KvYXf4s7XIkYXBgv1npYpn7e1Y9FvrtgtpsA38my9rtFW+70uXEojRPX5/OqnuDqJPw==} - engines: {node: '>=22.19.0'} + openclaw@2026.7.1: + resolution: {integrity: sha512-ge/Xss99CHAjPL/ikmH/UFoiOrjcxDB4sW3y9mhyCD+dYW3wzV7TKbAVdkrXFgAG2d2BjpJofP97zUZ+umxo8g==} + engines: {node: '>=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0'} hasBin: true openid-client@6.8.4: @@ -4107,10 +3923,6 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pdfjs-dist@5.7.284: - resolution: {integrity: sha512-h4EdYQczmGhbOlqc3PPZwxevn7ApdWPbovAuWXOB/DjIyigSnwfy2oze7c6mRcSr9XgLp3eN3EeL4DyySTPMFw==} - engines: {node: '>=22.13.0 || >=24'} - pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -4140,8 +3952,8 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - playwright-core@1.60.0: - resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==} + playwright-core@1.61.1: + resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==} engines: {node: '>=18'} hasBin: true @@ -4272,10 +4084,6 @@ packages: pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} - punycode.js@2.3.1: - resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} - engines: {node: '>=6'} - puppeteer-core@22.14.0: resolution: {integrity: sha512-rl4tOY5LcA3e374GAlsGGHc05HL3eGNf5rZ+uxkl6id9zVZKcwcp1Z+Nd6byb6WPiPeecT/dwz8f/iUm+AZQSw==} engines: {node: '>=18'} @@ -4306,16 +4114,16 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} - quickjs-wasi@2.2.0: - resolution: {integrity: sha512-zQxXmQMrEoD3S+jQdYsloq4qAuaxKFHZj6hHqOYGwB2iQZH+q9e/lf5zQPXCKOk0WJuAjzRFbO4KwHIp2D05Iw==} + quickjs-wasi@3.0.2: + resolution: {integrity: sha512-SyfPzlrfz67/kv0SogmQgW4c2I1klkLcbvj9Y2gc1h7+VylmvuGevFljLXibGKajKJKiJV29d4S6FQLA6Sc80A==} range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - rastermill@0.3.0: - resolution: {integrity: sha512-4g2i0I7M5sba//lFBh19Wi0hDGw8o+isnt/BtEyqQXIZaYclhcNBwL/Fw/6gDCp7aaLwQHADuUvyHCB0Oat5Vw==} - engines: {node: '>=20'} + rastermill@0.3.1: + resolution: {integrity: sha512-CX4nij6+ZLHYIaojJNfLTr7W+AiH/IPJi6E9Aw1br2///1KZL2KBOHd68rkcLedc47MPvb4hhH+fzYeGFa4A/Q==} + engines: {node: '>=22'} raw-body@2.5.3: resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} @@ -4788,6 +4596,9 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -4897,12 +4708,8 @@ packages: engines: {node: '>=10'} deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - tar@7.5.13: - resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} - engines: {node: '>=18'} - - tar@7.5.15: - resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} + tar@7.5.19: + resolution: {integrity: sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==} engines: {node: '>=18'} teex@1.0.1: @@ -4954,11 +4761,6 @@ packages: resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} engines: {node: '>=14.16'} - tokenjuice@0.7.1: - resolution: {integrity: sha512-eO048hm9UcGHASjYkIWEij8QN68amGp+S1nJyo685qB1/ol+VGEYjPglcVPvCbJbZyFHvI+BBAMvOfnqYCtpsQ==} - engines: {node: '>=20'} - hasBin: true - tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -5049,8 +4851,8 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} - typebox@1.1.38: - resolution: {integrity: sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==} + typebox@1.3.3: + resolution: {integrity: sha512-URXGUE31PJDQC+PtRMJeLdF4kmmOdFoVPikPCtV2oOIhUpNpppEdIz7W8bH8cFYPYHdDpaRvqwdegMTmHliudg==} typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} @@ -5073,9 +4875,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - uc.micro@2.1.0: - resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - ufo@1.6.4: resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} @@ -5096,8 +4895,8 @@ packages: undici-types@7.19.2: resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} - undici@8.3.0: - resolution: {integrity: sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q==} + undici@8.5.0: + resolution: {integrity: sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==} engines: {node: '>=22.19.0'} unified@11.0.5: @@ -5312,8 +5111,8 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} - web-tree-sitter@0.26.9: - resolution: {integrity: sha512-YJwSHANl6XFgeEjB8nitgj0qZYt5gkIesJ4w2srS2wcLB4GUa4xcOkM0YaMsU6WNR53YVIkDSY7Ej4pf3IXtCA==} + web-tree-sitter@0.26.10: + resolution: {integrity: sha512-vengBGYS7FpAerkR3o04oBL4L8MkVmjawK50AFBu7v0HZBkmF9ZavPGKoXLSSmRhp7T/YgsJ7joAS3yAxHPEqQ==} webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -5491,7 +5290,7 @@ packages: snapshots: - '@agentclientprotocol/sdk@0.22.1(zod@4.4.3)': + '@agentclientprotocol/sdk@1.1.0(zod@4.4.3)': dependencies: zod: 4.4.3 @@ -5502,9 +5301,10 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@anthropic-ai/sdk@0.91.1(zod@4.4.3)': + '@anthropic-ai/sdk@0.109.1(zod@4.4.3)': dependencies: json-schema-to-ts: 3.1.1 + standardwebhooks: 1.0.0 optionalDependencies: zod: 4.4.3 @@ -5545,51 +5345,39 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.8 + '@aws-sdk/types': 3.973.9 tslib: 2.8.1 + optional: true '@aws-crypto/sha256-browser@5.2.0': dependencies: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.8 + '@aws-sdk/types': 3.973.9 '@aws-sdk/util-locate-window': 3.965.5 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 + optional: true '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.8 + '@aws-sdk/types': 3.973.9 tslib: 2.8.1 + optional: true '@aws-crypto/supports-web-crypto@5.2.0': dependencies: tslib: 2.8.1 + optional: true '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.973.8 + '@aws-sdk/types': 3.973.9 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - - '@aws-sdk/client-bedrock-runtime@3.1048.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.15 - '@aws-sdk/credential-provider-node': 3.972.47 - '@aws-sdk/eventstream-handler-node': 3.972.18 - '@aws-sdk/middleware-eventstream': 3.972.14 - '@aws-sdk/middleware-websocket': 3.972.23 - '@aws-sdk/token-providers': 3.1048.0 - '@aws-sdk/types': 3.973.8 - '@smithy/core': 3.24.5 - '@smithy/fetch-http-handler': 5.4.5 - '@smithy/node-http-handler': 4.7.3 - '@smithy/types': 4.14.1 - tslib: 2.8.1 + optional: true '@aws-sdk/core@3.974.15': dependencies: @@ -5601,6 +5389,7 @@ snapshots: '@smithy/types': 4.14.2 bowser: 2.14.1 tslib: 2.8.1 + optional: true '@aws-sdk/credential-provider-env@3.972.41': dependencies: @@ -5609,6 +5398,7 @@ snapshots: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@aws-sdk/credential-provider-http@3.972.43': dependencies: @@ -5619,6 +5409,7 @@ snapshots: '@smithy/node-http-handler': 4.7.5 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@aws-sdk/credential-provider-ini@3.972.46': dependencies: @@ -5635,6 +5426,7 @@ snapshots: '@smithy/credential-provider-imds': 4.3.6 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@aws-sdk/credential-provider-login@3.972.45': dependencies: @@ -5644,6 +5436,7 @@ snapshots: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@aws-sdk/credential-provider-node@3.972.47': dependencies: @@ -5658,6 +5451,7 @@ snapshots: '@smithy/credential-provider-imds': 4.3.6 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@aws-sdk/credential-provider-process@3.972.41': dependencies: @@ -5666,6 +5460,7 @@ snapshots: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@aws-sdk/credential-provider-sso@3.972.45': dependencies: @@ -5676,6 +5471,7 @@ snapshots: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@aws-sdk/credential-provider-web-identity@3.972.45': dependencies: @@ -5685,30 +5481,7 @@ snapshots: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 - - '@aws-sdk/eventstream-handler-node@3.972.18': - dependencies: - '@aws-sdk/types': 3.973.9 - '@smithy/core': 3.24.5 - '@smithy/types': 4.14.2 - tslib: 2.8.1 - - '@aws-sdk/middleware-eventstream@3.972.14': - dependencies: - '@aws-sdk/types': 3.973.9 - '@smithy/core': 3.24.5 - '@smithy/types': 4.14.2 - tslib: 2.8.1 - - '@aws-sdk/middleware-websocket@3.972.23': - dependencies: - '@aws-sdk/core': 3.974.15 - '@aws-sdk/types': 3.973.9 - '@smithy/core': 3.24.5 - '@smithy/fetch-http-handler': 5.4.5 - '@smithy/signature-v4': 5.4.5 - '@smithy/types': 4.14.2 - tslib: 2.8.1 + optional: true '@aws-sdk/nested-clients@3.997.13': dependencies: @@ -5722,6 +5495,7 @@ snapshots: '@smithy/node-http-handler': 4.7.5 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@aws-sdk/signature-v4-multi-region@3.996.30': dependencies: @@ -5729,15 +5503,7 @@ snapshots: '@smithy/signature-v4': 5.4.5 '@smithy/types': 4.14.2 tslib: 2.8.1 - - '@aws-sdk/token-providers@3.1048.0': - dependencies: - '@aws-sdk/core': 3.974.15 - '@aws-sdk/nested-clients': 3.997.13 - '@aws-sdk/types': 3.973.8 - '@smithy/core': 3.24.5 - '@smithy/types': 4.14.1 - tslib: 2.8.1 + optional: true '@aws-sdk/token-providers@3.1056.0': dependencies: @@ -5747,28 +5513,28 @@ snapshots: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 - - '@aws-sdk/types@3.973.8': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 + optional: true '@aws-sdk/types@3.973.9': dependencies: '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@aws-sdk/util-locate-window@3.965.5': dependencies: tslib: 2.8.1 + optional: true '@aws-sdk/xml-builder@3.972.26': dependencies: '@smithy/types': 4.14.2 fast-xml-parser: 5.7.3 tslib: 2.8.1 + optional: true - '@aws/lambda-invoke-store@0.2.4': {} + '@aws/lambda-invoke-store@0.2.4': + optional: true '@babel/code-frame@7.29.0': dependencies: @@ -5819,85 +5585,22 @@ snapshots: '@canvas/image-data@1.1.0': {} - '@clack/core@1.3.1': + '@clack/core@1.4.2': dependencies: fast-wrap-ansi: 0.2.0 sisteransi: 1.0.5 - '@clack/prompts@1.4.0': + '@clack/prompts@1.6.0': dependencies: - '@clack/core': 1.3.1 + '@clack/core': 1.4.2 fast-string-width: 3.0.2 fast-wrap-ansi: 0.2.0 sisteransi: 1.0.5 - '@earendil-works/pi-agent-core@0.75.5(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)': - dependencies: - '@earendil-works/pi-ai': 0.75.5(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) - ignore: 7.0.5 - typebox: 1.1.38 - yaml: 2.9.0 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - - '@earendil-works/pi-ai@0.75.5(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)': - dependencies: - '@anthropic-ai/sdk': 0.91.1(zod@4.4.3) - '@aws-sdk/client-bedrock-runtime': 3.1048.0 - '@google/genai': 1.52.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)) - '@mistralai/mistralai': 2.2.1 - '@smithy/node-http-handler': 4.7.3 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - openai: 6.26.0(ws@8.21.0)(zod@4.4.3) - partial-json: 0.1.7 - typebox: 1.1.38 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - - '@earendil-works/pi-coding-agent@0.75.5(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)': - dependencies: - '@earendil-works/pi-agent-core': 0.75.5(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) - '@earendil-works/pi-ai': 0.75.5(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) - '@earendil-works/pi-tui': 0.75.5 - '@silvia-odwyer/photon-node': 0.3.4 - chalk: 5.6.2 - cross-spawn: 7.0.6 - diff: 8.0.4 - glob: 13.0.6 - highlight.js: 10.7.3 - hosted-git-info: 9.0.3 - ignore: 7.0.5 - jiti: 2.7.0 - minimatch: 10.2.5 - proper-lockfile: 4.1.2 - typebox: 1.1.38 - undici: 8.3.0 - yaml: 2.9.0 - optionalDependencies: - '@mariozechner/clipboard': 0.3.6 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - - '@earendil-works/pi-tui@0.75.5': + '@earendil-works/pi-tui@0.80.3': dependencies: get-east-asian-width: 1.6.0 - marked: 15.0.12 + marked: 18.0.5 '@emnapi/runtime@1.10.0': dependencies: @@ -5999,20 +5702,7 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@google/genai@1.52.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))': - dependencies: - google-auth-library: 10.6.2 - p-retry: 4.6.2 - protobufjs: 7.5.6 - ws: 8.21.0 - optionalDependencies: - '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - '@google/genai@2.6.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))': + '@google/genai@2.10.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))': dependencies: google-auth-library: 10.6.2 p-retry: 4.6.2 @@ -6025,19 +5715,19 @@ snapshots: - supports-color - utf-8-validate - '@grammyjs/runner@2.0.3(grammy@1.43.0)': + '@grammyjs/runner@2.0.3(grammy@1.44.0)': dependencies: abort-controller: 3.0.0 - grammy: 1.43.0 + grammy: 1.44.0 - '@grammyjs/transformer-throttler@1.2.1(grammy@1.43.0)': + '@grammyjs/transformer-throttler@1.2.1(grammy@1.44.0)': dependencies: bottleneck: 2.19.5 - grammy: 1.43.0 + grammy: 1.44.0 - '@grammyjs/types@3.27.3': {} + '@grammyjs/types@3.28.0': {} - '@homebridge/ciao@1.3.8': + '@homebridge/ciao@1.3.9': dependencies: debug: 4.4.3 fast-deep-equal: 3.1.3 @@ -6309,50 +5999,6 @@ snapshots: '@lydell/node-pty-win32-arm64': 1.2.0-beta.12 '@lydell/node-pty-win32-x64': 1.2.0-beta.12 - '@mariozechner/clipboard-darwin-arm64@0.3.6': - optional: true - - '@mariozechner/clipboard-darwin-universal@0.3.6': - optional: true - - '@mariozechner/clipboard-darwin-x64@0.3.6': - optional: true - - '@mariozechner/clipboard-linux-arm64-gnu@0.3.6': - optional: true - - '@mariozechner/clipboard-linux-arm64-musl@0.3.6': - optional: true - - '@mariozechner/clipboard-linux-riscv64-gnu@0.3.6': - optional: true - - '@mariozechner/clipboard-linux-x64-gnu@0.3.6': - optional: true - - '@mariozechner/clipboard-linux-x64-musl@0.3.6': - optional: true - - '@mariozechner/clipboard-win32-arm64-msvc@0.3.6': - optional: true - - '@mariozechner/clipboard-win32-x64-msvc@0.3.6': - optional: true - - '@mariozechner/clipboard@0.3.6': - optionalDependencies: - '@mariozechner/clipboard-darwin-arm64': 0.3.6 - '@mariozechner/clipboard-darwin-universal': 0.3.6 - '@mariozechner/clipboard-darwin-x64': 0.3.6 - '@mariozechner/clipboard-linux-arm64-gnu': 0.3.6 - '@mariozechner/clipboard-linux-arm64-musl': 0.3.6 - '@mariozechner/clipboard-linux-riscv64-gnu': 0.3.6 - '@mariozechner/clipboard-linux-x64-gnu': 0.3.6 - '@mariozechner/clipboard-linux-x64-musl': 0.3.6 - '@mariozechner/clipboard-win32-arm64-msvc': 0.3.6 - '@mariozechner/clipboard-win32-x64-msvc': 0.3.6 - optional: true - '@mdx-js/mdx@3.1.1': dependencies: '@types/estree': 1.0.8 @@ -6828,8 +6474,9 @@ snapshots: - supports-color - typescript - '@mistralai/mistralai@2.2.1': + '@mistralai/mistralai@2.4.0': dependencies: + '@opentelemetry/semantic-conventions': 1.43.0 ws: 8.21.0 zod: 4.4.3 zod-to-json-schema: 3.25.2(zod@4.4.3) @@ -6861,56 +6508,9 @@ snapshots: '@mozilla/readability@0.6.0': {} - '@napi-rs/canvas-android-arm64@0.1.100': - optional: true - - '@napi-rs/canvas-darwin-arm64@0.1.100': - optional: true - - '@napi-rs/canvas-darwin-x64@0.1.100': - optional: true - - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': - optional: true - - '@napi-rs/canvas-linux-arm64-gnu@0.1.100': - optional: true - - '@napi-rs/canvas-linux-arm64-musl@0.1.100': - optional: true - - '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': - optional: true - - '@napi-rs/canvas-linux-x64-gnu@0.1.100': - optional: true - - '@napi-rs/canvas-linux-x64-musl@0.1.100': - optional: true - - '@napi-rs/canvas-win32-arm64-msvc@0.1.100': - optional: true - - '@napi-rs/canvas-win32-x64-msvc@0.1.100': - optional: true - - '@napi-rs/canvas@0.1.100': - optionalDependencies: - '@napi-rs/canvas-android-arm64': 0.1.100 - '@napi-rs/canvas-darwin-arm64': 0.1.100 - '@napi-rs/canvas-darwin-x64': 0.1.100 - '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.100 - '@napi-rs/canvas-linux-arm64-gnu': 0.1.100 - '@napi-rs/canvas-linux-arm64-musl': 0.1.100 - '@napi-rs/canvas-linux-riscv64-gnu': 0.1.100 - '@napi-rs/canvas-linux-x64-gnu': 0.1.100 - '@napi-rs/canvas-linux-x64-musl': 0.1.100 - '@napi-rs/canvas-win32-arm64-msvc': 0.1.100 - '@napi-rs/canvas-win32-x64-msvc': 0.1.100 + '@nodable/entities@2.1.0': optional: true - '@nodable/entities@2.1.0': {} - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -6927,14 +6527,36 @@ snapshots: dependencies: fast-deep-equal: 3.1.3 - '@openclaw/fs-safe@0.3.0': + '@openclaw/ai@2026.7.1(@aws-sdk/credential-provider-node@3.972.47)(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@smithy/signature-v4@5.4.5)(ws@8.21.0)(zod@4.4.3)': + dependencies: + '@anthropic-ai/sdk': 0.109.1(zod@4.4.3) + '@google/genai': 2.10.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)) + '@mistralai/mistralai': 2.4.0 + openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.47)(@smithy/signature-v4@5.4.5)(ws@8.21.0)(zod@4.4.3) + partial-json: 0.1.7 + typebox: 1.3.3 + transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' + - '@modelcontextprotocol/sdk' + - '@opentelemetry/api' + - '@smithy/hash-node' + - '@smithy/signature-v4' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@openclaw/fs-safe@0.4.1': optionalDependencies: jszip: 3.10.1 - tar: 7.5.13 + tar: 7.5.19 - '@openclaw/proxyline@0.3.3(undici@8.3.0)': + '@openclaw/proxyline@0.3.3(undici@8.5.0)': dependencies: - undici: 8.3.0 + undici: 8.5.0 + + '@opentelemetry/semantic-conventions@1.43.0': {} '@posthog/core@1.7.1': dependencies: @@ -7300,61 +6922,62 @@ snapshots: '@aws-crypto/crc32': 5.2.0 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@smithy/credential-provider-imds@4.3.6': dependencies: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@smithy/fetch-http-handler@5.4.5': dependencies: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - - '@smithy/node-http-handler@4.7.3': - dependencies: - '@smithy/core': 3.24.5 - '@smithy/types': 4.14.2 - tslib: 2.8.1 + optional: true '@smithy/node-http-handler@4.7.5': dependencies: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 + optional: true '@smithy/signature-v4@5.4.5': dependencies: '@smithy/core': 3.24.5 '@smithy/types': 4.14.2 tslib: 2.8.1 - - '@smithy/types@4.14.1': - dependencies: - tslib: 2.8.1 + optional: true '@smithy/types@4.14.2': dependencies: tslib: 2.8.1 + optional: true '@smithy/util-buffer-from@2.2.0': dependencies: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 + optional: true '@smithy/util-utf8@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 + optional: true '@socket.io/component-emitter@3.1.2': {} + '@stablelib/base64@1.0.1': {} + '@standard-schema/spec@1.1.0': {} '@stoplight/better-ajv-errors@1.0.3(ajv@8.20.0)': @@ -7912,7 +7535,8 @@ snapshots: bottleneck@2.19.5: {} - bowser@2.14.1: {} + bowser@2.14.1: + optional: true brace-expansion@1.1.14: dependencies: @@ -8048,6 +7672,8 @@ snapshots: urlpattern-polyfill: 10.0.0 zod: 3.23.8 + clawpdf@0.3.0: {} + clean-stack@4.2.0: dependencies: escape-string-regexp: 5.0.0 @@ -8109,7 +7735,7 @@ snapshots: comma-separated-tokens@2.0.3: {} - commander@14.0.3: {} + commander@15.0.0: {} commander@2.20.3: {} @@ -8292,7 +7918,7 @@ snapshots: didyoumean@1.2.2: {} - diff@8.0.4: {} + diff@9.0.0: {} dijkstrajs@1.0.3: {} @@ -8695,6 +8321,8 @@ snapshots: fast-memoize@2.5.2: {} + fast-sha256@1.3.0: {} + fast-string-truncated-width@3.0.3: {} fast-string-width@3.0.2: @@ -8711,6 +8339,7 @@ snapshots: dependencies: path-expression-matcher: 1.5.0 xml-naming: 0.1.0 + optional: true fast-xml-parser@5.7.3: dependencies: @@ -8718,6 +8347,7 @@ snapshots: fast-xml-builder: 1.2.0 path-expression-matcher: 1.5.0 strnum: 2.2.3 + optional: true fastq@1.20.1: dependencies: @@ -8999,9 +8629,9 @@ snapshots: graceful-fs@4.2.11: {} - grammy@1.43.0: + grammy@1.44.0: dependencies: - '@grammyjs/types': 3.27.3 + '@grammyjs/types': 3.28.0 abort-controller: 3.0.0 debug: 4.4.3 node-fetch: 2.7.0 @@ -9210,11 +8840,11 @@ snapshots: hex-rgb@5.0.0: {} - highlight.js@10.7.3: {} + highlight.js@11.11.1: {} hono@4.12.16: {} - hosted-git-info@9.0.3: + hosted-git-info@10.1.1: dependencies: lru-cache: 11.3.5 @@ -9355,8 +8985,6 @@ snapshots: ipaddr.js@1.9.1: {} - ipaddr.js@2.4.0: {} - is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -9637,10 +9265,6 @@ snapshots: htmlparser2: 10.1.0 uhyphen: 0.2.0 - linkify-it@5.0.0: - dependencies: - uc.micro: 2.1.0 - load-tsconfig@0.2.5: {} locate-path@5.0.0: @@ -9673,18 +9297,9 @@ snapshots: markdown-extensions@2.0.0: {} - markdown-it@14.1.1: - dependencies: - argparse: 2.0.1 - entities: 4.5.0 - linkify-it: 5.0.0 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 - markdown-table@3.0.4: {} - marked@15.0.12: {} + marked@18.0.5: {} math-intrinsics@1.1.0: {} @@ -9920,8 +9535,6 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - mdurl@2.0.0: {} - media-typer@0.3.0: {} media-typer@1.1.0: {} @@ -10434,74 +10047,81 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@6.26.0(ws@8.21.0)(zod@4.4.3): - optionalDependencies: - ws: 8.21.0 - zod: 4.4.3 - - openai@6.39.0(ws@8.21.0)(zod@4.4.3): + openai@6.45.0(@aws-sdk/credential-provider-node@3.972.47)(@smithy/signature-v4@5.4.5)(ws@8.21.0)(zod@4.4.3): optionalDependencies: + '@aws-sdk/credential-provider-node': 3.972.47 + '@smithy/signature-v4': 5.4.5 ws: 8.21.0 zod: 4.4.3 openapi-types@12.1.3: {} - openclaw@2026.5.27: - dependencies: - '@agentclientprotocol/sdk': 0.22.1(zod@4.4.3) - '@clack/core': 1.3.1 - '@clack/prompts': 1.4.0 - '@earendil-works/pi-agent-core': 0.75.5(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) - '@earendil-works/pi-ai': 0.75.5(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) - '@earendil-works/pi-coding-agent': 0.75.5(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) - '@earendil-works/pi-tui': 0.75.5 - '@google/genai': 2.6.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)) - '@grammyjs/runner': 2.0.3(grammy@1.43.0) - '@grammyjs/transformer-throttler': 1.2.1(grammy@1.43.0) - '@homebridge/ciao': 1.3.8 + openclaw@2026.7.1(@aws-sdk/credential-provider-node@3.972.47)(@smithy/signature-v4@5.4.5): + dependencies: + '@agentclientprotocol/sdk': 1.1.0(zod@4.4.3) + '@anthropic-ai/sdk': 0.109.1(zod@4.4.3) + '@clack/core': 1.4.2 + '@clack/prompts': 1.6.0 + '@earendil-works/pi-tui': 0.80.3 + '@google/genai': 2.10.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)) + '@grammyjs/runner': 2.0.3(grammy@1.44.0) + '@grammyjs/transformer-throttler': 1.2.1(grammy@1.44.0) + '@homebridge/ciao': 1.3.9 '@lydell/node-pty': 1.2.0-beta.12 + '@mistralai/mistralai': 2.4.0 '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) '@mozilla/readability': 0.6.0 - '@openclaw/fs-safe': 0.3.0 - '@openclaw/proxyline': 0.3.3(undici@8.3.0) + '@openclaw/ai': 2026.7.1(@aws-sdk/credential-provider-node@3.972.47)(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@smithy/signature-v4@5.4.5)(ws@8.21.0)(zod@4.4.3) + '@openclaw/fs-safe': 0.4.1 + '@openclaw/proxyline': 0.3.3(undici@8.5.0) + '@silvia-odwyer/photon-node': 0.3.4 chalk: 5.6.2 chokidar: 5.0.0 - commander: 14.0.3 + clawpdf: 0.3.0 + commander: 15.0.0 croner: 10.0.1 + diff: 9.0.0 dotenv: 17.4.2 express: 5.2.1 file-type: 22.0.1 - grammy: 1.43.0 - ipaddr.js: 2.4.0 + glob: 13.0.6 + grammy: 1.44.0 + highlight.js: 11.11.1 + hosted-git-info: 10.1.1 + ignore: 7.0.5 jiti: 2.7.0 json5: 2.2.3 jszip: 3.10.1 kysely: 0.29.2 linkedom: 0.18.12 - markdown-it: 14.1.1 + minimatch: 10.2.5 node-edge-tts: 1.2.10 - openai: 6.39.0(ws@8.21.0)(zod@4.4.3) - pdfjs-dist: 5.7.284 - playwright-core: 1.60.0 + openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.47)(@smithy/signature-v4@5.4.5)(ws@8.21.0)(zod@4.4.3) + partial-json: 0.1.7 + playwright-core: 1.61.1 + proper-lockfile: 4.1.2 qrcode: 1.5.4 - quickjs-wasi: 2.2.0 - rastermill: 0.3.0 - tar: 7.5.15 - tokenjuice: 0.7.1 + quickjs-wasi: 3.0.2 + rastermill: 0.3.1 + tar: 7.5.19 tree-sitter-bash: 0.25.1 tslog: 4.10.2 - typebox: 1.1.38 + typebox: 1.3.3 typescript: 6.0.3 - undici: 8.3.0 + undici: 8.5.0 web-push: 3.6.7 - web-tree-sitter: 0.26.9 + web-tree-sitter: 0.26.10 ws: 8.21.0 yaml: 2.9.0 zod: 4.4.3 optionalDependencies: sqlite-vec: 0.1.9 transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' - '@cfworker/json-schema' + - '@opentelemetry/api' + - '@smithy/hash-node' + - '@smithy/signature-v4' - bufferutil - canvas - encoding @@ -10611,7 +10231,8 @@ snapshots: path-exists@4.0.0: {} - path-expression-matcher@1.5.0: {} + path-expression-matcher@1.5.0: + optional: true path-key@3.1.1: {} @@ -10628,10 +10249,6 @@ snapshots: pathe@2.0.3: {} - pdfjs-dist@5.7.284: - optionalDependencies: - '@napi-rs/canvas': 0.1.100 - pend@1.2.0: {} picocolors@1.1.1: {} @@ -10652,7 +10269,7 @@ snapshots: mlly: 1.8.2 pathe: 2.0.3 - playwright-core@1.60.0: {} + playwright-core@1.61.1: {} pngjs@5.0.0: {} @@ -10675,7 +10292,7 @@ snapshots: postcss-load-config@4.0.2(postcss@8.5.6): dependencies: lilconfig: 3.1.3 - yaml: 2.8.3 + yaml: 2.9.0 optionalDependencies: postcss: 8.5.6 @@ -10793,8 +10410,6 @@ snapshots: end-of-stream: 1.4.5 once: 1.4.0 - punycode.js@2.3.1: {} - puppeteer-core@22.14.0: dependencies: '@puppeteer/browsers': 2.3.0 @@ -10843,11 +10458,11 @@ snapshots: quick-lru@5.1.1: {} - quickjs-wasi@2.2.0: {} + quickjs-wasi@3.0.2: {} range-parser@1.2.1: {} - rastermill@0.3.0: + rastermill@0.3.1: dependencies: '@silvia-odwyer/photon-node': 0.3.4 @@ -11584,6 +11199,11 @@ snapshots: stackback@0.0.2: {} + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + statuses@2.0.2: {} std-env@4.1.0: {} @@ -11662,7 +11282,8 @@ snapshots: strip-json-comments@2.0.1: optional: true - strnum@2.2.3: {} + strnum@2.2.3: + optional: true strtok3@10.3.5: dependencies: @@ -11794,16 +11415,7 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 - tar@7.5.13: - dependencies: - '@isaacs/fs-minipass': 4.0.1 - chownr: 3.0.0 - minipass: 7.1.3 - minizlib: 3.1.0 - yallist: 5.0.0 - optional: true - - tar@7.5.15: + tar@7.5.19: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -11861,8 +11473,6 @@ snapshots: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 - tokenjuice@0.7.1: {} - tr46@0.0.3: {} tree-kill@1.2.2: {} @@ -11950,7 +11560,7 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 - typebox@1.1.38: {} + typebox@1.3.3: {} typed-array-buffer@1.0.3: dependencies: @@ -11987,8 +11597,6 @@ snapshots: typescript@6.0.3: {} - uc.micro@2.1.0: {} - ufo@1.6.4: {} uhyphen@0.2.0: {} @@ -12009,7 +11617,7 @@ snapshots: undici-types@7.19.2: {} - undici@8.3.0: {} + undici@8.5.0: {} unified@11.0.5: dependencies: @@ -12142,7 +11750,7 @@ snapshots: vfile-matter@5.0.1: dependencies: vfile: 6.0.3 - yaml: 2.8.3 + yaml: 2.9.0 vfile-message@4.0.3: dependencies: @@ -12209,7 +11817,7 @@ snapshots: web-streams-polyfill@3.3.3: {} - web-tree-sitter@0.26.9: {} + web-tree-sitter@0.26.10: {} webidl-conversions@3.0.1: {} @@ -12298,7 +11906,8 @@ snapshots: ws@8.21.0: {} - xml-naming@0.1.0: {} + xml-naming@0.1.0: + optional: true xml2js@0.6.2: dependencies: diff --git a/src/cli/plugin-cli.ts b/src/cli/plugin-cli.ts index cb2c391..0540581 100644 --- a/src/cli/plugin-cli.ts +++ b/src/cli/plugin-cli.ts @@ -6,6 +6,7 @@ import { LEGACY_SIMPLEX_PLUGIN_ID, SIMPLEX_PLUGIN_ID, } from "../constants.js"; +import { readRequiredPositiveInteger } from "../params.js"; import { connectSimplexLink, planSimplexConnectionLink, @@ -107,15 +108,6 @@ function printJson(value: unknown): void { console.log(JSON.stringify(value, null, 2)); } -function readPositiveInteger(value: string | undefined, label: string): number { - const token = value?.trim() ?? ""; - const parsed = /^[0-9]+$/.test(token) ? Number(token) : Number.NaN; - if (!Number.isInteger(parsed) || parsed <= 0) { - throw new Error(`${label} must be a positive integer`); - } - return parsed; -} - function readRequiredString(value: string | undefined, label: string): string { const trimmed = value?.trim(); if (!trimmed) { @@ -238,7 +230,7 @@ async function runVerificationShowCli( await showSimplexContactVerification({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - contactId: readPositiveInteger(opts.contactId, "contactId"), + contactId: readRequiredPositiveInteger(opts, "contactId"), }) ); } @@ -251,7 +243,7 @@ async function runVerificationCheckCli( await checkSimplexContactVerification({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - contactId: readPositiveInteger(opts.contactId, "contactId"), + contactId: readRequiredPositiveInteger(opts, "contactId"), code: opts.code, }) ); @@ -274,7 +266,7 @@ async function runRequestsAcceptCli( await acceptSimplexContactRequest({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - contactRequestId: readPositiveInteger(opts.contactRequestId, "contactRequestId"), + contactRequestId: readRequiredPositiveInteger(opts, "contactRequestId"), }) ); } @@ -287,7 +279,7 @@ async function runRequestsRejectCli( await rejectSimplexContactRequest({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - contactRequestId: readPositiveInteger(opts.contactRequestId, "contactRequestId"), + contactRequestId: readRequiredPositiveInteger(opts, "contactRequestId"), }) ); } @@ -314,7 +306,7 @@ async function runGroupLinkCreateCli( const result = await createSimplexGroupLink({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - groupId: readPositiveInteger(opts.groupId, "groupId"), + groupId: readRequiredPositiveInteger(opts, "groupId"), role: opts.role, }); printJson(result); @@ -330,7 +322,7 @@ async function runGroupLinkListCli( const result = await listSimplexGroupLink({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - groupId: readPositiveInteger(opts.groupId, "groupId"), + groupId: readRequiredPositiveInteger(opts, "groupId"), }); printJson(result); if (opts.qr && result.link) { @@ -346,7 +338,7 @@ async function runGroupLinkRevokeCli( await revokeSimplexGroupLink({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - groupId: readPositiveInteger(opts.groupId, "groupId"), + groupId: readRequiredPositiveInteger(opts, "groupId"), }) ); } @@ -359,8 +351,8 @@ async function runGroupMemberBlockCli( await blockSimplexGroupMember({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - groupId: readPositiveInteger(opts.groupId, "groupId"), - memberId: readPositiveInteger(opts.memberId, "memberId"), + groupId: readRequiredPositiveInteger(opts, "groupId"), + memberId: readRequiredPositiveInteger(opts, "memberId"), }) ); } @@ -373,8 +365,8 @@ async function runGroupMemberDeleteMessagesCli( await deleteSimplexGroupMemberMessages({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - groupId: readPositiveInteger(opts.groupId, "groupId"), - memberId: readPositiveInteger(opts.memberId, "memberId"), + groupId: readRequiredPositiveInteger(opts, "groupId"), + memberId: readRequiredPositiveInteger(opts, "memberId"), }) ); } @@ -384,7 +376,7 @@ async function runFileReceiveCli(api: OpenClawPluginApi, opts: FileCliOptions): await receiveSimplexFile({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - fileId: readPositiveInteger(opts.fileId, "fileId"), + fileId: readRequiredPositiveInteger(opts, "fileId"), }) ); } @@ -394,7 +386,7 @@ async function runFileCancelCli(api: OpenClawPluginApi, opts: FileCliOptions): P await cancelSimplexFile({ cfg: api.config, accountId: readOptionalAccountId(opts.accountId), - fileId: readPositiveInteger(opts.fileId, "fileId"), + fileId: readRequiredPositiveInteger(opts, "fileId"), }) ); } diff --git a/src/config/config-schema.test.ts b/src/config/config-schema.test.ts index 817cc9e..4be237a 100644 --- a/src/config/config-schema.test.ts +++ b/src/config/config-schema.test.ts @@ -80,15 +80,17 @@ describe("simplex config schema manifest", () => { expect(channelManifest?.description).toBe(packageJson.openclaw?.channel?.blurb); }); - it("advertises the 2026.5.27 compatibility and channel selection metadata", () => { - expect(packageJson.openclaw?.install?.minHostVersion).toBe(">=2026.5.27"); + it("advertises the 2026.7.1 compatibility and channel selection metadata", () => { + // The `-0` prerelease floor is deliberate: npm's `latest` tag is 2026.7.1-2, + // which sorts below 2026.7.1 and would fail a plain `>=2026.7.1` gate. + expect(packageJson.openclaw?.install?.minHostVersion).toBe(">=2026.7.1-0"); expect(packageJson.openclaw?.compat).toEqual({ - pluginApi: ">=2026.5.27", - minGatewayVersion: "2026.5.27", + pluginApi: ">=2026.7.1-0", + minGatewayVersion: "2026.7.1-0", }); expect(packageJson.openclaw?.build).toEqual({ - openclawVersion: "2026.5.27", - pluginSdkVersion: "2026.5.27", + openclawVersion: "2026.7.1", + pluginSdkVersion: "2026.7.1", }); expect(packageJson.openclaw?.channel).toMatchObject({ detailLabel: "SimpleX Chat", diff --git a/src/config/config-schema.ts b/src/config/config-schema.ts index 4cb38df..12ad282 100644 --- a/src/config/config-schema.ts +++ b/src/config/config-schema.ts @@ -10,7 +10,7 @@ import { MarkdownConfigSchema, ToolPolicySchema, } from "openclaw/plugin-sdk/channel-config-schema"; -import { z } from "zod"; +import { z } from "openclaw/plugin-sdk/zod"; import { simplexChannelConfigUiHints } from "./config-ui-hints.js"; const SimplexAllowFromListSchema = AllowFromListSchema.pipe(z.array(z.string()).optional()); diff --git a/src/gateway/methods.ts b/src/gateway/methods.ts index cbc000f..9abf2f7 100644 --- a/src/gateway/methods.ts +++ b/src/gateway/methods.ts @@ -1,5 +1,6 @@ import { renderQrPngDataUrl } from "openclaw/plugin-sdk/media-runtime"; import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; +import { readRequiredPositiveInteger } from "../params.js"; import { connectSimplexLink, planSimplexConnectionLink, @@ -65,20 +66,6 @@ function readRequiredString(params: Record | undefined, key: st return value; } -function readRequiredInteger(params: Record | undefined, key: string): number { - const raw = params?.[key]; - const value = - typeof raw === "number" - ? raw - : typeof raw === "string" && /^[0-9]+$/.test(raw.trim()) - ? Number(raw.trim()) - : NaN; - if (!Number.isInteger(value) || value <= 0) { - throw new Error(`${key} must be a positive integer`); - } - return value; -} - function unavailable(prefix: string, err: unknown): GatewayError { return createError(UNAVAILABLE, `${prefix}: ${err instanceof Error ? err.message : String(err)}`); } @@ -279,7 +266,7 @@ export function registerSimplexGatewayMethods(api: OpenClawPluginApi): void { await acceptSimplexContactRequest({ cfg: api.config, accountId: readAccountId(params), - contactRequestId: readRequiredInteger(params, "contactRequestId"), + contactRequestId: readRequiredPositiveInteger(params, "contactRequestId"), }) ); } catch (err) { @@ -298,7 +285,7 @@ export function registerSimplexGatewayMethods(api: OpenClawPluginApi): void { await rejectSimplexContactRequest({ cfg: api.config, accountId: readAccountId(params), - contactRequestId: readRequiredInteger(params, "contactRequestId"), + contactRequestId: readRequiredPositiveInteger(params, "contactRequestId"), }) ); } catch (err) { diff --git a/src/params.ts b/src/params.ts new file mode 100644 index 0000000..54251ad --- /dev/null +++ b/src/params.ts @@ -0,0 +1,18 @@ +import { readPositiveIntegerParam } from "openclaw/plugin-sdk/channel-actions"; + +/** + * SimpleX protocol ids (contact, group, member, file) are positive integers. + * + * The SDK helper treats a missing value as `undefined` so it can back optional + * params; every call site here needs the id, so absence is an error. + */ +export function readRequiredPositiveInteger( + params: Record | undefined, + key: string +): number { + const value = readPositiveIntegerParam(params ?? {}, key); + if (value === undefined) { + throw new Error(`${key} must be a positive integer`); + } + return value; +} diff --git a/src/simplex/state/keyed-store.test.ts b/src/simplex/state/keyed-store.test.ts index ae453a8..94b745f 100644 --- a/src/simplex/state/keyed-store.test.ts +++ b/src/simplex/state/keyed-store.test.ts @@ -41,6 +41,16 @@ describe("simplex keyed store fallback", () => { openKeyedStore() { throw new Error("openKeyedStore is only available for bundled plugins in this release"); }, + openSyncKeyedStore() { + throw new Error( + "openSyncKeyedStore is only available for bundled plugins in this release" + ); + }, + openChannelIngressQueue() { + throw new Error( + "openChannelIngressQueue is only available for bundled plugins in this release" + ); + }, }, }; const store = openSimplexKeyedStore({ From fc309a0f9dd5f2b1dd61e25c6659013e17a05ed8 Mon Sep 17 00:00:00 2001 From: dangoldbj Date: Sun, 19 Jul 2026 21:06:33 +0200 Subject: [PATCH 2/5] Stop dropping inbound messages on dedupe races and failed attachments --- CHANGELOG.md | 3 + .../events/simplex-inbound-files.test.ts | 68 +++++++++++++++++++ src/channel/events/simplex-inbound-files.ts | 54 ++++++++++++++- src/channel/events/simplex-monitor.ts | 35 +++++++--- src/simplex/state/event-dedupe.test.ts | 39 +++++++++++ src/simplex/state/event-dedupe.ts | 38 ++++++++--- 6 files changed, 218 insertions(+), 19 deletions(-) create mode 100644 src/simplex/state/event-dedupe.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 77c2274..2b769f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ All notable changes to this project will be documented in this file. - Fixed SimpleX channel config schema construction against OpenClaw `2026.7.x`, which inlines zod's types into its own bundled declarations. A plugin-owned zod no longer shares declaration identity with the host, so `buildCatchallMultiAccountChannelSchema` and `buildChannelConfigSchema` rejected the account schema and collapsed the inferred config type, breaking account-scoped policy fields in the runtime doctor and account resolution. - Fixed protocol id parsing so unsafe integers beyond `Number.MAX_SAFE_INTEGER` are rejected instead of being silently rounded to a different id. +- Fixed inbound events being marked as seen before dispatch, which permanently dropped a message if the process stopped between the two. Dedupe is now recorded after the inbound turn is durably registered, so an interrupted turn replays instead of disappearing. +- Fixed oversized inbound attachments being dropped together with their caption. The message is now delivered with an explicit notice that the attachment exceeded the account limit. +- Fixed inbound attachments that time out or complete without a usable path arriving as an ordinary message with no indication that a file was missing. ## [1.7.3] - 2026-06-07 diff --git a/src/channel/events/simplex-inbound-files.test.ts b/src/channel/events/simplex-inbound-files.test.ts index 06f7907..dd96eee 100644 --- a/src/channel/events/simplex-inbound-files.test.ts +++ b/src/channel/events/simplex-inbound-files.test.ts @@ -266,3 +266,71 @@ describe("simplex inbound live replies", () => { expect(secondCancel).toHaveBeenCalledWith(42); }); }); + +describe("inbound media unavailable notices", () => { + function installCapturingRuntime(): { recorded: Array> } { + const recorded: Array> = []; + const runtime = { + channel: { + session: { + recordInboundSession: vi.fn(async (params: { ctx: Record }) => { + recorded.push(params.ctx); + }), + }, + reply: { + dispatchReplyWithBufferedBlockDispatcher: vi.fn(async () => undefined), + }, + }, + }; + setSimplexRuntime(runtime as object as Partial as PluginRuntime); + return { recorded }; + } + + it("appends a size-limit notice to the caption when the attachment is refused", async () => { + const { recorded } = installCapturingRuntime(); + const target = pending(); + target.ctxPayload = { ...target.ctxPayload, Body: "look at this", RawBody: "look at this" }; + + await dispatchInbound({ + pending: target, + mediaUnavailable: { reason: "too-large", sizeBytes: 30_000_000, maxBytes: 10_000_000 }, + }); + + expect(recorded[0]?.Body).toBe( + "look at this\n\n[SimpleX attachment not delivered: 30.0 MB exceeds the 10.0 MB limit for this account.]" + ); + // Command parsing must keep seeing the literal transport text. + expect(recorded[0]?.RawBody).toBe("look at this"); + }); + + it("uses the notice alone when the message carried no caption", async () => { + const { recorded } = installCapturingRuntime(); + const target = pending(); + target.ctxPayload = { ...target.ctxPayload, Body: "" }; + + await dispatchInbound({ + pending: target, + mediaUnavailable: { reason: "transfer-incomplete" }, + }); + + expect(recorded[0]?.Body).toBe( + "[SimpleX attachment not delivered: the file transfer did not complete.]" + ); + }); + + it("leaves the body untouched when media was delivered", async () => { + const { recorded } = installCapturingRuntime(); + const target = pending(); + target.ctxPayload = { ...target.ctxPayload, Body: "look at this" }; + + await dispatchInbound({ + pending: target, + mediaPath: "/media/inbound/photo.jpg", + mediaType: "image/jpeg", + mediaUnavailable: { reason: "transfer-incomplete" }, + }); + + expect(recorded[0]?.Body).toBe("look at this"); + expect(recorded[0]?.MediaPath).toBe("/media/inbound/photo.jpg"); + }); +}); diff --git a/src/channel/events/simplex-inbound-files.ts b/src/channel/events/simplex-inbound-files.ts index a3aa657..e9de5d1 100644 --- a/src/channel/events/simplex-inbound-files.ts +++ b/src/channel/events/simplex-inbound-files.ts @@ -2,10 +2,12 @@ import { readFile } from "node:fs/promises"; import path from "node:path"; import type { ChannelAccountSnapshot } from "openclaw/plugin-sdk/channel-contract"; import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core"; +import { formatInboundMediaUnavailableText } from "openclaw/plugin-sdk/channel-inbound"; import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env"; import { DEFAULT_SIMPLEX_FILES_FOLDER } from "../../constants.js"; import { expandHome } from "../../fs-paths.js"; import type { SimplexClient } from "../../simplex/runtime/client.js"; +import { markSimplexEventSeen, type SimplexEventKey } from "../../simplex/state/event-dedupe.js"; import type { ResolvedSimplexAccount } from "../../types/config.js"; import { resolveSimplexMediaMaxBytes } from "../media/simplex-media.js"; import { @@ -54,6 +56,7 @@ export type PendingInboundFile = { client: SimplexClient; sendPayload: (payload: SimplexReplyPayload) => Promise; statusSink?: (patch: Partial) => void; + eventKey?: SimplexEventKey | null; }; type QueuedPendingInboundFile = { @@ -136,6 +139,7 @@ export function queuePendingFile(params: { pending: current.pending, mediaPath: undefined, mediaType: undefined, + mediaUnavailable: { reason: "transfer-incomplete" }, }).catch((err) => { current.pending.runtime.error?.( `[${accountId}] SimpleX pending file timeout: ${String(err)}` @@ -205,19 +209,48 @@ export async function finalizePendingFile(params: { mediaPath = rawPath; } } - await dispatchInbound({ pending, mediaPath, mediaType }); + // The transfer reported completion but the runtime gave us no usable path, + // so the turn would otherwise arrive with a silently missing attachment. + await dispatchInbound({ + pending, + mediaPath, + mediaType, + mediaUnavailable: mediaPath ? undefined : { reason: "transfer-incomplete" }, + }); } export function hasPendingFile(accountId: string, fileId: number): boolean { return pendingFiles.has(pendingKey(accountId, fileId)); } +export type SimplexInboundMediaUnavailable = { + reason: "too-large" | "transfer-incomplete"; + sizeBytes?: number; + maxBytes?: number; +}; + +function formatMegabytes(bytes: number): string { + return `${(bytes / 1_000_000).toFixed(1)} MB`; +} + +function describeMediaUnavailable(unavailable: SimplexInboundMediaUnavailable): string { + switch (unavailable.reason) { + case "too-large": + return unavailable.sizeBytes !== undefined && unavailable.maxBytes !== undefined + ? `[SimpleX attachment not delivered: ${formatMegabytes(unavailable.sizeBytes)} exceeds the ${formatMegabytes(unavailable.maxBytes)} limit for this account.]` + : "[SimpleX attachment not delivered: it exceeds the configured size limit for this account.]"; + case "transfer-incomplete": + return "[SimpleX attachment not delivered: the file transfer did not complete.]"; + } +} + export async function dispatchInbound(params: { pending: PendingInboundFile; mediaPath?: string; mediaType?: string; + mediaUnavailable?: SimplexInboundMediaUnavailable; }): Promise { - const { pending, mediaPath, mediaType } = params; + const { pending, mediaPath, mediaType, mediaUnavailable } = params; const core = getSimplexRuntime(); const liveReply = createSimplexLiveReplyController({ cfg: pending.cfg, @@ -226,8 +259,19 @@ export async function dispatchInbound(params: { replyToId: pending.replyToId, logError: (message) => pending.runtime.error?.(`[${pending.account.accountId}] ${message}`), }); + // Only `Body` carries the notice. `RawBody`/`CommandBody` stay the literal + // transport text so command parsing is unaffected. + const noticeBody = + mediaUnavailable && !mediaPath + ? formatInboundMediaUnavailableText({ + body: typeof pending.ctxPayload.Body === "string" ? pending.ctxPayload.Body : "", + notice: describeMediaUnavailable(mediaUnavailable), + }) + : undefined; + const ctxPayload = { ...pending.ctxPayload, + ...(noticeBody === undefined ? {} : { Body: noticeBody }), MediaPath: mediaPath, MediaPaths: mediaPath ? [mediaPath] : undefined, MediaType: mediaType, @@ -243,6 +287,12 @@ export async function dispatchInbound(params: { }, }); + // Dedupe is recorded here rather than before dispatch: once the inbound turn + // is durably recorded, OpenClaw owns replaying the agent run, so marking is + // safe. Marking any earlier would drop the message if this process died + // before the record landed. + await markSimplexEventSeen(pending.eventKey ?? null); + pending.statusSink?.({ lastInboundAt: Date.now() }); await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({ diff --git a/src/channel/events/simplex-monitor.ts b/src/channel/events/simplex-monitor.ts index b5a0df8..bbfbc22 100644 --- a/src/channel/events/simplex-monitor.ts +++ b/src/channel/events/simplex-monitor.ts @@ -5,7 +5,11 @@ import { SIMPLEX_CHANNEL_ID } from "../../constants.js"; import { formatSimplexChatRef } from "../../simplex/runtime/api.js"; import { SimplexClient } from "../../simplex/runtime/client.js"; import { recordSimplexContactRequest } from "../../simplex/state/contact-requests.js"; -import { markSimplexEventSeen } from "../../simplex/state/event-dedupe.js"; +import { + hasSimplexEventBeenSeen, + markSimplexEventSeen, + type SimplexEventKey, +} from "../../simplex/state/event-dedupe.js"; import type { ResolvedSimplexAccount } from "../../types/config.js"; import type { SimplexChatItem } from "../../types/events.js"; import type { SimplexChatEvent } from "../../types/simplex.js"; @@ -240,14 +244,16 @@ async function handleSimplexEvent(params: { id: context.chatType === "group" ? context.chatId : dmPeerId, }); - if ( - currentMessageId !== undefined && - !(await markSimplexEventSeen({ - accountId: account.accountId, - chatId: context.chatId, - messageId: currentMessageId, - })) - ) { + const eventKey: SimplexEventKey | null = + currentMessageId === undefined + ? null + : { + accountId: account.accountId, + chatId: context.chatId, + messageId: currentMessageId, + }; + + if (await hasSimplexEventBeenSeen(eventKey)) { continue; } @@ -286,6 +292,7 @@ async function handleSimplexEvent(params: { }, }); if (!access.allowed) { + await markSimplexEventSeen(eventKey); continue; } @@ -334,6 +341,7 @@ async function handleSimplexEvent(params: { }); }, statusSink, + eventKey, }; if (typeof fileId === "number") { @@ -341,6 +349,15 @@ async function handleSimplexEvent(params: { runtime.error?.( `[${account.accountId}] SimpleX file ${fileId} exceeds limit (${fileSize} > ${maxBytes})` ); + // The attachment is refused, but the turn still carries the sender's + // caption. Dispatch it with an unavailable notice instead of dropping + // the message, so the agent sees that something was sent. + await dispatchInbound({ + pending, + mediaPath: undefined, + mediaType: undefined, + mediaUnavailable: { reason: "too-large", sizeBytes: fileSize, maxBytes }, + }); continue; } if (isFileAutoAcceptEnabled(account)) { diff --git a/src/simplex/state/event-dedupe.test.ts b/src/simplex/state/event-dedupe.test.ts new file mode 100644 index 0000000..b46f9f7 --- /dev/null +++ b/src/simplex/state/event-dedupe.test.ts @@ -0,0 +1,39 @@ +import type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store"; +import { beforeEach, describe, expect, it } from "vitest"; +import { setSimplexRuntime } from "../../channel/runtime.js"; +import { hasSimplexEventBeenSeen, markSimplexEventSeen } from "./event-dedupe.js"; + +function key(messageId: number) { + return { accountId: `acct-${messageId}`, chatId: 7, messageId }; +} + +describe("simplex event dedupe", () => { + beforeEach(() => { + // No `state` on the runtime, so the keyed store falls back to memory. + setSimplexRuntime({} as object as Partial as PluginRuntime); + }); + + it("does not treat an event as seen until it is marked", async () => { + const event = key(1001); + expect(await hasSimplexEventBeenSeen(event)).toBe(false); + // Checking twice must stay false: a crash before the mark has to replay. + expect(await hasSimplexEventBeenSeen(event)).toBe(false); + + await markSimplexEventSeen(event); + expect(await hasSimplexEventBeenSeen(event)).toBe(true); + }); + + it("scopes seen state per account, chat, and message", async () => { + await markSimplexEventSeen(key(1002)); + expect(await hasSimplexEventBeenSeen(key(1002))).toBe(true); + expect(await hasSimplexEventBeenSeen(key(1003))).toBe(false); + expect( + await hasSimplexEventBeenSeen({ accountId: "acct-1002", chatId: 8, messageId: 1002 }) + ).toBe(false); + }); + + it("treats events without a message id as never seen", async () => { + expect(await hasSimplexEventBeenSeen(null)).toBe(false); + await expect(markSimplexEventSeen(null)).resolves.toBeUndefined(); + }); +}); diff --git a/src/simplex/state/event-dedupe.ts b/src/simplex/state/event-dedupe.ts index 7e55764..1dcb260 100644 --- a/src/simplex/state/event-dedupe.ts +++ b/src/simplex/state/event-dedupe.ts @@ -5,22 +5,44 @@ const SEEN_EVENT_NAMESPACE = "simplex-seen-events"; const SEEN_EVENT_MAX_ENTRIES = 10_000; const SEEN_EVENT_TTL_MS = 7 * 24 * 60 * 60 * 1000; -export async function markSimplexEventSeen(params: { +export type SimplexEventKey = { accountId: string; chatId: number | string; messageId: number | string; -}): Promise { - const key = `${params.accountId}:${params.chatId}:${params.messageId}`; - const store = openSimplexKeyedStore({ +}; + +function seenKey(params: SimplexEventKey): string { + return `${params.accountId}:${params.chatId}:${params.messageId}`; +} + +function seenStore() { + return openSimplexKeyedStore({ runtime: getSimplexRuntime(), namespace: SEEN_EVENT_NAMESPACE, maxEntries: SEEN_EVENT_MAX_ENTRIES, defaultTtlMs: SEEN_EVENT_TTL_MS, }); - const seen = await store.lookup(key); - if (seen) { +} + +export async function hasSimplexEventBeenSeen(params: SimplexEventKey | null): Promise { + if (!params) { return false; } - await store.register(key, true, { ttlMs: SEEN_EVENT_TTL_MS }); - return true; + return (await seenStore().lookup(seenKey(params))) === true; +} + +/** + * Records an inbound event as handled. + * + * Callers must mark only after the event has been dispatched or otherwise + * handed off. Marking before dispatch makes a crash in between drop the message + * permanently, because the replay after reconnect then sees it as already + * handled. Checking first and marking afterwards is deliberately at-least-once: + * a crash mid-dispatch replays the event rather than losing it. + */ +export async function markSimplexEventSeen(params: SimplexEventKey | null): Promise { + if (!params) { + return; + } + await seenStore().register(seenKey(params), true, { ttlMs: SEEN_EVENT_TTL_MS }); } From c250bd9235a69c0b1e75b332ead28bc981209fa2 Mon Sep 17 00:00:00 2001 From: dangoldbj Date: Sun, 19 Jul 2026 21:06:48 +0200 Subject: [PATCH 3/5] Scope mention pattern matching per SimpleX group --- CHANGELOG.md | 1 + openclaw.plugin.json | 100 ++++++++++++++++++ .../events/simplex-inbound-auth.test.ts | 27 +++++ src/channel/events/simplex-inbound-auth.ts | 17 ++- src/config/config-schema.ts | 2 + src/config/config-ui-hints.ts | 20 ++++ 6 files changed, 165 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b769f9..764e2d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - Version floors now carry an explicit `-0` prerelease suffix (`>=2026.7.1-0`). npm's `latest` tag resolves to `2026.7.1-2`, which sorts below `2026.7.1` under semver, so a plain floor would have rejected the version a default `npm install openclaw` produces. - Zod schemas are now built with OpenClaw's own zod instance from `openclaw/plugin-sdk/zod` instead of a plugin-owned `zod` dependency, and `zod` was dropped from runtime dependencies. - Replaced the duplicated hand-rolled positive-integer parsers in the gateway methods and plugin CLI with a shared `readRequiredPositiveInteger` helper backed by the SDK's `readPositiveIntegerParam`. +- Added an optional `mentionPatterns` account policy (`mode`/`allowIn`/`denyIn`) so agent name patterns can be scoped to specific SimpleX groups, and passed SimpleX provider/conversation context into OpenClaw's mention-regex builder, which previously ignored per-conversation mention policy. ### Fixed diff --git a/openclaw.plugin.json b/openclaw.plugin.json index f2d7e2f..20b14a8 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -182,6 +182,36 @@ "type": "string" } }, + "mentionPatterns": { + "type": "object", + "properties": { + "mode": { + "anyOf": [ + { + "type": "string", + "const": "allow" + }, + { + "type": "string", + "const": "deny" + } + ] + }, + "allowIn": { + "type": "array", + "items": { + "type": "string" + } + }, + "denyIn": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, "groups": { "type": "object", "properties": {}, @@ -431,6 +461,36 @@ "type": "string" } }, + "mentionPatterns": { + "type": "object", + "properties": { + "mode": { + "anyOf": [ + { + "type": "string", + "const": "allow" + }, + { + "type": "string", + "const": "deny" + } + ] + }, + "allowIn": { + "type": "array", + "items": { + "type": "string" + } + }, + "denyIn": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, "groups": { "type": "object", "properties": {}, @@ -963,6 +1023,46 @@ "help": "Allowed SimpleX senders for groups when groupPolicy is allowlist.", "tags": ["security"] }, + "mentionPatterns": { + "label": "Mention Patterns", + "help": "Restrict where agent name patterns count as a mention. Use allowIn/denyIn with SimpleX group ids.", + "advanced": true + }, + "accounts.*.mentionPatterns": { + "label": "Mention Patterns", + "help": "Restrict where agent name patterns count as a mention. Use allowIn/denyIn with SimpleX group ids.", + "advanced": true + }, + "mentionPatterns.mode": { + "label": "Mention Pattern Mode", + "help": "\"allow\" limits pattern matching to allowIn conversations; \"deny\" disables it in denyIn conversations.", + "advanced": true + }, + "accounts.*.mentionPatterns.mode": { + "label": "Mention Pattern Mode", + "help": "\"allow\" limits pattern matching to allowIn conversations; \"deny\" disables it in denyIn conversations.", + "advanced": true + }, + "mentionPatterns.allowIn": { + "label": "Mention Patterns Allowed In", + "help": "SimpleX group ids where agent name patterns count as a mention.", + "advanced": true + }, + "accounts.*.mentionPatterns.allowIn": { + "label": "Mention Patterns Allowed In", + "help": "SimpleX group ids where agent name patterns count as a mention.", + "advanced": true + }, + "mentionPatterns.denyIn": { + "label": "Mention Patterns Denied In", + "help": "SimpleX group ids where agent name patterns must not count as a mention.", + "advanced": true + }, + "accounts.*.mentionPatterns.denyIn": { + "label": "Mention Patterns Denied In", + "help": "SimpleX group ids where agent name patterns must not count as a mention.", + "advanced": true + }, "groups": { "label": "Group Overrides", "help": "Optional per-group overrides for mention and tool policy behavior.", diff --git a/src/channel/events/simplex-inbound-auth.test.ts b/src/channel/events/simplex-inbound-auth.test.ts index 150017e..e979ab5 100644 --- a/src/channel/events/simplex-inbound-auth.test.ts +++ b/src/channel/events/simplex-inbound-auth.test.ts @@ -124,6 +124,33 @@ describe("resolveSimplexInboundAccess", () => { }); }); + it("scopes mention pattern matching to the SimpleX group and account policy", async () => { + const core = runtimeCore({ mentioned: true }); + const mentionPatterns = { mode: "deny" as const, denyIn: ["7"] }; + + await resolveSimplexInboundAccess({ + account: account({ + groupPolicy: "allowlist", + groupAllowFrom: ["group:7"], + mentionPatterns, + }), + cfg: {}, + runtime, + core, + context: groupContext(), + rawBody: "@agent status", + normalizedSenderId: "42", + routeAgentId: "agent", + replyToPairingRequest: vi.fn(), + }); + + expect(core.channel.mentions.buildMentionRegexes).toHaveBeenCalledWith({}, "agent", { + provider: "openclaw-simplex", + conversationId: "7", + providerPolicy: mentionPatterns, + }); + }); + it("logs group id and sender details for allowlist drops", async () => { const result = await resolveSimplexInboundAccess({ account: account({ groupPolicy: "allowlist", groupAllowFrom: ["group:99"] }), diff --git a/src/channel/events/simplex-inbound-auth.ts b/src/channel/events/simplex-inbound-auth.ts index b1fce46..050c6ec 100644 --- a/src/channel/events/simplex-inbound-auth.ts +++ b/src/channel/events/simplex-inbound-auth.ts @@ -1,6 +1,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core"; import { resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-inbound"; import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env"; +import type { SimplexAccountConfig } from "../../config/config-schema.js"; import { SIMPLEX_CHANNEL_ID } from "../../constants.js"; import type { ResolvedSimplexAccount } from "../../types/config.js"; import type { SimplexChatContext } from "../../types/events.js"; @@ -35,7 +36,15 @@ export type SimplexInboundCore = { }) => string; }; mentions: { - buildMentionRegexes: (cfg: OpenClawConfig, agentId: string) => RegExp[]; + buildMentionRegexes: ( + cfg: OpenClawConfig, + agentId: string, + options?: { + provider?: string; + conversationId?: string | null; + providerPolicy?: SimplexAccountConfig["mentionPatterns"]; + } + ) => RegExp[]; matchesMentionPatterns: (text: string, mentionRegexes: RegExp[]) => boolean; }; text: { @@ -212,7 +221,11 @@ export async function resolveSimplexInboundAccess(params: { account, groupId: context.chatId, }); - const mentionRegexes = core.channel.mentions.buildMentionRegexes(cfg, params.routeAgentId); + const mentionRegexes = core.channel.mentions.buildMentionRegexes(cfg, params.routeAgentId, { + provider: SIMPLEX_CHANNEL_ID, + conversationId: String(context.chatId), + providerPolicy: account.config.mentionPatterns, + }); const wasMentioned = core.channel.mentions.matchesMentionPatterns(rawBody, mentionRegexes); const allowTextCommands = core.channel.commands.shouldHandleTextCommands({ cfg, diff --git a/src/config/config-schema.ts b/src/config/config-schema.ts index 12ad282..688e9b9 100644 --- a/src/config/config-schema.ts +++ b/src/config/config-schema.ts @@ -8,6 +8,7 @@ import { DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, + MentionPatternsPolicySchema, ToolPolicySchema, } from "openclaw/plugin-sdk/channel-config-schema"; import { z } from "openclaw/plugin-sdk/zod"; @@ -83,6 +84,7 @@ export const SimplexAccountConfigSchema = z experimentalChannels: z.boolean().optional(), groupPolicy: GroupPolicySchema.optional(), groupAllowFrom: SimplexAllowFromListSchema, + mentionPatterns: MentionPatternsPolicySchema.optional(), groups: z.object({}).catchall(groupConfigSchema).optional(), connection: SimplexConnectionSchema.optional(), }) diff --git a/src/config/config-ui-hints.ts b/src/config/config-ui-hints.ts index bde577a..c5ade99 100644 --- a/src/config/config-ui-hints.ts +++ b/src/config/config-ui-hints.ts @@ -220,6 +220,26 @@ const accountScopedUiHints = withAccountScope({ help: "Allowed SimpleX senders for groups when groupPolicy is allowlist.", tags: ["security"], }, + mentionPatterns: { + label: "Mention Patterns", + help: "Restrict where agent name patterns count as a mention. Use allowIn/denyIn with SimpleX group ids.", + advanced: true, + }, + "mentionPatterns.mode": { + label: "Mention Pattern Mode", + help: '"allow" limits pattern matching to allowIn conversations; "deny" disables it in denyIn conversations.', + advanced: true, + }, + "mentionPatterns.allowIn": { + label: "Mention Patterns Allowed In", + help: "SimpleX group ids where agent name patterns count as a mention.", + advanced: true, + }, + "mentionPatterns.denyIn": { + label: "Mention Patterns Denied In", + help: "SimpleX group ids where agent name patterns must not count as a mention.", + advanced: true, + }, groups: { label: "Group Overrides", help: "Optional per-group overrides for mention and tool policy behavior.", From 9c0ed77af24e8c40813a0b8668cac217257423a7 Mon Sep 17 00:00:00 2001 From: dangoldbj Date: Sun, 19 Jul 2026 21:06:59 +0200 Subject: [PATCH 4/5] Add optional draftChunk live-draft chunking --- CHANGELOG.md | 1 + openclaw.plugin.json | 80 +++++++++++ .../messaging/simplex-live-reply.test.ts | 125 +++++++++++++++++- src/channel/messaging/simplex-outbound.ts | 8 +- src/channel/messaging/simplex-send.ts | 113 +++++++++++++++- src/config/config-schema.ts | 14 ++ src/config/config-ui-hints.ts | 20 +++ src/constants.ts | 6 + 8 files changed, 355 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 764e2d5..8f2187a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - Zod schemas are now built with OpenClaw's own zod instance from `openclaw/plugin-sdk/zod` instead of a plugin-owned `zod` dependency, and `zod` was dropped from runtime dependencies. - Replaced the duplicated hand-rolled positive-integer parsers in the gateway methods and plugin CLI with a shared `readRequiredPositiveInteger` helper backed by the SDK's `readPositiveIntegerParam`. - Added an optional `mentionPatterns` account policy (`mode`/`allowIn`/`denyIn`) so agent name patterns can be scoped to specific SimpleX groups, and passed SimpleX provider/conversation context into OpenClaw's mention-regex builder, which previously ignored per-conversation mention policy. +- Added optional `draftChunk` live-draft chunking (`minChars`/`maxChars`/`breakPreference`) using OpenClaw's canonical config shape. Live drafts can now trim to paragraph, newline, or sentence boundaries and cap their length; the final reply is never truncated. This is dual-read and additive: when `draftChunk` is unset, the existing `streaming.minChars`/`streaming.wordBoundary` behavior and defaults are unchanged. `streaming.throttleMs` and `streaming.nativeTransport` remain plugin-owned because the host chunking model has no equivalent. ### Fixed diff --git a/openclaw.plugin.json b/openclaw.plugin.json index 20b14a8..6b9c28e 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -150,6 +150,26 @@ }, "additionalProperties": false }, + "draftChunk": { + "type": "object", + "properties": { + "minChars": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "maxChars": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "breakPreference": { + "type": "string", + "enum": ["paragraph", "newline", "sentence"] + } + }, + "additionalProperties": false + }, "messageTtlSeconds": { "type": "integer", "exclusiveMinimum": 0, @@ -429,6 +449,26 @@ }, "additionalProperties": false }, + "draftChunk": { + "type": "object", + "properties": { + "minChars": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "maxChars": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "breakPreference": { + "type": "string", + "enum": ["paragraph", "newline", "sentence"] + } + }, + "additionalProperties": false + }, "messageTtlSeconds": { "type": "integer", "exclusiveMinimum": 0, @@ -1023,6 +1063,46 @@ "help": "Allowed SimpleX senders for groups when groupPolicy is allowlist.", "tags": ["security"] }, + "draftChunk": { + "label": "Live Draft Chunking", + "help": "Optional OpenClaw-standard live-draft chunking. When set, it overrides streaming.minChars and streaming.wordBoundary.", + "advanced": true + }, + "accounts.*.draftChunk": { + "label": "Live Draft Chunking", + "help": "Optional OpenClaw-standard live-draft chunking. When set, it overrides streaming.minChars and streaming.wordBoundary.", + "advanced": true + }, + "draftChunk.minChars": { + "label": "Draft Min Characters", + "help": "Minimum characters before the live draft updates.", + "advanced": true + }, + "accounts.*.draftChunk.minChars": { + "label": "Draft Min Characters", + "help": "Minimum characters before the live draft updates.", + "advanced": true + }, + "draftChunk.maxChars": { + "label": "Draft Max Characters", + "help": "Maximum characters shown in the live draft; the final reply is never truncated.", + "advanced": true + }, + "accounts.*.draftChunk.maxChars": { + "label": "Draft Max Characters", + "help": "Maximum characters shown in the live draft; the final reply is never truncated.", + "advanced": true + }, + "draftChunk.breakPreference": { + "label": "Draft Break Preference", + "help": "Where the live draft trims back to: paragraph, newline, or sentence.", + "advanced": true + }, + "accounts.*.draftChunk.breakPreference": { + "label": "Draft Break Preference", + "help": "Where the live draft trims back to: paragraph, newline, or sentence.", + "advanced": true + }, "mentionPatterns": { "label": "Mention Patterns", "help": "Restrict where agent name patterns count as a mention. Use allowIn/denyIn with SimpleX group ids.", diff --git a/src/channel/messaging/simplex-live-reply.test.ts b/src/channel/messaging/simplex-live-reply.test.ts index af4e033..363fece 100644 --- a/src/channel/messaging/simplex-live-reply.test.ts +++ b/src/channel/messaging/simplex-live-reply.test.ts @@ -1,6 +1,9 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import type { ResolvedSimplexAccount } from "../../types/config.js"; -import { createSimplexLiveReplyController } from "./simplex-send.js"; +import { + createSimplexLiveReplyController, + resolveSimplexLiveStreamingConfig, +} from "./simplex-send.js"; const simplexClientMock = vi.hoisted(() => ({ sendMessages: vi.fn(async () => [{ chatItem: { meta: { itemId: 10 } } }]), @@ -121,3 +124,123 @@ describe("simplex live reply controller", () => { expect(errors.join("\n")).toContain("update failed"); }); }); + +describe("live draft chunking dual-read", () => { + function accountWith(config: ResolvedSimplexAccount["config"]): ResolvedSimplexAccount { + return { ...account({}), config: { ...config } }; + } + + it("keeps the legacy streaming defaults when draftChunk is unset", () => { + const resolved = resolveSimplexLiveStreamingConfig( + accountWith({ streaming: { nativeTransport: true } }) + ); + + expect(resolved).toMatchObject({ + enabled: true, + throttleMs: 2000, + minChars: 24, + wordBoundary: true, + }); + expect(resolved.maxChars).toBeUndefined(); + expect(resolved.breakPreference).toBeUndefined(); + }); + + it("keeps explicit legacy streaming values when draftChunk is unset", () => { + expect( + resolveSimplexLiveStreamingConfig( + accountWith({ + streaming: { nativeTransport: true, minChars: 5, throttleMs: 50, wordBoundary: false }, + }) + ) + ).toMatchObject({ minChars: 5, throttleMs: 50, wordBoundary: false }); + }); + + it("switches to the shared resolver once draftChunk is configured", () => { + const resolved = resolveSimplexLiveStreamingConfig( + accountWith({ + streaming: { nativeTransport: true, minChars: 5, throttleMs: 50 }, + draftChunk: { minChars: 120, maxChars: 600, breakPreference: "sentence" }, + }) + ); + + expect(resolved).toMatchObject({ + enabled: true, + // throttleMs stays plugin-owned: the host chunking model has no equivalent. + throttleMs: 50, + minChars: 120, + maxChars: 600, + breakPreference: "sentence", + }); + }); +}); + +describe("live draft rendering with draftChunk", () => { + afterEach(() => { + simplexClientMock.sendMessages.mockClear(); + simplexClientMock.editMessage.mockClear(); + }); + + it("trims a partial draft back to the last sentence boundary", async () => { + const live = createSimplexLiveReplyController({ + cfg: {}, + account: { + ...account({ nativeTransport: true, throttleMs: 0 }), + config: { + connection: { wsUrl: "ws://127.0.0.1:5225" }, + streaming: { nativeTransport: true, throttleMs: 0 }, + draftChunk: { minChars: 1, breakPreference: "sentence" }, + }, + }, + chatRef: "@123", + }); + + await live.updatePartial({ text: "First done. Second half unfinis" }); + + expect(simplexClientMock.sendMessages).toHaveBeenCalledWith({ + chatRef: "@123", + composedMessages: [ + { + msgContent: { type: "text", text: "First done." }, + quotedItemId: undefined, + mentions: {}, + }, + ], + liveMessage: true, + ttl: undefined, + }); + }); + + it("caps the live draft at maxChars but never truncates the final reply", async () => { + const live = createSimplexLiveReplyController({ + cfg: {}, + account: { + ...account({ nativeTransport: true, throttleMs: 0 }), + config: { + connection: { wsUrl: "ws://127.0.0.1:5225" }, + streaming: { nativeTransport: true, throttleMs: 0 }, + draftChunk: { minChars: 1, maxChars: 10, breakPreference: "newline" }, + }, + }, + chatRef: "@123", + }); + + await live.updatePartial({ text: "abcdefghijklmnopqrstuvwxyz" }); + expect(simplexClientMock.sendMessages).toHaveBeenCalledWith( + expect.objectContaining({ + composedMessages: [ + expect.objectContaining({ msgContent: { type: "text", text: "abcdefghij" } }), + ], + }) + ); + + await live.finalize({ text: "abcdefghijklmnopqrstuvwxyz" }); + expect(simplexClientMock.editMessage).toHaveBeenLastCalledWith( + expect.objectContaining({ + updatedMessage: expect.objectContaining({ + msgContent: { type: "text", text: "abcdefghijklmnopqrstuvwxyz" }, + }), + liveMessage: false, + }) + ); + }); +}); diff --git a/src/channel/messaging/simplex-outbound.ts b/src/channel/messaging/simplex-outbound.ts index a30581b..9ad900b 100644 --- a/src/channel/messaging/simplex-outbound.ts +++ b/src/channel/messaging/simplex-outbound.ts @@ -4,7 +4,7 @@ import { renderMessagePresentationFallbackText } from "openclaw/plugin-sdk/inter import { normalizePollInput } from "openclaw/plugin-sdk/poll-runtime"; import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking"; import { resolveSimplexAccount } from "../../config/accounts.js"; -import { SIMPLEX_CHANNEL_ID } from "../../constants.js"; +import { SIMPLEX_CHANNEL_ID, SIMPLEX_TEXT_CHUNK_LIMIT } from "../../constants.js"; import type { ResolvedSimplexAccount } from "../../types/config.js"; import { assertSimplexOutboundAccountReady, @@ -64,9 +64,9 @@ export function buildSimplexOutbound(): NonNullable< > { return { deliveryMode: "direct" as const, - // SimpleX silently drops JSON messages over ~15,610 bytes. `textChunkLimit` - // alone does nothing: the dispatcher only splits when a `chunker` is set too. - textChunkLimit: 4000, + // `textChunkLimit` alone does nothing: the dispatcher only splits when a + // `chunker` is set too. + textChunkLimit: SIMPLEX_TEXT_CHUNK_LIMIT, chunker: chunkTextForOutbound, chunkerMode: "markdown", shouldTreatDeliveredTextAsVisible: ({ kind, text }) => diff --git a/src/channel/messaging/simplex-send.ts b/src/channel/messaging/simplex-send.ts index d522dd9..a72d8c3 100644 --- a/src/channel/messaging/simplex-send.ts +++ b/src/channel/messaging/simplex-send.ts @@ -1,5 +1,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core"; import type { MessageReceipt } from "openclaw/plugin-sdk/channel-outbound"; +import { resolveChannelStreamingPreviewChunk } from "openclaw/plugin-sdk/channel-streaming"; +import { SIMPLEX_TEXT_CHUNK_LIMIT } from "../../constants.js"; import { parseSimplexNumericId, resolveSimplexChatItemId } from "../../simplex/runtime/api.js"; import { withSimplexClient } from "../../simplex/runtime/transport.js"; import type { ResolvedSimplexAccount } from "../../types/config.js"; @@ -107,18 +109,67 @@ export type SimplexLiveReplyPayload = { replyToId?: string | number | null; }; -export function resolveSimplexLiveStreamingConfig(account: ResolvedSimplexAccount): { +// Mirrors OpenClaw's draft-stream defaults, which are not exported to plugins. +const DEFAULT_DRAFT_STREAM_MIN = 200; +const DEFAULT_DRAFT_STREAM_MAX = 800; + +export type SimplexLiveStreamingConfig = { enabled: boolean; throttleMs: number; minChars: number; + maxChars?: number; + breakPreference?: "paragraph" | "newline" | "sentence"; wordBoundary: boolean; -} { +}; + +/** + * Live-draft chunking is dual-read. When OpenClaw's canonical chunk config is + * present, chunk sizes and break points come from it. Otherwise the original + * `streaming.*` behavior and defaults are kept, so existing installs are + * unaffected. + * + * `throttleMs` and `nativeTransport` stay plugin-owned: the host chunking model + * has no equivalent for either. + */ +export function resolveSimplexLiveStreamingConfig( + account: ResolvedSimplexAccount +): SimplexLiveStreamingConfig { const streaming = account.config.streaming; + const enabled = streaming?.nativeTransport === true; + const throttleMs = streaming?.throttleMs ?? 2000; + const wordBoundary = streaming?.wordBoundary ?? true; + + // `account.config` is the merged channel+account view, so it is the single + // source of truth here. The SDK reader is used for the *shape* (it accepts + // both `streaming.preview.chunk` and `draftChunk`), which is the part that + // tracks host config conventions. + const chunk = resolveChannelStreamingPreviewChunk(account.config); + if (chunk) { + const maxChars = Math.min( + Math.max(1, Math.floor(chunk.maxChars ?? DEFAULT_DRAFT_STREAM_MAX)), + SIMPLEX_TEXT_CHUNK_LIMIT + ); + return { + enabled, + throttleMs, + minChars: Math.min( + Math.max(1, Math.floor(chunk.minChars ?? DEFAULT_DRAFT_STREAM_MIN)), + maxChars + ), + maxChars, + breakPreference: + chunk.breakPreference === "newline" || chunk.breakPreference === "sentence" + ? chunk.breakPreference + : "paragraph", + wordBoundary, + }; + } + return { - enabled: streaming?.nativeTransport === true, - throttleMs: streaming?.throttleMs ?? 2000, + enabled, + throttleMs, minChars: streaming?.minChars ?? 24, - wordBoundary: streaming?.wordBoundary ?? true, + wordBoundary, }; } @@ -134,6 +185,32 @@ function trimToWordBoundary(text: string): string { return trimmed.slice(0, lastSpace).trimEnd(); } +const BREAK_MARKERS: Record< + NonNullable, + string[] +> = { + paragraph: ["\n\n"], + newline: ["\n"], + sentence: [". ", "! ", "? ", ".\n", "!\n", "?\n"], +}; + +/** + * Trims a partial draft back to the last configured break point so the live + * message does not flicker mid-sentence. Falls back to word-boundary trimming + * when no break point has been reached yet. + */ +function trimToBreakPreference( + text: string, + breakPreference: NonNullable +): string { + const trimmed = text.trimEnd(); + let cut = -1; + for (const marker of BREAK_MARKERS[breakPreference]) { + cut = Math.max(cut, trimmed.lastIndexOf(marker) + marker.length); + } + return cut > 0 ? trimmed.slice(0, cut).trimEnd() : trimToWordBoundary(trimmed); +} + function payloadHasMedia(payload: SimplexLiveReplyPayload): boolean { return Boolean( payload.mediaUrl || @@ -141,8 +218,28 @@ function payloadHasMedia(payload: SimplexLiveReplyPayload): boolean { ); } -function renderLiveText(text: string, params: { final?: boolean; wordBoundary: boolean }): string { - return params.final || !params.wordBoundary ? text.trimEnd() : trimToWordBoundary(text); +function renderLiveText( + text: string, + params: { + final?: boolean; + wordBoundary: boolean; + maxChars?: number; + breakPreference?: SimplexLiveStreamingConfig["breakPreference"]; + } +): string { + // `maxChars` caps the live draft only. A final reply is never truncated here; + // outbound chunking owns splitting it. + const capped = + !params.final && params.maxChars !== undefined && text.length > params.maxChars + ? text.slice(0, params.maxChars) + : text; + if (params.final) { + return capped.trimEnd(); + } + if (params.breakPreference) { + return trimToBreakPreference(capped, params.breakPreference); + } + return params.wordBoundary ? trimToWordBoundary(capped) : capped.trimEnd(); } export function createSimplexLiveReplyController(params: { @@ -216,6 +313,8 @@ export function createSimplexLiveReplyController(params: { const rendered = renderLiveText(text, { final: options.final, wordBoundary: live.wordBoundary, + maxChars: live.maxChars, + breakPreference: live.breakPreference, }); if (!rendered) { return true; diff --git a/src/config/config-schema.ts b/src/config/config-schema.ts index 688e9b9..58a6247 100644 --- a/src/config/config-schema.ts +++ b/src/config/config-schema.ts @@ -56,6 +56,19 @@ const SimplexStreamingSchema = z }) .strict(); +/** + * OpenClaw's canonical live-draft chunking shape. Reading it through the SDK + * keeps SimpleX aligned with host streaming conventions; when unset, the legacy + * `streaming.minChars` / `streaming.wordBoundary` behavior is preserved. + */ +const SimplexDraftChunkSchema = z + .object({ + minChars: z.number().int().positive().optional(), + maxChars: z.number().int().positive().optional(), + breakPreference: z.enum(["paragraph", "newline", "sentence"]).optional(), + }) + .strict(); + const SimplexFilePolicySchema = z .object({ autoAccept: z.boolean().optional(), @@ -79,6 +92,7 @@ export const SimplexAccountConfigSchema = z blockStreaming: z.boolean().optional(), blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(), streaming: SimplexStreamingSchema.optional(), + draftChunk: SimplexDraftChunkSchema.optional(), messageTtlSeconds: z.number().int().positive().optional(), filePolicy: SimplexFilePolicySchema.optional(), experimentalChannels: z.boolean().optional(), diff --git a/src/config/config-ui-hints.ts b/src/config/config-ui-hints.ts index c5ade99..dd64741 100644 --- a/src/config/config-ui-hints.ts +++ b/src/config/config-ui-hints.ts @@ -220,6 +220,26 @@ const accountScopedUiHints = withAccountScope({ help: "Allowed SimpleX senders for groups when groupPolicy is allowlist.", tags: ["security"], }, + draftChunk: { + label: "Live Draft Chunking", + help: "Optional OpenClaw-standard live-draft chunking. When set, it overrides streaming.minChars and streaming.wordBoundary.", + advanced: true, + }, + "draftChunk.minChars": { + label: "Draft Min Characters", + help: "Minimum characters before the live draft updates.", + advanced: true, + }, + "draftChunk.maxChars": { + label: "Draft Max Characters", + help: "Maximum characters shown in the live draft; the final reply is never truncated.", + advanced: true, + }, + "draftChunk.breakPreference": { + label: "Draft Break Preference", + help: "Where the live draft trims back to: paragraph, newline, or sentence.", + advanced: true, + }, mentionPatterns: { label: "Mention Patterns", help: "Restrict where agent name patterns count as a mention. Use allowIn/denyIn with SimpleX group ids.", diff --git a/src/constants.ts b/src/constants.ts index 817ae9a..e750ed3 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -4,6 +4,12 @@ export const LEGACY_SIMPLEX_PLUGIN_ID = "simplex"; export const LEGACY_SIMPLEX_CHANNEL_ID = "simplex"; export const SIMPLEX_PROVIDER_PREFIXES = [SIMPLEX_CHANNEL_ID, LEGACY_SIMPLEX_CHANNEL_ID] as const; +/** + * Outbound text chunk limit. SimpleX silently drops JSON messages over ~15,610 + * bytes, so outbound splitting and live-draft chunking share this ceiling. + */ +export const SIMPLEX_TEXT_CHUNK_LIMIT = 4000; + /** Default runtime folders (simplex-chat `--files-folder` / `--temp-folder`). */ export const DEFAULT_SIMPLEX_FILES_FOLDER = "~/.simplex/files"; export const DEFAULT_SIMPLEX_TEMP_FOLDER = "~/.simplex/tmp"; From ae06c562aa7e51a2c2ce482cdef47c597a8d8a3c Mon Sep 17 00:00:00 2001 From: dangoldbj Date: Sun, 19 Jul 2026 21:07:10 +0200 Subject: [PATCH 5/5] Validate poll duration as a positive integer --- CHANGELOG.md | 1 + src/actions/schema.ts | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f2187a..a3de56d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. - Replaced the duplicated hand-rolled positive-integer parsers in the gateway methods and plugin CLI with a shared `readRequiredPositiveInteger` helper backed by the SDK's `readPositiveIntegerParam`. - Added an optional `mentionPatterns` account policy (`mode`/`allowIn`/`denyIn`) so agent name patterns can be scoped to specific SimpleX groups, and passed SimpleX provider/conversation context into OpenClaw's mention-regex builder, which previously ignored per-conversation mention policy. - Added optional `draftChunk` live-draft chunking (`minChars`/`maxChars`/`breakPreference`) using OpenClaw's canonical config shape. Live drafts can now trim to paragraph, newline, or sentence boundaries and cap their length; the final reply is never truncated. This is dual-read and additive: when `draftChunk` is unset, the existing `streaming.minChars`/`streaming.wordBoundary` behavior and defaults are unchanged. `streaming.throttleMs` and `streaming.nativeTransport` remain plugin-owned because the host chunking model has no equivalent. +- Poll duration is now validated as a positive integer through the shared SDK schema helper instead of an unbounded integer. ### Fixed diff --git a/src/actions/schema.ts b/src/actions/schema.ts index e8fb788..4a33853 100644 --- a/src/actions/schema.ts +++ b/src/actions/schema.ts @@ -1,4 +1,5 @@ import { Type } from "@sinclair/typebox"; +import { optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions"; import type { ChannelMessageActionName, ChannelMessageToolSchemaContribution, @@ -145,11 +146,9 @@ export function buildSimplexMessageToolSchema(): ChannelMessageToolSchemaContrib description: "Allow multiple poll selections.", }) ), - pollDurationHours: Type.Optional( - Type.Integer({ - description: "Optional poll window in hours.", - }) - ), + pollDurationHours: optionalPositiveIntegerSchema({ + description: "Optional poll window in hours.", + }), displayName: Type.Optional( Type.String({ description: "New SimpleX group display name.",