Compare commits

..

No commits in common. "main" and "pe/use-clawhub-reusable-workflow" have entirely different histories.

35 changed files with 1434 additions and 6284 deletions

View File

@ -17,8 +17,8 @@ jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
@ -26,7 +26,7 @@ jobs:
- run: npm test
- run: npm run plugin:inspect:runtime
- run: npm run pack:check
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
if: always()
with:
name: plugin-inspector-reports

View File

@ -25,7 +25,7 @@ jobs:
draft:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
ref: ${{ inputs.target || 'main' }}
fetch-depth: 0

View File

@ -148,6 +148,8 @@ jobs:
npm publish "./release-artifacts/${{ needs.validate.outputs.tarball_name }}" --access public --tag "${{ needs.validate.outputs.npm_tag }}"
publish-clawhub:
# Disabled until the kitchen-sink package exists in the ClawHub org/registry.
if: ${{ false }}
needs: validate
permissions:
contents: read
@ -155,7 +157,6 @@ jobs:
uses: openclaw/clawhub/.github/workflows/package-publish.yml@main
with:
dry_run: false
owner: openclaw
ref: ${{ needs.validate.outputs.tag_name }}
version: ${{ needs.validate.outputs.package_version }}
source_ref: refs/tags/${{ needs.validate.outputs.tag_name }}

View File

@ -20,8 +20,8 @@ jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

View File

@ -8,7 +8,7 @@ Work from repo root. Keep changes small and commit/push them to `main` when aske
- Trusted publisher: GitHub Actions, repository `openclaw/kitchen-sink`, workflow `release.yml`.
- Do not publish npm releases locally. Cut releases by bumping `package.json`/`package-lock.json`, syncing generated surface files, pushing `main`, creating an annotated `vX.Y.Z` tag, pushing the tag, then publishing the GitHub release with `gh release create vX.Y.Z --verify-tag --generate-notes --title vX.Y.Z`.
- The `release.yml` workflow owns npm publishing through OIDC trusted publishing. Keep `permissions.id-token: write`; do not add `NODE_AUTH_TOKEN` or long-lived npm token secrets for publish.
- ClawHub release publishing is enabled through the canonical reusable ClawHub workflow. Keep `permissions.id-token: write` and continue passing the `CLAWHUB_TOKEN` secret for release publishes.
- ClawHub release publishing is intentionally disabled until the ClawHub package/org setup exists. Keep dry-run CI wired, but do not enable real ClawHub publish without credentials and registry ownership being ready.
## Validation

100
README.md
View File

@ -11,103 +11,38 @@ This repo is both:
The generated runtime probes are credential-free. The hand-owned Kitchen Sink
runtime also registers deterministic direct commands, tools, image generation,
speech, realtime transcription/voice, video, music, media understanding, web
search, web fetch, memory, compaction, gateway/service/CLI, channel, hook,
detached-task, and text-provider catalog surfaces.
media understanding, web search, web fetch, channel, hook, detached-task, and
text-provider catalog surfaces.
It should not call external services, read secrets, spawn processes, or require
live credentials.
The plugin exposes three test personalities through
`plugins.entries.openclaw-kitchen-sink-fixture.config.personality`:
- `full` is the default compatibility mode and keeps both generated probe
registrations and the hand-owned runtime.
- `conformance` loads only the valid runtime surfaces and skips intentionally
invalid probes so OpenClaw can assert a clean external-plugin install.
- `adversarial` loads only generated invalid probes so OpenClaw can assert
expected diagnostics without mixing them with a live runtime smoke.
## Source Layout
The hand-owned runtime is intentionally split by plugin surface so it can be
used as reference code instead of one giant fixture file:
- `src/index.js` selects the Kitchen Sink personality and registers the runtime
plus generated probes.
- `src/kitchen-runtime.js` is the runtime registrar entrypoint. It wires
builders together but keeps the implementation in smaller modules.
- `src/runtime/commands.js`, `channel.js`, `providers.js`, `tasks.js`, and
`platform.js` hold the command/tool, channel, provider, detached-task, and
service/gateway/CLI registrations.
- `src/scenarios.js` is the deterministic scenario router shared by dry
commands, tools, providers, hooks, channel delivery, and tests.
- `src/fixtures/` holds deterministic mock payloads such as the bundled image
asset and text-provider stream fixture.
- `src/generated-*` files are diagnostic surface probes generated from the
installed OpenClaw SDK. They are not the code plugin authors should copy.
- `scripts/lib/` holds test harness code reused by runtime and contract probes;
`scripts/fixtures/` holds reviewable consumer-smoke programs.
## Kitchen Runtime
The fixture can be used dry, without an LLM:
```text
kitchen image generate a kitchen sink
kitchen image rate limit
kitchen image timeout
kitchen search kitchen sink provider routing
kitchen fetch kitchen://fixture/redirect
kitchen explain the fixture
```
It also exposes provider and tool surfaces for live model routing:
- `listKitchenHumanScenarios()` and `runKitchenHumanScenario(runtime, id)`
provide deterministic end-to-end user scenarios for fixture consumers:
`dry.prefix-image`, `live.openai-text-kitchen-image`,
`search.fetch.summarize`, `channel.prefix-image`, `hook.block-tool`, and
`memory.compact-fixture`.
- When a live text provider such as OpenAI is active and Kitchen Sink is
selected as the image provider, the `live.openai-text-kitchen-image` scenario
proves the human prompt can route to the Kitchen Sink image provider and
return the bundled `kitchen_sink_office.png` asset without external image
credentials.
- The `hook.block-tool` scenario proves terminal `before_tool_call` blocking,
and the contract probe script also checks the approval path and conversation
privacy observations for `llm_input`, `llm_output`, and `agent_end`.
- `src/scenarios.js` routes deterministic user scenarios; reusable mock payloads
live in `src/fixtures/`.
- `src/scenarios.js` is the shared deterministic fixture engine used by dry
commands, tools, providers, hooks, channel delivery, and tests.
- `kitchen_sink_image_job` returns a deterministic image job, waits 10 seconds
in real runtime execution, then returns the bundled `kitchen_sink_office.png`
image payload with PNG dimensions, byte size, SHA-256 hash, seed, model, and
finish metadata.
in real runtime execution, then returns a bundled SVG image payload.
- `kitchen-sink-image` is a registered image generation provider with aliases
`kitchen`, `kitchen-sink`, and `openclaw-kitchen-sink`; prompts containing
`rate limit`, `timeout`, or `fail` exercise deterministic provider error
paths.
`kitchen`, `kitchen-sink`, and `openclaw-kitchen-sink`.
- `kitchen-sink-media` describes images with deterministic fixture text.
- `kitchen-sink-speech`, `kitchen-sink-realtime-transcription`,
`kitchen-sink-realtime-voice`, `kitchen-sink-video`, and
`kitchen-sink-music` expose credential-free media provider fixtures with
deterministic WAV, transcript, bridge, storyboard, and track payloads.
- `kitchen-sink-search` and `kitchen-sink-fetch` provide credential-free web
tool fixtures with realistic status codes, request ids, result metadata,
redirects, headers, cache metadata, links, and markdown content.
- `kitchen-sink-memory-embedding`, `kitchen-sink-memory-corpus`, and
`kitchen-sink-compaction` provide deterministic memory vectors, corpus
results, reads, and transcript summaries.
tool fixtures.
- `kitchen-sink-channel` is a credential-free channel fixture that can resolve
local ready/disabled/misconfigured accounts, route outbound sessions, and
deliver deterministic text/media records.
- `kitchen.status`, `/kitchen-sink/status`, `kitchen-sink-service`, and the
lazy CLI descriptor exercise gateway method, HTTP route, service, and CLI
registration surfaces.
local accounts, route outbound sessions, and deliver deterministic text/media
records.
- `kitchen-sink-llm` exposes a deterministic text-provider catalog row,
provider-owned stream function, and prompt guidance so live LLM providers can
discover the Kitchen Sink routes; responses describe which real plugin
surface would handle image, search, fetch, and failure prompts.
discover the Kitchen Sink routes.
- generated hooks classify Kitchen Sink prompts, tool calls, and provider
selections into shared scenario ids such as `image.generate`, `web.search`,
and `text.reply`.
@ -131,11 +66,8 @@ contract coverage.
```sh
npm install
npm run sync:surface
npm run check:runtime
npm run check:inspector
npm run check:install
npm test
npm run pack:check
npm run pack:zip
```
The `Update OpenClaw SDK Surface` workflow automatically checks
@ -155,11 +87,6 @@ Tagged GitHub releases publish the validated package to npm through trusted
publishing. The release tag must match `package.json`, for example `v0.0.1` for
version `0.0.1`.
`npm pack` remains the canonical npm artifact. `npm run pack:zip` builds the
legacy archive artifact at `dist/openclaw-kitchen-sink-fixture-<version>.zip`
with `package.json`, `openclaw.plugin.json`, `plugin-inspector.config.json`,
`README.md`, and `src/**` at the archive root for old archive installers.
Use the `Draft Release` workflow to create the tag and generated GitHub release
notes. Publishing that draft release runs the npm publish workflow. `0.0.x`
verification releases publish under the `verification` npm dist-tag so they do
@ -167,5 +94,6 @@ not replace the stable `latest` tag.
Pull requests run a ClawHub package-publish dry run through the canonical
`openclaw/clawhub` reusable workflow on `main`, so the fixture tests the current
ClawHub publishing path instead of a vendored copy. Releases publish to ClawHub
through the same canonical workflow after validation.
ClawHub publishing path instead of a vendored copy. Release publishing is wired
the same way, but remains disabled until the ClawHub org/package ownership for
this fixture is set up.

View File

@ -1,8 +1,8 @@
{
"id": "openclaw-kitchen-sink-fixture",
"name": "OpenClaw Kitchen Sink",
"version": "0.2.5",
"description": "Generated kitchen-sink fixture for OpenClaw plugin API surface 2026.5.7.",
"version": "0.1.2",
"description": "Generated kitchen-sink fixture for OpenClaw plugin API surface 2026.4.26.",
"enabledByDefault": false,
"kind": [
"tool",
@ -15,11 +15,7 @@
],
"providers": [
"kitchen-sink-provider",
"kitchen-sink-llm",
"kitchen-sink-image",
"kitchen-sink-speech",
"kitchen-sink-video",
"kitchen-sink-music"
"kitchen-sink-llm"
],
"cliBackends": [
"kitchen-sink-cli-backend"
@ -38,10 +34,7 @@
"onProviders": [
"kitchen-sink-provider",
"kitchen-sink-llm",
"kitchen-sink-image",
"kitchen-sink-speech",
"kitchen-sink-video",
"kitchen-sink-music"
"kitchen-sink-image"
],
"onChannels": [
"kitchen-sink-channel"
@ -57,42 +50,6 @@
"hook"
]
},
"channelConfigs": {
"kitchen-sink-channel": {
"schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"configured": {
"type": "boolean",
"default": true
},
"disabled": {
"type": "boolean",
"default": false
},
"enabled": {
"type": "boolean",
"default": true
},
"token": {
"type": "string"
}
}
},
"uiHints": {
"token": {
"sensitive": true
}
},
"label": "Kitchen Sink",
"description": "Credential-free channel fixture for deterministic Kitchen Sink conversations.",
"commands": {
"nativeCommandsAutoEnabled": true,
"nativeSkillsAutoEnabled": true
}
}
},
"setup": {
"providers": [
{
@ -115,41 +72,6 @@
"none"
],
"envVars": []
},
{
"id": "kitchen-sink-speech",
"authMethods": [
"none"
],
"envVars": []
},
{
"id": "kitchen-sink-realtime-transcription",
"authMethods": [
"none"
],
"envVars": []
},
{
"id": "kitchen-sink-realtime-voice",
"authMethods": [
"none"
],
"envVars": []
},
{
"id": "kitchen-sink-video",
"authMethods": [
"none"
],
"envVars": []
},
{
"id": "kitchen-sink-music",
"authMethods": [
"none"
],
"envVars": []
}
],
"cliBackends": [
@ -182,27 +104,22 @@
"kitchen-sink-media"
],
"memoryEmbeddingProviders": [
"kitchen-sink-memory-embedding-providers",
"kitchen-sink-memory-embedding"
"kitchen-sink-memory-embedding-providers"
],
"migrationProviders": [
"kitchen-sink-migration-providers"
],
"musicGenerationProviders": [
"kitchen-sink-music-generation-providers",
"kitchen-sink-music"
"kitchen-sink-music-generation-providers"
],
"realtimeTranscriptionProviders": [
"kitchen-sink-realtime-transcription-providers",
"kitchen-sink-realtime-transcription"
"kitchen-sink-realtime-transcription-providers"
],
"realtimeVoiceProviders": [
"kitchen-sink-realtime-voice-providers",
"kitchen-sink-realtime-voice"
"kitchen-sink-realtime-voice-providers"
],
"speechProviders": [
"kitchen-sink-speech-providers",
"kitchen-sink-speech"
"kitchen-sink-speech-providers"
],
"tools": [
"kitchen-sink-tools",
@ -211,8 +128,7 @@
"kitchen_sink_search"
],
"videoGenerationProviders": [
"kitchen-sink-video-generation-providers",
"kitchen-sink-video"
"kitchen-sink-video-generation-providers"
],
"webContentExtractors": [
"kitchen-sink-web-content-extractors"
@ -233,15 +149,6 @@
"enabled": {
"type": "boolean",
"default": false
},
"personality": {
"type": "string",
"enum": [
"full",
"conformance",
"adversarial"
],
"default": "full"
}
}
}

2497
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/kitchen-sink",
"version": "0.2.5",
"version": "0.1.2",
"private": false,
"description": "Credential-free kitchen-sink OpenClaw plugin fixture covering the public plugin API surface.",
"type": "module",
@ -30,8 +30,7 @@
},
"exports": {
".": "./src/index.js",
"./personality": "./src/personality.js",
"./runtime": "./src/kitchen-runtime.js",
"./runtime": "./src/index.js",
"./scenarios": "./src/scenarios.js",
"./setup": "./src/setup.js"
},
@ -46,29 +45,14 @@
"compat": {
"pluginApi": "2026.4"
},
"install": {
"clawhubSpec": "clawhub:@openclaw/kitchen-sink",
"npmSpec": "@openclaw/kitchen-sink",
"defaultChoice": "clawhub",
"minHostVersion": ">=2026.5.7"
},
"release": {
"publishToClawHub": true,
"publishToNpm": true
},
"build": {
"openclawVersion": "2026.5.7",
"pluginSdkVersion": "2026.5.7"
"openclawVersion": "2026.4.26",
"pluginSdkVersion": "2026.4.26"
}
},
"scripts": {
"acceptance:install": "node scripts/check-installed-package.mjs",
"check": "npm run sync:surface -- --check && node scripts/check-sdk-surface.mjs && npm run check:runtime && npm run plugin:inspect && npm run check:install",
"check:install": "node scripts/check-installed-package.mjs",
"check:inspector": "npm run plugin:inspect && npm run plugin:inspect:runtime",
"check:runtime": "node scripts/check-kitchen-runtime.mjs && node scripts/check-kitchen-contract-probes.mjs",
"check": "npm run sync:surface -- --check && node scripts/check-sdk-surface.mjs && node scripts/check-kitchen-runtime.mjs && npm run plugin:inspect",
"pack:check": "node scripts/check-pack-payload.mjs",
"pack:zip": "node scripts/pack-zip.mjs",
"plugin:inspect": "plugin-inspector check --config plugin-inspector.config.json --no-openclaw",
"plugin:inspect:runtime": "PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 plugin-inspector check --config plugin-inspector.config.json --no-openclaw --runtime --mock-sdk",
"sdk:target-check": "node scripts/check-target-sdk-imports.mjs",
@ -76,14 +60,10 @@
"test": "npm run check"
},
"dependencies": {
"openclaw": "2026.5.7"
"openclaw": "2026.4.26"
},
"devDependencies": {
"@openclaw/plugin-inspector": "0.3.10"
},
"overrides": {
"@anthropic-ai/sdk": "0.91.1",
"uuid": "14.0.0"
"@openclaw/plugin-inspector": "0.3.3"
},
"engines": {
"node": ">=22"

View File

@ -1,549 +0,0 @@
{
"beforeToolCall": {
"allow": {
"kitchenSink": true,
"pluginId": "openclaw-kitchen-sink-fixture",
"hook": "before_tool_call",
"route": "hook:before_tool_call",
"matchedKitchen": true,
"scenarioId": "image.generate",
"observedEventKeys": [
"toolId",
"args"
],
"observedContextKeys": [
"providerId"
],
"params": {
"args": {
"prompt": "generate a kitchen image",
"kitchenSinkScenario": "image.generate",
"kitchenSinkPluginId": "openclaw-kitchen-sink-fixture"
}
},
"decision": "allow"
},
"block": {
"kitchenSink": true,
"pluginId": "openclaw-kitchen-sink-fixture",
"hook": "before_tool_call",
"route": "hook:before_tool_call",
"matchedKitchen": true,
"scenarioId": "image.generate",
"observedEventKeys": [
"toolId",
"args"
],
"observedContextKeys": [
"providerId"
],
"params": {
"args": {
"prompt": "kitchen block image generation",
"kitchenSinkScenario": "image.generate",
"kitchenSinkPluginId": "openclaw-kitchen-sink-fixture"
}
},
"block": true,
"blockReason": "Kitchen Sink fixture blocked kitchen_sink_image_job for image.generate.",
"terminal": true,
"decision": "block"
},
"approval": {
"kitchenSink": true,
"pluginId": "openclaw-kitchen-sink-fixture",
"hook": "before_tool_call",
"route": "hook:before_tool_call",
"matchedKitchen": true,
"scenarioId": "image.generate",
"observedEventKeys": [
"toolId",
"args"
],
"observedContextKeys": [
"providerId"
],
"params": {
"args": {
"prompt": "kitchen image generation needs approval",
"kitchenSinkScenario": "image.generate",
"kitchenSinkPluginId": "openclaw-kitchen-sink-fixture"
}
},
"requireApproval": {
"id": "ks_approval_9863b78c",
"title": "Kitchen Sink tool approval",
"reason": "Kitchen Sink fixture requires approval before kitchen_sink_image_job runs.",
"summary": "Approve deterministic image.generate fixture execution.",
"scenarioId": "image.generate",
"pluginId": "openclaw-kitchen-sink-fixture"
},
"decision": "approval"
}
},
"conversationPrivacy": {
"input": {
"kitchenSink": true,
"pluginId": "openclaw-kitchen-sink-fixture",
"hook": "llm_input",
"route": "hook:llm_input",
"matchedKitchen": true,
"scenarioId": "text.reply",
"observedEventKeys": [
"prompt",
"apiKey",
"token"
],
"observedContextKeys": [
"providerId",
"authorization"
],
"privacy": {
"boundary": "conversation-observer",
"promptHash": "21ed2705",
"promptLength": 55,
"redactedFields": [
"event.apiKey",
"event.token",
"context.authorization"
],
"secretPatternCount": 3,
"storesRawPayload": false,
"exposesRawPayload": false
}
},
"output": {
"kitchenSink": true,
"pluginId": "openclaw-kitchen-sink-fixture",
"hook": "llm_output",
"route": "hook:llm_output",
"matchedKitchen": true,
"scenarioId": "text.reply",
"observedEventKeys": [
"prompt",
"apiKey",
"token"
],
"observedContextKeys": [
"providerId",
"authorization"
],
"privacy": {
"boundary": "conversation-observer",
"promptHash": "a3e6f809",
"promptLength": 41,
"redactedFields": [
"event.apiKey",
"event.token",
"context.authorization"
],
"secretPatternCount": 3,
"storesRawPayload": false,
"exposesRawPayload": false
}
},
"end": {
"kitchenSink": true,
"pluginId": "openclaw-kitchen-sink-fixture",
"hook": "agent_end",
"route": "hook:agent_end",
"matchedKitchen": true,
"scenarioId": "text.reply",
"observedEventKeys": [
"prompt",
"apiKey",
"token"
],
"observedContextKeys": [
"providerId",
"authorization"
],
"privacy": {
"boundary": "conversation-observer",
"promptHash": "8bf533dd",
"promptLength": 41,
"redactedFields": [
"event.apiKey",
"event.token",
"context.authorization"
],
"secretPatternCount": 3,
"storesRawPayload": false,
"exposesRawPayload": false
}
}
},
"channel": {
"account": {
"accountId": "local",
"name": "Kitchen Sink Local",
"enabled": true,
"configured": true,
"statusState": "ready",
"linked": true,
"running": true,
"connected": true,
"mode": "local",
"health": {
"ok": true,
"checkedAt": "2026-04-28T00:00:00.000Z",
"message": "Kitchen Sink local fixture account is ready."
},
"capabilities": [
"text",
"media",
"threads",
"dry-run"
]
},
"delivery": {
"channel": "kitchen-sink-channel",
"messageId": "ks_channel_d813aa04",
"conversationId": "kitchen-demo",
"channelId": "kitchen-demo",
"timestamp": 1777334400000,
"deliveryStatus": "sent",
"transport": "kitchen-sink-local",
"meta": {
"kitchenSink": true,
"pluginId": "openclaw-kitchen-sink-fixture",
"scenarioId": "image.generate",
"kind": "text"
}
},
"route": {
"sessionKey": "kitchen:fixture-agent:kitchen-demo",
"baseSessionKey": "kitchen:fixture-agent:kitchen-demo",
"peer": {
"kind": "direct",
"id": "kitchen-demo"
},
"chatType": "direct",
"from": "local",
"to": "kitchen-demo",
"threadId": "thread-1"
}
},
"runtimeRegistrations": {
"registerAgentEventSubscription": {
"count": 1,
"ids": [
"kitchen-sink-agent-event-subscription"
]
},
"registerAgentHarness": {
"count": 1,
"ids": [
"kitchen-sink-agent-harness"
]
},
"registerAgentToolResultMiddleware": {
"count": 2,
"ids": [
"kitchen-sink-agent-tool-result-middleware",
"kitchen-sink-agent-tool-result-middleware"
]
},
"registerAutoEnableProbe": {
"count": 1,
"ids": [
"kitchen-sink-auto-enable-probe"
]
},
"registerChannel": {
"count": 2,
"ids": [
"kitchen-sink-channel",
"kitchen-sink-channel-probe"
]
},
"registerCli": {
"count": 2,
"ids": [
"kitchen-sink",
"kitchen-sink-cli"
]
},
"registerCliBackend": {
"count": 1,
"ids": [
"kitchen-sink-cli-backend"
]
},
"registerCodexAppServerExtensionFactory": {
"count": 1,
"ids": [
"kitchen-sink-codex-app-server-extension-factory"
]
},
"registerCommand": {
"count": 3,
"ids": [
"kitchen",
"kitchen-sink",
"kitchen-sink-command"
]
},
"registerCompactionProvider": {
"count": 2,
"ids": [
"kitchen-sink-compaction",
"kitchen-sink-compaction-provider"
]
},
"registerConfigMigration": {
"count": 1,
"ids": [
"kitchen-sink-config-migration"
]
},
"registerContextEngine": {
"count": 1,
"ids": [
"kitchen-sink-context-engine"
]
},
"registerControlUiDescriptor": {
"count": 1,
"ids": [
"kitchen-sink-control-ui-descriptor"
]
},
"registerDetachedTaskRuntime": {
"count": 1,
"ids": [
"kitchen-sink-detached-task-runtime"
]
},
"registerGatewayDiscoveryService": {
"count": 1,
"ids": [
"kitchen-sink-gateway-discovery-service"
]
},
"registerGatewayMethod": {
"count": 2,
"ids": [
"kitchen-sink-gateway-method",
"kitchen.status"
]
},
"registerHook": {
"count": 1,
"ids": [
"kitchen-sink-hook"
]
},
"registerHttpRoute": {
"count": 2,
"ids": [
"kitchen-sink-http-route",
"kitchen-sink-http-status"
]
},
"registerImageGenerationProvider": {
"count": 2,
"ids": [
"kitchen-sink-image",
"kitchen-sink-image-generation-provider"
]
},
"registerInteractiveHandler": {
"count": 2,
"ids": [
"kitchen-sink-interactive-handler",
"kitchen-sink-interactive-handler"
]
},
"registerMediaUnderstandingProvider": {
"count": 2,
"ids": [
"kitchen-sink-media",
"kitchen-sink-media-understanding-provider"
]
},
"registerMemoryCapability": {
"count": 1,
"ids": [
"kitchen-sink-memory-capability"
]
},
"registerMemoryCorpusSupplement": {
"count": 2,
"ids": [
"kitchen-sink-memory-corpus",
"kitchen-sink-memory-corpus-supplement"
]
},
"registerMemoryEmbeddingProvider": {
"count": 2,
"ids": [
"kitchen-sink-memory-embedding",
"kitchen-sink-memory-embedding-provider"
]
},
"registerMemoryFlushPlan": {
"count": 1,
"ids": [
"kitchen-sink-memory-flush-plan"
]
},
"registerMemoryPromptSection": {
"count": 1,
"ids": [
"kitchen-sink-memory-prompt-section"
]
},
"registerMemoryPromptSupplement": {
"count": 2,
"ids": [
"kitchen-sink-memory-prompt-supplement",
"kitchen-sink-memory-prompt-supplement"
]
},
"registerMemoryRuntime": {
"count": 1,
"ids": [
"kitchen-sink-memory-runtime"
]
},
"registerMigrationProvider": {
"count": 1,
"ids": [
"kitchen-sink-migration-provider"
]
},
"registerMusicGenerationProvider": {
"count": 2,
"ids": [
"kitchen-sink-music",
"kitchen-sink-music-generation-provider"
]
},
"registerNodeHostCommand": {
"count": 1,
"ids": [
"kitchen-sink-node-host-command"
]
},
"registerNodeInvokePolicy": {
"count": 1,
"ids": [
"kitchen-sink-node-invoke-policy"
]
},
"registerProvider": {
"count": 2,
"ids": [
"kitchen-sink-llm",
"kitchen-sink-provider"
]
},
"registerRealtimeTranscriptionProvider": {
"count": 2,
"ids": [
"kitchen-sink-realtime-transcription",
"kitchen-sink-realtime-transcription-provider"
]
},
"registerRealtimeVoiceProvider": {
"count": 2,
"ids": [
"kitchen-sink-realtime-voice",
"kitchen-sink-realtime-voice-provider"
]
},
"registerReload": {
"count": 1,
"ids": [
"kitchen-sink-reload"
]
},
"registerRuntimeLifecycle": {
"count": 1,
"ids": [
"kitchen-sink-runtime-lifecycle"
]
},
"registerSecurityAuditCollector": {
"count": 1,
"ids": [
"kitchen-sink-security-audit-collector"
]
},
"registerService": {
"count": 2,
"ids": [
"kitchen-sink-service",
"kitchen-sink-service"
]
},
"registerSessionExtension": {
"count": 1,
"ids": [
"kitchen-sink-session-extension"
]
},
"registerSessionSchedulerJob": {
"count": 1,
"ids": [
"kitchen-sink-session-scheduler-job"
]
},
"registerSpeechProvider": {
"count": 2,
"ids": [
"kitchen-sink-speech",
"kitchen-sink-speech-provider"
]
},
"registerTextTransforms": {
"count": 1,
"ids": [
"kitchen-sink-text-transforms"
]
},
"registerTool": {
"count": 4,
"ids": [
"kitchen-sink-tool",
"kitchen_sink_image_job",
"kitchen_sink_search",
"kitchen_sink_text"
]
},
"registerToolMetadata": {
"count": 1,
"ids": [
"kitchen-sink-tool-metadata"
]
},
"registerTrustedToolPolicy": {
"count": 1,
"ids": [
"kitchen-sink-trusted-tool-policy"
]
},
"registerVideoGenerationProvider": {
"count": 2,
"ids": [
"kitchen-sink-video",
"kitchen-sink-video-generation-provider"
]
},
"registerWebFetchProvider": {
"count": 2,
"ids": [
"kitchen-sink-fetch",
"kitchen-sink-web-fetch-provider"
]
},
"registerWebSearchProvider": {
"count": 2,
"ids": [
"kitchen-sink-search",
"kitchen-sink-web-search-provider"
]
}
}
}

View File

@ -1,65 +0,0 @@
# Kitchen Sink Contract Probes
Generated: deterministic
Status: PASS
## Covered Inspector Gaps
- before_tool_call allow/block/approval semantics
- llm_input, llm_output, and agent_end privacy-boundary probes
- runtime registrar capture for service, route, gateway, command, interactive handler, and channel surfaces
- channel account, envelope, and outbound route probes
## Runtime Registrations
| Method | Count | IDs |
| ------ | ----- | --- |
| registerAgentEventSubscription | 1 | kitchen-sink-agent-event-subscription |
| registerAgentHarness | 1 | kitchen-sink-agent-harness |
| registerAgentToolResultMiddleware | 2 | kitchen-sink-agent-tool-result-middleware, kitchen-sink-agent-tool-result-middleware |
| registerAutoEnableProbe | 1 | kitchen-sink-auto-enable-probe |
| registerChannel | 2 | kitchen-sink-channel, kitchen-sink-channel-probe |
| registerCli | 2 | kitchen-sink, kitchen-sink-cli |
| registerCliBackend | 1 | kitchen-sink-cli-backend |
| registerCodexAppServerExtensionFactory | 1 | kitchen-sink-codex-app-server-extension-factory |
| registerCommand | 3 | kitchen, kitchen-sink, kitchen-sink-command |
| registerCompactionProvider | 2 | kitchen-sink-compaction, kitchen-sink-compaction-provider |
| registerConfigMigration | 1 | kitchen-sink-config-migration |
| registerContextEngine | 1 | kitchen-sink-context-engine |
| registerControlUiDescriptor | 1 | kitchen-sink-control-ui-descriptor |
| registerDetachedTaskRuntime | 1 | kitchen-sink-detached-task-runtime |
| registerGatewayDiscoveryService | 1 | kitchen-sink-gateway-discovery-service |
| registerGatewayMethod | 2 | kitchen-sink-gateway-method, kitchen.status |
| registerHook | 1 | kitchen-sink-hook |
| registerHttpRoute | 2 | kitchen-sink-http-route, kitchen-sink-http-status |
| registerImageGenerationProvider | 2 | kitchen-sink-image, kitchen-sink-image-generation-provider |
| registerInteractiveHandler | 2 | kitchen-sink-interactive-handler, kitchen-sink-interactive-handler |
| registerMediaUnderstandingProvider | 2 | kitchen-sink-media, kitchen-sink-media-understanding-provider |
| registerMemoryCapability | 1 | kitchen-sink-memory-capability |
| registerMemoryCorpusSupplement | 2 | kitchen-sink-memory-corpus, kitchen-sink-memory-corpus-supplement |
| registerMemoryEmbeddingProvider | 2 | kitchen-sink-memory-embedding, kitchen-sink-memory-embedding-provider |
| registerMemoryFlushPlan | 1 | kitchen-sink-memory-flush-plan |
| registerMemoryPromptSection | 1 | kitchen-sink-memory-prompt-section |
| registerMemoryPromptSupplement | 2 | kitchen-sink-memory-prompt-supplement, kitchen-sink-memory-prompt-supplement |
| registerMemoryRuntime | 1 | kitchen-sink-memory-runtime |
| registerMigrationProvider | 1 | kitchen-sink-migration-provider |
| registerMusicGenerationProvider | 2 | kitchen-sink-music, kitchen-sink-music-generation-provider |
| registerNodeHostCommand | 1 | kitchen-sink-node-host-command |
| registerNodeInvokePolicy | 1 | kitchen-sink-node-invoke-policy |
| registerProvider | 2 | kitchen-sink-llm, kitchen-sink-provider |
| registerRealtimeTranscriptionProvider | 2 | kitchen-sink-realtime-transcription, kitchen-sink-realtime-transcription-provider |
| registerRealtimeVoiceProvider | 2 | kitchen-sink-realtime-voice, kitchen-sink-realtime-voice-provider |
| registerReload | 1 | kitchen-sink-reload |
| registerRuntimeLifecycle | 1 | kitchen-sink-runtime-lifecycle |
| registerSecurityAuditCollector | 1 | kitchen-sink-security-audit-collector |
| registerService | 2 | kitchen-sink-service, kitchen-sink-service |
| registerSessionExtension | 1 | kitchen-sink-session-extension |
| registerSessionSchedulerJob | 1 | kitchen-sink-session-scheduler-job |
| registerSpeechProvider | 2 | kitchen-sink-speech, kitchen-sink-speech-provider |
| registerTextTransforms | 1 | kitchen-sink-text-transforms |
| registerTool | 4 | kitchen-sink-tool, kitchen_sink_image_job, kitchen_sink_search, kitchen_sink_text |
| registerToolMetadata | 1 | kitchen-sink-tool-metadata |
| registerTrustedToolPolicy | 1 | kitchen-sink-trusted-tool-policy |
| registerVideoGenerationProvider | 2 | kitchen-sink-video, kitchen-sink-video-generation-provider |
| registerWebFetchProvider | 2 | kitchen-sink-fetch, kitchen-sink-web-fetch-provider |
| registerWebSearchProvider | 2 | kitchen-sink-search, kitchen-sink-web-search-provider |

View File

@ -1,68 +0,0 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
const repoRoot = process.cwd();
const tempRoot = mkdtempSync(path.join(tmpdir(), "kitchen-sink-install-"));
const keepTemp = process.env.KEEP_KITCHEN_INSTALL_SMOKE === "1";
let lastStdout = "";
try {
const packDir = path.join(tempRoot, "pack");
mkdirSync(packDir, { recursive: true });
run("npm", ["pack", "--json", "--pack-destination", packDir], { cwd: repoRoot });
const packOutput = JSON.parse(lastStdout);
const tarball = path.join(packDir, packOutput[0].filename);
const projectDir = path.join(tempRoot, "consumer");
mkdirSync(projectDir, { recursive: true });
run("npm", ["init", "-y"], { cwd: tempRoot });
run("npm", ["install", "--prefix", projectDir, "--package-lock=false", "--ignore-scripts", "--no-audit", "--no-fund", tarball], {
cwd: tempRoot,
});
const packageDir = path.join(projectDir, "node_modules", "@openclaw", "kitchen-sink");
const installedPackageJson = JSON.parse(readFileSync(path.join(packageDir, "package.json"), "utf8"));
assert.equal(installedPackageJson.name, "@openclaw/kitchen-sink");
assert.equal(installedPackageJson.version, JSON.parse(readFileSync("package.json", "utf8")).version);
const probeFile = path.join(projectDir, "probe.mjs");
writeFileSync(probeFile, readFileSync(new URL("./fixtures/installed-consumer-probe.mjs", import.meta.url), "utf8"));
run(process.execPath, [probeFile], { cwd: projectDir });
const inspectorBin = path.join(repoRoot, "node_modules", ".bin", "plugin-inspector");
run(inspectorBin, ["check", "--config", "plugin-inspector.config.json", "--no-openclaw", "--runtime", "--mock-sdk"], {
cwd: packageDir,
env: {
...process.env,
PLUGIN_INSPECTOR_EXECUTE_ISOLATED: "1",
},
});
console.log(`Installed package smoke OK: ${installedPackageJson.name}@${installedPackageJson.version}`);
} finally {
if (!keepTemp) {
rmSync(tempRoot, { recursive: true, force: true });
} else {
console.log(`Kept install smoke temp dir: ${tempRoot}`);
}
}
function run(command, args, options = {}) {
const result = spawnSync(command, args, {
cwd: options.cwd,
env: options.env || process.env,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
lastStdout = result.stdout;
if (result.status !== 0) {
process.stdout.write(result.stdout);
process.stderr.write(result.stderr);
process.exit(result.status ?? 1);
}
}

View File

@ -1,135 +0,0 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { mkdirSync, writeFileSync } from "node:fs";
import { plugin } from "../src/index.js";
import {
capturePluginRegistration,
createHookFinder,
registrationSummary,
} from "./lib/plugin-registration-harness.mjs";
const registrations = capturePluginRegistration(plugin);
const findHook = createHookFinder(registrations);
const beforeToolCall = findHook("before_tool_call");
const llmInput = findHook("llm_input");
const llmOutput = findHook("llm_output");
const agentEnd = findHook("agent_end");
const probes = {
beforeToolCall: {
allow: await beforeToolCall(toolEvent("generate a kitchen image"), { providerId: "kitchen-sink-image" }),
block: await beforeToolCall(toolEvent("kitchen block image generation"), { providerId: "kitchen-sink-image" }),
approval: await beforeToolCall(toolEvent("kitchen image generation needs approval"), {
providerId: "kitchen-sink-image",
}),
},
conversationPrivacy: {
input: await llmInput(secretEvent("kitchen explain the fixture"), secretContext()),
output: await llmOutput(secretEvent("kitchen image result"), secretContext()),
end: await agentEnd(secretEvent("kitchen final answer"), secretContext()),
},
channel: await captureChannelProbe(),
runtimeRegistrations: registrationSummary(registrations),
};
assert.equal(probes.beforeToolCall.allow.decision, "allow");
assert.equal(probes.beforeToolCall.allow.params.args.kitchenSinkScenario, "image.generate");
assert.equal(probes.beforeToolCall.block.block, true);
assert.equal(probes.beforeToolCall.block.terminal, true);
assert.equal(probes.beforeToolCall.approval.decision, "approval");
assert.equal(probes.beforeToolCall.approval.requireApproval.pluginId, "openclaw-kitchen-sink-fixture");
for (const result of Object.values(probes.conversationPrivacy)) {
assert.equal(result.privacy.boundary, "conversation-observer");
assert.equal(result.privacy.storesRawPayload, false);
assert.equal(result.privacy.exposesRawPayload, false);
assert.ok(result.privacy.redactedFields.length >= 2);
assert.ok(result.privacy.secretPatternCount >= 1);
}
for (const method of [
"registerChannel",
"registerCommand",
"registerGatewayMethod",
"registerHttpRoute",
"registerInteractiveHandler",
"registerService",
]) {
assert.ok(probes.runtimeRegistrations[method]?.count > 0, `${method} was not captured`);
}
assert.equal(probes.channel.account.statusState, "ready");
assert.equal(probes.channel.delivery.deliveryStatus, "sent");
assert.equal(probes.channel.route.peer.kind, "direct");
mkdirSync("reports", { recursive: true });
writeFileSync("reports/kitchen-contract-probes.json", `${JSON.stringify(probes, null, 2)}\n`);
writeFileSync("reports/kitchen-contract-probes.md", renderMarkdown(probes));
console.log(
`Kitchen contract probes OK: ${Object.keys(probes.runtimeRegistrations).length} registration methods, before_tool_call allow/block/approval, conversation privacy, channel envelope`,
);
async function captureChannelProbe() {
const channel = registrations.registerChannel?.map(([value]) => value).find((value) => value.id === "kitchen-sink-channel");
assert.ok(channel, "kitchen-sink-channel registered");
return {
account: channel.config.resolveAccount({}, "local"),
delivery: await channel.outbound.sendText({ to: "kitchen demo", text: "kitchen generate image" }),
route: await channel.messaging.resolveOutboundSessionRoute({
agentId: "fixture-agent",
target: "kitchen demo",
threadId: "thread-1",
}),
};
}
function toolEvent(prompt) {
return {
toolId: "kitchen_sink_image_job",
args: { prompt },
};
}
function secretEvent(prompt) {
return {
prompt,
apiKey: "sk-local-secret-not-stored",
token: "fixture-token-not-stored",
};
}
function secretContext() {
return {
providerId: "kitchen-sink-llm",
authorization: "Bearer fixture-secret-not-stored",
};
}
function renderMarkdown(report) {
const methods = Object.entries(report.runtimeRegistrations)
.map(([method, summary]) => `| ${method} | ${summary.count} | ${summary.ids.join(", ")} |`)
.join("\n");
return [
"# Kitchen Sink Contract Probes",
"",
"Generated: deterministic",
"Status: PASS",
"",
"## Covered Inspector Gaps",
"",
"- before_tool_call allow/block/approval semantics",
"- llm_input, llm_output, and agent_end privacy-boundary probes",
"- runtime registrar capture for service, route, gateway, command, interactive handler, and channel surfaces",
"- channel account, envelope, and outbound route probes",
"",
"## Runtime Registrations",
"",
"| Method | Count | IDs |",
"| ------ | ----- | --- |",
methods,
"",
].join("\n");
}

View File

@ -2,16 +2,38 @@
import assert from "node:assert/strict";
import { plugin } from "../src/index.js";
import {
capturePluginRegistration,
createHookFinder,
createRegistrationFinder,
fixedNow,
} from "./lib/plugin-registration-harness.mjs";
const registrations = capturePluginRegistration(plugin);
const findRegistration = createRegistrationFinder(registrations);
const findHook = createHookFinder(registrations);
const registrations = {};
const api = new Proxy(
{
id: "openclaw-kitchen-sink-fixture",
registrationMode: "full",
config: {},
logger: console,
},
{
get(target, property) {
if (property in target) {
return target[property];
}
if (property === "on") {
return (...args) => {
registrations.on ??= [];
registrations.on.push(args);
};
}
if (typeof property !== "string" || !property.startsWith("register")) {
return undefined;
}
return (...args) => {
registrations[property] ??= [];
registrations[property].push(args);
};
},
},
);
plugin.register(api);
const commands = registrations.registerCommand?.map(([command]) => command) ?? [];
assert.ok(commands.some((command) => command.name === "kitchen"), "registers kitchen command");
@ -26,48 +48,11 @@ assert.equal(hookResult.pluginId, "openclaw-kitchen-sink-fixture");
assert.equal(hookResult.route, "hook:before_tool_call");
assert.equal(hookResult.scenarioId, "image.generate");
assert.equal(hookResult.matchedKitchen, true);
assert.equal(hookResult.decision, "allow");
assert.equal(hookResult.params.args.kitchenSinkScenario, "image.generate");
const blockedToolHookResult = await beforeToolHook(
{ toolId: "kitchen_sink_image_job", args: { prompt: "kitchen block this image" } },
{ providerId: "kitchen-sink-image" },
);
assert.equal(blockedToolHookResult.block, true);
assert.equal(blockedToolHookResult.terminal, true);
assert.equal(blockedToolHookResult.decision, "block");
assert.match(blockedToolHookResult.blockReason, /blocked kitchen_sink_image_job/);
const approvalToolHookResult = await beforeToolHook(
{ toolId: "kitchen_sink_image_job", args: { prompt: "kitchen image needs approval" } },
{ providerId: "kitchen-sink-image" },
);
assert.equal(approvalToolHookResult.decision, "approval");
assert.equal(approvalToolHookResult.requireApproval.pluginId, "openclaw-kitchen-sink-fixture");
assert.equal(approvalToolHookResult.requireApproval.scenarioId, "image.generate");
const llmInputHook = findHook("llm_input");
const llmInputResult = await llmInputHook(
{
prompt: "kitchen explain image routing with api_key sk-test-redacted",
apiKey: "sk-real-secret-not-stored",
},
{ providerId: "kitchen-sink-llm", authorization: "Bearer local-secret" },
);
assert.equal(llmInputResult.scenarioId, "text.reply");
assert.equal(llmInputResult.privacy.boundary, "conversation-observer");
assert.equal(llmInputResult.privacy.storesRawPayload, false);
assert.equal(llmInputResult.privacy.exposesRawPayload, false);
assert.deepEqual(llmInputResult.privacy.redactedFields, ["event.apiKey", "context.authorization"]);
assert.ok(llmInputResult.privacy.secretPatternCount >= 2);
const channel = findRegistration("registerChannel", "kitchen-sink-channel");
const channelAccount = channel.config.resolveAccount({}, "local");
assert.equal(channelAccount.configured, true);
assert.equal(channelAccount.enabled, true);
assert.equal(channelAccount.statusState, "ready");
assert.equal(channelAccount.health.ok, true);
assert.equal(channel.config.resolveAccount({ disabled: true }, "disabled").statusState, "disabled");
const channelDelivery = await channel.outbound.sendText({
cfg: {},
to: "kitchen demo",
@ -75,8 +60,6 @@ const channelDelivery = await channel.outbound.sendText({
});
assert.equal(channelDelivery.channel, "kitchen-sink-channel");
assert.equal(channelDelivery.conversationId, "kitchen-demo");
assert.equal(channelDelivery.deliveryStatus, "sent");
assert.equal(channelDelivery.transport, "kitchen-sink-local");
assert.equal(channelDelivery.meta.scenarioId, "image.generate");
const channelRoute = await channel.messaging.resolveOutboundSessionRoute({
cfg: {},
@ -109,13 +92,7 @@ const imageProvider = findRegistration("registerImageGenerationProvider", "kitch
assert.equal(imageProvider.defaultModel, "kitchen-sink-image-v1");
const sleeps = [];
const {
PLUGIN_ID,
listKitchenHumanScenarios,
runKitchenHumanScenario,
runKitchenImageTool,
runKitchenScenario,
} = await import("../src/scenarios.js");
const { PLUGIN_ID, runKitchenScenario } = await import("../src/scenarios.js");
const { createKitchenSinkRuntime } = await import("../src/kitchen-runtime.js");
const fastRuntime = createKitchenSinkRuntime({
delayMs: 10_000,
@ -130,94 +107,19 @@ assert.equal(imageResult.scenarioId, "image.generate");
assert.equal(imageResult.route, "provider:image");
assert.equal(imageResult.job.status, "completed");
assert.equal(imageResult.job.pluginId, "openclaw-kitchen-sink-fixture");
assert.equal(imageResult.job.progressPercent, 100);
assert.equal(imageResult.job.statusUrl, `kitchen://jobs/${imageResult.job.id}`);
assert.deepEqual(
imageResult.job.timeline.map((entry) => entry.status),
["queued", "running", "completed"],
);
assert.equal(imageResult.job.output.contentHash, "e126064123bb13d8");
assert.equal(imageResult.image.metadata.pluginId, "openclaw-kitchen-sink-fixture");
assert.equal(imageResult.image.metadata.assetId, "office-lobby-sink");
assert.equal(imageResult.image.metadata.assetName, "kitchen_sink_office.png");
assert.equal(imageResult.image.metadata.source, "bundled-real-image");
assert.equal(imageResult.image.metadata.model, "kitchen-sink-image-v1");
assert.equal(imageResult.image.metadata.width, 1024);
assert.equal(imageResult.image.metadata.height, 1024);
assert.equal(imageResult.image.metadata.sizeBytes, 948291);
assert.equal(imageResult.image.metadata.sha256, "e126064123bb13d8ee01a22c204e079bc22397c103ed1c3a191c60d5ae3319aa");
assert.equal(imageResult.image.metadata.contentHash, "e126064123bb13d8");
assert.equal(imageResult.image.metadata.finishReason, "success");
assert.equal(imageResult.image.mimeType, "image/png");
assert.equal(imageResult.image.fileName, `${imageResult.job.id}.png`);
assert.deepEqual(
[...imageResult.image.buffer.subarray(0, 8)],
[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
);
assert.ok(imageResult.image.dataUrl.startsWith("data:image/png;base64,"));
const humanScenarios = listKitchenHumanScenarios();
assert.deepEqual(
humanScenarios.map((scenario) => scenario.id),
[
"dry.prefix-image",
"live.openai-text-kitchen-image",
"search.fetch.summarize",
"channel.prefix-image",
"hook.block-tool",
"memory.compact-fixture",
],
);
const liveImageScenario = await runKitchenHumanScenario(fastRuntime, "live.openai-text-kitchen-image");
assert.equal(liveImageScenario.mode, "live-llm-compatible");
assert.equal(liveImageScenario.result.route, "human:live-llm-image-provider");
assert.equal(liveImageScenario.result.image.metadata.assetName, "kitchen_sink_office.png");
const searchFetchScenario = await runKitchenHumanScenario(fastRuntime, "search.fetch.summarize");
assert.equal(searchFetchScenario.result.search.results[0].id, "ks-result-image-provider");
assert.equal(searchFetchScenario.result.fetch.finalUrl, "kitchen://fixture/readme");
assert.match(searchFetchScenario.result.summary, /Kitchen Sink text fixture/);
const channelScenario = await runKitchenHumanScenario(fastRuntime, "channel.prefix-image");
assert.equal(channelScenario.result.delivery.channel, "kitchen-sink-channel");
assert.equal(channelScenario.result.delivery.meta.scenarioId, "image.generate");
const hookBlockScenario = await runKitchenHumanScenario(fastRuntime, "hook.block-tool");
assert.equal(hookBlockScenario.result.block, true);
assert.equal(hookBlockScenario.result.decision, "block");
const memoryScenario = await runKitchenHumanScenario(fastRuntime, "memory.compact-fixture");
assert.equal(memoryScenario.result.embedding.length, 8);
assert.equal(memoryScenario.result.memory.results[0].id, "ks-memory-runtime-surfaces");
assert.deepEqual(memoryScenario.result.compaction.preservedIdentifiers, ["ks_image_1f8a5a98"]);
sleeps.length = 0;
const failedImageResult = await fastRuntime.runImageJob({ prompt: "kitchen rate limit image" });
assert.deepEqual(sleeps, [10_000]);
assert.equal(failedImageResult.job.status, "failed");
assert.deepEqual(
failedImageResult.job.timeline.map((entry) => entry.status),
["queued", "running", "failed"],
);
assert.equal(failedImageResult.error.code, "rate_limited");
assert.equal(failedImageResult.error.statusCode, 429);
assert.equal(failedImageResult.error.retryAfterMs, 30_000);
const failedToolResult = await runKitchenImageTool(fastRuntime, { prompt: "kitchen timeout image" });
assert.deepEqual(sleeps, [10_000, 10_000]);
assert.equal(failedToolResult.ok, false);
assert.equal(failedToolResult.error.code, "timeout");
assert.equal(failedToolResult.mediaUrl, undefined);
assert.equal(imageResult.image.mimeType, "image/svg+xml");
assert.ok(imageResult.image.buffer.toString("utf8").includes("Kitchen Sink Fixture"));
assert.ok(imageResult.image.dataUrl.startsWith("data:image/svg+xml;base64,"));
const scenarioResult = await runKitchenScenario(fastRuntime, {
scenario: "web.fetch",
url: "kitchen://fixture/redirect",
url: "kitchen://fixture/readme",
route: "test:scenario-engine",
});
assert.equal(PLUGIN_ID, "openclaw-kitchen-sink-fixture");
assert.equal(scenarioResult.scenarioId, "web.fetch");
assert.equal(scenarioResult.route, "test:scenario-engine");
assert.equal(scenarioResult.ok, true);
assert.equal(scenarioResult.statusCode, 200);
assert.equal(scenarioResult.finalUrl, "kitchen://fixture/readme");
assert.equal(scenarioResult.redirects.length, 1);
assert.equal(scenarioResult.headers["x-kitchen-sink-fixture"], "true");
assert.match(scenarioResult.content, /deterministic document/);
const mediaProvider = findRegistration("registerMediaUnderstandingProvider", "kitchen-sink-media");
@ -226,77 +128,17 @@ const mediaResult = await mediaProvider.describeImage({
model: "kitchen-sink-vision-v1",
});
assert.match(mediaResult.text, /Kitchen Sink media fixture/);
const audioDescription = await mediaProvider.transcribeAudio({
audio: Buffer.from("audio fixture"),
prompt: "transcribe this kitchen audio",
});
assert.match(audioDescription.text, /Kitchen Sink transcript/);
assert.equal(audioDescription.segments.length, 2);
const videoDescription = await mediaProvider.describeVideo({ prompt: "describe kitchen video" });
assert.match(videoDescription.text, /three deterministic frames/);
const speechProvider = findRegistration("registerSpeechProvider", "kitchen-sink-speech");
const speechResult = await speechProvider.synthesize({ text: "say kitchen sink" });
assert.equal(speechResult.mimeType, "audio/wav");
assert.equal(speechResult.audioBuffer.subarray(0, 4).toString("ascii"), "RIFF");
assert.equal(speechResult.metadata.providerId, "kitchen-sink-speech");
const realtimeTranscriptionProvider = findRegistration(
"registerRealtimeTranscriptionProvider",
"kitchen-sink-realtime-transcription",
);
const realtimeTranscripts = [];
const realtimeSession = realtimeTranscriptionProvider.createSession({
onTranscript: (text) => realtimeTranscripts.push(text),
});
await realtimeSession.connect();
realtimeSession.sendAudio(Buffer.from("abc"));
const realtimeFinal = await realtimeSession.close();
assert.match(realtimeFinal.text, /Kitchen Sink transcript/);
assert.ok(realtimeTranscripts.some((text) => /partial transcript/.test(text)));
const realtimeVoiceProvider = findRegistration("registerRealtimeVoiceProvider", "kitchen-sink-realtime-voice");
const realtimeVoiceEvents = [];
const realtimeBridge = realtimeVoiceProvider.createBridge({
onEvent: (event) => realtimeVoiceEvents.push(event.type),
});
await realtimeBridge.connect();
assert.equal(realtimeBridge.isConnected(), true);
realtimeBridge.setMediaTimestamp(123);
realtimeBridge.submitToolResult({ ok: true });
realtimeBridge.close();
assert.equal(realtimeBridge.isConnected(), false);
assert.deepEqual(realtimeVoiceEvents, ["connected", "media_timestamp", "tool_result", "closed"]);
const videoProvider = findRegistration("registerVideoGenerationProvider", "kitchen-sink-video");
const videoResult = await videoProvider.generateVideo({ prompt: "kitchen video" });
assert.equal(videoResult.videos[0].mimeType, "application/vnd.openclaw.kitchen-video+json");
assert.equal(videoResult.job.status, "completed");
const musicProvider = findRegistration("registerMusicGenerationProvider", "kitchen-sink-music");
const musicResult = await musicProvider.generateMusic({ prompt: "kitchen song" });
assert.equal(musicResult.tracks[0].mimeType, "audio/wav");
assert.equal(musicResult.tracks[0].audioBuffer.subarray(0, 4).toString("ascii"), "RIFF");
const searchProvider = findRegistration("registerWebSearchProvider", "kitchen-sink-search");
const searchTool = searchProvider.createTool({});
const searchResult = await searchTool.execute({ query: "kitchen sink image provider" });
assert.equal(searchResult.results.length, 3);
assert.equal(searchResult.provider, "kitchen-sink-search");
assert.equal(searchResult.ok, true);
assert.equal(searchResult.statusCode, 200);
assert.equal(searchResult.results[0].id, "ks-result-image-provider");
assert.equal(searchResult.results[0].metadata.provider, "kitchen-sink-image");
const emptySearchResult = await searchTool.execute({ query: "kitchen empty results" });
assert.equal(emptySearchResult.ok, true);
assert.equal(emptySearchResult.results.length, 0);
const textProvider = findRegistration("registerProvider", "kitchen-sink-llm");
const catalog = await textProvider.staticCatalog.run({ config: {}, env: {} });
assert.equal(catalog.provider.models[0].id, "kitchen-sink-text-v1");
assert.equal(catalog.provider.models[0].api, "kitchen-sink");
const authResult = await textProvider.auth[0].run();
assert.equal(authResult.profiles[0].id, "kitchen-sink-local");
const streamFn = textProvider.createStreamFn({});
const stream = streamFn(catalog.provider.models[0], {
messages: [{ role: "user", content: "kitchen explain text inference", timestamp: 0 }],
@ -308,107 +150,25 @@ for await (const event of stream) {
const streamMessage = await stream.result();
assert.deepEqual(streamEvents, ["start", "text_start", "text_delta", "text_end", "done"]);
assert.match(streamMessage.content[0].text, /kitchen explain text inference/);
assert.ok(streamMessage.usage.totalTokens > 0);
const embeddingProvider = findRegistration("registerMemoryEmbeddingProvider", "kitchen-sink-memory-embedding");
const embeddingResult = await embeddingProvider.embed({ text: "kitchen memory" });
assert.equal(embeddingResult.embedding.length, 8);
assert.equal(embeddingResult.model, "kitchen-sink-embed-v1");
const embeddingBatch = await embeddingProvider.embedMany({ texts: ["one", "two"] });
assert.equal(embeddingBatch.embeddings.length, 2);
const memoryCorpus = findRegistration("registerMemoryCorpusSupplement", "kitchen-sink-memory-corpus");
const memorySearch = await memoryCorpus.search({ query: "runtime surfaces" });
assert.equal(memorySearch.results[0].id, "ks-memory-runtime-surfaces");
const memoryRead = await memoryCorpus.read("ks-memory-runtime-surfaces");
assert.match(memoryRead.text, /providers, channels, hooks/);
const compactionProvider = findRegistration("registerCompactionProvider", "kitchen-sink-compaction");
const compacted = await compactionProvider.compact({
messages: [{ role: "user", content: "remember job ks_image_1f8a5a98 and the image fixture" }],
});
assert.match(compacted.summary, /Kitchen Sink compacted/);
assert.deepEqual(compacted.preservedIdentifiers, ["ks_image_1f8a5a98"]);
const middleware = registrations.registerAgentToolResultMiddleware.at(-1);
assert.equal(typeof middleware?.[0], "function");
assert.deepEqual(middleware[1].runtimes, ["pi", "codex", "cli"]);
const middlewareResult = await middleware[0]({ result: { content: "tool output" } });
assert.equal(middlewareResult.metadata.kitchenSinkToolResultMiddleware, true);
const service = registrations.registerService.map(([value]) => value).at(-1);
assert.equal(service.id, "kitchen-sink-service");
assert.equal((await service.probe()).state, "ready");
assert.equal((await service.start()).state, "started");
const httpRoute = findRegistration("registerHttpRoute", "kitchen-sink-http-status");
let httpBody = "";
const httpResult = await httpRoute.handler({}, {
setHeader: () => {},
end: (body) => {
httpBody = body;
},
});
assert.equal(httpRoute.path, "/kitchen-sink/status");
assert.equal(httpResult.ok, true);
assert.match(httpBody, /openclaw-kitchen-sink-fixture/);
const gatewayMethod = registrations.registerGatewayMethod.find(([name]) => name === "kitchen.status");
assert.ok(gatewayMethod, "registers kitchen.status gateway method");
const gatewayResult = await gatewayMethod[1]({});
assert.ok(gatewayResult.providerIds.includes("kitchen-sink-video"));
const cliRegistration = registrations.registerCli.at(-1);
assert.equal(typeof cliRegistration?.[0], "function");
assert.equal(cliRegistration[1].descriptors[0].name, "kitchen-sink");
const imageTool = findRegistration("registerTool", "kitchen_sink_image_job");
assert.equal(typeof imageTool.execute, "function");
const { KITCHEN_SINK_EXPECTED_DIAGNOSTICS } = await import("../src/personality.js");
assert.deepEqual(KITCHEN_SINK_EXPECTED_DIAGNOSTICS.conformance, []);
assert.ok(
KITCHEN_SINK_EXPECTED_DIAGNOSTICS.adversarial.includes(
'channel "kitchen-sink-channel-probe" registration missing required config helpers',
),
);
assert.ok(
KITCHEN_SINK_EXPECTED_DIAGNOSTICS.full.includes(
"only bundled plugins can register agent tool result middleware",
),
);
const conformance = capturePluginRegistration(plugin, { personality: "conformance" });
assert.ok(
conformance.registerCommand?.some(([command]) => command.name === "kitchen"),
"conformance registers usable commands",
);
assert.ok(
conformance.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel"),
"conformance registers the usable channel",
);
assert.equal(conformance.registerAgentToolResultMiddleware, undefined);
assert.equal(
conformance.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel-probe"),
false,
);
const adversarial = capturePluginRegistration(plugin, { personality: "adversarial" });
assert.equal(
adversarial.registerCommand?.some(([command]) => command.name === "kitchen"),
false,
);
assert.equal(
adversarial.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel"),
false,
);
assert.ok(
adversarial.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel-probe"),
"adversarial registers generated invalid channel probe",
);
assert.ok(
adversarial.registerCompactionProvider?.some(([provider]) => provider.id === "kitchen-sink-compaction-provider"),
"adversarial registers generated invalid compaction probe",
);
console.log("Kitchen runtime OK");
function findRegistration(method, id) {
const entry = registrations[method]?.map(([value]) => value).find((value) => value?.id === id);
assert.ok(entry, `${method} ${id} registered`);
return entry;
}
function findHook(name) {
const entry = registrations.on?.find(([hookName]) => hookName === name);
assert.ok(entry, `hook ${name} registered`);
return entry[1];
}
function fixedNow() {
let tick = 0;
return () => new Date(Date.UTC(2026, 3, 28, 12, 0, tick++));
}

View File

@ -31,17 +31,7 @@ const requiredFiles = [
"openclaw.plugin.json",
"plugin-inspector.config.json",
"src/index.js",
"src/assets/kitchen_sink_office.png",
"src/constants.js",
"src/fixtures/images.js",
"src/fixtures/text.js",
"src/kitchen-runtime.js",
"src/personality.js",
"src/runtime/channel.js",
"src/runtime/commands.js",
"src/runtime/platform.js",
"src/runtime/providers.js",
"src/runtime/tasks.js",
"src/scenarios.js",
"src/setup.js",
"src/generated-hooks.js",
@ -51,7 +41,6 @@ const requiredFiles = [
const missingFiles = requiredFiles.filter((file) => !files.has(file));
const packageJson = JSON.parse(fs.readFileSync("package.json", "utf8"));
const pluginManifest = JSON.parse(fs.readFileSync("openclaw.plugin.json", "utf8"));
const issues = [];
function sameStringArray(actual, expected) {
@ -101,37 +90,9 @@ if (buildPluginSdkVersion !== buildOpenClawVersion) {
if (packageJson.dependencies?.openclaw !== buildOpenClawVersion) {
issues.push("dependencies.openclaw must match openclaw.build.openclawVersion");
}
if (packageJson.openclaw?.install?.clawhubSpec !== "clawhub:@openclaw/kitchen-sink") {
issues.push('openclaw.install.clawhubSpec must be "clawhub:@openclaw/kitchen-sink"');
}
if (packageJson.openclaw?.install?.npmSpec !== "@openclaw/kitchen-sink") {
issues.push('openclaw.install.npmSpec must be "@openclaw/kitchen-sink"');
}
if (packageJson.openclaw?.install?.defaultChoice !== "clawhub") {
issues.push('openclaw.install.defaultChoice must be "clawhub"');
}
if (packageJson.openclaw?.install?.minHostVersion !== `>=${buildOpenClawVersion}`) {
issues.push("openclaw.install.minHostVersion must be a semver floor for openclaw.build.openclawVersion");
}
const kitchenSinkChannelConfig = pluginManifest.channelConfigs?.["kitchen-sink-channel"];
if (!kitchenSinkChannelConfig?.schema || kitchenSinkChannelConfig.schema.type !== "object") {
issues.push("openclaw.plugin.json must declare channelConfigs.kitchen-sink-channel.schema");
}
if (packageJson.openclaw?.release?.publishToClawHub !== true) {
issues.push("openclaw.release.publishToClawHub must be true");
}
if (packageJson.openclaw?.release?.publishToNpm !== true) {
issues.push("openclaw.release.publishToNpm must be true");
}
if (!packageJson.files?.includes("src/")) {
issues.push("package files must include src/");
}
if (packageJson.exports?.["./runtime"] !== "./src/kitchen-runtime.js") {
issues.push('package exports "./runtime" must point at "./src/kitchen-runtime.js"');
}
if (packageJson.exports?.["./scenarios"] !== "./src/scenarios.js") {
issues.push('package exports "./scenarios" must point at "./src/scenarios.js"');
}
if (issues.length > 0) {
console.error(`Package payload check failed:\n- ${issues.join("\n- ")}`);

View File

@ -1,59 +0,0 @@
import assert from "node:assert/strict";
import { plugin } from "@openclaw/kitchen-sink";
import { createKitchenSinkRuntime } from "@openclaw/kitchen-sink/runtime";
import { createKitchenSinkImageAsset, kitchenPromptGuidance } from "@openclaw/kitchen-sink/scenarios";
import setup from "@openclaw/kitchen-sink/setup";
const registrations = {};
const api = new Proxy(
{ id: "consumer-install-smoke", registrationMode: "full", config: {}, logger: console },
{
get(target, property) {
if (property in target) return target[property];
if (property === "on") {
return (...args) => {
registrations.on ??= [];
registrations.on.push(args);
};
}
if (typeof property !== "string" || !property.startsWith("register")) return undefined;
return (...args) => {
registrations[property] ??= [];
registrations[property].push(args);
};
},
},
);
plugin.register(api);
assert.equal(plugin.id, "openclaw-kitchen-sink-fixture");
assert.ok(registrations.registerImageGenerationProvider?.some(([provider]) => provider.id === "kitchen-sink-image"));
assert.ok(registrations.registerProvider?.some(([provider]) => provider.id === "kitchen-sink-llm"));
assert.ok(registrations.registerWebSearchProvider?.some(([provider]) => provider.id === "kitchen-sink-search"));
assert.ok(registrations.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel"));
const runtime = createKitchenSinkRuntime({
delayMs: 10_000,
sleep: async () => {},
now: (() => {
let tick = 0;
return () => new Date(Date.UTC(2026, 3, 29, 12, 0, tick++));
})(),
});
const image = await runtime.runImageJob({ prompt: "generate an image with kitchen sink" });
assert.equal(image.job.status, "completed");
assert.equal(image.image.metadata.assetName, "kitchen_sink_office.png");
assert.equal(image.image.metadata.sha256, "e126064123bb13d8ee01a22c204e079bc22397c103ed1c3a191c60d5ae3319aa");
const directImage = createKitchenSinkImageAsset({
prompt: "consumer import smoke",
jobId: "ks_consumer_install_smoke",
});
assert.equal(directImage.mimeType, "image/png");
assert.ok(directImage.dataUrl.startsWith("data:image/png;base64,"));
assert.ok(kitchenPromptGuidance().some((line) => line.includes("kitchen_sink_image_job")));
assert.equal(setup.id, "openclaw-kitchen-sink-setup");
assert.equal(typeof setup.setup, "function");
const setupResult = await setup.setup({ config: {} });
assert.equal(setupResult.configured, true);

View File

@ -1,90 +0,0 @@
import assert from "node:assert/strict";
export function capturePluginRegistration(plugin, config = {}) {
// The harness captures every register* call through one proxy, which lets
// scripts inspect new SDK registrars without updating bespoke mocks first.
const captured = {};
const api = new Proxy(
{
id: "openclaw-kitchen-sink-fixture",
registrationMode: "full",
config,
logger: console,
},
{
get(target, property) {
if (property in target) {
return target[property];
}
if (property === "on") {
return (...args) => capture(captured, "on", args);
}
if (typeof property !== "string" || !property.startsWith("register")) {
return undefined;
}
return (...args) => capture(captured, property, args);
},
},
);
plugin.register(api);
return captured;
}
export function createRegistrationFinder(registrations) {
return (method, id) => {
const entry = registrations[method]?.map(([value]) => value).find((value) => value?.id === id);
assert.ok(entry, `${method} ${id} registered`);
return entry;
};
}
export function createHookFinder(registrations) {
return (name) => {
const entry = registrations.on?.find(([hookName]) => hookName === name);
assert.ok(entry, `hook ${name} registered`);
return entry[1];
};
}
export function registrationSummary(registrations) {
return Object.fromEntries(
Object.entries(registrations)
.filter(([method]) => method !== "on")
.sort(([a], [b]) => a.localeCompare(b))
.map(([method, entries]) => [
method,
{
count: entries.length,
ids: entries.map((args) => idForRegistration(method, args)).filter(Boolean).sort(),
},
]),
);
}
export function fixedNow(start = Date.UTC(2026, 3, 28, 12, 0, 0)) {
let tick = 0;
return () => new Date(start + tick++ * 1000);
}
function capture(registrations, method, args) {
registrations[method] ??= [];
registrations[method].push(args);
}
function idForRegistration(method, args) {
const [value, second] = args;
if (method === "registerGatewayMethod" && typeof value === "string") {
return value;
}
if (method === "registerCli" && second?.descriptors?.length > 0) {
return second.descriptors.map((descriptor) => descriptor.name).join(", ");
}
if (value?.id || value?.name) {
return value.id || value.name;
}
if (typeof second === "string") {
return second;
}
const slug = method.slice("register".length).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
return `kitchen-sink-${slug}`;
}

View File

@ -1,134 +0,0 @@
#!/usr/bin/env node
import { Buffer } from "node:buffer";
import fs from "node:fs/promises";
import path from "node:path";
const repoRoot = process.cwd();
const packageJson = JSON.parse(await fs.readFile(path.join(repoRoot, "package.json"), "utf8"));
const pluginJson = JSON.parse(await fs.readFile(path.join(repoRoot, "openclaw.plugin.json"), "utf8"));
const packageVersion = packageJson.version;
const pluginId = pluginJson.id;
const crcTable = Array.from({ length: 256 }, (_, index) => {
let value = index;
for (let bit = 0; bit < 8; bit += 1) {
value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
}
return value >>> 0;
});
if (pluginId !== "openclaw-kitchen-sink-fixture") {
throw new Error(`unexpected plugin id: ${pluginId}`);
}
if (typeof packageVersion !== "string" || packageVersion.trim().length === 0) {
throw new Error("package.json version must be a non-empty string");
}
const distDir = path.join(repoRoot, "dist");
const zipName = `${pluginId}-${packageVersion}.zip`;
const zipPath = path.join(distDir, zipName);
const entries = [
"package.json",
"openclaw.plugin.json",
"plugin-inspector.config.json",
"README.md",
...(await listFiles("src")),
];
await fs.mkdir(distDir, { recursive: true });
await fs.writeFile(zipPath, await createZip(entries));
console.log(`Wrote ${path.relative(repoRoot, zipPath)} (${entries.length} files)`);
async function listFiles(root) {
const files = [];
const stack = [root];
while (stack.length > 0) {
const relativeDir = stack.pop();
const dirents = await fs.readdir(path.join(repoRoot, relativeDir), { withFileTypes: true });
for (const dirent of dirents.sort((left, right) => left.name.localeCompare(right.name))) {
const relativePath = path.posix.join(relativeDir, dirent.name);
if (dirent.isDirectory()) {
stack.push(relativePath);
} else if (dirent.isFile()) {
files.push(relativePath);
}
}
}
return files.sort();
}
async function createZip(files) {
const localParts = [];
const centralParts = [];
let offset = 0;
for (const relativePath of files) {
if (relativePath.startsWith("/") || relativePath.includes("..")) {
throw new Error(`invalid zip entry path: ${relativePath}`);
}
const data = await fs.readFile(path.join(repoRoot, relativePath));
const name = Buffer.from(relativePath, "utf8");
const crc = crc32(data);
const localHeader = Buffer.alloc(30);
localHeader.writeUInt32LE(0x04034b50, 0);
localHeader.writeUInt16LE(20, 4);
localHeader.writeUInt16LE(0x0800, 6);
localHeader.writeUInt16LE(0, 8);
localHeader.writeUInt16LE(0, 10);
localHeader.writeUInt16LE(0, 12);
localHeader.writeUInt32LE(crc, 14);
localHeader.writeUInt32LE(data.length, 18);
localHeader.writeUInt32LE(data.length, 22);
localHeader.writeUInt16LE(name.length, 26);
localHeader.writeUInt16LE(0, 28);
localParts.push(localHeader, name, data);
const centralHeader = Buffer.alloc(46);
centralHeader.writeUInt32LE(0x02014b50, 0);
centralHeader.writeUInt16LE(20, 4);
centralHeader.writeUInt16LE(20, 6);
centralHeader.writeUInt16LE(0x0800, 8);
centralHeader.writeUInt16LE(0, 10);
centralHeader.writeUInt16LE(0, 12);
centralHeader.writeUInt16LE(0, 14);
centralHeader.writeUInt32LE(crc, 16);
centralHeader.writeUInt32LE(data.length, 20);
centralHeader.writeUInt32LE(data.length, 24);
centralHeader.writeUInt16LE(name.length, 28);
centralHeader.writeUInt16LE(0, 30);
centralHeader.writeUInt16LE(0, 32);
centralHeader.writeUInt16LE(0, 34);
centralHeader.writeUInt16LE(0, 36);
centralHeader.writeUInt32LE(0, 38);
centralHeader.writeUInt32LE(offset, 42);
centralParts.push(centralHeader, name);
offset += localHeader.length + name.length + data.length;
}
const centralDirectory = Buffer.concat(centralParts);
const end = Buffer.alloc(22);
end.writeUInt32LE(0x06054b50, 0);
end.writeUInt16LE(0, 4);
end.writeUInt16LE(0, 6);
end.writeUInt16LE(files.length, 8);
end.writeUInt16LE(files.length, 10);
end.writeUInt32LE(centralDirectory.length, 12);
end.writeUInt32LE(offset, 16);
end.writeUInt16LE(0, 20);
return Buffer.concat([...localParts, centralDirectory, end]);
}
function crc32(buffer) {
let crc = 0xffffffff;
for (const byte of buffer) {
crc = (crc >>> 8) ^ crcTable[(crc ^ byte) & 0xff];
}
return (crc ^ 0xffffffff) >>> 0;
}

View File

@ -127,32 +127,19 @@ ${pluginSdkExports.map((_, index) => ` | typeof sdk${index}`).join("\n")};
function renderRuntimeIndex() {
const packageJson = JSON.parse(readFileSync(path.join(rootDir, "package.json"), "utf8"));
return `import { PLUGIN_ID } from "./constants.js";
import { registerAllHooks } from "./generated-hooks.js";
return `import { registerAllHooks } from "./generated-hooks.js";
import { registerAllRegistrars } from "./generated-registrars.js";
import { registerKitchenSinkRuntime } from "./kitchen-runtime.js";
import {
KITCHEN_SINK_EXPECTED_DIAGNOSTICS,
resolveKitchenSinkPersonality,
} from "./personality.js";
export const plugin = {
id: PLUGIN_ID,
id: "openclaw-kitchen-sink-fixture",
name: "OpenClaw Kitchen Sink",
version: "${packageJson.version}",
description: "Credential-free fixture covering OpenClaw plugin API seams.",
expectedDiagnostics: KITCHEN_SINK_EXPECTED_DIAGNOSTICS,
register(api) {
const personality = resolveKitchenSinkPersonality(api);
registerAllHooks(api);
if (personality !== "conformance") {
registerAllRegistrars(api);
}
if (personality !== "adversarial") {
registerKitchenSinkRuntime(api, {
includeAgentToolResultMiddleware: personality !== "conformance",
});
}
registerAllRegistrars(api);
registerKitchenSinkRuntime(api);
},
};
@ -169,13 +156,6 @@ function renderManifest({ manifestContracts, packageVersion }) {
const contracts = Object.fromEntries(manifestContracts.map((field) => [field, [`kitchen-sink-${kebab(field)}`]]));
appendContract(contracts, "imageGenerationProviders", "kitchen-sink-image");
appendContract(contracts, "mediaUnderstandingProviders", "kitchen-sink-media");
appendContract(contracts, "speechProviders", "kitchen-sink-speech");
appendContract(contracts, "realtimeTranscriptionProviders", "kitchen-sink-realtime-transcription");
appendContract(contracts, "realtimeVoiceProviders", "kitchen-sink-realtime-voice");
appendContract(contracts, "videoGenerationProviders", "kitchen-sink-video");
appendContract(contracts, "musicGenerationProviders", "kitchen-sink-music");
appendContract(contracts, "memoryEmbeddingProviders", "kitchen-sink-memory-embedding");
appendContract(contracts, "agentToolResultMiddleware", "kitchen-sink-agent-tool-result-middleware");
appendContract(contracts, "webSearchProviders", "kitchen-sink-search");
appendContract(contracts, "webFetchProviders", "kitchen-sink-fetch");
appendContract(contracts, "tools", "kitchen_sink_image_job");
@ -189,65 +169,23 @@ function renderManifest({ manifestContracts, packageVersion }) {
enabledByDefault: false,
kind: ["tool", "hook", "channel", "provider"],
channels: ["kitchen-sink-channel"],
providers: [
"kitchen-sink-provider",
"kitchen-sink-llm",
"kitchen-sink-image",
"kitchen-sink-speech",
"kitchen-sink-video",
"kitchen-sink-music",
],
providers: ["kitchen-sink-provider", "kitchen-sink-llm"],
cliBackends: ["kitchen-sink-cli-backend"],
commandAliases: [
{ command: "kitchen", pluginId: "openclaw-kitchen-sink-fixture" },
{ command: "kitchen-sink", pluginId: "openclaw-kitchen-sink-fixture" },
],
activation: {
onProviders: [
"kitchen-sink-provider",
"kitchen-sink-llm",
"kitchen-sink-image",
"kitchen-sink-speech",
"kitchen-sink-video",
"kitchen-sink-music",
],
onProviders: ["kitchen-sink-provider", "kitchen-sink-llm", "kitchen-sink-image"],
onChannels: ["kitchen-sink-channel"],
onCommands: ["kitchen", "kitchen-sink"],
onCapabilities: ["provider", "channel", "tool", "hook"],
},
channelConfigs: {
"kitchen-sink-channel": {
schema: {
type: "object",
additionalProperties: false,
properties: {
configured: { type: "boolean", default: true },
disabled: { type: "boolean", default: false },
enabled: { type: "boolean", default: true },
token: { type: "string" },
},
},
uiHints: {
token: { sensitive: true },
},
label: "Kitchen Sink",
description: "Credential-free channel fixture for deterministic Kitchen Sink conversations.",
commands: {
nativeCommandsAutoEnabled: true,
nativeSkillsAutoEnabled: true,
},
},
},
setup: {
providers: [
{ id: "kitchen-sink-provider", authMethods: ["none"], envVars: [] },
{ id: "kitchen-sink-llm", authMethods: ["none"], envVars: [] },
{ id: "kitchen-sink-image", authMethods: ["none"], envVars: [] },
{ id: "kitchen-sink-speech", authMethods: ["none"], envVars: [] },
{ id: "kitchen-sink-realtime-transcription", authMethods: ["none"], envVars: [] },
{ id: "kitchen-sink-realtime-voice", authMethods: ["none"], envVars: [] },
{ id: "kitchen-sink-video", authMethods: ["none"], envVars: [] },
{ id: "kitchen-sink-music", authMethods: ["none"], envVars: [] },
],
cliBackends: ["kitchen-sink-cli-backend"],
configMigrations: ["kitchen-sink-config-migration"],
@ -259,7 +197,6 @@ function renderManifest({ manifestContracts, packageVersion }) {
additionalProperties: false,
properties: {
enabled: { type: "boolean", default: false },
personality: { type: "string", enum: ["full", "conformance", "adversarial"], default: "full" },
},
},
};
@ -274,18 +211,6 @@ function renderPackageJson({ packageVersion }) {
openclawVersion: packageVersion,
pluginSdkVersion: packageVersion,
};
packageJson.openclaw.install = {
...(packageJson.openclaw.install ?? {}),
clawhubSpec: "clawhub:@openclaw/kitchen-sink",
npmSpec: "@openclaw/kitchen-sink",
defaultChoice: "clawhub",
minHostVersion: `>=${packageVersion}`,
};
packageJson.openclaw.release = {
...(packageJson.openclaw.release ?? {}),
publishToClawHub: true,
publishToNpm: true,
};
if (packageJson.dependencies?.openclaw) {
packageJson.dependencies.openclaw = packageVersion;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 KiB

View File

@ -1,23 +0,0 @@
export const PLUGIN_ID = "openclaw-kitchen-sink-fixture";
export const IMAGE_PROVIDER_ID = "kitchen-sink-image";
export const MEDIA_PROVIDER_ID = "kitchen-sink-media";
export const TEXT_PROVIDER_ID = "kitchen-sink-llm";
export const WEB_SEARCH_PROVIDER_ID = "kitchen-sink-search";
export const WEB_FETCH_PROVIDER_ID = "kitchen-sink-fetch";
export const SPEECH_PROVIDER_ID = "kitchen-sink-speech";
export const REALTIME_TRANSCRIPTION_PROVIDER_ID = "kitchen-sink-realtime-transcription";
export const REALTIME_VOICE_PROVIDER_ID = "kitchen-sink-realtime-voice";
export const VIDEO_PROVIDER_ID = "kitchen-sink-video";
export const MUSIC_PROVIDER_ID = "kitchen-sink-music";
export const MEMORY_EMBEDDING_PROVIDER_ID = "kitchen-sink-memory-embedding";
export const COMPACTION_PROVIDER_ID = "kitchen-sink-compaction";
export const CHANNEL_ID = "kitchen-sink-channel";
export const CHANNEL_ACCOUNT_ID = "local";
export const DEFAULT_IMAGE_MODEL = "kitchen-sink-image-v1";
export const DEFAULT_MEDIA_MODEL = "kitchen-sink-vision-v1";
export const DEFAULT_TEXT_MODEL = "kitchen-sink-text-v1";
export const DEFAULT_SPEECH_MODEL = "kitchen-sink-tts-v1";
export const DEFAULT_VIDEO_MODEL = "kitchen-sink-video-v1";
export const DEFAULT_MUSIC_MODEL = "kitchen-sink-music-v1";
export const DEFAULT_EMBEDDING_MODEL = "kitchen-sink-embed-v1";
export const DEFAULT_IMAGE_DELAY_MS = 10_000;

View File

@ -1,81 +0,0 @@
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { DEFAULT_IMAGE_MODEL, PLUGIN_ID } from "../constants.js";
// Use a real bundled PNG so image-provider consumers exercise binary payloads,
// data URLs, hashes, and dimensions instead of a text-only mock.
const KITCHEN_SINK_OFFICE_IMAGE_FILE = "kitchen_sink_office.png";
const KITCHEN_SINK_OFFICE_IMAGE = readFileSync(
new URL(`../assets/${KITCHEN_SINK_OFFICE_IMAGE_FILE}`, import.meta.url),
);
const KITCHEN_SINK_OFFICE_SHA256 = sha256Hex(KITCHEN_SINK_OFFICE_IMAGE);
const KITCHEN_IMAGE_FIXTURES = [
{
id: "office-lobby-sink",
label: "Kitchen Sink Office",
assetName: KITCHEN_SINK_OFFICE_IMAGE_FILE,
buffer: KITCHEN_SINK_OFFICE_IMAGE,
sha256: KITCHEN_SINK_OFFICE_SHA256,
mimeType: "image/png",
width: 1024,
height: 1024,
description: "office lobby scene with a lobster-costumed figure holding a real sink",
source: "bundled-real-image",
},
];
export function createKitchenSinkImageAsset({
prompt,
jobId,
scenario = "image.generate",
model = DEFAULT_IMAGE_MODEL,
}) {
const fixture = selectKitchenImageFixture(prompt);
const buffer = Buffer.from(fixture.buffer);
const seed = stableHash(`${jobId}:${prompt}:${fixture.id}`);
return {
buffer,
mimeType: fixture.mimeType,
fileName: `${jobId}.png`,
dataUrl: `data:${fixture.mimeType};base64,${buffer.toString("base64")}`,
revisedPrompt: `Kitchen Sink office image fixture: ${prompt}`,
metadata: {
kitchenSink: true,
assetId: fixture.id,
assetName: fixture.assetName,
source: fixture.source,
model,
width: fixture.width,
height: fixture.height,
sizeBytes: buffer.byteLength,
sha256: fixture.sha256,
contentHash: fixture.sha256.slice(0, 16),
seed,
finishReason: "success",
pluginId: PLUGIN_ID,
scenarioId: scenario,
jobId,
prompt,
},
};
}
function selectKitchenImageFixture(_prompt) {
// Single fixture today, prompt-aware selection later if we add more real
// assets for edit/upscale/multi-image scenarios.
return KITCHEN_IMAGE_FIXTURES[0];
}
function sha256Hex(buffer) {
return createHash("sha256").update(buffer).digest("hex");
}
function stableHash(input) {
let hash = 2166136261;
for (let index = 0; index < input.length; index += 1) {
hash ^= input.charCodeAt(index);
hash = Math.imul(hash, 16777619);
}
return (hash >>> 0).toString(16).padStart(8, "0");
}

View File

@ -1,209 +0,0 @@
import {
DEFAULT_IMAGE_MODEL,
DEFAULT_MEDIA_MODEL,
DEFAULT_TEXT_MODEL,
IMAGE_PROVIDER_ID,
TEXT_PROVIDER_ID,
WEB_FETCH_PROVIDER_ID,
WEB_SEARCH_PROVIDER_ID,
} from "../constants.js";
export function kitchenTextProviderConfig() {
// Looks like a real provider config, but stays credential-free and local so
// installs can use it in CI, Crabpot, and plugin-inspector.
return {
baseUrl: "kitchen-sink://local",
apiKey: "kitchen-sink-local-fixture",
auth: "token",
api: "kitchen-sink",
models: [kitchenTextModelDefinition()],
};
}
export function kitchenTextModelDefinition() {
return {
id: DEFAULT_TEXT_MODEL,
name: "Kitchen Sink Text Fixture",
api: "kitchen-sink",
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 8192,
maxTokens: 2048,
description: "Deterministic OpenClaw plugin text-provider fixture.",
};
}
export function createKitchenTextStream(model, context) {
// Emit the same coarse lifecycle events a streaming text provider would emit;
// consumers can test stream handling without a live model.
const stream = createAssistantMessageEventStream();
queueMicrotask(() => {
const prompt = extractLastUserPrompt(context);
const text = kitchenTextResponse(prompt);
const message = {
role: "assistant",
content: [{ type: "text", text }],
api: model?.api || "kitchen-sink",
provider: TEXT_PROVIDER_ID,
model: model?.id || DEFAULT_TEXT_MODEL,
usage: estimateUsage(prompt, text),
stopReason: "stop",
timestamp: Date.now(),
};
stream.push({ type: "start", partial: { ...message, content: [] } });
stream.push({ type: "text_start", contentIndex: 0, partial: { ...message, content: [] } });
stream.push({ type: "text_delta", contentIndex: 0, delta: text, partial: message });
stream.push({ type: "text_end", contentIndex: 0, content: text, partial: message });
stream.push({ type: "done", reason: "stop", message });
stream.end(message);
});
return stream;
}
export function kitchenTextResponse(prompt) {
const normalized = normalizePrompt(prompt, "kitchen sink text inference");
if (/\b(image|picture|draw|generate)\b/i.test(normalized)) {
return [
"Kitchen Sink text fixture:",
`prompt="${normalized}"`,
`I would route this to ${IMAGE_PROVIDER_ID}/${DEFAULT_IMAGE_MODEL}, create a queued image job, wait for completion, then return the bundled kitchen_sink_office.png asset with PNG metadata.`,
].join(" ");
}
if (/\b(search|find|lookup|web)\b/i.test(normalized)) {
return [
"Kitchen Sink text fixture:",
`prompt="${normalized}"`,
`I would call ${WEB_SEARCH_PROVIDER_ID} for ranked fixture results and ${WEB_FETCH_PROVIDER_ID} for deterministic document fetches.`,
].join(" ");
}
if (/\b(rate limit|timeout|fail|error)\b/i.test(normalized)) {
return [
"Kitchen Sink text fixture:",
`prompt="${normalized}"`,
"Failure fixtures are available: rate limit returns 429 with retry metadata, timeout returns 504, and fail returns a deterministic provider error.",
].join(" ");
}
return [
"Kitchen Sink text fixture:",
`prompt="${normalized}"`,
"Available realistic surfaces: direct prefix, registered tools, image provider lifecycle, media understanding, web search, web fetch, channel health, hooks, detached tasks, and text provider catalog.",
].join(" ");
}
export function kitchenImageDescription(prompt, count) {
return [
`Kitchen Sink media fixture described ${count || 1} image${count === 1 ? "" : "s"}.`,
`Prompt: ${normalizePrompt(prompt, "describe kitchen sink image")}.`,
"Visible content: the bundled kitchen_sink_office PNG: an office lobby scene with a lobster-costumed figure holding a real sink.",
].join(" ");
}
export function estimateUsage(prompt = "", text = "") {
const input = estimateTokens(prompt);
const output = estimateTokens(text);
return {
input,
output,
cacheRead: 0,
cacheWrite: 0,
totalTokens: input + output,
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
total: 0,
},
};
}
function createAssistantMessageEventStream() {
const queue = [];
const waiters = [];
let done = false;
let finalResult;
let resolveResult;
const resultPromise = new Promise((resolve) => {
resolveResult = resolve;
});
return {
push(event) {
if (done) {
return;
}
if (event.type === "done" || event.type === "error") {
finalResult = event.type === "done" ? event.message : event.error;
done = true;
resolveResult(finalResult);
}
const waiter = waiters.shift();
if (waiter) {
waiter({ value: event, done: false });
} else {
queue.push(event);
}
},
end(result) {
if (result !== undefined && finalResult === undefined) {
finalResult = result;
resolveResult(result);
}
done = true;
while (waiters.length > 0) {
waiters.shift()({ value: undefined, done: true });
}
},
async *[Symbol.asyncIterator]() {
while (true) {
if (queue.length > 0) {
yield queue.shift();
} else if (done) {
return;
} else {
const next = await new Promise((resolve) => waiters.push(resolve));
if (next.done) {
return;
}
yield next.value;
}
}
},
result() {
return resultPromise;
},
};
}
function extractLastUserPrompt(context) {
const messages = Array.isArray(context?.messages) ? context.messages : [];
for (let index = messages.length - 1; index >= 0; index -= 1) {
const message = messages[index];
if (message?.role !== "user") {
continue;
}
if (typeof message.content === "string") {
return message.content;
}
if (Array.isArray(message.content)) {
const text = message.content
.filter((item) => item?.type === "text" && typeof item.text === "string")
.map((item) => item.text)
.join(" ")
.trim();
if (text) {
return text;
}
}
}
return "kitchen sink text inference";
}
function estimateTokens(text) {
return Math.max(1, Math.ceil(String(text).trim().split(/\s+/).filter(Boolean).length * 1.35));
}
function normalizePrompt(value, fallback) {
const text = String(value ?? "").replace(/\s+/g, " ").trim();
return text || fallback;
}

View File

@ -1,11 +1,10 @@
// Generated by scripts/sync-surface.mjs from openclaw 2026.5.7. Do not edit by hand.
// Generated by scripts/sync-surface.mjs from openclaw 2026.4.26. Do not edit by hand.
import { observeKitchenHook } from "./scenarios.js";
export function registerAllHooks(api) {
api.on("after_compaction", kitchenSinkHook("after_compaction"));
api.on("after_tool_call", kitchenSinkHook("after_tool_call"));
api.on("agent_end", kitchenSinkHook("agent_end"));
api.on("agent_turn_prepare", kitchenSinkHook("agent_turn_prepare"));
api.on("before_agent_finalize", kitchenSinkHook("before_agent_finalize"));
api.on("before_agent_reply", kitchenSinkHook("before_agent_reply"));
api.on("before_agent_start", kitchenSinkHook("before_agent_start"));
@ -17,10 +16,8 @@ export function registerAllHooks(api) {
api.on("before_prompt_build", kitchenSinkHook("before_prompt_build"));
api.on("before_reset", kitchenSinkHook("before_reset"));
api.on("before_tool_call", kitchenSinkHook("before_tool_call"));
api.on("cron_changed", kitchenSinkHook("cron_changed"));
api.on("gateway_start", kitchenSinkHook("gateway_start"));
api.on("gateway_stop", kitchenSinkHook("gateway_stop"));
api.on("heartbeat_prompt_contribution", kitchenSinkHook("heartbeat_prompt_contribution"));
api.on("inbound_claim", kitchenSinkHook("inbound_claim"));
api.on("llm_input", kitchenSinkHook("llm_input"));
api.on("llm_output", kitchenSinkHook("llm_output"));

View File

@ -1,7 +1,6 @@
// Generated by scripts/sync-surface.mjs from openclaw 2026.5.7. Do not edit by hand.
// Generated by scripts/sync-surface.mjs from openclaw 2026.4.26. Do not edit by hand.
export function registerAllRegistrars(api) {
safeRegister("registerAgentEventSubscription", () => api.registerAgentEventSubscription(payloadFor("registerAgentEventSubscription")));
safeRegister("registerAgentHarness", () => api.registerAgentHarness(payloadFor("registerAgentHarness")));
safeRegister("registerAgentToolResultMiddleware", () => api.registerAgentToolResultMiddleware(payloadFor("registerAgentToolResultMiddleware")));
safeRegister("registerAutoEnableProbe", () => api.registerAutoEnableProbe(payloadFor("registerAutoEnableProbe")));
@ -13,7 +12,6 @@ export function registerAllRegistrars(api) {
safeRegister("registerCompactionProvider", () => api.registerCompactionProvider(payloadFor("registerCompactionProvider")));
safeRegister("registerConfigMigration", () => api.registerConfigMigration(payloadFor("registerConfigMigration")));
safeRegister("registerContextEngine", () => api.registerContextEngine(payloadFor("registerContextEngine")));
safeRegister("registerControlUiDescriptor", () => api.registerControlUiDescriptor(payloadFor("registerControlUiDescriptor")));
void "api.registerDetachedTaskRuntime("; // Covered by the hand-owned Kitchen Sink task runtime.
safeRegister("registerGatewayDiscoveryService", () => api.registerGatewayDiscoveryService(payloadFor("registerGatewayDiscoveryService")));
safeRegister("registerGatewayMethod", () => api.registerGatewayMethod(payloadFor("registerGatewayMethod")));
@ -32,21 +30,15 @@ export function registerAllRegistrars(api) {
safeRegister("registerMigrationProvider", () => api.registerMigrationProvider(payloadFor("registerMigrationProvider")));
safeRegister("registerMusicGenerationProvider", () => api.registerMusicGenerationProvider(payloadFor("registerMusicGenerationProvider")));
safeRegister("registerNodeHostCommand", () => api.registerNodeHostCommand(payloadFor("registerNodeHostCommand")));
safeRegister("registerNodeInvokePolicy", () => api.registerNodeInvokePolicy(payloadFor("registerNodeInvokePolicy")));
safeRegister("registerProvider", () => api.registerProvider(payloadFor("registerProvider")));
safeRegister("registerRealtimeTranscriptionProvider", () => api.registerRealtimeTranscriptionProvider(payloadFor("registerRealtimeTranscriptionProvider")));
safeRegister("registerRealtimeVoiceProvider", () => api.registerRealtimeVoiceProvider(payloadFor("registerRealtimeVoiceProvider")));
safeRegister("registerReload", () => api.registerReload(payloadFor("registerReload")));
safeRegister("registerRuntimeLifecycle", () => api.registerRuntimeLifecycle(payloadFor("registerRuntimeLifecycle")));
safeRegister("registerSecurityAuditCollector", () => api.registerSecurityAuditCollector(payloadFor("registerSecurityAuditCollector")));
safeRegister("registerService", () => api.registerService(payloadFor("registerService")));
safeRegister("registerSessionExtension", () => api.registerSessionExtension(payloadFor("registerSessionExtension")));
safeRegister("registerSessionSchedulerJob", () => api.registerSessionSchedulerJob(payloadFor("registerSessionSchedulerJob")));
safeRegister("registerSpeechProvider", () => api.registerSpeechProvider(payloadFor("registerSpeechProvider")));
safeRegister("registerTextTransforms", () => api.registerTextTransforms(payloadFor("registerTextTransforms")));
safeRegister("registerTool", () => api.registerTool(payloadFor("registerTool")));
safeRegister("registerToolMetadata", () => api.registerToolMetadata(payloadFor("registerToolMetadata")));
safeRegister("registerTrustedToolPolicy", () => api.registerTrustedToolPolicy(payloadFor("registerTrustedToolPolicy")));
safeRegister("registerVideoGenerationProvider", () => api.registerVideoGenerationProvider(payloadFor("registerVideoGenerationProvider")));
safeRegister("registerWebFetchProvider", () => api.registerWebFetchProvider(payloadFor("registerWebFetchProvider")));
safeRegister("registerWebSearchProvider", () => api.registerWebSearchProvider(payloadFor("registerWebSearchProvider")));

View File

@ -1,4 +1,4 @@
// Generated by scripts/sync-surface.mjs from openclaw 2026.5.7. Do not edit by hand.
// Generated by scripts/sync-surface.mjs from openclaw 2026.4.26. Do not edit by hand.
import type * as sdk0 from "openclaw/plugin-sdk";
import type * as sdk1 from "openclaw/plugin-sdk/account-core";
import type * as sdk2 from "openclaw/plugin-sdk/account-helpers";
@ -8,290 +8,261 @@ import type * as sdk5 from "openclaw/plugin-sdk/account-resolution-runtime";
import type * as sdk6 from "openclaw/plugin-sdk/acp-binding-resolve-runtime";
import type * as sdk7 from "openclaw/plugin-sdk/acp-binding-runtime";
import type * as sdk8 from "openclaw/plugin-sdk/acp-runtime";
import type * as sdk9 from "openclaw/plugin-sdk/acp-runtime-backend";
import type * as sdk10 from "openclaw/plugin-sdk/agent-config-primitives";
import type * as sdk11 from "openclaw/plugin-sdk/agent-harness";
import type * as sdk12 from "openclaw/plugin-sdk/agent-harness-runtime";
import type * as sdk13 from "openclaw/plugin-sdk/agent-media-payload";
import type * as sdk14 from "openclaw/plugin-sdk/agent-runtime";
import type * as sdk15 from "openclaw/plugin-sdk/agent-runtime-test-contracts";
import type * as sdk16 from "openclaw/plugin-sdk/allow-from";
import type * as sdk17 from "openclaw/plugin-sdk/allowlist-config-edit";
import type * as sdk18 from "openclaw/plugin-sdk/approval-auth-runtime";
import type * as sdk19 from "openclaw/plugin-sdk/approval-client-runtime";
import type * as sdk20 from "openclaw/plugin-sdk/approval-delivery-runtime";
import type * as sdk21 from "openclaw/plugin-sdk/approval-gateway-runtime";
import type * as sdk22 from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
import type * as sdk23 from "openclaw/plugin-sdk/approval-handler-runtime";
import type * as sdk24 from "openclaw/plugin-sdk/approval-native-runtime";
import type * as sdk25 from "openclaw/plugin-sdk/approval-reply-runtime";
import type * as sdk26 from "openclaw/plugin-sdk/approval-runtime";
import type * as sdk27 from "openclaw/plugin-sdk/async-lock-runtime";
import type * as sdk28 from "openclaw/plugin-sdk/boolean-param";
import type * as sdk29 from "openclaw/plugin-sdk/browser-config";
import type * as sdk30 from "openclaw/plugin-sdk/bundled-channel-config-schema";
import type * as sdk31 from "openclaw/plugin-sdk/channel-actions";
import type * as sdk32 from "openclaw/plugin-sdk/channel-activity-runtime";
import type * as sdk33 from "openclaw/plugin-sdk/channel-config-helpers";
import type * as sdk34 from "openclaw/plugin-sdk/channel-config-primitives";
import type * as sdk35 from "openclaw/plugin-sdk/channel-config-schema";
import type * as sdk36 from "openclaw/plugin-sdk/channel-config-schema-legacy";
import type * as sdk37 from "openclaw/plugin-sdk/channel-config-writes";
import type * as sdk38 from "openclaw/plugin-sdk/channel-contract";
import type * as sdk39 from "openclaw/plugin-sdk/channel-contract-testing";
import type * as sdk40 from "openclaw/plugin-sdk/channel-core";
import type * as sdk41 from "openclaw/plugin-sdk/channel-entry-contract";
import type * as sdk42 from "openclaw/plugin-sdk/channel-envelope";
import type * as sdk43 from "openclaw/plugin-sdk/channel-feedback";
import type * as sdk44 from "openclaw/plugin-sdk/channel-inbound";
import type * as sdk45 from "openclaw/plugin-sdk/channel-inbound-debounce";
import type * as sdk46 from "openclaw/plugin-sdk/channel-inbound-roots";
import type * as sdk47 from "openclaw/plugin-sdk/channel-lifecycle";
import type * as sdk48 from "openclaw/plugin-sdk/channel-location";
import type * as sdk49 from "openclaw/plugin-sdk/channel-logging";
import type * as sdk50 from "openclaw/plugin-sdk/channel-mention-gating";
import type * as sdk51 from "openclaw/plugin-sdk/channel-pairing";
import type * as sdk52 from "openclaw/plugin-sdk/channel-pairing-paths";
import type * as sdk53 from "openclaw/plugin-sdk/channel-plugin-common";
import type * as sdk54 from "openclaw/plugin-sdk/channel-policy";
import type * as sdk55 from "openclaw/plugin-sdk/channel-reply-options-runtime";
import type * as sdk56 from "openclaw/plugin-sdk/channel-reply-pipeline";
import type * as sdk57 from "openclaw/plugin-sdk/channel-route";
import type * as sdk58 from "openclaw/plugin-sdk/channel-runtime";
import type * as sdk59 from "openclaw/plugin-sdk/channel-runtime-context";
import type * as sdk60 from "openclaw/plugin-sdk/channel-secret-basic-runtime";
import type * as sdk61 from "openclaw/plugin-sdk/channel-secret-runtime";
import type * as sdk62 from "openclaw/plugin-sdk/channel-secret-tts-runtime";
import type * as sdk63 from "openclaw/plugin-sdk/channel-send-result";
import type * as sdk64 from "openclaw/plugin-sdk/channel-setup";
import type * as sdk65 from "openclaw/plugin-sdk/channel-status";
import type * as sdk66 from "openclaw/plugin-sdk/channel-streaming";
import type * as sdk67 from "openclaw/plugin-sdk/channel-target-testing";
import type * as sdk68 from "openclaw/plugin-sdk/channel-targets";
import type * as sdk69 from "openclaw/plugin-sdk/channel-test-helpers";
import type * as sdk70 from "openclaw/plugin-sdk/cli-backend";
import type * as sdk71 from "openclaw/plugin-sdk/cli-runtime";
import type * as sdk72 from "openclaw/plugin-sdk/collection-runtime";
import type * as sdk73 from "openclaw/plugin-sdk/command-auth";
import type * as sdk74 from "openclaw/plugin-sdk/command-auth-native";
import type * as sdk75 from "openclaw/plugin-sdk/command-detection";
import type * as sdk76 from "openclaw/plugin-sdk/command-gating";
import type * as sdk77 from "openclaw/plugin-sdk/command-primitives-runtime";
import type * as sdk78 from "openclaw/plugin-sdk/command-status";
import type * as sdk79 from "openclaw/plugin-sdk/command-status-runtime";
import type * as sdk80 from "openclaw/plugin-sdk/command-surface";
import type * as sdk81 from "openclaw/plugin-sdk/compat";
import type * as sdk82 from "openclaw/plugin-sdk/concurrency-runtime";
import type * as sdk83 from "openclaw/plugin-sdk/config-mutation";
import type * as sdk84 from "openclaw/plugin-sdk/config-runtime";
import type * as sdk85 from "openclaw/plugin-sdk/config-schema";
import type * as sdk86 from "openclaw/plugin-sdk/config-types";
import type * as sdk87 from "openclaw/plugin-sdk/context-visibility-runtime";
import type * as sdk88 from "openclaw/plugin-sdk/conversation-binding-runtime";
import type * as sdk89 from "openclaw/plugin-sdk/conversation-runtime";
import type * as sdk90 from "openclaw/plugin-sdk/core";
import type * as sdk91 from "openclaw/plugin-sdk/cron-store-runtime";
import type * as sdk92 from "openclaw/plugin-sdk/dangerous-name-runtime";
import type * as sdk93 from "openclaw/plugin-sdk/dedupe-runtime";
import type * as sdk94 from "openclaw/plugin-sdk/delivery-queue-runtime";
import type * as sdk95 from "openclaw/plugin-sdk/device-bootstrap";
import type * as sdk96 from "openclaw/plugin-sdk/diagnostic-runtime";
import type * as sdk97 from "openclaw/plugin-sdk/direct-dm";
import type * as sdk98 from "openclaw/plugin-sdk/direct-dm-access";
import type * as sdk99 from "openclaw/plugin-sdk/direct-dm-guard-policy";
import type * as sdk100 from "openclaw/plugin-sdk/directory-config-runtime";
import type * as sdk101 from "openclaw/plugin-sdk/directory-runtime";
import type * as sdk102 from "openclaw/plugin-sdk/discord";
import type * as sdk103 from "openclaw/plugin-sdk/document-extractor";
import type * as sdk104 from "openclaw/plugin-sdk/error-runtime";
import type * as sdk105 from "openclaw/plugin-sdk/extension-shared";
import type * as sdk106 from "openclaw/plugin-sdk/fetch-runtime";
import type * as sdk107 from "openclaw/plugin-sdk/file-access-runtime";
import type * as sdk108 from "openclaw/plugin-sdk/file-lock";
import type * as sdk109 from "openclaw/plugin-sdk/gateway-runtime";
import type * as sdk110 from "openclaw/plugin-sdk/global-singleton";
import type * as sdk111 from "openclaw/plugin-sdk/group-access";
import type * as sdk112 from "openclaw/plugin-sdk/group-activation";
import type * as sdk113 from "openclaw/plugin-sdk/heartbeat-runtime";
import type * as sdk114 from "openclaw/plugin-sdk/hook-runtime";
import type * as sdk115 from "openclaw/plugin-sdk/host-runtime";
import type * as sdk116 from "openclaw/plugin-sdk/image-generation";
import type * as sdk117 from "openclaw/plugin-sdk/image-generation-core";
import type * as sdk118 from "openclaw/plugin-sdk/image-generation-runtime";
import type * as sdk119 from "openclaw/plugin-sdk/inbound-envelope";
import type * as sdk120 from "openclaw/plugin-sdk/inbound-reply-dispatch";
import type * as sdk121 from "openclaw/plugin-sdk/infra-runtime";
import type * as sdk122 from "openclaw/plugin-sdk/interactive-runtime";
import type * as sdk123 from "openclaw/plugin-sdk/json-store";
import type * as sdk124 from "openclaw/plugin-sdk/keyed-async-queue";
import type * as sdk125 from "openclaw/plugin-sdk/lazy-runtime";
import type * as sdk126 from "openclaw/plugin-sdk/lmstudio";
import type * as sdk127 from "openclaw/plugin-sdk/lmstudio-runtime";
import type * as sdk128 from "openclaw/plugin-sdk/logging-core";
import type * as sdk129 from "openclaw/plugin-sdk/markdown-table-runtime";
import type * as sdk130 from "openclaw/plugin-sdk/media-generation-runtime";
import type * as sdk131 from "openclaw/plugin-sdk/media-generation-runtime-shared";
import type * as sdk132 from "openclaw/plugin-sdk/media-mime";
import type * as sdk133 from "openclaw/plugin-sdk/media-runtime";
import type * as sdk134 from "openclaw/plugin-sdk/media-store";
import type * as sdk135 from "openclaw/plugin-sdk/media-understanding";
import type * as sdk136 from "openclaw/plugin-sdk/media-understanding-runtime";
import type * as sdk137 from "openclaw/plugin-sdk/memory-core-engine-runtime";
import type * as sdk138 from "openclaw/plugin-sdk/memory-core-host-engine-embeddings";
import type * as sdk139 from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
import type * as sdk140 from "openclaw/plugin-sdk/memory-core-host-engine-qmd";
import type * as sdk141 from "openclaw/plugin-sdk/memory-core-host-engine-storage";
import type * as sdk142 from "openclaw/plugin-sdk/memory-core-host-events";
import type * as sdk143 from "openclaw/plugin-sdk/memory-core-host-multimodal";
import type * as sdk144 from "openclaw/plugin-sdk/memory-core-host-query";
import type * as sdk145 from "openclaw/plugin-sdk/memory-core-host-runtime-cli";
import type * as sdk146 from "openclaw/plugin-sdk/memory-core-host-runtime-core";
import type * as sdk147 from "openclaw/plugin-sdk/memory-core-host-runtime-files";
import type * as sdk148 from "openclaw/plugin-sdk/memory-core-host-secret";
import type * as sdk149 from "openclaw/plugin-sdk/memory-core-host-status";
import type * as sdk150 from "openclaw/plugin-sdk/memory-host-core";
import type * as sdk151 from "openclaw/plugin-sdk/memory-host-events";
import type * as sdk152 from "openclaw/plugin-sdk/memory-host-files";
import type * as sdk153 from "openclaw/plugin-sdk/memory-host-markdown";
import type * as sdk154 from "openclaw/plugin-sdk/memory-host-search";
import type * as sdk155 from "openclaw/plugin-sdk/memory-host-status";
import type * as sdk156 from "openclaw/plugin-sdk/messaging-targets";
import type * as sdk157 from "openclaw/plugin-sdk/migration";
import type * as sdk158 from "openclaw/plugin-sdk/migration-runtime";
import type * as sdk159 from "openclaw/plugin-sdk/model-session-runtime";
import type * as sdk160 from "openclaw/plugin-sdk/models-provider-runtime";
import type * as sdk161 from "openclaw/plugin-sdk/music-generation";
import type * as sdk162 from "openclaw/plugin-sdk/music-generation-core";
import type * as sdk163 from "openclaw/plugin-sdk/native-command-config-runtime";
import type * as sdk164 from "openclaw/plugin-sdk/native-command-registry";
import type * as sdk165 from "openclaw/plugin-sdk/number-runtime";
import type * as sdk166 from "openclaw/plugin-sdk/outbound-media";
import type * as sdk167 from "openclaw/plugin-sdk/outbound-runtime";
import type * as sdk168 from "openclaw/plugin-sdk/outbound-send-deps";
import type * as sdk169 from "openclaw/plugin-sdk/param-readers";
import type * as sdk170 from "openclaw/plugin-sdk/persistent-dedupe";
import type * as sdk171 from "openclaw/plugin-sdk/plugin-config-runtime";
import type * as sdk172 from "openclaw/plugin-sdk/plugin-entry";
import type * as sdk173 from "openclaw/plugin-sdk/plugin-runtime";
import type * as sdk174 from "openclaw/plugin-sdk/plugin-test-api";
import type * as sdk175 from "openclaw/plugin-sdk/plugin-test-contracts";
import type * as sdk176 from "openclaw/plugin-sdk/plugin-test-runtime";
import type * as sdk177 from "openclaw/plugin-sdk/poll-runtime";
import type * as sdk178 from "openclaw/plugin-sdk/process-runtime";
import type * as sdk179 from "openclaw/plugin-sdk/provider-auth";
import type * as sdk180 from "openclaw/plugin-sdk/provider-auth-api-key";
import type * as sdk181 from "openclaw/plugin-sdk/provider-auth-login";
import type * as sdk182 from "openclaw/plugin-sdk/provider-auth-result";
import type * as sdk183 from "openclaw/plugin-sdk/provider-auth-runtime";
import type * as sdk184 from "openclaw/plugin-sdk/provider-catalog-runtime";
import type * as sdk185 from "openclaw/plugin-sdk/provider-catalog-shared";
import type * as sdk186 from "openclaw/plugin-sdk/provider-entry";
import type * as sdk187 from "openclaw/plugin-sdk/provider-env-vars";
import type * as sdk188 from "openclaw/plugin-sdk/provider-http";
import type * as sdk189 from "openclaw/plugin-sdk/provider-http-test-mocks";
import type * as sdk190 from "openclaw/plugin-sdk/provider-model-shared";
import type * as sdk191 from "openclaw/plugin-sdk/provider-model-types";
import type * as sdk192 from "openclaw/plugin-sdk/provider-onboard";
import type * as sdk193 from "openclaw/plugin-sdk/provider-selection-runtime";
import type * as sdk194 from "openclaw/plugin-sdk/provider-setup";
import type * as sdk195 from "openclaw/plugin-sdk/provider-stream";
import type * as sdk196 from "openclaw/plugin-sdk/provider-stream-family";
import type * as sdk197 from "openclaw/plugin-sdk/provider-stream-shared";
import type * as sdk198 from "openclaw/plugin-sdk/provider-test-contracts";
import type * as sdk199 from "openclaw/plugin-sdk/provider-tools";
import type * as sdk200 from "openclaw/plugin-sdk/provider-transport-runtime";
import type * as sdk201 from "openclaw/plugin-sdk/provider-usage";
import type * as sdk202 from "openclaw/plugin-sdk/provider-web-fetch";
import type * as sdk203 from "openclaw/plugin-sdk/provider-web-fetch-contract";
import type * as sdk204 from "openclaw/plugin-sdk/provider-web-search";
import type * as sdk205 from "openclaw/plugin-sdk/provider-web-search-config-contract";
import type * as sdk206 from "openclaw/plugin-sdk/provider-web-search-contract";
import type * as sdk207 from "openclaw/plugin-sdk/provider-zai-endpoint";
import type * as sdk208 from "openclaw/plugin-sdk/proxy-capture";
import type * as sdk209 from "openclaw/plugin-sdk/qa-runner-runtime";
import type * as sdk210 from "openclaw/plugin-sdk/realtime-transcription";
import type * as sdk211 from "openclaw/plugin-sdk/realtime-voice";
import type * as sdk212 from "openclaw/plugin-sdk/reply-chunking";
import type * as sdk213 from "openclaw/plugin-sdk/reply-dedupe";
import type * as sdk214 from "openclaw/plugin-sdk/reply-dispatch-runtime";
import type * as sdk215 from "openclaw/plugin-sdk/reply-history";
import type * as sdk216 from "openclaw/plugin-sdk/reply-payload";
import type * as sdk217 from "openclaw/plugin-sdk/reply-reference";
import type * as sdk218 from "openclaw/plugin-sdk/reply-runtime";
import type * as sdk219 from "openclaw/plugin-sdk/request-url";
import type * as sdk220 from "openclaw/plugin-sdk/response-limit-runtime";
import type * as sdk221 from "openclaw/plugin-sdk/retry-runtime";
import type * as sdk222 from "openclaw/plugin-sdk/routing";
import type * as sdk223 from "openclaw/plugin-sdk/run-command";
import type * as sdk224 from "openclaw/plugin-sdk/runtime";
import type * as sdk225 from "openclaw/plugin-sdk/runtime-config-snapshot";
import type * as sdk226 from "openclaw/plugin-sdk/runtime-doctor";
import type * as sdk227 from "openclaw/plugin-sdk/runtime-env";
import type * as sdk228 from "openclaw/plugin-sdk/runtime-fetch";
import type * as sdk229 from "openclaw/plugin-sdk/runtime-group-policy";
import type * as sdk230 from "openclaw/plugin-sdk/runtime-logger";
import type * as sdk231 from "openclaw/plugin-sdk/runtime-secret-resolution";
import type * as sdk232 from "openclaw/plugin-sdk/runtime-store";
import type * as sdk233 from "openclaw/plugin-sdk/sandbox";
import type * as sdk234 from "openclaw/plugin-sdk/secret-file-runtime";
import type * as sdk235 from "openclaw/plugin-sdk/secret-input";
import type * as sdk236 from "openclaw/plugin-sdk/secret-input-runtime";
import type * as sdk237 from "openclaw/plugin-sdk/secret-ref-runtime";
import type * as sdk238 from "openclaw/plugin-sdk/secure-random-runtime";
import type * as sdk239 from "openclaw/plugin-sdk/security-runtime";
import type * as sdk240 from "openclaw/plugin-sdk/self-hosted-provider-setup";
import type * as sdk241 from "openclaw/plugin-sdk/session-binding-runtime";
import type * as sdk242 from "openclaw/plugin-sdk/session-key-runtime";
import type * as sdk243 from "openclaw/plugin-sdk/session-store-runtime";
import type * as sdk244 from "openclaw/plugin-sdk/session-transcript-hit";
import type * as sdk245 from "openclaw/plugin-sdk/session-visibility";
import type * as sdk246 from "openclaw/plugin-sdk/setup";
import type * as sdk247 from "openclaw/plugin-sdk/setup-adapter-runtime";
import type * as sdk248 from "openclaw/plugin-sdk/setup-runtime";
import type * as sdk249 from "openclaw/plugin-sdk/setup-tools";
import type * as sdk250 from "openclaw/plugin-sdk/simple-completion-runtime";
import type * as sdk251 from "openclaw/plugin-sdk/skill-commands-runtime";
import type * as sdk252 from "openclaw/plugin-sdk/skills-runtime";
import type * as sdk253 from "openclaw/plugin-sdk/speech";
import type * as sdk254 from "openclaw/plugin-sdk/speech-core";
import type * as sdk255 from "openclaw/plugin-sdk/ssrf-dispatcher";
import type * as sdk256 from "openclaw/plugin-sdk/ssrf-policy";
import type * as sdk257 from "openclaw/plugin-sdk/ssrf-runtime";
import type * as sdk258 from "openclaw/plugin-sdk/state-paths";
import type * as sdk259 from "openclaw/plugin-sdk/status-helpers";
import type * as sdk260 from "openclaw/plugin-sdk/string-coerce-runtime";
import type * as sdk261 from "openclaw/plugin-sdk/string-normalization-runtime";
import type * as sdk262 from "openclaw/plugin-sdk/system-event-runtime";
import type * as sdk263 from "openclaw/plugin-sdk/talk-config-runtime";
import type * as sdk264 from "openclaw/plugin-sdk/target-resolver-runtime";
import type * as sdk265 from "openclaw/plugin-sdk/telegram-account";
import type * as sdk266 from "openclaw/plugin-sdk/telegram-command-config";
import type * as sdk267 from "openclaw/plugin-sdk/temp-path";
import type * as sdk268 from "openclaw/plugin-sdk/test-env";
import type * as sdk269 from "openclaw/plugin-sdk/test-fixtures";
import type * as sdk270 from "openclaw/plugin-sdk/test-node-mocks";
import type * as sdk271 from "openclaw/plugin-sdk/testing";
import type * as sdk272 from "openclaw/plugin-sdk/text-autolink-runtime";
import type * as sdk273 from "openclaw/plugin-sdk/text-chunking";
import type * as sdk274 from "openclaw/plugin-sdk/text-runtime";
import type * as sdk275 from "openclaw/plugin-sdk/thread-bindings-runtime";
import type * as sdk276 from "openclaw/plugin-sdk/thread-bindings-session-runtime";
import type * as sdk277 from "openclaw/plugin-sdk/time-runtime";
import type * as sdk278 from "openclaw/plugin-sdk/tool-payload";
import type * as sdk279 from "openclaw/plugin-sdk/tool-send";
import type * as sdk280 from "openclaw/plugin-sdk/transport-ready-runtime";
import type * as sdk281 from "openclaw/plugin-sdk/tts-runtime";
import type * as sdk282 from "openclaw/plugin-sdk/video-generation";
import type * as sdk283 from "openclaw/plugin-sdk/video-generation-core";
import type * as sdk284 from "openclaw/plugin-sdk/video-generation-runtime";
import type * as sdk285 from "openclaw/plugin-sdk/web-content-extractor";
import type * as sdk286 from "openclaw/plugin-sdk/web-media";
import type * as sdk287 from "openclaw/plugin-sdk/webhook-ingress";
import type * as sdk288 from "openclaw/plugin-sdk/webhook-path";
import type * as sdk289 from "openclaw/plugin-sdk/webhook-request-guards";
import type * as sdk290 from "openclaw/plugin-sdk/webhook-targets";
import type * as sdk291 from "openclaw/plugin-sdk/windows-spawn";
import type * as sdk292 from "openclaw/plugin-sdk/zod";
import type * as sdk9 from "openclaw/plugin-sdk/agent-config-primitives";
import type * as sdk10 from "openclaw/plugin-sdk/agent-harness";
import type * as sdk11 from "openclaw/plugin-sdk/agent-harness-runtime";
import type * as sdk12 from "openclaw/plugin-sdk/agent-media-payload";
import type * as sdk13 from "openclaw/plugin-sdk/agent-runtime";
import type * as sdk14 from "openclaw/plugin-sdk/allow-from";
import type * as sdk15 from "openclaw/plugin-sdk/allowlist-config-edit";
import type * as sdk16 from "openclaw/plugin-sdk/approval-auth-runtime";
import type * as sdk17 from "openclaw/plugin-sdk/approval-client-runtime";
import type * as sdk18 from "openclaw/plugin-sdk/approval-delivery-runtime";
import type * as sdk19 from "openclaw/plugin-sdk/approval-gateway-runtime";
import type * as sdk20 from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
import type * as sdk21 from "openclaw/plugin-sdk/approval-handler-runtime";
import type * as sdk22 from "openclaw/plugin-sdk/approval-native-runtime";
import type * as sdk23 from "openclaw/plugin-sdk/approval-reply-runtime";
import type * as sdk24 from "openclaw/plugin-sdk/approval-runtime";
import type * as sdk25 from "openclaw/plugin-sdk/boolean-param";
import type * as sdk26 from "openclaw/plugin-sdk/browser-config";
import type * as sdk27 from "openclaw/plugin-sdk/channel-actions";
import type * as sdk28 from "openclaw/plugin-sdk/channel-config-helpers";
import type * as sdk29 from "openclaw/plugin-sdk/channel-config-primitives";
import type * as sdk30 from "openclaw/plugin-sdk/channel-config-schema";
import type * as sdk31 from "openclaw/plugin-sdk/channel-config-schema-legacy";
import type * as sdk32 from "openclaw/plugin-sdk/channel-config-writes";
import type * as sdk33 from "openclaw/plugin-sdk/channel-contract";
import type * as sdk34 from "openclaw/plugin-sdk/channel-contract-testing";
import type * as sdk35 from "openclaw/plugin-sdk/channel-core";
import type * as sdk36 from "openclaw/plugin-sdk/channel-entry-contract";
import type * as sdk37 from "openclaw/plugin-sdk/channel-envelope";
import type * as sdk38 from "openclaw/plugin-sdk/channel-feedback";
import type * as sdk39 from "openclaw/plugin-sdk/channel-inbound";
import type * as sdk40 from "openclaw/plugin-sdk/channel-inbound-debounce";
import type * as sdk41 from "openclaw/plugin-sdk/channel-inbound-roots";
import type * as sdk42 from "openclaw/plugin-sdk/channel-lifecycle";
import type * as sdk43 from "openclaw/plugin-sdk/channel-location";
import type * as sdk44 from "openclaw/plugin-sdk/channel-logging";
import type * as sdk45 from "openclaw/plugin-sdk/channel-mention-gating";
import type * as sdk46 from "openclaw/plugin-sdk/channel-pairing";
import type * as sdk47 from "openclaw/plugin-sdk/channel-pairing-paths";
import type * as sdk48 from "openclaw/plugin-sdk/channel-plugin-common";
import type * as sdk49 from "openclaw/plugin-sdk/channel-policy";
import type * as sdk50 from "openclaw/plugin-sdk/channel-reply-options-runtime";
import type * as sdk51 from "openclaw/plugin-sdk/channel-reply-pipeline";
import type * as sdk52 from "openclaw/plugin-sdk/channel-runtime";
import type * as sdk53 from "openclaw/plugin-sdk/channel-runtime-context";
import type * as sdk54 from "openclaw/plugin-sdk/channel-secret-basic-runtime";
import type * as sdk55 from "openclaw/plugin-sdk/channel-secret-runtime";
import type * as sdk56 from "openclaw/plugin-sdk/channel-secret-tts-runtime";
import type * as sdk57 from "openclaw/plugin-sdk/channel-send-result";
import type * as sdk58 from "openclaw/plugin-sdk/channel-setup";
import type * as sdk59 from "openclaw/plugin-sdk/channel-status";
import type * as sdk60 from "openclaw/plugin-sdk/channel-streaming";
import type * as sdk61 from "openclaw/plugin-sdk/channel-targets";
import type * as sdk62 from "openclaw/plugin-sdk/cli-backend";
import type * as sdk63 from "openclaw/plugin-sdk/cli-runtime";
import type * as sdk64 from "openclaw/plugin-sdk/collection-runtime";
import type * as sdk65 from "openclaw/plugin-sdk/command-auth";
import type * as sdk66 from "openclaw/plugin-sdk/command-auth-native";
import type * as sdk67 from "openclaw/plugin-sdk/command-detection";
import type * as sdk68 from "openclaw/plugin-sdk/command-gating";
import type * as sdk69 from "openclaw/plugin-sdk/command-primitives-runtime";
import type * as sdk70 from "openclaw/plugin-sdk/command-status";
import type * as sdk71 from "openclaw/plugin-sdk/command-status-runtime";
import type * as sdk72 from "openclaw/plugin-sdk/command-surface";
import type * as sdk73 from "openclaw/plugin-sdk/compat";
import type * as sdk74 from "openclaw/plugin-sdk/config-mutation";
import type * as sdk75 from "openclaw/plugin-sdk/config-runtime";
import type * as sdk76 from "openclaw/plugin-sdk/config-schema";
import type * as sdk77 from "openclaw/plugin-sdk/config-types";
import type * as sdk78 from "openclaw/plugin-sdk/context-visibility-runtime";
import type * as sdk79 from "openclaw/plugin-sdk/conversation-binding-runtime";
import type * as sdk80 from "openclaw/plugin-sdk/conversation-runtime";
import type * as sdk81 from "openclaw/plugin-sdk/core";
import type * as sdk82 from "openclaw/plugin-sdk/cron-store-runtime";
import type * as sdk83 from "openclaw/plugin-sdk/dangerous-name-runtime";
import type * as sdk84 from "openclaw/plugin-sdk/device-bootstrap";
import type * as sdk85 from "openclaw/plugin-sdk/diagnostic-runtime";
import type * as sdk86 from "openclaw/plugin-sdk/direct-dm";
import type * as sdk87 from "openclaw/plugin-sdk/direct-dm-access";
import type * as sdk88 from "openclaw/plugin-sdk/direct-dm-guard-policy";
import type * as sdk89 from "openclaw/plugin-sdk/directory-config-runtime";
import type * as sdk90 from "openclaw/plugin-sdk/directory-runtime";
import type * as sdk91 from "openclaw/plugin-sdk/document-extractor";
import type * as sdk92 from "openclaw/plugin-sdk/error-runtime";
import type * as sdk93 from "openclaw/plugin-sdk/extension-shared";
import type * as sdk94 from "openclaw/plugin-sdk/fetch-runtime";
import type * as sdk95 from "openclaw/plugin-sdk/file-lock";
import type * as sdk96 from "openclaw/plugin-sdk/gateway-runtime";
import type * as sdk97 from "openclaw/plugin-sdk/global-singleton";
import type * as sdk98 from "openclaw/plugin-sdk/group-access";
import type * as sdk99 from "openclaw/plugin-sdk/group-activation";
import type * as sdk100 from "openclaw/plugin-sdk/hook-runtime";
import type * as sdk101 from "openclaw/plugin-sdk/host-runtime";
import type * as sdk102 from "openclaw/plugin-sdk/image-generation";
import type * as sdk103 from "openclaw/plugin-sdk/image-generation-core";
import type * as sdk104 from "openclaw/plugin-sdk/image-generation-runtime";
import type * as sdk105 from "openclaw/plugin-sdk/inbound-envelope";
import type * as sdk106 from "openclaw/plugin-sdk/inbound-reply-dispatch";
import type * as sdk107 from "openclaw/plugin-sdk/infra-runtime";
import type * as sdk108 from "openclaw/plugin-sdk/interactive-runtime";
import type * as sdk109 from "openclaw/plugin-sdk/json-store";
import type * as sdk110 from "openclaw/plugin-sdk/keyed-async-queue";
import type * as sdk111 from "openclaw/plugin-sdk/lazy-runtime";
import type * as sdk112 from "openclaw/plugin-sdk/lmstudio";
import type * as sdk113 from "openclaw/plugin-sdk/lmstudio-runtime";
import type * as sdk114 from "openclaw/plugin-sdk/logging-core";
import type * as sdk115 from "openclaw/plugin-sdk/markdown-table-runtime";
import type * as sdk116 from "openclaw/plugin-sdk/media-generation-runtime";
import type * as sdk117 from "openclaw/plugin-sdk/media-generation-runtime-shared";
import type * as sdk118 from "openclaw/plugin-sdk/media-mime";
import type * as sdk119 from "openclaw/plugin-sdk/media-runtime";
import type * as sdk120 from "openclaw/plugin-sdk/media-store";
import type * as sdk121 from "openclaw/plugin-sdk/media-understanding";
import type * as sdk122 from "openclaw/plugin-sdk/media-understanding-runtime";
import type * as sdk123 from "openclaw/plugin-sdk/memory-core-engine-runtime";
import type * as sdk124 from "openclaw/plugin-sdk/memory-core-host-engine-embeddings";
import type * as sdk125 from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
import type * as sdk126 from "openclaw/plugin-sdk/memory-core-host-engine-qmd";
import type * as sdk127 from "openclaw/plugin-sdk/memory-core-host-engine-storage";
import type * as sdk128 from "openclaw/plugin-sdk/memory-core-host-events";
import type * as sdk129 from "openclaw/plugin-sdk/memory-core-host-multimodal";
import type * as sdk130 from "openclaw/plugin-sdk/memory-core-host-query";
import type * as sdk131 from "openclaw/plugin-sdk/memory-core-host-runtime-cli";
import type * as sdk132 from "openclaw/plugin-sdk/memory-core-host-runtime-core";
import type * as sdk133 from "openclaw/plugin-sdk/memory-core-host-runtime-files";
import type * as sdk134 from "openclaw/plugin-sdk/memory-core-host-secret";
import type * as sdk135 from "openclaw/plugin-sdk/memory-core-host-status";
import type * as sdk136 from "openclaw/plugin-sdk/memory-host-core";
import type * as sdk137 from "openclaw/plugin-sdk/memory-host-events";
import type * as sdk138 from "openclaw/plugin-sdk/memory-host-files";
import type * as sdk139 from "openclaw/plugin-sdk/memory-host-markdown";
import type * as sdk140 from "openclaw/plugin-sdk/memory-host-search";
import type * as sdk141 from "openclaw/plugin-sdk/memory-host-status";
import type * as sdk142 from "openclaw/plugin-sdk/messaging-targets";
import type * as sdk143 from "openclaw/plugin-sdk/migration";
import type * as sdk144 from "openclaw/plugin-sdk/migration-runtime";
import type * as sdk145 from "openclaw/plugin-sdk/model-session-runtime";
import type * as sdk146 from "openclaw/plugin-sdk/models-provider-runtime";
import type * as sdk147 from "openclaw/plugin-sdk/music-generation";
import type * as sdk148 from "openclaw/plugin-sdk/music-generation-core";
import type * as sdk149 from "openclaw/plugin-sdk/native-command-config-runtime";
import type * as sdk150 from "openclaw/plugin-sdk/native-command-registry";
import type * as sdk151 from "openclaw/plugin-sdk/outbound-media";
import type * as sdk152 from "openclaw/plugin-sdk/outbound-runtime";
import type * as sdk153 from "openclaw/plugin-sdk/outbound-send-deps";
import type * as sdk154 from "openclaw/plugin-sdk/param-readers";
import type * as sdk155 from "openclaw/plugin-sdk/persistent-dedupe";
import type * as sdk156 from "openclaw/plugin-sdk/plugin-config-runtime";
import type * as sdk157 from "openclaw/plugin-sdk/plugin-entry";
import type * as sdk158 from "openclaw/plugin-sdk/plugin-runtime";
import type * as sdk159 from "openclaw/plugin-sdk/poll-runtime";
import type * as sdk160 from "openclaw/plugin-sdk/process-runtime";
import type * as sdk161 from "openclaw/plugin-sdk/provider-auth";
import type * as sdk162 from "openclaw/plugin-sdk/provider-auth-api-key";
import type * as sdk163 from "openclaw/plugin-sdk/provider-auth-login";
import type * as sdk164 from "openclaw/plugin-sdk/provider-auth-result";
import type * as sdk165 from "openclaw/plugin-sdk/provider-auth-runtime";
import type * as sdk166 from "openclaw/plugin-sdk/provider-catalog-shared";
import type * as sdk167 from "openclaw/plugin-sdk/provider-entry";
import type * as sdk168 from "openclaw/plugin-sdk/provider-env-vars";
import type * as sdk169 from "openclaw/plugin-sdk/provider-http";
import type * as sdk170 from "openclaw/plugin-sdk/provider-model-shared";
import type * as sdk171 from "openclaw/plugin-sdk/provider-model-types";
import type * as sdk172 from "openclaw/plugin-sdk/provider-onboard";
import type * as sdk173 from "openclaw/plugin-sdk/provider-selection-runtime";
import type * as sdk174 from "openclaw/plugin-sdk/provider-setup";
import type * as sdk175 from "openclaw/plugin-sdk/provider-stream";
import type * as sdk176 from "openclaw/plugin-sdk/provider-stream-family";
import type * as sdk177 from "openclaw/plugin-sdk/provider-stream-shared";
import type * as sdk178 from "openclaw/plugin-sdk/provider-tools";
import type * as sdk179 from "openclaw/plugin-sdk/provider-transport-runtime";
import type * as sdk180 from "openclaw/plugin-sdk/provider-usage";
import type * as sdk181 from "openclaw/plugin-sdk/provider-web-fetch";
import type * as sdk182 from "openclaw/plugin-sdk/provider-web-fetch-contract";
import type * as sdk183 from "openclaw/plugin-sdk/provider-web-search";
import type * as sdk184 from "openclaw/plugin-sdk/provider-web-search-config-contract";
import type * as sdk185 from "openclaw/plugin-sdk/provider-web-search-contract";
import type * as sdk186 from "openclaw/plugin-sdk/provider-zai-endpoint";
import type * as sdk187 from "openclaw/plugin-sdk/proxy-capture";
import type * as sdk188 from "openclaw/plugin-sdk/qa-runner-runtime";
import type * as sdk189 from "openclaw/plugin-sdk/realtime-transcription";
import type * as sdk190 from "openclaw/plugin-sdk/realtime-voice";
import type * as sdk191 from "openclaw/plugin-sdk/reply-chunking";
import type * as sdk192 from "openclaw/plugin-sdk/reply-dedupe";
import type * as sdk193 from "openclaw/plugin-sdk/reply-dispatch-runtime";
import type * as sdk194 from "openclaw/plugin-sdk/reply-history";
import type * as sdk195 from "openclaw/plugin-sdk/reply-payload";
import type * as sdk196 from "openclaw/plugin-sdk/reply-reference";
import type * as sdk197 from "openclaw/plugin-sdk/reply-runtime";
import type * as sdk198 from "openclaw/plugin-sdk/request-url";
import type * as sdk199 from "openclaw/plugin-sdk/response-limit-runtime";
import type * as sdk200 from "openclaw/plugin-sdk/retry-runtime";
import type * as sdk201 from "openclaw/plugin-sdk/routing";
import type * as sdk202 from "openclaw/plugin-sdk/run-command";
import type * as sdk203 from "openclaw/plugin-sdk/runtime";
import type * as sdk204 from "openclaw/plugin-sdk/runtime-config-snapshot";
import type * as sdk205 from "openclaw/plugin-sdk/runtime-doctor";
import type * as sdk206 from "openclaw/plugin-sdk/runtime-env";
import type * as sdk207 from "openclaw/plugin-sdk/runtime-fetch";
import type * as sdk208 from "openclaw/plugin-sdk/runtime-group-policy";
import type * as sdk209 from "openclaw/plugin-sdk/runtime-logger";
import type * as sdk210 from "openclaw/plugin-sdk/runtime-secret-resolution";
import type * as sdk211 from "openclaw/plugin-sdk/runtime-store";
import type * as sdk212 from "openclaw/plugin-sdk/sandbox";
import type * as sdk213 from "openclaw/plugin-sdk/secret-file-runtime";
import type * as sdk214 from "openclaw/plugin-sdk/secret-input";
import type * as sdk215 from "openclaw/plugin-sdk/secret-input-runtime";
import type * as sdk216 from "openclaw/plugin-sdk/secret-ref-runtime";
import type * as sdk217 from "openclaw/plugin-sdk/security-runtime";
import type * as sdk218 from "openclaw/plugin-sdk/self-hosted-provider-setup";
import type * as sdk219 from "openclaw/plugin-sdk/session-binding-runtime";
import type * as sdk220 from "openclaw/plugin-sdk/session-key-runtime";
import type * as sdk221 from "openclaw/plugin-sdk/session-store-runtime";
import type * as sdk222 from "openclaw/plugin-sdk/session-transcript-hit";
import type * as sdk223 from "openclaw/plugin-sdk/session-visibility";
import type * as sdk224 from "openclaw/plugin-sdk/setup";
import type * as sdk225 from "openclaw/plugin-sdk/setup-adapter-runtime";
import type * as sdk226 from "openclaw/plugin-sdk/setup-runtime";
import type * as sdk227 from "openclaw/plugin-sdk/setup-tools";
import type * as sdk228 from "openclaw/plugin-sdk/simple-completion-runtime";
import type * as sdk229 from "openclaw/plugin-sdk/skill-commands-runtime";
import type * as sdk230 from "openclaw/plugin-sdk/skills-runtime";
import type * as sdk231 from "openclaw/plugin-sdk/speech";
import type * as sdk232 from "openclaw/plugin-sdk/speech-core";
import type * as sdk233 from "openclaw/plugin-sdk/ssrf-dispatcher";
import type * as sdk234 from "openclaw/plugin-sdk/ssrf-policy";
import type * as sdk235 from "openclaw/plugin-sdk/ssrf-runtime";
import type * as sdk236 from "openclaw/plugin-sdk/state-paths";
import type * as sdk237 from "openclaw/plugin-sdk/status-helpers";
import type * as sdk238 from "openclaw/plugin-sdk/string-coerce-runtime";
import type * as sdk239 from "openclaw/plugin-sdk/string-normalization-runtime";
import type * as sdk240 from "openclaw/plugin-sdk/talk-config-runtime";
import type * as sdk241 from "openclaw/plugin-sdk/target-resolver-runtime";
import type * as sdk242 from "openclaw/plugin-sdk/telegram-command-config";
import type * as sdk243 from "openclaw/plugin-sdk/temp-path";
import type * as sdk244 from "openclaw/plugin-sdk/testing";
import type * as sdk245 from "openclaw/plugin-sdk/text-autolink-runtime";
import type * as sdk246 from "openclaw/plugin-sdk/text-chunking";
import type * as sdk247 from "openclaw/plugin-sdk/text-runtime";
import type * as sdk248 from "openclaw/plugin-sdk/thread-bindings-runtime";
import type * as sdk249 from "openclaw/plugin-sdk/thread-bindings-session-runtime";
import type * as sdk250 from "openclaw/plugin-sdk/tool-payload";
import type * as sdk251 from "openclaw/plugin-sdk/tool-send";
import type * as sdk252 from "openclaw/plugin-sdk/tts-runtime";
import type * as sdk253 from "openclaw/plugin-sdk/video-generation";
import type * as sdk254 from "openclaw/plugin-sdk/video-generation-core";
import type * as sdk255 from "openclaw/plugin-sdk/video-generation-runtime";
import type * as sdk256 from "openclaw/plugin-sdk/web-content-extractor";
import type * as sdk257 from "openclaw/plugin-sdk/web-media";
import type * as sdk258 from "openclaw/plugin-sdk/webhook-ingress";
import type * as sdk259 from "openclaw/plugin-sdk/webhook-path";
import type * as sdk260 from "openclaw/plugin-sdk/webhook-request-guards";
import type * as sdk261 from "openclaw/plugin-sdk/webhook-targets";
import type * as sdk262 from "openclaw/plugin-sdk/windows-spawn";
import type * as sdk263 from "openclaw/plugin-sdk/zod";
export type KitchenSinkSdkImportSurface =
| typeof sdk0
@ -557,33 +528,4 @@ export type KitchenSinkSdkImportSurface =
| typeof sdk260
| typeof sdk261
| typeof sdk262
| typeof sdk263
| typeof sdk264
| typeof sdk265
| typeof sdk266
| typeof sdk267
| typeof sdk268
| typeof sdk269
| typeof sdk270
| typeof sdk271
| typeof sdk272
| typeof sdk273
| typeof sdk274
| typeof sdk275
| typeof sdk276
| typeof sdk277
| typeof sdk278
| typeof sdk279
| typeof sdk280
| typeof sdk281
| typeof sdk282
| typeof sdk283
| typeof sdk284
| typeof sdk285
| typeof sdk286
| typeof sdk287
| typeof sdk288
| typeof sdk289
| typeof sdk290
| typeof sdk291
| typeof sdk292;
| typeof sdk263;

View File

@ -1,29 +1,16 @@
import { PLUGIN_ID } from "./constants.js";
import { registerAllHooks } from "./generated-hooks.js";
import { registerAllRegistrars } from "./generated-registrars.js";
import { registerKitchenSinkRuntime } from "./kitchen-runtime.js";
import {
KITCHEN_SINK_EXPECTED_DIAGNOSTICS,
resolveKitchenSinkPersonality,
} from "./personality.js";
export const plugin = {
id: PLUGIN_ID,
id: "openclaw-kitchen-sink-fixture",
name: "OpenClaw Kitchen Sink",
version: "0.2.5",
version: "0.1.2",
description: "Credential-free fixture covering OpenClaw plugin API seams.",
expectedDiagnostics: KITCHEN_SINK_EXPECTED_DIAGNOSTICS,
register(api) {
const personality = resolveKitchenSinkPersonality(api);
registerAllHooks(api);
if (personality !== "conformance") {
registerAllRegistrars(api);
}
if (personality !== "adversarial") {
registerKitchenSinkRuntime(api, {
includeAgentToolResultMiddleware: personality !== "conformance",
});
}
registerAllRegistrars(api);
registerKitchenSinkRuntime(api);
},
};

View File

@ -1,51 +1,43 @@
import { buildKitchenChannel } from "./runtime/channel.js";
import {
buildKitchenCommand,
buildKitchenImageTool,
buildKitchenInteractiveHandler,
buildKitchenSearchTool,
buildKitchenSinkCommand,
buildKitchenTextTool,
} from "./runtime/commands.js";
import {
buildKitchenCliMetadata,
buildKitchenCliRegistrar,
buildKitchenGatewayMethod,
buildKitchenHttpRoute,
buildKitchenService,
buildKitchenToolResultMiddleware,
} from "./runtime/platform.js";
import {
buildKitchenCompactionProvider,
buildKitchenImageProvider,
buildKitchenMediaProvider,
buildKitchenMemoryCorpusSupplement,
buildKitchenMemoryEmbeddingProvider,
buildKitchenMusicProvider,
buildKitchenRealtimeTranscriptionProvider,
buildKitchenRealtimeVoiceProvider,
buildKitchenSpeechProvider,
buildKitchenTextProvider,
buildKitchenVideoProvider,
buildKitchenWebFetchProvider,
buildKitchenWebSearchProvider,
} from "./runtime/providers.js";
import { buildKitchenDetachedTaskRuntime } from "./runtime/tasks.js";
import {
DEFAULT_IMAGE_MODEL,
DEFAULT_MEDIA_MODEL,
DEFAULT_TEXT_MODEL,
CHANNEL_ACCOUNT_ID,
CHANNEL_ID,
IMAGE_PROVIDER_ID,
MEDIA_PROVIDER_ID,
PLUGIN_ID,
TEXT_PROVIDER_ID,
WEB_FETCH_PROVIDER_ID,
WEB_SEARCH_PROVIDER_ID,
createKitchenChannelDelivery,
createKitchenScenarioRuntime,
createKitchenSinkImageAsset,
createKitchenTextStream,
extractInteractiveText,
kitchenChannelAccount,
kitchenImageDescription,
kitchenPromptGuidance,
kitchenSearchSchema,
kitchenTextModelDefinition,
kitchenTextProviderConfig,
kitchenToolSchema,
normalizeKitchenTarget,
readPrompt,
readQuery,
readUrl,
runKitchenCommand,
runKitchenFetch,
runKitchenImageTool,
runKitchenSearch,
shouldHandleKitchenText,
stripDataUrl,
} from "./scenarios.js";
export { createKitchenSinkImageAsset, kitchenPromptGuidance, shouldHandleKitchenText };
// Keep this file as the readable runtime table of contents. The builders live
// under src/runtime/* so plugin authors can inspect one OpenClaw surface at a
// time without losing the full registration order.
export function registerKitchenSinkRuntime(api, options = {}) {
const runtime = createKitchenSinkRuntime(options);
const includeAgentToolResultMiddleware = options.includeAgentToolResultMiddleware !== false;
optionalRegister(api, "registerCommand", () => api.registerCommand(buildKitchenCommand(runtime)));
optionalRegister(api, "registerCommand", () => api.registerCommand(buildKitchenSinkCommand(runtime)));
@ -63,19 +55,6 @@ export function registerKitchenSinkRuntime(api, options = {}) {
optionalRegister(api, "registerMediaUnderstandingProvider", () =>
api.registerMediaUnderstandingProvider(buildKitchenMediaProvider()),
);
optionalRegister(api, "registerSpeechProvider", () => api.registerSpeechProvider(buildKitchenSpeechProvider()));
optionalRegister(api, "registerRealtimeTranscriptionProvider", () =>
api.registerRealtimeTranscriptionProvider(buildKitchenRealtimeTranscriptionProvider()),
);
optionalRegister(api, "registerRealtimeVoiceProvider", () =>
api.registerRealtimeVoiceProvider(buildKitchenRealtimeVoiceProvider()),
);
optionalRegister(api, "registerVideoGenerationProvider", () =>
api.registerVideoGenerationProvider(buildKitchenVideoProvider()),
);
optionalRegister(api, "registerMusicGenerationProvider", () =>
api.registerMusicGenerationProvider(buildKitchenMusicProvider()),
);
optionalRegister(api, "registerWebSearchProvider", () =>
api.registerWebSearchProvider(buildKitchenWebSearchProvider()),
);
@ -85,28 +64,6 @@ export function registerKitchenSinkRuntime(api, options = {}) {
optionalRegister(api, "registerDetachedTaskRuntime", () =>
api.registerDetachedTaskRuntime(buildKitchenDetachedTaskRuntime()),
);
optionalRegister(api, "registerMemoryEmbeddingProvider", () =>
api.registerMemoryEmbeddingProvider(buildKitchenMemoryEmbeddingProvider()),
);
optionalRegister(api, "registerMemoryCorpusSupplement", () =>
api.registerMemoryCorpusSupplement(buildKitchenMemoryCorpusSupplement()),
);
optionalRegister(api, "registerCompactionProvider", () =>
api.registerCompactionProvider(buildKitchenCompactionProvider()),
);
if (includeAgentToolResultMiddleware) {
optionalRegister(api, "registerAgentToolResultMiddleware", () =>
api.registerAgentToolResultMiddleware(buildKitchenToolResultMiddleware(), {
runtimes: ["pi", "codex", "cli"],
}),
);
}
optionalRegister(api, "registerService", () => api.registerService(buildKitchenService()));
optionalRegister(api, "registerHttpRoute", () => api.registerHttpRoute(buildKitchenHttpRoute()));
optionalRegister(api, "registerGatewayMethod", () =>
api.registerGatewayMethod("kitchen.status", buildKitchenGatewayMethod()),
);
optionalRegister(api, "registerCli", () => api.registerCli(buildKitchenCliRegistrar(), buildKitchenCliMetadata()));
optionalRegister(api, "registerMemoryPromptSupplement", () =>
api.registerMemoryPromptSupplement(async () => kitchenPromptGuidance().join("\n")),
);
@ -118,9 +75,470 @@ export function createKitchenSinkRuntime(options = {}) {
return createKitchenScenarioRuntime(options);
}
function buildKitchenCommand(runtime) {
return {
name: "kitchen",
nativeNames: { default: "kitchen" },
description: "Run deterministic Kitchen Sink fixture scenarios.",
acceptsArgs: true,
requireAuth: false,
agentPromptGuidance: kitchenPromptGuidance(),
handler: async (ctx) => runKitchenCommand(runtime, ctx?.args ?? ctx?.commandBody ?? ""),
};
}
function buildKitchenSinkCommand(runtime) {
return {
...buildKitchenCommand(runtime),
name: "kitchen-sink",
nativeNames: { default: "kitchen-sink" },
};
}
function buildKitchenInteractiveHandler(runtime) {
return {
channel: "*",
namespace: "kitchen-sink",
handler: async (ctx) => {
const text = extractInteractiveText(ctx);
if (!shouldHandleKitchenText(text)) {
return { handled: false };
}
return {
handled: true,
reply: await runKitchenCommand(runtime, text.replace(/^kitchen\b/i, "").trim()),
};
},
};
}
function buildKitchenImageTool(runtime) {
return {
id: "kitchen_sink_image_job",
name: "kitchen_sink_image_job",
description:
"Generate a deterministic Kitchen Sink image fixture. Use when the user asks for a kitchen sink image, fixture image, or image-provider smoke test.",
inputSchema: kitchenToolSchema("Prompt for the deterministic image fixture."),
schema: kitchenToolSchema("Prompt for the deterministic image fixture."),
parameters: kitchenToolSchema("Prompt for the deterministic image fixture."),
handler: async (input) => runKitchenImageTool(runtime, input),
run: async (input) => runKitchenImageTool(runtime, input),
execute: async (input) => runKitchenImageTool(runtime, input),
};
}
function buildKitchenTextTool(runtime) {
return {
id: "kitchen_sink_text",
name: "kitchen_sink_text",
description:
"Return a deterministic text inference fixture response for Kitchen Sink plugin smoke tests.",
inputSchema: kitchenToolSchema("Prompt for the deterministic text fixture."),
schema: kitchenToolSchema("Prompt for the deterministic text fixture."),
parameters: kitchenToolSchema("Prompt for the deterministic text fixture."),
handler: async (input) => runtime.runTextJob({ prompt: readPrompt(input), route: "tool:kitchen_sink_text" }),
run: async (input) => runtime.runTextJob({ prompt: readPrompt(input), route: "tool:kitchen_sink_text" }),
execute: async (input) => runtime.runTextJob({ prompt: readPrompt(input), route: "tool:kitchen_sink_text" }),
};
}
function buildKitchenSearchTool() {
return {
id: "kitchen_sink_search",
name: "kitchen_sink_search",
description: "Return deterministic Kitchen Sink search results for tool-routing smoke tests.",
inputSchema: kitchenSearchSchema(),
schema: kitchenSearchSchema(),
parameters: kitchenSearchSchema(),
handler: async (input) => runKitchenSearch(readQuery(input)),
run: async (input) => runKitchenSearch(readQuery(input)),
execute: async (input) => runKitchenSearch(readQuery(input)),
};
}
function buildKitchenChannel() {
return {
id: CHANNEL_ID,
meta: {
id: CHANNEL_ID,
label: "Kitchen Sink",
selectionLabel: "Kitchen Sink",
docsPath: "/plugins/kitchen-sink",
docsLabel: "Kitchen Sink",
blurb: "Credential-free channel fixture for deterministic Kitchen Sink conversations.",
aliases: ["kitchen", "kitchen-sink"],
exposure: { configured: true, setup: true, docs: true },
showConfigured: true,
showInSetup: true,
},
capabilities: {
chatTypes: ["direct", "group", "channel"],
media: true,
nativeCommands: true,
reply: true,
threads: true,
},
config: {
listAccountIds: () => [CHANNEL_ACCOUNT_ID],
defaultAccountId: () => CHANNEL_ACCOUNT_ID,
resolveAccount: (_cfg, accountId) => kitchenChannelAccount(accountId || CHANNEL_ACCOUNT_ID),
isEnabled: () => true,
isConfigured: () => true,
describeAccount: (account) => kitchenChannelAccount(account.accountId),
resolveDefaultTo: () => "kitchen",
},
status: {
defaultRuntime: kitchenChannelAccount(),
probeAccount: async ({ account }) => ({
ok: true,
accountId: account.accountId,
scenarioId: "channel.probe",
}),
buildAccountSnapshot: ({ account }) => kitchenChannelAccount(account.accountId),
},
outbound: {
deliveryMode: "direct",
textChunkLimit: 2000,
sendText: async (ctx) =>
createKitchenChannelDelivery({ kind: "text", text: ctx?.text, to: ctx?.to }),
sendMedia: async (ctx) =>
createKitchenChannelDelivery({ kind: "media", text: ctx?.mediaUrl || ctx?.text, to: ctx?.to }),
},
messaging: {
normalizeTarget: (raw) => normalizeKitchenTarget(raw),
parseExplicitTarget: ({ raw }) => ({
to: normalizeKitchenTarget(raw),
chatType: "direct",
}),
inferTargetChatType: () => "direct",
resolveOutboundSessionRoute: ({ agentId, target, threadId }) => {
const to = normalizeKitchenTarget(target);
return {
sessionKey: `kitchen:${agentId || "agent"}:${to}`,
baseSessionKey: `kitchen:${agentId || "agent"}:${to}`,
peer: { kind: "direct", id: to },
chatType: "direct",
from: CHANNEL_ACCOUNT_ID,
to,
threadId: threadId || undefined,
};
},
},
agentPrompt: {
messageToolHints: () => kitchenPromptGuidance(),
messageToolCapabilities: () => [
"Kitchen Sink channel accepts deterministic dry messages prefixed with kitchen.",
"Kitchen Sink channel can deliver text and media without external credentials.",
],
},
};
}
function buildKitchenImageProvider(runtime) {
return {
id: IMAGE_PROVIDER_ID,
aliases: ["kitchen", "kitchen-sink", "openclaw-kitchen-sink"],
label: "Kitchen Sink Image",
defaultModel: DEFAULT_IMAGE_MODEL,
models: [DEFAULT_IMAGE_MODEL],
capabilities: {
generate: {
maxCount: 1,
supportsSize: true,
supportsAspectRatio: true,
supportsResolution: true,
},
edit: {
enabled: true,
maxInputImages: 1,
maxCount: 1,
},
geometry: {
sizes: ["1024x1024"],
aspectRatios: ["1:1"],
resolutions: ["1K"],
},
},
isConfigured: () => true,
generateImage: async (req) => {
const result = await runtime.runScenario({
scenario: "image.generate",
prompt: req?.prompt,
route: "provider:image",
});
return {
images: [stripDataUrl(result.image)],
model: req?.model || DEFAULT_IMAGE_MODEL,
metadata: {
kitchenSink: true,
job: result.job,
provider: IMAGE_PROVIDER_ID,
pluginId: PLUGIN_ID,
scenarioId: result.scenarioId,
route: result.route,
},
};
},
};
}
function buildKitchenMediaProvider() {
return {
id: MEDIA_PROVIDER_ID,
capabilities: ["image"],
defaultModels: { image: DEFAULT_MEDIA_MODEL },
autoPriority: { image: 5 },
describeImage: async (req) => ({
text: kitchenImageDescription(req?.prompt, 1),
model: req?.model || DEFAULT_MEDIA_MODEL,
}),
describeImages: async (req) => ({
text: kitchenImageDescription(req?.prompt, Array.isArray(req?.images) ? req.images.length : 0),
model: req?.model || DEFAULT_MEDIA_MODEL,
}),
};
}
function buildKitchenTextProvider() {
return {
id: TEXT_PROVIDER_ID,
label: "Kitchen Sink LLM",
docsPath: "/providers/models",
aliases: ["kitchen-sink-text", "kitchen"],
envVars: [],
auth: [
{
id: "none",
label: "No credentials",
hint: "Deterministic local fixture provider.",
kind: "custom",
run: async () => ({
profiles: [],
defaultModel: `${TEXT_PROVIDER_ID}/${DEFAULT_TEXT_MODEL}`,
notes: ["Kitchen Sink LLM is deterministic and does not call a network service."],
}),
},
],
staticCatalog: {
order: "simple",
run: async () => ({
provider: kitchenTextProviderConfig(),
}),
},
catalog: {
order: "simple",
run: async () => ({
provider: kitchenTextProviderConfig(),
}),
},
resolveDynamicModel: ({ modelId }) =>
modelId === DEFAULT_TEXT_MODEL ? kitchenTextModelDefinition() : undefined,
resolveSyntheticAuth: () => ({
apiKey: "kitchen-sink-local-fixture",
source: "kitchen-sink fixture",
mode: "token",
}),
createStreamFn: () => createKitchenTextStream,
resolveSystemPromptContribution: () => ({
stablePrefix: kitchenPromptGuidance().join("\n"),
}),
};
}
function buildKitchenWebSearchProvider() {
return {
id: WEB_SEARCH_PROVIDER_ID,
label: "Kitchen Sink Search",
hint: "Credential-free deterministic search fixture.",
requiresCredential: false,
envVars: [],
placeholder: "no key required",
signupUrl: "https://github.com/openclaw/kitchen-sink",
docsUrl: "https://github.com/openclaw/kitchen-sink#readme",
credentialPath: `${pluginConfigPath()}.search`,
getCredentialValue: () => "fixture",
setCredentialValue: (target, value) => {
target.fixture = value;
},
applySelectionConfig: (config) => config,
resolveRuntimeMetadata: async () => ({ provider: WEB_SEARCH_PROVIDER_ID, pluginId: PLUGIN_ID }),
createTool: () => ({
description: "Search the deterministic Kitchen Sink fixture corpus.",
parameters: kitchenSearchSchema(),
execute: async (args) => runKitchenSearch(readQuery(args)),
}),
};
}
function buildKitchenWebFetchProvider() {
return {
id: WEB_FETCH_PROVIDER_ID,
label: "Kitchen Sink Fetch",
hint: "Credential-free deterministic fetch fixture.",
requiresCredential: false,
envVars: [],
placeholder: "no key required",
signupUrl: "https://github.com/openclaw/kitchen-sink",
docsUrl: "https://github.com/openclaw/kitchen-sink#readme",
credentialPath: `${pluginConfigPath()}.fetch`,
getCredentialValue: () => "fixture",
setCredentialValue: (target, value) => {
target.fixture = value;
},
applySelectionConfig: (config) => config,
resolveRuntimeMetadata: async () => ({ provider: WEB_FETCH_PROVIDER_ID, pluginId: PLUGIN_ID }),
createTool: () => ({
description: "Fetch deterministic Kitchen Sink fixture documents.",
parameters: {
type: "object",
additionalProperties: false,
properties: {
url: { type: "string", description: "Fixture URL or topic." },
},
},
execute: async (args) => runKitchenFetch(readUrl(args)),
}),
};
}
function buildKitchenDetachedTaskRuntime() {
const tasks = new Map();
function create(params, status) {
const now = Date.now();
const runId = params.runId || `ks_task_${Math.abs(hashTask(params.task || status))}`;
const task = {
taskId: runId,
runId,
runtime: params.runtime || "cli",
taskKind: params.taskKind || "kitchen-sink",
sourceId: params.sourceId || PLUGIN_ID,
requesterSessionKey: params.requesterSessionKey || "kitchen-sink",
ownerKey: params.ownerKey || PLUGIN_ID,
scopeKind: params.scopeKind || "session",
childSessionKey: params.childSessionKey,
parentFlowId: params.parentFlowId,
parentTaskId: params.parentTaskId,
agentId: params.agentId,
label: params.label || "Kitchen Sink task",
task: params.task,
status,
deliveryStatus: params.deliveryStatus || "not_applicable",
notifyPolicy: params.notifyPolicy || "done_only",
createdAt: now,
startedAt: status === "running" ? params.startedAt || now : params.startedAt,
lastEventAt: params.lastEventAt || now,
progressSummary: params.progressSummary || undefined,
};
tasks.set(runId, task);
return task;
}
function update(runId, patch) {
const current = tasks.get(runId);
if (!current) {
return [];
}
const cleanPatch = Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined));
const next = { ...current, ...cleanPatch };
tasks.set(runId, next);
return [next];
}
return {
createQueuedTaskRun: (params) => create(params, "queued"),
createRunningTaskRun: (params) => create(params, "running"),
startTaskRunByRunId: (params) =>
update(params.runId, {
status: "running",
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
startedAt: params.startedAt || Date.now(),
lastEventAt: params.lastEventAt || Date.now(),
progressSummary: params.progressSummary || params.eventSummary || "Kitchen Sink task started.",
}),
recordTaskRunProgressByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
lastEventAt: params.lastEventAt || Date.now(),
progressSummary: params.progressSummary || params.eventSummary || "Kitchen Sink task progressed.",
}),
finalizeTaskRunByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
status: params.status,
endedAt: params.endedAt,
lastEventAt: params.lastEventAt || params.endedAt,
error: params.error,
progressSummary: params.progressSummary || undefined,
terminalSummary: params.terminalSummary || undefined,
terminalOutcome: params.terminalOutcome || undefined,
}),
completeTaskRunByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
status: "succeeded",
endedAt: params.endedAt,
lastEventAt: params.lastEventAt || params.endedAt,
progressSummary: params.progressSummary || undefined,
terminalSummary: params.terminalSummary || "Kitchen Sink task completed.",
terminalOutcome: params.terminalOutcome || "succeeded",
}),
failTaskRunByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
status: params.status || "failed",
endedAt: params.endedAt,
lastEventAt: params.lastEventAt || params.endedAt,
error: params.error,
progressSummary: params.progressSummary || undefined,
terminalSummary: params.terminalSummary || "Kitchen Sink task failed.",
}),
setDetachedTaskDeliveryStatusByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
deliveryStatus: params.deliveryStatus,
error: params.error,
}),
cancelDetachedTaskRunById: async ({ taskId }) => {
const current = tasks.get(taskId);
if (!current) {
return { found: false, cancelled: false, reason: "not owned by Kitchen Sink" };
}
const task = {
...current,
status: "cancelled",
endedAt: Date.now(),
lastEventAt: Date.now(),
terminalSummary: "Kitchen Sink task cancelled.",
};
tasks.set(taskId, task);
return { found: true, cancelled: true, task };
},
tryRecoverTaskBeforeMarkLost: ({ task }) => ({
recovered: Boolean(task?.taskId && tasks.has(task.taskId)),
}),
};
}
function hashTask(input) {
let hash = 0;
for (const char of String(input)) {
hash = Math.imul(31, hash) + char.charCodeAt(0);
}
return hash;
}
function pluginConfigPath() {
return `plugins.${PLUGIN_ID}`;
}
function optionalRegister(api, method, register) {
// Kitchen Sink runs against multiple SDK/inspector versions. Missing optional
// registrar methods should quietly no-op instead of making older hosts fail.
if (typeof api?.[method] !== "function") {
return;
}

View File

@ -1,52 +0,0 @@
export const KITCHEN_SINK_PERSONALITIES = ["full", "conformance", "adversarial"];
export const DEFAULT_KITCHEN_SINK_PERSONALITY = "full";
export const KITCHEN_SINK_EXPECTED_DIAGNOSTICS = {
full: [
"only bundled plugins can register agent tool result middleware",
"agent event subscription registration requires id and handle",
'agent harness "kitchen-sink-agent-harness" registration missing required runtime methods',
'channel "kitchen-sink-channel-probe" registration missing required config helpers',
"cli registration missing explicit commands metadata",
"only bundled plugins can register Codex app-server extension factories",
'compaction provider "kitchen-sink-compaction-provider" registration missing summarize',
"context engine registration missing id",
"control UI descriptor registration requires id, surface, label, and valid optional fields",
"http route registration missing or invalid auth: /kitchen-sink/http-route",
"node invoke policy registration missing commands",
"only bundled plugins can register trusted tool policies",
"plugin must declare contracts.tools for: kitchen-sink-tool",
"plugin must own memory slot or declare contracts.memoryEmbeddingProviders for adapter: kitchen-sink-memory-embedding-provider",
"memory prompt supplement registration missing builder",
"session extension registration requires namespace and description",
"session scheduler job registration requires unique id, sessionKey, and kind",
"tool metadata registration missing toolName",
],
conformance: [],
adversarial: [
"only bundled plugins can register agent tool result middleware",
"agent event subscription registration requires id and handle",
'agent harness "kitchen-sink-agent-harness" registration missing required runtime methods',
'channel "kitchen-sink-channel-probe" registration missing required config helpers',
"cli registration missing explicit commands metadata",
"only bundled plugins can register Codex app-server extension factories",
'compaction provider "kitchen-sink-compaction-provider" registration missing summarize',
"context engine registration missing id",
"control UI descriptor registration requires id, surface, label, and valid optional fields",
"http route registration missing or invalid auth: /kitchen-sink/http-route",
"node invoke policy registration missing commands",
"only bundled plugins can register trusted tool policies",
"plugin must declare contracts.tools for: kitchen-sink-tool",
"plugin must own memory slot or declare contracts.memoryEmbeddingProviders for adapter: kitchen-sink-memory-embedding-provider",
"memory prompt supplement registration missing builder",
"session extension registration requires namespace and description",
"session scheduler job registration requires unique id, sessionKey, and kind",
"tool metadata registration missing toolName",
],
};
export function resolveKitchenSinkPersonality(api) {
const configured = api?.config?.personality || process.env.OPENCLAW_KITCHEN_SINK_PERSONALITY;
return KITCHEN_SINK_PERSONALITIES.includes(configured) ? configured : DEFAULT_KITCHEN_SINK_PERSONALITY;
}

View File

@ -1,88 +0,0 @@
import {
CHANNEL_ACCOUNT_ID,
CHANNEL_ID,
} from "../constants.js";
import {
createKitchenChannelDelivery,
kitchenChannelAccount,
kitchenPromptGuidance,
normalizeKitchenTarget,
} from "../scenarios.js";
export function buildKitchenChannel() {
return {
id: CHANNEL_ID,
meta: {
id: CHANNEL_ID,
label: "Kitchen Sink",
selectionLabel: "Kitchen Sink",
docsPath: "/plugins/kitchen-sink",
docsLabel: "Kitchen Sink",
blurb: "Credential-free channel fixture for deterministic Kitchen Sink conversations.",
aliases: ["kitchen", "kitchen-sink"],
exposure: { configured: true, setup: true, docs: true },
showConfigured: true,
showInSetup: true,
},
capabilities: {
chatTypes: ["direct", "group", "channel"],
media: true,
nativeCommands: true,
reply: true,
threads: true,
},
config: {
listAccountIds: () => [CHANNEL_ACCOUNT_ID],
defaultAccountId: () => CHANNEL_ACCOUNT_ID,
resolveAccount: (cfg, accountId) => kitchenChannelAccount(accountId || CHANNEL_ACCOUNT_ID, cfg),
isEnabled: (cfg) => cfg?.disabled !== true,
isConfigured: (cfg) => cfg?.configured !== false,
describeAccount: (account) => kitchenChannelAccount(account.accountId, account),
resolveDefaultTo: () => "kitchen",
},
status: {
defaultRuntime: kitchenChannelAccount(),
probeAccount: async ({ account }) => ({
ok: true,
accountId: account.accountId,
scenarioId: "channel.probe",
}),
buildAccountSnapshot: ({ account }) => kitchenChannelAccount(account.accountId),
},
outbound: {
deliveryMode: "direct",
textChunkLimit: 2000,
sendText: async (ctx) =>
createKitchenChannelDelivery({ kind: "text", text: ctx?.text, to: ctx?.to }),
sendMedia: async (ctx) =>
createKitchenChannelDelivery({ kind: "media", text: ctx?.mediaUrl || ctx?.text, to: ctx?.to }),
},
messaging: {
normalizeTarget: (raw) => normalizeKitchenTarget(raw),
parseExplicitTarget: ({ raw }) => ({
to: normalizeKitchenTarget(raw),
chatType: "direct",
}),
inferTargetChatType: () => "direct",
resolveOutboundSessionRoute: ({ agentId, target, threadId }) => {
const to = normalizeKitchenTarget(target);
return {
sessionKey: `kitchen:${agentId || "agent"}:${to}`,
baseSessionKey: `kitchen:${agentId || "agent"}:${to}`,
peer: { kind: "direct", id: to },
chatType: "direct",
from: CHANNEL_ACCOUNT_ID,
to,
threadId: threadId || undefined,
};
},
},
agentPrompt: {
messageToolHints: () => kitchenPromptGuidance(),
messageToolCapabilities: () => [
"Kitchen Sink channel accepts deterministic dry messages prefixed with kitchen.",
"Kitchen Sink channel can deliver text and media without external credentials.",
],
},
};
}

View File

@ -1,93 +0,0 @@
import {
extractInteractiveText,
kitchenPromptGuidance,
kitchenSearchSchema,
kitchenToolSchema,
readPrompt,
readQuery,
runKitchenCommand,
runKitchenImageTool,
runKitchenSearch,
shouldHandleKitchenText,
} from "../scenarios.js";
export function buildKitchenCommand(runtime) {
return {
name: "kitchen",
nativeNames: { default: "kitchen" },
description: "Run deterministic Kitchen Sink fixture scenarios.",
acceptsArgs: true,
requireAuth: false,
agentPromptGuidance: kitchenPromptGuidance(),
handler: async (ctx) => runKitchenCommand(runtime, ctx?.args ?? ctx?.commandBody ?? ""),
};
}
export function buildKitchenSinkCommand(runtime) {
return {
...buildKitchenCommand(runtime),
name: "kitchen-sink",
nativeNames: { default: "kitchen-sink" },
};
}
export function buildKitchenInteractiveHandler(runtime) {
return {
channel: "*",
namespace: "kitchen-sink",
handler: async (ctx) => {
const text = extractInteractiveText(ctx);
if (!shouldHandleKitchenText(text)) {
return { handled: false };
}
return {
handled: true,
reply: await runKitchenCommand(runtime, text.replace(/^kitchen\b/i, "").trim()),
};
},
};
}
export function buildKitchenImageTool(runtime) {
return {
id: "kitchen_sink_image_job",
name: "kitchen_sink_image_job",
description:
"Generate a deterministic Kitchen Sink image fixture. Use when the user asks for a kitchen sink image, fixture image, or image-provider smoke test.",
inputSchema: kitchenToolSchema("Prompt for the deterministic image fixture."),
schema: kitchenToolSchema("Prompt for the deterministic image fixture."),
parameters: kitchenToolSchema("Prompt for the deterministic image fixture."),
handler: async (input) => runKitchenImageTool(runtime, input),
run: async (input) => runKitchenImageTool(runtime, input),
execute: async (input) => runKitchenImageTool(runtime, input),
};
}
export function buildKitchenTextTool(runtime) {
return {
id: "kitchen_sink_text",
name: "kitchen_sink_text",
description:
"Return a deterministic text inference fixture response for Kitchen Sink plugin smoke tests.",
inputSchema: kitchenToolSchema("Prompt for the deterministic text fixture."),
schema: kitchenToolSchema("Prompt for the deterministic text fixture."),
parameters: kitchenToolSchema("Prompt for the deterministic text fixture."),
handler: async (input) => runtime.runTextJob({ prompt: readPrompt(input), route: "tool:kitchen_sink_text" }),
run: async (input) => runtime.runTextJob({ prompt: readPrompt(input), route: "tool:kitchen_sink_text" }),
execute: async (input) => runtime.runTextJob({ prompt: readPrompt(input), route: "tool:kitchen_sink_text" }),
};
}
export function buildKitchenSearchTool() {
return {
id: "kitchen_sink_search",
name: "kitchen_sink_search",
description: "Return deterministic Kitchen Sink search results for tool-routing smoke tests.",
inputSchema: kitchenSearchSchema(),
schema: kitchenSearchSchema(),
parameters: kitchenSearchSchema(),
handler: async (input) => runKitchenSearch(readQuery(input)),
run: async (input) => runKitchenSearch(readQuery(input)),
execute: async (input) => runKitchenSearch(readQuery(input)),
};
}

View File

@ -1,88 +0,0 @@
import {
COMPACTION_PROVIDER_ID,
MEMORY_EMBEDDING_PROVIDER_ID,
MUSIC_PROVIDER_ID,
PLUGIN_ID,
REALTIME_TRANSCRIPTION_PROVIDER_ID,
REALTIME_VOICE_PROVIDER_ID,
SPEECH_PROVIDER_ID,
VIDEO_PROVIDER_ID,
} from "../constants.js";
export function buildKitchenToolResultMiddleware() {
return async (event = {}) => ({
...event,
kitchenSink: true,
pluginId: PLUGIN_ID,
scenarioId: "tool-result.middleware",
result: event.result,
metadata: {
...(event.metadata || {}),
kitchenSinkToolResultMiddleware: true,
},
});
}
export function buildKitchenService() {
return {
id: "kitchen-sink-service",
name: "Kitchen Sink Service",
description: "Credential-free background service fixture.",
start: async () => ({ ok: true, service: "kitchen-sink-service", state: "started" }),
stop: async () => ({ ok: true, service: "kitchen-sink-service", state: "stopped" }),
probe: async () => ({ ok: true, service: "kitchen-sink-service", state: "ready" }),
};
}
export function buildKitchenHttpRoute() {
return {
id: "kitchen-sink-http-status",
path: "/kitchen-sink/status",
auth: "gateway",
match: "exact",
handler: async (_req, res) => {
const body = JSON.stringify({ ok: true, pluginId: PLUGIN_ID, scenarioId: "http.status" });
if (res && typeof res === "object") {
res.statusCode = 200;
res.setHeader?.("content-type", "application/json");
res.end?.(body);
}
return { ok: true, body };
},
};
}
export function buildKitchenGatewayMethod() {
return async () => ({
ok: true,
pluginId: PLUGIN_ID,
providerIds: [
SPEECH_PROVIDER_ID,
REALTIME_TRANSCRIPTION_PROVIDER_ID,
REALTIME_VOICE_PROVIDER_ID,
VIDEO_PROVIDER_ID,
MUSIC_PROVIDER_ID,
MEMORY_EMBEDDING_PROVIDER_ID,
COMPACTION_PROVIDER_ID,
],
});
}
export function buildKitchenCliRegistrar() {
return async ({ program } = {}) => {
program?.command?.("kitchen-sink")?.description?.("Run Kitchen Sink fixture commands.");
return { ok: true, command: "kitchen-sink" };
};
}
export function buildKitchenCliMetadata() {
return {
descriptors: [
{
name: "kitchen-sink",
description: "Run Kitchen Sink fixture commands.",
hasSubcommands: true,
},
],
};
}

View File

@ -1,431 +0,0 @@
import {
COMPACTION_PROVIDER_ID,
DEFAULT_EMBEDDING_MODEL,
DEFAULT_IMAGE_MODEL,
DEFAULT_MEDIA_MODEL,
DEFAULT_TEXT_MODEL,
IMAGE_PROVIDER_ID,
MEDIA_PROVIDER_ID,
MEMORY_EMBEDDING_PROVIDER_ID,
MUSIC_PROVIDER_ID,
PLUGIN_ID,
REALTIME_TRANSCRIPTION_PROVIDER_ID,
REALTIME_VOICE_PROVIDER_ID,
SPEECH_PROVIDER_ID,
TEXT_PROVIDER_ID,
VIDEO_PROVIDER_ID,
WEB_FETCH_PROVIDER_ID,
WEB_SEARCH_PROVIDER_ID,
} from "../constants.js";
import {
createKitchenCompaction,
createKitchenEmbedding,
createKitchenMemorySearch,
createKitchenMusicResult,
createKitchenSpeechAsset,
createKitchenTextStream,
createKitchenTranscription,
createKitchenVideoResult,
kitchenImageDescription,
kitchenPromptGuidance,
kitchenSearchSchema,
kitchenTextModelDefinition,
kitchenTextProviderConfig,
readQuery,
readUrl,
runKitchenFetch,
runKitchenSearch,
stripDataUrl,
} from "../scenarios.js";
// Provider builders intentionally stay thin: map OpenClaw provider contracts to
// deterministic scenarios/fixtures, and keep the mock behavior outside runtime wiring.
export function buildKitchenImageProvider(runtime) {
return {
id: IMAGE_PROVIDER_ID,
aliases: ["kitchen", "kitchen-sink", "openclaw-kitchen-sink"],
label: "Kitchen Sink Image",
defaultModel: DEFAULT_IMAGE_MODEL,
models: [DEFAULT_IMAGE_MODEL],
capabilities: {
generate: {
maxCount: 1,
supportsSize: true,
supportsAspectRatio: true,
supportsResolution: true,
},
edit: {
enabled: true,
maxInputImages: 1,
maxCount: 1,
},
geometry: {
sizes: ["1024x1024"],
aspectRatios: ["1:1"],
resolutions: ["1K"],
},
},
isConfigured: () => true,
generateImage: async (req) => {
const result = await runtime.runScenario({
scenario: "image.generate",
prompt: req?.prompt,
route: "provider:image",
model: req?.model,
});
if (result.error) {
throw kitchenProviderError(result);
}
return {
images: [stripDataUrl(result.image)],
model: req?.model || DEFAULT_IMAGE_MODEL,
metadata: {
kitchenSink: true,
job: result.job,
asset: result.image.metadata,
provider: IMAGE_PROVIDER_ID,
pluginId: PLUGIN_ID,
scenarioId: result.scenarioId,
route: result.route,
request: {
prompt: req?.prompt,
size: req?.size,
aspectRatio: req?.aspectRatio,
count: req?.count || 1,
},
},
};
},
};
}
export function buildKitchenMediaProvider() {
return {
id: MEDIA_PROVIDER_ID,
capabilities: ["image", "audio", "video"],
defaultModels: { image: DEFAULT_MEDIA_MODEL },
autoPriority: { image: 5 },
describeImage: async (req) => ({
text: kitchenImageDescription(req?.prompt, 1),
model: req?.model || DEFAULT_MEDIA_MODEL,
}),
describeImages: async (req) => ({
text: kitchenImageDescription(req?.prompt, Array.isArray(req?.images) ? req.images.length : 0),
model: req?.model || DEFAULT_MEDIA_MODEL,
}),
transcribeAudio: async (req) => createKitchenTranscription({ audio: req?.audio, prompt: req?.prompt }),
describeVideo: async (req) => ({
text: "Kitchen Sink video fixture: three deterministic frames show the office sink asset, a close-up, and a fixture badge.",
model: req?.model || DEFAULT_MEDIA_MODEL,
metadata: { kitchenSink: true, provider: MEDIA_PROVIDER_ID, scenarioId: "media.video-describe" },
}),
};
}
export function buildKitchenSpeechProvider() {
return {
id: SPEECH_PROVIDER_ID,
label: "Kitchen Sink Speech",
voices: ["kitchen-neutral", "kitchen-robot"],
defaultVoice: "kitchen-neutral",
isConfigured: () => true,
synthesize: async (req) => createKitchenSpeechAsset({
text: req?.text,
voice: req?.voice,
model: req?.model,
}),
speak: async (req) => createKitchenSpeechAsset({
text: req?.text,
voice: req?.voice,
model: req?.model,
}),
};
}
export function buildKitchenRealtimeTranscriptionProvider() {
return {
id: REALTIME_TRANSCRIPTION_PROVIDER_ID,
label: "Kitchen Sink Realtime Transcription",
isConfigured: () => true,
createSession: (req = {}) => {
const chunks = [];
return {
provider: REALTIME_TRANSCRIPTION_PROVIDER_ID,
async connect() {
req.onReady?.({ provider: REALTIME_TRANSCRIPTION_PROVIDER_ID });
return { ok: true, provider: REALTIME_TRANSCRIPTION_PROVIDER_ID };
},
sendAudio(audio) {
chunks.push(audio);
req.onTranscript?.(`Kitchen Sink partial transcript ${chunks.length}.`);
},
async close() {
const result = createKitchenTranscription({ audio: Buffer.concat(chunks.map(toBuffer)) });
req.onTranscript?.(result.text);
req.onClose?.({ code: 1000, reason: "kitchen sink complete" });
return result;
},
};
},
};
}
export function buildKitchenRealtimeVoiceProvider() {
return {
id: REALTIME_VOICE_PROVIDER_ID,
label: "Kitchen Sink Realtime Voice",
isConfigured: () => true,
createBridge: (req = {}) => {
let connected = false;
const audio = [];
return {
supportsToolResultContinuation: true,
async connect() {
connected = true;
req.onEvent?.({ type: "connected", provider: REALTIME_VOICE_PROVIDER_ID });
},
sendAudio(chunk) {
audio.push(chunk);
req.onTranscript?.("Kitchen Sink realtime voice heard audio.");
},
setMediaTimestamp(timestampMs) {
req.onEvent?.({ type: "media_timestamp", timestampMs });
},
submitToolResult(result) {
req.onEvent?.({ type: "tool_result", result });
},
acknowledgeMark(mark) {
req.onEvent?.({ type: "mark", mark });
},
close() {
connected = false;
req.onEvent?.({ type: "closed", audioChunks: audio.length });
},
isConnected: () => connected,
};
},
};
}
export function buildKitchenVideoProvider() {
return {
id: VIDEO_PROVIDER_ID,
label: "Kitchen Sink Video",
defaultModel: "kitchen-sink-video-v1",
capabilities: {
generate: { maxVideos: 1, maxDurationSeconds: 3, supportsResolution: true },
imageToVideo: { enabled: true, maxVideos: 1, maxInputImages: 1, maxDurationSeconds: 3 },
videoToVideo: { enabled: false },
},
isConfigured: () => true,
generateVideo: async (req) => createKitchenVideoResult({ prompt: req?.prompt, model: req?.model }),
};
}
export function buildKitchenMusicProvider() {
return {
id: MUSIC_PROVIDER_ID,
label: "Kitchen Sink Music",
defaultModel: "kitchen-sink-music-v1",
capabilities: {
generate: { maxTracks: 1, maxDurationSeconds: 1 },
edit: { enabled: true, maxInputAudio: 1, maxTracks: 1 },
},
isConfigured: () => true,
generateMusic: async (req) => createKitchenMusicResult({ prompt: req?.prompt, model: req?.model }),
generate: async (req) => createKitchenMusicResult({ prompt: req?.prompt, model: req?.model }),
};
}
export function buildKitchenTextProvider() {
return {
id: TEXT_PROVIDER_ID,
label: "Kitchen Sink LLM",
docsPath: "/providers/models",
aliases: ["kitchen-sink-text", "kitchen"],
envVars: [],
auth: [
{
id: "none",
label: "No credentials",
hint: "Deterministic local fixture provider.",
kind: "custom",
run: async () => ({
profiles: [
{
id: "kitchen-sink-local",
label: "Kitchen Sink Local",
configured: true,
source: "fixture",
},
],
defaultModel: `${TEXT_PROVIDER_ID}/${DEFAULT_TEXT_MODEL}`,
notes: ["Kitchen Sink LLM is deterministic and does not call a network service."],
}),
},
],
staticCatalog: {
order: "simple",
run: async () => ({
provider: kitchenTextProviderConfig(),
}),
},
catalog: {
order: "simple",
run: async () => ({
provider: kitchenTextProviderConfig(),
}),
},
resolveDynamicModel: ({ modelId }) =>
modelId === DEFAULT_TEXT_MODEL ? kitchenTextModelDefinition() : undefined,
resolveSyntheticAuth: () => ({
apiKey: "kitchen-sink-local-fixture",
source: "kitchen-sink fixture",
mode: "token",
}),
createStreamFn: () => createKitchenTextStream,
resolveSystemPromptContribution: () => ({
stablePrefix: kitchenPromptGuidance().join("\n"),
}),
};
}
export function buildKitchenWebSearchProvider() {
return {
id: WEB_SEARCH_PROVIDER_ID,
label: "Kitchen Sink Search",
hint: "Credential-free deterministic search fixture.",
requiresCredential: false,
envVars: [],
placeholder: "no key required",
signupUrl: "https://github.com/openclaw/kitchen-sink",
docsUrl: "https://github.com/openclaw/kitchen-sink#readme",
credentialPath: `${pluginConfigPath()}.search`,
getCredentialValue: () => "fixture",
setCredentialValue: (target, value) => {
target.fixture = value;
},
applySelectionConfig: (config) => config,
resolveRuntimeMetadata: async () => ({ provider: WEB_SEARCH_PROVIDER_ID, pluginId: PLUGIN_ID }),
createTool: () => ({
description: "Search the deterministic Kitchen Sink fixture corpus.",
parameters: kitchenSearchSchema(),
execute: async (args) => runKitchenSearch(readQuery(args)),
}),
};
}
export function buildKitchenWebFetchProvider() {
return {
id: WEB_FETCH_PROVIDER_ID,
label: "Kitchen Sink Fetch",
hint: "Credential-free deterministic fetch fixture.",
requiresCredential: false,
envVars: [],
placeholder: "no key required",
signupUrl: "https://github.com/openclaw/kitchen-sink",
docsUrl: "https://github.com/openclaw/kitchen-sink#readme",
credentialPath: `${pluginConfigPath()}.fetch`,
getCredentialValue: () => "fixture",
setCredentialValue: (target, value) => {
target.fixture = value;
},
applySelectionConfig: (config) => config,
resolveRuntimeMetadata: async () => ({ provider: WEB_FETCH_PROVIDER_ID, pluginId: PLUGIN_ID }),
createTool: () => ({
description: "Fetch deterministic Kitchen Sink fixture documents.",
parameters: {
type: "object",
additionalProperties: false,
properties: {
url: { type: "string", description: "Fixture URL or topic." },
},
},
execute: async (args) => runKitchenFetch(readUrl(args)),
}),
};
}
export function buildKitchenMemoryEmbeddingProvider() {
return {
id: MEMORY_EMBEDDING_PROVIDER_ID,
label: "Kitchen Sink Memory Embeddings",
model: DEFAULT_EMBEDDING_MODEL,
dimensions: 8,
isConfigured: () => true,
embed: async (input) => ({
provider: MEMORY_EMBEDDING_PROVIDER_ID,
model: DEFAULT_EMBEDDING_MODEL,
embedding: createKitchenEmbedding(typeof input === "string" ? input : input?.text),
}),
embedMany: async (input) => {
const texts = Array.isArray(input) ? input : Array.isArray(input?.texts) ? input.texts : [input?.text ?? ""];
return {
provider: MEMORY_EMBEDDING_PROVIDER_ID,
model: DEFAULT_EMBEDDING_MODEL,
embeddings: texts.map((text) => createKitchenEmbedding(text)),
};
},
};
}
export function buildKitchenMemoryCorpusSupplement() {
return {
id: "kitchen-sink-memory-corpus",
label: "Kitchen Sink Memory Corpus",
search: async (query) => createKitchenMemorySearch(typeof query === "string" ? query : query?.query),
read: async (id = "ks-memory-runtime-surfaces") => ({
id,
title: "Kitchen Sink runtime surfaces",
text: "Kitchen Sink memory corpus fixture covering providers, channels, hooks, compaction, and tasks.",
metadata: { kitchenSink: true, pluginId: PLUGIN_ID, scenarioId: "memory.read" },
}),
list: async () => ({
items: [{ id: "ks-memory-runtime-surfaces", title: "Kitchen Sink runtime surfaces" }],
}),
};
}
export function buildKitchenCompactionProvider() {
return {
id: COMPACTION_PROVIDER_ID,
label: "Kitchen Sink Compaction",
compact: async (input) => createKitchenCompaction(input),
summarize: async (input) => createKitchenCompaction(input),
};
}
function pluginConfigPath() {
return `plugins.${PLUGIN_ID}`;
}
function kitchenProviderError(result) {
const error = new Error(result.error.message);
error.name = "KitchenSinkProviderError";
error.code = result.error.code;
error.statusCode = result.error.statusCode;
error.retryable = result.error.retryable;
error.retryAfterMs = result.error.retryAfterMs;
error.metadata = {
kitchenSink: true,
job: result.job,
pluginId: PLUGIN_ID,
provider: IMAGE_PROVIDER_ID,
scenarioId: result.scenarioId,
route: result.route,
};
return error;
}
function toBuffer(value) {
if (Buffer.isBuffer(value)) {
return value;
}
if (value instanceof Uint8Array) {
return Buffer.from(value);
}
if (typeof value === "string") {
return Buffer.from(value);
}
return Buffer.alloc(0);
}

View File

@ -1,134 +0,0 @@
import { PLUGIN_ID } from "../constants.js";
export function buildKitchenDetachedTaskRuntime() {
const tasks = new Map();
function create(params, status) {
const now = Date.now();
const runId = params.runId || `ks_task_${Math.abs(hashTask(params.task || status))}`;
const task = {
taskId: runId,
runId,
runtime: params.runtime || "cli",
taskKind: params.taskKind || "kitchen-sink",
sourceId: params.sourceId || PLUGIN_ID,
requesterSessionKey: params.requesterSessionKey || "kitchen-sink",
ownerKey: params.ownerKey || PLUGIN_ID,
scopeKind: params.scopeKind || "session",
childSessionKey: params.childSessionKey,
parentFlowId: params.parentFlowId,
parentTaskId: params.parentTaskId,
agentId: params.agentId,
label: params.label || "Kitchen Sink task",
task: params.task,
status,
deliveryStatus: params.deliveryStatus || "not_applicable",
notifyPolicy: params.notifyPolicy || "done_only",
createdAt: now,
startedAt: status === "running" ? params.startedAt || now : params.startedAt,
lastEventAt: params.lastEventAt || now,
progressSummary: params.progressSummary || undefined,
};
tasks.set(runId, task);
return task;
}
function update(runId, patch) {
const current = tasks.get(runId);
if (!current) {
return [];
}
const cleanPatch = Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined));
const next = { ...current, ...cleanPatch };
tasks.set(runId, next);
return [next];
}
return {
createQueuedTaskRun: (params) => create(params, "queued"),
createRunningTaskRun: (params) => create(params, "running"),
startTaskRunByRunId: (params) =>
update(params.runId, {
status: "running",
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
startedAt: params.startedAt || Date.now(),
lastEventAt: params.lastEventAt || Date.now(),
progressSummary: params.progressSummary || params.eventSummary || "Kitchen Sink task started.",
}),
recordTaskRunProgressByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
lastEventAt: params.lastEventAt || Date.now(),
progressSummary: params.progressSummary || params.eventSummary || "Kitchen Sink task progressed.",
}),
finalizeTaskRunByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
status: params.status,
endedAt: params.endedAt,
lastEventAt: params.lastEventAt || params.endedAt,
error: params.error,
progressSummary: params.progressSummary || undefined,
terminalSummary: params.terminalSummary || undefined,
terminalOutcome: params.terminalOutcome || undefined,
}),
completeTaskRunByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
status: "succeeded",
endedAt: params.endedAt,
lastEventAt: params.lastEventAt || params.endedAt,
progressSummary: params.progressSummary || undefined,
terminalSummary: params.terminalSummary || "Kitchen Sink task completed.",
terminalOutcome: params.terminalOutcome || "succeeded",
}),
failTaskRunByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
status: params.status || "failed",
endedAt: params.endedAt,
lastEventAt: params.lastEventAt || params.endedAt,
error: params.error,
progressSummary: params.progressSummary || undefined,
terminalSummary: params.terminalSummary || "Kitchen Sink task failed.",
}),
setDetachedTaskDeliveryStatusByRunId: (params) =>
update(params.runId, {
runtime: params.runtime,
requesterSessionKey: params.sessionKey,
deliveryStatus: params.deliveryStatus,
error: params.error,
}),
cancelDetachedTaskRunById: async ({ taskId }) => {
const current = tasks.get(taskId);
if (!current) {
return { found: false, cancelled: false, reason: "not owned by Kitchen Sink" };
}
const task = {
...current,
status: "cancelled",
endedAt: Date.now(),
lastEventAt: Date.now(),
terminalSummary: "Kitchen Sink task cancelled.",
};
tasks.set(taskId, task);
return { found: true, cancelled: true, task };
},
tryRecoverTaskBeforeMarkLost: ({ task }) => ({
recovered: Boolean(task?.taskId && tasks.has(task.taskId)),
}),
};
}
function hashTask(input) {
let hash = 0;
for (const char of String(input)) {
hash = Math.imul(31, hash) + char.charCodeAt(0);
}
return hash;
}

File diff suppressed because it is too large Load Diff